2026-03-31-flow-editor-rebuild-design.md 15.6 KB

Flow Editor 重构需求与安全迁移方案

1. 背景

现有项目 vue-flow-editor 已经长期稳定承载业务,但底层技术栈混杂、构建链老旧,继续在原基座上升级的风险越来越高。当前代码同时存在以下问题:

  • 图编辑内核基于 G6 3.x 时代的能力与自定义 behavior/shape 体系。
  • 工程基座仍是 Vue CLI 风格,难以承接后续现代化构建与测试。
  • UI 侧存在 element-uielement-plus 混用痕迹。
  • 编辑器能力并不只是“画图”,还包含命令系统、插槽扩展、右侧属性面板、预览、业务节点配置等完整交互闭环。

因此本项目不是“升级依赖”,而是一次有严格兼容目标的重构:在新仓库 vue-flow-editor2 中,以 Vue 3 + Element Plus + LogicFlow 2.0 + Vite 重建流程编辑器,并通过自动化测试最大限度保住现有业务行为。

2. 项目目标

2.1 核心目标

  • 新建一个可持续维护的流程图编辑器项目,技术栈统一为 Vue 3、Element Plus、LogicFlow 2.0、Vite。
  • 对老项目关键业务行为保持一致,优先保证“用户怎么操作、系统怎么响应”不变。
  • 在迁移前期先建立自动化回归护栏,再开始大规模功能迁移。
  • 新项目在任一阶段都应可运行、可验证,而不是等到全部完成后再一起联调。

2.2 非目标

  • 不追求源码结构 1:1 复刻。
  • 不追求一次性把所有业务定制表单都重写完再测试。
  • 不把这次重构变成 UI 风格大改版项目。
  • 不在没有测试护栏的情况下直接全量替换老编辑器。

3. 兼容范围定义

本次迁移应以“行为兼容”作为验收标准,至少覆盖以下维度。

3.1 对外 Props 能力

老项目当前已暴露的重要输入包括:

  • data
  • grid
  • miniMap
  • disabledDragEdge
  • disabledUndo
  • height
  • toolbarHeight
  • menuWidth
  • modelWidth
  • onRef
  • toolbarButtonHandler
  • loading
  • multipleSelect
  • beforeDelete
  • afterDelete
  • beforeAdd
  • afterAdd
  • activityConfig
  • controlConfig

3.2 对外事件

老项目已对宿主页面透出的关键事件包括:

  • click-canvas
  • drag-canvas
  • click-node-mousedown
  • click-node
  • dblclick-node
  • dragend-node
  • click-edge
  • dblclick-edge
  • select-change

3.3 对外实例 API

通过 onRef 暴露的能力至少包括:

  • editorState
  • commander
  • openModel
  • closeModel
  • addNode
  • updateModel
  • openPreview
  • read
  • clearStates

3.4 用户操作能力

根据 README 与现有实现,必须优先保留的用户操作有:

  • 工具栏控制:网格、缩略图、适应画布、实际尺寸、放大、缩小、删除、预览、撤销、重做
  • 画布选择:单选、多选、框选、全选
  • 节点操作:拖拽添加、拖拽移动、双击编辑、删除
  • 连线操作:拖拽创建边、边选择、边删除
  • 右侧编辑面板:宿主自定义表单内容展示与关闭/打开
  • 预览能力:按当前图数据输出预览

4. 对旧项目的审计结论

目前可以明确的结论如下:

  • 老项目是“组件库 + demo 文档页”的形式,不是单页应用直接 hardcode。
  • 编辑器稳定性的核心不在样式,而在 commander + graph event + before/after hook 这一套行为层。
  • src/editor/editor.ts 已经将关键操作抽成命令:undoredoswitchGridswitchMiniMapfitViewactualViewzoomInzoomOutdeletedragselectAlladdEdgeaddNodeupdate
  • src/behavior 负责交互行为编排,包括拖拽节点、点击选择、锚点悬浮、拖拽连线、框选、拖动画布。
  • doc/index.vue 是一个高价值基线样板页,已经串起了 onRef、事件监听、增删前后钩子、菜单插槽、复杂右侧表单等真实用法,适合作为自动化基线回归页面。

结论:新项目最应该保护的是“命令语义 + 事件语义 + 宿主接入语义”,而不是老实现里的具体 G6 细节。

5. 可选迁移路径对比

方案 A:直接从老代码重写到新栈

做法:

  • 先搭新仓库。
  • 直接按老功能清单一口气实现 LogicFlow 版本。
  • 最后统一联调和回归。

优点:

  • 前期推进快,文档负担最轻。

缺点:

  • 风险最高。
  • 等到最后才发现偏差时,返工成本极大。
  • 无法证明“老功能没有丢”。

结论:不推荐。

方案 B:先搭新壳子,再边迁边手工点

做法:

  • 先搭 Vite + Vue3 + Element Plus + LogicFlow。
  • 每迁一个能力,人工自己点一遍。

优点:

  • 进度感较强。

缺点:

  • 依赖人工回归,且用户明确没有办法做大量点击验证。
  • 随着功能变多,测试深度必然衰减。

结论:不推荐。

方案 C:先抽行为基线测试,再分阶段替换内核

做法:

  • 先对老项目建立可复用的基线测试和 fixture。
  • 再在新项目中按“数据层、命令层、交互层、业务层”分批迁移。
  • 每迁完一层,就跑同一套或共享大部分逻辑的测试。

优点:

  • 风险最低。
  • 可以做到“先迁测试,再迁功能”。
  • 每一阶段都有明确的退出条件。

缺点:

  • 前期准备时间更多。

结论:推荐,且这是本项目最安全的路线。

6. 推荐总体策略

推荐采用“基线优先 + 分层迁移 + 双轨验证”的方式推进。

核心原则

  • 先定义兼容契约,再写新实现。
  • 先建立自动化回归样板,再迁移核心行为。
  • 每一阶段只替换一个主要风险面。
  • 数据结构适配优先于交互细节重写。
  • 优先保护宿主可感知行为,而不是内部实现完全一致。

双轨验证

双轨验证指:

  • 轨道 1:老项目基线测试,负责定义“过去正确是什么”。
  • 轨道 2:新项目迁移测试,负责验证“现在是否仍然正确”。

理想状态下,同一份 fixture、同一份操作脚本、同一份断言语义,分别跑在旧实现和新实现上,只允许底层引擎不同。

7. 目标架构建议

新项目建议仍然保留“编辑器组件库 + 示例宿主页”的结构,而不是只做一个业务页面。推荐分层如下:

7.1 工程层

  • apps/demo
    • 迁移自旧项目 doc/index.vue 的演示/基线页面
  • packages/flow-editor
    • 真正的编辑器组件库
  • tests
    • 跨项目共享的 fixture、回归脚本、视觉快照、契约测试

7.2 编辑器内部层次

  • core/adapter
    • 旧数据结构与 LogicFlow 数据结构适配
  • core/commands
    • 命令总线、撤销重做、行为封装
  • core/events
    • 统一对外事件派发与兼容转换
  • core/schema
    • 节点/边/锚点/属性 schema
  • features/canvas
    • 画布初始化、缩放、视图控制、grid、miniMap
  • features/selection
    • 单选、多选、框选、全选
  • features/edge
    • 连线创建、校验、edge hook
  • features/node
    • 节点创建、更新、拖拽、删除
  • features/panel
    • 右侧属性面板、预览面板、插槽封装
  • compat
    • 老 props、老事件名、老 onRef API 兼容层

7.3 最重要的设计要求

新项目不要让宿主业务代码直接依赖 LogicFlow 实例。宿主继续依赖兼容层暴露出来的编辑器 API,这样后续底层再演进时成本最低。

8. 分阶段迁移计划

阶段 0:建立旧项目行为基线

目标:

  • 把老项目的“正确行为”变成自动化脚本和 fixture。

产出:

  • 老项目示例页可稳定启动
  • 回归样例数据集
  • 第一批 Playwright 场景
  • 第一批截图快照
  • 第一批命令层契约说明

优先测试场景:

  • 初始图渲染
  • 工具栏按钮可见性与禁用状态
  • 网格开关
  • 缩略图开关
  • 缩放、适应画布、实际尺寸
  • 选择节点/边
  • 多选与框选
  • 删除节点/边
  • 撤销/重做
  • 双击节点打开编辑面板
  • 拖拽添加节点
  • 拖拽创建连线
  • beforeAdd/afterAddbeforeDelete/afterDelete 是否触发
  • onRef 暴露方法是否可调用

退出条件:

  • 至少 70% 的核心主路径操作已有自动化覆盖
  • 至少 3 套真实 fixture 图数据可重复运行
  • 老项目测试结果可稳定复现

阶段 1:新项目基础架构与测试底座

目标:

  • 在新仓库搭出标准工程骨架与测试设施,但暂不急于追求全部功能。

产出:

  • Vite + Vue3 + TypeScript + Element Plus 基础工程
  • LogicFlow 2.0 集成验证页
  • Vitest + Vue Test Utils
  • Playwright E2E
  • 统一 fixture 目录
  • 统一截图基线目录
  • CI 执行脚本

退出条件:

  • 新项目示例页可启动
  • 测试命令可在本地一键执行
  • 至少有 1 个冒烟场景打通

阶段 2:数据兼容与静态渲染

目标:

  • 先让新项目能正确读出旧数据并画出来。

产出:

  • 旧 G6 数据到 LogicFlow 数据的适配器
  • 节点类型、边类型、文本属性映射
  • 静态渲染一致性检查

优先验证:

  • 读入老 fixture 不报错
  • 节点数量、边数量一致
  • 关键 label、坐标、类型、业务字段保留
  • 预览数据导出格式可控

退出条件:

  • 旧 fixture 在新项目中可稳定渲染
  • 核心数据往返读写可验证

阶段 3:命令系统与基础交互迁移

目标:

  • 优先重建最核心的命令语义和用户主操作。

产出:

  • 新版 commander
  • undo/redo
  • zoomIn/zoomOut
  • fitView/actualView
  • delete
  • selectAll
  • 节点拖拽移动
  • 单选/多选/框选

验证重点:

  • 命令是否能回放和撤销
  • 选择状态变化是否正确发出 select-change
  • 删除前后钩子是否触发且支持阻断

退出条件:

  • 主工具栏行为全部通过自动化测试
  • 命令栈回退行为稳定

阶段 4:新增节点、连线与 hook 迁移

目标:

  • 打通完整编辑链路。

产出:

  • 左侧菜单拖入创建节点
  • 锚点拖拽创建边
  • beforeAdd/afterAdd
  • updateModel
  • read
  • clearStates

验证重点:

  • 节点新增后数据正确
  • 边新增后数据正确
  • 新增失败或校验失败时状态可恢复
  • 撤销/重做对新增和连线同样有效

退出条件:

  • “新建节点 -> 连线 -> 编辑 -> 删除 -> 撤销/重做”全链路通过

阶段 5:右侧表单、预览和宿主集成迁移

目标:

  • 迁移复杂业务接入能力,而不是只保留基础画图。

产出:

  • model 插槽
  • menu 插槽
  • foot 插槽
  • 预览弹窗
  • onRef 实例 API 兼容
  • 示例页迁移版

验证重点:

  • 双击节点可打开对应业务表单
  • 宿主可通过 onRef 调用实例方法
  • 工具栏按钮可通过 toolbarButtonHandler 定制

退出条件:

  • 示例页具备替代旧文档页的主要能力

阶段 6:切换与收尾

目标:

  • 从“功能可用”进入“可替换旧项目”的状态。

产出:

  • 迁移文档
  • 差异说明
  • 已知不兼容点清单
  • 发布与回滚方案

退出条件:

  • 核心回归用例通过
  • 关键业务场景通过
  • 已知差异可被接受或已补齐

9. 测试迁移策略

这是本项目最关键的部分。推荐将测试分成四层,而且必须先做前两层再做大规模迁移。

9.1 第一层:行为基线端到端测试

工具建议:

  • Playwright

用途:

  • 在旧项目 demo 页上执行真实点击、拖拽、键盘操作
  • 记录主路径行为
  • 截图关键状态

为什么必须先做:

  • 用户无法手工重复大量点击验证
  • 这是最接近真实使用方式的回归定义

9.2 第二层:共享 fixture + 结果断言

内容:

  • 建立 fixtures/graphs/*.json
  • 建立 fixtures/operations/*.json
  • 把“图数据 + 操作序列 + 预期结果”沉淀成通用规格

示例:

  • base-approval-flow.json
  • multi-branch-flow.json
  • edge-validation-flow.json

预期断言:

  • 节点/边数量
  • 当前选中项
  • 缩放等级
  • 钩子触发次数
  • 导出图数据结构

意义:

  • 未来旧项目和新项目都能吃同一批数据

9.3 第三层:命令与适配器单元测试

工具建议:

  • Vitest

范围:

  • commander
  • 数据适配器
  • hook 包装器
  • 事件兼容转换器

重点:

  • undo/redo 的队列行为
  • delete/add/update 的前后状态恢复
  • 旧数据结构到新结构的兼容转换

9.4 第四层:视觉与布局快照

工具建议:

  • Playwright screenshot
  • 必要时加像素容差比对

范围:

  • 默认画布
  • 节点选中态
  • 多选框选态
  • 预览弹窗

注意:

  • 视觉快照只做辅助,不作为唯一正确性判断

10. “先迁测试再迁功能”的具体执行法

推荐按下面的顺序:

  1. 在老项目上补一个最小可运行的自动化测试工程。
  2. 先把 doc/index.vue 作为官方基线页固定下来。
  3. 先写 8 到 12 条核心操作回归脚本。
  4. 把核心图数据样本沉淀成 fixture。
  5. 新项目初始化后,先把这些 fixture 和脚本目录直接复制或共享过来。
  6. 先让新项目通过静态渲染和简单命令测试,再逐条恢复复杂交互。
  7. 每迁完一个命令簇,就同时补对应单元测试和 E2E。

这里的关键不是“测试写得多”,而是“测试写得可迁移”。测试必须尽量面向:

  • 用户操作
  • 对外 API
  • 输出数据
  • 关键视觉状态

不要让测试过度绑定旧 DOM 结构,否则到了 LogicFlow 后会全部失效。

11. 风险清单与规避方案

风险 1:LogicFlow 与 G6 交互语义不完全一致

规避:

  • 通过适配层屏蔽底层事件差异
  • 将兼容目标定为用户可感知行为一致,而非引擎内部事件完全一致

风险 2:拖拽、框选、连线是最容易出现细微偏差的区域

规避:

  • 优先对这些能力建立 Playwright 操作脚本
  • 每次迁移只处理一类交互

风险 3:复杂业务表单耦合在 demo 页面中,容易阻塞底层迁移

规避:

  • 将“业务表单展示能力”与“编辑器内核能力”拆开迁移
  • 先迁插槽和实例 API,再迁具体表单逻辑

风险 4:撤销/重做在重构后容易悄悄失真

规避:

  • 将命令栈行为做独立单元测试
  • 针对 add/delete/update/drag 各自建立回放测试

风险 5:想一次做完所有节点类型和业务配置

规避:

  • 先做最小节点集
  • 用 fixture 优先覆盖真实高频业务节点
  • 按业务价值排序,而不是按代码文件排序

12. 第一阶段建议交付物

建议我们下一步先做这几件事,而不是直接开写新编辑器:

  1. 初始化新项目文档和计划文件。
  2. 在老项目建立 Playwright 基线测试。
  3. 提取第一批 fixture:
    • 单节点流程
    • 线性审批流程
    • 带分支流程
  4. 梳理旧项目对外契约并固化成 checklist。
  5. 再开始新项目工程初始化。

13. 初始验收标准

只要以下条件满足,就说明这次升级是在“安全轨道”上:

  • 老项目已有可重复执行的自动化回归脚本
  • 新项目已完成基础工程和测试底座
  • 新项目已能渲染老 fixture
  • 新项目已恢复核心命令链:选择、缩放、删除、撤销、重做
  • 新项目已恢复核心新增链:新增节点、新增边、更新节点
  • 新项目已恢复宿主接入链:插槽、事件、onRef

14. 结论

这次最安全的升级方式不是“直接用新栈重写”,而是:

  • 先把老项目变成可测试的行为基线
  • 再用新栈分阶段替换底层
  • 每一阶段都以自动化测试作为继续推进的前提

如果按这个方案执行,虽然前期会比“直接开写”慢一点,但整体成功率和可控性会高很多,尤其适合当前“业务不能出问题、人工回归能力又有限”的情况。