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

11 KiB
Raw Blame History

漫剧画布工作流(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(已完成)

交付物

  • 路由 /film/:id/canvas
  • FilmCreate / DramaDetail 顶部「画布模式」入口
  • dramaCanvasAdapter.jsdrama API 数据 → nodes/edges 自动布局
  • DramaCanvas.vue:平移缩放、小地图、集数筛选
  • 双击分镜节点 → 跳转列表模式并定位集数

阶段 B:布局持久化 + 素材侧栏交互(已完成)

交付物

  • 节点可拖动,debounce 保存 metadata.canvas_layoutPUT /dramas/:id/canvas-layout
  • 导出 ZIP 时 canvas_layout 写入 drama.metadata
  • 左侧素材库点击高亮关联分镜与连线
  • 画布 ↔ 列表模式双向入口,保留集数筛选 query
  • 分镜节点展示生成状态,生成中自动轮询刷新

阶段 C:工作流编排(整组重跑)(已完成)

交付物

  • 框选 / Ctrl 多选分镜 →「创建工作流」
  • metadata.workflow_groups 持久化(随项目导出)
  • 流水线配置:生图 → 生视频 → 配音(可勾选)
  • 「整组重跑」按组内分镜顺序依次执行
  • 分镜节点显示所属工作流标签

数据结构(实际实现)

{
  "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. 连线与布局

  • 连线改为 Vue Flow 贝塞尔曲线(type: defaultcurvature: 0.62
  • 默认布局改为竖排:每个分镜占一行,自上而下;单行内仍为「分镜 → 媒体链」横向展开
  • 分镜顺序链改为上下连接(chain-out / chain-in 锚点)

2. 节点内操作面板(无需切列表模式)

  • 单击分镜 / 媒体 / 素材节点,下方展开操作面板;单击空白画布关闭
  • 分镜面板:编辑动作、对白、提示词;保存 / 润色 / 生图 / 生视频 / 配音
  • 媒体面板:预览 + 对应步骤重跑
  • 素材面板:信息展示 + 生成参考图 + 高亮关联分镜
  • 面板区域 nodrag nopan,避免与画布拖拽冲突

3. 媒体读取与列表模式对齐

  • useCanvasStoryboardMedia.js:进入画布时按集批量拉取 /images/videos
  • storyboardMedia.js:首帧 / 尾帧 / 主图 / 视频解析(对齐 FilmCreate.getSbFirstImage 等)
  • 后端 dramaService.rowToStoryboard 补全 first_frame_*last_frame_* 字段
  • 首尾帧模式:画布展示 首帧 + 尾帧 两个图节点(sbimg-first / sbimg-last
  • 画布内生成视频时传递 first_frame_url / last_frame_url

4. 全能模式(creation_mode === 'universal'

  • 不展示空的分镜图节点
  • 流水线:分镜 → 全能分镜词 → 视频(节点 sbuni:{id}kind universal
  • 分镜卡片显示「全能」徽章;操作面板编辑 universal_segment_text,隐藏生图入口

5. 框选与工作流交互修复

  • Vue Flow 1.48 移除 selection-on-drag,改为 :selection-key-code="true" 实现左键框选
  • :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 配置要点

<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 / 四视图流程的衔接方式

其他

  • 分镜参考图自由上传(列表模式已有部分能力,画布侧统一入口)
  • 参考图自由选择(生成分镜图时手动指定角色/场景参考)