vue+activiti做工作流,前端bpmn-js的一些配置
bpmn的使用
bpmn.io官网,可下载bpmn-js用于前端制作流程图,用于配合后端activiti,使用流程定义工具,按照BPMN规范,用流程符号将流程描述出来创建bpmn文件(实为xml文件):
Download bpmn-js | Toolkits | bpmn.io
pnpm install bpmn-js@7.3.1
pnpm install bpmn-js-properties-panel@0.37.2
pnpm install camunda-bpmn-moddle@4.4.0
【注】bpmn-js版本不一样,会提示装相应的版本其他插件。此处标准版本,仅在例子中使用
1.创建,编辑(包含左侧工具栏和右侧工具栏)
(此处有些因为需求而修改代理人、候选人、候选组维护方式,不需要可忽略。维护方式参考bpmn-js画流程图 (四)右侧属性面板中执行人,候选人与候选组可以通过 用户、机构(角色)选择 - 猪脚踏浪 - 博客园 (cnblogs.com) )
<template>
<div>
<div class="containers">
<div class="canvas" ref="canvas"></div>
<div id="js-properties-panel" class="panel"></div>
</div>
<EmpModal @register="registerModalEmp" @success="empSubmit" />
<EmpModalCheck
@register="registerModalEmpCheck"
@success="empSubmitCheck"
/>
<RoleModal
@register="registerModalRole"
@success="roleSubmitCheck"
></RoleModal>
</div>
</template>
<script lang='ts'>
import { defineComponent, onMounted, ref } from 'vue'
import BpmnModeler from 'bpmn-js/lib/Modeler' //画流程图
// import { xmlStr } from './mock' //初始流程图配置文件
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 customTranslate from './customTranslate/customTranslate' // 汉化包引入
import DefaultEmptyXML from './default/defaultEmpty'
import { EmpModal } from '@/components/Common' //单选
import { EmpModalCheck } from '@/components/Common' //多选
import { RoleModal } from '@/components/Common' //多选
import { useModal } from '@/components/Modal'
import $ from 'jquery'
export default defineComponent({
name: 'bpmnDesigner',
components: { EmpModal, EmpModalCheck, RoleModal },
emits: ['save'],
props: ['processId', 'processName', 'xmlStr'],
setup(props, { emit }) {
// bpmn建模器
const bpmnModeler = ref(null)
const container = ref(null)
const canvas = ref(null)
const processId = ref('')
const processName = ref('')
const xmlStr = ref('')
processId.value = props.processId
processName.value = props.processName
xmlStr.value = props.xmlStr
// 将汉化包包装成一个模块
const customTranslateMolude = {
translate: ['value', customTranslate],
}
function init() {
// 获取到属性ref为“canvas”的dom节点
const canvasTmp = canvas.value
// 建模
bpmnModeler.value = new BpmnModeler({
container: canvasTmp, //
//添加控制板
propertiesPanel: {
parent: '#js-properties-panel',
},
additionalModules: [
// 右边的属性栏
//低版本
propertiesPanelModule,
propertiesProviderModule,
customTranslateMolude,
],
moddleExtensions: {
camunda: camundaModdleDescriptor,
},
})
createNewDiagram()
}
/* 创建新的流程图 */
function createNewDiagram() {
// 将字符串转换成图显示出来,
let prefix = 'activiti'
let newId = processId.value ||`Process_${new Date().getTime()}` //processId.value ||
let newName = processName.value || `业务流程_${new Date().getTime()}`
let xmlString = xmlStr.value || DefaultEmptyXML(newId, newName, prefix)//xmlStr用于回显
bpmnModeler.value.importXML(xmlString, (err) => {
if (err) {
// console.error(err)
} else {
// 这里是成功之后的回调, 可以在这里做一系列事情
success()
}
})
}
function success() {
// console.log('创建成功!')
}
//----------------修改属性人员相关内容------------
function empSelect(value) {
if (value == 'assignee') {
openModalEmp(true)
} else if (value == 'candidateUsers') {
openModalEmpCheck(true)
} else if (value == 'candidateGroups') {
openModalRole(true)
}
}
//---------------代理人
const [registerModalEmp, { openModal: openModalEmp }] = useModal()
const empSubmit = (values: any) => {
setAassignee(values.empNo)
}
//----------------候选选用户--------------------
const [registerModalEmpCheck, { openModal: openModalEmpCheck }] = useModal()
const empSubmitCheck = (values: any) => {
setCandidateUsers(values.ids)
}
//------------------候选用户组------------------
const [registerModalRole, { openModal: openModalRole }] = useModal()
const roleSubmitCheck = (values: any) => {
setCandidateGroups(values.roleKeys)
}
//---------------------------------------
onMounted(() => {
init()
$('#js-properties-panel').on(
'click',
'#camunda-assignee',
function () {
empSelect('assignee')
},
)
$('#js-properties-panel').on(
'click',
'#camunda-candidateUsers',
function () {
empSelect('candidateUsers')
},
)
$('#js-properties-panel').on(
'click',
'#camunda-candidateGroups',
function () {
empSelect('candidateGroups')
},
)
})
function save() {
bpmnModeler.value.saveXML({ format: true }, function (err, xml) {
if (err) {
console.log('保存失败')
} else {
emit('save', xml)
}
})
}
//camunda-assignee代理人
function setAassignee(value) {
//触发修改事件,触发绑定的事件,更新数据
$('#camunda-assignee').val(value)
var changeEvent = document.createEvent('HTMLEvents')
changeEvent.initEvent('change', true, true)
$('#camunda-assignee')[0].dispatchEvent(changeEvent)
}
//camunda-candidateUsers候选用户
function setCandidateUsers(value) {
$('#camunda-candidateUsers').val(value)
var changeEvent = document.createEvent('HTMLEvents')
changeEvent.initEvent('change', true, true)
$('#camunda-candidateUsers')[0].dispatchEvent(changeEvent)
}
//camunda-candidateGroups候选组
function setCandidateGroups(value) {
$('#camunda-candidateGroups').val(value)
var changeEvent = document.createEvent('HTMLEvents')
changeEvent.initEvent('change', true, true)
$('#camunda-candidateGroups')[0].dispatchEvent(changeEvent)
}
return {
canvas,
bpmnModeler,
save,
processId,
init,
registerModalEmp,
empSubmit,
registerModalEmpCheck,
empSubmitCheck,
registerModalRole,
roleSubmitCheck,
}
},
})
</script>
<style scoped lang="less">
.containers {
position: absolute;
background-color: #ffffff;
/* width: 100%;
height: 100%;*/
width: 94%;
height: calc(100% - 45px - 120px);
}
.canvas {
width: 80%;
height: 100%;
}
.panel {
position: absolute;
right: 0;
top: 0;
width: 20%;
height: calc(100% - 45px - 120px);
.bpp-properties-panel {
height: 100%;
.bpp-properties {
height: 100%;
.bpp-properties-tabs-container {
height: 100%;
:deep(.bpp-properties-tab) {
overflow-y: scroll;
}
}
}
}
}
:deep(.bpp-properties-tab) {
height: calc(100vh - 350px);
overflow-y: scroll;
}
:deep(#camunda-id) {
pointer-events: none;
background-color: #f8f8f8;
}
:deep(#camunda-name) {
pointer-events: none;
background-color: #f8f8f8;
}
:deep(.clear) {
display: none;
}
:deep(#camunda-id + .bpp-error-message) {
display: none;
}
</style>
2.main.ts或main.js需要引入bpmn-js相关样式
//工作流样式
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'
// import 'bpmn-js-properties-panel/dist/assets/properties-panel.css'//右侧信息栏(高版本)
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css'//右侧信息栏(低版本)
import $ from 'jquery'
3.初始模板DefaultEmptyXML.js文件
export default (key, name, type) => {
if (!type) type = "camunda";
const TYPE_TARGET = {
activiti: "http://activiti.org/bpmn",
camunda: "http://bpmn.io/schema/bpmn",
flowable: "http://flowable.org/bpmn"
};
return `<?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_${key}"
targetNamespace="${TYPE_TARGET[type]}">
<bpmn2:process id="${key}" name="${name}" isExecutable="true">
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="${key}">
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>`;
};