Files
qhmes/.trae/skills/jeecg-bpmn/references/bpmn-layout.md

126 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 节点 ID 命名与图形布局规则
## 1. 节点 ID 命名规范
| 节点类型 | ID 前缀 | 示例 |
|----------|---------|------|
| 开始事件 | `start` | `start` |
| 结束事件 | `end` | `end` |
| 用户任务 | `task_` | `task_apply`, `task_manager`, `task_hr` |
| 排他网关 | `gateway_` | `gateway_result`, `gateway_amount` |
| 并行网关 | `pgw_` | `pgw_fork`, `pgw_join` |
| 连线 | `flow_` | `flow_1`, `flow_approve`, `flow_reject` |
## 2. 图形布局计算规则
### 2.1 尺寸常量
| 元素 | 宽度(width) | 高度(height) |
|------|------------|-------------|
| startEvent | 36 | 36 |
| endEvent | 36 | 36 |
| userTask | 100 | 60 |
| exclusiveGateway | 50 | 50 |
| parallelGateway | 50 | 50 |
### 2.2 布局策略 — 垂直主轴
所有节点沿 **垂直方向Y轴** 从上到下排列,中心线 X 固定。
**基准参数:**
- 主轴中心 X = `218`(所有节点以此为中心对齐)
- 起始 Y = `30`
- 节点间垂直间距 = `40`(节点底部到下一节点顶部的距离)
**计算公式:**
```python
CENTER_X = 218
START_Y = 30
VERTICAL_GAP = 40
# 节点尺寸
SIZES = {
"startEvent": {"w": 36, "h": 36},
"endEvent": {"w": 36, "h": 36},
"userTask": {"w": 100, "h": 60},
"exclusiveGateway": {"w": 50, "h": 50},
"parallelGateway": {"w": 50, "h": 50},
}
def layout_nodes(nodes):
"""计算每个节点的 Bounds (x, y, width, height)"""
y = START_Y
positions = []
for node in nodes:
size = SIZES[node["type"]]
x = CENTER_X - size["w"] / 2
positions.append({
"id": node["id"],
"x": x, "y": y,
"w": size["w"], "h": size["h"],
"center_x": CENTER_X,
"center_y": y + size["h"] / 2,
"bottom_y": y + size["h"]
})
y += size["h"] + VERTICAL_GAP
return positions
```
### 2.3 Shape XML 生成
```xml
<!-- startEvent / endEvent -->
<bpmndi:BPMNShape id="shape_{id}" bpmnElement="{id}">
<dc:Bounds x="{x}" y="{y}" width="{w}" height="{h}" />
<bpmndi:BPMNLabel>
<dc:Bounds x="{x+7}" y="{y+h+7}" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<!-- userTask -->
<bpmndi:BPMNShape id="shape_{id}" bpmnElement="{id}">
<dc:Bounds x="{x}" y="{y}" width="{w}" height="{h}" />
</bpmndi:BPMNShape>
<!-- gateway (isMarkerVisible="true" 用于排他网关) -->
<bpmndi:BPMNShape id="shape_{id}" bpmnElement="{id}" isMarkerVisible="true">
<dc:Bounds x="{x}" y="{y}" width="{w}" height="{h}" />
<bpmndi:BPMNLabel>
<dc:Bounds x="{x+w+10}" y="{center_y-7}" width="44" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
```
### 2.4 Edge XML 生成
**直线连接(垂直方向、上下相邻节点):**
```xml
<bpmndi:BPMNEdge id="edge_{flow_id}" bpmnElement="{flow_id}">
<di:waypoint x="{source.center_x}" y="{source.bottom_y}" />
<di:waypoint x="{target.center_x}" y="{target.y}" />
</bpmndi:BPMNEdge>
```
**分支连线(排他网关的非主路径,从右侧绕行):**
当网关有拒绝/回退路径需要连接到非相邻节点时,使用右侧绕行:
```xml
<bpmndi:BPMNEdge id="edge_{flow_id}" bpmnElement="{flow_id}">
<di:waypoint x="{gateway.center_x + 25}" y="{gateway.center_y}" />
<di:waypoint x="{gateway.center_x + 132}" y="{gateway.center_y}" />
<di:waypoint x="{gateway.center_x + 132}" y="{target.center_y}" />
<di:waypoint x="{target.center_x + target.w/2}" y="{target.center_y}" />
</bpmndi:BPMNEdge>
```
**并行分支连线(从左侧出发):**
```xml
<bpmndi:BPMNEdge id="edge_{flow_id}" bpmnElement="{flow_id}">
<di:waypoint x="{gateway.center_x - 25}" y="{gateway.center_y}" />
<di:waypoint x="{target.center_x - target.w/2 - 50}" y="{gateway.center_y}" />
<di:waypoint x="{target.center_x - target.w/2 - 50}" y="{target.center_y}" />
<di:waypoint x="{target.center_x - target.w/2}" y="{target.center_y}" />
</bpmndi:BPMNEdge>
```