zhaojinyu 2 月之前
当前提交
9850abad8c
共有 100 个文件被更改,包括 11106 次插入0 次删除
  1. 3 0
      .vscode/extensions.json
  2. 15 0
      README.md
  3. 13 0
      index.html
  4. 45 0
      lib/alignElements/AlignElementContextPad.ts
  5. 18 0
      lib/alignElements/index.ts
  6. 217 0
      lib/autoPlace/YmAutoPlaceUtil.ts
  7. 16 0
      lib/autoPlace/index.ts
  8. 21 0
      lib/business/index.ts
  9. 148 0
      lib/commandStack/index.ts
  10. 2 0
      lib/config/constants/index.ts
  11. 365 0
      lib/config/contextPad/index.ts
  12. 35 0
      lib/config/element/approver/index.ts
  13. 29 0
      lib/config/element/end/index.ts
  14. 32 0
      lib/config/element/execute/addData/index.ts
  15. 32 0
      lib/config/element/execute/delData/index.ts
  16. 32 0
      lib/config/element/execute/getData/index.ts
  17. 32 0
      lib/config/element/execute/interface/index.ts
  18. 32 0
      lib/config/element/execute/launch/index.ts
  19. 32 0
      lib/config/element/execute/message/index.ts
  20. 32 0
      lib/config/element/execute/schedule/index.ts
  21. 32 0
      lib/config/element/execute/updateData/index.ts
  22. 38 0
      lib/config/element/gateway/choose/index.ts
  23. 2 0
      lib/config/element/gateway/exclusive/index.d.ts
  24. 35 0
      lib/config/element/gateway/exclusive/index.js
  25. 36 0
      lib/config/element/gateway/exclusive/index.ts
  26. 2 0
      lib/config/element/gateway/inclusive/index.d.ts
  27. 36 0
      lib/config/element/gateway/inclusive/index.js
  28. 37 0
      lib/config/element/gateway/inclusive/index.ts
  29. 2 0
      lib/config/element/gateway/parallel/index.d.ts
  30. 36 0
      lib/config/element/gateway/parallel/index.js
  31. 38 0
      lib/config/element/gateway/parallel/index.ts
  32. 34 0
      lib/config/element/group/index.ts
  33. 53 0
      lib/config/element/label/index.ts
  34. 37 0
      lib/config/element/outside/index.ts
  35. 35 0
      lib/config/element/processing/index.ts
  36. 13 0
      lib/config/element/sequenceFlow/index.ts
  37. 32 0
      lib/config/element/start/index.ts
  38. 35 0
      lib/config/element/subFlow/index.ts
  39. 31 0
      lib/config/element/timer/index.ts
  40. 31 0
      lib/config/element/trigger/event/index.ts
  41. 32 0
      lib/config/element/trigger/index.ts
  42. 31 0
      lib/config/element/trigger/notice/index.ts
  43. 31 0
      lib/config/element/trigger/time/index.ts
  44. 31 0
      lib/config/element/trigger/webhook/index.ts
  45. 178 0
      lib/config/index.ts
  46. 136 0
      lib/config/variableName/index.ts
  47. 172 0
      lib/contextPad/index.ts
  48. 274 0
      lib/contextPad/provider/CustomizeContextPad.ts
  49. 85 0
      lib/contextPad/provider/index.ts
  50. 107 0
      lib/copyPaste/index.ts
  51. 34 0
      lib/factory/index.ts
  52. 88 0
      lib/factory/shapeSize.ts
  53. 367 0
      lib/gridSnapping/connect/index.ts
  54. 231 0
      lib/gridSnapping/index.ts
  55. 59 0
      lib/helper/defaultXml.ts
  56. 6 0
      lib/labelEditing/labelEditingProvider.ts
  57. 58 0
      lib/modeler/index.ts
  58. 53 0
      lib/outline/index.ts
  59. 21 0
      lib/palette/CustomizePalette/index.ts
  60. 32 0
      lib/palette/CustomizePalette/task.ts
  61. 150 0
      lib/palette/bpmnPalette.ts
  62. 853 0
      lib/palette/bpmnReplace.ts
  63. 64 0
      lib/palette/bpmnType.ts
  64. 31 0
      lib/palette/index.ts
  65. 26 0
      lib/previewModeler/index.ts
  66. 145 0
      lib/previewModeler/renderer/CustomizeRenderer.ts
  67. 61 0
      lib/previewModeler/renderer/index.ts
  68. 149 0
      lib/renderer/CustomizeRenderer.ts
  69. 68 0
      lib/renderer/index.ts
  70. 762 0
      lib/rule/index.ts
  71. 183 0
      lib/simpleModeler/contextPad/index.ts
  72. 619 0
      lib/simpleModeler/contextPad/provider/CustomizeContextPad.ts
  73. 59 0
      lib/simpleModeler/contextPad/provider/index.ts
  74. 199 0
      lib/simpleModeler/gridSnapping/autoPlacebehavior/index.ts
  75. 235 0
      lib/simpleModeler/gridSnapping/connect/index.ts
  76. 153 0
      lib/simpleModeler/keyboard/BpmnKeyboardBindings.ts
  77. 57 0
      lib/simpleModeler/modeler/index.ts
  78. 184 0
      lib/simpleModeler/renderer/CustomizeRenderer.ts
  79. 76 0
      lib/simpleModeler/renderer/connect/button.ts
  80. 61 0
      lib/simpleModeler/renderer/connect/marker.ts
  81. 119 0
      lib/simpleModeler/renderer/index.ts
  82. 177 0
      lib/taskModeler/contextPad/index.ts
  83. 221 0
      lib/taskModeler/contextPad/provider/CustomizeContextPad.ts
  84. 85 0
      lib/taskModeler/contextPad/provider/index.ts
  85. 64 0
      lib/taskModeler/modeler/index.ts
  86. 88 0
      lib/taskModeler/renderer/CustomizeRenderer.ts
  87. 61 0
      lib/taskModeler/renderer/index.ts
  88. 240 0
      lib/translate/cn.ts
  89. 15 0
      lib/translate/index.ts
  90. 58 0
      lib/utils/DiUtil.ts
  91. 1 0
      lib/utils/bpmn.ts
  92. 169 0
      lib/utils/bpmnLintUtil.ts
  93. 768 0
      lib/utils/constructTreeUtil.ts
  94. 110 0
      lib/utils/dictUtil.ts
  95. 8 0
      lib/utils/index.ts
  96. 51 0
      lib/utils/modelUtil.ts
  97. 842 0
      lib/utils/nodeUtil.ts
  98. 31 0
      lib/utils/uuidUtil.ts
  99. 78 0
      package.json
  100. 13 0
      prettier.config.js

+ 3 - 0
.vscode/extensions.json

@@ -0,0 +1,3 @@
+{
+  "recommendations": ["Vue.volar"]
+}

+ 15 - 0
README.md

@@ -0,0 +1,15 @@
+# @jnpf/bpmn
+
+JNPF 快速开发平台 bpmn 组件
+
+构建包
+
+```bash
+ npm run build
+```
+
+发布 npm
+
+```bash
+ npm publish --force
+```

+ 13 - 0
index.html

@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>jnpf-bpmn</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>

+ 45 - 0
lib/alignElements/AlignElementContextPad.ts

@@ -0,0 +1,45 @@
+import { assign } from 'min-dash';
+import ICONS from 'bpmn-js/lib/features/align-elements/AlignElementsIcons';
+
+const AlignElementContextPad = (contextPadProvider: any, element: any) => {
+  let type = element.type;
+  const { _alignElements: alignElements, _translate: translate, _popupMenu: popupMenu, _rules: rules } = contextPadProvider;
+
+  return defaultContextPad();
+
+  function defaultContextPad() {
+    return [];
+    // assign({
+    //   'align-elements': {
+    //     group: 'align-elements',
+    //     title: '对齐方式',
+    //     action: {
+    //       click: function (event: any, elements: any) {
+    //         var position = getMenuPosition(elements);
+
+    //         // 展开对齐标签中包含的内容
+    //         contextPadProvider._popupMenu.open(elements, 'align-elements', position);
+    //       },
+    //     },
+    //   },
+    // });
+  }
+
+  function getMenuPosition(elements: any) {
+    var Y_OFFSET = 5;
+    var diagramContainer = contextPadProvider._canvas.getContainer(),
+      pad = contextPadProvider._contextPad.getPad(elements).html;
+    var diagramRect = diagramContainer.getBoundingClientRect(),
+      padRect = pad.getBoundingClientRect();
+    var top = padRect.top - diagramRect.top;
+    var left = padRect.left - diagramRect.left;
+    var pos = {
+      x: left,
+      y: top + padRect.height + Y_OFFSET,
+    };
+
+    return pos;
+  }
+};
+
+export default AlignElementContextPad;

+ 18 - 0
lib/alignElements/index.ts

@@ -0,0 +1,18 @@
+import AlignElementsContextPadProvider from 'bpmn-js/lib/features/align-elements/AlignElementsContextPadProvider';
+import AlignElementContextPad from './AlignElementContextPad';
+
+class YmAlignElementsContextPadProvider extends AlignElementsContextPadProvider {
+  constructor(contextPad: any, popupMenu: any, translate: any, canvas: any) {
+    super(contextPad, popupMenu, translate, canvas);
+  }
+
+  getMultiElementContextPadEntries(element: any): any {
+    // actions 获取默认的父节点contextPad
+    // let actions = super.getMultiElementContextPadEntries(element);
+    return AlignElementContextPad(this, element);
+  }
+}
+
+YmAlignElementsContextPadProvider.$inject = ['contextPad', 'popupMenu', 'translate', 'canvas'];
+
+export default YmAlignElementsContextPadProvider;

+ 217 - 0
lib/autoPlace/YmAutoPlaceUtil.ts

@@ -0,0 +1,217 @@
+// @ts-nocheck
+import { asTRBL, getOrientation, getMid } from 'diagram-js/lib/layout/LayoutUtil';
+import { find, reduce } from 'min-dash';
+import { Shape } from 'diagram-js/lib/model';
+import { Point } from 'diagram-js/lib/util/Types';
+import { DEFAULT_DISTANCE } from '../config/constants';
+
+// padding to detect element placement
+const PLACEMENT_DETECTION_PAD = 10;
+const DEFAULT_MAX_DISTANCE = 350;
+
+/**
+ * Get free position starting from given position.
+ *
+ * @param {Shape} source
+ * @param {Shape} element
+ * @param {Point} position
+ * @param {Function} getNextPosition
+ * @return {Point}
+ */
+export function findFreePosition(source: Shape, element: Shape, position: Point, getNextPosition: Function): Point {
+  let connectedAtPosition;
+  while ((connectedAtPosition = getConnectedAtPosition(source, position, element))) {
+    position = getNextPosition(element, position, connectedAtPosition);
+  }
+  return position;
+}
+
+/**
+ * Returns function that returns next position.
+ *
+ * @param {Object} nextPositionDirection
+ * @param {Object} [nextPositionDirection.x]
+ * @param {Object} [nextPositionDirection.y]
+ * @returns {Function}
+ */
+export function generateGetNextPosition(nextPositionDirection: { x?: any; y?: any }): Function {
+  return function (element: Shape, previousPosition: Point, connectedAtPosition: Point) {
+    const nextPosition: any = {
+      x: previousPosition.x,
+      y: previousPosition.y,
+    };
+    ['x', 'y'].forEach(axis => {
+      const nextPositionDirectionForAxis = nextPositionDirection[axis];
+      if (!nextPositionDirectionForAxis) return;
+      const dimension = axis === 'x' ? 'width' : 'height';
+      const margin = nextPositionDirectionForAxis.margin;
+      const minDistance = nextPositionDirectionForAxis.minDistance;
+      if (margin < 0) nextPosition[axis] = Math.min(connectedAtPosition[axis] + margin - element[dimension] / 2, previousPosition[axis] - minDistance + margin);
+      else {
+        nextPosition[axis] = Math.max(
+          connectedAtPosition[axis] + connectedAtPosition[dimension] + margin + element[dimension] / 2,
+          previousPosition[axis] + minDistance + margin,
+        );
+      }
+    });
+    return nextPosition;
+  };
+}
+
+/**
+ * Return target at given position, if defined.
+ *
+ * This takes connected elements from host and attachers
+ * into account, too.
+ */
+export function getConnectedAtPosition(source: Shape, position: Point, element: Shape): Shape | undefined {
+  const bounds = {
+    x: position.x - element.width / 2,
+    y: position.y - element.height / 2,
+    width: element.width,
+    height: element.height,
+  };
+  const closure = getAutoPlaceClosure(source, element);
+  return find(closure, (target: Shape) => {
+    if (target === element) return false;
+    const orientation = getOrientation(target, bounds, PLACEMENT_DETECTION_PAD);
+    return orientation === 'intersect';
+  });
+}
+
+/**
+ * Compute optimal distance between source and target based on existing connections to and from source.
+ * Assumes left-to-right and top-to-down modeling.
+ *
+ * @param {Shape} source
+ * @param {Object} [hints]
+ * @param {number} [hints.defaultDistance]
+ * @param {string} [hints.direction]
+ * @param {Function} [hints.filter]
+ * @param {Function} [hints.getWeight]
+ * @param {number} [hints.maxDistance]
+ * @param {string} [hints.reference]
+ * @return {number}
+ */
+export function getConnectedDistance(
+  source: Shape,
+  hints?: {
+    defaultDistance?: number;
+    direction?: string;
+    filter?: Function;
+    getWeight?: Function;
+    maxDistance?: number;
+    reference?: string;
+  },
+): number {
+  if (!hints) hints = {};
+  function getDefaultWeight(connection: any): number {
+    return connection.source === source ? 1 : -1;
+  }
+  const defaultDistance = hints.defaultDistance || DEFAULT_DISTANCE;
+  const direction = hints.direction || 'e';
+  let filter = hints.filter;
+  const getWeight = hints.getWeight || getDefaultWeight;
+  const maxDistance = hints.maxDistance || DEFAULT_MAX_DISTANCE;
+  const reference = hints.reference || 'start';
+  if (!filter) filter = noneFilter;
+  function getDistance(a: any, b: any): number {
+    if (direction === 'n') {
+      if (reference === 'start') {
+        return asTRBL(a).top - asTRBL(b).bottom;
+      } else if (reference === 'center') {
+        return asTRBL(a).top - getMid(b).y;
+      } else {
+        return asTRBL(a).top - asTRBL(b).top;
+      }
+    } else if (direction === 'w') {
+      if (reference === 'start') return asTRBL(a).left - asTRBL(b).right;
+      else if (reference === 'center') return asTRBL(a).left - getMid(b).x;
+      else return asTRBL(a).left - asTRBL(b).left;
+    } else if (direction === 's') {
+      if (reference === 'start') return asTRBL(b).top - asTRBL(a).bottom;
+      else if (reference === 'center') return getMid(b).y - asTRBL(a).bottom;
+      else return asTRBL(b).bottom - asTRBL(a).bottom;
+    } else {
+      if (reference === 'start') return asTRBL(b).left - asTRBL(a).right;
+      else if (reference === 'center') return getMid(b).x - asTRBL(a).right;
+      else return asTRBL(b).right - asTRBL(a).right;
+    }
+  }
+
+  const sourcesDistances = source.incoming.filter(filter).map((connection: any) => {
+    const weight = getWeight(connection);
+    const distance = weight < 5 ? getDistance(connection.source, source) : getDistance(source, connection.source);
+    return {
+      id: connection.source.id,
+      distance: distance,
+      weight: weight,
+    };
+  });
+
+  const targetsDistances = source.outgoing.filter(filter).map((connection: any) => {
+    const weight = getWeight(connection);
+    const distance = weight > 5 ? getDistance(source, connection.target) : getDistance(connection.target, source);
+    return {
+      id: connection.target.id,
+      distance: distance,
+      weight: weight,
+    };
+  });
+
+  const distances = sourcesDistances.concat(targetsDistances).reduce((accumulator, currentValue) => {
+    accumulator[currentValue.id + '__weight_' + currentValue.weight] = currentValue;
+    return accumulator;
+  }, {});
+
+  const distancesGrouped = reduce(
+    distances,
+    (accumulator, currentValue) => {
+      const distance = currentValue.distance;
+      const weight = currentValue.weight;
+      if (distance < 0 || distance > maxDistance) return accumulator;
+      if (!accumulator[String(distance)]) accumulator[String(distance)] = 0;
+      accumulator[String(distance)] += 1 * weight;
+      if (!accumulator.distance || accumulator[accumulator.distance] < accumulator[String(distance)]) accumulator.distance = distance;
+      return accumulator;
+    },
+    {},
+  );
+  return distancesGrouped.distance || defaultDistance;
+}
+/**
+ * Returns all connected elements around the given source.
+ * This includes:
+ *   - connected elements
+ *   - host connected elements
+ *   - attachers connected elements
+ * @param {Shape} source
+ * @return {Array<Shape>}
+ */
+function getAutoPlaceClosure(source: Shape, element: Shape): Array<Shape> {
+  let allConnected = getConnected(source);
+  if (source.host) allConnected = allConnected.concat(getConnected(source.host));
+  if (source.attachers)
+    allConnected = allConnected.concat(
+      source.attachers.reduce((shapes: Array<Shape>, attacher: Shape) => {
+        return shapes.concat(getConnected(attacher));
+      }, []),
+    );
+  return allConnected;
+}
+function getConnected(element: Shape): Array<Shape> {
+  return getTargets(element).concat(getSources(element));
+}
+function getSources(shape: Shape): Array<Shape> {
+  return shape.incoming.map((connection: any) => {
+    return connection.source;
+  });
+}
+function getTargets(shape: Shape): Array<Shape> {
+  return shape.outgoing.map((connection: any) => {
+    return connection.target;
+  });
+}
+function noneFilter(): boolean {
+  return true;
+}

+ 16 - 0
lib/autoPlace/index.ts

@@ -0,0 +1,16 @@
+// /**
+//  * A palette that allows you to create BPMN _and_ custom elements.
+//  */
+// import AutoPlace from 'bpmn-js/lib/features/auto-place/BpmnAutoPlace.js';
+// import { getYmNewShapePosition } from '../autoPlaceUtil';
+// function YmAutoPlace(eventBus: any) {
+//   eventBus.on('autoPlace', function (context: any) {
+//     var shape = context.shape,
+//       source = context.source;
+//     return getYmNewShapePosition(source, shape);
+//   });
+// }
+//
+// YmAutoPlace.$inject = ['eventBus'];
+//
+// export default YmAutoPlace;

+ 21 - 0
lib/business/index.ts

@@ -0,0 +1,21 @@
+class YmBusinessData {
+  data: any = { layout: { value: 'horizontal' } };
+  constructor() {}
+
+  // 设置业务属性参数
+  setValue(elementId: string, value: any) {
+    if (this.data[elementId]) {
+      this.data[elementId] = { ...this.data[elementId], ...value };
+    } else {
+      this.data[elementId] = { ...value };
+    }
+  }
+
+  getValue(elementId: string = '', key: string = '') {
+    if (elementId && key) return (this.data[elementId] && this.data[elementId][key]) || '';
+    if (elementId && !key) return this.data[elementId] || '';
+    return this.data || {};
+  }
+}
+
+export default YmBusinessData;

+ 148 - 0
lib/commandStack/index.ts

@@ -0,0 +1,148 @@
+// @ts-nocheck
+import CommandStack from 'diagram-js/lib/command/CommandStack';
+import { bpmnGroup } from '../config/variableName';
+
+class CustomCommandStack extends CommandStack {
+  constructor(eventBus, modeling) {
+    super(eventBus, modeling);
+  }
+}
+
+let sameId = '';
+let isVerticalSameId = false;
+let isHorizontalSameId = false;
+
+CommandStack.prototype._internalExecute = function (action, redo) {
+  const command = action.command;
+  const context = action.context;
+  const handler = this._getHandler(command);
+  if (!handler) throw new Error('no command handler registered for <' + command + '>');
+  this._pushAction(action);
+  if (!redo) {
+    this._fire(command, 'preExecute', action);
+    if (handler.preExecute) handler.preExecute(context);
+    this._fire(command, 'preExecuted', action);
+  }
+  // guard against illegal nested command stack invocations
+  this._atomicDo(() => {
+    this._fire(command, 'execute', action);
+    if (handler.execute) this._markDirty(handler.execute(context));
+    if (action.command == 'element.updateProperties' || sameId == action.id) {
+      sameId = action.id;
+    } else {
+      if (action.id != 'updateProperties') this._executedAction(action, redo);
+    }
+    this._fire(command, 'executed', action);
+  });
+  if (!redo) {
+    this._fire(command, 'postExecute', action);
+    if (handler.postExecute) handler.postExecute(context);
+    this._fire(command, 'postExecuted', action);
+  }
+  this._popAction(action);
+};
+CommandStack.prototype._pushAction = function (action, isSameId = false) {
+  const execution = this._currentExecution;
+  const actions = execution.actions;
+  const baseAction = actions[0];
+  if (execution.atomic) throw new Error('illegal invocation in <execute> or <revert> phase (action: ' + action.command + ')');
+  if (action.command == 'element.updateProperties') action.id = 'updateProperties';
+  if (!action.id) action.id = (baseAction && baseAction.id) || this._createId(isSameId);
+  if (isVerticalSameId) action.type = 'beautification.vertical';
+  if (isHorizontalSameId) action.type = 'beautification.horizontal';
+  actions.push(action);
+};
+CommandStack.prototype._createId = function (isSameId = false) {
+  if (isSameId) {
+    this._uid = this._uid - 1;
+  }
+  return isHorizontalSameId || isVerticalSameId ? this._uid : this._uid++;
+};
+CommandStack.prototype.execute = function (command, context) {
+  let isSameId = false;
+  if (!command) throw new Error('command required');
+  // 设置一个开始和一个结束状态 如果遇到遇到开始 则全局设置该值为true 后续的入栈id设置成相同id
+  if (command === 'vertical.action.same.id.start') return (isVerticalSameId = true);
+  if (command === 'vertical.action.same.id.end') {
+    this._uid++;
+    return (isVerticalSameId = false);
+  }
+  if (command === 'horizontal.action.same.id.start') return (isHorizontalSameId = true);
+  if (command === 'horizontal.action.same.id.end') {
+    this._uid++;
+    return (isHorizontalSameId = false);
+  }
+
+  if (command === 'shape.create' && context.shape?.type === bpmnGroup) {
+    isSameId = true;
+  }
+  if (command === 'shape.resize') {
+    if (!(isVerticalSameId || isHorizontalSameId)) isSameId = true;
+  }
+
+  this._currentExecution.trigger = 'execute';
+  const action = { command: command, context: context };
+  this._pushAction(action, isSameId);
+  this._internalExecute(action);
+  this._popAction(action);
+};
+CommandStack.prototype.undo = function () {
+  let action = this._getUndoAction(),
+    next;
+  if (action) {
+    this._currentExecution.trigger = 'undo';
+    this._pushAction(action);
+    while (action) {
+      this._internalUndo(action);
+      next = this._getUndoAction();
+      if (!next || next.id !== action.id) {
+        break;
+      }
+      action = next;
+    }
+    if (action.command === 'shape.resize' || (action.command === 'shape.create' && action.context.shape?.type === bpmnGroup)) {
+      let id = action.id - 1;
+      while (action) {
+        this._internalUndo(action);
+        next = this._getUndoAction();
+        if (!next || next.id !== id) break;
+        action = next;
+      }
+    }
+    this._popAction();
+  }
+};
+CommandStack.prototype.redo = function () {
+  let action = this._getRedoAction(),
+    resizeNext,
+    next;
+  if (action) {
+    this._currentExecution.trigger = 'redo';
+    this._pushAction(action);
+    while (action) {
+      this._internalExecute(action, true);
+      next = this._getRedoAction();
+      if (!next || next.id !== action.id) {
+        if (next?.command === 'shape.resize') {
+          resizeNext = next;
+        }
+        break;
+      }
+      action = next;
+    }
+    if (resizeNext) {
+      let id = resizeNext.id + 1;
+      while (resizeNext) {
+        this._internalExecute(resizeNext, true);
+        next = this._getRedoAction();
+        if (!next || next.id !== id) {
+          break;
+        }
+        action = next;
+      }
+    }
+    this._popAction();
+  }
+};
+
+export default CustomCommandStack;

+ 2 - 0
lib/config/constants/index.ts

@@ -0,0 +1,2 @@
+export const DEFAULT_DISTANCE = 120;
+export const DEFAULT_CONNECT = 160;

+ 365 - 0
lib/config/contextPad/index.ts

@@ -0,0 +1,365 @@
+import {
+  bpmnStart,
+  bpmnTask,
+  bpmnEnd,
+  bpmnSubFlow,
+  bpmnInclusive,
+  bpmnParallel,
+  bpmnExclusive,
+  typeStart,
+  typeTask,
+  typeEnd,
+  typeLabel,
+  typeSubFlow,
+  bpmnGroup,
+  typeGroup,
+  typeTrigger,
+  typeGetData,
+  typeAddData,
+  typeUpdateData,
+  typeDelData,
+  typeInterface,
+  typeLaunchFlow,
+  typeMessage,
+  typeSchedule,
+  bpmnExecute,
+  bpmnWebhook,
+  bpmnNotice,
+  bpmnTime,
+  bpmnEvent,
+  typeEventTrigger,
+  typeTimeTrigger,
+  typeNoticeTrigger,
+  typeWebhookTrigger,
+  bpmnCopy,
+  typeCopy,
+  bpmnPaste,
+  typePaste,
+  bpmnProcessing,
+  typeProcessing,
+  bpmnChoose,
+  typeChoose,
+  typeInclusion,
+  typeParallel,
+  typeExclusive,
+  bpmnOutside,
+  typeOutside,
+} from '../variableName';
+
+const START = {
+  name: 'append.jnpf-start',
+  group: 'model',
+  className: 'context-pad-start icon-ym icon-ym-flow-node-start',
+  icon: 'icon-ym icon-ym-flow-node-start',
+  title: '流程发起',
+  ymName: 'jnpf-startEvent',
+  type: bpmnStart,
+  elementName: typeStart,
+  wnType: typeStart,
+};
+const APPROVER = {
+  name: 'append.jnpf-task',
+  group: 'model',
+  className: 'context-pad-approver icon-ym icon-ym-flow-node-approve',
+  title: '审批节点',
+  type: bpmnTask,
+  elementName: typeTask,
+  wnType: typeTask,
+};
+const PROCESSING = {
+  name: 'append.jnpf-processing',
+  group: 'model',
+  className: 'context-pad-processing icon-ym icon-ym-generator-todo',
+  title: '办理节点',
+  type: bpmnProcessing,
+  elementName: typeProcessing,
+  wnType: typeProcessing,
+};
+const SUBFLOW = {
+  name: 'append.jnpf-subFlow',
+  group: 'model',
+  className: 'context-pad-sub-flow icon-ym icon-ym-flow-node-subFlow',
+  title: '子流程',
+  type: bpmnSubFlow,
+  elementName: typeSubFlow,
+  wnType: typeSubFlow,
+};
+const TRIGGER = {
+  name: 'append.jnpf-trigger',
+  group: 'model',
+  className: 'context-pad-trigger icon-ym icon-ym-flow-trigger-event',
+  title: '触发节点',
+  type: bpmnTask,
+  elementName: typeTrigger,
+  wnType: typeTrigger,
+};
+const OUTSIDE = {
+  name: 'append.jnpf-outside',
+  group: 'model',
+  className: 'context-pad-outside icon-ym icon-ym-flow-node-external',
+  title: '外部节点',
+  type: bpmnOutside,
+  elementName: typeOutside,
+  wnType: typeOutside,
+};
+const GROUP = {
+  name: 'append.jnpf-group',
+  group: 'model',
+  className: 'context-pad-sub-flow icon-ym icon-ym-flow-node-subFlow',
+  title: '分组',
+  type: bpmnGroup,
+  elementName: typeGroup,
+  wnType: typeGroup,
+};
+const END = {
+  name: 'append.jnpf-end',
+  group: 'model',
+  className: 'context-pad-end icon-ym icon-ym-flow-node-end',
+  title: '结束',
+  type: bpmnEnd,
+  elementName: typeEnd,
+  wnType: typeEnd,
+};
+const CONNECT = {
+  name: 'append.jnpf-connect',
+  group: 'connect',
+  className: 'context-pad-connect icon-ym icon-ym-flow-line',
+  title: '连线',
+  type: 'connect',
+  wnType: typeLabel,
+};
+const DELETE = {
+  name: 'delete',
+  group: 'edit',
+  className: 'context-pad-delete icon-ym icon-ym-app-delete',
+  title: '删除',
+  ymName: 'jnpf-delete',
+  type: 'delete',
+};
+const INCLUSIVE = {
+  name: 'append.jnpf-inclusive',
+  group: 'model',
+  className: 'context-pad-condition icon-ym icon-ym-flow-node-condition-multiple',
+  title: '包容分支 ',
+  type: bpmnInclusive,
+  elementName: typeInclusion,
+  wnType: typeInclusion,
+};
+const PARALLEL = {
+  name: 'append.jnpf-parallel',
+  group: 'model',
+  className: 'context-pad-interflow icon-ym icon-ym-flow-node-parallel',
+  title: '并行分支',
+  type: bpmnParallel,
+  elementName: typeParallel,
+  wnType: typeParallel,
+};
+const EXCLUSIVE = {
+  name: 'append.jnpf-exclusive',
+  group: 'model',
+  className: 'context-pad-branch icon-ym icon-ym-flow-node-condition-single',
+  title: '排它分支',
+  type: bpmnExclusive,
+  elementName: typeExclusive,
+  wnType: typeExclusive,
+};
+const GETDATA = {
+  name: 'append.jnpf-getData',
+  group: 'model',
+  className: 'context-pad-getData icon-ym icon-ym-header-search',
+  title: '获取数据',
+  type: bpmnExecute,
+  elementName: typeGetData,
+  wnType: typeGetData,
+};
+const ADDDATA = {
+  name: 'append.jnpf-addData',
+  group: 'model',
+  className: 'context-pad-addData icon-ym icon-ym-btn-add',
+  title: '新增数据',
+  type: bpmnExecute,
+  elementName: typeAddData,
+  wnType: typeAddData,
+};
+const UPDATEDATA = {
+  name: 'append.jnpf-updateData',
+  group: 'model',
+  className: 'context-pad-updateData icon-ym icon-ym-generator-annular',
+  title: '更新数据',
+  type: bpmnExecute,
+  elementName: typeUpdateData,
+  wnType: typeUpdateData,
+};
+const DELDATA = {
+  name: 'append.jnpf-delData',
+  group: 'model',
+  className: 'context-pad-delData icon-ym icon-ym-btn-clearn',
+  title: '删除数据',
+  type: bpmnExecute,
+  elementName: typeDelData,
+  wnType: typeDelData,
+};
+const INTERFACE = {
+  name: 'append.jnpf-interface',
+  group: 'model',
+  className: 'context-pad-interface icon-ym icon-ym-options',
+  title: '数据接口',
+  type: bpmnExecute,
+  elementName: typeInterface,
+  wnType: typeInterface,
+};
+const LAUNCH = {
+  name: 'append.jnpf-launch',
+  group: 'model',
+  className: 'context-pad-launch icon-ym icon-ym-flow-node-branch',
+  title: '发起审批',
+  type: bpmnExecute,
+  elementName: typeLaunchFlow,
+  wnType: typeLaunchFlow,
+};
+const MESSAGE = {
+  name: 'append.jnpf-message',
+  group: 'model',
+  className: 'context-pad-message icon-ym icon-ym-header-message',
+  title: '消息通知',
+  type: bpmnExecute,
+  elementName: typeMessage,
+  wnType: typeMessage,
+};
+const SCHEDULE = {
+  name: 'append.jnpf-schedule',
+  group: 'model',
+  className: 'context-pad-schedule icon-ym icon-ym-xingcheng',
+  title: '创建日程',
+  type: bpmnExecute,
+  elementName: typeSchedule,
+  wnType: typeSchedule,
+};
+const EVENTTRIGGER = {
+  name: 'append.jnpf-event-trigger',
+  group: 'model',
+  className: 'context-pad-event-trigger icon-ym icon-ym-flow-trigger-event',
+  title: '事件触发',
+  type: bpmnEvent,
+  elementName: typeEventTrigger,
+  wnType: typeEventTrigger,
+};
+
+const TIMETRIGGER = {
+  name: 'append.jnpf-timeout-trigger',
+  group: 'model',
+  className: 'context-pad-timeout-trigger icon-ym icon-ym-flow-trigger-timer',
+  title: '定时触发',
+  type: bpmnTime,
+  elementName: typeTimeTrigger,
+  wnType: typeTimeTrigger,
+};
+const NOTICETRIGGER = {
+  name: 'append.jnpf-notice-trigger',
+  group: 'model',
+  className: 'context-pad-notice-trigger icon-ym icon-ym-flow-trigger-notice',
+  title: '通知触发',
+  type: bpmnNotice,
+  elementName: typeNoticeTrigger,
+  wnType: typeNoticeTrigger,
+};
+const WEBHOOKTRIGGER = {
+  name: 'append.jnpf-webhook-trigger',
+  group: 'model',
+  className: 'context-pad-webhook-trigger icon-ym icon-ym-flow-trigger-webhook',
+  title: 'webhook',
+  type: bpmnWebhook,
+  elementName: typeWebhookTrigger,
+  wnType: typeWebhookTrigger,
+};
+const COPY = {
+  name: 'append.jnpf-copy',
+  group: 'model',
+  className: 'context-pad-copy ym-custom ym-custom-content-copy',
+  title: '复制',
+  type: bpmnCopy,
+  elementName: null,
+  wnType: typeCopy,
+};
+const PASTE = {
+  name: 'append.jnpf-paste',
+  group: 'model',
+  className: 'context-pad-paste ym-custom ym-custom-content-paste',
+  title: '粘贴',
+  type: bpmnPaste,
+  elementName: null,
+  wnType: typePaste,
+};
+const CHOOSE = {
+  name: 'append.jnpf-choose',
+  group: 'model',
+  className: 'context-pad-condition icon-ym icon-ym-flow-node-branch',
+  title: '选择分支',
+  type: bpmnChoose,
+  elementName: typeChoose,
+  wnType: typeChoose,
+};
+
+interface jnpfConfigBpmnContextPadProp {
+  start: any;
+  approver: any;
+  subFlow: any;
+  end: any;
+  connect: any;
+  del: any;
+  inclusive: any;
+  parallel: any;
+  exclusive: any;
+  group: any;
+  trigger: any;
+  getData: any;
+  addData: any;
+  updateData: any;
+  delData: any;
+  interfaceData: any;
+  launch: any;
+  message: any;
+  schedule: any;
+  event: any;
+  timeout: any;
+  notice: any;
+  webhook: any;
+  copy: any;
+  paste: any;
+  processing: any;
+  choose: any;
+  outside: any;
+}
+
+const jnpfConfigBpmnContextPad: jnpfConfigBpmnContextPadProp = {
+  start: START,
+  approver: APPROVER,
+  subFlow: SUBFLOW,
+  end: END,
+  connect: CONNECT,
+  del: DELETE,
+  inclusive: INCLUSIVE,
+  parallel: PARALLEL,
+  exclusive: EXCLUSIVE,
+  group: GROUP,
+  trigger: TRIGGER,
+  getData: GETDATA,
+  addData: ADDDATA,
+  updateData: UPDATEDATA,
+  delData: DELDATA,
+  interfaceData: INTERFACE,
+  launch: LAUNCH,
+  message: MESSAGE,
+  schedule: SCHEDULE,
+  event: EVENTTRIGGER,
+  timeout: TIMETRIGGER,
+  notice: NOTICETRIGGER,
+  webhook: WEBHOOKTRIGGER,
+  copy: COPY,
+  paste: PASTE,
+  processing: PROCESSING,
+  choose: CHOOSE,
+  outside: OUTSIDE
+};
+
+export { jnpfConfigBpmnContextPad, jnpfConfigBpmnContextPadProp };

+ 35 - 0
lib/config/element/approver/index.ts

@@ -0,0 +1,35 @@
+import { bpmnTask, typeTask } from '../../variableName';
+
+const jnpfApproverConfig: any = {
+  name: typeTask,
+  shapeType: bpmnTask,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnTask,
+    },
+  },
+  palette: {
+    name: 'create.jnpf-task',
+    group: 'model',
+    className: 'icon-jnpf-create icon-jnpf-task',
+    title: '创建一个类型为jnpf-task的任务节点',
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-node-approve',
+    iconColor: '#1DACEB',
+    titleColor: 'linear-gradient(90deg, #C0EDF8 0%, #B4DEF2 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '审批节点',
+    bodyDefaultText: '请设置审批人',
+  },
+  contextPad: {
+    default: true, // contextPad 中的元素使用默认 否则遵循自定义
+  },
+  properties: {},
+};
+
+export { jnpfApproverConfig };

+ 29 - 0
lib/config/element/end/index.ts

@@ -0,0 +1,29 @@
+import { jnpfConfigBpmnContextPad } from '../../contextPad';
+import { bpmnEnd, typeEnd } from '../../variableName';
+
+const { del } = jnpfConfigBpmnContextPad;
+
+const jnpfEndConfig: any = {
+  name: typeEnd,
+  shapeType: bpmnEnd,
+  palette: {
+    name: 'create.jnpf-task',
+    group: 'model',
+    className: 'icon-jnpf-create icon-jnpf-task',
+    title: '创建一个类型为jnpf-task的任务节点',
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-node-end',
+    iconColor: '#8B8BA0',
+    titleColor: '#EDF3F8',
+    attr: { x: 0, y: 0, width: 90, rx: 16, height: 32 },
+    rendererName: '流程结束',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { del }, // 自定义 只有default = false 开启
+  },
+  properties: {},
+};
+
+export { jnpfEndConfig };

+ 32 - 0
lib/config/element/execute/addData/index.ts

@@ -0,0 +1,32 @@
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+import { bpmnAddData, typeAddData } from '../../../variableName';
+const { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, del, paste, copy } = jnpfConfigBpmnContextPad;
+const jnpfAddDataConfig: any = {
+  name: typeAddData,
+  shapeType: bpmnAddData,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnAddData,
+    },
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-btn-add',
+    iconColor: '#439815',
+    titleColor: 'linear-gradient(90deg, #D6FABF 0%, #68C62C 100%',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '新增数据',
+    bodyDefaultText: '请设置新增数据',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, del },
+    otherCustomization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del },
+  },
+  properties: {},
+};
+
+export { jnpfAddDataConfig };

+ 32 - 0
lib/config/element/execute/delData/index.ts

@@ -0,0 +1,32 @@
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+import { bpmnDelData, typeDelData } from '../../../variableName';
+const { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, del, paste, copy } = jnpfConfigBpmnContextPad;
+const jnpfDelDataConfig: any = {
+  name: typeDelData,
+  shapeType: bpmnDelData,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnDelData,
+    },
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-btn-clearn',
+    iconColor: '#DD363C',
+    titleColor: 'linear-gradient(90deg, #FFCDC1 0%, #FF8E92 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '删除数据',
+    bodyDefaultText: '请设置删除节点',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, del },
+    otherCustomization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del },
+  },
+  properties: {},
+};
+
+export { jnpfDelDataConfig };

+ 32 - 0
lib/config/element/execute/getData/index.ts

@@ -0,0 +1,32 @@
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+import { bpmnGetData, typeGetData } from '../../../variableName';
+const { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, del, paste, copy } = jnpfConfigBpmnContextPad;
+const jnpfGetDataConfig: any = {
+  name: typeGetData,
+  shapeType: bpmnGetData,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnGetData,
+    },
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-header-search',
+    iconColor: '#4936DD',
+    titleColor: 'linear-gradient(90deg, #C1C8FF 0%, #A481F2 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '获取数据',
+    bodyDefaultText: '请设置获取数据',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, del },
+    otherCustomization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del },
+  },
+  properties: {},
+};
+
+export { jnpfGetDataConfig };

+ 32 - 0
lib/config/element/execute/interface/index.ts

@@ -0,0 +1,32 @@
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+import { bpmnInterface, typeInterface } from '../../../variableName';
+const { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, del, paste, copy } = jnpfConfigBpmnContextPad;
+const jnpfInterfaceConfig: any = {
+  name: typeInterface,
+  shapeType: bpmnInterface,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnInterface,
+    },
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-options',
+    iconColor: '#3C5EEF',
+    titleColor: 'linear-gradient(90deg, #D0DCFF 0%, #90A5FF 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '数据接口',
+    bodyDefaultText: '请设置数据接口',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, del },
+    otherCustomization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del },
+  },
+  properties: {},
+};
+
+export { jnpfInterfaceConfig };

+ 32 - 0
lib/config/element/execute/launch/index.ts

@@ -0,0 +1,32 @@
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+import { bpmnLaunchFlow, typeLaunchFlow } from '../../../variableName';
+const { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del } = jnpfConfigBpmnContextPad;
+const jnpfLaunchConfig: any = {
+  name: typeLaunchFlow,
+  shapeType: bpmnLaunchFlow,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnLaunchFlow,
+    },
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-node-branch',
+    iconColor: '#5ED87F',
+    titleColor: 'linear-gradient(90deg, #B6F7BB 0%, #5ED87F 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '发起审批',
+    bodyDefaultText: '请设置发起审批',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, del },
+    otherCustomization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del },
+  },
+  properties: {},
+};
+
+export { jnpfLaunchConfig };

+ 32 - 0
lib/config/element/execute/message/index.ts

@@ -0,0 +1,32 @@
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+import { bpmnMessage, typeMessage } from '../../../variableName';
+const { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del } = jnpfConfigBpmnContextPad;
+const jnpfMessageConfig: any = {
+  name: typeMessage,
+  shapeType: bpmnMessage,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnMessage,
+    },
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-header-message',
+    iconColor: '#F5CD61',
+    titleColor: 'linear-gradient(90deg, #FCE6AB 0%, #F5CD61 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '消息通知',
+    bodyDefaultText: '请设置消息通知',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, del },
+    otherCustomization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del },
+  },
+  properties: {},
+};
+
+export { jnpfMessageConfig };

+ 32 - 0
lib/config/element/execute/schedule/index.ts

@@ -0,0 +1,32 @@
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+import { bpmnSchedule, typeSchedule } from '../../../variableName';
+const { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del } = jnpfConfigBpmnContextPad;
+const jnpfScheduleConfig: any = {
+  name: typeSchedule,
+  shapeType: bpmnSchedule,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnSchedule,
+    },
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-xingcheng',
+    iconColor: '#7DB2F0',
+    titleColor: 'linear-gradient(90deg, #ABE6FC 0%, #7DB2F0 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '创建日程',
+    bodyDefaultText: '请设置创建日程信息',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, del },
+    otherCustomization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del },
+  },
+  properties: {},
+};
+
+export { jnpfScheduleConfig };

+ 32 - 0
lib/config/element/execute/updateData/index.ts

@@ -0,0 +1,32 @@
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+import { bpmnUpdateData, typeUpdateData } from '../../../variableName';
+const { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del } = jnpfConfigBpmnContextPad;
+const jnpfUpdateDataConfig: any = {
+  name: typeUpdateData,
+  shapeType: bpmnUpdateData,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnUpdateData,
+    },
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-generator-annular',
+    iconColor: '#24BEC4',
+    titleColor: 'linear-gradient(90deg, #CDFAF3 0%, #55E2CC 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '更新数据',
+    bodyDefaultText: '请设置更新数据',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, del },
+    otherCustomization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del },
+  },
+  properties: {},
+};
+
+export { jnpfUpdateDataConfig };

+ 38 - 0
lib/config/element/gateway/choose/index.ts

@@ -0,0 +1,38 @@
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+import { bpmnChoose, typeChoose } from '../../../variableName';
+const { approver } = jnpfConfigBpmnContextPad;
+
+const jnpfChooseConfig: any = {
+  name: typeChoose,
+  shapeType: bpmnChoose,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: typeChoose,
+    },
+  },
+  palette: {
+    name: 'create.jnpf-task',
+    group: 'model',
+    className: 'icon-jnpf-create icon-jnpf-task',
+    title: '创建一个类型为jnpf-task的任务节点',
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-node-approve',
+    iconColor: '#1DACEB',
+    titleColor: '#C0EDF8',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, width: 90, rx: 16, height: 32 },
+    rendererName: '新增分支',
+    bodyDefaultText: '请设置审批人',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { approver }, // 自定义 只有default = false 开启
+  },
+  properties: {},
+};
+
+export { jnpfChooseConfig };

+ 2 - 0
lib/config/element/gateway/exclusive/index.d.ts

@@ -0,0 +1,2 @@
+declare const jnpfExclusiveConfig: any;
+export { jnpfExclusiveConfig };

+ 35 - 0
lib/config/element/gateway/exclusive/index.js

@@ -0,0 +1,35 @@
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+import { bpmnExclusive } from '../../../variableName';
+const { approver } = jnpfConfigBpmnContextPad;
+const jnpfExclusiveConfig = {
+    name: bpmnExclusive,
+    shapeType: bpmnExclusive,
+    element: {
+        label: 'Timer',
+        actionName: 'replace-with-timer',
+        className: 'bpmn-icon-timer',
+        target: {
+            type: bpmnExclusive,
+        },
+    },
+    palette: {
+        name: 'create.yinmai-timer',
+        group: 'model',
+        className: 'icon-yinmai-create icon-yinmai-timer',
+    },
+    renderer: {
+        icon: 'icon-ym icon-ym-flow-node-approve',
+        iconColor: '#1DACEB',
+        titleColor: '#C0EDF8',
+        background: '#ffffff',
+        attr: { x: 0, y: 0, width: 90, rx: 16, height: 32 },
+        rendererName: '条件分支',
+        bodyDefaultText: '请设置审批人',
+    },
+    contextPad: {
+        default: false,
+        customization: { approver },
+    },
+    properties: {},
+};
+export { jnpfExclusiveConfig };

+ 36 - 0
lib/config/element/gateway/exclusive/index.ts

@@ -0,0 +1,36 @@
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+import { bpmnExclusive } from '../../../variableName';
+const { approver } = jnpfConfigBpmnContextPad;
+const jnpfExclusiveConfig: any = {
+  name: bpmnExclusive,
+  shapeType: bpmnExclusive,
+  element: {
+    label: 'Timer',
+    actionName: 'replace-with-timer',
+    className: 'bpmn-icon-timer',
+    target: {
+      type: bpmnExclusive,
+    },
+  },
+  palette: {
+    name: 'create.yinmai-timer',
+    group: 'model',
+    className: 'icon-yinmai-create icon-yinmai-timer',
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-node-approve',
+    iconColor: '#1DACEB',
+    titleColor: '#C0EDF8',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, width: 90, rx: 16, height: 32 },
+    rendererName: '条件分支',
+    bodyDefaultText: '请设置审批人',
+  },
+  contextPad: {
+    default: false,
+    customization: { approver }, // 自定义 只有default = false 开启
+  },
+  properties: {},
+};
+
+export { jnpfExclusiveConfig };

+ 2 - 0
lib/config/element/gateway/inclusive/index.d.ts

@@ -0,0 +1,2 @@
+declare const jnpfInclusiveConfig: any;
+export { jnpfInclusiveConfig };

+ 36 - 0
lib/config/element/gateway/inclusive/index.js

@@ -0,0 +1,36 @@
+import { bpmnInclusive, typeInclusion } from '../../../variableName';
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+const { approver } = jnpfConfigBpmnContextPad;
+const jnpfInclusiveConfig = {
+    name: typeInclusion,
+    shapeType: bpmnInclusive,
+    element: {
+        label: 'Task',
+        actionName: 'replace-with-task',
+        className: 'bpmn-icon-task',
+        target: {
+            type: bpmnInclusive,
+        },
+    },
+    palette: {
+        name: 'create.jnpf-task',
+        group: 'model',
+        className: 'icon-jnpf-create icon-jnpf-task',
+        title: '创建一个类型为jnpf-task的任务节点',
+    },
+    renderer: {
+        icon: 'icon-ym icon-ym-flow-node-approve',
+        iconColor: '#1DACEB',
+        titleColor: '#C0EDF8',
+        background: '#ffffff',
+        attr: { x: 0, y: 0, width: 90, rx: 16, height: 32 },
+        rendererName: '条件分支',
+        bodyDefaultText: '请设置审批人',
+    },
+    contextPad: {
+        default: false,
+        customization: { approver },
+    },
+    properties: {},
+};
+export { jnpfInclusiveConfig };

+ 37 - 0
lib/config/element/gateway/inclusive/index.ts

@@ -0,0 +1,37 @@
+import { bpmnInclusive, typeInclusion } from '../../../variableName';
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+const { approver } = jnpfConfigBpmnContextPad;
+const jnpfInclusiveConfig: any = {
+  name: typeInclusion,
+  shapeType: bpmnInclusive,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnInclusive,
+    },
+  },
+  palette: {
+    name: 'create.jnpf-task',
+    group: 'model',
+    className: 'icon-jnpf-create icon-jnpf-task',
+    title: '创建一个类型为jnpf-task的任务节点',
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-node-approve',
+    iconColor: '#1DACEB',
+    titleColor: '#C0EDF8',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, width: 90, rx: 16, height: 32 },
+    rendererName: '条件分支',
+    bodyDefaultText: '请设置审批人',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { approver }, // 自定义 只有default = false 开启
+  },
+  properties: {},
+};
+
+export { jnpfInclusiveConfig };

+ 2 - 0
lib/config/element/gateway/parallel/index.d.ts

@@ -0,0 +1,2 @@
+declare const jnpfParallelConfig: any;
+export { jnpfParallelConfig };

+ 36 - 0
lib/config/element/gateway/parallel/index.js

@@ -0,0 +1,36 @@
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+import { bpmnParallel, typeParallel } from '../../../variableName';
+const { approver } = jnpfConfigBpmnContextPad;
+const jnpfParallelConfig = {
+    name: typeParallel,
+    shapeType: bpmnParallel,
+    element: {
+        label: 'Task',
+        actionName: 'replace-with-task',
+        className: 'bpmn-icon-task',
+        target: {
+            type: typeParallel,
+        },
+    },
+    palette: {
+        name: 'create.jnpf-task',
+        group: 'model',
+        className: 'icon-jnpf-create icon-jnpf-task',
+        title: '创建一个类型为jnpf-task的任务节点',
+    },
+    renderer: {
+        icon: 'icon-ym icon-ym-flow-node-approve',
+        iconColor: '#1DACEB',
+        titleColor: '#C0EDF8',
+        background: '#ffffff',
+        attr: { x: 0, y: 0, width: 90, rx: 16, height: 32 },
+        rendererName: '并行分支',
+        bodyDefaultText: '请设置审批人',
+    },
+    contextPad: {
+        default: false,
+        customization: { approver },
+    },
+    properties: {},
+};
+export { jnpfParallelConfig };

+ 38 - 0
lib/config/element/gateway/parallel/index.ts

@@ -0,0 +1,38 @@
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+import { bpmnParallel, typeParallel } from '../../../variableName';
+const { approver } = jnpfConfigBpmnContextPad;
+
+const jnpfParallelConfig: any = {
+  name: typeParallel,
+  shapeType: bpmnParallel,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: typeParallel,
+    },
+  },
+  palette: {
+    name: 'create.jnpf-task',
+    group: 'model',
+    className: 'icon-jnpf-create icon-jnpf-task',
+    title: '创建一个类型为jnpf-task的任务节点',
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-node-approve',
+    iconColor: '#1DACEB',
+    titleColor: '#C0EDF8',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, width: 90, rx: 16, height: 32 },
+    rendererName: '并行分支',
+    bodyDefaultText: '请设置审批人',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { approver }, // 自定义 只有default = false 开启
+  },
+  properties: {},
+};
+
+export { jnpfParallelConfig };

+ 34 - 0
lib/config/element/group/index.ts

@@ -0,0 +1,34 @@
+import { bpmnGroup } from '../../variableName';
+import { jnpfConfigBpmnContextPad } from '../../contextPad';
+const { approver, subFlow, inclusive, parallel, exclusive } = jnpfConfigBpmnContextPad;
+const jnpfGroupConfig: any = {
+  name: bpmnGroup,
+  shapeType: bpmnGroup,
+  element: {
+    label: 'Group',
+    actionName: 'replace-with-group',
+    className: 'bpmn-icon-group',
+    target: {
+      type: bpmnGroup,
+    },
+  },
+  palette: {
+    name: 'create.yinmai-group',
+    group: 'model',
+    className: 'icon-yinmai-create icon-yinmai-group',
+    title: '修改为渲染按钮',
+  },
+  renderer: {
+    background: '#ffffff',
+    attr: { x: 0, y: 0, width: 28, height: 28 },
+    rendererName: '分组123',
+    fillColor: 'rgb(255,255,255)',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { approver, subFlow, inclusive, parallel, exclusive }, // 自定义 只有default = false 开启
+  },
+  properties: {},
+};
+
+export { jnpfGroupConfig };

+ 53 - 0
lib/config/element/label/index.ts

@@ -0,0 +1,53 @@
+import { bpmnLabel } from '../../variableName';
+import { jnpfConfigBpmnContextPad } from '../../contextPad';
+const {
+  approver,
+  subFlow,
+  inclusive,
+  parallel,
+  exclusive,
+  trigger,
+  getData,
+  addData,
+  updateData,
+  delData,
+  interfaceData,
+  message,
+  launch,
+  schedule,
+  choose,
+  processing,
+  outside
+} = jnpfConfigBpmnContextPad;
+const jnpfLabelConfig: any = {
+  name: bpmnLabel,
+  shapeType: bpmnLabel,
+  element: {
+    label: 'Timer',
+    actionName: 'replace-with-timer',
+    className: 'bpmn-icon-timer',
+    target: {
+      type: bpmnLabel,
+    },
+  },
+  palette: {
+    name: 'create.yinmai-timer',
+    group: 'model',
+    className: 'icon-yinmai-create icon-yinmai-timer',
+    title: '修改为渲染按钮',
+  },
+  renderer: {
+    background: '#ffffff',
+    attr: { x: 0, y: 0, width: 28, height: 28 },
+    rendererName: 'label按钮',
+    fillColor: 'rgb(255,255,255)',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { approver, subFlow, processing, outside, exclusive, inclusive, parallel, choose, trigger }, // 自定义 只有default = false 开启
+    groupCustomization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule }, // 分组内部使用这个配置
+  },
+  properties: {},
+};
+
+export { jnpfLabelConfig };

+ 37 - 0
lib/config/element/outside/index.ts

@@ -0,0 +1,37 @@
+import { jnpfConfigBpmnContextPad } from '../../contextPad';
+import { bpmnOutside, typeOutside } from '../../variableName';
+const { approver, processing, connect, copy, del } = jnpfConfigBpmnContextPad;
+const jnpfOutsideConfig: any = {
+  name: typeOutside,
+  shapeType: bpmnOutside,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnOutside,
+    },
+  },
+  palette: {
+    name: 'create.jnpf-task',
+    group: 'model',
+    className: 'icon-jnpf-create icon-ym icon-ym-flow-node-external',
+    title: '创建一个类型为jnpf-task的任务节点',
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-node-external',
+    iconColor: '#FC821A',
+    titleColor: 'linear-gradient(90deg, #FCEDE2 0%, #FFBB81 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '外部节点',
+    bodyDefaultText: '请设置外部审批',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { approver, processing, connect, copy, del },
+  },
+  properties: {},
+};
+
+export { jnpfOutsideConfig };

+ 35 - 0
lib/config/element/processing/index.ts

@@ -0,0 +1,35 @@
+import { bpmnProcessing, typeProcessing } from '../../variableName';
+
+const jnpfProcessingConfig: any = {
+  name: typeProcessing,
+  shapeType: bpmnProcessing,
+  element: {
+    label: 'Processing',
+    actionName: 'replace-with-processing',
+    className: 'bpmn-icon-processing',
+    target: {
+      type: bpmnProcessing,
+    },
+  },
+  palette: {
+    name: 'create.jnpf-processing',
+    group: 'model',
+    className: 'icon-jnpf-create icon-jnpf-processing',
+    title: '创建一个类型为jnpf-processing的任务节点',
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-generator-todo',
+    iconColor: '#C33B5F',
+    titleColor: 'linear-gradient(90deg, #FFC1DB 0%, #F2819F 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '办理节点',
+    bodyDefaultText: '请设置办理人',
+  },
+  contextPad: {
+    default: true, // contextPad 中的元素使用默认 否则遵循自定义
+  },
+  properties: {},
+};
+
+export { jnpfProcessingConfig };

+ 13 - 0
lib/config/element/sequenceFlow/index.ts

@@ -0,0 +1,13 @@
+import { jnpfConfigBpmnContextPad } from '../../contextPad';
+import { bpmnSequenceFlow } from '../../variableName';
+const { del } = jnpfConfigBpmnContextPad;
+const jnpfSequenceFlow: any = {
+  name: bpmnSequenceFlow,
+  shapeType: bpmnSequenceFlow,
+  contextPad: {
+    default: false,
+    customization: { del }, // 自定义 只有default = false 开启
+  },
+  properties: {},
+};
+export { jnpfSequenceFlow };

+ 32 - 0
lib/config/element/start/index.ts

@@ -0,0 +1,32 @@
+import { jnpfConfigBpmnContextPad } from '../../contextPad';
+import { bpmnStart, typeStart } from '../../variableName';
+
+const { approver, processing, subFlow, connect, event, timeout, notice, webhook, outside } = jnpfConfigBpmnContextPad;
+
+const jnpfStartConfig: any = {
+  name: typeStart,
+  shapeType: bpmnStart,
+  palette: {
+    name: 'create.jnpf-task',
+    group: 'model',
+    className: 'icon-jnpf-create icon-jnpf-task',
+    title: '创建一个类型为jnpf-task的任务节点',
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-node-start',
+    iconColor: '#4cd823',
+    titleColor: '#f0f5ff',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, width: 90, rx: 16, height: 32 },
+    rendererName: '流程发起',
+    bodyDefaultText: '暂未选择表单',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { approver, processing, subFlow, outside, connect }, // 自定义 只有default = false 开启
+    taskCustomization: { event, timeout, notice, webhook },
+  },
+  properties: {},
+};
+
+export { jnpfStartConfig };

+ 35 - 0
lib/config/element/subFlow/index.ts

@@ -0,0 +1,35 @@
+import { bpmnSubFlow, typeSubFlow } from '../../variableName';
+
+const jnpfSubFlowConfig: any = {
+  name: typeSubFlow,
+  shapeType: bpmnSubFlow,
+  element: {
+    label: 'subProcess',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnSubFlow,
+    },
+  },
+  palette: {
+    name: 'create.jnpf-task',
+    group: 'model',
+    className: 'icon-jnpf-create icon-jnpf-task',
+    title: '创建一个类型为jnpf-task的任务节点',
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-node-subFlow',
+    iconColor: '#F0962D',
+    titleColor: 'linear-gradient(90deg, #FFDFC1 0%, #FDDAA7 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, width: 200, height: 88, background: '#e6f4ff' },
+    rendererName: '子流程',
+    bodyDefaultText: '请设置发起人',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+  },
+  properties: {},
+};
+
+export { jnpfSubFlowConfig };

+ 31 - 0
lib/config/element/timer/index.ts

@@ -0,0 +1,31 @@
+import { bpmnTimer, typeTimer } from '../../variableName';
+const jnpfTimerConfig: any = {
+  name: typeTimer,
+  shapeType: bpmnTimer,
+  element: {
+    label: 'Timer',
+    actionName: 'replace-with-timer',
+    className: 'bpmn-icon-timer',
+    target: {
+      type: bpmnTimer,
+    },
+  },
+  palette: {
+    name: 'create.yinmai-timer',
+    group: 'model',
+    className: 'icon-yinmai-create icon-yinmai-timer',
+    title: '创建一个类型为yinmai-task的定时器',
+  },
+  renderer: {
+    attr: { x: 0, y: 0, width: 200, height: 38 },
+    rendererName: '定时器',
+    fillColor: 'rgb(240,245,255)',
+  },
+  contextPad: {
+    default: true, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: {}, // 自定义 只有default = false 开启
+  },
+  properties: {},
+};
+
+export { jnpfTimerConfig };

+ 31 - 0
lib/config/element/trigger/event/index.ts

@@ -0,0 +1,31 @@
+import { bpmnTask, bpmnEvent, typeEventTrigger } from '../../../variableName';
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+const { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, del } = jnpfConfigBpmnContextPad;
+const jnpfEventConfig: any = {
+  name: typeEventTrigger,
+  shapeType: bpmnEvent,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnTask,
+    },
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-trigger-event',
+    iconColor: '#4DCE62',
+    titleColor: 'linear-gradient(90deg, #BFEBC6 0%, #A4EBAF 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '事件触发',
+    bodyDefaultText: '请设置触发事件',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, del },
+  },
+  properties: {},
+};
+
+export { jnpfEventConfig };

+ 32 - 0
lib/config/element/trigger/index.ts

@@ -0,0 +1,32 @@
+import { bpmnTask, bpmnTrigger, typeTrigger } from '../../variableName';
+import { jnpfConfigBpmnContextPad } from '../../contextPad';
+const { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del } = jnpfConfigBpmnContextPad;
+const jnpfTriggerConfig: any = {
+  name: typeTrigger,
+  shapeType: bpmnTrigger,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnTask,
+    },
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-trigger-event',
+    iconColor: '#4DCE62',
+    titleColor: 'linear-gradient(90deg, #BFEBC6 0%, #A4EBAF 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '触发节点',
+    bodyDefaultText: '请设置触发事件',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, del },
+    otherCustomization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, copy, paste, del },
+  },
+  properties: {},
+};
+
+export { jnpfTriggerConfig };

+ 31 - 0
lib/config/element/trigger/notice/index.ts

@@ -0,0 +1,31 @@
+import { bpmnTask, bpmnNotice, typeNoticeTrigger } from '../../../variableName';
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+const { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, del } = jnpfConfigBpmnContextPad;
+const jnpfNoticeConfig: any = {
+  name: typeNoticeTrigger,
+  shapeType: bpmnNotice,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnTask,
+    },
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-trigger-notice',
+    iconColor: '#4DCE62',
+    titleColor: 'linear-gradient(90deg, #BFEBC6 0%, #A4EBAF 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '通知触发',
+    bodyDefaultText: '请设置触发事件',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, del },
+  },
+  properties: {},
+};
+
+export { jnpfNoticeConfig };

+ 31 - 0
lib/config/element/trigger/time/index.ts

@@ -0,0 +1,31 @@
+import { bpmnTask, bpmnTime, typeTimeTrigger } from '../../../variableName';
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+const { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, del } = jnpfConfigBpmnContextPad;
+const jnpfTimeConfig: any = {
+  name: typeTimeTrigger,
+  shapeType: bpmnTime,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnTask,
+    },
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym icon-ym icon-ym-flow-trigger-timer',
+    iconColor: '#4DCE62',
+    titleColor: 'linear-gradient(90deg, #BFEBC6 0%, #A4EBAF 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: '定时触发',
+    bodyDefaultText: '请设置触发事件',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, del },
+  },
+  properties: {},
+};
+
+export { jnpfTimeConfig };

+ 31 - 0
lib/config/element/trigger/webhook/index.ts

@@ -0,0 +1,31 @@
+import { bpmnTask, bpmnWebhook, typeWebhookTrigger } from '../../../variableName';
+import { jnpfConfigBpmnContextPad } from '../../../contextPad';
+const { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, del } = jnpfConfigBpmnContextPad;
+const jnpfWebhookConfig: any = {
+  name: typeWebhookTrigger,
+  shapeType: bpmnWebhook,
+  element: {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: bpmnTask,
+    },
+  },
+  renderer: {
+    icon: 'icon-ym icon-ym-flow-trigger-webhook',
+    iconColor: '#4DCE62',
+    titleColor: 'linear-gradient(90deg, #BFEBC6 0%, #A4EBAF 100%)',
+    background: '#ffffff',
+    attr: { x: 0, y: 0, rx: 8, width: 200, height: 88 },
+    rendererName: 'webhook触发',
+    bodyDefaultText: '请设置触发事件',
+  },
+  contextPad: {
+    default: false, // contextPad 中的元素使用默认 否则遵循自定义
+    customization: { getData, addData, updateData, delData, interfaceData, message, launch, schedule, connect, del },
+  },
+  properties: {},
+};
+
+export { jnpfWebhookConfig };

+ 178 - 0
lib/config/index.ts

@@ -0,0 +1,178 @@
+import { jnpfApproverConfig } from './element/approver';
+import { jnpfEndConfig } from './element/end';
+import { jnpfAddDataConfig } from './element/execute/addData';
+import { jnpfDelDataConfig } from './element/execute/delData';
+import { jnpfGetDataConfig } from './element/execute/getData';
+import { jnpfInterfaceConfig } from './element/execute/interface';
+import { jnpfLaunchConfig } from './element/execute/launch';
+import { jnpfMessageConfig } from './element/execute/message';
+import { jnpfScheduleConfig } from './element/execute/schedule';
+import { jnpfUpdateDataConfig } from './element/execute/updateData';
+import { jnpfChooseConfig } from './element/gateway/choose';
+import { jnpfExclusiveConfig } from './element/gateway/exclusive';
+import { jnpfInclusiveConfig } from './element/gateway/inclusive';
+import { jnpfParallelConfig } from './element/gateway/parallel';
+import { jnpfGroupConfig } from './element/group';
+import { jnpfLabelConfig } from './element/label';
+import { jnpfProcessingConfig } from './element/processing';
+import { jnpfSequenceFlow } from './element/sequenceFlow';
+import { jnpfStartConfig } from './element/start';
+import { jnpfSubFlowConfig } from './element/subFlow';
+import { jnpfTriggerConfig } from './element/trigger';
+import { jnpfEventConfig } from './element/trigger/event';
+import { jnpfNoticeConfig } from './element/trigger/notice';
+import { jnpfTimeConfig } from './element/trigger/time';
+import { jnpfWebhookConfig } from './element/trigger/webhook';
+import { jnpfOutsideConfig } from './element/outside';
+import {
+  bpmnAddData,
+  bpmnChoose,
+  bpmnDelData,
+  bpmnEnd,
+  bpmnEvent,
+  bpmnExclusive,
+  bpmnGetData,
+  bpmnGroup,
+  bpmnInclusive,
+  bpmnInterface,
+  bpmnLabel,
+  bpmnLaunchFlow,
+  bpmnMessage,
+  bpmnNotice,
+  bpmnParallel,
+  bpmnProcessing,
+  bpmnOutside,
+  bpmnSchedule,
+  bpmnSequenceFlow,
+  bpmnStart,
+  bpmnSubFlow,
+  bpmnTask,
+  bpmnTime,
+  bpmnTimer,
+  bpmnTrigger,
+  bpmnUpdateData,
+  bpmnWebhook,
+  typeAddData,
+  typeChoose,
+  typeDelData,
+  typeEnd,
+  typeEventTrigger,
+  typeExclusive,
+  typeGateway,
+  typeGetData,
+  typeGroup,
+  typeInclusion,
+  typeInterface,
+  typeLabel,
+  typeLaunchFlow,
+  typeMessage,
+  typeNoticeTrigger,
+  typeParallel,
+  typeSchedule,
+  typeStart,
+  typeSubFlow,
+  typeTask,
+  typeTimer,
+  typeTimeTrigger,
+  typeTrigger,
+  typeUpdateData,
+  typeWebhookTrigger,
+  typeOutside
+} from './variableName';
+
+const hasLabelElements: any = [
+  'bpmn:StartEvent',
+  'bpmn:EndEvent',
+  'bpmn:InclusiveGateway',
+]; // 一开始就有label标签的元素类型
+const BpmnBusinessObjectKey = {
+  id: 'wnId',
+};
+const typeConfig: any = {
+  [bpmnAddData]: jnpfAddDataConfig,
+  [bpmnChoose]: jnpfChooseConfig,
+  [bpmnDelData]: jnpfDelDataConfig,
+  [bpmnEnd]: jnpfEndConfig,
+  [bpmnEvent]: jnpfEventConfig,
+  [bpmnExclusive]: jnpfExclusiveConfig,
+  [bpmnGetData]: jnpfGetDataConfig,
+  [bpmnGroup]: jnpfGroupConfig,
+  [bpmnInclusive]: jnpfInclusiveConfig,
+  [bpmnInterface]: jnpfInterfaceConfig,
+  [bpmnLabel]: jnpfLabelConfig,
+  [bpmnLaunchFlow]: jnpfLaunchConfig,
+  [bpmnMessage]: jnpfMessageConfig,
+  [bpmnNotice]: jnpfNoticeConfig,
+  [bpmnParallel]: jnpfParallelConfig,
+  [bpmnProcessing]: jnpfProcessingConfig,
+  [bpmnSchedule]: jnpfScheduleConfig,
+  [bpmnSequenceFlow]: jnpfSequenceFlow,
+  [bpmnStart]: jnpfStartConfig,
+  [bpmnSubFlow]: jnpfSubFlowConfig,
+  [bpmnTask]: jnpfApproverConfig,
+  [bpmnTime]: jnpfTimeConfig,
+  [bpmnTrigger]: jnpfTriggerConfig,
+  [bpmnUpdateData]: jnpfUpdateDataConfig,
+  [bpmnWebhook]: jnpfWebhookConfig,
+  [bpmnOutside]: jnpfOutsideConfig,
+};
+// 默认wnType值
+const conversionWnType: any = {
+  [bpmnEnd]: typeEnd,
+  [bpmnExclusive]: typeExclusive,
+  [bpmnGroup]: typeGroup,
+  [bpmnInclusive]: typeInclusion,
+  [bpmnLabel]: typeLabel,
+  [bpmnParallel]: typeParallel,
+  [bpmnStart]: typeStart,
+  [bpmnSubFlow]: typeSubFlow,
+  [bpmnTask]: typeTask,
+  [bpmnTimer]: typeTimer,
+};
+// 任务节点类型
+const changeTypeByTaskShape: any = {
+  [typeAddData]: bpmnAddData,
+  [typeDelData]: bpmnDelData,
+  [typeGetData]: bpmnGetData,
+  [typeInterface]: bpmnInterface,
+  [typeLaunchFlow]: bpmnLaunchFlow,
+  [typeMessage]: bpmnMessage,
+  [typeSchedule]: bpmnSchedule,
+  [typeUpdateData]: bpmnUpdateData,
+};
+// 判断是否为触发节点
+const triggerTypeChange: any = {
+  [bpmnEvent]: typeTrigger,
+  [bpmnNotice]: typeTrigger,
+  [bpmnTime]: typeTrigger,
+  [bpmnTrigger]: typeTrigger,
+  [bpmnWebhook]: typeTrigger,
+};
+const changeTypeByTrigger: any = {
+  [typeEventTrigger]: bpmnEvent,
+  [typeNoticeTrigger]: bpmnNotice,
+  [typeTimeTrigger]: bpmnTime,
+  [typeWebhookTrigger]: bpmnWebhook,
+};
+const hasGatewayType = new Set([
+  typeChoose,
+  typeExclusive,
+  typeGateway,
+  typeInclusion,
+  typeParallel,
+]);
+
+export {
+  BpmnBusinessObjectKey,
+  changeTypeByTaskShape,
+  changeTypeByTrigger,
+  conversionWnType,
+  hasGatewayType,
+  hasLabelElements,
+  triggerTypeChange,
+  typeConfig,
+};
+
+export * from './constants';
+export * from './contextPad';
+export * from './variableName';

+ 136 - 0
lib/config/variableName/index.ts

@@ -0,0 +1,136 @@
+const bpmnTask = 'bpmn:UserTask';
+const bpmnStart = 'bpmn:StartEvent';
+const bpmnEnd = 'bpmn:EndEvent';
+const bpmnGateway = 'bpmn:InclusiveGateway';
+const bpmnSequenceFlow = 'bpmn:SequenceFlow';
+const bpmnTimer = 'bpmn:IntermediateCatchEvent';
+const bpmnSubFlow = 'subFlow';
+const bpmnInclusive = 'bpmn:InclusiveGateway';
+const bpmnParallel = 'bpmn:ParallelGateway';
+const bpmnExclusive = 'bpmn:ExclusiveGateway';
+const bpmnChoose = 'choose';
+const bpmnGroup = 'bpmn:Group';
+const bpmnLabel = 'label';
+const bpmnIncoming = 'bpmn2:incoming';
+const bpmnTrigger = 'trigger';
+const bpmnExecute = 'execute';
+const bpmnOutgoing = 'bpmn2:outgoing';
+const bpmnAddData = 'addData';
+const bpmnGetData = 'getData';
+const bpmnUpdateData = 'updateData';
+const bpmnDelData = 'deleteData';
+const bpmnInterface = 'dataInterface';
+const bpmnLaunchFlow = 'launchFlow';
+const bpmnMessage = 'message';
+const bpmnSchedule = 'schedule';
+const bpmnEvent = 'event';
+const bpmnTime = 'timeout';
+const bpmnNotice = 'notice';
+const bpmnWebhook = 'webhook';
+const bpmnCopy = 'copy';
+const bpmnPaste = 'paste';
+const bpmnProcessing = 'processing';
+const bpmnOutside = 'outside';
+
+const typeStart = 'start';
+const typeGateway = 'gateway';
+const typeEnd = 'end';
+const typeTask = 'approver';
+const typeLabel = 'label';
+const typeTimer = 'timer';
+const typeSubFlow = 'subFlow';
+const typeConfluence = 'confluence';
+const typeGroup = 'group';
+const typeTrigger = 'trigger';
+const typeExecute = 'execute';
+const typeAddData = 'addData';
+const typeGetData = 'getData';
+const typeUpdateData = 'updateData';
+const typeDelData = 'deleteData';
+const typeInterface = 'dataInterface';
+const typeLaunchFlow = 'launchFlow';
+const typeMessage = 'message';
+const typeSchedule = 'schedule';
+const typeEventTrigger = 'eventTrigger';
+const typeTimeTrigger = 'timeTrigger';
+const typeNoticeTrigger = 'noticeTrigger';
+const typeWebhookTrigger = 'webhookTrigger';
+const typeConnect = 'connect';
+const typeCondition = 'condition';
+const typeCopy = 'copy';
+const typePaste = 'paste';
+const typeProcessing = 'processing';
+const typeInclusion = 'inclusion';
+const typeParallel = 'parallel';
+const typeExclusive = 'exclusive';
+const typeChoose = 'choose';
+const typeOutside = 'outside';
+
+export {
+  bpmnTask,
+  bpmnStart,
+  bpmnEnd,
+  bpmnGateway,
+  bpmnSequenceFlow,
+  bpmnLabel,
+  bpmnTimer,
+  bpmnSubFlow,
+  bpmnInclusive,
+  bpmnParallel,
+  bpmnExclusive,
+  bpmnGroup,
+  bpmnTrigger,
+  bpmnExecute,
+  bpmnIncoming,
+  bpmnOutgoing,
+  bpmnAddData,
+  bpmnGetData,
+  bpmnUpdateData,
+  bpmnDelData,
+  bpmnInterface,
+  bpmnLaunchFlow,
+  bpmnMessage,
+  bpmnSchedule,
+  bpmnEvent,
+  bpmnTime,
+  bpmnNotice,
+  bpmnWebhook,
+  bpmnCopy,
+  bpmnPaste,
+  bpmnProcessing,
+  bpmnChoose,
+  bpmnOutside,
+  typeAddData,
+  typeGetData,
+  typeUpdateData,
+  typeDelData,
+  typeInterface,
+  typeLaunchFlow,
+  typeMessage,
+  typeSchedule,
+  typeEventTrigger,
+  typeTimeTrigger,
+  typeNoticeTrigger,
+  typeWebhookTrigger,
+  typeConnect,
+  typeCondition,
+  typeStart,
+  typeGateway,
+  typeEnd,
+  typeTask,
+  typeLabel,
+  typeTimer,
+  typeSubFlow,
+  typeConfluence,
+  typeGroup,
+  typeTrigger,
+  typeExecute,
+  typeCopy,
+  typePaste,
+  typeProcessing,
+  typeInclusion,
+  typeParallel,
+  typeExclusive,
+  typeChoose,
+  typeOutside,
+};

+ 172 - 0
lib/contextPad/index.ts

@@ -0,0 +1,172 @@
+// @ts-nocheck 过滤ts校验
+import { attr as domAttr, query as domQuery, classes as domClasses, domify as domify, delegate as domDelegate, event as domEvent } from 'min-dom';
+import { assign, forEach, isArray } from 'min-dash';
+import { escapeCSS } from 'diagram-js/lib/util/EscapeUtil';
+import contextPad from 'diagram-js/lib/features/context-pad/ContextPad';
+import { bpmnGroup, bpmnLabel, bpmnTask } from '../config/variableName';
+
+var entrySelector = '.entry';
+
+function addClasses(element: any, classNames: any) {
+  var classes = domClasses(element);
+
+  classNames = isArray(classNames) ? classNames : classNames.split(/\s+/g);
+
+  classNames.forEach(function (cls: any) {
+    classes.add(cls);
+  });
+}
+
+class YmContextPad extends contextPad {
+  constructor(canvas: any, config: any, eventBus: any, overlays: any) {
+    super(canvas, config, eventBus, overlays);
+  }
+  // 打开节点右侧面板
+  open(target: any, force: any) {
+    if (target.type === bpmnLabel || target.type === bpmnGroup) return;
+    if (!force && this.isOpen(target)) return;
+    this.close();
+    this._updateAndOpen(target);
+  }
+  isShown() {
+    return this.isOpen();
+  }
+  arrowOpen(target: any) {
+    var entries = this.getEntries(target),
+      arrow = this.getArrow(target),
+      html = arrow.html;
+
+    let container = domify('<div class="rightArrow" id="rightArrow"></div>');
+    html.appendChild(container);
+    domClasses(html).add('open');
+
+    this._current = {
+      target: target,
+      entries: entries,
+      pad: arrow,
+    };
+    this._eventBus.fire('contextPad.open', { current: this._current });
+  }
+  getArrow(target: any) {
+    if (this.isOpen()) {
+      return this._current.pad;
+    }
+    var self = this;
+    var overlays = this._overlays;
+    var html = domify('<div class="ymArrow"></div>');
+    var position = this._getPosition(target);
+    domDelegate.bind(html, entrySelector, 'click', function (event) {
+      self.trigger('click', event);
+    });
+    let newPosition = {
+      position: {
+        top: position.position.top + target.height / 2,
+        left: position.position.left,
+      },
+    };
+
+    var overlaysConfig = assign({ html: html }, this._overlaysConfig, newPosition);
+    var overlaysConfig1 = assign({ html: html }, this._overlaysConfig, position);
+
+    domDelegate.bind(html, '.rightArrow', 'click', function (event) {
+      self._updateAndOpen(target);
+      self.trigger('click', event);
+    });
+
+    domDelegate.bind(html, entrySelector, 'dragstart', function (event) {
+      self.trigger('dragstart', event);
+    });
+
+    // stop propagation of mouse events
+    domEvent.bind(html, 'mousedown', function (event: any) {
+      event.stopPropagation();
+    });
+    var activeRootElement = this._canvas.getRootElement();
+    this._overlayId = overlays.add(activeRootElement, 'context-pad', overlaysConfig);
+    var arrow = overlays.get(this._overlayId);
+    this._eventBus.fire('contextPad.create', { target: target, pad: arrow });
+
+    return arrow;
+  }
+  getPad(target: any) {
+    if (this.isOpen()) return this._current.pad;
+    var self = this;
+    var overlays = this._overlays;
+    var html = domify('<div class="djs-context-pad"></div>');
+    var position = this._getPosition(target);
+    var overlaysConfig = assign(
+      {
+        html: html,
+      },
+      this._overlaysConfig,
+      position,
+    );
+
+    domDelegate.bind(html, entrySelector, 'click', function (event) {
+      self.trigger('click', event);
+    });
+
+    domDelegate.bind(html, entrySelector, 'dragstart', function (event) {
+      self.trigger('dragstart', event);
+    });
+
+    // stop propagation of mouse events
+    domEvent.bind(html, 'mousedown', function (event: any) {
+      event.stopPropagation();
+    });
+
+    var activeRootElement = this._canvas.getRootElement();
+
+    this._overlayId = overlays?.add(activeRootElement, 'context-pad', overlaysConfig);
+
+    var pad = overlays.get(this._overlayId);
+
+    this._eventBus.fire('contextPad.create', {
+      target: target,
+      pad: pad,
+    });
+
+    return pad;
+  }
+  // 重写父类contextPad样式
+  _updateAndOpen(target: any) {
+    var entries = this.getEntries(target),
+      pad = this.getPad(target),
+      html = pad.html,
+      image;
+    forEach(entries, (entry: any, id: any) => {
+      let grouping = 'default',
+        control = domify(entry.html || '<div class="entry" draggable="true"></div>'),
+        container;
+      let svg = domify(entry.html || `<span class='svgText' >${entry.title}</span>`);
+      control.appendChild(svg);
+      domAttr(control, 'data-action', id);
+      container = domQuery('[data-group=' + escapeCSS(grouping) + ']', html);
+      if (!container) {
+        container = domify('<div class="group"></div>');
+        domAttr(container, 'data-group', grouping);
+        html.appendChild(container);
+      }
+      container.appendChild(control);
+      if (entry.className) addClasses(control, entry.className);
+      if (entry.title) domAttr(control, 'title', entry.title);
+      if (entry.imageUrl) {
+        image = domify('<img>');
+        domAttr(image, 'src', entry.imageUrl);
+        image.style.width = '100%';
+        image.style.height = '100%';
+        control.appendChild(image);
+      }
+    });
+    domClasses(html).add('open');
+
+    this._current = {
+      target: target,
+      entries: entries,
+      pad: pad,
+    };
+    this._eventBus.fire('contextPad.open', { current: this._current });
+  }
+}
+YmContextPad.$inject = ['canvas', 'config.contextPad', 'eventBus', 'overlays'];
+export default YmContextPad;

+ 274 - 0
lib/contextPad/provider/CustomizeContextPad.ts

@@ -0,0 +1,274 @@
+import { changeTypeByTaskShape, hasGatewayType, typeConfig } from '../../config';
+import { jnpfConfigBpmnContextPad } from '../../config/contextPad';
+import {
+  bpmnEnd,
+  bpmnTask,
+  bpmnSubFlow,
+  typeEnd,
+  typeTask,
+  typeSubFlow,
+  bpmnStart,
+  bpmnTrigger,
+  typeTrigger,
+  bpmnSequenceFlow,
+  bpmnCopy,
+  typeCopy,
+  bpmnProcessing,
+  typeProcessing,
+  bpmnOutside,
+  typeOutside,
+  typeStart,
+} from '../../config/variableName';
+import { jnpfApproverConfig } from '../../config/element/approver';
+import { jnpfSubFlowConfig } from '../../config/element/subFlow';
+import { jnpfEndConfig } from '../../config/element/end';
+import { jnpfTriggerConfig } from '../../config/element/trigger';
+import { jnpfProcessingConfig } from '../../config/element/processing';
+import { jnpfOutsideConfig } from '../../config/element/outside';
+const CustomizeContextPad = (contextPadProvider: any, element: any) => {
+  let type = element.type;
+  let wnType = element.wnType;
+  if (wnType === bpmnTrigger) type = bpmnTrigger;
+  if (wnType === bpmnOutside) type = bpmnOutside;
+  if (changeTypeByTaskShape[wnType]) type = changeTypeByTaskShape[wnType];
+
+  if (typeConfig[type]) {
+    const {
+      _autoPlace: autoPlace,
+      _create: create,
+      _elementFactory: elementFactory,
+      _modeling: modeling,
+      _connect: connects,
+      _injector: injector,
+      _eventBus: eventBus,
+    } = contextPadProvider;
+    const { contextPad, shapeType } = typeConfig[type];
+    const { connect, end, approver, processing, subFlow, del, trigger, copy, outside } = jnpfConfigBpmnContextPad;
+    // 根据类型 判断contextPad
+    if (type === shapeType) {
+      if (contextPad) {
+        if (contextPad.default) {
+          return defaultContextPad;
+        } else if (contextPad.customization) {
+          let result: any = {};
+          for (let key of Object.keys(contextPad.customization)) {
+            let data = contextPad.customization[key];
+            if (data.group === 'model') {
+              let options: any = {
+                wnName: typeConfig[key]?.renderer.rendererName,
+              };
+              if (element.type === bpmnStart && hasGatewayType.has(key)) {
+                // 开始节点只有分类节点 因为网关的分流节点和合流节点类型一致 多增加一个字段来表示
+                options = {
+                  wnName: typeConfig[key]?.renderer.rendererName,
+                  // wnGatewayType: typeGateway,
+                  wnType: key,
+                  icon: data.icon,
+                };
+              }
+              result[data.name] = appendAction(data.type, data.elementName, data.className, data.title, data.wnType, options);
+            } else if (data.group === 'connect') {
+              result[data.name] = {
+                group: data.group,
+                className: data.className,
+                title: data.title,
+                action: {
+                  click: startConnect,
+                  dragstart: startConnect,
+                },
+              };
+            } else if (data.group === 'edit') {
+              result[data.name] = {
+                group: data.group,
+                className: data.className,
+                title: data.title,
+                action: {
+                  click: removeElement,
+                },
+              };
+            }
+          }
+          return Object.assign(result);
+        } else return defaultContextPad();
+      }
+      // 单个节点删除功能
+      function removeElement() {
+        if (element.type === bpmnSequenceFlow) {
+          modeling.removeElements([element]);
+        } else {
+          eventBus.fire('commandStack.canExecute', {
+            command: 'shape.delete',
+            context: {
+              shape: element,
+            },
+          });
+        }
+      }
+      // 开始连线(拖拽)
+      function startConnect(event: any, element: any) {
+        connects.start(event, element);
+      }
+      // 添加事件
+      function appendAction(type: any, name: any, className: any, title: any, wnType: any, options?: any) {
+        const appendStart = (event: any, element: any) => {
+          let bpmnFactory = elementFactory._bpmnFactory;
+          if (type === typeCopy) {
+            let businessObject = bpmnFactory.create(element.type);
+            let shape = elementFactory.createShape(Object.assign({ type: element.type, name: element.name, wnType: element.name, ...options }, businessObject));
+            create.start(event, shape, { source: element });
+            // 复制属性
+            let jnpfData = injector.get('jnpfData');
+            let data = jnpfData.getValue(element.id);
+            jnpfData.setValue(shape.id, { ...data, nodeId: shape.id });
+          } else {
+            let hasTaskSet = new Set([typeSubFlow, typeProcessing, typeOutside]);
+            if (hasTaskSet.has(type)) type = bpmnTask;
+            let businessObject = bpmnFactory.create(type);
+            let shape = elementFactory.createShape(Object.assign({ type, name: name, businessObject, wnType: wnType }, options));
+            create.start(event, shape, { source: element });
+          }
+        };
+        const autoPlaceAppend = async (_event: any, element: any) => {
+          let hasTaskSet = new Set([typeSubFlow, typeTrigger, typeProcessing, typeOutside]);
+          if (hasTaskSet.has(type) || changeTypeByTaskShape[wnType]) type = bpmnTask;
+          let bpmnFactory = elementFactory._bpmnFactory;
+          // 复制元素
+          if (wnType === typeCopy) {
+            let businessObject = bpmnFactory.create(element.type);
+            let canvas = injector.get('canvas');
+            let rootElement = canvas.getRootElement();
+            let position = { x: element.x + 100, y: element.y + 200 };
+            let shape = elementFactory.createShape(Object.assign({ type: element.type, name: name, businessObject, wnType: element.wnType }, options));
+            modeling.createShape(shape, position, rootElement);
+            // 复制属性
+            let jnpfData = injector.get('jnpfData');
+            let data = jnpfData.getValue(element.id);
+            jnpfData.setValue(shape.id, { ...data, nodeId: shape.id });
+            // 复制执行元素
+            if (changeTypeByTaskShape[shape.wnType]) resizeShape(shape, element);
+            if (shape.wnType === typeTrigger) {
+              var groupShape = modeling.createShape(
+                {
+                  type: 'bpmn:Group',
+                },
+                { x: shape.x - 25, y: shape.y - 15, width: 250, height: 118 },
+                shape.parent,
+              );
+              modeling.updateProperties(shape, {
+                customGroupId: groupShape.id,
+              });
+            } else {
+              let shapeData = jnpfData.getValue(shape.id);
+              shape['elementBodyName'] = shapeData.content;
+              shape['nodeName'] = shapeData.nodeName;
+              modeling.updateProperties(shape, {});
+            }
+            const selection: any = injector.get('selection');
+            selection.select(shape);
+          } else {
+            let businessObject = bpmnFactory.create(type);
+            let shape = elementFactory.createShape(Object.assign({ type, name: name, businessObject, wnType: wnType }, options));
+            autoPlace.append(element, shape);
+            // 触发节点
+            if (wnType === typeTrigger) {
+              var groupShape = modeling.createShape(
+                {
+                  type: 'bpmn:Group',
+                },
+                { x: shape.x - 25, y: shape.y - 15, width: 250, height: 118 },
+                shape.parent,
+              );
+              modeling.updateProperties(shape, {
+                customGroupId: groupShape.id,
+              });
+            }
+            // 执行节点
+            if (changeTypeByTaskShape[wnType]) resizeShape(shape, element);
+          }
+        };
+        var append = autoPlace ? autoPlaceAppend : appendStart;
+        return {
+          group: 'model',
+          className: className,
+          title: title,
+          action: { dragstart: appendStart, click: append },
+        };
+      }
+      // 默认contextPad
+      function defaultContextPad() {
+        return Object.assign({
+          [approver.name]: appendAction(bpmnTask, typeTask, approver.className, approver.title, typeTask, { wnName: jnpfApproverConfig.renderer.rendererName }),
+          [processing.name]: appendAction(bpmnProcessing, typeProcessing, processing.className, processing.title, typeProcessing, {
+            wnName: jnpfProcessingConfig.renderer.rendererName,
+          }),
+          ...(element.wnType != typeOutside
+            ? {
+              [subFlow.name]: appendAction(bpmnTask, bpmnSubFlow, subFlow.className, subFlow.title, typeSubFlow, {
+                wnName: jnpfSubFlowConfig.renderer.rendererName,
+              }),
+            }
+            : {}),
+          ...(element.wnType != typeSubFlow
+            ? {
+              [trigger.name]: appendAction(bpmnTrigger, bpmnTrigger, trigger.className, trigger.title, typeTrigger, {
+                wnName: jnpfTriggerConfig.renderer.rendererName,
+              }),
+            }
+            : {}),
+          ...([typeTask, typeProcessing, typeSubFlow].includes(element.wnType)
+            ? {
+              [outside.name]: appendAction(bpmnOutside, bpmnOutside, outside.className, outside.title, typeOutside, {
+                wnName: jnpfOutsideConfig.renderer.rendererName,
+              }),
+            }
+            : {}),
+          [end.name]: appendAction(bpmnEnd, typeEnd, end.className, end.title, typeEnd, { wnName: jnpfEndConfig.renderer.rendererName }),
+          [connect.name]: {
+            group: connect.group,
+            className: connect.className,
+            title: connect.title,
+            action: {
+              click: startConnect,
+              dragstart: startConnect,
+            },
+          },
+          [copy.name]: appendAction(bpmnCopy, typeCopy, copy.className, copy.title, typeCopy, { wnName: null }),
+          [del.name]: {
+            group: del.group,
+            className: del.className,
+            title: del.title,
+            action: {
+              click: removeElement,
+            },
+          },
+        });
+      }
+      function resizeShape(shape, element) {
+        modeling.updateProperties(shape, {
+          customGroupId: element.businessObject.$attrs.customGroupId,
+        });
+        let groupShape = injector.get('elementRegistry').get(element.businessObject.$attrs.customGroupId);
+        let shapeRight = shape.x + shape.width;
+        let shapeBottom = shape.y + shape.height;
+        let groupRight = groupShape.x + groupShape.width;
+        let groupBottom = groupShape.y + groupShape.height;
+        var newBounds: any = {
+          x: groupShape.x, // 保持 x 位置不变
+          y: groupShape.y, // 保持 y 位置不变
+          width: groupShape.width,
+          height: groupShape.height,
+        };
+        if (shapeRight >= groupRight) {
+          newBounds.width = shapeRight + 25 - groupShape.x;
+        }
+        if (shapeBottom >= groupBottom) {
+          newBounds.height = shapeBottom + 15 - groupShape.y;
+        }
+        modeling.resizeShape(groupShape, newBounds);
+      }
+    }
+    return undefined;
+  }
+  return undefined;
+};
+export default CustomizeContextPad;

+ 85 - 0
lib/contextPad/provider/index.ts

@@ -0,0 +1,85 @@
+import ContextPadProvider from 'bpmn-js/lib/features/context-pad/ContextPadProvider';
+import CustomizeContextPad from '../provider/CustomizeContextPad';
+import { jnpfConfigBpmnContextPad } from '../../config/contextPad';
+class YmContextPadProvider extends ContextPadProvider {
+  _modeling: any;
+  _rules: any;
+  _injector: any;
+  _eventBus: any;
+  _canvas: any;
+  constructor(
+    config: any,
+    injector: any,
+    eventBus: any,
+    contextPad: any,
+    modeling: any,
+    elementFactory: any,
+    connect: any,
+    create: any,
+    popupMenu: any,
+    canvas: any,
+    rules: any,
+    translate: any,
+  ) {
+    super(config, injector, eventBus, contextPad, modeling, elementFactory, connect, create, popupMenu, canvas, rules, translate);
+    this._rules = rules;
+    this._modeling = modeling;
+    this._injector = injector;
+    this._eventBus = eventBus;
+    this._canvas = canvas;
+  }
+
+  override getContextPadEntries(element: any): (() => any) | any | undefined {
+    return CustomizeContextPad(this, element);
+  }
+
+  // 多个元素框选时 默认包含框选删除元素
+  override getMultiElementContextPadEntries(element: any) {
+    var actions = {};
+    const { del } = jnpfConfigBpmnContextPad;
+    Object.assign(actions, {
+      delete: {
+        group: 'edit',
+        className: del.className,
+        title: del.title,
+        action: {
+          click: (_event: any, elements: any) => {
+            //判断存在开始节点给出不能删除的提示
+            const hasStartElement = elements.some(o => o.type == 'bpmn:StartEvent');
+            hasStartElement &&
+              this._eventBus.fire('custom.message', {
+                context: '无法删除开始节点',
+                messageType: 'warning',
+              });
+            //过滤开始节点后删除选择的节点
+            const newElements = elements.filter(o => o.type != 'bpmn:StartEvent');
+            this._eventBus.fire('commandStack.canExecute', {
+              command: 'elements.delete',
+              context: {
+                elements: newElements,
+              },
+            });
+          },
+        },
+      },
+    });
+    return actions;
+  }
+}
+
+YmContextPadProvider.$inject = [
+  'config.contextPad',
+  'injector',
+  'eventBus',
+  'contextPad',
+  'modeling',
+  'elementFactory',
+  'connect',
+  'create',
+  'popupMenu',
+  'canvas',
+  'rules',
+  'translate',
+];
+
+export default YmContextPadProvider;

+ 107 - 0
lib/copyPaste/index.ts

@@ -0,0 +1,107 @@
+import { getBusinessObject, getDi, is } from 'bpmn-js/lib/util/ModelUtil';
+import { forEach, isArray, isUndefined, omit, reduce } from 'min-dash';
+import { isLabel } from 'bpmn-js/lib/util/LabelUtil';
+import { buildBitUUID } from '../utils/uuidUtil';
+function copyProperties(source, target, properties) {
+  if (!isArray(properties)) properties = [properties];
+  forEach(properties, function (property: any) {
+    if (!isUndefined(source[property])) target[property] = source[property];
+  });
+}
+let LOW_PRIORITY = 750;
+/**
+ * BPMN-specific copy & paste.
+ *
+ * @param {BpmnFactory} bpmnFactory
+ * @param {EventBus} eventBus
+ * @param {ModdleCopy} moddleCopy
+ */
+export default function BpmnCopyPaste(bpmnFactory, eventBus, moddleCopy) {
+  function copy(bo: any, clone?: any) {
+    bo.id = `${bo.id.split('_')[0]}_${buildBitUUID()}`
+    let targetBo = bpmnFactory.create(bo.$type);
+    return moddleCopy.copyElement(bo, targetBo, null, clone);
+  }
+  eventBus.on('copyPaste.copyElement', LOW_PRIORITY, function (context) {
+    let descriptor = context.descriptor,
+      element = context.element,
+      businessObject = getBusinessObject(element);
+    if (isLabel(element)) return descriptor;
+    let businessObjectCopy = (descriptor.businessObject = copy(businessObject, true));
+    let diCopy = (descriptor.di = copy(getDi(element), true));
+    diCopy.bpmnElement = businessObjectCopy;
+    copyProperties(businessObjectCopy, descriptor, 'name');
+    copyProperties(diCopy, descriptor, 'isExpanded');
+    descriptor.wnType = element?.wnType || null;
+    if (businessObject.default) {
+      descriptor.default = businessObject.default.id;
+    }
+  });
+  let referencesKey = '-bpmn-js-refs';
+  function getReferences(cache) {
+    return (cache[referencesKey] = cache[referencesKey] || {});
+  }
+  function setReferences(cache: any, references: any) {
+    cache[referencesKey] = references;
+  }
+  function resolveReferences(descriptor, cache, references) {
+    let businessObject = getBusinessObject(descriptor);
+    if (descriptor.default) {
+      references[descriptor.default] = {
+        element: businessObject,
+        property: 'default',
+      };
+    }
+    if (descriptor.host) getBusinessObject(descriptor).attachedToRef = getBusinessObject(cache[descriptor.host]);
+    return omit(
+      references,
+      reduce(
+        references,
+        function (array: any, reference: any, key: any) {
+          let element = reference.element,
+            property = reference.property;
+          if (key === descriptor.id) {
+            element.set(property, businessObject);
+            array.push(descriptor.id);
+          }
+          return array;
+        },
+        [],
+      ),
+    );
+  }
+  eventBus.on('copyPaste.pasteElement', function (context) {
+    let cache = context.cache,
+      descriptor = context.descriptor,
+      businessObject = descriptor.businessObject,
+      di = descriptor.di;
+    if (isLabel(descriptor)) {
+      descriptor.businessObject = getBusinessObject(cache[descriptor.labelTarget]);
+      descriptor.di = getDi(cache[descriptor.labelTarget]);
+      return;
+    }
+    businessObject = descriptor.businessObject = copy(businessObject);
+    di = descriptor.di = copy(di);
+    di.bpmnElement = businessObject;
+    copyProperties(descriptor, businessObject, ['isExpanded', 'name', 'wnType']); // 添加自定义的wnType
+    descriptor.type = businessObject.$type;
+  });
+  eventBus.on('copyPaste.copyElement', LOW_PRIORITY, function (context) {
+    let descriptor = context.descriptor,
+      element = context.element;
+    if (!is(element, 'bpmn:Participant')) return;
+    let participantBo = getBusinessObject(element);
+    if (participantBo.processRef) descriptor.processRef = copy(participantBo.processRef, true);
+  });
+  eventBus.on('copyPaste.pasteElement', function (context) {
+    let descriptor = context.descriptor,
+      processRef = descriptor.processRef;
+    if (processRef) descriptor.processRef = copy(processRef);
+  });
+  eventBus.on('copyPaste.pasteElement', LOW_PRIORITY, function (context) {
+    let cache = context.cache,
+      descriptor = context.descriptor;
+    setReferences(cache, resolveReferences(descriptor, cache, getReferences(cache)));
+  });
+}
+BpmnCopyPaste.$inject = ['bpmnFactory', 'eventBus', 'moddleCopy'];

+ 34 - 0
lib/factory/index.ts

@@ -0,0 +1,34 @@
+import ElementFactory from 'bpmn-js/lib/features/modeling/ElementFactory';
+import YmDefaultSize from './shapeSize';
+import { bpmnInclusive, typeConfluence } from '../config/variableName';
+
+class YmElementFactory extends ElementFactory {
+  constructor(bpmnFactory: any, moddle: any, translate: any) {
+    super(bpmnFactory, moddle, translate);
+  }
+
+  create(elementType: any, attrs: any): any {
+    if (attrs.type === bpmnInclusive + '_confluence') {
+      attrs.type = bpmnInclusive;
+      attrs.businessObject.wnType = typeConfluence;
+    }
+    return super.create(elementType, attrs);
+  }
+
+  createBpmnElement(elementType: any, attrs: any) {
+    // @ts-ignore
+    return super.createBpmnElement(elementType, attrs);
+  }
+
+  getDefaultSize(element: any, di: any) {
+    return YmDefaultSize(element, di);
+  }
+
+  createParticipantShape(attrs: any) {
+    return super.createParticipantShape(attrs);
+  }
+}
+
+YmElementFactory.$inject = ['bpmnFactory', 'moddle', 'translate'];
+
+export default YmElementFactory;

+ 88 - 0
lib/factory/shapeSize.ts

@@ -0,0 +1,88 @@
+import { isExpanded } from '../utils/DiUtil';
+import { getBusinessObject, getDi, is } from '../utils/modelUtil';
+import { typeConfig } from '../config';
+import { bpmnTask, bpmnStart, bpmnEnd, bpmnGateway, bpmnSubFlow } from '../config/variableName';
+
+const YmDefaultSize = (element: any, di: any) => {
+  var bo = getBusinessObject(element);
+  di = di || getDi(element);
+  if (is(bo, 'bpmn:SubProcess')) {
+    return isExpanded(bo, di) ? { width: 350, height: 200 } : { width: 100, height: 80 };
+  }
+
+  if (is(bo, bpmnTask)) {
+    try {
+      let { renderer } = typeConfig[bpmnTask];
+      return { width: renderer.attr.width, height: renderer.attr.height };
+    } catch (err: any) {
+      return { width: 50, height: 50 };
+    }
+  }
+
+  if (is(bo, bpmnGateway)) {
+    try {
+      let { renderer } = typeConfig[bpmnGateway];
+      return { width: renderer.attr.width, height: renderer.attr.height };
+    } catch (err: any) {
+      return { width: 0, height: 0 };
+    }
+  }
+  if (is(bo, bpmnStart)) {
+    try {
+      let { renderer } = typeConfig[bpmnStart];
+      return { width: renderer.attr.width, height: renderer.attr.height };
+    } catch (err: any) {
+      return { width: 50, height: 50 };
+    }
+  }
+
+  if (is(bo, bpmnEnd)) {
+    try {
+      let { renderer } = typeConfig[bpmnEnd];
+      return { width: renderer.attr.width, height: renderer.attr.height };
+    } catch (err: any) {
+      return { width: 50, height: 50 };
+    }
+  }
+
+  if (is(bo, bpmnSubFlow)) {
+    try {
+      let { renderer } = typeConfig[bpmnEnd];
+      return { width: renderer.attr.width, height: renderer.attr.height };
+    } catch (err: any) {
+      return { width: 50, height: 50 };
+    }
+  }
+
+  if (is(bo, 'bpmn:Participant')) {
+    if (isExpanded(bo, di)) {
+      return { width: 600, height: 250 };
+    } else {
+      return { width: 400, height: 60 };
+    }
+  }
+
+  if (is(bo, 'bpmn:Lane')) {
+    return { width: 400, height: 100 };
+  }
+
+  if (is(bo, 'bpmn:DataObjectReference')) {
+    return { width: 36, height: 50 };
+  }
+
+  if (is(bo, 'bpmn:DataStoreReference')) {
+    return { width: 50, height: 50 };
+  }
+
+  if (is(bo, 'bpmn:TextAnnotation')) {
+    return { width: 100, height: 30 };
+  }
+
+  if (is(bo, 'bpmn:Group')) {
+    return { width: 300, height: 300 };
+  }
+
+  return { width: 100, height: 80 };
+};
+
+export default YmDefaultSize;

+ 367 - 0
lib/gridSnapping/connect/index.ts

@@ -0,0 +1,367 @@
+import inherits from 'inherits-browser';
+import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
+import { pointsAligned } from 'diagram-js/lib/util/Geometry';
+import { assign } from 'min-dash';
+import { DEFAULT_CONNECT } from '../../config/constants';
+import { NodeUtils } from '../../utils/nodeUtil';
+/**
+ * @typedef {import('diagram-js/lib/core/EventBus').default} EventBus
+ * @typedef {import('diagram-js/lib/features/grid-snapping/GridSnapping').default} GridSnapping
+ * @typedef {import('../../modeling/Modeling').default} Modeling
+ *
+ * @typedef {import('diagram-js/lib/util/Types').Point} Point
+ */
+
+const HIGH_PRIORITY = 3000;
+
+export default function GridSnappingLayoutConnectionBehavior(this: any, eventBus: any, gridSnapping: any, modeling: any, jnpfData: any, injector: any) {
+  CommandInterceptor.call(this, eventBus);
+  this._gridSnapping = gridSnapping;
+  this._injector = injector;
+  const self = this;
+  this.postExecuted(['connection.create', 'connection.layout'], HIGH_PRIORITY, function (event: any) {
+    let context = event.context,
+      connection = context.connection,
+      waypoints = connection.waypoints;
+    let target = connection.target,
+      source = connection.source;
+    // 横向美化
+    if (jnpfData.data?.layout?.value === 'horizontal') {
+      let x = source.x;
+      let y = source.y + source.height / 2;
+      let targetX = target.x + target.width / 2;
+      if (source.x + source.width <= target.x) {
+        // 右侧
+        x = source.x + source.width;
+        if (y === target.y + target.height / 2) {
+          waypoints = [
+            { x: x, y: y },
+            { x: target.x, y: target.y + target.height / 2 },
+          ];
+        } else if (y >= target.y + target.height && x + 30 > target.x) {
+          waypoints = [
+            { x: x, y: y },
+            { x: targetX, y: y },
+            { x: targetX, y: target.y + target.height },
+          ];
+        } else if (y - 10 < target.y && x + 30 > target.x) {
+          waypoints = [
+            { x: x, y: y },
+            { x: targetX, y: y },
+            { x: targetX, y: target.y },
+          ];
+        } else {
+          if (target.x - source.x - source.width - DEFAULT_CONNECT / 2 > 0) {
+            waypoints = [
+              { x: x, y: y },
+              { x: target.x - DEFAULT_CONNECT / 2, y: y },
+              { x: target.x - DEFAULT_CONNECT / 2, y: target.y + target.height / 2 },
+              { x: target.x, y: target.y + target.height / 2 },
+            ];
+          } else {
+            waypoints = [
+              { x: x, y: y },
+              { x: source.x + source.width + (target.x - source.x - source.width) / 2, y: y },
+              { x: source.x + source.width + (target.x - source.x - source.width) / 2, y: target.y + target.height / 2 },
+              { x: target.x, y: target.y + target.height / 2 },
+            ];
+          }
+        }
+      } else if (source.x > target.x + target.width) {
+        // 左侧
+        x = target.x + target.width;
+        if (x + 30 > source.x) {
+          if (y - 10 > target.y + target.height) {
+            waypoints = [
+              { x: source.x, y: source.y + source.height / 2 },
+              { x: target.x + target.width / 2, y: source.y + source.height / 2 },
+              { x: target.x + target.width / 2, y: target.y + target.height },
+            ];
+          } else if (y - 10 < target.y + target.height) {
+            waypoints = [
+              { x: source.x, y: source.y + source.height / 2 },
+              { x: target.x + target.width / 2, y: source.y + source.height / 2 },
+              { x: target.x + target.width / 2, y: target.y },
+            ];
+          }
+        } else {
+          waypoints = [
+            { x: source.x, y: source.y + source.height / 2 },
+            { x: (source.x - x) / 2 + x, y: source.y + source.height / 2 },
+            { x: (source.x - x) / 2 + x, y: target.y + target.height / 2 },
+            { x: x, y: target.y + target.height / 2 },
+          ];
+        }
+      } else {
+        // 上侧
+        if (target.y < source.y) {
+          if (source.x + source.width / 2 === target.x + target.width / 2) {
+            waypoints = [
+              { x: source.x + source.width / 2, y: source.y },
+              { x: target.x + target.width / 2, y: target.y + target.height },
+            ];
+          } else if (target.y + target.height > source.y - 20) {
+            if (source.x + source.width / 2 >= target.x + target.width) {
+              // 左上
+              waypoints = [
+                { x: source.x + source.width / 2, y: source.y },
+                { x: source.x + source.width / 2, y: target.y + target.height / 2 },
+                { x: target.x + target.width, y: target.y + target.height / 2 },
+              ];
+            } else if (source.x + source.width / 2 <= target.x) {
+              // 右上
+              waypoints = [
+                { x: source.x + source.width / 2, y: source.y },
+                { x: source.x + source.width / 2, y: target.y + target.height / 2 },
+                { x: target.x, y: target.y + target.height / 2 },
+              ];
+            } else {
+              waypoints = [
+                { x: source.x + source.width / 2, y: source.y },
+                { x: source.x + source.width / 2, y: source.y - (source.y - target.y - target.height) / 2 },
+                { x: target.x + target.width / 2, y: target.y + target.height },
+              ];
+            }
+          } else {
+            waypoints = [
+              { x: source.x + source.width / 2, y: source.y },
+              { x: source.x + source.width / 2, y: source.y - (source.y - target.y - target.height) / 2 },
+              { x: target.x + target.width / 2, y: source.y - (source.y - target.y - target.height) / 2 },
+              { x: target.x + target.width / 2, y: target.y + target.height },
+            ];
+          }
+        } else if (target.y + target.height > source.y + source.height) {
+          // 下侧
+          if (source.x + source.width / 2 === target.x + target.width / 2) {
+            waypoints = [
+              { x: source.x + source.width / 2, y: source.y + source.height },
+              { x: target.x + target.width / 2, y: target.y },
+            ];
+          } else if (target.y - 20 < source.y + source.height) {
+            if (source.x + source.width / 2 >= target.x + target.width) {
+              // 左下
+              waypoints = [
+                { x: source.x + source.width / 2, y: source.y + source.height },
+                { x: source.x + source.width / 2, y: target.y + target.height / 2 },
+                { x: target.x + target.width, y: target.y + target.height / 2 },
+              ];
+            } else if (source.x + source.width / 2 <= target.x) {
+              // 右下
+              waypoints = [
+                { x: source.x + source.width / 2, y: source.y + source.height },
+                { x: source.x + source.width / 2, y: target.y + target.height / 2 },
+                { x: target.x, y: target.y + target.height / 2 },
+              ];
+            } else {
+              waypoints = [
+                { x: source.x + source.width / 2, y: source.y + source.height },
+                { x: source.x + source.width / 2, y: target.y - (target.y - source.y - source.height) / 2 },
+                { x: target.x + target.width / 2, y: target.y - (target.y - source.y - source.height) / 2 },
+                { x: target.x + target.width / 2, y: target.y },
+              ];
+            }
+          } else {
+            waypoints = [
+              { x: source.x + source.width / 2, y: source.y + source.height },
+              { x: source.x + source.width / 2, y: target.y - (target.y - source.y - source.height) / 2 },
+              { x: target.x + target.width / 2, y: target.y - (target.y - source.y - source.height) / 2 },
+              { x: target.x + target.width / 2, y: target.y },
+            ];
+          }
+        }
+      }
+    }
+    if (jnpfData.data?.layout?.value === 'vertical') {
+      if (source.y + source.height < target.y) {
+        if (source.x + source.width / 2 === target.x + target.width / 2) {
+          waypoints = [
+            { x: source.x + source.width / 2, y: source.y + source.height },
+            { x: target.x + target.width / 2, y: target.y },
+          ];
+        } else {
+          if (source.y + source.height + 20 > target.y && source.x + source.width / 2 < target.x) {
+            waypoints = [
+              { x: source.x + source.width / 2, y: source.y + source.height },
+              { x: source.x + source.width / 2, y: target.y + target.height / 2 },
+              { x: target.x, y: target.y + target.height / 2 },
+            ];
+          } else if (source.y + source.height + 20 > target.y && source.x + source.width / 2 > target.x + target.width + 10) {
+            waypoints = [
+              { x: source.x + source.width / 2, y: source.y + source.height },
+              { x: source.x + source.width / 2, y: target.y + target.height / 2 },
+              { x: target.x + target.width, y: target.y + target.height / 2 },
+            ];
+          } else
+            waypoints = [
+              { x: source.x + source.width / 2, y: source.y + source.height },
+              { x: source.x + source.width / 2, y: source.y + source.height + (target.y - source.y - source.height) / 2 },
+              { x: target.x + target.width / 2, y: source.y + source.height + (target.y - source.y - source.height) / 2 },
+              { x: target.x + target.width / 2, y: target.y },
+            ];
+        }
+      } else if (source.y > target.y + target.height) {
+        if (source.x + source.width / 2 === target.x + target.width / 2) {
+          waypoints = [
+            { x: source.x + source.width / 2, y: source.y },
+            { x: target.x + target.width / 2, y: target.y + target.height },
+          ];
+        } else {
+          if (source.y - 20 < target.y + target.height && source.x + source.width / 2 < target.x) {
+            waypoints = [
+              { x: source.x + source.width / 2, y: source.y },
+              { x: source.x + source.width / 2, y: target.y + target.height / 2 },
+              { x: target.x, y: target.y + target.height / 2 },
+            ];
+          } else if (source.y - 20 < target.y + target.height && source.x + source.width / 2 > target.x + target.width + 10) {
+            waypoints = [
+              { x: source.x + source.width / 2, y: source.y },
+              { x: source.x + source.width / 2, y: target.y + target.height / 2 },
+              { x: target.x + target.width, y: target.y + target.height / 2 },
+            ];
+          } else
+            waypoints = [
+              { x: source.x + source.width / 2, y: source.y },
+              { x: source.x + source.width / 2, y: source.y - (source.y - target.y - target.height) / 2 },
+              { x: target.x + target.width / 2, y: source.y - (source.y - target.y - target.height) / 2 },
+              { x: target.x + target.width / 2, y: target.y + target.height },
+            ];
+        }
+      } else if (source.x + source.width / 2 < target.x - 10) {
+        // 右侧
+        if (source.x + source.width >= target.x - 10 && source.y + source.height / 2 >= target.y + target.height - 10) {
+          waypoints = [
+            { x: source.x + source.width, y: source.y + source.height / 2 },
+            { x: target.x + target.width / 2, y: source.y + source.height / 2 },
+            { x: target.x + target.width / 2, y: target.y + target.height },
+          ];
+        } else if (source.x + source.width >= target.x - 10 && source.y + source.height / 2 + 10 <= target.y) {
+          waypoints = [
+            { x: source.x + source.width, y: source.y + source.height / 2 },
+            { x: target.x + target.width / 2, y: source.y + source.height / 2 },
+            { x: target.x + target.width / 2, y: target.y },
+          ];
+        } else {
+          waypoints = [
+            { x: source.x + source.width, y: source.y + source.height / 2 },
+            { x: source.x + source.width + (target.x - source.x - source.width) / 2, y: source.y + source.height / 2 },
+            { x: source.x + source.width + (target.x - source.x - source.width) / 2, y: target.y + target.height / 2 },
+            { x: target.x, y: target.y + target.height / 2 },
+          ];
+        }
+      } else if (source.x + source.width / 2 > target.x + target.width - 10) {
+        // 左侧
+        if (source.x < target.x + target.width + 20 && source.y + source.height / 2 >= target.y + target.height - 10) {
+          waypoints = [
+            { x: source.x, y: source.y + source.height / 2 },
+            { x: target.x + target.width / 2, y: source.y + source.height / 2 },
+            { x: target.x + target.width / 2, y: target.y + target.height },
+          ];
+        } else if (source.x < target.x + target.width + 20 && source.y + source.height / 2 + 10 <= target.y) {
+          waypoints = [
+            { x: source.x, y: source.y + source.height / 2 },
+            { x: target.x + target.width / 2, y: source.y + source.height / 2 },
+            { x: target.x + target.width / 2, y: target.y },
+          ];
+        } else
+          waypoints = [
+            { x: source.x, y: source.y + source.height / 2 },
+            { x: target.x + target.width + (source.x - target.x - target.width) / 2, y: source.y + source.height / 2 },
+            { x: target.x + target.width + (source.x - target.x - target.width) / 2, y: target.y + target.height / 2 },
+            { x: target.x + target.width, y: target.y + target.height / 2 },
+          ];
+      }
+    }
+    // 判断是横向还是纵向 如果是纵向 需要重新修改坐标
+    modeling.updateWaypoints(connection, self.snapMiddleSegments(waypoints));
+    let elementRegistry = injector.get('elementRegistry');
+    let labelCenter = NodeUtils.updateLabelWaypoints(connection, elementRegistry, jnpfData);
+    let connectLabel = connection.label;
+    if (connectLabel) {
+      let label = elementRegistry.get(connectLabel.id);
+      label.x = labelCenter.x;
+      label.y = labelCenter.y;
+      modeling.updateProperties(label, {});
+    }
+  });
+}
+
+GridSnappingLayoutConnectionBehavior.$inject = ['eventBus', 'gridSnapping', 'modeling', 'jnpfData', 'injector'];
+inherits(GridSnappingLayoutConnectionBehavior, CommandInterceptor);
+/**
+ * Snap middle segments of a given connection.
+ *
+ * @param {Point[]} waypoints
+ *
+ * @return {Point[]}
+ */
+GridSnappingLayoutConnectionBehavior.prototype.snapMiddleSegments = function (this: any, waypoints: any[]) {
+  const gridSnapping = this._gridSnapping;
+  waypoints = waypoints.slice();
+
+  for (let i = 1; i < waypoints.length - 2; i++) {
+    const snapped = snapSegment(gridSnapping, waypoints[i], waypoints[i + 1]);
+    waypoints[i] = snapped[0];
+    waypoints[i + 1] = snapped[1];
+  }
+  return waypoints;
+};
+
+/**
+ * Check whether a connection has middle segments.
+ *
+ * @param {Point[]} waypoints
+ *
+ * @return {boolean}
+ */
+function hasMiddleSegments(waypoints: any[]) {
+  return waypoints.length > 3;
+}
+
+/**
+ * Check whether an alignment is horizontal.
+ *
+ * @param {string} aligned
+ *
+ * @return {boolean}
+ */
+function horizontallyAligned(aligned: string) {
+  return aligned === 'h';
+}
+
+/**
+ * Check whether an alignment is vertical.
+ *
+ * @param {string} aligned
+ *
+ * @return {boolean}
+ */
+function verticallyAligned(aligned: string) {
+  return aligned === 'v';
+}
+
+/**
+ * Get middle segments from a given connection.
+ *
+ * @param {Point[]} waypoints
+ *
+ * @return {Point[]}
+ */
+function snapSegment(gridSnapping: any, segmentStart: any, segmentEnd: any) {
+  const aligned: any = pointsAligned(segmentStart, segmentEnd);
+  const snapped: any = {};
+
+  if (horizontallyAligned(aligned)) {
+    snapped.y = gridSnapping.snapValue(segmentStart.y);
+  }
+
+  if (verticallyAligned(aligned)) {
+    snapped.x = gridSnapping.snapValue(segmentStart.x);
+  }
+
+  if ('x' in snapped || 'y' in snapped) {
+    segmentStart = assign({}, segmentStart, snapped);
+    segmentEnd = assign({}, segmentEnd, snapped);
+  }
+
+  return [segmentStart, segmentEnd];
+}

+ 231 - 0
lib/gridSnapping/index.ts

@@ -0,0 +1,231 @@
+import { is, isAny } from '../utils/modelUtil';
+import { findFreePosition, generateGetNextPosition, getConnectedDistance } from '../autoPlace/YmAutoPlaceUtil';
+import { isObject } from 'min-dash';
+import { bpmnStart } from '../config/variableName';
+import { DEFAULT_CONNECT } from '../config/constants';
+var HIGH_PRIORITY = 2000;
+function distance(a: any, b: any) {
+  return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
+}
+/**
+ * Convert the given bounds to a { top, left, bottom, right } descriptor.
+ *
+ * @param {Point|Rect} bounds
+ *
+ * @return {RectTRBL}
+ */
+export function asTRBL(bounds: any) {
+  return {
+    top: bounds.y,
+    right: bounds.x + (bounds.width || 0),
+    bottom: bounds.y + (bounds.height || 0),
+    left: bounds.x,
+  };
+}
+export function getConnectionMid(connection: any) {
+  var waypoints = connection.waypoints;
+  var parts = waypoints.reduce(function (parts: any, point: any, index: any) {
+    var lastPoint = waypoints[index - 1];
+    if (lastPoint) {
+      var lastPart = parts[parts.length - 1];
+      var startLength = (lastPart && lastPart.endLength) || 0;
+      var length = distance(lastPoint, point);
+      parts.push({
+        start: lastPoint,
+        end: point,
+        startLength: startLength,
+        endLength: startLength + length,
+        length: length,
+      });
+    }
+    return parts;
+  }, []);
+  var totalLength = parts.reduce(function (length: any, part: any) {
+    return length + part.length;
+  }, 0);
+  // find which segement contains middle point
+  var midLength = totalLength / 2;
+  var i = 0;
+  var midSegment = parts[i];
+  while (midSegment.endLength < midLength) {
+    midSegment = parts[++i];
+  }
+  // calculate relative position on mid segment
+  var segmentProgress = (midLength - midSegment.startLength) / midSegment.length;
+  var midPoint = {
+    x: midSegment.start.x + (midSegment.end.x - midSegment.start.x) * segmentProgress,
+    y: midSegment.start.y + (midSegment.end.y - midSegment.start.y) * segmentProgress,
+  };
+
+  return midPoint;
+}
+export function roundPoint(point: any) {
+  return {
+    x: Math.round(point.x),
+    y: Math.round(point.y),
+  };
+}
+export function getBoundsMid(bounds: any) {
+  return roundPoint({
+    x: bounds.x + (bounds.width || 0) / 2,
+    y: bounds.y + (bounds.height || 0) / 2,
+  });
+}
+export function getMid(element: any) {
+  if (!!element.waypoints) return getConnectionMid(element);
+  return getBoundsMid(element);
+}
+export function getOrientation(rect: any, reference: any, padding: any) {
+  padding = padding || 0;
+  if (!isObject(padding)) padding = { x: padding, y: padding };
+
+  var rectOrientation = asTRBL(rect),
+    referenceOrientation = asTRBL(reference);
+  var top = rectOrientation.bottom + padding.y <= referenceOrientation.top,
+    right = rectOrientation.left - padding.x >= referenceOrientation.right,
+    bottom = rectOrientation.top - padding.y >= referenceOrientation.bottom,
+    left = rectOrientation.right + padding.x <= referenceOrientation.left;
+  var vertical = top ? 'top' : bottom ? 'bottom' : null,
+    horizontal = left ? 'left' : right ? 'right' : null;
+  if (horizontal && vertical) return vertical + '-' + horizontal;
+  else return horizontal || vertical || 'intersect';
+}
+
+function getVerticalDistance(orientation: any, minDistance: any) {
+  if (orientation.indexOf('top') != -1) return -1 * minDistance;
+  else if (orientation.indexOf('bottom') != -1) return minDistance;
+  else return 0;
+}
+/**
+ * horizontalDistance 控制距离 计算默认取50 如果有连接过 获取之前的连接距离
+ * */
+export function getFlowNodePosition(source: any, element: any, jnpfData: any) {
+  var sourceTrbl = asTRBL(source);
+  var sourceMid = getMid(source);
+  let layout = jnpfData.data['layout']; // horizontal: 横向, vertical:纵向
+  var horizontalDistance =
+    layout?.value === 'horizontal'
+      ? getConnectedDistance(source, {
+          filter: function (connection: any) {
+            return is(connection, 'bpmn:SequenceFlow');
+          },
+        })
+      : 120;
+  var margin = 30,
+    minDistance = 50,
+    orientation = 'left';
+  if (is(source, 'bpmn:BoundaryEvent')) {
+    orientation = getOrientation(source, source.host, -25);
+    if (orientation.indexOf('top') !== -1) margin *= -1;
+  }
+  var position: any = {};
+  let nextPositionDirection: any = {};
+  // 获取排序 横向还是竖向 横向
+  if (layout?.value === 'horizontal') {
+    position = {
+      x: sourceTrbl.right + DEFAULT_CONNECT + element.width / 2,
+      y: sourceMid.y + getVerticalDistance(orientation, minDistance),
+    };
+    nextPositionDirection = {
+      y: {
+        margin: margin,
+        minDistance: minDistance,
+      },
+    };
+  } else {
+    position = {
+      x: sourceTrbl.right + getVerticalDistance(orientation, minDistance),
+      y: sourceMid.y + (element.height + source.height) / 2 + horizontalDistance,
+    };
+    nextPositionDirection = {
+      x: {
+        margin: (source.width - element.width) / 2 + 220,
+        minDistance: minDistance,
+      },
+    };
+  }
+  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
+}
+export function getNewShapePosition(source: any, element: any, jnpfData: any) {
+  if (is(element, 'bpmn:TextAnnotation')) return getTextAnnotationPosition(source, element);
+  if (isAny(element, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference'])) return getDataElementPosition(source, element);
+  if (is(element, 'bpmn:FlowNode')) return getFlowNodePosition(source, element, jnpfData);
+}
+/**
+ * Always put element bottom right of source.
+ */
+export function getDataElementPosition(source: any, element: any) {
+  var sourceTrbl = asTRBL(source);
+  var position = {
+    x: sourceTrbl.right - 10 + element.width / 2,
+    y: sourceTrbl.bottom + 40 + element.width / 2,
+  };
+  var nextPositionDirection = {
+    x: {
+      margin: 30,
+      minDistance: 30,
+    },
+  };
+  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
+}
+/**
+ * Always try to place text annotations top right of source.
+ */
+export function getTextAnnotationPosition(source: any, element: any) {
+  var sourceTrbl = asTRBL(source);
+  var position = {
+    x: sourceTrbl.right + element.width / 2,
+    y: sourceTrbl.top - 50 - element.height / 2,
+  };
+  if (!!source.waypoints) {
+    position = getMid(source);
+    position.x += 100;
+    position.y -= 50;
+  }
+  var nextPositionDirection = {
+    y: {
+      margin: -30,
+      minDistance: 20,
+    },
+  };
+  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
+}
+export default function YmGridSnappingAutoPlaceBehavior(eventBus: any, gridSnapping: any, jnpfData: any) {
+  eventBus.on('autoPlace', HIGH_PRIORITY, function (context: any) {
+    var source = context.source,
+      sourceMid = getMid(source),
+      shape = context.shape,
+      layout = jnpfData.data['layout']; // horizontal: 横向, vertical:纵向
+    var position: any = getNewShapePosition(source, shape, jnpfData);
+    ['x', 'y'].forEach(function (axis: any) {
+      var options: any = {};
+      if (position[axis] === sourceMid[axis]) return;
+      if (position[axis] > sourceMid[axis]) options.min = position[axis];
+      else options.max = position[axis];
+
+      if (is(shape, 'bpmn:TextAnnotation')) {
+        if (isHorizontal(axis)) options.offset = -shape.width / 2;
+        else options.offset = -shape.height / 2;
+      }
+    });
+    if (layout?.value === 'vertical') {
+      if (source.type === bpmnStart) {
+        position = {
+          x: position.x + (source.width - shape.width) / 2 + 10,
+          y: position.y,
+        };
+      } else {
+        position = {
+          x: position.x - 100,
+          y: position.y,
+        };
+      }
+    }
+    return position;
+  });
+}
+
+YmGridSnappingAutoPlaceBehavior.$inject = ['eventBus', 'gridSnapping', 'jnpfData'];
+function isHorizontal(axis: any) {
+  return axis === 'x';
+}

+ 59 - 0
lib/helper/defaultXml.ts

@@ -0,0 +1,59 @@
+const simpleDefaultXml = `<?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" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="sample-diagram" targetNamespace="http://www.flowable.org/processdef" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
+  <bpmn2:process id="Process_1" name="" isExecutable="true">
+    <bpmn2:startEvent id="Event_0oz4pwh" >
+      <bpmn2:outgoing>Flow_1iadlmn</bpmn2:outgoing>
+    </bpmn2:startEvent>
+    <bpmn2:userTask id="Activity_1qf73gq">
+      <bpmn2:incoming>Flow_1iadlmn</bpmn2:incoming>
+      <bpmn2:outgoing>Flow_1kz1x8a</bpmn2:outgoing>
+    </bpmn2:userTask>
+    <bpmn2:sequenceFlow id="Flow_1iadlmn" sourceRef="Event_0oz4pwh" targetRef="Activity_1qf73gq" />
+    <bpmn2:endEvent id="Event_0zlhfee">
+      <bpmn2:incoming>Flow_1kz1x8a</bpmn2:incoming>
+    </bpmn2:endEvent>
+    <bpmn2:sequenceFlow id="Flow_1kz1x8a" sourceRef="Activity_1qf73gq" targetRef="Event_0zlhfee" />
+  </bpmn2:process>
+  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
+    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
+      <bpmndi:BPMNShape id="Event_0oz4pwh_di" bpmnElement="Event_0oz4pwh">
+        <dc:Bounds x="675" y="75" width="90" height="32" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="Activity_1qf73gq_di" bpmnElement="Activity_1qf73gq">
+        <dc:Bounds x="620" y="227" width="200" height="88" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="Event_0zlhfee_di" bpmnElement="Event_0zlhfee">
+        <dc:Bounds x="675" y="435" width="90" height="32" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNEdge id="Flow_1iadlmn_di" bpmnElement="Flow_1iadlmn">
+        <di:waypoint x="720" y="107" />
+        <di:waypoint x="720" y="227" />
+        <bpmndi:BPMNLabel>
+          <dc:Bounds x="706" y="181" width="28" height="28" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge id="Flow_1kz1x8a_di" bpmnElement="Flow_1kz1x8a">
+        <di:waypoint x="720" y="315" />
+        <di:waypoint x="720" y="435" />
+        <bpmndi:BPMNLabel>
+          <dc:Bounds x="706" y="361" width="28" height="28" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+    </bpmndi:BPMNPlane>
+  </bpmndi:BPMNDiagram>
+</bpmn2:definitions>`;
+const defaultXml = `<?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://www.flowable.org/processdef" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
+  <bpmn2:process id="Process_1"  isExecutable="true">
+    <bpmn2:startEvent id="Event_04q7xii"  />
+  </bpmn2:process>
+  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
+    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
+      <bpmndi:BPMNShape id="Event_04q7xii_di" bpmnElement="Event_04q7xii" >
+        <dc:Bounds x="145" y="304" width="90" height="32" />
+      </bpmndi:BPMNShape>
+    </bpmndi:BPMNPlane>
+  </bpmndi:BPMNDiagram>
+</bpmn2:definitions>`;
+
+export { defaultXml, simpleDefaultXml };

+ 6 - 0
lib/labelEditing/labelEditingProvider.ts

@@ -0,0 +1,6 @@
+class CustomLabelEditingProvider {
+// 改写原bpmn编辑逻辑
+
+}
+
+export default CustomLabelEditingProvider;

+ 58 - 0
lib/modeler/index.ts

@@ -0,0 +1,58 @@
+import Modeler from 'bpmn-js/lib/Modeler';
+import jnpfPaletteProvider from '../palette';
+import jnpfRenderer from '../renderer';
+import jnpfElementFactory from '../factory';
+import jnpfOutline from '../outline';
+import jnpfBusinessData from '../business';
+import jnpfGridSnappingAutoPlaceBehavior from '../gridSnapping';
+import jnpfAlignElementsContextPadProvider from '../alignElements';
+import jnpfContextPad from '../contextPad';
+import jnpfContextPadProvider from '../contextPad/provider';
+import jnpfCustomBpmnRules from '../rule';
+import jnpfCommandStack from '../commandStack';
+import jnpfCustomBpmnCopyPaste from '../copyPaste';
+import GridSnappingLayoutConnectionBehavior from '../gridSnapping/connect';
+let flowInfo: any;
+const modeler: any = options => [
+  {
+    __init__: [
+      'paletteProvider',
+      'bpmnRenderer',
+      'contextPadProvider',
+      'replaceMenuProvider',
+      'elementFactory',
+      'jnpfData',
+      'gridSnappingAutoPlaceBehavior',
+      'alignElementsContextPadProvider',
+      'alignElementsMenuProvider',
+      'bpmnAlignElements',
+      'outlineProvider',
+      'contextPad',
+      'bpmnRules',
+      'bpmnCopyPaste',
+    ],
+    paletteProvider: ['type', jnpfPaletteProvider], // 左侧的元素 目前不用该方法
+    bpmnRenderer: ['type', jnpfRenderer, { options }], // 画布渲染
+    elementFactory: ['type', jnpfElementFactory], // 元素工厂
+    jnpfData: ['type', jnpfBusinessData], // 用于放置业务数据
+    gridSnappingAutoPlaceBehavior: ['type', jnpfGridSnappingAutoPlaceBehavior], // 自动生成元素位置 在点击coontext-pad时计算元素生成位置
+    alignElementsContextPadProvider: ['type', jnpfAlignElementsContextPadProvider], // 元素的排序等
+    outlineProvider: ['type', jnpfOutline, { options }], // 元素的外边框(用于修改边框颜色,注:线条颜色有svg获取标签再去修改颜色及箭头)
+    contextPad: ['type', jnpfContextPad], // 点击元素后的元素右侧弹窗框(显示开始节点 结束节点等)
+    contextPadProvider: ['type', jnpfContextPadProvider], // context-pad 属性
+    bpmnRules: ['type', jnpfCustomBpmnRules], // 自定义规则
+    commandStack: ['type', jnpfCommandStack], // 自定义CommandStack
+    gridSnappingLayoutConnectionBehavior: ['type', GridSnappingLayoutConnectionBehavior], // 修改连线的排序
+    bpmnCopyPaste: ['type', jnpfCustomBpmnCopyPaste], // 复制元素
+  },
+];
+class bpmnModeler extends Modeler {
+  constructor(options: any) {
+    flowInfo = options.flowInfo;
+    super(options);
+  }
+}
+
+bpmnModeler.prototype['_modules'] = [].concat(bpmnModeler.prototype['_modules'], modeler(flowInfo));
+
+export default bpmnModeler;

+ 53 - 0
lib/outline/index.ts

@@ -0,0 +1,53 @@
+import OutlineProvider from 'bpmn-js/lib/features/outline/OutlineProvider.js';
+import { attr as svgAttr, create as svgCreate } from 'tiny-svg';
+import { isLabel } from 'bpmn-js/lib/util/LabelUtil';
+import { typeConfig } from '../config';
+import { typeCondition, typeConfluence } from '../config/variableName';
+class YmOutlineProvider extends OutlineProvider {
+  private offset: number = 0;
+  private _styles;
+  private _type;
+  constructor(outline: any, styles: any, type: any) {
+    super(outline, styles);
+    this._styles = styles;
+    this._type = type;
+  }
+
+  getOutline(element: any) {
+    const OUTLINE_STYLE = this._styles.cls('djs-outline', ['no-fill']);
+    var outline;
+    if (isLabel(element)) return;
+    let { renderer } = typeConfig[element.type];
+    const { attr } = renderer;
+    outline = svgCreate('rect');
+    svgAttr(
+      outline,
+      Object.assign(
+        {
+          x: attr.x,
+          y: attr.y,
+          rx: attr.rx ? attr.rx : 3,
+          width: (this._type === 0 || element.wnType === typeCondition) && isLabel(element) ? 128 : element.wnType === typeConfluence ? 0 : attr.width,
+          height: (this._type === 0 || element.wnType === typeCondition) && isLabel(element) ? 28 : element.wnType === typeConfluence ? 0 : attr.height,
+        },
+        OUTLINE_STYLE,
+      ),
+    );
+    return outline;
+  }
+
+  updateOutline(element: any, outline: any): any {
+    svgAttr(outline, {
+      x: 0,
+      y: 0,
+      width: (this._type === 0 || element.wnType === typeCondition) && isLabel(element) ? 128 : element.width + this.offset * 2,
+      height: (this._type === 0 || element.wnType === typeCondition) && isLabel(element) ? 28 : element.height + this.offset * 2,
+    });
+    return outline;
+  }
+}
+
+// 注入的依赖项
+YmOutlineProvider.$inject = ['outline', 'styles', 'config.type'];
+
+export default YmOutlineProvider;

+ 21 - 0
lib/palette/CustomizePalette/index.ts

@@ -0,0 +1,21 @@
+import jnpfTask from './task';
+
+const YmPalette = (paletteProvider: any) => {
+  let actions = {};
+  const {
+    _bpmnFactory: bpmnFactory,
+    _create: create,
+    _elementFactory: elementFactory,
+    _translate: translate,
+    _spaceTool: spaceTool,
+    _lassoTool: lassoTool,
+    _handTool: handTool,
+    _globalConnect: globalConnect,
+  } = paletteProvider;
+
+  Object.assign(actions, jnpfTask(bpmnFactory, elementFactory, create, translate));
+
+  return actions;
+};
+
+export default YmPalette;

+ 32 - 0
lib/palette/CustomizePalette/task.ts

@@ -0,0 +1,32 @@
+import { jnpfApproverConfig } from '../../config/element/approver';
+
+export default function (bpmnFactory: any, elementFactory: any, create: any, translate: any) {
+  const { palette, name, shapeType, BpmnBusinessObjectKey } = jnpfApproverConfig;
+
+  function createTask() {
+    return function (event: any) {
+      // 自定义的 不需要根据elementFactory来自动创建业务对象
+      const businessObject = bpmnFactory.create(shapeType);
+      businessObject[BpmnBusinessObjectKey.name] = name;
+      // 在刚创建的bpmnFactory创建的业务对象中 增加新的图表形状
+      const shape = elementFactory.createShape({
+        type: shapeType,
+        businessObject,
+      });
+      create.start(event, shape);
+    };
+  }
+  return {
+    [palette.name]: {
+      group: palette.group, // 分组名
+      className: palette.className,
+      // className: 'bpmn-icon-user-task',
+      title: translate(palette.title), // 提示信息
+      // html: `<div><${translate(palette.title)}></div>`,
+      action: {
+        dragstart: createTask(), // 拖拽是触发
+        click: createTask(), // 点击后触发
+      },
+    },
+  };
+}

+ 150 - 0
lib/palette/bpmnPalette.ts

@@ -0,0 +1,150 @@
+// bpmn-js工具栏默认配置
+import cn from '../translate/cn';
+
+function getDi(element: any) {
+  return element && element.di;
+}
+
+const bpmnPalette = (paletteProvider: any) => {
+  const actions = {},
+    create = paletteProvider._create,
+    elementFactory = paletteProvider._elementFactory,
+    spaceTool = paletteProvider._spaceTool,
+    lassoTool = paletteProvider._lassoTool,
+    handTool = paletteProvider._handTool,
+    globalConnect = paletteProvider._globalConnect,
+    translate = paletteProvider._translate;
+
+  function createAction(type: any, group: any, className: any, title: any, options?: any) {
+    function createListener(event: any) {
+      var shape = elementFactory.createShape(Object.assign({ type: type, name11: 'csj' }, options));
+
+      if (options) {
+        var di = getDi(shape);
+        di.isExpanded = options.isExpanded;
+      }
+
+      create.start(event, shape);
+    }
+
+    var shortType = type.replace(/^bpmn:/, '');
+
+    return {
+      group: group,
+      className: className,
+      title: title || translate('Create {type}', { type: shortType }),
+      action: {
+        dragstart: createListener,
+        click: createListener,
+      },
+    };
+  }
+
+  function createSubprocess(event: any) {
+    var subProcess = elementFactory.createShape({
+      type: 'bpmn:SubProcess',
+      x: 0,
+      y: 0,
+      isExpanded: false,
+    });
+    //createShape 创建新的图表形状
+    var startEvent = elementFactory.createShape({
+      type: 'bpmn:StartEvent',
+      x: 40,
+      y: 82,
+      parent: subProcess,
+    });
+
+    create.start(event, [subProcess, startEvent], {
+      hints: {
+        autoSelect: [subProcess],
+      },
+    });
+  }
+
+  function createParticipant(event: any) {
+    create.start(event, elementFactory.createParticipantShape());
+  }
+
+  Object.assign(actions, {
+    'hand-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-hand-tool',
+      title: translate(cn['Activate the hand tool']),
+      action: {
+        click: function (event: any) {
+          handTool.activateHand(event);
+        },
+      },
+    },
+    'lasso-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-lasso-tool',
+      title: translate(cn['Activate the lasso tool']),
+      action: {
+        click: function (event: any) {
+          lassoTool.activateSelection(event);
+        },
+      },
+    },
+    'space-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-space-tool',
+      title: translate(cn['Activate the create/remove space tool']),
+      action: {
+        click: function (event: any) {
+          spaceTool.activateSelection(event);
+        },
+      },
+    },
+    'global-connect-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-connection-multi',
+      title: translate(cn['Activate the global connect tool']),
+      action: {
+        click: function (event: any) {
+          globalConnect.start(event);
+        },
+      },
+    },
+    'tool-separator': {
+      group: 'tools',
+      separator: true,
+    },
+    'create.start-event': createAction('bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none', translate(cn['Create StartEvent'])),
+    'create.intermediate-event': createAction(
+      'bpmn:IntermediateThrowEvent',
+      'event',
+      'bpmn-icon-intermediate-event-none',
+      translate(cn['Create Intermediate/Boundary Event']),
+    ),
+    'create.end-event': createAction('bpmn:EndEvent', 'event', 'bpmn-icon-end-event-none', translate(cn['Create EndEvent'])),
+    'create.exclusive-gateway': createAction('bpmn:ExclusiveGateway', 'gateway', 'bpmn-icon-gateway-none', translate(cn['Create Gateway'])),
+    'create.task': createAction('bpmn:Task', 'activity', 'bpmn-icon-task', translate(cn['Create Task'])),
+    'create.data-object': createAction('bpmn:DataObjectReference', 'data-object', 'bpmn-icon-data-object', translate(cn['Create DataObjectReference'])),
+    'create.data-store': createAction('bpmn:DataStoreReference', 'data-store', 'bpmn-icon-data-store', translate(cn['Create DataStoreReference'])),
+    'create.subprocess-expanded': {
+      group: 'activity',
+      className: 'bpmn-icon-subprocess-expanded',
+      title: translate(cn['Create expanded SubProcess']),
+      action: {
+        dragstart: createSubprocess,
+        click: createSubprocess,
+      },
+    },
+    'create.participant-expanded': {
+      group: 'collaboration',
+      className: 'bpmn-icon-participant',
+      title: translate(cn['Create Pool/Participant']),
+      action: {
+        dragstart: createParticipant,
+        click: createParticipant,
+      },
+    },
+    'create.group': createAction('bpmn:Group', 'artifact', 'bpmn-icon-group', translate(cn['Create Group'])),
+  });
+
+  return actions;
+};
+
+export default bpmnPalette;

+ 853 - 0
lib/palette/bpmnReplace.ts

@@ -0,0 +1,853 @@
+// bpmn-js/lib/features/replace/ReplaceOptions.js
+// bpmn-js流程节点转换为BPMN2.0的配置
+export var START_EVENT = [
+  {
+    label: 'Start Event',
+    actionName: 'replace-with-none-start',
+    className: 'bpmn-icon-start-event-none',
+    target: {
+      type: 'bpmn:StartEvent',
+    },
+  },
+  {
+    label: 'Intermediate Throw Event',
+    actionName: 'replace-with-none-intermediate-throwing',
+    className: 'bpmn-icon-intermediate-event-none',
+    target: {
+      type: 'bpmn:IntermediateThrowEvent',
+    },
+  },
+  {
+    label: 'End Event',
+    actionName: 'replace-with-none-end',
+    className: 'bpmn-icon-end-event-none',
+    target: {
+      type: 'bpmn:EndEvent',
+    },
+  },
+  {
+    label: 'Message Start Event',
+    actionName: 'replace-with-message-start',
+    className: 'bpmn-icon-start-event-message',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:MessageEventDefinition',
+    },
+  },
+  {
+    label: 'Timer Start Event',
+    actionName: 'replace-with-timer-start',
+    className: 'bpmn-icon-start-event-timer',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:TimerEventDefinition',
+    },
+  },
+  {
+    label: 'Conditional Start Event',
+    actionName: 'replace-with-conditional-start',
+    className: 'bpmn-icon-start-event-condition',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:ConditionalEventDefinition',
+    },
+  },
+  {
+    label: 'Signal Start Event',
+    actionName: 'replace-with-signal-start',
+    className: 'bpmn-icon-start-event-signal',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:SignalEventDefinition',
+    },
+  },
+];
+
+export var START_EVENT_SUB_PROCESS = [
+  {
+    label: 'Start Event',
+    actionName: 'replace-with-none-start',
+    className: 'bpmn-icon-start-event-none',
+    target: {
+      type: 'bpmn:StartEvent',
+    },
+  },
+  {
+    label: 'Intermediate Throw Event',
+    actionName: 'replace-with-none-intermediate-throwing',
+    className: 'bpmn-icon-intermediate-event-none',
+    target: {
+      type: 'bpmn:IntermediateThrowEvent',
+    },
+  },
+  {
+    label: 'End Event',
+    actionName: 'replace-with-none-end',
+    className: 'bpmn-icon-end-event-none',
+    target: {
+      type: 'bpmn:EndEvent',
+    },
+  },
+];
+
+export var INTERMEDIATE_EVENT = [
+  {
+    label: 'Start Event',
+    actionName: 'replace-with-none-start',
+    className: 'bpmn-icon-start-event-none',
+    target: {
+      type: 'bpmn:StartEvent',
+    },
+  },
+  {
+    label: 'Intermediate Throw Event',
+    actionName: 'replace-with-none-intermediate-throw',
+    className: 'bpmn-icon-intermediate-event-none',
+    target: {
+      type: 'bpmn:IntermediateThrowEvent',
+    },
+  },
+  {
+    label: 'End Event',
+    actionName: 'replace-with-none-end',
+    className: 'bpmn-icon-end-event-none',
+    target: {
+      type: 'bpmn:EndEvent',
+    },
+  },
+  {
+    label: 'Message Intermediate Catch Event',
+    actionName: 'replace-with-message-intermediate-catch',
+    className: 'bpmn-icon-intermediate-event-catch-message',
+    target: {
+      type: 'bpmn:IntermediateCatchEvent',
+      eventDefinitionType: 'bpmn:MessageEventDefinition',
+    },
+  },
+  {
+    label: 'Message Intermediate Throw Event',
+    actionName: 'replace-with-message-intermediate-throw',
+    className: 'bpmn-icon-intermediate-event-throw-message',
+    target: {
+      type: 'bpmn:IntermediateThrowEvent',
+      eventDefinitionType: 'bpmn:MessageEventDefinition',
+    },
+  },
+  {
+    label: 'Timer Intermediate Catch Event',
+    actionName: 'replace-with-timer-intermediate-catch',
+    className: 'bpmn-icon-intermediate-event-catch-timer',
+    target: {
+      type: 'bpmn:IntermediateCatchEvent',
+      eventDefinitionType: 'bpmn:TimerEventDefinition',
+    },
+  },
+  {
+    label: 'Escalation Intermediate Throw Event',
+    actionName: 'replace-with-escalation-intermediate-throw',
+    className: 'bpmn-icon-intermediate-event-throw-escalation',
+    target: {
+      type: 'bpmn:IntermediateThrowEvent',
+      eventDefinitionType: 'bpmn:EscalationEventDefinition',
+    },
+  },
+  {
+    label: 'Conditional Intermediate Catch Event',
+    actionName: 'replace-with-conditional-intermediate-catch',
+    className: 'bpmn-icon-intermediate-event-catch-condition',
+    target: {
+      type: 'bpmn:IntermediateCatchEvent',
+      eventDefinitionType: 'bpmn:ConditionalEventDefinition',
+    },
+  },
+  {
+    label: 'Link Intermediate Catch Event',
+    actionName: 'replace-with-link-intermediate-catch',
+    className: 'bpmn-icon-intermediate-event-catch-link',
+    target: {
+      type: 'bpmn:IntermediateCatchEvent',
+      eventDefinitionType: 'bpmn:LinkEventDefinition',
+      eventDefinitionAttrs: {
+        name: '',
+      },
+    },
+  },
+  {
+    label: 'Link Intermediate Throw Event',
+    actionName: 'replace-with-link-intermediate-throw',
+    className: 'bpmn-icon-intermediate-event-throw-link',
+    target: {
+      type: 'bpmn:IntermediateThrowEvent',
+      eventDefinitionType: 'bpmn:LinkEventDefinition',
+      eventDefinitionAttrs: {
+        name: '',
+      },
+    },
+  },
+  {
+    label: 'Compensation Intermediate Throw Event',
+    actionName: 'replace-with-compensation-intermediate-throw',
+    className: 'bpmn-icon-intermediate-event-throw-compensation',
+    target: {
+      type: 'bpmn:IntermediateThrowEvent',
+      eventDefinitionType: 'bpmn:CompensateEventDefinition',
+    },
+  },
+  {
+    label: 'Signal Intermediate Catch Event',
+    actionName: 'replace-with-signal-intermediate-catch',
+    className: 'bpmn-icon-intermediate-event-catch-signal',
+    target: {
+      type: 'bpmn:IntermediateCatchEvent',
+      eventDefinitionType: 'bpmn:SignalEventDefinition',
+    },
+  },
+  {
+    label: 'Signal Intermediate Throw Event',
+    actionName: 'replace-with-signal-intermediate-throw',
+    className: 'bpmn-icon-intermediate-event-throw-signal',
+    target: {
+      type: 'bpmn:IntermediateThrowEvent',
+      eventDefinitionType: 'bpmn:SignalEventDefinition',
+    },
+  },
+];
+
+export var END_EVENT = [
+  {
+    label: 'Start Event',
+    actionName: 'replace-with-none-start',
+    className: 'bpmn-icon-start-event-none',
+    target: {
+      type: 'bpmn:StartEvent',
+    },
+  },
+  {
+    label: 'Intermediate Throw Event',
+    actionName: 'replace-with-none-intermediate-throw',
+    className: 'bpmn-icon-intermediate-event-none',
+    target: {
+      type: 'bpmn:IntermediateThrowEvent',
+    },
+  },
+  {
+    label: 'End Event',
+    actionName: 'replace-with-none-end',
+    className: 'bpmn-icon-end-event-none',
+    target: {
+      type: 'bpmn:EndEvent',
+    },
+  },
+  {
+    label: 'Message End Event',
+    actionName: 'replace-with-message-end',
+    className: 'bpmn-icon-end-event-message',
+    target: {
+      type: 'bpmn:EndEvent',
+      eventDefinitionType: 'bpmn:MessageEventDefinition',
+    },
+  },
+  {
+    label: 'Escalation End Event',
+    actionName: 'replace-with-escalation-end',
+    className: 'bpmn-icon-end-event-escalation',
+    target: {
+      type: 'bpmn:EndEvent',
+      eventDefinitionType: 'bpmn:EscalationEventDefinition',
+    },
+  },
+  {
+    label: 'Error End Event',
+    actionName: 'replace-with-error-end',
+    className: 'bpmn-icon-end-event-error',
+    target: {
+      type: 'bpmn:EndEvent',
+      eventDefinitionType: 'bpmn:ErrorEventDefinition',
+    },
+  },
+  {
+    label: 'Cancel End Event',
+    actionName: 'replace-with-cancel-end',
+    className: 'bpmn-icon-end-event-cancel',
+    target: {
+      type: 'bpmn:EndEvent',
+      eventDefinitionType: 'bpmn:CancelEventDefinition',
+    },
+  },
+  {
+    label: 'Compensation End Event',
+    actionName: 'replace-with-compensation-end',
+    className: 'bpmn-icon-end-event-compensation',
+    target: {
+      type: 'bpmn:EndEvent',
+      eventDefinitionType: 'bpmn:CompensateEventDefinition',
+    },
+  },
+  {
+    label: 'Signal End Event',
+    actionName: 'replace-with-signal-end',
+    className: 'bpmn-icon-end-event-signal',
+    target: {
+      type: 'bpmn:EndEvent',
+      eventDefinitionType: 'bpmn:SignalEventDefinition',
+    },
+  },
+  {
+    label: 'Terminate End Event',
+    actionName: 'replace-with-terminate-end',
+    className: 'bpmn-icon-end-event-terminate',
+    target: {
+      type: 'bpmn:EndEvent',
+      eventDefinitionType: 'bpmn:TerminateEventDefinition',
+    },
+  },
+];
+
+export var GATEWAY = [
+  {
+    label: 'Exclusive Gateway',
+    actionName: 'replace-with-exclusive-gateway',
+    className: 'bpmn-icon-gateway-xor',
+    target: {
+      type: 'bpmn:ExclusiveGateway',
+    },
+  },
+  {
+    label: 'Parallel Gateway',
+    actionName: 'replace-with-parallel-gateway',
+    className: 'bpmn-icon-gateway-parallel',
+    target: {
+      type: 'bpmn:InclusiveGateway',
+    },
+  },
+  {
+    label: 'Inclusive Gateway',
+    actionName: 'replace-with-inclusive-gateway',
+    className: 'bpmn-icon-gateway-or',
+    target: {
+      type: 'bpmn:InclusiveGateway',
+    },
+  },
+  {
+    label: 'Complex Gateway',
+    actionName: 'replace-with-complex-gateway',
+    className: 'bpmn-icon-gateway-complex',
+    target: {
+      type: 'bpmn:ComplexGateway',
+    },
+  },
+  {
+    label: 'Event based Gateway',
+    actionName: 'replace-with-event-based-gateway',
+    className: 'bpmn-icon-gateway-eventbased',
+    target: {
+      type: 'bpmn:EventBasedGateway',
+      instantiate: false,
+      eventGatewayType: 'Exclusive',
+    },
+  },
+
+  // Gateways deactivated until https://github.com/bpmn-io/bpmn-js/issues/194
+  // {
+  //   label: 'Event based instantiating Gateway',
+  //   actionName: 'replace-with-exclusive-event-based-gateway',
+  //   className: 'bpmn-icon-exclusive-event-based',
+  //   target: {
+  //     type: 'bpmn:EventBasedGateway'
+  //   },
+  //   options: {
+  //     businessObject: { instantiate: true, eventGatewayType: 'Exclusive' }
+  //   }
+  // },
+  // {
+  //   label: 'Parallel Event based instantiating Gateway',
+  //   actionName: 'replace-with-parallel-event-based-instantiate-gateway',
+  //   className: 'bpmn-icon-parallel-event-based-instantiate-gateway',
+  //   target: {
+  //     type: 'bpmn:EventBasedGateway'
+  //   },
+  //   options: {
+  //     businessObject: { instantiate: true, eventGatewayType: 'Parallel' }
+  //   }
+  // }
+];
+
+export var SUBPROCESS_EXPANDED = [
+  {
+    label: 'Transaction',
+    actionName: 'replace-with-transaction',
+    className: 'bpmn-icon-transaction',
+    target: {
+      type: 'bpmn:Transaction',
+      isExpanded: true,
+    },
+  },
+  {
+    label: 'Event Sub Process',
+    actionName: 'replace-with-event-subprocess',
+    className: 'bpmn-icon-event-subprocess-expanded',
+    target: {
+      type: 'bpmn:SubProcess',
+      triggeredByEvent: true,
+      isExpanded: true,
+    },
+  },
+  {
+    label: 'Sub Process (collapsed)',
+    actionName: 'replace-with-collapsed-subprocess',
+    className: 'bpmn-icon-subprocess-collapsed',
+    target: {
+      type: 'bpmn:SubProcess',
+      isExpanded: false,
+    },
+  },
+];
+
+export var TRANSACTION = [
+  {
+    label: 'Sub Process',
+    actionName: 'replace-with-subprocess',
+    className: 'bpmn-icon-subprocess-expanded',
+    target: {
+      type: 'bpmn:SubProcess',
+      isExpanded: true,
+    },
+  },
+  {
+    label: 'Event Sub Process',
+    actionName: 'replace-with-event-subprocess',
+    className: 'bpmn-icon-event-subprocess-expanded',
+    target: {
+      type: 'bpmn:SubProcess',
+      triggeredByEvent: true,
+      isExpanded: true,
+    },
+  },
+];
+
+export var EVENT_SUB_PROCESS = [
+  {
+    label: 'Sub Process',
+    actionName: 'replace-with-subprocess',
+    className: 'bpmn-icon-subprocess-expanded',
+    target: {
+      type: 'bpmn:SubProcess',
+      isExpanded: true,
+    },
+  },
+  {
+    label: 'Transaction',
+    actionName: 'replace-with-transaction',
+    className: 'bpmn-icon-transaction',
+    target: {
+      type: 'bpmn:Transaction',
+      isExpanded: true,
+    },
+  },
+];
+
+export var TASK = [
+  {
+    label: 'Task',
+    actionName: 'replace-with-task',
+    className: 'bpmn-icon-task',
+    target: {
+      type: 'bpmn:Task',
+    },
+  },
+  {
+    label: 'Send Task',
+    actionName: 'replace-with-send-task',
+    className: 'bpmn-icon-send',
+    target: {
+      type: 'bpmn:SendTask',
+    },
+  },
+  {
+    label: 'Receive Task',
+    actionName: 'replace-with-receive-task',
+    className: 'bpmn-icon-receive',
+    target: {
+      type: 'bpmn:ReceiveTask',
+    },
+  },
+  {
+    label: 'User Task',
+    actionName: 'replace-with-user-task',
+    className: 'bpmn-icon-user',
+    target: {
+      type: 'bpmn:UserTask',
+      name: 'jnpf-task',
+    },
+  },
+  {
+    label: 'Manual Task',
+    actionName: 'replace-with-manual-task',
+    className: 'bpmn-icon-manual',
+    target: {
+      type: 'bpmn:ManualTask',
+    },
+  },
+  {
+    label: 'Business Rule Task',
+    actionName: 'replace-with-rule-task',
+    className: 'bpmn-icon-business-rule',
+    target: {
+      type: 'bpmn:BusinessRuleTask',
+    },
+  },
+  {
+    label: 'Service Task',
+    actionName: 'replace-with-service-task',
+    className: 'bpmn-icon-service',
+    target: {
+      type: 'bpmn:ServiceTask',
+    },
+  },
+  {
+    label: 'Script Task',
+    actionName: 'replace-with-script-task',
+    className: 'bpmn-icon-script',
+    target: {
+      type: 'bpmn:ScriptTask',
+    },
+  },
+  {
+    label: 'Call Activity',
+    actionName: 'replace-with-call-activity',
+    className: 'bpmn-icon-call-activity',
+    target: {
+      type: 'bpmn:CallActivity',
+    },
+  },
+  {
+    label: 'Sub Process (collapsed)',
+    actionName: 'replace-with-collapsed-subprocess',
+    className: 'bpmn-icon-subprocess-collapsed',
+    target: {
+      type: 'bpmn:SubProcess',
+      isExpanded: false,
+    },
+  },
+  {
+    label: 'Sub Process (expanded)',
+    actionName: 'replace-with-expanded-subprocess',
+    className: 'bpmn-icon-subprocess-expanded',
+    target: {
+      type: 'bpmn:SubProcess',
+      isExpanded: true,
+    },
+  },
+];
+
+export var DATA_OBJECT_REFERENCE = [
+  {
+    label: 'Data Store Reference',
+    actionName: 'replace-with-data-store-reference',
+    className: 'bpmn-icon-data-store',
+    target: {
+      type: 'bpmn:DataStoreReference',
+    },
+  },
+];
+
+export var DATA_STORE_REFERENCE = [
+  {
+    label: 'Data Object Reference',
+    actionName: 'replace-with-data-object-reference',
+    className: 'bpmn-icon-data-object',
+    target: {
+      type: 'bpmn:DataObjectReference',
+    },
+  },
+];
+
+export var BOUNDARY_EVENT = [
+  {
+    label: 'Message Boundary Event',
+    actionName: 'replace-with-message-boundary',
+    className: 'bpmn-icon-intermediate-event-catch-message',
+    target: {
+      type: 'bpmn:BoundaryEvent',
+      eventDefinitionType: 'bpmn:MessageEventDefinition',
+    },
+  },
+  {
+    label: 'Timer Boundary Event',
+    actionName: 'replace-with-timer-boundary',
+    className: 'bpmn-icon-intermediate-event-catch-timer',
+    target: {
+      type: 'bpmn:BoundaryEvent',
+      eventDefinitionType: 'bpmn:TimerEventDefinition',
+    },
+  },
+  {
+    label: 'Escalation Boundary Event',
+    actionName: 'replace-with-escalation-boundary',
+    className: 'bpmn-icon-intermediate-event-catch-escalation',
+    target: {
+      type: 'bpmn:BoundaryEvent',
+      eventDefinitionType: 'bpmn:EscalationEventDefinition',
+    },
+  },
+  {
+    label: 'Conditional Boundary Event',
+    actionName: 'replace-with-conditional-boundary',
+    className: 'bpmn-icon-intermediate-event-catch-condition',
+    target: {
+      type: 'bpmn:BoundaryEvent',
+      eventDefinitionType: 'bpmn:ConditionalEventDefinition',
+    },
+  },
+  {
+    label: 'Error Boundary Event',
+    actionName: 'replace-with-error-boundary',
+    className: 'bpmn-icon-intermediate-event-catch-error',
+    target: {
+      type: 'bpmn:BoundaryEvent',
+      eventDefinitionType: 'bpmn:ErrorEventDefinition',
+    },
+  },
+  {
+    label: 'Cancel Boundary Event',
+    actionName: 'replace-with-cancel-boundary',
+    className: 'bpmn-icon-intermediate-event-catch-cancel',
+    target: {
+      type: 'bpmn:BoundaryEvent',
+      eventDefinitionType: 'bpmn:CancelEventDefinition',
+    },
+  },
+  {
+    label: 'Signal Boundary Event',
+    actionName: 'replace-with-signal-boundary',
+    className: 'bpmn-icon-intermediate-event-catch-signal',
+    target: {
+      type: 'bpmn:BoundaryEvent',
+      eventDefinitionType: 'bpmn:SignalEventDefinition',
+    },
+  },
+  {
+    label: 'Compensation Boundary Event',
+    actionName: 'replace-with-compensation-boundary',
+    className: 'bpmn-icon-intermediate-event-catch-compensation',
+    target: {
+      type: 'bpmn:BoundaryEvent',
+      eventDefinitionType: 'bpmn:CompensateEventDefinition',
+    },
+  },
+  {
+    label: 'Message Boundary Event (non-interrupting)',
+    actionName: 'replace-with-non-interrupting-message-boundary',
+    className: 'bpmn-icon-intermediate-event-catch-non-interrupting-message',
+    target: {
+      type: 'bpmn:BoundaryEvent',
+      eventDefinitionType: 'bpmn:MessageEventDefinition',
+      cancelActivity: false,
+    },
+  },
+  {
+    label: 'Timer Boundary Event (non-interrupting)',
+    actionName: 'replace-with-non-interrupting-timer-boundary',
+    className: 'bpmn-icon-intermediate-event-catch-non-interrupting-timer',
+    target: {
+      type: 'bpmn:BoundaryEvent',
+      eventDefinitionType: 'bpmn:TimerEventDefinition',
+      cancelActivity: false,
+    },
+  },
+  {
+    label: 'Escalation Boundary Event (non-interrupting)',
+    actionName: 'replace-with-non-interrupting-escalation-boundary',
+    className: 'bpmn-icon-intermediate-event-catch-non-interrupting-escalation',
+    target: {
+      type: 'bpmn:BoundaryEvent',
+      eventDefinitionType: 'bpmn:EscalationEventDefinition',
+      cancelActivity: false,
+    },
+  },
+  {
+    label: 'Conditional Boundary Event (non-interrupting)',
+    actionName: 'replace-with-non-interrupting-conditional-boundary',
+    className: 'bpmn-icon-intermediate-event-catch-non-interrupting-condition',
+    target: {
+      type: 'bpmn:BoundaryEvent',
+      eventDefinitionType: 'bpmn:ConditionalEventDefinition',
+      cancelActivity: false,
+    },
+  },
+  {
+    label: 'Signal Boundary Event (non-interrupting)',
+    actionName: 'replace-with-non-interrupting-signal-boundary',
+    className: 'bpmn-icon-intermediate-event-catch-non-interrupting-signal',
+    target: {
+      type: 'bpmn:BoundaryEvent',
+      eventDefinitionType: 'bpmn:SignalEventDefinition',
+      cancelActivity: false,
+    },
+  },
+];
+
+export var EVENT_SUB_PROCESS_START_EVENT = [
+  {
+    label: 'Message Start Event',
+    actionName: 'replace-with-message-start',
+    className: 'bpmn-icon-start-event-message',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:MessageEventDefinition',
+    },
+  },
+  {
+    label: 'Timer Start Event',
+    actionName: 'replace-with-timer-start',
+    className: 'bpmn-icon-start-event-timer',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:TimerEventDefinition',
+    },
+  },
+  {
+    label: 'Conditional Start Event',
+    actionName: 'replace-with-conditional-start',
+    className: 'bpmn-icon-start-event-condition',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:ConditionalEventDefinition',
+    },
+  },
+  {
+    label: 'Signal Start Event',
+    actionName: 'replace-with-signal-start',
+    className: 'bpmn-icon-start-event-signal',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:SignalEventDefinition',
+    },
+  },
+  {
+    label: 'Error Start Event',
+    actionName: 'replace-with-error-start',
+    className: 'bpmn-icon-start-event-error',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:ErrorEventDefinition',
+    },
+  },
+  {
+    label: 'Escalation Start Event',
+    actionName: 'replace-with-escalation-start',
+    className: 'bpmn-icon-start-event-escalation',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:EscalationEventDefinition',
+    },
+  },
+  {
+    label: 'Compensation Start Event',
+    actionName: 'replace-with-compensation-start',
+    className: 'bpmn-icon-start-event-compensation',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:CompensateEventDefinition',
+    },
+  },
+  {
+    label: 'Message Start Event (non-interrupting)',
+    actionName: 'replace-with-non-interrupting-message-start',
+    className: 'bpmn-icon-start-event-non-interrupting-message',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:MessageEventDefinition',
+      isInterrupting: false,
+    },
+  },
+  {
+    label: 'Timer Start Event (non-interrupting)',
+    actionName: 'replace-with-non-interrupting-timer-start',
+    className: 'bpmn-icon-start-event-non-interrupting-timer',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:TimerEventDefinition',
+      isInterrupting: false,
+    },
+  },
+  {
+    label: 'Conditional Start Event (non-interrupting)',
+    actionName: 'replace-with-non-interrupting-conditional-start',
+    className: 'bpmn-icon-start-event-non-interrupting-condition',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:ConditionalEventDefinition',
+      isInterrupting: false,
+    },
+  },
+  {
+    label: 'Signal Start Event (non-interrupting)',
+    actionName: 'replace-with-non-interrupting-signal-start',
+    className: 'bpmn-icon-start-event-non-interrupting-signal',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:SignalEventDefinition',
+      isInterrupting: false,
+    },
+  },
+  {
+    label: 'Escalation Start Event (non-interrupting)',
+    actionName: 'replace-with-non-interrupting-escalation-start',
+    className: 'bpmn-icon-start-event-non-interrupting-escalation',
+    target: {
+      type: 'bpmn:StartEvent',
+      eventDefinitionType: 'bpmn:EscalationEventDefinition',
+      isInterrupting: false,
+    },
+  },
+];
+
+export var SEQUENCE_FLOW = [
+  {
+    label: 'Sequence Flow',
+    actionName: 'replace-with-sequence-flow',
+    className: 'bpmn-icon-connection',
+  },
+  {
+    label: 'Default Flow',
+    actionName: 'replace-with-default-flow',
+    className: 'bpmn-icon-default-flow',
+  },
+  {
+    label: 'Conditional Flow',
+    actionName: 'replace-with-conditional-flow',
+    className: 'bpmn-icon-conditional-flow',
+  },
+];
+
+export var PARTICIPANT = [
+  {
+    label: 'Expanded Pool',
+    actionName: 'replace-with-expanded-pool',
+    className: 'bpmn-icon-participant',
+    target: {
+      type: 'bpmn:Participant',
+      isExpanded: true,
+    },
+  },
+  {
+    label: function (element: any) {
+      var label = 'Empty Pool';
+
+      if (element.children && element.children.length) {
+        label += ' (removes content)';
+      }
+
+      return label;
+    },
+    actionName: 'replace-with-collapsed-pool',
+
+    // TODO(@janstuemmel): maybe design new icon
+    className: 'bpmn-icon-lane',
+    target: {
+      type: 'bpmn:Participant',
+      isExpanded: false,
+    },
+  },
+];

+ 64 - 0
lib/palette/bpmnType.ts

@@ -0,0 +1,64 @@
+// bpmn-js/lib/draw/BpmnRenderer.js -> handlers
+// BPMN2.0文件节点类型
+export default {
+  Event: 'bpmn:Event',
+  StartEvent: 'bpmn:StartEvent',
+  MessageEventDefinition: 'bpmn:MessageEventDefinition',
+  TimerEventDefinition: 'bpmn:TimerEventDefinition',
+  EscalationEventDefinition: 'bpmn:EscalationEventDefinition',
+  ConditionalEventDefinition: 'bpmn:ConditionalEventDefinition',
+  LinkEventDefinition: 'bpmn:LinkEventDefinition',
+  ErrorEventDefinition: 'bpmn:ErrorEventDefinition',
+  CancelEventDefinition: 'bpmn:CancelEventDefinition',
+  CompensateEventDefinition: 'bpmn:CompensateEventDefinition',
+  SignalEventDefinition: 'bpmn:SignalEventDefinition',
+  MultipleEventDefinition: 'bpmn:MultipleEventDefinition',
+  ParallelMultipleEventDefinition: 'bpmn:ParallelMultipleEventDefinition',
+  EndEvent: 'bpmn:EndEvent',
+  TerminateEventDefinition: 'bpmn:TerminateEventDefinition',
+  IntermediateEvent: 'bpmn:IntermediateEvent',
+  IntermediateCatchEvent: 'bpmn:IntermediateCatchEvent',
+  IntermediateThrowEvent: 'bpmn:IntermediateThrowEvent',
+  Activity: 'bpmn:Activity',
+  Task: 'bpmn:Task',
+  ServiceTask: 'bpmn:ServiceTask',
+  UserTask: 'bpmn:UserTask',
+  ManualTask: 'bpmn:ManualTask',
+  SendTask: 'bpmn:SendTask',
+  ReceiveTask: 'bpmn:ReceiveTask',
+  ScriptTask: 'bpmn:ScriptTask',
+  BusinessRuleTask: 'bpmn:BusinessRuleTask',
+  SubProcess: 'bpmn:SubProcess',
+  AdHocSubProcess: 'bpmn:AdHocSubProcess',
+  Transaction: 'bpmn:Transaction',
+  CallActivity: 'bpmn:CallActivity',
+  Participant: 'bpmn:Participant',
+  Lane: 'bpmn:Lane',
+  InclusiveGateway: 'bpmn:InclusiveGateway',
+  ExclusiveGateway: 'bpmn:ExclusiveGateway',
+  ComplexGateway: 'bpmn:ComplexGateway',
+  ParallelGateway: 'bpmn:ParallelGateway',
+  EventBasedGateway: 'bpmn:EventBasedGateway',
+  Gateway: 'bpmn:Gateway',
+  SequenceFlow: 'bpmn:SequenceFlow',
+  Association: 'bpmn:Association',
+  DataInputAssociation: 'bpmn:DataInputAssociation',
+  DataOutputAssociation: 'bpmn:DataOutputAssociation',
+  MessageFlow: 'bpmn:MessageFlow',
+  DataObject: 'bpmn:DataObject',
+  DataObjectReference: 'bpmn:DataObjectReference',
+  DataInput: 'bpmn:DataInput',
+  DataOutput: 'bpmn:DataOutput',
+  DataStoreReference: 'bpmn:DataStoreReference',
+  BoundaryEvent: 'bpmn:BoundaryEvent',
+  Group: 'bpmn:Group',
+  label: 'label',
+  TextAnnotation: 'bpmn:TextAnnotation',
+  ParticipantMultiplicityMarker: 'ParticipantMultiplicityMarker',
+  SubProcessMarker: 'SubProcessMarker',
+  ParallelMarker: 'ParallelMarker',
+  SequentialMarker: 'SequentialMarker',
+  CompensationMarker: 'CompensationMarker',
+  LoopMarker: 'LoopMarker',
+  AdhocMarker: 'AdhocMarker',
+};

+ 31 - 0
lib/palette/index.ts

@@ -0,0 +1,31 @@
+import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider';
+import bpmnPalette from './bpmnPalette';
+import YmPalette from './CustomizePalette';
+
+class YmPaletteProvider extends PaletteProvider {
+  constructor(
+    palette: any,
+    create: any,
+    elementFactory: any,
+    spaceTool: any,
+    lassoTool: any,
+    handTool: any,
+    globalConnect: any,
+    translate: any,
+    bpmnFactory: any,
+  ) {
+    super(palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate);
+    //  this._bpmnFactory = bpmnFactory;
+    palette.registerProvider(this);
+  }
+
+  // @ts-ignore
+  getPaletteEntries(element: any) {
+    // 继承 扩展自定义 palette 需要原生怎开启注释
+    return Object.assign(bpmnPalette(this), YmPalette(this));
+  }
+}
+
+YmPaletteProvider.$inject = ['palette', 'create', 'elementFactory', 'spaceTool', 'lassoTool', 'handTool', 'globalConnect', 'translate', 'bpmnFactory'];
+
+export default YmPaletteProvider;

+ 26 - 0
lib/previewModeler/index.ts

@@ -0,0 +1,26 @@
+import Modeler from 'bpmn-js/lib/Modeler';
+import jnpfRenderer from './renderer';
+import jnpfElementFactory from '../factory';
+import jnpfOutline from '../outline';
+import jnpfBusinessData from '../business';
+let flowInfo: any;
+const modeler: any = options => [
+  {
+    __init__: ['bpmnRenderer', 'elementFactory', 'jnpfData', 'outlineProvider'],
+    bpmnRenderer: ['type', jnpfRenderer, { options }], // 画布渲染
+    elementFactory: ['type', jnpfElementFactory], // 元素工厂
+    jnpfData: ['type', jnpfBusinessData], // 用于放置业务数据
+    outlineProvider: ['type', jnpfOutline, { options }], // 元素的外边框(用于修改边框颜色,注:线条颜色有svg获取标签再去修改颜色及箭头)
+  },
+];
+
+class bpmnModeler extends Modeler {
+  constructor(options: any) {
+    flowInfo = options.flowInfo;
+    super(options);
+  }
+}
+
+bpmnModeler.prototype['_modules'] = [].concat(bpmnModeler.prototype['_modules'], modeler(flowInfo));
+
+export default bpmnModeler;

+ 145 - 0
lib/previewModeler/renderer/CustomizeRenderer.ts

@@ -0,0 +1,145 @@
+import { changeTypeByTaskShape, typeConfig } from '../../config';
+import { append as svgAppend, create as svgCreate } from 'tiny-svg';
+import {
+  bpmnEnd,
+  bpmnExecute,
+  bpmnGroup,
+  bpmnLabel,
+  bpmnOutside,
+  bpmnProcessing,
+  bpmnStart,
+  bpmnSubFlow,
+  bpmnTask,
+  bpmnTrigger,
+  typeExecute,
+  typeOutside,
+  typeProcessing,
+  typeSubFlow,
+  typeTrigger,
+} from '../../config/variableName';
+
+/**
+ * svg重画bpmn节点
+ */
+export default (parentNode: any, element: any, jnpfFlowInfo: any, injector: any) => {
+  let data = jnpfFlowInfo?.flowNodes[element.id];
+  let nodeMap = jnpfFlowInfo?.nodeList;
+  let isPreview = jnpfFlowInfo?.isPreview;
+  let type = element.type; // 获取到类型
+  if (typeConfig && typeConfig[type]) {
+    if (type === bpmnGroup) return null;
+    if (data?.type === typeSubFlow || element.wnType === typeSubFlow) type = bpmnSubFlow;
+    if (data?.type === typeProcessing || element.wnType === typeProcessing) type = bpmnProcessing;
+    if (data?.type === typeTrigger || element.wnType === typeTrigger) type = bpmnTrigger;
+    if (data?.type === typeExecute || element.wnType === typeExecute) type = bpmnExecute;
+    if (data?.type === typeOutside || element.wnType === typeOutside) type = bpmnOutside;
+    if (changeTypeByTaskShape[data?.type] || changeTypeByTaskShape[element?.wnType]) type = changeTypeByTaskShape[element?.wnType || data?.type];
+    let { renderer } = typeConfig[type];
+    let { icon, iconColor, rendererName, background, titleColor, attr, bodyDefaultText } = renderer;
+    //  直接修改元素的宽高
+    element['width'] = type === bpmnLabel ? 128 : attr.width;
+    element['height'] = type === bpmnLabel ? 28 : attr.height;
+    let nodeName = element.nodeName != null ? element.nodeName : data?.nodeName != null ? data.nodeName : rendererName;
+    let nodeContent = element.elementBodyName || nodeMap.get(element.id)?.userName || data?.content || bodyDefaultText;
+    if (isPreview) {
+      if (nodeMap.get(element.id)?.type) {
+        if (nodeMap.get(element.id)?.type === '0') {
+          titleColor = 'linear-gradient(90deg, #AEEFC2 0%, #4ED587 100%)';
+          iconColor = '#25a210';
+        }
+        if (nodeMap.get(element.id)?.type === '1') {
+          titleColor = 'linear-gradient(90deg, #C0EDF8 0%, #A6DEF8 100%)';
+          iconColor = '#1eaceb';
+        }
+        if (nodeMap.get(element.id)?.type === '3') {
+          titleColor = 'linear-gradient(90deg, #FDC9D1 0%,#E03845 100%)';
+          iconColor = '#E03845';
+        }
+      } else {
+        titleColor = 'linear-gradient(90deg, #CED1D5 0%, #CBCBCC 100%);';
+        iconColor = '#4c4c58';
+      }
+    }
+    let foreignObject: any = svgCreate('foreignObject', {
+      width: type === bpmnLabel ? 128 : attr.width,
+      height: type === bpmnLabel ? 28 : attr.height,
+      class: type === bpmnStart || type === bpmnEnd ? 'begin-or-end-node' : 'task-node',
+    });
+    // 开始节点
+    if (type === bpmnStart) {
+      foreignObject.innerHTML = `
+      <div class="node-container start-node-container" style="background:${background}" >
+        <div class='node-top-container'>
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+    // 审批节点
+    if (type === bpmnTask) {
+      foreignObject.innerHTML = `
+      <div class="node-container" style="background:${background}" >
+        <div class='node-top-container' style="background:${titleColor};">
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+        <div class='node-bottom-container'>
+          <span>${nodeContent}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+    // 子流程节点
+    if (type === bpmnSubFlow || type === bpmnProcessing || type === bpmnTrigger || changeTypeByTaskShape[element?.wnType || data?.type]) {
+      foreignObject.innerHTML = `
+      <div class="node-container" style="background:${background}" >
+        <div class='node-top-container' style="background:${titleColor}">
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+        <div class='node-bottom-container'>
+          <span>${nodeContent}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+    // 结束节点
+    if (type === bpmnEnd) {
+      foreignObject.innerHTML = `
+      <div class="node-container end-node-container" style="background:${background}" >
+        <div class='node-top-container'>
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+    // label渲染
+    if (type === bpmnLabel) {
+      let jnpfData = injector?.get('jnpfData');
+      let data = jnpfData.getValue('global');
+      let connectName = jnpfData.getValue(element.id.replace('_label', ''))?.nodeName || '连接线';
+      if (data.isShowConditions) {
+        let foreignObject: any = svgCreate('foreignObject', {
+          width: 128,
+          height: 28,
+          class: 'label-node',
+        });
+        foreignObject.innerHTML = `
+          <div class="node-container-label" >
+            <div class='node-top-container'>
+              <span> ${data.showNameType === 1 ? element.text : connectName}</span>
+            </div>
+          </div>`;
+        element.text && svgAppend(parentNode, foreignObject);
+        return parentNode;
+      }
+      return null;
+    }
+  }
+};

+ 61 - 0
lib/previewModeler/renderer/index.ts

@@ -0,0 +1,61 @@
+import BpmnRenderer from 'bpmn-js/lib/draw/BpmnRenderer';
+import CustomizeRenderer from './CustomizeRenderer';
+import { getRectPath } from 'bpmn-js/lib/draw/BpmnRenderUtil';
+import createAddMarkerSelect from '../../simpleModeler/renderer/connect/marker';
+let jnpfCanvas: any;
+let jnpfFlowInfo: any;
+class YmRenderer extends BpmnRenderer {
+  _injector: any;
+  constructor(config: any, injector: any, eventBus: any, styles: any, pathMap: any, canvas: any, textRenderer: any, flowInfo: any, priority: number) {
+    super(
+      (config = {
+        defaultLabelColor: 'rgb(102,102,102)',
+        defaultStrokeColor: '#A2B9D5',
+        ...config,
+      }),
+      eventBus,
+      styles,
+      pathMap,
+      canvas,
+      textRenderer,
+      priority,
+    );
+    jnpfCanvas = canvas;
+    jnpfFlowInfo = flowInfo;
+    this._injector = injector;
+  }
+
+  canRender(element: any) {
+    return super.canRender(element);
+  }
+  // 绘制画布上元素
+  drawShape(parentNode: any, element: any) {
+    if (element) return CustomizeRenderer(parentNode, element, jnpfFlowInfo, this._injector) || super.drawShape(parentNode, element);
+    return super.drawShape(parentNode, element);
+  }
+
+  drawConnection(parentGfx: any, element: any) {
+    let source = element.source;
+    let target = element.target;
+    let nodeMap = jnpfFlowInfo?.nodeList;
+    let stroke = '';
+    if (nodeMap.has(source?.id) && nodeMap.has(target?.id)) {
+      const targetType = nodeMap.get(target.id)?.type;
+      const sourceType = nodeMap.get(source.id)?.type;
+      if (sourceType === '0' && (targetType === '0' || targetType === '3')) stroke = '#4ED587';
+      if (sourceType === '0' && targetType === '1') stroke = '#1eaceb';
+    }
+    let connect = super.drawConnection(parentGfx, element, { stroke });
+    createAddMarkerSelect(element, jnpfCanvas);
+    return connect;
+  }
+
+  // 绘制
+  getShapePath(shape: any) {
+    return getRectPath(shape);
+  }
+}
+
+YmRenderer.$inject = ['config.bpmnRenderer', 'injector', 'eventBus', 'styles', 'pathMap', 'canvas', 'textRenderer', 'config.flowInfo'];
+
+export default YmRenderer;

+ 149 - 0
lib/renderer/CustomizeRenderer.ts

@@ -0,0 +1,149 @@
+import { changeTypeByTaskShape, typeConfig } from '../config';
+import { append as svgAppend, create as svgCreate } from 'tiny-svg';
+import {
+  bpmnEnd,
+  bpmnExecute,
+  bpmnGroup,
+  bpmnLabel,
+  bpmnOutside,
+  bpmnProcessing,
+  bpmnStart,
+  bpmnSubFlow,
+  bpmnTask,
+  bpmnTrigger,
+  typeExecute,
+  typeOutside,
+  typeProcessing,
+  typeSubFlow,
+  typeTrigger,
+} from '../config/variableName';
+
+/**
+ * svg重画bpmn节点
+ */
+export default (parentNode: any, element: any, jnpfFlowInfo: any, injector: any) => {
+  let data = jnpfFlowInfo?.flowNodes[element.id];
+  let nodeMap = jnpfFlowInfo?.nodeList;
+  let isPreview = jnpfFlowInfo?.isPreview;
+  let type = element.type; // 获取到类型
+  if (typeConfig && typeConfig[type]) {
+    if (type === bpmnGroup) return null;
+    const typeMap = {
+      [typeSubFlow]: bpmnSubFlow,
+      [typeProcessing]: bpmnProcessing,
+      [typeTrigger]: bpmnTrigger,
+      [typeExecute]: bpmnExecute,
+      [typeOutside]: bpmnOutside,
+    };
+    let matchedType = typeMap[data?.type] || typeMap[element.wnType];
+    if(matchedType) type = matchedType;
+    if (changeTypeByTaskShape[data?.type] || changeTypeByTaskShape[element?.wnType]) type = changeTypeByTaskShape[element?.wnType || data?.type];
+    let { renderer } = typeConfig[type];
+    let { icon, iconColor, rendererName, background, titleColor, attr, bodyDefaultText } = renderer;
+    //  直接修改元素的宽高
+    element['width'] = type === bpmnLabel ? 128 : attr.width;
+    element['height'] = type === bpmnLabel ? 28 : attr.height;
+    let nodeName = element.nodeName != null ? element.nodeName : data?.nodeName != null ? data.nodeName : rendererName;
+    let nodeContent = element.elementBodyName || nodeMap.get(element.id)?.userName || data?.content || bodyDefaultText;
+    if (isPreview) {
+      if (nodeMap.get(element.id)?.type) {
+        if (nodeMap.get(element.id)?.type === '0') {
+          titleColor = 'linear-gradient(90deg, #AEEFC2 0%, #4ED587 100%)';
+          iconColor = '#25a210';
+        }
+        if (nodeMap.get(element.id)?.type === '1') {
+          titleColor = 'linear-gradient(90deg, #C0EDF8 0%, #A6DEF8 100%)';
+          iconColor = '#1eaceb';
+        }
+        if (nodeMap.get(element.id)?.type === '3') {
+          titleColor = 'linear-gradient(90deg, #FDC9D1 0%,#E03845 100%)';
+          iconColor = '#E03845';
+        }
+      } else {
+        titleColor = 'linear-gradient(90deg, #CED1D5 0%, #CBCBCC 100%);';
+        iconColor = '#4c4c58';
+      }
+    }
+    let foreignObject: any = svgCreate('foreignObject', {
+      width: type === bpmnLabel ? 128 : attr.width,
+      height: type === bpmnLabel ? 28 : attr.height,
+      class: type === bpmnStart || type === bpmnEnd ? 'begin-or-end-node' : 'task-node',
+    });
+    // 开始节点
+    if (type === bpmnStart) {
+      foreignObject.innerHTML = `
+      <div class="node-container start-node-container" style="background:${background}" >
+        <div class='node-top-container'>
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+    // 审批节点
+    if (type === bpmnTask) {
+      foreignObject.innerHTML = `
+      <div class="node-container" style="background:${background}" >
+        <div class='node-top-container' style="background:${titleColor};">
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+        <div class='node-bottom-container'>
+          <span>${nodeContent}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+
+    if (type === bpmnSubFlow || type === bpmnProcessing || type === bpmnOutside || type === bpmnTrigger || changeTypeByTaskShape[element?.wnType || data?.type]) {
+      foreignObject.innerHTML = `
+      <div class="node-container" style="background:${background}" >
+        <div class='node-top-container' style="background:${titleColor}">
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+        <div class='node-bottom-container'>
+          <span>${nodeContent}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+    // 结束节点
+    if (type === bpmnEnd) {
+      foreignObject.innerHTML = `
+      <div class="node-container end-node-container" style="background:${background}" >
+        <div class='node-top-container'>
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+    // label渲染
+    if (type === bpmnLabel) {
+      let jnpfData = injector?.get('jnpfData');
+      let data = jnpfData.getValue('global');
+      let connectName = jnpfData.getValue(element.id.replace('_label', ''))?.nodeName || '连接线';
+      if (data.isShowConditions) {
+        let foreignObject: any = svgCreate('foreignObject', {
+          width: 128,
+          height: 28,
+          class: 'label-node',
+        });
+        foreignObject.innerHTML = `
+          <div class="node-container-label" >
+            <div class='node-top-container'>
+              <span> ${data.showNameType === 1 ? element.text : connectName}</span>
+            </div>
+          </div>`;
+        element.text && svgAppend(parentNode, foreignObject);
+        return parentNode;
+      }
+      return null;
+    }
+  }
+};

+ 68 - 0
lib/renderer/index.ts

@@ -0,0 +1,68 @@
+import BpmnRenderer from 'bpmn-js/lib/draw/BpmnRenderer';
+import CustomizeRenderer from './CustomizeRenderer';
+import { getRectPath } from 'bpmn-js/lib/draw/BpmnRenderUtil';
+import createAddMarkerSelect from '../simpleModeler/renderer/connect/marker';
+import { changeTypeByTaskShape, triggerTypeChange } from '../config';
+import { typeTrigger } from '../config/variableName';
+let jnpfCanvas: any;
+let jnpfFlowInfo: any;
+class YmRenderer extends BpmnRenderer {
+  _injector: any;
+  constructor(config: any, injector: any, eventBus: any, styles: any, pathMap: any, canvas: any, textRenderer: any, flowInfo: any, priority: number) {
+    super(
+      (config = {
+        defaultLabelColor: 'rgb(102,102,102)',
+        defaultStrokeColor: '#A2B9D5',
+        ...config,
+      }),
+      eventBus,
+      styles,
+      pathMap,
+      canvas,
+      textRenderer,
+      priority,
+    );
+    jnpfCanvas = canvas;
+    jnpfFlowInfo = flowInfo;
+    this._injector = injector;
+  }
+
+  canRender(element: any) {
+    return super.canRender(element);
+  }
+  // 绘制画布上元素
+  drawShape(parentNode: any, element: any) {
+    if (element) return CustomizeRenderer(parentNode, element, jnpfFlowInfo, this._injector) || super.drawShape(parentNode, element);
+    return super.drawShape(parentNode, element);
+  }
+
+  drawConnection(parentGfx: any, element: any) {
+    let source = element.source;
+    let target = element.target;
+    let nodeMap = jnpfFlowInfo?.nodeList;
+    let hasLineKey = jnpfFlowInfo.lineKeyList.some(item => item === element?.id);
+    let stroke = '';
+      // 过滤任务节点(source为触发节点及执行节点的元素)
+      let isTaskNode = false;
+      if (changeTypeByTaskShape[source?.wnType] || triggerTypeChange[source?.wnType] || source?.wnType === typeTrigger) isTaskNode = true;
+    if (nodeMap.has(source?.id) && nodeMap.has(target?.id)) {
+      const targetType = nodeMap.get(target.id)?.type;
+      const sourceType = nodeMap.get(source.id)?.type;
+      if (sourceType === '0' && targetType === '0') stroke = '#4ED587';
+      if (sourceType === '0' && targetType === '3' && (hasLineKey || isTaskNode)) stroke = '#4ED587';
+      if (sourceType === '0' && targetType === '1' && (hasLineKey || isTaskNode)) stroke = '#1eaceb';
+    }
+    let connect = super.drawConnection(parentGfx, element, { stroke });
+    createAddMarkerSelect(element, jnpfCanvas);
+    return connect;
+  }
+
+  // 绘制
+  getShapePath(shape: any) {
+    return getRectPath(shape);
+  }
+}
+
+YmRenderer.$inject = ['config.bpmnRenderer', 'injector', 'eventBus', 'styles', 'pathMap', 'canvas', 'textRenderer', 'config.flowInfo'];
+
+export default YmRenderer;

+ 762 - 0
lib/rule/index.ts

@@ -0,0 +1,762 @@
+// @ts-nocheck
+import { every, find, forEach, some } from 'min-dash';
+import { is, getBusinessObject } from 'bpmn-js/lib/util/ModelUtil';
+import { getParent, isAny } from 'bpmn-js/lib/features/modeling/util/ModelingUtil';
+import { isLabel } from 'bpmn-js/lib/util/LabelUtil';
+import {
+  isExpanded,
+  isEventSubProcess,
+  isInterrupting,
+  hasErrorEventDefinition,
+  hasEscalationEventDefinition,
+  hasCompensateEventDefinition,
+} from 'bpmn-js/lib/util/DiUtil';
+import RuleProvider from 'diagram-js/lib/features/rules/RuleProvider';
+import { getBoundaryAttachment as isBoundaryAttachment } from 'bpmn-js/lib/features/snapping/BpmnSnappingUtil';
+import {
+  bpmnEnd,
+  bpmnExecute,
+  bpmnGroup,
+  bpmnProcessing,
+  bpmnSubFlow,
+  bpmnTask,
+  bpmnTrigger,
+  typeEnd,
+  typeExecute,
+  typeOutside,
+  typeProcessing,
+  typeSubFlow,
+  typeTask,
+  typeTrigger,
+  typeStart
+} from '../config/variableName';
+import { changeTypeByTaskShape, changeTypeByTrigger, triggerTypeChange } from '../config';
+function inherits(e, t) {
+  t && ((e.super_ = t), (e.prototype = Object.create(t.prototype, { constructor: { value: e, enumerable: !1, writable: !0, configurable: !0 } })));
+}
+
+/**
+ * BPMN specific modeling rule
+ */
+export default function BpmnRules(eventBus, type, injector) {
+  RuleProvider.call(this, eventBus, type, injector);
+  this.type = type;
+  this._injector = injector;
+}
+
+inherits(BpmnRules, RuleProvider);
+
+BpmnRules.$inject = ['eventBus', 'config.type', 'injector'];
+
+BpmnRules.prototype.init = function () {
+  // 自定义删除规则
+  this.addRule('connection.start', context => {
+    var source = context.source;
+    return canStartConnection(source);
+  });
+  this.addRule('connection.create', context => {
+    const { source, target, hints = {} } = context;
+    const targetParent = hints.targetParent;
+    const targetAttach = hints.targetAttach;
+    let isSameGroupId = false
+    if (changeTypeByTaskShape[target.wnType] && source.wnType === typeTrigger) {
+      if (source.businessObject.$attrs?.customGroupId === target.businessObject.$attrs?.customGroupId) {
+        isSameGroupId = true
+      }
+    }
+    if (!isSameGroupId) {
+      if (!canConnectNodes(source, target, this)) return false;
+      if (!canConnectTaskNodes(source, target, this)) return false;
+      if (!canConnectTriggerElements(source, target, this)) return false;
+    }
+    // 如果目标需要附加或指定父节点
+    if (targetAttach) return false;
+    if (targetParent) target.parent = targetParent;
+    try {
+      return canConnect(source, target);
+    } finally {
+      if (targetParent) target.parent = null;
+    }
+  });
+  this.addRule('connection.reconnect', context => {
+    const { connection, source, target } = context;
+    if (!canConnectNodes(source, target, this, connection)) return false;
+    if (!canConnectTaskNodes(source, target, this)) return false;
+    if (!canConnectTriggerElements(source, target, this)) return false;
+    return canConnect(source, target, connection);
+  });
+  this.addRule('connection.updateWaypoints', context => {
+    return { type: context.connection.type };
+  });
+  this.addRule('shape.resize', context => {
+    var shape = context.shape,
+      newBounds = context.newBounds;
+    return canResize(shape, newBounds);
+  });
+  // 元素创建
+  this.addRule('elements.create', context => {
+    var elements = context.elements,
+      position = context.position,
+      target = context.target;
+    if (isConnection(target) && !canInsert(elements, target, position)) return false;
+    return every(elements, element => {
+      if (isConnection(element)) return canConnect(element.source, element.target, element);
+      if (element.host) return canAttach(element, element.host, null, position);
+      return canCreate(element, target, null, position);
+    });
+  });
+  // 移动规则
+  this.addRule('elements.move', context => {
+    var target = context.target,
+      shapes = context.shapes,
+      position = context.position;
+    return (
+      canAttach(shapes, target, null, position) ||
+      canReplace(shapes, target, position) ||
+      canMove(shapes, target, position) ||
+      canInsert(shapes, target, position)
+    );
+  });
+  // 创建规则 发起节点只允许存在一个
+  this.addRule('shape.create', context => {
+    let startEventCount = 0;
+    if (context?.target?.children?.length) {
+      context.target.children.map((children: any) => {
+        if (children.type === 'bpmn:StartEvent') return startEventCount++;
+      });
+    }
+    if (context.shape.type === 'bpmn:StartEvent' && startEventCount > 0) return false;
+    return canCreate(context.shape, context.target, context.source, context.position);
+  });
+  this.addRule('shape.attach', context => {
+    return canAttach(context.shape, context.target, null, context.position);
+  });
+  this.addRule('element.copy', context => {
+    var element = context.element,
+      elements = context.elements;
+    const hasStartElement = elements.some(o => o.type === 'bpmn:StartEvent');
+    return !hasStartElement ? canCopy(elements, element, this) : false;
+  });
+};
+
+BpmnRules.prototype.canConnectMessageFlow = canConnectMessageFlow;
+
+BpmnRules.prototype.canConnectSequenceFlow = canConnectSequenceFlow;
+
+BpmnRules.prototype.canConnectDataAssociation = canConnectDataAssociation;
+
+BpmnRules.prototype.canConnectAssociation = canConnectAssociation;
+
+BpmnRules.prototype.canMove = canMove;
+
+BpmnRules.prototype.canAttach = canAttach;
+
+BpmnRules.prototype.canReplace = canReplace;
+
+BpmnRules.prototype.canDrop = canDrop;
+
+BpmnRules.prototype.canInsert = canInsert;
+
+BpmnRules.prototype.canCreate = canCreate;
+
+BpmnRules.prototype.canConnect = canConnect;
+
+BpmnRules.prototype.canResize = canResize;
+
+BpmnRules.prototype.canCopy = canCopy;
+
+BpmnRules.prototype.canConnectNodes = canConnectNodes;
+BpmnRules.prototype.canConnectTaskNodes = canConnectTaskNodes;
+BpmnRules.prototype.canConnectTriggerElements = canConnectTriggerElements;
+/**
+ * Utility functions for rule checking
+ */
+
+/**
+ * Checks if given element can be used for starting connection.
+ *
+ * @param  {Element} source
+ * @return {boolean}
+ */
+function canStartConnection(element) {
+  if (nonExistingOrLabel(element)) return null;
+  return isAny(element, ['bpmn:FlowNode', 'bpmn:InteractionNode', 'bpmn:DataObjectReference', 'bpmn:DataStoreReference', 'bpmn:Group', 'bpmn:TextAnnotation']);
+}
+
+function nonExistingOrLabel(element) {
+  return !element || isLabel(element);
+}
+
+function isSame(a, b) {
+  return a === b;
+}
+
+function getOrganizationalParent(element) {
+  do {
+    if (is(element, 'bpmn:Process')) {
+      return getBusinessObject(element);
+    }
+    if (is(element, 'bpmn:Participant')) {
+      return getBusinessObject(element).processRef || getBusinessObject(element);
+    }
+  } while ((element = element.parent));
+}
+
+function isTextAnnotation(element) {
+  return is(element, 'bpmn:TextAnnotation');
+}
+
+function isGroup(element) {
+  return is(element, 'bpmn:Group') && !element.labelTarget;
+}
+
+function isCompensationBoundary(element) {
+  return is(element, 'bpmn:BoundaryEvent') && hasEventDefinition(element, 'bpmn:CompensateEventDefinition');
+}
+
+function isForCompensation(e) {
+  return getBusinessObject(e).isForCompensation;
+}
+
+function isSameOrganization(a, b) {
+  var parentA = getOrganizationalParent(a),
+    parentB = getOrganizationalParent(b);
+  return parentA === parentB;
+}
+
+function isMessageFlowSource(element) {
+  return (
+    is(element, 'bpmn:InteractionNode') &&
+    !is(element, 'bpmn:BoundaryEvent') &&
+    (!is(element, 'bpmn:Event') || (is(element, 'bpmn:ThrowEvent') && hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')))
+  );
+}
+
+function isMessageFlowTarget(element) {
+  return (
+    is(element, 'bpmn:InteractionNode') &&
+    !isForCompensation(element) &&
+    (!is(element, 'bpmn:Event') || (is(element, 'bpmn:CatchEvent') && hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition'))) &&
+    !(is(element, 'bpmn:BoundaryEvent') && !hasEventDefinition(element, 'bpmn:MessageEventDefinition'))
+  );
+}
+
+function getScopeParent(element) {
+  var parent = element;
+  while ((parent = parent.parent)) {
+    if (is(parent, 'bpmn:FlowElementsContainer')) {
+      return getBusinessObject(parent);
+    }
+    if (is(parent, 'bpmn:Participant')) {
+      return getBusinessObject(parent).processRef;
+    }
+  }
+  return null;
+}
+
+function isSameScope(a, b) {
+  var scopeParentA = getScopeParent(a),
+    scopeParentB = getScopeParent(b);
+  return scopeParentA === scopeParentB;
+}
+
+function hasEventDefinition(element, eventDefinition) {
+  var bo = getBusinessObject(element);
+  return !!find(bo.eventDefinitions || [], function (definition) {
+    return is(definition, eventDefinition);
+  });
+}
+
+function hasEventDefinitionOrNone(element, eventDefinition) {
+  var bo = getBusinessObject(element);
+  return (bo.eventDefinitions || []).every(function (definition) {
+    return is(definition, eventDefinition);
+  });
+}
+
+function isSequenceFlowSource(element) {
+  return (
+    is(element, 'bpmn:FlowNode') &&
+    !is(element, 'bpmn:EndEvent') &&
+    !isEventSubProcess(element) &&
+    !(is(element, 'bpmn:IntermediateThrowEvent') && hasEventDefinition(element, 'bpmn:LinkEventDefinition')) &&
+    !isCompensationBoundary(element) &&
+    !isForCompensation(element)
+  );
+}
+
+function isSequenceFlowTarget(element) {
+  return (
+    is(element, 'bpmn:FlowNode') &&
+    !is(element, 'bpmn:StartEvent') &&
+    !is(element, 'bpmn:BoundaryEvent') &&
+    !isEventSubProcess(element) &&
+    !(is(element, 'bpmn:IntermediateCatchEvent') && hasEventDefinition(element, 'bpmn:LinkEventDefinition')) &&
+    !isForCompensation(element)
+  );
+}
+
+function isEventBasedTarget(element) {
+  return (
+    is(element, 'bpmn:ReceiveTask') ||
+    (is(element, 'bpmn:IntermediateCatchEvent') &&
+      (hasEventDefinition(element, 'bpmn:MessageEventDefinition') ||
+        hasEventDefinition(element, 'bpmn:TimerEventDefinition') ||
+        hasEventDefinition(element, 'bpmn:ConditionalEventDefinition') ||
+        hasEventDefinition(element, 'bpmn:SignalEventDefinition')))
+  );
+}
+
+function isConnection(element) {
+  return element.waypoints;
+}
+
+function getParents(element) {
+  var parents = [];
+  while (element) {
+    element = element.parent;
+    if (element) parents.push(element);
+  }
+  return parents;
+}
+
+function isParent(possibleParent, element) {
+  var allParents = getParents(element);
+  return allParents.indexOf(possibleParent) !== -1;
+}
+
+function canConnect(source, target, connection) {
+  if (nonExistingOrLabel(source) || nonExistingOrLabel(target)) return null;
+  if (!is(connection, 'bpmn:DataAssociation')) {
+    if (canConnectMessageFlow(source, target)) return { type: 'bpmn:MessageFlow' };
+    if (canConnectSequenceFlow(source, target)) return { type: 'bpmn:SequenceFlow' };
+  }
+  var connectDataAssociation = canConnectDataAssociation(source, target);
+  if (connectDataAssociation) return connectDataAssociation;
+  if (isCompensationBoundary(source) && isForCompensation(target)) {
+    return {
+      type: 'bpmn:Association',
+      associationDirection: 'One',
+    };
+  }
+  if (canConnectAssociation(source, target)) {
+    return {
+      type: 'bpmn:Association',
+    };
+  }
+
+  return false;
+}
+
+/**
+ * Can an element be dropped into the target element
+ *
+ * @return {boolean}
+ */
+function canDrop(element, target, position) {
+  // can move labels and groups everywhere
+  if (isLabel(element) || isGroup(element)) return true;
+  // disallow to create elements on collapsed pools
+  if (is(target, 'bpmn:Participant') && !isExpanded(target)) return false;
+  // allow to create new participants on
+  // existing collaboration and process diagrams
+  if (is(element, 'bpmn:Participant')) return is(target, 'bpmn:Process') || is(target, 'bpmn:Collaboration');
+  // allow moving DataInput / DataOutput within its original container only
+  if (isAny(element, ['bpmn:DataInput', 'bpmn:DataOutput'])) {
+    if (element.parent) return target === element.parent;
+  }
+  // allow creating lanes on participants and other lanes only
+  if (is(element, 'bpmn:Lane')) return is(target, 'bpmn:Participant') || is(target, 'bpmn:Lane');
+  // disallow dropping boundary events which cannot replace with intermediate event
+  if (is(element, 'bpmn:BoundaryEvent') && !isDroppableBoundaryEvent(element)) return false;
+  // drop flow elements onto flow element containers
+  // and participants
+  if (is(element, 'bpmn:FlowElement') && !is(element, 'bpmn:DataStoreReference')) {
+    if (is(target, 'bpmn:FlowElementsContainer')) return isExpanded(target);
+    return isAny(target, ['bpmn:Participant', 'bpmn:Lane']);
+  }
+  // disallow dropping data store reference if there is no process to append to
+  if (is(element, 'bpmn:DataStoreReference') && is(target, 'bpmn:Collaboration')) {
+    return some(getBusinessObject(target).get('participants'), function (participant) {
+      return !!participant.get('processRef');
+    });
+  }
+  // account for the fact that data associations are always
+  // rendered and moved to top (Process or Collaboration level)
+  //
+  // artifacts may be placed wherever, too
+  if (isAny(element, ['bpmn:Artifact', 'bpmn:DataAssociation', 'bpmn:DataStoreReference'])) {
+    return isAny(target, ['bpmn:Collaboration', 'bpmn:Lane', 'bpmn:Participant', 'bpmn:Process', 'bpmn:SubProcess']);
+  }
+  if (is(element, 'bpmn:MessageFlow')) {
+    return is(target, 'bpmn:Collaboration') || element.source.parent == target || element.target.parent == target;
+  }
+  return false;
+}
+
+function isDroppableBoundaryEvent(event) {
+  return getBusinessObject(event).cancelActivity && (hasNoEventDefinition(event) || hasCommonBoundaryIntermediateEventDefinition(event));
+}
+
+function isBoundaryEvent(element) {
+  return !isLabel(element) && is(element, 'bpmn:BoundaryEvent');
+}
+
+function isLane(element) {
+  return is(element, 'bpmn:Lane');
+}
+
+/**
+ * We treat IntermediateThrowEvents as boundary events during create,
+ * this must be reflected in the rules.
+ */
+function isBoundaryCandidate(element) {
+  if (isBoundaryEvent(element)) return true;
+  if (is(element, 'bpmn:IntermediateThrowEvent') && hasNoEventDefinition(element)) return true;
+  return is(element, 'bpmn:IntermediateCatchEvent') && hasCommonBoundaryIntermediateEventDefinition(element);
+}
+
+function hasNoEventDefinition(element) {
+  var bo = getBusinessObject(element);
+  return bo && !(bo.eventDefinitions && bo.eventDefinitions.length);
+}
+
+function hasCommonBoundaryIntermediateEventDefinition(element) {
+  return hasOneOfEventDefinitions(element, [
+    'bpmn:MessageEventDefinition',
+    'bpmn:TimerEventDefinition',
+    'bpmn:SignalEventDefinition',
+    'bpmn:ConditionalEventDefinition',
+  ]);
+}
+
+function hasOneOfEventDefinitions(element, eventDefinitions) {
+  return eventDefinitions.some(function (definition) {
+    return hasEventDefinition(element, definition);
+  });
+}
+
+function isReceiveTaskAfterEventBasedGateway(element) {
+  return (
+    is(element, 'bpmn:ReceiveTask') &&
+    find(element.incoming, function (incoming) {
+      return is(incoming.source, 'bpmn:EventBasedGateway');
+    })
+  );
+}
+
+function canAttach(elements, target, source, position) {
+  if (!Array.isArray(elements)) elements = [elements];
+  // only (re-)attach one element at a time
+  if (elements.length !== 1) return false;
+  var element = elements[0];
+  // do not attach labels
+  if (isLabel(element)) return false;
+  // only handle boundary events
+  if (!isBoundaryCandidate(element)) return false;
+  // disallow drop on event sub processes
+  if (isEventSubProcess(target)) return false;
+  // only allow drop on non compensation activities
+  if (!is(target, 'bpmn:Activity') || isForCompensation(target)) return false;
+  // only attach to subprocess border
+  if (position && !isBoundaryAttachment(position, target)) return false;
+  // do not attach on receive tasks after event based gateways
+  if (isReceiveTaskAfterEventBasedGateway(target)) return false;
+  return 'attach';
+}
+
+/**
+ * Defines how to replace elements for a given target.
+ *
+ * Returns an array containing all elements which will be replaced.
+ *
+ * @example
+ *
+ *  [{ id: 'IntermediateEvent_2',
+ *     type: 'bpmn:StartEvent'
+ *   },
+ *   { id: 'IntermediateEvent_5',
+ *     type: 'bpmn:EndEvent'
+ *   }]
+ *
+ * @param  {Array} elements
+ * @param  {Object} target
+ *
+ * @return {Object} an object containing all elements which have to be replaced
+ */
+function canReplace(elements, target, position) {
+  if (!target) return false;
+  var canExecute: any = {
+    replacements: [],
+  };
+  forEach(elements, function (element) {
+    if (!isEventSubProcess(target)) {
+      if (is(element, 'bpmn:StartEvent') && element.type !== 'label' && canDrop(element, target)) {
+        // replace a non-interrupting start event by a blank interrupting start event
+        // when the target is not an event sub process
+        if (!isInterrupting(element)) {
+          canExecute.replacements.push({
+            oldElementId: element.id,
+            newElementType: 'bpmn:StartEvent',
+          });
+        }
+        // replace an error/escalation/compensate start event by a blank interrupting start event
+        // when the target is not an event sub process
+        if (hasErrorEventDefinition(element) || hasEscalationEventDefinition(element) || hasCompensateEventDefinition(element)) {
+          canExecute.replacements.push({
+            oldElementId: element.id,
+            newElementType: 'bpmn:StartEvent',
+          });
+        }
+        // replace a typed start event by a blank interrupting start event
+        // when the target is a sub process but not an event sub process
+        if (
+          hasOneOfEventDefinitions(element, [
+            'bpmn:MessageEventDefinition',
+            'bpmn:TimerEventDefinition',
+            'bpmn:SignalEventDefinition',
+            'bpmn:ConditionalEventDefinition',
+          ]) &&
+          is(target, 'bpmn:SubProcess')
+        ) {
+          canExecute.replacements.push({
+            oldElementId: element.id,
+            newElementType: 'bpmn:StartEvent',
+          });
+        }
+      }
+    }
+    if (!is(target, 'bpmn:Transaction')) {
+      if (hasEventDefinition(element, 'bpmn:CancelEventDefinition') && element.type !== 'label') {
+        if (is(element, 'bpmn:EndEvent') && canDrop(element, target)) {
+          canExecute.replacements.push({
+            oldElementId: element.id,
+            newElementType: 'bpmn:EndEvent',
+          });
+        }
+        if (is(element, 'bpmn:BoundaryEvent') && canAttach(element, target, null, position)) {
+          canExecute.replacements.push({
+            oldElementId: element.id,
+            newElementType: 'bpmn:BoundaryEvent',
+          });
+        }
+      }
+    }
+  });
+  return canExecute.replacements.length ? canExecute : false;
+}
+
+function canMove(elements, target) {
+  // do not move selection containing lanes
+  if (some(elements, isLane) || some(elements, isGroup) || some(elements, isLabel)) return false;
+  // allow default move check to start move operation
+  if (!target) return true;
+  return elements.every(function (element) {
+    return canDrop(element, target);
+  });
+}
+
+function canCreate(shape, target, source, position) {
+  if (!target) return false;
+  if (isLabel(shape) || isGroup(shape)) return true;
+  if (isSame(source, target)) return false;
+  // ensure we do not drop the element
+  // into source
+  if (source && isParent(source, target)) return false;
+  return canDrop(shape, target, position) || canInsert(shape, target, position);
+}
+
+function canResize(shape, newBounds) {
+  if (is(shape, 'bpmn:SubProcess')) {
+    return isExpanded(shape) && (!newBounds || (newBounds.width >= 100 && newBounds.height >= 80));
+  }
+  if (is(shape, 'bpmn:Lane')) {
+    return !newBounds || (newBounds.width >= 130 && newBounds.height >= 60);
+  }
+  if (is(shape, 'bpmn:Participant')) {
+    return !newBounds || (newBounds.width >= 250 && newBounds.height >= 50);
+  }
+  if (isTextAnnotation(shape)) return true;
+  if (isGroup(shape)) return true;
+  return false;
+}
+
+/**
+ * Check, whether one side of the relationship
+ * is a text annotation.
+ */
+function isOneTextAnnotation(source, target) {
+  var sourceTextAnnotation = isTextAnnotation(source),
+    targetTextAnnotation = isTextAnnotation(target);
+  return (sourceTextAnnotation || targetTextAnnotation) && sourceTextAnnotation !== targetTextAnnotation;
+}
+
+function canConnectAssociation(source, target) {
+  // compensation boundary events are exception
+  if (isCompensationBoundary(source) && isForCompensation(target)) return true;
+  // don't connect parent <-> child
+  if (isParent(target, source) || isParent(source, target)) return false;
+  // allow connection of associations between <!TextAnnotation> and <TextAnnotation>
+  if (isOneTextAnnotation(source, target)) return true;
+  // can connect associations where we can connect
+  // data associations, too (!)
+  return !!canConnectDataAssociation(source, target);
+}
+
+function canConnectMessageFlow(source, target) {
+  // during connect user might move mouse out of canvas
+  // https://github.com/bpmn-io/bpmn-js/issues/1033
+  if (getRootElement(source) && !getRootElement(target)) return false;
+  return isMessageFlowSource(source) && isMessageFlowTarget(target) && !isSameOrganization(source, target);
+}
+
+function canConnectSequenceFlow(source, target) {
+  if (
+    isEventBasedTarget(target) &&
+    target.incoming.length > 0 &&
+    areOutgoingEventBasedGatewayConnections(target.incoming) &&
+    !is(source, 'bpmn:EventBasedGateway')
+  ) {
+    return false;
+  }
+  return (
+    isSequenceFlowSource(source) &&
+    isSequenceFlowTarget(target) &&
+    isSameScope(source, target) &&
+    !(is(source, 'bpmn:EventBasedGateway') && !isEventBasedTarget(target))
+  );
+}
+
+function canConnectDataAssociation(source, target) {
+  if (isAny(source, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference']) && isAny(target, ['bpmn:Activity', 'bpmn:ThrowEvent'])) {
+    return { type: 'bpmn:DataInputAssociation' };
+  }
+  if (isAny(target, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference']) && isAny(source, ['bpmn:Activity', 'bpmn:CatchEvent'])) {
+    return { type: 'bpmn:DataOutputAssociation' };
+  }
+  return false;
+}
+
+function canInsert(shape, flow, position) {
+  if (!flow) return false;
+  if (Array.isArray(shape)) {
+    if (shape.length !== 1) return false;
+    shape = shape[0];
+  }
+  if (flow.source === shape || flow.target === shape) return false;
+  // return true if we can drop on the
+  // underlying flow parent
+  // at this point we are not really able to talk
+  // about connection rules (yet)
+  return (
+    isAny(flow, ['bpmn:SequenceFlow', 'bpmn:MessageFlow']) &&
+    !isLabel(flow) &&
+    is(shape, 'bpmn:FlowNode') &&
+    !is(shape, 'bpmn:BoundaryEvent') &&
+    canDrop(shape, flow.parent, position)
+  );
+}
+
+function includes(elements, element) {
+  return elements && element && elements.indexOf(element) !== -1;
+}
+
+function canCopy(elements, element, context) {
+  if (context.type === 1) return false;
+  if (isLabel(element)) return true;
+  if (is(element, 'bpmn:Lane') && !includes(elements, element.parent)) return false;
+  if (element.wnType === typeTrigger || changeTypeByTaskShape[element.wnType]) return false;
+  return true;
+}
+
+function isOutgoingEventBasedGatewayConnection(connection) {
+  if (connection && connection.source) {
+    return is(connection.source, 'bpmn:EventBasedGateway');
+  }
+}
+
+function areOutgoingEventBasedGatewayConnections(connections) {
+  connections = connections || [];
+  return connections.some(isOutgoingEventBasedGatewayConnection);
+}
+
+function getRootElement(element) {
+  return getParent(element, 'bpmn:Process') || getParent(element, 'bpmn:Collaboration');
+}
+
+function getElementsUpToElement(element) {
+  const result = new Set();
+  const findIncoming = el => {
+    const incoming = el.incoming || [];
+    incoming.map(connection => {
+      result.add(connection.source?.id);
+      const sourceElement = connection.source; // 直接访问源元素
+      // 检查源元素的wntype是否为trigger
+      if (sourceElement.wnType === typeTrigger) return true; // 找到满足条件的元素
+      if (sourceElement) {
+        const found = findIncoming(sourceElement);
+        if (found) return true;
+      }
+    });
+    return false;
+  };
+  const foundTrigger = findIncoming(element);
+  return result; // 如果找到满足条件的元素,则返回结果,否则返回空数组
+}
+
+function canConnectNodes(source, target, context, connection?) {
+  if (source.id === target.id) return false; // 自连接检查
+  if (source.type === 'bpmn:StartEvent' && ([bpmnEnd].includes(target.type) || target.wnType === typeTrigger || changeTypeByTaskShape[target.wnType])) return false; // 开始节点不能连接结束节点
+  if ((changeTypeByTrigger[source.wnType] || [typeOutside].includes(source.wnType)) && target.type === 'bpmn:EndEvent') return false; // 
+  if ((source.wnType === typeSubFlow && target.wnType === typeTrigger) || (source.wnType === typeTrigger && target.wnType === typeSubFlow)) return false; // 子流程不能连接触发节点
+  if (!connection && source.outgoing && source.outgoing.some(o => o.target.id === target.id)) return false; // 检查重复连接
+  if (connection && !(connection.target.id === target.id)) return false;
+  if (source.wnType === typeTrigger && (target.wnType === typeTask || target.wnType === typeEnd)) return false; // 触发节点连接审批节点及结束节点
+  if (source.wnType === typeOutside && ([typeSubFlow, typeOutside].includes(target.wnType))) return false; // 外部节点不能连接子流程外部节点
+  if (source.wnType === typeOutside) {
+    // 获取该线条的data 判断是否包含条件 有条件的话禁用重新连接
+    if(context._injector?.get('jnpfData')?.getValue(connection?.id)?.conditions?.length > 0) return false
+  }
+  return true;
+}
+
+// 检查任务节点之间的连接规则
+function canConnectTaskNodes(source, target, context) {
+  const sourceType = changeTypeByTaskShape[source.wnType];
+  const targetType = changeTypeByTaskShape[target.wnType];
+  const isTaskToOtherType = (sourceType && !targetType) || (!sourceType && source.wnType !== bpmnTrigger && targetType);
+  if (isTaskToOtherType) {
+    if (!(context.type === 2 && target.wnType === typeEnd) && !(context.type === 2 && !changeTypeByTrigger[target.wnType])) {
+      return false;
+    }
+  }
+  // 检查任务节点是否在相同分组中
+  if (sourceType && targetType) {
+    if (source.businessObject.$attrs?.customGroupId !== target.businessObject.$attrs?.customGroupId) return false;
+    const groupSet = getElementsUpToElement(source);
+    if (groupSet.has(target.id)) return false;
+  }
+  return true;
+}
+
+// 检查触发元素的连接规则
+function canConnectTriggerElements(source, target, context) {
+  const isSourceTrigger = source.wnType === typeTrigger;
+  const isTargetTrigger = target.wnType === typeTrigger;
+  if (isSourceTrigger && isTargetTrigger) return false;
+  // 不同分组检查
+  if (isSourceTrigger || isTargetTrigger) {
+    const elementRegistry = context._injector.get('elementRegistry');
+    const element = isTargetTrigger ? elementRegistry.get(target.id) : elementRegistry.get(source.id);
+    if (element.incoming.length > 0) {
+      let wnType = element.incoming[0].source.wnType;
+      if (source.wnType != wnType) return false;
+    }
+    if (source.businessObject.$attrs?.customGroupId !== target.businessObject.$attrs?.customGroupId) {
+      if (isSourceTrigger && !isTargetTrigger && !(element.wnType === typeTask || element.wnType === typeProcessing)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}

+ 183 - 0
lib/simpleModeler/contextPad/index.ts

@@ -0,0 +1,183 @@
+// @ts-nocheck 过滤ts校验
+import { attr as domAttr, query as domQuery, classes as domClasses, domify as domify, delegate as domDelegate, event as domEvent } from 'min-dom';
+import { assign, forEach, isArray } from 'min-dash';
+import { escapeCSS } from 'diagram-js/lib/util/EscapeUtil';
+import contextPad from 'diagram-js/lib/features/context-pad/ContextPad';
+import { bpmnGateway, bpmnTask, bpmnLabel, typeCondition, bpmnGroup, typeConnect } from '../../config/variableName';
+import { hasGatewayType } from '../../config';
+
+var entrySelector = '.entry';
+
+function addClasses(element: any, classNames: any) {
+  var classes = domClasses(element);
+  classNames = isArray(classNames) ? classNames : classNames.split(/\s+/g);
+  classNames.forEach(function (cls: any) {
+    classes.add(cls);
+  });
+}
+
+class YmContextPad extends contextPad {
+  constructor(canvas: any, config: any, eventBus: any, overlays: any) {
+    super(canvas, config, eventBus, overlays);
+  }
+  // 打开节点右侧面板
+  open(target: any, force: any) {
+    this.close();
+    if (target.wnType === typeCondition) return;
+    if (!(target.type === bpmnLabel || hasGatewayType.has(target.wnType) || target.type === bpmnTask)) return;
+    if (!force && this.isOpen(target)) return;
+    this._updateAndOpen(target);
+  }
+  isShown() {
+    return this.isOpen();
+  }
+  arrowOpen(target: any) {
+    var entries = this.getEntries(target),
+      arrow = this.getArrow(target),
+      html = arrow.html;
+
+    let container = domify('<div class="rightArrow" id="rightArrow"></div>');
+    html.appendChild(container);
+    domClasses(html).add('open');
+
+    this._current = {
+      target: target,
+      entries: entries,
+      pad: arrow,
+    };
+    this._eventBus.fire('contextPad.open', { current: this._current });
+  }
+  getArrow(target: any) {
+    if (this.isOpen()) {
+      return this._current.pad;
+    }
+    var self = this;
+    var overlays = this._overlays;
+    var html = domify('<div class="ymArrow"></div>');
+    var position = this._getPosition(target);
+    domDelegate.bind(html, entrySelector, 'click', function (event) {
+      self.trigger('click', event);
+    });
+    let newPosition = {
+      position: {
+        top: position.position.top + target.height / 2,
+        left: position.position.left,
+      },
+    };
+    var overlaysConfig = assign({ html: html }, this._overlaysConfig, newPosition);
+    var overlaysConfig1 = assign({ html: html }, this._overlaysConfig, position);
+    domDelegate.bind(html, '.rightArrow', 'click', function (event) {
+      self._updateAndOpen(target);
+      self.trigger('click', event);
+    });
+    domDelegate.bind(html, entrySelector, 'dragstart', function (event) {
+      self.trigger('dragstart', event);
+    });
+    domEvent.bind(html, 'mousedown', function (event: any) {
+      event.stopPropagation();
+    });
+    var activeRootElement = this._canvas.getRootElement();
+    this._overlayId = overlays.add(activeRootElement, 'context-pad', overlaysConfig);
+    var arrow = overlays.get(this._overlayId);
+    this._eventBus.fire('contextPad.create', { target: target, pad: arrow });
+    return arrow;
+  }
+  getPad(target: any) {
+    if (this.isOpen()) return this._current.pad;
+    var self = this;
+    var overlays = this._overlays;
+    let width = 32 + (target.type === bpmnLabel ? 92 * 7 : 92);
+    if (target.type === bpmnTask) width = 32;
+    let contextPad = 'djs-context-pad-jnpf';
+    if (target.type === bpmnTask) contextPad = 'djs-context-pad';
+    var html = domify(`<div class=${contextPad} style="width:${width}px;border-radius: 8px;"></div>`);
+    var position = this._getPosition(target);
+    var overlaysConfig = assign(
+      {
+        html: html,
+      },
+      this._overlaysConfig,
+      position,
+    );
+    domDelegate.bind(html, entrySelector, 'click', function (event) {
+      self.trigger('click', event);
+    });
+    domDelegate.bind(html, entrySelector, 'dragstart', function (event) {
+      self.trigger('dragstart', event);
+    });
+    // stop propagation of mouse events
+    domEvent.bind(html, 'mousedown', function (event: any) {
+      event.stopPropagation();
+    });
+    var activeRootElement = this._canvas.getRootElement();
+    this._overlayId = overlays.add(activeRootElement, 'context-pad', overlaysConfig);
+    var pad = overlays.get(this._overlayId);
+    this._eventBus.fire('contextPad.create', {
+      target: target,
+      pad: pad,
+    });
+    return pad;
+  }
+  // 重写父类contextPad样式
+  _updateAndOpen(target: any) {
+    var entries = this.getEntries(target),
+      pad = this.getPad(target),
+      html = pad.html,
+      image;
+    forEach(entries, (entry: any, id: any) => {
+      let textClass = ['svgText'];
+      let entryClass = ['entry'];
+      let groupClass = ['group'];
+      if (entry.group === typeConnect) return; //简单流程过滤连线
+      if (target.type === bpmnTask) {
+        entryClass.push('taskEntry');
+        groupClass.push('taskGroup');
+      }
+      if (!entry?.disable && String(entry?.disable) === 'false') {
+        entryClass = ['entry-disabled'];
+      }
+      let classAttribute = entryClass.join(' ');
+      let groupClassAttribute = groupClass.join(' ');
+      let textAttribute = textClass.join(' ');
+      let grouping = 'default',
+        control = domify(entry.html || `<div class="${classAttribute}" draggable="true"></div>`),
+        container;
+      let svg = domify(entry.html || target.type === bpmnTask ? '' : `<span class="${textAttribute}" style="font-size: ${'12px'}" >${entry.title}</span>`);
+      control.appendChild(svg);
+      domAttr(control, 'data-action', id);
+      container = domQuery('[data-group=' + escapeCSS(grouping) + ']', html);
+      if (!container) {
+        container = domify(`<div class="${groupClassAttribute}"></div>`);
+        domAttr(container, 'data-group', grouping);
+        if (Object.keys(entries).length > 1 && target?.type === bpmnLabel) {
+          let content = '包容分支:多个分支条件满足时,同时流转,排他分支:多个分支条件满足时,也只流转其中一条。';
+          let contentDiv = domify(`<div class="djs-context-pad-jnpf-content"></div>`);
+          let text = domify(`<span class="djs-context-pad-jnpf-text">${content}</span>`);
+          contentDiv.appendChild(text);
+          html.appendChild(contentDiv);
+          addClasses(contentDiv, 'icon-ym icon-ym-generator-alert');
+        }
+        html.appendChild(container);
+      }
+      container.appendChild(control);
+      if (entry.className) addClasses(control, entry.className);
+      if (entry.title) domAttr(control, 'title', entry.title);
+      if (entry.imageUrl) {
+        image = domify('<img>');
+        domAttr(image, 'src', entry.imageUrl);
+        image.style.width = '100%';
+        image.style.height = '100%';
+        control.appendChild(image);
+      }
+    });
+    domClasses(html).add('open');
+    this._current = {
+      target: target,
+      entries: entries,
+      pad: pad,
+    };
+    this._eventBus.fire('contextPad.open', { current: this._current });
+  }
+}
+YmContextPad.$inject = ['canvas', 'config.contextPad', 'eventBus', 'overlays'];
+export default YmContextPad;

+ 619 - 0
lib/simpleModeler/contextPad/provider/CustomizeContextPad.ts

@@ -0,0 +1,619 @@
+import { changeTypeByTaskShape, changeTypeByTrigger, hasGatewayType, typeConfig } from '../../../config';
+import { jnpfConfigBpmnContextPad } from '../../../config/contextPad';
+import {
+  bpmnEnd,
+  bpmnTask,
+  typeTask,
+  typeSubFlow,
+  typeLabel,
+  bpmnInclusive,
+  typeConfluence,
+  bpmnStart,
+  typeTrigger,
+  bpmnTrigger,
+  typeExecute,
+  bpmnLabel,
+  typeStart,
+  typeCopy,
+  bpmnCopy,
+  bpmnPaste,
+  typePaste,
+  bpmnChoose,
+  typeProcessing,
+  typeOutside,
+  typeEnd,
+  typeChoose,
+  typeCondition,
+} from '../../../config/variableName';
+import { BPMNTreeBuilder } from '../../../utils/constructTreeUtil';
+import { DEFAULT_DISTANCE } from '../../../config/constants';
+import { buildBitUUID } from '../../../utils/uuidUtil';
+import { NodeUtils } from '../../../utils/nodeUtil';
+import bpmnType from '../../../palette/bpmnType';
+
+const CustomizeContextPad = (contextPadProvider: any, element: any) => {
+  let type = element.type;
+  let isAction = true;
+  if (typeConfig[type]) {
+    const {
+      _autoPlace: autoPlace,
+      _create: create,
+      _elementFactory: elementFactory,
+      _modeling: modeling,
+      _connect: connects,
+      _injector: injector,
+      _rules: rules,
+      _eventBus: eventBus,
+    } = contextPadProvider;
+    let wnType = element.wnType;
+    if (wnType === bpmnTrigger) type = bpmnTrigger;
+    if (changeTypeByTaskShape[wnType]) type = changeTypeByTaskShape[wnType];
+    const { contextPad, shapeType } = typeConfig[type];
+    const { del, copy, paste } = jnpfConfigBpmnContextPad;
+    let customization = contextPad?.customization;
+
+    if (type === bpmnLabel) {
+      let connect = element?.labelTarget;
+      let target = connect.target;
+      // 执行节点的线label使用groupCustomization
+      if (changeTypeByTaskShape[target.wnType]) {
+        customization = contextPad?.groupCustomization;
+      }
+    }
+    if (type === shapeType) {
+      if (contextPad) {
+        let jnpfData = injector.get('jnpfData');
+        let copyElement = jnpfData.getValue('copyType');
+        if (element.wnType === typeTrigger && changeTypeByTaskShape[copyElement.type]) customization = contextPad?.otherCustomization;
+        if (changeTypeByTaskShape[element.wnType] && changeTypeByTaskShape[copyElement.type]) customization = contextPad?.otherCustomization;
+        if (contextPad.default) return defaultContextPad;
+        else if (customization) {
+          let result: any = {};
+          for (let key of Object.keys(customization)) {
+            let data = customization[key];
+            if (data.group === 'model') {
+              let options: any = {
+                wnName: typeConfig[key]?.renderer.rendererName,
+              };
+              if (element.type === bpmnStart && hasGatewayType.has(key)) {
+                // 开始节点只有分类节点 因为网关的分流节点和合流节点类型一致 多增加一个字段来表示
+                options = {
+                  wnName: typeConfig[key]?.renderer.rendererName,
+                  wnGatewayType: key,
+                  wnType: key,
+                  icon: data.icon,
+                };
+              }
+              result[data.name] = appendAction(data.type, data.elementName, data.className, data.title, data.wnType, options);
+            } else if (data.group === 'connect') {
+              result[data.name] = {
+                group: data.group,
+                className: data.className,
+                title: data.title,
+                action: {
+                  click: isAction && startConnect,
+                },
+              };
+            } else if (data.group === 'edit') {
+              result[data.name] = {
+                group: data.group,
+                className: data.className,
+                title: data.title,
+                action: {
+                  click: removeElement,
+                },
+              };
+            }
+          }
+          return Object.assign(result);
+        } else return defaultContextPad();
+      }
+      // 单个节点删除功能
+      function removeElement() {
+        rules.allowed('elements.delete', { elements: [element] });
+      }
+      // 开始连线(拖拽)
+      function startConnect(event: any, element: any) {
+        connects.start(event, element);
+      }
+      // 添加事件
+      function appendAction(type: any, name: any, className: any, title: any, wnType: any, options?: any) {
+        const appendStart = (event: any, element: any) => {
+          let bpmnFactory = elementFactory._bpmnFactory;
+          if ([typeSubFlow, typeProcessing, typeOutside].includes(type)) type = bpmnTask;
+          let businessObject = bpmnFactory.create(type);
+          let shape = elementFactory.createShape(Object.assign({ type, name, wnType, ...options }, businessObject));
+          create.start(event, shape, { source: element });
+        };
+        const autoPlaceAppend = async (_event: any, element: any) => {
+          let isPaste = false;
+          if (wnType === typeCopy) {
+            copyElement(element.wnType);
+            return;
+          }
+          if (wnType === typePaste) {
+            let jnpfData = injector.get('jnpfData');
+            let data = jnpfData.getValue(bpmnCopy);
+            let copyElement = jnpfData.getValue('copyType');
+            isPaste = true;
+            if ([typeOutside, typeTrigger, 'default'].includes(copyElement.type)) {
+              type = data.type;
+              wnType = data.wnType;
+              className = data.className;
+              title = data.title;
+              options = data.options;
+              if (element.outgoing.length > 1) {
+                let connect = element.outgoing.find(connect => connect.target.wnType != typeTrigger);
+                element = connect.label;
+              } else {
+                element = element.outgoing[0]?.label;
+              }
+            }
+            if (copyElement.type === typeTrigger || changeTypeByTaskShape[copyElement.type]) {
+              type = data.type;
+              wnType = data.wnType;
+              className = data.className;
+              title = data.title;
+              options = data.options;
+              name = data.name;
+            }
+          }
+          let hasTaskType = new Set([typeSubFlow, typeProcessing, typeTrigger, typeOutside]);
+          if (hasTaskType.has(type) || changeTypeByTaskShape[wnType]) type = bpmnTask;
+          let bpmnFactory = elementFactory._bpmnFactory;
+          let isChoose = false;
+          if (type === bpmnChoose) {
+            type = bpmnInclusive;
+            isChoose = true;
+          }
+          let businessObject = type === bpmnType ? bpmnFactory.create(element.type) : bpmnFactory.create(type);
+          let shape = elementFactory.createShape(Object.assign({ type, name: name, businessObject, wnType: wnType }, options));
+          if (element.type === typeLabel) {
+            let businessTaskObject = bpmnFactory.create(bpmnTask);
+            let businessTask2Object = bpmnFactory.create(bpmnTask);
+            let businessInclusiveObject = bpmnFactory.create(bpmnInclusive);
+            let shape: any;
+            if (hasGatewayType.has(wnType)) {
+              let id = `Gateway_${buildBitUUID()}_isSimple`;
+              businessObject.id = id;
+              shape = elementFactory.createShape(Object.assign({ type, id: id, name: name, businessObject, wnType: wnType }, options));
+            } else {
+              shape = elementFactory.createShape(Object.assign({ type, name: name, businessObject, wnType: wnType }, options));
+            }
+            updateShapePosition(shape, element, businessTaskObject, businessInclusiveObject, businessTask2Object);
+            if (isChoose) {
+              modeling.updateProperties(shape, {
+                type: bpmnChoose,
+              });
+            }
+          } else {
+            autoPlace.append(element, shape);
+            // 自研
+            if (hasGatewayType.has(element.wnType)) {
+              // 获取对应的合流网关
+              const gateway = injector.get('elementRegistry').get(element.id + '_confluence');
+              modeling.connect(shape, gateway);
+            }
+            // 处理执行节点的美化 (获取到最近的合流节点 判断分组与合流坐标距离计算是否需要进行偏移)
+            if (changeTypeByTaskShape[shape.wnType]) {
+              modeling.updateProperties(shape, {
+                customGroupId: element.businessObject.$attrs.customGroupId,
+              });
+              let groupShape = injector.get('elementRegistry').get(element.businessObject.$attrs.customGroupId);
+              let shapeRight = shape.x + shape.width;
+              let shapeBottom = shape.y + shape.height;
+              let groupRight = groupShape.x + groupShape.width;
+              let groupBottom = groupShape.y + groupShape.height;
+              var newBounds: any = {
+                x: groupShape.x, // 保持 x 位置不变
+                y: groupShape.y, // 保持 y 位置不变
+                width: groupShape.width,
+                height: groupShape.height,
+              };
+              if (shapeRight >= groupRight) newBounds.width = shapeRight + 25 - groupShape.x;
+              if (shapeBottom >= groupBottom) newBounds.height = shapeBottom + 15 - groupShape.y;
+              modeling.resizeShape(groupShape, newBounds);
+              autoExecute(groupShape);
+            }
+          }
+          // 计算需要横向偏移的元素(只有出现分流的情况下需要用到)
+          if (element?.type === typeLabel || hasGatewayType.has(element.wnType) || element.wnType === typeTrigger || changeTypeByTaskShape[element.wnType])
+            onAutoPosition();
+          // 粘贴节点属性(需modeling.create后才能使用modeling.updatePropertie()生效)
+          if (isPaste) {
+            let jnpfData = injector.get('jnpfData');
+             let copyElement = jnpfData.getValue('copyType');
+            let newData = copyElement.data;
+            let newShape = injector.get('elementRegistry').get(shape.id);
+            let customGroupId = "";
+
+            if (changeTypeByTrigger[newShape.wnType] || newShape.wnType === typeTrigger) customGroupId = newShape?.businessObject?.$attrs?.customGroupId
+            else if (changeTypeByTaskShape[newShape.wnType]) customGroupId = element?.businessObject?.$attrs?.customGroupId
+            jnpfData.setValue(shape.id, { ...newData, nodeId: shape.id, groupId: customGroupId });
+
+            if (newShape) {
+              newShape['elementBodyName'] = newData?.content || '';
+              newShape['nodeName'] = newData?.nodeName || '';
+              modeling.updateProperties(newShape, {
+                customGroupId: customGroupId,
+              });
+            }
+          }
+        };
+        var append = autoPlace ? autoPlaceAppend : appendStart;
+        let disable = isAction;
+        let lastsource = element?.labelTarget?.source;
+        let nextTarget = element?.labelTarget?.target;
+        let targetType = nextTarget?.wnType
+        let sourceType = lastsource?.wnType
+        if ([typeConfluence].includes(sourceType) && (hasGatewayType.has(wnType) || wnType === typeTrigger)) disable = false;
+        if (sourceType === typeSubFlow && ([typeChoose].includes(wnType))) disable = false;
+        if (sourceType === typeOutside && ([typeChoose, typeTrigger, typeOutside, typeSubFlow].includes(wnType))) disable = false;
+        if (hasGatewayType.has(nextTarget?.wnType) && hasGatewayType.has(wnType)) disable = false;
+        if (nextTarget?.wnType === typeTrigger && ([typeSubFlow, typeSubFlow, typeExecute, typeTask].includes(wnType) || hasGatewayType.has(wnType)))
+          if (changeTypeByTaskShape[nextTarget?.wnType] && hasGatewayType.has(wnType)) disable = false;
+        if ([typeStart, typeSubFlow].includes(sourceType) && wnType === typeTrigger) disable = false;
+        if (([typeOutside].includes(wnType) && [typeOutside, typeSubFlow, typeChoose, typeEnd].includes(targetType))) disable = false;
+        if (([typeSubFlow].includes(wnType) && [typeChoose].includes(targetType))) disable = false;
+        if (([typeOutside].includes(wnType) && hasGatewayType.has(targetType))) {
+          if (nextTarget.outgoing.some(o => [typeOutside, typeSubFlow].includes(o.target.wnType))) disable = false;
+          let jnpfData = injector.get('jnpfData');
+          if (nextTarget.outgoing.some(o => jnpfData.getValue(o.id)?.conditions?.length > 0)) disable = false; // 线条如果有条件也无法添加外部节点
+        }
+
+        if ([typeOutside].includes(wnType)) {
+          // 如果外部节点的下一个节点是合流节点,则继续检查合流节点的下一个节点是否为结束节点,如果是则不可添加外部节点
+          while (nextTarget && targetType === typeConfluence) {
+            let nextOutgoing = nextTarget.outgoing || [];
+            //  合流网关后面如果是子流程,外部节点,结束节点,则禁用当前的外部节点
+            if (nextOutgoing.some(out => out.target && [typeEnd, typeSubFlow, typeOutside].includes(out.target.wnType))) {
+              disable = false;
+              break;
+            }
+            // 如果有多个分支,取第一个不是合流节点的分支继续判断,否则继续循环
+            let nextConfluence = nextOutgoing.find(out => out.target && out.target.wnType === typeConfluence);
+            if (nextConfluence) nextTarget = nextConfluence.target;
+            else break;
+          }
+          while (lastsource && sourceType === typeConfluence) {
+            let lastIncoming = lastsource.incoming || [];
+            //  合流网关后面如果是子流程,外部节点,结束节点,则禁用当前的外部节点
+            if (lastIncoming.some(incoming => incoming.source && [typeOutside].includes(incoming.source.wnType))) {
+              disable = false;
+              break;
+            }
+            // 如果有多个分支,取第一个不是合流节点的分支继续判断,否则继续循环
+            let lastConfluence = lastIncoming.find(incoming => incoming.source?.wnType === typeConfluence);
+            if (lastConfluence) lastsource = lastConfluence.source;
+            else break;
+          }
+        }
+
+        return {
+          group: 'model',
+          className: className,
+          title: title,
+          disable: disable,
+          action: { click: disable && append },
+        };
+      }
+      // 默认contextPad
+      function defaultContextPad() {
+        let obj = Object.assign({
+          [del.name]: {
+            group: del.group,
+            className: del.className,
+            title: del.title,
+            action: {
+              click: removeElement,
+            },
+          },
+          [copy.name]: {
+            group: copy.group,
+            className: copy.className,
+            title: copy.title,
+            action: {
+              click: () => copyElement(wnType === typeOutside ? typeOutside : "default"),
+            },
+          },
+        });
+        let jnpfData = injector.get('jnpfData');
+        let data = jnpfData.getValue('copyType');
+        let copyNode = jnpfData.getValue('copy');
+        let outgoing = element.outgoing?.filter(o => o.target.wnType != typeTrigger)
+        if (data.type === typeOutside) {
+          if ([typeTask, typeProcessing, typeSubFlow, typeStart].includes(wnType)) {
+            if (![typeOutside, typeSubFlow].includes(outgoing[0]?.target?.wnType)) {
+              obj[paste.name] = appendAction(bpmnPaste, typePaste, paste.className, paste.title, typePaste, { wnName: null });
+            }
+          }
+        } else if (wnType === typeOutside) {
+          if (([typeTask, typeProcessing, typeSubFlow, typeEnd].includes(outgoing[0]?.target?.wnType) && [typeTask, typeProcessing].includes(copyNode?.wnType)) || data.type === bpmnTrigger) {
+            obj[paste.name] = appendAction(bpmnPaste, typePaste, paste.className, paste.title, typePaste, { wnName: null });
+          }
+        } else {
+          if (data.type === 'default' || data.type === bpmnTrigger) {
+            if (!(data.type === bpmnTrigger && wnType === typeSubFlow)) {
+              if (!(outgoing[0]?.target?.wnType === typeOutside && [typeSubFlow].includes(copyNode.wnType)))
+                obj[paste.name] = appendAction(bpmnPaste, typePaste, paste.className, paste.title, typePaste, { wnName: null });
+            }
+          }
+        }
+        return obj;
+      }
+      // 判断元素与网关之间的垂直距离是否小于某个阈值
+      function isWithinThreshold(target, source, threshold, processedElements: any) {
+        // 这里假设网关在上方,即网关的 y 坐标小于当前元素的 y 坐标
+        let gatewayY = target.y;
+        let sourceElementY = source.y;
+        // let elementObj: any;
+        let map = new Map(
+          Array.from(processedElements, (item: any) => [item.id, item]), // 使用 id 作为键
+        );
+        // 如果当前元素是合流网关 获取分流到合流内所有元素 并且获取y最大值的元素与 当前分流坐标做对比
+        if (target.wnType === typeConfluence) {
+          let allElements = injector.get('elementRegistry').getAll();
+          let treeBuilder = new BPMNTreeBuilder(allElements);
+          let gatewayElement = injector.get('elementRegistry').get(target.id.replace('_confluence', ''));
+          treeBuilder.onComputerMaxElementH(injector, target, gatewayElement, [], null, false, map, threshold);
+        }
+        if (map.has(source.id)) {
+          sourceElementY = sourceElementY + threshold;
+        }
+        return gatewayY - sourceElementY > threshold && gatewayY > sourceElementY;
+      }
+      function moveConnectedElements(connection: any, height: any, useWithinThreshold: boolean = true) {
+        const stack: any = []; // 用于存储待处理的连接线
+        const processedElements = new Set(); // 记录已经处理过的目标元素
+        stack.push(connection); // 从给定的连接线开始
+        while (stack.length > 0) {
+          const currentConnection: any = stack.pop();
+          const target = currentConnection.target;
+          if (!target) continue; // 如果没有目标元素,跳过
+          if (processedElements.has(target)) continue; // 如果目标元素已经处理过,跳过
+          if (useWithinThreshold && isWithinThreshold(target, currentConnection.source, height, processedElements)) continue;
+          processedElements.add(target); // 标记该元素已经被处理
+          // 遍历目标元素的所有出线连接,并将它们压入栈
+          const outgoingConnections: any = target.outgoing || [];
+          for (const outgoingConnection of outgoingConnections) {
+            stack.push(outgoingConnection); // 将所有关联的连接线压入栈中
+          }
+        }
+        return Array.from(processedElements);
+      }
+      function autoExecute(groupShape: any) {
+        // 判断groupBottom 和该触发节点对应的任务节点元素与合流节点间的距离
+        // 1. 获取到触发节点对应的任务节点 后续获取到该元素在哪个合流元素内部 获取到合流网关 判断合流网关和当前的判断groupBottom坐标 如果小于则需要对合流后的所有元素进行统一偏移
+        let triggerSourceShape: any;
+        injector.get('elementRegistry').forEach(e => {
+          if (e.businessObject.$attrs.customGroupId === groupShape.id && e.wnType === typeTrigger) triggerSourceShape = e.incoming[0].source;
+        });
+        // 迭代获取最近的合流节点 如果下一个节点时分流节点 则获取到对应的合流节点继续获取下一个节点
+        function getConfluence(shape: any) {
+          let targetList = NodeUtils.getNextElementList(shape, injector.get('elementRegistry').getAll());
+          let confluence: any;
+          targetList.map((element: any) => {
+            if (element.wnType === typeConfluence) {
+              confluence = element;
+              return;
+            }
+            if (element.wnType != typeTrigger) confluence = getConfluence(element);
+            if (hasGatewayType.has(element.wnType)) confluence = getConfluence(injector.get('elementRegistry').get(element.id + '_confluence'));
+          });
+          return confluence;
+        }
+        let confluenceShape = getConfluence(triggerSourceShape);
+        if (confluenceShape) {
+          if (groupShape && groupShape.y + groupShape.height + 50 >= confluenceShape.y) {
+            modeling.moveElements(moveConnectedElements(confluenceShape.incoming[0], 0, false), {
+              x: 0,
+              y: groupShape.y + groupShape.height + DEFAULT_DISTANCE - 15 - confluenceShape.y,
+            });
+          }
+        }
+      }
+      async function updateShapePosition(shape: any, element: any, businessTaskObject, businessInclusiveObject, businessTask2Object) {
+        let connectElement: any = element?.labelTarget;
+        let targetElement = connectElement?.target;
+        let sourceElement = connectElement?.source;
+        let promises: any = [];
+        if (shape.wnType === typeTrigger) {
+          // 生成一个触发节点
+          let labelTargetY = (DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height) * 2;
+          let y = labelTargetY - (targetElement.y - sourceElement.y);
+          if (targetElement.wnType === typeConfluence) {
+            modeling.moveElements(moveConnectedElements(element.labelTarget, labelTargetY), {
+              x: 0,
+              y: y,
+            });
+          }
+          autoPlace.append(sourceElement, shape);
+          let groupShapes = modeling.createShape(
+            {
+              type: 'bpmn:Group',
+            },
+            { x: shape.x - 25, y: shape.y - 15, width: 250, height: 118 },
+            shape.parent,
+          );
+          modeling.updateProperties(shape, {
+            customGroupId: groupShapes.id,
+          });
+        } else if (hasGatewayType.has(shape.wnType)) {
+          // 生成两个分支 包含分支 → 条件节点*2 → 任务节点*2 → 合流节点*1 最后融入到旧元素内
+          let shape1 = elementFactory.createShape(
+            Object.assign({ type: bpmnTask, name: typeTask, businessObject: businessTaskObject, wnType: typeTask }, { wnName: '审批节点' }),
+          );
+          businessInclusiveObject.id = shape.id + '_confluence';
+          let gateway = elementFactory.createShape(
+            Object.assign(
+              {
+                id: shape.id + '_confluence',
+                type: bpmnInclusive,
+                name: typeConfluence,
+                businessObject: businessInclusiveObject,
+                wnType: typeConfluence,
+              },
+              { wnName: '合流' },
+            ),
+          );
+          let shape2 = elementFactory.createShape(
+            Object.assign({ type: bpmnTask, name: typeTask, businessObject: businessTask2Object, wnType: typeTask }, { wnName: '审批节点' }),
+          );
+          let labelTargetY =
+            DEFAULT_DISTANCE * 4 +
+            typeConfig[bpmnTask].renderer.attr.height +
+            typeConfig[sourceElement.type].renderer.attr.height +
+            typeConfig[bpmnInclusive].renderer.attr.height;
+          promises.push(
+            modeling.moveElements(moveConnectedElements(element.labelTarget, labelTargetY), {
+              x: 0,
+              y: labelTargetY - (targetElement.y - sourceElement.y),
+            }),
+          );
+          promises.push(modeling.removeConnection(connectElement));
+          promises.push(autoPlace.append(sourceElement, shape));
+          promises.push(autoPlace.append(shape, shape1));
+          promises.push(autoPlace.append(shape, shape2));
+          promises.push(autoPlace.append(shape1, gateway));
+          promises.push(modeling.connect(shape2, gateway));
+          promises.push(modeling.connect(gateway, targetElement));
+        } else if (targetElement) {
+          let labelTargetY = (DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height) * 2;
+          let y = labelTargetY - (targetElement.y - sourceElement.y);
+          if (targetElement.type === bpmnEnd) y = DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height;
+          if (sourceElement.type === bpmnStart) y = DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height;
+          if (sourceElement.wnType === typeConfluence) {
+            y = DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height;
+            if (targetElement.y - sourceElement.y - DEFAULT_DISTANCE * 2 - typeConfig[bpmnTask].renderer.attr.height > 0) y = 0;
+            if (targetElement.y - sourceElement.y - DEFAULT_DISTANCE * 2 - typeConfig[bpmnTask].renderer.attr.height < 0)
+              y = (sourceElement.y + DEFAULT_DISTANCE * 2 + typeConfig[bpmnTask].renderer.attr.height) - targetElement.y;
+
+          }
+          if (sourceElement?.wnType === typeConfluence && targetElement?.wnType === typeConfluence) {
+            let maxY = -Infinity;
+            let id;
+            targetElement = injector.get('elementRegistry').get(targetElement.id);
+            targetElement.incoming?.length > 0 &&
+              targetElement.incoming.map(item => {
+                let currentElement: any = injector.get('elementRegistry').get(item.id);
+                if (currentElement.source?.y > maxY) {
+                  id = currentElement.source.id;
+                  maxY = currentElement.source.y;
+                }
+              });
+            if (targetElement.y - maxY <= DEFAULT_DISTANCE && sourceElement.id != id) {
+              y = DEFAULT_DISTANCE - (targetElement.y - maxY);
+            }
+          }
+          if (changeTypeByTaskShape[shape.wnType]) {
+            let source = contextPadProvider._injector.get('elementRegistry').get(connectElement.id).source;
+            // 需要增加customGroupId
+            modeling.updateProperties(shape, {
+              customGroupId: source.businessObject.$attrs.customGroupId,
+            });
+          }
+          promises.push(
+            modeling.moveElements(moveConnectedElements(element.labelTarget, labelTargetY), {
+              x: 0,
+              y: y,
+            }),
+          );
+          promises.push(modeling.removeConnection(connectElement));
+          autoPlace.append(sourceElement, shape);
+          promises.push(modeling.connect(shape, targetElement));
+        }
+        Promise.all(promises);
+        let elementRegistry = injector.get('elementRegistry');
+        let treeBuilder = new BPMNTreeBuilder(elementRegistry.getAll()); // 实例化工具类
+        treeBuilder.resizeGroupShape(elementRegistry.getAll(), injector);
+        if (changeTypeByTaskShape[shape.wnType]) {
+          let groupShape = injector.get('elementRegistry').get(targetElement.businessObject.$attrs.customGroupId);
+          autoExecute(groupShape);
+        }
+      }
+      async function onAutoPosition() {
+        let elementRegistry: any = await contextPadProvider._injector.get('elementRegistry');
+        let allElements = elementRegistry.getAll();
+        let treeBuilder = new BPMNTreeBuilder(allElements); // 实例化工具类
+        let bpmnTree = treeBuilder.constructTree(1); // 构建树状数据结构
+        let visited: any = new Map(); // 用于防止重复访问
+        let shapeList: any = []; // 修改触发节点旁的连接线坐标
+        let confluenceMap: any = new Map();
+        treeBuilder.calculateVirtualWidth(bpmnTree, elementRegistry); // 计算虚拟宽度
+        treeBuilder.traverseTreeBFS(bpmnTree, node => {
+          node?.offset && node.x != node.offset.x && visited.set(node.id, node);
+          if (node?.children?.length > 0) {
+            let hasTrigger = node.children.some(o => o.wnType === typeTrigger);
+            let hasConfluence = node.children.some(o => o.wnType === typeConfluence);
+            let shape = elementRegistry.get(node.id);
+            let confluence: any;
+            if (shape.outgoing?.length) {
+              shape.outgoing.map((connect: any) => {
+                if (connect.target.wnType === typeTrigger) hasTrigger = true;
+                if (connect.target.wnType === typeConfluence) {
+                  confluence = connect.target;
+                  hasConfluence = true;
+                }
+              });
+            }
+            if (hasTrigger && hasConfluence)
+              shapeList.push({ shape: elementRegistry.get(node.id), treeShape: node, treeConfluence: node.children[0], confluence: confluence });
+            if (node.wnType === typeConfluence) confluenceMap.set(node.id, node);
+          }
+        });
+        treeBuilder.formatCanvas(Array.from(visited.values()), modeling, elementRegistry);
+        // 如果某个节点的出线包含合流及触发节点元素 则将连接合流节点的线进行进行重新绘制(根据当前节点的虚拟宽度计算出偏移的xy轴坐标中点)
+        // 虚拟宽度的最左侧坐标为 当前元素的x + width/2 - 虚拟宽度/2
+        shapeList.map(({ shape, treeShape, treeConfluence, confluence }) => {
+          let confluenceElement = confluenceMap.get(treeConfluence.id);
+          let x = shape.x + shape.width / 2 - treeShape.virtualWidth / 2 + shape.width / 2;
+          let newWaypoints: any = [];
+          if (!confluenceElement) {
+            confluenceElement = confluenceMap.get(confluence.id);
+            let childrenShape = treeShape.children[treeShape.children.length - 1];
+            if (childrenShape.wnType === typeConfluence) {
+              childrenShape = treeShape.children[treeShape.children.length - 2];
+            }
+            x = (childrenShape.offset?.x ? childrenShape.offset.x : childrenShape.x) + childrenShape.virtualWidth / 2 + childrenShape.width / 2 + 120;
+            newWaypoints = [
+              { x: shape.x + shape.width, y: shape.y + shape.height / 2 },
+              { x: x, y: shape.y + shape.height / 2 },
+              { x: x, y: confluenceElement.y },
+              { x: confluenceElement.x, y: confluenceElement.y },
+            ];
+          } else {
+            newWaypoints = [
+              { x: shape.x, y: shape.y + shape.height / 2 },
+              { x: x, y: shape.y + shape.height / 2 },
+              { x: x, y: confluenceElement.y },
+              { x: confluenceElement.x, y: confluenceElement.y },
+            ];
+          }
+          let connect = shape.outgoing[0];
+          if (shape.outgoing?.length) {
+            connect = shape.outgoing.find(connect => connect.target.wnType != typeTrigger);
+          }
+          modeling.updateWaypoints(connect, newWaypoints);
+        });
+        treeBuilder.resizeGroupShape(elementRegistry.getAll(), injector);
+      }
+      // 复制节点
+      function copyElement(type) {
+        let jnpfData = injector.get('jnpfData');
+        let selection = injector.get('selection');
+        let copyData = jnpfData.getValue(element.id);
+        jnpfData.setValue(bpmnCopy, element);
+        jnpfData.setValue('copyType', { type: type, data: copyData });
+        eventBus.fire('custom.message', {
+          context: '复制成功',
+          messageType: 'success',
+        });
+        selection.select(null);
+      }
+    }
+
+    return undefined;
+  }
+  return undefined;
+};
+export default CustomizeContextPad;

+ 59 - 0
lib/simpleModeler/contextPad/provider/index.ts

@@ -0,0 +1,59 @@
+import ContextPadProvider from 'bpmn-js/lib/features/context-pad/ContextPadProvider';
+import CustomizeContextPad from './CustomizeContextPad';
+class YmContextPadProvider extends ContextPadProvider {
+  _modeling: any;
+  _rules: any;
+  _eventBus: any;
+  _injector: any;
+  _commandStack: any;
+  constructor(
+    config: any,
+    injector: any,
+    eventBus: any,
+    contextPad: any,
+    modeling: any,
+    elementFactory: any,
+    connect: any,
+    create: any,
+    popupMenu: any,
+    canvas: any,
+    rules: any,
+    translate: any,
+    commandStack: any,
+  ) {
+    super(config, injector, eventBus, contextPad, modeling, elementFactory, connect, create, popupMenu, canvas, rules, translate);
+    this._rules = rules;
+    this._modeling = modeling;
+    this._injector = injector;
+    this._eventBus = eventBus;
+    this._commandStack = commandStack;
+  }
+
+  getContextPadEntries(element: any): (() => any) | any | undefined {
+    return CustomizeContextPad(this, element);
+  }
+
+  // 多个元素框选时 默认包含框选删除元素
+  getMultiElementContextPadEntries() {
+    var actions = {};
+    return actions;
+  }
+}
+
+YmContextPadProvider.$inject = [
+  'config.contextPad',
+  'injector',
+  'eventBus',
+  'contextPad',
+  'modeling',
+  'elementFactory',
+  'connect',
+  'create',
+  'popupMenu',
+  'canvas',
+  'rules',
+  'translate',
+  'commandStack',
+];
+
+export default YmContextPadProvider;

+ 199 - 0
lib/simpleModeler/gridSnapping/autoPlacebehavior/index.ts

@@ -0,0 +1,199 @@
+import { is, isAny } from '../../../utils/modelUtil';
+import { findFreePosition, generateGetNextPosition, getConnectedDistance } from '../../../autoPlace/YmAutoPlaceUtil';
+import { isObject } from 'min-dash';
+import { typeLabel, typeTrigger } from '../../../config/variableName';
+var HIGH_PRIORITY = 2000;
+function distance(a: any, b: any) {
+  return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
+}
+/**
+ * Convert the given bounds to a { top, left, bottom, right } descriptor.
+ * @param {Point|Rect} bounds
+ * @return {RectTRBL}
+ *
+ */
+export function asTRBL(bounds: any) {
+  return {
+    top: bounds.y,
+    right: bounds.x + (bounds.width || 0),
+    bottom: bounds.y + (bounds.height || 0),
+    left: bounds.x,
+  };
+}
+export function getConnectionMid(connection: any) {
+  var waypoints = connection.waypoints;
+  var parts = waypoints.reduce(function (parts: any, point: any, index: any) {
+    var lastPoint = waypoints[index - 1];
+    if (lastPoint) {
+      var lastPart = parts[parts.length - 1];
+      var startLength = (lastPart && lastPart.endLength) || 0;
+      var length = distance(lastPoint, point);
+      parts.push({
+        start: lastPoint,
+        end: point,
+        startLength: startLength,
+        endLength: startLength + length,
+        length: length,
+      });
+    }
+    return parts;
+  }, []);
+  var totalLength = parts.reduce(function (length: any, part: any) {
+    return length + part.length;
+  }, 0);
+  var midLength = totalLength / 2;
+  var i = 0;
+  var midSegment = parts[i];
+  while (midSegment.endLength < midLength) {
+    midSegment = parts[++i];
+  }
+  var segmentProgress = (midLength - midSegment.startLength) / midSegment.length;
+  var midPoint = {
+    x: midSegment.start.x + (midSegment.end.x - midSegment.start.x) * segmentProgress,
+    y: midSegment.start.y + (midSegment.end.y - midSegment.start.y) * segmentProgress,
+  };
+  return midPoint;
+}
+export function roundPoint(point: any) {
+  return {
+    x: Math.round(point.x),
+    y: Math.round(point.y),
+  };
+}
+export function getBoundsMid(bounds: any) {
+  return roundPoint({
+    x: bounds.x + (bounds.width || 0) / 2,
+    y: bounds.y + (bounds.height || 0) / 2,
+  });
+}
+export function getMid(element: any) {
+  if (!!element.waypoints) return getConnectionMid(element);
+  return getBoundsMid(element);
+}
+export function getOrientation(rect: any, reference: any, padding: any) {
+  padding = padding || 0;
+  if (!isObject(padding)) padding = { x: padding, y: padding };
+  var rectOrientation = asTRBL(rect),
+    referenceOrientation = asTRBL(reference);
+  var top = rectOrientation.bottom + padding.y <= referenceOrientation.top,
+    right = rectOrientation.left - padding.x >= referenceOrientation.right,
+    bottom = rectOrientation.top - padding.y >= referenceOrientation.bottom,
+    left = rectOrientation.right + padding.x <= referenceOrientation.left;
+  var vertical = top ? 'top' : bottom ? 'bottom' : null,
+    horizontal = left ? 'left' : right ? 'right' : null;
+  if (horizontal && vertical) return vertical + '-' + horizontal;
+  else return horizontal || vertical || 'intersect';
+}
+/**
+ * Always try to place element right of source;
+ * compute actual distance from previous nodes in flow.
+ */
+export function getFlowNodePosition(source: any, element: any) {
+  var sourceTrbl = asTRBL(source); // 描述图形或区域在页面或画布上的位置和大小
+  var sourceMid = getMid(source); // 获取中心点坐标
+  var horizontalDistance = getConnectedDistance(source, {
+    direction: 's',
+    filter: function (connection: any) {
+      return is(connection, 'bpmn:SequenceFlow');
+    },
+  });
+  var margin = 30,
+    minDistance = 50,
+    orientation = 'top';
+  if (is(source, 'bpmn:BoundaryEvent')) {
+    orientation = getOrientation(source, source.host, -25);
+    if (orientation.indexOf('top') !== -1) margin *= -1;
+  }
+  var position: any = {};
+  position = {
+    // x: sourceTrbl.right + horizontalDistance + element.width / 2,
+    // y: sourceMid.y + getVerticalDistance(orientation, minDistance),
+    x: sourceMid.x,
+    y: sourceTrbl.bottom + horizontalDistance + element.height / 2,
+  };
+
+  // y轴距离
+  var nextPositionDirection = {
+    x: {
+      margin: margin,
+      minDistance: minDistance,
+    },
+  };
+  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
+}
+export function getNewShapePosition(source: any, element: any) {
+  if (is(element, 'bpmn:TextAnnotation')) return getTextAnnotationPosition(source, element);
+  if (isAny(element, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference'])) return getDataElementPosition(source, element);
+  if (is(element, 'bpmn:FlowNode')) return getFlowNodePosition(source, element);
+}
+export function getDataElementPosition(source: any, element: any) {
+  var sourceTrbl = asTRBL(source);
+  var position = {
+    x: sourceTrbl.right - 10 + element.width / 2,
+    y: sourceTrbl.bottom + 40 + element.width / 2,
+  };
+  var nextPositionDirection = {
+    x: {
+      margin: 30,
+      minDistance: 30,
+    },
+  };
+  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
+}
+export function getTextAnnotationPosition(source: any, element: any) {
+  var sourceTrbl = asTRBL(source);
+  var position = {
+    x: sourceTrbl.right + element.width / 2,
+    y: sourceTrbl.top - 50 - element.height / 2,
+  };
+  if (!!source.waypoints) {
+    position = getMid(source);
+    position.x += 100;
+    position.y -= 50;
+  }
+  var nextPositionDirection = {
+    y: {
+      margin: -30,
+      minDistance: 20,
+    },
+  };
+  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
+}
+function getNewSourceInLabel(source: any) {
+  if (source.type === typeLabel) {
+    let connectElement: any = source?.labelTarget;
+    if (connectElement?.target) return connectElement.target;
+  }
+  return source;
+}
+export default function YmGridSnappingAutoPlaceBehavior(eventBus: any, injector: any) {
+  eventBus.on('autoPlace', HIGH_PRIORITY, function (context: any) {
+    var source = context.source,
+      sourceMid = getMid(source),
+      shape = context.shape;
+    let elementRegistry = injector.get('elementRegistry');
+    let element = elementRegistry.get(source.id);
+    var position: any = getNewShapePosition(getNewSourceInLabel(element), shape);
+    ['x', 'y'].forEach(function (axis: any) {
+      var options: any = {};
+      if (position[axis] === sourceMid[axis]) return;
+      if (position[axis] > sourceMid[axis]) options.min = position[axis];
+      else options.max = position[axis];
+      if (is(shape, 'bpmn:TextAnnotation')) {
+        if (isHorizontal(axis)) options.offset = -shape.width / 2;
+        else options.offset = -shape.height / 2;
+      }
+    });
+
+    let height = shape.wnType === typeTrigger ? 254 : 160;
+    position = {
+      x: position.x,
+      y: Math.abs(position.y - element.y) > 260 ? element.y + height : position.y,
+    };
+    return position;
+  });
+}
+YmGridSnappingAutoPlaceBehavior.$inject = ['eventBus', 'injector'];
+function isHorizontal(axis: any) {
+  return axis === 'x';
+}

+ 235 - 0
lib/simpleModeler/gridSnapping/connect/index.ts

@@ -0,0 +1,235 @@
+import inherits from 'inherits-browser';
+import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
+import { pointsAligned } from 'diagram-js/lib/util/Geometry';
+import { assign } from 'min-dash';
+import { typeConfluence, typeEnd, typeOutside, typeProcessing, typeStart, typeSubFlow, typeTask, typeTrigger } from '../../../config/variableName';
+import { changeTypeByTaskShape, hasGatewayType } from '../../../config';
+
+/**
+ * @typedef {import('diagram-js/lib/core/EventBus').default} EventBus
+ * @typedef {import('diagram-js/lib/features/grid-snapping/GridSnapping').default} GridSnapping
+ * @typedef {import('../../modeling/Modeling').default} Modeling
+ * @typedef {import('diagram-js/lib/util/Types').Point} Point
+ */
+
+const HIGH_PRIORITY = 3000;
+
+export default function GridSnappingLayoutConnectionBehavior(this: any, eventBus: any, gridSnapping: any, modeling: any) {
+  CommandInterceptor.call(this, eventBus);
+  this._gridSnapping = gridSnapping;
+  const self = this;
+  this.postExecuted(['connection.create', 'connection.layout'], HIGH_PRIORITY, function (event: any) {
+    let context = event.context,
+      connection = context.connection,
+      waypoints = connection.waypoints;
+    let target = connection.target,
+      source = connection.source;
+    if (hasGatewayType.has(source.wnType) || hasGatewayType.has(target.wnType)) {
+      let x = source.x;
+      if (target.x > source.x) x = source.x + source.width;
+      waypoints = [
+        {
+          x: x,
+          y: source.y + source.height / 2,
+        },
+        {
+          x: target.x + target.width / 2,
+          y: source.y + source.height / 2,
+        },
+        {
+          x: target.x + target.width / 2,
+          y: target.y,
+        },
+      ];
+      if (source.x + source.width / 2 === target.x + target.width / 2) {
+        waypoints = [
+          {
+            x: source.x + source.width / 2,
+            y: source.y + source.height,
+          },
+          {
+            x: target.x + target.width / 2,
+            y: target.y,
+          },
+        ];
+      }
+    }
+    if (target.wnType === typeConfluence) {
+      let x = target.x;
+      if (target.x < source.x) {
+        x = target.x + target.width;
+      }
+      waypoints = [
+        {
+          x: source.x + source.width / 2,
+          y: source.y + source.height,
+        },
+        {
+          x: source.x + source.width / 2,
+          y: target.y + target.height / 2,
+        },
+        {
+          x: x,
+          y: target.y + target.height / 2,
+        },
+      ];
+      if (source.x + source.width / 2 === target.x + target.width / 2) {
+        waypoints = [
+          {
+            x: source.x + source.width / 2,
+            y: source.y + source.height,
+          },
+          {
+            x: target.x + target.width / 2,
+            y: target.y,
+          },
+        ];
+      }
+    }
+ 
+    if (([typeStart, typeTask, typeProcessing, typeTrigger, typeOutside].includes(source.wnType) || changeTypeByTaskShape[source.wnType]) &&
+    (
+      changeTypeByTaskShape[target.wnType] ||
+       [typeEnd, typeSubFlow, typeOutside, typeTrigger, typeProcessing, typeTask].includes(target.wnType)
+      )
+    ) {
+      if (target.x + target.width / 2 > source.x + source.width / 2) {
+        waypoints = [
+          {
+            x: source.x + source.width,
+            y: source.y + source.height / 2,
+          },
+          {
+            x: target.x + target.width / 2,
+            y: source.y + source.height / 2,
+          },
+          {
+            x: target.x + target.width / 2,
+            y: target.y,
+          },
+        ];
+      }
+      if (target.x + target.width / 2 < source.x + source.width / 2) {
+        waypoints = [
+          {
+            x: source.x,
+            y: source.y + source.height / 2,
+          },
+          {
+            x: target.x + target.width / 2,
+            y: source.y + source.height / 2,
+          },
+          {
+            x: target.x + target.width / 2,
+            y: target.y,
+          },
+        ];
+      }
+      if (target.x + target.width / 2 === source.x + source.width / 2) {
+        waypoints = [
+          {
+            x: source.x + source.width / 2,
+            y: source.y + source.height,
+          },
+          {
+            x: target.x + target.width / 2,
+            y: target.y,
+          },
+        ];
+      }
+    }
+    if (connection.target?.wnType != typeConfluence) modeling.updateWaypoints(connection, self.snapMiddleSegments(waypoints));
+    if (connection.target?.wnType === typeConfluence && connection.source.incoming?.length === 1) {
+      modeling.updateWaypoints(connection, self.snapMiddleSegments(waypoints));
+    }
+
+    if ( connection.label && ([typeTask, typeProcessing, typeTrigger, typeSubFlow, typeOutside, typeEnd].includes(target.wnType) ||
+     changeTypeByTaskShape[target.wnType])
+    ) {
+      connection.label.x = target.x + target.width / 2 - 14;
+      connection.label.y = connection.label.y;
+      modeling.updateProperties(connection.label, {});
+    }
+  });
+}
+
+GridSnappingLayoutConnectionBehavior.$inject = ['eventBus', 'gridSnapping', 'modeling'];
+inherits(GridSnappingLayoutConnectionBehavior, CommandInterceptor);
+/**
+ * Snap middle segments of a given connection.
+ *
+ * @param {Point[]} waypoints
+ *
+ * @return {Point[]}
+ */
+GridSnappingLayoutConnectionBehavior.prototype.snapMiddleSegments = function (this: any, waypoints: any[]) {
+  const gridSnapping = this._gridSnapping;
+  waypoints = waypoints.slice();
+
+  for (let i = 1; i < waypoints.length - 2; i++) {
+    const snapped = snapSegment(gridSnapping, waypoints[i], waypoints[i + 1]);
+    waypoints[i] = snapped[0];
+    waypoints[i + 1] = snapped[1];
+  }
+  return waypoints;
+};
+
+/**
+ * Check whether a connection has middle segments.
+ *
+ * @param {Point[]} waypoints
+ *
+ * @return {boolean}
+ */
+function hasMiddleSegments(waypoints: any[]) {
+  return waypoints.length > 3;
+}
+
+/**
+ * Check whether an alignment is horizontal.
+ *
+ * @param {string} aligned
+ *
+ * @return {boolean}
+ */
+function horizontallyAligned(aligned: string) {
+  return aligned === 'h';
+}
+
+/**
+ * Check whether an alignment is vertical.
+ *
+ * @param {string} aligned
+ *
+ * @return {boolean}
+ */
+function verticallyAligned(aligned: string) {
+  return aligned === 'v';
+}
+
+/**
+ * Get middle segments from a given connection.
+ *
+ * @param {Point[]} waypoints
+ *
+ * @return {Point[]}
+ */
+function snapSegment(gridSnapping: any, segmentStart: any, segmentEnd: any) {
+  const aligned: any = pointsAligned(segmentStart, segmentEnd);
+  const snapped: any = {};
+
+  if (horizontallyAligned(aligned)) {
+    snapped.y = gridSnapping.snapValue(segmentStart.y);
+  }
+
+  if (verticallyAligned(aligned)) {
+    snapped.x = gridSnapping.snapValue(segmentStart.x);
+  }
+
+  if ('x' in snapped || 'y' in snapped) {
+    segmentStart = assign({}, segmentStart, snapped);
+    segmentEnd = assign({}, segmentEnd, snapped);
+  }
+
+  return [segmentStart, segmentEnd];
+}

+ 153 - 0
lib/simpleModeler/keyboard/BpmnKeyboardBindings.ts

@@ -0,0 +1,153 @@
+import inherits from 'inherits-browser';
+
+import KeyboardBindings from 'diagram-js/lib/features/keyboard/KeyboardBindings';
+
+/**
+ * @typedef {import('didi').Injector} Injector
+ * @typedef {import('diagram-js/lib/features/editor-actions/EditorActions').default} EditorActions
+ * @typedef {import('diagram-js/lib/features/keyboard/Keyboard').default} Keyboard
+ */
+
+/**
+ * BPMN 2.0 specific keyboard bindings.
+ *
+ * @param {Injector} injector
+ */
+export default function BpmnKeyboardBindings(injector: any) {
+  //@ts-ignore
+  injector.invoke(KeyboardBindings, this);
+}
+
+inherits(BpmnKeyboardBindings, KeyboardBindings);
+
+BpmnKeyboardBindings.$inject = ['injector'];
+
+/**
+ * Register available keyboard bindings.
+ *
+ * @param {Keyboard} keyboard
+ * @param {EditorActions} editorActions
+ */
+BpmnKeyboardBindings.prototype.registerBindings = function (keyboard: any, editorActions: any) {
+  // inherit default bindings
+  KeyboardBindings.prototype.registerBindings.call(this, keyboard, editorActions);
+
+  /**
+   * Add keyboard binding if respective editor action
+   * is registered.
+   *
+   * @param {string} action name
+   * @param {Function} fn that implements the key binding
+   */
+  function addListener(action: string, fn: Function) {
+    if (editorActions.isRegistered(action)) {
+      keyboard.addListener(fn);
+    }
+  }
+
+  addListener('find', function (context: any) {
+    var event = context.keyEvent;
+
+    if (keyboard.isKey(['f', 'F'], event) && keyboard.isCmd(event)) {
+      editorActions.trigger('find');
+
+      return true;
+    }
+  });
+
+  // activate space tool
+  // S
+  addListener('spaceTool', function (context: any) {
+    var event = context.keyEvent;
+
+    if (keyboard.hasModifier(event)) {
+      return;
+    }
+
+    if (keyboard.isKey(['s', 'S'], event)) {
+      editorActions.trigger('spaceTool');
+
+      return true;
+    }
+  });
+
+  // activate lasso tool
+  // L
+  addListener('lassoTool', function (context: any) {
+    var event = context.keyEvent;
+
+    if (keyboard.hasModifier(event)) {
+      return;
+    }
+
+    if (keyboard.isKey(['l', 'L'], event)) {
+      editorActions.trigger('lassoTool');
+
+      return true;
+    }
+  });
+
+  // activate hand tool
+  // H
+  addListener('handTool', function (context: any) {
+    var event = context.keyEvent;
+
+    if (keyboard.hasModifier(event)) {
+      return;
+    }
+
+    if (keyboard.isKey(['h', 'H'], event)) {
+      editorActions.trigger('handTool');
+
+      return true;
+    }
+  });
+
+  // activate global connect tool
+  // C
+  addListener('globalConnectTool', function (context: any) {
+    var event = context.keyEvent;
+
+    if (keyboard.hasModifier(event)) {
+      return;
+    }
+
+    if (keyboard.isKey(['c', 'C'], event)) {
+      editorActions.trigger('globalConnectTool');
+
+      return true;
+    }
+  });
+
+  // activate direct editing
+  // E
+  addListener('directEditing', function (context: any) {
+    var event = context.keyEvent;
+
+    if (keyboard.hasModifier(event)) {
+      return;
+    }
+
+    if (keyboard.isKey(['e', 'E'], event)) {
+      editorActions.trigger('directEditing');
+
+      return true;
+    }
+  });
+
+  // activate replace element
+  // R
+  addListener('replaceElement', function (context: any) {
+    var event = context.keyEvent;
+
+    if (keyboard.hasModifier(event)) {
+      return;
+    }
+
+    if (keyboard.isKey(['r', 'R'], event)) {
+      editorActions.trigger('replaceElement', event);
+
+      return true;
+    }
+  });
+};

+ 57 - 0
lib/simpleModeler/modeler/index.ts

@@ -0,0 +1,57 @@
+import Modeler from 'bpmn-js/lib/Modeler';
+import YmPaletteProvider from '../..//palette';
+import YmRenderer from '../renderer';
+import YmElementFactory from '../../factory';
+import YmOutline from '../../outline';
+import YmBusinessData from '../../business';
+import YmGridSnappingAutoPlaceBehavior from '../gridSnapping/autoPlacebehavior';
+import YmContextPad from '../../simpleModeler/contextPad';
+import YmContextPadProvider from '../../simpleModeler/contextPad/provider';
+import YmCustomBpmnRules from '../../rule';
+import CustomLabelEditingProvider from '../../labelEditing/labelEditingProvider';
+import GridSnappingLayoutConnectionBehavior from '../gridSnapping/connect';
+import BpmnKeyboardBindings from '../keyboard/BpmnKeyboardBindings';
+let flowInfo: any;
+const modeler: any = options => [
+  {
+    __init__: [
+      'paletteProvider',
+      'bpmnRenderer',
+      'contextPadProvider',
+      'replaceMenuProvider',
+      'elementFactory',
+      'jnpfData',
+      'gridSnappingAutoPlaceBehavior',
+      'alignElementsContextPadProvider',
+      'alignElementsMenuProvider',
+      'bpmnAlignElements',
+      'outlineProvider',
+      'contextPad',
+      'bpmnRules',
+      'keyboardBindings',
+    ],
+    paletteProvider: ['type', YmPaletteProvider], // 左侧的元素
+    bpmnRenderer: ['type', YmRenderer, { options }], // 画布渲染
+    elementFactory: ['type', YmElementFactory], // 元素工厂
+    jnpfData: ['type', YmBusinessData], // 用于放置业务数据
+    gridSnappingAutoPlaceBehavior: ['type', YmGridSnappingAutoPlaceBehavior], // 自动生成元素位置 在点击coontext-pad时计算元素生成位置
+    outlineProvider: ['type', YmOutline], // 元素的外边框(用于修改边框颜色,注:线条颜色有svg获取标签再去修改颜色及箭头)
+    contextPad: ['type', YmContextPad], // 点击元素后的元素右侧弹窗框(显示开始节点 结束节点等)
+    contextPadProvider: ['type', YmContextPadProvider], // context-pad 属性
+    bpmnRules: ['type', YmCustomBpmnRules], // 自定义规则
+    labelEditingProvider: ['type', CustomLabelEditingProvider], // 编辑
+    gridSnappingLayoutConnectionBehavior: ['type', GridSnappingLayoutConnectionBehavior], // 修改连线的排序
+    keyboardBindings: ['type', BpmnKeyboardBindings], // 快捷键
+  },
+];
+
+class jnpfModeler extends Modeler {
+  constructor(options: any) {
+    flowInfo = options.flowInfo;
+    super(options);
+  }
+}
+
+jnpfModeler.prototype['_modules'] = [].concat(jnpfModeler.prototype['_modules'], modeler(flowInfo));
+
+export default jnpfModeler;

+ 184 - 0
lib/simpleModeler/renderer/CustomizeRenderer.ts

@@ -0,0 +1,184 @@
+import { changeTypeByTaskShape, typeConfig } from '../../config';
+import { append as svgAppend, create as svgCreate } from 'tiny-svg';
+import {
+  bpmnChoose,
+  bpmnEnd,
+  bpmnExclusive,
+  bpmnGroup,
+  bpmnInclusive,
+  bpmnLabel,
+  bpmnOutside,
+  bpmnParallel,
+  bpmnProcessing,
+  bpmnStart,
+  bpmnSubFlow,
+  bpmnTask,
+  bpmnTrigger,
+  typeChoose,
+  typeCondition,
+  typeConfluence,
+  typeOutside,
+  typeProcessing,
+  typeSubFlow,
+  typeTrigger,
+} from '../../config/variableName';
+
+/**
+ * svg重画bpmn节点
+ */
+export default (parentNode: any, element: any, jnpfFlowInfo: any, injector: any) => {
+  let type = element.type; // 获取到类型
+  let data = jnpfFlowInfo?.flowNodes[element.id];
+  let wnType = element.wnType || data?.type;
+  if (typeConfig && typeConfig[type]) {
+    if (type === bpmnGroup) return null;
+    const typeMap = {
+      [typeSubFlow]: bpmnSubFlow,
+      [typeProcessing]: bpmnProcessing,
+      [typeTrigger]: bpmnTrigger,
+      [typeOutside]: bpmnOutside,
+    };
+    let matchedType = typeMap[data?.type] || typeMap[element.wnType];
+    if(matchedType) type = matchedType;
+    if (changeTypeByTaskShape[data?.type] || changeTypeByTaskShape[element?.wnType]) type = changeTypeByTaskShape[element?.wnType || data?.type];
+    if (type === bpmnInclusive && wnType === typeChoose) type = bpmnChoose;
+    let { renderer } = typeConfig[type];
+    let { icon, iconColor, rendererName, background, titleColor, attr, bodyDefaultText } = renderer;
+    //  直接修改元素的宽高
+    element['width'] = wnType === typeConfluence ? 1 : element.isPreview ? 1 : attr.width;
+    element['height'] = wnType === typeConfluence ? 1 : element.isPreview ? 1 : attr.height;
+    let nodeName = element.nodeName != null ? element.nodeName : data?.nodeName != null ? data.nodeName : rendererName;
+    let nodeContent = element.elementBodyName || data?.content || bodyDefaultText;
+    let foreignObject: any = svgCreate('foreignObject', {
+      width: wnType === typeConfluence ? 0 : element.isPreview ? 1 : wnType === typeCondition ? 128 : attr.width,
+      height: wnType === typeConfluence ? 0 : element.isPreview ? 1 : wnType === typeCondition ? 28 : attr.height,
+      class: type === bpmnStart || type === bpmnEnd ? 'begin-or-end-node' : 'task-node',
+    });
+    // 开始节点
+    if (type === bpmnStart) {
+      foreignObject.innerHTML = `
+      <div class="node-container start-node-container" style="background:${background}" >
+        <div class='node-top-container'>
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+    // 审批节点
+    if (type === bpmnTask) {
+      foreignObject.innerHTML = `
+      <div class="node-container" style="background:${background}" >
+        <div class='node-top-container' style="background:${titleColor};">
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+        <div class='node-bottom-container'>
+          <span>${nodeContent}</span>  
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+    // 子流程节点
+    if (type === bpmnSubFlow || type === bpmnProcessing || type === bpmnOutside || type === bpmnTrigger || changeTypeByTaskShape[element?.wnType || data?.type]) {
+      foreignObject.innerHTML = `
+      <div class="node-container" style="background:${background}" >
+        <div class='node-top-container' style="background:${titleColor}">
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+        <div class='node-bottom-container'>
+          <span>${nodeContent}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+    // 结束节点
+    if (type === bpmnEnd) {
+      foreignObject.innerHTML = `
+      <div class="node-container end-node-container" style="background:${background}" >
+        <div class='node-top-container'>
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+    // label改为按钮
+    if (type === bpmnLabel) {
+      if (wnType === 'condition') {
+        let jnpfData = injector?.get('jnpfData');
+        let data = jnpfData.getValue('global');
+        let connectName = jnpfData.getValue(element.id.replace('_label', ''))?.nodeName || '连接线';
+        if (data.isShowConditions) {
+          let foreignObject: any = svgCreate('foreignObject', {
+            width: 128,
+            height: 28,
+            class: 'label-node',
+          });
+          foreignObject.innerHTML = `
+          <div class="node-container-label" >
+            <div class='node-top-container'>
+              <span>${data.showNameType === 1 ? element.text : connectName}</span>
+            </div>
+          </div>`;
+          element.text && svgAppend(parentNode, foreignObject);
+        }
+        return null;
+      } else {
+        foreignObject.innerHTML = `
+        <div class="node-container"  >
+          <div class='label-node-container'>+</div>
+        </div>`;
+        svgAppend(parentNode, foreignObject);
+      }
+      return parentNode;
+    }
+
+    // 条件分支
+    if (type === bpmnInclusive) {
+      // 合流 展示一个点
+      if (element.wnType === typeConfluence) {
+        foreignObject.innerHTML = `
+        <div class="node-container start-node-container node-simpleModeler" style="background:${background}" >
+          <div class='node-top-container'>
+            <span>合流</span>
+          </div>
+        </div>`;
+      } else if (wnType === typeChoose) {
+        foreignObject.innerHTML = `
+        <div class="node-container start-node-container node-simpleModeler" style="background:${background}" >
+          <div class='node-top-container'>
+            <span>${rendererName}</span>
+          </div>
+        </div>`;
+      } else {
+        foreignObject.innerHTML = `
+        <div class="node-container start-node-container node-simpleModeler" style="background:${background}" >
+          <div class='node-top-container'>
+            <span>添加条件</span>
+          </div>
+        </div>`;
+      }
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+
+    // 其它网关
+    let otherGatewayRule = new Set([bpmnParallel, bpmnExclusive, bpmnChoose]);
+    if (otherGatewayRule.has(type)) {
+      foreignObject.innerHTML = `
+      <div class="node-container start-node-container node-simpleModeler" style="background:${background}" >
+        <div class='node-top-container'>
+          <span>${rendererName}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+  }
+};

+ 76 - 0
lib/simpleModeler/renderer/connect/button.ts

@@ -0,0 +1,76 @@
+//
+import { append as svgAppend, attr as svgAttr, create as svgCreate } from 'tiny-svg';
+import { assign } from 'min-dash';
+import { query as domQuery } from 'min-dom';
+
+export default function createButton(element: any, ymCanvas: any) {
+  var activeElement = document.getElementById('bpmnSequenceFlowActiveId');
+  if (!activeElement) {
+    if (element.type === 'bpmn:SequenceFlow') {
+      // svgAttr(sequenceflowEnd, { d: 'M 1 5 L 11 10 L 1 15 Z' });
+      const foreignObject: any = svgCreate('foreignObject', {
+        class: 'startBody',
+        x: 8,
+        y: 40,
+        width: 184,
+        height: 38,
+      });
+      addMarker(
+        'bpmnSequenceFlowActiveId',
+        {
+          element: foreignObject,
+          ref: { x: 11, y: 10 },
+          scale: 0.5,
+          attrs: {
+            fill: 'rgb(24, 131, 255)',
+            stroke: 'rgb(24, 131, 255)',
+          },
+        },
+        ymCanvas,
+      );
+    }
+  }
+}
+
+function addMarker(id: any, options: any, ymCanvas: any) {
+  let attrs = assign(
+    {
+      fill: 'hsl(225, 10%, 15%)',
+      strokeWidth: 1,
+      strokeLinecap: 'round',
+      strokeDasharray: 'none',
+    },
+    options.attrs,
+  );
+
+  let ref = options.ref || { x: 0, y: 0 };
+  let scale = options.scale || 1;
+  if (attrs.strokeDasharray === 'none') {
+    attrs.strokeDasharray = [10000, 1];
+  }
+
+  let marker = svgCreate('marker');
+
+  svgAttr(options.element, attrs);
+
+  svgAppend(marker, options.element);
+
+  svgAttr(marker, {
+    id: id,
+    viewBox: '0 0 20 20',
+    refX: ref.x,
+    refY: ref.y,
+    markerWidth: 20 * scale,
+    markerHeight: 20 * scale,
+    orient: 'auto',
+  });
+
+  let defs = domQuery('defs', ymCanvas._svg);
+  if (!defs) {
+    defs = svgCreate('defs');
+    svgAppend(ymCanvas._svg, defs);
+  }
+
+  svgAppend(defs, marker);
+  // markers[id] = marker;
+}

+ 61 - 0
lib/simpleModeler/renderer/connect/marker.ts

@@ -0,0 +1,61 @@
+import { append as svgAppend, attr as svgAttr, create as svgCreate } from 'tiny-svg';
+import { assign } from 'min-dash';
+import { query as domQuery } from 'min-dom';
+
+export default function createAddMarkerSelect(element: any, ymCanvas: any) {
+  var activeElement = document.getElementById('bpmnSequenceFlowActiveId');
+  if (!activeElement) {
+    if (element.type === 'bpmn:SequenceFlow') {
+      var sequenceFlowEnd = svgCreate('path');
+      svgAttr(sequenceFlowEnd, { d: 'M 1 5 L 11 10 L 1 15 Z' });
+      addMarker(
+        'bpmnSequenceFlowActiveId',
+        {
+          element: sequenceFlowEnd,
+          ref: { x: 11, y: 10 },
+          scale: 0.5,
+          attrs: {
+            fill: 'rgb(24, 131, 255)',
+            stroke: 'rgb(24, 131, 255)',
+          },
+        },
+        ymCanvas,
+      );
+    }
+  }
+}
+function addMarker(id: any, options: any, ymCanvas: any) {
+  let attrs = assign(
+    {
+      fill: 'hsl(225, 10%, 15%)',
+      strokeWidth: 1,
+      strokeLinecap: 'round',
+      strokeDasharray: 'none',
+    },
+    options.attrs,
+  );
+  let ref = options.ref || { x: 0, y: 0 };
+  let scale = options.scale || 1;
+  if (attrs.strokeDasharray === 'none') {
+    attrs.strokeDasharray = [10000, 1];
+  }
+  let marker = svgCreate('marker');
+  svgAttr(options.element, attrs);
+  svgAppend(marker, options.element);
+  svgAttr(marker, {
+    id: id,
+    viewBox: '0 0 20 20',
+    refX: ref.x,
+    refY: ref.y,
+    markerWidth: 20 * scale,
+    markerHeight: 20 * scale,
+    orient: 'auto',
+  });
+  let defs = domQuery('defs', ymCanvas._svg);
+  if (!defs) {
+    defs = svgCreate('defs');
+    svgAppend(ymCanvas._svg, defs);
+  }
+  svgAppend(defs, marker);
+  // markers[id] = marker;
+}

+ 119 - 0
lib/simpleModeler/renderer/index.ts

@@ -0,0 +1,119 @@
+import BpmnRenderer from 'bpmn-js/lib/draw/BpmnRenderer';
+import CustomizeRenderer from './CustomizeRenderer';
+import { getRectPath } from 'bpmn-js/lib/draw/BpmnRenderUtil';
+import createAddMarkerSelect from './connect/marker';
+import { typeConfluence } from '../../config/variableName';
+import { createLine } from 'diagram-js/lib/util/RenderUtil';
+import { append as svgAppend } from 'tiny-svg';
+import { hasGatewayType } from '../../config';
+let jnpfCanvas: any;
+let defaultStrokeColor = '#A2B9D5';
+let jnpfFlowInfo: any;
+class YmRenderer extends BpmnRenderer {
+  _styles: any;
+  _injector: any;
+  constructor(config: any, injector: any, eventBus: any, styles: any, pathMap: any, canvas: any, textRenderer: any, flowInfo: any, priority: number) {
+    super(
+      (config = {
+        defaultLabelColor: 'rgb(102,102,102)',
+        defaultStrokeColor: defaultStrokeColor,
+        ...config,
+      }),
+      eventBus,
+      styles,
+      pathMap,
+      canvas,
+      textRenderer,
+      priority,
+    );
+    this._styles = styles;
+    jnpfCanvas = canvas;
+    jnpfFlowInfo = flowInfo;
+    this._injector = injector;
+  }
+
+  canRender(element: any) {
+    return super.canRender(element);
+  }
+
+  // 绘制画布上元素
+  drawShape(parentNode: any, element: any) {
+    if (element) return CustomizeRenderer(parentNode, element, jnpfFlowInfo, this._injector) || super.drawShape(parentNode, element);
+    return super.drawShape(parentNode, element);
+  }
+
+  drawConnection(parentGfx: any, element: any) {
+    let stroke = '';
+    if (
+      element.target?.wnType === typeConfluence ||
+      (element.isPreview && hasGatewayType.has(element.target.wnType)) ||
+      (element.isPreview && hasGatewayType.has(element.source.wnType))
+    ) {
+      function lineStyle(_styles, attrs) {
+        return _styles.computeStyle(attrs, ['no-fill'], {
+          strokeLinecap: 'round',
+          strokeLinejoin: 'round',
+          stroke: defaultStrokeColor,
+          strokeWidth: 2,
+        });
+      }
+      function drawLine(parentGfx, waypoints, attrs, radius, _styles) {
+        attrs = lineStyle(_styles, attrs);
+        var line = createLine(waypoints, attrs, radius);
+        svgAppend(parentGfx, line);
+        return line;
+      }
+      if (element.isPreview && element.waypoints?.length > 1) {
+        if (element.waypoints.length === 2) {
+          element.waypoints = [
+            {
+              x: element.waypoints[0].x,
+              y: element.waypoints[0].y,
+            },
+            {
+              x: element.waypoints[1].x,
+              y: element.waypoints[1].y + 15,
+            },
+          ];
+        } else {
+          element.waypoints = [
+            {
+              x: element.waypoints[0].x > element.waypoints[1].x ? element.waypoints[0].x + 45 : element.waypoints[0].x - 45,
+              y: element.waypoints[0].y,
+            },
+            {
+              x: element.waypoints[1].x,
+              y: element.waypoints[1].y,
+            },
+            {
+              x: element.waypoints[2].x,
+              y: element.waypoints[2].y,
+            },
+          ];
+        }
+      }
+      var connection = drawLine(
+        parentGfx,
+        element.waypoints,
+        {
+          markerEnd: '',
+          stroke: stroke || defaultStrokeColor,
+        },
+        5,
+        this._styles,
+      );
+      return connection;
+    } else {
+      let connect = super.drawConnection(parentGfx, element, { stroke });
+      createAddMarkerSelect(element, jnpfCanvas); // 选中线的箭头
+      return connect;
+    }
+  }
+  // 绘制
+  getShapePath(shape: any) {
+    return getRectPath(shape);
+  }
+}
+YmRenderer.$inject = ['config.bpmnRenderer', 'injector', 'eventBus', 'styles', 'pathMap', 'canvas', 'textRenderer', 'config.flowInfo'];
+
+export default YmRenderer;

+ 177 - 0
lib/taskModeler/contextPad/index.ts

@@ -0,0 +1,177 @@
+// @ts-nocheck 过滤ts校验
+import { attr as domAttr, query as domQuery, classes as domClasses, domify as domify, delegate as domDelegate, event as domEvent } from 'min-dom';
+import { assign, forEach, isArray } from 'min-dash';
+import { escapeCSS } from 'diagram-js/lib/util/EscapeUtil';
+import contextPad from 'diagram-js/lib/features/context-pad/ContextPad';
+
+var entrySelector = '.entry';
+
+function addClasses(element: any, classNames: any) {
+  var classes = domClasses(element);
+  classNames = isArray(classNames) ? classNames : classNames.split(/\s+/g);
+  classNames.forEach(function (cls: any) {
+    classes.add(cls);
+  });
+}
+
+class YmContextPad extends contextPad {
+  constructor(canvas: any, config: any, eventBus: any, overlays: any) {
+    super(canvas, config, eventBus, overlays);
+  }
+  // 打开节点右侧面板
+  open(target: any, force: any) {
+    if (!force && this.isOpen(target)) return;
+    this.close();
+    this._updateAndOpen(target);
+  }
+  isShown() {
+    return this.isOpen();
+  }
+  arrowOpen(target: any) {
+    var entries = this.getEntries(target),
+      arrow = this.getArrow(target),
+      html = arrow.html;
+
+    let container = domify('<div class="rightArrow" id="rightArrow"></div>');
+    html.appendChild(container);
+    domClasses(html).add('open');
+
+    this._current = {
+      target: target,
+      entries: entries,
+      pad: arrow,
+    };
+    this._eventBus.fire('contextPad.open', { current: this._current });
+  }
+  getArrow(target: any) {
+    if (this.isOpen()) {
+      return this._current.pad;
+    }
+    var self = this;
+    var overlays = this._overlays;
+    var html = domify('<div class="ymArrow"></div>');
+    var position = this._getPosition(target);
+    domDelegate.bind(html, entrySelector, 'click', function (event) {
+      self.trigger('click', event);
+    });
+    let newPosition = {
+      position: {
+        top: position.position.top + target.height / 2,
+        left: position.position.left,
+      },
+    };
+
+    var overlaysConfig = assign({ html: html }, this._overlaysConfig, newPosition);
+    var overlaysConfig1 = assign({ html: html }, this._overlaysConfig, position);
+
+    domDelegate.bind(html, '.rightArrow', 'click', function (event) {
+      self._updateAndOpen(target);
+      self.trigger('click', event);
+    });
+
+    domDelegate.bind(html, entrySelector, 'dragstart', function (event) {
+      self.trigger('dragstart', event);
+    });
+
+    // stop propagation of mouse events
+    domEvent.bind(html, 'mousedown', function (event: any) {
+      event.stopPropagation();
+    });
+    var activeRootElement = this._canvas.getRootElement();
+    this._overlayId = overlays.add(activeRootElement, 'context-pad', overlaysConfig);
+    var arrow = overlays.get(this._overlayId);
+    this._eventBus.fire('contextPad.create', { target: target, pad: arrow });
+
+    return arrow;
+  }
+  getPad(target: any) {
+    if (this.isOpen()) return this._current.pad;
+    var self = this;
+    var overlays = this._overlays;
+    var html = domify('<div class="djs-context-pad"></div>');
+    var position = this._getPosition(target);
+    var overlaysConfig = assign(
+      {
+        html: html,
+      },
+      this._overlaysConfig,
+      position,
+    );
+
+    domDelegate.bind(html, entrySelector, 'click', function (event) {
+      self.trigger('click', event);
+    });
+
+    domDelegate.bind(html, entrySelector, 'dragstart', function (event) {
+      self.trigger('dragstart', event);
+    });
+
+    // stop propagation of mouse events
+    domEvent.bind(html, 'mousedown', function (event: any) {
+      event.stopPropagation();
+    });
+
+    var activeRootElement = this._canvas.getRootElement();
+
+    this._overlayId = overlays?.add(activeRootElement, 'context-pad', overlaysConfig);
+
+    var pad = overlays.get(this._overlayId);
+
+    this._eventBus.fire('contextPad.create', {
+      target: target,
+      pad: pad,
+    });
+
+    return pad;
+  }
+  // 重写父类contextPad样式
+  _updateAndOpen(target: any) {
+    var entries = this.getEntries(target),
+      pad = this.getPad(target),
+      html = pad.html,
+      image;
+    forEach(entries, (entry: any, id: any) => {
+      let textClass = ['svgText'];
+      let entryClass = ['entry'];
+      if (!entry?.disable && String(entry?.disable) === 'false') {
+        textClass = ['svgText-disabled'];
+        entryClass = ['entry-disabled'];
+      }
+      let grouping = 'default',
+        control = domify(entry.html || `<div class="${entryClass}""  draggable="true"></div>`),
+        container;
+      let svg = domify(entry.html || `<span class="${textClass}" >${entry.title}</span>`);
+
+      control.appendChild(svg);
+      domAttr(control, 'data-action', id);
+      container = domQuery('[data-group=' + escapeCSS(grouping) + ']', html);
+      if (!container) {
+        container = domify('<div class="group"></div>');
+        domAttr(container, 'data-group', grouping);
+        html.appendChild(container);
+      }
+
+      container.appendChild(control);
+
+      if (entry.className) addClasses(control, entry.className);
+      if (entry.title) domAttr(control, 'title', entry.title);
+      if (entry.imageUrl) {
+        image = domify('<img>');
+        domAttr(image, 'src', entry.imageUrl);
+        image.style.width = '100%';
+        image.style.height = '100%';
+        control.appendChild(image);
+      }
+    });
+    domClasses(html).add('open');
+
+    this._current = {
+      target: target,
+      entries: entries,
+      pad: pad,
+    };
+    this._eventBus.fire('contextPad.open', { current: this._current });
+  }
+}
+YmContextPad.$inject = ['canvas', 'config.contextPad', 'eventBus', 'overlays'];
+export default YmContextPad;

+ 221 - 0
lib/taskModeler/contextPad/provider/CustomizeContextPad.ts

@@ -0,0 +1,221 @@
+import { changeTypeByTaskShape, changeTypeByTrigger, hasGatewayType, triggerTypeChange, typeConfig } from '../../../config';
+import { jnpfConfigBpmnContextPad } from '../../../config/contextPad';
+import {
+  bpmnEnd,
+  bpmnTask,
+  bpmnSubFlow,
+  typeEnd,
+  typeTask,
+  typeSubFlow,
+  bpmnStart,
+  bpmnTrigger,
+  typeTrigger,
+  bpmnSequenceFlow,
+  typeCopy,
+  typeOutside,
+} from '../../../config/variableName';
+import { jnpfApproverConfig } from '../../../config/element/approver';
+import { jnpfSubFlowConfig } from '../../../config/element/subFlow';
+import { jnpfEndConfig } from '../../../config/element/end';
+import { jnpfTriggerConfig } from '../../../config/element/trigger';
+import { jnpfMessageConfig } from '../../../config/element/execute/message';
+import { cloneDeep } from 'lodash-es';
+
+const CustomizeContextPad = (contextPadProvider: any, element: any) => {
+  let type = element.type;
+  let wnType = element.wnType;
+  let isAction = true;
+  if (wnType === bpmnTrigger) type = bpmnTrigger;
+  if (changeTypeByTrigger[element.wnType]) type = changeTypeByTrigger[wnType];
+  if (changeTypeByTaskShape[wnType]) type = changeTypeByTaskShape[wnType];
+  if (typeConfig[type]) {
+    const {
+      _autoPlace: autoPlace,
+      _create: create,
+      _elementFactory: elementFactory,
+      _modeling: modeling,
+      _connect: connects,
+      _injector: injector,
+      _eventBus: eventBus,
+    } = contextPadProvider;
+    const { contextPad, shapeType } = typeConfig[type];
+    const { connect, end, approver, subFlow, del, trigger } = jnpfConfigBpmnContextPad;
+    // 根据类型 判断contextPad
+    if (type === shapeType) {
+      if (contextPad) {
+        if (contextPad.default) {
+          return defaultContextPad;
+        } else if (contextPad.customization) {
+          let customization: any = cloneDeep(type === bpmnStart ? contextPad.taskCustomization : contextPad.customization);
+          if (changeTypeByTaskShape[type]) customization[end] = end;
+          let result: any = {};
+          for (let key of Object.keys(customization)) {
+            let data = customization[key];
+            if (data.group === 'model') {
+              let options: any = {
+                wnName: typeConfig[key]?.renderer.rendererName,
+              };
+              if (element.type === bpmnStart && hasGatewayType.has(key)) {
+                // 开始节点只有分类节点 因为网关的分流节点和合流节点类型一致 多增加一个字段来表示
+                options = {
+                  wnName: typeConfig[key]?.renderer.rendererName,
+                  // wnGatewayType: typeGateway,
+                  wnType: key,
+                  icon: data.icon,
+                };
+              }
+              result[data.name] = appendAction(data.type, data.elementName, data.className, data.title, data.wnType, options);
+            } else if (data.group === 'connect') {
+              result[data.name] = {
+                group: data.group,
+                className: data.className,
+                title: data.title,
+                action: {
+                  click: startConnect,
+                  dragstart: startConnect,
+                },
+              };
+            } else if (data.group === 'edit') {
+              result[data.name] = {
+                group: data.group,
+                className: data.className,
+                title: data.title,
+                action: {
+                  click: removeElement,
+                },
+              };
+            }
+          }
+          return Object.assign(result);
+        } else return defaultContextPad();
+      }
+
+      // 单个节点删除功能
+      function removeElement() {
+        if (element.type === bpmnSequenceFlow) {
+          modeling.removeElements([element]);
+        } else {
+          eventBus.fire('commandStack.canExecute', {
+            command: 'shape.delete',
+            context: {
+              shape: element,
+            },
+          });
+        }
+      }
+      // 开始连线(拖拽)
+      function startConnect(event: any, element: any) {
+        connects.start(event, element);
+      }
+      // 添加事件
+      function appendAction(type: any, name: any, className: any, title: any, wnType: any, options?: any) {
+        const appendStart = (event: any, element: any) => {
+          let bpmnFactory = elementFactory._bpmnFactory;
+          if (type === typeCopy) {
+            let businessObject = bpmnFactory.create(element.type);
+            let shape = elementFactory.createShape(Object.assign({ type: element.type, name: element.name, wnType: element.name, ...options }, businessObject));
+            create.start(event, shape, { source: element });
+            // 复制属性
+            let jnpfData = injector.get('jnpfData');
+            let data = jnpfData.getValue(element.id);
+            jnpfData.setValue(shape.id, { ...data, nodeId: shape.id });
+          } else {
+            if ([typeOutside, typeSubFlow].includes(type)) type = bpmnTask;
+            let businessObject = bpmnFactory.create(type);
+            let shape = elementFactory.createShape(Object.assign({ type, name, wnType, ...options }, businessObject));
+            create.start(event, shape, { source: element });
+          }
+        };
+        const autoPlaceAppend = async (_event: any, element: any) => {
+          if ([typeOutside, typeSubFlow, typeTrigger].includes(type) || triggerTypeChange[type] || changeTypeByTaskShape[wnType]) type = bpmnTask;
+          let bpmnFactory = elementFactory._bpmnFactory;
+          // 复制元素
+          if (wnType === typeCopy) {
+            let businessObject = bpmnFactory.create(element.type);
+            let canvas = injector.get('canvas');
+            let rootElement = canvas.getRootElement();
+            let position = { x: element.x + 100, y: element.y + 200 };
+            let shape = elementFactory.createShape(Object.assign({ type: element.type, name: name, businessObject, wnType: element.wnType }, options));
+            modeling.createShape(shape, position, rootElement);
+            // 复制属性
+            let jnpfData = injector.get('jnpfData');
+            let data = jnpfData.getValue(element.id);
+            jnpfData.setValue(shape.id, { ...data, nodeId: shape.id });
+            let shapeData = jnpfData.getValue(shape.id);
+            shape['nodeName'] = shapeData.nodeName;
+            shape['elementBodyName'] = shapeData.content;
+            modeling.updateProperties(shape, {});
+            const selection: any = injector.get('selection');
+            selection.select(shape);
+          } else {
+            let businessObject = bpmnFactory.create(type);
+            // 任务流程只有一个触发节点
+            let triggerShape = injector
+              .get('elementRegistry')
+              .getAll()
+              .find(element => changeTypeByTrigger[element.wnType]);
+            if (triggerShape && changeTypeByTrigger[wnType]) {
+              let { renderer } = typeConfig[changeTypeByTrigger[wnType]];
+              triggerShape.wnType = wnType;
+              triggerShape.elementBodyName = renderer?.bodyDefaultText;
+              triggerShape.nodeName = renderer?.rendererName;
+              injector.get('jnpfData').setValue(element.id, {}, false);
+              modeling.updateProperties(triggerShape, {});
+            } else {
+              let shape = elementFactory.createShape(Object.assign({ type, name: name, businessObject, wnType: wnType }, options));
+              autoPlace.append(element, shape);
+            }
+          }
+        };
+        var append = autoPlace ? autoPlaceAppend : appendStart;
+        let disable = isAction;
+        let triggerShape = injector
+          .get('elementRegistry')
+          .getAll()
+          .find(element => changeTypeByTrigger[element.wnType]);
+        if (triggerShape && changeTypeByTrigger[wnType]) disable = false;
+
+        return {
+          group: 'model',
+          className: className,
+          title: title,
+          disable: disable,
+          action: { dragstart: disable && appendStart, click: disable && append },
+        };
+      }
+      // 默认contextPad
+      function defaultContextPad() {
+        return Object.assign({
+          [approver.name]: appendAction(bpmnTask, typeTask, approver.className, approver.title, typeTask, { wnName: jnpfApproverConfig.renderer.rendererName }),
+          [subFlow.name]: appendAction(bpmnTask, bpmnSubFlow, subFlow.className, subFlow.title, typeSubFlow, {
+            wnName: jnpfSubFlowConfig.renderer.rendererName,
+          }),
+          [trigger.name]: appendAction(bpmnTrigger, bpmnTrigger, trigger.className, trigger.title, typeTrigger, {
+            wnName: jnpfTriggerConfig.renderer.rendererName,
+          }),
+          [end.name]: appendAction(bpmnEnd, typeEnd, end.className, end.title, typeEnd, { wnName: jnpfEndConfig.renderer.rendererName }),
+          [connect.name]: {
+            group: connect.group,
+            className: connect.className,
+            title: connect.title,
+            action: {
+              click: startConnect,
+              dragstart: startConnect,
+            },
+          },
+          [del.name]: {
+            group: del.group,
+            className: del.className,
+            title: del.title,
+            action: {
+              click: removeElement,
+            },
+          },
+        });
+      }
+    }
+    return undefined;
+  }
+  return undefined;
+};
+export default CustomizeContextPad;

+ 85 - 0
lib/taskModeler/contextPad/provider/index.ts

@@ -0,0 +1,85 @@
+import ContextPadProvider from 'bpmn-js/lib/features/context-pad/ContextPadProvider';
+import { jnpfConfigBpmnContextPad } from '../../../config/contextPad';
+import CustomizeContextPad from './CustomizeContextPad';
+class YmContextPadProvider extends ContextPadProvider {
+  _modeling: any;
+  _rules: any;
+  _injector: any;
+  _eventBus: any;
+  _canvas: any;
+  constructor(
+    config: any,
+    injector: any,
+    eventBus: any,
+    contextPad: any,
+    modeling: any,
+    elementFactory: any,
+    connect: any,
+    create: any,
+    popupMenu: any,
+    canvas: any,
+    rules: any,
+    translate: any,
+  ) {
+    super(config, injector, eventBus, contextPad, modeling, elementFactory, connect, create, popupMenu, canvas, rules, translate);
+    this._rules = rules;
+    this._modeling = modeling;
+    this._injector = injector;
+    this._eventBus = eventBus;
+    this._canvas = canvas;
+  }
+
+  getContextPadEntries(element: any): (() => any) | any | undefined {
+    return CustomizeContextPad(this, element);
+  }
+
+  // 多个元素框选时 默认包含框选删除元素
+  getMultiElementContextPadEntries(element: any) {
+    var actions = {};
+    const { del } = jnpfConfigBpmnContextPad;
+    Object.assign(actions, {
+      delete: {
+        group: 'edit',
+        className: del.className,
+        title: del.title,
+        action: {
+          click: (_event: any, elements: any) => {
+            //判断存在开始节点给出不能删除的提示
+            const hasStartElement = elements.some(o => o.type == 'bpmn:StartEvent');
+            hasStartElement &&
+              this._eventBus.fire('custom.message', {
+                context: '无法删除开始节点',
+                messageType: 'warning',
+              });
+            //过滤开始节点后删除选择的节点
+            const newElements = elements.filter(o => o.type != 'bpmn:StartEvent');
+            this._eventBus.fire('commandStack.canExecute', {
+              command: 'elements.delete',
+              context: {
+                elements: newElements,
+              },
+            });
+          },
+        },
+      },
+    });
+    return actions;
+  }
+}
+
+YmContextPadProvider.$inject = [
+  'config.contextPad',
+  'injector',
+  'eventBus',
+  'contextPad',
+  'modeling',
+  'elementFactory',
+  'connect',
+  'create',
+  'popupMenu',
+  'canvas',
+  'rules',
+  'translate',
+];
+
+export default YmContextPadProvider;

+ 64 - 0
lib/taskModeler/modeler/index.ts

@@ -0,0 +1,64 @@
+import Modeler from 'bpmn-js/lib/Modeler';
+import jnpfPaletteProvider from '../../palette';
+import jnpfRenderer from '../../taskModeler/renderer';
+import jnpfElementFactory from '../../factory';
+import jnpfOutline from '../../outline';
+import jnpfBusinessData from '../../business';
+import jnpfGridSnappingAutoPlaceBehavior from '../../gridSnapping';
+import jnpfAlignElementsContextPadProvider from '../../alignElements';
+import jnpfContextPad from '../../taskModeler/contextPad';
+import jnpfContextPadProvider from '../../taskModeler/contextPad/provider';
+import jnpfCustomBpmnRules from '../../rule';
+import jnpfCommandStack from '../../commandStack';
+import jnpfCustomBpmnCopyPaste from '../../copyPaste';
+import GridSnappingLayoutConnectionBehavior from '../../gridSnapping/connect';
+let flowInfo: any;
+let flowType: any;
+// 任务流程
+const modeler: any = options => [
+  {
+    __init__: [
+      'paletteProvider',
+      'bpmnRenderer',
+      'contextPadProvider',
+      'replaceMenuProvider',
+      'elementFactory',
+      'jnpfData',
+      'gridSnappingAutoPlaceBehavior',
+      'alignElementsContextPadProvider',
+      'alignElementsMenuProvider',
+      'bpmnAlignElements',
+      'outlineProvider',
+      'contextPad',
+      'bpmnRules',
+      'bpmnCopyPaste',
+    ],
+    paletteProvider: ['type', jnpfPaletteProvider], // 左侧的元素 目前不用该方法
+    bpmnRenderer: ['type', jnpfRenderer, { options }], // 画布渲染
+    elementFactory: ['type', jnpfElementFactory], // 元素工厂
+    jnpfData: ['type', jnpfBusinessData], // 用于放置业务数据
+    gridSnappingAutoPlaceBehavior: ['type', jnpfGridSnappingAutoPlaceBehavior], // 自动生成元素位置 在点击coontext-pad时计算元素生成位置
+    alignElementsContextPadProvider: ['type', jnpfAlignElementsContextPadProvider], // 元素的排序等
+    outlineProvider: ['type', jnpfOutline], // 元素的外边框(用于修改边框颜色,注:线条颜色有svg获取标签再去修改颜色及箭头)
+    contextPad: ['type', jnpfContextPad], // 点击元素后的元素右侧弹窗框(显示开始节点 结束节点等)
+    contextPadProvider: ['type', jnpfContextPadProvider], // context-pad 属性
+    bpmnRules: ['type', jnpfCustomBpmnRules, flowType], // 自定义规则
+    commandStack: ['type', jnpfCommandStack], // 自定义CommandStack
+    gridSnappingLayoutConnectionBehavior: ['type', GridSnappingLayoutConnectionBehavior], // 修改连线的排序
+    bpmnCopyPaste: ['type', jnpfCustomBpmnCopyPaste], // 复制元素
+  },
+];
+
+class bpmnModeler extends Modeler {
+  constructor(options: any) {
+    flowInfo = options.flowInfo;
+    flowType = options.type;
+    super(options);
+  }
+}
+
+bpmnModeler.prototype['_modules'] = []
+  .concat(bpmnModeler.prototype['_modules'], modeler(flowInfo))
+  .concat(bpmnModeler.prototype['_modules'], modeler(flowType));
+
+export default bpmnModeler;

+ 88 - 0
lib/taskModeler/renderer/CustomizeRenderer.ts

@@ -0,0 +1,88 @@
+import { changeTypeByTaskShape, changeTypeByTrigger, typeConfig } from '../../config';
+import { append as svgAppend, create as svgCreate } from 'tiny-svg';
+import { bpmnEnd, bpmnGroup, bpmnStart } from '../../config/variableName';
+
+/**
+ * svg重画bpmn节点
+ */
+export default (parentNode: any, element: any, jnpfFlowInfo: any) => {
+  let data = jnpfFlowInfo?.flowNodes[element.id];
+  let nodeMap = jnpfFlowInfo?.nodeList;
+  let isPreview = jnpfFlowInfo?.isPreview;
+  let type = element.type; // 获取到类型;
+  if (typeConfig && typeConfig[type]) {
+    if (type === bpmnGroup) return null;
+    if (changeTypeByTrigger[element.wnType || data?.type]) type = changeTypeByTrigger[element.wnType || data?.type];
+    if (changeTypeByTaskShape[element.wnType || data?.type]) type = changeTypeByTaskShape[element.wnType || data?.type];
+    let { renderer } = typeConfig[type];
+    let { icon, iconColor, rendererName, background, titleColor, attr, bodyDefaultText } = renderer;
+    element['width'] = attr.width;
+    element['height'] = attr.height;
+    let nodeName = element.nodeName != null ? element.nodeName : data?.nodeName != null ? data.nodeName : rendererName;
+    let nodeContent = element.elementBodyName || nodeMap.get(element.id)?.userName || data?.content || bodyDefaultText;
+    if (element.elementBodyName === '') nodeContent = bodyDefaultText;
+    if (isPreview) {
+      if (nodeMap) {
+        if (nodeMap.get(element.id)?.type === '0') {
+          titleColor = 'linear-gradient(90deg, #AEEFC2 0%, #4ED587 100%)';
+          iconColor = '#25a210';
+        }
+        if (nodeMap.get(element.id)?.type === '1') {
+          titleColor = 'linear-gradient(90deg, #C0EDF8 0%, #A6DEF8 100%)';
+          iconColor = '#1eaceb';
+        }
+        if (nodeMap.get(element.id)?.type === '3') {
+          titleColor = 'linear-gradient(90deg, #FDC9D1 0%,#E03845 100%)';
+          iconColor = '#E03845';
+        }
+      } else {
+        titleColor = 'linear-gradient(90deg, #CED1D5 0%, #CBCBCC 100%);';
+        iconColor = '#4c4c58';
+      }
+    }
+    let foreignObject: any = svgCreate('foreignObject', {
+      width: attr.width,
+      height: attr.height,
+      class: type === bpmnStart || type === bpmnEnd ? 'begin-or-end-node' : 'task-node',
+    });
+    // 开始节点
+    if (type === bpmnStart) {
+      foreignObject.innerHTML = `
+      <div class="node-container start-node-container" style="background:${background}" >
+        <div class='node-top-container'>
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span title=${nodeName}>${nodeName}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+    // 任务节点
+    if (changeTypeByTaskShape[element.wnType || data?.type] || changeTypeByTrigger[element.wnType || data?.type]) {
+      foreignObject.innerHTML = `
+      <div class="node-container" style="background:${background}" >
+        <div class='node-top-container' style="background:${titleColor}">
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+        <div class='node-bottom-container'>
+          <span>${nodeContent}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+    // 结束节点
+    if (type === bpmnEnd) {
+      foreignObject.innerHTML = `
+      <div class="node-container end-node-container" style="background:${background}" >
+        <div class='node-top-container'>
+          <i class="${icon}" style="color:${iconColor}"></i>
+          <span>${nodeName}</span>
+        </div>
+      </div>`;
+      svgAppend(parentNode, foreignObject);
+      return parentNode;
+    }
+  }
+};

+ 61 - 0
lib/taskModeler/renderer/index.ts

@@ -0,0 +1,61 @@
+import BpmnRenderer from 'bpmn-js/lib/draw/BpmnRenderer';
+import CustomizeRenderer from './CustomizeRenderer';
+import { getRectPath } from 'bpmn-js/lib/draw/BpmnRenderUtil';
+import createAddMarkerSelect from '../../simpleModeler/renderer/connect/marker';
+let jnpfCanvas: any;
+let jnpfFlowInfo: any;
+class YmRenderer extends BpmnRenderer {
+  constructor(config: any, eventBus: any, styles: any, pathMap: any, canvas: any, textRenderer: any, flowInfo: any, priority: number) {
+    super(
+      (config = {
+        defaultLabelColor: 'rgb(102,102,102)',
+        defaultStrokeColor: '#A2B9D5',
+        ...config,
+      }),
+      eventBus,
+      styles,
+      pathMap,
+      canvas,
+      textRenderer,
+      priority,
+    );
+    jnpfCanvas = canvas;
+    jnpfFlowInfo = flowInfo;
+  }
+
+  canRender(element: any) {
+    return super.canRender(element);
+  }
+  // 绘制画布上元素
+  drawShape(parentNode: any, element: any) {
+    if (element) return CustomizeRenderer(parentNode, element, jnpfFlowInfo) || super.drawShape(parentNode, element);
+    return super.drawShape(parentNode, element);
+  }
+
+  drawConnection(parentGfx: any, element: any) {
+    let source = element.source;
+    let target = element.target;
+    let nodeMap = jnpfFlowInfo?.nodeList;
+    let stroke = '';
+    if (nodeMap.has(source?.id) && nodeMap.has(target?.id)) {
+      if (nodeMap.get(source.id)?.type === '0' && nodeMap.get(target.id)?.type === '0') {
+        stroke = '#4ED587';
+      }
+      if (nodeMap.get(source.id)?.type === '0' && nodeMap.get(target.id)?.type === '1') {
+        stroke = '#1eaceb';
+      }
+    }
+    let connect = super.drawConnection(parentGfx, element, { stroke });
+    createAddMarkerSelect(element, jnpfCanvas);
+    return connect;
+  }
+
+  // 绘制
+  getShapePath(shape: any) {
+    return getRectPath(shape);
+  }
+}
+
+YmRenderer.$inject = ['config.bpmnRenderer', 'eventBus', 'styles', 'pathMap', 'canvas', 'textRenderer', 'config.flowInfo'];
+
+export default YmRenderer;

+ 240 - 0
lib/translate/cn.ts

@@ -0,0 +1,240 @@
+export default {
+  '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: '循环',
+  'Ad-hoc': '即席',
+  'Create {type}': '创建 {type}',
+  'Create Task': '创建任务',
+  'Create StartEvent': '创建开始事件',
+  'Create EndEvent': '创建结束事件',
+  'Create Group': '创建组',
+  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': '创建网关',
+  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 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': '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: '值',
+  Add: '添加',
+  Values: '值',
+  'Add 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': '跟踪日期',
+  Priority: '优先级',
+  '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',
+  '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 Configuration': '候选开始配置',
+  'Task Listener': '任务监听器',
+  'Candidate Starter Groups': '候选开始组',
+  'Candidate Starter Users': '候选开始用户',
+  'Tasklist Configuration': '任务列表配置',
+  Startable: '启动',
+  'Specify more than one group as a comma separated list.': '指定多个组,用逗号分隔',
+  'Specify more than one user as a comma separated list.': '指定多个用户,用逗号分隔',
+  'This maps to the process definition key.': '这会映射为流程定义的键',
+  'CallActivity Type': '调用活动类型',
+  'Condition Type': '条件类型',
+  'Create UserTask': '创建用户任务',
+  'Create CallActivity': '创建调用活动',
+  'Called Element': '调用元素',
+  'Create DataObjectReference': '创建数据对象引用',
+  'Create DataStoreReference': '创建数据存储引用',
+  'Multi Instance': '多实例',
+  'Loop Cardinality': '实例数量',
+  Collection: '任务参与人列表',
+  'Element Variable': '元素变量',
+  'Completion Condition': '完成条件',
+};

+ 15 - 0
lib/translate/index.ts

@@ -0,0 +1,15 @@
+import cn from './cn';
+
+
+export default function customTranslate(template: any, replacements: any) {
+  replacements = replacements || {};
+
+  // Translate
+  // @ts-ignore
+  template = cn[template] || template;
+
+  // Replace
+  return template.replace(/{([^}]+)}/g, function(_: any, key: any) {
+    return replacements[key] || '{' + key + '}';
+  });
+}

+ 58 - 0
lib/utils/DiUtil.ts

@@ -0,0 +1,58 @@
+import { is, getBusinessObject, getDi } from './modelUtil';
+
+export function isExpanded(element: any, di: any) {
+  if (is(element, 'bpmn:CallActivity')) {
+    return false;
+  }
+
+  if (is(element, 'bpmn:SubProcess')) {
+    di = di || getDi(element);
+
+    if (di && is(di, 'bpmndi:BPMNPlane')) {
+      return true;
+    }
+
+    return di && !!di.isExpanded;
+  }
+
+  if (is(element, 'bpmn:Participant')) {
+    return !!getBusinessObject(element).processRef;
+  }
+
+  return true;
+}
+
+export function isInterrupting(element: any) {
+  return element && getBusinessObject(element).isInterrupting !== false;
+}
+
+export function isEventSubProcess(element: any) {
+  return element && !!getBusinessObject(element).triggeredByEvent;
+}
+
+export function hasEventDefinition(element: any, eventType: any) {
+  var bo = getBusinessObject(element),
+    hasEventDefinition = false;
+
+  if (bo.eventDefinitions) {
+    bo.eventDefinitions.forEach(event => {
+      if (is(event, eventType)) {
+        hasEventDefinition = true;
+      }
+    });
+  }
+
+  return hasEventDefinition;
+}
+
+export function hasErrorEventDefinition(element: any) {
+  return hasEventDefinition(element, 'bpmn:ErrorEventDefinition');
+}
+
+export function hasEscalationEventDefinition(element: any) {
+  return hasEventDefinition(element, 'bpmn:EscalationEventDefinition');
+}
+
+export function hasCompensateEventDefinition(element: any) {
+  return hasEventDefinition(element, 'bpmn:CompensateEventDefinition');
+}

+ 1 - 0
lib/utils/bpmn.ts

@@ -0,0 +1 @@
+export { getExternalLabelMid } from 'bpmn-js/lib/util/LabelUtil';

+ 169 - 0
lib/utils/bpmnLintUtil.ts

@@ -0,0 +1,169 @@
+import ElementRegistry from 'diagram-js/lib/core/ElementRegistry';
+import { typeTask, typeTrigger } from '../config/variableName';
+import { changeTypeByTaskShape } from '../config';
+export class BpmnLintError {
+  public element: any;
+  public message: string;
+  constructor(element: any | any[], message: string) {
+    this.element = element;
+    this.message = message;
+  }
+}
+
+// 自定义bpmnLint规则
+abstract class BpmnLintBase {
+  protected registry: ElementRegistry;
+  protected root: any;
+  protected constructor(registry: ElementRegistry, root: any) {
+    this.registry = registry;
+    this.root = root;
+  }
+  public abstract validate(): boolean;
+  protected error: BpmnLintError | undefined;
+  public getError(): BpmnLintError | undefined {
+    return this.error;
+  }
+}
+/**
+ * xml元素校验
+ *  1.流程必须要有一个【开始,结束】节点 且只能有一个。
+ *  2.至少有一个【审批】节点,
+ *  3.分流网关和合流官网必须成对关系。
+ *  4.除了开始节点(只有出线)及结束节点(只有进线) 其它节点必须要含有出线和入线。
+ *  5.分流节点出线必须有多条出线,只有一条进线; 合流节点必须有多条进线,只能有一条出线。
+ *
+ */
+let jnpfProps: any;
+class FormValidator extends BpmnLintBase {
+  public constructor(registry: ElementRegistry, root: any, props: any) {
+    super(registry, root);
+    jnpfProps = props;
+  }
+  validate(): boolean {
+    let errorList: any = [];
+    let allElements = this.root.children;
+    if (allElements && allElements.length > 0) {
+      let newElementData = allElements.reduce((classified: any, element: any) => {
+        // 获取元素类型 (网关元素存在分流和合流 所以需要取 wnGatewayType)
+        let elementType = (element.type === 'bpmn:InclusiveGateway' ? element.wnGatewayType : element.type)?.replace('bpmn:', '')?.toLowerCase();
+        // 如果分类对象中不存在当前类型的数组,则创建一个新数组
+        if (!classified[elementType]) classified[elementType] = [];
+        // 将元素添加到相应类型的数组中
+        classified[elementType].push(element);
+        return classified;
+      }, {});
+      if (newElementData) {
+        if (!(newElementData.hasOwnProperty('startevent') && newElementData.hasOwnProperty('endevent') && newElementData.hasOwnProperty('usertask'))) {
+          this.error = new BpmnLintError(null, '确保画布上至少包含【开始-审批-结束】节点');
+          return false;
+        }
+        // 开始节点
+        if (newElementData['startevent']) {
+          if (newElementData['startevent'].length > 1) {
+            // 开始 结束节点只有拥有一个
+            this.error = new BpmnLintError(null, '画布上只能存在一个开始节点');
+            return false;
+          }
+          // 开始节点只能有一个出线
+          if (newElementData['startevent'][0]) {
+            let outLine = newElementData['startevent'][0].outgoing;
+            let inLine = newElementData['startevent'][0].incoming; // 进线
+            if (inLine && inLine.length > 0) {
+              errorList.push({
+                element: newElementData['startevent'][0],
+                error: '开始节点不存在进线,请删除多余线条!',
+              });
+            }
+            if (!outLine) {
+              errorList.push({
+                element: newElementData['startevent'][0],
+                error: '开始元素不能单独使用,请于其它元素进行连线使用!',
+              });
+            }
+          }
+        }
+        // 结束节点:
+        if (newElementData['endevent']) {
+          if (newElementData['endevent'].length > 1) {
+            this.error = new BpmnLintError(null, '画布上只能存在一个结束节点,请删除多余节点元素!');
+            return false;
+          }
+          // 结束节点只能有一个进线
+          if (newElementData['endevent'][0]) {
+            let outLine = newElementData['endevent'][0].outgoing;
+            let inLine = newElementData['endevent'][0].incoming; // 进线
+            if (outLine && outLine.length > 0) {
+              errorList.push({
+                element: newElementData['endevent'][0],
+                error: '结束节点不存在出线,请删除多余线条!',
+              });
+            }
+            if (!inLine) {
+              errorList.push({
+                element: newElementData['endevent'][0],
+                error: '结束元素不能单独使用,请于其它元素进行连线使用!',
+              });
+            }
+          }
+        }
+        // 网关
+        if (newElementData.hasOwnProperty('confluence') || newElementData.hasOwnProperty('divide')) {
+          if (!(newElementData['confluence'] && newElementData['confluence'].length && newElementData['divide'] && newElementData['divide'].length)) {
+            this.error = new BpmnLintError(null, '请您检查分流与合流是否成对使用!');
+            return false;
+          }
+        }
+        // 任务节点
+        if (newElementData['usertask']) {
+          // 遍历进线元素 必须有一条进线及出线元素
+          let userTaskList = newElementData['usertask'];
+          userTaskList.map((userTaskShape: any) => {
+            let outLine = userTaskShape.outgoing;
+            let inLine = userTaskShape.incoming; // 进线
+            // 任务节点必须要有一个进线和出线
+            if (!(outLine?.length && inLine?.length)) {
+              if (!changeTypeByTaskShape[userTaskShape?.wnType] || (jnpfProps.type === 2 && changeTypeByTaskShape[userTaskShape?.wnType])) {
+                errorList.push({
+                  element: userTaskShape,
+                  error: '元素不能单独使用,请确保连接至少一个执行节点',
+                });
+              } else if (changeTypeByTaskShape[userTaskShape?.wnType] && !inLine?.length) {
+                errorList.push({
+                  element: userTaskShape,
+                  error: '执行元素不能单独使用,请确保连接到触发节点',
+                });
+              }
+            }
+            if (userTaskShape?.wnType === typeTask) {
+              // 如果任务节点只有触发节点 则报错 需要连接其它节点或者结束节点。
+              if (outLine.every(s => s.target.wnType === typeTrigger))
+                errorList.push({
+                  element: userTaskShape,
+                  error: '审批元素不能只包含触发节点, 请确保至少有一条出线与其他元素相连接',
+                });
+            }
+          });
+        }
+        if (errorList && errorList.length > 0) {
+          this.error = new BpmnLintError(errorList, errorList[0].error);
+          return false;
+        }
+      }
+    } else {
+      this.error = new BpmnLintError(null, '画布上不存在元素,至少包含【开始-审批-结束】节点');
+      return false;
+    }
+    return true;
+  }
+}
+
+export function validate(registry: ElementRegistry, root: any, props: any): BpmnLintError | undefined {
+  /***/
+  const validators = [new FormValidator(registry, root, props)];
+
+  for (let validator of validators) {
+    if (!validator.validate()) {
+      return validator.getError();
+    }
+  }
+}

+ 768 - 0
lib/utils/constructTreeUtil.ts

@@ -0,0 +1,768 @@
+import { bpmnStart, bpmnTask, typeConfluence, typeTrigger } from '../config/variableName';
+import { NodeUtils } from './nodeUtil';
+import { changeTypeByTaskShape, hasGatewayType, typeConfig } from '../config';
+import { DEFAULT_CONNECT, DEFAULT_DISTANCE } from '../config/constants';
+// 定义 TreeNode 接口,表示树状数据结构中的节点
+export interface TreeNode {
+  id: string;
+  name: string;
+  type: string;
+  wnType: string;
+  children: TreeNode[];
+  virtualWidth: number; // 虚拟宽度(纵向排布)
+  virtualHeight: number; // 虚拟高度(横向排布)
+  isGateway?: boolean;
+  level?: number;
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+  parentData?: any;
+  offset?: any;
+  subTree?: TreeNode; // 引用子树
+}
+
+type direction = 'vertical' | 'horizontal';
+// 封装为工具类来处理树状数据结构
+
+export class BPMNTreeBuilder {
+  _allElement: any;
+  _connectMap: any;
+
+  constructor(allElement: any) {
+    this._allElement = allElement;
+    this._connectMap = new Map();
+  }
+  // 检查并添加不重复的子节点
+  private addUniqueChild(parent: TreeNode, child: TreeNode): void {
+    if (!parent.children.some(c => c.id === child.id)) parent.children.push(child);
+  }
+  public findStartElement(obj) {
+    // 获取对象的键并遍历
+    for (const key in obj) {
+      if (obj.hasOwnProperty(key)) {
+        const item = obj[key];
+        if (item.type === bpmnStart) return item;
+      }
+    }
+    return null; // 如果没有找到符合条件的对象,返回 null
+  }
+  public constructTree(treeType?: number): TreeNode {
+    let startNode: any = this.findStartElement(this._allElement);
+    if (!startNode) throw new Error('开始节点未找到');
+    // 创建根节点
+    const rootNode: TreeNode = {
+      id: startNode.id,
+      name: (startNode.businessObject as any).name,
+      type: startNode.type,
+      wnType: startNode.wnType,
+      children: [],
+      virtualWidth: 320,
+      virtualHeight: DEFAULT_DISTANCE,
+      level: 0,
+      x: startNode.x,
+      y: startNode.y,
+      width: startNode.width,
+      height: startNode.height,
+    };
+    // 构建连接关系
+    let connections: Record<string, TreeNode[]> = {};
+    let newConnectsMap = new Map();
+    for (let key in this._allElement) {
+      let element = this._allElement[key];
+      // 如果某个节点有多个进线元素并且这些进线元素还有其它不同的出线元素 则 该元素的进线线条存在交叉的连接
+      if (element.incoming?.length > 1) {
+        let connectMap = new Map(); // 作用是过滤掉一些交叉的线条影响排序的样式
+        element.incoming.map((item: any) => {
+          if (!connectMap.has(item.id)) {
+            let connectId = item.id;
+            // 获取该元素的父元素 如果其父元素有多个子元素,则该connectId线条有出现交叉连接的情况
+            let parentELement = item.source;
+            if (parentELement.outgoing?.length > 1) {
+              parentELement.outgoing.map((connect: any) => {
+                if (!connectMap.has(connect.id)) {
+                  connectMap.set(connectId, parentELement.outgoing.length || 0);
+                }
+              });
+            }
+          }
+        });
+        if (treeType != 1) {
+          // 过滤简流中的使用
+          if (connectMap.size > 0) {
+            if (connectMap.size === element.incoming?.length) {
+              // 删除value最小的
+              let minKey = null;
+              let minValue = Infinity;
+              for (let [key, value] of connectMap) {
+                if (value < minValue) {
+                  minValue = value;
+                  minKey = key;
+                }
+              }
+              // 如果找到了最小值的键,则删除该键值对
+              if (minKey !== null) {
+                connectMap.delete(minKey);
+              }
+            }
+          }
+          connectMap.forEach((value, key) => {
+            newConnectsMap.set(key, value);
+          });
+        }
+      }
+    }
+    for (let key in this._allElement) {
+      let element = this._allElement[key];
+      if (element.type === 'bpmn:SequenceFlow' && !newConnectsMap.has(element.id)) {
+        let sourceId = element.source.id;
+        let targetId = element.target.id;
+        if (!connections[sourceId]) connections[sourceId] = [];
+        let targetElement = element.target;
+        let childNode: TreeNode = {
+          id: targetId,
+          name: targetElement.name,
+          type: targetElement.type,
+          wnType: targetElement.wnType,
+          children: [],
+          virtualWidth: 320,
+          virtualHeight: DEFAULT_DISTANCE,
+          x: targetElement.x,
+          y: targetElement.y,
+          width: targetElement.width,
+          height: targetElement.height,
+        };
+        if (!connections[sourceId].some(child => child.id === childNode.id)) connections[sourceId].push(childNode);
+      }
+    }
+    // 使用广度优先遍历构建树
+    let queue: TreeNode[] = [rootNode];
+    let processedNodes = new Map(); // 跟踪处理过的节点
+    while (queue.length > 0) {
+      let current = queue.shift();
+      if (current && connections[current.id]) {
+        connections[current.id].forEach(child => {
+          child.parentData = current;
+          if (!processedNodes.has(child.id)) {
+            this.addUniqueChild(current, child); // 添加到父节点
+            queue.push(child); // 推入队列
+            processedNodes.set(child.id, child); // 标记为已处理
+          } else this.addUniqueChild(current, processedNodes.get(child.id)); // 添加到父节点
+        });
+      }
+    }
+    this._connectMap = newConnectsMap;
+    return rootNode;
+  }
+  /**
+   * 使用栈进行深度优先遍历,计算虚拟宽度
+   * @type 0:自研, 1:简流
+   *  */
+  public calculateVirtualWidth(root: TreeNode, elementRegistry: any): number {
+    // 栈存储的是节点和已计算的子节点总宽度
+    let stack: { node: TreeNode; totalWidth: number }[] = [];
+    stack.push({ node: root, totalWidth: 0 });
+    // 遍历过程中保存父子关系的映射
+    let parentChildMapping = new Map<TreeNode, TreeNode[]>();
+    while (stack.length > 0) {
+      let current = stack[stack.length - 1];
+      let { node, totalWidth } = current;
+      // 如果节点没有子节点,则直接设置默认宽度
+      if (node.children.length === 0) {
+        node.virtualWidth = 320;
+        stack.pop(); // 从栈中移除
+        this.updateParent(stack, node.virtualWidth!, 'horizontal');
+        continue;
+      }
+      // 如果子节点还没有完全遍历,则将子节点压入栈
+      let children = node.children;
+      let unprocessedChildren = parentChildMapping.get(node);
+      if (!unprocessedChildren) {
+        unprocessedChildren = [...children]; // 克隆子节点列表
+        parentChildMapping.set(node, unprocessedChildren);
+      }
+      if (unprocessedChildren.length > 0) {
+        let child = unprocessedChildren.pop(); // 取出一个未处理的子节点
+        stack.push({ node: child!, totalWidth: 0 });
+      } else {
+        // 所有子节点都已处理完毕,计算虚拟宽度
+        let finalWidth = totalWidth;
+        let hasMergeChild = children.some(child => child.wnType === typeConfluence);
+        let hasTrigger = children.some(child => child.wnType === typeTrigger);
+        if (hasMergeChild && !hasTrigger) finalWidth = 320;
+        else {
+          let newElement = elementRegistry?.get(node.id);
+          finalWidth = children.reduce((sum, child) => {
+            let virtualWidth = child.virtualWidth ?? 0;
+            if (child.isGateway) virtualWidth = 320;
+            return sum + virtualWidth;
+          }, 0);
+          let hasTrigger = newElement.outgoing?.some(o => o.target.wnType === typeTrigger);
+          let isNotTriggerChildren = children?.some(o => o.wnType != typeTrigger);
+          if (hasTrigger && !isNotTriggerChildren) finalWidth += 320;
+        }
+        // 重新计算父元素为分流网关元素的虚拟宽度 该元素的虚拟宽度需要获取到该分流到合流内的所有分流虚拟宽度 取最大值(过滤chidren包含触发节点的元素)
+        if (hasGatewayType.has(node.parentData?.wnType) && !hasTrigger) {
+          // 辅助函数,用于递归遍历路径
+          let elements: any = new Map();
+          let number: any = 0; // 取最大值
+          function getChildrenMaxWidth(targetElement) {
+            elements.set(targetElement.id, targetElement.virtualWidth);
+            if (number < targetElement.virtualWidth && targetElement.wnType != typeConfluence) number = targetElement.virtualWidth;
+            if (targetElement.id !== node.parentData?.id + '_confluence') findPath(targetElement, false);
+          }
+          function findPath(currentElement, isRoot: boolean, id?) {
+            currentElement?.children.forEach(targetElement => {
+              if (isRoot) {
+                if (targetElement.id === id) getChildrenMaxWidth(targetElement);
+              } else getChildrenMaxWidth(targetElement);
+            });
+          }
+          findPath(node.parentData, true, node.id);
+          finalWidth = number;
+        }
+        // 如果该节点有多个incoming线 则 将该节点默认为是合流节点 将它宽度设置为320
+        if (elementRegistry) {
+          let currentElement = elementRegistry.get(node.id);
+          if (currentElement.incoming?.length > 1) node.isGateway = true;
+        }
+        node.virtualWidth = finalWidth;
+        stack.pop(); // 从栈中移除
+        this.updateParent(stack, finalWidth, 'horizontal');
+      }
+    }
+    return root.virtualWidth!;
+  }
+  public calculateVirtualHeight(root: TreeNode, elementRegistry?: any): number {
+    // 栈存储的是节点和已计算的子节点总高度
+    let stack: { node: TreeNode; totalHeight: number }[] = [];
+    stack.push({ node: root, totalHeight: 0 });
+    // 遍历过程中保存父子关系的映射
+    let parentChildMapping = new Map<TreeNode, TreeNode[]>();
+    while (stack.length > 0) {
+      let current = stack[stack.length - 1];
+      let { node, totalHeight } = current;
+      // 如果节点没有子节点,则直接设置默认高度
+      if (node.children.length === 0) {
+        node.virtualHeight = 118;
+        stack.pop(); // 从栈中移除
+        this.updateParent(stack, node.virtualHeight!, 'vertical');
+        continue;
+      }
+      // 如果子节点还没有完全遍历,则将子节点压入栈
+      let children = node.children;
+      let unprocessedChildren = parentChildMapping.get(node);
+      if (!unprocessedChildren) {
+        unprocessedChildren = [...children]; // 克隆子节点列表
+        parentChildMapping.set(node, unprocessedChildren);
+      }
+      if (unprocessedChildren.length > 0) {
+        let child = unprocessedChildren.pop(); // 取出一个未处理的子节点
+        stack.push({ node: child!, totalHeight: 0 });
+      } else {
+        // 所有子节点都已处理完毕,计算虚拟高度
+        let finalHeight = totalHeight;
+        let hasMergeChild = children.some(child => child.wnType === typeConfluence);
+        if (hasMergeChild) finalHeight = 208;
+        else
+          finalHeight = children.reduce((sum, child) => {
+            let virtualHeight = child.virtualHeight ?? 0;
+            if (child.isGateway) virtualHeight = 208;
+            return sum + virtualHeight;
+          }, 0);
+        // 重新计算父元素为分流网关元素的虚拟高度 该元素的虚拟高度需要获取到该分流到合流内的所有分流虚拟高度 取最大值
+        if (hasGatewayType.has(node.parentData?.wnType)) {
+          // 辅助函数,用于递归遍历路径
+          let elements: any = new Map();
+          let number: any = 0; // 取最大值
+          function getChildrenMaxHeight(targetElement) {
+            elements.set(targetElement.id, targetElement.virtualHeight);
+            if (number < targetElement.virtualHeight && targetElement.wnType != typeConfluence) number = targetElement.virtualHeight;
+            if (targetElement.id !== node.parentData?.id + '_confluence') findPath(targetElement, false);
+          }
+          function findPath(currentElement, isRoot: boolean, id?) {
+            currentElement?.children.forEach(targetElement => {
+              if (isRoot) {
+                if (targetElement.id === id) getChildrenMaxHeight(targetElement);
+              } else getChildrenMaxHeight(targetElement);
+            });
+          }
+          findPath(node.parentData, true, node.id);
+          finalHeight = number;
+        }
+        // 如果该节点有多个incoming线 则 将该节点默认为是合流节点 将它高度设置为320
+        if (elementRegistry) {
+          let currentElement = elementRegistry.get(node.id);
+          if (currentElement.incoming?.length > 1) node.isGateway = true;
+        }
+        node.virtualHeight = finalHeight;
+        stack.pop(); // 从栈中移除
+        this.updateParent(stack, finalHeight, 'vertical');
+      }
+    }
+    return root.virtualHeight!;
+  }
+  // 更新栈顶的父节点的 totalWidth
+  private updateParent(stack: { node: TreeNode }[], number: number, type: 'horizontal' | 'vertical'): void {
+    if (stack.length > 0) {
+      const parent = stack[stack.length - 1];
+      type === 'horizontal' ? (parent['totalWidth'] += number) : (parent['totalHeight'] += number);
+    }
+  }
+  // 根据 ID 查询树中对应的节点,并返回其虚拟宽度
+  public findNodeById(root: TreeNode, id: string): TreeNode | undefined {
+    if (root.id === id) return root;
+    for (const child of root.children) {
+      const foundNode = this.findNodeById(child, id);
+      if (foundNode) return foundNode; // 返回找到的节点
+    }
+    return undefined; // 如果找不到匹配的节点
+  }
+  // 判断是否为网关
+  public isGateway(element) {
+    const gatewayTypes = ['bpmn:ExclusiveGateway', 'bpmn:ParallelGateway', 'bpmn:InclusiveGateway', 'bpmn:EventBasedGateway'];
+    if (element.wnType === typeConfluence) return false;
+    return gatewayTypes.includes(element.type);
+  }
+  public formatCanvas(visited, modeling, elementRegistry) {
+    let obj = visited.reduce((acc, item) => {
+      let x = item.offset?.x - item.x || 0;
+      if (!acc[x]) acc[x] = [];
+      acc[x].push(elementRegistry.get(item.id));
+      return acc;
+    }, {});
+    // 分组移动 优化性能
+    for (const key in obj) {
+      if (obj.hasOwnProperty(key)) {
+        let list = obj[key];
+        modeling.moveElements(list, { x: Number(key), y: 0 });
+      }
+    }
+  }
+  public formatCanvasHorizontal(visited, modeling, elementRegistry) {
+    let obj = visited.reduce((acc, item) => {
+      let y = item.offset?.y - item.y || 0;
+      if (!acc[y]) acc[y] = [];
+      acc[y].push(elementRegistry.get(item.id));
+      return acc;
+    }, {});
+    // 分组移动 优化性能
+    for (const key in obj) obj.hasOwnProperty(key) && modeling.moveElements(obj[key], { x: 0, y: Number(key) });
+  }
+  public getParentOffsetById(data: any, id: string) {
+    if (data.parentData) {
+      if (data.parentData.id === id) {
+        // 如果合流宽度为0 则x轴宽度和合流网关一致
+        let offset = {
+          x: data.parentData.offset.x + data.parentData.width / 2,
+          y: data.parentData.offset.y,
+        };
+        return offset;
+      }
+      return this.getParentOffsetById(data.parentData, id);
+    }
+  }
+  // 广度优先遍历树状结构
+  public traverseTreeBFS(root: TreeNode, callback: (node: TreeNode) => void): void {
+    let queue: TreeNode[] = [root]; // 初始化队列
+    const processedNodes = new Set<string>(); // 用于跟踪已处理节点
+    const AUTO_HEIGHT = 150;
+    // 获取开始节点的虚拟高度
+    while (queue.length > 0) {
+      let current = queue.shift(); // 从队列中取出第一个元素
+      if (current && !processedNodes.has(current.id)) {
+        processedNodes.add(current.id); // 标记为已处理
+        // 对开始节点外的元素进行偏移
+        if (current.id != root.id) {
+          let parentData = current.parentData;
+          let n = 0;
+          if (parentData) {
+            // 遍历数组,遇到条件终止
+            for (let i = 0; i < parentData.children.length; i++) {
+              if (parentData.children[i].id === current.id) break;
+              if (parentData.children[i].wnType === typeConfluence) {
+                parentData.children[i].virtualWidth = 320;
+              }
+              n += parentData.children[i].virtualWidth;
+            }
+          }
+          let parentX = parentData.offset ? parentData.offset.x : parentData.x;
+          let parentY = parentData.offset ? parentData.offset.y : parentData.y;
+          // X轴坐标边界
+          let minX = parentX - parentData.virtualWidth / 2;
+          let currentVirtualWidth = current.virtualWidth / 2;
+
+          if (current.children)
+            if (parentData.virtualWidth > current.virtualWidth && parentData.children?.length === 1) currentVirtualWidth = parentData.virtualWidth / 2;
+          // 如果某个节点children不包含除typeTrigger外的元素
+          if (parentData.children?.length === 1 && !parentData.children.some(o => o.wnType != typeTrigger)) {
+            minX = parentX - (parentData.virtualWidth + 320) / 2;
+          }
+          // 如果parentData.children 只含有触发节点(任务节点连接合流节点)
+          let isTrigger = parentData.children.every(s => s.wnType === typeTrigger);
+          if (isTrigger) {
+            n += 320;
+          }
+          let offset = {
+            x: minX + currentVirtualWidth - (current.width - parentData.width) / 2 + n,
+            y: parentY + AUTO_HEIGHT + current.height,
+          };
+          if (current.id.includes('_confluence')) {
+            let id = current.id.replace('_confluence', '');
+            let gatewayOffset = this.getParentOffsetById(current, id);
+            offset = {
+              x: gatewayOffset.x,
+              y: parentY + AUTO_HEIGHT + current.height,
+            };
+          }
+          current.offset = offset;
+        }
+        callback(current); // 执行操作
+        current.children = current.children.map((children: any) => {
+          return { ...children, parentData: { ...current } };
+        });
+        queue.push(...current.children); // 将子节点添加到队列中
+      }
+    }
+  }
+  // 广度优先遍历树状结构
+  public bpmnTraverseTreeBFS(root: TreeNode, callback: (node: TreeNode) => void, type: direction): void {
+    let queue: TreeNode[] = [root]; // 初始化队列
+    const AUTO_HEIGHT = 150;
+    // 获取开始节点的虚拟高度
+    while (queue.length > 0) {
+      let current = queue.shift(); // 从队列中取出第一个元素
+      if (current) {
+        // 对开始节点外的元素进行偏移
+        if (current.id != root.id) {
+          let parentData = current.parentData;
+          let n = 0;
+          if (parentData) {
+            // 遍历数组,遇到条件终止
+            for (let i = 0; i < parentData.children.length; i++) {
+              if (parentData.children[i].id === current.id) break;
+              type === 'horizontal' ? (n += parentData.children[i].virtualHeight || 208) : (n += parentData.children[i].virtualWidth || 320);
+            }
+          }
+          let parentX = parentData.offset ? parentData.offset.x : parentData.x;
+          let parentY = parentData.offset ? parentData.offset.y : parentData.y;
+          // X轴坐标边界
+          if (type === 'horizontal') {
+            let minY = parentY - parentData.virtualHeight / 2;
+            let currentVirtualHeight = current.virtualHeight / 2;
+            if (parentData.virtualHeight > current.virtualHeight && parentData.children?.length === 1) currentVirtualHeight = parentData.virtualHeight / 2;
+            let offset = {
+              x: parentX + AUTO_HEIGHT + current.width,
+              y: minY + n + currentVirtualHeight + (parentData.height - current.height) / 2,
+            };
+            let level = current.level;
+            current.offset = offset;
+            current.level = level;
+          } else {
+            let minX = parentX - parentData.virtualWidth / 2;
+            let currentVirtualWidth = current.virtualWidth / 2;
+            if (parentData.virtualWidth > current.virtualWidth && parentData.children?.length === 1) currentVirtualWidth = parentData.virtualWidth / 2;
+            let offset = {
+              x: minX + n + currentVirtualWidth + (parentData.width - current.width) / 2,
+              y: parentY + AUTO_HEIGHT + current.height,
+            };
+            let level = current.level;
+            current.offset = offset;
+            current.level = level;
+          }
+        }
+        callback(current); // 执行操作
+        let level = current?.level ?? 0;
+        current.children = current.children.map((children: any) => {
+          return { ...children, parentData: { ...current }, level: level + 1 };
+        });
+        queue.push(...current.children); // 将子节点添加到队列中
+      }
+    }
+  }
+  // 修改线条坐标
+  public updateConnectionWaypoints(connect: any, modeling: any, type: direction) {
+    let source = connect.source;
+    let target = connect.target;
+    let newWaypoints: any = [];
+    if (type === 'vertical') {
+      if (source.x < target.x) {
+        newWaypoints = [
+          { x: source.x + source.width / 2, y: source.y + source.height },
+          { x: source.x + source.width / 2, y: target.y - 60 },
+          { x: target.x + target.width / 2, y: target.y - 60 },
+          { x: target.x + target.width / 2, y: target.y },
+        ];
+      } else if (source.x > target.x) {
+        newWaypoints = [
+          { x: source.x + source.width / 2, y: source.y + source.height },
+          { x: source.x + source.width / 2, y: target.y - 60 },
+          { x: target.x + target.width / 2, y: target.y - 60 },
+          { x: target.x + target.width / 2, y: target.y },
+        ];
+      } else {
+        newWaypoints = [
+          { x: source.x + source.width / 2, y: source.y + source.height },
+          { x: target.x + target.width / 2, y: target.y },
+        ];
+      }
+    } else {
+      // 横向
+      if (source.y < target.y) {
+        newWaypoints = [
+          { x: source.x + source.width, y: source.y + source.height / 2 },
+          { x: target.x - DEFAULT_CONNECT / 2, y: source.y + source.height / 2 },
+          { x: target.x - DEFAULT_CONNECT / 2, y: target.y + target.height / 2 },
+          { x: target.x, y: target.y + target.height / 2 },
+        ];
+      } else if (source.y > target.y) {
+        newWaypoints = [
+          { x: source.x + source.width, y: source.y + source.height / 2 },
+          { x: target.x - DEFAULT_CONNECT / 2, y: source.y + source.height / 2 },
+          { x: target.x - DEFAULT_CONNECT / 2, y: target.y + target.height / 2 },
+          { x: target.x, y: target.y + target.height / 2 },
+        ];
+      } else {
+        newWaypoints = [
+          { x: source.x + source.width, y: source.y + source.height / 2 },
+          { x: target.x, y: target.y + target.height / 2 },
+        ];
+      }
+    }
+    modeling.updateWaypoints(connect, newWaypoints);
+  }
+  // 判断元素与网关之间的垂直距离是否小于某个阈值
+  public isWithinThresholdDel(target, source, threshold) {
+    // 这里假设网关在上方,即网关的 y 坐标小于当前元素的 y 坐标
+    let gatewayY = target.y;
+    let sourceElementY = source.y;
+    // 如果是合流节点 获取其所有的上一个节点 判断上一个节点的y轴最大值
+    if (target.wnType === typeConfluence) {
+      if (target.incoming?.length > 1) {
+        let y = -Infinity;
+        target.incoming.map((item: any) => {
+          if (item?.source?.y > y) y = item.source.y;
+        });
+        return gatewayY - y <= threshold;
+      }
+    }
+    return gatewayY - sourceElementY < threshold && gatewayY > sourceElementY;
+  }
+  public moveConnectedElements(connection: any, height?: any) {
+    const stack: any = []; // 用于存储待处理的连接线
+    const processedElements = new Set(); // 记录已经处理过的目标元素
+    stack.push(connection); // 从给定的连接线开始
+    while (stack.length > 0) {
+      const currentConnection: any = stack.pop();
+      const target = currentConnection.target;
+      if (!target) continue; // 如果没有目标元素,跳过
+      if (processedElements.has(target)) continue; // 如果目标元素已经处理过,跳过
+      if (this.isWithinThresholdDel(target, currentConnection.source, height)) continue;
+      processedElements.add(target);
+      // 遍历目标元素的所有出线连接,并将它们压入栈
+      const outgoingConnections: any = target.outgoing || [];
+      for (const outgoingConnection of outgoingConnections) {
+        stack.push(outgoingConnection); // 将所有关联的连接线压入栈中
+      }
+    }
+    return Array.from(processedElements);
+  }
+  public getElementsByGateway(gateway: any) {
+    const elementsMap = new Map();
+    let allElements = this._allElement;
+    function getList(list: any) {
+      list.map((element: any) => {
+        if (element.id === gateway.id + '_confluence') return;
+        if (!elementsMap.has(element.id)) {
+          elementsMap.set(element.id, element);
+          let childrenList = NodeUtils.getNextElementList(element, allElements);
+          getList(childrenList);
+        }
+        return;
+      });
+    }
+    let list = NodeUtils.getNextElementList(gateway, allElements);
+    getList(list);
+    return Array.from(elementsMap.values());
+  }
+  public resizeGroupShape(shapes: any[], bpmn: any) {
+    let elementRegistry: any = bpmn.get('elementRegistry');
+    let modeling: any = bpmn.get('modeling');
+    let groupSet = new Set();
+    shapes.length > 0 &&
+      shapes.map((shape: any) => {
+        //  1. 循环获取到移动元素判断是否为触发节点或执行节点 记录对应的分组id
+        if (changeTypeByTaskShape[shape.wnType] || shape.wnType === typeTrigger) {
+          groupSet.add(shape.businessObject.$attrs.customGroupId);
+        }
+      });
+    for (let groupId of groupSet) {
+      let minX = Infinity,
+        minY = Infinity,
+        maxX = -Infinity,
+        maxY = -Infinity;
+      let groupShape = elementRegistry.get(groupId);
+      if (groupShape) {
+        // 2. 遍历分组id的所有元素 获取到分组id内的边界坐标 根据坐标计算长宽高
+        bpmn.get('elementRegistry').forEach(element => {
+          if (element.businessObject.$attrs.customGroupId === groupId) {
+            minX = Math.min(minX, element.x);
+            minY = Math.min(minY, element.y);
+            maxX = Math.max(maxX, element.x + element.width);
+            maxY = Math.max(maxY, element.y + element.height);
+          }
+        });
+        // 3. 根据触发节点位置 重新设置分组元素的坐标
+        modeling.resizeShape(groupShape, {
+          x: minX - 25,
+          y: minY - 15,
+          width: maxX - minX + 50,
+          height: maxY - minY + 30,
+        });
+      }
+    }
+  }
+  public getGroupElementById(groupId: string, bpmn: any) {
+    let groupList: any = [];
+    let groupShape = bpmn.get('elementRegistry').get(groupId);
+    if (groupShape) {
+      // 2. 遍历分组id的所有元素 获取到分组id内的边界坐标 根据坐标计算长宽高
+      bpmn.get('elementRegistry').forEach(element => {
+        if (element.businessObject.$attrs?.customGroupId === groupId || element.id === groupId) groupList.push(element);
+      });
+    }
+    return groupList;
+  }
+  public getOutgoingConnections(element) {
+    return element.outgoing || [];
+  }
+  public findUniqueElementsBetween(currentElement, targetElement, visitedElements = new Set()) {
+    // 添加当前元素到访问路径集合
+    visitedElements.add(currentElement);
+    // 如果当前元素是目标元素,返回集合
+    if (currentElement === targetElement) {
+      return visitedElements;
+    }
+    const outgoingConnections = this.getOutgoingConnections(currentElement);
+    outgoingConnections.forEach(connection => {
+      const nextElement = connection.target;
+      if (!visitedElements.has(nextElement)) {
+        this.findUniqueElementsBetween(nextElement, targetElement, visitedElements);
+      }
+    });
+    return visitedElements;
+  }
+  public onComputerMaxElementH(bpmn, current, gatewayElement, groupList, delElement, isGateway, processedElements?, threshold?) {
+    // 获取对应的分流节点 并且获取到分流节点到合流内的所有元素
+    let elementRegistry: any = bpmn.get('elementRegistry');
+    let confluenceElement = elementRegistry.get(current.id);
+    let treeBuilder = new BPMNTreeBuilder(elementRegistry.getAll()); // 实例化工具类
+    let list: any = [];
+    let groupH: number = 0;
+    const uniqueElementsSet = treeBuilder.findUniqueElementsBetween(gatewayElement, confluenceElement);
+    // 查找具有最大属性值的元素
+    let maxElement: any = null;
+    let maxValue = -Infinity; // 初始最大值设为负无穷大
+    uniqueElementsSet.forEach((element: any) => {
+      let y = element?.y; // 假设需要比较的属性名为 'value'
+      if (processedElements?.has(element.id)) y += threshold;
+      // 比较属性值并更新最大值和对应的元素
+      if(element?.wnType === typeConfluence && maxElement?.wnType != typeConfluence) y = y + 220
+      if (y > maxValue && current.id != element.id) {
+        maxValue = y;
+        maxElement = element;
+      }
+    });
+    if (maxValue <= current.y - 220) {
+      list.push(current);
+      const taskHeight =  typeConfig?.[bpmnTask]?.renderer?.attr?.height ;
+      groupH = current.y - (maxValue + DEFAULT_DISTANCE + taskHeight);
+    } else if (maxElement.wnType === current.wnType && current.wnType === typeConfluence) {
+      list.push(current);
+    }
+
+    if (groupList?.length) {
+      if (current.y > maxValue) {
+        list.push(current);
+      }
+    }
+    return { list: list, h: groupH };
+  }
+  public handleCollisionsByLevel(list: any[], type: direction) {
+    const MIN_SPACE_X = 120; // 最小间距
+    const MIN_SPACE_Y = 30;
+    // 根据 level 对元素进行分组
+    const grouped = groupByLevel(list);
+    // 对每个层级的元素进行碰撞检测和位置调整
+    grouped.forEach(group => {
+      collisionFun(group);
+    });
+    function collisionFun(elements) {
+      let y = elements[elements.length - 1].offset ? elements[elements.length - 1].offset.y : elements[elements.length - 1].y;
+      let x = elements[elements.length - 1].offset ? elements[elements.length - 1].offset.x : elements[elements.length - 1].x;
+      // 遍历每一对元素进行碰撞检测
+      for (let i = 0; i < elements.length; i++) {
+        for (let j = i + 1; j < elements.length; j++) {
+          const rect1 = elements[i];
+          const rect2 = elements[j];
+          // 如果发生碰撞
+          if (isColliding(rect1, rect2, type)) {
+            // 调整位置,将碰撞元素移动到最后
+            if (type === 'horizontal') {
+              y = y + rect2.height + MIN_SPACE_Y;
+              rect2.offset.y = y;
+            } else {
+              x = x + rect2.width + MIN_SPACE_X;
+              rect2.offset.x = x;
+            }
+          }
+        }
+      }
+    }
+    function groupByLevel(list) {
+      list.sort((a, b) => a.level - b.level);
+      const grouped: any = [];
+      let currentLevel = null;
+      let currentGroup: any = [];
+      list.forEach(item => {
+        if (item.level !== currentLevel) {
+          if (currentGroup.length > 0) {
+            grouped.push(currentGroup);
+          }
+          currentGroup = [item];
+          currentLevel = item.level;
+        } else {
+          currentGroup.push(item);
+        }
+      });
+      if (currentGroup.length > 0) {
+        currentGroup.sort((a, b) => {
+          if (type === 'horizontal') {
+            let aY = a.offset ? a.offset.y : a.y;
+            let bY = b.offset ? b.offset.y : b.y;
+            return aY - bY;
+          } else {
+            let aX = a.offset ? a.offset.x : a.x;
+            let bX = b.offset ? b.offset.x : b.x;
+            return aX - bX;
+          }
+        });
+        grouped.push(currentGroup);
+      }
+      return grouped;
+    }
+    // y坐标碰撞检测
+    function isColliding(element1: any, element2: any, type: direction) {
+      if (type === 'horizontal') {
+        let element1Y = element1.offset ? element1.offset.y : element1.y;
+        let element2Y = element2.offset ? element2.offset.y : element2.y;
+        return element1Y < element2Y + element2.height + MIN_SPACE_Y && element1Y + element1.height + MIN_SPACE_Y > element2Y;
+      }
+      if (type === 'vertical') {
+        let element1X = element1.offset ? element1.offset.x : element1.x;
+        let element2X = element2.offset ? element2.offset.x : element2.x;
+        return element1X < element2X + element2.width + MIN_SPACE_X && element1X + element1.width + MIN_SPACE_X > element2X;
+      }
+    }
+  }
+}

+ 110 - 0
lib/utils/dictUtil.ts

@@ -0,0 +1,110 @@
+export interface DictItem {
+  dictValue: string;
+  dictItemValue: string;
+  dictItemLabel: string;
+}
+
+export const dict = {
+  YesNo: {
+    name: 'YesNo',
+    Yes: 'Y',
+    No: 'N',
+  },
+  EnabledDisabled: {
+    enabled: 'enabled',
+    disabled: 'disabled',
+  },
+  ProviderState: {
+    name: 'ProviderState',
+  },
+  ProviderType: {
+    name: 'ProviderType',
+    alibaba: 'alibaba',
+    tencent: 'tencent',
+    huawei: 'huawei',
+    minio: 'minio',
+  },
+  IAMProviderProtocol: {
+    name: 'IAMProviderProtocol',
+    oidc: 'oidc',
+    saml2: 'saml2',
+    cas: 'cas',
+  },
+  IAMProviderType: {
+    name: 'IAMProviderType',
+    qq: 'qq',
+    dingtalk: 'dingtalk',
+    qywechat: 'qywechat',
+    feishu: 'feishu',
+    wechat: 'wechat',
+    alipay: 'alipay',
+    weibo: 'weibo',
+    alibaba: 'alibaba',
+    tencent: 'tencent',
+  },
+  Gender: {
+    name: 'Gender',
+  },
+  Political: {
+    name: 'Political',
+  },
+  Marital: {
+    name: 'Marital',
+  },
+  StudentStatus: {
+    name: 'StudentStatus',
+  },
+  GBNation: {
+    name: 'GBNation',
+  },
+  GradeLevel: {
+    name: 'GradeLevel',
+  },
+  AccountType: {
+    name: 'AccountType',
+  },
+  UserCertType: {
+    name: 'UserCertType',
+  },
+  AppInfoVersionState: {
+    name: 'AppInfoVersionState',
+    publish: 'publish',
+    develop: 'develop',
+    history: 'history',
+  },
+  LanguageType: {
+    name: 'LanguageType',
+    enUS: 'en_US',
+    zhCN: 'zh_CN',
+    zhTW: 'zh_TW',
+  },
+};
+export const dictName = [
+  dict.YesNo.name,
+  dict.Gender.name,
+  dict.Political.name,
+  dict.Marital.name,
+  dict.StudentStatus.name,
+  dict.GBNation.name,
+  dict.GradeLevel.name,
+  dict.AccountType.name,
+  dict.UserCertType.name,
+  dict.LanguageType.name,
+];
+
+export const YesNo2Boolean = (value: any) => {
+  return dict.YesNo.Yes === value || dict.YesNo.No === value ? dict.YesNo.Yes === value : value;
+};
+
+export const Boolean2YesNo = (value: any) => {
+  return value === true || value === false ? value ? dict.YesNo.Yes : dict.YesNo.No : value;
+};
+
+export const Enabled2Boolean = (value: any) => {
+  return dict.EnabledDisabled.enabled === value || dict.EnabledDisabled.disabled === value ?
+    dict.EnabledDisabled.enabled === value : value;
+};
+
+export const Boolean2Enabled = (value: any) => {
+  return value === true || value === false ? value ? dict.EnabledDisabled.enabled : dict.EnabledDisabled.disabled : value;
+};

+ 8 - 0
lib/utils/index.ts

@@ -0,0 +1,8 @@
+export * from './bpmnLintUtil';
+export * from './constructTreeUtil';
+export * from './dictUtil';
+export * from './DiUtil';
+export * from './modelUtil';
+export * from './nodeUtil';
+export * from './uuidUtil';
+export * from './bpmn';

+ 51 - 0
lib/utils/modelUtil.ts

@@ -0,0 +1,51 @@
+import { some } from 'min-dash';
+
+/**
+ * Is an element of the given BPMN type?
+ *
+ * @param  {djs.model.Base|ModdleElement} element
+ * @param  {string} type
+ *
+ * @return {boolean}
+ */
+export function is(element: any, type: any) {
+  var bo = getBusinessObject(element);
+
+  return bo && typeof bo.$instanceOf === 'function' && bo.$instanceOf(type);
+}
+
+/**
+ * Return true if element has any of the given types.
+ *
+ * @param {djs.model.Base} element
+ * @param {Array<string>} types
+ *
+ * @return {boolean}
+ */
+export function isAny(element: any, types: any) {
+  return some(types, function (t: any) {
+    return is(element, t);
+  });
+}
+
+/**
+ * Return the business object for a given element.
+ *
+ * @param  {djs.model.Base|ModdleElement} element
+ *
+ * @return {ModdleElement}
+ */
+export function getBusinessObject(element: any) {
+  return (element && element.businessObject) || element;
+}
+
+/**
+ * Return the di object for a given element.
+ *
+ * @param  {djs.model.Base} element
+ *
+ * @return {ModdleElement}
+ */
+export function getDi(element: any) {
+  return element && element.di;
+}

+ 842 - 0
lib/utils/nodeUtil.ts

@@ -0,0 +1,842 @@
+import {
+  bpmnSequenceFlow,
+  bpmnIncoming,
+  bpmnOutgoing,
+  bpmnInclusive,
+  typeStart,
+  typeTask,
+  typeSubFlow,
+  typeEnd,
+  typeTrigger,
+  typeEventTrigger,
+  typeTimeTrigger,
+  typeNoticeTrigger,
+  typeWebhookTrigger,
+  typeGetData,
+  typeAddData,
+  typeUpdateData,
+  typeDelData,
+  typeInterface,
+  typeMessage,
+  typeLaunchFlow,
+  typeSchedule,
+  typeProcessing,
+  typeGateway,
+  typeInclusion,
+  typeOutside,
+} from '../config/variableName';
+import { buildBitUUID } from '../utils/uuidUtil';
+import { hasGatewayType, typeConfig } from '../config';
+import { is } from 'bpmn-js/lib/util/ModelUtil';
+import { getExternalLabelMid } from 'bpmn-js/lib/util/LabelUtil';
+export class NodeUtils {
+  /**
+   * 判断节点类型
+   * @param {Node} node - 节点数据
+   * @returns Boolean
+   */
+  static isStartNode(node) {
+    return node && node?.type === typeStart;
+  }
+  static isApproverNode(node) {
+    return node && node?.type === typeTask;
+  }
+  static isProcessingNode(node) {
+    return node && node?.type === typeProcessing;
+  }
+  static isSubFlowNode(node) {
+    return node && node?.type === typeSubFlow;
+  }
+  static isInterflowNode(node) {
+    return node && node?.type === typeTask && node?.isInterflow;
+  }
+  static isConnectNode(node) {
+    return node && node?.type === bpmnSequenceFlow;
+  }
+  static isEndNode(node) {
+    return node && node?.type === typeEnd;
+  }
+  static isTriggerNode(node) {
+    return node && node?.type === typeTrigger;
+  }
+  static isEventTriggerNode(node) {
+    return node && node?.type === typeEventTrigger;
+  }
+  static isTimeTriggerNode(node) {
+    return node && node?.type === typeTimeTrigger;
+  }
+  static isNoticeTriggerNode(node) {
+    return node && node?.type === typeNoticeTrigger;
+  }
+  static isWebhookTriggerNode(node) {
+    return node && node?.type === typeWebhookTrigger;
+  }
+  static isGetDataNode(node) {
+    return node && node?.type === typeGetData;
+  }
+  static isAddDataNode(node) {
+    return node && node?.type === typeAddData;
+  }
+  static isUpdateDataNode(node) {
+    return node && node?.type === typeUpdateData;
+  }
+  static isDeleteDataNode(node) {
+    return node && node?.type === typeDelData;
+  }
+  static isDataInterfaceNode(node) {
+    return node && node?.type === typeInterface;
+  }
+  static isMessageNode(node) {
+    return node && node?.type === typeMessage;
+  }
+  static isLaunchFlowNode(node) {
+    return node && node?.type === typeLaunchFlow;
+  }
+  static isScheduleNode(node) {
+    return node && node?.type === typeSchedule;
+  }
+  static isOutsideNode(node) {
+    return node && node?.type === typeOutside;
+  }
+
+  /**
+   * 获取上节点元素
+   * @param element  当前元素
+   */
+  static getPreNodeList(element) {
+    let preList: any[] = [];
+    if (!element || !element.incoming || !element.incoming.length) return preList;
+    for (let i = 0; i < element.incoming.length; i++) {
+      const item = element.incoming[i];
+      preList.push(item.source);
+    }
+    return preList;
+  }
+  /**
+   * 生成条件组名称
+   * @param conditions  条件组
+   * @param matchLogic  and / or
+   */
+  static getConditionsContent(conditions, matchLogic) {
+    let content = '';
+    for (let i = 0; i < conditions.length; i++) {
+      const e = conditions[i];
+      content += conditions.length == 1 ? '' : (i == 0 ? '' : ` ${matchLogic} `) + '( ';
+      for (let j = 0; j < e.groups.length; j++) {
+        const groups = e.groups[j];
+        const logic = j == 0 ? '' : ` ${e.logic} `;
+        const text = ` ${groups.fieldName} ${groups.symbolName}${groups.fieldLabel ? groups.fieldLabel : groups.fieldValue || groups.fieldValue === 0 ? groups.fieldValue : ''
+          } `;
+        content += logic + text;
+      }
+      content += conditions.length == 1 ? '' : ' )';
+    }
+    return content;
+  }
+  /**
+   * 自动生成网关
+   * @param xml  xml
+   * @param elementRegistry  节点元素
+   */
+  static autoCreateGateWay(xml, elementRegistry, jnpfData) {
+    let parser = new DOMParser();
+    let xmlDoc = parser.parseFromString(xml, 'text/xml');
+    let process: any = xmlDoc.querySelector('#Process_1');
+    let plane: any = xmlDoc.querySelector('#BPMNPlane_1');
+    let divideList: any[] = [];
+    let confluenceList: any = [];
+    const allList: any[] = elementRegistry.getAll() || [];
+    allList.map((item: any) => {
+      // 过滤任务节点的网关
+      let groupId = item?.businessObject?.$attrs?.customGroupId;
+      if (item.incoming?.length > 1 && !groupId) confluenceList.push({ key: item.id, gatewayType: 'inclusion' });
+      if (item.outgoing && item.outgoing.length > 1 && !groupId) {
+        divideList.push({
+          key: item.id,
+          gatewayType: jnpfData.data[item.id]?.divideRule || 'inclusion',
+        });
+      }
+    });
+
+    // 新增分流xml标签
+    if (divideList?.length) {
+      divideList.map((item: any) => {
+        let targetElement = xmlDoc.querySelector(`#${item.key}`);
+        if (targetElement) {
+          let targeChildren: any = targetElement.children;
+          let outgoingList: any = [];
+          let incomingList: any = [];
+          for (var i = 0; i < targeChildren.length; i++) {
+            if (targeChildren[i].nodeName === bpmnOutgoing) {
+              // 将该值赋值给新生成的分流元素上 删除原来元素上的outgoing 并且生成一条新的outgoing连接到该线条上。
+              outgoingList.push(targeChildren[i]);
+            } else {
+              incomingList.push(targeChildren[i]);
+            }
+          }
+          // 新增一个分流节点 并且连接线到目前的元素上
+          let gatewayElement = NodeUtils.createGateway(xmlDoc, item.gatewayType);
+          let gatewayId = 'Gateway_' + buildBitUUID();
+          let flowId = 'flow_' + buildBitUUID();
+          gatewayElement.setAttribute('id', gatewayId);
+          let flowElement = xmlDoc.createElement('bpmn2:sequenceFlow');
+          flowElement.setAttribute('id', flowId);
+          flowElement.setAttribute('sourceRef', item.key);
+          flowElement.setAttribute('targetRef', gatewayId);
+          let conditionExpression = xmlDoc.createElement('bpmn2:conditionExpression');
+          conditionExpression.setAttribute('xsi:type', 'bpmn2:tFormalExpression');
+          let testText = xmlDoc.createTextNode('${' + `${flowId}` + '}');
+          conditionExpression.appendChild(testText);
+          let childElement = xmlDoc.createElement(bpmnIncoming); // 替换为您想要创建的子标签的名称
+          let textNode = xmlDoc.createTextNode(flowId);
+          childElement.appendChild(textNode);
+          gatewayElement.appendChild(childElement);
+          if (outgoingList?.length) {
+            let set: any = new Set();
+            outgoingList.map((item: any) => {
+              let outgoingId = item.textContent;
+              let outgoingItemElementId = `#${outgoingId}`; // 替换成您要操作的元素的 ID
+              let outgoingItemElement: any = xmlDoc.querySelector(outgoingItemElementId);
+              outgoingItemElement.setAttribute('sourceRef', gatewayId);
+              allList.map((itemElement: any) => {
+                if (itemElement.id === outgoingId) set.add(itemElement.source?.id);
+              });
+              gatewayElement.appendChild(item);
+            });
+            set.forEach((item: any) => {
+              let element: any = xmlDoc.getElementById(item);
+              let childElement = xmlDoc.createElement(bpmnOutgoing);
+              element.appendChild(childElement);
+              let textNode = xmlDoc.createTextNode(flowId);
+              childElement.appendChild(textNode);
+            });
+          }
+          flowElement.appendChild(conditionExpression);
+          process.appendChild(gatewayElement);
+          process.appendChild(flowElement);
+          this.handleCreateGatewayBounds(xmlDoc, gatewayId, plane);
+        }
+      });
+    }
+    if (confluenceList?.length) {
+      confluenceList.map((item: any) => {
+        let targetElement = xmlDoc.querySelector(`#${item.key}`);
+        if (targetElement) {
+          let targeChildren: any = targetElement.children;
+          let outgoingList: any = [];
+          let incomingList: any = [];
+          for (var i = 0; i < targeChildren.length; i++) {
+            if (targeChildren[i].nodeName === bpmnOutgoing) {
+              // 将该值赋值给新生成的分流元素上 删除原来元素上的outgoing 并且生成一条新的outgoing连接到该线条上。
+              outgoingList.push(targeChildren[i]);
+            } else {
+              incomingList.push(targeChildren[i]);
+            }
+          }
+          // 新增一个分流节点 并且连接线到目前的元素上
+          let gatewayElement = NodeUtils.createGateway(xmlDoc, item.gatewayType);
+          let gatewayId = 'Gateway_' + buildBitUUID();
+          let flowId = 'Flow_' + buildBitUUID();
+          gatewayElement.setAttribute('id', gatewayId);
+          let flowElement = xmlDoc.createElement('bpmn2:sequenceFlow');
+          let conditionExpression = xmlDoc.createElement('bpmn2:conditionExpression');
+          conditionExpression.setAttribute('xsi:type', 'bpmn2:tFormalExpression');
+          let testText = xmlDoc.createTextNode('${' + `${flowId}` + '}');
+          conditionExpression.appendChild(testText);
+          flowElement.setAttribute('id', flowId);
+          flowElement.setAttribute('sourceRef', gatewayId);
+          flowElement.setAttribute('targetRef', item.key);
+          let childElement = xmlDoc.createElement(bpmnOutgoing); // 替换为您想要创建的子标签的名称
+          let textNode = xmlDoc.createTextNode(flowId);
+          childElement.appendChild(textNode);
+          gatewayElement.appendChild(childElement);
+          if (incomingList && incomingList.length) {
+            let set: any = new Set();
+            incomingList.map((item: any) => {
+              let incomingId = item.textContent;
+              let incomingItemElementId = `#${incomingId}`; // 替换成您要操作的元素的 ID
+              let incomingItemElement: any = xmlDoc.querySelector(incomingItemElementId);
+              incomingItemElement.setAttribute('targetRef', gatewayId);
+              allList.map((itemElement: any) => {
+                if (itemElement.id === incomingId) set.add(itemElement.target?.id);
+              });
+              gatewayElement.appendChild(item);
+            });
+            set.forEach((item: any) => {
+              let element: any = xmlDoc.getElementById(item);
+              let childElement = xmlDoc.createElement(bpmnIncoming);
+              // 新增新的出线线条在元素上
+              element.appendChild(childElement);
+              let textNode = xmlDoc.createTextNode(flowId);
+              childElement.appendChild(textNode);
+            });
+          }
+          flowElement.appendChild(conditionExpression);
+          process.appendChild(gatewayElement);
+          process.appendChild(flowElement);
+          this.handleCreateGatewayBounds(xmlDoc, gatewayId, plane);
+        }
+      });
+    }
+    const newXml = new XMLSerializer().serializeToString(xmlDoc);
+    return encodeURIComponent(newXml);
+  }
+  /**
+   * 自动删除网关
+   * @param flowXml
+   * @returns
+   */
+  static autoDelGateWay(flowXml: string, type: number, nodeMap: any, isPreview: boolean) {
+    let parser = new DOMParser();
+    let xmlDoc = parser.parseFromString(decodeURIComponent(flowXml), 'text/xml');
+    let oldXmlDoc = parser.parseFromString(decodeURIComponent(flowXml), 'text/xml');
+    if (type != 1) {
+      let process: any = xmlDoc.querySelector('#Process_1');
+      let plane: any = xmlDoc.querySelector('#BPMNPlane_1');
+      let gatewayList = NodeUtils.getAllGateway(xmlDoc);
+      gatewayList.map((item: any) => {
+        let incoming = item.getElementsByTagName(bpmnIncoming) || []; // 对于网关的进线线条
+        let outgoing = item.getElementsByTagName(bpmnOutgoing) || []; // 对于网关的出线线条
+        let sourceElement: any = []; // 当前网关的进线元素
+        let targetElement: any = []; // 当前网关的出线元素
+        for (let i = 0; i < incoming.length; i++) {
+          // 获取进线元素的id
+          let flowId = `#${incoming[i].innerText || incoming[i].textContent}`;
+          // 然后在全局内找到该线条
+          let connectElement: any = xmlDoc.querySelector(flowId);
+          // 获取到该线条的进线元素
+          let sourceElementId = connectElement.getAttribute('sourceRef');
+          sourceElement.push(xmlDoc.querySelector(`#${sourceElementId}`));
+        }
+        for (let i = 0; i < outgoing.length; i++) {
+          let flowId = `#${outgoing[i].innerText || outgoing[i].textContent}`;
+          let connectElement: any = xmlDoc.querySelector(flowId);
+          let targetElementId = connectElement.getAttribute('targetRef');
+          targetElement.push(xmlDoc.querySelector(`#${targetElementId}`));
+        }
+        // 合流网关(网关有多条进线 一条出线)
+        if (sourceElement.length > 1 && targetElement.length === 1) {
+          // 获取当前的出线元素
+          let targetElementId = targetElement[0]?.id;
+          // 删除targetElement旧的进线元素
+          const incomingList = NodeUtils.getIncomingConnectByElement(targetElement[0]) || [];
+          incomingList.map((targetComingChildren: any) => {
+            targetElement[0].removeChild(targetComingChildren);
+          });
+          // 遍历网关进线线条
+          let list: any = [];
+          // 迭代器
+          for (let i = 0; i < incoming.length; i++) {
+            let flowId = incoming[i].innerText || incoming[i].textContent;
+            let connectElement: any = xmlDoc.querySelector(`#${flowId}`);
+            let connectElementDi: any = xmlDoc.querySelector(`#${flowId}_di`);
+            let connectWaypoint = connectElementDi.getElementsByTagName('di:waypoint');
+            connectElement.setAttribute('targetRef', targetElementId);
+            list.push(incoming[i]);
+            let id = item.getAttribute('id');
+            if (id.includes('_isSimple')) {
+              let outGoingId = outgoing[0].innerText || outgoing[0].textContent;
+              let outGoingDi: any = xmlDoc.querySelector(`#${outGoingId}_di`);
+              let test: any = new Set();
+              let newWayList = this.getGatewayWaypoints(test, outGoingDi, outgoing, oldXmlDoc, connectWaypoint);
+              for (let i = 0; i < newWayList?.length; i++) {
+                if (!connectWaypoint[i]) {
+                  let newWaypoint = xmlDoc.createElementNS('http://www.omg.org/spec/DD/20100524/DI', 'di:waypoint');
+                  newWaypoint.setAttribute('x', newWayList[i].x);
+                  newWaypoint.setAttribute('y', newWayList[i].y);
+                  connectElementDi.appendChild(newWaypoint);
+                }
+              }
+            }
+          }
+          // 给targetElement设置新的进线元素
+          list.map((item: any) => {
+            targetElement[0]?.appendChild(item);
+          });
+          // 删除该网关及网关对应的出线元素
+          process.removeChild(item);
+          let flowId = outgoing[0].innerText || outgoing[0].textContent;
+          let connectElement: any = xmlDoc.querySelector(`#${flowId}`);
+          let connectElementDi: any = xmlDoc.querySelector(`#${flowId}_di`);
+          let itemDi: any = xmlDoc.querySelector(`#${item.getAttribute('id')}_di`);
+          process.removeChild(connectElement);
+          connectElementDi && plane.removeChild(connectElementDi);
+          itemDi && plane.removeChild(itemDi);
+        }
+        // 分流网关(网关有一条进线 多条出线)
+        if (sourceElement.length === 1 && targetElement.length > 1) {
+          // 获取当前的进元素
+          let sourceElementId = sourceElement[0].id;
+          // 删除sourceElement旧的出线元素
+          const outgoingList = NodeUtils.getOutgoingConnectByElement(sourceElement[0]) || [];
+          outgoingList.map((sourceElementChildren: any) => {
+            sourceElement[0].removeChild(sourceElementChildren);
+          });
+          // 遍历网关出线线条
+          let list: any = [];
+          for (let i = 0; i < outgoing.length; i++) {
+            // 获取进线元素的id
+            let flowId = outgoing[i].innerText || outgoing[i].textContent;
+            let incomingId = incoming[0].innerText || incoming[0].textContent;
+            // 然后在全局内找到该线条 并且设置该线条的出线元素为targetElementId
+            let connectElement: any = xmlDoc.querySelector(`#${flowId}`);
+            connectElement.setAttribute('sourceRef', sourceElementId);
+            // 给sourceElement设置新的出线信息
+            list.push(outgoing[i]);
+            let id = item.getAttribute('id');
+            if (id.includes('_isSimple')) {
+              let itemDi: any = xmlDoc.querySelector(`#${flowId}_di`);
+              let itemDiWaypoint = itemDi.getElementsByTagName('di:waypoint');
+              let incomingDi: any = xmlDoc.querySelector(`#${incomingId}_di`);
+              let waypoint: any = incomingDi.getElementsByTagName('di:waypoint');
+              let newWayList: any = [];
+              for (let i = 0; i < waypoint.length; i++) {
+                newWayList.push({
+                  x: waypoint[i].getAttribute('x'),
+                  y:
+                    i === waypoint.length - 1
+                      ? String(Number(waypoint[i].getAttribute('y')) + typeConfig[bpmnInclusive].renderer.attr.height / 2)
+                      : waypoint[i].getAttribute('y'),
+                });
+              }
+              for (let i = 0; i < itemDiWaypoint.length; i++) {
+                i != 0 && newWayList.push({ x: itemDiWaypoint[i].getAttribute('x'), y: itemDiWaypoint[i].getAttribute('y') });
+              }
+              for (let i = 0; i < newWayList.length; i++) {
+                if (itemDiWaypoint[i]) {
+                  itemDiWaypoint[i].setAttribute('x', newWayList[i].x);
+                  itemDiWaypoint[i].setAttribute('y', newWayList[i].y);
+                } else {
+                  let newWaypoint = xmlDoc.createElementNS('http://www.omg.org/spec/DD/20100524/DI', 'di:waypoint');
+                  newWaypoint.setAttribute('x', newWayList[i].x);
+                  newWaypoint.setAttribute('y', newWayList[i].y);
+                  itemDi.appendChild(newWaypoint);
+                }
+              }
+            }
+          }
+          list.map((item: any) => {
+            sourceElement[0].appendChild(item);
+          });
+
+          // 删除该网关及网关对应的出线元素
+          process.removeChild(item);
+          let flowId = incoming[0].innerText || incoming[0].textContent;
+          let connectElement: any = xmlDoc.querySelector(`#${flowId}`);
+          let connectElementDi: any = xmlDoc.querySelector(`#${flowId}_di`);
+          let itemDi: any = xmlDoc.querySelector(`#${item.getAttribute('id')}_di`);
+          process.removeChild(connectElement);
+          connectElementDi && plane.removeChild(connectElementDi);
+          itemDi && plane.removeChild(itemDi);
+        }
+      });
+      // 有颜色的线条颜色标签重新生成
+      if (isPreview) {
+        let list = process.getElementsByTagName('bpmn2:sequenceFlow');
+        let newList: any = [];
+        for (let i = 0; i < list.length; i++) {
+          let sourceRef = list[i].getAttribute('sourceRef');
+          let targetRef = list[i].getAttribute('targetRef');
+          if (
+            nodeMap &&
+            nodeMap.has(sourceRef) &&
+            nodeMap.has(targetRef) &&
+            nodeMap.get(sourceRef)?.type === '0' &&
+            (nodeMap.get(targetRef)?.type === '0' || nodeMap.get(targetRef)?.type === '1')
+          ) {
+            newList.push(list[i]);
+          }
+        }
+        newList.forEach((node: any) => {
+          process.removeChild(node);
+          process.appendChild(node);
+        });
+      }
+    }
+    return xmlDoc;
+  }
+  static createGateway(xmlDoc: any, type: 'parallel' | 'inclusion' | 'exclusive' | 'choose') {
+    if (type === 'inclusion' || type === 'choose') return xmlDoc.createElement('bpmn2:inclusiveGateway');
+    if (type === 'parallel') return xmlDoc.createElement('bpmn2:parallelGateway');
+    if (type === 'exclusive') return xmlDoc.createElement('bpmn2:exclusiveGateway');
+  }
+  static getAllGateway(xmlDoc: any) {
+    let gateWayList: any = [];
+    let parallelGateways = xmlDoc.getElementsByTagName('bpmn2:parallelGateway');
+    let inclusiveGateways = xmlDoc.getElementsByTagName('bpmn2:inclusiveGateway');
+    let exclusiveGateways = xmlDoc.getElementsByTagName('bpmn2:exclusiveGateway');
+    for (let i = 0; i < parallelGateways.length; i++) {
+      gateWayList.push(parallelGateways[i]);
+    }
+    for (let i = 0; i < inclusiveGateways.length; i++) {
+      gateWayList.push(inclusiveGateways[i]);
+    }
+    for (let i = 0; i < exclusiveGateways.length; i++) {
+      gateWayList.push(exclusiveGateways[i]);
+    }
+    return gateWayList;
+  }
+  /**
+   * 获取element下的进线元素
+   * @param element 元素
+   * @returns {Array} 进线元素数组
+   */
+  static getIncomingConnectByElement(element: any) {
+    let list: any = [];
+    let incomingElements = element?.getElementsByTagName(bpmnIncoming);
+    for (let i = 0; i < incomingElements?.length; i++) {
+      list.push(incomingElements[i]);
+    }
+    return list;
+  }
+  /**
+   * 获取element下的出线元素
+   * @param element 元素
+   * @returns {Array} 出线元素数组
+   */
+  static getOutgoingConnectByElement(element: any) {
+    let list: any = [];
+    let outgoingElements = element?.getElementsByTagName(bpmnOutgoing);
+    for (let i = 0; i < outgoingElements?.length; i++) {
+      list.push(outgoingElements[i]);
+    }
+    return list;
+  }
+  static getLastElementList(element: any, allElements: any[]) {
+    let lastList: any = [];
+    if (element && element.incoming && element.incoming.length) {
+      element.incoming.forEach((item: any) => {
+        return allElements.forEach((last: any) => {
+          // last中的出线若和 item的id相同 则获取到上一个节点的信息
+          if (last.outgoing && last.outgoing.length) {
+            let nextElement = last.outgoing.find((outgoing: any) => {
+              return outgoing.id === item.id;
+            });
+            if (nextElement) {
+              lastList.push(last);
+              return element;
+            }
+          }
+        });
+      });
+    }
+    return lastList;
+  }
+  static getNextElementList = (element: any, allElements: any[]) => {
+    let lastList: any = [];
+    if (element && element.outgoing && element.outgoing.length) {
+      element.outgoing.forEach((item: any) => {
+        let list = allElements.forEach((last: any) => {
+          // last中的出线若和 item的id相同 则获取到上一个节点的信息
+          if (last.incoming && last.incoming.length) {
+            let nextElement = last.incoming.find((incoming: any) => {
+              return incoming.id === item.id;
+            });
+            if (nextElement) {
+              lastList.push(last);
+              return element;
+            }
+          }
+        });
+        return list;
+      });
+    }
+    return lastList;
+  };
+  static getEndlessLoop = (bpmn: any) => {
+    // 获取 BPMN 图中的所有元素
+    let elementRegistry = bpmn.get('elementRegistry');
+    let elements = elementRegistry.getAll();
+    // 构建图数据结构
+    let graph = {};
+    let edgeMap = {}; // 用于存储边信息,key 是源节点,value 是目标节点和连线 id
+    elements.forEach(element => {
+      if (element.type === 'bpmn:SequenceFlow') {
+        const sourceId = element.source.id;
+        const targetId = element.target.id;
+        const edgeId = element.id;
+        if (!graph[sourceId]) graph[sourceId] = [];
+        graph[sourceId].push(targetId);
+        if (!edgeMap[sourceId]) edgeMap[sourceId] = [];
+        edgeMap[sourceId].push({ targetId, edgeId });
+      }
+    });
+    // 使用修改后的 DFS 检测所有环路并找出每个环路的最后一个进入环路的连线
+    function findAllCycles(graph, edgeMap) {
+      let visited = new Set();
+      let stack: any = [];
+      let stackSet: any = new Set();
+      let cycles: any = [];
+      function visit(node, startNode) {
+        if (stackSet.has(node)) {
+          // 找到环,记录从开始节点到环路的路径上的连线,并选择路径上的最后一条连线作为最后一个进入环路的连线
+          let pathEdges: any = [];
+          let isCycle = false;
+          for (let i = stack.indexOf(startNode); i < stack.length - 1; i++) {
+            let source = stack[i];
+            let target = stack[i + 1];
+            let edge = edgeMap[source].find(edge => edge.targetId === target);
+            if (edge) {
+              pathEdges.push(edge.edgeId);
+              if (source === node) {
+                isCycle = true;
+                break;
+              }
+            }
+          }
+          if (isCycle) cycles.push(pathEdges[pathEdges.length - 1]);
+          return;
+        }
+        if (visited.has(node)) return; // 已访问过,且无环
+        visited.add(node);
+        stack.push(node);
+        stackSet.add(node);
+        let neighbors = graph[node] || [];
+        for (const neighbor of neighbors) {
+          visit(neighbor, startNode);
+        }
+        stack.pop();
+        stackSet.delete(node);
+      }
+      for (const node in graph) {
+        visit(node, node);
+      }
+      return cycles;
+    }
+    return findAllCycles(graph, edgeMap);
+  };
+  // 网关坐标
+  static getGatewayWaypoints = (test: any, outGoingDi: any, outgoing: any, xmlDoc: any, connectWaypoint?: any) => {
+    let newList: any = [];
+    let id = outGoingDi.getAttribute('bpmnElement');
+    let gateway = xmlDoc.querySelector(`#${id}`);
+    let itemDi: any = xmlDoc.querySelector(`#${id}_di`);
+    let itemDiWaypoint = itemDi.getElementsByTagName('di:waypoint');
+    for (let i = 0; i < itemDiWaypoint.length; i++) {
+      newList.push({
+        x: itemDiWaypoint[i].getAttribute('x'),
+        y: itemDiWaypoint[i].getAttribute('y'),
+      });
+    }
+    // 获取网关子坐标 如果子元素还是网关则继续查找后代元素
+    let childrenElementId = gateway.getAttribute('targetRef');
+    let childrenElement = xmlDoc.querySelector(`#${childrenElementId}`);
+    if (childrenElementId.includes('Gateway_')) {
+      let childrenOutGoingList = childrenElement.getElementsByTagName(bpmnOutgoing);
+      let childrenOutGoingId = outgoing[0].innerText || outgoing[0].textContent;
+      let childrenOutGoingDi: any = xmlDoc.querySelector(`#${childrenOutGoingId}_di`);
+      let childrenList: any = this.getGatewayWaypoints(test, childrenOutGoingDi, childrenOutGoingList, xmlDoc);
+      if (!test.has(id)) test.add(id);
+      else newList.pop();
+      newList = newList.concat(childrenList);
+    } else if (connectWaypoint?.length > 0) {
+      // 如果是其它节点
+      for (let i = 0; i < connectWaypoint.length - 1; i++) {
+        newList.unshift({
+          x: connectWaypoint[i].getAttribute('x'),
+          y: connectWaypoint[i].getAttribute('y'),
+        });
+      }
+    }
+    return newList;
+  };
+  // 校验连线是否存在条件标签 不存则需要手动添加。
+  static verificationConnect = bpmn => {
+    let elementRegistry: any = bpmn.get('elementRegistry');
+    let modeling = bpmn.get('modeling');
+    let moddle = bpmn.get('moddle');
+    let connect = elementRegistry.getAll().filter((element: any) => {
+      if (is(element, 'bpmn:SequenceFlow') && !element?.businessObject?.conditionExpression && element.type != 'label') return element;
+    });
+    if (connect?.length > 0) {
+      connect.forEach(sequenceFlow => {
+        let conditionExpression = moddle.create('bpmn:FormalExpression', {
+          body: '${' + `${sequenceFlow.id}` + '}',
+        });
+        modeling.updateProperties(sequenceFlow, {
+          conditionExpression: conditionExpression,
+        });
+        if (sequenceFlow.label?.x) {
+          let label = elementRegistry.get(sequenceFlow.label.id);
+          label.x = sequenceFlow.label.x;
+          label.y = sequenceFlow.label.y;
+          modeling.updateProperties(label, {});
+        }
+      });
+    }
+  };
+  static gatewayTypeSettings = (bpmn: any, node: any) => {
+    let elementRegistry: any = bpmn.get('elementRegistry');
+    let allElement = elementRegistry.getAll();
+    let jnpfData = bpmn.get('jnpfData');
+    allElement.map((element: any) => {
+      if (hasGatewayType.has(element.wnType)) {
+        let sourceElement = element.incoming[0]?.source;
+        jnpfData.setValue(sourceElement.id, { divideRule: element.wnType });
+        if(sourceElement.wmType != typeOutside && node[sourceElement.id])  node[sourceElement.id].divideRule = element.wnType;
+      } else if (element.wnType != typeTrigger) {
+        let sourceElement = element.incoming[0]?.source;
+        if (sourceElement?.id && !hasGatewayType.has(sourceElement.wnType) && node[sourceElement.id]) {
+          jnpfData.setValue(sourceElement.id, { divideRule: typeInclusion });
+          node[sourceElement.id].divideRule = typeInclusion;
+        }
+      }
+    });
+    return node;
+  };
+  // 自动生成网关位置
+  static handleCreateGatewayBounds = (xmlDoc, gatewayId, plane) => {
+    let gatewayBpmnEdge = xmlDoc.createElement('bpmndi:BPMNShape');
+    let waypoint = xmlDoc.createElement('dc:Bounds');
+    gatewayBpmnEdge.setAttribute('id', gatewayId + '_di');
+    gatewayBpmnEdge.setAttribute('bpmnElement', gatewayId);
+    waypoint.setAttribute('x', '1');
+    waypoint.setAttribute('y', '1');
+    waypoint.setAttribute('width', '1');
+    waypoint.setAttribute('height', '1');
+    gatewayBpmnEdge.appendChild(waypoint);
+    plane.appendChild(gatewayBpmnEdge);
+  };
+  // label生成位置
+  static updateLabelWaypoints: any = (connection, elementRegistry, jnpfData, type = 0) => {
+    let targetElement = elementRegistry.get(connection.target?.id);
+    let sourceElement = elementRegistry.get(connection.source?.id);
+    let labelCenter = getExternalLabelMid(connection);
+    if (connection?.label && targetElement && sourceElement)
+      labelCenter = this.updateLabelCenter(targetElement, sourceElement, labelCenter, connection, jnpfData.data?.layout?.value, type);
+    return labelCenter;
+  };
+  static getNewLabelWaypoints: any = (connection, elementRegistry, jnpfData, type = 0) => {
+    let targetElement = elementRegistry.get(connection.target.id);
+    let sourceElement = elementRegistry.get(connection.source.id);
+    let labelCenter = getExternalLabelMid(connection); 
+    labelCenter = this.updateLabelCenter(targetElement, sourceElement, labelCenter, connection, jnpfData.data?.layout?.value, type);
+    return labelCenter;
+  };
+  static updateLabelCenter = (targetElement, sourceElement, labelCenter, connection, layoutType, type) => {
+    let connectWaypointsStart = connection.waypoints[0];
+    let connectWaypointsEnd = connection.waypoints[connection.waypoints.length - 1];
+    let defaultLabelCenter = labelCenter;
+    if (layoutType === 'horizontal' && type != 1) {
+      if (targetElement?.incoming?.length > 1) {
+        labelCenter = {
+          x: sourceElement.x + sourceElement.width + 16,
+          y: sourceElement.y + sourceElement.height / 2 - 35,
+        };
+        // 如果线条箭头y坐标在targetElement 顶部或者底部 取线条的顶部或者底部位置
+        if (connectWaypointsEnd?.y === targetElement.y || connectWaypointsEnd?.y === targetElement.y + targetElement.height) {
+          labelCenter.y = connection.waypoints[1]?.y - 35;
+        }
+        if (connectWaypointsStart?.y === sourceElement.y) {
+          labelCenter = {
+            x: connectWaypointsStart.x - 70,
+            y: connectWaypointsStart.y - 35,
+          };
+        }
+        if (connectWaypointsStart.y === sourceElement.y + sourceElement.height) {
+          labelCenter = {
+            x: connectWaypointsStart.x - 70,
+            y: connectWaypointsStart.y + 10,
+          };
+        }
+        if (connectWaypointsStart.x === sourceElement.x) {
+          labelCenter = {
+            x: connectWaypointsStart.x - 140,
+            y: connectWaypointsStart.y - 35,
+          };
+        }
+        if (sourceElement.outgoing.length > 1) {
+         labelCenter = defaultLabelCenter 
+        }
+      } else {
+        // 根据箭头坐标位置不同显示不同的坐标
+        labelCenter = {
+          x: targetElement.x - 140,
+          y: targetElement.y + targetElement.height / 2 - 35,
+        };
+        // 上方
+        if (connectWaypointsEnd?.y === targetElement.y)
+          labelCenter = {
+            x: connectWaypointsEnd?.x - 70,
+            y: connectWaypointsEnd?.y - 35,
+          };
+        // 下方
+        if (connectWaypointsEnd?.y === targetElement.y + targetElement.height)
+          labelCenter = {
+            x: connectWaypointsEnd?.x - 70,
+            y: connectWaypointsEnd?.y + 10,
+          };
+        // 右方
+        if (connectWaypointsEnd?.x === targetElement.x + targetElement.width) {
+          labelCenter = {
+            x: connectWaypointsEnd?.x + 10,
+            y: connectWaypointsEnd?.y - 35,
+          };
+        }
+        // 左方
+        if (connectWaypointsEnd?.x === targetElement.x) {
+          labelCenter = {
+            x: connectWaypointsEnd?.x - 140,
+            y: connectWaypointsEnd?.y - 35,
+          };
+        }
+      }
+    }
+    if ((layoutType === 'vertical' && type != 1) || type === 1) {
+      if (targetElement?.incoming?.length > 1) {
+        labelCenter = {
+          x: connectWaypointsStart.x - 70,
+          y: sourceElement.y + sourceElement.height + 10,
+        };
+        // 左右
+        if (connectWaypointsStart?.x === sourceElement.x || connectWaypointsStart?.x === sourceElement.x + sourceElement.width) {
+          labelCenter = {
+            x: connection.waypoints[1].x - 70,
+            y: connection.waypoints[2]?.y > connection.waypoints[1]?.y ? connection.waypoints[1].y + 35 : connection.waypoints[1].y - 35,
+          };
+        }
+        //上方
+        if (connectWaypointsStart?.y === sourceElement.y) {
+          labelCenter = {
+            x: connectWaypointsStart.x - 70,
+            y: sourceElement.y - 35,
+          };
+        }
+        if (sourceElement.outgoing.length > 1) {
+         labelCenter = defaultLabelCenter 
+        }
+      } else {
+        labelCenter = {
+          x: targetElement.x + targetElement.width / 2 - 20,
+          y: targetElement.y - 60,
+        };
+        // 上方
+        if (connectWaypointsEnd?.y === targetElement.y)
+          labelCenter = {
+            x: connectWaypointsEnd?.x - 70,
+            y: connectWaypointsEnd?.y - 35,
+          };
+        // 下方
+        if (connectWaypointsEnd?.y === targetElement.y + targetElement.height)
+          labelCenter = {
+            x: connectWaypointsEnd?.x - 70,
+            y: connectWaypointsEnd?.y + 10,
+          };
+        // 左方
+        if (connectWaypointsEnd?.x === targetElement.x)
+          labelCenter = {
+            x: targetElement.x - 140,
+            y: connectWaypointsEnd?.y - 35,
+          };
+        // 右方
+        if (connectWaypointsEnd.x === targetElement?.x + targetElement.width) {
+          labelCenter = {
+            x: connectWaypointsEnd?.x + 10,
+            y: connectWaypointsEnd?.y - 35,
+          };
+        }
+      }
+    }
+    return labelCenter;
+  };
+}

+ 31 - 0
lib/utils/uuidUtil.ts

@@ -0,0 +1,31 @@
+const hexList: string[] = [];
+for (let i = 0; i <= 15; i++) {
+  hexList[i] = i.toString(16);
+}
+
+export function buildUUID(): string {
+  let uuid = '';
+  for (let i = 1; i <= 36; i++) {
+    if (i === 9 || i === 14 || i === 19 || i === 24) {
+      uuid += '-';
+    } else if (i === 15) {
+      uuid += 4;
+    } else if (i === 20) {
+      uuid += hexList[(Math.random() * 4) | 8];
+    } else {
+      uuid += hexList[(Math.random() * 16) | 0];
+    }
+  }
+  return uuid.replace(/-/g, '');
+}
+export function buildBitUUID(length = 6): string {
+  return buildUUID().substring(0, length);
+}
+
+let unique = 0;
+export function buildShortUUID(prefix = ''): string {
+  const time = Date.now();
+  const random = Math.floor(Math.random() * 1000000000);
+  unique++;
+  return prefix + '_' + random + unique + String(time);
+}

+ 78 - 0
package.json

@@ -0,0 +1,78 @@
+{
+  "name": "@jnpf/bpmn",
+  "version": "1.0.1",
+  "description": "JNPF快速开发平台bpmn组件",
+  "keywords": [
+    "vue",
+    "bpmn",
+    "jnpf-bpmn",
+    "jnpf",
+    "jnpfsoft",
+    "福建引迈信息技术有限公司"
+  ],
+  "author": {
+    "name": "福建引迈信息技术有限公司",
+    "email": "support@yinmaisoft.com",
+    "url": "https://www.jnpfsoft.com"
+  },
+  "type": "module",
+  "scripts": {
+    "serve": "vite",
+    "build": "vue-tsc -b && vite build"
+  },
+  "files": [
+    "dist",
+    "lib/**/*.d.ts",
+    "lib/**/*.js"
+  ],
+  "main": "./dist/index.umd.cjs",
+  "module": "./dist/index.js",
+  "exports": {
+    ".": {
+      "types": "./src/components/bpmn/index.ts",
+      "development": "./src/components/bpmn/index.ts",
+      "default": "./dist/index.umd.cjs"
+    },
+    "./style": {
+      "development": "./src/components/bpmn/src/style/index.scss",
+      "default": "./dist/style.css"
+    },
+    "./config": {
+      "types": "./lib/config/index.js",
+      "development": "./lib/config/index.ts",
+      "default": "./lib/config/index.js"
+    },
+    "./utils": {
+      "types": "./lib/utils/index.ts",
+      "development": "./lib/utils/index.ts",
+      "default": "./lib/utils/index.js"
+    }
+  },
+  "publishConfig": {
+    "access": "public"
+  },
+  "dependencies": {
+    "@ant-design/icons-vue": "^7.0.1",
+    "ant-design-vue": "^4.2.6",
+    "bpmn-js": "16.3.2",
+    "bpmn-js-properties-panel": "5.7.0",
+    "camunda-bpmn-moddle": "6.1.2",
+    "diagram-js": "11.9.1",
+    "diagram-js-minimap": "4.1.0",
+    "inherits-browser": "^0.1.0",
+    "lodash-es": "^4.17.21",
+    "min-dash": "^4.2.3",
+    "min-dom": "^4.2.1",
+    "tiny-svg": "^3.1.3",
+    "vue": "^3.5.17"
+  },
+  "devDependencies": {
+    "@types/node": "^22.16.3",
+    "@vitejs/plugin-vue": "^5.2.4",
+    "sass": "^1.89.2",
+    "terser": "^5.43.1",
+    "typescript": "^5.8.3",
+    "vite": "^5.4.19",
+    "vue-tsc": "^2.2.12"
+  }
+}

+ 13 - 0
prettier.config.js

@@ -0,0 +1,13 @@
+module.exports = {
+  printWidth: 160,
+  semi: true,
+  vueIndentScriptAndStyle: true,
+  singleQuote: true,
+  trailingComma: 'all',
+  proseWrap: 'never',
+  htmlWhitespaceSensitivity: 'strict',
+  endOfLine: 'auto',
+  bracketSameLine: true,
+  jsxBracketSameLine: true,
+  arrowParens: 'avoid',
+};

部分文件因为文件数量过多而无法显示