|
|
|
|
@@ -13,7 +13,9 @@
|
|
|
|
|
|
|
|
|
|
<!-- 顶部栏: 标题 + 新对话 -->
|
|
|
|
|
<view class="chat-topbar">
|
|
|
|
|
<text class="chat-title">{{ $lang('ai-chat.index.aiNickname') }}</text>
|
|
|
|
|
<!-- PR-CHAT-UI-CONFIG-V2 (2026-09-24): 智能体显示名走后端配置, 空则回落 i18n 默认
|
|
|
|
|
PR-CHAT-UI-CONFIG-V3 (2026-09-24): ui_show_assistant_name 控制顶栏名称显隐 -->
|
|
|
|
|
<text class="chat-title" v-if="chatUiConfig.ui_show_assistant_name">{{ chatUiConfig.ui_assistant_name || $lang('ai-chat.index.aiNickname') }}</text>
|
|
|
|
|
<view class="new-chat-btn" @click="onNewChat">
|
|
|
|
|
<!-- 气泡+加号线性图标 (base64 SVG: 小程序不支持内联 SVG) -->
|
|
|
|
|
<view class="new-chat-icon"></view>
|
|
|
|
|
@@ -137,7 +139,8 @@
|
|
|
|
|
|
|
|
|
|
<!-- AI消息 -->
|
|
|
|
|
<view v-else class="ai-message">
|
|
|
|
|
<view class="avatar">
|
|
|
|
|
<!-- PR-CHAT-UI-CONFIG-V3 (2026-09-24): ui_show_assistant_avatar 控制头像显隐; 自定义头像在 aiAvatarUrl computed 优先 -->
|
|
|
|
|
<view class="avatar" v-if="chatUiConfig.ui_show_assistant_avatar">
|
|
|
|
|
<!-- 智能客服头像 -->
|
|
|
|
|
<image
|
|
|
|
|
v-if="aiAvatarUrl && !avatarError"
|
|
|
|
|
@@ -163,7 +166,7 @@
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- AI 回答上方只显示时间(昵称"智能助手"已按需求移除) -->
|
|
|
|
|
<view class="message-nickname ai-nickname">
|
|
|
|
|
<view class="message-nickname ai-nickname" v-if="chatUiConfig.ui_show_message_time">
|
|
|
|
|
<text class="message-time">{{ formatTime(message.timestamp) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="message-bubble">
|
|
|
|
|
@@ -737,7 +740,16 @@ export default {
|
|
|
|
|
ui_enable_attachment_types: ['image', 'file', 'audio', 'video'],
|
|
|
|
|
ui_input_max_length: 500,
|
|
|
|
|
ui_available_models: ['Auto'],
|
|
|
|
|
ui_enable_feedback: true
|
|
|
|
|
ui_enable_feedback: true,
|
|
|
|
|
// PR-CHAT-UI-CONFIG-V2 (2026-09-24): 与后端 Config::DEFAULT_UI_CONFIG 同步
|
|
|
|
|
ui_assistant_name: '',
|
|
|
|
|
// PR-CHAT-UI-CONFIG-V3 (2026-09-24): 与后端 Config::DEFAULT_UI_CONFIG 同步
|
|
|
|
|
ui_show_assistant_name: true,
|
|
|
|
|
ui_show_assistant_avatar: true,
|
|
|
|
|
ui_assistant_avatar: '',
|
|
|
|
|
ui_show_message_time: true,
|
|
|
|
|
ui_time_format: 'auto',
|
|
|
|
|
ui_time_humanize: false
|
|
|
|
|
},
|
|
|
|
|
chatUiConfigLoaded: false,
|
|
|
|
|
|
|
|
|
|
@@ -768,13 +780,18 @@ export default {
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
// PR-CHAT-UI-CONFIG-V3 (2026-09-24): 后台配置了自定义头像则优先 ($util.img 兼容 pic_path 与完整 URL)
|
|
|
|
|
aiAvatar() {
|
|
|
|
|
const custom = this.chatUiConfig && this.chatUiConfig.ui_assistant_avatar;
|
|
|
|
|
if (custom) return this.$util.img(custom);
|
|
|
|
|
return this.$util.img(IMAGE_CONFIG.aiAvatar);
|
|
|
|
|
},
|
|
|
|
|
defaultAvatar() {
|
|
|
|
|
return this.$util.img(IMAGE_CONFIG.userAvatar);
|
|
|
|
|
},
|
|
|
|
|
aiAvatarUrl() {
|
|
|
|
|
const custom = this.chatUiConfig && this.chatUiConfig.ui_assistant_avatar;
|
|
|
|
|
if (custom) return this.$util.img(custom);
|
|
|
|
|
return this.$util.img(IMAGE_CONFIG.aiAvatar);
|
|
|
|
|
},
|
|
|
|
|
chatMessagesStyle() {
|
|
|
|
|
@@ -2316,7 +2333,10 @@ _handleStreamError(msg) {
|
|
|
|
|
return html
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 格式化时间(人性化显示)
|
|
|
|
|
// PR-CHAT-UI-CONFIG-V3 (2026-09-24): 时间格式化支持后端配置
|
|
|
|
|
// ui_time_format = 'auto' 智能分段(默认, 与历史行为一致): 今天 HH:mm / 昨天 / 前天 / 本年 M/D / 跨年 Y/M/D
|
|
|
|
|
// ui_time_format = 'full' 始终完整 YYYY-MM-DD HH:mm
|
|
|
|
|
// ui_time_humanize = true 微信式相对时间(优先于 format): 刚刚 / N分钟前 / N小时前 / 昨天 / 前天 / M月D日 / Y年M月D日
|
|
|
|
|
formatTime(timestamp) {
|
|
|
|
|
if (!timestamp) return '';
|
|
|
|
|
|
|
|
|
|
@@ -2352,6 +2372,19 @@ _handleStreamError(msg) {
|
|
|
|
|
const minutes = date.getMinutes().toString().padStart(2, '0');
|
|
|
|
|
const timeStr = `${hours}:${minutes}`;
|
|
|
|
|
|
|
|
|
|
// 人性化相对时间 (开启后优先于 ui_time_format)
|
|
|
|
|
if (this.chatUiConfig && this.chatUiConfig.ui_time_humanize) {
|
|
|
|
|
return this.humanizeTime(date, msgDate, today, timeStr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 完整时间格式: 始终 YYYY-MM-DD HH:mm
|
|
|
|
|
if (this.chatUiConfig && this.chatUiConfig.ui_time_format === 'full') {
|
|
|
|
|
const mm = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
|
|
|
const dd = date.getDate().toString().padStart(2, '0');
|
|
|
|
|
return `${date.getFullYear()}-${mm}-${dd} ${timeStr}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// auto: 智能分段 (原有逻辑)
|
|
|
|
|
if (msgDate.getTime() === today.getTime()) {
|
|
|
|
|
return timeStr;
|
|
|
|
|
}
|
|
|
|
|
@@ -2373,6 +2406,42 @@ _handleStreamError(msg) {
|
|
|
|
|
const yearStr = date.getFullYear().toString();
|
|
|
|
|
return `${yearStr}/${monthStr}/${dayStr} ${timeStr}`;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// PR-CHAT-UI-CONFIG-V3 (2026-09-24): 微信式人性化相对时间
|
|
|
|
|
// 刚刚 / N分钟前 / 今天内N小时前 / 昨天 HH:mm / 前天 HH:mm / 本年 M月D日 HH:mm / 跨年 Y年M月D日 HH:mm
|
|
|
|
|
humanizeTime(date, msgDate, today, timeStr) {
|
|
|
|
|
const diff = Date.now() - date.getTime();
|
|
|
|
|
const oneMin = 60 * 1000;
|
|
|
|
|
const oneHour = 60 * oneMin;
|
|
|
|
|
|
|
|
|
|
if (diff >= 0 && diff < oneMin) {
|
|
|
|
|
return this.$lang('ai-chat.index.timeJustNow');
|
|
|
|
|
}
|
|
|
|
|
if (diff >= 0 && diff < oneHour) {
|
|
|
|
|
return this.$lang('ai-chat.index.timeMinutesAgo', { n: Math.floor(diff / oneMin) });
|
|
|
|
|
}
|
|
|
|
|
// 今天内且已超过 1 小时
|
|
|
|
|
if (msgDate.getTime() === today.getTime()) {
|
|
|
|
|
if (diff < 24 * oneHour) {
|
|
|
|
|
return this.$lang('ai-chat.index.timeHoursAgo', { n: Math.floor(diff / oneHour) });
|
|
|
|
|
}
|
|
|
|
|
return timeStr; // 兜底(理论不可达: 同一天 diff 不可能 >= 24h)
|
|
|
|
|
}
|
|
|
|
|
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
|
|
|
|
|
if (msgDate.getTime() === yesterday.getTime()) {
|
|
|
|
|
return `${this.$lang('ai-chat.index.timeFormatYesterday')} ${timeStr}`;
|
|
|
|
|
}
|
|
|
|
|
const beforeYesterday = new Date(today.getTime() - 2 * 24 * 60 * 60 * 1000);
|
|
|
|
|
if (msgDate.getTime() === beforeYesterday.getTime()) {
|
|
|
|
|
return `${this.$lang('ai-chat.index.timeFormatBeforeYesterday')} ${timeStr}`;
|
|
|
|
|
}
|
|
|
|
|
const m = date.getMonth() + 1;
|
|
|
|
|
const d = date.getDate();
|
|
|
|
|
if (date.getFullYear() === new Date().getFullYear()) {
|
|
|
|
|
return `${this.$lang('ai-chat.index.timeMonthDay', { m, d })} ${timeStr}`;
|
|
|
|
|
}
|
|
|
|
|
return `${this.$lang('ai-chat.index.timeYearMonthDay', { y: date.getFullYear(), m, d })} ${timeStr}`;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 格式化文件大小
|
|
|
|
|
formatFileSize(bytes) {
|
|
|
|
|
|