Files
happy-life-star/docs/superpowers/specs/2026-07-28-novel-message-actions-restore-design.md

377 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
author: AI Assistant
created_at: 2026-07-28
purpose: 恢复小程序小说生成页和详情页丢失的"复制、播放、折叠/展开"功能
---
# 小说消息操作按钮恢复设计
## 问题背景
小程序的小说生成页(ScriptView.vue)和详情页(ScriptDetailView.vue)在之前的重构过程中丢失了"复制、播放、折叠/展开"等操作按钮。用户要求完整恢复这些功能,且必须有真实实现,不能有任何 mock 或占位。
## 根因分析
### 重构历史追溯
通过 git 历史分析,发现功能丢失的关键节点:
1. **commit 9b3006d**(2026-06-27):添加消息卡片功能方法
- 新增 `copyMessageContent(message)`:复制消息内容
- 新增 `playMessageTts(message)`:播放 TTS 音频
- 新增 `toggleMessageCollapse(message)`:折叠/展开消息
2. **commit 0db434c**2026-06-27):在 result-chat-list 中添加功能按钮组
- 在每条 assistant 消息下方添加 5 个按钮:复制、换个方向、不像我、继续生成、播放
- 使用 `.message-actions` 样式类
3. **commit c2ed051**ScriptView 复用 MessageCard 组件
- 将消息卡片提取为独立的 `MessageCard.vue` 组件
- MessageCard 包含完整功能:折叠、复制、换个方向、不像我、续写、改写、查看历史、删除、播放
4. **commit 2c778fa**2026-07-01):**ScriptView 删除 read 模式 + 兼容旧模式,统一对话流入口**
- 删除了 102 行代码,包括所有使用 MessageCard 的调用
- 模板中改为裸的 `<view class="chat-bubble">` 渲染消息
- **MessageCard 导入保留但不再使用**(行 241)
- **JS 函数保留**`copyMessageContent`(行 422-438)、`playMessageTts`(行 440-447)、`toggleMessageCollapse`(行 409-420)、`getMessageDisplayContent`(行 402-407)、`isMessageCollapsed`(行 399
- **`collapsedMessageIds` ref 保留**(行 287
- **按钮组 UI 丢失**
5. **ScriptDetailView.vue**:从未使用过 MessageCard,也从未有过这些功能
### 当前状态
**ScriptView.vue(生成页)**
- ✅ 所有相关函数已存在(copyMessageContent、playMessageTts、toggleMessageCollapse 等)
-`collapsedMessageIds` ref 已存在
- ❌ 模板中按钮组 UI 已丢失
- ❌ 相关样式可能已删除或无用
**ScriptDetailView.vue(详情页)**
- ❌ 完全没有相关函数
- ❌ 完全没有 `collapsedMessageIds` ref
- ❌ 完全没有按钮组 UI
- ✅ 底部有全局播放按钮(`ttsPlayer.playSource`
## 设计方案
### 核心设计决策
| 维度 | 决策 |
|------|------|
| **功能范围** | 完整恢复所有按钮功能,但布局采用极简设计 |
| **页面范围** | 生成页(ScriptView+ 详情页(ScriptDetailView |
| **应用对象** | 仅 `kind === 'novel'` 的小说正文消息 |
| **按钮布局** | 每条 novel 消息下方:折叠/展开 + 复制 + 播放(3 个按钮)|
| **修订类按钮** | 放在底部操作区(已有),不在每条消息下重复 |
### 设计原则
1. **极简设计**:每条 novel 消息下方只放 3 个核心功能按钮(折叠/展开、复制、播放)
2. **功能就近**:内容操作类按钮(复制、播放、折叠)贴近消息,用户操作直观
3. **修订集中**:方向修订类按钮(换个方向、不像我、继续生成)集中在底部操作区,避免重复
4. **样式统一**:两个页面使用相同的按钮组样式,保持视觉一致性
5. **代码复用**ScriptView 已有函数直接复用,ScriptDetailView 新增相同逻辑
### 功能实现细节
#### 1. 折叠/展开
**状态管理**
```javascript
const collapsedMessageIds = ref({})
```
**核心函数**
```javascript
// 判断消息是否折叠
const isMessageCollapsed = (message) => {
return Boolean(collapsedMessageIds.value[message.id])
}
// 获取消息显示内容(折叠时截断)
const getMessageDisplayContent = (message) => {
const content = String(message?.content || '')
if (!isMessageCollapsed(message)) return content
if (content.length <= 200) return content
return `${content.slice(0, 200)}...`
}
// 切换折叠状态
const toggleMessageCollapse = (message) => {
collapsedMessageIds.value = {
...collapsedMessageIds.value,
[message.id]: !isMessageCollapsed(message)
}
analytics.track('script_message_collapse_toggle', {
message_id: message?.id || '',
collapsed: collapsedMessageIds.value[message.id]
}, { eventType: 'script', pagePath })
}
```
**UI 交互**
- 折叠时:显示前 200 字符 + "展开全文"按钮
- 展开时:显示完整内容 + "收起全文"按钮
- 折叠/展开按钮放在消息内容下方,按钮组上方
#### 2. 复制
**核心函数**
```javascript
const copyMessageContent = (message) => {
const content = String(message?.content || '')
if (!content.trim()) {
uni.showToast({ title: '暂无可复制内容', icon: 'none' })
return
}
uni.setClipboardData({
data: content,
success: () => {
uni.showToast({ title: '已复制', icon: 'success' })
}
})
analytics.track('script_message_copy_click', {
message_id: message?.id || '',
content_length: content.length
}, { eventType: 'script', pagePath })
}
```
**UI 交互**
- 点击"复制"按钮,调用 `uni.setClipboardData`
- 成功复制后显示 Toast 提示"已复制"
- 内容为空时显示"暂无可复制内容"
#### 3. 播放
**核心函数**
```javascript
const playMessageTts = (message) => {
const scriptId = currentResult.value?.id || '' // ScriptView
// 或 script.value?.id || '' // ScriptDetailView
ttsPlayer.playSource(scriptId)
analytics.track('script_message_tts_click', {
message_id: message?.id || '',
script_id: scriptId
}, { eventType: 'script', pagePath })
}
```
**UI 交互**
- 播放图标动态显示:`▶`(未播放)/ `Ⅱ`(播放中)
- 播放文本动态显示:`播放`(未播放)/ `暂停`(播放中)
- 点击按钮调用 `ttsPlayer.playSource(scriptId)` 播放整个小说的 TTS 音频
### UI 设计
**每条 novel 消息下方按钮组**
```
┌─────────────────────────────────────┐
│ [小说正文内容] │
│ (折叠时显示前 200 字...) │
│ │
│ [▼ 展开全文] ← 折叠/展开按钮 │
│ │
│ ┌──────────┬──────────┬──────────┐ │
│ │ 📋 复制 │ ▶ 播放 │ ⚙ 更多 │ │ ← 操作按钮组
│ └──────────┴──────────┴──────────┘ │
└─────────────────────────────────────┘
```
**实际实现(3 个按钮)**
```
┌─────────────────────────────────────┐
│ [小说正文内容] │
│ │
│ [▼ 展开全文] │
│ │
│ ┌──────────┬──────────┬──────────┐ │
│ │ 复制 │ ▶ 播放 │ 收起 │ │
│ └──────────┴──────────┴──────────┘ │
└─────────────────────────────────────┘
```
**按钮样式**
- 参考 MessageCard 的 `result-actions` 样式(grid 布局)
- 3 个按钮横向排列,等宽分布
- 播放按钮为 `primary` 样式(紫色渐变,突出显示)
- 折叠/展开按钮为独立行,放在按钮组上方
- 按钮高度:72rpx
- 按钮圆角:28rpx
- 按钮背景:`rgba(88, 28, 135, 0.18)`
- 按钮边框:`1rpx solid rgba(192, 132, 252, 0.35)`
### 页面差异化
| 页面 | 现有修订类按钮 | 需新增功能 |
|------|--------------|-----------|
| **生成页(ScriptView** | 底部输入区已有"换个方向"、"不像我"等按钮 | 每条 novel 消息下加 3 个按钮(复用已有函数) |
| **详情页(ScriptDetailView** | 底部已有"继续生成"按钮 | 每条 novel 消息下加 3 个按钮 + 新增所有函数 |
### 应用对象
**只在 `kind === 'novel'` 的 assistant 消息上显示按钮组**
-`kind === 'novel'`:小说正文,需要复制、播放、折叠/展开
-`kind === 'card'`:澄清卡片,有特殊交互(提交按钮),不需要这些按钮
-`kind === 'outline'`:大纲,结构化数据,不需要这些按钮
-`kind === 'text'`:用户消息,不需要这些按钮
## 数据流与代码复用
### ScriptView(生成页)
**已有资源**
-`collapsedMessageIds` ref(行 287
-`isMessageCollapsed(message)` 函数(行 399
-`getMessageDisplayContent(message)` 函数(行 402-407
-`toggleMessageCollapse(message)` 函数(行 409-420
-`copyMessageContent(message)` 函数(行 422-438
-`playMessageTts(message)` 函数(行 440-447
-`ttsPlayer` composable(行 320
-`ttsActionText` computed(行 986-990
-`ttsActionIcon` computed(行 992-994
**需要补充**
- ❌ 模板中按钮组 UI(在 `kind === 'novel'` 的消息下方添加)
- ❌ 按钮组样式(`.message-actions``.action-btn` 等)
### ScriptDetailView(详情页)
**需要新增**
-`collapsedMessageIds` ref
-`isMessageCollapsed(message)` 函数
-`getMessageDisplayContent(message)` 函数
-`toggleMessageCollapse(message)` 函数
-`copyMessageContent(message)` 函数
-`playMessageTts(message)` 函数
- ❌ 模板中按钮组 UI
- ❌ 按钮组样式
**已有资源**
-`ttsPlayer` composable(行 110
-`detailTtsActionText` computed(行 118-122
-`detailTtsIcon` computed(行 124-126
## 实现步骤
### 步骤 1ScriptView 恢复按钮组 UI
1. 打开 `mini-program/src/pages/main/ScriptView.vue`
2. 定位到 `kind === 'novel'` 的消息渲染部分(约行 180-187)
3.`<view v-else-if="msg.kind === 'novel'" class="chat-bubble system novel-bubble">` 内部:
-`<Markdown :content="msg.content" />` 改为 `<Markdown :content="getMessageDisplayContent(msg)" />`
- 在 Markdown 下方添加折叠/展开按钮
- 在折叠/展开按钮下方添加操作按钮组(复制、播放)
4. 添加按钮组样式(`.message-actions``.action-btn` 等)
### 步骤 2ScriptDetailView 新增函数
1. 打开 `mini-program/src/pages/main/ScriptDetailView.vue`
2.`<script setup>` 中添加:
- `const collapsedMessageIds = ref({})`
- `isMessageCollapsed(message)` 函数
- `getMessageDisplayContent(message)` 函数
- `toggleMessageCollapse(message)` 函数
- `copyMessageContent(message)` 函数
- `playMessageTts(message)` 函数
### 步骤 3ScriptDetailView 添加按钮组 UI
1. 定位到 `kind === 'novel'` 的消息渲染部分(约行 74-76
2.`<view v-else-if="msg.kind === 'novel'" class="chat-bubble system novel-bubble">` 内部:
-`<Markdown :content="msg.content" />` 改为 `<Markdown :content="getMessageDisplayContent(msg)" />`
- 在 Markdown 下方添加折叠/展开按钮
- 在折叠/展开按钮下方添加操作按钮组(复制、播放)
3. 添加按钮组样式(与 ScriptView 保持一致)
### 步骤 4H5 端到端验收
1. 启动 mini-program H5`python dev-services.py start mini-program`(端口 5180
2. 浏览器访问 `http://localhost:5180/#/pages/main/index?tab=script`
3. **生成页验收**
- 进入一个已有小说的生成页(或新建一个)
- 验证小说正文下方有 3 个按钮:折叠/展开、复制、播放
- 点击"折叠":内容截断显示前 200 字 + "展开全文"按钮
- 点击"展开":内容完整显示 + "收起全文"按钮
- 点击"复制"Toast 提示"已复制",剪贴板中有内容
- 点击"播放"TTS 开始播放,按钮变为"暂停"
- 浏览器 Console 检查:0 新增错误
4. **详情页验收**
- 进入小说详情页(从列表页点击剧本)
- 验证小说正文下方有 3 个按钮:折叠/展开、复制、播放
- 重复上述验收步骤
- 浏览器 Console 检查:0 新增错误
## 验收标准
### 功能验收
-**生成页(ScriptView**
- 每条 novel 消息下方有 3 个按钮:折叠/展开、复制、播放
- 折叠/展开功能正常:折叠时显示前 200 字,展开时显示完整内容
- 复制功能正常:点击后 Toast 提示"已复制",剪贴板中有内容
- 播放功能正常:点击后 TTS 开始播放,按钮变为"暂停"
- Console 无新增错误
-**详情页(ScriptDetailView**
- 每条 novel 消息下方有 3 个按钮:折叠/展开、复制、播放
- 折叠/展开功能正常
- 复制功能正常
- 播放功能正常
- Console 无新增错误
### 样式验收
- ✅ 按钮组横向排列,3 个按钮等宽分布
- ✅ 播放按钮为 primary 样式(紫色渐变)
- ✅ 折叠/展开按钮为独立行,在按钮组上方
- ✅ 按钮样式与 MessageCard 保持一致(圆角、背景、边框)
### 数据流验收
- ✅ 埋点正常触发:`script_message_collapse_toggle``script_message_copy_click``script_message_tts_click`
- ✅ TTS 播放状态同步:播放时按钮显示"暂停",暂停时按钮显示"播放"
## 影响范围
### 修改文件
1. `mini-program/src/pages/main/ScriptView.vue`
- 模板:为 `kind === 'novel'` 的消息添加按钮组 UI
- 样式:添加 `.message-actions``.action-btn` 等样式
2. `mini-program/src/pages/main/ScriptDetailView.vue`
- Script:新增 6 个函数/ref
- 模板:为 `kind === 'novel'` 的消息添加按钮组 UI
- 样式:添加 `.message-actions``.action-btn` 等样式
### 不修改的部分
- ❌ 后端代码:不涉及
- ❌ 数据库:不涉及
- ❌ MessageCard.vue 组件:不修改(保持现状)
- ❌ 其他页面:不涉及
### 兼容性
- ✅ 老剧本(无 clarification/outline 数据):不影响,仍可正常显示
- ✅ 新剧本(有完整数据):新增按钮组功能
- ✅ 无小说正文的剧本:不显示按钮组(`kind === 'novel'` 的消息不存在)
## 风险与注意事项
1. **TTS 播放的是整个小说**`ttsPlayer.playSource(scriptId)` 播放的是整个小说的音频,不是单条消息的音频。这是历史设计,保持一致。
2. **折叠阈值**:折叠时显示前 200 字符。这个阈值可以根据实际需求调整。
3. **样式一致性**:两个页面的按钮组样式必须保持一致,建议直接复制 ScriptView 的样式到 ScriptDetailView。
4. **性能考虑**`collapsedMessageIds` 是响应式对象,每次切换折叠状态都会触发重新渲染。对于大量消息的场景,可能需要优化(当前场景下消息数量有限,不构成问题)。
5. **埋点完整性**:所有用户操作(折叠、复制、播放)都必须触发埋点,用于数据分析。
## 总结
本设计方案通过极简设计恢复小程序小说生成页和详情页丢失的"复制、播放、折叠/展开"功能。核心决策是:每条 novel 消息下方只放 3 个核心功能按钮,修订类按钮集中在底部操作区。实现上,ScriptView 复用已有函数,ScriptDetailView 新增相同逻辑。验收标准明确,影响范围可控。