5.0 KiB
5.0 KiB
author, created_at, purpose
| author | created_at | purpose |
|---|---|---|
| AI Assistant | 2026-07-19 | 修复心愿实现 chat 视图在等待接口响应期间没有 loading 标识、呈现空白等待状态的问题 |
心愿实现 chat 视图 Loading 标识修复设计
概述
心愿实现页面(ScriptView.vue 的 chat 视图)在用户发送消息后、等待后端 SSE 接口响应的阶段,页面处于空白状态,没有任何 loading 标识,无法判断是否卡住。需要在等待期间展示动态 loading,提升等待体验。
问题根因
ScriptView.vue 中 chat-loading 组件的显示条件(line 177):
<view v-if="generating && generationStatus !== 'failed'" class="chat-loading">
runGeneration 函数(line 1618-1672)中:
generating.value = true // Line 1630
// ...
try {
currentStreamTask.value = startNovelStream({...}) // Line 1653 - 异步 fire-and-forget
} finally {
generating.value = false // Line 1671 - 立即执行!
}
Bug:startNovelStream 是异步流式调用(fire-and-forget,没有 await),finally 在流启动后立即把 generating 设回 false。结果:
- 用户点发送 →
generating = true→ 切换到 chat 视图 - 几乎同时 →
finally执行 →generating = false→ chat-loading 立即隐藏 - 在第一个 SSE 事件(
status)到达前,页面只有用户消息气泡,下方空白 - 用户看到"发送 → 空白等待 → 突然跳出内容",无法判断系统是否在工作
修复方案
用新的 computed showChatLoading 替换原有显示条件,语义精确匹配"等待首个 assistant 响应":
const showChatLoading = computed(() => {
if (generationPhase.value !== 'generating') return false
if (generationStatus.value === 'failed') return false
const lastMsg = resultMessages.value[resultMessages.value.length - 1]
// 最后一条消息是 user(刚发送的请求),说明还没收到 assistant 响应
return lastMsg && lastMsg.role === 'user'
})
<view v-if="showChatLoading" class="chat-loading">
<view class="loading-orbit" :class="{ streaming: generationStatus === 'streaming' }">
<view class="orbit-ring outer"></view>
<view class="orbit-ring inner"></view>
<view class="orbit-core"></view>
</view>
<text class="loading-copy">{{ generationCopy }}</text>
</view>
场景覆盖表
| 场景 | generationPhase | 最后一条消息 | loading 显示 |
|---|---|---|---|
| 用户刚发送 | 'generating' | user | ✅ 显示 |
| SSE status 事件(processing 阶段) | 'generating' | user | ✅ 显示 |
clarification_card 到达 |
'generating' | assistant (card) | ❌ 隐藏 |
| 用户回答澄清问题 | 'generating' | user | ✅ 显示 |
outline_created 到达 |
'generating' | assistant (outline) | ❌ 隐藏 |
| 用户提交大纲修改意见 | 'generating' | user | ✅ 显示 |
novel_start 到达 |
'generating' | assistant (novel) | ❌ 隐藏 |
novel_done 到达 |
'done' | assistant | ❌ 隐藏 |
为什么这样设计
-
用
generationPhase而非generating:generationPhase在runGeneration开始时设为 'generating',在novel_done时设为 'done',完整覆盖生成流程生命周期。generating因为 try-finally 立即重置,不可靠。 -
用最后一条消息的 role 判断:assistant 消息(card/outline/novel)到达后,用户看到的是交互内容而非空白,loading 应自动隐藏。通过判断最后一条消息的 role,精确实现"等待首个响应"的语义。
-
支持多轮交互:用户回答澄清问题、提交大纲修改后,最后一条又变回 user,loading 自动重新显示。无需额外的 flag 管理。
改动文件
mini-program/src/pages/main/ScriptView.vue- 新增 computed
showChatLoading(在generationCopy附近,约 line 930-950) - 替换 line 177 的
v-if条件
- 新增 computed
不影响范围
chat-loading组件的样式、动画内容保持不变generating变量的其他用途(按钮禁用、重试逻辑等)不变generationStatus、generationPhase、generationCopy等 computed 不变home视图、generation视图、chat-input-bar不受影响sendChat、sendRewrite、sendChatAsContinue等其他流程不受影响(它们本身有 await,generating 状态正确)
验收标准
- H5(
http://localhost:5180):点击发送后,chat 视图立即显示 loading 动画(loading-orbit + generationCopy 文本) - Loading 持续到
clarification_card/outline_created/novel_start任一事件到达前 - 澄清卡片 / 大纲 / 小说消息出现后,loading 自动隐藏
- 用户回答澄清、提交大纲修改后,loading 重新显示直到下一个 assistant 响应到达
- 失败状态(
generationStatus === 'failed')不显示 loading,显示 resume 按钮 - 浏览器 Console 无新增错误
- mp-weixin 编译通过(
npm run build:mp-weixin无 error) - 编译产物中
.chat-loading的 v-if 使用showChatLoading