88 KiB
Jointo(jointo)产品需求开发文档(完整版)
目标:用 React 前端完整还原 Figma Make 设计稿的所有 UI 元素、交互逻辑和功能细节。
文档版本:v2.0 - 完整详细版
项目域名:https://www.jointo.ai
目录
1. 产品概述
1.1 产品定位
Jointo是一个面向视频创作者、运营人员和营销团队的 AI 驱动视频制作平台。提供从分镜策划、素材管理、视频剪辑到音效/字幕/配音的一体化工作流程。
1.2 核心价值
- 全流程覆盖:分镜 → 资源 → 视频 → 音效 → 字幕 → 配音 → 导出
- AI 能力集成:大量使用 AI 生成和辅助功能,降低制作门槛
- 协作支持:支持多人协同编辑和项目管理
- 实时预览:所见即所得的预览体验
1.3 产品名称
- 中文:Jointo
- 英文/文件标识:jointo
- 域名:https://www.jointo.ai
2. 目标与范围
2.1 本期(MVP)目标
2.1.1 UI 还原
- ✅ 完整还原所有页面布局和视觉元素
- ✅ 还原所有按钮、图标、文本内容
- ✅ 还原所有颜色、字体、间距、阴影等视觉样式
- ✅ 还原所有交互状态(hover、active、disabled、selected)
2.1.2 功能实现
- ✅ 所有前端交互逻辑(点击、切换、选择、输入)
- ✅ 数据联动(项目切换、分镜选择、资源关联)
- ✅ 状态管理(当前项目、选中分镜、时间轴位置等)
- ✅ AI 功能入口(统一弹窗/抽屉,mock 数据返回)
2.1.3 交互体验
- ✅ 流畅的页面切换和状态变化
- ✅ 合理的加载状态和反馈提示
- ✅ 新手引导流程
- ✅ 响应式布局适配
2.2 不在本期范围
- ❌ 真实的 AI 推理和生成(使用 mock 数据)
- ❌ 真实的视频渲染和播放(使用占位图)
- ❌ 真实的视频导出功能(占位按钮)
- ❌ 完整的账号体系和权限系统(前端 mock)
- ❌ 后端 API 对接(预留接口,使用 mock)
3. 用户与使用场景
3.1 用户角色
-
视频创作者/编辑
- 使用分镜功能规划视频结构
- 使用 AI 生成视频素材
- 在时间轴上编排视频片段
- 添加音效、字幕、配音
-
运营/营销人员
- 快速制作广告片、宣传片
- 使用 AI 工具提升制作效率
- 管理多个项目
-
协作者
- 查看和编辑协同项目
- 参与项目讨论和备注
3.2 典型使用流程
-
创建/选择项目
- 点击「新建项目」或从列表选择现有项目
- 项目自动加载,显示分镜列表
-
编辑分镜
- 查看/编辑分镜描述文案
- 为每个分镜配置资源(人物、场景、道具)
- 插入新分镜或删除分镜
-
生成和管理素材
- 使用 AI 生成视频素材
- 从素材库选择已有素材
- 在时间轴上排列素材
-
完善视频
- 添加音效、字幕、配音
- 在预览窗口查看效果
- 调整时间轴上的素材位置
-
导出项目
- 点击「导出项目」(MVP 为占位功能)
4. 整体布局架构
4.1 页面结构(单页应用 SPA)
┌─────────────────────────────────────────────────────────────┐
│ 应用顶部栏(TopBar) │
│ Logo | 新建/导出/AI工具箱 | 权限/协作者/通知/备注/设置 │
├─────────────────────────────────────────────────────────────┤
│ ┌──────────┬──────────────────────────────────┬──────────┐ │
│ │ │ 中间区域(CenterArea) │ │ │
│ │ 左侧栏 │ ┌──────┬──────────────┬────────┐ │ 右侧栏 │ │
│ │ │ │分镜 │ 预览窗口 │ AI │ │ │ │
│ │ 项目/ │ │列表 │ │ 输入区 │ │ 资源/ │ │
│ │ 素材库 │ │ │ 1920x1080 │ │ │ 视频/ │ │
│ │ │ │ │ 主预览图 │ │ │ 音效/ │ │
│ │ │ │ │ 预览1/2 │ │ │ 字幕/ │ │
│ │ │ │ │ │ │ │ 配音 │ │
│ │ │ │ │ 播放控制 │ │ │ │ │
│ │ │ │ │ 时间码 │ │ │ │ │
│ └──────────┴──────────────────────────────────┴──────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 时间轴(TimelinePanel) │
│ 时间轴 | 缩小 | 100% | 放大 │
│ 0s───────────────────────────────────────────────60s │
│ 分镜/资源/视频/音效/字幕/配音轨道 │
│ 播放控制 | 时间码 | 缩放 │
│ │
│ 新手引导浮层(OnboardingOverlay) │
│ Cookie 提示(Consent Manager) │
└─────────────────────────────────────────────────────────────┘
4.2 React 组件层级结构
App
├── Layout
│ ├── TopBar
│ │ ├── Logo
│ │ ├── ProjectActions (新建/导出/AI工具箱)
│ │ └── RightActions (权限/协作者/通知/备注/设置)
│ ├── MainContent
│ │ ├── LeftSidebar
│ │ │ ├── Tabs (项目/素材库)
│ │ │ ├── ProjectList
│ │ │ │ ├── MyProjects
│ │ │ │ └── CollabProjects
│ │ │ └── LibraryPanel
│ │ │ ├── SearchBox (搜索项目文件)
│ │ │ ├── LibraryTabs (角色/场景/道具/实拍)
│ │ │ └── LibraryContentList (内容列表)
│ │ ├── CenterArea
│ │ │ ├── StoryboardPanel (分镜列表)
│ │ │ │ ├── StoryboardHeader (分镜描述 | 调整按钮)
│ │ │ │ ├── StoryboardList
│ │ │ │ │ └── StoryboardItem (×6)
│ │ │ │ └── InsertStoryboardButton (×6)
│ │ │ ├── PreviewPanel (预览窗口)
│ │ │ │ ├── PreviewHeader (预览窗口 | 适应/填充)
│ │ │ │ ├── PreviewContent (内容预览)
│ │ │ │ │ ├── MainPreviewImage
│ │ │ │ │ └── ThumbnailImages (预览1/预览2)
│ │ │ │ ├── ResolutionDisplay (1920 x 1080)
│ │ │ │ ├── ZoomControls (缩小 | 100% | 放大)
│ │ │ │ └── StatusBar (未选择素材)
│ │ │ ├── PlaybackControls
│ │ │ │ ├── PrevFrameButton
│ │ │ │ ├── PlayButton
│ │ │ │ ├── NextFrameButton
│ │ │ │ └── TimecodeDisplay (00:00:00:00)
│ │ │ └── AIPromptPanel (AI 输入区)
│ │ │ ├── AITabs (图片提示词/角度变换/镜头调整)
│ │ │ ├── PromptExamples (示例文本)
│ │ │ ├── PromptInput (输入框)
│ │ │ ├── ActionButtons (对话改图/发送)
│ │ │ └── ModelSelector (当前模型: 智能选择)
│ │ └── RightSidebar
│ │ ├── ResourceTabs (资源/视频/音效/字幕/配音)
│ │ ├── ResourcePanel
│ │ │ ├── AIGenerateButton (AI生成资源素材)
│ │ │ └── StoryboardGroups (×6,每组包含人物/场景/道具/视频)
│ │ ├── VideoPanel
│ │ │ ├── AIGenerateButton (AI生成视频素材)
│ │ │ └── VideoList (所有视频项)
│ │ ├── SoundEffectPanel
│ │ │ ├── AIGenerateButton (AI生成音效素材)
│ │ │ └── SoundList (所有音效项)
│ │ ├── SubtitlePanel
│ │ │ ├── AIGenerateButton (AI生成字幕素材)
│ │ │ └── SubtitleList (所有字幕项)
│ │ └── VoiceoverPanel
│ │ ├── AIGenerateButton (AI生成配音素材)
│ │ └── VoiceoverList (所有配音项)
│ └── TimelinePanel (时间轴)
│ ├── TimelineHeader (时间轴 | 缩小 | 100% | 放大)
│ ├── TimeScale (0s-60s)
│ ├── TimelineTracks
│ │ ├── StoryboardTrack (分镜轨道)
│ │ ├── ResourceTrack (资源轨道)
│ │ ├── VideoTrack (视频轨道)
│ │ ├── SoundTrack (音效轨道)
│ │ ├── SubtitleTrack (字幕轨道)
│ │ └── VoiceoverTrack (配音轨道)
│ └── AddTrackButton (添加轨道)
└── OnboardingOverlay (新手引导浮层)
├── StepIndicator (步骤 1 / 4)
├── StepTitle (项目与素材管理)
├── StepDescription
├── StepJumpButtons (跳转到步骤 1/2/3/4)
└── NavigationButtons (上一步/跳过引导/下一步)
5. 详细功能需求
5.1 应用顶部栏(TopBar)
5.3.1 左侧区域
Logo 和标题
- Logo 图标:可点击的图标
- 标题文本:
"Jointo" - 交互:点击 Logo 可返回项目列表或刷新当前项目(可选功能)
项目操作按钮组
-
新建项目按钮
- 图标 + 文本:
"新建项目" - 点击行为:打开「新建项目」弹窗
- 输入项目名称
- 输入项目描述(可选)
- 选择项目类型(我的项目/协同项目)
- 确认后创建项目,自动选中新项目
- 状态:正常状态,hover 有反馈
- 图标 + 文本:
-
导出项目按钮
- 图标 + 文本:
"导出项目" - 点击行为:MVP 阶段显示提示「导出功能开发中」或下载项目 JSON 数据
- 后续:调用导出 API,生成视频文件
- 图标 + 文本:
-
AI工具箱按钮
- 图标 + 文本:
"AI工具箱" - 点击行为:打开侧边抽屉或 Modal,展示所有 AI 功能入口
- 图片提示词
- 角度变换
- 镜头调整
- AI生成分镜素材
- AI生成资源素材
- AI生成视频素材
- AI生成音效素材
- AI生成字幕素材
- AI生成配音素材
- 图标 + 文本:
5.3.2 右侧区域
权限管理按钮
- 图标按钮(无文本)
- 点击行为:打开权限管理弹窗
- 显示项目成员列表(mock 数据)
- 显示成员角色(所有者、编辑者、查看者)
- 添加成员功能(MVP 为占位)
- 移除成员功能(MVP 为占位)
协作者头像组按钮
- 显示多个用户头像
- 内容:
"ZS""LS""WW""+1" - 点击行为:显示协作者详情弹窗
- 显示所有协作者列表
- 显示每个协作者的角色和权限
- Hover 效果:显示协作者名称提示
通知按钮
- 图标按钮(无文本)
- 点击行为:打开通知列表
- 显示未读通知数量(红点标识)
- 通知列表(mock 数据)
- 标记已读功能
项目备注按钮
- 图标按钮(无文本)
- 点击行为:打开项目备注面板
- 显示/编辑项目备注(富文本或纯文本)
- 保存备注功能
- 显示备注历史(可选)
设置按钮
- 图标按钮(无文本)
- 点击行为:打开项目设置弹窗
- 项目基本信息(名称、描述)
- 视频设置(分辨率、帧率、时长)
- 导出设置(格式、质量)
- 其他设置选项
5.4 左侧栏(LeftSidebar)
5.4.1 Tab 切换
Tab 按钮组
- 项目 Tab(默认选中)
- 素材库 Tab
- 样式:选中状态有视觉区分(下划线/背景色/文字颜色)
- 交互:点击切换显示对应内容区域
5.4.2 项目列表(项目 Tab 下)
分组结构
-
我的项目分组
- 分组标题:
"我的项目" - 分组图标:文件夹图标
- 可折叠/展开(默认展开)
- 分组标题:
-
协同项目分组
- 分组标题:
"协同项目" - 分组图标:协作图标
- 可折叠/展开(默认展开)
- 分组标题:
项目卡片列表
每个项目卡片包含:
- 缩略图:项目封面图(左侧)
- 项目名称:文本显示
- 更多操作按钮:hover 显示,点击显示菜单(编辑、删除、复制等)
示例项目(按设计稿):
- 西游记之大圣归来
- 琅琊榜
- 流浪地球
- 蒙牛新年好礼2025广告片
- 十三邀
交互行为:
- 点击项目卡片:切换当前项目
- 更新中间区域的分镜列表
- 更新右侧的资源/视频/音效/字幕/配音数据
- 更新时间轴内容
- 项目卡片显示选中状态(高亮/边框)
- Hover 项目卡片:显示预览信息或操作按钮
- 点击更多按钮:显示操作菜单(编辑、删除、复制、分享等)
5.4.3 素材库(素材库 Tab 下)
搜索功能:
- 搜索框:位于素材库顶部
- 占位文本:
"搜索项目文件..." - 支持输入项目名称进行搜索
- 实时搜索,显示匹配的项目文件列表
- 占位文本:
Tab 分类:
- 搜索框下方显示 4 个 Tab 按钮:
- 角色 Tab
- 场景 Tab
- 道具 Tab
- 实拍 Tab
- Tab 样式:选中状态有视觉区分(下划线/背景色/文字颜色)
- Tab 互斥,只能选中一个
内容展示:
- 点击 Tab 后,显示对应类型的项目内容列表
- 每个内容项显示:
- 缩略图:素材预览图
- 名称:素材名称
- 来源项目:显示所属项目名称(可选)
- 支持滚动浏览
- 点击内容项:可添加到当前分镜(根据类型添加到对应资源分类)
交互行为:
- 搜索时:实时过滤项目文件
- 切换 Tab:更新显示对应类型的内容
- 点击内容项:添加到当前分镜的资源列表
5.5 中间区域 - 分镜列表(StoryboardPanel)
5.5.1 分镜描述区域头部
左侧标题
- 图标 + 文本:
"分镜描述"
右侧调整按钮
- 图标 + 文本:
"调整" - 点击行为:打开分镜调整面板
- 批量编辑分镜
- 调整分镜顺序
- 分镜设置(时长、转场等)
5.5.2 分镜列表
分镜项结构(每个分镜包含):
- 分镜缩略图:左侧显示
- 分镜标题:
"分镜1"~"分镜6"(Heading 5 级别) - 更多按钮:右侧显示,点击显示菜单(编辑、删除、复制、插入等)
- 分镜描述文本:段落文本,显示详细场景描述
分镜内容(按设计稿):
分镜1
- 标题:
"分镜1" - 描述:
"清晨的阳光照进窗户,主人公在床上慢慢睁开眼睛,开始新的一天。窗外传来鸟鸣声,窗帘随着微风轻轻飘动。"
分镜2
- 标题:
"分镜2" - 描述:
"主人公来到厨房,打开冰箱,拿出牛奶和面包,开始准备早餐。厨房里充满了温馨的气息,阳光从窗户洒进来。"
分镜3
- 标题:
"分镜3" - 描述:
"坐在餐桌前,主人公一边享用早餐,一边浏览手机上的新闻。桌上摆放着精致的餐具和一杯热气腾腾的咖啡。"
分镜4
- 标题:
"分镜4" - 描述:
"吃完早餐,主人公整理好餐具,将碗筷放进洗碗池,打开水龙头清洗。流水声和餐具碰撞的声音交织在一起。"
分镜5
- 标题:
"分镜5" - 描述:
"回到房间,主人公打开衣柜,精心挑选今天要穿的衣服,换好衣服后对着镜子整理仪容。镜子里映出满意的笑容。"
分镜6
- 标题:
"分镜6" - 描述:
"拿起背包和钥匙,主人公走向门口,回头看了一眼房间,打开门走出去,开始新一天的旅程。门在身后轻轻关上。"
5.5.3 插入新分镜按钮
位置:
- 在每个分镜项下方
- 最后一个分镜项下方也有
样式:
- 图标 + 文本:
"插入新分镜" - 按钮样式:次要按钮或链接样式
交互:
- 点击按钮:在当前分镜下方插入新分镜
- 新分镜标题:
"分镜X"(X 为下一个序号) - 新分镜描述:空文本,可编辑
- 新分镜资源:空资源列表
- 自动选中新插入的分镜
- 新分镜标题:
5.5.4 交互行为
分镜选择:
- 点击分镜项:选中该分镜
- 分镜项显示选中状态(高亮/边框/背景色)
- 预览窗口更新为当前分镜的预览图
- 右侧资源面板高亮当前分镜的资源
- 时间轴上高亮当前分镜的时间块
分镜编辑:
-
点击分镜标题:进入编辑模式(inline edit)
- 标题变为可编辑输入框
- 失去焦点或按 Enter 保存
- 按 Esc 取消编辑
-
点击分镜描述:进入编辑模式
- 描述文本变为可编辑文本域
- 支持多行编辑
- 保存/取消按钮或自动保存
分镜删除:
- 点击更多按钮 → 删除:显示确认对话框
- 确认后删除分镜
- 更新分镜序号
- 如果删除的是当前选中分镜,自动选中下一个分镜
分镜复制:
- 点击更多按钮 → 复制:复制当前分镜
- 创建新分镜,内容与原分镜相同
- 标题自动添加"(副本)"后缀
- 插入到当前分镜下方
滚动行为:
- 分镜列表可垂直滚动
- 选中分镜时,自动滚动到可视区域(如果不在视野内)
5.6 中间区域 - 预览窗口(PreviewPanel)
5.6.1 预览窗口头部
左侧标题
- 图标 + 文本:
"预览窗口"
右侧视图模式切换
-
适应窗口按钮
- 图标 + 文本:
"适应窗口" - 选中状态:高亮显示
- 点击行为:切换预览图适应窗口大小(保持宽高比)
- 图标 + 文本:
-
填充窗口按钮
- 图标 + 文本:
"填充窗口" - 选中状态:高亮显示
- 点击行为:切换预览图填充整个窗口(可能裁剪)
- 图标 + 文本:
-
两个按钮互斥,只能选中一个
5.6.2 内容预览区域
主预览图
- 显示当前选中分镜的主预览图
- 尺寸标识:
"1920 x 1080"(显示在预览图下方) - 支持点击放大查看(可选功能)
缩略图组
- 预览1:小缩略图
- 预览2:小缩略图
- 点击缩略图:切换为主预览图
- Hover 缩略图:显示预览信息
5.6.3 缩放控制
控制按钮组
- 缩小按钮:图标按钮
- 百分比显示:
"100%"(文本显示) - 放大按钮:图标按钮
交互行为:
- 点击缩小:缩放比例减小(如 100% → 75% → 50% → 25%)
- 点击放大:缩放比例增大(如 25% → 50% → 75% → 100%)
- 缩放范围:25% - 200%(可配置)
- 缩放步进:25%(可配置)
- 缩放影响:预览图大小和清晰度(前端 CSS transform)
5.6.4 状态栏
状态文本:
- 未选择素材时:
"未选择素材" - 选择素材时:显示素材信息(名称、类型等)
5.7 中间区域 - 播放控制(PlaybackControls)
5.7.1 控制按钮
上一帧按钮
- 图标按钮
- 点击行为:播放位置向前移动一帧
- 更新时间码显示
播放按钮
- 图标按钮(播放/暂停图标切换)
- 点击行为:
- 当前为暂停状态:开始播放,图标变为暂停
- 当前为播放状态:暂停播放,图标变为播放
- MVP 阶段:仅更新状态,不真实播放视频
下一帧按钮
- 图标按钮
- 点击行为:播放位置向后移动一帧
- 更新时间码显示
5.7.2 时间码显示
显示格式:"00:00:00:00"
- 格式:
HH:MM:SS:FF(时:分:秒:帧) - 实时更新:播放时自动更新,手动操作时立即更新
- 可编辑:点击时间码可手动输入(可选功能)
5.7.3 状态显示
缩放信息:
- 文本:
"缩放: 100%" - 与预览窗口的缩放控制联动
素材状态:
- 文本:
"未选择素材"或显示当前选中素材信息
5.8 中间区域 - AI 输入区(AIPromptPanel)
5.8.1 AI 功能 Tab
Tab 按钮组:
-
图片提示词 Tab
- 图标 + 文本:
"图片提示词" - 选中状态:高亮显示
- 图标 + 文本:
-
角度变换 Tab
- 图标 + 文本:
"角度变换" - 选中状态:高亮显示
- 图标 + 文本:
-
镜头调整 Tab
- 图标 + 文本:
"镜头调整" - 选中状态:高亮显示
- 图标 + 文本:
-
三个 Tab 互斥,只能选中一个
-
切换 Tab 时,输入框和示例文本相应更新
5.8.2 提示词示例区域
示例文本(图片提示词 Tab 下):
- 第一段:
"一位中年男性,穿着正装,站在办公室里" - 第二段:
"背景是现代化的办公环境,玻璃窗外是城市景观"
显示方式:
- 灰色文本,作为提示示例
- 可点击复制到输入框(可选功能)
5.8.3 输入框
占位文本:"输入图片描述..."
当前示例内容(设计稿中):
"@孙悟空 拿着/金箍棒 在 #火焰山 跟@牛魔王 手持/芭蕉扇 互相攻击。"
输入框特性:
- 支持多行输入
- 支持特殊语法:
@角色名:引用角色/道具名:引用道具#场景名:引用场景
- 实时字数统计(可选)
- 输入验证(可选)
5.8.4 操作按钮
对话改图按钮
- 文本 + 图标:
"对话改图" - 点击行为:
- 调用 AI 对话改图 API(mock)
- 显示加载状态
- 返回结果(mock 数据)
- 显示 Toast 提示
发送按钮
- 图标按钮
- 点击行为:
- 验证输入内容
- 调用 AI 生成 API(mock)
- 显示加载状态
- 返回结果并更新预览
- 显示 Toast 提示
5.8.5 模型选择器
显示文本:"当前模型: 智能选择"
交互:
- 点击打开下拉菜单
- 显示可用模型列表:
- 智能选择(默认)
- 模型1
- 模型2
- ...
- 选择模型后更新显示文本
5.9 时间轴(TimelinePanel)
5.9.1 时间轴头部
左侧标题
- 图标 + 文本:
"时间轴"
右侧缩放控制
- 缩小按钮:图标按钮
- 百分比显示:
"100%"(文本显示) - 放大按钮:图标按钮
交互行为:
- 点击缩小:时间轴刻度放大(显示更少时间范围,更精细)
- 点击放大:时间轴刻度缩小(显示更多时间范围,更粗略)
- 缩放范围:25% - 400%(可配置)
- 缩放影响:时间刻度的显示密度和轨道块的宽度
5.9.2 时间刻度
刻度范围:0s 到 60s
显示方式:
- 每秒一个刻度标记
- 每 5 秒一个主要刻度(更粗/更明显)
- 每 10 秒显示时间标签(0s, 10s, 20s, ...)
滚动行为:
- 时间轴可水平滚动
- 支持鼠标滚轮水平滚动
- 支持拖拽滚动(可选)
5.9.3 轨道结构
分镜轨道
- 轨道标题:
"分镜" - 右侧按钮:
"AI生成分镜素材"(图标 + 文本) - 轨道内容:显示 6 个分镜块
- 每个分镜块显示:分镜名称(如"分镜1")+ 缩略图图标 + 删除按钮
- 分镜块可点击选中
- 分镜块可拖拽移动(MVP 可选实现)
- 点击删除按钮:从时间轴移除该分镜块(不删除分镜本身)
资源轨道
- 轨道标题:
"资源" - 右侧按钮:
"AI生成资源素材"(图标 + 文本) - 轨道内容:按分镜分组显示资源块
- 每个分镜的资源块显示在对应时间位置
- 资源块显示资源类型图标和名称
视频轨道
- 轨道标题:
"视频" - 右侧按钮:
"AI生成视频素材"(图标 + 文本) - 轨道内容:显示所有视频片段
- 每个视频块显示:视频名称 + 类型标签 + 删除按钮
- 视频块显示时间范围(开始时间 - 结束时间)
- 视频块可点击选中
- 视频块可拖拽移动和调整长度(MVP 可选实现)
- 点击删除按钮:从时间轴移除该视频块(不删除视频本身)
音效轨道
- 轨道标题:
"音效" - 右侧按钮:
"AI生成音效素材"(图标 + 文本) - 轨道内容:显示所有音效片段
- 每个音效块显示:音效名称 + 删除按钮
- 音效块显示时间范围
- 点击删除按钮:从时间轴移除该音效块(不删除音效本身)
字幕轨道
- 轨道标题:
"字幕" - 右侧按钮:
"AI生成字幕素材"(图标 + 文本) - 轨道内容:显示所有字幕片段
- 每个字幕块显示:字幕文本 + 删除按钮
- 字幕块显示时间范围
- 点击删除按钮:从时间轴移除该字幕块(不删除字幕本身)
配音轨道
- 轨道标题:
"配音" - 右侧按钮:
"AI生成配音素材"(图标 + 文本) - 轨道内容:显示所有配音片段
- 每个配音块显示:配音文本(如"旁白1:清晨醒来")+ 删除按钮
- 配音块显示时间范围
- 点击删除按钮:从时间轴移除该配音块(不删除配音本身)
5.9.4 添加轨道按钮
位置:时间轴底部
样式:图标 + 文本:"添加轨道"
交互:
- 点击打开「添加轨道」对话框
- 选择轨道类型:
- 资源
- 视频
- 音效
- 字幕
- 配音
- 确认后添加新轨道到时间轴
5.9.6 时间轴状态显示
位置:时间轴底部,添加轨道按钮下方
状态文本:
- 未选择素材时:
"未选择素材" - 选择素材时:显示选中素材信息(名称、类型、时间范围等)
5.9.5 时间轴交互
点击时间轴块:
- 选中对应素材/分镜
- 更新预览窗口
- 更新右侧面板选中状态
- 播放位置跳转到块开始时间
拖拽时间轴块(MVP 可选):
- 拖拽移动块的位置
- 实时更新开始/结束时间
- 显示时间提示
调整时间轴块长度(MVP 可选):
- 拖拽块的两端调整长度
- 实时更新结束时间
- 显示时间提示
时间轴缩放:
- 使用缩放控制按钮调整显示范围
- 使用鼠标滚轮缩放(Ctrl + 滚轮,可选)
5.10 右侧栏 - 资源面板(ResourcePanel)
5.10.1 面板头部
AI生成按钮
- 图标 + 文本:
"AI生成资源素材" - 点击行为:打开 AI 生成资源弹窗
- 选择生成类型(人物/场景/道具/视频)
- 输入描述
- 选择分镜
- 生成资源(mock)
5.10.2 分镜资源分组
分组结构:按分镜分组,共 6 组(分镜1-6)
每组包含子分类:
人物分类
- 分类标题:
"人物" - 资源标签列表:
- 每个资源显示为按钮标签
- 标签右侧有
"×"删除按钮 - 示例(分镜1):
"主角-睡姿"
- 添加人物按钮
- 图标 + 文本:
"添加人物" - 点击行为:打开添加人物对话框
- 从素材库选择
- 或使用 AI 生成
- 或上传图片
- 图标 + 文本:
场景分类
- 分类标题:
"场景" - 资源标签列表:
- 示例(分镜1):
"卧室场景"
- 示例(分镜1):
- 添加场景按钮(如果有)
道具分类
- 分类标题:
"道具" - 资源标签列表:
- 示例(分镜1):
"床+窗帘"
- 示例(分镜1):
- 添加道具按钮
- 图标 + 文本:
"添加道具" - 点击行为:打开添加道具对话框
- 图标 + 文本:
视频分类
- 分类标题:
"视频" - 添加视频按钮
- 图标 + 文本:
"添加视频" - 点击行为:打开添加视频对话框
- 图标 + 文本:
5.10.3 资源数据(按设计稿)
分镜1 资源:
- 人物:
"主角-睡姿" - 场景:
"卧室场景" - 道具:
"床+窗帘"
分镜2 资源:
- 人物:
"主角-厨房" - 场景:
"厨房场景" - 道具:
"冰箱+食物"
分镜3 资源:
- 人物:
"主角-用餐" - 场景:
"餐桌场景" - 道具:
"手机+餐具"
分镜4 资源:
- 人物:
"主角-清洁" - 场景:
"洗碗池场景" - 道具:
"碗筷+水槽"
分镜5 资源:
- 人物:
"主角-换装" - 场景:
"卧室+衣柜" - 道具:
"衣服+镜子"
分镜6 资源:
- 人物:
"主角-出门" - 场景:
"门口场景" - 道具:
"背包+钥匙"
5.10.4 交互行为
资源标签交互:
- 点击资源标签:选中资源,更新预览
- 点击
"×"按钮:删除资源,显示确认提示(可选)
添加资源交互:
- 点击添加按钮:打开添加对话框
- 选择/生成资源后:自动添加到当前分镜的资源列表
分镜切换联动:
- 切换选中分镜时:资源面板自动滚动到对应分镜的资源组
- 高亮当前分镜的资源组
5.11 右侧栏 - 视频面板(VideoPanel)
5.11.1 面板头部
AI生成按钮
- 图标 + 文本:
"AI生成视频素材" - 点击行为:打开 AI 生成视频弹窗
- 选择视频类型(图生视频/文生视频/首尾帧/融合生/实拍替换/实视频)
- 输入描述或上传图片
- 选择分镜
- 生成视频(mock)
5.11.2 视频列表
视频项结构(每个视频包含):
- 视频名称:文本显示
- 类型标签:显示视频类型
"图生视频""文生视频""首尾帧视频""融合生视频""实拍替换""实视频"
- 更多按钮:右侧显示,点击显示菜单(编辑、删除、复制等)
5.11.3 视频数据(按设计稿)
完整视频列表(共 24 个视频):
- 睁眼 - 图生视频
- 阳光 - 文生视频
- 起床 - 首尾帧视频
- 走廊 - 融合生视频
- 开冰箱 - 实拍替换
- 拿牛奶 - 图生视频
- 拿面包 - 文生视频
- 坐下 - 实视频
- 看手机 - 图生视频
- 收拾餐具 - 图生视频
- 走向洗碗池 - 文生视频
- 清洗 - 实拍替换
- 打开衣柜 - 图生视频
- 挑选衣服 - 融合生视频
- 换衣服 - 文生视频
- 照镜子 - 图生视频
- 整理发型 - 首尾帧视频
- 拿包 - 图生视频
- 回望 - 文生视频
- 开门离开 - 实拍替换
5.11.4 交互行为
视频选择:
- 点击视频项:选中视频
- 视频项显示选中状态
- 更新预览窗口
- 时间轴上高亮对应视频块
- 播放位置跳转到视频开始时间
视频编辑:
- 点击更多按钮 → 编辑:打开视频编辑对话框
- 修改视频名称
- 修改视频类型
- 调整视频参数(MVP 为占位)
视频删除:
- 点击更多按钮 → 删除:显示确认对话框
- 确认后删除视频
- 从时间轴上移除对应视频块
5.12 右侧栏 - 音效面板(SoundEffectPanel)
5.12.1 面板头部
AI生成按钮
- 图标 + 文本:
"AI生成音效素材" - 点击行为:打开 AI 生成音效弹窗
- 输入音效描述
- 选择音效类型
- 生成音效(mock)
5.12.2 音效列表
音效项结构(每个音效包含):
- 音效名称:文本显示
- 更多按钮:右侧显示,点击显示菜单
5.12.3 音效数据(按设计稿)
完整音效列表(共 16 个音效):
- 鸟叫
- 床铺声
- 脚步声
- 开门
- 拿东西
- 背景音
- 碗筷碰撞
- 水声
- 洗碗声
- 柜门
- 衣物摩擦
- 拉链
- 环境音
- 拉链(重复,可能是不同场景)
- 脚步声(重复)
- 开关门
5.12.4 交互行为
音效选择:
- 点击音效项:选中音效
- 音效项显示选中状态
- 时间轴上高亮对应音效块
- 播放音效预览(MVP 为占位)
音效编辑/删除:
- 点击更多按钮:显示操作菜单
- 编辑:修改音效名称
- 删除:删除音效
5.13 右侧栏 - 字幕面板(SubtitlePanel)
5.13.1 面板头部
AI生成按钮
- 图标 + 文本:
"AI生成字幕素材" - 点击行为:打开 AI 生成字幕弹窗
- 输入字幕文本
- 选择时间位置
- 生成字幕(mock)
5.13.2 字幕列表
字幕项结构(每个字幕包含):
- 字幕文本:文本显示
- 更多按钮:右侧显示,点击显示菜单
5.13.3 字幕数据(按设计稿)
完整字幕列表(共 16 个字幕):
- 早安
- 新的一天
- 去厨房
- 准备早餐
- 牛奶面包
- 享用
- 看新闻
- 收拾
- 清洗餐具
- 保持整洁
- 选衣服
- 今天穿什么
- 换装完成
- 完美
- 准备出门
- 新的旅程
5.13.4 交互行为
字幕选择:
- 点击字幕项:选中字幕
- 字幕项显示选中状态
- 时间轴上高亮对应字幕块
- 预览窗口显示字幕(可选)
字幕编辑/删除:
- 点击更多按钮:显示操作菜单
- 编辑:修改字幕文本和时间
- 删除:删除字幕
5.14 右侧栏 - 配音面板(VoiceoverPanel)
5.14.1 面板头部
AI生成按钮
- 图标 + 文本:
"AI生成配音素材" - 点击行为:打开 AI 生成配音弹窗
- 输入配音文本
- 选择语音类型
- 选择时间位置
- 生成配音(mock)
5.14.2 配音列表
配音项结构(每个配音包含):
- 配音文本:文本显示(如"旁白1:清晨醒来")
- 更多按钮:右侧显示,点击显示菜单
5.14.3 配音数据(按设计稿)
完整配音列表(共 8 个配音):
- 旁白1:清晨醒来
- 旁白2:去厨房
- 旁白3:准备食物
- 旁白4:享用早餐
- 旁白5:收拾餐具
- 旁白6:选择穿搭
- 旁白7:整理仪容
- 旁白8:出发
5.14.4 交互行为
配音选择:
- 点击配音项:选中配音
- 配音项显示选中状态
- 时间轴上高亮对应配音块
- 播放配音预览(MVP 为占位)
配音编辑/删除:
- 点击更多按钮:显示操作菜单
- 编辑:修改配音文本和时间
- 删除:删除配音
5.15 右侧栏 - Tab 切换
5.15.1 Tab 按钮组
Tab 列表:
- 资源 Tab
- 视频 Tab
- 音效 Tab
- 字幕 Tab
- 配音 Tab
5.15.2 交互行为
Tab 切换:
- 点击 Tab:切换显示对应面板
- 选中 Tab 有视觉区分(下划线/背景色/文字颜色)
- 切换时保持滚动位置(可选)
Tab 状态联动:
- 切换 Tab 时,时间轴上对应轨道自动滚动到可视区域(可选)
5.16 新手引导浮层(OnboardingOverlay)
5.16.1 浮层结构
跳过按钮
- 位置:浮层右上角
- 样式:图标按钮
- 点击行为:关闭引导,写入 localStorage,不再显示
步骤指示器
- 图标 + 文本:
"步骤 1 / 4" - 显示当前步骤和总步骤数
步骤标题
- Heading 3 级别:
"项目与素材管理"
步骤描述
- 段落文本:
"在这里您可以新增、查看和管理项目,同时可以查看和管理项目中的所有素材资源。"
步骤跳转按钮组
- 跳转到步骤 1 按钮
- 跳转到步骤 2 按钮
- 跳转到步骤 3 按钮
- 跳转到步骤 4 按钮
- 点击按钮:跳转到对应步骤,更新内容
导航按钮组
- 上一步按钮
- 图标 + 文本:
"上一步" - 第一步时:disabled 状态
- 点击行为:返回上一步
- 图标 + 文本:
- 跳过引导按钮
- 文本:
"跳过引导" - 点击行为:关闭引导,写入 localStorage
- 文本:
- 下一步按钮
- 文本 + 图标:
"下一步" - 最后一步时:显示"完成"或隐藏
- 点击行为:进入下一步
- 文本 + 图标:
5.16.2 引导步骤内容(需要补充)
步骤 1:项目与素材管理
- 描述:
"在这里您可以新增、查看和管理项目,同时可以查看和管理项目中的所有素材资源。" - 高亮区域:左侧项目列表和素材库
步骤 2:分镜编辑(需要补充内容)
- 描述:待补充
- 高亮区域:分镜列表
步骤 3:预览和时间轴(需要补充内容)
- 描述:待补充
- 高亮区域:预览窗口和时间轴
步骤 4:AI 功能(需要补充内容)
- 描述:待补充
- 高亮区域:AI 输入区和 AI 生成按钮
5.16.3 交互行为
引导显示逻辑:
- 首次访问时自动显示
- 检查 localStorage,如果已跳过则不显示
- 可手动重新打开引导(设置中,可选功能)
步骤切换:
- 点击"下一步":进入下一步,更新内容和高亮区域
- 点击"上一步":返回上一步
- 点击步骤跳转按钮:直接跳转到指定步骤
引导关闭:
- 点击"跳过引导":关闭引导,写入 localStorage
- 点击右上角关闭按钮:关闭引导,写入 localStorage
- 完成所有步骤:自动关闭引导,写入 localStorage
5.17 Cookie 提示(Consent Manager)
5.17.1 提示内容
文本内容:
- 主文本:
"This website uses cookies, pixel tags, and local storage for performance, personalization, and marketing purposes. Our use of some cookies may be considered a sale, sharing for behavioral advertising, or targeted advertising. For more, see our" - 隐私政策链接:
"privacy policy"→https://www.figma.com/privacy/ - 中间文本:
". California Residents can learn how personal information is" - 收集链接:
"collected"→https://www.figma.com/privacy/#ca-notice - 中间文本:
", including how it is" - 使用链接:
"used"→https://www.figma.com/privacy/#purpose - 中间文本:
", whether it is" - 销售/共享链接:
""sold" or "shared""→https://www.figma.com/privacy/#ca-notice - 中间文本:
", and how long it is" - 保留链接:
"retained"→https://www.figma.com/privacy/#retention - 结尾文本:
"."
5.17.2 操作按钮
Opt out 按钮
- 文本:
"Opt out" - 点击行为:选择退出 Cookie,写入 localStorage
关闭按钮
- 图标按钮
- 点击行为:关闭提示,写入 localStorage(同意使用 Cookie)
5.17.3 交互行为
提示显示逻辑:
- 首次访问时显示
- 检查 localStorage,如果已选择则不显示
- 可手动重新打开(设置中,可选功能)
链接跳转:
- 所有链接在新标签页打开
6. UI/UX 交互细节
6.1 视觉样式规范
6.1.1 颜色系统
- 主色调:根据 Figma 设计稿提取
- 辅助色:警告色、成功色、错误色
- 中性色:文本色、背景色、边框色
- 状态色:hover、active、selected、disabled
6.1.2 字体系统
- 字体家族:根据设计稿(可能包含中文字体)
- 字体大小:标题、正文、辅助文本
- 字重:正常、中等、粗体
- 行高:根据设计稿
6.1.3 间距系统
- 基础间距单位:4px 或 8px
- 组件间距:根据设计稿提取
- 内边距:按钮、输入框、卡片等
- 外边距:组件之间的间距
6.1.4 圆角系统
- 小圆角:按钮、标签
- 中圆角:卡片、面板
- 大圆角:弹窗、抽屉
6.1.5 阴影系统
- 小阴影:卡片、按钮 hover
- 中阴影:弹窗、抽屉
- 大阴影:模态框
6.2 交互状态
6.2.1 按钮状态
- 默认状态:正常显示
- Hover 状态:鼠标悬停,视觉反馈(颜色变化、阴影、缩放)
- Active 状态:点击时,视觉反馈
- Disabled 状态:禁用,灰色,不可点击
- Loading 状态:加载中,显示加载动画
6.2.2 输入框状态
- 默认状态:正常显示
- Focus 状态:获得焦点,边框高亮
- Error 状态:错误,红色边框和提示
- Disabled 状态:禁用,灰色
6.2.3 选中状态
- 分镜选中:高亮背景、边框、或左侧指示条
- 资源选中:高亮背景或边框
- Tab 选中:下划线、背景色、或文字颜色变化
6.3 动画与过渡
6.3.1 页面切换
- 淡入淡出:页面切换时
- 滑动:侧边栏展开/收起
- 缩放:弹窗打开/关闭
6.3.2 状态变化
- 颜色过渡:hover、active 状态变化
- 位置过渡:拖拽、滚动
- 尺寸过渡:展开/收起
6.3.3 加载动画
- 按钮加载:旋转图标或进度条
- 内容加载:骨架屏或加载指示器
- AI 生成:进度条或动画效果
6.4 响应式设计
6.4.1 断点设置
- 桌面:> 1200px(默认布局)
- 平板:768px - 1200px(调整布局)
- 移动端:< 768px(简化布局,可选实现)
6.4.2 布局适配
- 侧边栏:可收起/展开
- 面板:可调整宽度(可选)
- 时间轴:可全屏显示(可选)
6.5 可访问性(Accessibility)
6.5.1 键盘导航
- Tab 键:在可交互元素间切换
- Enter/Space:激活按钮
- Esc:关闭弹窗/取消操作
- 方向键:在列表项间导航
6.5.2 屏幕阅读器
- ARIA 标签:为交互元素添加标签
- 语义化 HTML:使用正确的 HTML 标签
- 焦点管理:弹窗打开时聚焦到第一个元素
6.5.3 视觉辅助
- 焦点指示器:清晰的焦点样式
- 对比度:文本与背景的对比度符合 WCAG 标准
- 字体大小:支持浏览器缩放
7. 数据模型
7.1 核心数据类型
// 项目类型
type Project = {
id: string;
name: string;
description?: string;
thumbnailUrl?: string;
type: "mine" | "collab";
createdAt: number;
updatedAt: number;
storyboards: Storyboard[];
settings: ProjectSettings;
};
// 项目设置
type ProjectSettings = {
resolution: {
width: number;
height: number;
};
frameRate: number;
duration: number; // 秒
exportFormat?: string;
exportQuality?: string;
};
// 分镜类型
type Storyboard = {
id: string;
title: string; // "分镜1", "分镜2", ...
description: string;
thumbnailUrl?: string;
resources: StoryboardResources;
startTime: number; // 在时间轴上的开始时间(秒)
endTime: number; // 在时间轴上的结束时间(秒)
order: number; // 分镜顺序
};
// 分镜资源
type StoryboardResources = {
characters: ResourceItem[]; // 人物
scenes: ResourceItem[]; // 场景
props: ResourceItem[]; // 道具
videos: string[]; // 视频 ID 列表
};
// 资源项
type ResourceItem = {
id: string;
name: string;
type: "character" | "scene" | "prop";
thumbnailUrl?: string;
assetUrl?: string;
};
// 视频资产
type VideoAsset = {
id: string;
name: string;
type: "img2video" | "text2video" | "keyframe" | "fusion" | "replace" | "real";
thumbnailUrl?: string;
videoUrl?: string;
startTime: number; // 在时间轴上的开始时间(秒)
endTime: number; // 在时间轴上的结束时间(秒)
storyboardId?: string; // 关联的分镜 ID
};
// 音效资产
type SoundEffect = {
id: string;
name: string;
audioUrl?: string;
startTime: number;
endTime: number;
volume?: number; // 0-100
};
// 字幕资产
type Subtitle = {
id: string;
text: string;
startTime: number;
endTime: number;
style?: SubtitleStyle;
};
type SubtitleStyle = {
fontSize?: number;
color?: string;
position?: "top" | "center" | "bottom";
backgroundColor?: string;
};
// 配音资产
type Voiceover = {
id: string;
text: string;
audioUrl?: string;
startTime: number;
endTime: number;
voiceType?: string; // 语音类型
volume?: number;
};
// 时间轴轨道
type TimelineTrack = {
id: string;
type: "storyboard" | "resource" | "video" | "sound" | "subtitle" | "voice";
name: string;
items: TimelineItem[];
visible: boolean;
locked: boolean;
};
// 时间轴项
type TimelineItem = {
id: string;
refId: string; // 指向 storyboard / video / sound / subtitle / voiceover 的 ID
type: "storyboard" | "video" | "sound" | "subtitle" | "voice";
start: number; // 开始时间(秒)
end: number; // 结束时间(秒)
trackId: string; // 所属轨道 ID
};
// AI 生成请求
type AIGenerateRequest = {
type: "image" | "video" | "sound" | "subtitle" | "voice" | "resource";
prompt: string;
model?: string;
parameters?: Record<string, any>;
storyboardId?: string;
};
// AI 生成响应
type AIGenerateResponse = {
success: boolean;
data?: any;
error?: string;
jobId?: string; // 异步任务 ID
};
7.2 状态管理数据结构
// 应用全局状态
type AppState = {
// 当前项目
currentProject: Project | null;
// 当前选中的分镜
selectedStoryboard: Storyboard | null;
// 当前选中的资源/视频/音效/字幕/配音
selectedItem: {
type: "resource" | "video" | "sound" | "subtitle" | "voice" | null;
id: string | null;
};
// 预览窗口状态
preview: {
mode: "fit" | "fill"; // 适应窗口 / 填充窗口
zoom: number; // 缩放比例,如 100 表示 100%
currentImageUrl?: string;
};
// 播放控制状态
playback: {
isPlaying: boolean;
currentTime: number; // 当前播放时间(秒)
timecode: string; // 时间码 "00:00:00:00"
};
// 时间轴状态
timeline: {
zoom: number; // 时间轴缩放比例
scrollPosition: number; // 滚动位置
selectedTrackId: string | null;
};
// 右侧面板状态
rightPanel: {
activeTab: "resource" | "video" | "sound" | "subtitle" | "voice";
scrollPosition: number;
};
// AI 输入区状态
aiPrompt: {
activeTab: "image" | "angle" | "lens";
inputText: string;
selectedModel: string;
};
// 新手引导状态
onboarding: {
isVisible: boolean;
currentStep: number;
totalSteps: number;
skipped: boolean; // 是否已跳过
};
// UI 状态
ui: {
isLeftSidebarCollapsed: boolean;
isRightSidebarCollapsed: boolean;
modals: {
[key: string]: boolean; // 各种弹窗的显示状态
};
};
};
7.3 Mock 数据示例
// Mock 项目数据
const mockProjects: Project[] = [
{
id: "proj-1",
name: "西游记之大圣归来",
type: "mine",
thumbnailUrl: "/thumbnails/proj-1.jpg",
createdAt: Date.now() - 86400000 * 7,
updatedAt: Date.now() - 86400000,
storyboards: mockStoryboards,
settings: {
resolution: { width: 1920, height: 1080 },
frameRate: 30,
duration: 60,
},
},
{
id: "proj-2",
name: "琅琊榜",
type: "mine",
thumbnailUrl: "/thumbnails/proj-2.jpg",
createdAt: Date.now() - 86400000 * 14,
updatedAt: Date.now() - 86400000 * 2,
storyboards: [],
settings: {
resolution: { width: 1920, height: 1080 },
frameRate: 30,
duration: 60,
},
},
// ... 更多项目
];
// Mock 分镜数据
const mockStoryboards: Storyboard[] = [
{
id: "sb-1",
title: "分镜1",
description:
"清晨的阳光照进窗户,主人公在床上慢慢睁开眼睛,开始新的一天。窗外传来鸟鸣声,窗帘随着微风轻轻飘动。",
order: 1,
startTime: 0,
endTime: 10,
resources: {
characters: [{ id: "char-1", name: "主角-睡姿", type: "character" }],
scenes: [{ id: "scene-1", name: "卧室场景", type: "scene" }],
props: [{ id: "prop-1", name: "床+窗帘", type: "prop" }],
videos: ["video-1", "video-2", "video-3", "video-4"],
},
},
// ... 更多分镜
];
// Mock 视频数据
const mockVideos: VideoAsset[] = [
{
id: "video-1",
name: "睁眼",
type: "img2video",
startTime: 0,
endTime: 2,
storyboardId: "sb-1",
},
{
id: "video-2",
name: "阳光",
type: "text2video",
startTime: 2,
endTime: 4,
storyboardId: "sb-1",
},
// ... 更多视频
];
8. API 接口规范
8.1 API 设计原则
8.1.1 基础规范
- 协议:HTTPS
- 数据格式:JSON
- 请求方法:RESTful(GET、POST、PUT、DELETE、PATCH)
- 认证方式:Bearer Token(JWT)
- 响应格式:统一响应结构
8.1.2 统一响应结构
// 成功响应
type ApiSuccessResponse<T> = {
success: true;
data: T;
message?: string;
};
// 错误响应
type ApiErrorResponse = {
success: false;
error: {
code: string;
message: string;
details?: any;
};
};
// 分页响应
type ApiPaginatedResponse<T> = {
success: true;
data: T[];
pagination: {
page: number;
pageSize: number;
total: number;
totalPages: number;
};
};
8.1.3 错误码规范
enum ApiErrorCode {
// 通用错误
UNAUTHORIZED = "UNAUTHORIZED", // 401 未授权
FORBIDDEN = "FORBIDDEN", // 403 禁止访问
NOT_FOUND = "NOT_FOUND", // 404 资源不存在
VALIDATION_ERROR = "VALIDATION_ERROR", // 400 参数验证失败
INTERNAL_ERROR = "INTERNAL_ERROR", // 500 服务器错误
// 业务错误
PROJECT_NOT_FOUND = "PROJECT_NOT_FOUND",
STORYBOARD_NOT_FOUND = "STORYBOARD_NOT_FOUND",
AI_GENERATION_FAILED = "AI_GENERATION_FAILED",
AI_GENERATION_TIMEOUT = "AI_GENERATION_TIMEOUT",
EXPORT_FAILED = "EXPORT_FAILED",
}
8.2 Mock 数据与真实 API 区分
8.2.1 前端纯状态管理(无需 API)
以下功能仅在前端状态管理,无需调用后端 API:
- ✅ UI 状态:Tab 切换、面板展开/收起、弹窗显示/隐藏
- ✅ 选中状态:分镜选中、资源选中、时间轴选中
- ✅ 预览窗口状态:缩放比例、视图模式(适应/填充)
- ✅ 时间轴状态:时间轴缩放、滚动位置
- ✅ 播放控制状态:播放/暂停、当前时间(前端模拟)
- ✅ 新手引导状态:当前步骤、是否跳过(localStorage)
8.2.2 需要 Mock 数据的 API(MVP 阶段)
以下功能在 MVP 阶段使用 Mock 数据,但需要按照真实 API 格式实现:
- 🔶 项目 CRUD:创建、读取、更新、删除项目
- 🔶 分镜 CRUD:创建、读取、更新、删除分镜
- 🔶 资源管理:添加、删除资源(人物、场景、道具)
- 🔶 视频管理:添加、删除、更新视频
- 🔶 音效管理:添加、删除、更新音效
- 🔶 字幕管理:添加、删除、更新字幕
- 🔶 配音管理:添加、删除、更新配音
- 🔶 时间轴操作:保存时间轴配置
- 🔶 AI 生成功能:所有 AI 生成请求
- 🔶 项目导出:导出项目配置或视频
8.2.3 真实后端 API(后续对接)
所有标记为 🔶 的功能,在后续阶段需要对接真实后端 API。
8.3 API 接口详细定义
8.3.1 项目管理 API
8.3.1.1 获取项目列表
// 请求
GET /api/v1/projects
Query Parameters:
- type?: 'mine' | 'collab' // 项目类型,不传则返回全部
- page?: number // 页码,默认 1
- pageSize?: number // 每页数量,默认 20
// 响应
type GetProjectsResponse = ApiPaginatedResponse<Project>;
// Mock 实现
// 返回本地 Mock 数据,格式与真实 API 一致
8.3.1.2 获取项目详情
// 请求
GET /api/v1/projects/:projectId
// 响应
type GetProjectResponse = ApiSuccessResponse<Project>;
// Mock 实现
// 从 Mock 数据中查找对应项目
8.3.1.3 创建项目
// 请求
POST /api/v1/projects
Body: {
name: string;
description?: string;
type: 'mine' | 'collab';
settings?: {
resolution: { width: number; height: number };
frameRate: number;
duration: number;
};
}
// 响应
type CreateProjectResponse = ApiSuccessResponse<Project>;
// Mock 实现
// 生成新项目 ID,添加到 Mock 数据列表
8.3.1.4 更新项目
// 请求
PUT /api/v1/projects/:projectId
Body: {
name?: string;
description?: string;
settings?: ProjectSettings;
}
// 响应
type UpdateProjectResponse = ApiSuccessResponse<Project>;
// Mock 实现
// 更新 Mock 数据中对应项目
8.3.1.5 删除项目
// 请求
DELETE /api/v1/projects/:projectId
// 响应
type DeleteProjectResponse = ApiSuccessResponse<{ id: string }>;
// Mock 实现
// 从 Mock 数据中删除对应项目
8.3.1.6 导出项目
// 请求
POST /api/v1/projects/:projectId/export
Body: {
format: 'json' | 'mp4' | 'mov'; // 导出格式
quality?: 'low' | 'medium' | 'high';
resolution?: { width: number; height: number };
}
// 响应(同步)
type ExportProjectResponse = ApiSuccessResponse<{
downloadUrl: string; // 下载链接
expiresAt: number; // 过期时间戳
}>;
// 响应(异步)
type ExportProjectAsyncResponse = ApiSuccessResponse<{
jobId: string; // 任务 ID
status: 'pending' | 'processing' | 'completed' | 'failed';
}>;
// Mock 实现
// 返回模拟的下载链接或任务 ID
8.3.2 分镜管理 API
8.3.2.1 获取项目分镜列表
// 请求
GET /api/v1/projects/:projectId/storyboards
// 响应
type GetStoryboardsResponse = ApiSuccessResponse<Storyboard[]>;
// Mock 实现
// 返回项目下的所有分镜
8.3.2.2 获取分镜详情
// 请求
GET /api/v1/storyboards/:storyboardId
// 响应
type GetStoryboardResponse = ApiSuccessResponse<Storyboard>;
// Mock 实现
// 从 Mock 数据中查找对应分镜
8.3.2.3 创建分镜
// 请求
POST /api/v1/projects/:projectId/storyboards
Body: {
title: string;
description?: string;
order?: number; // 插入位置,不传则追加到最后
}
// 响应
type CreateStoryboardResponse = ApiSuccessResponse<Storyboard>;
// Mock 实现
// 生成新分镜 ID,插入到指定位置
8.3.2.4 更新分镜
// 请求
PUT /api/v1/storyboards/:storyboardId
Body: {
title?: string;
description?: string;
startTime?: number;
endTime?: number;
order?: number;
}
// 响应
type UpdateStoryboardResponse = ApiSuccessResponse<Storyboard>;
// Mock 实现
// 更新 Mock 数据中对应分镜
8.3.2.5 删除分镜
// 请求
DELETE /api/v1/storyboards/:storyboardId
// 响应
type DeleteStoryboardResponse = ApiSuccessResponse<{ id: string }>;
// Mock 实现
// 从 Mock 数据中删除对应分镜
8.3.2.6 更新分镜资源
// 请求
PUT /api/v1/storyboards/:storyboardId/resources
Body: {
characters?: ResourceItem[];
scenes?: ResourceItem[];
props?: ResourceItem[];
videos?: string[]; // 视频 ID 列表
}
// 响应
type UpdateStoryboardResourcesResponse = ApiSuccessResponse<StoryboardResources>;
// Mock 实现
// 更新分镜的资源列表
8.3.3 资源管理 API
8.3.3.1 获取资源列表
// 请求
GET /api/v1/resources
Query Parameters:
- type?: 'character' | 'scene' | 'prop';
- storyboardId?: string;
- page?: number;
- pageSize?: number;
// 响应
type GetResourcesResponse = ApiPaginatedResponse<ResourceItem>;
// Mock 实现
// 返回资源列表,支持筛选
8.3.3.2 上传资源
// 请求
POST /api/v1/resources
Content-Type: multipart/form-data
Body: {
file: File;
name: string;
type: 'character' | 'scene' | 'prop';
storyboardId?: string;
}
// 响应
type UploadResourceResponse = ApiSuccessResponse<ResourceItem>;
// Mock 实现
// 生成资源 ID 和 URL,保存到 Mock 数据
8.3.3.3 删除资源
// 请求
DELETE /api/v1/resources/:resourceId
// 响应
type DeleteResourceResponse = ApiSuccessResponse<{ id: string }>;
// Mock 实现
// 从 Mock 数据中删除资源
8.3.4 视频管理 API
8.3.4.1 获取视频列表
// 请求
GET /api/v1/videos
Query Parameters:
- projectId?: string;
- storyboardId?: string;
- page?: number;
- pageSize?: number;
// 响应
type GetVideosResponse = ApiPaginatedResponse<VideoAsset>;
// Mock 实现
// 返回视频列表
8.3.4.2 创建视频
// 请求
POST /api/v1/videos
Body: {
name: string;
type: 'img2video' | 'text2video' | 'keyframe' | 'fusion' | 'replace' | 'real';
storyboardId?: string;
startTime?: number;
endTime?: number;
}
// 响应
type CreateVideoResponse = ApiSuccessResponse<VideoAsset>;
// Mock 实现
// 生成视频 ID,添加到 Mock 数据
8.3.4.3 更新视频
// 请求
PUT /api/v1/videos/:videoId
Body: {
name?: string;
startTime?: number;
endTime?: number;
thumbnailUrl?: string;
}
// 响应
type UpdateVideoResponse = ApiSuccessResponse<VideoAsset>;
// Mock 实现
// 更新视频信息
8.3.4.4 删除视频
// 请求
DELETE /api/v1/videos/:videoId
// 响应
type DeleteVideoResponse = ApiSuccessResponse<{ id: string }>;
// Mock 实现
// 从 Mock 数据中删除视频
8.3.5 音效管理 API
8.3.5.1 获取音效列表
// 请求
GET /api/v1/sound-effects
Query Parameters:
- projectId?: string;
- page?: number;
- pageSize?: number;
// 响应
type GetSoundEffectsResponse = ApiPaginatedResponse<SoundEffect>;
// Mock 实现
// 返回音效列表
8.3.5.2 创建音效
// 请求
POST /api/v1/sound-effects
Body: {
name: string;
startTime: number;
endTime: number;
volume?: number;
audioUrl?: string; // 如果上传文件
}
// 响应
type CreateSoundEffectResponse = ApiSuccessResponse<SoundEffect>;
// Mock 实现
// 生成音效 ID,添加到 Mock 数据
8.3.5.3 更新音效
// 请求
PUT /api/v1/sound-effects/:soundEffectId
Body: {
name?: string;
startTime?: number;
endTime?: number;
volume?: number;
}
// 响应
type UpdateSoundEffectResponse = ApiSuccessResponse<SoundEffect>;
// Mock 实现
// 更新音效信息
8.3.5.4 删除音效
// 请求
DELETE /api/v1/sound-effects/:soundEffectId
// 响应
type DeleteSoundEffectResponse = ApiSuccessResponse<{ id: string }>;
// Mock 实现
// 从 Mock 数据中删除音效
8.3.6 字幕管理 API
8.3.6.1 获取字幕列表
// 请求
GET /api/v1/subtitles
Query Parameters:
- projectId?: string;
- page?: number;
- pageSize?: number;
// 响应
type GetSubtitlesResponse = ApiPaginatedResponse<Subtitle>;
// Mock 实现
// 返回字幕列表
8.3.6.2 创建字幕
// 请求
POST /api/v1/subtitles
Body: {
text: string;
startTime: number;
endTime: number;
style?: SubtitleStyle;
}
// 响应
type CreateSubtitleResponse = ApiSuccessResponse<Subtitle>;
// Mock 实现
// 生成字幕 ID,添加到 Mock 数据
8.3.6.3 更新字幕
// 请求
PUT /api/v1/subtitles/:subtitleId
Body: {
text?: string;
startTime?: number;
endTime?: number;
style?: SubtitleStyle;
}
// 响应
type UpdateSubtitleResponse = ApiSuccessResponse<Subtitle>;
// Mock 实现
// 更新字幕信息
8.3.6.4 删除字幕
// 请求
DELETE /api/v1/subtitles/:subtitleId
// 响应
type DeleteSubtitleResponse = ApiSuccessResponse<{ id: string }>;
// Mock 实现
// 从 Mock 数据中删除字幕
8.3.7 配音管理 API
8.3.7.1 获取配音列表
// 请求
GET /api/v1/voiceovers
Query Parameters:
- projectId?: string;
- page?: number;
- pageSize?: number;
// 响应
type GetVoiceoversResponse = ApiPaginatedResponse<Voiceover>;
// Mock 实现
// 返回配音列表
8.3.7.2 创建配音
// 请求
POST /api/v1/voiceovers
Body: {
text: string;
startTime: number;
endTime: number;
voiceType?: string;
volume?: number;
}
// 响应
type CreateVoiceoverResponse = ApiSuccessResponse<Voiceover>;
// Mock 实现
// 生成配音 ID,添加到 Mock 数据
8.3.7.3 更新配音
// 请求
PUT /api/v1/voiceovers/:voiceoverId
Body: {
text?: string;
startTime?: number;
endTime?: number;
voiceType?: string;
volume?: number;
}
// 响应
type UpdateVoiceoverResponse = ApiSuccessResponse<Voiceover>;
// Mock 实现
// 更新配音信息
8.3.7.4 删除配音
// 请求
DELETE /api/v1/voiceovers/:voiceoverId
// 响应
type DeleteVoiceoverResponse = ApiSuccessResponse<{ id: string }>;
// Mock 实现
// 从 Mock 数据中删除配音
8.3.8 AI 生成 API
8.3.8.1 图片生成(图片提示词)
// 请求
POST /api/v1/ai/generate-image
Body: {
prompt: string;
model?: string;
parameters?: {
width?: number;
height?: number;
style?: string;
negativePrompt?: string;
};
storyboardId?: string;
}
// 响应(同步,快速生成)
type GenerateImageResponse = ApiSuccessResponse<{
imageUrl: string;
thumbnailUrl: string;
}>;
// 响应(异步,复杂生成)
type GenerateImageAsyncResponse = ApiSuccessResponse<{
jobId: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
result?: {
imageUrl: string;
thumbnailUrl: string;
};
}>;
// Mock 实现
// 返回模拟的图片 URL,延迟 1-3 秒模拟异步
8.3.8.2 角度变换
// 请求
POST /api/v1/ai/transform-angle
Body: {
imageUrl: string; // 原图 URL
angle: 'front' | 'side' | 'back' | 'top' | 'bottom';
model?: string;
}
// 响应
type TransformAngleResponse = ApiSuccessResponse<{
imageUrl: string;
thumbnailUrl: string;
}>;
// Mock 实现
// 返回模拟的变换后图片 URL
8.3.8.3 镜头调整
// 请求
POST /api/v1/ai/adjust-lens
Body: {
imageUrl: string;
lensType: 'wide' | 'normal' | 'telephoto' | 'macro';
model?: string;
}
// 响应
type AdjustLensResponse = ApiSuccessResponse<{
imageUrl: string;
thumbnailUrl: string;
}>;
// Mock 实现
// 返回模拟的调整后图片 URL
8.3.8.4 生成分镜素材
// 请求
POST /api/v1/ai/generate-storyboard-assets
Body: {
storyboardId: string;
prompt: string;
assetTypes: ('character' | 'scene' | 'prop')[];
model?: string;
}
// 响应
type GenerateStoryboardAssetsResponse = ApiSuccessResponse<{
jobId: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
result?: {
characters: ResourceItem[];
scenes: ResourceItem[];
props: ResourceItem[];
};
}>;
// Mock 实现
// 返回模拟的资源列表
8.3.8.5 生成资源素材
// 请求
POST /api/v1/ai/generate-resource
Body: {
type: 'character' | 'scene' | 'prop';
prompt: string;
storyboardId?: string;
model?: string;
}
// 响应
type GenerateResourceResponse = ApiSuccessResponse<ResourceItem>;
// Mock 实现
// 返回模拟的资源项
8.3.8.6 生成视频
// 请求
POST /api/v1/ai/generate-video
Body: {
type: 'img2video' | 'text2video' | 'keyframe' | 'fusion' | 'replace' | 'real';
prompt?: string;
imageUrl?: string; // 图生视频需要
startFrame?: string; // 首尾帧视频需要
endFrame?: string; // 首尾帧视频需要
model?: string;
storyboardId?: string;
}
// 响应(异步)
type GenerateVideoResponse = ApiSuccessResponse<{
jobId: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
result?: {
videoId: string;
videoUrl: string;
thumbnailUrl: string;
duration: number;
};
progress?: number; // 0-100
}>;
// Mock 实现
// 返回模拟的任务 ID,模拟进度更新
8.3.8.7 生成音效
// 请求
POST /api/v1/ai/generate-sound
Body: {
prompt: string;
duration?: number; // 音效时长(秒)
model?: string;
}
// 响应(异步)
type GenerateSoundResponse = ApiSuccessResponse<{
jobId: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
result?: {
soundEffectId: string;
audioUrl: string;
duration: number;
};
}>;
// Mock 实现
// 返回模拟的音效 ID 和 URL
8.3.8.8 生成字幕
// 请求
POST /api/v1/ai/generate-subtitle
Body: {
videoId?: string;
prompt?: string;
startTime?: number;
endTime?: number;
model?: string;
}
// 响应
type GenerateSubtitleResponse = ApiSuccessResponse<Subtitle>;
// Mock 实现
// 返回模拟的字幕项
8.3.8.9 生成配音
// 请求
POST /api/v1/ai/generate-voiceover
Body: {
text: string;
voiceType?: string;
startTime?: number;
endTime?: number;
model?: string;
}
// 响应(异步)
type GenerateVoiceoverResponse = ApiSuccessResponse<{
jobId: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
result?: {
voiceoverId: string;
audioUrl: string;
duration: number;
};
}>;
// Mock 实现
// 返回模拟的配音 ID 和 URL
8.3.8.10 查询 AI 任务状态
// 请求
GET /api/v1/ai/jobs/:jobId
// 响应
type GetAIJobResponse = ApiSuccessResponse<{
jobId: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
progress?: number;
result?: any;
error?: string;
}>;
// Mock 实现
// 根据任务 ID 返回任务状态
8.3.9 时间轴管理 API
8.3.9.1 获取项目时间轴
// 请求
GET /api/v1/projects/:projectId/timeline
// 响应
type GetTimelineResponse = ApiSuccessResponse<{
tracks: TimelineTrack[];
items: TimelineItem[];
}>;
// Mock 实现
// 返回项目的时间轴配置
8.3.9.2 更新时间轴
// 请求
PUT /api/v1/projects/:projectId/timeline
Body: {
tracks: TimelineTrack[];
items: TimelineItem[];
}
// 响应
type UpdateTimelineResponse = ApiSuccessResponse<{
tracks: TimelineTrack[];
items: TimelineItem[];
}>;
// Mock 实现
// 更新时间轴配置
8.3.10 其他功能 API
8.3.10.1 获取项目成员
// 请求
GET /api/v1/projects/:projectId/members
// 响应
type GetProjectMembersResponse = ApiSuccessResponse<{
members: Array<{
id: string;
name: string;
avatar?: string;
role: 'owner' | 'editor' | 'viewer';
email?: string;
}>;
}>;
// Mock 实现
// 返回模拟的成员列表
8.3.10.2 获取项目备注
// 请求
GET /api/v1/projects/:projectId/notes
// 响应
type GetProjectNotesResponse = ApiSuccessResponse<{
notes: Array<{
id: string;
content: string;
authorId: string;
createdAt: number;
updatedAt: number;
}>;
}>;
// Mock 实现
// 返回项目备注列表
8.3.10.3 更新项目备注
// 请求
PUT /api/v1/projects/:projectId/notes
Body: {
content: string;
}
// 响应
type UpdateProjectNotesResponse = ApiSuccessResponse<{
id: string;
content: string;
updatedAt: number;
}>;
// Mock 实现
// 更新项目备注
8.3.10.4 获取通知列表
// 请求
GET /api/v1/notifications
Query Parameters:
- unreadOnly?: boolean;
- page?: number;
- pageSize?: number;
// 响应
type GetNotificationsResponse = ApiPaginatedResponse<{
id: string;
type: 'project' | 'comment' | 'mention' | 'system';
title: string;
content: string;
read: boolean;
createdAt: number;
}>;
// Mock 实现
// 返回模拟的通知列表
8.4 API 调用封装
8.4.1 API Client 实现
// src/services/api/client.ts
class ApiClient {
private baseURL: string;
private token: string | null = null;
constructor(baseURL: string) {
this.baseURL = baseURL;
}
setToken(token: string) {
this.token = token;
}
private async request<T>(
method: string,
endpoint: string,
data?: any,
): Promise<T> {
const url = `${this.baseURL}${endpoint}`;
const headers: HeadersInit = {
"Content-Type": "application/json",
};
if (this.token) {
headers["Authorization"] = `Bearer ${this.token}`;
}
const config: RequestInit = {
method,
headers,
};
if (data && method !== "GET") {
config.body = JSON.stringify(data);
}
const response = await fetch(url, config);
const result = await response.json();
if (!result.success) {
throw new ApiError(result.error);
}
return result.data;
}
get<T>(endpoint: string, params?: Record<string, any>): Promise<T> {
const queryString = params
? "?" + new URLSearchParams(params).toString()
: "";
return this.request<T>("GET", endpoint + queryString);
}
post<T>(endpoint: string, data?: any): Promise<T> {
return this.request<T>("POST", endpoint, data);
}
put<T>(endpoint: string, data?: any): Promise<T> {
return this.request<T>("PUT", endpoint, data);
}
delete<T>(endpoint: string): Promise<T> {
return this.request<T>("DELETE", endpoint);
}
}
export const apiClient = new ApiClient(
import.meta.env.VITE_API_BASE_URL || "http://localhost:3000/api/v1",
);
8.4.2 Mock API 实现
// src/services/api/mockApi.ts
import { apiClient } from "./client";
import { mockData } from "../mockData";
// 判断是否使用 Mock
const USE_MOCK = import.meta.env.VITE_USE_MOCK === "true";
// Mock API 实现
export const mockApiClient = {
// 项目相关
getProjects: async (params?: any) => {
if (USE_MOCK) {
// 模拟延迟
await new Promise((resolve) => setTimeout(resolve, 500));
return mockData.projects;
}
return apiClient.get("/projects", params);
},
getProject: async (id: string) => {
if (USE_MOCK) {
await new Promise((resolve) => setTimeout(resolve, 300));
return mockData.projects.find((p) => p.id === id);
}
return apiClient.get(`/projects/${id}`);
},
createProject: async (data: any) => {
if (USE_MOCK) {
await new Promise((resolve) => setTimeout(resolve, 500));
const newProject = {
...data,
id: `proj-${Date.now()}`,
createdAt: Date.now(),
updatedAt: Date.now(),
storyboards: [],
};
mockData.projects.push(newProject);
return newProject;
}
return apiClient.post("/projects", data);
},
// ... 其他 API 方法
};
8.4.3 API Hooks 封装
// src/hooks/useProject.ts
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { mockApiClient } from "../services/api/mockApi";
export const useProjects = (params?: any) => {
return useQuery({
queryKey: ["projects", params],
queryFn: () => mockApiClient.getProjects(params),
});
};
export const useProject = (id: string) => {
return useQuery({
queryKey: ["project", id],
queryFn: () => mockApiClient.getProject(id),
enabled: !!id,
});
};
export const useCreateProject = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: any) => mockApiClient.createProject(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["projects"] });
},
});
};
// ... 其他 Hooks
8.5 环境配置
8.5.1 环境变量
# .env.development
VITE_API_BASE_URL=http://localhost:3000/api/v1
VITE_USE_MOCK=true
# .env.production
VITE_API_BASE_URL=https://api.jointo.ai/api/v1
VITE_USE_MOCK=false
8.5.2 API 切换机制
// src/services/api/index.ts
const isMock = import.meta.env.VITE_USE_MOCK === "true";
export const api = isMock ? mockApiClient : apiClient;
// 使用示例
import { api } from "@/services/api";
// 自动根据环境使用 Mock 或真实 API
const projects = await api.getProjects();
8.6 API 调用清单
8.6.1 需要实现 Mock 的 API(MVP 阶段)
| 功能模块 | API 端点 | 方法 | Mock 状态 |
|---|---|---|---|
| 项目管理 | /api/v1/projects |
GET | ✅ 需要 Mock |
| 项目管理 | /api/v1/projects |
POST | ✅ 需要 Mock |
| 项目管理 | /api/v1/projects/:id |
GET | ✅ 需要 Mock |
| 项目管理 | /api/v1/projects/:id |
PUT | ✅ 需要 Mock |
| 项目管理 | /api/v1/projects/:id |
DELETE | ✅ 需要 Mock |
| 项目管理 | /api/v1/projects/:id/export |
POST | ✅ 需要 Mock |
| 分镜管理 | /api/v1/projects/:id/storyboards |
GET | ✅ 需要 Mock |
| 分镜管理 | /api/v1/projects/:id/storyboards |
POST | ✅ 需要 Mock |
| 分镜管理 | /api/v1/storyboards/:id |
GET | ✅ 需要 Mock |
| 分镜管理 | /api/v1/storyboards/:id |
PUT | ✅ 需要 Mock |
| 分镜管理 | /api/v1/storyboards/:id |
DELETE | ✅ 需要 Mock |
| 资源管理 | /api/v1/resources |
GET | ✅ 需要 Mock |
| 资源管理 | /api/v1/resources |
POST | ✅ 需要 Mock |
| 资源管理 | /api/v1/resources/:id |
DELETE | ✅ 需要 Mock |
| 视频管理 | /api/v1/videos |
GET | ✅ 需要 Mock |
| 视频管理 | /api/v1/videos |
POST | ✅ 需要 Mock |
| 视频管理 | /api/v1/videos/:id |
PUT | ✅ 需要 Mock |
| 视频管理 | /api/v1/videos/:id |
DELETE | ✅ 需要 Mock |
| 音效管理 | /api/v1/sound-effects |
GET | ✅ 需要 Mock |
| 音效管理 | /api/v1/sound-effects |
POST | ✅ 需要 Mock |
| 音效管理 | /api/v1/sound-effects/:id |
PUT | ✅ 需要 Mock |
| 音效管理 | /api/v1/sound-effects/:id |
DELETE | ✅ 需要 Mock |
| 字幕管理 | /api/v1/subtitles |
GET | ✅ 需要 Mock |
| 字幕管理 | /api/v1/subtitles |
POST | ✅ 需要 Mock |
| 字幕管理 | /api/v1/subtitles/:id |
PUT | ✅ 需要 Mock |
| 字幕管理 | /api/v1/subtitles/:id |
DELETE | ✅ 需要 Mock |
| 配音管理 | /api/v1/voiceovers |
GET | ✅ 需要 Mock |
| 配音管理 | /api/v1/voiceovers |
POST | ✅ 需要 Mock |
| 配音管理 | /api/v1/voiceovers/:id |
PUT | ✅ 需要 Mock |
| 配音管理 | /api/v1/voiceovers/:id |
DELETE | ✅ 需要 Mock |
| AI 生成 | /api/v1/ai/generate-image |
POST | ✅ 需要 Mock |
| AI 生成 | /api/v1/ai/transform-angle |
POST | ✅ 需要 Mock |
| AI 生成 | /api/v1/ai/adjust-lens |
POST | ✅ 需要 Mock |
| AI 生成 | /api/v1/ai/generate-storyboard-assets |
POST | ✅ 需要 Mock |
| AI 生成 | /api/v1/ai/generate-resource |
POST | ✅ 需要 Mock |
| AI 生成 | /api/v1/ai/generate-video |
POST | ✅ 需要 Mock |
| AI 生成 | /api/v1/ai/generate-sound |
POST | ✅ 需要 Mock |
| AI 生成 | /api/v1/ai/generate-subtitle |
POST | ✅ 需要 Mock |
| AI 生成 | /api/v1/ai/generate-voiceover |
POST | ✅ 需要 Mock |
| AI 生成 | /api/v1/ai/jobs/:id |
GET | ✅ 需要 Mock |
| 时间轴 | /api/v1/projects/:id/timeline |
GET | ✅ 需要 Mock |
| 时间轴 | /api/v1/projects/:id/timeline |
PUT | ✅ 需要 Mock |
| 其他 | /api/v1/projects/:id/members |
GET | ✅ 需要 Mock |
| 其他 | /api/v1/projects/:id/notes |
GET | ✅ 需要 Mock |
| 其他 | /api/v1/projects/:id/notes |
PUT | ✅ 需要 Mock |
| 其他 | /api/v1/notifications |
GET | ✅ 需要 Mock |
8.6.2 前端纯状态(无需 API)
| 功能 | 说明 |
|---|---|
| Tab 切换 | 前端状态管理 |
| 面板展开/收起 | 前端状态管理 |
| 弹窗显示/隐藏 | 前端状态管理 |
| 选中状态 | 前端状态管理 |
| 预览窗口缩放 | 前端状态管理 |
| 时间轴缩放/滚动 | 前端状态管理 |
| 播放控制(模拟) | 前端状态管理 |
| 新手引导状态 | localStorage |
9. 技术方案
8.1 技术栈选择
8.1.1 核心框架
- React 19:使用最新版本的 React
- TypeScript:类型安全,提升开发效率
- Vite:快速构建工具,开发体验好
8.1.2 状态管理
- Zustand 或 Jotai:轻量级状态管理
- 或 React Context + useReducer:如果状态不复杂
8.1.3 UI 组件库
- 自定义组件:完全按照 Figma 设计稿实现
- 图标库:
lucide-react(与设计稿中使用的图标库一致) - 工具库:
clsx或classnames:条件类名react-dnd:拖拽功能(时间轴拖拽,可选)
8.1.4 样式方案
- Tailwind CSS:推荐,快速开发,易于维护
- 或 CSS Modules:如果偏好传统 CSS
- 或 Styled Components:如果偏好 CSS-in-JS
8.1.5 工具库
- 日期时间:
dayjs或date-fns - HTTP 请求:
axios或fetch - 表单处理:
react-hook-form(如果需要复杂表单) - 动画:
framer-motion或 CSS transitions
8.2 项目结构
jointo/
├── public/ # 静态资源
│ ├── images/ # 图片资源
│ └── mock-data/ # Mock 数据文件
├── src/
│ ├── components/ # 组件
│ │ ├── layout/ # 布局组件
│ │ │ ├── TopBar.tsx
│ │ │ ├── LeftSidebar.tsx
│ │ │ ├── RightSidebar.tsx
│ │ │ └── Layout.tsx
│ │ ├── storyboard/ # 分镜相关组件
│ │ │ ├── StoryboardPanel.tsx
│ │ │ ├── StoryboardItem.tsx
│ │ │ └── StoryboardHeader.tsx
│ │ ├── preview/ # 预览相关组件
│ │ │ ├── PreviewPanel.tsx
│ │ │ └── PreviewControls.tsx
│ │ ├── timeline/ # 时间轴相关组件
│ │ │ ├── TimelinePanel.tsx
│ │ │ ├── TimelineTrack.tsx
│ │ │ ├── TimelineItem.tsx
│ │ │ └── TimeScale.tsx
│ │ ├── resource/ # 资源相关组件
│ │ │ ├── ResourcePanel.tsx
│ │ │ ├── ResourceGroup.tsx
│ │ │ └── ResourceTag.tsx
│ │ ├── video/ # 视频相关组件
│ │ │ ├── VideoPanel.tsx
│ │ │ └── VideoItem.tsx
│ │ ├── common/ # 通用组件
│ │ │ ├── Button.tsx
│ │ │ ├── Input.tsx
│ │ │ ├── Modal.tsx
│ │ │ ├── Tab.tsx
│ │ │ └── Toast.tsx
│ │ └── onboarding/ # 新手引导
│ │ └── OnboardingOverlay.tsx
│ ├── hooks/ # 自定义 Hooks
│ │ ├── useProject.ts
│ │ ├── useStoryboard.ts
│ │ ├── useTimeline.ts
│ │ └── useAIGenerate.ts
│ ├── store/ # 状态管理
│ │ ├── projectStore.ts
│ │ ├── storyboardStore.ts
│ │ ├── timelineStore.ts
│ │ └── uiStore.ts
│ ├── services/ # 服务层
│ │ ├── api/ # API 调用
│ │ │ ├── projectApi.ts
│ │ │ ├── aiApi.ts
│ │ │ └── mockApi.ts
│ │ └── storage/ # 本地存储
│ │ └── localStorage.ts
│ ├── types/ # TypeScript 类型
│ │ ├── project.ts
│ │ ├── storyboard.ts
│ │ └── timeline.ts
│ ├── utils/ # 工具函数
│ │ ├── timecode.ts
│ │ ├── format.ts
│ │ └── validation.ts
│ ├── styles/ # 全局样式
│ │ ├── globals.css
│ │ └── variables.css
│ ├── App.tsx # 根组件
│ └── main.tsx # 入口文件
├── .env # 环境变量
├── .gitignore
├── package.json
├── tsconfig.json
├── tailwind.config.js # Tailwind 配置(如果使用)
└── vite.config.ts # Vite 配置
8.3 关键实现细节
8.3.1 时间轴实现
- 使用 Canvas 或 SVG 绘制时间刻度(性能更好)
- 或使用 CSS + 虚拟滚动(实现简单)
- 支持水平滚动和缩放
- 支持拖拽和调整时间块(可选)
8.3.2 预览窗口实现
- 使用
<img>标签显示预览图 - 支持缩放(CSS transform)
- 支持适应/填充模式切换
- 支持点击放大查看(可选)
8.3.3 AI 功能实现
- 统一封装 AI 请求函数
- 使用 Promise 模拟异步请求
- 返回 mock 数据
- 显示加载状态和结果反馈
8.3.4 状态同步
- 项目切换时,同步更新所有相关状态
- 分镜选择时,同步更新预览、资源、时间轴
- 时间轴操作时,同步更新播放位置
8.4 性能优化
8.4.1 组件优化
- 使用
React.memo避免不必要的重渲染 - 使用
useMemo和useCallback优化计算和函数 - 虚拟滚动处理长列表
8.4.2 资源优化
- 图片懒加载
- 代码分割(路由级别或组件级别)
- 压缩静态资源
8.4.3 状态优化
- 按需加载数据
- 缓存常用数据
- 防抖和节流处理频繁操作
9. 技术方案
10.1 性能要求
- 首屏加载时间:< 3 秒(不含真实 AI 请求)
- 交互响应时间:< 100ms(点击、切换等操作)
- 页面滚动帧率:≥ 60 FPS
- 内存使用:合理控制,避免内存泄漏
10.2 兼容性要求
- 浏览器:Chrome、Edge、Safari 最新版本
- 屏幕分辨率:1920x1080 及以上(主要适配)
- 操作系统:Windows、macOS、Linux
10.3 可用性要求
- 中英文混排:界面不破版,文字正确显示
- 响应式布局:适配不同屏幕尺寸(可选)
- 错误处理:友好的错误提示和处理
- 加载状态:清晰的加载指示
10.4 可维护性要求
- 代码规范:遵循 ESLint、Prettier 规范
- 类型安全:完整的 TypeScript 类型定义
- 组件文档:关键组件添加注释
- 测试覆盖:关键功能添加单元测试(可选)
10.5 可扩展性要求
- API 接口:预留后端 API 接口,易于切换
- 插件化:AI 功能模块化,易于扩展
- 配置化:关键参数可配置
10. 非功能需求
11.1 第一阶段:基础架构搭建(1-2 天)
目标:搭建项目基础结构和核心布局
任务清单:
- 初始化 React + TypeScript + Vite 项目
- 配置 Tailwind CSS 或 CSS Modules
- 配置 ESLint、Prettier
- 创建基础目录结构
- 实现 Layout 组件(TopBar、LeftSidebar、RightSidebar、CenterArea)
- 实现基础样式和变量
- 配置路由(如果需要)
验收标准:
- 项目可以正常运行
- 基础布局结构完整
- 样式系统配置完成
11.2 第二阶段:静态数据展示(2-3 天)
目标:填充所有静态数据和 UI 元素
任务清单:
- 创建 Mock 数据文件
- 实现项目列表组件
- 实现分镜列表组件(6 个分镜)
- 实现预览窗口组件
- 实现时间轴组件(静态显示)
- 实现资源面板组件
- 实现视频/音效/字幕/配音面板组件
- 实现 AI 输入区组件
- 实现所有按钮、图标、文本内容
验收标准:
- 所有 UI 元素完整显示
- 所有文本内容正确
- 所有数据正确展示
11.3 第三阶段:交互逻辑实现(3-4 天)
目标:实现所有交互逻辑和状态管理
任务清单:
- 实现状态管理(Zustand 或 Context)
- 实现项目切换逻辑
- 实现分镜选择逻辑
- 实现 Tab 切换逻辑
- 实现预览窗口交互(缩放、模式切换)
- 实现播放控制逻辑
- 实现时间轴交互(选中、滚动、缩放)
- 实现资源添加/删除逻辑
- 实现分镜编辑逻辑(标题、描述)
- 实现插入新分镜逻辑
验收标准:
- 所有交互功能正常工作
- 状态同步正确
- 用户体验流畅
11.4 第四阶段:AI 功能和弹窗(2-3 天)
目标:实现所有 AI 功能入口和弹窗
任务清单:
- 实现 AI 生成弹窗组件
- 封装 AI API 调用(mock)
- 实现图片提示词功能
- 实现角度变换功能
- 实现镜头调整功能
- 实现所有 AI 生成按钮(分镜、资源、视频、音效、字幕、配音)
- 实现模型选择器
- 实现 Toast 提示组件
- 实现加载状态显示
验收标准:
- 所有 AI 功能入口可点击
- 弹窗显示和交互正常
- Mock 数据返回正确
11.5 第五阶段:新手引导和全局功能(1-2 天)
目标:实现新手引导和其他全局功能
任务清单:
- 实现新手引导浮层组件
- 实现引导步骤切换逻辑
- 实现引导跳过和持久化
- 实现 Cookie 提示
- 实现权限管理弹窗(mock)
- 实现项目备注功能
- 实现设置弹窗(mock)
- 实现通知功能(mock)
验收标准:
- 新手引导流程完整
- 全局功能正常工作
- 持久化功能正常
11.6 第六阶段:优化和打磨(2-3 天)
目标:优化性能、修复问题、完善细节
任务清单:
- 性能优化(组件 memo、虚拟滚动等)
- 交互细节优化(动画、过渡效果)
- 响应式适配(可选)
- 错误处理和边界情况
- 代码重构和优化
- 添加注释和文档
- 测试和修复 bug
验收标准:
- 性能指标达标
- 无明显 bug
- 代码质量良好
- 用户体验流畅
11.7 总计时间估算
- 第一阶段:1-2 天
- 第二阶段:2-3 天
- 第三阶段:3-4 天
- 第四阶段:2-3 天
- 第五阶段:1-2 天
- 第六阶段:2-3 天
总计:11-17 个工作日(约 2.5-3.5 周)
附录
A. 设计稿参考
- Figma 文件:
https://www.figma.com/make/xgPuMR3GZzHpYh0RZJdiYK/kaidong.ai?p=f&fullscreen=1 - 密码:
0592
B. 关键文本内容汇总
B.1 分镜描述文本
- 分镜1:
"清晨的阳光照进窗户,主人公在床上慢慢睁开眼睛,开始新的一天。窗外传来鸟鸣声,窗帘随着微风轻轻飘动。" - 分镜2:
"主人公来到厨房,打开冰箱,拿出牛奶和面包,开始准备早餐。厨房里充满了温馨的气息,阳光从窗户洒进来。" - 分镜3:
"坐在餐桌前,主人公一边享用早餐,一边浏览手机上的新闻。桌上摆放着精致的餐具和一杯热气腾腾的咖啡。" - 分镜4:
"吃完早餐,主人公整理好餐具,将碗筷放进洗碗池,打开水龙头清洗。流水声和餐具碰撞的声音交织在一起。" - 分镜5:
"回到房间,主人公打开衣柜,精心挑选今天要穿的衣服,换好衣服后对着镜子整理仪容。镜子里映出满意的笑容。" - 分镜6:
"拿起背包和钥匙,主人公走向门口,回头看了一眼房间,打开门走出去,开始新一天的旅程。门在身后轻轻关上。"
B.2 AI 提示词示例
- 图片提示词示例1:
"一位中年男性,穿着正装,站在办公室里" - 图片提示词示例2:
"背景是现代化的办公环境,玻璃窗外是城市景观" - 输入框示例:
"@孙悟空 拿着/金箍棒 在 #火焰山 跟@牛魔王 手持/芭蕉扇 互相攻击。"
B.3 新手引导文本
- 步骤1标题:
"项目与素材管理" - 步骤1描述:
"在这里您可以新增、查看和管理项目,同时可以查看和管理项目中的所有素材资源。"
C. 待补充内容
- 新手引导步骤 2-4:需要补充完整内容
- 项目设置选项:需要补充详细设置项
- 权限管理功能:需要补充详细权限选项
- 导出功能:需要补充导出选项和格式
D. 变更记录
- v2.0 (2025-01-27):完整详细版,包含所有 UI/UX 细节和交互逻辑
- v1.0 (2025-01-27):初始版本,基础功能需求
文档结束
本文档将作为开发指南,在开发过程中可根据实际情况进行调整和补充。