自定義 Palette
[了解 BPMN 内部】後,對一些子產品以及它們之間的配合應該有了一定的概念,下面開始動手嘗試修改一下
palette
工具欄
案例代碼在這裡取:
customPalette
開始
你可以實作
- 通過配置生成工具欄
- 自定義工具欄樣式、布局
- 可指定工具欄容器
為了了解更簡單
- 自定義工具欄樣式、布局 (非必須,後續實作)
- 可指定工具欄容器(非必須,後續實作)
注意:标記 🎯 的地方為重點
1. 建立相關檔案
建立自定義工具欄的相關檔案,結構如下
| -- palette
|-- CustomPaletteProvider.js
|-- CustomPalette.js
|-- index.js
PaletteProvider 顧名思義
”調色闆提供程式“
,也就是将工具欄的資料告訴
Palette
,由
Palette
構造工具欄。
是以我們需要先将代碼準備好,然後去修改它:
- 前往 bpmn-js 将源碼 PaletteProvider.js 拷貝至 CustomPaletteProvider.js
- 前往 diagram-js 将源碼 Palette.js 拷貝至 CustomPalette.js
- 參考
和PaletteProvider.js
對應的Palette.js
,将剛剛建立的index.js
,修改成如下index.js
import customPalette from './CustomPalette'
import PaletteProvider from './CustomPaletteProvider'
// 除了引進的子產品的名字可以修改,其他的不建議修改,會報錯
export default {
__depends__: [
{
__init__: ['customPalette'],
customPalette: ['type', customPalette]
}
], // 依賴于 customPalette 這個子產品
__init__: ['customPaletteProvider'], // 調用 customPaletteProvider 來初始化
customPaletteProvider: ['type', PaletteProvider]
}
到此三個檔案已經準備就緒了,下面我們來引用它。
2. 引用
引入剛剛建立的檔案
export default {
// ...
init() {
this.bpmnModeler = new BpmnModeler({
additionalModules: [customPalette]
})
// ...
}
}
到此,目前頁面應該是正常顯示的,因為我們隻是将檔案拷貝出來,修改引用,友善後續的修改。
3. 修改工具欄構造者 CustomPalette
修改
CustomPalette.js
檔案
首先老規則,修改注入需要用的資料
Palette.$inject = [
'eventBus',
'canvas',
// ---------- 自定義區域 ------------
'elementFactory',
'create',
'config.paletteContainer',
'config.paletteEntries'
// ---------- 自定義區域 ------------
]
預設隻注入了兩個,其餘根據需要來增加。
下面将注入的資料指派
function Palette(
eventBus,
canvas,
elementFactory,
create,
paletteContainer,
paletteEntries
) {
this._eventBus = eventBus
this._canvas = canvas
// 新增指派
this._entries = paletteEntries // 傳入的工具欄資料
this._paletteContainer = paletteContainer // 傳入的工具欄容器
this._elementFactory = elementFactory
this._create = create
// ...
}
然後就可以在這個函數中資料注入的資料了,
注意注入順序和函數參數的順序要一緻哦~
到此,一個資料以及布局已經準備完畢,接下來該實作它的
拖動或者點選生成元素
的功能了
Palette.prototype.trigger = function(action, event, autoActivate) {
var entries = this._entries,
entry,
handler,
originalEvent,
button = event.delegateTarget || event.target
// ---------- 自定義區域 ------------
// 建立元素的方法需要這兩個構造器
var elementFactory = this._elementFactory,
create = this._create
// ---------- 自定義區域 ------------
handler = entry.action
originalEvent = event.originalEvent || event
// simple action (via callback function)
if (isFunction(handler)) {
if (action === 'click') {
handler(originalEvent, autoActivate, elementFactory, create)
}
} else {
// ---------- 自定義區域 ------------
if (handler[action]) {
// 在原來 2 個參數的基礎上,新增 2 個參數 elementFactory, create
handler[action](originalEvent, autoActivate, elementFactory, create) // 🎯 這裡便是回調 action.dragstart 或者click 或者 其他事件
}
// ---------- 自定義區域 ------------
}
event.preventDefault()
}
在
init
的時候綁定了兩個事件,當我們點選或在拖動工具欄的時候觸發,進而可以生成元素。
CustomPalette.js
至此基本修改完成了。
4. 修改資料的提供者 PaletteProvider
現在開始修改
PaletteProvider.js
檔案
修改前,可以先看一下
PaletteProvider.prototype.getPaletteEntries
這個方法
源碼是在這個方法中将預設工具欄資料傳入給
palette.js
可以學習一下他是如何構造工具欄資料的,後面會用到。
如下代碼實作了一條分割線和三個事件
// l
'global-connect-tool': {
group: 'tools',
className: 'bpmn-icon-connection-multi',
title: translate('Activate the global connect tool'),
action: {
click: function (event) {
globalConnect.toggle(event)
}
}
},
// 2
'tool-separator': {
group: 'tools',
separator: true
},
// 3
'create.start-event': createAction(
'bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none',
translate('Create StartEvent')
),
// 4
'create.intermediate-event': createAction(
'bpmn:IntermediateThrowEvent', 'event', 'bpmn-icon-intermediate-event-none',
translate('Create Intermediate/Boundary Event')
)

因為我們的目的是希望有一個獨立的檔案來配置工具欄,而不是在源碼中,是以我們開始簡化這個 getPaletteEntries 如下:
// 注意: 可以通過 config這個對象拿到 執行個體化 Modeler 的時候的參數
// 是以這裡通過注入 congif.paletteEntries 拿到 paletteEntries 的值
// 後續會介紹如何傳入
PaletteProvider.$inject = ['config.paletteEntries', 'customPalette']
export default function PaletteProvider(paletteEntries, customPalette) {
this._entries = paletteEntries
customPalette.registerProvider(this)
}
PaletteProvider.prototype.getPaletteEntries = function(element) {
return this._entries // 🎯 傳回工具欄資料
}
5. 配置工具欄 paletteEntries
同級目錄下建立
config/paletteEntries.js
,
paletteEntries.js
的目的是傳回一個包含工具資料的集合(對象或數組)
這裡簡單建立兩個工具元素,
開始和結束
export default {
'create.start-event': createAction(
'bpmn:StartEvent',
'event',
'bpmn-icon-start-event-none',
'Create StartEvent'
),
'create.task': createAction(
'bpmn:Task',
'activity',
'bpmn-icon-task',
'Create Task'
)
}
function createAction(type, group, className, title, options) {
// 還記得 CustomPalette.js 嗎?便是這裡回調 createListener 函數
// if (action === 'click') {
// handler(originalEvent, autoActivate, elementFactory, create)
// }
function createListener(event, autoActivate, elementFactory, create) {
var shape = elementFactory.createShape({ type })
create.start(event, shape)
}
return {
group: group,
className: className,
title: title,
action: {
dragstart: createListener,
click: createListener
}
}
}
然後再做兩件事,
引入工具欄配置
、
去除預設工具欄
export default {
// ...
init() {
// // 去除預設工具欄
const modules = Modeler.prototype._modules
const index = modules.findIndex(it => it.paletteProvider)
modules.splice(index, 1)
this.bpmnModeler = new BpmnModeler({
paletteEntries,
additionalModules: [customPalette]
})
// ...
}
}
效果如下:
6. 修改樣式
bpmn:Task
使用了字型圖示
bpmn-icon-task
,
下面我們修改它,換成 img。
修改
paletteEntries.js
export default {
'create.task': createAction(
'bpmn:Task',
'activity',
'bpmn-icon-task-custom', // 🙋♂️ 使用圖檔後,記得修改成自己的類名
'Create Task',
require('./img/task.png') // 📌
)
}
function createAction(type, group, className, title, imageUrl) {
// ...
return {
group: group,
className: className,
title: title,
imageUrl, // 📌
action: {
dragstart: createListener,
click: createListener
}
}
}
最後
一切大功告成,你将擁有一個全新的工具欄。
突然,你發現通過工具欄生成的元素還保持着
最初
的樣子。
無需擔心,因為我們還沒告訴
bpmn
該怎麼渲染它
點選了解如何自定義渲染 customRenderer
相關
可能對你有幫助的官方資源:
- bpmn-js-example-custom-elements
目錄:
- 基礎使用
- 了解 BPMN 内部
- 自定義 Palette
- 自定義 Palette => 指定 Palette 容器
- 自定義 Renderer
- 自定義 contextPad
- 自定義連線和箭頭的顔色
- 自定義規則
- 自定義 properties-panel
- 右上角小地圖
- 總結常用 API 🚩
- 為 Viewer 添加一些功能
- bpmn-camunda DEMO