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

前言

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

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

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

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

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

不求贊??不求心??. 只希望能對(duì)你有一點(diǎn)小小的幫助.

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

事件篇

上一章節(jié)我們介紹了利用bpmn.js與后臺(tái)進(jìn)行交互, 要是對(duì)bpmn.js不了解的小伙請(qǐng)移步:

《全網(wǎng)最詳bpmn.js教材-http請(qǐng)求篇》

這一章節(jié)要講解是關(guān)于bpmn.js的一些事件, 通過學(xué)習(xí)此章節(jié)你可以學(xué)習(xí)到:

監(jiān)聽modeler并綁定事件

很多時(shí)候你期望的是在用戶在進(jìn)行不同操作的時(shí)候能夠監(jiān)聽到他操作的是什么, 從而做想要做的事情.

是進(jìn)行了shape的新增還是進(jìn)行了線的新增.

比如如下的一些監(jiān)聽事件:

  • shape.added 新增一個(gè)shape之后觸發(fā);
  • shape.move.end 移動(dòng)完一個(gè)shape之后觸發(fā);
  • shape.removed 刪除一個(gè)shape之后觸發(fā);

繼續(xù)在項(xiàng)目案例bpmn-vue-basic的基礎(chǔ)上創(chuàng)建一個(gè)event.vue文件:

并在success()函數(shù)中添加上監(jiān)聽事件的函數(shù):

// event.vue
<script>
...
success () {
  this.addModelerListener()
},
// 監(jiān)聽 modeler
addModelerListener() {
  const bpmnjs = this.bpmnModeler
  const that = this
  // 這里我是用了一個(gè)forEach給modeler上添加要綁定的事件
  const events = ['shape.added', 'shape.move.end', 'shape.removed', 'connect.end',          'connect.move']
  events.forEach(function(event) {
    that.bpmnModeler.on(event, e => {
      console.log(event, e)
      var elementRegistry = bpmnjs.get('elementRegistry')
      var shape = e.element ? elementRegistry.get(e.element.id) : e.shape
      console.log(shape)
    })
  })
},

如圖所示, 在這里你就可以獲取到相關(guān)節(jié)點(diǎn)的所有信息了:

img1

案例Git地址: LinDaiDai-bpmn.js案例event.vue

其實(shí)具體有哪些事件我在官網(wǎng)上都沒有找到說明, 以上只是我在查找到bpmn.io/diagram.js/modeling文件之后, 取的一些我項(xiàng)目里有用到的事件.

監(jiān)聽element并綁定事件

上面介紹的是監(jiān)聽modeler并綁定事件, 可能你也需要監(jiān)聽用戶點(diǎn)擊圖形上的element或者監(jiān)聽某個(gè)element改變:

  • element.click 點(diǎn)擊元素;
  • element.changed 當(dāng)元素發(fā)生改變的時(shí)候(包括新增、移動(dòng)、刪除元素)

繼續(xù)在success()上添加監(jiān)聽事件:

// event.vue
<script>
...
success () {
    ...
    this.addEventBusListener()
},
addEventBusListener () {
    let that = this
  const eventBus = this.bpmnModeler.get('eventBus') // 需要使用eventBus
  const eventTypes = ['element.click', 'element.changed'] // 需要監(jiān)聽的事件集合
  eventTypes.forEach(function(eventType) {
    eventBus.on(eventType, function(e) {
      console.log(e)
    })
  })
}
</script>

配置好addEventBusListener()函數(shù)后, 在進(jìn)行元素的點(diǎn)擊、新增、移動(dòng)、刪除的時(shí)候都能監(jiān)聽到了.

但是有一點(diǎn)很不好, 你在點(diǎn)擊“畫布”的時(shí)候, 也就是根元素也可能會(huì)觸發(fā)此事件, 我們一般都不希望此時(shí)會(huì)觸發(fā), 因此我們可以在on回調(diào)中添加一些判斷, 來避免掉不需要的情況:

eventBus.on(eventType, function(e) {
  if (!e || e.element.type == 'bpmn:Process') return // 這里我的根元素是bpmn:Process
  console.log(e)
})

此時(shí)我們可以把監(jiān)聽到返回的節(jié)點(diǎn)信息打印出來看看:

img2

如上圖, 它會(huì)打印出該節(jié)點(diǎn)的Shape信息和DOM信息等, 但我們可能只關(guān)注于Shape信息(也就是該節(jié)點(diǎn)的id、type等等信息), 此時(shí)我們可以使用elementRegistry來獲取Shape信息:

eventBus.on(eventType, function(e) {
  if (!e || e.element.type == 'bpmn:Process') return // 這里我的根元素是bpmn:Process
  console.log(e)
  var elementRegistry = this.bpmnModeler.get('elementRegistry')
  var shape = elementRegistry.get(e.element.id) // 傳遞id進(jìn)去
  console.log(shape) // {Shape}
  console.log(e.element) // {Shape}
  console.log(JSON.stringify(shape)===JSON.stringify(e.element)) // true
})

或者你也可以直接就用e.element獲取到Shape的信息, 我比較了一下它們兩是一樣的. 但是官方是推薦使用elementRegistry的方式.

通過監(jiān)聽事件判斷操作方式

上面我們已經(jīng)介紹了modelerelement的監(jiān)聽綁定方式, 在事件應(yīng)用中, 你更多的需要知道用戶要進(jìn)行什么操作, 好寫對(duì)應(yīng)的業(yè)務(wù)邏輯.

這里我就以我工作中要用到的場(chǎng)景為案例進(jìn)行講解.

  • 新增了shape
  • 新增了線(connection)
  • 刪除了shape和connection
  • 移動(dòng)了shape和線
// event.vue
    ...
    success () {
      this.addModelerListener()
      this.addEventBusListener()
    },
    // 添加綁定事件
    addBpmnListener () {
      const that = this
      // 獲取a標(biāo)簽dom節(jié)點(diǎn)
      const downloadLink = this.$refs.saveDiagram
      const downloadSvgLink = this.$refs.saveSvg
        // 給圖綁定事件,當(dāng)圖有發(fā)生改變就會(huì)觸發(fā)這個(gè)事件
      this.bpmnModeler.on('commandStack.changed', function () {
        that.saveSVG(function(err, svg) {
            that.setEncoded(downloadSvgLink, 'diagram.svg', err ? null : svg)
        })
        that.saveDiagram(function(err, xml) {
            that.setEncoded(downloadLink, 'diagram.bpmn', err ? null : xml)
        })
      })
    },
    addModelerListener() {
      // 監(jiān)聽 modeler
      const bpmnjs = this.bpmnModeler
      const that = this
      // 'shape.removed', 'connect.end', 'connect.move'
      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
          // console.log(shape)
          if (event === 'shape.added') {
            console.log('新增了shape')
          } else if (event === 'shape.move.end') {
            console.log('移動(dòng)了shape')
          } else if (event === 'shape.removed') {
            console.log('刪除了shape')
          }
        })
      })
    },
    addEventBusListener() {
      // 監(jiān)聽 element
      let that = this
      const eventBus = this.bpmnModeler.get('eventBus')
      const eventTypes = ['element.click', 'element.changed']
      eventTypes.forEach(function(eventType) {
        eventBus.on(eventType, function(e) {
          if (!e || e.element.type == 'bpmn:Process') return
          if (eventType === 'element.changed') {
            that.elementChanged(eventType, e)
          } else if (eventType === 'element.click') {
            console.log('點(diǎn)擊了element')
          }
        })
      })
    },
    elementChanged(eventType, e) {
      var shape = this.getShape(e.element.id)
      if (!shape) {
        // 若是shape為null則表示刪除, 無(wú)論是shape還是connect刪除都調(diào)用此處
        console.log('無(wú)效的shape')
        // 由于上面已經(jīng)用 shape.removed 檢測(cè)了shape的刪除, 因此這里只判斷是否是線
        if (this.isSequenceFlow(shape.type)) {
          console.log('刪除了線')
        }
      }
      if (!this.isInvalid(shape.type)) {
        if (this.isSequenceFlow(shape.type)) {
          console.log('改變了線')
        }
      }
    },
    getShape(id) {
      var elementRegistry = this.bpmnModeler.get('elementRegistry')
      return elementRegistry.get(id)
    },
    isInvalid (param) { // 判斷是否是無(wú)效的值
      return param === null || param === undefined || param === ''
    },
    isSequenceFlow (type) { // 判斷是否是線
      return type === 'bpmn:SequenceFlow'
    }

案例Git地址: LinDaiDai-bpmn.js案例event.vue 喜歡的小伙伴請(qǐng)給個(gè)Star??呀, 謝謝??

后語(yǔ)

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

系列相關(guān)推薦:

《全網(wǎng)最詳bpmn.js教材-基礎(chǔ)篇》

《全網(wǎng)最詳bpmn.js教材-http請(qǐng)求篇》

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

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

《全網(wǎng)最詳bpmn.js教材-編輯、刪除節(jié)點(diǎn)篇》

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

相關(guān)閱讀更多精彩內(nèi)容

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