Q: bpmn.js是什麼? 🤔️javascript
」
bpmn.js是一個BPMN2.0渲染工具包和web建模器, 使得畫流程圖的功能在前端來完成.css
Q: 我爲何要寫該系列的教材? 🤔️前端
」
由於公司業務的須要於是要在項目中使用到bpmn.js
,可是因爲bpmn.js
的開發者是國外友人, 所以國內對這方面的教材不多, 也沒有詳細的文檔. 因此不少使用方式不少坑都得本身去找.在將其琢磨完以後, 決定寫一系列關於它的教材來幫助更多bpmn.js
的使用者或者是期於找到一種好的繪製流程圖的開發者. 同時也是本身對其的一種鞏固.vue
因爲是系列的文章, 因此更新的可能會比較頻繁, 您要是無心間刷到了且不是您所須要的還請諒解😊.java
求贊👍求心❤️. 更但願能對你有一點幫助.webpack
本教材全部內容已更新至GitHub 🌟ios
請認準GitHub地址: bpmn-chinese-document 🎉🎉🎉git
這一章節主要是將近段時間前端bpmn.js交流羣中羣友提的一些問題作一個彙總...github
後面有碰到一樣問題的小夥伴但願能幫到大家...web
問題的解答有的是羣友給出的方案有些是我本身想的方案, 可能不是最優解, 若是有更好解決辦法的小夥伴還但願可以提出來呀 😁.
palette
左側工具欄
palette
和
renderer
中的圖片如何用本地圖片
palette
中如何使用它自己的圖標樣式
contextPad
contextPad
中的內容根據元素類型不一樣顯示不一樣
bpmn
或者
xml
文件
id
是否可以修改
logo
可否隱去
實現相似於下面這張圖的效果:
原先咱們實現自定義palette的時候只考慮到了顯示圖片的狀況, 有一些業務場景可能須要將每種元素的標題顯示出來.
這裏我提供了兩種解決方案:
這裏我主要講解一下第一種實現方式.
首先咱們知道在customPalette
中是有這麼一個東西的:
'append.lindaidai-task': {
group: 'model',
className: 'icon-custom lindaidai-task',
title: translate('建立一個類型爲lindaidai-task的任務節點'),
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');
}
複製代碼
如今我想在它下面加一個標題:
.icon-custom.lindaidai-task::after {
font-size: 12px;
content: 'LinDaiDai'; /* 這裏放的就是標題 */
position: absolute;
top: 17px;
left: 0;
}
複製代碼
這樣就簡單的實現了這麼一個顯示標題的功能.
具體案例能夠看這裏: bpmn-vue-basic
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裏想要實現自定義效果主要是靠svgCreate
方法建立出一個image
元素而後添加到返回值中, 這個圖片的url我原先一直用的是網絡圖片, 那確定沒什麼問題.
而若是你想要用一張本地圖片的話, 你開始想到的多是這樣使用相對路徑:
// 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
})
複製代碼
可是保存打開頁面以後發現不盡人意...
在這裏你須要使用CommonJS
的引入方式才能夠, 將它轉換爲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
})
複製代碼
保存打開頁面發現是能夠的.
可是在這裏我不推薦你使用相對路徑的方式, 由於配置文件的位置可能隨時會變, 一變的話相對路徑也得更這邊, 因此若是你是使用以webpack
打包工具爲基礎的腳手架的話, 我建議你配置一個alias
(別名), 那樣也能方便你開發.
配置alias
的方式很簡單, 若是你和我同樣是用vue
開發項目的話, 請檢查一下你的根目錄有沒有一個叫vue.config.js
的文件, 若是沒有的話, 建立一個, 並在其中寫上:
// 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
.
如今讓咱們來修改一下前面的路徑:
// 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
})
複製代碼
如今不管你如何移動你的customRenderer.js
文件, 圖片的路徑都不會錯了.
案例GitHub地址: bpmn-vue-custom
(該問題解決方案來自簡書網友 夢想仍是要有的_bfc7)
咱們以前的自定義palette一直都是使用咱們本身找的一些圖片圖標...
而若是你某一個元素的樣式就想要它官方提供的怎麼辦 🤔️?
例如我要實現這樣的效果:
前兩個元素是我自定義的, 最後一個網關用官方提供的原始樣式, 以下圖:
想要作到這一點其實很簡單, 還記得咱們自定義palette的時候是依賴着一個className
屬性的嗎?
你只須要將這個className
設置成它官方提供的就能夠了.
那有人就要問了,這個官方原始的className
我該到哪找呢 😂?
審查元素, 找到對應的類名, 好比這裏是bpmn-icon-gateway-none
而後在將customPalette
中的網關設置成這個className
:
PaletteProvider.prototype.getPaletteEntries = function(element) {
...
return {
...
'create.exclusive-gateway': {
group: 'gateway',
className: 'bpmn-icon-gateway-none', // 重點是這個
title: '建立一個網關',
action: {
dragstart: createGateway(),
click: createGateway()
}
}
}
}
複製代碼
如今左側的工具欄就已經能夠將原始的網關樣式顯示出來了.
可是有一個問題了, 那就是此時你想要用你定義好的這個網關在右邊畫圖, 也就是進入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)
}
}
複製代碼
這樣修改以後, 在執行drawShape
方法的時候, 它就會判斷是不是自定義元素, 若是是自定義元素的話纔有返回值, 不然就沒有返回值.
沒有返回值時它就會根據原始的樣式進行渲染了.
這是由於咱們在設計自定義modeler的時候將原始的modeler也引用進來了:
關於上述案例可查看: bpmn-vue-custom 中的自定義modeler那一個tab項.
不一樣類型的節點出現的contextPad的內容多是不一樣的. 好比:
StartEvent會出現edit、delete、Task、BusinessRuleTask、ExclusiveGateway等等;
EndEvent只能出現edit、delete;SequenceFlow只能出現edit、delete.
也就是說咱們須要根據節點類型來返回不一樣的contextPad.
這個其實我在《全網最詳bpmn.js教材-封裝組件篇》 這裏面已經提到過該如何處理了, 具體能夠看那篇文章:
在http篇
那一章節, 我向你們演示的是經過一個遠程的文件連接(多是後臺傳遞過來的), 而後經過axios
解析獲取的文件, 從而獲得xml
的字符串再調用importXML
方法顯示出圖形.
那麼如何加載一個本地的bpmn
文件或者xml
文件呢.
我首先想到的是經過xml-loader
解析這兩類文件, 可是不知道能不能成, 因而試了試.
(項目案例基於: bpmn-vue-custom)
首先在項目中安裝xml-loader
:
$ npm i --save-dev xml-loader
複製代碼
而後配置一下vue.config.js
這個文件(這個文件在上面👆palette和renderer中的圖片如何用本地圖片
已經提到過了, 沒有的話就在根目錄建立一個)
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
處理.
如今讓咱們在custom-renderer.vue
這個頁面中來試試:
<script> const bpmnXml = require('../mock/diagram.bpmn') console.log(bpmnXml) </script>
複製代碼
打印出來的bpmnXml
倒是一個對象, 而不是字符串:
並且使用importXML
想要轉換這個對象顯然是不行的.
這可怎麼辦呢...
等等, 既然importXML
解析只須要一個字符串的話, 讓我想到了前幾天剛學到的raw-loader
, 它能夠獲取txt
中的文本內容, 那是否是也能獲取bpmn和xml
呢 🤔️?
說幹就幹, 繼續安裝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字符串
因此咱們取default
屬性就能夠了:
this.bpmnModeler.importXML(bpmnXml.default, err => {
if (err) {
} else {
// 這裏是成功以後的回調, 能夠在這裏作一系列事情
this.success()
}
})
複製代碼
不知道是否是版本的緣由, 有些經過raw-loader
轉換的bpmn
文件就直接是字符串, 而不是這個對象, 你們在使用的時候注意一下.
注意⚠️:
關於上面vue.config.js
是vue-cli3
中webpack
的配置, 若是你的項目的構建方式是使用原始webpack
的話, 它就至關於webpack.config.js
中的:
module.exports = {
...
module: {
rules: [
{
test: /.(bpmn|xml)$/,
use: 'raw-loader'
}
]
}
}
複製代碼
其它打包方式我這裏就不說了.
這個方案是羣裏的羣友火蓮提出來的, 他已經實現了, 我就沒去試了, 不過應該是能夠的.
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(oFREvent){
var xmlDoc = oFREvent.target.result;
openDiagram(xmlDoc);
}
複製代碼
其實每一個元素的id也是一個屬性而已, 可是它並不會隨着元素類型的改變而改變, 也就是說正常狀況下它是不會變更的.
不過既然它是一個屬性, 那麼咱們就能經過modeling.updateProperties()
修改它:
const properties = { id: 'id0001' }
const { modeler, element } = this
const modeling = modeler.get('modeling')
modeling.updateProperties(element, properties)
複製代碼
建立線節點在《全網最詳bpmn.js教材-封裝組件篇》 這裏面也提到過該如何處理, 具體能夠看那篇文章.
關於右下角logo可否隱去這個問題, 羣裏產生了激烈的討論, 由於你們都怕吃官司侵權...
用官網的話來講就是不能:
不過羣友zaw也提供了一種解決方案😂:
找到那個類名, 而後樣式設置 display : none
.
我認爲你能不隱就不要隱去了吧, 雖然人家這東西是開源的, 可是也說了不要去掉, 就聽從做者的意願吧(就像我在這裏求你們一鍵三連同樣: 點贊, 收藏, Star 呀 哈哈哈...)
所有教材目錄: 《全網最詳bpmn.js教材》
GitHub教材地址: bpmn-chinese-document 求Star 🌟 求Fork 📓...
疫情四溢, 足不出戶, 霖呆呆從大年初二到今天就只出過一次門 😂...
不知道大家那邊狀況怎麼樣, 反正我家後面300米處的那戶人家夫妻倆已經被感染隔離起來了...
因此咱們小鎮也被全面封鎖了, 還不知道啥時候能返深...
不過在家呆着挺好的, 可貴有和家人相處的機會, 要好好珍惜呀, 並且能趁着假期惡補一下本身薄弱的知識點就很好, 哈哈😄.
喜歡霖呆呆的小夥還但願能夠關注霖呆呆的公衆號 LinDaiDai
或者掃一掃下面的二維碼👇👇👇.
我會不定時的更新一些前端方面的知識內容以及本身的原創文章🎉
你的鼓勵就是我持續創做的主要動力 😊.
相關推薦:
《前面系列-this/apply/call問點(假期一塊兒來學習吧, 武漢加油!!!)》