样式与外观:自定义流程图的视觉效果

默认的BPMN样式往往无法满足企业品牌或业务需求。本文讲解如何通过CSS、Marker标记与自定义渲染,来改变流程图的外观与主题

CSS样式覆盖

基础样式修改

Modeler 和 Viewer 的样式由 bpmn-js 提供的CSS决定。可通过编写CSS来覆盖默认样式:

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
<style>
/* 默认的流程图背景 */
.djs-container {
background-color: #f8f9fa !important;
}

/* 任务节点 */
.djs-shape[data-type="bpmn:UserTask"] {
fill: #e3f2fd !important;
stroke: #1976d2 !important;
stroke-width: 2 !important;
}

/* 启动事件 */
.djs-shape[data-type="bpmn:StartEvent"] {
fill: #c8e6c9 !important;
stroke: #388e3c !important;
}

/* 结束事件 */
.djs-shape[data-type="bpmn:EndEvent"] {
fill: #ffcdd2 !important;
stroke: #d32f2f !important;
}

/* 顺序流 */
.djs-connection[data-type="bpmn:SequenceFlow"] {
stroke: #666 !important;
stroke-width: 2 !important;
}

/* 连线标签 */
.djs-label {
font-size: 12px;
fill: #333 !important;
}
</style>

主题切换示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 默认亮色主题 */
.theme-light .djs-shape[data-type="bpmn:UserTask"] {
fill: #e3f2fd;
stroke: #1976d2;
}

/* 暗色主题 */
.theme-dark .djs-shape[data-type="bpmn:UserTask"] {
fill: #1e3a5f;
stroke: #64b5f6;
}

.theme-dark .djs-label {
fill: #e0e0e0;
}

.theme-dark .djs-container {
background-color: #121212;
}
1
2
3
4
5
// 切换主题
function switchTheme(theme) {
document.body.classList.remove('theme-light', 'theme-dark');
document.body.classList.add(`theme-${theme}`);
}

Marker标记与高亮

Marker 是给元素添加CSS类的方式,用于临时高亮状态标记

常见用途

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const canvas = modeler.get('canvas');
const element = modeler.get('elementRegistry').get('Task_1');

// 示例1:已执行
canvas.addMarker(element, 'executed');

// 示例2:当前位置
canvas.addMarker(element, 'current-position');

// 示例3:错误
canvas.addMarker(element, 'error');

// 示例4:审批已通过
canvas.addMarker(element, 'approved');

// 移除marker
canvas.removeMarker(element, 'approved');

对应的CSS样式

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
/* 已执行 - 绿色背景 */
.djs-element.executed {
background-color: #c8e6c9 !important;
fill: #c8e6c9 !important;
}

/* 当前位置 - 黄色脉冲 */
.djs-element.current-position {
fill: #fff9c4 !important;
animation: pulse 1.5s infinite;
}

@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}

/* 错误 - 红色 */
.djs-element.error {
fill: #ffcdd2 !important;
stroke: #d32f2f !important;
}

/* 已批准 - 绿色边框加粗 */
.djs-element.approved {
stroke-width: 3 !important;
stroke: #4caf50 !important;
}

/* 连接线的marker */
.djs-connection.executed {
stroke: #4caf50 !important;
stroke-width: 3 !important;
}

.djs-connection.error {
stroke: #d32f2f !important;
stroke-width: 2 !important;
stroke-dasharray: 5,5; /* 虚线 */
}

实战案例:流程执行追踪

需求:加载流程,根据执行历史高亮已执行路径与当前位置。

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
async function visualizeProcessExecution(bpmnXml, executionHistory) {
// 1. 加载流程图
await modeler.importXML(bpmnXml);
modeler.get('canvas').zoom('fit-viewport');

const canvas = modeler.get('canvas');
const registry = modeler.get('elementRegistry');

// 2. 高亮已执行步骤
executionHistory.forEach((step, index) => {
const element = registry.get(step.elementId);
if (!element) return;

// 样式化任务节点
if (step.status === 'completed') {
canvas.addMarker(element, 'executed');
} else if (step.status === 'active') {
canvas.addMarker(element, 'current-position');
} else if (step.status === 'failed') {
canvas.addMarker(element, 'error');
}
});

// 3. 高亮连线(连接已执行步骤)
executionHistory.forEach((step, index) => {
if (index < executionHistory.length - 1) {
const nextStep = executionHistory[index + 1];

// 查找连接这两个节点的连线
const fromEl = registry.get(step.elementId);
const toEl = registry.get(nextStep.elementId);

fromEl.outgoing.forEach(connection => {
if (connection.target === toEl) {
canvas.addMarker(connection, 'executed');
}
});
}
});

// 4. 绑定元素点击查看详情
modeler.get('eventBus').on('element.click', (event) => {
const element = event.element;
const history = executionHistory.find(h => h.elementId === element.id);

if (history) {
showExecutionDetails({
name: element.businessObject.name,
status: history.status,
timestamp: history.timestamp,
result: history.result
});
}
});
}

// 使用示例
const bpmnXml = '...'; // 流程图

const executionHistory = [
{ elementId: 'StartEvent_1', status: 'completed', timestamp: '10:00' },
{ elementId: 'ApproveTask_1', status: 'completed', timestamp: '10:05' },
{ elementId: 'Gateway_1', status: 'completed', timestamp: '10:06' },
{ elementId: 'ShipTask_1', status: 'active', timestamp: '10:07' }
];

visualizeProcessExecution(bpmnXml, executionHistory);

小结

掌握样式与外观:

  • ✓ CSS覆盖默认样式
  • ✓ 使用Marker标记不同状态
  • ✓ 颜色方案统一与可读
  • ✓ 流程执行可视化追踪
  • ✓ 了解自定义渲染器(进阶)

便能构建专业、易读、美观的流程界面

参考资料