12 KiB
12 KiB
author, created_at, purpose
| author | created_at | purpose |
|---|---|---|
| Peanut | 2026-06-29 | 完整修复 ScriptView 对话模式的按钮缺失问题,补齐 spec 要求的版本/改写/续写/历史版本/删除功能 |
ScriptView 对话模式按钮完整修复设计
背景
bf6f279 feat: ScriptView 改造为对话中心查看/修改模式 重构后,对话模式的按钮出现多处丢失:
- 旧版按钮丢失:
MessageCard在isShortMessage=true(短消息)分支没有按钮组,而旧版 assistant 消息后面有「复制/换个方向/不像我/继续生成/播放」按钮组(commit0db434c添加) - Spec 功能未实现:
2026-06-28-script-dialogue-design.md第 5.4 节要求 AI 剧本消息有「改写 / 续写 / 查看历史版本 / 删除版本」按钮,当前完全没实现 - 根版本被过滤:
displayMessages用m.type !== 'script' || !m.parentMessageId过滤,把根版本 AI 剧本消息丢掉了 - 版本标签缺失: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) 函数:
- 调用
listMessageVersions(msg.id) - 用
uni.showActionSheet列出所有版本(V1、V2...) - 用户选择后:
- 如果选的是当前版本:
uni.showToast提示「当前已生效」 - 如果选的是其他版本:调用
switchVersion({ scriptId, messageId: selectedId })→ 更新currentVersionMessageId→loadMessages()刷新
- 如果选的是当前版本:
4. 历史剧本兼容层(高优先级)
现状
DB 中绝大多数历史剧本的 conversation_id 是前端早期自造的临时 ID(conv-<ts>-<rand> 格式),t_conversation 表中没有对应记录。
329987360219471872 我的人生剧本 → YES (最新剧本,走新流程创建)
f82818b702d5ad4a... 高考 → NO
a3dadb7e85a6c75b... 我高考了 → NO
49ae0f432253aaff... 我中了100w → NO
... 其余 6 条 → NO
影响分析
| 场景 | 是否受影响 | 原因 |
|---|---|---|
| ScriptDetailView 阅读模式 | ✅ 正常 | 读 t_epic_script.plotJson,不依赖 conversation |
ScriptView viewMode='read' 阅读模式 |
✅ 正常 | 通过 currentScriptContent 读 currentVersionMessageId 指向的 message;fallback 到 plotJson |
ScriptView viewMode='chat' 聊天模式 |
❌ 异常 | listByConversation 返回空,对话历史丢失 |
| 改写 / 续写 / 版本切换 | ❌ 异常 | 需要有效的 conversation 和 message 才能操作 |
兼容策略:前端 fallback + 后端自动建表
改动 A:后端自动建 conversation
在 EpicScriptServiceImpl.getScriptById(或 getListByCurrentUser 返回前)检查:
- 若
script.conversation_id指向的 conversation 不存在 → 自动创建一条t_conversation记录,写入user_id、script_id、type='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) - 后续改写/续写/版本切换基于这条真实消息操作
改动 C:ScriptView 阅读模式 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:登录
- 打开
http://localhost:5180 - 输入手机号
19928748688 - 必须点击"发送验证码"按钮(触发
/auth/sms-code接口,后端写入 Redis 并返回固定验证码) - 输入验证码
123456 - 提交登录
步骤 3:新剧本完整流程
- 首页输入心愿 → 生成第一个剧本
- 查看剧本详情 → 标题、summary、content 完整显示
- 点击"继续" → 进入 ScriptView chat 模式
- 验证:
- 用户心愿气泡显示
- AI 剧本消息显示,带
V1 (当前)版本标签 - AI 消息按钮组包含:收起/展开、复制、TTS 播放、改写、续写、查看历史版本、删除版本(当前版本不显示删除)
- 点"改写" → 输入改写意图 → 生成新版本 V2
- 点"查看历史版本" → ActionSheet 列出 V1/V2,点击切换
- 点"续写" → 生成新内容
步骤 4:历史剧本完整流程
- 点"历史"按钮 → 进入剧本库
- 点任一历史剧本(非本次新生成的)→ 进入 ScriptDetailView
- 验证:
- 标题、summary、content 完整显示(读
plotJson.fullContent) - 风格、篇幅、字数正确
- 标题、summary、content 完整显示(读
- 点"继续" → 进入 ScriptView
- 验证:
- 自动创建
t_conversation(DB 中新增记录) - 自动初始化首条 AI 消息(基于 plotJson)
- chat 模式看到完整对话历史(心愿 + AI 剧本)
- AI 消息带版本标签
- 改写/续写/查看历史版本 可正常操作
- 自动创建
步骤 5:Console 与 Network 检查
- DevTools Console 无报错
- Network 中所有 API 调用返回 2xx(无 5xx、无 401、无 403)
listByConversation调用返回 200 且有数据
步骤 6:验收通过标准
- ✅ 新剧本 + 历史剧本所有功能正常
- ✅ 按钮组完整
- ✅ 版本标签正确
- ✅ Console 无报错
- ✅ 历史剧本 conversation 自动创建成功
未通过的处理:任一验收项失败 → 停止 → 定位问题 → 修复 → 重新验收 → 直到全部通过才算完成
风险与回退
- MessageCard 重构风险:新增 props 都带默认值,不会破坏非对话模式(生成页、结果页)的现有用法
- 向后兼容:旧
continueemit 保留,新增continue-script作为 script 专属,避免语义冲突 - 回退方案:git revert 单个 commit 即可