chore(build): 调整ai-chat-message组件的位置,减少对主包尺寸的影响
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"ns-loading": "../ns-loading/ns-loading"
|
||||
}
|
||||
}
|
||||
@@ -109,6 +109,7 @@ export default {
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* 初始化多语言配置
|
||||
*/
|
||||
@@ -130,12 +131,14 @@ export default {
|
||||
this.currentLangIndex = 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 电话联系客服
|
||||
*/
|
||||
call() {
|
||||
this.customerService.makePhoneCall(this.tel);
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换中英文语言,并刷新当前页面(保留所有参数)
|
||||
*/
|
||||
@@ -153,12 +156,12 @@ export default {
|
||||
openAIChat() {
|
||||
this.$util.redirectTo(this.$util.AI_CHAT_PAGE_URL);
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开客服选择对话框
|
||||
*/
|
||||
openCustomerSelectPopup() {
|
||||
if (this.customerService) {
|
||||
this.customerService.openCustomerSelectPopupDialog();
|
||||
} else {
|
||||
uni.showToast({ title: '客服初始化中,请稍后重试', icon: 'none' });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
<template>
|
||||
<view class="ai-chat-container">
|
||||
<!-- 聊天消息列表 -->
|
||||
<scroll-view
|
||||
class="chat-messages"
|
||||
scroll-y
|
||||
:scroll-top="scrollTop"
|
||||
@scroll="onScroll"
|
||||
<scroll-view class="chat-messages" scroll-y :scroll-top="scrollTop" @scroll="onScroll"
|
||||
:scroll-with-animation="false">
|
||||
|
||||
<!-- 加载更多历史消息 -->
|
||||
@@ -14,11 +10,8 @@
|
||||
</view>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<view
|
||||
v-for="(message, index) in messages"
|
||||
:key="`msg-${message.id || message.timestamp}-${index}`"
|
||||
class="message-item"
|
||||
:class="[message.role, { 'first-message': index === 0 }]">
|
||||
<view v-for="(message, index) in messages" :key="`msg-${message.id || message.timestamp}-${index}`"
|
||||
class="message-item" :class="[message.role, { 'first-message': index === 0 }]">
|
||||
|
||||
<!-- 用户消息 -->
|
||||
<view v-if="message.role === 'user'" class="user-message">
|
||||
@@ -85,21 +78,12 @@
|
||||
<text class="audio-duration">{{ formatDuration(message.duration) }}</text>
|
||||
</view>
|
||||
<view class="audio-controls">
|
||||
<button
|
||||
class="play-btn"
|
||||
:class="{ playing: message.playing }"
|
||||
@click="toggleAudio(message)">
|
||||
<button class="play-btn" :class="{ playing: message.playing }" @click="toggleAudio(message)">
|
||||
<text class="iconfont" :class="message.playing ? 'icon-pause' : 'icon-play'"></text>
|
||||
</button>
|
||||
<slider
|
||||
class="audio-slider"
|
||||
:value="message.currentTime || 0"
|
||||
:max="message.duration || 0"
|
||||
@changing="onAudioSliderChange"
|
||||
@change="onAudioSliderChangeEnd"
|
||||
activeColor="#8a9fb8"
|
||||
backgroundColor="#e8edf3"
|
||||
block-size="12" />
|
||||
<slider class="audio-slider" :value="message.currentTime || 0" :max="message.duration || 0"
|
||||
@changing="onAudioSliderChange" @change="onAudioSliderChangeEnd" activeColor="#8a9fb8"
|
||||
backgroundColor="#e8edf3" block-size="12" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -107,14 +91,8 @@
|
||||
<!-- 视频消息 -->
|
||||
<view v-else-if="message.type === 'video'" class="video-content">
|
||||
<view class="video-player">
|
||||
<video
|
||||
:src="message.url"
|
||||
:poster="message.cover"
|
||||
:controls="true"
|
||||
:autoplay="false"
|
||||
class="video-element"
|
||||
@play="onVideoPlay(message)"
|
||||
@pause="onVideoPause(message)">
|
||||
<video :src="message.url" :poster="message.cover" :controls="true" :autoplay="false"
|
||||
class="video-element" @play="onVideoPlay(message)" @pause="onVideoPause(message)">
|
||||
</video>
|
||||
<view class="video-info">
|
||||
<text class="video-title">{{ message.title || '视频消息' }}</text>
|
||||
@@ -163,12 +141,8 @@
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="message-actions" v-if="message.actions && message.actions.length > 0">
|
||||
<button
|
||||
v-for="action in message.actions"
|
||||
:key="action.id"
|
||||
class="action-btn"
|
||||
:class="[`iconfont`, action.icon, action.type]"
|
||||
@click="handleAction(action, message)">
|
||||
<button v-for="action in message.actions" :key="action.id" class="action-btn"
|
||||
:class="[`iconfont`, action.icon, action.type]" @click="handleAction(action, message)">
|
||||
{{ action.text }}
|
||||
</button>
|
||||
</view>
|
||||
@@ -199,21 +173,11 @@
|
||||
</view>
|
||||
|
||||
<view class="input-container">
|
||||
<textarea
|
||||
v-model="inputText"
|
||||
class="message-input"
|
||||
placeholder="请输入您的问题..."
|
||||
:maxlength="500"
|
||||
:auto-height="true"
|
||||
:show-confirm-bar="false"
|
||||
@confirm="sendMessage"
|
||||
@input="onInput" />
|
||||
<textarea v-model="inputText" class="message-input" placeholder="请输入您的问题..." :maxlength="500"
|
||||
:auto-height="true" :show-confirm-bar="false" @confirm="sendMessage" @input="onInput" />
|
||||
|
||||
<!-- 发送按钮 -->
|
||||
<button
|
||||
class="send-btn"
|
||||
:class="{ disabled: !inputText.trim() }"
|
||||
@click="sendMessage"
|
||||
<button class="send-btn" :class="{ disabled: !inputText.trim() }" @click="sendMessage"
|
||||
:disabled="!inputText.trim()">
|
||||
<text>发送</text>
|
||||
</button>
|
||||
@@ -271,11 +235,7 @@
|
||||
</view>
|
||||
<view class="voice-content">
|
||||
<view class="voice-wave" :class="{ recording: voiceInputing }">
|
||||
<view
|
||||
v-for="i in 8"
|
||||
:key="i"
|
||||
class="wave-bar"
|
||||
:style="{ animationDelay: (i * 0.1) + 's' }"></view>
|
||||
<view v-for="i in 8" :key="i" class="wave-bar" :style="{ animationDelay: (i * 0.1) + 's' }"></view>
|
||||
</view>
|
||||
<text class="voice-tip">{{ voiceInputing ? '正在录音,松开结束' : '点击开始录音' }}</text>
|
||||
</view>
|
||||
@@ -331,23 +291,15 @@
|
||||
</view>
|
||||
<view class="nickname-editor-content">
|
||||
<view class="nickname-input-container">
|
||||
<input
|
||||
v-model="tempNickname"
|
||||
class="nickname-input"
|
||||
placeholder="请输入您的新昵称"
|
||||
:maxlength="12"
|
||||
type="text" />
|
||||
<input v-model="tempNickname" class="nickname-input" placeholder="请输入您的新昵称" :maxlength="12" type="text" />
|
||||
</view>
|
||||
<view class="nickname-tip">昵称长度限制1-12个字符,仅自己可见</view>
|
||||
<view class="nickname-editor-actions">
|
||||
<button class="nickname-action-btn cancel" @click="closeNicknameEditor">
|
||||
<text>取消</text>
|
||||
</button>
|
||||
<button
|
||||
class="nickname-action-btn confirm"
|
||||
:class="{ disabled: !tempNickname.trim() }"
|
||||
:disabled="!tempNickname.trim()"
|
||||
@click="saveNickname">
|
||||
<button class="nickname-action-btn confirm" :class="{ disabled: !tempNickname.trim() }"
|
||||
:disabled="!tempNickname.trim()" @click="saveNickname">
|
||||
<text>保存</text>
|
||||
</button>
|
||||
</view>
|
||||
@@ -1402,7 +1354,8 @@ $radius-lg: 36rpx;
|
||||
padding: 24rpx 32rpx;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
min-height: 0; /* 重要:防止flex元素溢出 */
|
||||
min-height: 0;
|
||||
/* 重要:防止flex元素溢出 */
|
||||
|
||||
.load-more {
|
||||
text-align: center;
|
||||
@@ -1417,7 +1370,8 @@ $radius-lg: 36rpx;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.user-message, .ai-message {
|
||||
.user-message,
|
||||
.ai-message {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
@@ -1448,17 +1402,20 @@ $radius-lg: 36rpx;
|
||||
margin-left: 24rpx;
|
||||
max-width: calc(100% - 112rpx);
|
||||
min-width: 0;
|
||||
width: 0; /* 添加这个属性防止flex元素溢出 */
|
||||
width: 0;
|
||||
/* 添加这个属性防止flex元素溢出 */
|
||||
|
||||
.message-nickname {
|
||||
font-size: 24rpx;
|
||||
color: $color-text-light;
|
||||
margin-bottom: 8rpx;
|
||||
letter-spacing: 0.5rpx;
|
||||
|
||||
// 用户昵称右对齐,AI昵称左对齐
|
||||
&:not(.ai-message .message-nickname) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
// AI昵称专属样式 - 左移到气泡上方
|
||||
&.ai-nickname {
|
||||
text-align: left;
|
||||
@@ -1898,20 +1855,27 @@ $radius-lg: 36rpx;
|
||||
text-align: right;
|
||||
|
||||
.message-bubble {
|
||||
background: linear-gradient(135deg, #ffadd2, #f783ac) !important; /* 粉色渐变 */
|
||||
background: linear-gradient(135deg, #ffadd2, #f783ac) !important;
|
||||
/* 粉色渐变 */
|
||||
color: white !important;
|
||||
border-radius: 16rpx 16rpx 4rpx 16rpx; /* 右对齐气泡尖角适配 */
|
||||
border-radius: 16rpx 16rpx 4rpx 16rpx;
|
||||
/* 右对齐气泡尖角适配 */
|
||||
box-shadow: 0 8rpx 20rpx rgba(247, 131, 172, 0.3) !important;
|
||||
border: none !important;
|
||||
/* ✅ 关键:允许内容撑开高度 */
|
||||
min-height: auto;
|
||||
height: auto;
|
||||
padding: 24rpx 32rpx; /* 保留内边距 */
|
||||
display: inline-block; /* 让宽度也随内容收缩(可选) */
|
||||
max-width: 80%; /* 防止过宽 */
|
||||
padding: 24rpx 32rpx;
|
||||
/* 保留内边距 */
|
||||
display: inline-block;
|
||||
/* 让宽度也随内容收缩(可选) */
|
||||
max-width: 80%;
|
||||
/* 防止过宽 */
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap; /* 保留用户输入的换行符 */
|
||||
white-space: pre-wrap;
|
||||
/* 保留用户输入的换行符 */
|
||||
line-height: 1.6;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
@@ -1919,7 +1883,8 @@ $radius-lg: 36rpx;
|
||||
right: -14rpx;
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
background: #ffffff !important; /* 菱形改为白色 */
|
||||
background: #ffffff !important;
|
||||
/* 菱形改为白色 */
|
||||
border-radius: 6rpx;
|
||||
transform: rotate(45deg);
|
||||
box-shadow: 4rpx -4rpx 4rpx rgba(0, 0, 0, 0.05);
|
||||
@@ -1932,11 +1897,9 @@ $radius-lg: 36rpx;
|
||||
left: -100%;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
background: linear-gradient(to right,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.15) 100%
|
||||
);
|
||||
rgba(255, 255, 255, 0.15) 100%);
|
||||
animation: shine 4s infinite linear;
|
||||
}
|
||||
}
|
||||
@@ -1952,9 +1915,11 @@ $radius-lg: 36rpx;
|
||||
align-items: flex-start;
|
||||
|
||||
.message-bubble {
|
||||
background: linear-gradient(135deg, #dbeafe, #bfdbfe) !important; /* 蓝色浅渐变 */
|
||||
background: linear-gradient(135deg, #dbeafe, #bfdbfe) !important;
|
||||
/* 蓝色浅渐变 */
|
||||
color: #2c3e50 !important;
|
||||
border-radius: 16rpx 16rpx 16rpx 4rpx; /* 左对齐气泡尖角适配 */
|
||||
border-radius: 16rpx 16rpx 16rpx 4rpx;
|
||||
/* 左对齐气泡尖角适配 */
|
||||
box-shadow: 0 8rpx 20rpx rgba(191, 219, 254, 0.4) !important;
|
||||
border: 1rpx solid #dbeafe !important;
|
||||
min-height: auto;
|
||||
@@ -1965,6 +1930,7 @@ $radius-lg: 36rpx;
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.6;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
@@ -1972,7 +1938,8 @@ $radius-lg: 36rpx;
|
||||
left: -14rpx;
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
background: #ffffff !important; /* 菱形改为白色 */
|
||||
background: #ffffff !important;
|
||||
/* 菱形改为白色 */
|
||||
border-radius: 6rpx;
|
||||
transform: rotate(45deg);
|
||||
box-shadow: -4rpx 4rpx 4rpx rgba(0, 0, 0, 0.05);
|
||||
@@ -2072,9 +2039,12 @@ $radius-lg: 36rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
text-align: center; /* 兼容多端文字居中 */
|
||||
white-space: nowrap; /* 强制文字单行横向显示 */
|
||||
line-height: 1; /* 重置行高,避免文字垂直偏移 */
|
||||
text-align: center;
|
||||
/* 兼容多端文字居中 */
|
||||
white-space: nowrap;
|
||||
/* 强制文字单行横向显示 */
|
||||
line-height: 1;
|
||||
/* 重置行高,避免文字垂直偏移 */
|
||||
top: -10px;
|
||||
|
||||
&.disabled {
|
||||
@@ -2098,8 +2068,10 @@ $radius-lg: 36rpx;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none; /* 让鼠标事件穿透容器,不影响主内容交互 */
|
||||
z-index: 999; /* 基础弹窗层级 */
|
||||
pointer-events: none;
|
||||
/* 让鼠标事件穿透容器,不影响主内容交互 */
|
||||
z-index: 999;
|
||||
/* 基础弹窗层级 */
|
||||
|
||||
/* 子弹窗需要开启 pointer-events,否则点击无效 */
|
||||
>.tools-popup,
|
||||
@@ -2470,14 +2442,45 @@ $radius-lg: 36rpx;
|
||||
border-radius: 4rpx;
|
||||
animation: none;
|
||||
|
||||
&:nth-child(1) { height: 60rpx; animation-delay: 0s; }
|
||||
&:nth-child(2) { height: 90rpx; animation-delay: 0.1s; }
|
||||
&:nth-child(3) { height: 50rpx; animation-delay: 0.2s; }
|
||||
&:nth-child(4) { height: 120rpx; animation-delay: 0.3s; }
|
||||
&:nth-child(5) { height: 70rpx; animation-delay: 0.4s; }
|
||||
&:nth-child(6) { height: 100rpx; animation-delay: 0.5s; }
|
||||
&:nth-child(7) { height: 80rpx; animation-delay: 0.6s; }
|
||||
&:nth-child(8) { height: 40rpx; animation-delay: 0.7s; }
|
||||
&:nth-child(1) {
|
||||
height: 60rpx;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
height: 90rpx;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
height: 50rpx;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
height: 120rpx;
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
&:nth-child(5) {
|
||||
height: 70rpx;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
&:nth-child(6) {
|
||||
height: 100rpx;
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
|
||||
&:nth-child(7) {
|
||||
height: 80rpx;
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
&:nth-child(8) {
|
||||
height: 40rpx;
|
||||
animation-delay: 0.7s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2645,10 +2648,13 @@ $radius-lg: 36rpx;
|
||||
|
||||
/* 动画定义 */
|
||||
@keyframes dotPulse {
|
||||
0%, 100% {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.4;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.2);
|
||||
@@ -2656,10 +2662,13 @@ $radius-lg: 36rpx;
|
||||
}
|
||||
|
||||
@keyframes wave {
|
||||
0%, 100% {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: scaleY(0.5);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scaleY(1);
|
||||
opacity: 1;
|
||||
@@ -2670,6 +2679,7 @@ $radius-lg: 36rpx;
|
||||
0% {
|
||||
left: -100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
left: 200%;
|
||||
}
|
||||
@@ -2679,9 +2689,11 @@ $radius-lg: 36rpx;
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
@@ -2718,7 +2730,8 @@ $radius-lg: 36rpx;
|
||||
.message-item {
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.user-message, .ai-message {
|
||||
.user-message,
|
||||
.ai-message {
|
||||
.avatar {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
@@ -52,7 +52,12 @@ import { mapGetters, mapMutations } from 'vuex'
|
||||
import navigationHelper from '@/common/js/navigation';
|
||||
import { EventSafety } from '@/common/js/event-safety';
|
||||
|
||||
import aiChatMessage from './ai-chat-message.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
aiChatMessage,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialMessages: [
|
||||
@@ -86,7 +91,6 @@ export default {
|
||||
safeEventHandlers: new Map()
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'globalAIKefuConfig'
|
||||
@@ -142,9 +146,6 @@ export default {
|
||||
this.cleanup()
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
methods: {
|
||||
// ========== 安全事件处理 ==========
|
||||
setupSafeEventListeners() {
|
||||
|
||||
Reference in New Issue
Block a user