搜索文档
安装 bpmn-js
sh
npm i bpmn-js事件监听
在 BpmnModeler 实例对象身上有个 on 方法,可以监听事件。所有事件可以通过 new BpmnModeler().get('eventBus')._listeners 查看。
selection.changed:选中/取消选中元素时触发。
工作流引擎
封装组件
Vue 文件
vue
<script setup>
import BPMN from "./components/BPMN.vue";
</script>
<template>
<div class="app">
<BPMN />
</div>
</template>
<style scoped>
.app {
width: 100vw;
height: 100vh;
}
</style>vue
<script setup>
import {onMounted, ref, useTemplateRef, watch} from "vue";
import BpmnModeler from 'bpmn-js/lib/Modeler';
import 'bpmn-js/dist/assets/bpmn-js.css';
import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
import bpmnXML from '../assets/default.bpmn?raw';
let modeler = null;
let currentNode = null;
let loading = ref(false);
let tabIndex = ref('flow'); // Tab 索引
let flowForm = ref(null); // 流程配置
let nodeForm = ref(null); // 节点配置
const container = useTemplateRef('container');
// 导出 XML
async function importXML() {
loading.value = true;
let res = await modeler.saveXML({format: true});
console.log('res', res.xml);
loading.value = false;
}
// 清空
async function clear() {
loading.value = true;
await modeler.importXML(bpmnXML);
let processBusinessObject = modeler.get('canvas').getRootElement().businessObject;
flowForm.value = {
id: processBusinessObject.get('id'),
name: processBusinessObject.get('name'),
zdy: processBusinessObject.get('zdy'),
}
loading.value = false;
}
// 监听流程表单
watch(flowForm, newVal => {
if (newVal) {
modeler.get('modeling').updateProperties(modeler.get('canvas').getRootElement(), {
name: newVal.name,
zdy: newVal.zdy
})
}
}, {deep: true})
// 监听节点表单
watch(nodeForm, (newVal, oldVal) => {
if (newVal && oldVal) {
modeler.get('modeling').updateProperties(currentNode, {
name: newVal.name,
zdy: newVal.zdy
});
}
}, {deep: true})
onMounted(async () => {
loading.value = true;
modeler = new BpmnModeler({
container: container.value,
additionalModules: [{
translate: ['value', (template, replacements) => {
replacements = replacements || {};
// 中文字典
const translations = {
'Search in diagram': '在图表中搜索',
'Activate hand tool': '小手工具',
'Activate lasso tool': '套索工具',
'Activate create/remove space tool': '激活创建/移除空间工具',
'Activate global connect tool': '箭头',
'Create start event': '开始节点',
'Create intermediate/boundary event': '中间件',
'Create end event': '结束节点',
'Create gateway': '创建网关',
'Create task': '创建任务',
'Create data object reference': '创建数据对象引用',
'Create data store reference': '创建数据存储引用',
'Create expanded sub-process': '创建子流程',
'Create pool/participant': '创建池/参与者',
'Create group': '创建群组',
};
template = translations[template] || template;
return template.replace(/{([^}]+)}/g, function(_, key) {
return replacements[key] || '{' + key + '}';
});
}]
}]
});
await modeler.importXML(bpmnXML);
// 在 processBusinessObject 中可获取流程属性
let processBusinessObject = modeler.get('canvas').getRootElement().businessObject;
flowForm.value = {
id: processBusinessObject.get('id'),
name: processBusinessObject.get('name'),
zdy: processBusinessObject.get('zdy'),
}
// 选择任务节点时触发
modeler.on('selection.changed', e => {
if (e.newSelection.length === 1 && e.newSelection[0].type === 'bpmn:Task') {
currentNode = e.newSelection[0];
// 通过 currentNode.businessObject 读取 bpmn 节点属性,自定义属性在 currentNode.businessObject.$attrs 里。
nodeForm.value = {
id: currentNode.businessObject.id,
name: currentNode.businessObject.name,
zdy: currentNode.businessObject.$attrs.zdy,
}
tabIndex.value = 'node';
} else {
currentNode = null;
nodeForm.value = null;
tabIndex.value = 'flow';
}
})
// 画布默认居中
modeler.get('canvas').zoom('fit-viewport', 'center');
// 隐藏右下角 BPMN.IO 的 Logo
document.querySelector('.bjs-powered-by').style.display = 'none';
loading.value = false;
})
</script>
<template>
<div class="bpmn" v-loading="loading">
<div ref="container" />
<div class="form">
<div class="btns">
<el-button type="primary" icon="Download" plain @click="importXML">导出 XML</el-button>
<el-button type="danger" icon="Delete" plain @click="clear">清空</el-button>
</div>
<el-tabs v-model="tabIndex">
<el-tab-pane label="流程配置" name="flow">
<el-form ref="flowFormRef" :model="flowForm" label-width="auto" v-if="flowForm">
<el-form-item label="流程 ID">
<el-input v-model="flowForm.id" disabled />
</el-form-item>
<el-form-item label="流程名称">
<el-input v-model="flowForm.name" />
</el-form-item>
<el-form-item label="自定义配置">
<el-input v-model="flowForm.zdy" />
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="节点配置" name="node">
<el-form ref="nodeFormRef" :model="nodeForm" label-width="auto" v-if="nodeForm">
<el-form-item label="节点 ID">
<el-input v-model="nodeForm.id" disabled />
</el-form-item>
<el-form-item label="节点名称">
<el-input v-model="nodeForm.name" />
</el-form-item>
<el-form-item label="自定义配置">
<el-input v-model="nodeForm.zdy" />
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<style scoped>
.bpmn {
height: 100%;
display: grid;
grid-template-columns: 1fr 400px;
gap: 10px;
> div {
height: 100%;
}
.form {
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
padding: 10px;
box-sizing: border-box;
.btns {
display: flex;
justify-content: flex-end;
}
}
}
</style>其他文件
js
import {createApp} from 'vue';
import ElementPlus from 'element-plus';
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import 'element-plus/dist/index.css';
import './style.css';
import App from './App.vue';
const app = createApp(App);
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(ElementPlus);
app.mount('#app');css
* {
margin: 0;
padding: 0;
}xml
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:bpmn="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="Definitions_0prl7x2"
targetNamespace="http://bpmn.io/schema/bpmn"
exporter="bpmn-js (https://demo.bpmn.io)"
exporterVersion="18.9.0"
>
<bpmn:process id="Process_03vftld" name="默认流程" zdy="流程自定义属性" isExecutable="false">
<bpmn:startEvent id="StartEvent_19wzb6o" name="开始">
<bpmn:outgoing>Flow_1kaoirg</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:task id="Activity_1j6wqk8" name="操作节点1" zdy="节点自定义属性">
<bpmn:incoming>Flow_1kaoirg</bpmn:incoming>
</bpmn:task>
<bpmn:sequenceFlow id="Flow_1kaoirg" sourceRef="StartEvent_19wzb6o" targetRef="Activity_1j6wqk8"/>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_03vftld">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_19wzb6o">
<dc:Bounds x="152" y="102" width="36" height="36"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1j6wqk8_di" bpmnElement="Activity_1j6wqk8">
<dc:Bounds x="280" y="80" width="100" height="80"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1kaoirg_di" bpmnElement="Flow_1kaoirg">
<di:waypoint x="188" y="120"/>
<di:waypoint x="280" y="120"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>json
{
"name": "vue3",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"bpmn-js": "^18.9.1",
"element-plus": "^2.11.9",
"vue": "^3.5.13"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"vite": "^6.2.0"
}
}
