Vue2使用Jeeplus-bpmn设计图

64 阅读8分钟

0. bpmn.js简介

📌BPMN (Business Process Model and Notation): 业务流程模型和标记法,是对象管理组织维护的关于业务流程建模的行业性标准。目标是通过提供一套既符合业务人员直观又能表现复杂流程语义的标记法,同时为技术人员和业务人员从事业务流程管理提供支持。

Bpmn.js: BPMN 2.0 rendering toolkit and web modeler.

一个 BPMN 2.0渲染工具包和web建模器。使得可以在浏览器中创建、嵌入和扩展 BPMN 流程图。

Bpmn.js 内部依赖 diagram.jsbpmn-moddle

bpmn-js 架构:部分和职责

其中 diagram.js 是一个用于在web应用程序中显示和修改图表的工具库,为 bpmn.js 提供了基础的图形元素交互方法,以及覆盖物、工具栏、ContentPad等基础工具和撤销恢复的操作命令栈。

bpmn-moddle了解BPMN 2.0 标准中定义的 BPMN 2.0 元模型。它允许我们读取和写入符合 BPMN 2.0 规范的 XML 文档,并访问图表上绘制的形状和连接背后的 BPMN 相关信息。

  • Palette:左侧元素工具栏,可以通过点击或者拖拽触发添加新元素
  • Shapes:所有 Bpmn.js 可见节点,Moddle 描述文件内可发现均继承自 Element
  • Connections:所有节点之间的连线,Bpmn.js 中连线的类型均为 SequenceFlow
  • ContentPad:用鼠标选中一个元素时会出现,主要是操作该元素的上下文以及节点自身的类型等
  • PopupMenu:默认在鼠标点击 ContentPad 中的扳手图表时出现,主要用于控制选中元素类型的调整等

image.png

1.安装jeeplus-bpmn.js

image.png

npm i jeeplus-bpmn@6.0.9 --registry=https://registry.npmmirror.com

image.png

2. 初始XML模板

// template.js
export const templateXml = `
<bpmn2:definitions
  xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
  xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
  xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
  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_processId" name="流程_processId">
    <bpmn2:startEvent id="StartEvent_01ydzqe" name="开始" />
    </bpmn2:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="process_processId">
      <bpmndi:BPMNShape id="StartEvent_01ydzqe_di" bpmnElement="StartEvent_01ydzqe">
        <dc:Bounds x="142" y="212" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="149" y="255" width="22" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>
`;

3. 翻译中文

// customTranslate.js
import translations from './zn.js'
function isNullOrUndefined(value) {
  return value === undefined || value === null
}
// eslint-disable-next-line node/no-deprecated-api
export default function customTranslate(template, replacements) {
  // eslint-disable-next-line no-param-reassign
  replacements = replacements || {}
  // eslint-disable-next-line no-param-reassign
  template = translations[template] || template
  return template.replace(/{([^}]+)}/g, function(_, key) {
    var str = replacements[key]
    if (!isNullOrUndefined(translations[replacements[key]])) {
      str = translations[replacements[key]]
    }
    return str || '{' + key + '}'
  })
}

/* zn.js */
export default {
  // Labels
  'Activate the global connect tool': '激活全局连接工具',
  'Append {type}': '追加{type}', //'追加{type}',
  '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': '顺序多重实例',
  'Loop': '循环',
  'Ad-hoc': '特别指定',
  'Create {type}': '创建{type}',
  'Task': '任务',
  'Send Task': '发送任务', // 发送任务
  'Receive Task': '接收节点',
  'User Task': '用户节点',
  'Manual Task': '手工任务',
  'Business Rule Task': '作业规则任务',
  'Service Task': '服务节点',
  'Script Task': '脚本任务',
  'Call Activity': '调用活动',
  'Sub Process (collapsed)': '子过程(折叠)',
  'Start Event': '开始事件',
  'Intermediate Throw Event': '抛出事件',
  'End Event': '结束事件',
  'Message Start Event': '消息开始事件',
  'Timer Start Event': '时间开始事件',
  'Conditional Start Event': '条件开始事件',
  'Signal Start Event': '信号开始事件',
  'Error Start Event': '错误开始事件',
  'Escalation Start Event': '升级开始事件',
  'Compensation Start Event': '补偿开始事件',
  'Message Start Event (non-interrupting)': '消息开始事件(不中断)',
  'Timer Start Event (non-interrupting)': '定时开始事件(不中断)',
  'Conditional Start Event (non-interrupting)': '条件开始事件(不中断)',
  'Signal Start Event (non-interrupting)': '信号开始事件(不中断)',
  'Escalation Start Event (non-interrupting)': '升级开始事件(不中断)',
  'Message Intermediate Catch Event': '信息捕获事件',
  'Message Intermediate Throw Event': '信息抛出事件',
  'Timer Intermediate Catch Event': '定时器捕获事件',
  'Escalation Intermediate Throw Event': '升级抛出事件',
  'Conditional Intermediate Catch Event': '条件捕获事件',
  'Link Intermediate Catch Event': '链接捕获事件',
  'Link Intermediate Throw Event': '链接抛出事件',
  'Compensation Intermediate Throw Event': '补偿抛出事件',
  'Signal Intermediate Catch Event': '信号捕获事件',
  'Signal Intermediate Throw Event': '信号抛出事件',
  'Message End Event': '消息结束事件',
  'Escalation End Event': '升级结束事件',
  'Error End Event': '错误结束事件',
  'Cancel End Event': '取消结束事件',
  'Compensation End Event': '补偿结束事件',
  'Signal End Event': '信号结束事件',
  'Terminate End Event': '终止结束事件',
  'Message Boundary Event': '消息边界事件',
  'Message Boundary Event (non-interrupting)': '消息边界事件(非中断)',
  'Timer Boundary Event': '定时边界事件',
  'Timer Boundary Event (non-interrupting)': '定时边界事件(非中断)',
  'Escalation Boundary Event': '升级边界事件',
  'Escalation Boundary Event (non-interrupting)': '升级边界事件(非中断)',
  'Conditional Boundary Event': '有条件的边界事件',
  'Conditional Boundary Event (non-interrupting)': '条件边界事件(非中断)',
  'Error Boundary Event': '错误边界事件',
  'Cancel Boundary Event': '取消边界事件',
  'Signal Boundary Event': '信号边界事件',
  'Signal Boundary Event (non-interrupting)': '信号边界事件(非中断)',
  'Compensation Boundary Event': '补偿边界事件',
  'Exclusive Gateway': '互斥网关',
  'Parallel Gateway': '并行网关',
  'Inclusive Gateway': '相容网关',
  'Complex Gateway': '复杂网关',
  'Event based Gateway': '事件网关',
  'Transaction': '交换',
  'Sub Process': '子流程',
  'Event Sub Process': '事件子流程',
  'Collapsed Pool': '合并泳池',
  'Expanded Pool': '扩展泳池',

  // Errors
  'no parent for {element} in {parent}': '在{parent}中的{element}没有父元素',
  'no shape type specified': '没有指定的形状类型',
  'flow elements must be children of pools/participants': '流动元素必须是游泳池/参与者',
  'out of bounds release': '跨界界释放',
  'more than {count} child lanes': '超出{count}分支',
  'element required': '被请求元素',
  'diagram not part of bpmn': '图在bpmn中',
  'no diagram to display': '没有图表来显示',
  'no process or collaboration to display': '没有流程或协作显示',
  'element {element} referenced by {referenced}#{property} not yet drawn': '元素{element}在{referenced}#{property}的引用还没有绘制',
  'already rendered {element}': '已经提供{element}',
  'failed to import {element}': '元素导入失败{element}',

  // props lable
  'General': '基本属性',
  'Documentation': '备注',
  'Element Documentation': '元素备注',
  'Executable': '可执行的',
  'Process Documentation': '过程说明',

  'Listeners': '监听器',
  'Execution Listener': '扩展监听器',
  'Extensions': '扩展',
  'Properties': '属性',
  'Add Property': '添加属性',
  'Name': '名称',
  'Id': '标识',
  'ID': '标识',
  'Value': '值',
  'Version Tag': '版本标记',
  'External Task Configuration': '外部任务配置',
  'Task Priority': '任务优先级',
  'Job Configuration': '作业配置',
  'Job Priority': '作业优先级',
  'History Configuration': '历史配置',
  'History Time To Live': '历史生存时间',
  'Details': '详情',
  'Initiator': '初始化',
  'Asynchronous Continuations': '异步连续',
  'Asynchronous Before': '异步之前',
  'Asynchronous After': '异步之后',
  'Forms': '表单',
  'Form Key': '表单键',
  'Form Fields': '表单字段',
  'Form Field': '表单字段',
  'Business Key': '业务键',
  'Type': '类型',
  'Label': '标签',
  'Default Value': '默认值',
  'Validation': '校验',
  'Add Constraint': '添加约束',
  'Config': '配置',
  'Must provide a value': '必须提供一个值',
  'Event Type': '事件类型',
  'Listener Type': '监听器类型',
  'Java Class': 'Java 类',
  'Field Injection': '字段注入',
  'Fields': '字段',
  'Expression': '表达式',
  'Delegate Expression': '委托表达式',
  'Script': '脚本',
  'Parameter must have a name': '参数必须有一个名称',
  'String': '字符串',
  'Implementation': '实现',
  'External': '外部',
  'Connector': '连接器',
  'Must configure Connector': '必须配置连接器',
  'Connector Id': '连接器ID',
  'Input/Output': '输入/输出',
  'Input Parameters': '输入参数',
  'Input Parameter': '输入参数',
  'Output Parameters': '输出参数',
  'Output Parameter': '输出参数',
  'Text': '文本',
  'List': '列表',
  'Add Entry': '增加条目',
  'Field Injections': '字段注入',
  '追加TextAnnotation': '文本注释',
  'Variables': '变量',
  'In Mapping': '输入映射',
  'Out Mapping': '输出映射',
  'Target': '目标',
  'Source': '来源',
  'Local': '本地',
  'Candidate Starter Configuration': '候选人起动器配置',
  'Candidate Starter Groups': '候选人起动器组',
  'Candidate Starter Users': '候选人起动器用户',
  'Configure Connector': '配置连接器',
  'Assignee': '受理人',
  'Candidate Users': '候选用户',
  'Candidate Groups': '候选组',
  'Due Date': '到期',
  'Follow Up Date': '跟踪日期',
  'Priority': '优先级',
  'The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': '可以使用EL表达式,(例如:${someDate} 或者ISO日期(例如:2015-06-26T09:54:00))',
  'The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': '可以使用EL表达式,(例如:${someDate} 或者ISO日期(例如:2015-06-26T09:54:00))',
  'Create Gateway': '创建网关节点',
  'Create StartEvent': '创建开始节点',
  'Create Intermediate/Boundary Event': '创建中间/边界事件',
  'Create EndEvent': '创建结束节点',
  'Create Task': '创建任务',
  'Create DataObjectReference': '创建数据对象引用',
  'Create DataStoreReference': '创建数据存储引用',


  //custom
  'Create UserTask': '创建用户任务',
  'Create CallActivity': '创建调用任务',
  'Open minimap': '打开缩略图',
  'Close minimap': '关闭缩略图',
  'Append Gateway': '追加网关节点',
  'Append Intermediate/Boundary Event': '追加中间/边界事件',
  'StartEvent': '开始节点',
  'EndEvent': '结束节点',
  'DataObjectReference': '数据对象引用',
  'DataStoreReference': '数据存储引用',
  'Element must have an unique id.': '节点编号不唯一!',
  'Create ExclusiveGateway': '创建排他网关',
  'Create Pool/Participant': '创建泳道或参与者',
  'Append ExclusiveGateway': '追加排他网关',
  'Append UserTask':"追加用户任务",
  'Append Sequence':"追加连线",
  'Append EndEvent':"追加结束节点",
  'UserTask': '用户任务',
  'ExclusiveGateway': '条件', //排他网关
  'Element must have an unique id.': '渲染元素失败,元素必须用于唯一编号,请刷新浏览器!',
  'Element is not set findUserType': '员工节点未设置节点人员',
  'Token Simulation ': '流程模拟'
}

4.创建实例

image.png

<template>
  <div>
    <div class="headerBtn">
      <el-button @click="handleSave" icon="el-icon-folder" size="mini" type="primary">保存</el-button>
    </div>
    <div id="bpmn-container"></div>
  </div>
</template>

<script>
import BpmnModeler from 'jeeplus-bpmn/lib/Modeler';
// import flowableModdle from './static/js/flowable.json';
import customTranslate from './static/js/customTranslate.js';
import { templateXml } from './static/js/template.js';
// import './static/css/bpmn.css';
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' // 节点完整图标

export default {
  data() {
    return {
      bpmnModeler: null
    };
  },
  mounted() {
    this.init();
  },
  methods: {
    init() {
      this.bpmnModeler = new BpmnModeler({
        container: '#bpmn-container',
        additionalModules: [
          {
            translate: ['value', customTranslate], //翻译
            // labelEditingProvider: ['value', ''] // 禁用节点编辑
          }
        ],
        moddleExtensions: {
          // flowable: flowableModdle
        }
      });
      var xml = templateXml;
      const processId = new Date().getTime();
      xml = xml.replace(/process_processId/g, 'process' + processId);
      xml = xml.replace(/流程_processId/g, '流程' + processId);

      this.bpmnModeler.importXML(xml, function (err) {
        if (err) {
          console.log('error rendering', err);
        } else {
          console.log('rendered');
        }
      });
    },
    handleSave() {
      this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
        if (err) {
          this.$message.error(err);
        } else {
          console.log(xml);
        }
      });
    }
  }
};
</script>

<style>
#bpmn-container {
  height: 100vh;
  background: url('./static/img/bpmnBg.svg');
}
.headerBtn {
  padding: 10px;
}
</style>

5. 左侧工具栏Palette,移除不需要的工具

node_modules\jeeplus-bpmn\lib\features\palette\PaletteProvider.js

注释不需要的工具(重新启动项目才能生效) 例如移除hand-tool(激活拖拽工具)和lasso-tool(激活框选工具)

image.png

6. 移除节点选中的ContentPad配置

node_modules\bpmn-js\lib\features\context-pad\ContextPadProvider.js

注释不需要的配置(重新启动项目才能生效),例如移除Change type(改变事件类型)和append.gateway(追加网关)

image.png

完整源码

git地址 github.com/Lxl95/test-…