最近在做表單流程基礎(chǔ)服務(wù)平臺(tái)蔓纠,流程設(shè)計(jì)器選用了react+bpmn-js實(shí)現(xiàn)溃槐,流程引擎采用flowable,實(shí)現(xiàn)自定義流程過程中遇到了不少坑偎肃,現(xiàn)將踩坑過程記錄如下煞烫,供道友們參考。
實(shí)現(xiàn)過程中主要參考了以下幾篇文章累颂,在此表示感謝滞详!
文章1:Bpmn.js 中文文檔(一)
文章2:bpmn-js(五) 線條上添加決策表達(dá)式
文章3:全網(wǎng)最詳bpmn.js教材-properties-panel篇(上)
文章4:前端vue+bpmn-js實(shí)現(xiàn)activiti的流程設(shè)計(jì)器,后端Springboot+Activiti開發(fā)工作流
一紊馏、屬性面板無法顯示問題
最初按照文章3配置料饥,屬性面板一直不顯示,未提示任何錯(cuò)誤朱监,后來排查多次方才發(fā)現(xiàn)岸啡,additionalModules屬性應(yīng)為數(shù)組 [propertiesPanelModule,propertiesProviderModule],原先給寫成了對(duì)象 {propertiesPanelModule,propertiesProviderModule}赫编,汗巡蘸。。擂送。赡若。
二、自定義屬性面板
bpmn-js官方示例中团甲,按照文章3 引入'bpmn-js-properties-panel/lib/provider/camunda'逾冬,添加 camunda-bpmn-moddle后,繪制流程圖生成的xml標(biāo)簽都是類似"camunda:candidateUsers"躺苦,是適配camunda流程引擎的身腻,使用xml字符串在flowable流程引擎中雖然也能部署成功,但是存在代理人匹厘、候選用戶等設(shè)置的變量${user}嘀趟,流程引擎無法自動(dòng)將下一步人員更新到流程中,所以自己實(shí)現(xiàn)flowable屬性愈诚。
1她按、先將需要引入的空xml模板調(diào)整為flowable定義的牛隅,我這里需要自定義流程id和名稱,就采用了下邊這種方式實(shí)現(xiàn):
const xmlStr = '<?xml version="1.0" encoding="UTF-8"?>' +
'<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" ' +
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
'xmlns:xsd="http://www.w3.org/2001/XMLSchema" ' +
'xmlns:flowable="http://flowable.org/bpmn" ' +
'xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" ' +
'xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" ' +
'xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" ' +
'typeLanguage="http://www.w3.org/2001/XMLSchema" ' +
'expressionLanguage="http://www.w3.org/1999/XPath" ' +
'targetNamespace="http://www.flowable.org/processdef">' +
'<process id="$ProcessId$" name="$ProcessNm$" isExecutable="true">' +
'</process>' +
'<bpmndi:BPMNDiagram id="BpmnDiagram_$ProcessId$">' +
'<bpmndi:BPMNPlane id="BpmnPlane_$ProcessId$" bpmnElement="$ProcessId$">' +
'</bpmndi:BPMNPlane>' +
'</bpmndi:BPMNDiagram>' +
'</definitions>';
export const InitXml = (processId, processNm) => {
return xmlStr.replace(/\$ProcessId\$/g, processId).replace(/\$ProcessNm\$/g, processNm);
}
2酌泰、自己寫provider媒佣,替換原來的propertiesProviderModule,直接上代碼
需要以下4個(gè)文件陵刹,BpmnPropertiesProvider.js 中定義了選項(xiàng)卡默伍、屬性組,具體屬性在 AssignableProps.js 和 MultiInstanceProps.js中衰琐,對(duì)應(yīng)我這里配置的用戶分配和會(huì)簽兩個(gè)屬性組也糊。
BpmnPropertiesProvider.js:
import inherits from 'inherits';
import {is} from 'bpmn-js/lib/util/ModelUtil';
import PropertiesActivator from 'bpmn-js-properties-panel/lib/PropertiesActivator';
import processProps from 'bpmn-js-properties-panel/lib/provider/bpmn/parts/ProcessProps';
import eventProps from 'bpmn-js-properties-panel/lib/provider/bpmn/parts/EventProps';
import linkProps from 'bpmn-js-properties-panel/lib/provider/bpmn/parts/LinkProps';
import documentationProps from 'bpmn-js-properties-panel/lib/provider/bpmn/parts/DocumentationProps';
import idProps from 'bpmn-js-properties-panel/lib/provider/bpmn/parts/IdProps';
import nameProps from 'bpmn-js-properties-panel/lib/provider/bpmn/parts/NameProps';
import executableProps from 'bpmn-js-properties-panel/lib/provider/bpmn/parts/ExecutableProps';
import assignableProps from './Parts/AssignableProps';
import multiInstanceProps from './Parts/MultiInstanceProps';
function getIdOptions(element) {
if (is(element, 'bpmn:Participant')) {
return {id: 'participant-id', label: 'Participant Id'};
}
}
function getNameOptions(element) {
if (is(element, 'bpmn:Participant')) {
return {id: 'participant-name', label: 'Participant Name'};
}
}
function createGeneralTabGroups(element, canvas, bpmnFactory, elementRegistry, translate) {
let generalGroup = {
id: 'general',
label: translate('General'),
entries: []
};
idProps(generalGroup, element, translate, getIdOptions(element));
nameProps(generalGroup, element, bpmnFactory, canvas, translate, getNameOptions(element));
processProps(generalGroup, element, translate);
// executableProps(generalGroup, element, translate);
let detailsGroup = {
id: 'details',
label: translate('Details'),
entries: []
};
linkProps(detailsGroup, element, translate);
eventProps(detailsGroup, element, bpmnFactory, elementRegistry, translate);
let documentationGroup = {
id: 'documentation',
label: translate('Documentation'),
entries: []
};
documentationProps(documentationGroup, element, bpmnFactory, translate);
let assignableGroup = {
id: 'assignable',
label: translate('task assignable'),
entries: []
}
assignableProps(assignableGroup, element, translate);
let multiInstanceGroup = {
id: 'multiInstance',
label: translate('multiInstance loopCharacteristics'),
entries: []
}
multiInstanceProps(multiInstanceGroup, element, translate);
return [
generalGroup,
detailsGroup,
// documentationGroup
assignableGroup,
multiInstanceGroup
];
}
function createCustomizeTabGroups(element, canvas, bpmnFactory, elementRegistry, translate) {
return [];
}
function BpmnPropertiesProvider(eventBus, canvas, bpmnFactory, elementRegistry, translate) {
PropertiesActivator.call(this, eventBus);
this.getTabs = function (element) {
let generalTab = {
id: 'general',
label: translate('General'),
groups: createGeneralTabGroups(element, canvas, bpmnFactory, elementRegistry, translate)
};
/*
let customizeTab = {
id: 'customize',
label: translate('customize'),
groups: createCustomizeTabGroups(element, canvas, bpmnFactory, elementRegistry, translate)
};
*/
return [
generalTab,
// customizeTab
];
};
}
BpmnPropertiesProvider.$inject = [
'eventBus',
'canvas',
'bpmnFactory',
'elementRegistry',
'translate'
];
inherits(BpmnPropertiesProvider, PropertiesActivator);
export default BpmnPropertiesProvider;
AssignableProps.js:
import entryFactory from 'bpmn-js-properties-panel/lib/factory/EntryFactory';
import { is } from 'bpmn-js/lib/util/ModelUtil';
export default function(group, element, translate) {
if (is(element, 'bpmn:UserTask')) {
// 用戶任務(wù)節(jié)點(diǎn)
group.entries.push(entryFactory.textField(translate, {
id : 'assignee',
label : translate('Assignee'),
modelProperty : 'flowable:assignee'
}));
group.entries.push(entryFactory.textField(translate, {
id : 'candidateUsers',
label : translate('Candidate Users'),
modelProperty : 'flowable:candidateUsers'
}));
}
}
MultiInstanceProps.js:
import entryFactory from 'bpmn-js-properties-panel/lib/factory/EntryFactory';
import {is} from 'bpmn-js/lib/util/ModelUtil';
export default function (group, element, translate) {
if (is(element, 'bpmn:UserTask')) {
// 用戶任務(wù)節(jié)點(diǎn)
group.entries.push(entryFactory.selectBox(translate, {
id: 'countersign',
label: translate('countersign task'),
selectOptions: [
{value: 'false', name: translate('false')},
{value: 'sequential', name: translate('sequential')},
{value: 'synchronize', name: translate('synchronize')}
],
modelProperty : 'flowable:countersign'
}));
}
}
由于設(shè)置會(huì)簽節(jié)點(diǎn)屬性設(shè)置比較復(fù)雜,為了減輕用戶使用難度羡宙,我在MultiInstanceProps中自定義了flowable:countersign標(biāo)簽狸剃,通過監(jiān)聽此標(biāo)簽值的變化,自動(dòng)設(shè)置取消會(huì)簽屬性狗热,詳見問題四捕捂。
index.js:
import BpmnPropertiesProvider from './BpmnPropertiesProvider';
export default {
__init__: [ 'propertiesProvider' ],
propertiesProvider: [ 'type', BpmnPropertiesProvider ]
};
3、最后在初始化BpmnModeler時(shí)引入斗搞。
import propertiesProviderModule from './Provider/bpmn';
this.bpmnModeler = new BpmnModeler({
container: '#canvas',
propertiesPanel: {
parent: '#properties'
},
height: '100vh',
additionalModules: [
propertiesPanelModule,
propertiesProviderModule //在此引入
]
})
三、刪除屬性問題
根據(jù)文章2慷妙,通過 modeling.updateProperties(element, { 'flowable:assignee': '${user01}'}) 可以設(shè)置元素屬性僻焚,那么如何刪除已設(shè)置的屬性呢,多次嘗試發(fā)現(xiàn)膝擂,更新為 undefined 即可刪除:
const modeling = this.bpmnModeler.get('modeling');
let ele = internalEvent.element;
modeling.updateProperties(ele, { 'flowable:assignee': undefined})
四虑啤、會(huì)簽屬性配置問題
文章4中說明了如何設(shè)置多實(shí)例(會(huì)簽),但是 collection 和 elementVariable 屬性無法更新到xml中架馋,flowable的屬性都是在 $attrs 中狞山,所以應(yīng)該這么設(shè)置:
loopCharacteristics = bpmnFactory.create('bpmn:MultiInstanceLoopCharacteristics');
loopCharacteristics['isSequential'] = isSequential;
//flowable屬性需要在$attrs中設(shè)置!2婕拧萍启!
loopCharacteristics.$attrs['flowable:collection'] = collectionUsers;
loopCharacteristics.$attrs['flowable:elementVariable'] = elementVariable;
loopCharacteristics['completionCondition'] = lementHelper.createElement('bpmn:Expression', {body: '${nrOfCompletedInstances/nrOfInstances>=0.99}'}, loopCharacteristics, bpmnFactory);
modeling.updateProperties(ele, {
'flowable:assignee': assignableUsers,
'flowable:candidateUsers': undefined,
loopCharacteristics: loopCharacteristics
});