全網(wǎng)最詳bpmn.js教材-properties篇

前言

Q: bpmn.js是什么? ???

bpmn.js是一個BPMN2.0渲染工具包和web建模器, 使得畫流程圖的功能在前端來完成.

Q: 我為什么要寫該系列的教材? ???

因為公司業(yè)務(wù)的需要因而要在項目中使用到bpmn.js,但是由于bpmn.js的開發(fā)者是國外友人, 因此國內(nèi)對這方面的教材很少, 也沒有詳細的文檔. 所以很多使用方式很多坑都得自己去找.在將其琢磨完之后, 決定寫一系列關(guān)于它的教材來幫助更多bpmn.js的使用者或者是期于找到一種好的繪制流程圖的開發(fā)者. 同時也是自己對其的一種鞏固.

由于是系列的文章, 所以更新的可能會比較頻繁, 您要是無意間刷到了且不是您所需要的還請諒解??.

求贊??, 求Start ??, 求Fork ??, 希望能對你有一點小小的幫助.

所有教材的github地址: 《bpmn-chinese-document》

Properties篇

哈哈 來了 來了 它終于來了??.

讓大家久等的Properties篇??.

其實霖呆呆工作上用到的bpmn.js的內(nèi)容也就只局限于之前寫的文章了, 算是將實際用到的知識全盤脫出了... 那么就有人會好奇的問了...為什么連Properties-panel這樣重要的功能都沒有用到呢????

這其實和我們團隊的用法有關(guān):

最開始接觸使用到bpmn.js是因為需要用它來繪制工作流實現(xiàn)決策引擎的這么一個功能. 而我們的做法是前端通過bpmn.js來繪制流程圖, 圖中的StartUserTask、BusinessRuleTask等等我在這里都稱之為節(jié)點. 每個節(jié)點都對應(yīng)著xml文件中的標簽, 傳統(tǒng)的做法可能是將各個節(jié)點的屬性都保存到標簽上, 例如我這里有一個開始節(jié)點的xml標簽:

<startEvent id="StartEvent_1y45yut" name="開始" roles="admin"></startEvent>

然后給這個節(jié)點增加上一個名為“權(quán)限(roles)”的自定義屬性, 這個屬性會保存在xml中, 并且導(dǎo)出這個文件的時候也會留在上面.

我們雖然每個節(jié)點也都會關(guān)聯(lián)很多信息, 但是這些信息并不是直接保存在xml 標簽里的. 而是每個節(jié)點都會有一個 id , 后臺有一個表專門用于存放每個節(jié)點的附加信息, 所以每次點擊節(jié)點的時候, 都通過這個id來調(diào)取后臺存儲的數(shù)據(jù), 從而拿到節(jié)點對應(yīng)的屬性, 右邊出現(xiàn)一個抽屜將這些屬性信息顯示在里面可以進行修改. 修改保存之后, 也是調(diào)用后臺的接口來修改表里的信息. 所以主要的邏輯還是集中在后臺上. 因此對于xml的操作還真不是太多, 自然的也就沒用上Properties-panel了.

但是我的這種做法, 你也可以理解為右邊出現(xiàn)的“抽屜” 就是我自定義的Properties-panel, 因為它確實也起到了與節(jié)點關(guān)聯(lián)屬性的作用.

OK, 言歸正傳啦, 雖然我工作中并沒有用到它, 但是經(jīng)過讀者給出的意見以及自己對它的一些研究, 還是能用它做一些業(yè)務(wù)實現(xiàn)的, 希望在你認真看完之后能有所收獲??.

通過這一章節(jié)的閱讀你可以學習到:

  • 什么是bpmn properties????
  • 如何讀取bpmn properties????
  • 如何修改bpmn properties????
  • 使用updateProperties方法????

bpmn properties屬性介紹以及基本用法

1. 什么是bpmn properties????

讓我們先來搞懂一下什么是bpmn properties????

我們在用bpmn.js畫的每一個節(jié)點其實都被稱之為diagram element(圖表元素, 是不是很好理解??)

而在bpmn文件中的每個xml標簽稱之為BPMN element.

diagram elementBPMN element的一些屬性關(guān)聯(lián)起來靠的是一個叫做businessObject的屬性. 從名稱上理解你也可以知道它是一個對象(Object), 你可以在這個對象中添加上一些特殊的屬性, 并且這些屬性是可以直接插到BPMN element上的.

舉個例子??:

我繪制了一個StartEvent節(jié)點, 它對應(yīng)的:

  • diagram element:

    {
      id: "StartEvent_1y45yut",
      type: "bpmn:StartEvent",
      businessObject: {
          $type: "bpmn:StartEvent",
          name: "開始"
      }
    }
    
  • BPMN element:

    <startEvent id="StartEvent_1y45yut" name="開始"></startEvent>
    

像這類屬性就是bpmn properties, 你可以用它來實現(xiàn)你的業(yè)務(wù)需要.

2. 如何讀取bpmn properties????

不知道大家是否還記得我在《事件篇》中用到的一段代碼:

addModelerListener () {
       // 監(jiān)聽 modeler
      const bpmnjs = this.bpmnModeler
      const that = this
      const events = ['shape.added', 'shape.move.end', 'shape.removed']
      events.forEach(function(event) {
        that.bpmnModeler.on(event, e => {
          var elementRegistry = bpmnjs.get('elementRegistry')
          var shape = e.element ? elementRegistry.get(e.element.id) : e.shape
          if (event === 'shape.added') {
            console.log('新增了shape')
          } else if (event === 'shape.move.end') {
            console.log('移動了shape')
          } else if (event === 'shape.removed') {
            console.log('刪除了shape')
          }
        })
      })
    }

這個方法是放在 將字符串轉(zhuǎn)換成圖顯示出來 之后, 用于給元素綁定事件.

其中就有用到elementRegistry.

所以如果是在html中, 你就可以用這種方式來獲取bpmn properties:

<body>
    <div id="canvas"></div>
<script>
    var bpmnJS = new BpmnJS({
    container: '#canvas'
  });
  bpmnJS.importXML(xmlStr, function(err) {
    if (!err) {
        var elementRegistry = bpmnJs.get('elementRegistry')
      var startEventElement = elementRegistry.get('StartEvent_1y45yut'),
          startEvent = startEventElement.businessObject;
        console.log(startEvent.name) // 開始
    }
  }
</script>

而在一些class里, 比如CustomRenderer.js里, 你可以直接用.的方式就獲取到了:

export default class CustomRenderer extends BaseRenderer {
    drawShape (parentNode, element) {
        // element.businessObject
        // or 解構(gòu)
        // const { businessObject } = element
    }
}

3. 如何修改bpmn properties????

你在bpmn文件中, 可能會看到這樣一段代碼:

<bpmn:sequenceFlow id="SequenceFlow_1" sourceRef="StartEvent_1" targetRef="Task_1" name="FOO &gt; BAR?">
      <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression"><![CDATA[${foo > bar}]]></bpmn:conditionExpression>
    </bpmn:sequenceFlow>

里面的xsi:typesourceRef這些屬性是啥啊???? 我怎么知道哪類標簽有哪些屬性????

你其實可以在官方給的這個bpmn.json中查找到:

《meta-model descriptor》

設(shè)置的話可以根據(jù)以下方法:

var moddle = bpmnJS.get('moddle');

// 創(chuàng)建一個BPMN element , 并且載入到導(dǎo)出的xml里
var newCondition = moddle.create('bpmn:FormalExpression', {
  body: '${ value > 100 }'
});

// 寫入屬性, 但是不支持撤銷 
sequenceFlow.conditionExpression = newCondition;

上面??的這種方式是不支持撤銷的, 如果你想要能夠 撤銷/重新 的話, 你可以通過以下這種方式:

var modeling = bpmnJS.get('modeling');

modeling.updateProperties(sequenceFlowElement, {
  conditionExpression: newCondition
});

也就是通過modeling.updateProperties()這個方法.

這個modeling好像是需要引入的, 反正如果我是使用DNS 遠程的引入下面的這個js好像就會報錯Uncaught Error: No provider for "modeling"!.

<script src="https://unpkg.com/bpmn-js@6.0.2/dist/bpmn-viewer.development.js"></script>

當然如果你是使用npm 下載的話就沒有這個問題了.

這個方法的第一個參數(shù)是一個 diagram element, 也就是前面我們提到的用elementRegistry獲取到的對象.

第二個參數(shù)是要修改的屬性, 它是一個Map結(jié)構(gòu).

4. 使用updateProperties方法

例如??, 我想在點擊某個類型為bpmn:Task的節(jié)點的時候, 修改它的name屬性, 我可以這么做:

  • 給節(jié)點添加點擊事件
  • 判斷節(jié)點類型為bpmn:Task , 只對這種類型的節(jié)點做后續(xù)處理
  • 使用updateProperties更新name
    createNewDiagram () {
        // 將字符串轉(zhuǎn)換成圖顯示出來
        this.bpmnModeler.importXML(xmlStr, (err) => {
            if (err) {
                // console.error(err)
            } else {
                // 這里是成功之后的回調(diào), 可以在這里做一系列事情
                this.success()
            }
        })
    },
    success () {
        this.addModelerListener() // 添加監(jiān)聽事件
    },
    addModelerListener () {
            const eventBus = this.bpmnModeler.get('eventBus')
        const modeling = this.bpmnModeler.get('modeling')
        const elementRegistry = this.bpmnModeler.get('elementRegistry');
        const eventTypes = ['element.click', 'element.changed'];
        eventTypes.forEach(function(eventType) {
            eventBus.on(eventType, function (e) {
                if (!e || !e.element) {
                  console.log('無效的e', e)
                  return
                }
                if (eventType === 'element.click') {
                  console.log('點擊了element', e)
                  var shape = e.element ? elementRegistry.get(e.element.id) : e.shape
                  if (shape.type === 'bpmn:Task') {
                    modeling.updateProperties(shape, {
                        name: '我是修改后的Task名稱'
                    })
                  }
                }
            })
        })
    }

當然你也可以一次性修改多個屬性:

modeling.updateProperties(startEventElement, {
    name: '我是修改后的虛線節(jié)點',
    isInterrupting: false
})

我通過查找前面的 meta-model descriptor 知道StartEvent還有一個isInterrupting屬性, 于是試著修改它, 結(jié)果竟然成功了, 將開始節(jié)點變?yōu)榱颂摼€為邊框的節(jié)點:

bpmnModeler5.png

當然你也可以加一些除了meta-model descriptor里的一些自定義屬性:

modeling.updateProperties(shape, {
    name: '我是修改后的虛線節(jié)點',
    isInterrupting: false,
    customText: '我是自定義的text屬性'
})

只不過, 它們會被放到$attrs中:

bpmnModeler6.png

并且這種方式, 也是可以直接修改到bpmn文件中的:

bpmnModeler7.png

后語

這一章節(jié)主要是向大家介紹了一下bpmn properties的概念以及操作方式...其實在這之前, 我甚至不知道怎么給xml標簽上添加屬性, 也甚至不知道updateProperties怎么使用??...

還好皮厚的我不恥下問, 在群里問了一些小伙伴...

哈哈哈, 手動艾特感謝 @網(wǎng)易-付超老哥 還有@李岱老哥 , 在研究bpmn.js屬性上面給我的幫助, 當然我也知道很多小伙伴希望我能快點更上一篇關(guān)于properties-panel的內(nèi)容...

但今天真的有點累了... 容我先緩一緩, 咱明天再更行不??.

(嗯...不行也得行, 我說了算...)

系列全部目錄請查看此處: 《全網(wǎng)最詳bpmn.js教材目錄》

最后, 如果你也對bpmn.js 感興趣可以進我們的bpmn.js交流群??????, 共同學習, 共同進步.

關(guān)注霖呆呆(LinDaiDai)的公眾號:LinDaiDai 選擇 其它 菜單中的 bpmn.js群 即可??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容