文章目录

一、bpmn.js是什么?二、使用步骤1.引入bpmn2.使用bpmn3.引入bpmn-左侧工具栏4.引入bpmn-左侧工具栏5.引入bpmn数据导出6.数据导出为svg格式7.监听modeler并绑定事件7.监听element点击……8.自定义左侧工具栏图标9.自定义左侧工具栏完整效果10.右侧展示自定义节点内容

总结

一、bpmn.js是什么?

bpmn.js是一个基于JavaScript的库,用于在Web应用程序中创建、查看和编辑BPMN 2.0流程图。

二、使用步骤

1.引入bpmn

import BpmnModeler from "bpmn-js/lib/Modeler";

import { xmlStr } from "../mock/xmlStr";

2.使用bpmn

代码如下:

//html

//数据

return {

// bpmn建模器

bpmnModeler: null,

container: null,

canvas: null

};

//methods

init() {

// 获取到属性ref为“canvas”的dom节点

const canvas = this.$refs.canvas;

// 建模

this.bpmnModeler = new BpmnModeler({

container: canvas

});

this.createNewDiagram();

},

createNewDiagram() {

// 将字符串转换成图显示出来

console.log(xmlStr);

this.bpmnModeler.importXML(xmlStr, err => {

if (err) {

// console.error(err)

} else {

// 这里是成功之后的回调, 可以在这里做一系列事情

this.success();

}

});

},

success() {

// console.log('创建成功!')

}

},

mounted() {

this.init();

},

};

页面效果如图:

3.引入bpmn-左侧工具栏

这个很方便 直接在main.js中引入即可

// main.js中引入以下为bpmn工作流绘图工具的样式

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'

效果如图:

4.引入bpmn-左侧工具栏

1. 安装bpmn-js-properties-panel插件

2. import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // main.css中引入右边工具栏样式

3. 在页面中引入propertiesProviderModule和propertiesPanelModule

...

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'

...

html结构

//在上边init基础上进行添加配置

init() {

// 获取到属性ref为“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()

加载成功

5.引入bpmn数据导出

之前的createNewDiagram事件就是用将数据显示出来,他的第一个参数就是xml数据,动态渲染在拿到后端返回的数据之后重新调用这个方法即可

// 将字符串转换成图显示出来

this.bpmnModeler.importXML(this.xmlStr, err => {

if (err) {

// console.error(err)

} else {

// 这里是成功之后的回调, 可以在这里做一系列事情

this.success();//在success回调中绑定事件进行监听添加绑定事件

}

});

success(){

const that = this;

// 给图绑定事件,当图有发生改变就会触发这个事件

this.bpmnModeler.on("commandStack.changed", function() {

that.saveDiagram(function(err, xml) {

console.log(xml); // 这里获取到的就是最新的xml信息

});

});

}

// 下载为bpmn格式,done是个函数,调用的时候传入的

saveDiagram(done) {

// 把传入的done再传给bpmn原型的saveXML函数调用

this.bpmnModeler.saveXML({ format: true }, function(err, xml) {

done(err, xml);

});

}

6.数据导出为svg格式

有时候需要数据导出为svg格式的

首先在页面上定义好a标签用来下载数据

html

xml

svg

js

上边讲过从后端拿数据渲染之后有个success()回调

我们在这个回调里进行监听每次改变就会拿到xml数据,svg和它一样的,只需稍微改造一下

const that = this;

const downloadLink = this.$refs.xml;//首先获取页面上的a标签

const downloadSvgLink = this.$refs.svg;

// 给图绑定事件,当图有发生改变就会触发这个事件

this.bpmnModeler.on("commandStack.changed", function() {

//每次更改页面都会获取到xml和svg类型的数据保存到href中备用

that.saveDiagram(function(err, xml) {

console.log(xml); // 这里获取到的就是最新的xml信息 saveDiagram返回的数据

const data = encodeURIComponent(xml);

downloadLink.href =

"data:application/bpmn20-xml;charset=UTF-8," + data;

downloadLink.download = "1.bpmn";

});

that.saveSvg(function(err, svg) {

const data = encodeURIComponent(svg);

console.log(svg); // 这里获取到的就是最新的xml信息 saveDiagram返回的数据

downloadSvgLink.href ="data:application/bpmn20-xml;charset=UTF-8," + data;

downloadSvgLink.download = "1.svg";

});

});

// 下载为bpmn格式,done是个函数,调用的时候传入的

saveDiagram(done) {

// 把传入的done再传给bpmn原型的saveXML函数调用

this.bpmnModeler.saveXML({ format: true }, function(err, xml) {

done(err, xml);

});

},

saveSvg(done) {

this.bpmnModeler.saveSVG(done);

}

7.监听modeler并绑定事件

sussec中调用下边这个方法 用来监听

this.addModelerListener()

// 监听 modeler

addModelerListener() {

const bpmnjs = this.bpmnModeler;

const that = this;

// 用一个forEach给modeler上添加要绑定的事件

const events = [

"shape.added",

"shape.move.end",

"shape.removed",

"connect.end",

"connect.move"

];

events.forEach(function(event) {

that.bpmnModeler.on(event, e => {

console.log(event, e);

var elementRegistry = bpmnjs.get("elementRegistry");

var shape = e.element ? elementRegistry.get(e.element.id) : e.shape;

console.log(shape);

});

});

},

7.监听element点击……

success() {

console.log("创建成功!");

this.addBpmnListener(); // 页面改变触发

this.addModelerListener(); // 监听 modeler

this.addEventBusListener(); //监听元素

},

addEventBusListener() {

let that = this;

const eventBus = this.bpmnModeler.get("eventBus"); // 需要使用eventBus

const eventTypes = ["element.click", "element.changed"]; // 需要监听的事件集合

eventTypes.forEach(function(eventType) {

eventBus.on(eventType, function(e) {

console.log(e);

});

});

},

8.自定义左侧工具栏图标

再以上的基础上去components文件夹创建文件

custom/CustomPalette.js 核心

index.js

// CustomPalette.js

export default class CustomPalette {

constructor(bpmnFactory, create, elementFactory, palette, translate) {

this.bpmnFactory = bpmnFactory;

this.create = create;

this.elementFactory = elementFactory;

this.translate = translate;

palette.registerProvider(this);

}

// 这个函数就是绘制palette的核心

getPaletteEntries(element) {

const {

bpmnFactory,

create,

elementFactory,

translate

} = this;

function createTask() {

return function (event) {

const businessObject = bpmnFactory.create('bpmn:Task');

businessObject['custom'] = 1

const shape = elementFactory.createShape({

type: 'bpmn:Task',

businessObject

});

console.log(shape) // 只在拖动或者点击时触发

create.start(event, shape);

}

}

return {

'create.lindaidai-task': {

group: 'model', // 分组名

className: 'icon-custom lindaidai-task', // 样式类名

title: translate('创建一个类型为lindaidai-task的任务节点'),

action: { // 操作

dragstart: createTask(), // 开始拖拽时调用的事件

click: createTask() // 点击时调用的事件

}

}

}

}

}

CustomPalette.$inject = [

'bpmnFactory',

'create',

'elementFactory',

'palette',

'translate'

]

-------------------------------------

// custom/index.js

import CustomPalette from './CustomPalette'

export default {

__init__: ['customPalette'],

customPalette: ['type', CustomPalette]

}

自定义定义完成在页面中引入 配置样式

创建css文件在main.js中全局引入 css名对应上即可

xx.css

/* app.css */

.bpmn-icon-task.red {

color: #cc0000 !important;

}

.icon-custom {

/* 定义一个公共的类名 */

border-radius: 50%;

background-size: 65%;

background-repeat: no-repeat;

background-position: center;

}

.icon-custom.lindaidai-task {

/* 加上背景图 */

background-image: url('https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/rules.png');

}

--------------------------

main.js

import '@/assets/a.css'

--------------------------

//xxx.vue 需要使用的页面

import customModule from "../components/custom";

propertiesPanel对象中 //添加控制板

......

propertiesPanel: {

parent: "#js-properties-panel"

},

additionalModules: [

// 左边工具栏以及节点

propertiesProviderModule,

// 自定义的节点!!!!在这里

customModule,

// 右边的工具栏

propertiesPanelModule

],

看看效果

9.自定义左侧工具栏完整效果

完整版为了方便阅读 避免混乱创建的文件和第八条完全独立 此处建议删除第八条数据重新开始

此处custom为第八步创建的文件 注意区分 新建customModeler文件如下图4个js文件

//1.CustomPalette.js

export default class CustomPalette {

constructor(bpmnFactory, create, elementFactory, palette, translate) {

this.bpmnFactory = bpmnFactory;

this.create = create;

this.elementFactory = elementFactory;

this.translate = translate;

palette.registerProvider(this);

}

getPaletteEntries(element) {

const {

bpmnFactory,

create,

elementFactory,

translate

} = this;

function createTask() {

return function (event) {

const businessObject = bpmnFactory.create('bpmn:Task');//这里固定

businessObject['custom'] = 1

const shape = elementFactory.createShape({

type: 'bpmn:Task',//这里是自定义的节点名称昂!!!!

businessObject

});

console.log(shape) // 只在拖动或者点击时触发

create.start(event, shape);

}

}

return {

'create.lindaidai-task': {

group: 'model',

className: 'icon-custom lindaidai-task',

// className: 'bpmn-icon-user-task',

title: translate('创建一个类型为lindaidai-task的任务节点'),

action: {

dragstart: createTask(),

click: createTask()

}

}

}

}

}

CustomPalette.$inject = [

'bpmnFactory',

'create',

'elementFactory',

'palette',

'translate'

]

//2.CustomRenderer.js

import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';

import {

append as svgAppend,

attr as svgAttr,

create as svgCreate

} from 'tiny-svg';

import { customElements, customConfig, hasLabelElements } from './util'

import { is } from 'bpmn-js/lib/util/ModelUtil';

const HIGH_PRIORITY = 1500

export default class CustomRenderer extends BaseRenderer {

constructor(eventBus, bpmnRenderer, modeling) {

super(eventBus, HIGH_PRIORITY);

this.bpmnRenderer = bpmnRenderer;

this.modeling = modeling;

}

canRender(element) {

// ignore labels

return !element.labelTarget;

}

drawShape(parentNode, element) {

console.log(element)

const type = element.type // 获取到类型

if (customElements.includes(type)) { // or customConfig[type]

const { url, attr } = customConfig[type]

const customIcon = svgCreate('image', {

...attr,

href: url

})

element['width'] = attr.width // 这里我是取了巧, 直接修改了元素的宽高

element['height'] = attr.height

svgAppend(parentNode, customIcon)

// 判断是否有name属性来决定是否要渲染出label

if (!hasLabelElements.includes(type) && element.businessObject.name) {

const text = svgCreate('text', {

x: attr.x,

y: attr.y + attr.height + 20,

"font-size": "14",

"fill": "#000"

})

text.innerHTML = element.businessObject.name

svgAppend(parentNode, text)

console.log(text)

}

// this.modeling.resizeShape(element, {

// x: element.x,

// y: element.y,

// width: element['width'] / 2,

// height: element['height'] / 2

// })

return customIcon

}

// else if (type === 'bpmn:TextAnnotation' && element.businessObject.color) {

// console.log('我是绿色的')

// let color = element.businessObject.color

// element.businessObject.di.set('bioc:stroke', color)

// const shape = this.bpmnRenderer.drawShape(parentNode, element)

// return shape

// }

const shape = this.bpmnRenderer.drawShape(parentNode, element)

return shape

}

getShapePath(shape) {

return this.bpmnRenderer.getShapePath(shape);

}

}

CustomRenderer.$inject = ['eventBus', 'bpmnRenderer', 'modeling'];

//3. index.js

import CustomPalette from './CustomPalette'

import CustomRenderer from './CustomRenderer'

export default {

__init__: ['customPalette', 'customRenderer'],

customPalette: ['type', CustomPalette],

customRenderer: ['type', CustomRenderer]

}

//4.util.js

const customElements = ['bpmn:Task', 'bpmn:StartEvent'] // 自定义元素的类型

const customConfig = { // 自定义元素的配置

'bpmn:Task': {

'url': require('../../assets/www.png'),

// 'url': require('../../assets/rules.png'),

// 'url': 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/rules.png',

'attr': { x: 0, y: 0, width: 48, height: 48 }

},

'bpmn:StartEvent': {

'url': 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/start.png',

'attr': { x: 0, y: 0, width: 40, height: 40 }

}

}

const hasLabelElements = ['bpmn:StartEvent', 'bpmn:EndEvent'] // 一开始就有label标签的元素类型

export { customElements, customConfig, hasLabelElements }

接下来康康效果 ok结束啦 图标不一样是因为util.js和全局的样式不一样,替换下即可 本文参考霖呆呆LinDaiDai_的文章 链接地址:https://juejin.cn/post/6844904017567416328

10.右侧展示自定义节点内容

创建组件 注入信息

根据自己需求 更改自定义样式 本demo源码

总结

还有版本问题 以下是本demo的版本 版本太高会报错哦 大家有什么问题也可以留言 希望可以帮到你

"bpmn-js": "^6.0.4",

"bpmn-js-properties-panel": "^0.33.0",

"camunda-bpmn-moddle": "^4.3.0",

未完待续~✿✿ヽ(°▽°)ノ✿

相关链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: