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

606 lines
17 KiB
Markdown
Raw 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.
# 小说消息操作按钮恢复实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 恢复小程序小说生成页和详情页丢失的"复制、播放、折叠/展开"功能,每条 novel 消息下方添加 3 个操作按钮。
**Architecture:** 在 ScriptView.vue 和 ScriptDetailView.vue 中为 `kind === 'novel'` 的消息添加按钮组 UI(折叠/展开、复制、播放)。ScriptView 复用已有函数,ScriptDetailView 新增相同逻辑。按钮组样式统一,参考 MessageCard 的 result-actions 样式。
**Tech Stack:** Vue 3 Composition API / UniApp / Pinia / uni.setClipboardData / useTtsPlayer composable / H5 端到端验收
## Global Constraints
- 所有注释必须使用中文
- 样式单位必须使用 rpx(小程序移动端)
- 禁止任何形式的 mock、兜底、默认值掩盖错误
- 每个按钮必须有真实实现,不能有任何占位或 mock 功能
- 修改后必须通过 H5 端到端验收(Console 0 新增错误)
- 部署使用 `python deploy.py backend`(后端)或热加载(前端)
- 埋点必须完整触发:`script_message_collapse_toggle``script_message_copy_click``script_message_tts_click`
---
## Task 1: ScriptView 恢复按钮组 UI
**Files:**
- Modify: `mini-program/src/pages/main/ScriptView.vue:180-187`novel 消息渲染部分)
- Modify: `mini-program/src/pages/main/ScriptView.vue:3500+`(样式部分)
**Interfaces:**
- Consumes: 已有函数 `getMessageDisplayContent(msg)``toggleMessageCollapse(msg)``copyMessageContent(msg)``playMessageTts(msg)``isMessageCollapsed(msg)`
- Produces: novel 消息下方显示 3 个按钮(折叠/展开、复制、播放),功能正常
### 背景
ScriptView.vue 中所有相关函数已存在(行 399-447),但模板中按钮组 UI 在 commit 2c778fa 中被删除。本任务恢复按钮组 UI。
### 步骤
- [ ] **Step 1: 定位 novel 消息渲染部分**
打开 `mini-program/src/pages/main/ScriptView.vue`,找到 `kind === 'novel'` 的消息渲染部分(约行 180-187):
```vue
<view v-else-if="msg.kind === 'novel'" class="chat-bubble system novel-bubble">
<Markdown :content="msg.content" />
</view>
```
- [ ] **Step 2: 修改 Markdown 渲染,使用折叠内容**
`<Markdown :content="msg.content" />` 改为:
```vue
<Markdown :content="getMessageDisplayContent(msg)" />
```
这样折叠时只显示前 200 字符。
- [ ] **Step 3: 添加折叠/展开按钮**
`<Markdown>` 下方添加折叠/展开按钮:
```vue
<view v-if="msg.content && msg.content.length > 200" class="collapse-toggle-row" @click="toggleMessageCollapse(msg)">
<text class="collapse-toggle-text">{{ isMessageCollapsed(msg) ? '展开全文' : '收起全文' }}</text>
<view class="collapse-chevron" :class="{ down: isMessageCollapsed(msg) }">
<view></view>
<view></view>
</view>
</view>
```
- [ ] **Step 4: 添加操作按钮组**
在折叠/展开按钮下方添加操作按钮组(复制、播放):
```vue
<view class="message-actions">
<button class="action-btn" @click="copyMessageContent(msg)">
<text>📋 复制</text>
</button>
<button class="action-btn primary" @click="playMessageTts(msg)">
<text class="action-icon">{{ ttsPlayer.playing.value ? 'Ⅱ' : '▶' }}</text>
<text>{{ ttsPlayer.playing.value ? '暂停' : '播放' }}</text>
</button>
</view>
```
注意:只有 2 个按钮(复制、播放),折叠/展开按钮已在上方独立显示。
- [ ] **Step 5: 添加按钮组样式**
`<style scoped>` 部分(约行 3500+)添加以下样式:
```css
/* 折叠/展开按钮行 */
.collapse-toggle-row {
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
margin-top: 20rpx;
padding: 16rpx 0;
border-radius: 999rpx;
background: rgba(88, 28, 135, 0.12);
border: 1rpx solid rgba(192, 132, 252, 0.28);
color: rgba(246, 230, 255, 0.96);
font-size: 26rpx;
font-weight: 700;
}
.collapse-toggle-row:active {
transform: scale(0.98);
opacity: 0.92;
}
.collapse-toggle-text {
line-height: 1;
}
/* 折叠箭头图标 */
.collapse-chevron {
position: relative;
width: 20rpx;
height: 16rpx;
flex-shrink: 0;
transition: transform 0.18s ease;
}
.collapse-chevron.down {
transform: rotate(180deg);
}
.collapse-chevron view {
position: absolute;
top: 7rpx;
width: 12rpx;
height: 4rpx;
border-radius: 999rpx;
background: linear-gradient(90deg, #fff3b0, #ffd86b);
box-shadow: 0 0 12rpx rgba(255, 216, 107, 0.5);
}
.collapse-chevron view:first-child {
left: 0;
transform: rotate(-38deg);
transform-origin: right center;
}
.collapse-chevron view:last-child {
right: 0;
transform: rotate(38deg);
transform-origin: left center;
}
/* 操作按钮组 */
.message-actions {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 14rpx;
margin-top: 20rpx;
}
.action-btn {
height: 72rpx;
min-height: 72rpx;
padding: 0 8rpx;
border-radius: 28rpx;
display: flex;
align-items: center;
justify-content: center;
color: #e8ccff;
font-size: 26rpx;
font-weight: 700;
line-height: 1.15;
text-align: center;
white-space: normal;
box-sizing: border-box;
background: rgba(88, 28, 135, 0.18);
border: 1rpx solid rgba(192, 132, 252, 0.35);
}
.action-btn.primary {
color: #fff;
background: linear-gradient(145deg, #8c44f2, #5f1db8);
}
.action-icon {
margin-right: 8rpx;
font-size: 24rpx;
font-weight: 900;
line-height: 1;
}
.action-btn::after {
border: 0;
}
```
- [ ] **Step 6: H5 验收**
启动 H5 并验收:
```bash
python dev-services.py start mini-program
```
浏览器访问 `http://localhost:5180/#/pages/main/index?tab=script`,进入一个已有小说的生成页:
验收项:
- ✅ 小说正文下方有折叠/展开按钮(内容超过 200 字时显示)
- ✅ 点击"展开全文":内容完整显示,按钮变为"收起全文"
- ✅ 点击"收起全文":内容截断显示前 200 字,按钮变为"展开全文"
- ✅ 小说正文下方有 2 个操作按钮:复制、播放
- ✅ 点击"复制"Toast 提示"已复制",剪贴板中有内容
- ✅ 点击"播放"TTS 开始播放,按钮变为"暂停"
- ✅ 浏览器 Console 检查:0 新增错误
- [ ] **Step 7: Commit**
```bash
git add mini-program/src/pages/main/ScriptView.vue
git commit -m "feat(mini-program): ScriptView 恢复小说消息操作按钮组
- 为 kind === 'novel' 的消息添加折叠/展开、复制、播放按钮
- 复用已有函数:getMessageDisplayContent、toggleMessageCollapse、copyMessageContent、playMessageTts
- 添加 .collapse-toggle-row、.message-actions、.action-btn 样式
- H5 验收通过,Console 0 新增错误"
```
### 验收标准
- ✅ 每条 novel 消息下方有折叠/展开按钮(内容超过 200 字时显示)
- ✅ 折叠/展开功能正常:折叠时显示前 200 字,展开时显示完整内容
- ✅ 每条 novel 消息下方有 2 个操作按钮:复制、播放
- ✅ 复制功能正常:点击后 Toast 提示"已复制"
- ✅ 播放功能正常:点击后 TTS 开始播放,按钮变为"暂停"
- ✅ Console 无新增错误
---
## Task 2: ScriptDetailView 新增函数和按钮组 UI
**Files:**
- Modify: `mini-program/src/pages/main/ScriptDetailView.vue:96-311`script 部分)
- Modify: `mini-program/src/pages/main/ScriptDetailView.vue:74-76`novel 消息渲染部分)
- Modify: `mini-program/src/pages/main/ScriptDetailView.vue:313+`(样式部分)
**Interfaces:**
- Consumes: `ttsPlayer` composable(已有)、`script` ref(已有)
- Produces: novel 消息下方显示 3 个按钮(折叠/展开、复制、播放),功能正常
### 背景
ScriptDetailView.vue 中完全没有相关函数和按钮组 UI。本任务新增所有函数 + 模板按钮组 + 样式。
### 步骤
- [ ] **Step 1: 新增 collapsedMessageIds ref**
打开 `mini-program/src/pages/main/ScriptDetailView.vue`,在 `<script setup>` 部分(约行 96-110)添加:
```javascript
const collapsedMessageIds = ref({})
```
放在 `const resultMessages = ref([])` 下方。
- [ ] **Step 2: 新增 isMessageCollapsed 函数**
`collapsedMessageIds` 下方添加:
```javascript
/**
* 判断消息是否折叠
*/
const isMessageCollapsed = (message) => {
return Boolean(collapsedMessageIds.value[message.id])
}
```
- [ ] **Step 3: 新增 getMessageDisplayContent 函数**
`isMessageCollapsed` 下方添加:
```javascript
/**
* 获取消息显示内容(折叠时截断)
*/
const getMessageDisplayContent = (message) => {
const content = String(message?.content || '')
if (!isMessageCollapsed(message)) return content
if (content.length <= 200) return content
return `${content.slice(0, 200)}...`
}
```
- [ ] **Step 4: 新增 toggleMessageCollapse 函数**
`getMessageDisplayContent` 下方添加:
```javascript
/**
* 切换消息折叠状态
*/
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 })
}
```
- [ ] **Step 5: 新增 copyMessageContent 函数**
`toggleMessageCollapse` 下方添加:
```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 })
}
```
- [ ] **Step 6: 新增 playMessageTts 函数**
`copyMessageContent` 下方添加:
```javascript
/**
* 播放消息 TTS 音频
*/
const playMessageTts = (message) => {
const scriptId = script.value?.id || ''
ttsPlayer.playSource(scriptId)
analytics.track('script_message_tts_click', {
message_id: message?.id || '',
script_id: scriptId
}, { eventType: 'script', pagePath })
}
```
- [ ] **Step 7: 定位 novel 消息渲染部分**
在模板中找到 `kind === 'novel'` 的消息渲染部分(约行 74-76):
```vue
<view v-else-if="msg.kind === 'novel'" class="chat-bubble system novel-bubble">
<Markdown :content="msg.content" />
</view>
```
- [ ] **Step 8: 修改 Markdown 渲染,使用折叠内容**
`<Markdown :content="msg.content" />` 改为:
```vue
<Markdown :content="getMessageDisplayContent(msg)" />
```
- [ ] **Step 9: 添加折叠/展开按钮**
`<Markdown>` 下方添加折叠/展开按钮:
```vue
<view v-if="msg.content && msg.content.length > 200" class="collapse-toggle-row" @click="toggleMessageCollapse(msg)">
<text class="collapse-toggle-text">{{ isMessageCollapsed(msg) ? '展开全文' : '收起全文' }}</text>
<view class="collapse-chevron" :class="{ down: isMessageCollapsed(msg) }">
<view></view>
<view></view>
</view>
</view>
```
- [ ] **Step 10: 添加操作按钮组**
在折叠/展开按钮下方添加操作按钮组(复制、播放):
```vue
<view class="message-actions">
<button class="action-btn" @click="copyMessageContent(msg)">
<text>📋 复制</text>
</button>
<button class="action-btn primary" @click="playMessageTts(msg)">
<text class="action-icon">{{ ttsPlayer.playing.value ? 'Ⅱ' : '▶' }}</text>
<text>{{ ttsPlayer.playing.value ? '暂停' : '播放' }}</text>
</button>
</view>
```
- [ ] **Step 11: 添加按钮组样式**
`<style scoped>` 部分(约行 313+)添加以下样式(与 ScriptView 完全一致):
```css
/* 折叠/展开按钮行 */
.collapse-toggle-row {
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
margin-top: 20rpx;
padding: 16rpx 0;
border-radius: 999rpx;
background: rgba(88, 28, 135, 0.12);
border: 1rpx solid rgba(192, 132, 252, 0.28);
color: rgba(246, 230, 255, 0.96);
font-size: 26rpx;
font-weight: 700;
}
.collapse-toggle-row:active {
transform: scale(0.98);
opacity: 0.92;
}
.collapse-toggle-text {
line-height: 1;
}
/* 折叠箭头图标 */
.collapse-chevron {
position: relative;
width: 20rpx;
height: 16rpx;
flex-shrink: 0;
transition: transform 0.18s ease;
}
.collapse-chevron.down {
transform: rotate(180deg);
}
.collapse-chevron view {
position: absolute;
top: 7rpx;
width: 12rpx;
height: 4rpx;
border-radius: 999rpx;
background: linear-gradient(90deg, #fff3b0, #ffd86b);
box-shadow: 0 0 12rpx rgba(255, 216, 107, 0.5);
}
.collapse-chevron view:first-child {
left: 0;
transform: rotate(-38deg);
transform-origin: right center;
}
.collapse-chevron view:last-child {
right: 0;
transform: rotate(38deg);
transform-origin: left center;
}
/* 操作按钮组 */
.message-actions {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 14rpx;
margin-top: 20rpx;
}
.action-btn {
height: 72rpx;
min-height: 72rpx;
padding: 0 8rpx;
border-radius: 28rpx;
display: flex;
align-items: center;
justify-content: center;
color: #e8ccff;
font-size: 26rpx;
font-weight: 700;
line-height: 1.15;
text-align: center;
white-space: normal;
box-sizing: border-box;
background: rgba(88, 28, 135, 0.18);
border: 1rpx solid rgba(192, 132, 252, 0.35);
}
.action-btn.primary {
color: #fff;
background: linear-gradient(145deg, #8c44f2, #5f1db8);
}
.action-icon {
margin-right: 8rpx;
font-size: 24rpx;
font-weight: 900;
line-height: 1;
}
.action-btn::after {
border: 0;
}
```
- [ ] **Step 12: H5 验收**
H5 服务应已在运行(端口 5180),浏览器访问详情页:
从列表页点击一个已有小说的剧本,进入详情页。
验收项:
- ✅ 小说正文下方有折叠/展开按钮(内容超过 200 字时显示)
- ✅ 点击"展开全文":内容完整显示,按钮变为"收起全文"
- ✅ 点击"收起全文":内容截断显示前 200 字,按钮变为"展开全文"
- ✅ 小说正文下方有 2 个操作按钮:复制、播放
- ✅ 点击"复制"Toast 提示"已复制",剪贴板中有内容
- ✅ 点击"播放"TTS 开始播放,按钮变为"暂停"
- ✅ 浏览器 Console 检查:0 新增错误
- [ ] **Step 13: Commit**
```bash
git add mini-program/src/pages/main/ScriptDetailView.vue
git commit -m "feat(mini-program): ScriptDetailView 新增小说消息操作按钮组
- 新增 collapsedMessageIds ref + 5 个函数(isMessageCollapsed、getMessageDisplayContent、toggleMessageCollapse、copyMessageContent、playMessageTts
- 为 kind === 'novel' 的消息添加折叠/展开、复制、播放按钮
- 添加 .collapse-toggle-row、.message-actions、.action-btn 样式
- H5 验收通过,Console 0 新增错误"
```
### 验收标准
- ✅ 每条 novel 消息下方有折叠/展开按钮(内容超过 200 字时显示)
- ✅ 折叠/展开功能正常:折叠时显示前 200 字,展开时显示完整内容
- ✅ 每条 novel 消息下方有 2 个操作按钮:复制、播放
- ✅ 复制功能正常:点击后 Toast 提示"已复制"
- ✅ 播放功能正常:点击后 TTS 开始播放,按钮变为"暂停"
- ✅ Console 无新增错误
- ✅ 埋点正常触发:`script_message_collapse_toggle``script_message_copy_click``script_message_tts_click`
---
## 整体验收
两个 Task 完成后,进行整体验收:
1. **生成页验收**
- 访问 `http://localhost:5180/#/pages/main/index?tab=script`
- 进入一个已有小说的生成页
- 验证折叠/展开、复制、播放功能
- Console 0 新增错误
2. **详情页验收**
- 从列表页点击一个已有小说的剧本,进入详情页
- 验证折叠/展开、复制、播放功能
- Console 0 新增错误
3. **样式一致性**
- 两个页面的按钮组样式完全一致
- 按钮布局、颜色、圆角、间距一致
4. **埋点验证**
- 浏览器 Console 中查看埋点日志
- 确认 `script_message_collapse_toggle``script_message_copy_click``script_message_tts_click` 正常触发
---
## 风险与注意事项
1. **TTS 播放的是整个小说**`ttsPlayer.playSource(scriptId)` 播放的是整个小说的音频,不是单条消息的音频。这是历史设计,保持一致。
2. **折叠阈值**:折叠时显示前 200 字符。这个阈值可以根据实际需求调整。
3. **样式一致性**:两个页面的按钮组样式必须保持一致,直接复制 ScriptView 的样式到 ScriptDetailView。
4. **性能考虑**`collapsedMessageIds` 是响应式对象,每次切换折叠状态都会触发重新渲染。对于大量消息的场景,可能需要优化(当前场景下消息数量有限,不构成问题)。
5. **热加载**:前端修改后利用热加载自动生效,不需要重启。如果热加载不生效,使用 `python dev-services.py restart mini-program`
---
## 提交历史
完成所有 Task 后,提交历史应为:
```
<hash> feat(mini-program): ScriptDetailView 新增小说消息操作按钮组
<hash> feat(mini-program): ScriptView 恢复小说消息操作按钮组
```