From 87d27225dcb5016d5ec641a7ffdd8edd70ccb24d Mon Sep 17 00:00:00 2001 From: Wangxin Date: Tue, 11 Nov 2025 23:20:41 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E3=80=90web=E3=80=91=20ai=E5=AF=B9?= =?UTF-8?q?=E8=AF=9D=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/QandA/Aiassistant/index.vue | 192 ++++++++++++++++-- 1 file changed, 175 insertions(+), 17 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 a5ec32da..2ba1a4d2 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 @@ -30,11 +30,13 @@ placeholder="请输入您的问题..." @keyup.enter.native="sendMessage" > - 发送 + v-if="!isTyping" type="primary" icon="el-icon-position" circle> +
+
+
@@ -57,24 +59,32 @@ export default { data() { return { newMessage: '', - messages: [ - { - text: '您好!我是AI智能助手,请问有什么可以帮您?', - sender: 'service', - time: this.formatTime(new Date()) - } - ], + messages: [], faqs: [ { question: '如何查询订单状态?', answer: '您可以在"我的订单"页面查看订单的当前状态和物流信息。' }, { question: '如何办理退货?', answer: '请在订单详情页点击"申请退货",填写相关信息后提交申请。' }, { question: '支付方式有哪些?', answer: '我们支持支付宝、微信支付、银联等多种支付方式。' }, { question: '会员有哪些优惠?', answer: '会员可享受购物折扣、专属优惠券、生日特权等多重优惠。' }, { question: '商品何时发货?', answer: '一般情况下,我们会在订单支付成功后24小时内安排发货。' } - ] + ], + STORAGE_KEY: { + permanent: 'ai_assistant_messages', + temporary: 'ai_assistant_messages_temp' + }, + storageType: 'temporary', // 默认使用临时存储 + typewriterInterval: null, // 存储打字效果的定时器引用 + isTyping: false, // 标记是否正在打字 + currentMessageIndex: -1 // 当前正在打字的消息索引 }; },mounted() { + // 从localStorage加载历史消息 + this.loadMessages(); // 初始化时滚动到底部,符合大模型聊天体验 this.scrollToBottom(); + // 监听页面卸载事件,可以在这里清除数据 + window.addEventListener('beforeunload', this.handleBeforeUnload); + // 初始化时使用默认存储类型加载消息 + this.loadMessages(this.storageType); }, methods: { sendMessage() { @@ -149,12 +159,22 @@ export default { typeWriter(messageIndex, fullText) { let currentIndex = 0; + // 设置打字状态 + this.isTyping = true; + this.currentMessageIndex = messageIndex; + // 优化滚动:先滚动到底部 this.$nextTick(() => { this.scrollToBottom(); }); - const typeInterval = setInterval(() => { + // 清除之前可能存在的定时器 + if (this.typewriterInterval) { + clearInterval(this.typewriterInterval); + } + + // 存储定时器引用 + this.typewriterInterval = setInterval(() => { if (currentIndex < fullText.length) { // 优化打字速度:根据文本长度动态调整速度 let typeSpeed = 15; // 加快速度到15毫秒/字符 @@ -184,12 +204,34 @@ export default { } } else { // 打字完成 - clearInterval(typeInterval); - // 最终滚动到底部 - this.scrollToBottom(); + this.stopTyping(); } }, 15); // 加快打字速度到15毫秒/字符 }, + // 停止打字效果 + stopTyping() { + if (this.typewriterInterval) { + clearInterval(this.typewriterInterval); + this.typewriterInterval = null; + } + this.isTyping = false; + this.currentMessageIndex = -1; + // 最终滚动到底部 + this.scrollToBottom(); + }, + // 立即显示完整答案并停止打字效果 + showFullAnswer() { + if (this.isTyping && this.currentMessageIndex >= 0 && this.currentMessageIndex < this.messages.length) { + // 获取当前正在处理的service消息 + const currentMessage = this.messages[this.currentMessageIndex]; + if (currentMessage && currentMessage.sender === 'service') { + // 停止打字效果 + this.stopTyping(); + // 这里我们无法直接获取完整文本,所以只保留已有的部分 + // 在实际应用中,你可能需要将完整文本存储在某个地方以便快速显示 + } + } + }, selectFaq(faq) { // 添加用户选择的常见问题 this.messages.push({ @@ -315,12 +357,95 @@ export default { const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${hours}:${minutes}`; + }, + // 处理页面卸载前的事件 + handleBeforeUnload() { + // 只在临时存储时清除记录 + if (this.storageType === 'temporary') { + this.clearChatHistory('temporary'); + } + }, + // 清除聊天记录 + clearChatHistory() { + // 保留第一条欢迎消息 + this.messages = [{...this.messages[0]}]; + // 清空输入框 + this.newMessage = ''; + // 滚动到底部 + this.scrollToBottom(); + }, + // 从存储加载消息 + loadMessages(type = this.storageType) { + try { + const storage = type === 'permanent' ? localStorage : sessionStorage; + const savedMessages = storage.getItem(this.STORAGE_KEY[type]); + if (savedMessages) { + this.messages = JSON.parse(savedMessages); + } else { + // 如果没有保存的消息,显示欢迎消息 + this.messages = [{ + text: '您好!我是AI智能助手,请问有什么可以帮您?', + sender: 'service', + time: this.formatTime(new Date()) + }]; + } + } catch (error) { + console.error('加载消息失败:', error); + // 加载失败时显示默认消息 + this.messages = [{ + text: '您好!我是AI智能助手,请问有什么可以帮您?', + sender: 'service', + time: this.formatTime(new Date()) + }]; + } + }, + // 保存消息到存储 + saveMessages(type = this.storageType) { + try { + const storage = type === 'permanent' ? localStorage : sessionStorage; + storage.setItem(this.STORAGE_KEY[type], JSON.stringify(this.messages)); + } catch (error) { + console.error('保存消息失败:', error); + } + }, + // 清除对话记录 + clearChatHistory(type = this.storageType) { + try { + const storage = type === 'permanent' ? localStorage : sessionStorage; + storage.removeItem(this.STORAGE_KEY[type]); + this.messages = [{ + text: '您好!我是AI智能助手,请问有什么可以帮您?', + sender: 'service', + time: this.formatTime(new Date()) + }]; + this.scrollToBottom(); + } catch (error) { + console.error('清除消息失败:', error); + } + }, + // 设置存储类型 + setStorageType(type) { + if (['permanent', 'temporary'].includes(type)) { + this.storageType = type; + // 切换存储类型时重新加载消息 + this.loadMessages(type); + return true; + } + return false; + }, + // 处理页面卸载前的事件 + handleBeforeUnload() { + // 这里可以根据需求决定是否在关闭浏览器时清除数据 + // 如果需要清除,可以取消下面这行代码的注释 + // this.clearChatHistory(); } }, watch: { messages: { handler() { - // 当消息数组发生变化时,滚动到底部,确保最新消息可见 + // 当消息数组发生变化时,保存到localStorage + this.saveMessages(); + // 滚动到底部,确保最新消息可见 this.scrollToBottom(); }, deep: true @@ -347,6 +472,13 @@ export default { border-top-left-radius: 4px; border-top-right-radius: 4px; } + .service-header h2 { + margin: 0 0 5px 0; + } + .service-header p { + margin: 0; + opacity: 0.9; + } .service-container { display: flex; min-height: 400px; @@ -403,9 +535,35 @@ export default { padding: 15px; border-top: 1px solid #e6e6e6; display: flex; + gap: 10px; } .chat-input .el-input { margin-right: 10px; + flex: 1; + } + .chat-input .el-button { + white-space: nowrap; + } + + .stop-btn { + align-items: center; + border: 2px solid rgba(73, 113, 245, .5); + border-radius: 20px; + display: flex; + height: 36px; + justify-content: center; + width: 36px; + cursor: pointer; + transition: all .2s; + } + + .stop-btn .el-icon-stop { + background: linear-gradient(0deg, #4971f5, #4971f5), #fff; + border-radius: 4px; + flex-shrink: 0; + height: 13px; + width: 13px; + } .faq-item { padding: 10px; From e84604582e77e7d1cd3e0ab57898d7b195ec812a Mon Sep 17 00:00:00 2001 From: Wangxin Date: Wed, 12 Nov 2025 21:02:03 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E3=80=90web=E3=80=91=20=E5=9C=A8=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E5=B9=B4=E9=99=90=E6=95=B0=E6=8D=AE=E6=97=B6=E5=81=9A?= =?UTF-8?q?=E4=B8=80=E4=B8=8B=E6=A0=A1=E5=A6=82=E6=9E=9C=E4=B8=8D=E6=98=AF?= =?UTF-8?q?=E6=95=B4=E6=95=B0=E5=88=99=E6=8F=90=E7=A4=BA=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E6=95=B4=E6=95=B0=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alops-ui/src/views/device/files/index.vue | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/ALOps_sys_fe/alops-ui/src/views/device/files/index.vue b/ALOps_sys_fe/alops-ui/src/views/device/files/index.vue index 73ab094b..2db37b7d 100644 --- a/ALOps_sys_fe/alops-ui/src/views/device/files/index.vue +++ b/ALOps_sys_fe/alops-ui/src/views/device/files/index.vue @@ -131,7 +131,8 @@ reserve-keyword placeholder="请输入" :remote-method="remoteMethod1" - :loading="loading1"> + :loading="loading1" + style="width: 100%;"> + :loading="loading2" + style="width: 100%;"> + :loading="loading3" + style="width: 100%;"> + placeholder="选择日期" + style="width: 100%;"> - + @@ -225,12 +229,16 @@ - + + + - + + + From dd896139a083a4a85775514f91c01df48c156577 Mon Sep 17 00:00:00 2001 From: Wangxin Date: Wed, 12 Nov 2025 21:07:02 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E3=80=90web=E3=80=91=20form=20=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ALOps_sys_fe/alops-ui/src/views/device/files/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ALOps_sys_fe/alops-ui/src/views/device/files/index.vue b/ALOps_sys_fe/alops-ui/src/views/device/files/index.vue index 2db37b7d..024d61fe 100644 --- a/ALOps_sys_fe/alops-ui/src/views/device/files/index.vue +++ b/ALOps_sys_fe/alops-ui/src/views/device/files/index.vue @@ -108,7 +108,7 @@ - + From b80d47b4d5b2239f4931ad88a6533c9271b8faa7 Mon Sep 17 00:00:00 2001 From: Wangxin Date: Wed, 12 Nov 2025 21:14:15 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E3=80=90web=E3=80=91=20=E9=80=80=E5=87=BA?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=80=BB=E8=BE=91=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ALOps_sys_fe/alops-ui/src/utils/request.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ALOps_sys_fe/alops-ui/src/utils/request.js b/ALOps_sys_fe/alops-ui/src/utils/request.js index 1cb32e6e..4728e8c1 100644 --- a/ALOps_sys_fe/alops-ui/src/utils/request.js +++ b/ALOps_sys_fe/alops-ui/src/utils/request.js @@ -87,7 +87,8 @@ service.interceptors.response.use(res => { isRelogin.show = true MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { isRelogin.show = false - store.dispatch('LogOut').then(() => { + // 由于user模块使用了namespaced:true,需要添加命名空间调用登出方法 + store.dispatch('user/LogOut').then(() => { // 使用Vue Router进行跳转,确保正确导航到登录页面 import('@/router').then(router => { router.default.push('/login')