【Bpmn.js】activiti 流程编辑器

这篇具有很好参考价值的文章主要介绍了【Bpmn.js】activiti 流程编辑器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

流程编辑器
什么是流程编辑器:
流程编辑器是一种用于创建、编辑和管理流程图的工具。它提供了一个可视化的界面,使用户能够以图形化的方式定义和配置流程的各个步骤、条件和流程间的关系。
流程编辑器通常用于业务流程管理、工作流程管理和业务流程自动化等领域。它可以帮助用户轻松地设计和管理复杂的流程,而无需编写大量的代码。通过拖拽和连接不同的图形元素,用户可以定义流程的起始点、结束点、流程分支、条件判断、任务执行等。
流程编辑器还通常提供了一些额外的功能,如版本控制、权限管理、流程模板的导入和导出等。它可以与其他系统集成,以便将流程定义应用于实际的业务场景中。
流程编辑器的目的是简化流程设计和管理的过程,提高工作效率,并确保流程的正确性和一致性。它在许多领域中都有广泛的应用,包括项目管理、工作流程自动化、电子商务等。

流程编辑器有多种不同的种类,每种都具有不同的特点和用途。以下是一些常见的流程编辑器种类:

  1. 工作流程编辑器(Workflow Editors):用于创建和管理工作流程,包括定义任务、流程分支、条件和工作流程的执行顺序等。
  2. 业务流程管理(BPM)编辑器(Business Process Management Editors):用于设计和管理业务流程,支持复杂的流程建模和流程优化。
  3. UML(统一建模语言)编辑器(UML Editors):用于创建和编辑UML图,包括类图、时序图、用例图等,用于软件系统的设计和建模。
  4. 数据流程编辑器(Data Flow Editors):用于创建和管理数据流程,包括数据输入、处理和输出的流程图。
  5. 网络拓扑编辑器(Network Topology Editors):用于设计和管理网络拓扑结构,包括节点、连接和网络设备的配置。
  6. 流程图编辑器(Flowchart Editors):用于创建和编辑流程图,包括流程的各个步骤、条件和流程控制的图形表示。
  7. 规则引擎编辑器(Rule Engine Editors):用于创建和管理规则引擎,包括定义规则、条件和规则执行顺序等。
    这只是一些常见的流程编辑器种类,实际上还有许多其他类型的流程编辑器,每种都有其特定的用途和功能。具体使用哪种编辑器取决于具体的需求和应用场景。

我用的是业务流程编辑器(bpmn.js)


一、bpmn.js是什么?

1.bpmn.js简介

bpmn.js是一个用于在Web应用程序中渲染和编辑BPMN(Business Process Model and Notation)流程图的JavaScript库。它提供了一套功能强大的API和工具,可以帮助开发人员在应用程序中集成BPMN流程图的显示和编辑功能。
使用bpmn.js,开发人员可以将BPMN流程图嵌入到他们的应用程序中,并与其它组件进行交互。它支持创建、修改和删除BPMN元素,如任务、网关、事件等,并提供了丰富的事件和回调函数,以便开发人员可以根据用户的操作进行相应的处理。
bpmn.js还支持将BPMN流程图导入和导出为XML格式,以便与其他BPMN工具进行交互和共享。它还提供了丰富的样式和主题选项,使开发人员可以自定义流程图的外观和样式。
总的来说,bpmn.js是一个强大的工具,可以帮助开发人员在Web应用程序中实现BPMN流程图的显示和编辑功能,并与其它组件进行集成。
官网:https://bpmn.io/.

2.为什么要选择bpmn.js

activiti 官方支持的流程编辑器是ActivitiModeler,现在已经停止维护而且如果需要前后端分离使用流程编辑器,并不是很友好。

选择使用bpmn.js有以下几个原因:

  1. 完整的BPMN支持:bpmn.js是一个专门用于处理BPMN流程图的库,它提供了完整的BPMN规范支持,包括各种BPMN元素、事件和流程控制等。这使得它成为构建和管理BPMN流程图的理想选择。
  2. 强大的功能和灵活性:bpmn.js提供了丰富的API和工具,使开发人员可以轻松地创建、修改和删除BPMN元素。它还支持导入和导出BPMN流程图,以便与其他BPMN工具进行交互和共享。此外,bpmn.js还提供了自定义样式和主题的选项,使开发人员可以根据需要自定义流程图的外观和样式。
  3. 跨平台和易于集成:bpmn.js是基于JavaScript的库,可以在各种Web应用程序中使用。它与现代Web技术和框架(如React、Angular和Vue.js)兼容,并且可以与其他组件和工具进行无缝集成。这使得它非常适合在现有的应用程序中添加BPMN流程图的显示和编辑功能。
  4. 社区支持和活跃度:bpmn.js拥有庞大的开源社区支持,并且由Camunda等知名公司进行维护和更新。这意味着它有一个活跃的开发者社区,可以提供帮助、解决问题并分享经验。
    总而言之,选择使用bpmn.js可以让开发人员轻松地在Web应用程序中实现BPMN流程图的显示和编辑功能,并且具有强大的功能、灵活性和跨平台的特点。

二、在vue中集成Bpmn.js

1.下载依赖

最简单的一种使用方式:直接使用CDNbpmn.js引入到代码中

<!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/bpmn-js@6.0.2/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>

(上面的xmlStr.js就是自定义的文件,里面放置了关于流程的xml,也可以不引用直接在vue文件中定义)
如上面的案例所示, 我们使用CDN加速直接引入bpmn.js, 然后本地指定一个容器(也就是idcanvas的那个div), 接着用bpmn.js提供的方法importXML就可以解析xml字符串生成对应的工作流图了。

运行代码:
bpmnjs,javascript,编辑器,前端,vue.js,java,程序人生,canva可画
上面提供的使用方式是一种最基本的方式,仅仅是将图展示出来,不能自己绘画也不能操作. 所以在工作中使用更多的还是采用npm安装到项目中使用. 我们可以使用以下命令进行安装:

使用npm下载

npm install --save bpmn-js

注意: 如果在已有项目引入,可能会因为版本问题导致启动失败,最好是看看相对于的版本
我这里使用的版本是:

"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.引入样式

安装好依赖后,在main.js文件中引入样式:

// 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'

新建一个bpmn.vue页面,编写html代码

<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>

编写js代码

<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>

编写styly样式


<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>

在这里需要注意我的代码中,在import导入camunda时我注释了,是因为Bpmn.js默认支持的是camunda,而我的后端使用的是activiti,两者是不兼容的。所以需要丢弃camunda,换成activiti

下载activiti 插件

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

更换对应引入的camunda
bpmnjs,javascript,编辑器,前端,vue.js,java,程序人生,canva可画
bpmnjs,javascript,编辑器,前端,vue.js,java,程序人生,canva可画
汉化包: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': '循环',
  } // 这里只是部分的汉化,多的就不写出来了,如果有需要的可以去网上找找有很多

然后还有activiti.json这个是更换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替换掉,还有挺多的无法全部展示

引入完成后就可以看看流程编辑器的样子。
bpmnjs,javascript,编辑器,前端,vue.js,java,程序人生,canva可画
到这里一个完整的bpmn.js就引入完成了,下面再讲讲bpmn.js的事件以及监听器吧


三,bpmn.js事件

这里主要是说明关于bpmn.js的一些事件, 通过此章节你可以了解到:

  • 监听modeler并绑定事件
  • 监听element并绑定事件
  • 通过监听事件判断操作方式

1,监听modeler并绑定事件

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

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

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

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

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

// 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)
    })
  })
},

然后就可以获取到相关节点的信息
bpmnjs,javascript,编辑器,前端,vue.js,java,程序人生,canva可画

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

2,监听element并绑定事件

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

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

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

// 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>

配置好addEventBusListener()函数后, 在进行元素的点击、新增、移动、删除的时候都能监听到了.
但是有一点很不好, 你在点击“画布”的时候, 也就是根元素也可能会触发此事件, 我们一般都不希望此时会触发, 因此我们可以在on回调中添加一些判断, 来避免掉不需要的情况:

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

此时我们可以把监听到返回的节点信息打印出来看看:
bpmnjs,javascript,编辑器,前端,vue.js,java,程序人生,canva可画
如上图, 它会打印出该节点的Shape信息和DOM信息等, 但我们可能只关注于Shape信息(也就是该节点的idtype等等信息), 此时我们可以使用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进去
  console.log(shape) // {Shape}
  console.log(e.element) // {Shape}
  console.log(JSON.stringify(shape)===JSON.stringify(e.element)) // true
})

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

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

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

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

  • 新增了shape
  • 新增了线(connection)
  • 删除了shape和connection
  • 移动了shape和线
// 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'
    }

更多关于bpmn.js的学习,可以看看这个大佬写的:
系列相关推荐:
《全网最详bpmn.js教材-基础篇》
《全网最详bpmn.js教材-http请求篇》
《全网最详bpmn.js教材-renderer篇》
《全网最详bpmn.js教材-contextPad篇》
《全网最详bpmn.js教材-编辑、删除节点篇》文章来源地址https://www.toymoban.com/news/detail-733474.html

到了这里,关于【Bpmn.js】activiti 流程编辑器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 流程图拖拽视觉编程-流程编辑器

    流程图拖拽视觉编程-流程编辑器

    目录 一、简介 二、流程编辑器-视图实现 三、参考资料 前期文章: 流程图拖拽视觉编程--概述_Jason~shen的博客-CSDN博客 本期内容: 本期将介绍流程编辑器模块的实现方法,效果图如下所示。该模块基于QT Graphics/View实现,由视图、自定义图元、图元管理器组成。 视图的功能是

    2024年02月05日
    浏览(98)
  • 基于drawio构建流程图编辑器

    drawio 是一款非常强大的开源在线的流程图编辑器,支持绘制各种形式的图表,提供了 Web 端与客户端支持,同时也支持多种资源类型的导出。 在我们平时写论文、文档时,为了更好地阐述具体的步骤和流程,我们经常会有绘制流程图的需求,这时我们可能会想到 Visio ,可能

    2024年02月10日
    浏览(8)
  • BPMNJS插件使用及汉化(Activiti绘制流程图插件)

    BPMNJS插件使用及汉化(Activiti绘制流程图插件)

    BPMNJS插件运行最重要的就是需要安装nodejs插件,这 一定要安装和测试好 。 主要是使用npm命令 1.1.1、下载nodejs 下载地址:https://nodejs.org/en  1.1.2、安装nodejs,傻瓜式安装 安装之后在安装目录下创建node_cache和node_global两个目录 如果目录已经存在可以不创建 node_cache:作为缓存路

    2024年02月14日
    浏览(9)
  • 使用JavaScript实现实时在线协作编辑器:从设计到实现

    随着Web技术的发展,实现在线协作编辑文档已经成为一种常见的需求。通过在线协作,多位用户可以同时编辑同一个文档,并实时看到其他用户的更改。这样的功能需要复杂的技术实现,包括数据同步、冲突解决和实时通信。本篇博客将带您深入了解如何使用JavaScript实现实时

    2024年01月18日
    浏览(14)
  • 使用 HTML、CSS 和 JavaScript 创建实时 Web 编辑器

    使用 HTML、CSS 和 JavaScript 创建实时 Web 编辑器

    在本文中,我们将创建一个实时网页编辑器。这是一个 Web 应用程序,允许我们在网页上编写 HTML 、 CSS 和 JavaScript 代码并实时查看结果。这是学习 Web 开发和测试代码片段的绝佳工具。我们将使用 iframe 元素来显示结果。 iframe 元素用于在当前 HTML 文档中嵌入另一个文档。 i

    2024年02月12日
    浏览(14)
  • 前端 富文本编辑器原理——从javascript、html、css开始入门

    前端 富文本编辑器原理——从javascript、html、css开始入门

    大家好,我是yma16,本文分享关于前端 富文本编辑器原理——从javascript、html、css开始。 富文本编辑器 富文本编辑器是指具有格式化文本和图像编辑功能的文本编辑器 参考文档:https://w3c.github.io/selection-api/#abstract 全局属性 contenteditable 是一个枚举属性,表示元素是否可被用

    2024年02月08日
    浏览(14)
  • camunda工作流实战项目(表单设计器+流程编辑器,零代码创建流程)

    camunda工作流实战项目(表单设计器+流程编辑器,零代码创建流程)

    基于ruoyi平台和camunda工作流开发而成,结合bpmn.js流程编辑器和vform表单设计器,实现常规流程零代码创建。 具备流程中心的能力,支持外部任务,可协调多个业务系统协同工作 具备SaaS平台的能力,支持多租户,各业务系统可作为租户,创建自己的流程,通过外部任务与自身

    2024年02月12日
    浏览(16)
  • 「AntV」X6图编辑器的应用——流程图实现

    「AntV」X6图编辑器的应用——流程图实现

    在线预览 源码 阮一峰:SVG图像入门 SVGtutorial 因为antv/x6是基于SVG的图编辑器,所以SVG的知识有必要了解下的 简介 可缩放矢量图形【基于图形】 全称:Scalable Vector Graphics 定义基于矢量的图形 基于XML语法 放大缩小不会失真 属于万维网标准 可以插入DOM,通过JavaScript和CSS来操作

    2024年02月09日
    浏览(41)
  • Svg Flow Editor 原生svg流程图编辑器(五)

    Svg Flow Editor 原生svg流程图编辑器(五)

    Svg Flow Editor 原生svg流程图编辑器(一) Svg Flow Editor 原生svg流程图编辑器(二) Svg Flow Editor 原生svg流程图编辑器(三) Svg Flow Editor 原生svg流程图编辑器(四) Svg Flow Editor 原生svg流程图编辑器(五)         对协同这块已经写了很多篇文章了,如果还是不了解,可以看看之

    2024年04月12日
    浏览(14)
  • 基于fabric.js的图片编辑器, 画布背景实现原理

    基于fabric.js的图片编辑器, 画布背景实现原理

    使用了element-plus提供的图片上传 el-upload 组件 图片上传支持两种元素,普通图片元素和背景图片元素,所以定义属性type进行区分 type为 Image 为普通图片, background 为背景 背景也是一张图片,使用fabric.Image创建图片元素 因为没有图片服务器所以把上传的文件转换了base64图片,作为f

    2024年02月02日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包