# 流程图工具选型避坑（mermaid / D2 / PlantUML / 自造 SVG）

**日期**：2026-05-17
**触发**：FFOA 流程图模板项目，验证了 6 种方案选最优形态。
**结论**：业界没有"垂直泳道 + 真折线 + AI 友好 + 零运行时依赖"四合一的开源工具。FFOA 选择**自造 YAML→SVG Node generator**（`docs/templates/flow-renderer.mjs`）。下次评估前先看本文件避免重新踩坑。

---

## 方案评估矩阵

| 工具 | 真垂直泳道 | 真折线 routing | AI 写图 | 运行时依赖 | 评估 |
|---|---|---|---|---|---|
| **Mermaid `subgraph`** | ❌ 假泳道 | ❌ dagre 自动布局产对角线 | ✅ | 3MB JS lib | 跳过（陷阱 1）|
| **D2 `grid-columns` + `direction: down`** | ✅ 真泳道 | ❌ 内置 routing 产对角线 | ✅ DSL | Go 二进制 25MB | 跳过（陷阱 2）|
| **D2 + `-l elk` (ELK 引擎)** | ✅ | ❌ grid 模式下 ELK 不接管 edge routing | ✅ | 同上 | 跳过（陷阱 2）|
| **PlantUML `\|Lane\|`** | ✅ | ✅ 原生 orthogonal | ✅ DSL | Java + jar | 备选（陷阱 3）|
| **bpmn-js (BPMN 2.0 XML)** | ✅ | ✅ | ⚠️ XML 啰嗦 | 浏览器 JS lib | 备选（BPMN 完整支持时） |
| **手画 inline SVG** | ✅ | ✅ | ❌ 算坐标累 + 易错位 | 0 | 跳过（不可维护） |
| **自造 YAML→SVG generator** | ✅ | ✅ | ✅ YAML | 0（产物 SVG） | **首选** |

## 关键陷阱

### 陷阱 1：Mermaid `subgraph` 不是真 swimlane

Mermaid `subgraph` 只是把节点框起来，**没有"行=部门"的 BPMN swimlane 语义**。每个 subgraph 宽度由内部节点数决定，导致泳道宽窄不一，跨 subgraph 流转线穿插混乱。

Mermaid 官方 issue #2028 也确认 swimlane 长期 "experimental" 没成熟，2026 年仍是这状态。

**结论**：要画 swimlane → 不要选 mermaid。

### 陷阱 2：D2 grid layout 不支持 orthogonal routing

D2 v0.7 的 `grid-columns: N + direction: down` 能做出**真垂直泳道布局**（这是 D2 的优势），**但 edge routing 走 D2 自己的算法（直线/贝塞尔），输出大量对角线**。

切换到 ELK 引擎（`d2 -l elk in.d2 out.svg`）**也无改善**——ELK 只接管顶层 layout，grid mode 内的 edge 仍用 D2 内置 routing。

D2 v0.7 没有暴露 connection routing 选项（没有 `style.routing: orthogonal` / `style.spline`），用户层无法控制。

**实测对比**（同一个 .d2 文件）：
```
v0.6 dagre:  M 1220.4 179.7  L 1028.5 508.5   ← 单 L 段 = 对角直线
v0.7 ELK:    M 1373.0 175.7  L 1139.9 584.5   ← 也是单 L 段 = 对角直线
```

真折线应该是 `M ... L ... L ... L ...` 多段。

**结论**：要"飞书钉钉风格直角折线" → 不要选 D2 grid。

### 陷阱 3：PlantUML 默认主题"老派"

PlantUML 的 `|Lane|` 是**业界唯一原生真折线 + 真垂直泳道的成熟工具**，但默认主题是 90 年代 UML 风（黄绿配色 + 粗黑边）。可换 theme（cerulean / spacelab / mars / sketchy 等）但要 Java 部署，对纯 Node 生态不友好。

**结论**：能接受 Java + 调主题成本 → PlantUML 是个 fallback；否则自造。

### 陷阱 4：自造 generator 的最大成本是 routing 算法，不是节点画法

自造 SVG generator 的难点不是节点画法，是 **edge routing**：

- **简单 L 型**：`from.bottom → channel_y → to.x → to.top`
- **关键约束**：channel_y 必须落在 row 之间的"空白带"（虚线位置），不能穿过 row 格子，否则线条穿过节点
- **反向流（reject）**：走顶部专用廊道（`REJECT_CORRIDOR_Y`），跟正常流分通道避免重叠
- **最小间距常量自动派生**：避免节点 / 廊道 / SVG 外框互相挤靠

实现详见 `docs/templates/flow-renderer.mjs` 的 `renderEdge` 函数 + `TOP_OFFSET` / `REJECT_CORRIDOR_Y` 派生量。

## 何时再用本结论

- 项目里需要画"业务流程图 / 审批流程 / 状态机泳道"时，**直接用 `docs/templates/flow-renderer.mjs`**
- 评估"换个图工具"前先重读本文件，避免重新踩 4 个陷阱
- 如果未来需要 BPMN 2.0 完整支持（含 message flow / boundary event / data store 等），重新评估 bpmn-js

## 衍生产物

- `docs/templates/flow-renderer.mjs` — 自造 YAML → 飞书风格 SVG generator（v1.0）
- `docs/templates/README.md` — YAML schema 完整使用说明（给后续 AI 写）
- `docs/templates/business-trip.yaml` — 出差申请示例 + 渲染产物

## 关联

- 关联工单 #409（审批 + 表单 AI-first 重设计）阶段 0「AI Form Spec 标准」视图层
- 关联模块 `docs/modules/flow-diagram/` —— flow-diagram 模块的真实化实现路径
