vue3+ts+element-plus集成bpmn.js

Bpmn.js集成文档

说明:

  1. 本文档主要是作为集成,不是原创(主要是填写转载他又让我写原文链接,但是我又没有原文链接哈哈哈),感谢以下参考博文。 本项目页面模板使用Geeker-Admin作为前端模板Geeker-Admin,嘎嘎好用。如有侵权联系删除。
  2. 本人集成bpmn.js并非用于工作流开发。存在一些地方不符合工作流场景。
  3. 该文档将持续更新。目前还未完结。
  4. 如有错误或者建议,请私信。

1. 官网及参考

Github-Bpmn.js:Bpmn.js的github代码仓库地址
Github-Bpmn-Examples:Bpmn.js的github的列子
掘金全网最详bpmn.js教材目录:二次开发集成参考

2. 项目技术版本

项目情况: vue+ts+vite+element-plus
Bpmn.js引用版本(package.json):
"@bpmn-io/properties-panel": "^3.13.0",
"bpmn-js": "^16.3.2",
"bpmn-js-properties-panel": "^5.6.1",
"bpmn-moddle": "^6.0.0",
"camunda-bpmn-moddle": "^7.0.1",

3. 集成代码

<template>
  <div class="designer-container">
    <div id="container" class="containerBox"></div>
    <div id="js-properties-panel" class="panel">
      <div class="custom-properties-panel">
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
import { markRaw, onMounted, ref } from "vue";
import BpmnModeler from "bpmn-js/lib/Modeler";
import { BpmnPropertiesPanelModule, BpmnPropertiesProviderModule } from "bpmn-js-properties-panel";
import "bpmn-js/dist/assets/diagram-js.css";
import "bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css";
import "bpmn-js-properties-panel/dist/assets/properties-panel.css";
const bpmnModeler = ref<any>({});
onMounted(() => {
  bpmnModeler.value = markRaw(
    new BpmnModeler({
      container: "#container",//设置容器document
      propertiesPanel: {// 设置控制面板
        parent: "#js-properties-panel"
      },
      additionalModules: [BpmnPropertiesPanelModule, BpmnPropertiesProviderModule] //添加模块 右侧工具栏
    })
  );
  bpmnModeler.value.createDiagram(); //默认初始化画板
});
</script>
<style lang="scss" scope>
@import "./index.scss";
</style>

在这里插入图片描述

4. 汉化

4.1 创建zh.js汉化文件
export default {
  "Activate global connect tool": "激活全局连接工具",
  "Append {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/message flow or association": "使用顺序/消息流或者关联连接",
  "Connect using dataInput association": "使用数据输入关联连接",
  "Change element": "修改类型",
  Remove: "移除",
  "Activate hand tool": "激活抓手工具",
  "Activate lasso tool": "激活套索工具",
  "Activate create/remove space tool": "激活创建/删除空间工具",
  "Create expanded sub process": "创建扩展子过程",
  "Create intermediate/boundary event": "创建中间抛出事件/边界事件",
  "Create pool/participant": "创建池/参与者",
  "Parallel multi instance": "并行多重事件",
  "Sequential multi instance": "时序多重事件",
  "Data object reference": "数据对象参考",
  "Data store reference": "数据存储参考",
  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)": "子流程(折叠的)",
  "Sub-process (expanded)": "子流程(展开的)",
  "Start event": "开始事件",
  StartEvent: "开始事件",
  "Intermediate throw event": "中间事件",
  "End event": "结束事件",
  endEvent: "结束事件",
  "Create gateway": "创建网关",
  "Create intermediate/boundary 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 untermediate 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": "展开池",
  "no parent for {element} in {parent}": "在{parent}里,{element}没有父类",
  "no shape type specified": "没有指定的形状类型",
  "flow elements must be children of pools/participants": "流元素必须是池/参与者的子类",
  "out of bounds release": "out of bounds release",
  "more than {count} child lanes": "子道大于{count} ",
  "element required": "元素不能为空",
  "diagram not part of bpmn:Definitions": "流程图不符合bpmn规范",
  "no diagram to display": "没有可展示的流程图",
  "no process or collaboration to display": "没有可展示的流程/协作",
  "element {element} referenced by {referenced}#{property} not yet drawn": "由{referenced}#{property}引用的{element}元素仍未绘制",
  "already rendered {element}": "{element} 已被渲染",
  "failed to import {element}": "导入{element}失败",
  Id: "编号",
  Name: "名称",
  General: "常规",
  Details: "详情",
  "Message Name": "消息名称",
  Message: "消息",
  Initiator: "创建者",
  "Asynchronous Continuations": "持续异步",
  "Asynchronous Before": "异步前",
  "Asynchronous After": "异步后",
  "Job Configuration": "工作配置",
  Exclusive: "排除",
  "Job Priority": "工作优先级",
  "Retry Time Cycle": "重试时间周期",
  Documentation: "文档",
  "Element Documentation": "元素文档",
  "History Configuration": "历史配置",
  "History Time To Live": "历史的生存时间",
  Forms: "表单",
  "Form Key": "表单key",
  "Form Fields": "表单字段",
  "Business Key": "业务key",
  "Form Field": "表单字段",
  ID: "编号",
  Type: "类型",
  Label: "名称",
  "Default Value": "默认值",
  Validation: "校验",
  "Add Constraint": "添加约束",
  Config: "配置",
  Properties: "属性",
  "Add Property": "添加属性",
  Value: "值",
  Listeners: "监听器",
  "Execution Listener": "执行监听",
  "Event Type": "事件类型",
  "Listener Type": "监听器类型",
  "Java Class": "Java类",
  Expression: "表达式",
  "Must provide a value": "必须提供一个值",
  "Delegate Expression": "代理表达式",
  Script: "脚本",
  "Script Format": "脚本格式",
  "Script Type": "脚本类型",
  "Inline Script": "内联脚本",
  "External Script": "外部脚本",
  Resource: "资源",
  "Field Injection": "字段注入",
  Extensions: "扩展",
  "Input/Output": "输入/输出",
  "Input Parameters": "输入参数",
  "Output Parameters": "输出参数",
  Parameters: "参数",
  "Output Parameter": "输出参数",
  "Timer Definition Type": "定时器定义类型",
  "Timer Definition": "定时器定义",
  Date: "日期",
  Duration: "持续",
  Cycle: "循环",
  Signal: "信号",
  "Signal Name": "信号名称",
  Escalation: "升级",
  Error: "错误",
  "Link Name": "链接名称",
  Condition: "条件名称",
  "Variable Name": "变量名称",
  "Variable event": "变量事件",
  "Specify more than one variable change event as a comma separated list.": "多个变量事件以逗号隔开",
  "Wait for Completion": "等待完成",
  "Activity Ref": "活动参考",
  "Version Tag": "版本标签",
  Executable: "可执行文件",
  "External Task Configuration": "扩展任务配置",
  "Task Priority": "任务优先级",
  External: "外部",
  Connector: "连接器",
  "Must configure Connector": "必须配置连接器",
  "Connector Id": "连接器编号",
  Implementation: "实现方式",
  "Field injections": "字段注入",
  Fields: "字段",
  "Result variable": "结果变量",
  Topic: "主题",
  "Configure connector": "配置连接器",
  "Input parameter": "输入参数",
  Assignee: "代理人",
  "Candidate Users": "候选用户",
  "Candidate Groups": "候选组",
  "Due Date": "到期时间",
  "Follow Up Date": "跟踪日期",
  "Specify more than one group as a comma separated list.": "多个用户使用逗号隔开",
  Priority: "优先级",
  // eslint-disable-next-line no-template-curly-in-string
  "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",
  // eslint-disable-next-line no-template-curly-in-string
  "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",
  Variables: "变量",
  "Candidate Starter Users": "选择启动候选人",
  "Candidate Starter Configuration": "候选人启动器配置",
  "Candidate Starter Groups": "候选人启动组",
  "This maps to the process definition key.": "编号将映射到流程主键.",

  save: "保存",
  Tools: "工具",
  "flow gateway": "流程网关",
  "Process control": "流程节点",
  "User task": "用户任务",
  "Create start event": "开始节点",
  "Create end event": "结束节点",
  "Create exclusive gateway": "互斥网关",
  "Create parallel gateway": "并行网关",
  "Create task": "任务节点",
  "Create user task": "用户任务节点",
  "Condition Type": "条件类型",
  "Create group": "创建组",
  "Create data object reference": "创建数据对象引用",
  "Create data store reference": "创建数据存储引用",
  "Append task": "添加任务",
  "Append end event": "追加结束事件节点",
  "Append gateway": "追加网关节点",
  "Append user task": "追加用户任务节点",
  "Append intermediate/boundary event": "追加中间或边界事件",
  "Append text annotation": "追加文本批注" // 此句要有效,必须在CustomContexPadProvide给此节点增加一个translate('Append TextAnnotation')
};
4.2 创建translate.js引用文件
import translations from "./zh";

export default function customTranslate(template, replacements) {
  replacements = replacements || {};
  template = translations[template] || template;
  return template.replace(/{([^}]+)}/g, function (_, key) {
    return replacements[key] || "{" + key + "}";
  });
}
4.3 初始化时加入汉化模块

在这里插入图片描述

在这里插入图片描述

5. 自定义右侧操作栏

注:我并没有像网上一样引用组件啥的来定义。我直接重写了右侧区域。然后使用监听eventBus的点击事件,来改变右侧的配置信息

5.1 加入代码

在这里插入图片描述

5.2 使用

在这里插入图片描述

5.3 监听eventBus
onMounted(() => {
  bpmnModeler.value = markRaw(
    new BpmnModeler({
      container: "#container",
      // 添加控制板
      propertiesPanel: {
        parent: "#js-properties-panel"
      },
      additionalModules: [customTranslateModule]
    })
  );
  bpmnModeler.value.createDiagram();
  // 注册事件监听
  registerEventBus();
});
const registerEventBus = () => {
  // 获取bpmn.js的EventBus。通过打印eventBus可以查看支持的所有EventBus: console.log(eventBus);
  const eventBus: EventBus = bpmnModeler.value.get("eventBus");
  // 注册节点事件,eventTypes中可以写多个事件,需要哪些写那些
  const eventTypes = ["element.click"];
  eventTypes.forEach(eventType => {
    eventBus.on(eventType, (e: { element: any }) => {
      const { element } = e;
      if (!element.parent) return;
      if (!e || element.type === "bpmn:Process") {
        return false;
      } else {
        if (eventType === "element.click") {
          // TODO 节点点击后想要做的处理
          console.log("点击的节点数据", element);
        }
      }
    });
  });
};
5.5 引入已有的xml文件

creteXml方法,调用bpmnModeler.value.importXML()方法

const creteXml = () => {
  bpmnModeler.value.importXML(`这里粘贴xml文件的字符串`);
};
5.6 获取画好流程图的xml文件
const downloadXml = () => {
  bpmnModeler.value.saveXML({ format: true }).then(res => {
    console.log("画出的xml数据为", res);
  });
};
5.7 效果

在这里插入图片描述

5.8 完整代码

index.vue

<template>
  <div class="designer-container">
    <div id="container" class="containerBox"></div>
    <div id="js-properties-panel" class="panel">
      <div class="custom-properties-panel">
        <div style="background-color: green; height: 400px">
          <el-row class="mb-4">
            <el-button>Default</el-button>
            <el-button type="primary" @click="downloadXml">点击下载xml</el-button>
            <el-button type="success" @click="creteXml">导入文件</el-button>
            <el-button type="info">Info</el-button>
            <el-button type="warning">Warning</el-button>
            <el-button type="danger">Danger</el-button>
          </el-row>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { markRaw, onMounted, ref } from "vue";
import BpmnModeler from "bpmn-js/lib/Modeler";
import translate from "@/views/bpmn/custom/translate/translate";
import "bpmn-js/dist/assets/diagram-js.css";
import "bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css";
import "bpmn-js-properties-panel/dist/assets/properties-panel.css"; // 右边工具栏样式
import EventBus from "diagram-js/lib/core/EventBus";
let customTranslateModule = {
  translate: ["value", translate]
};
const bpmnModeler = ref<any>({});
onMounted(() => {
  bpmnModeler.value = markRaw(
    new BpmnModeler({
      container: "#container",
      // 添加控制板
      propertiesPanel: {
        parent: "#js-properties-panel"
      },
      additionalModules: [customTranslateModule]
    })
  );
  bpmnModeler.value.createDiagram();
  // 注册事件监听
  registerEventBus();
});

const registerEventBus = () => {
  // 获取bpmn.js的EventBus。通过打印eventBus可以查看支持的所有EventBus: console.log(eventBus);
  const eventBus: EventBus = bpmnModeler.value.get("eventBus");
  // 注册节点事件,eventTypes中可以写多个事件,需要哪些写那些
  const eventTypes = ["element.click"];
  eventTypes.forEach(eventType => {
    eventBus.on(eventType, (e: { element: any }) => {
      const { element } = e;
      if (!element.parent) return;
      if (!e || element.type === "bpmn:Process") {
        return false;
      } else {
        if (eventType === "element.click") {
          // TODO 节点点击后想要做的处理
          console.log("点击的节点数据", element);
        }
      }
    });
  });
};

const downloadXml = () => {
  bpmnModeler.value.saveXML({ format: true }).then(res => {
    console.log("画出的xml数据为", res);
  });
};
const creteXml = () => {
  bpmnModeler.value.importXML(``);
};
</script>

<style lang="scss" scope>
@import "./index.scss";
</style>

index.scss

.processDrawBody {
  height: 100%;
  text-align: left;
}
.containerBox {
  width: 100%;
  flex: 1;
  overflow: hidden;
  display: flex;
}
.containerBox #container {
  height: 100%;
  border: 1px solid #ffffff;
}
.bpp-properties-panel [type="text"] {
  box-sizing: border-box;
}
.panel {
  width: 400px;
  position: absolute;
  top: 1px;
  right: 1px;
  height: 100%;
  overflow: auto;
}
/* 右下角logo */
.bjs-powered-by {
  display: none;
}
.designer-container {
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
  background-color: #ffffff;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  position: relative;
}
.icon-custom {
  /* 定义一个公共的类名 */
  border-radius: 50%;
  background-size: 65%;
  background-repeat: no-repeat;
  background-position: center;
}

.icon-custom.lindaidai-task {
  /* 加上背景图 */
  background-image: url("@/assets/bpmn/RESTfulAPI.png");
}
.icon-custom.red {
  /* color: #cc0000 !important; */
  background-image: url("@/assets/bpmn/database.png");
}

.bpmn-icon-task.yellow {
  color: #ffc800 !important;
}

.bpmn-icon-task.green {
  color: #52b415 !important;
}

6. 自定义左侧组件

按照道理这部分的js应该是可以使用ts来写的,但是时间本人技术不是太够,就直接引用js了。抱歉

6.1 参考掘金文档全网最详bpmn.js教材-自定义palette篇

在这里插入图片描述

6.2 定义CustomPalette.js
const SUITABILITY_SCORE_HIGH = "Mysql",
  SUITABILITY_SCORE_AVERGE = "Redis",
  SUITABILITY_SCORE_LOW = "Api";

export default class CustomPalette {
  constructor(bpmnFactory, create, elementFactory, palette, translate) {
    this.bpmnFactory = bpmnFactory;
    this.create = create;
    this.elementFactory = elementFactory;
    this.translate = translate;

    palette.registerProvider(this);
  }

  getPaletteEntries() {
    const { bpmnFactory, create, elementFactory, translate } = this;

    function createTask(suitabilityScore) {
      return function (event) {
        const businessObject = bpmnFactory.create("bpmn:Task");
        businessObject.suitable = suitabilityScore;

        const shape = elementFactory.createShape({
          type: "bpmn:Task",
          businessObject: businessObject
        });

        create.start(event, shape);
      };
    }
    // 返回需要的组件
    return {
      "create.lindaidai-task": {
        group: "activity",
        className: "icon-custom lindaidai-task",
        title: translate("创建一个类型为lindaidai-task的任务节点"),
        action: {
          dragstart: createTask(SUITABILITY_SCORE_LOW),
          click: createTask(SUITABILITY_SCORE_LOW)
        }
      },
      "create.low-task": {
        group: "activity",
        className: "icon-custom red",
        title: translate("Create Task with low suitability score"),
        action: {
          dragstart: createTask(SUITABILITY_SCORE_LOW),
          click: createTask(SUITABILITY_SCORE_LOW)
        }
      },
      "create.average-task": {
        group: "activity",
        className: "bpmn-icon-task yellow",
        title: translate("Create Task with average suitability score"),
        action: {
          dragstart: createTask(SUITABILITY_SCORE_AVERGE),
          click: createTask(SUITABILITY_SCORE_AVERGE)
        }
      },
      "create.high-task": {
        group: "activity",
        className: "bpmn-icon-task green",
        title: translate("Create Task with high suitability score"),
        action: {
          dragstart: createTask(SUITABILITY_SCORE_HIGH),
          click: createTask(SUITABILITY_SCORE_HIGH)
        }
      }
    };
  }
}

CustomPalette.$inject = ["bpmnFactory", "create", "elementFactory", "palette", "translate"];
6.3 定义CustomContextPad.js
const SUITABILITY_SCORE_HIGH = "Mysql",
  SUITABILITY_SCORE_AVERGE = "Redis",
  SUITABILITY_SCORE_LOW = "Api";

export default class CustomContextPad {
  constructor(bpmnFactory, config, contextPad, create, elementFactory, injector, translate) {
    this.bpmnFactory = bpmnFactory;
    this.create = create;
    this.elementFactory = elementFactory;
    this.translate = translate;

    if (config.autoPlace !== false) {
      this.autoPlace = injector.get("autoPlace", false);
    }

    contextPad.registerProvider(this);
  }

  getContextPadEntries(element) {
    const { autoPlace, bpmnFactory, create, elementFactory, translate } = this;

    function appendServiceTask(suitabilityScore) {
      return function (event, element) {
        if (autoPlace) {
          const businessObject = bpmnFactory.create("bpmn:Task");

          businessObject.suitable = suitabilityScore;

          const shape = elementFactory.createShape({
            type: "bpmn:Task",
            businessObject: businessObject
          });

          autoPlace.append(element, shape);
        } else {
          appendServiceTaskStart(event, element);
        }
      };
    }

    function appendServiceTaskStart(suitabilityScore) {
      return function (event) {
        const businessObject = bpmnFactory.create("bpmn:Task");

        businessObject.suitable = suitabilityScore;

        const shape = elementFactory.createShape({
          type: "bpmn:Task",
          businessObject: businessObject
        });

        create.start(event, shape, element);
      };
    }

    return {
      "append.low-task": {
        group: "model",
        className: "bpmn-icon-task red",
        title: translate("Append Task with low suitability score"),
        action: {
          click: appendServiceTask(SUITABILITY_SCORE_LOW),
          dragstart: appendServiceTaskStart(SUITABILITY_SCORE_LOW)
        }
      },
      "append.average-task": {
        group: "model",
        className: "bpmn-icon-task yellow",
        title: translate("Append Task with average suitability score"),
        action: {
          click: appendServiceTask(SUITABILITY_SCORE_AVERGE),
          dragstart: appendServiceTaskStart(SUITABILITY_SCORE_AVERGE)
        }
      },
      "append.high-task": {
        group: "model",
        className: "bpmn-icon-task green",
        title: translate("Append Task with high suitability score"),
        action: {
          click: appendServiceTask(SUITABILITY_SCORE_HIGH),
          dragstart: appendServiceTaskStart(SUITABILITY_SCORE_HIGH)
        }
      }
    };
  }
}

CustomContextPad.$inject = ["bpmnFactory", "config", "contextPad", "create", "elementFactory", "injector", "translate"];
6.4 定义CustomRenderer.js
import BaseRenderer from "diagram-js/lib/draw/BaseRenderer";

import { append as svgAppend, attr as svgAttr, classes as svgClasses, create as svgCreate } from "tiny-svg";

import { getRoundRectPath } from "bpmn-js/lib/draw/BpmnRenderUtil";

import { is, getBusinessObject } from "bpmn-js/lib/util/ModelUtil";

import { isNil } from "min-dash";

const HIGH_PRIORITY = 1500,
  TASK_BORDER_RADIUS = 2,
  COLOR_GREEN = "#52B415",
  COLOR_YELLOW = "#ffc800",
  COLOR_RED = "#cc0000";

export default class CustomRenderer extends BaseRenderer {
  constructor(eventBus, bpmnRenderer) {
    super(eventBus, HIGH_PRIORITY);

    this.bpmnRenderer = bpmnRenderer;
  }

  canRender(element) {
    // ignore labels
    return !element.labelTarget;
  }

  drawShape(parentNode, element) {
    const shape = this.bpmnRenderer.drawShape(parentNode, element);
    const suitabilityScore = this.getSuitabilityScore(element);
    if (!isNil(suitabilityScore)) {
      const color = this.getColor(suitabilityScore);

      const rect = drawRect(parentNode, 50, 20, TASK_BORDER_RADIUS, color);

      svgAttr(rect, {
        transform: "translate(-20, -10)"
      });

      let text = svgCreate("text");
      svgAttr(text, {
        fill: "#fff",
        transform: "translate(-15, 5)"
      });

      svgClasses(text).add("djs-label");

      svgAppend(text, document.createTextNode(suitabilityScore));

      svgAppend(parentNode, text);
    }

    return shape;
  }

  getShapePath(shape) {
    if (is(shape, "bpmn:Task")) {
      return getRoundRectPath(shape, TASK_BORDER_RADIUS);
    }

    return this.bpmnRenderer.getShapePath(shape);
  }

  getSuitabilityScore(element) {
    const businessObject = getBusinessObject(element);

    const { suitable } = businessObject;

    return Number.isFinite(suitable) ? suitable : suitable;
  }

  getColor(suitabilityScore) {
    if (suitabilityScore === "Mysql") {
      return COLOR_GREEN;
    } else if (suitabilityScore === "Redis") {
      return COLOR_YELLOW;
    }

    return COLOR_RED;
  }
}

CustomRenderer.$inject = ["eventBus", "bpmnRenderer"];

// helpers //

// copied from https://github.com/bpmn-io/bpmn-js/blob/master/lib/draw/BpmnRenderer.js
function drawRect(parentNode, width, height, borderRadius, color) {
  const rect = svgCreate("rect");

  svgAttr(rect, {
    width: width,
    height: height,
    rx: borderRadius,
    ry: borderRadius,
    stroke: color,
    strokeWidth: 2,
    fill: color
  });

  svgAppend(parentNode, rect);

  return rect;
}
6.5 创建引用index.js

这里有两个选择,第一是直接用我们自定义的覆盖原有的

import CustomContextPad from "./CustomContextPad";
import CustomPalette from "./CustomPalette";
import CustomRenderer from "./CustomRenderer";

export default {
  __init__: ["customContextPad", "paletteProvider", "customRenderer"],
  paletteProvider: ["type", CustomPalette],
  customContextPad: ["type", CustomContextPad],
  customRenderer: ["type", CustomRenderer]
};

效果

在这里插入图片描述

第二种是在原有的基础上添加

import CustomContextPad from "./CustomContextPad";
import CustomPalette from "./CustomPalette";
import CustomRenderer from "./CustomRenderer";
// 关键在于对customPalette引用的不同
export default {
  __init__: ["customContextPad", "customPalette", "customRenderer"],
  customPalette: ["type", CustomPalette],
  customContextPad: ["type", CustomContextPad],
  customRenderer: ["type", CustomRenderer]
};

效果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/344706.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据链路层——笔记·续

使用集线器的星形拓扑 传统以太网传输媒体&#xff1a;粗同轴电缆 -> 细同轴电缆 -> 双绞线。 采用双绞线的以太网采用星形拓扑。 在星形的中心则增加了一种可靠性非常高的设备&#xff0c;叫做集线器 (hub)。 传统以太网使用同轴电缆&#xff0c;采用总线形拓扑结构&am…

php no input file specified

一、修改 .user.ini 文件 内容 open_basedir/wab/led-sht.com/:/tmp/ led-sportslight.com是项目根目录位置 改好后保存并清空缓存硬刷新网站就行了 二、mkdir(): Permission denied /core/library/think/cache/driver/File.php 第 84 行左右 mkdir(): Permission denied 这个…

Windows AD 组策略 通过脚本修改管理员密码:以安全方式

因为本文主要讲的是通过脚本如何以安全方式设置密码&#xff0c;所以关于组策略如何设置请参考这里&#xff1a; WinServer 2019 AD 组策略 启用本地管理员账号&#xff0c;重置密码_ad域命令启用administrator账户-CSDN博客 我们首先要讲一下&#xff0c;以一般方法创建的脚…

FineReport链接本地DBeaver

finereport链接本地DBeaver fanruan.com/finereport/doc-view-101.html help.fanruan.com/finereport/doc-view-2583.html

PMP证书要怎么考,含金量怎么样?

PMP含金量更多的是“敲门砖”作用&#xff0c;公司招聘的门槛&#xff0c;现在坐项目的大部分都需要PMP/NPDP证书。 当然现在PMP管理模式也很热门&#xff0c;对企业发展很有利&#xff0c;各大企业都有引进改良应用在公司的项目上&#xff0c;之前在校友群里面大家在讨论PMP …

【MySQL源码】Seconds_Behind_Master是如何计算的

作为MySQL DBA&#xff0c;相信大家对参数 Seconds_Behind_Master 并不陌生&#xff0c;该字段的值可以通过 show slave status\G的输出&#xff0c;表示主从延迟的时间&#xff0c;单位为秒。监控主从延迟一般取这个值就足够了。0 表示无延迟&#xff0c;理想状态该值不要超…

selenium执行出现异常,SessionNotCreatedException ChromeDriver only supports

问题现状&#xff1a; 运行程序报错&#xff1a; selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 114 Current browser version is 121.0.6167.85 with binary path /App…

GraphicsMagick 的 OpenCL 开发记录(二十一)

文章目录 支持windows平台windows平台不能生成内核的.bin文件_aligned_free()和free()不匹配的问题 <2022-04-13 Wed> 支持windows平台 支持windows平台需要做的&#xff1a; 为GraphicsMagick/VisualMagick/configure/configure.exe增加“Enable OpenCL”多选框。 从…

【jQuery入门】链式编程、修改css、类操作和className的区别

文章目录 前言一、链式编程二、修改css2.1 获取css的值2.2 设置单个css属性2.3 设置类样式添加类移除类切换类 三、类操作与className的区别总结 前言 jQuery是一个流行的JavaScript库&#xff0c;广泛用于简化DOM操作和处理事件。在jQuery中&#xff0c;链式编程是一种强大的…

Tree-Shaking 作用和实现原理

一、什么是Tree-shaking Tree-shaking 它的名字来源于通过摇晃&#xff08;shake&#xff09;JavaScript代码的抽象语法树&#xff08;AST&#xff09;&#xff0c;是一种用于优化JavaScript代码的技术&#xff0c;主要用于移除未被使用的代码&#xff0c;使得最终生成的代码包…

【机组】计算机组成原理实验指导书.

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《机组 | 模块单元实验》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 ​ 目录 第一章 性能特点 1.1 系…

OpenTCS IDEA 全流程搭建和运行指南

OpenTCS IDEA 全流程搭建和运行指南 JDK安装下载JDK版本 openTCS源码下载IDEA 打开运行查看下载源码中gradle版本号下载gradle 二进制文件配置IDEA Gradle本地仓库IDEA打开openTCS项目运行顺序 JDK安装 下载JDK版本 JDK网址 注意&#xff1a; 请下载官方文档标准的java13JDK o…

httpClient忽略https的证书认证

忽略https证书认证代码: /*** 创建模拟客户端&#xff08;针对 https 客户端禁用 SSL 验证&#xff09;* return* throws Exception*/public static CloseableHttpClient createHttpClientWithNoSsl() throws Exception {// Create a trust manager that does not validate cer…

软考复习之UML设计篇

UML统一建模语言 构件图&#xff1a;描述系统的物理结构&#xff0c;它可以用来显示程序代码如何分解成模块 部署图&#xff1a;描述系统中硬件和软件的物理结构&#xff0c;它描述构成系统架构的软件构件&#xff0c;处理器和设备 用例图&#xff1a;描述系统与外部系统及用…

C++读取txt文件中的逐个字符

为了增加读取的灵活性&#xff0c;所以separator和filename都设置为在主函数中获取输入或者在函数中传参的视线方法 举个例子&#xff0c;txt文件如下&#xff1a; household;2;true; 首先声明一个读取数据的文件 void read_data_file(const string& filename,char se…

【STM32】USB程序烧录需要重新上电 软件复位方法

文章目录 一、问题二、解决思路2.1 直接插拔USB2.2 给芯片复位 三、解决方法3.1 别人的解决方法3.2 在下载界面进行设置 一、问题 最近学习STM32的USB功能&#xff0c;主要是想要使用虚拟串口功能&#xff08;VCP&#xff09;&#xff0c;发现每次烧录之后都需要重新上电才可以…

训练模型时 遇到速度过慢时的深思 速度提升 (From GPU CPU)

训练模型时 遇到速度过慢时的深思 & 速度提升 GPU查看GPU使用情况 配置单机多卡并行训练torch.nn.DataParallel平衡DataParallel带来的显存使用不平衡的问题torch.nn.parallel.DistributedDataParallel 多机多gpu训练Reference 使用半精度训练更好的显卡&#xff0c;更轻的…

编译和链接---C语言

引言 众所周知&#xff0c;C语言是一门高级的编程语言&#xff0c;是无法被计算机直接读懂的&#xff0c;C语言也不同于汇编PHP&#xff0c;无法直接翻译成机器语言&#xff0c;在学习的过程中&#xff0c;你是否好奇过我们所敲的C语言代码&#xff0c;是如何一步步翻译成机器…

Docker容器引擎(1)

目录 一.Docker 概述 为什么要用到容器&#xff1f; docker是什么&#xff1f; 容器与虚拟机的区别&#xff1f; docker的三个核心概念&#xff1a; 二.安装docker 安装依赖包&#xff1a; 安装 Docker-CE并设置为开机自动启动&#xff1a; 查看 docker 版本信息&#…

10个常考的前端手写题,你全都会吗?(下)

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热爱技术和分享&#xff0c;欢迎大家交流&#xff0c;一起学习进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 今天接着上篇再来分享一下10个常见的JavaScript手写功能。 目录 1.实现继承 ES5继…