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

17 KiB
Raw Blame History

小说消息操作按钮恢复实施计划

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_togglescript_message_copy_clickscript_message_tts_click

Task 1: ScriptView 恢复按钮组 UI

Files:

  • Modify: mini-program/src/pages/main/ScriptView.vue:180-187novel 消息渲染部分)
  • 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):

<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" /> 改为:

<Markdown :content="getMessageDisplayContent(msg)" />

这样折叠时只显示前 200 字符。

  • Step 3: 添加折叠/展开按钮

<Markdown> 下方添加折叠/展开按钮:

<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: 添加操作按钮组

在折叠/展开按钮下方添加操作按钮组(复制、播放):

<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+)添加以下样式:

/* 折叠/展开按钮行 */
.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 并验收:

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

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-311script 部分)
  • Modify: mini-program/src/pages/main/ScriptDetailView.vue:74-76novel 消息渲染部分)
  • 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)添加:

const collapsedMessageIds = ref({})

放在 const resultMessages = ref([]) 下方。

  • Step 2: 新增 isMessageCollapsed 函数

collapsedMessageIds 下方添加:

/**
 * 判断消息是否折叠
 */
const isMessageCollapsed = (message) => {
  return Boolean(collapsedMessageIds.value[message.id])
}
  • Step 3: 新增 getMessageDisplayContent 函数

isMessageCollapsed 下方添加:

/**
 * 获取消息显示内容(折叠时截断)
 */
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 下方添加:

/**
 * 切换消息折叠状态
 */
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 下方添加:

/**
 * 复制消息内容到剪贴板
 */
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 下方添加:

/**
 * 播放消息 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):

<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" /> 改为:

<Markdown :content="getMessageDisplayContent(msg)" />
  • Step 9: 添加折叠/展开按钮

<Markdown> 下方添加折叠/展开按钮:

<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: 添加操作按钮组

在折叠/展开按钮下方添加操作按钮组(复制、播放):

<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 完全一致):

/* 折叠/展开按钮行 */
.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

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_togglescript_message_copy_clickscript_message_tts_click

整体验收

两个 Task 完成后,进行整体验收:

  1. 生成页验收

    • 访问 http://localhost:5180/#/pages/main/index?tab=script
    • 进入一个已有小说的生成页
    • 验证折叠/展开、复制、播放功能
    • Console 0 新增错误
  2. 详情页验收

    • 从列表页点击一个已有小说的剧本,进入详情页
    • 验证折叠/展开、复制、播放功能
    • Console 0 新增错误
  3. 样式一致性

    • 两个页面的按钮组样式完全一致
    • 按钮布局、颜色、圆角、间距一致
  4. 埋点验证

    • 浏览器 Console 中查看埋点日志
    • 确认 script_message_collapse_togglescript_message_copy_clickscript_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 恢复小说消息操作按钮组