BPMN.js 自定义工具栏、节点图标和右键菜单

把 BPMN.js 嵌入 Vue 页面后,企业通常会继续增加顶部工具栏、企业节点、节点图标和右键菜单。真正的难点不是"把按钮画出来",而是让所有入口共享同一套动作、权限、建模规则和撤销机制。

结论可以概括为四句话:顶部业务工具栏由 Vue 实现;Palette、Context Pad 和 Popup Menu 通过 Provider 扩展;节点持久图标使用 Custom Renderer,运行状态使用 Overlay;所有模型写操作进入 Modeling 或 CommandStack,并由 Rules 与后端发布校验共同约束。

图 1 不同入口负责不同交互场景,但底层动作和权限策略应统一。

一、先分清四种操作入口

"工具栏"在流程设计器中常被混用,至少应拆成四类:

入口 位置与用途 推荐扩展点
顶部工具栏 保存、撤销、缩放、校验、发布 Vue 组件调用公开服务
Palette 创建任务、网关和企业节点 PaletteProvider
Context Pad 选中节点后的追加、连接、删除 ContextPadProvider
右键菜单 配置、复制、转换、删除 element.contextmenu + PopupMenuProvider

四类入口可以显示不同动作,但不要分别实现业务逻辑。推荐建立统一 actionCatalog,再通过 actionPolicy 判断当前用户、元素、设计模式和只读状态是否允许执行。

二、BPMN.js 扩展应该怎样分层

BPMN.js 建立在 diagram-js 和 bpmn-moddle 之上。前者提供画布、事件总线、Palette、Context Pad、Popup Menu、Modeling 和 CommandStack;后者负责 BPMN 元模型与 XML 读写。企业扩展应围绕公开服务和模块注入组织,而不是直接操作 SVG DOM。

javascript 复制代码
export default {
  __init__: [
    "enterprisePalette",
    "enterpriseContextPad",
    "enterpriseRenderer",
    "enterprisePopupMenu",
    "enterpriseRules"
  ],
  enterprisePalette: ["type", EnterprisePalette],
  enterpriseContextPad: ["type", EnterpriseContextPad],
  enterpriseRenderer: ["type", EnterpriseRenderer],
  enterprisePopupMenu: ["type", EnterprisePopupMenu],
  enterpriseRules: ["type", EnterpriseRules]
};

建议按五层组织代码:

  1. Vue 应用层:页面、业务工具栏、弹窗、路由和发布权限;
  2. Provider 层:Palette、Context Pad、Popup Menu 条目;
  3. 建模服务层:Create、AutoPlace、Modeling、CommandStack;
  4. 规则与协议层:Rules、moddle 扩展、后端发布校验;
  5. 渲染层:Custom Renderer、图标注册表和 Overlay。

三、顶部工具栏为什么留在 Vue

保存草稿、发布版本、打开评审和切换环境属于宿主应用能力,不是 BPMN 图形建模行为。让 Vue 工具栏通过 modeler.get() 调用公开服务,能避免查询 bpmn-js 内部 DOM 或依赖私有字段。

typescript 复制代码
const commandStack = modeler.get("commandStack");
const canvas = modeler.get("canvas");

function undo() {
  if (commandStack.canUndo()) commandStack.undo();
}

function zoomIn() {
  canvas.zoom(canvas.zoom() + 0.1);
}

发布按钮也不应在浏览器中直接携带流程引擎凭证。正确链路是:浏览器提交草稿修订号,平台后端重新读取模型、执行权限与语义校验、冻结不可变版本,再由服务端适配器部署到目标引擎并记录审计。

四、Palette 和 Context Pad 怎样扩展

Palette 负责创建元素,Context Pad 负责对当前元素执行高频动作。两者应使用 Create、AutoPlace、Modeling 等服务产生建模命令,不能手写 XML 或直接插入 SVG。

javascript 复制代码
getPaletteEntries() {
  return {
    "create-approval-task": {
      group: "activity",
      className: "icon-approval-task",
      title: "创建审批节点",
      action: {
        dragstart: e => this.create(e),
        click: e => this.create(e)
      }
    }
  };
}

企业节点优先使用标准 BPMN 类型加业务属性。例如"审批节点"仍可使用 bpmn:UserTask,通过 moddle 扩展保存审批人策略、表单、按钮和数据权限。只有标准类型无法表达结构语义时,才考虑新增自定义类型。

Provider 只负责返回条目,不要在 getPaletteEntries()getContextPadEntries() 中请求后端。权限、字典和节点模板应提前缓存,否则每次选中节点都会触发异步请求,造成菜单闪烁和性能问题。

五、节点图标应该放在哪一层

Palette 图标、模型节点图标和运行时状态图标不是同一件事:

  • Palette 图标只是创建入口,可以使用 bpmn-font 或 CSS 图标;
  • 模型节点图标属于持久语义,适合由 Custom Renderer 绘制;
  • "运行中、已完成、异常"等状态不属于模型,适合使用 Overlay。

图 2 业务对象保存稳定图标键,Renderer 从白名单注册表解析 SVG,运行状态不写回模型。

不要把任意 SVG 或外部 URL 直接保存到 BPMN XML。更安全的方式是保存稳定键值,例如 yc:iconKey="approval",再从前端白名单图标注册表中解析。

javascript 复制代码
class EnterpriseRenderer extends BaseRenderer {
  canRender(element) {
    return !element.labelTarget &&
      is(element, "bpmn:UserTask") &&
      !!element.businessObject.iconKey;
  }

  drawShape(parent, element) {
    const shape = this.bpmnRenderer.drawShape(parent, element);
    appendIcon(shape, iconRegistry[element.businessObject.iconKey]);
    return shape;
  }
}

如果改变节点几何尺寸,还要同步处理连接点和 getShapePath();如果只是增加角标或中心小图标,尽量复用默认 BpmnRenderer 画出的基础形状,减少与升级版本的耦合。

六、右键菜单应该怎样实现

element.contextmenu 是事件入口,Popup Menu Provider 是菜单条目提供者,两者不是同一个概念。事件处理器负责阻止浏览器默认菜单、换算可视区域坐标并打开指定 Provider;Provider 根据元素类型和权限返回可用动作。

图 3 右键事件只负责打开菜单,动作过滤交给策略层,模型变更最终进入建模内核。

javascript 复制代码
eventBus.on("element.contextmenu", 1500, event => {
  event.originalEvent.preventDefault();
  if (!canOpen(event.element)) return;

  popupMenu.open(event.element, "enterprise-actions", {
    x: event.originalEvent.clientX,
    y: event.originalEvent.clientY
  });
});

右键菜单至少应过滤 Label、根元素、只读模式和无权限动作。复制、删除、转换类型、修改属性等写操作必须调用 Modeling 或自定义 CommandHandler,不能直接修改 businessObject,否则撤销、重做、脏状态和相关图形更新都会失效。

右键不是唯一入口。键盘用户和触屏用户无法稳定使用右键,因此关键动作还应出现在顶部工具栏、Context Pad 或可聚焦的更多菜单中。

七、所有入口如何共享动作和权限

统一动作目录可以把显示条件与执行逻辑分开:

typescript 复制代码
const actions = {
  delete: {
    visible: ctx => !ctx.readonly && ctx.policy.canDelete(ctx.element),
    execute: ctx => ctx.modeling.removeElements([ctx.element])
  },
  configure: {
    visible: ctx => ctx.policy.canConfigure(ctx.element),
    execute: ctx => ctx.bridge.openConfig(ctx.element.id)
  }
};

Palette、Context Pad、右键菜单和顶部工具栏只把自身上下文传入动作目录。对于真正修改模型的动作,再由 Rules 判断连接、移动、删除或替换是否合法。前端规则改善建模体验,但导入 XML 和发布请求仍要经过后端校验,因为前端可以被绕过。

只读模式也不能只靠隐藏按钮。纯查看优先使用 Viewer;需要选择、缩放和查看属性时,可使用受限 Modeler,但要同时禁用键盘编辑、直接编辑、拖拽、Context Pad、Palette 和程序化命令入口。

八、从当前项目看,精简改造从哪里开始

当前项目的 ych-bpm-designer 已经组装了 Modeler、Viewer、Properties Panel、Camunda Provider、moddle 扩展以及 simpleDesign / simpleRender,并在 UserTask 属性中加入候选人、多实例、自动选人和审批操作等能力。

这套基础适合采用渐进方式治理:

  1. 先建立统一动作目录和权限策略;
  2. 把顶部工具栏改为只调用公开服务;
  3. 将企业节点创建收敛到 Palette Provider;
  4. 用稳定 iconKey 和注册表管理节点图标;
  5. 用 Popup Menu Provider 承载右键动作;
  6. 把所有模型写操作收敛到 Modeling 或 CommandStack;
  7. 增加 XML、SVG、条目集合和撤销重做回归测试;
  8. 最后再升级较旧的 bpmn-js 依赖并减少对内部源码的修改。

云程低代码开发平台可以保留当前审批属性与简洁渲染能力,同时把交互扩展逐步迁移到公开模块边界,降低后续升级和多人协作维护成本。

九、上线检查清单

检查对象 必查项
顶部工具栏 撤销状态来自 CommandStack;发布只调用可信后端
Palette / Context Pad 条目按模式和权限过滤;创建动作进入 Create/AutoPlace
Renderer canRender() 排除 Label;图标来自白名单;SVG 导出已回归
右键菜单 坐标正确;过滤根元素和只读模式;有键盘与触屏替代入口
模型变更 全部进入 Modeling/CommandStack;Rules 与后端共同校验
升级与测试 锁定依赖;不修改 node_modules;覆盖 XML、SVG 和撤销重做

十、如果只记住六句话

  1. 顶部业务工具栏留在 Vue;
  2. Palette、Context Pad 和 Popup Menu 使用 Provider;
  3. 企业节点优先使用标准 BPMN 类型加业务属性;
  4. 持久图标用 Renderer,运行状态用 Overlay;
  5. 所有写操作进入 Modeling 或 CommandStack;
  6. 前端 Rules 负责体验,后端校验负责可信发布。

这六条能让工具栏、节点图标和右键菜单继续扩展,同时保持模型语义、撤销能力、权限边界和版本升级路径一致。

相关推荐
大龄码农有梦想2 天前
仿钉钉流程模型如何转换成 BPMN 2.0?
工作流引擎·流程引擎·bpmn.js·oa·流程审批·仿钉钉流程设计器·bpmn流程设计器
愚农搬码5 天前
Camunda 7 与 Camunda 8 不是升级关系:架构差异与选型边界
工作流引擎
天丁o14 天前
我把一套 Flowable + Spring Boot + Vue 的 BPM 流程引擎整理开源了:从流程设计器到待办闭环
spring boot·vue·工作流引擎·flowable·bpm流程引擎·流程审批
昭阳16 天前
我的AI工作流整理【个人向】
人工智能·工作流引擎
Yao80618 天前
Warm-Flow工作流引擎入门:比Flowable轻量在哪
工作流引擎
愚农搬码24 天前
Agentic AI、AI Agent、AI 工作流有什么区别?
agent·ai编程·工作流引擎
驰骋工作流1 个月前
流程引擎BPM设计之:流程消息
java·工作流引擎·bpm·jflow·ccflow
驰骋工作流1 个月前
开源 BPM 工作流引擎六方对比选型分析(java领域)
开源·工作流引擎·flowable·camunda·jflow·ccflow