bpmn.js + vue + vuex的业务实现-根据状态而呈现节点不同的颜色或图片

1,064 阅读3分钟

前言

上一章节我们介绍了关于bpmn.js如何与后端交互,实现特定的线用特定的颜色, 要是不了解的小伙请移步 bpmn.js + vue + vuex的业务实现-与后端交互

这一章节主要讲解的是关于bpmn.js如何根据状态而呈现节点不同的颜色或图片

开源链接

gitee:gitee.com/zh-howe/bpm…

根据状态而呈现不同的颜色或图片

在上一章节中已经与后端交互,实现了赋予不同节点不同的class,我们可以用同样的方法添加不同的图片和颜色

设置颜色

//.customRenderer.less
.djs-visual.running {
    path {
      display: none !important;
    }
    rect {
      fill: #1890ff !important;
      stroke: #1890ff !important;
    }
    .djs-label {
      font-weight: bold !important;
      fill: #fff !important;
    }
  }
//.customRenderer.less

// 给节点添加class以更改样式
  let className = this.runningActivitiesIds.includes(element.id)
    ? 'djs-visual running'
    : this.highLightedActivitiesIds.includes(element.id)
    ? 'djs-visual Activities'
    : 'djs-visual unActivities';
  svgAttr(parentNode, 'class', className);

设置图片

//customRenderer.js

// 计数器,当匹配到就加1,匹配不到为0就默认图片
      let count = 0;
      customConfig.map(item => {
        if (element.businessObject.name.includes(item.labelKey)) {
          count++;
          const customIcon = svgCreate('image', {
            ...attr,
            href: isFinish ? item.url : item.unUrl,
          });
          svgAppend(parentNode, customIcon);
          return customIcon;
        }
        if (!count) {
          const customIcon = svgCreate('image', {
            ...attr,
            href: isFinish
              ? require('@/images/workflow/approve.png')
              : require('@/images/workflow/unapprove.png'),
          });
          svgAppend(parentNode, customIcon);
          return customIcon;
        }
      });

完整的customRenderer.js


import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';

import {
  append as svgAppend,
  attr as svgAttr,
  create as svgCreate,
  select as svgSelect,
  selectAll as svgSelectAll,
  clone as svgClone,
  clear as svgClear,
  remove as svgRemove,
} from 'tiny-svg';
import store from '@/store';
import { customConfig, createArr } from '../utils/util';
import { is } from 'bpmn-js/lib/util/ModelUtil';
const HIGH_PRIORITY = 1500;

export default class CustomRenderer extends BaseRenderer {
  constructor(eventBus, bpmnRenderer, modeling) {
    super(eventBus, HIGH_PRIORITY);
    this.bpmnRenderer = bpmnRenderer;
    this.modeling = modeling;
    // 已完成的id数组
    this.highLightedActivitiesIds = store.getters.highLightedActivitiesIds;
    // 正在进行的id数组
    this.runningActivitiesIds = store.getters.runningActivitiesIds;
  }
  canRender(element) {
    // ignore labels
    return !element.labelTarget;
  }
  /**
   * 自定义连接线的样式
   * @param {*} parentNode 当前元素的svgNode
   * @param {*} element
   * @returns
   */
  drawConnection(parentNode, element) {
    const line = this.bpmnRenderer.drawConnection(parentNode, element);
    // 给节点添加class以更改样式
    let className = this.highLightedActivitiesIds.includes(element.id)
      ? 'djs-visual Activities'
      : this.runningActivitiesIds.includes(element.id)
      ? 'djs-visual running'
      : 'djs-visual unActivities';
    console.log(className);
    svgAttr(parentNode, 'class', className);

    return line;
  }
  /**
   * 自定义节点图形
   * @param {*} parentNode 当前元素的svgNode
   * @param {*} element
   * @returns
   */
  drawShape(parentNode, element) {
    const shape = this.bpmnRenderer.drawShape(parentNode, element);
    const { type, width, height, x, y } = element;
    if (type === 'bpmn:ExclusiveGateway') {
      svgAttr(parentNode, 'class', 'djs-visual gateway-grey');
      // 计数器,当匹配到就加1,匹配不到为0就默认图片
      // 判断当前的id是否在已完成或正在进行的id数组里,以添加不同的图片
      const isFinish =
        this.highLightedActivitiesIds.includes(element.id) ||
        this.runningActivitiesIds.includes(element.id);
      const attr = { x: -7, y: 5, width: 65, height: 42 };
      const customIcon = svgCreate('image', {
        ...attr,
        href: isFinish
          ? require('@/images/workflow/gateway.png')
          : require('@/images/workflow/ungateway.png'),
      });
      svgAppend(parentNode, customIcon);
      return customIcon;
    }
    // 根据不同节点添加相应图片
    if (type == 'bpmn:UserTask' && !!element.businessObject.name) {
      // 获取label
      const text = svgSelect(parentNode, 'text');
      const tspan = svgSelectAll(text, 'tspan');
      // 偏移label
      const tspanText = element.businessObject.name;
      let len = tspanText.length;
      // 每五个字符换行
      if (len > 5) {
        let textArr = createArr(tspanText, 0, 5, 5);
        textArr.map((item, index) => {
          if (tspan.length >= textArr.length) {
            tspan[index].innerHTML = textArr[index];
          } else {
            const num = textArr.length - tspan.length;
            let y = svgAttr(tspan[tspan.length - 1], 'y');
            let ts = svgCreate('tspan', {
              x: 30,
              y: parseFloat(y) + 14.4,
            });
            svgAppend(text, ts);
            const newTspan = svgSelectAll(text, 'tspan');
            newTspan[index].innerHTML = textArr[index];
          }
        });
        const newTspan = svgSelectAll(text, 'tspan');
        newTspan.map(item => {
          svgAttr(item, 'x', 35);
        });
      } else {
        svgAttr(tspan[0], 'x', 35);
      }
      // if (tspan?.length > 1) {
      //   svgAttr(tspan[0], 'x', 40);
      //   svgAttr(tspan[1], 'x', 25);
      //   // 重写label,换行
      //   if (tspanText.includes('(')) {
      //     console.log(tspanText);
      //     tspan[0].innerHTML = tspanText.split('(')[0];
      //     tspan[1].innerHTML = '(' + tspanText.split('(')[1];
      //   } else if (tspanText.includes('(')) {
      //     console.log(tspanText);
      //     tspan[0].innerHTML = tspanText.split('(')[0];
      //     tspan[1].innerHTML = '(' + tspanText.split('(')[1];
      //   }
      //   if (tspanText.split('(')[1]?.length < 5 || tspanText.split('(')[1]?.length < 5) {
      //     svgAttr(tspan[1], 'x', 35);
      //   }
      // } else {
      //   svgAttr(tspan[0], 'x', 35);
      // }
      // 判断当前的id是否在已完成或正在进行的id数组里,以添加不同的图片
      const isFinish =
        this.highLightedActivitiesIds.includes(element.id) ||
        this.runningActivitiesIds.includes(element.id);
      const attr = { x: width / 20, y: height / 3, width: 25, height: 25 };
      // 给节点添加class以更改样式
      let className = this.runningActivitiesIds.includes(element.id)
        ? 'djs-visual running'
        : this.highLightedActivitiesIds.includes(element.id)
        ? 'djs-visual Activities'
        : 'djs-visual unActivities';
      svgAttr(parentNode, 'class', className);
      // 计数器,当匹配到就加1,匹配不到为0就默认图片
      let count = 0;
      customConfig.map(item => {
        if (element.businessObject.name.includes(item.labelKey)) {
          count++;
          const customIcon = svgCreate('image', {
            ...attr,
            href: isFinish ? item.url : item.unUrl,
          });
          svgAppend(parentNode, customIcon);
          return customIcon;
        }
        if (!count) {
          const customIcon = svgCreate('image', {
            ...attr,
            href: isFinish
              ? require('@/images/workflow/approve.png')
              : require('@/images/workflow/unapprove.png'),
          });
          svgAppend(parentNode, customIcon);
          return customIcon;
        }
      });
    }

    return shape;
  }

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

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

完整文件目录展示

image.png

  • custom 文件用于编写自定义renderer
  • utils用于存储图片及文字数据
  • customRenderer.vue是用于渲染流程图

系列文章目录