update at 2025-10-15 15:07:45

This commit is contained in:
douboer
2025-10-15 15:07:45 +08:00
parent eb8fb51283
commit 901d00e4e1
21 changed files with 4030 additions and 57 deletions

208
STOP_GENERATION_PATCH.md Normal file
View File

@@ -0,0 +1,208 @@
# 停止生成功能 - 补充修复
## 问题描述
在之前的修复后,发现两个新问题:
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. ✅ 消息状态正确显示"已停止"而不是"发送中..."