当前位置: 首页 > news >正文

bpmn-js系列-自定义Palette工具(二)

前面文章介绍了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

http://www.jsqmd.com/news/1400417/

相关文章:

  • 2026 河南新乡市文武学校招生指南|省内知名院校大盘点,不同地区武校怎么选 - Luckyone王
  • 令牌管理深度剖析:ng-token-auth如何保障你的应用安全
  • 快速风格迁移完全指南:ml-projects中5种预训练模型的简单应用
  • Anthropic SDK for Go 示例大全:从基础到高级应用场景
  • tumblr.js常见问题解决:从认证错误到API请求限制的完整方案
  • trackerslist公共Tracker列表完整指南:3个问题告别BT下载龟速
  • 郑州家长收藏|嵩山少林小龙武术学校2026 招生简章,全托班收费、地址齐全 - Luckyone王
  • 健身API开发者的数据难题:Exercises Dataset 1324个动作5分钟上手
  • 禹州恒达金郡专业户型设计公司推荐 - 猜不透的vv
  • 近酒吧街大理古城附近别墅 月租8折旅居体验解析 - 全域品牌推荐
  • 15个boolinq核心操作符详解:从where到groupBy的终极教程
  • 告别Excel资产台账:用Snipe-IT开源资产管理系统管好每一台设备
  • 5分钟上手gemma4-browser-extension:从安装到高效使用的快速入门教程
  • Welcome UI核心组件解析:Button、Modal与Form组件的实战应用
  • 2026天猫养车加盟保证金能退吗?退费条款、签约前必问清单 - Chencen
  • RemoteCam完全指南:从安装到OBS设置,3分钟打造专业直播摄像头
  • 51P001.基于51单片机的智能温度控制系统Proteus仿真
  • 2026年8月衢州涂料门店综合盘点:按人群需求选更省心 - 滚动商讯
  • 澄城白水城乡住宅现浇楼梯改造,资质核验避开土建改造陷阱 - 名字不是很重要
  • 从源码到可视化:Torchvista如何追踪PyTorch模型前向传播的技术原理
  • 2026 北京救护车出租转运公司推荐 长途转院车辆预约卧床患者接送服务 - 产品推荐官
  • COLMAP点云可视化完整教程:从照片到可分析的3D点云只需4步
  • Sentient安全与隐私设置:保护你的个人数据
  • 架构图不敢上云?我花了一周时间把企业画图工作流整体搬到本地
  • 从安装到发布:Vue Chrome Extension Template完整部署指南
  • Locust Plugins完全指南:10大核心插件助你轻松实现高性能负载测试
  • 多语言场景下的lm-watermarking应用:跨语种文本追踪技术
  • 渭南县域自建房现浇楼梯改造,资质才是工程安全第一道防线 - 名字不是很重要
  • angular/router未来路线图:即将到来的5大令人兴奋的新功能
  • 优选算法的时序之窗:滑动窗口专题(二)