Files
happy-life-star/docs/superpowers/specs/2026-06-29-scriptview-chat-buttons-fix-design.md
T

12 KiB
Raw Blame History

author, created_at, purpose
author created_at purpose
Peanut 2026-06-29 完整修复 ScriptView 对话模式的按钮缺失问题,补齐 spec 要求的版本/改写/续写/历史版本/删除功能

ScriptView 对话模式按钮完整修复设计

背景

bf6f279 feat: ScriptView 改造为对话中心查看/修改模式 重构后,对话模式的按钮出现多处丢失:

  1. 旧版按钮丢失MessageCardisShortMessage=true(短消息)分支没有按钮组,而旧版 assistant 消息后面有「复制/换个方向/不像我/继续生成/播放」按钮组(commit 0db434c 添加)
  2. Spec 功能未实现2026-06-28-script-dialogue-design.md 第 5.4 节要求 AI 剧本消息有「改写 / 续写 / 查看历史版本 / 删除版本」按钮,当前完全没实现
  3. 根版本被过滤displayMessagesm.type !== 'script' || !m.parentMessageId 过滤,把根版本 AI 剧本消息丢掉了
  4. 版本标签缺失AI 消息没有 V1 / V2 版本号标签和「(当前)」标记

设计

1. MessageCard.vue 重构

新增 props

  • messageType: 'script' | 'chat' | 'system',默认 'script'
  • versionLabel: 版本号显示(如 V1 (当前)),script 类型必填,其他类型不传
  • hasChildren: 是否有子版本(布尔,用于控制「查看历史版本」按钮是否显示)
  • canDelete: 是否可删除(布尔,当前生效版本不可删除)
  • canRewrite: 是否可改写(布尔,默认 true
  • canContinue: 是否可续写(布尔,默认 true

渲染规则

messageType 渲染方式
'script' 永远走长文本 story-card 分支(不论 isShortMessage),因为剧本消息总是需要按钮组
'chat''system' 保持原逻辑(短消息气泡 / 长消息 story-card

按钮组(按 messageType 切换):

script 消息按钮组:
  ┌─ 顶部操作(story-head-actions,不变)
  │   · 收起/展开
  │   · 复制
  ├─ 版本标签(新增,仅在 versionLabel 有值时显示)
  │   · V1 (当前)  /  V2  /  ...
  ├─ 功能按钮(新增,script 专属)
  │   · 改写(emit 'rewrite'
  │   · 续写(emit 'continue-script'
  │   · 查看历史版本(emit 'view-versions'hasChildren=true 才显示)
  │   · 删除版本(emit 'delete-version'canDelete=true 才显示)
  └─ 通用操作(保留旧版)
      · TTS 播放(emit 'play-tts'

chat 消息按钮组(isShortMessage=false 时):
  ┌─ 复制
  ├─ 收起/展开
  └─ TTS 播放(emit 'play-tts'

system 消息(isShortMessage=true 默认走气泡):
  └─ 不变

新增 emits

  • rewrite — 改写按钮
  • continue-script — 续写按钮(避免和旧 continue 语义冲突,但保留旧 continue 兼容)
  • view-versions — 查看历史版本
  • delete-version — 删除版本

2. ScriptView.vue 改动

改动 2.1:显示根版本

// 旧
const displayMessages = computed(() => {
  return messages.value.filter(m => m.type !== 'script' || !m.parentMessageId)
})

// 新:保留所有消息,根版本作为 chat 列表首条
const displayMessages = computed(() => messages.value)

改动 2.2:传 messageType 给 MessageCard

<MessageCard
  v-if="message.sender === 'assistant' || message.type === 'script'"
  message-type="script"
  :version-label="formatVersionLabel(message)"
  :has-children="messageHasChildren(message)"
  :can-delete="message.id !== currentVersionMessageId"
  :can-rewrite="true"
  :can-continue="true"
  :content="message.content"
  :collapsed="isMessageCollapsed(message)"
  :content-length="message.content.length"
  :is-short-message="false"
  :tts-icon="..."
  :tts-text="..."
  @toggle-collapse="toggleMessageCollapse(message)"
  @copy="copyMessageContent(message)"
  @rewrite="rewriteMessage(message)"
  @continue-script="continueMessage(message)"
  @view-versions="viewMessageVersions(message)"
  @delete-version="removeVersion(message)"
  @play-tts="playMessageTts(message)"
  @change-direction="changeDirection"
  @not-like-me="notLikeMe"
/>

改动 2.3:版本标签生成函数

const formatVersionLabel = (message) => {
  if (message.type !== 'script') return ''
  const versionNum = message.versionNumber || 1
  const isCurrent = message.id === currentVersionMessageId.value
  return isCurrent ? `V${versionNum} (当前)` : `V${versionNum}`
}

const messageHasChildren = (message) => {
  // 通过 versions 数组长度判断(已加载的)或 metadata 推断
  return versions.value.some(v => v.parentMessageId === message.id)
    || (message.versionNumber && message.versionNumber > 1)
    || (message.childrenCount && message.childrenCount > 0)
}

改动 2.4:查看历史版本

新增 viewMessageVersions(msg) 函数:

  1. 调用 listMessageVersions(msg.id)
  2. uni.showActionSheet 列出所有版本(V1、V2...
  3. 用户选择后:
    • 如果选的是当前版本:uni.showToast 提示「当前已生效」
    • 如果选的是其他版本:调用 switchVersion({ scriptId, messageId: selectedId }) → 更新 currentVersionMessageIdloadMessages() 刷新

4. 历史剧本兼容层(高优先级)

现状

DB 中绝大多数历史剧本的 conversation_id 是前端早期自造的临时 IDconv-<ts>-<rand> 格式),t_conversation 表中没有对应记录

329987360219471872  我的人生剧本     → YES (最新剧本,走新流程创建)
f82818b702d5ad4a...  高考            → NO
a3dadb7e85a6c75b...  我高考了        → NO
49ae0f432253aaff...  我中了100w      → NO
... 其余 6 条 → NO

影响分析

场景 是否受影响 原因
ScriptDetailView 阅读模式 正常 t_epic_script.plotJson,不依赖 conversation
ScriptView viewMode='read' 阅读模式 正常 通过 currentScriptContentcurrentVersionMessageId 指向的 messagefallback 到 plotJson
ScriptView viewMode='chat' 聊天模式 异常 listByConversation 返回空,对话历史丢失
改写 / 续写 / 版本切换 异常 需要有效的 conversation 和 message 才能操作

兼容策略:前端 fallback + 后端自动建表

改动 A:后端自动建 conversation

EpicScriptServiceImpl.getScriptById(或 getListByCurrentUser 返回前)检查:

  • script.conversation_id 指向的 conversation 不存在 → 自动创建一条 t_conversation 记录,写入 user_idscript_idtype='script'status='active',并回填 t_epic_script.conversation_id 为新 ID

改动 B:前端首次进入聊天模式时,基于 plotJson 初始化首条消息

在 ScriptView.vue 的 loadMessages 中:

  • 若 API 返回空列表 && script.plotJson?.fullContent 存在 → 自动调用 /message/create 创建一条 type='script' 的 AI 剧本消息(content 来自 plotJson
  • 后续改写/续写/版本切换基于这条真实消息操作

改动 CScriptView 阅读模式 fallback 强化

当前 currentScriptContent 已在 message 不存在时 fallback 到 plotJson。需确保:

  • viewMode='read' 下,版本标签在没有 versions 数据时隐藏(不显示"V0"之类)
  • "进入对话修改"按钮在没有 conversation 时仍可点击,触发改动 B 的初始化流程

验收标准(历史剧本)

  • 历史剧本在 ScriptDetailView 完整显示标题、summary、content
  • 历史剧本在 ScriptView viewMode='read' 完整显示标题、章节内容(无版本标签时 UI 不异常)
  • 历史剧本点击"进入对话修改" → 自动创建 conversation + 初始化首条 AI 消息 → 进入 chat 模式
  • 历史剧本进入 chat 模式后,能看到完整对话历史(首条为用户心愿 + AI 剧本)
  • 历史剧本的"改写/续写/查看历史版本"功能可正常使用
  • 新创建的剧本所有功能不受影响(回归验证)

5. 实施顺序

顺序 任务 类型
1 后端 getScriptById 自动建 conversation 数据兼容
2 前端 MessageCard 重构 + 按钮组 功能补齐
3 前端 ScriptView displayMessages 去掉根版本过滤 Bug 修复
4 前端 loadMessages 空结果时自动初始化首条消息 数据兼容
5 前端 chat 模式传 messageType/versionLabel 等 props 功能补齐
6 端到端验收(新剧本 + 历史剧本) 验收

6. 影响范围

文件 改动量
server/.../EpicScriptServiceImpl.java 小(getScriptById 检查 + 自动建 conversation
mini-program/src/components/MessageCard.vue 中(+props、+emits、+按钮组条件渲染)
mini-program/src/pages/main/ScriptView.vue 中(displayMessages filter、MessageCard props、formatVersionLabel、viewMessageVersions、loadMessages 初始化)

验收标准(代码层面)

  • chat 模式下,根版本 AI 剧本消息显示为 chat 列表首条
  • AI 剧本消息显示 V1 / V2 版本号 + (当前) 标记
  • AI 剧本消息按钮组包含:收起/展开、复制、TTS 播放、改写、续写、查看历史版本、删除版本
  • 用户消息(type=chat)保持气泡样式,有收起/展开按钮
  • 点击「改写」弹出 modal 输入改写意图,提交后生成新版本
  • 点击「续写」直接生成新版本
  • 点击「查看历史版本」弹 ActionSheet 列出版本,切换后 UI 更新
  • 当前生效版本不显示「删除版本」按钮
  • 历史剧本进入 ScriptView chat 模式 → 自动创建 conversation + 初始化首条 AI 消息
  • 浏览器 Console 无报错
  • 不影响 read 模式(阅读模式保持现状)

H5 端到端验收流程(强制)

后端验证码AuthServiceImpl.java:80 已硬编码 DEFAULT_SMS_CODE = "123456",无需改动。

测试账号:手机号 19928748688,验证码 123456

步骤 1:启动本地 H5

python dev-services.py start mini-program
# 访问 http://localhost:5180

步骤 2:登录

  1. 打开 http://localhost:5180
  2. 输入手机号 19928748688
  3. 必须点击"发送验证码"按钮(触发 /auth/sms-code 接口,后端写入 Redis 并返回固定验证码)
  4. 输入验证码 123456
  5. 提交登录

步骤 3:新剧本完整流程

  1. 首页输入心愿 → 生成第一个剧本
  2. 查看剧本详情 → 标题、summary、content 完整显示
  3. 点击"继续" → 进入 ScriptView chat 模式
  4. 验证
    • 用户心愿气泡显示
    • AI 剧本消息显示,带 V1 (当前) 版本标签
    • AI 消息按钮组包含:收起/展开、复制、TTS 播放、改写续写查看历史版本删除版本(当前版本不显示删除)
  5. 点"改写" → 输入改写意图 → 生成新版本 V2
  6. 点"查看历史版本" → ActionSheet 列出 V1/V2,点击切换
  7. 点"续写" → 生成新内容

步骤 4:历史剧本完整流程

  1. 点"历史"按钮 → 进入剧本库
  2. 点任一历史剧本(非本次新生成的)→ 进入 ScriptDetailView
  3. 验证
    • 标题、summary、content 完整显示(读 plotJson.fullContent
    • 风格、篇幅、字数正确
  4. 点"继续" → 进入 ScriptView
  5. 验证
    • 自动创建 t_conversationDB 中新增记录)
    • 自动初始化首条 AI 消息(基于 plotJson
    • chat 模式看到完整对话历史(心愿 + AI 剧本)
    • AI 消息带版本标签
    • 改写/续写/查看历史版本 可正常操作

步骤 5Console 与 Network 检查

  • DevTools Console 无报错
  • Network 中所有 API 调用返回 2xx(无 5xx、无 401、无 403
  • listByConversation 调用返回 200 且有数据

步骤 6:验收通过标准

  • 新剧本 + 历史剧本所有功能正常
  • 按钮组完整
  • 版本标签正确
  • Console 无报错
  • 历史剧本 conversation 自动创建成功

未通过的处理:任一验收项失败 → 停止 → 定位问题 → 修复 → 重新验收 → 直到全部通过才算完成

风险与回退

  • MessageCard 重构风险:新增 props 都带默认值,不会破坏非对话模式(生成页、结果页)的现有用法
  • 向后兼容:旧 continue emit 保留,新增 continue-script 作为 script 专属,避免语义冲突
  • 回退方案git revert 单个 commit 即可