From 480b5f1a8f2e871abf421aec1a83ee63b9ac82fe Mon Sep 17 00:00:00 2001 From: Wangxin Date: Mon, 10 Nov 2025 23:54:26 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E3=80=90web=E3=80=91=20ai=E5=8A=A9?= =?UTF-8?q?=E6=89=8B=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/QandA/Aiassistant/index.vue | 280 ++++++++++++++++-- 1 file changed, 252 insertions(+), 28 deletions(-) diff --git a/ALOps_sys_fe/alops-ui/src/views/QandA/Aiassistant/index.vue b/ALOps_sys_fe/alops-ui/src/views/QandA/Aiassistant/index.vue index eaca675a..a5ec32da 100644 --- a/ALOps_sys_fe/alops-ui/src/views/QandA/Aiassistant/index.vue +++ b/ALOps_sys_fe/alops-ui/src/views/QandA/Aiassistant/index.vue @@ -18,8 +18,8 @@ :class="['message', message.sender]" >
-
{{ message.text }}
-
{{ message.time }}
+
+
@@ -73,7 +73,7 @@ export default { ] }; },mounted() { - // 初始化时滚动到底部 + // 初始化时滚动到底部,符合大模型聊天体验 this.scrollToBottom(); }, methods: { @@ -86,33 +86,109 @@ export default { sender: 'customer', time: this.formatTime(new Date()) }); + // 用户发送消息后滚动到底部,符合大模型聊天体验 + this.scrollToBottom(); let newMessageContent = this.newMessage; - // 模拟自动回复 - setTimeout(() => { - this.autoReply(newMessageContent); - }, 1000); + // 直接调用autoReply,不再延迟 + this.autoReply(newMessageContent); this.newMessage = ''; }, async autoReply(userMessage) { console.log("%c 🇱🇺: autoReply -> userMessage ", "font-size:16px;background-color:#c70b31;color:white;", userMessage) let replyText = ''; - let res = await knowledgeAsk({question: userMessage }); - if (res.code == 200) { - replyText = res.data.answer; - }else { - replyText = '很抱歉,暂时无法回答您的问题。您可以尝试其他常见问题或联系客服人员。'; + + // 测试模式:当用户输入"测试数据"时,返回格式化的虚拟数据 + if (userMessage.includes('测试数据')) { + // 模拟短暂延迟,让测试数据也有加载感觉 + setTimeout(() => { + replyText = this.getTestData(); + // 添加一个空的回复消息,稍后通过打字效果填充内容 + const messageIndex = this.messages.length; + this.messages.push({ + text: '', // 初始为空 + sender: 'service', + time: this.formatTime(new Date()) + }); + + // 调用打字效果方法 + this.typeWriter(messageIndex, replyText); + }, 500); + } else { + // 设置loading状态 + this.isLoading = true; + + try { + let res = await knowledgeAsk({question: userMessage }); + if (res.code == 200) { + replyText = this.formatResponse(res.data.answer); + } else { + replyText = '很抱歉,暂时无法回答您的问题。您可以尝试其他常见问题或联系客服人员。'; + } + + // 添加一个空的回复消息,稍后通过打字效果填充内容 + const messageIndex = this.messages.length; + this.messages.push({ + text: '', // 初始为空 + sender: 'service', + time: this.formatTime(new Date()) + }); + + // 调用打字效果方法 + this.typeWriter(messageIndex, replyText); + } finally { + // 无论请求成功失败,都关闭loading + setTimeout(() => { + this.isLoading = false; + }, 300); // 延迟一点关闭,让用户有完成感 + } } - - // 添加客服回复 - this.messages.push({ - text: replyText, - sender: 'service', - time: this.formatTime(new Date()) + }, + + // 打字效果方法 + typeWriter(messageIndex, fullText) { + let currentIndex = 0; + + // 优化滚动:先滚动到底部 + this.$nextTick(() => { + this.scrollToBottom(); }); - // 滚动到底部 - this.scrollToBottom(); + const typeInterval = setInterval(() => { + if (currentIndex < fullText.length) { + // 优化打字速度:根据文本长度动态调整速度 + let typeSpeed = 15; // 加快速度到15毫秒/字符 + + // 快速处理HTML标签字符,避免标签被拆分导致的闪烁 + if (fullText[currentIndex] === '<' || (currentIndex > 0 && fullText[currentIndex-1] === '<')) { + // 查找完整的HTML标签 + const tagEndIndex = fullText.indexOf('>', currentIndex); + if (tagEndIndex !== -1) { + // 一次性添加整个标签 + this.messages[messageIndex].text = fullText.substring(0, tagEndIndex + 1); + currentIndex = tagEndIndex + 1; + } else { + // 正常逐字添加 + this.messages[messageIndex].text = fullText.substring(0, currentIndex + 1); + currentIndex++; + } + } else { + // 普通字符正常添加 + this.messages[messageIndex].text = fullText.substring(0, currentIndex + 1); + currentIndex++; + } + + // 优化滚动:减少滚动频率,避免抖动 + if (currentIndex % 10 === 0 || currentIndex === fullText.length) { + this.scrollToBottom(); + } + } else { + // 打字完成 + clearInterval(typeInterval); + // 最终滚动到底部 + this.scrollToBottom(); + } + }, 15); // 加快打字速度到15毫秒/字符 }, selectFaq(faq) { // 添加用户选择的常见问题 @@ -122,14 +198,16 @@ export default { time: this.formatTime(new Date()) }); - // 添加对应的回答 + // 添加对应的回答,使用打字效果 setTimeout(() => { + const messageIndex = this.messages.length; this.messages.push({ - text: faq.answer, + text: '', // 初始为空 sender: 'service', time: this.formatTime(new Date()) }); - this.scrollToBottom(); + // 使用打字效果显示回答 + this.typeWriter(messageIndex, faq.answer); }, 1000); }, scrollToBottom() { @@ -138,6 +216,101 @@ export default { container.scrollTop = container.scrollHeight; }); }, + // 滚动到顶部方法 - 保留但不自动调用 + scrollToTop() { + this.$nextTick(() => { + const container = this.$refs.messagesContainer; + container.scrollTop = 0; + }); + }, + // 格式化返回值的方法 - 将Markdown转换为HTML + formatResponse(answer) { + if (!answer) return ''; + + // 简单的Markdown解析实现 + return this.markdownToHtml(answer); + }, + // 简单的Markdown转HTML函数 + markdownToHtml(text) { + if (!text) return ''; + + // 转换标题 + text = text.replace(/^### (.*$)/gm, '

$1

'); + text = text.replace(/^## (.*$)/gm, '

$1

'); + text = text.replace(/^# (.*$)/gm, '

$1

'); + + // 转换分隔线 + text = text.replace(/^---$/gm, '
'); + + // 转换加粗文本 + text = text.replace(/\*\*(.*?)\*\*/g, '$1'); + + // 转换斜体文本 + text = text.replace(/\*(.*?)\*/g, '$1'); + + // 转换引用块 + text = text.replace(/^> (.*$)/gm, '
$1
'); + + // 转换无序列表项 + text = text.replace(/^- (.*$)/gm, ''); + // 合并连续的列表项 + text = text.replace(/<\/li><\/ul>\s*