Bpmn.js activiti 流程編輯器詳細(xì)教程
前言
流程編輯器
什么是流程編輯器:
流程編輯器是一種用于創(chuàng)建、編輯和管理流程圖的工具。它提供了一個(gè)可視化的界面,使用戶能夠以圖形化的方式定義和配置流程的各個(gè)步驟、條件和流程間的關(guān)系。
流程編輯器通常用于業(yè)務(wù)流程管理、工作流程管理和業(yè)務(wù)流程自動(dòng)化等領(lǐng)域。它可以幫助用戶輕松地設(shè)計(jì)和管理復(fù)雜的流程,而無(wú)需編寫(xiě)大量的代碼。通過(guò)拖拽和連接不同的圖形元素,用戶可以定義流程的起始點(diǎn)、結(jié)束點(diǎn)、流程分支、條件判斷、任務(wù)執(zhí)行等。
流程編輯器還通常提供了一些額外的功能,如版本控制、權(quán)限管理、流程模板的導(dǎo)入和導(dǎo)出等。它可以與其他系統(tǒng)集成,以便將流程定義應(yīng)用于實(shí)際的業(yè)務(wù)場(chǎng)景中。
流程編輯器的目的是簡(jiǎn)化流程設(shè)計(jì)和管理的過(guò)程,提高工作效率,并確保流程的正確性和一致性。它在許多領(lǐng)域中都有廣泛的應(yīng)用,包括項(xiàng)目管理、工作流程自動(dòng)化、電子商務(wù)等。
流程編輯器有多種不同的種類,每種都具有不同的特點(diǎn)和用途。以下是一些常見(jiàn)的流程編輯器種類:
- 工作流程編輯器(Workflow Editors):用于創(chuàng)建和管理工作流程,包括定義任務(wù)、流程分支、條件和工作流程的執(zhí)行順序等。
- 業(yè)務(wù)流程管理(BPM)編輯器(Business Process Management Editors):用于設(shè)計(jì)和管理業(yè)務(wù)流程,支持復(fù)雜的流程建模和流程優(yōu)化。
- UML(統(tǒng)一建模語(yǔ)言)編輯器(UML Editors):用于創(chuàng)建和編輯UML圖,包括類圖、時(shí)序圖、用例圖等,用于軟件系統(tǒng)的設(shè)計(jì)和建模。
- 數(shù)據(jù)流程編輯器(Data Flow Editors):用于創(chuàng)建和管理數(shù)據(jù)流程,包括數(shù)據(jù)輸入、處理和輸出的流程圖。
- 網(wǎng)絡(luò)拓?fù)渚庉嬈鳎∟etwork Topology Editors):用于設(shè)計(jì)和管理網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu),包括節(jié)點(diǎn)、連接和網(wǎng)絡(luò)設(shè)備的配置。
- 流程圖編輯器(Flowchart Editors):用于創(chuàng)建和編輯流程圖,包括流程的各個(gè)步驟、條件和流程控制的圖形表示。
- 規(guī)則引擎編輯器(Rule Engine Editors):用于創(chuàng)建和管理規(guī)則引擎,包括定義規(guī)則、條件和規(guī)則執(zhí)行順序等。
- 這只是一些常見(jiàn)的流程編輯器種類,實(shí)際上還有許多其他類型的流程編輯器,每種都有其特定的用途和功能。具體使用哪種編輯器取決于具體的需求和應(yīng)用場(chǎng)景。
我用的是業(yè)務(wù)流程編輯器(bpmn.js)
一、bpmn.js是什么?
1.bpmn.js簡(jiǎn)介
bpmn.js是一個(gè)用于在Web應(yīng)用程序中渲染和編輯BPMN(Business Process Model and Notation)流程圖的JavaScript庫(kù)。它提供了一套功能強(qiáng)大的API和工具,可以幫助開(kāi)發(fā)人員在應(yīng)用程序中集成BPMN流程圖的顯示和編輯功能。
使用bpmn.js,開(kāi)發(fā)人員可以將BPMN流程圖嵌入到他們的應(yīng)用程序中,并與其它組件進(jìn)行交互。它支持創(chuàng)建、修改和刪除BPMN元素,如任務(wù)、網(wǎng)關(guān)、事件等,并提供了豐富的事件和回調(diào)函數(shù),以便開(kāi)發(fā)人員可以根據(jù)用戶的操作進(jìn)行相應(yīng)的處理。
bpmn.js還支持將BPMN流程圖導(dǎo)入和導(dǎo)出為XML格式,以便與其他BPMN工具進(jìn)行交互和共享。它還提供了豐富的樣式和主題選項(xiàng),使開(kāi)發(fā)人員可以自定義流程圖的外觀和樣式。
總的來(lái)說(shuō),bpmn.js是一個(gè)強(qiáng)大的工具,可以幫助開(kāi)發(fā)人員在Web應(yīng)用程序中實(shí)現(xiàn)BPMN流程圖的顯示和編輯功能,并與其它組件進(jìn)行集成。
官網(wǎng):https://bpmn.io/.
2.為什么要選擇bpmn.js
activiti 官方支持的流程編輯器是ActivitiModeler,現(xiàn)在已經(jīng)停止維護(hù)而且如果需要前后端分離使用流程編輯器,并不是很友好。
選擇使用bpmn.js有以下幾個(gè)原因:
- 完整的BPMN支持:bpmn.js是一個(gè)專門用于處理BPMN流程圖的庫(kù),它提供了完整的BPMN規(guī)范支持,包括各種BPMN元素、事件和流程控制等。這使得它成為構(gòu)建和管理BPMN流程圖的理想選擇。
- 強(qiáng)大的功能和靈活性:bpmn.js提供了豐富的API和工具,使開(kāi)發(fā)人員可以輕松地創(chuàng)建、修改和刪除BPMN元素。它還支持導(dǎo)入和導(dǎo)出BPMN流程圖,以便與其他BPMN工具進(jìn)行交互和共享。此外,bpmn.js還提供了自定義樣式和主題的選項(xiàng),使開(kāi)發(fā)人員可以根據(jù)需要自定義流程圖的外觀和樣式。
- 跨平臺(tái)和易于集成:bpmn.js是基于JavaScript的庫(kù),可以在各種Web應(yīng)用程序中使用。它與現(xiàn)代Web技術(shù)和框架(如React、Angular和Vue.js)兼容,并且可以與其他組件和工具進(jìn)行無(wú)縫集成。這使得它非常適合在現(xiàn)有的應(yīng)用程序中添加BPMN流程圖的顯示和編輯功能。
- 社區(qū)支持和活躍度:bpmn.js擁有龐大的開(kāi)源社區(qū)支持,并且由Camunda等知名公司進(jìn)行維護(hù)和更新。這意味著它有一個(gè)活躍的開(kāi)發(fā)者社區(qū),可以提供幫助、解決問(wèn)題并分享經(jīng)驗(yàn)。
- 總而言之,選擇使用bpmn.js可以讓開(kāi)發(fā)人員輕松地在Web應(yīng)用程序中實(shí)現(xiàn)BPMN流程圖的顯示和編輯功能,并且具有強(qiáng)大的功能、靈活性和跨平臺(tái)的特點(diǎn)。
二、在vue中集成Bpmn.js
1.下載依賴
最簡(jiǎn)單的一種使用方式:直接使用
CDN
將bpmn.js
引入到代碼中
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>BPMNJS</title> <!--CDN加速--> <script src="https://unpkg.com/bpmn-js@6.0.2/dist/bpmn-viewer.development.js"></script><!--引入一個(gè)簡(jiǎn)單的xml字符串--> <script src="./xmlStr.js"></script> <style> #canvas { height: 400px; } </style> </head> <body> <div id="canvas"></div> <script> var bpmnJS = new BpmnJS({ container: '#canvas' }); bpmnJS.importXML(xmlStr, err => { if (!err) { // 讓圖能自適應(yīng)屏幕 var canvas = bpmnJS.get('canvas') canvas.zoom('fit-viewport') } else { console.log('something went wrong:', err); } }); </script> </body> </html>
(上面的xmlStr.js
就是自定義的文件,里面放置了關(guān)于流程的xml,也可以不引用直接在vue文件中定義)
如上面的案例所示, 我們使用CDN
加速直接引入bpmn.js
, 然后本地指定一個(gè)容器(也就是id
為canvas
的那個(gè)div
), 接著用bpmn.js
提供的方法importXML
就可以解析xml
字符串生成對(duì)應(yīng)的工作流圖了。
運(yùn)行代碼:
上面提供的使用方式是一種最基本的方式,僅僅是將圖展示出來(lái),不能自己繪畫(huà)也不能操作. 所以在工作中使用更多的還是采用npm安裝到項(xiàng)目中使用. 我們可以使用以下命令進(jìn)行安裝:
使用
npm
下載
npm install --save bpmn-js
注意: 如果在已有項(xiàng)目引入,可能會(huì)因?yàn)榘姹締?wèn)題導(dǎo)致啟動(dòng)失敗,最好是看看相對(duì)于的版本
我這里使用的版本是:
"dependencies": { "bpmn-js": "^6.2.1", "bpmn-js-properties-panel": "^0.33.1", "camunda-bpmn-moddle": "^4.3.0", "core-js": "^3.4.4", "houtaroy-bpmn-js-properties-panel-activiti": "0.0.1", "svg-sprite-loader": "3.7.3", },
2.引入樣式
安裝好依賴后,在main.js
文件中引入樣式:
// bpmn 相關(guān)依賴 import 'bpmn-js/dist/assets/diagram-js.css' import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css' import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css' import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css' // 左邊工具欄以及編輯節(jié)點(diǎn)的樣式 import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css'
新建一個(gè)bpmn.vue
頁(yè)面,編寫(xiě)html
代碼
<template> <div id="app"> <div class="container"> <!-- 創(chuàng)建一個(gè)canvas畫(huà)布 npmn-js是通過(guò)canvas實(shí)現(xiàn)繪圖的,并設(shè)置ref讓vue獲取到element --> <div class="bpmn-container"> <div class="bpmn-canvas" ref="canvas"></div> <!-- 工具欄顯示的地方 --> <div class="bpmn-js-properties-panel" id="js-properties-panel"></div> </div> <!-- 把操作按鈕寫(xiě)在這里面 --> <div class="action"> <el-button icon="el-icon-download" @click="downloadBpmn" title="下載流程文件"></el-button> <el-button icon="el-icon-picture" @click="downloadSvg" title="下載流程圖"></el-button> <el-button type="success" icon="el-icon-check" circle title="保存修改" @click="editModel"></el-button> <a hidden ref="downloadLink"></a> </div> </div> </div> </template>
編寫(xiě)js
代碼
<script> import BpmnModeler from 'bpmn-js/lib/Modeler' // 工具欄相關(guān) // import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda' import propertiesPanelModule from 'bpmn-js-properties-panel' // import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda' import activitiModdleDescriptor from './activiti.json' // 引入 import propertiesProviderModule from 'houtaroy-bpmn-js-properties-panel-activiti/lib/provider/activiti' // 漢化 import customTranslate from './customTranslate.js' export default { data () { return { modelId: '', bpmnModeler: null, canvas: null, bpmnTemplate: `` } }, methods: { newDiagram () { this.createNewDiagram(this.bpmnTemplate) }, // 下載bpmn xml文件 downloadBpmn () { const that = this that.bpmnModeler.saveXML({ format: true }, (err, xml) => { if (!err) { // 獲取文件名 const name = `${that.getFilename(xml)}.bpmn20.xml` // 將文件名以及數(shù)據(jù)交給下載方法 that.download({ name: name, data: xml }) } }) }, // 下載bpmn.svg流程圖片 downloadSvg () { const that = this that.bpmnModeler.saveXML({ format: true }, (err, date) => { if (!err) { // 獲取文件名 const name = `${that.getFilename(date)}.svg` // 從建模器畫(huà)布中提取svg圖形標(biāo)簽 let context = '' const djsGroupAll = that.$refs.canvas.querySelectorAll('.djs-group') for (let item of djsGroupAll) { context += item.innerHTML } // 獲取svg的基本數(shù)據(jù),長(zhǎng)寬高 const viewport = that.$refs.canvas .querySelector('.viewport') .getBBox() // 將標(biāo)簽和數(shù)據(jù)拼接成一個(gè)完整正常的svg圖形 const svg = ` <svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='${viewport.width}' height='${viewport.height}' viewBox='${viewport.x} ${viewport.y} ${viewport.width} ${viewport.height}' version='1.1' > ${context} </svg> ` // 將文件名以及數(shù)據(jù)交給下載方法 that.download({ name: name, data: svg }) } }) }, // 獲取文件名 getFilename (xml) { const regex = /<process.*?id="(.*?)"/ const match = xml.match(regex) if (match) { return match[1] } return null }, // 編輯模型 editModel () { const that = this that.bpmnModeler.saveXML({ format: true }, (err, xml) => { if (!err) { // 獲取文件名 const name = `${that.getFilename(xml)}` // // 從建模器畫(huà)布中提取svg圖形標(biāo)簽 // let context = '' // const djsGroupAll = this.$refs.canvas.querySelectorAll('.djs-group') // for (let item of djsGroupAll) { // context += item.innerHTML // } // // 獲取svg的基本數(shù)據(jù),長(zhǎng)寬高 // const viewport = this.$refs.canvas // .querySelector('.viewport') // .getBBox() // // 將標(biāo)簽和數(shù)據(jù)拼接成一個(gè)完整正常的svg圖形 // const svg = ` // <svg // xmlns='http://www.w3.org/2000/svg' // xmlns:xlink='http://www.w3.org/1999/xlink' // width='${viewport.width}' // height='${viewport.height}' // viewBox='${viewport.x} ${viewport.y} ${viewport.width} ${viewport.height}' // version='1.1' // > // ${context} // </svg> // ` that.$http({ url: '', method: 'post', data: that.$http.adornData({ modelId: that.modelId, name: name, bpmnXml: xml, svg: '', descritpion: '' }) }).then(({ data }) => { that.$message({ message: that.$i18n.t('publics.operation'), type: 'success', duration: 1500, onClose: () => {} }) }) } }) }, // 獲取流程圖數(shù)據(jù) getModel () { const that = this this.$http({ url: '', method: 'get', params: this.$http.adornParams({ modelId: that.modelId }) }).then(({ data }) => { that.bpmnTemplate = '`' + data + '`' this.$message({ message: this.$i18n.t('publics.operation'), type: 'success', duration: 1500, onClose: () => { that.init() } }) }) }, download ({ name = 'diagram.bpmn', data }) { // 這里就獲取到了之前設(shè)置的隱藏鏈接 const downloadLink = this.$refs.downloadLink // 把數(shù)據(jù)轉(zhuǎn)換為URI,下載要用到的 const encodedData = encodeURIComponent(data) if (data) { // 將數(shù)據(jù)給到鏈接 downloadLink.href = 'data:application/bpmn20-xml;charset=UTF-8,' + encodedData // 設(shè)置文件名 downloadLink.download = name // 觸發(fā)點(diǎn)擊事件開(kāi)始下載 downloadLink.click() } }, async init () { // 獲取畫(huà)布 element const that = this that.canvas = that.$refs.canvas // 將漢化包裝成一個(gè)模塊 const customTranslateModule = { translate: ['value', customTranslate] } // 創(chuàng)建Bpmn對(duì)象 that.bpmnModeler = new BpmnModeler({ // 設(shè)置bpmn的繪圖容器為上門獲取的畫(huà)布 element container: that.canvas, // 加入工具欄支持 propertiesPanel: { parent: '#js-properties-panel' }, additionalModules: [ // 工具欄模塊 propertiesProviderModule, propertiesPanelModule, // 漢化模塊 customTranslateModule ], moddleExtensions: { activiti: activitiModdleDescriptor } }) await that.createNewDiagram(that.bpmnTemplate) }, clearBpmn () { this.bpmnModeler.clear() }, async createNewDiagram (bpmnTemplate) { const that = this // 將字符串轉(zhuǎn)換成圖顯示出來(lái); this.bpmnModeler.importXML(bpmnTemplate, err => { if (err) { that.$Message.error('打開(kāi)模型出錯(cuò),請(qǐng)確認(rèn)該模型符合Bpmn2.0規(guī)范') } else { // 讓圖能自適應(yīng)屏幕 var canvas = that.bpmnModeler.get('canvas') canvas.zoom('fit-viewport') } }) } }, created () { this.getModel() // // 刪除 bpmn logo bpmn.io官方要求不給刪或者隱藏,否則侵權(quán) 內(nèi)部使用 // const bjsIoLogo = document.querySelector('.bjs-powered-by') // while (bjsIoLogo.firstChild) { // bjsIoLogo.removeChild(bjsIoLogo.firstChild) // } }, beforeDestroy () { this.clearBpmn() } } </script>
編寫(xiě)styly
樣式
<style> .bpmn-container { width: 100%; height: 100vh; display: flex; } .bpmn-canvas { width: calc(100% - 300px); height: 100vh; } .bpmn-js-properties-panel { width: 320px; height: inherit; overflow-y: auto; } .action { position: fixed; bottom: 40px; left: 800px; display: flex; } </style>
在這里需要注意我的代碼中,在
import
導(dǎo)入camunda
時(shí)我注釋了,是因?yàn)锽pmn.js默認(rèn)支持的是camunda
,而我的后端使用的是activiti
,兩者是不兼容的。所以需要丟棄camunda
,換成activiti
下載activiti
插件
"houtaroy-bpmn-js-properties-panel-activiti": "0.0.1",
更換對(duì)應(yīng)引入的camunda
漢化包:customTranslate.js
+translationsGerman
import translations from './translationsGerman' export default function customTranslate (template, replacements) { replacements = replacements || {} // Translate template = translations[template] || template // Replace return template.replace(/{([^}]+)}/g, function (_, key) { var str = replacements[key] if ( translations[replacements[key]] !== null && translations[replacements[key]] !== 'undefined' ) { str = translations[replacements[key]] } return str || '{' + key + '}' }) }
export default { // Labels 'Activate the global connect tool': '激活全局連接工具', 'Append {type}': '追加 {type}', 'Append EndEvent': '追加 結(jié)束事件 ', 'Append Task': '追加 任務(wù)', 'Append Gateway': '追加 網(wǎng)關(guān)', 'Append Intermediate/Boundary Event': '追加 中間/邊界 事件', 'Add Lane above': '在上面添加道', 'Divide into two Lanes': '分割成兩個(gè)道', 'Divide into three Lanes': '分割成三個(gè)道', 'Add Lane below': '在下面添加道', 'Append compensation activity': '追加補(bǔ)償活動(dòng)', 'Change type': '修改類型', 'Connect using Association': '使用關(guān)聯(lián)連接', 'Connect using Sequence/MessageFlow or Association': '使用順序/消息流或者關(guān)聯(lián)連接', 'Connect using DataInputAssociation': '使用數(shù)據(jù)輸入關(guān)聯(lián)連接', 'Remove': '移除', 'Activate the hand tool': '激活抓手工具', 'Activate the lasso tool': '激活套索工具', 'Activate the create/remove space tool': '激活創(chuàng)建/刪除空間工具', 'Create expanded SubProcess': '創(chuàng)建擴(kuò)展子過(guò)程', 'Create IntermediateThrowEvent/BoundaryEvent': '創(chuàng)建中間拋出事件/邊界事件', 'Create Pool/Participant': '創(chuàng)建池/參與者', 'Parallel Multi Instance': '并行多重事件', 'Sequential Multi Instance': '時(shí)序多重事件', 'DataObjectReference': '數(shù)據(jù)對(duì)象參考', 'DataStoreReference': '數(shù)據(jù)存儲(chǔ)參考', 'Loop': '循環(huán)', } // 這里只是部分的漢化,多的就不寫(xiě)出來(lái)了,如果有需要的可以去網(wǎng)上找找有很多
然后還有activiti.json
這個(gè)是更換activiti
必不可少的,可以看看元示例
{ "name": "Activiti", "uri": "http://activiti.org/bpmn", "prefix": "activiti", "xml": { "tagAlias": "lowerCase" }, "associations": [], "types": [ { "name": "Process", "isAbstract": true, "extends": [ "bpmn:Process" ], "properties": [ { "name": "diagramRelationId", "isAttr": true, "type": "String" } ] }, { "name": "InOutBinding", "superClass": [ "Element" ], // 就是將camunda用activiti替換掉,還有挺多的無(wú)法全部展示
引入完成后就可以看看流程編輯器的樣子。
到這里一個(gè)完整的bpmn.js
就引入完成了,下面再講講bpmn.js的事件以及監(jiān)聽(tīng)器吧
三,bpmn.js事件
這里主要是說(shuō)明關(guān)于bpmn.js
的一些事件, 通過(guò)此章節(jié)你可以了解到:
- 監(jiān)聽(tīng)
modeler
并綁定事件 - 監(jiān)聽(tīng)
element
并綁定事件 - 通過(guò)監(jiān)聽(tīng)事件判斷操作方式
1,監(jiān)聽(tīng)modeler并綁定事件
有些時(shí)候我們期望的是在用戶在進(jìn)行不同操作的時(shí)候能夠監(jiān)聽(tīng)到他操作的是什么, 從而做想要做的事情.
是進(jìn)行了shape
的新增還是進(jìn)行了線的新增.
比如如下的一些監(jiān)聽(tīng)事件:
shape.added
新增一個(gè)shape
之后觸發(fā);shape.move.end
移動(dòng)完一個(gè)shape
之后觸發(fā);shape.removed
刪除一個(gè)shape
之后觸發(fā);
繼續(xù)在項(xiàng)目案例bpmn.vue
的基礎(chǔ)上創(chuàng)建一個(gè)event.vue
文件:
// event.vue <script> ... success () { this.addModelerListener() }, // 監(jiān)聽(tīng) modeler addModelerListener() { const bpmnjs = this.bpmnModeler const that = this // 這里我是用了一個(gè)forEach給modeler上添加要綁定的事件 const events = ['shape.added', 'shape.move.end', 'shape.removed', 'connect.end', 'connect.move'] events.forEach(function(event) { that.bpmnModeler.on(event, e => { console.log(event, e) var elementRegistry = bpmnjs.get('elementRegistry') var shape = e.element ? elementRegistry.get(e.element.id) : e.shape console.log(shape) }) }) },
然后就可以獲取到相關(guān)節(jié)點(diǎn)的信息
其實(shí)具體有哪些事件我在官網(wǎng)上都沒(méi)有找到說(shuō)明, 以上只是我在查找到bpmn.io/diagram.js/…文件之后, 取的一些我項(xiàng)目里有用到的事件.
2,監(jiān)聽(tīng)element并綁定事件
上面介紹的是監(jiān)聽(tīng)modeler并綁定事件, 可能你也需要監(jiān)聽(tīng)用戶點(diǎn)擊圖形上的element或者監(jiān)聽(tīng)某個(gè)element改變:
- element.click 點(diǎn)擊元素;
- element.changed 當(dāng)元素發(fā)生改變的時(shí)候(包括新增、移動(dòng)、刪除元素)
繼續(xù)在success()
上添加監(jiān)聽(tīng)事件:
// event.vue <script> ... success () { ... this.addEventBusListener() }, addEventBusListener () { let that = this const eventBus = this.bpmnModeler.get('eventBus') // 需要使用eventBus const eventTypes = ['element.click', 'element.changed'] // 需要監(jiān)聽(tīng)的事件集合 eventTypes.forEach(function(eventType) { eventBus.on(eventType, function(e) { console.log(e) }) }) } </script>
配置好addEventBusListener()
函數(shù)后, 在進(jìn)行元素的點(diǎn)擊、新增、移動(dòng)、刪除的時(shí)候都能監(jiān)聽(tīng)到了.
但是有一點(diǎn)很不好, 你在點(diǎn)擊“畫(huà)布”的時(shí)候, 也就是根元素也可能會(huì)觸發(fā)此事件, 我們一般都不希望此時(shí)會(huì)觸發(fā), 因此我們可以在on
回調(diào)中添加一些判斷, 來(lái)避免掉不需要的情況:
eventBus.on(eventType, function(e) { if (!e || e.element.type == 'bpmn:Process') return // 這里我的根元素是bpmn:Process console.log(e) })
此時(shí)我們可以把監(jiān)聽(tīng)到返回的節(jié)點(diǎn)信息打印出來(lái)看看:
如上圖, 它會(huì)打印出該節(jié)點(diǎn)的Shape
信息和DOM
信息等, 但我們可能只關(guān)注于Shape
信息(也就是該節(jié)點(diǎn)的id
、type
等等信息), 此時(shí)我們可以使用elementRegistry
來(lái)獲取Shape
信息:
eventBus.on(eventType, function(e) { if (!e || e.element.type == 'bpmn:Process') return // 這里我的根元素是bpmn:Process console.log(e) var elementRegistry = this.bpmnModeler.get('elementRegistry') var shape = elementRegistry.get(e.element.id) // 傳遞id進(jìn)去 console.log(shape) // {Shape} console.log(e.element) // {Shape} console.log(JSON.stringify(shape)===JSON.stringify(e.element)) // true })
或者你也可以直接就用e.element
獲取到Shape
的信息, 我比較了一下它們兩是一樣的. 但是官方是推薦使用elementRegistry
的方式.
3.通過(guò)監(jiān)聽(tīng)事件判斷操作方式
上面我們已經(jīng)介紹了modeler
和element
的監(jiān)聽(tīng)綁定方式, 在事件應(yīng)用中, 你更多的需要知道用戶要進(jìn)行什么操作, 好寫(xiě)對(duì)應(yīng)的業(yè)務(wù)邏輯.
這里就以工作中要用到的場(chǎng)景為案例進(jìn)行講解.
- 新增了shape
- 新增了線(connection)
- 刪除了shape和connection
- 移動(dòng)了shape和線
// event.vue ... success () { this.addModelerListener() this.addEventBusListener() }, // 添加綁定事件 addBpmnListener () { const that = this // 獲取a標(biāo)簽dom節(jié)點(diǎn) const downloadLink = this.$refs.saveDiagram const downloadSvgLink = this.$refs.saveSvg // 給圖綁定事件,當(dāng)圖有發(fā)生改變就會(huì)觸發(fā)這個(gè)事件 this.bpmnModeler.on('commandStack.changed', function () { that.saveSVG(function(err, svg) { that.setEncoded(downloadSvgLink, 'diagram.svg', err ? null : svg) }) that.saveDiagram(function(err, xml) { that.setEncoded(downloadLink, 'diagram.bpmn', err ? null : xml) }) }) }, addModelerListener() { // 監(jiān)聽(tīng) modeler const bpmnjs = this.bpmnModeler const that = this // 'shape.removed', 'connect.end', 'connect.move' 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 // console.log(shape) if (event === 'shape.added') { console.log('新增了shape') } else if (event === 'shape.move.end') { console.log('移動(dòng)了shape') } else if (event === 'shape.removed') { console.log('刪除了shape') } }) }) }, addEventBusListener() { // 監(jiān)聽(tīng) element let that = this const eventBus = this.bpmnModeler.get('eventBus') const eventTypes = ['element.click', 'element.changed'] eventTypes.forEach(function(eventType) { eventBus.on(eventType, function(e) { if (!e || e.element.type == 'bpmn:Process') return if (eventType === 'element.changed') { that.elementChanged(eventType, e) } else if (eventType === 'element.click') { console.log('點(diǎn)擊了element') } }) }) }, elementChanged(eventType, e) { var shape = this.getShape(e.element.id) if (!shape) { // 若是shape為null則表示刪除, 無(wú)論是shape還是connect刪除都調(diào)用此處 console.log('無(wú)效的shape') // 由于上面已經(jīng)用 shape.removed 檢測(cè)了shape的刪除, 因此這里只判斷是否是線 if (this.isSequenceFlow(shape.type)) { console.log('刪除了線') } } if (!this.isInvalid(shape.type)) { if (this.isSequenceFlow(shape.type)) { console.log('改變了線') } } }, getShape(id) { var elementRegistry = this.bpmnModeler.get('elementRegistry') return elementRegistry.get(id) }, isInvalid (param) { // 判斷是否是無(wú)效的值 return param === null || param === undefined || param === '' }, isSequenceFlow (type) { // 判斷是否是線 return type === 'bpmn:SequenceFlow' }
到此這篇關(guān)于Bpmn.js activiti 流程編輯器詳細(xì)教程的文章就介紹到這了,更多相關(guān)activiti 流程編輯器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于JavaScript中forEach和each用法淺析
這篇文章主要給大家介紹了關(guān)于JavaScript中forEach和each使用方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。2017-07-07JS定時(shí)器使用,定時(shí)定點(diǎn),固定時(shí)刻,循環(huán)執(zhí)行詳解
下面小編就為大家?guī)?lái)一篇JS定時(shí)器使用,定時(shí)定點(diǎn),固定時(shí)刻,循環(huán)執(zhí)行詳解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-05-05深入理解JavaScript系列(3) 全面解析Module模式
Module模式是JavaScript編程中一個(gè)非常通用的模式,一般情況下,大家都知道基本用法,本文嘗試著給大家更多該模式的高級(jí)使用方式2012-01-01js實(shí)現(xiàn)日期天數(shù)、時(shí)分秒的倒計(jì)時(shí)完整代碼
這篇文章主要給大家介紹了關(guān)于js實(shí)現(xiàn)日期天數(shù)、時(shí)分秒的倒計(jì)時(shí)的相關(guān)資料,實(shí)現(xiàn)倒計(jì)時(shí)功能首先是得到目標(biāo)時(shí)間,然后用當(dāng)前時(shí)間減去目標(biāo)時(shí)間,最后將時(shí)間差傳化為天數(shù)、時(shí)、分、秒,需要的朋友可以參考下2023-11-11js字符串轉(zhuǎn)換成數(shù)字與數(shù)字轉(zhuǎn)換成字符串的實(shí)現(xiàn)方法
本篇文章主要是對(duì)js字符串轉(zhuǎn)換成數(shù)字與數(shù)字轉(zhuǎn)換成字符串的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-01-01Javascript:為input設(shè)置readOnly屬性(示例講解)
本篇文章主要是對(duì)Javascript中為input設(shè)置readOnly屬性的示例代碼進(jìn)行了介紹。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-12-12從setTimeout看js函數(shù)執(zhí)行過(guò)程
這篇文章主要介紹了從setTimeout看js函數(shù)執(zhí)行過(guò)程,需要的朋友可以參考下2017-12-12