本文在首发于胖蔡说技术首发,点击查看[原文],获取更多bpmn-js相关知识。
bpmn.js
是由开源工作流引擎camunda
内部组织BPMN.IO
组织开发的一款基于BPMN 2.0
的工作流展示、编辑的web
端工具库。由于工作流引擎activiti、flowable、camunda
属于同宗分流,其工作流定义格式大致相同,所以我们可以使用bpmn.js
完美融合其中任一工作流引。
什么是工作流引擎?
这里引用咖啡兔在 **《Activiti实战》 **一书中对于工作流的描述 :工作流(Work Flow)引擎被广泛应用于各种信息化系统中,将原本散乱甚至混乱的业务梳理后制定成业务规范流程,进而约束业务的规范化处理和运转。需求人员、开发人员共同协作制定了符合BPMN 2.0规范的流程定义,之后将其部署到工作流引擎中,由它自动驱动业务流程的进行。
完整的工作流周期如上:
- 定义:收集业务需求并转化为流程定义。
- 发布:在系统管理(平台)中发布流程定义。
- 执行:体的流程引擎按照事先定义的流程处理路线以任务驱动的方式执行业务流程。
- 监控:办理任务的同时收集每个任务(Task)的结果,然后根据结果做出相应处理。
- 优化:根据整个流程的运行过程结果分析问题的根源,然后在此基础上进一步改进,并再次开始一个新的周期。
BPMN是什么?
BPMN
是Business Process Modeling Notation
的简写,中文含义是业务流程管理,是一套达成企业各种业务环节整合的全面管理模式。是由BPMN
标准组织发布的,其第一版BPMN 1.0
规范于2004年5月
发布。经过多年的改进新的规范BPMN 2.0
于2011年发布。而bpmn.js则是一款由BPMN.IO
组织基于BPMN 2.0开发的一款前端开发工具集。
bpmn.js
的官网对其定位也是"View and edit BPMN 2.0 diagrams in the browser.",如下介绍如何在vue3中使用bpmn.j来绘制和展示流程图。
安装
我使用的是vue3
接入bpmn.js
,可以通过npm对齐进行包管理,需要注意的bpmn.js目前尚未支持typescript,所以开发过程中可能会爆红,直接忽略即可。
css
$ npm i --save bpmn-js
展示流程图
bpmn
支持使用读取xml
文件通过xml
文件反解析生成对应的svg
组合的方式生成流程图。如下为一个简单的Web
展示流程图的样式。
xml
<script setup lang="ts">
import BpmnViewer from 'bpmn-js'
import { onMounted } from 'vue'
onMounted(() => {
// the diagram you are going to display
const bpmnXML: string = `<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="diagram_Process_1706756685602" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"><bpmn2:process id="Process_1706756685602" name="业务流程_1706756685602" isExecutable="true"><bpmn2:startEvent id="Event_1smj9ot"><bpmn2:outgoing>Flow_0jdcl54</bpmn2:outgoing></bpmn2:startEvent><bpmn2:intermediateThrowEvent id="Event_0jfhfba"><bpmn2:incoming>Flow_0jdcl54</bpmn2:incoming><bpmn2:outgoing>Flow_1ol0ntk</bpmn2:outgoing></bpmn2:intermediateThrowEvent><bpmn2:sequenceFlow id="Flow_0jdcl54" sourceRef="Event_1smj9ot" targetRef="Event_0jfhfba" /><bpmn2:endEvent id="Event_0a5hgl0"><bpmn2:incoming>Flow_1ol0ntk</bpmn2:incoming></bpmn2:endEvent><bpmn2:sequenceFlow id="Flow_1ol0ntk" sourceRef="Event_0jfhfba" targetRef="Event_0a5hgl0" /></bpmn2:process><bpmndi:BPMNDiagram id="BPMNDiagram_1"><bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1706756685602"><bpmndi:BPMNEdge id="Flow_0jdcl54_di" bpmnElement="Flow_0jdcl54"><di:waypoint x="448" y="180" /><di:waypoint x="572" y="180" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_1ol0ntk_di" bpmnElement="Flow_1ol0ntk"><di:waypoint x="608" y="180" /><di:waypoint x="732" y="180" /></bpmndi:BPMNEdge><bpmndi:BPMNShape id="Event_1smj9ot_di" bpmnElement="Event_1smj9ot"><dc:Bounds x="412" y="162" width="36" height="36" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Event_0jfhfba_di" bpmnElement="Event_0jfhfba"><dc:Bounds x="572" y="162" width="36" height="36" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Event_0a5hgl0_di" bpmnElement="Event_0a5hgl0"><dc:Bounds x="732" y="162" width="36" height="36" /></bpmndi:BPMNShape></bpmndi:BPMNPlane></bpmndi:BPMNDiagram></bpmn2:definitions>
`
const viewer = new BpmnViewer({
container: '#bpmnCanvas',
})
viewer
.importXML(bpmnXML)
.then((result) => {
const { warnings } = result
console.log('success !', warnings)
viewer.get('canvas').zoom('fit-viewport')
})
.catch((err) => {
const { warnings, message } = err
console.log('something went wrong:', warnings, message)
})
})
</script>
<template>
<div>
测试页面
<div id="bpmnCanvas" class="bpmn-viewer" style="width: 90%; height: 70vh" />
</div>
</template>
展示结果如下:
如上,需要注意的是:
bpmn-js
是通过挂载元素实现canvas
绘制的,需要在元素加载后进行创建操作。- 导入的
xml
数据为txt
文本数据
流程编辑器
bpmn-js
提供 BpmnModeler
用于实现流程编辑器的绘制工作。编辑器支持基础流程组件的绘制,同时也可以根据需要自行进行客制化组件的开发。流程编辑器除了主编辑模块还需要右侧的属性板用于配置组件属性等相关信息。
css
$ npm i --save bpmn-js-properties-panel camunda-bpmn-moddle
通过将其使用tsx
封装,在vue
文件中直接调用使用,以便于后期的扩展和自定义组件信息。如下为创建自定义组件 bpmn.tsx
typescript
import { defineComponent, markRaw, onMounted } from 'vue'
import type { ExtractPublicPropTypes } from 'vue'
// bpmn相关引入
import 'bpmn-js/dist/assets/diagram-js.css' // 左边工具栏以及编辑节点的样式
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
import BpmnModeler from 'bpmn-js/lib/Modeler'
// bpmn-js-properties-panel相关
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css'
import propertiesPanelModule from 'bpmn-js-properties-panel'
import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'
import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda'
import './bpmn.css'
const bpmnViewProps = {
data: {
type: String,
},
}
export type BpmnViewProps = ExtractPublicPropTypes<typeof bpmnViewProps>
const BpmnView = defineComponent((props: BpmnViewProps, context) => {
onMounted(() => {
const containerEl = document.getElementById('container')
const bpmnModeler = markRaw(
new BpmnModeler({
container: containerEl,
// 添加控制板
propertiesPanel: {
parent: '#js-properties-panel',
},
// 右侧属性面板
additionalModules: [propertiesPanelModule, propertiesProviderModule, translateModule],
moddleExtensions: {
camunda: camundaModdleDescriptor,
},
}),
)
bpmnModeler.createDiagram(() => {
bpmnModeler.get('canvas').zoom('fit-viewport')
})
})
return () => {
return (
<div class="containerBox" style="position: relative">
<div id="container" style="width: calc(100vw - 750px); height: calc(100vh - 150px)"></div>
<div id="js-properties-panel" class="panel"></div>
</div>
)
}
})
export default BpmnView
组件创建完成后只需在Vue中进行引用即可:
xml
// main.vue
<script setup lang="ts">
import { BpmnView } from '../../components'
</script>
<template>
<div>
<BpmnView />
</div>
</template>
<style scoped></style>