欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 文化 > bpmn-js 扩展元素模型

bpmn-js 扩展元素模型

2025/3/18 19:32:15 来源:https://blog.csdn.net/weixin_43359503/article/details/139986847  浏览:    关键词:bpmn-js 扩展元素模型

✨✨✨目前成都的"小学生"大佬和作者一起开发了 Flowable 流程引擎组件(包含前端设计器与后端流程引擎)。

该组件与 Flowable 流程引擎深度融合,结合实际业务场景和使用方式,对属性编辑面板进行了重新设计,优化了用户体验。 增加了符合业务场景的流程校验与进度预览、引入富文本编辑器与代码编辑器。 结合后端引擎,可直接嵌入系统中使用。

详情请访问:https://www.bpmport.com/products ;

设计器预览:

  1. 编辑器:designer,
  2. 预览与模拟:viewer,
  3. DMN决策设计器:dmn

image.png

在开发流图编辑器的过程中,为了深度适配后端 flowable 引擎的功能,经常需要对 bpmn-js 进行扩展,主要分为两个部分:propertie-panel 和 bpmn model。

之前有介绍过,bpmn-js 是由 camunda 团队开发的,用来实现 BPMN 2.0 规则的流程图绘制;结合 bpmn-js-properties-panel 可以完美适配 Camunda 流程引擎。

但是,官方提供的 properties-panel 基本上很难满足国内的使用场景(主要还是 UI 以及交互方式),并且无法适配 flowable 和 activiti。

所以基本上都需要开发人员根据 bpmn-js 提供的 API 来单独实现 properties-panel,而关于 properties-panel 的实现我们在之前的文章中也有讲述过,这里不再赘述。

另外一个问题:bpmn model,则是与 Bpmn.js自定义描述文件说明 中的内容有关。

BPMN Model

关于 bpmn model 的具体概念,很难有一个具体的说法,大致解释就是:通过一个 JSON Schema 来对 BPMN 2.0 规则的 XML 文件内的标签以及属性进行描述。

在 bpmn-js 中,xml 和 JavaScript 之间的联系,是通过 bpmn-moddle 来实现的,而 bpmn-moddle 依赖于 moddle 和 moddle-xml。

其中 moddle 的作用就是 “A utility library for working with meta-model based data structures”,也就是 提供了一种通过 json 文件来定义 XML 元模型的方式,并且实现 XML 字符串与 JavaScript 对象之间的转换

不过仓库中并没有直接给出这个 json schema 的具体结构,只有通过代码大概梳理如下:

type Schema = {name: stringuri: stringprefix: stringxml?: {typePrefix?: stringtagAlias?: 'lowerCase'}types: ModelType[]enumerations?: EnumType[]associations?: unknown[]
}type EnumType = {name: stringliteralValues: LiteralValue[]
}type LiteralValue = {name: string
}type ModelType = {name: stringisAbstract?: booleansuperClass?: string[]extends?: string[]meta?: {allowedIn?: string[][key: string]: unknown}properties?: ModelProperty[]
}type ModelProperty = {name: stringtype: 'Boolean' | 'String' | 'Integer' | AnotherModelTypeisId?: booleanisAttr?: booleanisBody?: booleanisReference?: booleanisMany?: booleanxml?: {serialize?: stringtypePrefix?: string}default?: string | number | booleanredefines?: string
}

关于 xml 业务逻辑部分的元素和属性声明,格式大概遵循上面这种类型定义。

至于 xml 中关于图形定义(图形类型、位置、大小等)部分,则 ModelProperty 中还会有 isVirtualsubsettedProperty 等属性,不过这些类型一般不涉及业务部分,对于我们的使用和扩展也没有多少关联,所以不用太过深入的了解。

Schema 与 ModelType 字段说明

在最外层的 Schema 中,主要有四个必要属性:

  • name:模型文件名,一般来说只需要描述这个模型文件的用途即可,没有实际作用。
  • prefix:前缀,用来区别不同元素模型,最常见的就是:bpmn、flowable、activiti、camunda,当然也可以自定义前缀。
  • uri:即统一资源标识符(Uniform Resource Identifier),定义该模型文件资源地址。
  • xml:指定配置 xml 的一些格式要求,目前只有 tagAliastypePrefix 两个属性,内部会比较 tagAlias 值是否是 lowerCase 来确定是否需要转换为小写驼峰;而 typePrefix 则是用于某些特定情况下的类型转换,当中新类型前缀,可选。
  • types:这里用于定义元素模型的所有元素和属性信息。
  • enumerationsassociations:这两个部分目前在实际业务中没有具体使用,可选。

在使用过程中,moddle 会解析所有 json schema 文件,生成一个 DescriptorTree。解析过程中,会校验文件中的 nameuri 属性值,如果存在相同值,则会抛出异常并忽略后面解析到的内容

至于 types 数组中的每个具体定义,参见 ModelType

  • name:属性/元素对应的类型名称(某些时候很有用~)。
  • isAbstract:确定这个类型是否可以通过 moddle.create 创建一个对象实例(实际上这个属性并没有实际作用,默认都可以通过 moddle.create 创建)。
  • meta: 元数据信息,一般来说都用来配置 allowedIn,设置该类型允许被添加到哪些类型对象下。
  • superClass:表示多继承,接受一个类型名称组成的数组,会继承数组内每个类型的所有属性。
  • extends:表示扩展,接受一个类型名称组成的数组,自身一般不会被直接使用,而是作为配置类型的补充,为配置类型增加新的属性配置。
  • properties:该类型对应的具体属性定义数组,类型为 ModelProperty[]

ModelProperty 的说明如下:

  • name:属性名称,作为该对象实例的一个属性键名。
  • type:该属性的类型,一般来说常用 BooleanString 两个参数来声明基础的属性,也可以用具体的某个 ModelType['name'] 对应的值,来表示该属性是一个指定类型的对象实例。
  • isId:标识属性是否可以作为 id 属性。
  • isAttr:标识该属性是否需要作为 xml 标签属性。
  • isBody:标识该属性是否需要显示在 xml 的标签内部。
  • isMany:是否是一个数组。
  • isReference:是否是通过 id 引用一个对象实例,在 xml 中显示为 id,而在 Javascript 运行中体现为对象。
  • redefines:是否是重定义,指定某个类型声明下的某个具体属性,用来覆盖该指定属性。
  • default:默认值,一般在 BooleanString 两个类型的属性中使用。
  • xml:xml 转换定义,包含 serializetypePrefix 两个定义,其中 serialize 常用的有两个值:xsi:typepropertytypePrefix 则没有固定值,但一般是使用单个字母。

关于 serializetypePrefix,两者与 xml 的生产和解析有关系,但比较复杂。具体影响后面会更新。

ModelProperty 中,部分属性的配置其实是 互斥 的,比如 isAttrisBodyisMany 等。

现在,我们已经了解了一个 BPMN 的 Json Schema 文件的结构,可以着手扩展元素模型了。

扩展 BPMN20 模型

在 bpmn-js 中,当我们需要在原基础上对元素模型进行扩展的时候,除了需要编写一个 Json Schema 文件之外,还需要在初始化编辑器的时候将该文件内容作为参数传递给 Modeler 构造函数,通过 moddle 进行文件解析。

import Modeler from 'bpmn-js/lib/Modeler'
import FlowableDescriptor from '@/additional/flowable.json'...const modeler = new Modeler({container: '#canvas',moddleExtensions: {flowable: FlowableDescriptor}
})

然后,通过 modeler.importXML 读取具有自定义属性或者元素的 xml 的时候,就可以正常读取了,也可以正常生成新的 xml 字符串。

当然,这些属性也必须要在我们的 flowable.json 文件中有声明。

假设我们现在有这样一个定义:

{"name": "Flowable","uri": "http://flowable.org/bpmn","prefix": "flowable","xml": {"tagAlias": "lowerCase"},types: [{"name": "AssigneeType","superClass": ["Element"],"meta": {"allowedIn": ["bpmn:UserTask"]},"properties": [{"name": "body","type": "String","isBody": true}]}]
}

这其中声明了一个新的类型 AssigneeType,继承自 Element

为什么这里的 Element 没有前缀部分呢?

这其实和底层依赖 moddl-xml 有关,如果说 moddle 是用来实现元模型定义和构造的话,moddle-xml 就是实现 xml 和 JavaScript 对象相互转换的角色。

在 moddle-xml 进行转换的过程中,如果此时继承的类型是 Element,并且是通过 moddle.create 创建的该类型对应的对象实例,则在转换时会作为一个新的标签插入到目标元素标签中(当然,也不是只有继承自 Element 才会被转换为标签)。

例如此时,我们的 AssigneeType 在创建了一个对应的实例之后,如果正常转换为 xml 的话,则会体现为:<flowable:assigneeType />

根据我们配置的 meta.allowedIn,限制了这个标签只能出现在 <bpmn:userTask></bpmn:userTask> 内部。当然,具体在内部的什么位置,还需要参考 bpmn:UserTask 的具体配置。

那么我们现在去到 bpmn-js 内置的 bpmn.json 中查看一下这个 UserTask 的配置到底是怎么样的呢?

{"name": "BPMN20","uri": "http://www.omg.org/spec/BPMN/20100524/MODEL","prefix": "bpmn","types": [{"name": "BaseElement","isAbstract": true,"properties": [{"name": "id","isAttr": true,"type": "String","isId": true},{"name": "documentation","type": "Documentation","isMany": true},{"name": "extensionDefinitions","type": "ExtensionDefinition","isMany": true,"isReference": true},{"name": "extensionElements","type": "ExtensionElements"}]},{"name": "FlowElement","isAbstract": true,"superClass": ["BaseElement"],"properties": [ ... ]},{"name": "FlowNode","isAbstract": true,"superClass": ["FlowElement"],"properties": [ ... ]},{"name": "InteractionNode","isAbstract": true,"properties": [ ... ]},{"name": "Activity","isAbstract": true,"superClass": ["FlowNode"],"properties": [ ... ]},{"name": "Task","superClass": ["Activity", "InteractionNode"]},{"name": "UserTask","superClass": ["Task"],"properties": [{"name": "renderings","type": "Rendering","isMany": true},{"name": "implementation","isAttr": true,"type": "String"}]},]
}

根据继承关系,我们可以看到 UserTask 一路下来继承了很多个类型,而每个类型都有它自己的属性定义,所以到 UserTask 这里它已经隐式的存在了很多种属性声明。

我们现在需要将 AssigneeType 插入到 UserTask 内部,需要 UserTask 中定义的属性值可以为 Element 的类型,或者某个复杂属性内部可以设置 Element 类型变量的属性值;并且,这个属性定义中不能设置 isReference,毕竟 Element 内置声明中并没有 Id 属性,而且提取 Id 引用之后,就没有办法转为标签结构了。

然后,在查阅了大半的属性声明之后,我们会发现 BaseElement 中声明的 extensionElements 属性最符合这个场景,extensionElements 属性对应 bpmn:ExtensionElements 类型,具有一个 values 属性可以设置多个 Element 类型的变量。

现在,我们可以通过下面这种方式,将 AssigneeType 插入到 UserTask 内部。

const moddle = modeler.get('moddle')
const modeling = modeler.get('modeling')const assigneeType = moddle.create('flowable:AssigneeType', { body: 'static' })
const extensionElements = moddle.create('bpmn:ExtensionElements', { values: [assigneeType] })modeling.updateProperties(userTaskElement, { extensionElements })

得到的 xml 结构如下:

image.png

当然,在使用 bpmn-js 的过程中,除了扩展元模型定义之外,很多时候还需要对原有的属性进行修改,也就是配置 redefines

但是在使用 redefines 时,还需要注意默认值的问题。在 bpmn-js(依赖的 bpmn-moddle => moddle-xml)中,如果属性值等于默认值,在 xml 中是不会显示的。

而当我们需要在等于默认值的情况下也显示到 xml 中的话,就需要对原有的属性进行重定义。

属性重定义

这里用一个例子来进行说明。

在 bpmn-js 中,AdHocSubProcess 临时子流程具有一个属性 cancelRemainingInstances,默认为 true,所以 xml 中会有如下情况:

image.png

使用 bpmn-js 解析该流程得到的两个子流程对应 js 对象如下:

image.png

此时可以看到虽然在 js 中我们可以获取到 cancelRemainingInstances 的实际值,但当其为 true 时无法显示到 xml 中。

那么这个属性在 Json Schema 中的定义是怎么样的呢?

{"name": "BPMN20""types": [{"name": "AdHocSubProcess","superClass": ["SubProcess"],"properties": [{"name": "cancelRemainingInstances","default": true,"isAttr": true,"type": "Boolean"}]}]
}

省略了部分内容。

其中可以看到 isAttrtrue,所以这个属性直接显示在 AdHocSubProcess 标签上,默认值 defaulttrue,所以该属性值为 true 时标签上没有该属性。

当我们需要在 cancelRemainingInstances 为 true 时也将其显示到 xml 上的时候,就需要对这个属性进行重定义了。

这里用项目中使用的 flowable 作为示例。

我们创建一个新的 Json Schema 文件 - flowable.json,添加一个针对其添加一个新的 type 类型。

{"name": "flowable""types": [{"name": "FlowableAdHocSubProcess","extends": ["bpmn:AdHocSubProcess"],"properties": [{"name": "cancelRemainingInstances","isAttr": true,"type": "Boolean","redefines": "bpmn:AdHocSubProcess#cancelRemainingInstances"}]}]
}

这部分在这里表示扩展 bpmn 对应的 Json Schema 文件中的 AdHocSubProcess 类型定义,使用 cancelRemainingInstances 属性覆盖 bpmn:AdHocSubProcess 对应的 cancelRemainingInstances 属性

为了统一,一般来说属性名和格式都会按照原有的格式来编写。

然后在 bpmn-js 的编辑器中引入这个声明文件。

此时我们的 xml 就会变成:

image.png

由于是另外一个文件声明,会带上 flowable 的前缀。

不过,虽然我们现在去掉了默认值,使得这个属性有值时都能显示在 xml 中,但是这也会带来一个问题。

重定义会遇到的问题

在之前的定义中,cancelRemainingInstances 具有默认值配置,所以即使 xml 中没有这个属性,也会将其解析为默认值 true。

而重定义之后,取消了默认值定义,那么此时必须按照 xml 中该属性对应的值来解析,如果不存在该属性,则在 js 中读取该元素 cancelRemainingInstances 属性的值便是 undefined

所以此时我们需要处理 在创建 AdHocSubProcess 时如何添加默认值 的问题。

至于设置默认值的问题,有几种解决思路:

  1. 创建完毕后手动赋值或者调api更新,但是会影响撤销恢复
  2. 通过 eventBus 在元素创建过程中赋值,这种方式比较完美,类似于 behavior,但是需要很了解 bpmn-js 的结构和执行逻辑
  3. 修改 bpmnFactory 模块,可能会对其他元素造成影响,但是还算方便

具体的实现方式,就留给各位小伙伴思考吧~


theme: devui-blue
highlight: darcula

✨✨✨目前成都的"小学生"大佬和作者一起开发了 Flowable 流程引擎组件(包含前端设计器与后端流程引擎)。

该组件与 Flowable 流程引擎深度融合,结合实际业务场景和使用方式,对属性编辑面板进行了重新设计,优化了用户体验。 增加了符合业务场景的流程校验与进度预览、引入富文本编辑器与代码编辑器。 结合后端引擎,可直接嵌入系统中使用。

详情请访问:https://www.bpmport.com/products ;

设计器预览:

  1. 编辑器:designer,
  2. 预览与模拟:viewer,
  3. DMN决策设计器:dmn

image.png

在开发流图编辑器的过程中,为了深度适配后端 flowable 引擎的功能,经常需要对 bpmn-js 进行扩展,主要分为两个部分:propertie-panel 和 bpmn model。

之前有介绍过,bpmn-js 是由 camunda 团队开发的,用来实现 BPMN 2.0 规则的流程图绘制;结合 bpmn-js-properties-panel 可以完美适配 Camunda 流程引擎。

但是,官方提供的 properties-panel 基本上很难满足国内的使用场景(主要还是 UI 以及交互方式),并且无法适配 flowable 和 activiti。

所以基本上都需要开发人员根据 bpmn-js 提供的 API 来单独实现 properties-panel,而关于 properties-panel 的实现我们在之前的文章中也有讲述过,这里不再赘述。

另外一个问题:bpmn model,则是与 Bpmn.js自定义描述文件说明 中的内容有关。

BPMN Model

关于 bpmn model 的具体概念,很难有一个具体的说法,大致解释就是:通过一个 JSON Schema 来对 BPMN 2.0 规则的 XML 文件内的标签以及属性进行描述。

在 bpmn-js 中,xml 和 JavaScript 之间的联系,是通过 bpmn-moddle 来实现的,而 bpmn-moddle 依赖于 moddle 和 moddle-xml。

其中 moddle 的作用就是 “A utility library for working with meta-model based data structures”,也就是 提供了一种通过 json 文件来定义 XML 元模型的方式,并且实现 XML 字符串与 JavaScript 对象之间的转换

不过仓库中并没有直接给出这个 json schema 的具体结构,只有通过代码大概梳理如下:

type Schema = {name: stringuri: stringprefix: stringxml?: {typePrefix?: stringtagAlias?: 'lowerCase'}types: ModelType[]enumerations?: EnumType[]associations?: unknown[]
}type EnumType = {name: stringliteralValues: LiteralValue[]
}type LiteralValue = {name: string
}type ModelType = {name: stringisAbstract?: booleansuperClass?: string[]extends?: string[]meta?: {allowedIn?: string[][key: string]: unknown}properties?: ModelProperty[]
}type ModelProperty = {name: stringtype: 'Boolean' | 'String' | 'Integer' | AnotherModelTypeisId?: booleanisAttr?: booleanisBody?: booleanisReference?: booleanisMany?: booleanxml?: {serialize?: stringtypePrefix?: string}default?: string | number | booleanredefines?: string
}

关于 xml 业务逻辑部分的元素和属性声明,格式大概遵循上面这种类型定义。

至于 xml 中关于图形定义(图形类型、位置、大小等)部分,则 ModelProperty 中还会有 isVirtualsubsettedProperty 等属性,不过这些类型一般不涉及业务部分,对于我们的使用和扩展也没有多少关联,所以不用太过深入的了解。

Schema 与 ModelType 字段说明

在最外层的 Schema 中,主要有四个必要属性:

  • name:模型文件名,一般来说只需要描述这个模型文件的用途即可,没有实际作用。
  • prefix:前缀,用来区别不同元素模型,最常见的就是:bpmn、flowable、activiti、camunda,当然也可以自定义前缀。
  • uri:即统一资源标识符(Uniform Resource Identifier),定义该模型文件资源地址。
  • xml:指定配置 xml 的一些格式要求,目前只有 tagAliastypePrefix 两个属性,内部会比较 tagAlias 值是否是 lowerCase 来确定是否需要转换为小写驼峰;而 typePrefix 则是用于某些特定情况下的类型转换,当中新类型前缀,可选。
  • types:这里用于定义元素模型的所有元素和属性信息。
  • enumerationsassociations:这两个部分目前在实际业务中没有具体使用,可选。

在使用过程中,moddle 会解析所有 json schema 文件,生成一个 DescriptorTree。解析过程中,会校验文件中的 nameuri 属性值,如果存在相同值,则会抛出异常并忽略后面解析到的内容

至于 types 数组中的每个具体定义,参见 ModelType

  • name:属性/元素对应的类型名称(某些时候很有用~)。
  • isAbstract:确定这个类型是否可以通过 moddle.create 创建一个对象实例(实际上这个属性并没有实际作用,默认都可以通过 moddle.create 创建)。
  • meta: 元数据信息,一般来说都用来配置 allowedIn,设置该类型允许被添加到哪些类型对象下。
  • superClass:表示多继承,接受一个类型名称组成的数组,会继承数组内每个类型的所有属性。
  • extends:表示扩展,接受一个类型名称组成的数组,自身一般不会被直接使用,而是作为配置类型的补充,为配置类型增加新的属性配置。
  • properties:该类型对应的具体属性定义数组,类型为 ModelProperty[]

ModelProperty 的说明如下:

  • name:属性名称,作为该对象实例的一个属性键名。
  • type:该属性的类型,一般来说常用 BooleanString 两个参数来声明基础的属性,也可以用具体的某个 ModelType['name'] 对应的值,来表示该属性是一个指定类型的对象实例。
  • isId:标识属性是否可以作为 id 属性。
  • isAttr:标识该属性是否需要作为 xml 标签属性。
  • isBody:标识该属性是否需要显示在 xml 的标签内部。
  • isMany:是否是一个数组。
  • isReference:是否是通过 id 引用一个对象实例,在 xml 中显示为 id,而在 Javascript 运行中体现为对象。
  • redefines:是否是重定义,指定某个类型声明下的某个具体属性,用来覆盖该指定属性。
  • default:默认值,一般在 BooleanString 两个类型的属性中使用。
  • xml:xml 转换定义,包含 serializetypePrefix 两个定义,其中 serialize 常用的有两个值:xsi:typepropertytypePrefix 则没有固定值,但一般是使用单个字母。

关于 serializetypePrefix,两者与 xml 的生产和解析有关系,但比较复杂。具体影响后面会更新。

ModelProperty 中,部分属性的配置其实是 互斥 的,比如 isAttrisBodyisMany 等。

现在,我们已经了解了一个 BPMN 的 Json Schema 文件的结构,可以着手扩展元素模型了。

扩展 BPMN20 模型

在 bpmn-js 中,当我们需要在原基础上对元素模型进行扩展的时候,除了需要编写一个 Json Schema 文件之外,还需要在初始化编辑器的时候将该文件内容作为参数传递给 Modeler 构造函数,通过 moddle 进行文件解析。

import Modeler from 'bpmn-js/lib/Modeler'
import FlowableDescriptor from '@/additional/flowable.json'...const modeler = new Modeler({container: '#canvas',moddleExtensions: {flowable: FlowableDescriptor}
})

然后,通过 modeler.importXML 读取具有自定义属性或者元素的 xml 的时候,就可以正常读取了,也可以正常生成新的 xml 字符串。

当然,这些属性也必须要在我们的 flowable.json 文件中有声明。

假设我们现在有这样一个定义:

{"name": "Flowable","uri": "http://flowable.org/bpmn","prefix": "flowable","xml": {"tagAlias": "lowerCase"},types: [{"name": "AssigneeType","superClass": ["Element"],"meta": {"allowedIn": ["bpmn:UserTask"]},"properties": [{"name": "body","type": "String","isBody": true}]}]
}

这其中声明了一个新的类型 AssigneeType,继承自 Element

为什么这里的 Element 没有前缀部分呢?

这其实和底层依赖 moddl-xml 有关,如果说 moddle 是用来实现元模型定义和构造的话,moddle-xml 就是实现 xml 和 JavaScript 对象相互转换的角色。

在 moddle-xml 进行转换的过程中,如果此时继承的类型是 Element,并且是通过 moddle.create 创建的该类型对应的对象实例,则在转换时会作为一个新的标签插入到目标元素标签中(当然,也不是只有继承自 Element 才会被转换为标签)。

例如此时,我们的 AssigneeType 在创建了一个对应的实例之后,如果正常转换为 xml 的话,则会体现为:<flowable:assigneeType />

根据我们配置的 meta.allowedIn,限制了这个标签只能出现在 <bpmn:userTask></bpmn:userTask> 内部。当然,具体在内部的什么位置,还需要参考 bpmn:UserTask 的具体配置。

那么我们现在去到 bpmn-js 内置的 bpmn.json 中查看一下这个 UserTask 的配置到底是怎么样的呢?

{"name": "BPMN20","uri": "http://www.omg.org/spec/BPMN/20100524/MODEL","prefix": "bpmn","types": [{"name": "BaseElement","isAbstract": true,"properties": [{"name": "id","isAttr": true,"type": "String","isId": true},{"name": "documentation","type": "Documentation","isMany": true},{"name": "extensionDefinitions","type": "ExtensionDefinition","isMany": true,"isReference": true},{"name": "extensionElements","type": "ExtensionElements"}]},{"name": "FlowElement","isAbstract": true,"superClass": ["BaseElement"],"properties": [ ... ]},{"name": "FlowNode","isAbstract": true,"superClass": ["FlowElement"],"properties": [ ... ]},{"name": "InteractionNode","isAbstract": true,"properties": [ ... ]},{"name": "Activity","isAbstract": true,"superClass": ["FlowNode"],"properties": [ ... ]},{"name": "Task","superClass": ["Activity", "InteractionNode"]},{"name": "UserTask","superClass": ["Task"],"properties": [{"name": "renderings","type": "Rendering","isMany": true},{"name": "implementation","isAttr": true,"type": "String"}]},]
}

根据继承关系,我们可以看到 UserTask 一路下来继承了很多个类型,而每个类型都有它自己的属性定义,所以到 UserTask 这里它已经隐式的存在了很多种属性声明。

我们现在需要将 AssigneeType 插入到 UserTask 内部,需要 UserTask 中定义的属性值可以为 Element 的类型,或者某个复杂属性内部可以设置 Element 类型变量的属性值;并且,这个属性定义中不能设置 isReference,毕竟 Element 内置声明中并没有 Id 属性,而且提取 Id 引用之后,就没有办法转为标签结构了。

然后,在查阅了大半的属性声明之后,我们会发现 BaseElement 中声明的 extensionElements 属性最符合这个场景,extensionElements 属性对应 bpmn:ExtensionElements 类型,具有一个 values 属性可以设置多个 Element 类型的变量。

现在,我们可以通过下面这种方式,将 AssigneeType 插入到 UserTask 内部。

const moddle = modeler.get('moddle')
const modeling = modeler.get('modeling')const assigneeType = moddle.create('flowable:AssigneeType', { body: 'static' })
const extensionElements = moddle.create('bpmn:ExtensionElements', { values: [assigneeType] })modeling.updateProperties(userTaskElement, { extensionElements })

得到的 xml 结构如下:

image.png

当然,在使用 bpmn-js 的过程中,除了扩展元模型定义之外,很多时候还需要对原有的属性进行修改,也就是配置 redefines

但是在使用 redefines 时,还需要注意默认值的问题。在 bpmn-js(依赖的 bpmn-moddle => moddle-xml)中,如果属性值等于默认值,在 xml 中是不会显示的。

而当我们需要在等于默认值的情况下也显示到 xml 中的话,就需要对原有的属性进行重定义。

属性重定义

这里用一个例子来进行说明。

在 bpmn-js 中,AdHocSubProcess 临时子流程具有一个属性 cancelRemainingInstances,默认为 true,所以 xml 中会有如下情况:

image.png

使用 bpmn-js 解析该流程得到的两个子流程对应 js 对象如下:

image.png

此时可以看到虽然在 js 中我们可以获取到 cancelRemainingInstances 的实际值,但当其为 true 时无法显示到 xml 中。

那么这个属性在 Json Schema 中的定义是怎么样的呢?

{"name": "BPMN20""types": [{"name": "AdHocSubProcess","superClass": ["SubProcess"],"properties": [{"name": "cancelRemainingInstances","default": true,"isAttr": true,"type": "Boolean"}]}]
}

省略了部分内容。

其中可以看到 isAttrtrue,所以这个属性直接显示在 AdHocSubProcess 标签上,默认值 defaulttrue,所以该属性值为 true 时标签上没有该属性。

当我们需要在 cancelRemainingInstances 为 true 时也将其显示到 xml 上的时候,就需要对这个属性进行重定义了。

这里用项目中使用的 flowable 作为示例。

我们创建一个新的 Json Schema 文件 - flowable.json,添加一个针对其添加一个新的 type 类型。

{"name": "flowable""types": [{"name": "FlowableAdHocSubProcess","extends": ["bpmn:AdHocSubProcess"],"properties": [{"name": "cancelRemainingInstances","isAttr": true,"type": "Boolean","redefines": "bpmn:AdHocSubProcess#cancelRemainingInstances"}]}]
}

这部分在这里表示扩展 bpmn 对应的 Json Schema 文件中的 AdHocSubProcess 类型定义,使用 cancelRemainingInstances 属性覆盖 bpmn:AdHocSubProcess 对应的 cancelRemainingInstances 属性

为了统一,一般来说属性名和格式都会按照原有的格式来编写。

然后在 bpmn-js 的编辑器中引入这个声明文件。

此时我们的 xml 就会变成:

image.png

由于是另外一个文件声明,会带上 flowable 的前缀。

不过,虽然我们现在去掉了默认值,使得这个属性有值时都能显示在 xml 中,但是这也会带来一个问题。

重定义会遇到的问题

在之前的定义中,cancelRemainingInstances 具有默认值配置,所以即使 xml 中没有这个属性,也会将其解析为默认值 true。

而重定义之后,取消了默认值定义,那么此时必须按照 xml 中该属性对应的值来解析,如果不存在该属性,则在 js 中读取该元素 cancelRemainingInstances 属性的值便是 undefined

所以此时我们需要处理 在创建 AdHocSubProcess 时如何添加默认值 的问题。

至于设置默认值的问题,有几种解决思路:

  1. 创建完毕后手动赋值或者调api更新,但是会影响撤销恢复
  2. 通过 eventBus 在元素创建过程中赋值,这种方式比较完美,类似于 behavior,但是需要很了解 bpmn-js 的结构和执行逻辑
  3. 修改 bpmnFactory 模块,可能会对其他元素造成影响,但是还算方便

具体的实现方式,就留给各位小伙伴思考吧~

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词