---
AIGC:
Label: "1"
ContentProducer: 001191110102MACQD9K64018705
ProduceID: 15868733686388_0/project_7655981463858544923-files/docs/frontend-v21-ui-development-plan.md
ReservedCode1: ""
ContentPropagator: 001191110102MACQD9K64028705
PropagateID: 15868733686388#1782829027273
ReservedCode2: ""
---
# V21 UI 前端开发指令文档
> 版本:V21 UI 1:1 还原开发指南
> 更新时间:2024年
> 技术栈:React 18 + TypeScript + Ant Design 5 + Tailwind CSS + React Query
---
## 目录
1. [全局设计系统规范](#1-全局设计系统规范)
2. [全局布局规范](#2-全局布局规范)
3. [逐页开发指令](#3-逐页开发指令)
4. [新增页面开发指令](#4-新增页面开发指令)
5. [基础组件封装规范](#5-基础组件封装规范)
6. [执行顺序建议](#6-执行顺序建议)
---
## 1. 全局设计系统规范
### 1.1 CSS 变量定义(global.css)
从原型中提取的完整 CSS 变量系统:
```css
/* ========== global.css ========== */
:root {
/* ==================== 颜色系统 ==================== */
/* 主色调 */
--primary: #4f46e5; /* 主色 */
--primary-dark: #4338ca; /* 主色深 */
--primary-soft: #eef2ff; /* 主色浅(背景) */
--primary-light: #818cf8; /* 主色亮 */
/* 中性色 */
--slate: #0f172a; /* 标题/重要文字 */
--muted: #64748b; /* 次要文字/占位符 */
--line: #e2e8f0; /* 边框/分隔线 */
--bg: #f8fafc; /* 页面背景 */
/* 功能色 */
--green: #10b981; /* 成功/通过 */
--amber: #f59e0b; /* 警告/进行中 */
--red: #ef4444; /* 错误/高风险 */
/* ==================== 圆角系统 ==================== */
--radius-sm: 14px; /* 小圆角(按钮、内边距) */
--radius-md: 18px; /* 中圆角(卡片、模态框) */
--radius-lg: 24px; /* 大圆角(容器) */
--radius-xl: 28px; /* 超大圆角(主内容卡片) */
/* ==================== 阴影系统 ==================== */
--shadow-sm: 0 10px 30px rgba(15, 23, 42, 0.06); /* 小阴影 */
--shadow-md: 0 24px 70px rgba(15, 23, 42, 0.09); /* 中阴影(卡片默认) */
/* ==================== 间距系统(已对齐) ==================== */
--space-xs: 4px;
--space-sm: 8px;
--space-md: 12px;
--space-lg: 16px;
--space-xl: 20px;
--space-2xl: 24px;
--space-3xl: 32px;
/* ==================== 布局尺寸 ==================== */
--sidebar-width: 240px;
--header-height: 68px;
--max-content-width: 1440px;
/* ==================== 字体系统 ==================== */
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
--font-size-xs: 11px;
--font-size-sm: 12px;
--font-size-base: 14px;
--font-size-md: 15px;
--font-size-lg: 16px;
--font-size-xl: 18px;
--font-size-2xl: 22px;
--font-size-3xl: 26px;
--font-size-4xl: 28px;
--font-size-hero: 44px;
/* ==================== 动效 ==================== */
--transition-fast: 0.15s ease;
--transition-base: 0.18s ease;
--transition-slow: 0.3s ease;
}
```
### 1.2 颜色使用规范
| 颜色名 | 色值 | 使用场景 |
|--------|------|----------|
| `--primary` | #4f46e5 | 主按钮、选中状态、强调文字 |
| `--primary-soft` | #eef2ff | 选中/激活背景、标签背景 |
| `--primary-light` | #818cf8 | 图标、渐变色 |
| `--slate` | #0f172a | 标题、主要文字 |
| `--muted` | #64748b | 次要文字、时间戳 |
| `--line` | #e2e8f0 | 边框、分隔线 |
| `--bg` | #f8fafc | 页面背景 |
| `--green` | #10b981 | 成功、通过、可发布 |
| `--amber` | #f59e0b | 警告、进行中、待复核 |
| `--red` | #ef4444 | 错误、高风险、删除 |
### 1.3 标签(Tag/Pill)样式规范
```css
/* 标签通用样式 */
.tag {
display: inline-flex;
padding: 6px 12px;
border-radius: 999px;
border: 1px solid #c7d2fe;
background: #eef2ff;
color: #4338ca;
font-weight: 700;
font-size: 13px;
}
/* 状态胶囊样式 */
.pill {
font-size: 11px;
padding: 4px 8px;
border-radius: 999px;
font-weight: 700;
display: inline-block;
}
.pill.ok { /* 成功/已通过 */
background: #dcfce7;
color: #166534;
}
.pill.warn { /* 警告/待复核 */
background: #ffedd5;
color: #c2410c;
}
.pill.bad { /* 错误/高风险 */
background: #ffe4e6;
color: #be123c;
}
.pill.info { /* 信息/视频类型 */
background: #dbeafe;
color: #1d4ed8;
}
.pill.muted { /* 禁用/品牌类 */
background: #f1f5f9;
color: #64748b;
}
```
### 1.4 按钮样式规范
```css
/* 按钮基础样式 */
.btn {
border: 0;
border-radius: var(--radius-sm); /* 14px */
padding: 11px 18px;
font-weight: 700;
cursor: pointer;
transition: var(--transition-base);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 7px;
font-size: var(--font-size-base);
}
/* 主要按钮(渐变) */
.btn.primary {
background: linear-gradient(135deg, #6366f1, #4f46e5);
color: #fff;
box-shadow: 0 14px 26px rgba(79, 70, 229, 0.22);
}
.btn.primary:hover {
transform: translateY(-2px);
box-shadow: 0 18px 34px rgba(79, 70, 229, 0.28);
}
/* 次要按钮(幽灵) */
.btn.ghost {
background: #fff;
border: 1px solid var(--line);
color: #475569;
}
.btn.ghost:hover {
border-color: #c7d2fe;
color: #4338ca;
background: #f8faff;
}
/* 小按钮 */
.btn.sm {
padding: 8px 14px;
font-size: var(--font-size-sm);
}
/* 激活态 */
.btn:active {
transform: translateY(0);
}
/* 禁用态 */
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
```
### 1.5 卡片样式规范
```css
/* 通用卡片 */
.card {
background: rgba(255, 255, 255, 0.94);
border: 1px solid rgba(226, 232, 240, 0.95);
border-radius: var(--radius-xl); /* 28px */
box-shadow: var(--shadow-md);
padding: 28px;
}
/* 网格卡片(Hover效果) */
.card-hover {
transition: var(--transition-base);
}
.card-hover:hover {
transform: translateY(-3px);
box-shadow: var(--shadow-sm);
}
```
---
## 2. 全局布局规范
### 2.1 主应用布局(AppLayout)
```
┌─────────────────────────────────────────────────────────────────┐
│ Header (height: 68px, sticky) │
├──────────────┬──────────────────────────────────────────────────┤
│ │ │
│ Sidebar │ Main Content │
│ (240px) │ (flex: 1) │
│ │ │
│ - Logo │ ┌─────────────────────────────────────────┐ │
│ - Nav │ │ Page Head (flex space-between) │ │
│ - Divider │ │ - Title + Description │ │
│ - Bottom │ │ - Action Buttons │ │
│ │ └─────────────────────────────────────────┘ │
│ │ │
│ │ ┌─────────────────────────────────────────┐ │
│ │ │ Content Area │ │
│ │ │ (根据页面类型不同布局) │ │
│ │ └─────────────────────────────────────────┘ │
│ │ │
└──────────────┴──────────────────────────────────────────────────┘
```
**布局实现代码:**
```tsx
// components/layout/AppLayout.tsx
import React from 'react';
import { Outlet } from 'react-router-dom';
import Sidebar from './Sidebar';
import Header from './Header';
const AppLayout: React.FC = () => {
return (
);
};
export default AppLayout;
```
### 2.2 侧边栏导航结构(Sidebar)
```tsx
// components/layout/Sidebar.tsx
const navItems = [
{ icon: '📊', label: '控制台', path: '/dashboard' },
{ icon: '📝', label: '标题库', path: '/titles' },
{ icon: '📦', label: '素材库', path: '/assets' },
{ icon: '🎙️', label: '配音库', path: '/voices' },
{ icon: '🎨', label: '模板库', path: '/templates' },
];
const navItems2 = [
{ icon: '✨', label: '一键生成', path: '/generate', highlight: true },
{ icon: '🎬', label: '成片库', path: '/products' },
{ icon: '📋', label: '任务历史', path: '/history' },
{ icon: '🔍', label: '查重检测', path: '/duplication' },
{ icon: '✂️', label: '剪辑计划', path: '/editing-planner' },
{ icon: '🎤', label: '我的音色', path: '/voice-clone' },
{ icon: '🔑', label: '账号管理', path: '/accounts' },
];
const bottomItems = [
{ icon: '💎', label: '订阅管理', path: '/subscription' },
];
```
**侧边栏样式(来自原型):**
```tsx
// Sidebar CSS
const sidebarStyle = {
background: 'rgba(255, 255, 255, 0.88)',
backdropFilter: 'blur(18px)',
borderRight: '1px solid rgba(226, 232, 240, 0.9)',
padding: '24px 16px',
position: 'sticky' as const,
top: '68px',
height: 'calc(100vh - 68px)',
overflowY: 'auto' as const,
};
const sidebarItemStyle = {
display: 'flex',
alignItems: 'center',
gap: '12px',
padding: '12px 14px',
borderRadius: 'var(--radius-sm)',
color: 'var(--muted)',
cursor: 'pointer',
fontWeight: 600,
fontSize: '14px',
transition: '0.15s ease',
};
const sidebarItemActiveStyle = {
...sidebarItemStyle,
background: 'var(--primary-soft)',
color: 'var(--primary)',
fontWeight: 700,
};
const iconStyle = {
width: '36px',
height: '36px',
borderRadius: '10px',
display: 'grid',
placeItems: 'center',
fontSize: '18px',
background: '#f1f5f9',
};
const iconActiveStyle = {
...iconStyle,
background: 'var(--primary)',
color: '#fff',
};
```
### 2.3 页面头部(PageHead)
```tsx
// components/common/PageHead.tsx
interface PageHeadProps {
title: string;
description?: string;
tag?: string;
actions?: React.ReactNode;
}
const PageHead: React.FC = ({
title,
description,
tag,
actions
}) => {
return (
{tag &&
{tag}}
{title}
{description && (
{description}
)}
{actions &&
{actions}
}
);
};
```
### 2.4 页面容器(Wrap)
```tsx
// 全局页面包装器
const Wrap: React.FC<{ children: React.ReactNode }> = ({ children }) => (
{children}
);
```
---
## 3. 逐页开发指令
### 3.1 控制台 (Dashboard)
**文件路径:** `pages/dashboard/Dashboard.tsx`
**路由:** `/dashboard`
**当前状态:**
- ✅ API 调用逻辑已实现
- ✅ 统计数据获取逻辑已实现
- ⚠️ KPI 卡片使用 Ant Design 样式
- ⚠️ 最近任务使用 Table 布局,需改为卡片式
- ⚠️ 缺少快速入口卡片网格(4列布局)
**UI 差异清单:**
| 差异项 | 当前状态 | 原型状态 | 优先级 |
|--------|----------|----------|--------|
| KPI 卡片样式 | Ant Design Card | 渐变背景 + 大号数字 + 趋势 | P2 |
| 最近任务布局 | Table | 卡片式列表 | P2 |
| 快速入口网格 | 无 | 4列 feature-grid | P2 |
| 页面头部欢迎语 | - | 带用户名的欢迎语 + 日期 | P2 |
**具体修改指令:**
#### 3.1.1 KPI 卡片网格
**改什么:** 将现有的 KPI 卡片改为原型设计的渐变背景卡片
**改成什么样子:**
```
┌────────────┬────────────┬────────────┬────────────┐
│ 12 │ 486 │ 156 │ 2.4GB │
│ 项目总数 │ 素材总数 │ 本月生成数 │ 存储空间 │
│ ↑2本月新增 │ ↑38本月上传 │ ↑23% vs上月│ 已用24% │
└────────────┴────────────┴────────────┴────────────┘
```
**使用 Ant Design/Tailwind 组件:**
```tsx
// 使用 Tailwind + 自定义样式
{kpiData.map((item) => (
{item.value}
{item.label}
{item.trend}
))}
```
#### 3.1.2 最近任务卡片列表
**改什么:** 将 Table 布局改为卡片式列表
**改成什么样子:**
```tsx
{recentTasks.map((task) => (
{/* 任务信息 */}
{task.name}
{task.type} · 模板:{task.template}
{/* 状态标签 */}
{/* 时间 */}
{task.date}
{task.status === 'completed' ? `耗时 ${task.duration}` : '进行中'}
{/* 操作按钮 */}
))}
```
#### 3.1.3 快速入口卡片网格
**改什么:** 新增快速入口区域,使用 4 列 feature-grid
**改成什么样子:**
```tsx
快速入口
{quickEntries.map((entry) => (
navigate(entry.path)}
>
{entry.icon}
{entry.title}
{entry.description}
))}
```
**新增数据:**
```tsx
const quickEntries = [
{ id: 'titles', icon: '📝', iconGradient: 'linear-gradient(135deg, #6366f1, #4f46e5)', title: '标题库', description: '24条标题 · 5个分类', path: '/titles' },
{ id: 'assets', icon: '📦', iconGradient: 'linear-gradient(135deg, #0ea5e9, #0284c7)', title: '素材库', description: '486个素材 · 3个素材库', path: '/assets' },
{ id: 'generate', icon: '✨', iconGradient: 'linear-gradient(135deg, #10b981, #059669)', title: '一键生成', description: '开始创作新视频', path: '/generate' },
{ id: 'products', icon: '🎬', iconGradient: 'linear-gradient(135deg, #f59e0b, #d97706)', title: '成片库', description: '89个成片 · 3个待复核', path: '/products' },
];
```
**优先级:** P2
---
### 3.2 一键生成 (Generate)
**文件路径:** `pages/generate/GeneratePage.tsx`
**路由:** `/generate`
**当前状态:**
- ✅ API 调用逻辑已实现
- ❌ 缺失克隆声音展开区域
- ❌ 缺失时间线预览区域
- ❌ 缺失视频预览区域
- ❌ 缺失重新生成按钮
- ⚠️ 步骤条样式需调整
- ⚠️ 表单卡片式选择需调整
- ⚠️ 左右分栏布局需调整
**UI 差异清单:**
| 差异项 | 当前状态 | 原型状态 | 优先级 |
|--------|----------|----------|--------|
| 克隆声音展开区 | 无 | 展开/收起音频上传区 | P1 |
| 时间线预览 | 无 | 右侧 sticky 预览 | P1 |
| 视频预览区 | 无 | 9:16 竖版预览 | P1 |
| 重新生成按钮 | 无 | 预览区左下角 | P1 |
| 步骤条 | - | 居中 4 步骤 | P2 |
| 选择卡片 | - | 选中带对勾图标 | P2 |
| 左右分栏 | - | 1fr:320px | P2 |
**具体修改指令:**
#### 3.2.1 整体布局改造
**改什么:** 改为左右分栏布局(1fr : 320px)
```tsx
{/* 左侧表单区 */}
{/* ... */}
{/* 右侧预览区 */}
{/* ... */}
```
#### 3.2.2 步骤条组件
**改什么:** 实现居中 4 步骤条
```tsx
{[
{ num: 1, label: '选择模板', active: true },
{ num: 2, label: '选择素材', active: false },
{ num: 3, label: '选择标题', active: false },
{ num: 4, label: '选择配音', active: false },
].map((step, idx) => (
{idx > 0 && →}
))}
```
#### 3.2.3 选择卡片组件
**改什么:** 模板/配音选择使用卡片式,带选中状态和勾选图标
```tsx
{options.map((opt) => (
setSelectedId(opt.id)}
style={{
background: '#fff',
border: `2px solid ${selectedId === opt.id ? 'var(--primary)' : 'var(--line)'}`,
borderRadius: 'var(--radius-md)',
padding: '14px',
cursor: 'pointer',
position: 'relative',
textAlign: 'center',
transition: 'var(--transition-base)',
}}
>
{/* 选中对勾 */}
{selectedId === opt.id && (
✓
)}
{/* 缩略图 */}
{opt.thumb}
{opt.title}
{opt.desc}
))}
```
#### 3.2.4 克隆声音展开区域
**改什么:** 在选择配音的第4步,点击"克隆我的声音"选项后展开音频上传区域
```tsx
{/* 克隆声音选项被选中时显示 */}
{selectedVoice === 'clone' && (
🎤 克隆我的声音
上传你的声音,AI将用你的音色生成配音
{/* 上传区域 */}
🎵
拖拽或点击上传音频文件(MP3/WAV,10秒以上)
支持格式:MP3、WAV · 建议时长:10秒~3分钟
{/* 录音按钮 */}
{/* 提示 */}
💡 建议上传10秒~3分钟的清晰语音,环境安静、语速均匀效果最佳
{/* 已克隆音色选择 */}
或选择已克隆的音色:
{clonedVoices.map((voice) => (
))}
{/* 开始克隆按钮 */}
)}
```
#### 3.2.5 右侧预览区域
**改什么:** 实现 sticky 定位的视频预览、时间线预览
```tsx
{/* 视频预览 */}
{/* 渐变叠加 */}
{/* 播放按钮 */}
▶
{/* 标题预览 */}
{selectedTitle}
{/* 剪辑计划标题 */}
剪辑计划预览
{/* 时间线 */}
{timeline.map((item, idx) => (
{idx + 1}
{item.scene}
{item.time}
))}
{/* 操作按钮 */}
```
**优先级:** P1
---
### 3.3 标题库 (Title Library)
**文件路径:** `pages/titles/TitleLibrary.tsx`
**路由:** `/titles`
**当前状态:**
- ✅ CRUD + 批量导入已实现
- ❌ 缺失左侧分类列表(需两栏布局)
- ❌ 缺失标题卡片网格布局
- ❌ 缺失收藏功能(★/☆切换)
**UI 差异清单:**
| 差异项 | 当前状态 | 原型状态 | 优先级 |
|--------|----------|----------|--------|
| 左侧分类列表 | 无 | 两栏布局左侧 | P1 |
| 标题卡片网格 | Table | 3列卡片网格 | P1 |
| 收藏功能 | 无 | ★/☆切换 | P1 |
| 页面头部 | - | 批量导入 + 新增按钮 | P2 |
**具体修改指令:**
#### 3.3.1 两栏布局
```tsx
{/* 左侧分类列表 */}
{categories.map((cat) => (
setSelectedCategory(cat.id)}
style={{
border: `1px solid ${selectedCategory === cat.id ? 'var(--primary)' : 'var(--line)'}`,
background: selectedCategory === cat.id ? 'var(--primary-soft)' : '#fff',
borderRadius: 'var(--radius-sm)',
padding: '14px',
cursor: 'pointer',
transition: '0.15s ease',
}}
>
{cat.name}
{cat.count} 条
))}
{/* 右侧标题卡片网格 */}
{titles.map((title) => (
))}
```
#### 3.3.2 标题卡片组件
```tsx
interface TitleCardProps {
title: {
id: string;
content: string;
category: string;
usageCount: number;
isFavorited: boolean;
};
}
const TitleCard: React.FC = ({ title }) => {
const [isFavorited, setIsFavorited] = useState(title.isFavorited);
return (
{title.content}
{title.category}
使用 {title.usageCount} 次
{
e.stopPropagation();
setIsFavorited(!isFavorited);
}}
style={{
color: isFavorited ? 'var(--amber)' : 'var(--muted)',
fontSize: '16px',
cursor: 'pointer',
}}
>
{isFavorited ? '★' : '☆'}
);
};
```
**优先级:** P1
---
### 3.4 素材库 (Asset Library)
**文件路径:** `pages/assets/AssetLibrary.tsx`
**路由:** `/assets`
**当前状态:**
- ✅ 上传功能、素材库管理已实现
- ❌ 缺失左侧素材库列表(需两栏布局)
- ⚠️ 素材卡片样式、状态标签、诊断按钮需对齐
**UI 差异清单:**
| 差异项 | 当前状态 | 原型状态 | 优先级 |
|--------|----------|----------|--------|
| 左侧素材库列表 | 无 | 260px 宽列表 | P1 |
| 素材卡片样式 | - | 9:16 竖版缩略图 | P2 |
| 状态标签 | - | ok/warn/bad/info | P2 |
| 诊断按钮 | - | 卡片底部 | P2 |
**具体修改指令:**
#### 3.4.1 两栏布局
```tsx
{/* 左侧素材库列表 */}
{assetLibraries.map((lib) => (
{lib.name}
{lib.count} 个素材 · {lib.size}
))}
{/* 右侧素材网格 */}
{assets.map((asset) => (
))}
```
#### 3.4.2 素材卡片组件
```tsx
const AssetCard: React.FC<{ asset: Asset }> = ({ asset }) => (
{/* 缩略图 */}
{/* 信息 */}
{asset.name}
{asset.duration}
);
```
**优先级:** P1
---
### 3.5 配音库 (Voice Library)
**文件路径:** `pages/voices/VoiceLibrary.tsx`
**路由:** `/voices`
**当前状态:**
- ✅ AI生成、CRUD 功能已实现
- ❌ 缺失左侧配音库分类列表
- ❌ 缺失配音卡片布局(voice-card + voice-wave 波形)
**UI 差异清单:**
| 差异项 | 当前状态 | 原型状态 | 优先级 |
|--------|----------|----------|--------|
| 左侧分类列表 | 无 | 220px 宽 | P1 |
| 配音卡片 | 表格 | voice-card 布局 | P1 |
| 波形可视化 | 无 | 灰色波形条 | P1 |
| 页面头部 | - | 新建配音库+AI生成+上传 | P2 |
**具体修改指令:**
#### 3.5.1 配音库两栏布局
```tsx
{/* 左侧配音库分类 */}
{voiceLibraries.map((lib) => (
{lib.name}
{lib.count} 条配音
))}
{/* 右侧配音卡片列表 */}
{voices.map((voice) => (
))}
```
#### 3.5.2 配音卡片组件
```tsx
const VoiceCard: React.FC<{ voice: Voice }> = ({ voice }) => (
{/* 头像 */}
🎙️
{/* 信息 */}
{voice.name}
{voice.library} · {voice.duration} · {voice.gender}
{/* 波形 */}
{/* 操作 */}
);
```
**优先级:** P1
---
### 3.6 成片库 (Product Library)
**文件路径:** `pages/products/ProductLibrary.tsx`
**路由:** `/products`
**当前状态:**
- ✅ 下载/预览/删除/查重率展示已实现
- ❌ 缺失批量选择功能
- ❌ 缺失批量发布/批量下载按钮
- ❌ 缺失已发布状态角标
**UI 差异清单:**
| 差异项 | 当前状态 | 原型状态 | 优先级 |
|--------|----------|----------|--------|
| 批量选择 | 无 | 复选框 + 全选 | P1 |
| 批量操作按钮 | 无 | 发布 + 下载 | P1 |
| 已发布角标 | 无 | 右上角绿色角标 | P1 |
| 选中效果 | 无 | 边框高亮 + 阴影 | P1 |
**具体修改指令:**
#### 3.6.1 页面头部批量操作
```tsx
```
#### 3.6.2 成片卡片(含批量选择)
```tsx
const ProductCard: React.FC<{ product: Product }> = ({ product }) => {
const [isSelected, setIsSelected] = useState(false);
return (
{/* 选择复选框 */}
setIsSelected(!isSelected)}
style={{
width: '18px',
height: '18px',
accentColor: 'var(--primary)',
opacity: product.isPublished ? 0.4 : 1,
}}
/>
{/* 已发布角标 */}
{product.isPublished && (
已发布
)}
{/* 缩略图 */}
{/* 信息 */}
{product.name}
{product.date}
{product.isPublished ? (
✅ 已发布
) : (
)}
);
};
```
**优先级:** P1
---
### 3.7 任务历史 (Task History)
**文件路径:** `pages/history/TaskHistory.tsx`
**路由:** `/history`
**当前状态:**
- ✅ 状态筛选逻辑、重试功能已实现
- ⚠️ 状态筛选 Tab 样式需调整
- ⚠️ 任务列表需改为卡片布局
**UI 差异清单:**
| 差异项 | 当前状态 | 原型状态 | 优先级 |
|--------|----------|----------|--------|
| 状态 Tab | - | 圆角胶囊样式 | P2 |
| 任务列表 | Table | 卡片式布局 | P2 |
**具体修改指令:**
#### 3.7.1 Tab 切换样式
```tsx
{[
{ key: 'all', label: '全部', count: 156 },
{ key: 'running', label: '进行中', count: 3 },
{ key: 'done', label: '已完成', count: 150 },
{ key: 'failed', label: '失败', count: 3 },
].map((tab) => (
setActiveTab(tab.key)}
style={{
padding: '8px 16px',
borderRadius: 'var(--radius-sm)',
cursor: 'pointer',
fontWeight: 600,
fontSize: '14px',
color: activeTab === tab.key ? 'var(--primary)' : 'var(--muted)',
background: activeTab === tab.key ? 'var(--primary-soft)' : 'transparent',
transition: '0.15s ease',
}}
>
{tab.label} ({tab.count})
))}
```
#### 3.7.2 任务卡片列表
```tsx
{tasks.map((task) => (
{/* 任务信息 */}
{task.name}
{task.type} · 模板:{task.template}
{/* 状态 */}
{/* 时间 */}
{task.date}
{task.status === 'failed' ? '失败' : task.status === 'running' ? '进行中' : `耗时 ${task.duration}`}
{/* 操作 */}
))}
```
**优先级:** P2
---
### 3.8 模板库 (Template Library)
**文件路径:** `pages/templates/TemplateLibrary.tsx`
**路由:** `/templates`
**当前状态:**
- ✅ 搜索/筛选/收藏逻辑已实现
- ⚠️ 分类筛选改为按钮组
- ⚠️ 卡片 4 列布局
- ⚠️ 创建模板按钮位置
**UI 差异清单:**
| 差异项 | 当前状态 | 原型状态 | 优先级 |
|--------|----------|----------|--------|
| 分类筛选 | 下拉框 | 胶囊按钮组 | P2 |
| 模板卡片 | - | 4列网格 | P2 |
| 卡片样式 | - | 16:9 缩略图 | P2 |
| 页面头部 | - | 标题+创建模板按钮 | P2 |
**具体修改指令:**
#### 3.8.1 工具栏(搜索 + 分类按钮组)
```tsx
{/* 搜索框 */}
🔍
{/* 分类按钮组 */}
{['全部', '口播', '种草', '产品', '品牌'].map((cat) => (
))}
```
#### 3.8.2 模板卡片
```tsx
{templates.map((tmpl) => (
{/* 缩略图 */}
{/* 信息 */}
{tmpl.name}
使用 {tmpl.usageCount} 次
))}
```
**优先级:** P2
---
### 3.9 订阅管理 (Subscription)
**文件路径:** `pages/subscription/SubscriptionPage.tsx`
**路由:** `/subscription`
**当前状态:**
- ✅ 订阅逻辑已实现
- ⚠️ 定价卡片样式、特性列表、价格展示需对齐
- ⚠️ 当前套餐信息卡片样式
- ❌ 账单记录表格
**UI 差异清单:**
| 差异项 | 当前状态 | 原型状态 | 优先级 |
|--------|----------|----------|--------|
| 定价卡片样式 | - | 原型卡片样式 | P2 |
| 特性列表 | - | 带勾选列表 | P2 |
| 当前套餐卡片 | - | 标签+按钮组 | P2 |
| 账单记录表格 | 无 | 完整表格 | P1 |
**具体修改指令:**
#### 3.9.1 账单记录表格
```tsx
账单记录
{['订单号', '套餐', '金额', '支付方式', '时间', '状态'].map((h) => (
| {h} |
))}
{bills.map((bill) => (
|
{bill.orderNo}
|
{bill.plan}
|
¥{bill.amount.toLocaleString()}
|
{bill.paymentMethod}
|
{bill.date}
|
|
))}
```
**优先级:** P1(账单表格)、P2(其他样式)
---
### 3.10 剪辑计划编辑器 (Editing Planner)
**文件路径:** `pages/editing-planner/EditingPlanner.tsx`
**路由:** `/editing-planner`
**当前状态:**
- ✅ 核心功能已实现
- ⚠️ 模式切换按钮样式
- ⚠️ 面板样式对齐
**优先级:** P2
---
## 4. 新增页面开发指令
### 4.1 查重检测页面组
**目录:** `pages/duplication/`
**涉及文件:**
- `DuplicationUpload.tsx` - 上传页面
- `DuplicationResults.tsx` - 结果页面
- `DuplicationDetail.tsx` - 详情页面
**路由配置:**
```tsx
{
path: '/duplication',
element: ,
children: [
{ path: '', element: },
{ path: 'results/:id', element: },
{ path: 'detail/:id', element: },
]
}
```
#### 4.1.1 DuplicationUpload 页面规格
**页面布局:** 左右分栏(1fr : 1fr)
```tsx
// pages/duplication/DuplicationUpload.tsx
const DuplicationUpload: React.FC = () => {
return (
{/* 左侧上传区 */}
📤
拖拽上传视频文件
或点击选择文件上传
支持格式:MP4 / AVI / MOV / MKV,单文件 ≤ 2GB
{/* 右侧结果区 */}
);
};
```
#### 4.1.2 DuplicationResults 页面规格
```tsx
// pages/duplication/DuplicationResults.tsx
const DuplicationResults: React.FC = () => {
const { id } = useParams();
return (
📥 下载报告
}
/>
{/* 相似度圆环 */}
{/* 匹配列表 */}
匹配的视频片段
{matches.map((match) => (
▶
{match.videoName}
相似片段:{match.segment}
{match.similarity}%
))}
);
};
```
**优先级:** P1
---
### 4.2 我的音色 (Voice Clone)
**目录:** `pages/voice-clone/`
**文件:** `VoiceClone.tsx`
**路由:** `/voice-clone`
#### 4.2.1 页面规格
```tsx
// pages/voice-clone/VoiceClone.tsx
const VoiceClone: React.FC = () => {
return (
✨ 克隆新音色
}
/>
{/* 音色卡片网格 */}
{clonedVoices.map((voice) => (
))}
{/* 空状态 */}
{clonedVoices.length === 0 && (
🎤
还没有克隆音色
上传你的声音,AI将克隆你的专属音色
)}
);
};
```
#### 4.2.2 音色克隆卡片组件
```tsx
interface VoiceCloneCardProps {
voice: {
id: string;
name: string;
duration: string;
createdAt: string;
status: 'ready' | 'processing';
};
}
const VoiceCloneCard: React.FC = ({ voice }) => (
{/* 操作按钮 */}
{/* 头部信息 */}
{/* 元信息 */}
🎵 时长:{voice.duration}
📅 创建于:{voice.createdAt}
{/* 操作区 */}
);
```
#### 4.2.3 克隆音色 Modal
```tsx
// components/modals/CloneVoiceModal.tsx
const CloneVoiceModal: React.FC<{ visible: boolean; onClose: () => void }> = ({
visible,
onClose,
}) => {
const [step, setStep] = useState<'input' | 'uploading' | 'success'>('input');
return (
{/* 音色名称 */}
{/* 上传区域 */}
🎵
拖拽音频文件到此处
支持 MP3、WAV 格式
{/* 或分隔 */}
{/* 录音 */}
{/* 提示 */}
💡 建议上传10秒~3分钟的清晰语音,环境安静、语速均匀效果最佳
{/* 按钮 */}
);
};
```
**优先级:** P1
---
### 4.3 账号管理 (Accounts)
**目录:** `pages/accounts/`
**文件:** `Accounts.tsx`
**路由:** `/accounts`
#### 4.3.1 页面规格
```tsx
// pages/accounts/Accounts.tsx
const Accounts: React.FC = () => {
const platforms = [
{ id: 'douyin', name: '抖音', icon: '📱', gradient: 'linear-gradient(135deg, #fe2c55, #25f4ee)' },
{ id: 'kuaishou', name: '快手', icon: '🎬', gradient: 'linear-gradient(135deg, #ff4906, #ffba00)' },
{ id: 'xiaohongshu', name: '小红书', icon: '📕', gradient: 'linear-gradient(135deg, #ff2442, #ff6b6b)' },
{ id: 'wechat', name: '微信视频号', icon: '💬', gradient: 'linear-gradient(135deg, #07c160, #4cd964)' },
];
return (
{platforms.map((platform) => (
))}
{/* 底部统计 */}
📊
已绑定 4 个账号 /
支持 4 个平台
);
};
```
#### 4.3.2 平台账号卡片组件
```tsx
interface PlatformCardProps {
platform: {
id: string;
name: string;
subName: string;
icon: string;
gradient: string;
};
}
const PlatformCard: React.FC = ({ platform }) => {
const accounts = useAccounts(platform.id);
return (
{/* 平台头部 */}
{platform.icon}
{platform.name}
{platform.subName}
{/* 账号列表 */}
{accounts.length > 0 ? (
accounts.map((account) => (
{account.avatar || platform.icon}
))
) : (
)}
{/* 绑定按钮 */}
);
};
```
**优先级:** P1
---
### 4.4 首页 (Landing Page)
**目录:** `pages/home/`
**文件:** `HomePage.tsx`
**路由:** `/`
#### 4.4.1 页面规格
```tsx
// pages/home/HomePage.tsx
const HomePage: React.FC = () => {
return (
{/* Hero 区域 */}
{/* 功能卡片 */}
{/* 工作流程 */}
{/* 定价预览 */}
{/* CTA */}
);
};
```
#### 4.4.2 Hero 区域组件
```tsx
const HeroSection: React.FC = () => (
🦐 小虾智剪 · AI智能视频创作平台
上传素材,AI自动剪辑
一键生成短视频
面向口播、产品介绍、直播切片和企业宣传的智能视频创作平台。
);
```
**优先级:** P3
---
## 5. 基础组件封装规范
### 5.1 StatusPill 状态标签
```tsx
// components/common/StatusPill.tsx
type StatusType = 'ok' | 'warn' | 'bad' | 'info' | 'muted';
interface StatusPillProps {
status: StatusType;
label?: string;
}
const statusConfig: Record = {
ok: { bg: '#dcfce7', color: '#166534', defaultLabel: '已通过' },
warn: { bg: '#ffedd5', color: '#c2410c', defaultLabel: '待复核' },
bad: { bg: '#ffe4e6', color: '#be123c', defaultLabel: '高风险' },
info: { bg: '#dbeafe', color: '#1d4ed8', defaultLabel: '视频' },
muted: { bg: '#f1f5f9', color: '#64748b', defaultLabel: '品牌' },
};
export const StatusPill: React.FC = ({ status, label }) => {
const config = statusConfig[status];
return (
{label || config.defaultLabel}
);
};
```
### 5.2 CategoryPill 分类标签
```tsx
// components/common/CategoryPill.tsx
const categoryConfig: Record = {
口播: { bg: '#dbeafe', color: '#1d4ed8' },
种草: { bg: '#dcfce7', color: '#166534' },
产品: { bg: '#ffedd5', color: '#c2410c' },
品牌: { bg: '#f1f5f9', color: '#64748b' },
直播: { bg: '#fef3c7', color: '#92400e' },
教程: { bg: '#e0e7ff', color: '#4338ca' },
};
interface CategoryPillProps {
category: string;
}
export const CategoryPill: React.FC = ({ category }) => {
const config = categoryConfig[category] || categoryConfig['口播'];
return (
{category}
);
};
```
### 5.3 IconBox 图标盒子
```tsx
// components/common/IconBox.tsx
interface IconBoxProps {
icon: string;
gradient?: string;
size?: number;
}
export const IconBox: React.FC = ({
icon,
gradient = 'linear-gradient(135deg, #6366f1, #4f46e5)',
size = 44,
}) => (
{icon}
);
```
### 5.4 UploadZone 上传区域
```tsx
// components/common/UploadZone.tsx
interface UploadZoneProps {
accept?: string;
maxSize?: number; // MB
onUpload: (file: File) => void;
}
export const UploadZone: React.FC = ({
accept = '*',
maxSize = 2048,
onUpload,
}) => {
const [isDragging, setIsDragging] = useState(false);
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
setIsDragging(false);
const file = e.dataTransfer.files[0];
if (file && file.size <= maxSize * 1024 * 1024) {
onUpload(file);
}
};
return (
{ e.preventDefault(); setIsDragging(true); }}
onDragLeave={() => setIsDragging(false)}
onDrop={handleDrop}
style={{
border: `2px dashed ${isDragging ? 'var(--primary-light)' : 'var(--line)'}`,
borderRadius: 'var(--radius-lg)',
padding: '60px',
textAlign: 'center',
background: isDragging ? 'var(--primary-soft)' : 'linear-gradient(180deg, #fff, #f8fafc)',
transition: 'var(--transition-base)',
cursor: 'pointer',
}}
>
拖拽上传文件
或点击选择文件
);
};
```
### 5.5 EmptyState 空状态
```tsx
// components/common/EmptyState.tsx
interface EmptyStateProps {
icon?: string;
title: string;
description?: string;
action?: {
label: string;
onClick: () => void;
type?: 'primary' | 'ghost';
};
}
export const EmptyState: React.FC = ({
icon = '📭',
title,
description,
action,
}) => (
{title}
{description && (
{description}
)}
{action && (
)}
);
```
---
## 6. 执行顺序建议
### Phase 1: 基础对齐(5-7 人天)
| 序号 | 任务 | 文件 | 优先级 | 预估人天 |
|------|------|------|--------|----------|
| 1.1 | 设计系统 CSS 变量统一 | `global.css` | P0 | 0.5 |
| 1.2 | 主布局组件 AppLayout | `components/layout/AppLayout.tsx` | P0 | 1 |
| 1.3 | 侧边栏组件 Sidebar | `components/layout/Sidebar.tsx` | P0 | 1 |
| 1.4 | 页面头部组件 PageHead | `components/common/PageHead.tsx` | P0 | 0.5 |
| 1.5 | 基础组件封装 | `components/common/*` | P1 | 2 |
### Phase 2: 核心页面改造(8-10 人天)
| 序号 | 任务 | 文件 | 优先级 | 预估人天 |
|------|------|------|--------|----------|
| 2.1 | 一键生成页面 | `pages/generate/GeneratePage.tsx` | P1 | 2 |
| 2.2 | 素材库页面 | `pages/assets/AssetLibrary.tsx` | P1 | 1.5 |
| 2.3 | 配音库页面 | `pages/voices/VoiceLibrary.tsx` | P1 | 1.5 |
| 2.4 | 标题库页面 | `pages/titles/TitleLibrary.tsx` | P1 | 1.5 |
| 2.5 | 成片库页面 | `pages/products/ProductLibrary.tsx` | P1 | 2 |
### Phase 3: 次要页面改造(5-7 人天)
| 序号 | 任务 | 文件 | 优先级 | 预估人天 |
|------|------|------|--------|----------|
| 3.1 | 控制台页面 | `pages/dashboard/Dashboard.tsx` | P2 | 1.5 |
| 3.2 | 模板库页面 | `pages/templates/TemplateLibrary.tsx` | P2 | 1 |
| 3.3 | 任务历史页面 | `pages/history/TaskHistory.tsx` | P2 | 1 |
| 3.4 | 订阅管理页面 | `pages/subscription/SubscriptionPage.tsx` | P2 | 1.5 |
| 3.5 | 剪辑计划编辑器 | `pages/editing-planner/EditingPlanner.tsx` | P2 | 1 |
### Phase 4: 新增页面(3-5 人天)
| 序号 | 任务 | 文件 | 优先级 | 预估人天 |
|------|------|------|--------|----------|
| 4.1 | 查重检测上传页 | `pages/duplication/DuplicationUpload.tsx` | P1 | 0.5 |
| 4.2 | 查重检测结果页 | `pages/duplication/DuplicationResults.tsx` | P1 | 0.5 |
| 4.3 | 查重检测详情页 | `pages/duplication/DuplicationDetail.tsx` | P1 | 0.5 |
| 4.4 | 我的音色页面 | `pages/voice-clone/VoiceClone.tsx` | P1 | 1 |
| 4.5 | 音色克隆 Modal | `components/modals/CloneVoiceModal.tsx` | P1 | 1 |
| 4.6 | 账号管理页面 | `pages/accounts/Accounts.tsx` | P1 | 1 |
| 4.7 | 首页落地页 | `pages/home/HomePage.tsx` | P3 | 1 |
---
## 附录
### A. 路由配置参考
```tsx
// router/index.tsx
const router = createBrowserRouter([
// 公开页面
{ path: '/', element: },
{ path: '/login', element: },
{ path: '/subscription', element: },
// 受保护页面(需要登录)
{
path: '/app',
element: ,
children: [
{ index: true, element: },
{ path: 'dashboard', element: },
{ path: 'titles', element: },
{ path: 'assets', element: },
{ path: 'voices', element: },
{ path: 'templates', element: },
{ path: 'generate', element: },
{ path: 'products', element: },
{ path: 'history', element: },
{ path: 'editing-planner', element: },
{ path: 'subscription', element: },
{ path: 'duplication/*', element: },
{ path: 'voice-clone', element: },
{ path: 'accounts', element: },
],
},
]);
```
### B. 样式文件结构
```
src/
├── styles/
│ ├── global.css # CSS 变量、reset、通用样式
│ ├── components.css # 组件样式(可选)
│ └── pages/
│ ├── dashboard.css
│ ├── generate.css
│ └── ...
```
### C. 注意事项
1. **不删除现有功能**:所有已有的 API 调用逻辑、数据获取/提交逻辑必须保持不变
2. **不改变业务逻辑**:只修改 UI 展示层
3. **保持路由一致**:路由配置必须与原型保持一致
4. **响应式适配**:参考原型中的 `@media` 断点进行响应式处理
---
> 文档版本:V21.1
> 最后更新:2024年
---
> 本内容由 Coze AI 生成,请遵循相关法律法规及《人工智能生成合成内容标识办法》使用与传播。