Q: bpmn.js是什么? ???
bpmn.js是一個BPMN2.0渲染工具包和web建模器, 使得畫流程圖的功能在前端來完成.
Q: 我為什么要寫該系列的教材? ???
因為公司業(yè)務的需要因而要在項目中使用到bpmn.js,但是由于bpmn.js的開發(fā)者是國外友人, 因此國內(nèi)對這方面的教材很少, 也沒有詳細的文檔. 所以很多使用方式很多坑都得自己去找.在將其琢磨完之后, 決定寫一系列關于它的教材來幫助更多bpmn.js的使用者或者是期于找到一種好的繪制流程圖的開發(fā)者. 同時也是自己對其的一種鞏固.
由于是系列的文章, 所以更新的可能會比較頻繁, 您要是無意間刷到了且不是您所需要的還請諒解??.
求贊??求心??. 更希望能對你有一點幫助.
本教材所有內(nèi)容已更新至GitHub ??
請認準GitHub地址: bpmn-chinese-document ??????
全網(wǎng)最詳bpmn.js教材-群友問題匯總(一)
這一章節(jié)主要是將近段時間前端bpmn.js交流群中群友提的一些問題做一個匯總...
后面有碰到同樣問題的小伙伴希望能幫到你們...
問題的解答有的是群友給出的方案有些是我自己想的方案, 可能不是最優(yōu)解, 如果有更好解決辦法的小伙伴還希望能夠提出來呀 ??.
目錄
-
palette左側(cè)工具欄- 如何給工具欄的每一項都加上標題
-
palette和renderer中的圖片如何用本地圖片 - 自定義
palette中如何使用它本身的圖標樣式
-
contextPad-
contextPad中的內(nèi)容根據(jù)元素類型不同顯示不同
-
- 文件
- 如何加載本地
bpmn或者xml文件
- 如何加載本地
- 屬性
- 每個元素的
id是否能夠修改
- 每個元素的
- 其它
- 如何創(chuàng)建線節(jié)點
- 右下角的綠色
logo能否隱去
palette左側(cè)工具欄
1. 如何給工具欄的每一項都加上標題
實現(xiàn)類似于下面這張圖的效果:
原先我們實現(xiàn)自定義palette的時候只考慮到了顯示圖片的情況, 有一些業(yè)務場景可能需要將每種元素的標題顯示出來.
這里我提供了兩種解決方案:
- 給每個類定義一個偽類, 將title寫到這個偽類里
- 額...要UI設計師將每個title畫到每個元素圖表的下面, 也就是將title作為圖標的一部分
這里我主要講解一下第一種實現(xiàn)方式.
首先我們知道在customPalette中是有這么一個東西的:
'append.lindaidai-task': {
group: 'model',
className: 'icon-custom lindaidai-task',
title: translate('創(chuàng)建一個類型為lindaidai-task的任務節(jié)點'),
action: {
click: appendTask,
dragstart: appendTaskStart
}
}
主要看className.
之前我教材中的css代碼是這樣寫的:
.icon-custom {
border-radius: 50%;
background-size: 65%;
background-repeat: no-repeat;
background-position: center;
}
.icon-custom.lindaidai-task {
position: relative;
background-image: url('https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/rules.png');
}
現(xiàn)在我想在它下面加一個標題:
.icon-custom.lindaidai-task::after {
font-size: 12px;
content: 'LinDaiDai'; /* 這里放的就是標題 */
position: absolute;
top: 17px;
left: 0;
}
這樣就簡單的實現(xiàn)了這么一個顯示標題的功能.
具體案例可以看這里: bpmn-vue-basic
2. palette和renderer中的圖片如何用本地圖片
palette上想要用本地圖片很簡單, 因為自定義palette主要是依靠className, 而className肯定是寫在css文件中的, 我們只需要找到圖片對應的相對路徑就可以了:
例如項目目錄為:
/src
|- /assets
|- rules.png
|- css
|- app.css
它對應的引用:
/*app.css*/
.icon-custom.lindaidai-task {
position: relative;
/* background-image: url('https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/rules.png'); */
background-image: url('../assets/rules.png');
}
我們知道自定義renderer里想要實現(xiàn)自定義效果主要是靠svgCreate方法創(chuàng)建出一個image元素然后添加到返回值中, 這個圖片的url我原先一直用的是網(wǎng)絡圖片, 那肯定沒什么問題.
而如果你想要用一張本地圖片的話, 你開始想到的可能是這樣使用相對路徑:
// customRenderer.js
const imageConfig = {
'url': '../../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 }
}
const { attr, url } = imageConfig;
const customIcon = svgCreate('image', {
...attr,
href: url
})
但是保存打開頁面之后發(fā)現(xiàn)不盡人意...
在這里你需要使用CommonJS的引入方式才可以, 將它轉(zhuǎn)換為base64的Data URL:
// customRenderer.js
const imageConfig = {
'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 }
}
const { attr, url } = imageConfig;
const customIcon = svgCreate('image', {
...attr,
href: url
})
保存打開頁面發(fā)現(xiàn)是可以的.
但是在這里我不推薦你使用相對路徑的方式, 因為配置文件的位置可能隨時會變, 一變的話相對路徑也得更這邊, 所以如果你是使用以webpack打包工具為基礎的腳手架的話, 我建議你配置一個alias(別名), 那樣也能方便你開發(fā).
配置alias的方式很簡單, 如果你和我一樣是用vue開發(fā)項目的話, 請檢查一下你的根目錄有沒有一個叫vue.config.js的文件, 如果沒有的話, 創(chuàng)建一個, 并在其中寫上:
// customRenderer.js
const path = require('path')
const resolve = dir => path.join(__dirname, dir)
module.exports = {
chainWebpack: config => {
config.resolve.alias
.set('@', resolve('src'))
.set('@assets', resolve('src/assets'))
}
}
(其它框架請自行度娘...)
是不是看著也很簡單, 和它的英文一樣, 其實也就是給某個文件夾配置一個別名.
比如我這里就是給src和src/assets配置了別名.
這樣你在代碼里寫@/views/xxx.vue就當于寫src/views/xxx.vue.
現(xiàn)在讓我們來修改一下前面的路徑:
// customRenderer.js
const imageConfig = {
'url': require('@assets/rules.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 }
}
const { attr, url } = imageConfig;
const customIcon = svgCreate('image', {
...attr,
href: url
})
現(xiàn)在無論你如何移動你的customRenderer.js文件, 圖片的路徑都不會錯了.
案例GitHub地址: bpmn-vue-custom
(該問題解決方案來自簡書網(wǎng)友 夢想還是要有的_bfc7)
3. 自定義palette中如何使用它本身的圖標樣式
我們之前的自定義palette一直都是使用我們自己找的一寫圖片圖標...
而如果你某一個元素的樣式就想要它官方提供的怎么辦 ????
例如我要實現(xiàn)這樣的效果:
前兩個元素是我自定義的, 最后一個網(wǎng)關用官方提供的原始樣式, 如下圖:
想要做到這一點其實很簡單, 還記得我們自定義palette的時候是依賴著一個className屬性的嗎?
你只需要將這個className設置成它官方提供的就可以了.
那有人就要問了,這個官方原始的className我該到哪找呢 ???
審查元素, 找到對應的類名, 比如這里是bpmn-icon-gateway-none
然后在將customPalette中的網(wǎng)關設置成這個className:
PaletteProvider.prototype.getPaletteEntries = function(element) {
...
return {
...
'create.exclusive-gateway': {
group: 'gateway',
className: 'bpmn-icon-gateway-none', // 重點是這個
title: '創(chuàng)建一個網(wǎng)關',
action: {
dragstart: createGateway(),
click: createGateway()
}
}
}
}
現(xiàn)在左側(cè)的工具欄就已經(jīng)可以將原始的網(wǎng)關樣式顯示出來了.
但是有一個問題了, 那就是此時你想要用你定義好的這個網(wǎng)關在右邊畫圖, 也就是進入renderer階段, 如果你是完全自定義renderer的話, 控制臺可能就會報錯了...
先讓我們來回顧一下customRenderer.js是怎么寫的:
export default function CustomRenderer(eventBus, styles, textRenderer) {
this.drawCustomElements = function(parentNode, element) {
if (customElements.includes(type)) { // or customConfig[type]
// 這里是自定義的元素
}
}
}
CustomRenderer.prototype.drawShape = function(p, element) {
return this.drawCustomElements(p, element)
}
如果你和我一樣是將是否是自定義的元素這個判斷放到drawCustomElements這個方法里寫的話你可能就會報錯了...因為它會告訴你找不到這個類型的渲染方式.
解決辦法是這層判斷放到CustomRenderer.prototype.drawShape里:
export default function CustomRenderer(eventBus, styles, textRenderer) {
this.drawCustomElements = function(parentNode, element) {
// 這里是自定義的元素
}
}
CustomRenderer.prototype.drawShape = function(p, element) {
if (customElements.includes(element.type)) { // 放到這里判斷
return this.drawCustomElements(p, element)
}
}
這樣修改之后, 在執(zhí)行drawShape方法的時候, 它就會判斷是否是自定義元素, 如果是自定義元素的話才有返回值, 否則就沒有返回值.
沒有返回值時它就會根據(jù)原始的樣式進行渲染了.
這是因為我們在設計自定義modeler的時候?qū)⒃嫉膍odeler也引用進來了:
關于上述案例可查看: bpmn-vue-custom 中的自定義modeler那一個tab項.
contextPad
1. contextPad中的內(nèi)容根據(jù)元素類型不同顯示不同
不同類型的節(jié)點出現(xiàn)的contextPad的內(nèi)容可能是不同的.
比如:
StartEvent會出現(xiàn)edit、delete、Task、BusinessRuleTask、ExclusiveGateway等等;
EndEvent只能出現(xiàn)edit、delete;SequenceFlow只能出現(xiàn)edit、delete.
也就是說我們需要根據(jù)節(jié)點類型來返回不同的contextPad.
這個其實我在《全網(wǎng)最詳bpmn.js教材-封裝組件篇》 這里面已經(jīng)提到過該如何處理了, 具體可以看那篇文章:
文件
1. 如何加載本地bpmn或者xml文件
在http篇那一章節(jié), 我向大家演示的是通過一個遠程的文件鏈接(可能是后臺傳遞過來的), 然后通過axios解析獲取的文件, 從而得到xml的字符串再調(diào)用importXML方法顯示出圖形.
那么如何加載一個本地的bpmn文件或者xml文件呢.
方案一: 使用raw-loader
我首先想到的是通過xml-loader解析這兩類文件, 但是不知道能不能成, 于是試了試.
(項目案例基于: bpmn-vue-custom)
首先在項目中安裝xml-loader:
$ npm i --save-dev xml-loader
然后配置一下vue.config.js這個文件(這個文件在上面??palette和renderer中的圖片如何用本地圖片已經(jīng)提到過了, 沒有的話就在根目錄創(chuàng)建一個)
vue.config.js:
const path = require('path')
const resolve = dir => path.join(__dirname, dir)
module.exports = {
chainWebpack: config => {
config.resolve.alias
.set('@', resolve('src'))
.set('@assets', resolve('src/assets'))
.end()
config.module // 主要是看這部分
.rule('xml-loader')
.test(/.(bpmn|xml)$/)
.use('xml-loader')
.loader('xml-loader')
.end()
}
}
這里的意思就是以bpmn或者xml為后綴的文件會被xml-loader處理.
現(xiàn)在讓我們在custom-renderer.vue這個頁面中來試試:
<script>
const bpmnXml = require('../mock/diagram.bpmn')
console.log(bpmnXml)
</script>
打印出來的bpmnXml卻是一個對象, 而不是字符串:
而且使用importXML想要轉(zhuǎn)換這個對象顯然是不行的.
這可怎么辦呢...
等等, 既然importXML解析只需要一個字符串的話, 讓我想到了前幾天剛學到的raw-loader, 它可以獲取txt中的文本內(nèi)容, 那是不是也能獲取bpmn和xml呢 ????
說干就干, 繼續(xù)安裝raw-loader:
$ npm i --save-dev raw-loader
然后修改vue.config.js:
const path = require('path')
const resolve = dir => path.join(__dirname, dir)
module.exports = {
chainWebpack: config => {
config.resolve.alias
.set('@', resolve('src'))
.set('@assets', resolve('src/assets'))
.end()
config.module // 將xml-loader替換成raw-loader
.rule('raw-loader')
.test(/.(bpmn|xml)$/)
.use('raw-loader')
.loader('raw-loader')
.end()
}
}
修改完之后記得重啟項目...
然后讓我們來看看效果:
<script>
const bpmnXml = require('../mock/diagram.bpmn')
console.log(bpmnXml)
console.log(typeof bpmnXml) // object
console.log(bpmnXml.default)
</script>
此時打印出來的雖然也是個對象, 但是里面有個default屬性, 它存儲的就是xml字符串
所以我們?nèi)?code>default屬性就可以了:
this.bpmnModeler.importXML(bpmnXml.default, err => {
if (err) {
} else {
// 這里是成功之后的回調(diào), 可以在這里做一系列事情
this.success()
}
})
不知道是不是版本的原因, 有些通過raw-loader轉(zhuǎn)換的bpmn文件就直接是字符串, 而不是這個對象, 大家在使用的時候注意一下.
注意??:
關于上面vue.config.js是vue-cli3中webpack的配置, 如果你的項目的構(gòu)建方式是使用原始webpack的話, 它就相當于webpack.config.js中的:
module.exports = {
...
module: {
rules: [
{
test: /.(bpmn|xml)$/,
use: 'raw-loader'
}
]
}
}
其它打包方式我這里就不說了.
方案二: 使用new FileReader()
這個方案是群里的群友火蓮提出來的, 他已經(jīng)實現(xiàn)了, 我就沒去試了, 不過應該是可以的.
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(oFREvent){
var xmlDoc = oFREvent.target.result;
openDiagram(xmlDoc);
}
屬性
1. 每個元素的id是否能夠修改
其實每個元素的id也是一個屬性而已, 但是它并不會隨著元素類型的改變而改變, 也就是說正常情況下它是不會變動的.
不過既然它是一個屬性, 那么我們就能通過modeling.updateProperties()修改它:
const properties = { id: 'id0001' }
const { modeler, element } = this
const modeling = modeler.get('modeling')
modeling.updateProperties(element, properties)
其它
1. 如何創(chuàng)建線節(jié)點
創(chuàng)建線節(jié)點在《全網(wǎng)最詳bpmn.js教材-封裝組件篇》 這里面也提到過該如何處理, 具體可以看那篇文章.
2. 右下角的綠色logo能否隱去
關于右下角logo能否隱去這個問題, 群里產(chǎn)生了激烈的討論, 因為大家都怕吃官司侵權...
用官網(wǎng)的話來說就是不能:
不過群友zaw也提供了一種解決方案??:
找到那個類名, 然后樣式設置 display : none.
我認為你能不隱就不要隱去了吧, 雖然人家這東西是開源的, 但是也說了不要去掉, 就遵從作者的意愿吧(就像我在這里求大家一鍵三連一樣: 點贊, 收藏, Star 呀 哈哈哈...)
后語
全部教材目錄: 《全網(wǎng)最詳bpmn.js教材》
GitHub教材地址: bpmn-chinese-document 求Star ?? 求Fork ??...
疫情四溢, 足不出戶, 霖呆呆從大年初二到今天就只出過一次門 ??...
不知道你們那邊情況怎么樣, 反正我家后面300米處的那戶人家夫妻倆已經(jīng)被感染隔離起來了...
所以我們小鎮(zhèn)也被全面封鎖了, 還不知道啥時候能返深...
不過在家呆著挺好的, 難得有和家人相處的機會, 要好好珍惜呀, 而且能趁著假期惡補一下自己薄弱的知識點就很好, 哈哈??.
喜歡霖呆呆的小伙還希望可以關注霖呆呆的公眾號 LinDaiDai
你的鼓勵就是我持續(xù)創(chuàng)作的主要動力 ??.
相關推薦:
《前面系列-this/apply/call問點(假期一起來學習吧, 武漢加油!!!)》