# 停止生成功能 - 补充修复 ## 问题描述 在之前的修复后,发现两个新问题: 1. **按钮文字问题**:"确认"应该改为"发送" 2. **停止后状态显示错误**:点击停止后,消息仍然显示"发送中..."而不是"已停止" ## 原因分析 ### 问题 1:按钮文字 这是 UI 文案问题,直接修改即可。 ### 问题 2:状态显示错误 **根本原因**:在 `chatStore.ts` 的 catch 块中,当捕获到 `AbortError` 时,虽然不抛出错误,但也没有更新 UI 的消息列表,导致消息状态仍然是 `sending`。 **代码分析**: ```typescript catch (error: any) { // 如果是用户主动取消,不显示错误 if (error.name !== 'AbortError') { throw error } // ❌ 问题:这里什么都不做,消息状态没有更新 } finally { state.isSending = false // 只重置了发送状态 state.abortController = null } ``` 虽然 `chatService` 中已经将消息状态设置为 `paused`,但 UI 层面的 `state.messages` 没有重新加载,所以还显示旧的 `sending` 状态。 ## 修复方案 ### 1. 修改按钮文字 **文件**:`web/src/components/Chat/ChatLayout.vue` ```vue {{ store.state.isSending ? '停止' : '发送' }} ``` ### 2. 在 AbortError 时更新消息状态 **文件**:`web/src/stores/chatStore.ts` ```typescript catch (error: any) { // 如果是用户主动取消,也要更新消息列表(显示 paused 状态) if (error.name === 'AbortError') { console.log('⏸️ [sendMessageStream] 用户中止,更新消息状态') if (state.currentTopicId === currentTopicId) { state.messages = [...chatService.getMessages(currentTopicId)] } loadTopics() } else { throw error } } finally { state.isSending = false state.abortController = null } ``` ## 工作流程(更新后) ``` 用户点击停止 ↓ handleStopGeneration() → store.stopGeneration() ↓ abortController.abort() → 触发 AbortError ↓ chatService 捕获 AbortError ↓ 设置 assistantMessage.status = 'paused' ↓ 保存到 conversation ↓ 抛出 AbortError 到 chatStore ↓ chatStore catch 块捕获 AbortError ↓ ✅ 重新加载消息列表:state.messages = [...chatService.getMessages()] ↓ ✅ Vue 响应式系统检测到 messages 变化 ↓ ✅ UI 重新渲染,显示 "已停止" 标签 ↓ finally 块:state.isSending = false ↓ ✅ 按钮文字变回 "发送" ``` ## 关键改进 ### Before(问题版本) ```typescript catch (error: any) { if (error.name !== 'AbortError') { throw error } // ❌ AbortError 被静默忽略,UI 不更新 } ``` ### After(修复版本) ```typescript catch (error: any) { if (error.name === 'AbortError') { // ✅ 更新消息列表,触发 UI 重新渲染 if (state.currentTopicId === currentTopicId) { state.messages = [...chatService.getMessages(currentTopicId)] } loadTopics() } else { throw error } } ``` ## 测试验证 ### 测试步骤 1. 发送消息 2. 在 AI 回复时点击"停止"按钮 3. **验证点**: - ✅ 消息上方的标签从"发送中..."变为"已停止"(黄色) - ✅ 不再显示 loading 动画(三个跳动的点) - ✅ 按钮从"停止"变回"发送" - ✅ 显示消息操作按钮(复制、重新生成、删除) ### 预期 UI 变化 **发送中:** ``` AI 助手 14:53 [发送中...] [... ... ...] <- loading 动画 正在生成的文字... ``` **停止后(修复前 ❌):** ``` AI 助手 14:53 [发送中...] <- ❌ 错误:仍显示发送中 [... ... ...] <- ❌ loading 动画还在 已生成的文字... ``` **停止后(修复后 ✅):** ``` AI 助手 14:53 [已停止] <- ✅ 正确:显示已停止 已生成的文字... [复制] [重新生成] [删除] <- ✅ 显示操作按钮 ``` ## 控制台日志 点击停止后应该看到: ``` 🛑 [handleStopGeneration] 用户请求停止生成 🛑 [makeChatRequestStream] 检测到中止信号,停止读取流 ⚠️ [makeChatRequestStream] 请求被中止: 用户中止操作 ⏸️ [sendMessageStream] 用户主动停止生成,保留已生成内容 ⏸️ [sendMessageStream] 用户中止,更新消息状态 <- ✅ 新增 ``` ## 修改文件清单 1. ✅ `web/src/components/Chat/ChatLayout.vue` - 按钮文字 2. ✅ `web/src/stores/chatStore.ts` - catch 块中更新消息列表 ## 注意事项 ### 为什么要重新加载消息列表? 1. **消息状态更新在 Service 层**:`chatService.sendMessageStream()` 中修改了 message.status 2. **Store 层持有的是引用**:虽然 service 中修改了对象,但 Vue 的响应式系统可能没有检测到 3. **强制触发响应式更新**:通过 `[...chatService.getMessages()]` 创建新数组,确保 Vue 检测到变化 ### 为什么在 catch 块中而不是 finally? - **时机问题**:需要在消息状态已被设置为 `paused` 之后再更新 UI - **条件判断**:只有 AbortError 才需要这个更新,其他错误不需要 - **finally 块作用**:只负责清理状态(isSending、abortController),不涉及业务逻辑 ## 相关文档 - `STOP_GENERATION_SUMMARY.md` - 初始修复总结 - `STOP_GENERATION_FIX.md` - 详细技术文档 - `STOP_GENERATION_TEST.md` - 测试指南 --- **补充修复完成!** 🎉 现在点击停止后: 1. ✅ 按钮显示"发送"而不是"确认" 2. ✅ 消息状态正确显示"已停止"而不是"发送中..."