Bpmn.js activiti 流程編輯器詳細(xì)教程
前言
流程編輯器
什么是流程編輯器:
流程編輯器是一種用于創(chuàng)建、編輯和管理流程圖的工具。它提供了一個(gè)可視化的界面,使用戶(hù)能夠以圖形化的方式定義和配置流程的各個(gè)步驟、條件和流程間的關(guān)系。
流程編輯器通常用于業(yè)務(wù)流程管理、工作流程管理和業(yè)務(wù)流程自動(dòng)化等領(lǐng)域。它可以幫助用戶(hù)輕松地設(shè)計(jì)和管理復(fù)雜的流程,而無(wú)需編寫(xiě)大量的代碼。通過(guò)拖拽和連接不同的圖形元素,用戶(hù)可以定義流程的起始點(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ù)等。
流程編輯器有多種不同的種類(lèi),每種都具有不同的特點(diǎn)和用途。以下是一些常見(jiàn)的流程編輯器種類(lèi):
- 工作流程編輯器(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圖,包括類(lèi)圖、時(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)的流程編輯器種類(lèi),實(shí)際上還有許多其他類(lèi)型的流程編輯器,每種都有其特定的用途和功能。具體使用哪種編輯器取決于具體的需求和應(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ù)用戶(hù)的操作進(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è)專(zhuān)門(mén)用于處理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.下載依賴(lài)
最簡(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.引入樣式
安裝好依賴(lài)后,在main.js文件中引入樣式:
// bpmn 相關(guān)依賴(lài) 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的繪圖容器為上門(mén)獲取的畫(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': '修改類(lèi)型',
'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í)候我們期望的是在用戶(hù)在進(jìn)行不同操作的時(shí)候能夠監(jiān)聽(tīng)到他操作的是什么, 從而做想要做的事情.
是進(jìn)行了shape的新增還是進(jìn)行了線(xià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)用戶(hù)點(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)用中, 你更多的需要知道用戶(hù)要進(jìn)行什么操作, 好寫(xiě)對(duì)應(yīng)的業(yè)務(wù)邏輯.
這里就以工作中要用到的場(chǎng)景為案例進(jìn)行講解.
- 新增了shape
- 新增了線(xiàn)(connection)
- 刪除了shape和connection
- 移動(dòng)了shape和線(xiàn)
// 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的刪除, 因此這里只判斷是否是線(xiàn)
if (this.isSequenceFlow(shape.type)) {
console.log('刪除了線(xiàn)')
}
}
if (!this.isInvalid(shape.type)) {
if (this.isSequenceFlow(shape.type)) {
console.log('改變了線(xiàn)')
}
}
},
getShape(id) {
var elementRegistry = this.bpmnModeler.get('elementRegistry')
return elementRegistry.get(id)
},
isInvalid (param) { // 判斷是否是無(wú)效的值
return param === null || param === undefined || param === ''
},
isSequenceFlow (type) { // 判斷是否是線(xiàn)
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-07
JS定時(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-01
js實(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-11
js字符串轉(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-01
Javascript:為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

