天天看點

bpmn-js 改造(一)添加會簽節點

bpmn-js應該是使用最廣最普遍的一個流程設計器。前段時間自己寫了個流程設計器,為了更好的完善自己寫的設計器,是以拿bpmn-js來進行一次研究。

bpmn-js 網上各種文章,但大部分都是停留在測試、學習層面,基本很少有結合企業業務來擴充的,是以我這裡結合業務記錄下bpmn-js的使用

  • 流程會簽:就是流程運轉過程中需要送出給多個人進行審批,每個人可以發表意見,而且會簽也存在一票否決、比例決策、全票決策等政策。為了實作該功能,第一步應該是要繪制這樣一個節點,表示進入到會簽,背景根據這個特殊節點進行代碼處理。

直接上圖

bpmn-js 改造(一)添加會簽節點

要實作這個功能要完成以下

  • 擴充Palette,定義customPalette,将會簽任務節點放入PalettePanel區域
  • 定義會簽任務相關屬性定義

實作customPalette大概檔案如下:

bpmn-js 改造(一)添加會簽節點

第一步定義:CustomPalette.js

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


  // 這個是繪制palette的核心,函數名不要變
  getPaletteEntries() {
    const elementFactory = this.elementFactory;
    const create = this.create;

    function dragEventFactory(type) {
      return function (event) {
        const taskShape = elementFactory.create('shape', {
          type: type
        });
        create.start(event, taskShape);
      };
    }

    return {
      'create.user-sign': {
        title: '流程會簽',    // 滑鼠懸浮到節點上顯示的文字
        className: 'goForm gf-huiqianrenwu',   // 樣式名
        action: {      // 操作該節點時會觸發的事件,此時隻注冊一個拖動事件即可,否則拖動時沒有效果
          dragstart: dragEventFactory('bpmn:Task')
        }
      }
    };
  }
}
CustomPalette.$inject = [
  'create',
  'elementFactory',
  'palette'
];

           

第二步:定義CustomRenderer.js

import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';
import { customElements, customConfig } from './util';
import { append as svgAppend, create as svgCreate } from 'tiny-svg';
import SVGUtil from "../utils/svgUtil";
import GoUserSignNode from "../bpmnExtend/goFlowNode/goUserSignNode";
import {GoingUtils} from 'goingutils';

const HIGH_PRIORITY = 1500; // 最高優先級

export default class CustomRenderer extends BaseRenderer {
  // 繼承BaseRenderer
  constructor(eventBus, bpmnRenderer) {
    super(eventBus, HIGH_PRIORITY);
    this.bpmnRenderer = bpmnRenderer;
  }

  canRender(element) {
    return !element.labelTarget;
  }

  _addSvgSymbol(nodeItem) {
    let symbolNode = SVGUtil.create("use");
    let A = 0;
    // @ts-ignore
    SVGUtil.setAttrs(symbolNode, {"xlink:href": "#"+nodeItem.iconName,fill: "#666"});
    return symbolNode;
  };

  _addBackCircleNode(nodeItem) {
    let outUseNode = SVGUtil.create("use");
    SVGUtil.setAttrs(outUseNode, {
      "xlink:href": "#icon_back_circle_"+nodeItem.getId()
    });
    let rectNode = SVGUtil.create("rect");
    SVGUtil.setAttrs(rectNode, {
      "rx": nodeItem.getAttr("rx"),
      "ry": nodeItem.getAttr("ry"),
      "width":nodeItem.getAttr("width"),
      "height":nodeItem.getAttr("height"),
      "fill": nodeItem.getAttr("nodeBgColor"),
      "id": "icon_back_circle_"+nodeItem.getId(),
      "stroke": nodeItem.getAttr("stroke"),
      "stroke-width":nodeItem.getAttr("strokeWidth")
    });
    outUseNode.appendChild(rectNode);
    return outUseNode;
  };

  /**
   * 添加節點的字
   * @param gGroupNode
   * @param element
   */
  addTextNode(gGroupNode,element){
    let tspan = SVGUtil.create("tspan");
    SVGUtil.setAttrs(tspan, {
      "x": "28",
      'y':'43',
    });
    tspan.innerHTML=(element.businessObject&&element.businessObject.name)||"會簽節點";
    let textNode = SVGUtil.create("text");
    SVGUtil.setAttrs(textNode, {
      "class": "djs-label",
      'font-size':'12px',
      'font-weight':'normal',
      'fill':'black',
      "font-family": "Arial, sans-serif",
    });
    textNode.appendChild(tspan);
    gGroupNode.appendChild(textNode);
    element.textNode=textNode;
  }

  /**
   * 添加用于顯示的節點
   * @param showNodeParent
   * @param element
   * @returns {g}
   */
  addShowNode(showNodeParent,element) {
    element.nodeType="userSign";

    function getIconGgroupNode(nodeItem) {
      // @ts-ignore
      let id = `node_icon_use_` + nodeItem.getId();
      let g = SVGUtil.create("g");
      SVGUtil.setAttrs(g, {
        fill: "#f00",
        id: id
      });
      return g;
    }

    let nodeItem=new GoUserSignNode({id:'user-sign-node-'+GoingUtils.getUUid(10)});
    let gGroupNode = getIconGgroupNode(nodeItem);
    //擷取節點的背景元素
    gGroupNode.appendChild(this._addBackCircleNode(nodeItem));
    //添加節點svg圖元素
    gGroupNode.appendChild(this._addSvgSymbol(nodeItem));
    //添加字型
    this.addTextNode(gGroupNode,element);
    //将最外層的group元素放到容器中
    svgAppend(showNodeParent, gGroupNode);
    return gGroupNode;
  }

  /**
   * 開始自定義圖形繪制
   * @param parentNode
   * @param element
   * @returns {*}
   */
  drawShape(parentNode, element) {
    const type = element.type; // 擷取到類型
    console.log(element,"------element--------")
    // 所有節點都會走這個函數,是以此時隻限制,需要自定義的才去自定義,否則仍顯示bpmn預設圖示
    if (customElements.includes(type)) {
      const {url, attr} = customConfig['cake'];
      const gGroupNode= this.addShowNode(parentNode,element);
      // const customIcon = svgCreate('image', {...attr, href: url});
      element['width'] = attr.width;
      element['height'] = attr.height;
      svgAppend(parentNode, gGroupNode);
      return gGroupNode;
    }
    const shape = this.bpmnRenderer.drawShape(parentNode, element);
    return shape;
  }


  getShapePath(shape) {
    return this.bpmnRenderer.getShapePath(shape);
  }
}

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

           

第三步:定義index.js

import CustomPalette from './CustomPalette';
import CustomRenderer from './CustomRenderer';

export default {
  __init__: ['customPalette', 'customRenderer'],
  customPalette: ['type', CustomPalette],
  customRenderer: ['type', CustomRenderer]
};

           

第三步:定義util.js

// 自定義元素的類型,此時我們隻需要自定義一種節點,是以數組隻有一個元素
const customElements = ['bpmn:Task'];
const customConfig = {
  // 自定義元素的配置
  cake: {
    url: "",
    attr: {x: 0, y: 0, width: 98, height: 80}
  }
};

export {customElements, customConfig};

           

第五:則在調用的地方申明下:

import customModule from '../customPalette';
...
  const canvas = this.$refs.canvas;
      // 生成執行個體
      this.bpmnModeler = new BpmnModeler({
        additionalModules: [ customModule ],
        container: canvas
      });
           

到此就完成了流程會簽節點的繪制擴充了。下一節再講會簽節點的屬性設定資訊

繼續閱讀