Files
workspace/code/fms/FMS审批流程设置器二次重构实施文档.md
T
2026-09-23 16:54:54 +08:00

25 KiB
Raw Blame History

FMS 审批流程设置器二次重构实施文档

本文承接《FMS审批流程交互重构设计.md》,记录现有 FMS 流程设置器已经完成的部分,以及接下来应基于当前代码推进的第二阶段改造。目标交互参考 D:\workspace\code\test\Workflow-Vue3,尤其是纵向审批主线、递归条件分支、节点间添加按钮、右侧抽屉和错误定位。

这是增量重构,不是从零重做。 先保留已经建立的 FMS 领域草稿、审批规则、条件 AST、校验和 API 边界,再把目前只支持单链路的卡片编辑器扩展为完整的钉钉式流程树。树是编辑视图,FMS 的节点/连线仍是执行配置来源。

1. 本阶段要解决的问题

目前已经具备可用的 FMS 流程领域模型和一版编辑器骨架,但用户体验仍是“线性步骤卡片 + 另一个 LogicFlow 画布”。二阶段的重点不是继续丰富画布,而是让审批、抄送和条件分支都能在同一棵纵向流程树里配置。

1.1 本次交互决策

  • 采用钉钉式流程树作为 FMS 审批设置的主交互,不把高级画布作为本次普通管理员的编辑入口。 这项选择是本次重构的明确目标,不是待 AI 自行比较后再决定的选项。
  • 借鉴的是审批配置的操作方式:纵向主线、节点间“+”、条件分支并列展开、分支内递归、属性抽屉、校验问题定位和缩放;不是逐像素仿钉钉,也不是复制 Workflow-Vue3 的实现。
  • 视觉遵循 FMS 现有主题、控件和组件规范;领域数据仍用 FMS 的节点、连线、审批人规则和条件 AST。
  • 这不等于永久禁止高级画布。只有当真实业务流程无法由已支持的流程树表达,且对应拓扑已有后端执行、校验和模拟语义,并通过无损读写验收后,才单独评估是否增加高级编辑器。不得为了“以后可能用到”而维持两个未完成的编辑模式。

完成后,管理员在一个连续页面内即可:

  1. 看见开始、审批、抄送、条件分支和结束节点;
  2. 在节点之间点“+”选择审批人、抄送人或条件分支;
  3. 展开并编辑各条件分支,分支内允许继续添加节点或嵌套分支;
  4. 在节点属性抽屉中配置审批人、审批方式和条件;
  5. 从错误提示定位到具体节点/分支,校验并保存草稿。

本阶段不改变工作流表结构、实例运行语义、权限和审计规则;也不把参考项目的旧 JSON 结构写入数据库。

2. 现状盘点:保留与改造

下表按当前 fms-vue/src/views/system/workflow 代码盘点。后续开发应在此基础上修改,避免重复实现已具备的能力。

当前文件/能力 当前状态 二阶段处理
design.vue 已有新建向导入口、按流程编码加载草稿、离开未保存确认 保留;新建定义仍需服务端持久化接通后才能获得真实版本 ID
designer/domain.js 已有 schemaVersion 2、表行与领域草稿转换、条件 AST、摘要、单链路操作和图校验 保留既有转换;增加扁平 FMS 图与递归流程树的双向适配、分支结构操作和 round-trip 测试
WorkflowDesigner.vue 已持有统一领域草稿、撤销/重做、节点/连线属性抽屉、选择器数据和版本/校验状态 保留草稿所有权、撤销栈、属性编辑和服务缺失提示;移除编辑器模式状态及画布同步逻辑
WorkflowModeTabs.vue 当前包含“基本信息 / 流程步骤 / 高级画布 / 版本记录”四个页签 移除“流程步骤 / 高级画布”二选一;流程树作为唯一主编辑区。基本信息和版本记录改为顶部入口、抽屉或对话框,不挤占流程画布
WorkflowStepEditor.vue 只识别单链路;遇到分支时提示切换画布;只调用线性插入、移动、复制和删除 改为递归流程树容器,不得再因条件分支拒绝编辑
WorkflowStepCard.vue 已能显示审批/抄送摘要、标签、错误状态和节点操作 保留并按参考项目调整视觉层级;开始/结束作为固定节点,条件分支使用独立分支卡片
WorkflowStepInsert.vue 目前直接显示“添加审批人”和“添加抄送”两个按钮 改为中心圆形“+”按钮,点击弹出“审批人 / 抄送人 / 条件分支”菜单
WorkflowPropertyDrawer.vue 已有节点名称、审批人规则、审批方式、驳回策略、时限、行为和连线条件编辑 保留 FMS 字段和控件;根据节点类型展示内容;条件分支优先级、分支删除等放到分支卡片/分支属性区
WorkflowActorSelector.vue 已有 FMS 审批人来源选择能力 保留;组织角色、岗位等主数据未接入时继续明确提示,不伪造候选项
WorkflowConditionBuilder.vue 已有结构化 AST、字段/操作符/值控件;空条件可表达默认出口 保留作为单分支条件编辑器;补足用户可理解的“其他情况”默认分支体验,并处理嵌套条件编辑能力的限制提示
WorkflowValidationPanel.vue 已能列出错误/警告并按节点定位 保留校验逻辑;改成流程画布上的错误计数入口 + 抽屉/弹窗列表,释放横向分支展示空间
WorkflowCanvas.vue、WorkflowNodePanel.vue、nodes.js 已有 LogicFlow 画布和节点面板,WorkflowDesigner 当前会同步图与领域草稿 不作为本阶段的主编辑入口;移除页面入口及同步路径。确认无其他引用后再决定是否清理文件/依赖
designer/api.js 已有表读取、字段和用户候选项查询,也定义了保存/校验/模拟/发布服务调用 保留当前“服务未接入则明确失败”的保护;不要把 404 当成功。新建、保存、校验、发布需与后端接口逐项验收
tests/unit/workflow-domain.spec.js 已覆盖领域行转换、画布转换、线性操作和发布校验 保留原测试;追加流程树、分支、默认出口、分支汇合和不支持拓扑测试

2.1 现状中的两个实际缺口

  • 当前 WorkflowStepEditor.vue 的分支保护会把用户导向高级画布;二阶段必须改成在同一个流程树里编辑条件分支。
  • 新建向导的确认目前只创建内存中的 WorkflowDraft;若后端创建/保存服务尚不可用,界面必须保持“未保存/服务未接入”,不能显示真实保存成功。

3. 参考项目如何用在 FMS

3.1 优先参考的源码

参考文件 需要借鉴的内容
D:\workspace\code\test\Workflow-Vue3\src\views\setting.vue 顶部流程标题/发布入口、中央流程设置区、缩放控制的页面组织
D:\workspace\code\test\Workflow-Vue3\src\components\nodeWrap.vue 普通节点沿 childNode 递归;条件节点展开多个横向分支;支路内继续递归;分支排序和配置错误提示
D:\workspace\code\test\Workflow-Vue3\src\components\addNode.vue 节点之间圆形“+”按钮和审批人/抄送人/条件分支弹出菜单
D:\workspace\code\test\Workflow-Vue3\src\components\drawer\approverDrawer.vue 审批方式与审批人设置按需出现在右侧抽屉
D:\workspace\code\test\Workflow-Vue3\src\components\drawer\copyerDrawer.vue 抄送人独立配置,不混入审批方式
D:\workspace\code\test\Workflow-Vue3\src\components\drawer\conditionDrawer.vue 条件项编辑、优先级变更和业务字段选择
D:\workspace\code\test\Workflow-Vue3\src\components\dialog\errorDialog.vue 发布/校验错误汇总,再定位到具体节点
D:\workspace\code\test\Workflow-Vue3\src\css\workflow.css 纵向连线、条件横向框、节点悬浮边框、背景层和缩放布局的视觉参考

参考项目采用递归 UI JSON;FMS 不能原样照搬。FMS 页面可以构造临时的递归树投影,但最终变更必须回写当前 WorkflowDraft.nodes / edges,审批人仍进入 actorRules,条件仍使用 AST。

3.2 样式借鉴边界

借鉴形态和空间关系,不复制实现:

  • 用 FMS 自己的 <Button>、<Drawer>、<Popover>、<Tag> 和 Lucide 图标;
  • 样式优先用现有主题变量,如 --fms-background、--fms-card、--fms-border、--fms-primary、--fms-danger;
  • 不复制参考项目的 Element UI、Ant Design 图标字体、远程图片、私有图标字体或整份 workflow.css;
  • 节点配色、圆角和阴影遵循 FMS 已有设计语言,只借鉴参考项目的浅灰流程底色、白色节点卡片、清晰连线和悬浮强调。

4. 二阶段目标页面

┌─────────────────────────────────────────────────────────────────────┐
│ ← 返回   流程名称  [草稿]                 撤销 重做 校验  保存  发布 │
│           基本信息  版本记录(入口,不切换流程编辑器)              │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│                     ● 开始 / 谁可以发起                             │
│                               │                                     │
│                              (+)                                    │
│                    ┌────────────────────┐                           │
│                    │ 部门负责人审批       │                           │
│                    │ 审批人:部门负责人   │                           │
│                    └────────────────────┘                           │
│                               │                                     │
│                              (+)                                    │
│             ┌──── 条件分支:按优先级匹配 ────┐                       │
│             │ 金额 > 10000 │ 其他情况       │                       │
│             │   财务审批   │   抄送财务     │                       │
│             └──────────────┴────────────────┘                       │
│                               │                                     │
│                           流程结束                                   │
│                                    [−] 100% [+] [适应流程]           │
└─────────────────────────────────────────────────────────────────────┘
  • 一个主编辑视图:进入流程后直接看到流程树;不再用页签在“线性步骤”和“高级画布”间切换。
  • 基本信息/版本记录:从顶部按钮打开对应抽屉、弹窗或只读详情;不改变当前流程树编辑上下文。
  • 校验反馈:工具栏显示错误/警告计数;点击后打开可关闭的错误列表。点击一项后关闭列表、滚动至对应节点、突出显示并按需打开属性抽屉。
  • 属性编辑:沿用右侧抽屉,节点主体点击后打开;窄屏时抽屉改为全屏或接近全屏。
  • 缩放控制:固定在流程舞台右下角或右上角,不遮住分支卡片;建议按参考项目 50%–300%、每次 10% 缩放,另有“适应流程”。

5. 流程树与 FMS 领域草稿的适配

5.1 内存树形结构

只在流程编辑视图中使用下列概念结构,不直接当持久化契约:

type FlowItem =
  | { kind: 'step'; nodeKey: string; next: FlowItem | null }
  | {
      kind: 'branch';
      gatewayKey: string;
      branches: Array<{
        edgeKey: string;
        label: string;
        priority: number;
        condition: ConditionAst | null;
        child: FlowItem | null;
      }>;
      next: FlowItem | null;
    };

step 映射 FMS 的开始、人工审批、抄送和结束节点;branch 映射 exclusive_gateway 与它的出边。child 是该分支专属路径,next 是各分支汇合后的公共后续。

5.2 适配约束

  1. 新增 workflowDraftToTree(draft) 和 workflowTreeToDraft(tree, baseDraft) 一类的纯函数,集中处理转换;Vue 组件只发操作意图,不自己拼 edges。
  2. 每个条件块对应一个 exclusive_gateway;每个分支对应一条网关出边。条件 AST 放在边上,边优先级稳定地表达匹配顺序。
  3. 一个条件块必须有且仅有一个 condition: null 的“其他情况”边;它在 UI 中固定为最后一项,不能被删除、编辑成普通条件或拖到前面。
  4. 条件分支最少有一个显式条件和一个默认分支。新增分支时生成空条件并显示错误状态;发布校验必须阻止空条件被误当默认分支。
  5. 各分支专属路径的末端连接到公共 next。若某分支没有子节点,则它直接连到公共后续节点并保留自身条件。
  6. 扩展 validateWorkflowDomain:开始、人工审批和抄送节点在本阶段支持的树形流程中必须恰有一条出边;结束节点没有出边;条件网关至少两条出边且恰有一个默认出口;普通连线不得携带条件;分支优先级在树投影中必须连续且唯一,写回时按此顺序生成边的 b_priority。
  7. 对已有领域图构树时,必须识别唯一开始点、gateway 出边、条件边、默认出口和分支汇合点。遇到并行网关、服务节点、环、交叉边或无法唯一识别汇合的拓扑,返回明确的不支持状态,不显示一个可写的简化树。
  8. 树编辑完成必须写回同一份领域草稿,wf_node、wf_edge、wf_actor_rule 及版本 checksum 语义保持不变。不新增 childNode 持久化 JSON、卡片顺序表或审批人固定列。

5.3 分支图操作规则

  • 插入条件块:A → B 变为 A → gateway → case paths → B;各 case 的出边优先级和 AST 必须同时更新。
  • 在某个分支中插入节点:只在该分支专属路径上将 X → Y 改为 X → 新节点 → Y。
  • 在分支框之后添加节点:这是所有分支共用的后续,树的 next 指向该节点;不能错误地把它插入某一个分支。
  • 调整条件优先级:只交换显式条件顺序,默认分支仍在最后;同步更新边优先级和卡片编号。
  • 删除条件块:先展示每个分支及其子节点数量;用户确认后将网关整体移除,并按明确的保留路径策略连接前后节点。不得直接丢弃分支配置。
  • 删除分支:如果有子树必须二次确认;剩余分支不足两条时不允许留下无意义 gateway。

6. 页面与组件改造清单

6.1 WorkflowDesigner.vue

  • 保留领域草稿、撤销/重做、数据选择器、校验、抽屉编辑、未保存拦截和服务失败提示。
  • 移除 EDITOR_MODES.CANVAS / mode 分支、canvasRef、onGraphChange、onDragNode 和画布文本镜像逻辑。
  • WorkflowValidationPanel 改为可开关的 Drawer/Modal;错误定位调用流程树暴露的 focusNode(nodeKey),包含 edge 错误时定位 gateway/条件分支。
  • 在工具栏增加“流程信息”“版本记录”入口,保留基础信息数据和版本列表逻辑,但不要再由页签卸载/切换主编辑器。
  • 结构变更继续通过 commit(nextDraft) 入撤销栈;树组件卸载/重建不应清空历史。

6.2 流程树组件

建议把当前 WorkflowStepEditor.vue 改成 WorkflowTreeEditor.vue(或保留文件名但彻底替换内部逻辑),再拆出:

WorkflowTreeEditor.vue       树投影、滚动舞台、快捷定位
WorkflowTreeNode.vue         普通节点递归与节点间插入入口
WorkflowBranchGroup.vue      条件横向分支、优先级、每个分支的子树和公共后续
WorkflowNodeInsert.vue       圆形“+”与审批/抄送/分支菜单
WorkflowTreeNodeCard.vue     开始、审批、抄送、结束摘要卡片

可以合并组件以控制复杂度,但“树转换逻辑不能散落在递归组件里”这条边界必须保留。组件操作通过 emit('operation', payload) 上报,纯图操作由 domain.js 生成 nextDraft。

6.3 domain.js 和常量

  • 保留 rowsToWorkflowDraft、draftToSavePayload、validateWorkflowDomain、摘要函数及条件 AST 工具。
  • 保留 inspectLinear 和线性操作用于迁移测试或单链路快捷逻辑,但它不能再决定“是否允许进入流程树”。
  • 新增树投影、结构签名、gateway 分支解析、分支插入/重排/删除/复制操作。
  • 扩展错误结构,使错误可包含 nodeKey、edgeKey/gatewayKey 和分支优先级,供 UI 稳定定位。
  • 不要为了 UI 顺序变更节点主键;nodeKey、edgeKey 在本版本内必须稳定。

6.4 插入菜单、卡片、属性抽屉

  • WorkflowStepInsert.vue 改为一个“+”按钮和 Popover 菜单,操作项为审批人、抄送人、条件分支。
  • WorkflowStepCard.vue 可复用摘要计算,但条件块要独立渲染“条件名 + 条件摘要 + 优先级 + 错误态”。
  • WorkflowPropertyDrawer.vue 继续复用;审批人/抄送人选择仍通过 FMS WorkflowActorSelector,条件值仍通过 WorkflowConditionBuilder。
  • 条件分支的“其他情况”在卡片文案中明确说明,不要求管理员打开抽屉填写一条空 AST。

7. 视觉规范:贴近参考项目,同时遵循 FMS

7.1 页面和节点外观

  • 流程舞台使用 FMS 背景 token 的浅灰层次;画布内容按树的自然高度展开,不再绘制 LogicFlow 网格。
  • 普通节点采用白底、有轻阴影、细边框和小圆角卡片;节点类型用小图标/色条区分,不使用参考项目的私有字体图标。
  • 节点 hover/selected 使用 --fms-primary 边框和轻微光环;错误使用 --fms-danger;警告使用 --fms-warning。
  • 参考项目节点宽约 220px。FMS 摘要更长,初版节点卡片可从 260px 左右起步;分支列设合理最小宽度,不随窄屏无限压缩文字。
  • 标题与摘要分层:标题行展示图标、名称、类型和操作入口;正文展示审批人/抄送人、审批方式、时限等摘要,最多 2–3 行后截断。
  • 开始/结束节点做轻量端点,不画成普通审批卡片;开始节点可显示发起范围摘要,结束节点固定显示“流程结束”。

7.2 连线、分支和按钮

  • 单链路使用居中竖向细线;节点间添加按钮落在线上,按钮尺寸和弹出菜单的点击面积满足 FMS 控件规范。
  • 条件块外框按参考项目呈横向分支:分支列顶部/底部连接线清楚,分支内流程独立纵向排列,分支后公共节点位于整个条件块下方中线。
  • 多分支超出视口时横向滚动舞台;不缩小分支卡片到不可读,也不让浏览器整页出现横向滚动。
  • 条件块顶部显示“添加条件”操作;分支头显示名称、优先级、条件摘要、移动操作和错误状态。
  • 分支连接线优先用稳定的 CSS 结构/伪元素实现;分支数量动态变化时若 CSS 不可靠,可单独使用 SVG 画连接线,但不能因此恢复通用图画布。

7.3 缩放与响应式

  • 借鉴 setting.vue 的 50%–300% 缩放区间和步进交互,但缩放状态只属于视图,不改变节点/连线语义。
  • 缩放容器设置清楚的 transform-origin,同时测量内容宽高;不能只缩放 CSS 视觉却留下错误的滚动区域。
  • “适应流程”以主流程和分支总宽度为基准;长流程可垂直滚动,宽分支可水平滚动。
  • 小屏布局时保留分支的横向结构并让舞台滚动;属性抽屉占满可用宽度;工具栏按钮折入更多菜单。
  • 缩放、滚动、弹窗打开/关闭、窗口 resize 后,错误定位仍要把目标卡片滚动到可视区域。

8. 分阶段实施和回归

阶段 0:基线确认

  1. 阅读《FMS审批流程交互重构设计.md》、本文件和《FMS工作流与审批设计.md》;本文件决定二阶段的增量执行顺序。
  2. 记录现有单链路草稿样例、审批人规则和条件 AST;执行现有工作流领域测试,确认重构前基线。
  3. 确认 exclusive_gateway 的运行语义和边优先级;确认条件支路合流是否被流程服务支持。若后端尚未实现,先锁定接口契约,不在前端猜运行规则。

阶段 1:树图适配和保护

  1. 先实现 FMS 图 → 树投影和树 → FMS 图转换纯函数。
  2. 使用固定 fixtures 覆盖:线性流程、两个条件、三条件排序、分支内多个步骤、分支后公共审批、嵌套条件、无默认出口、重复默认出口、并行/环/交叉边。
  3. 对不支持的拓扑返回只读保护提示,不允许 UI 提交修改后的扁平草稿。
  4. 确认加载→构树→不编辑→编图→保存载荷的节点/边/规则语义一致后,再做页面组件。

阶段 2:递归流程树 UI

  1. 移除 WorkflowStepEditor 的“只能编辑单链路”分支和“切换到高级画布”按钮。
  2. 加入递归节点、条件分支组、公共后续和新的“+”菜单。
  3. 接入选中状态、属性抽屉、错误标记和校验面板定位。
  4. 对插入、删除、复制、移动、重排和撤销/重做补上确认与回归测试。

阶段 3:钉钉式样式与页面收口

  1. 按本文件第 7 节调整舞台、线条、节点卡片、条件列和缩放工具。
  2. 将固定右栏错误面板改为可收起/弹出的校验列表,保证宽分支有足够空间。
  3. 基本信息、版本记录保持可访问,但不让其与主编辑器形成“流程步骤/高级画布”模式切换。
  4. 完成常规 1440×900、1366×768 和窄屏浏览器的截图/人工走查。

阶段 4:清理旧入口与验收

  1. 全局搜索 WorkflowCanvas、EDITOR_MODES.CANVAS、WorkflowModeTabs 及 LogicFlow 适配器引用。
  2. 先删除页面入口和死掉的状态同步;确认所有引用后再清理组件与依赖,不直接批量删除未知文件。
  3. 保存/发布服务缺失时维持明确提示;接服务后验证版本只读、并发、幂等和服务端校验。
  4. 执行工作流单测、前端构建和人工交互验收。

9. 必测场景

领域图和树之间

  • 开始 → 审批 → 结束 往返不改变任何节点/边编码或配置。
  • 插入条件后有“条件 A / 其他情况”两条路径;每条路径可添加审批或抄送。
  • 条件分支之后添加公共审批节点,两个 case 都能正确汇入该节点。
  • 条件路径中继续插入第二层条件,保存重开后嵌套层级不变。
  • 变更条件优先级只更改显式分支次序,默认分支一直在末尾。
  • 空条件、缺失字段、重复默认边和无出口节点能被准确报错。
  • 遇到不支持的并行、服务、循环或交叉拓扑时,不呈现可覆盖保存的简化树。

页面交互

  • “+”菜单正确插入三种类型,并将焦点打开到新节点属性配置。
  • 点击节点摘要打开对应抽屉;审批和抄送不显示彼此无关的字段。
  • 删除有子节点的分支前显示影响范围;取消操作完全不改草稿。
  • 校验列表定位到嵌套分支中的节点;缩放和滚动后仍能看到高亮目标。
  • 撤销/重做覆盖树结构和节点配置;切换基本信息/版本列表后返回,草稿和滚动体验合理。
  • 发布版本只读;服务缺失或发布失败不会显示成功状态。

10. 验收命令和完成标准

在 fms-vue 目录执行:

pnpm exec vitest run tests/unit/workflow-domain.spec.js
pnpm test
pnpm build

若改动涉及全局格式或 lint,再补:

pnpm lint
pnpm fmt:check

完成标准不是“有一个看起来像流程图的页面”,而是管理员不切换编辑模式就能配置完整的审批树;编辑器始终准确维护 FMS 节点/连线草稿;无法安全表达的旧图被保护;界面风格与参考项目接近,但不破坏 FMS 组件和主题体系。

11. 不要做的事

  • 不要重写或替换 WorkflowDraft、版本状态和 wf_* 领域表。
  • 不要把参考项目的 nodeConfig.childNode JSON 当成新的数据库契约。
  • 不要在组件模板里直接增删 draft.edges;结构修改必须集中在领域操作函数。
  • 不要把条件 AST 空值、默认分支和普通“条件未填”混为一谈。
  • 不要让一个条件 gateway 出现多个默认出口,也不要通过分支显示顺序隐式代替 b_priority。
  • 不要在后端服务未接入时伪造保存、模拟、校验或发布成功。
  • 不要未检查全项目依赖引用就删除 LogicFlow 包或共享组件。
  • 不要复制参考项目的旧 UI 组件、私有图片、远程 icon 或 Element UI 样式;只在 FMS 组件体系下复现交互和视觉结构。