diff --git a/common/js/ai-service.js b/common/js/ai-service.js index 372c131..d0d21d7 100644 --- a/common/js/ai-service.js +++ b/common/js/ai-service.js @@ -451,7 +451,16 @@ const CHAT_UI_DEFAULTS = { 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 }; // chat_ui_config 自带短 TTL 缓存(60s, 与 service_config 同步); diff --git a/lang/en-us/ai-chat/index.js b/lang/en-us/ai-chat/index.js index e880b56..d24ad50 100644 --- a/lang/en-us/ai-chat/index.js +++ b/lang/en-us/ai-chat/index.js @@ -12,6 +12,12 @@ export const lang = { timeFormatToday: 'Today', timeFormatYesterday: 'Yesterday', timeFormatBeforeYesterday: 'Day before yesterday', + // PR-CHAT-UI-CONFIG-V3 (2026-09-24): Humanized relative time (ui_time_humanize) + timeJustNow: 'Just now', + timeMinutesAgo: '{n} min ago', + timeHoursAgo: '{n} h ago', + timeMonthDay: '{m}/{d}', + timeYearMonthDay: '{y}/{m}/{d}', // ====== ai-chat-message ====== aiNickname: 'AI Assistant', diff --git a/lang/zh-cn/ai-chat/index.js b/lang/zh-cn/ai-chat/index.js index f5bda06..3668e11 100644 --- a/lang/zh-cn/ai-chat/index.js +++ b/lang/zh-cn/ai-chat/index.js @@ -12,6 +12,12 @@ export const lang = { timeFormatToday: '今天', timeFormatYesterday: '昨天', timeFormatBeforeYesterday: '前天', + // PR-CHAT-UI-CONFIG-V3 (2026-09-24): 人性化相对时间 (ui_time_humanize) + timeJustNow: '刚刚', + timeMinutesAgo: '{n}分钟前', + timeHoursAgo: '{n}小时前', + timeMonthDay: '{m}月{d}日', + timeYearMonthDay: '{y}年{m}月{d}日', // ====== ai-chat-message ====== aiNickname: '智能助手', diff --git a/pages_tool/ai-chat/ai-chat-message.vue b/pages_tool/ai-chat/ai-chat-message.vue index 9c8add5..cc87ba1 100644 --- a/pages_tool/ai-chat/ai-chat-message.vue +++ b/pages_tool/ai-chat/ai-chat-message.vue @@ -13,7 +13,9 @@ - {{ $lang('ai-chat.index.aiNickname') }} + + {{ chatUiConfig.ui_assistant_name || $lang('ai-chat.index.aiNickname') }} @@ -137,7 +139,8 @@ - + + - + {{ formatTime(message.timestamp) }} @@ -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) {