之前使用的是本地写死的 xml 字符串来渲染流程图,实际开发中肯定不满足我们的需求,那么我们应该如何解决这个问题呢?

常用的做法是:前端发送 axios 请求,获取到 xml 的字符串,然后使用 bpmn.js 中的 importXML 的方法将 xml 字符串转换为图形在页面上渲染出来。

接下来进入正题啦~

bpmn.js在vue中发送http请求

通过http请求获取并渲染数据

还是在之前创建好的项目 vue-bpmn 的基础上添加代码

先在项目中安装 axios 用于发送 http 请求

npm i axios --save-D

然后在 views 文件夹下新建 axios.vue 的文件,并配置好路由规则

// axios.vue
<template>
<div class="container">
<template v-if="loading">
<div class="loading">
Loading
</div>
</template>
<template v-else>
<div class="canvas" ref="canvas"></div>
<div id="js-properties-panel" class="panel"></div>
</template>
</div>
</template>

css 部分的样式

<style scoped>
.container {
position: relative;
width: 100%;
height: 100%;
}
.canvas {
width: 100%;
height: 100%;
}
.panel {
position: absolute;
top: 0;
right: 0;
width: 300px;
}
</style>

然后在 js 部分引入 axios 并模拟向后端发送请求获取并渲染数据

<script>
import axios from 'axios'
import BpmnModeler from 'bpmn-js/lib/Modeler'
// 引入一个本地的xml字符串,若是没有获取到后台的数据则用它
import { xmlStr } from '../mock/xmlStr'

// 使用右侧属性栏
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'
export default {
data () {
return {
bpmnModeler: null, // bpmn 建模器
container: null,
canvas: null,
loading: true,
xmlUrl: '',
defaultXmlStr: xmlStr
}
},
mounted() {
this.init()
},
methods: {
async init () {
this.loading = true
this.xmlUrl = await this.getXmlUrl()
console.log(this.xmlUrl)
this.loading = false
// 等待 DOM 更新之后再对工作流进行初始化
this.$nextTick(() => {
this.initBpmn()
})
},
// 该方法模拟请求后台获取 bpmn 文件
getXmlUrl () {
return new Promise(resolve => {
setTimeout(() => {
const url = 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmnMock.bpmn'
resolve(url)
}, 1000)
})
},
initBpmn () {
// 获取canvas的dom节点
const canvas = this.$refs.canvas
// 建模
this.bpmnModeler = new BpmnModeler({
container: canvas,
// 添加控制板
propertiesPanel: {
parent: '#js-properties-panel'
},
additionalModules: [
// 右边的属性栏
propertiesProviderModule,
propertiesPanelModule
],
moddleExtensions: {
camunda: camundaModdleDescriptor
}
})
this.createNewDiagram()
},
async createNewDiagram () {
const that = this
let bpmnXmlStr = ''
if (this.xmlUrl === '') { // 若是后台没有数据则使用默认的一个 xml
bpmnXmlStr = this.defaultXmlStr
this.transformCanvas(bpmnXmlStr)
} else {
let res = await axios({
method: 'get',
timeout: 120000,
url: that.xmlUrl,
headers: { 'Content-Type': 'multipart/form-data' }
})
console.log(res)
bpmnXmlStr = res['data']
this.transformCanvas(bpmnXmlStr)
}
},
transformCanvas (bpmnXmlStr) {
// 将字符串转换成图显示出来
try {
const result = this.bpmnModeler.importXML(bpmnXmlStr)
const { warnings } = result
console.log(warnings)
this.success()
// 让图能自适应屏幕
var canvas = this.bpmnModeler.get('canvas')
canvas.zoom('fit-viewport')
} catch (err) {
console.log(err.message, err.warnings)
}
},
success () {
console.log('创建成功')
}
}
}
</script>

效果演示

在这里插入图片描述

请求接口来源 多谢

编辑之后将最新的bpmn发送给后台

仅仅涉及到数据渲染是远远不够的,如果我们修改了数据,该怎么把数据发送给后台并存储呢?

这个问题就涉及到 bpmn.js 中的事件绑定,需要给图形绑定一个事件 来检测到图形的改变,并获取到最新的 xml 信息发送给后台

views 文件夹下新建一个 save.vue 文件,并将 axios.vue 中的内容复制到 save.vue 中,同时记得配置路由哦
success() 方法中新增一个 addBpmnListener() 的方法

// save.vue
<script>
success () {
console.log('创建成功')
this.addBpmnListener()
},
// 添加绑定事件
addBpmnListener () {
// 给图绑定事件,当图有发生改变的时候就会触发这个事件
this.bpmnModeler.on('commandStack.changed', () => {
this.saveDiagram((err, xml) => {
console.log(xml) // 这里获取到的就是最新的 xml 信息
})
})
},
// 下载为 bpmn 格式,done 是个函数,调用的时候传入的
saveDiagram (done) {
// 把传入的 done 再传给 bpmn 原型的 saveXML 函数调用
this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
done(err, xml)
})
}
</script>

编辑图后,就能获得最新的 xml 内容

在这里插入图片描述

编辑完后保存为bpmn文件或svg文件

通过监听 commandStack.changed 事件我们就能获取到修改完成之后最新的 xml 信息了
我们可以了最新的 xml 信息传递给后台,也可以直接下载为 bpmn/svg 文件

views 文件夹下新建 download.vue 并配置好路由规则,把 save.vue 的内容复制到 download.vue
先给添加两个按钮,这两个按钮先隐藏(因为此时 href 中没有连接),待图更新完后显示出来供用户下载

<template v-else>
<a href="javascript:;" ref="saveBpmn" class="hidden">保存为bpmn</a>
<a href="javascript:;" ref="saveSvg" class="hidden">保存为svg</a>
<div class="canvas" ref="canvas"></div>
<div id="js-properties-panel" class="panel"></div>
</template>

然后修改 js 部分的代码

<script>
methods: {
// 添加绑定事件
addBpmnListener () {
const downloadLink = this.$refs.saveBpmn
const downloadSvgLink = this.$refs.saveSvg
// 给图绑定事件,当图有发生改变的时候就会触发这个事件
this.bpmnModeler.on('commandStack.changed', () => {
downloadLink.display = 'block'
downloadSvgLink.display = 'block'
this.saveBpmn((err, xml) => {
this.setEncoded(downloadLink, 'diagram.bpmn', err ? null : xml) // 这里获取到的 xml 就是最新的 xml 信息
})
this.saveSvg((err, svg) => {
this.setEncoded(downloadSvgLink, 'diagram.svg', err ? null : svg)
})
})
},
// 下载为 Svg 格式,done 是个函数,调用的时候传入的
saveSvg (done) {
// 把传入的 done 再传给 bpmn 原型的 saveSVG 函数调用
this.bpmnModeler.saveSVG(done)
},
// 下载为 bpmn 格式,done 是个函数,调用的时候传入的
saveBpmn (done) {
// 把传入的 done 再传给 bpmn 原型的 saveXML 函数调用
this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
done(err, xml)
})
},
// 当图发生改变的时候会调用这个函数,这个 data 就是图的 xml
setEncoded (link, name, data) {
// 把 xml 转换为 URI,下载要用到的
const encodeData = encodeURIComponent(data)
// 下载图的具体操作,改变 a 的属性,className 另 a 标签可点击,href 可以下载,download 是下载的文件的名字
console.log(link, name, data)
let xmlFile = new File([data], 'test.bpmn')
console.log(xmlFile)
if (data) {
link.className = 'active'
// 将数据给到连接
link.href = 'data:application/bpmn20-xml;charset=UTF-8,' + encodeData
// 设置文件名
link.download = name
}
}
}
</script>

效果演示

在这里插入图片描述