docs:新增 MessageCard 组件实现计划
This commit is contained in:
@@ -0,0 +1,658 @@
|
||||
---
|
||||
author: claude
|
||||
created_at: 2026-06-27
|
||||
purpose: MessageCard 组件统一提取实现计划
|
||||
---
|
||||
|
||||
# MessageCard 组件统一实现计划
|
||||
|
||||
> **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:** 提取 MessageCard.vue 组件,统一 story-card 和 result-chat-list 中 AI 消息卡片的样式,并按 100 字阈值自动切换短消息/完整卡片模式
|
||||
|
||||
**Architecture:** 新建 `MessageCard.vue` 组件封装长消息卡片结构,ScriptView.vue 中 story-card 和 result-chat-list 都复用该组件,短消息直接渲染简洁气泡
|
||||
|
||||
**Tech Stack:** Vue 3, UniApp, 微信小程序
|
||||
|
||||
---
|
||||
|
||||
## 文件结构
|
||||
|
||||
**创建:**
|
||||
- `mini-program/src/components/MessageCard.vue` - 通用 AI 消息卡片组件
|
||||
|
||||
**修改:**
|
||||
- `mini-program/src/pages/main/ScriptView.vue` - 替换 story-card 和 result-chat-list 中的 assistant 消息渲染,并迁移相关样式
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 创建 MessageCard.vue 组件(短消息模式 + 基础结构)
|
||||
|
||||
**Files:**
|
||||
- Create: `mini-program/src/components/MessageCard.vue`
|
||||
- Modify: 无
|
||||
|
||||
- [ ] **Step 1: 创建组件文件并写入基础模板**
|
||||
|
||||
在 `mini-program/src/components/MessageCard.vue` 写入以下内容:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<view v-if="isShortMessage" class="chat-bubble system">
|
||||
<text>{{ content }}</text>
|
||||
</view>
|
||||
<view v-else class="story-card" :class="{ collapsed }">
|
||||
<view class="story-head">
|
||||
<view class="story-title-wrap">
|
||||
<text class="story-title">{{ title || '我的人生剧本' }}</text>
|
||||
<view v-if="tags?.length" class="tag-row">
|
||||
<text v-for="tag in tags" :key="tag" class="tag">{{ tag }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="story-head-actions">
|
||||
<button class="collapse-icon" @click="$emit('toggle-collapse')">
|
||||
<text class="collapse-icon-text">{{ collapsed ? '展开' : '收起' }}</text>
|
||||
<view class="collapse-chevron" :class="{ down: collapsed }">
|
||||
<view></view>
|
||||
<view></view>
|
||||
</view>
|
||||
</button>
|
||||
<button class="copy-card-btn" @click="$emit('copy')">复制</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
v-if="collapsed"
|
||||
class="story-body-scroll"
|
||||
scroll-y
|
||||
:enhanced="true"
|
||||
:show-scrollbar="true"
|
||||
>
|
||||
<text class="story-body" :selectable="true" :user-select="true">{{ content }}</text>
|
||||
</scroll-view>
|
||||
<text v-else class="story-body" :selectable="true" :user-select="true">{{ content }}</text>
|
||||
|
||||
<view class="collapse-row" @click="$emit('toggle-collapse')">
|
||||
<text class="collapse-row-text">{{ collapsed ? '展开全文' : '收起全文' }}</text>
|
||||
<view class="collapse-chevron" :class="{ down: collapsed }">
|
||||
<view></view>
|
||||
<view></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="result-actions">
|
||||
<button class="action-btn" @click="$emit('change-direction')">换个方向</button>
|
||||
<button class="action-btn" @click="$emit('not-like-me')">不像我</button>
|
||||
<button class="action-btn" @click="$emit('continue')">继续生成</button>
|
||||
<button class="action-btn primary" @click="$emit('play-tts')">
|
||||
<text class="action-icon">{{ ttsIcon || '▶' }}</text>
|
||||
<text>{{ ttsText || '播放' }}</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
content: { type: String, required: true },
|
||||
title: { type: String, default: '' },
|
||||
tags: { type: Array, default: () => [] },
|
||||
collapsed: { type: Boolean, default: false },
|
||||
contentLength: { type: Number, required: true },
|
||||
isShortMessage: { type: Boolean, required: true },
|
||||
ttsIcon: { type: String, default: '▶' },
|
||||
ttsText: { type: String, default: '播放' }
|
||||
})
|
||||
|
||||
defineEmits([
|
||||
'toggle-collapse',
|
||||
'copy',
|
||||
'change-direction',
|
||||
'not-like-me',
|
||||
'continue',
|
||||
'play-tts'
|
||||
])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chat-bubble {
|
||||
max-width: 86%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
padding: 24rpx 28rpx;
|
||||
border-radius: 36rpx;
|
||||
font-size: 34rpx;
|
||||
line-height: 52rpx;
|
||||
}
|
||||
|
||||
.chat-bubble.system {
|
||||
align-self: flex-start;
|
||||
background: rgba(16, 8, 34, 0.28);
|
||||
border: 1rpx solid rgba(192, 132, 252, 0.3);
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
backdrop-filter: blur(8rpx);
|
||||
-webkit-backdrop-filter: blur(8rpx);
|
||||
}
|
||||
|
||||
.story-card {
|
||||
border-radius: 52rpx;
|
||||
padding: 34rpx;
|
||||
background: rgba(12, 5, 28, 0.34);
|
||||
border: 1rpx solid rgba(192, 132, 252, 0.46);
|
||||
box-shadow: 0 0 48rpx rgba(125, 55, 205, 0.14);
|
||||
backdrop-filter: blur(6rpx);
|
||||
-webkit-backdrop-filter: blur(6rpx);
|
||||
}
|
||||
|
||||
.story-card.collapsed {
|
||||
box-shadow: 0 0 42rpx rgba(125, 55, 205, 0.14);
|
||||
}
|
||||
|
||||
.story-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.story-head-actions {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.story-title-wrap {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.story-title {
|
||||
display: block;
|
||||
font-size: 52rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.collapse-icon {
|
||||
width: 120rpx;
|
||||
min-width: 120rpx;
|
||||
height: 58rpx;
|
||||
min-height: 58rpx;
|
||||
line-height: 1;
|
||||
padding: 0 18rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
color: rgba(255, 244, 255, 0.95);
|
||||
background:
|
||||
linear-gradient(135deg, rgba(134, 72, 255, 0.44), rgba(39, 15, 76, 0.42)),
|
||||
rgba(255, 255, 255, 0.05);
|
||||
border: 1rpx solid rgba(216, 180, 254, 0.46);
|
||||
box-shadow:
|
||||
0 14rpx 30rpx rgba(61, 18, 113, 0.22),
|
||||
inset 0 1rpx 0 rgba(255, 255, 255, 0.26),
|
||||
inset 0 -10rpx 18rpx rgba(33, 9, 73, 0.16);
|
||||
backdrop-filter: blur(8rpx);
|
||||
-webkit-backdrop-filter: blur(8rpx);
|
||||
font-size: 23rpx;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.collapse-icon::after {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.collapse-icon:active,
|
||||
.copy-card-btn:active,
|
||||
.collapse-row:active {
|
||||
transform: scale(0.98);
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.copy-card-btn {
|
||||
width: 120rpx;
|
||||
min-width: 120rpx;
|
||||
height: 50rpx;
|
||||
min-height: 50rpx;
|
||||
line-height: 1;
|
||||
padding: 0;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgba(255, 244, 255, 0.9);
|
||||
font-size: 23rpx;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0;
|
||||
background:
|
||||
radial-gradient(circle at 50% -30%, rgba(255, 255, 255, 0.18), transparent 50%),
|
||||
rgba(88, 28, 135, 0.26);
|
||||
border: 1rpx solid rgba(216, 180, 254, 0.32);
|
||||
box-shadow:
|
||||
inset 0 1rpx 0 rgba(255, 255, 255, 0.16),
|
||||
0 10rpx 22rpx rgba(61, 18, 113, 0.12);
|
||||
backdrop-filter: blur(8rpx);
|
||||
-webkit-backdrop-filter: blur(8rpx);
|
||||
}
|
||||
|
||||
.copy-card-btn::after {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.collapse-icon-text,
|
||||
.collapse-row-text {
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.tag-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 999rpx;
|
||||
color: #d18aff;
|
||||
font-size: 24rpx;
|
||||
background: rgba(168, 85, 247, 0.22);
|
||||
}
|
||||
|
||||
.story-body {
|
||||
display: block;
|
||||
margin-top: 28rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 400;
|
||||
line-height: 1.78;
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.story-body-scroll {
|
||||
max-height: 420rpx;
|
||||
height: auto;
|
||||
margin-top: 28rpx;
|
||||
padding-right: 8rpx;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.story-body-scroll .story-body {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.collapse-row {
|
||||
position: relative;
|
||||
height: 62rpx;
|
||||
margin-top: 22rpx;
|
||||
padding: 0 28rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12rpx;
|
||||
border-radius: 999rpx;
|
||||
color: rgba(246, 230, 255, 0.96);
|
||||
font-size: 26rpx;
|
||||
font-weight: 800;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(circle at 50% -20%, rgba(255, 255, 255, 0.2), transparent 46%),
|
||||
linear-gradient(135deg, rgba(98, 40, 174, 0.3), rgba(24, 8, 58, 0.24));
|
||||
border: 1rpx solid rgba(192, 132, 252, 0.36);
|
||||
box-shadow:
|
||||
inset 0 1rpx 0 rgba(255, 255, 255, 0.16),
|
||||
0 12rpx 28rpx rgba(61, 18, 113, 0.14);
|
||||
backdrop-filter: blur(6rpx);
|
||||
-webkit-backdrop-filter: blur(6rpx);
|
||||
}
|
||||
|
||||
.collapse-row::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 34rpx;
|
||||
right: 34rpx;
|
||||
top: 0;
|
||||
height: 1rpx;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 236, 180, 0.6), transparent);
|
||||
}
|
||||
|
||||
.result-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14rpx;
|
||||
margin-top: 26rpx;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 验证组件文件创建成功**
|
||||
|
||||
确认文件路径:`mini-program/src/components/MessageCard.vue` 存在。
|
||||
|
||||
```bash
|
||||
ls "G:/IdeaProjects/emotion-museun/mini-program/src/components/MessageCard.vue"
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 提交组件创建**
|
||||
|
||||
```bash
|
||||
cd "G:/IdeaProjects/emotion-museun"
|
||||
git add mini-program/src/components/MessageCard.vue
|
||||
git commit -m "feat: 创建 MessageCard 组件
|
||||
|
||||
封装长消息/故事卡片结构,支持短消息简洁气泡模式"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 在 ScriptView.vue 中注册并使用 MessageCard 组件
|
||||
|
||||
**Files:**
|
||||
- Modify: `mini-program/src/pages/main/ScriptView.vue`
|
||||
|
||||
- [ ] **Step 1: 导入组件**
|
||||
|
||||
在 `ScriptView.vue` 的 `<script setup>` 顶部,导入 MessageCard 组件:
|
||||
|
||||
修改前:
|
||||
```javascript
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
```
|
||||
|
||||
修改后:
|
||||
```javascript
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import MessageCard from '../../components/MessageCard.vue'
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 替换 story-card 模板**
|
||||
|
||||
找到第 172-219 行的 `story-card` 模板,替换为 MessageCard 组件。
|
||||
|
||||
修改前:
|
||||
```vue
|
||||
<view class="story-card" :class="{ collapsed: storyCollapsed, dialogMode: resultHasScriptReplies }">
|
||||
...(原有 story-card 全部内容)...
|
||||
</view>
|
||||
```
|
||||
|
||||
修改后:
|
||||
```vue
|
||||
<MessageCard
|
||||
:content="displayedResultContent"
|
||||
:title="currentResult?.title"
|
||||
:tags="resultTags"
|
||||
:collapsed="storyCollapsed"
|
||||
:content-length="displayedResultContent.length"
|
||||
:is-short-message="false"
|
||||
:tts-icon="ttsActionIcon"
|
||||
:tts-text="ttsActionText"
|
||||
@toggle-collapse="toggleStoryCollapse"
|
||||
@copy="copyResultContent"
|
||||
@change-direction="changeDirection"
|
||||
@not-like-me="notLikeMe"
|
||||
@continue="continueInChat"
|
||||
@play-tts="trackTtsClick"
|
||||
/>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 替换 result-chat-list 中的 assistant 消息**
|
||||
|
||||
找到第 221-254 行的 `result-chat-list` 模板,替换为使用 MessageCard。
|
||||
|
||||
修改前:
|
||||
```vue
|
||||
<view v-if="resultMessages.length" class="result-chat-list">
|
||||
<view
|
||||
v-for="message in resultMessages"
|
||||
:key="message.id"
|
||||
class="chat-bubble"
|
||||
:class="{ user: message.role === 'user', system: message.role === 'assistant', pending: message.pending }"
|
||||
>
|
||||
<text selectable user-select>{{ getMessageDisplayContent(message) }}</text>
|
||||
<view v-if="message.pending" class="thinking-dots">
|
||||
<view></view>
|
||||
<view></view>
|
||||
<view></view>
|
||||
</view>
|
||||
<view
|
||||
v-if="isAssistantMessage(message)"
|
||||
class="message-toggle"
|
||||
@click.stop="toggleMessageCollapse(message)"
|
||||
>
|
||||
<text>{{ isMessageCollapsed(message) ? '展开' : '收起' }}</text>
|
||||
...
|
||||
</view>
|
||||
<view v-if="isAssistantMessage(message)" class="message-actions">
|
||||
...
|
||||
</view>
|
||||
<text class="bubble-time">{{ message.time }}</text>
|
||||
</view>
|
||||
</view>
|
||||
```
|
||||
|
||||
修改后:
|
||||
```vue
|
||||
<view v-if="resultMessages.length" class="result-chat-list">
|
||||
<view
|
||||
v-for="message in resultMessages"
|
||||
:key="message.id"
|
||||
>
|
||||
<MessageCard
|
||||
v-if="isAssistantMessage(message)"
|
||||
:content="message.content"
|
||||
:collapsed="isMessageCollapsed(message)"
|
||||
:content-length="message.content.length"
|
||||
:is-short-message="message.content.length < 100"
|
||||
:tts-icon="ttsPlayer.playing.value ? 'Ⅱ' : '▶'"
|
||||
:tts-text="ttsPlayer.playing.value ? '暂停' : '播放'"
|
||||
@toggle-collapse="toggleMessageCollapse(message)"
|
||||
@copy="copyMessageContent(message)"
|
||||
@change-direction="changeDirection"
|
||||
@not-like-me="notLikeMe"
|
||||
@continue="continueInChat"
|
||||
@play-tts="playMessageTts(message)"
|
||||
/>
|
||||
<view v-else class="chat-bubble user">
|
||||
<text>{{ message.content }}</text>
|
||||
<text class="bubble-time">{{ message.time }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 提交使用组件**
|
||||
|
||||
```bash
|
||||
cd "G:/IdeaProjects/emotion-museun"
|
||||
git add mini-program/src/pages/main/ScriptView.vue
|
||||
git commit -m "feat: ScriptView 复用 MessageCard 组件
|
||||
|
||||
- story-card 使用 MessageCard
|
||||
- result-chat-list 中的 assistant 消息按内容长度自动切换短消息/完整卡片模式"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: 迁移并清理样式
|
||||
|
||||
**Files:**
|
||||
- Modify: `mini-program/src/pages/main/ScriptView.vue`
|
||||
|
||||
- [ ] **Step 1: 从 ScriptView.vue 中删除已迁移到 MessageCard 的样式**
|
||||
|
||||
删除以下 CSS 类(从 `<style scoped>` 中移除):
|
||||
- `.story-card`、`.story-card.collapsed`
|
||||
- `.story-head`、`.story-head-actions`、`.story-title-wrap`
|
||||
- `.story-title`、`.tag-row`、`.tag`
|
||||
- `.collapse-icon`、`.copy-card-btn`、`.collapse-row`
|
||||
- `.collapse-chevron`、`.story-body`、`.story-body-scroll`
|
||||
- `.result-actions`、`.action-btn`
|
||||
- `.message-actions`(上一轮临时添加的,不再使用)
|
||||
- `.message-toggle`(不再使用)
|
||||
|
||||
保留 `ScriptView.vue` 中仍然需要的样式,如:
|
||||
- `.chat-bubble.user`
|
||||
- `.bubble-time`
|
||||
- `.result-chat-list`
|
||||
- `.thinking-dots`
|
||||
- 页面整体布局相关样式
|
||||
|
||||
- [ ] **Step 2: 验证样式清理后无遗漏**
|
||||
|
||||
```bash
|
||||
cd "G:/IdeaProjects/emotion-museun/mini-program"
|
||||
npm run build:h5 2>&1 | tail -20
|
||||
```
|
||||
|
||||
预期输出:`DONE Build complete.`
|
||||
|
||||
- [ ] **Step 3: 提交样式清理**
|
||||
|
||||
```bash
|
||||
cd "G:/IdeaProjects/emotion-museun"
|
||||
git add mini-program/src/pages/main/ScriptView.vue
|
||||
git commit -m "refactor: 迁移 story-card 相关样式到 MessageCard
|
||||
|
||||
删除 ScriptView.vue 中已迁移到 MessageCard 的样式类"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: 最终验证
|
||||
|
||||
**Files:**
|
||||
- 无需修改文件,仅做验证
|
||||
|
||||
- [ ] **Step 1: 构建验证**
|
||||
|
||||
```bash
|
||||
cd "G:/IdeaProjects/emotion-museun/mini-program"
|
||||
npm run build:h5 2>&1 | tail -20
|
||||
```
|
||||
|
||||
预期输出:`DONE Build complete.`
|
||||
|
||||
- [ ] **Step 2: 功能验证(在 H5 浏览器中)**
|
||||
|
||||
启动 H5 开发服务器并验证:
|
||||
|
||||
```bash
|
||||
cd "G:/IdeaProjects/emotion-museun/mini-program"
|
||||
npm run dev:h5
|
||||
```
|
||||
|
||||
打开浏览器访问显示的端口(默认 `http://localhost:5173`),执行以下验证:
|
||||
|
||||
1. **第一次生成故事**:确认 story-card 样式、标题、标签、展开/收起、复制、功能按钮组均正常
|
||||
2. **重新生成(生成 script 类型长消息)**:
|
||||
- 内容 ≥ 100 字:显示与 story-card 完全一致的卡片样式
|
||||
- 内容 < 100 字:显示简洁气泡,无任何功能按钮
|
||||
3. **普通聊天 assistant 消息**:
|
||||
- 内容 ≥ 100 字:显示完整卡片
|
||||
- 内容 < 100 字:显示简洁气泡
|
||||
4. **用户消息**:保持原有 `.chat-bubble.user` 样式不变
|
||||
5. **控制台**:确认无 Vue 警告和 JavaScript 错误
|
||||
|
||||
- [ ] **Step 3: 提交验证结果(如有修复)**
|
||||
|
||||
如果在验证中修复了问题,提交:
|
||||
|
||||
```bash
|
||||
git add mini-program/src/components/MessageCard.vue mini-program/src/pages/main/ScriptView.vue
|
||||
git commit -m "fix: 修复 MessageCard 组件验证中发现的问题
|
||||
|
||||
[描述具体修复内容]"
|
||||
```
|
||||
|
||||
如果验证全部通过,无需额外提交。
|
||||
|
||||
---
|
||||
|
||||
## 自审检查
|
||||
|
||||
- **Spec 覆盖:**
|
||||
- ✅ 创建 MessageCard.vue 组件 → Task 1
|
||||
- ✅ ScriptView.vue 复用组件 → Task 2
|
||||
- ✅ 100 字阈值判断 → Task 2 (`:is-short-message="message.content.length < 100"`)
|
||||
- ✅ 样式迁移和清理 → Task 3
|
||||
- ✅ 验证 → Task 4
|
||||
- **无占位符:** 计划中没有 TBD/TODO
|
||||
- **类型一致:** props 名称 `contentLength` 与模板中使用一致,事件名称在组件和父组件中一致
|
||||
Reference in New Issue
Block a user