前面文章介绍了bpmn-js的基本使用,本篇介绍对左侧工具栏的自定义修改。
自定义Palette
bpmn-js默认的Palette有不怎么用的,项目要求左侧把常用的展示出来,所以我们只能自定义写个新的来代替默认的Palette。
以下代码基于搭建好的React v18项目和引入了bpmn-js,语法可根据自己项目修改
1. 创建customBpmn文件夹,在customBpmn文件夹下新建custom文件夹和index.tsx文件,在custom文件夹下新建CustomPalette.tsx和index.tsx文件。最终目录结构如下图所示
2. custom/CustomPalette.tsx文件,内容如下:
import { assign } from "min-dash"; export default function PaletteProvider( palette, create, elementFactory, handTool, lassoTool, spaceTool, globalConnect, translate ) { this.create = create; this.elementFactory = elementFactory; this.handTool = handTool; this.lassoTool = lassoTool; this.spaceTool = spaceTool; this.globalConnect = globalConnect; this.translate = translate; palette.registerProvider(this); } PaletteProvider.$inject = [ "palette", "create", "elementFactory", "handTool", "lassoTool", "spaceTool", "globalConnect", "translate" ]; PaletteProvider.prototype.getPaletteEntries = function (element) { const { create, elementFactory, handTool, lassoTool, spaceTool, globalConnect, translate } = this; function createAction(type, group, className, title, options?: any) { function createListener(event) { var shape = elementFactory.createShape(assign({ type: type }, options)); if (options) { shape.businessObject.di.isExpanded = options.isExpanded; } create.start(event, shape, { hints: { autoSelect: [ shape ] } }); } var shortType = type.replace(/^bpmn:/, ""); return { group: group, className: className, title: title || translate("Create {type}", { type: shortType }), action: { dragstart: createListener, click: createListener } }; } return { "lasso-tool": { group: "tools", className: "bpmn-icon-lasso-tool", title: "Activate the lasso tool", action: { click: function (event) { lassoTool.activateSelection(event); } } }, "tool-separator": { group: "tools", separator: true }, "create.start-event": createAction( "bpmn:StartEvent", "event", "bpmn-icon-start-event-none", "开始事件 StartEvent" ), "create.end-event": createAction( "bpmn:EndEvent", "event", "bpmn-icon-end-event-none", "结束事件 EndEvent" ), "create.task": createAction( "bpmn:Task", "activity", "bpmn-icon-task", "任务 Task" ), "create.service-task": createAction( "bpmn:ServiceTask", "activity", "bpmn-icon-service-task", "服务任务 ServiceTask" ), "create.script-task": createAction( "bpmn:ScriptTask", "activity", "bpmn-icon-script-task", "脚本任务 ScriptTask" ), "create.receive-task": createAction( "bpmn:ReceiveTask", "activity", "bpmn-icon-receive-task", "接收任务 ReceiveTask" ), "create.subprocess-collapsed": createAction( "bpmn:SubProcess", "activity", "bpmn-icon-subprocess-collapsed", "子流程(折叠) SubProcess(collapsed)", {isExpanded: false} ), "create.subprocess-expanded": createAction( "bpmn:SubProcess", "activity", "bpmn-icon-subprocess-expanded", "子流程(展开) SubProcess(expanded)", {isExpanded: true} ), "create.call-activity": createAction( "bpmn:CallActivity", "activity", "bpmn-icon-call-activity", "调用活动 CallActivity" ), "create.exclusive-gateway": createAction( "bpmn:ExclusiveGateway", "gateway", "bpmn-icon-gateway-xor", "排他网关 ExclusiveGateway" ), "create.parallel-gateway": createAction( "bpmn:ParallelGateway", "gateway", "bpmn-icon-gateway-parallel", "并行网关 ParallelGateway" ), "create.inclusive-gateway": createAction( "bpmn:InclusiveGateway", "gateway", "bpmn-icon-gateway-none", "包容网关 InclusiveGateway" ), }; };Platte展示的shape就是最后return输出的那个字典数据定义的,一个shape对应的数据格式如下:
"create.start-event": { group: "event", className: "bpmn-icon-start-event-none", title: "开始事件 StartEvent", action: { click: function (event) { var shape = elementFactory.createShape({ type: 'bpmn:StartEvent' }); create.start(event, shape, { hints: { autoSelect: [ shape ] } }); }, dragstart: function() {...} } }其中key为这个shape的名称,value为这个shape定义的一些属性,主要有四个:
- group: 定义shape属于哪个组,主要有tools、event、gateway和activity可以选择
- className: 定义shape的chass名称,可以通过这个class给shape指定具体的样式
- title: 定义shape的title,也就是鼠标移动到shape上的提示
- action: 用户操作时触发的事件
通过这个数据我们就可以随意添加、删除或者修改Palette的shape了
3. custom/index.tsx中将Palette导出
import CustomPalette from "./CustomPalette"; export default { __init__: ["paletteProvider"], paletteProvider: ["type", CustomPalette], };4. customBpmn/index.tsx 中编写自定义的CustomModeler类
import inherits from "inherits"; import Modeler from "bpmn-js/lib/Modeler"; import CustomModule from "./custom"; function CustomModeler(options) { Modeler.call(this, options); this._customElements = []; } inherits(CustomModeler, Modeler); CustomModeler.prototype._modules = [].concat(CustomModeler.prototype._modules, [ CustomModule ]); export { CustomModeler };5. 在bpmn组件中引用自定义的CustomModeler来替换原本引用的BpmnModeler
import React, { useEffect, useRef, useState } from 'react'; import BpmnModeler from 'bpmn-js/lib/Modeler'; //默认引入 import { CustomModeler } from "./customBpmn"; //自定义引入 import 'bpmn-js/dist/assets/diagram-js.css'; import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'; import 'bpmn-js/dist/assets/bpmn-js.css'; import { defXmlStr } from "./xmlStr"; import './styles.less' const BpmnModelerWidget = ({ initialXml, onSave }) => { const containerRef = useRef(null); const modelerRef = useRef(null); const [xml, setXml] = useState(''); const [error, setError] = useState(null); useEffect(() => { // 初始化建模器 // modelerRef.current = new BpmnModeler({ //默认左侧Palette modelerRef.current = new CustomModeler({ //自定义左侧Palette !!!重要修改------- container: containerRef.current }); // 加载初始XML if (initialXml) { loadDiagram(initialXml); } else { createNewDiagram(); } // 监听变化 modelerRef.current.on('commandStack.changed', async () => { try { const { xml: newXml } = await modelerRef.current.saveXML({ format: true }); setXml(newXml); } catch (err) { console.error('Failed to save XML', err); } }); return () => { modelerRef.current?.destroy(); }; }, []); const loadDiagram = async (xml) => { try { await modelerRef.current.importXML(xml); const canvas = modelerRef.current.get('canvas'); canvas.zoom('fit-viewport'); setError(null); } catch (err) { setError('Failed to load diagram'); console.error(err); } }; const createNewDiagram = async () => { const newDiagram = defXmlStr('New_Process', 'New Process') await loadDiagram(newDiagram); }; return ( <div className="bpmn-modeler"> <div id='canvas' className='modeler-container' ref={containerRef} /> </div> ); }; export default BpmnModelerWidget;最终效果如图:
配置源码:
使用的时候可以直接查看源码bpmn-js/lib/features/palette/PaletteProvider.js和bpmn-js/lib/features/context-pad/ContextPadProvider.js文件进行自定义修改。
对于部分类型,需要添加options选项,如上例中的子流程 bpmn:SubProcess
代码仓库
完整代码已上传至https://gitee.com/z-beast/bpmn-logic-flowchart