前言

Q: bpmn.js是什么? 🤔️

bpmn.js是一个BPMN2.0渲染工具包和web建模器, 使得画流程图的功能在前端来完成.

Q: 我为什么要写该系列的教材? 🤔️

因为公司业务的需要因而要在项目中使用到bpmn.js,但是由于bpmn.js的开发者是国外友人, 因此国内对这方面的教材很少, 也没有详细的文档. 所以很多使用方式很多坑都得自己去找.在将其琢磨完之后, 决定写一系列关于它的教材来帮助更多bpmn.js的使用者或者是期于找到一种好的绘制流程图的开发者. 同时也是自己对其的一种巩固.

由于是系列的文章, 所以更新的可能会比较频繁, 您要是无意间刷到了且不是您所需要的还请谅解😊.

不求赞👍不求心❤️. 只希望能对你有一点小小的帮助.

事件篇

上一章节我们介绍了利用bpmn.js与后台进行交互, 要是对bpmn.js不了解的小伙请移步:

《全网最详bpmn.js教材-http请求篇》

这一章节要讲解是关于bpmn.js的一些事件, 通过学习此章节你可以学习到:

监听modeler并绑定事件

很多时候你期望的是在用户在进行不同操作的时候能够监听到他操作的是什么, 从而做想要做的事情.

是进行了shape的新增还是进行了线的新增.

比如如下的一些监听事件:

  • shape.added 新增一个shape之后触发;
  • shape.move.end 移动完一个shape之后触发;
  • shape.removed 删除一个shape之后触发;

继续在项目案例bpmn-vue-basic的基础上创建一个event.vue文件:

并在success()函数中添加上监听事件的函数:

  1. // event.vue
  2. <script>
  3. ...
  4. success () {
  5. this.addModelerListener()
  6. },
  7. // 监听 modeler
  8. addModelerListener() {
  9. const bpmnjs = this.bpmnModeler
  10. const that = this
  11. // 这里我是用了一个forEach给modeler上添加要绑定的事件
  12. const events = ['shape.added', 'shape.move.end', 'shape.removed', 'connect.end', 'connect.move']
  13. events.forEach(function(event) {
  14. that.bpmnModeler.on(event, e => {
  15. console.log(event, e)
  16. var elementRegistry = bpmnjs.get('elementRegistry')
  17. var shape = e.element ? elementRegistry.get(e.element.id) : e.shape
  18. console.log(shape)
  19. })
  20. })
  21. },

如图所示, 在这里你就可以获取到相关节点的所有信息了:

img1

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

其实具体有哪些事件我在官网上都没有找到说明, 以上只是我在查找到bpmn.io/diagram.js/modeling文件之后, 取的一些我项目里有用到的事件.

监听element并绑定事件

上面介绍的是监听modeler并绑定事件, 可能你也需要监听用户点击图形上的element或者监听某个element改变:

  • element.click 点击元素;
  • element.changed 当元素发生改变的时候(包括新增、移动、删除元素)

继续在success()上添加监听事件:

  1. // event.vue
  2. <script>
  3. ...
  4. success () {
  5. ...
  6. this.addEventBusListener()
  7. },
  8. addEventBusListener () {
  9. let that = this
  10. const eventBus = this.bpmnModeler.get('eventBus') // 需要使用eventBus
  11. const eventTypes = ['element.click', 'element.changed'] // 需要监听的事件集合
  12. eventTypes.forEach(function(eventType) {
  13. eventBus.on(eventType, function(e) {
  14. console.log(e)
  15. })
  16. })
  17. }
  18. </script>

配置好addEventBusListener()函数后, 在进行元素的点击、新增、移动、删除的时候都能监听到了.

但是有一点很不好, 你在点击“画布”的时候, 也就是根元素也可能会触发此事件, 我们一般都不希望此时会触发, 因此我们可以在on回调中添加一些判断, 来避免掉不需要的情况:

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

此时我们可以把监听到返回的节点信息打印出来看看:

img2

如上图, 它会打印出该节点的Shape信息和DOM信息等, 但我们可能只关注于Shape信息(也就是该节点的id、type等等信息), 此时我们可以使用elementRegistry来获取Shape信息:

  1. eventBus.on(eventType, function(e) {
  2. if (!e || e.element.type == 'bpmn:Process') return // 这里我的根元素是bpmn:Process
  3. console.log(e)
  4. var elementRegistry = this.bpmnModeler.get('elementRegistry')
  5. var shape = elementRegistry.get(e.element.id) // 传递id进去
  6. console.log(shape) // {Shape}
  7. console.log(e.element) // {Shape}
  8. console.log(JSON.stringify(shape)===JSON.stringify(e.element)) // true
  9. })

或者你也可以直接就用e.element获取到Shape的信息, 我比较了一下它们两是一样的. 但是官方是推荐使用elementRegistry的方式.

通过监听事件判断操作方式

上面我们已经介绍了modelerelement的监听绑定方式, 在事件应用中, 你更多的需要知道用户要进行什么操作, 好写对应的业务逻辑.

这里我就以我工作中要用到的场景为案例进行讲解.

  • 新增了shape
  • 新增了线(connection)
  • 删除了shape和connection
  • 移动了shape和线
  1. // event.vue
  2. ...
  3. success () {
  4. this.addModelerListener()
  5. this.addEventBusListener()
  6. },
  7. // 添加绑定事件
  8. addBpmnListener () {
  9. const that = this
  10. // 获取a标签dom节点
  11. const downloadLink = this.$refs.saveDiagram
  12. const downloadSvgLink = this.$refs.saveSvg
  13. // 给图绑定事件,当图有发生改变就会触发这个事件
  14. this.bpmnModeler.on('commandStack.changed', function () {
  15. that.saveSVG(function(err, svg) {
  16. that.setEncoded(downloadSvgLink, 'diagram.svg', err ? null : svg)
  17. })
  18. that.saveDiagram(function(err, xml) {
  19. that.setEncoded(downloadLink, 'diagram.bpmn', err ? null : xml)
  20. })
  21. })
  22. },
  23. addModelerListener() {
  24. // 监听 modeler
  25. const bpmnjs = this.bpmnModeler
  26. const that = this
  27. // 'shape.removed', 'connect.end', 'connect.move'
  28. const events = ['shape.added', 'shape.move.end', 'shape.removed']
  29. events.forEach(function(event) {
  30. that.bpmnModeler.on(event, e => {
  31. var elementRegistry = bpmnjs.get('elementRegistry')
  32. var shape = e.element ? elementRegistry.get(e.element.id) : e.shape
  33. // console.log(shape)
  34. if (event === 'shape.added') {
  35. console.log('新增了shape')
  36. } else if (event === 'shape.move.end') {
  37. console.log('移动了shape')
  38. } else if (event === 'shape.removed') {
  39. console.log('删除了shape')
  40. }
  41. })
  42. })
  43. },
  44. addEventBusListener() {
  45. // 监听 element
  46. let that = this
  47. const eventBus = this.bpmnModeler.get('eventBus')
  48. const eventTypes = ['element.click', 'element.changed']
  49. eventTypes.forEach(function(eventType) {
  50. eventBus.on(eventType, function(e) {
  51. if (!e || e.element.type == 'bpmn:Process') return
  52. if (eventType === 'element.changed') {
  53. that.elementChanged(eventType, e)
  54. } else if (eventType === 'element.click') {
  55. console.log('点击了element')
  56. }
  57. })
  58. })
  59. },
  60. elementChanged(eventType, e) {
  61. var shape = this.getShape(e.element.id)
  62. if (!shape) {
  63. // 若是shape为null则表示删除, 无论是shape还是connect删除都调用此处
  64. console.log('无效的shape')
  65. // 由于上面已经用 shape.removed 检测了shape的删除, 因此这里只判断是否是线
  66. if (this.isSequenceFlow(shape.type)) {
  67. console.log('删除了线')
  68. }
  69. }
  70. if (!this.isInvalid(shape.type)) {
  71. if (this.isSequenceFlow(shape.type)) {
  72. console.log('改变了线')
  73. }
  74. }
  75. },
  76. getShape(id) {
  77. var elementRegistry = this.bpmnModeler.get('elementRegistry')
  78. return elementRegistry.get(id)
  79. },
  80. isInvalid (param) { // 判断是否是无效的值
  81. return param === null || param === undefined || param === ''
  82. },
  83. isSequenceFlow (type) { // 判断是否是线
  84. return type === 'bpmn:SequenceFlow'
  85. }

案例Git地址: LinDaiDai-bpmn.js案例event.vue 喜欢的小伙伴请给个Star🌟呀, 谢谢😊

后语

系列全部目录请查看此处: 《全网最详bpmn.js教材目录》

最后, 如果你也对bpmn.js 感兴趣可以进我们的bpmn.js交流群👇👇👇, 共同学习, 共同进步.

关注霖呆呆(LinDaiDai)的公众号, 选择 其它 菜单中的 bpmn.js群 即可😊.

LinDaiDai公众号二维码.jpg