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]" >
@@ -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, '
说明:此为2025-2026学年秋季学期的固定选课窗口期+ +