bpmn.js Modeler:拖拽编辑与流程创建

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); // 元素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;

// 方案1:下载为文件
downloadFile(xmlString, 'process.bpmn');

// 方案2:发送到服务器
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;

// 下载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); // 1秒无操作后保存
}

常见操作速查

操作 代码
获取建模服务 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导出保存

便能构建完整的流程设计工具

参考资料