前言
Q: bpmn.js是什么? ???
bpmn.js是一個BPMN2.0渲染工具包和web建模器, 使得畫流程圖的功能在前端來完成.
Q: 我為什么要寫該系列的教材? ???
因為公司業(yè)務(wù)的需要因而要在項目中使用到bpmn.js,但是由于bpmn.js的開發(fā)者是國外友人, 因此國內(nèi)對這方面的教材很少, 也沒有詳細的文檔. 所以很多使用方式很多坑都得自己去找.在將其琢磨完之后, 決定寫一系列關(guān)于它的教材來幫助更多bpmn.js的使用者或者是期于找到一種好的繪制流程圖的開發(fā)者. 同時也是自己對其的一種鞏固.
由于是系列的文章, 所以更新的可能會比較頻繁, 您要是無意間刷到了且不是您所需要的還請諒解??.
求贊??, 求Start ??, 求Fork ??, 希望能對你有一點小小的幫助.
所有教材的github地址: 《bpmn-chinese-document》
Properties篇
哈哈 來了 來了 它終于來了??.
讓大家久等的Properties篇??.
其實霖呆呆工作上用到的bpmn.js的內(nèi)容也就只局限于之前寫的文章了, 算是將實際用到的知識全盤脫出了... 那么就有人會好奇的問了...為什么連Properties-panel這樣重要的功能都沒有用到呢????
這其實和我們團隊的用法有關(guān):
最開始接觸使用到bpmn.js是因為需要用它來繪制工作流實現(xiàn)決策引擎的這么一個功能. 而我們的做法是前端通過bpmn.js來繪制流程圖, 圖中的Start、UserTask、BusinessRuleTask等等我在這里都稱之為節(jié)點. 每個節(jié)點都對應(yīng)著xml文件中的標簽, 傳統(tǒng)的做法可能是將各個節(jié)點的屬性都保存到標簽上, 例如我這里有一個開始節(jié)點的xml標簽:
<startEvent id="StartEvent_1y45yut" name="開始" roles="admin"></startEvent>
然后給這個節(jié)點增加上一個名為“權(quán)限(roles)”的自定義屬性, 這個屬性會保存在xml中, 并且導(dǎo)出這個文件的時候也會留在上面.
我們雖然每個節(jié)點也都會關(guān)聯(lián)很多信息, 但是這些信息并不是直接保存在xml 標簽里的. 而是每個節(jié)點都會有一個 id , 后臺有一個表專門用于存放每個節(jié)點的附加信息, 所以每次點擊節(jié)點的時候, 都通過這個id來調(diào)取后臺存儲的數(shù)據(jù), 從而拿到節(jié)點對應(yīng)的屬性, 右邊出現(xiàn)一個抽屜將這些屬性信息顯示在里面可以進行修改. 修改保存之后, 也是調(diào)用后臺的接口來修改表里的信息. 所以主要的邏輯還是集中在后臺上. 因此對于xml的操作還真不是太多, 自然的也就沒用上Properties-panel了.
但是我的這種做法, 你也可以理解為右邊出現(xiàn)的“抽屜” 就是我自定義的Properties-panel, 因為它確實也起到了與節(jié)點關(guān)聯(lián)屬性的作用.
OK, 言歸正傳啦, 雖然我工作中并沒有用到它, 但是經(jīng)過讀者給出的意見以及自己對它的一些研究, 還是能用它做一些業(yè)務(wù)實現(xiàn)的, 希望在你認真看完之后能有所收獲??.
通過這一章節(jié)的閱讀你可以學習到:
- 什么是
bpmn properties???? - 如何讀取
bpmn properties???? - 如何修改
bpmn properties???? - 使用
updateProperties方法????
bpmn properties屬性介紹以及基本用法
1. 什么是bpmn properties????
讓我們先來搞懂一下什么是bpmn properties????
我們在用bpmn.js畫的每一個節(jié)點其實都被稱之為diagram element(圖表元素, 是不是很好理解??)
而在bpmn文件中的每個xml標簽稱之為BPMN element.
將diagram element與BPMN element的一些屬性關(guān)聯(lián)起來靠的是一個叫做businessObject的屬性. 從名稱上理解你也可以知道它是一個對象(Object), 你可以在這個對象中添加上一些特殊的屬性, 并且這些屬性是可以直接插到BPMN element上的.
舉個例子??:
我繪制了一個StartEvent節(jié)點, 它對應(yīng)的:
-
diagram element:{ id: "StartEvent_1y45yut", type: "bpmn:StartEvent", businessObject: { $type: "bpmn:StartEvent", name: "開始" } } -
BPMN element:<startEvent id="StartEvent_1y45yut" name="開始"></startEvent>
像這類屬性就是bpmn properties, 你可以用它來實現(xiàn)你的業(yè)務(wù)需要.
2. 如何讀取bpmn properties????
不知道大家是否還記得我在《事件篇》中用到的一段代碼:
addModelerListener () {
// 監(jiān)聽 modeler
const bpmnjs = this.bpmnModeler
const that = this
const events = ['shape.added', 'shape.move.end', 'shape.removed']
events.forEach(function(event) {
that.bpmnModeler.on(event, e => {
var elementRegistry = bpmnjs.get('elementRegistry')
var shape = e.element ? elementRegistry.get(e.element.id) : e.shape
if (event === 'shape.added') {
console.log('新增了shape')
} else if (event === 'shape.move.end') {
console.log('移動了shape')
} else if (event === 'shape.removed') {
console.log('刪除了shape')
}
})
})
}
這個方法是放在 將字符串轉(zhuǎn)換成圖顯示出來 之后, 用于給元素綁定事件.
其中就有用到elementRegistry.
所以如果是在html中, 你就可以用這種方式來獲取bpmn properties:
<body>
<div id="canvas"></div>
<script>
var bpmnJS = new BpmnJS({
container: '#canvas'
});
bpmnJS.importXML(xmlStr, function(err) {
if (!err) {
var elementRegistry = bpmnJs.get('elementRegistry')
var startEventElement = elementRegistry.get('StartEvent_1y45yut'),
startEvent = startEventElement.businessObject;
console.log(startEvent.name) // 開始
}
}
</script>
而在一些class里, 比如CustomRenderer.js里, 你可以直接用.的方式就獲取到了:
export default class CustomRenderer extends BaseRenderer {
drawShape (parentNode, element) {
// element.businessObject
// or 解構(gòu)
// const { businessObject } = element
}
}
3. 如何修改bpmn properties????
你在bpmn文件中, 可能會看到這樣一段代碼:
<bpmn:sequenceFlow id="SequenceFlow_1" sourceRef="StartEvent_1" targetRef="Task_1" name="FOO > BAR?">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression"><![CDATA[${foo > bar}]]></bpmn:conditionExpression>
</bpmn:sequenceFlow>
里面的xsi:type、sourceRef這些屬性是啥啊???? 我怎么知道哪類標簽有哪些屬性????
你其實可以在官方給的這個bpmn.json中查找到:
設(shè)置的話可以根據(jù)以下方法:
var moddle = bpmnJS.get('moddle');
// 創(chuàng)建一個BPMN element , 并且載入到導(dǎo)出的xml里
var newCondition = moddle.create('bpmn:FormalExpression', {
body: '${ value > 100 }'
});
// 寫入屬性, 但是不支持撤銷
sequenceFlow.conditionExpression = newCondition;
上面??的這種方式是不支持撤銷的, 如果你想要能夠 撤銷/重新 的話, 你可以通過以下這種方式:
var modeling = bpmnJS.get('modeling');
modeling.updateProperties(sequenceFlowElement, {
conditionExpression: newCondition
});
也就是通過modeling.updateProperties()這個方法.
這個modeling好像是需要引入的, 反正如果我是使用DNS 遠程的引入下面的這個js好像就會報錯Uncaught Error: No provider for "modeling"!.
<script src="https://unpkg.com/bpmn-js@6.0.2/dist/bpmn-viewer.development.js"></script>
當然如果你是使用npm 下載的話就沒有這個問題了.
這個方法的第一個參數(shù)是一個 diagram element, 也就是前面我們提到的用elementRegistry獲取到的對象.
第二個參數(shù)是要修改的屬性, 它是一個Map結(jié)構(gòu).
4. 使用updateProperties方法
例如??, 我想在點擊某個類型為bpmn:Task的節(jié)點的時候, 修改它的name屬性, 我可以這么做:
- 給節(jié)點添加點擊事件
- 判斷節(jié)點類型為
bpmn:Task, 只對這種類型的節(jié)點做后續(xù)處理 - 使用
updateProperties更新name
createNewDiagram () {
// 將字符串轉(zhuǎn)換成圖顯示出來
this.bpmnModeler.importXML(xmlStr, (err) => {
if (err) {
// console.error(err)
} else {
// 這里是成功之后的回調(diào), 可以在這里做一系列事情
this.success()
}
})
},
success () {
this.addModelerListener() // 添加監(jiān)聽事件
},
addModelerListener () {
const eventBus = this.bpmnModeler.get('eventBus')
const modeling = this.bpmnModeler.get('modeling')
const elementRegistry = this.bpmnModeler.get('elementRegistry');
const eventTypes = ['element.click', 'element.changed'];
eventTypes.forEach(function(eventType) {
eventBus.on(eventType, function (e) {
if (!e || !e.element) {
console.log('無效的e', e)
return
}
if (eventType === 'element.click') {
console.log('點擊了element', e)
var shape = e.element ? elementRegistry.get(e.element.id) : e.shape
if (shape.type === 'bpmn:Task') {
modeling.updateProperties(shape, {
name: '我是修改后的Task名稱'
})
}
}
})
})
}
當然你也可以一次性修改多個屬性:
modeling.updateProperties(startEventElement, {
name: '我是修改后的虛線節(jié)點',
isInterrupting: false
})
我通過查找前面的 meta-model descriptor 知道StartEvent還有一個isInterrupting屬性, 于是試著修改它, 結(jié)果竟然成功了, 將開始節(jié)點變?yōu)榱颂摼€為邊框的節(jié)點:
當然你也可以加一些除了meta-model descriptor里的一些自定義屬性:
modeling.updateProperties(shape, {
name: '我是修改后的虛線節(jié)點',
isInterrupting: false,
customText: '我是自定義的text屬性'
})
只不過, 它們會被放到$attrs中:
并且這種方式, 也是可以直接修改到bpmn文件中的:
后語
這一章節(jié)主要是向大家介紹了一下bpmn properties的概念以及操作方式...其實在這之前, 我甚至不知道怎么給xml標簽上添加屬性, 也甚至不知道updateProperties怎么使用??...
還好皮厚的我不恥下問, 在群里問了一些小伙伴...
哈哈哈, 手動艾特感謝 @網(wǎng)易-付超老哥 還有@李岱老哥 , 在研究bpmn.js屬性上面給我的幫助, 當然我也知道很多小伙伴希望我能快點更上一篇關(guān)于properties-panel的內(nèi)容...
但今天真的有點累了... 容我先緩一緩, 咱明天再更行不??.
(嗯...不行也得行, 我說了算...)
系列全部目錄請查看此處: 《全網(wǎng)最詳bpmn.js教材目錄》
最后, 如果你也對bpmn.js 感興趣可以進我們的bpmn.js交流群??????, 共同學習, 共同進步.
關(guān)注霖呆呆(LinDaiDai)的公眾號:LinDaiDai 選擇 其它 菜單中的 bpmn.js群 即可??