react+bpmn-js+flowable問題解決記錄

最近在做表單流程基礎(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è)屬性組也糊。

文件結(jié)構(gòu)

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
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者屏鳍。
  • 序言:七十年代末勘纯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子钓瞭,更是在濱河造成了極大的恐慌驳遵,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件山涡,死亡現(xiàn)場(chǎng)離奇詭異堤结,居然都是意外死亡唆迁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門竞穷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來唐责,“玉大人,你說我怎么就攤上這事来庭《饰担” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵月弛,是天一觀的道長(zhǎng)肴盏。 經(jīng)常有香客問我,道長(zhǎng)帽衙,這世上最難降的妖魔是什么菜皂? 我笑而不...
    開封第一講書人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮厉萝,結(jié)果婚禮上恍飘,老公的妹妹穿的比我還像新娘。我一直安慰自己谴垫,他們只是感情好章母,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著翩剪,像睡著了一般乳怎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上前弯,一...
    開封第一講書人閱讀 52,337評(píng)論 1 310
  • 那天蚪缀,我揣著相機(jī)與錄音,去河邊找鬼恕出。 笑死询枚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的浙巫。 我是一名探鬼主播金蜀,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼的畴!你這毒婦竟也來了廉油?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤苗傅,失蹤者是張志新(化名)和其女友劉穎抒线,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渣慕,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嘶炭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年抱慌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片眨猎。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抑进,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出睡陪,到底是詐尸還是另有隱情寺渗,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布兰迫,位于F島的核電站信殊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏汁果。R本人自食惡果不足惜涡拘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望据德。 院中可真熱鬧鳄乏,春花似錦、人聲如沸棘利。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽善玫。三九已至水援,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蝌焚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工誓斥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留只洒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓劳坑,卻偏偏與公主長(zhǎng)得像毕谴,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子距芬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容