Appearance
Feature: 画布系统
一句话目标
用户在画布上管理节点和视口,构建 AI 内容生产流水线的空间容器。
行为约束
约束 1:画布初始化
前置条件: 用户从 Workspace 点击打开一个画布 行为: 系统加载画布数据(节点、边、视口),初始化画布引擎,预加载 text/image/video 三种模型列表 后置条件: 画布中显示所有已保存的节点和连线,视口恢复到上次位置
约束 2:画布不存在时回退
前置条件: 用户访问的 canvasId 在持久化存储中不存在 行为: 显示错误提示 后置条件: 用户可返回 Workspace
约束 3:节点创建 — 工具栏
前置条件: 画布已加载 行为: 用户点击工具栏 "+" 按钮,弹出节点类型对话框,选择类型后在画布中心创建节点 后置条件: 新节点出现在画布中,默认模型已填充,数据已持久化
约束 4:节点创建 — 双击空白区域
前置条件: 画布已加载 行为: 用户双击画布空白区域,在点击位置弹出浮动菜单,选择类型后在该位置创建节点 后置条件: 同约束 3
约束 5:视口操作
前置条件: 画布已加载 行为: 用户可平移(双指滑动/左键拖拽背景)、缩放(滚轮/底部滑块,范围 0.3~2.0) 后置条件: 视口变更自动持久化
约束 6:节点删除
前置条件: 画布中有节点 行为: 用户点击节点删除按钮或选中后按 Delete 键 后置条件: 节点被删除,数据已持久化(关联连线的删除见 edge-system)
状态机
画布页面生命周期
loading ──[数据加载成功]-→ ready
└──[画布不存在]-→ error
ready ──[用户操作]-→ ready(持续交互)
error ──[用户点击返回]-→ [跳转 Workspace]| 状态 | 页面内容 | 用户操作 |
|---|---|---|
| loading | 加载画布数据 + 预加载模型列表 | 不可交互 |
| ready | 画布完整渲染,节点/连线/视口恢复 | 可创建/编辑/连线/缩放 |
| error | 显示错误提示 + 返回 Workspace 入口 | 仅可返回 |
Acceptance Criteria
- [x] AC-01:从 Workspace 打开画布后,节点和连线正确渲染
- [x] AC-02:通过工具栏 "+" 创建三种类型节点
- [x] AC-03:双击空白区域弹出浮动菜单创建节点
- [x] AC-04:缩放范围限制在 0.3~2.0
- [x] AC-05:节点拖拽、视口平移后数据自动持久化
- [ ] AC-06:撤销/重做操作(已实现,有 bug 待修复)
连线相关 AC 已迁移至 edge-system
BDD Scenarios
gherkin
Feature: 画布系统
Scenario: 通过工具栏创建节点
Given 画布编辑器已打开
When 用户点击工具栏 "+" 按钮
And 选择 "文本" 节点类型
Then 画布中出现一个文本节点
And 节点数据已自动持久化
Scenario: 双击空白区域创建节点
Given 画布编辑器已打开
When 用户双击画布空白区域
Then 在双击位置弹出浮动菜单
When 选择 "图片" 节点类型
Then 画布中出现一个图片节点TDD Unit Test Pointers
- [ ]
canvasStore.createCanvas()— 生成合法 UUID、默认名称、空节点/边、默认视口(0,0,zoom=1)→stores/canvasStore.ts - [ ]
canvasStore.loadCanvas(id)— 存在的 id 返回画布对象,不存在的 id 返回 null →stores/canvasStore.ts - [ ]
flowStore.createDefaultNodeData(type)— 三种节点类型各返回正确的默认数据结构和默认模型 →stores/flowStore.ts - [ ]
flowStore.addNode(type, position)— 创建的节点包含 UUID、指定位置、默认数据 →stores/flowStore.ts - [ ]
flowStore.loadFromCanvas()/syncToCanvas()— 节点/边/视口在 Vue Flow 与 canvasStore 之间正确同步 →stores/flowStore.ts - [ ]
isCanvasBlank(target)— 点击画布空白返回 true,点击节点/handle/edge 返回 false →app/utils/canvas.ts
Out of Scope
- 节点内部的编辑逻辑(见各节点 spec)
- 连线的创建、校验、渲染(见 edge-system)