11 KiB
11 KiB
流程图 (Flowchart)
适用于:各种业务流转图、决策树、审批流、时序控制逻辑、带条件判断的链路、系统架构拓扑等。
通用字段语义详见 references/schema.md,通用布局原则详见 references/layout.md;本文件只描述流程图场景下的选型边界与范式。
Important
流程图必须走 DSL 路径,不再使用 Mermaid! 复杂分支、判断、回路、跳级关系优先使用
layout: "dagre"计算拓扑;如果只是规整的单线流水线,且卡片强对齐比自动拓扑更重要,也可以使用 Flex + 顶层connector组合实现。
美学规范
- 摒弃简陋节点,推崇全卡片化:核心业务节点不要只用一个纯文本
rect。应优先采用 Flex 组合卡片(如:在verticalframe 内上下组合【Emoji 标题项】和【补充说明项】),使得节点信息结构化、层级分明。 - 语义化色彩编排:节点底色严禁随机分配。必须按状态语义映射:常规链路用浅蓝/浅紫、核心风控/检查用预警黄、成功通过用生命绿、失败熔断用危险红。边框颜色可同色系加深,以凸显卡片边缘。
- 统一判定逻辑:条件分支必须使用
diamond菱形节点,并且严禁漏掉layoutOptions.edges边定义里的第三个标签参数(必须清晰写明"是/否"、"通过/拒绝")。 - 形状多样化:合理搭配不同形状来表达语义 ——
ellipse用于外部实体/起终点、diamond用于判断路由、rect用于业务处理节点、cylinder用于持久化存储。
Layout 选型
| 模式 | 适用条件 | 核心配置 |
|---|---|---|
| 主体用 Dagre | 有判断、分支、回路、回退、跳级关系的标准流程图 | 主体 frame 设定 layout: "dagre",按需配置 rankdir: "TB" 或 rankdir: "LR"。 |
| 局部复合节点 | 流程中的某一步本身是一个小型 UI 组合体 | 外层仍用 dagre,复合步骤内部改用 layout: "vertical" / "horizontal"。此类节点为不透明节点,外层连线只能连到外壳。 |
| 透明子图 | 需按业务区域分组,且连线穿越区域边界 | 子容器声明 layout: "dagre" + layoutOptions: { isCluster: true },成为透明子图。内部节点直接参与外层拓扑运算。 |
| 规整流水线 | 基本是单线 A → B → C → D,且卡片对齐要求极高 | 主体可用 Flex 排版,连线改用顶层 connector;不要为了"自动"而硬上 Dagre。 |
核心属性
rankdir:TB(上下)或LR(左右)。强烈推荐优先使用LR,充分利用宽屏横向空间。edges: 在根 Dagre 的layoutOptions.edges中按[fromId, toId, "标签"]声明。支持反向连接实现闭环。所有 edges 统一写在最外层根 Dagre,不要写在 cluster 内部。ranksep与边文本: 若边上标注了说明文字,必须根据字数调大间距:ranksep = max(60, 字数 × 16)。- 自适应尺寸: dagre 容器必须设定
width: "fit-content"和height: "fit-content"。 clusterTitle: 透明子图可通过clusterTitle声明悬浮标题(自动吸附左上角、加粗 14px),搭配clusterTitleColor指定标题颜色。
两种嵌套模式
不透明节点(Opaque Node)
Dagre 内的子容器,只要未声明 isCluster: true,对外层 Dagre 就是具有确定宽高的原子节点。外层连线无法寻址其内部子节点。适合封装复杂的组合卡片(如带图标、版本号、多行描述的业务模块)。
透明子图(Compound Cluster)
子容器同时声明 layout: "dagre" 与 layoutOptions: { isCluster: true } 时,成为外层 Dagre 的复合子图。其内部子节点直接参与外层拓扑运算,连线可穿越子图边界。适合划分网络区域、功能层级、命名空间等边界容器。推荐搭配 borderDash: "dashed" 虚线边框 + 淡色背景。
骨架示例(推荐范本)
以下是一个混合架构拓扑的完整示例。它同时展示了透明子图(Kubernetes Zone,连线可穿透)和不透明复合节点(DB 集群、AI 引擎,连线只能连外壳)的标准写法,以及多种形状(ellipse / diamond / rect / cylinder)和语义化配色规范。
{
"version": 2,
"nodes": [
{
"type": "frame",
"id": "root",
"x": 20, "y": 20,
"layout": "dagre",
"width": "fit-content", "height": "fit-content",
"padding": 60,
"fillColor": "#F8FAFC",
"borderColor": "#CBD5E1",
"borderWidth": 1,
"borderRadius": 16,
"layoutOptions": {
"rankdir": "LR",
"nodesep": 60,
"ranksep": 120,
"edges": [
["user", "k8s_ingress", "HTTPS request"],
["k8s_ingress", "web_pod", "Route UI"],
["k8s_ingress", "api_pod", "Route API"],
["web_pod", "api_pod", "Internal REST"],
["api_pod", "db_cluster", "SQL Query"],
["api_pod", "ai_service", "gRPC Stream"]
]
},
"children": [
{
"type": "ellipse", "id": "user", "text": "Global Users",
"width": 110, "height": 60,
"fillColor": "#E2E8F0", "borderColor": "#64748B", "borderWidth": 1,
"fontSize": 14, "textColor": "#334155"
},
{
"type": "frame", "id": "zone_k8s",
"layout": "dagre",
"layoutOptions": {
"isCluster": true,
"clusterTitle": "☸️ Kubernetes Zone (isCluster)",
"clusterTitleColor": "#2563EB"
},
"fillColor": "#EFF6FF", "borderColor": "#60A5FA",
"borderWidth": 2, "borderDash": "dashed", "borderRadius": 24,
"children": [
{
"type": "diamond", "id": "k8s_ingress", "text": "Nginx Ingress",
"width": 130, "height": 70,
"fillColor": "#DBEAFE", "borderColor": "#3B82F6", "borderWidth": 2,
"textColor": "#1E40AF"
},
{
"type": "rect", "id": "web_pod", "text": "Next.js SSR Pod",
"width": 140, "height": 48,
"fillColor": "#BFDBFE", "borderColor": "#2563EB", "borderWidth": 2,
"borderRadius": 8, "textColor": "#1E3A8A"
},
{
"type": "rect", "id": "api_pod", "text": "Go Lang API Pod",
"width": 140, "height": 48,
"fillColor": "#BFDBFE", "borderColor": "#2563EB", "borderWidth": 2,
"borderRadius": 8, "textColor": "#1E3A8A"
}
]
},
{
"type": "frame", "id": "db_cluster",
"layout": "vertical", "gap": 16, "padding": [20, 24],
"alignItems": "center",
"fillColor": "#F0FDF4", "borderColor": "#22C55E",
"borderWidth": 2, "borderRadius": 16,
"children": [
{
"type": "text", "id": "db_title",
"text": "🗄️ Highly Available DB (不透明)", "fontSize": 14, "textColor": "#14532D"
},
{
"type": "frame", "id": "db_row", "layout": "horizontal", "gap": 20,
"children": [
{
"type": "cylinder", "id": "db_master", "text": "Master",
"width": 80, "height": 50,
"fillColor": "#DCFCE7", "borderColor": "#16A34A", "borderWidth": 1,
"textColor": "#166534"
},
{
"type": "cylinder", "id": "db_replica", "text": "Replica",
"width": 80, "height": 50,
"fillColor": "#DCFCE7", "borderColor": "#16A34A", "borderWidth": 1,
"textColor": "#166534"
}
]
}
]
},
{
"type": "frame", "id": "ai_service",
"layout": "vertical", "gap": 10, "padding": [16, 20],
"alignItems": "center",
"fillColor": "#FAF5FF", "borderColor": "#A855F7",
"borderWidth": 2, "borderRadius": 12,
"children": [
{
"type": "text", "id": "ai_title",
"text": "🧠 Multi-Modal Engine (不透明)", "fontSize": 14, "textColor": "#6B21A8"
},
{
"type": "rect", "id": "ai_version",
"text": "v4.2.1-beta", "width": 90, "height": 22,
"fillColor": "#E9D5FF", "borderColor": "#C084FC", "borderWidth": 1,
"borderRadius": 4, "fontSize": 11, "textColor": "#581C87"
},
{
"type": "text", "id": "ai_desc",
"text": "Includes Vector Store\n& Transformer Blocks",
"fontSize": 12, "textColor": "#7E22CE", "textAlign": "center"
}
]
}
]
}
]
}
范本要点:
zone_k8s是透明子图(isCluster: true+clusterTitle),外部连线穿越虚线边界直达k8s_ingress、web_pod、api_pod。db_cluster和ai_service是不透明节点(layout: "vertical"),内部用 Flex 组合了多行结构化信息,对外层 Dagre 是固定宽高的原子。连线只能连到外壳 ID。- 所有
edges统一写在最外层根 Dagre 的layoutOptions中。 - 本范本中用到了
ellipse(外部实体)、diamond(路由判断)、rect(业务节点)、cylinder(数据库存储)四种形状。
陷阱与常见报错防范
- 误用 Mermaid:只要用户没有带
mermaid具体语法代码,哪怕描述明确是"流程图",也强制使用 DSL 框架下的 Dagre 模式。 - 重复画线:
dagre里的所有子节点关系通过edges定义,引擎会自动生成连线。绝对不要再去外层用connector节点重复连一次。 - 穿透黑盒:普通子容器是不透明节点,外部连线无法直接寻址其内部子节点(引擎会自动重定向至外壳)。若需穿透,必须声明
layout: "dagre"与layoutOptions: { isCluster: true }。 id缺失:只要是在edges里出现的标识符,children里一定能找到同名id的节点对应,拼写必须完全一致。- 宽度灾难:Dagre 内容器禁止子框使用
fill-container,因为 dagre 父容器本身是被内容撑开的。