// 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() 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 = { startEvent: '开始事件', endEvent: '结束事件', task: '任务', userTask: '人工任务', exclusiveGateway: '排他网关(分支判断)', parallelGateway: '并行网关', intermediateCatchEvent: '中间事件', } /** bpmn 元素 id → 详情(点击查看:任务关联行为、网关分支、连线条件)。 */ export function detailMap(pm: ProcessModel, onto: Ontology): Record { const idmap = new Map() 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 = {} 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 }