bpmn-js应该是使用最广最普遍的一个流程设计器。前段时间自己写了个流程设计器,为了更好的完善自己写的设计器,所以拿bpmn-js来进行一次研究。
bpmn-js 网上各种帖子,但大部分都是停留在测试、学习层面,基本很少有结合企业业务来扩展的,所以我这里结合业务记录下bpmn-js的使用
- 流程会签:就是流程运转过程中需要提交给多个人进行审批,每个人可以发表意见,而且会签也存在一票否决、比例决策、全票决策等策略。为了实现该功能,第一步应该是要绘制这样一个节点,表示进入到会签,后台根据这个特殊节点进行代码处理。
直接上图
要实现这个功能要完成以下
- 扩展Palette,定义customPalette,将会签任务节点放入PalettePanel区域
- 定义会签任务相关属性定义
实现customPalette大概文件如下:
第一步定义: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
});
到此就完成了流程会签节点的绘制扩展了。下一节再讲会签节点的属性设置信息