bpmn-js Edit 和Viewer(一)


BPMN

Web-based tooling for BPMN, DMN, CMMN, and Forms | bpmn.io

bpmn-js 是 BPMN 2.0 呈现工具包和 Web 建模器。它是用 JavaScript 编写的,将 BPMN 2.0 图表嵌入到现代浏览器中,并且不需要服务器后端。这样可以轻松将其嵌入到任何 Web 应用程序中。

该库以既可以查看器又可以是 Web 建模器的方式构建。使用 viewer 将 BPMN 2.0 嵌入到您的应用程序中,并用您的数据丰富它。使用 modeler 在应用程序内部创建 BPMN 2.0 图表。

了解内部之前了解外观:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u7Znmpcr-1603036717663)(./img/bpmnLayout.png)]

 一、Bpmn内部

bpmn-js 体系结构:bpmn-js 建立在两个重要的库之上:diagram-jsbpmn-moddle

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WH6B28kH-1603036717664)(./img/overview.svg)]

 diagram-js:使用diagram-js绘制图形和连线:它为我们提供了与这些图形元素进行交互的方法,以及诸如叠加层之类的其他工具,可以帮助用户构建功能强大的 BPMN 查看器。对于诸如建模之类的高级用例,它提供了上下文内容面板,调色板和重做/撤消之类的功能。

bpmn-moddle: BPMN 2.0标准中定义的 BPMN 2.0 元模型。它使我们能够读写 BPMN 2.0 架构兼容的 XML 文档,并访问在图上绘制的图形和连线背后的 BPMN 相关信息。


二、BPMN

BPMN(Business Process Modeling Notation)是由业务流程管理倡议组织BPMI(The Business Process Management Initiative)开发的一套标准的业务流程建模符号规范。其目的是为用户提供一套容易理解的标准符号,这些符号作为BPMN的基础元素,将业务流程建模简单化、图形化,将复杂的建模过程视觉化,让业务建模者、业务实施人员、管理监督人员对BPMN描述的业务流程都有一个更加清晰明了的了解。

BPMN的主要意义在于其作为一个标准,业务相关者都按照这个标准来绘制业务流程图,能够减少各方对于流程图的理解歧义,从而达到高效协作的目的

BPMN包含以下四类基本元素

  • 流对象(Flow Objects):包括事件、活动、网关,是BPMN中的核心元素
  • 连接对象(Connecting Objects):包括顺序流、消息流、关联
  • 泳道(Swimlanes):包括池和道两种类型
  • 人工信息(Artifacts):包括数据对象、组、注释

bpmn-js就是基于BPMN标准实现的一套渲染工具包和web建模器,以下系列文章将会介绍我的使用过程

1.基本使用

bpmn-js的使用非常简单,我们可以在VUE项目中使用,或者是直接在HTML文件中引入JS/CSS资源文件,就像下边这样

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <title>运维咖啡吧 - BPMNJS</title>

  <!-- 引入BPMN-JS的CSS文件 -->
  <link rel="stylesheet" href="https://unpkg.com/bpmn-js@7.3.0/dist/assets/diagram-js.css" />
  <link rel="stylesheet" href="https://unpkg.com/bpmn-js@7.3.0/dist/assets/bpmn-font/css/bpmn.css" />
</head>

<body>
  <div id="canvas" style="height:80vh;"></div>

  <!-- 引入BPMN-JS的JS文件 -->
  <script src="https://unpkg.com/bpmn-js@7.3.0/dist/bpmn-modeler.development.js"></script>
  <script>
    var diagramXML = `<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="sample-diagram" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
<bpmn2:process id="Process_1" isExecutable="false">
  <bpmn2:startEvent id="StartEvent_1" />
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
  <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
    <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
      <dc:Bounds x="192" y="82" width="36" height="36" />
    </bpmndi:BPMNShape>
  </bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>`

    var bpmnModeler = new BpmnJS({
      container: '#canvas'
    });

    bpmnModeler.importXML(diagramXML, function(err) {
      if (err) {
        return console.error('failed to load diagram', err);
      }
    });
  </script>
</body>

</html>

2.Bpmn+Vue

下载bpmn相关库

npm install bpmn-js --save

 安装完成后就可以开始使用bpmn-js了,首先要引入bpmn-js的CSS,修改src/main.js文件

//main.js文件
//bpmnjs
// 以下为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-js有两种模式:Modeler模式和Viewer模式,在Modeler模式下可以对流程图进行编辑,而Viewer模式则不能,仅作为展示用。

2.1 bpmn-modeler.vue:bpmn编辑器-Modeler模式

<template>
  <div class="containers"  ref="content">
    <div class="loading" v-if="loading">
      Loading...
    </div>
    <template v-else>
      <div class="canvas" ref="canvas"></div>
      <div id="js-properties-panel" class="panel"></div>
      <ul class="buttons">
        <li>
          <a ref="saveDiagram" href="javascript:" title="保存为bpmn">保存为bpmn</a>
        </li>
        <li>
          <a ref="saveSvg" href="javascript:" title="保存为svg">保存为svg</a>
        </li>
      </ul>
    </template>
  </div>
</template>

<script>
  //引入相关依赖
  import BpmnModeler from 'bpmn-js/lib/Modeler'
  import propertiesPanelModule from 'bpmn-js-properties-panel'
  import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'
  import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda'
  import { xmlStr } from './xmlStr'
  const fs = require('fs')
  import bpmnApi from "@/api/bpmn";
  export default {
    name: "bpmnEdit",
    components:{},
    data(){
      return{
        //  bpmn建模器
        bpmnModeler:null,
        container:null,
        canvas:null,
        loading:true,
        xmlUrl:"",
        defaultXmlStr:xmlStr,
      }
    },
    created() {
    },
    mounted() {
      this.init();
    },
    methods:{
      async init() {
        this.loading = true;
        this.xmlUrl = await this.getXmlUrl();
        this.loading = false;
        this.$nextTick(() => {
          this.initBpmn();
        })
      },
      getXmlUrl(){
        return new Promise(resolve => {
          setTimeout(()=>{
            const url = './diagram.bpmn';
            // const url = 'D:\\901工作\\talent_select\\admin-font\\src\\views\\bpmn\\diagram.bpmn';
            resolve(url);
          },1000)
        })
      },
      initBpmn(){
        // 获取到属性ref为“content”的dom节点
        // this.container = this.$refs.content
        // 获取到属性ref为“canvas”的dom节点
        const canvas = this.$refs.canvas
        // 建模
        this.bpmnModeler = new BpmnModeler({
          container: canvas,
          propertiesPanel: {
            parent: '#js-properties-panel'
          },
          additionalModules: [
            // 左边工具栏以及节点
            propertiesProviderModule,
            // 右边的工具栏
            propertiesPanelModule,
          ],
          moddleExtensions: {
            camunda: camundaModdleDescriptor
          }
        })
        console.log(this.bpmnModeler)
        this.createNewDiagram()
      },

    async createNewDiagram() {
        const that = this;
        let bpmnXmlStr = "";
        if (this.xmlUrl === '') {
          bpmnXmlStr = this.defaultXmlStr
          this.transformCanvas(bpmnXmlStr);
        } else {
          let res = require('./diagram.bpmn').default
          bpmnXmlStr = res;
          this.transformCanvas(bpmnXmlStr);
        }
      },
      transformCanvas(bpmnXmlStr){
        this.bpmnModeler.importXML(bpmnXmlStr, (err) => {
          if (err) {
            console.error(err)
          }
          else {
            // 这里是成功之后的回调, 可以在这里做一系列事情
            this.success()
          }
          var canvas = this.bpmnModeler.get("canvas");
          canvas.zoom("fit-viewport")
        })

      },
      success(){
        console.log('创建成功!')
        this.addBpmnListener()
        this.addModelerListener()
      },
      //添加绑定事件
      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(){
        // 监听 element
        const bpmnjs = this.bpmnModeler
        const that = this
        // 'shape.removed'
        const events = ['shape.added', 'shape.move.end', 'connect.end', 'connection.create', 'connection.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)
          })
        })
      },
      // 下载为SVG格式,done是个函数,调用的时候传入的
      saveSVG(done) {
        // 把传入的done再传给bpmn原型的saveSVG函数调用
        this.bpmnModeler.saveSVG(done)
      },
      saveDiagram(done) {
        // 把传入的done再传给bpmn原型的saveXML函数调用
        this.bpmnModeler.saveXML({ format: true }, function(err, xml) {
          done(err, xml)
        })
      },
      // 当图发生改变的时候会调用这个函数,这个data就是图的xml
      setEncoded(link, name, data) {
        // 把xml转换为URI,下载要用到的
        const encodedData = encodeURIComponent(data)
        // 下载图的具体操作,改变a的属性,className令a标签可点击,href令能下载,download是下载的文件的名字
        console.log(link, name, data)
        let xmlFile = new File([data], 'test.bpmn')
        console.log(xmlFile)
        if (data) {
          link.className = 'active'
          link.href = 'data:application/bpmn20-xml;charset=UTF-8,' + encodedData
          link.download = name
        }
      }
    }
  }
</script>

<style scoped>

  .containers{
    position: absolute;
    background-color: #ffffff;
    width: 100%;
    height: 100%;
  }
  .canvas{
    width: 100%;
    height: 100%;
  }
  .panel{
    position: absolute;
    right: 0;
    top: 0;
    width: 300px;
  }

  .buttons {
    position: absolute;
    left: 20px;
    bottom: 50px;
  }
  .buttons li {
    display: inline-block;
    margin: 5px;
  }
  .buttons li a {
    color: #999;
    background: #eee;
    cursor: not-allowed;
    padding: 8px;
    border: 1px solid #ccc;
    text-decoration: none;
  }
  .buttons li a.active {
    color: #333;
    background: #fff;
    cursor: pointer;
  }
</style>s

2.2 bpmn-viewer.vue:bpmn查看器-Viewer模式

<template>
  <div>
    bpmn查看器
    对多个业务流程进行查看
    <ul>
      下一步需要完成:
      改造编辑器为查看器
     <li>1.禁止编辑 <b style="color: crimson">完成</b></li>
     <li>2.禁止移动  <b style="color: crimson">完成</b></li>
     <li>3.可以移动整体画布 <b style="color: crimson">完成</b></li>
     <li>4.创建流程状态数据结构,根据状态数据重绘流程颜色和信息</li>
     <li>5.节点配置:表单设计,审批指定(多签,会签),状态</li>


    </ul>
    <div class="canvas" ref="" ref="canvas"></div>
  </div>
</template>

<script>
  import BpmnViewer from "bpmn-js/lib/Viewer"
  import MoveCanvasModule from "diagram-js/lib/navigation/movecanvas"
  import BpmnJS from "../../utils/bpmn-modeler.development"
  export default {
    name: "bpmnViewer",
    data(){
      return{

      }
    },
    mounted() {
      this.initBpmnViewer();
    },
    methods:{
      async initBpmnViewer(){
        const canvas = this.$refs.canvas;
        console.log("canvas",canvas)
        const scale = 1;
        const bpmnXml = require("./diagram.bpmn").default;
        const viewer = new BpmnViewer({
          container:canvas,
          height:1000,
          additionalModules:[
            MoveCanvasModule,//移动整个画布
          ],
        });
        try {
          console.log(bpmnXml)
          await viewer.importXML(bpmnXml);
          //屏幕自适应
          viewer.get('canvas').zoom('fit-viewport')
        }catch (err) {
          console.log(err)
        }
      },
      handleZoom(flag){
        if (flag < 0 && this.scale <= 1){
          return;
        }
        this.scale+=flag;
      }
    }
  }
</script>

<style scoped>
.canvas{
  height: 800px;
}
</style>
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

关了个封

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值