因公司需求节点可以自定义且节点非常多,如果将所有节点放在左侧菜单中会非常难以定位需要的task
故设计右键幕布空白处弹出节点选择菜单
菜单定位
首先我们给canvas的父级节点加一个id
document.getElementById('content').oncontextmenu=function(ev){
_this.actionName = ''
let clintX = ev.clientX; //ev获取的只是屏幕可视范围的x,y值
let clintY = ev.clientY;
// let scollTop = document.documentElement.scrollTop|| document.body.scrollTop; //当有下拉条的时候必须加上当前屏幕不可视范围的left,和top值
// let scollLeft = document.documentElement.scrollLeft || document.body.scrollLeft
_this.mouse.x = clintX
_this.mouse.y = clintY
document.getElementById('actionMenu').style.left=(clintX - 200)+'px';
document.getElementById('actionMenu').style.top=(clintY - 68)+'px';
document.getElementById('actionMenu').style.display='block';
return false; //屏蔽右键菜单
}
然后监听幕布右键菜单事件弹出节点列表 这里通过右键时的鼠标坐标来定位菜单位置 然后将鼠标坐标保存起来用于点击task节点后的定位
菜单内容
管理节点的页面,task可以自定义task的图标和颜色
菜单样式
点击task
addAction(item,event){
//先隐藏task菜单
document.getElementById('actionMenu').style.display='none';
this.$nextTick(e =>{
let elementRegistry = this.bpmnModeler.get('elementRegistry')
const elementFactory = this.bpmnModeler.get("elementFactory")
const modeling = this.bpmnModeler.get("modeling")
const canvas = this.bpmnModeler.get("canvas")
const rootElement =canvas.getRootElement()
let x_canvas
let y_canvas
if (canvas._cachedViewbox){
this.cachedViewbox = canvas._cachedViewbox
x_canvas = canvas._cachedViewbox.x
y_canvas = canvas._cachedViewbox.y
} else {
x_canvas = this.cachedViewbox.x
y_canvas = this.cachedViewbox.y
}
//此处考虑到如果图有缩放 那么鼠标在中id为content的div中的坐标就和鼠标在幕布中的坐标不同了 所以通过缩放值计算缩放后的坐标
let x = x_canvas + (this.mouse.x / canvas.zoom())
let y = y_canvas + (this.mouse.y / canvas.zoom())
//创建节点
let branchShape = elementFactory.createShape({
type: "bpmn:Task",
id: 'element_id_'+ new Date().getTime()
})
branchShape.businessObject.name = item.actionName
branchShape.businessObject.actionId = item.id
modeling.createShape(
branchShape,
{
x: x,
y: y
},
rootElement
)
})
},
显示效果
渲染
通过 businessObject 中 自定义字段判断task需要显示什么图标什么颜色
drawShape(parentNode, element) {
const shape = this.bpmnRenderer.drawShape(parentNode, element);
const type = element.type; // 获取到类型
// 所有节点都会走这个函数,所以此时只限制,需要自定义的才去自定义,否则仍显示bpmn默认图标
const businessObject = getBusinessObject(element);
const { actionId } = businessObject;
if (customElements.includes(type) && actionId) {
let tasks = JSON.parse(sessionStorage.getItem('tasks'))
let task = tasks.find(e =>{
return e.id === actionId
})
const {url, attr} = customConfig[task.icon];
const customIcon = svgCreate('image', {...attr, href: url});
element['width'] = 56;
element['height'] = 56;
svgAppend(parentNode, customIcon);
let textNode = parentNode.childNodes[1]
console.log(textNode)
textNode.childNodes.forEach(e =>{
// e.setAttribute('y',parseInt(e.getAttribute('y')) + 52)
e.style.display = 'none'
})
// 下方为更改节点名称位置
if (element.businessObject.name) {
const text = svgCreate('text', {
x: attr.x + 26,
y: attr.y + attr.height + 20, //位置可以随意调,我理解此时的attr.y 是此时元素的左上角纵坐标
'font-size': '12',
'text-anchor': 'middle',
'dominant-baseline':'middle'
});
text.innerHTML = element.businessObject.name;
svgAppend(parentNode, text);
}
if (!element.businessObject.name){
element.businessObject.name = task.actionName
}
//修改边框颜色
if(task.color){
shape.style.setProperty('fill', task.color)
}
return customIcon;
}
return shape;
}
这里参考 # LinDaiDai_霖呆呆 的文章 juejin.cn/post/684490…