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
系列相關推薦: