BPMN.js 自定义属性面板:配置审批人、按钮、表单和数据权限
BPMN.js 属性面板可以配置审批人、按钮、表单和数据权限,但不应该把组织用户、表单结构和权限明细全部塞进 BPMN XML。更稳妥的设计是:模型保存稳定引用与策略,业务服务保存可变目录数据,运行时重新解析并执行授权,必要时保存最小证据快照。
这类扩展真正解决的不是“增加几个输入框”,而是建立一套跨设计器、流程引擎和业务系统都能长期演进的模型协议。实现时应把 Properties Provider、moddle、Vue 业务弹窗、CommandStack、后端校验和运行时解析看成一条完整链路。

图 1 属性面板编辑模型引用和策略,业务弹窗负责复杂选择,后端负责发布校验、运行时解析与审计。
一、先回答最重要的问题:配置应该存在哪里
审批配置可以分为三类数据:
| 数据类型 | 示例 | 推荐存储位置 |
|---|---|---|
| 流程语义 | 审批策略、表单引用、按钮策略、字段权限引用 | BPMN XML / moddle 扩展 |
| 业务目录 | 用户、部门、角色、表单版本、按钮定义 | 组织、表单和权限服务 |
| 运行证据 | 实际审批人、按钮授权结果、字段权限快照 | 任务上下文或审计存储 |
BPMN XML 适合保存“如何找到和解释配置”,不适合保存完整用户资料、整张表单 Schema 或动态权限结果。否则人员调岗、表单升级和权限调整都会迫使流程模型重新发布。
建议只保存稳定 ID、解析策略和版本约束。例如审批人保存 policyKey 与参数,表单保存 formKey 与版本策略,按钮保存 buttonPolicyKey,字段权限保存规则引用或小型规则快照。
1. 企业审批属性配置的总体分层
一套可维护的属性配置通常包含五层:
- BPMN.js Modeler:选中节点、导入导出 XML、触发 CommandStack;
- Properties Provider:按元素类型提供属性分组和编辑项;
- 企业 moddle:定义 XML 中的属性、扩展元素和命名空间;
- Vue Bridge:打开审批人、表单、按钮和权限等复杂弹窗;
- 后端服务:发布校验、引用解析、权限授权、快照与审计。
属性面板只是编辑器,不是业务数据库,也不是可信授权端。浏览器里的只读、隐藏和禁用只能改善体验,生产运行时仍要由后端重新计算审批人与操作权限。
二、用 moddle 建立稳定的企业模型协议
企业属性不要混进 Camunda 或其他引擎命名空间,应使用独立命名空间,例如 ent:。这样可以把“企业审批语义”与“引擎执行属性”分开,便于适配 Camunda 7、Camunda 8、Flowable 或其他内核。
{
"name": "Enterprise",
"prefix": "ent",
"uri": "https://example.com/schema/bpmn/enterprise/1.0",
"types": [{
"name": "ApprovalConfig",
"extends": ["bpmn:UserTask"],
"properties": [
{ "name": "assigneePolicyKey", "isAttr": true, "type": "String" },
{ "name": "formKey", "isAttr": true, "type": "String" },
{ "name": "buttonPolicyKey", "isAttr": true, "type": "String" }
]
}]
}
模型协议至少要有四项治理能力:
schemaVersion:标识企业扩展协议版本;- 迁移器:导入旧 XML 时转换字段和默认值;
- 未知扩展保留:普通保存不能静默删除暂不识别的数据;
- 发布校验:检查引用是否存在、可用且属于当前租户。
三、Properties Provider 只负责编辑模型
新版 Properties Panel 通过 getGroups(element) 返回属性分组。Provider 应根据元素类型和设计模式添加企业配置项,修改时调用 modeling.updateProperties() 或 commandStack.execute()。
class ApprovalPropertiesProvider {
constructor(propertiesPanel) {
propertiesPanel.registerProvider(500, this);
}
getGroups(element) {
return groups => {
if (is(element, "bpmn:UserTask")) {
groups.push(buildApprovalGroup(element));
}
return groups;
};
}
}
Provider 不应直接查询组织树或完整表单列表。属性面板会频繁刷新,如果每次刷新都调用后端,就会造成请求风暴、焦点丢失和侧栏闪烁。目录数据可以缓存,复杂选择应交给 Vue 弹窗。
四、审批人配置为什么要建模成策略
审批人不是一个字符串,而是一套运行时解析策略。常见策略包括:
- 指定用户、角色或部门岗位;
- 发起人、发起人部门负责人或逐级负责人;
- 根据表单字段选择人员;
- 由上一个节点或业务服务动态计算;
- 发起时或任务到达时由用户选择。
模型可以保存如下结构:
{
"policyKey": "deptLeader",
"params": { "level": 2, "from": "initiatorDept" },
"emptyPolicy": "error",
"duplicatePolicy": "skip"
}
必须明确解析时点、无人可办、重复审批人、审批人是发起人和组织变更等边界。会签还要保存串行或并行、完成比例、拒绝策略和剩余任务处理方式,不能只保存一个 multiInstance=true。
1. 按钮配置不是前端按钮数组
“同意、拒绝、退回、转办、加签、暂存”等按钮会触发不同的任务命令、权限检查和审计要求。BPMN XML 中应保存按钮策略引用或动作白名单,而不是颜色、CSS 类名和前端事件函数。
| 配置内容 | 应放在模型中 | 应放在业务服务中 |
|---|---|---|
| 动作代码 | approve、reject、rollback |
动作名称、图标、排序 |
| 可用条件 | 表达式或策略引用 | 策略实现与版本 |
| 意见要求 | 必填、选填、隐藏 | 文案、提示与国际化 |
| 后续行为 | 提交、退回、转办 | 命令处理器和审计逻辑 |
运行时必须由后端根据当前用户、任务状态和业务数据重新计算按钮集合。即使前端隐藏了“退回”,也不能认为接口已经安全。
2. 表单绑定和数据权限怎样配合
表单绑定至少要保存 formKey、版本策略和渲染模式。版本策略通常有三种:固定版本、始终使用最新兼容版本、启动实例时冻结版本。审批流程更适合在实例启动或任务创建时冻结关键版本,避免同一实例前后看到不同字段。
数据权限至少包含四个维度:
- 字段可见:用户能否看到字段;
- 字段可编辑:用户能否修改字段;
- 数据范围:子表、关联数据和附件能看到哪些记录;
- 动作约束:提交前校验、脱敏、导出和打印限制。
权限规则可以只保存 permissionPolicyKey,也可以在 extensionElements 中保存小型规则快照。规则经常变化且需要统一治理时使用引用;流程必须长期复现当时规则时保存版本或快照。不要在 XML 中复制完整表单 Schema。
五、复杂选择器如何与 Vue 弹窗连接
组织树、表单设计器和字段权限矩阵不适合挤进窄侧栏。推荐在属性项中显示摘要与“配置”按钮,再通过可注入 Bridge 打开 Vue 弹窗。

图 2 Provider 只传递当前配置,Vue 弹窗返回结构化结果,模型修改统一进入 CommandStack。
export interface DesignerBridge {
selectAssignee(input: AssigneePolicy): Promise<AssigneePolicy | null>;
selectForm(input: FormBinding): Promise<FormBinding | null>;
configureButtons(input: ButtonPolicy): Promise<ButtonPolicy | null>;
configurePermission(input: PermissionPolicy): Promise<PermissionPolicy | null>;
}
弹窗确认后应一次性提交结构化配置,取消时不修改模型。不要用大量 window.showXxxDialog 和全局回调连接属性面板,否则多设计器实例、异步取消和自动化测试都会变得困难。
六、运行时如何解析配置并保存证据
设计时保存的是策略,任务到达时需要把策略解析为可执行上下文:实际办理人、表单版本、可用按钮、字段权限和数据范围。

图 3 运行时从模型读取引用,通过业务服务解析后形成任务上下文,并按审计要求保存快照。
推荐统一输出 TaskContext:
interface TaskContext {
assignees: string[];
form: { key: string; version: string };
actions: string[];
fieldPermissions: Record<string, "hidden" | "read" | "edit">;
resolvedAt: string;
policyVersions: Record<string, string>;
}
以下情况通常需要保存运行快照:审批人依赖组织关系、权限策略可能更新、表单版本可变、流程需要审计复现或任务会长期挂起。快照应保存解析结果与版本证据,不要复制无关个人资料。
七、校验、安全和版本迁移缺一不可
设计时校验建议分三层:
- 输入校验:必填、格式、枚举和表达式语法;
- 模型校验:UserTask 是否绑定审批策略、表单字段是否存在、会签配置是否完整;
- 发布校验:后端按租户、状态和版本重新检查所有引用。
安全上要遵守三个原则:前端隐藏不等于授权;模型 XML 属于不可信输入;运行时按钮和字段权限必须重新计算。导入外部 XML 时,还要限制扩展属性、脚本和表达式的可用范围。
升级 Properties Panel、bpmn-js 或引擎 Provider 时,不能只看页面能否打开。必须比对导入后的业务对象、导出的 XML、未知扩展保留、运行解析结果和旧模型迁移结果。
八、从当前项目出发怎样渐进改造
当前项目已经有独立的 ych-bpm-designer,并集成审批人设置、变量选择、表单选择、按钮列表、字段权限、任务时限和脚本等业务组件。现有能力可以保留,不需要一次性重写。
建议按以下顺序改造:
- 盘点当前 XML 中所有自定义字段和命名空间;
- 建立旧模型导入、导出和运行回归样例;
- 新增独立
ent:moddle 协议与schemaVersion; - 用 Bridge 适配现有 Vue 弹窗,逐步替换全局回调;
- 按审批人、表单、按钮、权限顺序迁移 Provider;
- 建立统一 TaskContext 解析器与后端发布校验;
- 最后升级依赖,并保持新旧模型并行兼容一段时间。
云程低代码开发平台可以复用已有审批配置弹窗,把长期演进重点放在模型协议、Bridge、运行时解析和版本治理上。


九、上线检查清单
- 企业属性是否使用独立命名空间并带
schemaVersion? - XML 是否只保存稳定引用、策略和必要快照?
- 所有模型修改是否进入 CommandStack?
- 复杂选择是否通过可注入 Bridge 打开 Vue 弹窗?
- 审批人无人可办、重复和会签边界是否明确?
- 按钮是否由后端重新授权并记录审计?
- 表单版本是否在明确时点冻结?
- 字段可见、可编辑、数据范围和动作约束是否分开?
- 发布时是否按租户和状态校验所有引用?
- 旧模型、未知扩展、XML 与运行结果是否有回归测试?
十. 如果只记住五句话
- 属性面板编辑模型,不充当业务数据库;
- 企业属性使用独立 moddle 命名空间;
- 复杂选择放进 Vue 弹窗,通过 Bridge 与 Provider 连接;
- 所有修改进入 CommandStack,所有发布经过可信后端;
- 运行时重新解析并授权,需要审计时保存最小证据快照。
做到这些,审批人、按钮、表单和数据权限才会成为可复用、可迁移、可审计的平台能力,而不是绑死设计器和流程引擎的私有字段。

浙公网安备 33010602011771号