Q: bpmn.js是什么? 🤔️javascript
bpmn.js是一个BPMN2.0渲染工具包和web建模器, 使得画流程图的功能在前端来完成.css
Q: 我为何要写该系列的教材? 🤔️html
由于公司业务的须要于是要在项目中使用到bpmn.js
,可是因为bpmn.js
的开发者是国外友人, 所以国内对这方面的教材不多, 也没有详细的文档. 因此不少使用方式不少坑都得本身去找.在将其琢磨完以后, 决定写一系列关于它的教材来帮助更多bpmn.js
的使用者或者是期于找到一种好的绘制流程图的开发者. 同时也是本身对其的一种巩固.前端
因为是系列的文章, 因此更新的可能会比较频繁, 您要是无心间刷到了且不是您所须要的还请谅解😊.vue
不求赞👍不求心❤️. 只但愿能对你有一点小小的帮助.java
通过前面几章的基础教程相信你们对bpmn.js
的基本使用已经有了一个很好的掌握.ios
从这一章节开始我会讲解一些关于bpmn.js
中自定义的部分, 包括自定义左侧工具栏、自定义渲染、自定义contextPad
等等.git
仍是先来看一张图了解一下咱们的绘图页面都有哪些东西:github
这一章我要介绍的时候如何自定义左侧的工具栏(Palette
, 也叫调色板), 经过阅读你能够学习到:web
对于上面👆的目录, 其实隐含意思就是自定义Palette
包括两种方式:
bpmn.js
默认提供的Palette
上进行修改(或者新增新的项)Palette
中有的全部项, 自定义一个全新的Palette
先来看看第一种最简单的, 咱们在官方提供的调色板里新增一个自定义的项.
bpmn:Task
lindaidai-task
bpmn:Task
原有的样式, 只不过将边框变为红色lindaidai-task
的任务节点效果是这样的:
如上所示, 只改变了任务框的颜色为红色, 因此效果不是很明显, 你甚至能够直接给它换一个样貌:
接下来让咱们看看该怎么实现它吧!🐶
由于是新的章节, 这里我也新建一个项目:
$ vue create bpmn-vue-custom
$ npm i vue-router axios bpmn-js-properties-panel bpmn-js --save-D
复制代码
按照以前的案例LinDaiDai/bpmn-vue-basic配置好相应的路由之类的东西.
在components
文件夹下新建一个名为custom-palette.vue
的文件, 并将provider.vue(以前的一个基础案例) 的内容复制进去.
继续在components
文件夹下新建文件夹custom
用于盛放咱们后面要写的一些自定义的东西.
来看看咱们如今的项目结构:
我已经在custom
文件夹新创建了一个CustomPalette.js
, 接下来就是要在这里面写上咱们要自定义的项.
CustomPalette.js
代码首先这个js
是导出一个类(类的名称你能够随意取, 可是在引用的时候不能随意取, 后面会说到):
这里我就取为CustomPalette
:
// 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) {}
}
CustomPalette.$inject = [
'bpmnFactory',
'create',
'elementFactory',
'palette',
'translate'
]
复制代码
上面👆的代码很好理解:
$inject
注入一些须要的变量palette.registerProvider(this)
指定这是一个palette
定义完CustomPalette.js
以后, 咱们须要在其同级的index.js
中将它导出:
// custom/index.js
import CustomPalette from './CustomPalette'
export default {
__init__: ['customPalette'],
customPalette: ['type', CustomPalette]
}
复制代码
注:️ 这里__init__
中的名字就必须是customPalette
了, 还有下面的属性名也必须是customPalette
, 否则就会报错了.
同时要在页面中使用它:
<!--custom-palette.vue-->
<script> ... import customModule from './custom' ... this.bpmnModeler = new BpmnModeler({ ... additionalModules: [ // 左边工具栏以及节点 propertiesProviderModule, // 自定义的节点 customModule ] }) </script>
复制代码
getPaletteEntries
代码抛开这些不看, 重点就是如何构造这个getPaletteEntries
函数
函数的名称你不能变, 否则会报错, 首先它返回的是一个对象, 对象中指定的就是你要自定义的项, 它大概长成这样:
// CustomPalette.js
getPaletteEntries(element) {
return {
'create.lindaidai-task': {
group: 'model', // 分组名
className: 'bpmn-icon-task red', // 样式类名
title: translate('建立一个类型为lindaidai-task的任务节点'),
action: { // 操做
dragstart: createTask(), // 开始拖拽时调用的事件
click: createTask() // 点击时调用的事件
}
}
}
}
复制代码
能够看到我定义的一项的名称就是: create.lindaidai-task
. 它会有几个固定的属性:
tools、event、gateway、activity
等等,用于分类接下来咱们要作的无非就是:
className
来设置样式action
来定义要触发的事情className
代码我在scr
的目录下新建了一个css
文件, 里面用来盛放一些全局的样式, 并在main.js
中引用这个全局样式:
// main.js
// 引入全局的css
import './css/app.css'
复制代码
而后在其中加上一下样式:
/* app.css */
.bpmn-icon-task.red {
color: #cc0000 !important;
}
复制代码
上面👆的className
我之因此要用bpmn-icon-task
, 是由于这个类是bpmn.js
中自带的一个iconfont
类, 使用它就能够实现一个task
的图标的效果:
因为iconfont
是一个字体, 因此这里我使用color
来改变它的颜色.
若是你想要给它彻底换一张图片的话也能够用className
来实现:
/* app.css */
.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');
}
复制代码
而后修改create.lindaidai-task
中的className
:
// CustomPalette.js
'create.lindaidai-task': {
className: 'icon-custom lindaidai-task'
}
复制代码
这样页面上显示的就是你定义的那张背景图了:
action
代码完成了上面的操做, 其实页面已经能正常渲染出一个咱们自定义的元素了, 可是你在点击或者拖拽它的时候是没有效果的💦.
此时咱们指望的是点击或者拖拽它能在画布中画出一个lindaidai-task
, 所以你得给它加上事件, 也就是编写一个函数用来建立bpmn:Task
这个元素:
// CustomPalette.js
function createTask() {
return function(event) {
const businessObject = bpmnFactory.create('bpmn:Task');
const shape = elementFactory.createShape({
type: 'bpmn:Task',
businessObject
});
console.log(shape) // 只在拖动或者点击时触发
create.start(event, shape);
}
}
复制代码
这里的核心其实就是利用bpmn.js
提供的一些方法建立shape
而后将其添加到画布上.
(我这里演示的是建立一个类型为bpmn:Task
的元素, 你还能够用来建立bpmn:StartEvent、bpmn:ServiceTask、bpmn:ExclusiveGateway
等等...)
此时你拖动或者点击lindaidai-task
就能够在页面上建立一个Task
元素了.
咱们看到虽然lindaidai-task
在左侧工具栏中是金黄金黄的, 可是实际画到页面却仍是呈现“裸体”状态😅, 这就和自定义渲染有关系了, 不要着急, 这些在后面的章节中会讲到.
CustomPalette.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);
}
getPaletteEntries(element) {
const {
bpmnFactory,
create,
elementFactory,
translate
} = this;
function createTask() {
return function(event) {
const businessObject = bpmnFactory.create('bpmn:Task'); // 其实这个也能够不要
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'
]
复制代码
项目案例Git地址: LinDaiDai/bpmn-vue-custom
能够看到, 上面👆的那种实现方式实际上就是定义了一个CustomPalette
而后在new BpmnModeler
生成的对象中引用进去.
可是这样作有一点很差👎, 那就是若是你不想要它提供的默认的这些项, 好比开始节点、结束节点、任务节点, 而是全都是本身自定义的, 就不能知足了. 好比这样:
此时你就须要重写BpmnModeler
这个类了, 实现本身独有的一套modeler
.
继续在上面👆的项目的基础上建立一个customModeler
文件夹和一个custom-modeler.vue
文件. 而后在customModeler
中建立一个index.js
和一个custom
文件夹.
customModeler
文件夹下的文件就是用来放自定义的modeler
custom-modeler.vue
做为页面展现(记得配置页面的路由)此时项目结构变成了:
CustomPalette.js
代码这里的CustomPalette.js
的编写方式就和第一种的有所不一样了:
/** * A palette that allows you to create BPMN _and_ custom elements. */
export default function PaletteProvider(palette, create, elementFactory, globalConnect) {
this.create = create
this.elementFactory = elementFactory
this.globalConnect = globalConnect
palette.registerProvider(this)
}
PaletteProvider.$inject = [
'palette',
'create',
'elementFactory',
'globalConnect'
]
PaletteProvider.prototype.getPaletteEntries = function(element) { // 此方法和上面案例的同样
const {
create,
elementFactory
} = this;
function createTask() {
return function(event) {
const shape = elementFactory.createShape({
type: 'bpmn:Task'
});
console.log(shape) // 只在拖动或者点击时触发
create.start(event, shape);
}
}
return {
'create.lindaidai-task': {
group: 'model',
className: 'icon-custom lindaidai-task',
title: '建立一个类型为lindaidai-task的任务节点',
action: {
dragstart: createTask(),
click: createTask()
}
}
}
}
复制代码
在这里是直接重写了PaletteProvider
这个类, 同时覆盖了其原型上的getPaletteEntries
方法, 从而达到覆盖原有的工具栏的效果.
(别看上面👆写的东西好像不少的样子, 可是其实静下心来看发现也没啥😊)
custom/index.js
代码接下来仍是和第一种方式同样, 须要将咱们自定义的Palette
导出:
// custom/index.js
import CustomPalette from './CustomPalette'
export default {
__init__: ['paletteProvider'],
paletteProvider: ['type', CustomPalette]
}
复制代码
这不过这里咱们就不是用customPalette
了, 而是直接用paletteProvider
.
customModeler/index.js
代码最重要的一步, 就是编写CustomModeler
这个类了:
import Modeler from 'bpmn-js/lib/Modeler'
import inherits from 'inherits'
import CustomModule from './custom'
export default function CustomModeler(options) {
Modeler.call(this, options)
this._customElements = []
}
inherits(CustomModeler, Modeler)
CustomModeler.prototype._modules = [].concat(
CustomModeler.prototype._modules, [
CustomModule
]
)
复制代码
导出的类继承了Modeler
这个核心的类, 这样就保证了其余功能的实现.
最后一步, 是须要将咱们本来经过BpmnModeler
建立的对象改成经过咱们自定义的CustomModeler
来建立, 编写custom-modeler.vue
.
<!--custom-modeler.vue-->
<script> ... import CustomModeler from './customModeler' ... this.bpmnModeler = new CustomModeler({ // 本来是用BpmnModeler ... additionalModules: [] // 能够不用引用任何东西 }) </script>
复制代码
快来打开页面看看效果:
上面👆两个案例用的都是同一个项目🦐
项目案例Git地址: LinDaiDai/bpmn-vue-custom
系列相关推荐: