# Draw.io 横向泳道流程图 XML 模板参考

## 文件基本结构

Draw.io 文件本质是 XML，扩展名为 `.drawio` 或 `.xml`，可直接用 Draw.io 打开。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<mxGraphModel dx="1422" dy="762" grid="1" gridSize="10"
              guides="1" tooltips="1" connect="1" arrows="1"
              fold="1" page="0" pageScale="1"
              pageWidth="1169" pageHeight="827"
              math="0" shadow="0">
  <root>
    <mxCell id="0" />
    <mxCell id="1" parent="0" />

    <!-- 整个泳道容器（横向） -->
    <mxCell id="swim_container" value="" style="shape=pool;startSize=20;horizontal=1;childLayout=stackLayout;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=1;fillColor=none;strokeColor=#000000;" vertex="1" parent="1">
      <mxGeometry x="40" y="40" width="1080" height="240" as="geometry" />
    </mxCell>

    <!-- 泳道1（角色A）— 横向泳道：horizontal=0 使分隔线垂直 → 角色标签在左侧 -->
    <mxCell id="lane1" value="角色A" style="swimlane;startSize=80;horizontal=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=13;fontStyle=1;" vertex="1" parent="swim_container">
      <mxGeometry x="0" y="0" width="1080" height="120" as="geometry" />
    </mxCell>

    <!-- 泳道2（角色B）— 角色在左侧 -->
    <mxCell id="lane2" value="角色B" style="swimlane;startSize=80;horizontal=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=13;fontStyle=1;" vertex="1" parent="swim_container">
      <mxGeometry x="0" y="120" width="1080" height="120" as="geometry" />
    </mxCell>

    <!-- 开始节点（椭圆）— 宽度 80 -->
    <mxCell id="start" value="开始" style="ellipse;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;" vertex="1" parent="lane1">
      <mxGeometry x="30" y="30" width="80" height="60" as="geometry" />
    </mxCell>

    <!-- 普通活动节点（圆角矩形）— 宽度 80 -->
    <mxCell id="act1" value="活动1" style="rounded=1;whiteSpace=wrap;html=1;fontSize=12;" vertex="1" parent="lane1">
      <mxGeometry x="150" y="30" width="80" height="60" as="geometry" />
    </mxCell>

    <!-- 决策节点（菱形）— 宽度 80 -->
    <mxCell id="dec1" value="是否审批？" style="rhombus;whiteSpace=wrap;html=1;fontSize=12;" vertex="1" parent="lane1">
      <mxGeometry x="270" y="20" width="80" height="80" as="geometry" />
    </mxCell>

    <!-- 事件触发节点（六边形）— 宽度 80，高度 60 -->
    <mxCell id="event1" value="事件名称"
      style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fillColor=#FFF2CC;strokeColor=#D6B656;fontSize=12;fontStyle=1;"
      vertex="1" parent="lane1">
      <mxGeometry x="390" y="30" width="80" height="60" as="geometry"/>
    </mxCell>

    <!-- 另一泳道中的活动（跨泳道串联 → 向右错位一个活动宽度） -->
    <mxCell id="act2" value="活动2" style="rounded=1;whiteSpace=wrap;html=1;fontSize=12;" vertex="1" parent="lane2">
      <mxGeometry x="390" y="30" width="80" height="60" as="geometry" />
    </mxCell>

    <!-- 结束节点 — 宽度 80 -->
    <mxCell id="end" value="结束" style="ellipse;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;" vertex="1" parent="lane1">
      <mxGeometry x="510" y="30" width="80" height="60" as="geometry" />
    </mxCell>

    <!-- 连接线：start → act1 -->
    <mxCell id="e1" style="edgeStyle=orthogonalEdgeStyle;" edge="1" source="start" target="act1" parent="swim_container">
      <mxGeometry relative="1" as="geometry" />
    </mxCell>

    <!-- 连接线：act1 → dec1 -->
    <mxCell id="e2" style="edgeStyle=orthogonalEdgeStyle;" edge="1" source="act1" target="dec1" parent="swim_container">
      <mxGeometry relative="1" as="geometry" />
    </mxCell>

    <!-- 决策分支线（带标签）：dec1 → act2（否） -->
    <mxCell id="e3" value="否" style="edgeStyle=orthogonalEdgeStyle;" edge="1" source="dec1" target="act2" parent="swim_container">
      <mxGeometry relative="1" as="geometry" />
    </mxCell>

    <!-- 决策分支线（带标签）：dec1 → end（是） -->
    <mxCell id="e4" value="是" style="edgeStyle=orthogonalEdgeStyle;" edge="1" source="dec1" target="end" parent="swim_container">
      <mxGeometry relative="1" as="geometry" />
    </mxCell>

  </root>
</mxGraphModel>
```

---

## 关键样式说明

| 元素类型 | style 关键字 |
|---------|------------|
| 横向泳道容器 | `shape=pool;horizontal=1;childLayout=stackLayout;horizontalStack=0` |
| 泳道行（横向，角色在左） | `swimlane;startSize=80;horizontal=0;whiteSpace=wrap;html=1` |
| 泳道列（纵向，角色在上） | `swimlane;startSize=30;horizontal=1;whiteSpace=wrap;html=1` |
| 普通活动 | `rounded=1;whiteSpace=wrap;html=1` |
| 决策菱形 | `rhombus;whiteSpace=wrap;html=1` |
| 事件触发六边形 | `shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1` |
| 开始/结束椭圆 | `ellipse;whiteSpace=wrap;html=1` |
| 连接线 | `edgeStyle=orthogonalEdgeStyle` |

---

## 连线布局规范

### 规则0：正向业务逻辑线优先 ⚠️

必须先处理正向业务逻辑连线，驳回连线基于正向连线方向决定位置。

---

### 规则1：横向泳道正向连线 — "直线优先，避免折弯"

#### 1a. 同泳道串联（水平方向）→ 直线

```xml
<mxCell id="e_same_lane" style="edgeStyle=orthogonalEdgeStyle;exitX=1;exitY=0.5;entryX=0;entryY=0.5;" edge="1" source="act_a" target="act_b" parent="swim_container">
  <mxGeometry relative="1" as="geometry"/>
</mxCell>
```

#### 1b. 串联活动在下方泳道 → 出口=下侧

```xml
<mxCell id="e_down" style="edgeStyle=orthogonalEdgeStyle;exitX=0.5;exitY=1;entryX=0;entryY=0.5;" edge="1" source="act_upper" target="act_lower" parent="swim_container">
  <mxGeometry relative="1" as="geometry"/>
</mxCell>
```

#### 1c. 串联活动在上方泳道 → 出口=右侧（避免折返）

```xml
<mxCell id="e_up" style="edgeStyle=orthogonalEdgeStyle;exitX=1;exitY=0.5;entryX=0;entryY=0.5;" edge="1" source="act_lower" target="act_upper" parent="swim_container">
  <mxGeometry relative="1" as="geometry"/>
</mxCell>
```

#### 正向出口决策表

| 目标位置 | 出口 | 原因 |
|---------|------|------|
| 同泳道右侧 | 右侧 `exitX=1;exitY=0.5` | 直线，零折弯 |
| 下方泳道 | 下侧 `exitX=0.5;exitY=1` | 近直线，最少折弯 |
| 上方泳道 | 右侧 `exitX=1;exitY=0.5` | 避免从上方折返 |

---

### 规则2：驳回连线 — 三角位置由正向方向决定

**三角形尺寸**：42×30 px | **距活动节点**：约 50px | **箭头连线**：`endArrow=block`

**驳回三角位置决策表**：

| 正向连线出口 | 驳回三角位置 | 三角方向 |
|-------------|------------|---------|
| 下方 (`exitY=1`) | 右侧 | `direction=east` |
| 右侧 (`exitX=1`) | 下方 | `direction=south` |
| 无正向出口 | 右侧（默认） | `direction=east` |

#### 驳回在下方（正向出口=右侧时）

```xml
<!-- 出口三角：位于活动节点下方约 50px -->
<mxCell id="rej_out_1" value="1"
  style="shape=triangle;whiteSpace=wrap;html=1;
    fillColor=#FFB366;strokeColor=#D79B00;fontSize=14;fontStyle=1;
    direction=south;pointerEvents=0;"
  vertex="1" parent="lane_xxx">
  <mxGeometry x="175" y="100" width="42" height="30" as="geometry"/>
</mxCell>

<!-- 活动 → 出口三角 -->
<mxCell id="e_rej_out" style="edgeStyle=orthogonalEdgeStyle;endArrow=block;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" edge="1" source="act_src" target="rej_out_1" parent="swim_container">
  <mxGeometry relative="1" as="geometry"/>
</mxCell>
```

#### 驳回在右侧（正向出口=下方时）

```xml
<!-- 出口三角：位于活动节点右侧约 50px -->
<mxCell id="rej_out_1" value="1"
  style="shape=triangle;whiteSpace=wrap;html=1;
    fillColor=#FFB366;strokeColor=#D79B00;fontSize=14;fontStyle=1;
    direction=east;pointerEvents=0;"
  vertex="1" parent="lane_xxx">
  <mxGeometry x="235" y="55" width="30" height="42" as="geometry"/>
</mxCell>

<!-- 活动 → 出口三角（从右侧出口到三角左侧） -->
<mxCell id="e_rej_out" style="edgeStyle=orthogonalEdgeStyle;endArrow=block;exitX=1;exitY=0.5;entryX=0;entryY=0.5;" edge="1" source="act_src" target="rej_out_1" parent="swim_container">
  <mxGeometry relative="1" as="geometry"/>
</mxCell>
```

#### 入口三角（目标活动上方）

```xml
<!-- 目标活动上方约 50px 处，所有驳回共用同一编号 -->
<mxCell id="rej_in_1" value="1"
  style="shape=triangle;whiteSpace=wrap;html=1;
    fillColor=#FFB366;strokeColor=#D79B00;fontSize=14;fontStyle=1;
    direction=north;pointerEvents=0;"
  vertex="1" parent="lane_target">
  <mxGeometry x="175" y="-35" width="42" height="30" as="geometry"/>
</mxCell>

<!-- 入口三角 → 目标活动 -->
<mxCell id="e_rej_in" style="edgeStyle=orthogonalEdgeStyle;endArrow=block;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" edge="1" source="rej_in_1" target="act_target" parent="swim_container">
  <mxGeometry relative="1" as="geometry"/>
</mxCell>
```

---

## 多分支汇聚防重叠模板

基本原则：**避免连线重叠导致逻辑难以辨认。**

```
适用场景：3+ 活动汇聚到同一目标节点（不限结束节点）
处理：目标节点下移 → 各源节点出口改为下侧
```

### 模板：三分支汇聚到结束

```xml
<!-- 结束节点：下移到三个活动下方 -->
<mxCell id="end" value="结束"
  style="ellipse;whiteSpace=wrap;html=1;fillColor=#FAD7D8;fontSize=12;fontStyle=1;"
  vertex="1" parent="lane_2">
  <mxGeometry x="420" y="90" width="60" height="40" as="geometry"/>
</mxCell>

<!-- 三个活动（水平排列）：出口统一为下侧 -->
<mxCell id="act_direct" value="直接入库确认" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="lane_2">
  <mxGeometry x="300" y="30" width="100" height="40" as="geometry"/>
</mxCell>
<mxCell id="act_repair" value="返修后入库确认" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="lane_2">
  <mxGeometry x="430" y="30" width="100" height="40" as="geometry"/>
</mxCell>
<mxCell id="act_scrap" value="报废处理确认" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="lane_2">
  <mxGeometry x="560" y="30" width="100" height="40" as="geometry"/>
</mxCell>

<!-- 汇聚连线：出口=下侧，入口=结束节点上侧（entryY=0） -->
<mxCell id="e_direct" style="edgeStyle=orthogonalEdgeStyle;exitX=0.5;exitY=1;entryX=0.3;entryY=0;" edge="1" source="act_direct" target="end" parent="lane_2"/>
<mxCell id="e_repair" style="edgeStyle=orthogonalEdgeStyle;exitX=0.5;exitY=1;entryX=0.5;entryY=0;" edge="1" source="act_repair" target="end" parent="lane_2"/>
<mxCell id="e_scrap"  style="edgeStyle=orthogonalEdgeStyle;exitX=0.5;exitY=1;entryX=0.7;entryY=0;" edge="1" source="act_scrap"  target="end" parent="lane_2"/>
```

## 同向多连线共点模板

```
适用场景：同一节点 2+ 条同方向连线
处理：共用同一个连接点（exitX/exitY 或 entryX/entryY 完全相同）
```

### 模板：决策节点三个出口共点

```xml
<!-- 三条出口连线全部使用菱形右侧中间同一个点 -->
<mxCell id="e_path_a" style="edgeStyle=orthogonalEdgeStyle;exitX=1;exitY=0.5;entryX=0;entryY=0.5;endArrow=block;" edge="1" source="dec_qc" target="act_a" parent="pool">
  <mxGeometry relative="1" as="geometry">
    <mxPoint as="offset"/>
  </mxGeometry>
</mxCell>
<mxCell id="e_path_b" style="edgeStyle=orthogonalEdgeStyle;exitX=1;exitY=0.5;entryX=0;entryY=0.5;endArrow=block;" edge="1" source="dec_qc" target="act_b" parent="pool">
  <mxGeometry relative="1" as="geometry">
    <mxPoint as="offset"/>
  </mxGeometry>
</mxCell>
<mxCell id="e_path_c" style="edgeStyle=orthogonalEdgeStyle;exitX=1;exitY=0.5;entryX=0;entryY=0.5;endArrow=block;" edge="1" source="dec_qc" target="act_c" parent="pool">
  <mxGeometry relative="1" as="geometry">
    <mxPoint as="offset"/>
  </mxGeometry>
</mxCell>
```

> ⚠️ 共点仅适用于同方向连线。一进一出不可共点。

---

### 规则3：纵向泳道连线（对称）

| 目标位置 | 出口 | 原因 |
|---------|------|------|
| 同列下方 | 下侧 `exitX=0.5;exitY=1` | 直线 |
| 右侧泳道 | 右侧 `exitX=1;exitY=0.5` | 最少折弯 |
| 左侧泳道 | 下侧 `exitX=0.5;exitY=1` | 避免从左侧折返 |

驳回位置对称：正向出口=下侧 → 驳回在右；正向出口=右侧 → 驳回在下。

---

### 完整连线决策流程

```
第1步：确定正向连线出口方向（直线优先、避免折弯）
第2步：根据正向出口确定驳回三角位置（正向在下→驳回在右；正向在右→驳回在下）
第3步：用带箭头连线连接活动节点与驳回三角
```

---

## 空白泳道（角色未知）规范

当某活动找不到执行角色时，使用如下泳道：

```xml
<mxCell id="lane_unknown" value="—" style="swimlane;startSize=80;horizontal=0;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;fontStyle=1;" vertex="1" parent="swim_container">
  <mxGeometry x="0" y="240" width="1080" height="120" as="geometry" />
</mxCell>
```

- 泳道标签设为 `—`（或保留空字符串 `""`）
- 使用黄色填充（`#fff2cc`）以示区分
- 活动节点正常放置在此泳道中

---

## 坐标与尺寸建议

| 参数 | 推荐值 |
|-----|-------|
| 活动节点宽度 | **80px** |
| 活动节点高度 | 60px |
| 决策节点宽度 | 80px |
| 决策节点高度 | 80px |
| 节点水平间距 | 40px |
| 泳道高度（每条） | 120px |
| 左边距（首个节点） | 30px |
| 泳道角色标签宽度（横向） | 80px（startSize=80） |
| 跨泳道串联偏移 | 上一个节点宽度 + 40px |

## 跨泳道串联活动错位规则（新增）

当活动 A（泳道 X）→ 活动 B（泳道 Y）为**串联关系**（非并行）时：

```
B.x = A.x + A.width + 40
```

这避免串联活动在不同泳道中 x 对齐，防止被误读为并行关系。

示例：
```
泳道 A:  [节点1]──┐          ← x=30
                  │ down
泳道 B:       [节点2]         ← x=30+80+40=150 (错位！)
```

---

## 颜色方案（推荐）

| 泳道编号 | 填充色 | 边框色 |
|---------|-------|-------|
| 1 | `#dae8fc` | `#6c8ebf` |
| 2 | `#d5e8d4` | `#82b366` |
| 3 | `#fff2cc` | `#d6b656` |
| 4 | `#f8cecc` | `#b85450` |
| 5+ | `#e1d5e7` | `#9673a6` |
| 空白/未知 | `#f5f5f5` | `#666666` |
