bpmn.js鼠标右键菜单选择节点
因公司需求节点可以自定义且节点非常多,如果将所有节点放在左侧菜单中会非常难以定位需要的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; }
作者:围_城
链接:https://juejin.cn/post/7025521883822948366