加载中...

vue3中使用事件流bpmn

vue3中使用事件流bpmn

前言

最近项目规划中有使用事件流引擎,就进行了简单的调研,参考若依 RuoYi-Flowable-Plus 做了一个vue3版本的事件流引擎demo。

"bpmn-js": "^7.3.1"

js
代码解读
复制代码
<template> <div id="container" style="width: calc(100vw - 750px); height: calc(100vh - 150px)"></div> </template> <script setup lang="ts"> import { onMounted, markRaw ,ref} from ''vue''; // bpmn-js相关 import BpmnModeler from ''bpmn-js/lib/Modeler'' ; import ''bpmn-js/dist/assets/diagram-js.css''; // 左边工具栏以及编辑节点的样式 import ''bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css''; let bpmnModeler:any=null onMounted(() => { const containerEl = document.getElementById(''container''); bpmnModeler = new BpmnModeler({ container: containerEl, }) }) </script> <style> .containerBox { height: calc(100vh - 160px); margin-top: 30px; display: flex; background: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+") repeat !important; } </style>

初始化完成,页面已经显示 New Image 常用的几个配置项

js
代码解读
复制代码
bpmnModeler = new BpmnModeler(options: Options); interface Options { container: DomElement; // 渲染容器 width:string | number;// 容器宽度 height: string | number; // 容器高度 moddleExtensions: object;// 需要用的扩展包 modules:<didi.Module>[]; // 自定义且需要覆盖默认扩展包的模块列表 additionalModules: <didi.Module>[]; // 自定义且与默认扩展包一起使用的模块列表 }

目前默认的语言是英文,需要我们在扩展里面增加语言包配置 translate.ts

js
代码解读
复制代码
import translations from "./zh"; function customTranslate(template, replacements) { replacements = replacements || {}; // Translate template = translations[template] || template; // Replace return template.replace(/{([^}]+)}/g, function(_, key) { let str = replacements[key]; if ( translations[replacements[key]] !== null && translations[replacements[key]] !== "undefined" ) { // eslint-disable-next-line no-mixed-spaces-and-tabs str = translations[replacements[key]]; // eslint-disable-next-line no-mixed-spaces-and-tabs } return str || "{" + key + "}"; }); } const customTranslateModule = { translate: [''value'', customTranslate] } export default customTranslateModule

语言包zh.ts

js
代码解读
复制代码
export default { // Labels "Activate the global connect tool": "激活全局连接工具", "Append {type}": "追加 {type}", "Append EndEvent": "追加 结束事件 ", "Append Task": "追加 任务", "Append Gateway": "追加 网关", "Append Intermediate/Boundary Event": "追加 中间/边界 事件", "Add Lane above": "在上面添加道", "Divide into two Lanes": "分割成两个道", "Divide into three Lanes": "分割成三个道", "Add Lane below": "在下面添加道", "Append compensation activity": "追加补偿活动", "Change type": "修改类型", "Connect using Association": "使用关联连接", "Connect using Sequence/MessageFlow or Association": "使用顺序/消息流或者关联连接", "Connect using DataInputAssociation": "使用数据输入关联连接", "Remove": "移除", "Activate the hand tool": "激活抓手工具", "Activate the lasso tool": "激活套索工具", "Activate the create/remove space tool": "激活创建/删除空间工具", "Create expanded SubProcess": "创建扩展子过程", "Create IntermediateThrowEvent/BoundaryEvent": "创建中间抛出事件/边界事件", "Create Pool/Participant": "创建池/参与者", "Parallel Multi Instance": "并行多重事件", "Sequential Multi Instance": "时序多重事件", "DataObjectReference": "数据对象参考", "DataStoreReference": "数据存储参考", "Loop": "循环", "Ad-hoc": "即席", "Create {type}": "创建 {type}", "Create Task": "创建任务", "Create StartEvent": "创建开始事件", "Create EndEvent": "创建结束事件", "Create Group": "创建组", "Task": "任务", "Send Task": "发送任务", "Receive Task": "接收任务", "User Task": "用户任务", "Manual Task": "手工任务", "Business Rule Task": "业务规则任务", "Service Task": "服务任务", "Script Task": "脚本任务", "Call Activity": "调用活动", "Sub Process (collapsed)": "子流程(折叠的)", "Sub Process (expanded)": "子流程(展开的)", "Start Event": "开始事件", "StartEvent": "开始事件", "Intermediate Throw Event": "中间事件", "End Event": "结束事件", "EndEvent": "结束事件", "Create Gateway": "创建网关", "GateWay": "网关", "Create Intermediate/Boundary Event": "创建中间/边界事件", "Message Start Event": "消息开始事件", "Timer Start Event": "定时开始事件", "Conditional Start Event": "条件开始事件", "Signal Start Event": "信号开始事件", "Error Start Event": "错误开始事件", "Escalation Start Event": "升级开始事件", "Compensation Start Event": "补偿开始事件", "Message Start Event (non-interrupting)": "消息开始事件(非中断)", "Timer Start Event (non-interrupting)": "定时开始事件(非中断)", "Conditional Start Event (non-interrupting)": "条件开始事件(非中断)", "Signal Start Event (non-interrupting)": "信号开始事件(非中断)", "Escalation Start Event (non-interrupting)": "升级开始事件(非中断)", "Message Intermediate Catch Event": "消息中间捕获事件", "Message Intermediate Throw Event": "消息中间抛出事件", "Timer Intermediate Catch Event": "定时中间捕获事件", "Escalation Intermediate Throw Event": "升级中间抛出事件", "Conditional Intermediate Catch Event": "条件中间捕获事件", "Link Intermediate Catch Event": "链接中间捕获事件", "Link Intermediate Throw Event": "链接中间抛出事件", "Compensation Intermediate Throw Event": "补偿中间抛出事件", "Signal Intermediate Catch Event": "信号中间捕获事件", "Signal Intermediate Throw Event": "信号中间抛出事件", "Message End Event": "消息结束事件", "Escalation End Event": "定时结束事件", "Error End Event": "错误结束事件", "Cancel End Event": "取消结束事件", "Compensation End Event": "补偿结束事件", "Signal End Event": "信号结束事件", "Terminate End Event": "终止结束事件", "Message Boundary Event": "消息边界事件", "Message Boundary Event (non-interrupting)": "消息边界事件(非中断)", "Timer Boundary Event": "定时边界事件", "Timer Boundary Event (non-interrupting)": "定时边界事件(非中断)", "Escalation Boundary Event": "升级边界事件", "Escalation Boundary Event (non-interrupting)": "升级边界事件(非中断)", "Conditional Boundary Event": "条件边界事件", "Conditional Boundary Event (non-interrupting)": "条件边界事件(非中断)", "Error Boundary Event": "错误边界事件", "Cancel Boundary Event": "取消边界事件", "Signal Boundary Event": "信号边界事件", "Signal Boundary Event (non-interrupting)": "信号边界事件(非中断)", "Compensation Boundary Event": "补偿边界事件", "Exclusive Gateway": "互斥网关", "Parallel Gateway": "并行网关", "Inclusive Gateway": "相容网关", "Complex Gateway": "复杂网关", "Event based Gateway": "事件网关", "Transaction": "转运", "Sub Process": "子流程", "Event Sub Process": "事件子流程", "Collapsed Pool": "折叠池", "Expanded Pool": "展开池", // Errors "no parent for {element} in {parent}": "在{parent}里,{element}没有父类", "no shape type specified": "没有指定的形状类型", "flow elements must be children of pools/participants": "流元素必须是池/参与者的子类", "out of bounds release": "out of bounds release", "more than {count} child lanes": "子道大于{count} ", "element required": "元素不能为空", "diagram not part of bpmn:Definitions": "流程图不符合bpmn规范", "no diagram to display": "没有可展示的流程图", "no process or collaboration to display": "没有可展示的流程/协作", "element {element} referenced by {referenced}#{property} not yet drawn": "由{referenced}#{property}引用的{element}元素仍未绘制", "already rendered {element}": "{element} 已被渲染", "failed to import {element}": "导入{element}失败", //属性面板的参数 "Id": "编号", "Name": "名称", "General": "常规", "Details": "详情", "Message Name": "消息名称", "Message": "消息", "Initiator": "创建者", "Asynchronous Continuations": "持续异步", "Asynchronous Before": "异步前", "Asynchronous After": "异步后", "Job Configuration": "工作配置", "Exclusive": "排除", "Job Priority": "工作优先级", "Retry Time Cycle": "重试时间周期", "Documentation": "文档", "Element Documentation": "元素文档", "History Configuration": "历史配置", "History Time To Live": "历史的生存时间", "Forms": "表单", "Form Key": "表单key", "Form Fields": "表单字段", "Business Key": "业务key", "Form Field": "表单字段", "ID": "编号", "Type": "类型", "Label": "名称", "Default Value": "默认值", "Validation": "校验", "Add Constraint": "添加约束", "Config": "配置", "Properties": "属性", "Add Property": "添加属性", "Value": "值", "Add": "添加", "Values": "值", "Add Value": "添加值", "Listeners": "监听器", "Execution Listener": "执行监听", "Event Type": "事件类型", "Listener Type": "监听器类型", "Java Class": "Java类", "Expression": "表达式", "Must provide a value": "必须提供一个值", "Delegate Expression": "代理表达式", "Script": "脚本", "Script Format": "脚本格式", "Script Type": "脚本类型", "Inline Script": "内联脚本", "External Script": "外部脚本", "Resource": "资源", "Field Injection": "字段注入", "Extensions": "扩展", "Input/Output": "输入/输出", "Input Parameters": "输入参数", "Output Parameters": "输出参数", "Parameters": "参数", "Output Parameter": "输出参数", "Timer Definition Type": "定时器定义类型", "Timer Definition": "定时器定义", "Date": "日期", "Duration": "持续", "Cycle": "循环", "Signal": "信号", "Signal Name": "信号名称", "Escalation": "升级", "Error": "错误", "Link Name": "链接名称", "Condition": "条件名称", "Variable Name": "变量名称", "Variable Event": "变量事件", "Specify more than one variable change event as a comma separated list.": "多个变量事件以逗号隔开", "Wait for Completion": "等待完成", "Activity Ref": "活动参考", "Version Tag": "版本标签", "Executable": "可执行文件", "External Task Configuration": "扩展任务配置", "Task Priority": "任务优先级", "External": "外部", "Connector": "连接器", "Must configure Connector": "必须配置连接器", "Connector Id": "连接器编号", "Implementation": "实现方式", "Field Injections": "字段注入", "Fields": "字段", "Result Variable": "结果变量", "Topic": "主题", "Configure Connector": "配置连接器", "Input Parameter": "输入参数", "Assignee": "代理人", "Candidate Users": "候选用户", "Candidate Groups": "候选组", "Due Date": "到期时间", "Follow Up Date": "跟踪日期", "Priority": "优先级", "The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)": "跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00", "The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)": "跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00", "Variables": "变量", "Candidate Starter Configuration": "候选开始配置", "Task Listener": "任务监听器", "Candidate Starter Groups": "候选开始组", "Candidate Starter Users": "候选开始用户", "Tasklist Configuration": "任务列表配置", "Startable": "启动", "Specify more than one group as a comma separated list.": "指定多个组,用逗号分隔", "Specify more than one user as a comma separated list.": "指定多个用户,用逗号分隔", "This maps to the process definition key.": "这会映射为流程定义的键", "CallActivity Type": "调用活动类型", "Condition Type": "条件类型", "Create UserTask": "创建用户任务", "Create CallActivity": "创建调用活动", "Called Element": "调用元素", "Create DataObjectReference": "创建数据对象引用", "Create DataStoreReference": "创建数据存储引用", "Multi Instance": "多实例", "Loop Cardinality": "实例数量", "Collection": "任务参与人列表", "Element Variable": "元素变量", "Completion Condition": "完成条件", "Open minimap": "打开小地图", "Close minimap": "关闭小地图", };

更新初始化配置

js
代码解读
复制代码
import defaultXml from "./defaultEmpty" const defaultXmlStr=defaultXml() bpmnModeler = new BpmnModeler({ container: containerEl, additionalModules: [ customTranslateModule //翻译模块 translate.ts ] }) bpmnModeler.importXML(defaultXmlStr) // 默认导入的xml文件,否则无法拖拽

生成默认的xmlStr defaultEmpty.ts

js
代码解读
复制代码
const key=`Process_${new Date().getTime()}` const name=`业务流程_${new Date().getTime()}` const type=''xmlStr'' 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" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" 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> `; };

此时鼠标悬浮左侧的流程框,就可以显示对应的中文翻译,可以完成正常的拖拽添加功能。

New Image

获取流程图数据

js
代码解读
复制代码
bpmnModeler.saveXML({ format: true }).then((res: any) => { console.log(res.xml) })

导入xml文件

通过 input file 来进行文件读取,获取文件内容,进行流程图渲染

js
代码解读
复制代码
<template> <button @click="refFile.click()">导入</button> <!-- 用于打开本地文件--> <input id="files" ref="refFile" type="file" style="display: none" accept=".xml, .bpmn" @change="importXml"> </template> <script setup lang="ts"> const refFile:any=ref(null) const importXml=()=>{ const file = refFile.value.files[0] const reader = new FileReader() reader.readAsText(file) reader.onload = function(res:any) { bpmnModeler.importXML(res.currentTarget.result) // bpmnModeler 为实例化的bpmn } } </script>

下载 XML,SVG

这些是从若依代码里面直接粘贴过来的

js
代码解读
复制代码
<template> <@click="downloadProcess(''xml'')" >下载XML</button> <@click="downloadProcess(''svg'')" >下载SVG</button> <@click="downloadProcess(''bpmn'')" >保存BPMN</button> </template> // 根据所需类型进行转码并返回下载地址 const setEncoded=(type: string, filename = ''diagram'', data: string | number | boolean)=> { const encodedData = encodeURIComponent(data) return { filename: `${filename}.${type}`, href: `data:application/${type === ''svg'' ? ''text/xml'' : ''bpmn20-xml''};charset=UTF-8,${encodedData}`, data: data } } // 下载流程图到本地 const downloadProcess=async(type: string)=> { try { // 按需要类型创建文件并下载 if (type === ''xml'' || type === ''bpmn'') { const { err, xml } = await bpmnModeler.saveXML() // 读取异常时抛出异常 if (err) { console.error(`[Process Designer Warn ]: ${err.message || err}`) } const { href, filename } = setEncoded(type.toUpperCase(), ''bpmn'', xml) downloadFunc(href, filename) } else { const { err, svg } = await bpmnModeler.saveSVG() // 读取异常时抛出异常 if (err) { return console.error(err) } const { href, filename } = setEncoded(''SVG'', ''bpmn'', svg) downloadFunc(href, filename) } } catch (e) { console.error(`[Process Designer Warn ]: ${e.message || e}`) } // 文件下载方法 function downloadFunc(href: string, filename: string) { if (href && filename) { const a = document.createElement(''a'') a.download = filename // 指定下载的文件名 a.href = href // URL对象 a.click() // 模拟点击 URL.revokeObjectURL(a.href) // 释放URL 对象 } } }

预览

使用 highlight.js进行预览

"highlight.js": "10.5.0";"@highlightjs/vue-plugin": "^2.1.0"

在main.ts中进行注册

js
代码解读
复制代码
import ''highlight.js/styles/atom-one-dark.css'' import ''highlight.js/lib'' import hljsVuePlugin from ''@highlightjs/vue-plugin'' ... app.use(hljsVuePlugin)

在弹窗中使用,还是使用 saveXML 获取 xml 文件进行数据填充,highlightjs 中 language 可以换成 json 等配置,等后面再深入去学习

js
代码解读
复制代码
<el-dialog v-model="dialogVisible" title="事件流预览"> <highlightjs language=''xml'' :code="showHtml" style="height: 60vh;overflow-y: auto" /> </el-dialog> ... const dialogVisible=ref(false) const saveXml=()=>{ bpmnModeler.saveXML({ format: true }).then((res: any) => { // const json = xmlJs.xml2json(res.xml, { compact: true, spaces: 4 }); // // console.log(json); // showHtml.value=json dialogVisible.value=true showHtml.value=res.xml }) } ... :deep(.hljs) { word-break: break-word; white-space: pre-wrap; } :deep(.hljs *) { font-family: Consolas, Monaco, monospace; }

预览效果,改下样式,字体看着更舒服一点

New Image

最后

这样基本实现了最基的事件流引擎 demo。具体还没有具体的项目需求,只能根据文档去学习。下一篇会对一些 api进行了解学习