【Bpmn.js】activiti process editor


Preface

流程编辑器
What is Process Editor:
Process Editor is a tool for creating, editing and managing process diagrams. It provides a visual interface that enables users to graphically define and configure the various steps, conditions, and relationships between processes.
Process editors are commonly used in areas such as business process management, workflow management, and business process automation. It helps users design and manage complex processes easily without writing a lot of code. By dragging and connecting different graphic elements, users can define the starting point and end point of the process, process branches, conditional judgments, task execution, etc.
Process editors also usually provide some additional functions, such as version control, permission management, import and export of process templates, etc. It can be integrated with other systems to apply process definitions to actual business scenarios.
The purpose of the process editor is to simplify the process of process design and management, improve work efficiency, and ensure the correctness and consistency of the process. It has wide applications in many fields, including project management, workflow automation, e-commerce, etc.

There are many different kinds of process editors, each with different features and uses. Here are some common types of process editors:

  1. Workflow Editors: Used to create and manage workflows, including defining tasks, process branches, conditions, and workflow execution sequences.
  2. Business Process Management (BPM) Editors (Business Process Management Editors): used to design and manage business processes, supporting complex process modeling and process optimization.
  3. UML (Unified Modeling Language) Editors (UML Editors): Used to create and edit UML diagrams, including class diagrams, sequence diagrams, use case diagrams, etc., for the design and modeling of software systems.
  4. Data Flow Editors: Used to create and manage data processes, including flowcharts for data input, processing, and output.
  5. Network Topology Editors: Used to design and manage network topology, including the configuration of nodes, connections, and network devices.
  6. Flowchart Editors: Used to create and edit flowcharts, including graphical representations of process steps, conditions, and process controls.
  7. Rule Engine Editors: Used to create and manage rule engines, including defining rules, conditions, and rule execution order.
    These are just some of the common types of process editors, there are actually many other types of process editors, each with its own specific purpose and functionality. Which editor to use depends on the specific needs and application scenarios.

I am using the business process editor (bpmn.js)


1. What is bpmn.js?

1.bpmn.js Introduction

bpmn.js is a JavaScript library for rendering and editing BPMN (Business Process Model and Notation) flowcharts in web applications. It provides a set of powerful APIs and tools that can help developers integrate the display and editing functions of BPMN flowcharts in applications.
Using bpmn.js, developers can embed BPMN flowcharts into their applications and interact with other components. It supports the creation, modification and deletion of BPMN elements, such as tasks, gateways, events, etc., and provides a rich set of events and callback functions so that developers can perform corresponding processing based on user operations.
bpmn.js also supports importing and exporting BPMN process diagrams to XML format for interaction and sharing with other BPMN tools. It also provides rich style and theme options that allow developers to customize the appearance and style of flowcharts.
Overall, bpmn.js is a powerful tool that can help developers implement the display and editing functions of BPMN flowcharts in web applications and integrate them with other components.
Official website: https://bpmn.io/.

2. Why choose bpmn.js

The process editor officially supported by activiti is ActivitiModeler. It has now stopped maintenance and it is not very user-friendly if you need to separate the front-end and back-end using the process editor.

There are several reasons for choosing to use bpmn.js:

  1. Complete BPMN support: bpmn.js is a library specifically designed to handle BPMN process diagrams. It provides complete BPMN specification support, including various BPMN elements, events, and process controls. This makes it ideal for building and managing BPMN process diagrams.
  2. Powerful functionality and flexibility: bpmn.js provides a rich API and tools that allow developers to easily create, modify and delete BPMN elements. It also supports importing and exporting BPMN process diagrams for interaction and sharing with other BPMN tools. In addition, bpmn.js also provides the option of custom styles and themes, allowing developers to customize the appearance and style of the flowchart according to their needs.
  3. Cross-platform and easy to integrate: bpmn.js is a JavaScript-based library that can be used in a variety of web applications. It is compatible with modern web technologies and frameworks such as React, Angular, and Vue.js, and integrates seamlessly with other components and tools. This makes it ideal for adding BPMN flowchart display and editing capabilities to existing applications.
  4. Community support and activity: bpmn.js has huge open source community support and is maintained and updated by well-known companies such as Camunda. This means it has an active community of developers who can offer help, solve problems, and share experiences.
    All in all, choosing to use bpmn.js allows developers to easily implement the display and editing functions of BPMN flowcharts in web applications, with powerful functionality, flexibility, and cross-platform features.

2. Integrate Bpmn.js in vue

1. Download dependencies

The simplest way to use it: direct use CDNwill bpmn.jsbe introduced into the code

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>BPMNJS</title>
    <!--CDN加速-->
    <script src="https://unpkg.com/[email protected]/dist/bpmn-viewer.development.js"></script><!--引入一个简单的xml字符串-->
    <script src="./xmlStr.js"></script>
    <style>
        #canvas {
      
      
            height: 400px;
        }
    </style>
</head>

<body>
    <div id="canvas"></div>
    <script>
        var bpmnJS = new BpmnJS({
      
      
            container: '#canvas'
        });
        bpmnJS.importXML(xmlStr, err => {
      
      
            if (!err) {
      
      
                // 让图能自适应屏幕
                var canvas = bpmnJS.get('canvas')
                canvas.zoom('fit-viewport')
            } else {
      
      
                console.log('something went wrong:', err);
            }
        });
    </script>
</body>
</html>

(The above xmlStr.jsis a customized file, which contains the XML about the process. It can also be defined directly in the vue file without reference.)
As shown in the above case, we use CDNacceleration to introduce it directly bpmn.js, and then specify a container locally (that is, idfor ), and then use the canvasprovided method to parse the string and generate the corresponding workflow diagram.divbpmn.jsimportXMLxml

Run the code:
Insert image description here
The usage method provided above is the most basic method. It only displays the picture. You cannot draw or operate it yourself. Therefore, if you use it more at work, you should use npm to install it into the project. We can use Install with the following command:

Use npmdownload

npm install --save bpmn-js

Note: If introduced into an existing project, startup may fail due to version issues. It is best to check the relative version. The
version I am using here is:

"dependencies": {
    
    
    "bpmn-js": "^6.2.1",
    "bpmn-js-properties-panel": "^0.33.1",
    "camunda-bpmn-moddle": "^4.3.0",
    "core-js": "^3.4.4",
    "houtaroy-bpmn-js-properties-panel-activiti": "0.0.1",
    "svg-sprite-loader": "3.7.3",
  },

2.Introduce styles

After installing the dependencies, main.jsintroduce the style into the file:

// bpmn 相关依赖
import 'bpmn-js/dist/assets/diagram-js.css'
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css'
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'

// 左边工具栏以及编辑节点的样式
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css'

Create a new bpmn.vuepage and write htmlcode

<template>
  <div id="app">
    <div class="container">
      <!-- 创建一个canvas画布 npmn-js是通过canvas实现绘图的,并设置ref让vue获取到element -->
      <div class="bpmn-container">
        <div class="bpmn-canvas" ref="canvas"></div>
        <!-- 工具栏显示的地方 -->
        <div class="bpmn-js-properties-panel" id="js-properties-panel"></div>
      </div>

      <!-- 把操作按钮写在这里面 -->
      <div class="action">
        <el-button icon="el-icon-download" @click="downloadBpmn" title="下载流程文件"></el-button>
        <el-button icon="el-icon-picture" @click="downloadSvg" title="下载流程图"></el-button>
        <el-button type="success" icon="el-icon-check" circle title="保存修改" @click="editModel"></el-button>
        <a hidden ref="downloadLink"></a>
      </div>
    </div>
  </div>
</template>

write jscode

<script>
import BpmnModeler from 'bpmn-js/lib/Modeler'
// 工具栏相关
// import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'
import propertiesPanelModule from 'bpmn-js-properties-panel'
// import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda'
import activitiModdleDescriptor from './activiti.json'
// 引入
import propertiesProviderModule from 'houtaroy-bpmn-js-properties-panel-activiti/lib/provider/activiti'

// 汉化
import customTranslate from './customTranslate.js'

export default {
    
    
  data () {
    
    
    return {
    
    
      modelId: '',
      bpmnModeler: null,
      canvas: null,
      bpmnTemplate: ``
    }
  },
  methods: {
    
    
    newDiagram () {
    
    
      this.createNewDiagram(this.bpmnTemplate)
    },

	// 下载bpmn xml文件
    downloadBpmn () {
    
    
      const that = this
      that.bpmnModeler.saveXML({
    
     format: true }, (err, xml) => {
    
    
        if (!err) {
    
    
          // 获取文件名
          const name = `${
      
      that.getFilename(xml)}.bpmn20.xml`
          // 将文件名以及数据交给下载方法
          that.download({
    
     name: name, data: xml })
        }
      })
    },

	// 下载bpmn.svg流程图片
    downloadSvg () {
    
    
      const that = this
      that.bpmnModeler.saveXML({
    
     format: true }, (err, date) => {
    
    
        if (!err) {
    
    
          // 获取文件名
          const name = `${
      
      that.getFilename(date)}.svg`
          // 从建模器画布中提取svg图形标签
          let context = ''
          const djsGroupAll = that.$refs.canvas.querySelectorAll('.djs-group')
          for (let item of djsGroupAll) {
    
    
            context += item.innerHTML
          }
          // 获取svg的基本数据,长宽高
          const viewport = that.$refs.canvas
            .querySelector('.viewport')
            .getBBox()

          // 将标签和数据拼接成一个完整正常的svg图形
          const svg = `
            <svg
              xmlns='http://www.w3.org/2000/svg'
              xmlns:xlink='http://www.w3.org/1999/xlink'
              width='${
      
      viewport.width}'
              height='${
      
      viewport.height}'
              viewBox='${
      
      viewport.x} ${
      
      viewport.y} ${
      
      viewport.width} ${
      
      viewport.height}'
              version='1.1'
              >
              ${
      
      context}
            </svg>
          `
          // 将文件名以及数据交给下载方法
          that.download({
    
     name: name, data: svg })
        }
      })
    },

	// 获取文件名
    getFilename (xml) {
    
    
      const regex = /<process.*?id="(.*?)"/
      const match = xml.match(regex)
      if (match) {
    
    
        return match[1]
      }
      return null
    },

    // 编辑模型
    editModel () {
    
    
      const that = this
      that.bpmnModeler.saveXML({
    
     format: true }, (err, xml) => {
    
    
        if (!err) {
    
    
            // 获取文件名
          const name = `${
      
      that.getFilename(xml)}`
          // // 从建模器画布中提取svg图形标签
          // let context = ''
          // const djsGroupAll = this.$refs.canvas.querySelectorAll('.djs-group')
          // for (let item of djsGroupAll) {
    
    
          //   context += item.innerHTML
          // }
          // // 获取svg的基本数据,长宽高
          // const viewport = this.$refs.canvas
          //   .querySelector('.viewport')
          //   .getBBox()
          // // 将标签和数据拼接成一个完整正常的svg图形
          // const svg = `
          //   <svg
          //     xmlns='http://www.w3.org/2000/svg'
          //     xmlns:xlink='http://www.w3.org/1999/xlink'
          //     width='${viewport.width}'
          //     height='${viewport.height}'
          //     viewBox='${viewport.x} ${viewport.y} ${viewport.width} ${viewport.height}'
          //     version='1.1'
          //     >
          //     ${context}
          //   </svg>
          // `
          that.$http({
    
    
            url: '',
            method: 'post',
            data: that.$http.adornData({
    
    
              modelId: that.modelId,
              name: name,
              bpmnXml: xml,
              svg: '',
              descritpion: ''
            })
          }).then(({
     
      data }) => {
    
    
            that.$message({
    
    
              message: that.$i18n.t('publics.operation'),
              type: 'success',
              duration: 1500,
              onClose: () => {
    
    }
            })
          })
        }
      })
    },
    // 获取流程图数据
    getModel () {
    
    
      const that = this
      this.$http({
    
    
        url: '',
        method: 'get',
        params: this.$http.adornParams({
    
    
          modelId: that.modelId
        })
      }).then(({
     
      data }) => {
    
    
        that.bpmnTemplate = '`' + data + '`'
        this.$message({
    
    
          message: this.$i18n.t('publics.operation'),
          type: 'success',
          duration: 1500,
          onClose: () => {
    
    
            that.init()
          }
        })
      })
    },

    download ({
    
     name = 'diagram.bpmn', data }) {
    
    
      // 这里就获取到了之前设置的隐藏链接
      const downloadLink = this.$refs.downloadLink
      // 把数据转换为URI,下载要用到的
      const encodedData = encodeURIComponent(data)
      if (data) {
    
    
        // 将数据给到链接
        downloadLink.href =
          'data:application/bpmn20-xml;charset=UTF-8,' + encodedData
        // 设置文件名
        downloadLink.download = name
        // 触发点击事件开始下载
        downloadLink.click()
      }
    },

    async init () {
    
    
      // 获取画布 element
      const that = this
      that.canvas = that.$refs.canvas
      // 将汉化包装成一个模块
      const customTranslateModule = {
    
    
        translate: ['value', customTranslate]
      }
      // 创建Bpmn对象
      that.bpmnModeler = new BpmnModeler({
    
    
        // 设置bpmn的绘图容器为上门获取的画布 element
        container: that.canvas,
        // 加入工具栏支持
        propertiesPanel: {
    
    
          parent: '#js-properties-panel'
        },
        additionalModules: [
          // 工具栏模块
          propertiesProviderModule,
          propertiesPanelModule,
          // 汉化模块
          customTranslateModule
        ],
        moddleExtensions: {
    
    
          activiti: activitiModdleDescriptor
        }
      })
      await that.createNewDiagram(that.bpmnTemplate)
    },

    clearBpmn () {
    
    
      this.bpmnModeler.clear()
    },

    async createNewDiagram (bpmnTemplate) {
    
    
      const that = this
      // 将字符串转换成图显示出来;
      this.bpmnModeler.importXML(bpmnTemplate, err => {
    
    
        if (err) {
    
    
          that.$Message.error('打开模型出错,请确认该模型符合Bpmn2.0规范')
        } else {
    
    
          // 让图能自适应屏幕
          var canvas = that.bpmnModeler.get('canvas')
          canvas.zoom('fit-viewport')
        }
      })
    }
  },
  created () {
    
    
    this.getModel()
    // // 删除 bpmn logo  bpmn.io官方要求不给删或者隐藏,否则侵权   内部使用
    // const bjsIoLogo = document.querySelector('.bjs-powered-by')
    // while (bjsIoLogo.firstChild) {
    
    
    //   bjsIoLogo.removeChild(bjsIoLogo.firstChild)
    // }
  },
  beforeDestroy () {
    
    
    this.clearBpmn()
  }
}
</script>

writing stylystyles


<style>
.bpmn-container {
    
    
  width: 100%;
  height: 100vh;
  display: flex;
}

.bpmn-canvas {
    
    
  width: calc(100% - 300px);
  height: 100vh;
}

.bpmn-js-properties-panel {
    
    
  width: 320px;
  height: inherit;
  overflow-y: auto;
}

.action {
    
    
  position: fixed;
  bottom: 40px;
  left: 800px;
  display: flex;
}
</style>

It should be noted here that in my code, I commented out when importimporting camunda, because Bpmn.js supports it by default camunda, and my backend uses it activiti, and the two are incompatible. So it needs to be discarded camundaand replaced withactiviti

Download activitiplugin

 "houtaroy-bpmn-js-properties-panel-activiti": "0.0.1",

Replace the corresponding imported camunda
Insert image description here
Insert image description here
Chinese package: customTranslate.js+translationsGerman

import translations from './translationsGerman'

export default function customTranslate (template, replacements) {
    
    
  replacements = replacements || {
    
    }

  // Translate
  template = translations[template] || template

  // Replace
  return template.replace(/{([^}]+)}/g, function (_, key) {
    
    
    var str = replacements[key]
    if (
      translations[replacements[key]] !== null &&
      translations[replacements[key]] !== 'undefined'
    ) {
    
    
      str = translations[replacements[key]]
    }
    return str || '{' + key + '}'
  })
}
export default {
    
    
  // Labels
  'Activate the global connect tool': '激活全局连接工具',
  'Append {type}': '追加 {type}',
  'Append EndEvent': '追加 结束事件 ',
  'Append Task': '追加 任务',
  'Append Gateway': '追加 网关',
  'Append Intermediate/Boundary Event': '追加 中间/边界 事件',
  'Add Lane above': '在上面添加道',
  'Divide into two Lanes': '分割成两个道',
  'Divide into three Lanes': '分割成三个道',
  'Add Lane below': '在下面添加道',
  'Append compensation activity': '追加补偿活动',
  'Change type': '修改类型',
  'Connect using Association': '使用关联连接',
  'Connect using Sequence/MessageFlow or Association': '使用顺序/消息流或者关联连接',
  'Connect using DataInputAssociation': '使用数据输入关联连接',
  'Remove': '移除',
  'Activate the hand tool': '激活抓手工具',
  'Activate the lasso tool': '激活套索工具',
  'Activate the create/remove space tool': '激活创建/删除空间工具',
  'Create expanded SubProcess': '创建扩展子过程',
  'Create IntermediateThrowEvent/BoundaryEvent': '创建中间抛出事件/边界事件',
  'Create Pool/Participant': '创建池/参与者',
  'Parallel Multi Instance': '并行多重事件',
  'Sequential Multi Instance': '时序多重事件',
  'DataObjectReference': '数据对象参考',
  'DataStoreReference': '数据存储参考',
  'Loop': '循环',
  } // 这里只是部分的汉化,多的就不写出来了,如果有需要的可以去网上找找有很多

Then there is this that is essential for activiti.jsonreplacement . You can take a look at the meta example.activiti

{
    
    
    "name": "Activiti",
    "uri": "http://activiti.org/bpmn",
    "prefix": "activiti",
    "xml": {
    
    
      "tagAlias": "lowerCase"
    },
    "associations": [],
    "types": [
      {
    
    
        "name": "Process",
        "isAbstract": true,
        "extends": [
          "bpmn:Process"
        ],
        "properties": [
          {
    
    
            "name": "diagramRelationId",
            "isAttr": true,
            "type": "String"
          }
        ]
      },
      {
    
    
        "name": "InOutBinding",
        "superClass": [
          "Element"
        ], // 就是将camunda用activiti替换掉,还有挺多的无法全部展示

After the introduction is complete, you can take a look at what the process editor looks like.
Insert image description here
At this point, the complete bpmn.jsintroduction is completed. Now let’s talk about the events and listeners of bpmn.js.


3. bpmn.js event

Here we mainly explain bpmn.jssome of the events. Through this chapter you can learn:

  • Listen modelerand bind events
  • Listen elementand bind events
  • Determine the operation mode by listening to events

1. Listen to the modeler and bind events

Sometimes what we hope is to be able to monitor what the user is doing when he performs different operations, so that he can do what he wants to do.

Is it shapea new addition or a new line?

For example, some monitoring events are as follows:

  • shape.addedTriggered after adding a new one shape;
  • shape.move.endTriggered after moving one shape;
  • shape.removedTriggered after deleting one shape;

Continue to bpmn.vuecreate a event.vuefile based on the project case:

// event.vue
<script>
...
success () {
    
    
  this.addModelerListener()
},
// 监听 modeler
addModelerListener() {
    
    
  const bpmnjs = this.bpmnModeler
  const that = this
  // 这里我是用了一个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)
    })
  })
},

Then you can get information about the relevant nodes
Insert image description here

In fact, I didn't find any description of the specific events on the official website. The above are just some events that I picked up after finding the bpmn.io/diagram.js/... file and used them in my project.

2. Listen to element and bind events

The above introduction is to monitor the modeler and bind events. You may also need to monitor the user clicking on an element on the graph or monitor the change of an element:

  • element.click click element;
  • element.changed When the element changes (including adding, moving, and deleting elements)

Continue success()to add listening events on:

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

After configuring addEventBusListener()the function, you can monitor the click, add, move, and delete of elements.
But there is a bad thing. When you click on the "canvas", the root element may also trigger this event. , we generally don’t want it to be triggered at this time, so we can onadd some judgments in the callback to avoid unnecessary situations:

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

At this time, we can print out the node information returned by monitoring:
Insert image description here
as shown above, it will print out the node's Shapeinformation and DOMinformation, etc., but we may only focus on Shapethe information (that is, the node's id, typeetc.), At this point we can use elementRegistryto obtain Shapeinformation:

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进去
  console.log(shape) // {Shape}
  console.log(e.element) // {Shape}
  console.log(JSON.stringify(shape)===JSON.stringify(e.element)) // true
})

Or you can use e.elementthe obtained Shapeinformation directly. I compared them and they are the same. But the official elementRegistrymethod is recommended.

3. Determine the operation mode by monitoring events

We have introduced the monitoring binding methods of modelerand above element. In event applications, you need to know more about what operations the user wants to perform so that you can write the corresponding business logic.

Here we will explain it using scenarios used in work as examples.

  • Added shape
  • Added a new line (connection)
  • deleted shape and connection
  • Moved shapes and lines
// event.vue
    ...
    success () {
    
    
      this.addModelerListener()
      this.addEventBusListener()
    },
    // 添加绑定事件
    addBpmnListener () {
    
    
      const that = this
      // 获取a标签dom节点
      const downloadLink = this.$refs.saveDiagram
      const downloadSvgLink = this.$refs.saveSvg
        // 给图绑定事件,当图有发生改变就会触发这个事件
      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() {
    
    
      // 监听 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('移动了shape')
          } else if (event === 'shape.removed') {
    
    
            console.log('删除了shape')
          }
        })
      })
    },
    addEventBusListener() {
    
    
      // 监听 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('点击了element')
          }
        })
      })
    },
    elementChanged(eventType, e) {
    
    
      var shape = this.getShape(e.element.id)
      if (!shape) {
    
    
        // 若是shape为null则表示删除, 无论是shape还是connect删除都调用此处
        console.log('无效的shape')
        // 由于上面已经用 shape.removed 检测了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) {
    
     // 判断是否是无效的值
      return param === null || param === undefined || param === ''
    },
    isSequenceFlow (type) {
    
     // 判断是否是线
      return type === 'bpmn:SequenceFlow'
    }

To learn more about bpmn.js, you can read what this guy wrote:
Recommended series:
"The most detailed bpmn.js textbook on the Internet - Basics"
"The most detailed bpmn.js textbook on the Internet - http request"
" The most detailed bpmn.js textbook on the entire web - renderer chapter"
"The most detailed bpmn.js textbook on the entire web - contextPad chapter"
"The most detailed bpmn.js textbook on the entire web - editing and deleting nodes"

Guess you like

Origin blog.csdn.net/m0_71621983/article/details/131824421