Modeler 是bpmn.js的编辑模式,用户可以通过拖拽、绘图、属性编辑来创建和修改流程。本文讲解Modeler的初始化、UI组件、编辑API与流程保存。
Modeler的初始化与UI组成
创建实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>bpmn-js Modeler 示例</title> <link rel="stylesheet" href="https://unpkg.com/bpmn-js@18.9.1/dist/assets/diagram-js.css"> <link rel="stylesheet" href="https://unpkg.com/bpmn-js@18.9.1/dist/assets/bpmn-js.css"> <link rel="stylesheet" href="https://unpkg.com/bpmn-js@18.9.1/dist/assets/bpmn-font/css/bpmn.css"> <style> body { margin: 0; padding: 0; height: 100vh; display: flex; flex-direction: column; } #canvas { flex: 1; border: 1px solid #ccc; } </style> </head> <body> <div id="canvas"></div>
<script src="https://unpkg.com/bpmn-js@18.9.1/dist/bpmn-modeler.development.js"></script> <script> // 初始化 Modeler const modeler = new BpmnJS({ container: '#canvas' // 注意:这里使用BpmnJS,不是Viewer // 自动包含Modeler功能 });
// 空白流程模板 const emptyBpmn = `<?xml version="1.0" encoding="UTF-8"?> <bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn"> <bpmn:process id="Process_1" isExecutable="false"> <bpmn:startEvent id="StartEvent_1" name="开始"/> <bpmn:userTask id="Task_1" name="处理"/> <bpmn:endEvent id="EndEvent_1" name="结束"/> <bpmn:sequenceFlow id="Flow_1" sourceRef="StartEvent_1" targetRef="Task_1"/> <bpmn:sequenceFlow id="Flow_2" sourceRef="Task_1" targetRef="EndEvent_1"/> </bpmn:process> <bpmndi:BPMNDiagram id="BPMNDiagram_1" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"> <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1"> <bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="StartEvent_1"> <dc:Bounds x="100" y="100" width="36" height="36"/> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="Task_1_di" bpmnElement="Task_1"> <dc:Bounds x="220" y="80" width="100" height="80"/> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="EndEvent_1_di" bpmnElement="EndEvent_1"> <dc:Bounds x="422" y="100" width="36" height="36"/> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="Flow_1_di" bpmnElement="Flow_1"> <di:waypoint x="136" y="118" xmlns:di="http://www.omg.org/spec/DD/20100524/DI"/> <di:waypoint x="220" y="118"/> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="Flow_2_di" bpmnElement="Flow_2"> <di:waypoint x="320" y="118"/> <di:waypoint x="422" y="118"/> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </bpmn:definitions>`;
// 加载流程 modeler.importXML(emptyBpmn).then(() => { console.log('✓ 编辑器已就绪'); }).catch(err => { console.error('✗ 初始化失败:', err); }); </script> </body> </html>
|
UI组件(自动生成)
Modeler启动时,自动生成以下UI:
1 2 3 4 5 6 7 8 9
| ┌──────────────────────────────────────────────┐ │ Palette(左侧工具箱) │ 流程图编辑区 │ ├──────────────┬───────────┼──────────────────┤ │ ○ 启动事件 │ │ [点击查看属性] │ │ □ 用户任务 │ 编辑画布 │ │ │ ◇ 排他网关 │ │ 右键菜单 │ │ ◊ 并行网关 │ │ (移除/复制/etc) │ │ ⃗ 连线工具 │ │ │ └──────────────┴───────────┴──────────────────┘
|
Palette(左侧工具箱):拖拽元素到画布
ContextPad(右键菜单):快捷操作
属性栏(右侧):需要单独集成
拖拽创建元素
基础操作(自动实现)
1 2 3 4 5 6
| 1. 点击Palette中的元素 2. 拖拽到画布上放置 3. 自动创建该类型的元素
例: 拖拽"用户任务"图标 → 放在画布上 → 生成一个用户任务节点
|
连接元素
1 2 3 4 5 6
| 1. 悬停在元素上,出现连接点(小圆圈) 2. 从一个元素的连接点拖拽到另一个元素 3. 自动生成顺序流
例: [启动事件] 的右侧连接点 → 拖拽到 [用户任务] → 生成连线
|
通过API编程创建元素
对于需要自动化创建大量元素的场景,可用API:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const modeling = modeler.get('modeling'); const elementFactory = modeler.get('elementFactory'); const canvas = modeler.get('canvas');
const rootElement = canvas.getRootElement();
const startEvent = elementFactory.createShape({ type: 'bpmn:StartEvent', x: 100, y: 100 }); modeling.createShape(startEvent, rootElement);
const userTask = elementFactory.createShape({ type: 'bpmn:UserTask', x: 250, y: 100, label: '审批订单' }); modeling.createShape(userTask, rootElement);
const connection = modeling.connect(startEvent, userTask);
|
属性编辑
获取元素属性
1 2 3 4 5 6 7 8 9 10 11
| const element = modeler.get('elementRegistry').get('Task_1');
console.log(element.id); console.log(element.type); console.log(element.businessObject);
const bo = element.businessObject; console.log(bo.name); console.log(bo.documentation);
|
修改元素属性
1 2 3 4 5 6 7 8 9 10 11 12 13
| const modeling = modeler.get('modeling'); const element = modeler.get('elementRegistry').get('Task_1');
modeling.updateProperties(element, { name: '新的任务名称' });
modeling.updateProperties(element, { name: '审批订单', documentation: '由主管进行审批' });
|
撤销/重做
1 2 3 4 5 6 7 8 9 10 11 12 13
| const commandStack = modeler.get('commandStack');
commandStack.undo();
commandStack.redo();
commandStack.clear();
const canUndo = commandStack.canUndo();
|
流程保存与导出
导出为XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| async function saveProcess() { try { const result = await modeler.saveXML({ format: true }); const xmlString = result.xml; downloadFile(xmlString, 'process.bpmn'); await fetch('/api/process/save', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ bpmn: xmlString }) }); console.log('✓ 流程已保存'); } catch (err) { console.error('✗ 保存失败:', err); } }
function downloadFile(content, filename) { const element = document.createElement('a'); element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content)); element.setAttribute('download', filename); element.style.display = 'none'; document.body.appendChild(element); element.click(); document.body.removeChild(element); }
|
导出为SVG
1 2 3 4 5 6 7 8 9 10 11
| async function downloadAsImage() { try { const result = await modeler.saveSVG(); const svgString = result.svg; downloadFile(svgString, 'process.svg'); } catch (err) { console.error('导出失败:', err); } }
|
实时保存与自动检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const commandStack = modeler.get('commandStack');
commandStack.on('post:execute', async (event) => { const result = await modeler.saveXML(); const bpmnXml = result.xml; debouncedSave(bpmnXml); });
function debouncedSave(xml) { clearTimeout(saveTimer); saveTimer = setTimeout(() => { fetch('/api/process/autosave', { method: 'POST', body: JSON.stringify({ bpmn: xml }) }); }, 1000); }
|
常见操作速查
| 操作 |
代码 |
| 获取建模服务 |
modeler.get('modeling') |
| 获取元素工厂 |
modeler.get('elementFactory') |
| 获取命令栈 |
modeler.get('commandStack') |
| 获取元素 |
modeler.get('elementRegistry').get(id) |
| 修改属性 |
modeling.updateProperties(el, {...}) |
| 删除元素 |
modeling.removeElements([el]) |
| 复制元素 |
右键菜单 / 快捷键Ctrl+C |
| 撤销 |
commandStack.undo() |
| 还原 |
commandStack.redo() |
| 保存XML |
modeler.saveXML() |
| 保存SVG |
modeler.saveSVG() |
小结
掌握Modeler:
- ✓ 初始化编辑器
- ✓ 通过拖拽或API创建元素
- ✓ 属性编辑与更新
- ✓ 撤销/重做管理
- ✓ XML与SVG导出保存
便能构建完整的流程设计工具。
参考资料