Files
map-client-vue/STOP_GENERATION_PATCH.md
2025-10-15 15:07:45 +08:00

209 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 停止生成功能 - 补充修复
## 问题描述
在之前的修复后,发现两个新问题:
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
<!-- 发送/停止按钮 -->
<n-button
size="small"
:type="store.state.isSending ? 'error' : 'primary'"
:disabled="!store.state.isSending && !inputText.trim()"
@click="handleButtonClick"
>
{{ store.state.isSending ? '停止' : '发送' }}
</n-button>
```
### 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. ✅ 消息状态正确显示"已停止"而不是"发送中..."