构建&部署
Bpmn 流程设计器使用说明
本项目是一个基于 bpmn.js
,Vue 2.x
和 ElementUI
开发的流程设计器。
二次开发
1. 安装依赖 Installation
yarn install
或
npm install
2. 运行 Quick Start
yarn run start
或
npm run start
3. 打包
npm run build
提示
注意: 本项目默认部署到二级目录/bpmn/designer下,如需要使用Nginx部署,请放到/bpmn/designer下面
配置如下:
location ^~ /bpmn/designer {
try_files $uri $uri/ /bpmn/designer/index.html @rewrites;
index index.html index.htm;
alias /data/www/bpmn/designer/dist;
}
4. 集成业务系统
1、设计器存放
将打包后的文件夹dist放入项目的public目录下 如 /public/bpmn/designer/
2、引入设计器Iframe
<template>
<div class="model-designer">
<div class="designer-tool-bar">
<Button type="primary" @click="doSave">保存</Button>
</div>
<div class="designer-main">
<!-- Iframe引入设计器-->
<iframe ref="designerIframeRef" src="/bpmn/designer/index.html"
class="designer-iframe"></iframe>
</div>
</div>
</template>
提示
iframe引用的路径为编辑器的主页。因为编辑器发布的二级目录是/bpmn/designer,所以需要放入public中的/bpmn/designer目录下
2、初始化设计器对象
mounted() {
this.$nextTick(() => {
// 获取Iframe的Window对象
this.iframe = this.$refs.designerIframeRef.contentWindow;
this.iframe.onload=()=>{
// 初始化XML内容
setTimeout(()=>{
this.initData();
}, 1000);
}
});
}
初始化xml数据
// 初始化XML内容 -
initData() {
// TODO 从后端异步加载xml
const tempXml = `<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://flowable.org/bpmn">
<process id="xxx" name="xxxx" isExecutable="true">
<startEvent id="aba1958401251475ca53b3f5d152fde3b" />
...
</definitions>`;
if (this.iframe) {
this.iframe.setBpmnXml(tempXml).then(res => {
debugger;
console.log('======================XML加载成功!');
console.log(res);
});
}
}
提示
初始化数据时,要等iframe加载完成后才能获取, iframe中暴露了setBpmnXml(xml)
方法用来初始化模型
使用iframe中暴露了getBpmnXml()
方法来获取设计器的xml
// 获取设计器的XML内容 - 保存
doSave() {
if (this.iframe) {
// 获取设计器的XML内容
this.iframe.getBpmnXml().then(res => {
console.log('======================XML获取成功!');
console.log(res);
});
}
}
初始化加载结果:
获取mxl结果 得到xml后
1、Vue - Iframe集成
<iframe src="/bpmn-designer/dist/index.html?modelKey=" ref="frameRef"></iframe>
1、流程相关的数据操作在iframe中完成
2、流程相关的数据操作在业务系统中完成,需要使用调用iframe内部的方法进行数据获取和绑定 获取流程xml内容
const iframeWindow = iframe.contentWindow;
iframe.getModelXml();
初始化xml
const iframeWindow = iframe.contentWindow;
iframe.initModelXml(xml);
2、React - Iframe集成
<iframe class="w-full" :src="frameSrc" class="designer_main" ref="frameRef"></iframe>
流程图渲染
流程审批/预览时都需要查看流程图和审批记录,流程图渲染可以很清晰地看清流程当前处于哪个节点,各节点的审批人,当前待办人。
如图:
下面是流程图渲染的集成方法,以VUE为例
package.json中添加引用:
"bpmn-js": "^8.8.2",
关键代码:
<div class="main">
<Spin tip="Loading..." :spinning="modelInfoLoading">
<div class="diagram">
<BpmnDiagram v-if="modelInfo&& modelInfo.modelXml" :model-info="modelInfo"/>
<div v-else class="diagram-empty">
请选择流程模板或输入流程实例进行预览!
</div>
</div>
<!--
<div>
<ApproveHistory />
</div>
-->
</Spin>
</div>
BpmnDiagram组件: 模板相关代码:
<template>
<div class="viewer-container">
<div class="svg-controller">
<div class="scale-rate">
{{ Math.floor(defaultZoom * 10 * 10) + "%" }}
</div>
<Space>
<Button title="缩小" shape="circle" size="small" @click="processZoomOut()" type="primary">
<MinusOutlined/>
</Button>
<Button :title="isFitView?'按窗口大小显示':'实际大小'" shape="circle" size="small"
@click="processFitDialog(isFitView)" type="primary">
<CompressOutlined v-if="isFitView"/>
<ExpandOutlined v-else/>
</Button>
<Button title="放大" shape="circle" size="small" @click="processZoomIn()" type="primary">
<PlusOutlined/>
</Button>
</Space>
</div>
<div class="containers" ref="container" @mousewheel="changeScale">
<div :id="bpmnCanvasId"
class="bpmnCanvas canvas"
ref="bpmnCanvas"
></div>
</div>
</div>
</template>
js关键代码:
// 引入bpmnViewer组件
import BpmnViewer from 'bpmn-js/lib/Viewer'
import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas'
data() {
return {
bpmnCanvasId: 'bpmnCanvas' + new Date().getTime(),
defaultZoom: 1,
fitViewScaleRate: 1,
isFitView: false,
}
}
mounted() {
this.initData();
this.importXml(this.modelInfo);
}
methods: {
initData() {
bpmnViewer && bpmnViewer.destroy();
bpmnViewer = new BpmnViewer({
container: document.getElementById(this.bpmnCanvasId),
width: '100%',
additionalModules: [
MoveCanvasModule // 移动整个画布
],
/*keyboard: {
bindTo: document
}*/
});
},
importXml(modelInfo) {
const that = this;
// 处理排他网关, 注:流程图预览时,排他网关需要在对应的<bpmndi:BPMNShape>节点上添加属性isMarkerVisible="true"
const gatewayIds = this.getHtmlAttr(modelInfo.modelXml, 'exclusiveGateway', 'id');
let modelXmlTemp = modelInfo.modelXml;
if(gatewayIds && gatewayIds.length > 0){
gatewayIds.forEach(item=>{
const result = new RegExp('id="(.+?)"').exec(item)
if(result && result[1]){
modelXmlTemp = modelXmlTemp.replace('bpmnElement="'+result[1]+'"', 'bpmnElement="'+result[1]+'" isMarkerVisible="true"');
}
})
}
bpmnViewer.importXML(modelXmlTemp, function (err) {
if(err){
console.error(err);
}else{
that.importXmlSuccess();
that.genBpmnSvgMarker();
}
});
}
}
高亮部分代码实现:
.hishighlight:not(.djs-connection) .djs-visual > :nth-child(1) {
fill: rgba(211, 238, 211, 0.99) !important; /* color elements as green */
}
.hishighlight g.djs-visual >:nth-child(1) {
stroke: rgba(0, 190, 0, 1) !important;
}
.highlight-line g.djs-visual >:nth-child(1) {
stroke: rgba(0, 190, 0, 1) !important;
}
.highlight-line{
path{
marker-end: url('#greenMarker') !important;
}
}
.highlight:not(.djs-connection) .djs-visual > :nth-child(1) {
fill: rgba(251, 233, 209, 1) !important; /* color elements as green */
}
.highlight g.djs-visual >:nth-child(1) {
stroke: rgba(214, 126, 125, 1) !important;
}
@keyframes dynamicNode {
to {
stroke-dashoffset: 100%;
}
}
@-webkit-keyframes dynamicNode {
to {
stroke-dashoffset: 100%;
}
}
.highlight {
.djs-visual {
animation: dynamicNode 18S linear infinite;
-webkit-animation: dynamicNode 18S linear infinite;
-webkit-animation-fill-mode: forwards;
}
}