bpmn.ts
3.67 KB
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
79
80
81
82
83
84
85
86
87
88
89
90
91
// ProcessModel → ELK-BPMN JSON(bpmn-elk-layout 的输入)+ 点击详情表。
// 节点 id 重编为合法 NCName(Node_N),序列流端点按映射改写,与详情表 id 完全一致。
import type { Ontology, ProcessModel, ProcessNode } from './types'
const EVENT_TYPES = new Set(['startEvent', 'endEvent', 'intermediateCatchEvent'])
const GATEWAY_TYPES = new Set(['exclusiveGateway', 'parallelGateway'])
function elkNode(n: ProcessNode, nid: string) {
const bpmn: any = {}
if (EVENT_TYPES.has(n.type)) { bpmn.type = n.type; bpmn.eventDefinitionType = 'none' }
else if (GATEWAY_TYPES.has(n.type)) bpmn.type = n.type
else bpmn.type = n.type === 'userTask' ? 'userTask' : 'task'
if (n.name) bpmn.name = n.name
return { id: nid, bpmn }
}
// 仅加大层间距,给分支条件标签留位置;其余布局用库默认。
const ELK_OPTS = { 'elk.layered.spacing.nodeNodeBetweenLayers': 90 }
/** ProcessModel → ELK-BPMN JSON。 */
export function toElkBpmn(pm: ProcessModel): any {
const idmap = new Map<string, string>()
pm.nodes.forEach((n, i) => idmap.set(n.id, `Node_${i + 1}`))
const edges = pm.flows.flatMap((f, i) => {
const s = idmap.get(f.source), t = idmap.get(f.target)
if (!s || !t) return []
const b: any = { type: 'sequenceFlow' }
const label = f.name || f.condition
if (label) b.name = label
if (f.condition) b.conditionExpression = { body: f.condition }
return [{ id: `Flow_${i + 1}`, sources: [s], targets: [t], bpmn: b }]
})
return {
id: 'definitions',
children: [{
id: 'Process_1',
bpmn: { type: 'process' },
layoutOptions: { ...ELK_OPTS },
children: pm.nodes.map((n) => elkNode(n, idmap.get(n.id)!)),
edges,
}],
}
}
const KIND_TEXT: Record<string, string> = {
startEvent: '开始事件', endEvent: '结束事件', task: '任务', userTask: '人工任务',
exclusiveGateway: '排他网关(分支判断)', parallelGateway: '并行网关', intermediateCatchEvent: '中间事件',
}
/** bpmn 元素 id → 详情(点击查看:任务关联行为、网关分支、连线条件)。 */
export function detailMap(pm: ProcessModel, onto: Ontology): Record<string, any> {
const idmap = new Map<string, string>()
pm.nodes.forEach((n, i) => idmap.set(n.id, `Node_${i + 1}`))
const nameOf = (id: string) => pm.nodes.find((n) => n.id === id)?.name || id
const details: Record<string, any> = {}
for (const n of pm.nodes) {
const d: any = { kind: n.type, kindLabel: KIND_TEXT[n.type] || n.type, name: n.name || '', lane: n.lane || '' }
if (n.behaviorId) {
const b = onto.behaviors.find((x) => x.id === n.behaviorId)
if (b) {
const owner = onto.objects.find((o) => o.id === b.owner_object_id)
d.behavior = {
id: b.id, name: b.name, type: b.type, commandType: b.commandType,
owner: owner ? owner.name : b.owner_object_id, description: b.description || '',
preconditions: b.preconditions, effects: b.effects,
produced_events: b.produced_events,
applied_rules: b.applied_rules.map((rid) => onto.rules.find((r) => r.id === rid)?.name || rid),
}
}
}
if (GATEWAY_TYPES.has(n.type)) {
d.branches = pm.flows.filter((f) => f.source === n.id).map((f) => ({
to: nameOf(f.target), label: f.name || '', condition: f.condition || '',
}))
}
details[idmap.get(n.id)!] = d
}
pm.flows.forEach((f, i) => {
if (!idmap.get(f.source) || !idmap.get(f.target)) return
details[`Flow_${i + 1}`] = {
kind: 'sequenceFlow', kindLabel: '连线 / 分支', name: f.name || '', condition: f.condition || '',
from: nameOf(f.source), to: nameOf(f.target),
}
})
return details
}