Files
happy-life-star/docs/superpowers/specs/2026-07-19-chat-view-loading-fix-design.md
T

5.0 KiB
Raw Blame History

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 - 立即执行!
}

BugstartNovelStream 是异步流式调用(fire-and-forget,没有 await),finally 在流启动后立即generating 设回 false。结果:

  1. 用户点发送 → generating = true → 切换到 chat 视图
  2. 几乎同时 → finally 执行 → generating = false → chat-loading 立即隐藏
  3. 在第一个 SSE 事件(status)到达前,页面只有用户消息气泡,下方空白
  4. 用户看到"发送 → 空白等待 → 突然跳出内容",无法判断系统是否在工作

修复方案

用新的 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 隐藏

为什么这样设计

  1. generationPhase 而非 generatinggenerationPhaserunGeneration 开始时设为 'generating',在 novel_done 时设为 'done',完整覆盖生成流程生命周期。generating 因为 try-finally 立即重置,不可靠。

  2. 用最后一条消息的 role 判断assistant 消息(card/outline/novel)到达后,用户看到的是交互内容而非空白,loading 应自动隐藏。通过判断最后一条消息的 role,精确实现"等待首个响应"的语义。

  3. 支持多轮交互:用户回答澄清问题、提交大纲修改后,最后一条又变回 user,loading 自动重新显示。无需额外的 flag 管理。

改动文件

  • mini-program/src/pages/main/ScriptView.vue
    • 新增 computed showChatLoading(在 generationCopy 附近,约 line 930-950
    • 替换 line 177 的 v-if 条件

不影响范围

  • chat-loading 组件的样式、动画内容保持不变
  • generating 变量的其他用途(按钮禁用、重试逻辑等)不变
  • generationStatusgenerationPhasegenerationCopy 等 computed 不变
  • home 视图、generation 视图、chat-input-bar 不受影响
  • sendChatsendRewritesendChatAsContinue 等其他流程不受影响(它们本身有 await,generating 状态正确)

验收标准

  • H5http://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