Files
localMiniDrama/docs/plans/2026-06-15-drama-canvas-workflow-plan.md
2026-06-30 15:07:31 +08:00

279 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 漫剧画布工作流(LibTV 式)实施计划
> 目标:在 LocalMiniDrama 现有 `project.json` / SQLite 数据之上,增加 LibTV 风格的无限画布视图;列表模式(FilmCreate)与画布模式双视图、单数据源。
**最后更新**2026-06-15(阶段 D:交互增强、媒体对齐、全能模式)
## 设计原则
1. **真源不变**:角色、分镜、图片、视频仍存现有表与 `project.json` 结构。
2. **画布是视图层**:额外持久化 `drama.metadata.canvas_layout`(坐标、视口)与 `workflow_groups`(工作流组)。
3. **旧 JSON 兼容**:无 `canvas_layout` 时自动布局;导入/导出忽略未知 metadata 字段不影响旧版。
4. **列表模式对齐**:画布读取分镜图/视频/首尾帧/全能词的逻辑与 `FilmCreate.vue` 一致(通过 `/images``/videos` API + `storyboardMedia.js`)。
5. **技术栈**Vue 3 + `@vue-flow/core` ^1.48(与 Element Plus / Pinia 一致)。
---
## 阶段 A:只读画布 MVP(已完成)
### 交付物
- [x] 路由 `/film/:id/canvas`
- [x] `FilmCreate` / `DramaDetail` 顶部「画布模式」入口
- [x] `dramaCanvasAdapter.js``drama` API 数据 → nodes/edges 自动布局
- [x] `DramaCanvas.vue`:平移缩放、小地图、集数筛选
- [x] 双击分镜节点 → 跳转列表模式并定位集数
---
## 阶段 B:布局持久化 + 素材侧栏交互(已完成)
### 交付物
- [x] 节点可拖动,debounce 保存 `metadata.canvas_layout`PUT `/dramas/:id/canvas-layout`
- [x] 导出 ZIP 时 `canvas_layout` 写入 `drama.metadata`
- [x] 左侧素材库点击高亮关联分镜与连线
- [x] 画布 ↔ 列表模式双向入口,保留集数筛选 query
- [x] 分镜节点展示生成状态,生成中自动轮询刷新
---
## 阶段 C:工作流编排(整组重跑)(已完成)
### 交付物
- [x] 框选 / Ctrl 多选分镜 →「创建工作流」
- [x] `metadata.workflow_groups` 持久化(随项目导出)
- [x] 流水线配置:生图 → 生视频 → 配音(可勾选)
- [x] 「整组重跑」按组内分镜顺序依次执行
- [x] 分镜节点显示所属工作流标签
### 数据结构(实际实现)
```json
{
"canvas_layout": {
"version": 1,
"viewport": { "x": 0, "y": 0, "zoom": 0.75 },
"nodes": { "sb:12": { "x": 360, "y": 144 } },
"updated_at": "2026-06-15T12:00:00.000Z"
},
"workflow_groups": [
{
"id": "wg-1700000000-abc123",
"title": "第一场批量",
"storyboard_ids": [12, 13, 14],
"pipeline": ["image", "video", "audio"],
"created_at": "2026-06-15T12:00:00.000Z"
}
]
}
```
> 注:早期草案中的 `node_refs` 已改为 `storyboard_ids`(仅存分镜 ID,媒体节点由 adapter 动态生成)。
---
## 阶段 D:交互与媒体对齐(2026-06-15,已完成)
### 交付物
#### 1. 连线与布局
- [x] 连线改为 Vue Flow 贝塞尔曲线(`type: default``curvature: 0.62`
- [x] **默认布局改为竖排**:每个分镜占一行,自上而下;单行内仍为「分镜 → 媒体链」横向展开
- [x] 分镜顺序链改为上下连接(`chain-out` / `chain-in` 锚点)
#### 2. 节点内操作面板(无需切列表模式)
- [x] 单击分镜 / 媒体 / 素材节点,下方展开操作面板;单击空白画布关闭
- [x] 分镜面板:编辑动作、对白、提示词;保存 / 润色 / 生图 / 生视频 / 配音
- [x] 媒体面板:预览 + 对应步骤重跑
- [x] 素材面板:信息展示 + 生成参考图 + 高亮关联分镜
- [x] 面板区域 `nodrag nopan`,避免与画布拖拽冲突
#### 3. 媒体读取与列表模式对齐
- [x] `useCanvasStoryboardMedia.js`:进入画布时按集批量拉取 `/images``/videos`
- [x] `storyboardMedia.js`:首帧 / 尾帧 / 主图 / 视频解析(对齐 `FilmCreate.getSbFirstImage` 等)
- [x] 后端 `dramaService.rowToStoryboard` 补全 `first_frame_*``last_frame_*` 字段
- [x] 首尾帧模式:画布展示 **首帧** + **尾帧** 两个图节点(`sbimg-first` / `sbimg-last`
- [x] 画布内生成视频时传递 `first_frame_url` / `last_frame_url`
#### 4. 全能模式(`creation_mode === 'universal'`
- [x] 不展示空的分镜图节点
- [x] 流水线:`分镜 → 全能分镜词 → 视频`(节点 `sbuni:{id}`kind `universal`
- [x] 分镜卡片显示「全能」徽章;操作面板编辑 `universal_segment_text`,隐藏生图入口
#### 5. 框选与工作流交互修复
- [x] Vue Flow 1.48 移除 `selection-on-drag`,改为 `:selection-key-code="true"` 实现左键框选
- [x] `:pan-on-drag="[1, 2]"`:左键框选,中键/右键平移画布
---
## 默认布局规则(2026-06-15
```
┌─────────────────────────────────────────────────────────────────┐
│ 顶栏:列表模式 | 集数 | 工作流条(创建/选择/整组重跑/删除) │
├──────────────┬──────────────────────────────────────────────────┤
│ 素材库 │ 第1集 │
│ 👤 角色 │ [SB#1] ─→ [文本] ─→ [首帧] ─→ [尾帧] ─→ [视频] │
│ 🏞 场景 │ [SB#2] ─→ ... (竖排,每镜一行) │
│ 🎭 道具 │ [SB#3] ─→ [全能分镜词] ─→ [视频] (全能模式) │
│ 工作流列表 │ │
└──────────────┴──────────────────────────────────────────────────┘
```
- 左栏(x≈48):角色 / 场景 / 道具 + 工作流列表
- 右栏(x≥360):每集标题 + 分镜竖排;单行内媒体节点横向排列
- 虚线(绿色):素材 → 分镜
- 实线(紫色/蓝):分镜 → 媒体、分镜 ↓ 分镜(顺序链)
- 曲线:所有边为贝塞尔曲线
> 已有 `canvas_layout` 的项目仍使用已保存坐标;清除 metadata 中 `canvas_layout.nodes` 可恢复新默认竖排。
---
## 节点 ID 规范
| ID 格式 | 含义 |
|---------|------|
| `char:{id}` | 角色 |
| `scene:{id}` | 场景 |
| `prop:{id}` | 道具 |
| `episode:{id}` | 集标题 |
| `drama:header` | 项目标题 |
| `sb:{id}` | 分镜 |
| `sbtxt:{id}` | 分镜文本摘要(经典模式) |
| `sbuni:{id}` | 全能分镜词(全能模式) |
| `sbimg:{id}` | 分镜图(经典单图) |
| `sbimg-first:{id}` | 首帧图(首尾帧模式) |
| `sbimg-last:{id}` | 尾帧图(首尾帧模式) |
| `sbvid:{id}` | 分镜视频 |
| `sbaud:{id}:dialogue` | 对白音频 |
---
## 用户使用说明
### 画布基本操作
| 操作 | 效果 |
|------|------|
| 左键在**空白处**拖拽 | 框选多个分镜 |
| Ctrl + 点击分镜 | 多选 / 取消选择 |
| 中键 / 右键拖拽 | 平移画布 |
| 滚轮 | 缩放 |
| 单击节点 | 展开下方操作面板 |
| 单击空白 | 关闭操作面板 |
| 双击分镜 | 跳转列表模式并定位 |
| 拖动节点 | 保存布局(debounce |
### 工作流(批量生成)
1. 框选或 Ctrl 选中多个**分镜节点**(带 `#N` 的卡片,不是媒体子节点)
2. 勾选步骤:生图 / 生视频 / 配音(全能模式分镜建议只勾生视频)
3.**创建工作流**,输入名称
4.**选择工作流** 下拉框(或左侧列表)选中该组
5.**整组重跑**:按组内分镜顺序依次执行;某一镜失败则停止
6. **删除工作流** 仅删除分组配置,不删除分镜与媒体
### 经典 / 首尾帧 / 全能 三种流水线展示
| 模式 | 画布媒体链 |
|------|------------|
| 经典 | 文本 → 分镜图 → 视频 →(音频) |
| 首尾帧(`metadata.storyboard_use_first_last_frame`) | 文本 → 首帧 → 尾帧 → 视频 →(音频) |
| 全能(`creation_mode: universal`) | 全能分镜词 → 视频 →(音频) |
---
## 文件结构
```
frontweb/src/
views/DramaCanvas.vue
composables/
useCanvasContext.js # provide/inject 画布上下文
useCanvasStoryboardMedia.js # 批量加载 images/videos
useCanvasWorkflowRunner.js # 单步/整组生成(export runImageStep 等)
utils/
dramaCanvasAdapter.js # drama → Vue Flow 图
canvasLayout.js # layout 解析/持久化
canvasWorkflow.js # workflow_groups CRUD
storyboardMedia.js # 首帧/尾帧/视频 URL 解析(对齐列表模式)
mediaUrl.js
components/dramaCanvas/
CanvasAssetNode.vue
CanvasAssetPanel.vue
CanvasEpisodeNode.vue
CanvasDramaHeaderNode.vue
CanvasLabelNode.vue
CanvasStoryboardNode.vue
CanvasStoryboardPanel.vue
CanvasMediaNode.vue
CanvasMediaPanel.vue
backend-node/src/services/
dramaService.js # rowToStoryboard 含首尾帧字段
```
### API
- `GET /api/v1/dramas/:id` — 项目数据(含 metadata
- `PUT /api/v1/dramas/:id/canvas-layout` — 保存 `canvas_layout` 和/或 `workflow_groups`
- `GET /api/v1/images?storyboard_id=` — 画布加载分镜图列表
- `GET /api/v1/videos?storyboard_id=` — 画布加载分镜视频列表
---
## Vue Flow 配置要点
```vue
<VueFlow
:selection-key-code="true"
:pan-on-drag="[1, 2]"
:pan-on-scroll="true"
:elements-selectable="true"
/>
```
> **勿使用** 已废弃的 `selection-on-drag`@vue-flow/core 1.41+ 已移除,配置了也不生效)。
---
## 风险与规避
| 风险 | 规避 |
|------|------|
| 分镜过多画布过长 | 集数筛选 + fitView + 小地图 + 竖排一镜一行 |
| metadata 合并覆盖 | update 时 merge 现有 metadata |
| 画布与列表媒体不一致 | 统一走 `storyboardMedia.js` + images/videos API |
| 全能模式误触生图 | 全能分镜隐藏生图;工作流勾选项需用户自行判断 |
| 框选无效 | 必须在空白处拖拽;或 Ctrl 点击多选 |
| Vue Flow 包体积 | 画布路由按需加载 |
---
## 后续可选 / TODO
### 画布与工作流
- [ ] 全能模式画布内润色 / 流式编辑全能词
- [ ] 工作流组内分镜顺序可视化拖拽调整
- [ ] 多集同时展示时的节点虚拟化(仅渲染视口内)
- [ ] 画布内首尾帧单独生图(走 frame-prompt 模块,对齐列表模式完整能力)
- [ ] 顶栏工作流区域增加简短帮助 tooltip
### 场景与素材
- [ ] **场景图 → 全景图**:基于已有场景参考图/背景图,AI 扩展或生成超宽/360° 全景图;可用于场景库展示、分镜大景别运镜参考,以及全能模式 `@图片N` 的环境图;需评估与现有 `scenes` 表、`generateImage` / 四视图流程的衔接方式
### 其他
- [ ] 分镜参考图自由上传(列表模式已有部分能力,画布侧统一入口)
- [ ] 参考图自由选择(生成分镜图时手动指定角色/场景参考)