飞书白板对比图/矩阵图实战指南:基于 lark-whiteboard DSL 的方案对比与功能矩阵绘制
【免费下载链接】cliThe official Lark/飞书 CLI tool, maintained by the larksuite team — built for humans and AI Agents. Covers core business domains including Messenger, Docs, Base, Sheets, Calendar, Mail, Tasks, Meetings, and more, with 200+ commands and 20+ AI Agent Skills.项目地址: https://gitcode.com/gh_mirrors/cli414/cli
本文基于 lark-whiteboard 技能(skills/lark-whiteboard)的对比图场景指南,系统讲解如何在飞书画板中用 Whiteboard DSL 绘制方案对比、功能矩阵、技术选型等多选项多维度对比图。你将掌握对比图的 Content 约束与两种布局选型(严格 grid 与卡片式对比)、完整的 DSL 骨架模板、可复用的配色与间距规则,以及从本地渲染审查到lark-cli whiteboard +update写入画板的完整交付链路。
对比图场景定位:什么时候用这张图
对比图/矩阵图适用于多选项按多维度比较的场景,典型用例包括:
- 方案对比:技术选型(如不同数据库、不同消息队列、不同框架)在多维度上的能力对照
- 功能矩阵:同一产品多个版本(免费版/专业版/企业版)的功能支持情况
- 竞品对比:多款产品在性能、成本、生态、易用性等维度上的横向比较
它在 lark-whiteboard 的场景指南体系中属于"结构化信息优先用 Flex 布局"的典型代表(对比表本质是一个 grid:纵向 frame 嵌套横向 frame),与架构图、组织架构图同属网格方法论下的图表类型,但结构更规整——行是维度、列是对象,天然适合"先画网格,再填内容"的网格方法论(elements/layout.md)。
Content 约束:每格内容要充实,布局才不会塌
对比图的信息密度直接决定图表质量。场景指南给出了五条硬性内容约束:
- 每格内容要充实:不要只写一个关键词,要给出具体说明。例如写"MVCC 多版本并发控制,支持行级锁"而不是只写"支持"——关键词会让格子空旷、表意模糊,具体说明才能承载技术选型场景下的真实信息量。
- 单格长度:不同格子允许不同长度,但每格不超过 5 行,防止某列被撑得过高破坏整体比例。
- 长文本对齐:超过 15 字(即可能换行)的文本用
textAlign: "left",不要居中——居中多行文本可读性差。这与排版规则中"长文本左对齐"的原则一致。 - 行列结构:第一行是标题行(对象名称),第一列是维度标签列。
- 维度数量:至少 4 个维度,充分展开对比维度。少于 4 个维度建议直接换用下面的卡片式对比布局。
Layout 选型:严格 grid 还是卡片式对比
场景指南给出了两种布局模式,按维度数量决策:
| 模式 | 适用条件 | 特征 |
|---|---|---|
| 严格 grid(默认) | 所有对比场景 | 表头行 + 数据行,每行 horizontal frame,行内 rect 等分 |
| 卡片式对比(替代) | 维度较少(2-3 个) | 每个对象做一张独立卡片,卡片内纵向列出各维度。卡片横向等分:外层layout: "horizontal",每张卡片width: "fill-container" |
当维度少于 4 个时,严格 grid 会出现"列宽正常但行数太少"的稀疏感,此时把每个对象做成一张独立卡片(卡片内用纵向布局列出各维度)信息呈现更紧凑。卡片式对比与布局系统中"横向等分(并列元素)"的写法一致:alignItems: 'stretch'+width: 'fill-container'保证各卡片等宽等高。
Layout 规则:让表格整齐的关键六条
对比图观感好坏,几乎全部由下面六条 Layout 规则决定(均可在骨架示例中一一对应验证):
- 最外层 frame:
layout: "vertical",固定width(如 1000),height: "fit-content"——表格总宽固定,总高由内容决定,避免固定高度导致文字截断。 - 每行:horizontal frame,
width: "fill-container",alignItems: "stretch"——行宽占满外框,stretch 保证同行单元格因文字行数不同时仍等高(见陷阱"行高不等")。 - 行内单元格:全部
width: "fill-container"等分列宽,不要给任何一列写固定宽度。 - 行间
gap >= 12:不要用 8,8 会让行与行视觉粘连(场景指南明确点名这个反例)。 - 行内列间
gap: 8-12:列间距比行距稍窄,既保持分隔又不割裂同一行的对比关系。 - 标题行:深色底白字,由 style 控制具体颜色(经典色板下即
fillColor: "#1F2329"、textColor: "#FFFFFF",见配色系统的"表头"规范)。 - 每列同色边框:同一列的单元格使用同色边框,保持视觉一致性——这正是配色系统中"对比表有 3 列 → 每列表头一种颜色,该列数据单元格用同色边框"的具体落法。
- 单元格
height: "fit-content":不要写固定 height,引擎不支持 overflow,固定高度会截断文字。
其中fill-container、fit-content、stretch这些尺寸与对齐值的语义,对应 elements/schema.md 中的 WBSizeValue 表:'fill-container'填满父级剩余空间(等价 CSSflex: 1),'fit-content'由内容决定大小(等价 CSSwidth: auto),alignItems: 'stretch'是 Flexbox 的等高拉伸——注意 DSL 里alignItems默认值是'start'而非 CSS 默认的stretch,所以等高必须显式声明alignItems: 'stretch'。
骨架示例:3 列 4 行表格的完整 DSL
场景指南给出了一个"3 列 4 行"(维度列 + 3 个对象列,含标题行共 4 行)的完整可运行骨架。完整 JSON 结构如下:
{ "version": 2, "nodes": [ { "type": "frame", "width": 1000, "height": "fit-content", "layout": "vertical", "gap": 12, "padding": 0, "children": [ { "type": "text", "id": "title", "width": "fill-container", "height": "fit-content", "text": "[对比图标题]", "fontSize": 24, "textAlign": "center", "verticalAlign": "middle" }, { "type": "frame", "id": "header-row", "width": "fill-container", "height": "fit-content", "layout": "horizontal", "gap": 8, "padding": 0, "alignItems": "stretch", "children": [ { "type": "rect", "id": "h-dim", "width": "fill-container", "height": "fit-content", "text": "[维度]", "fontSize": 15, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 0, "borderWidth": 2 }, { "type": "rect", "id": "h-col-1", "width": "fill-container", "height": "fit-content", "text": "[对象A]", "fontSize": 15, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 }, { "type": "rect", "id": "h-col-2", "width": "fill-container", "height": "fit-content", "text": "[对象B]", "fontSize": 15, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 }, { "type": "rect", "id": "h-col-3", "width": "fill-container", "height": "fit-content", "text": "[对象C]", "fontSize": 15, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 } ] }, { "type": "frame", "id": "data-row-1", "width": "fill-container", "height": "fit-content", "layout": "horizontal", "gap": 8, "padding": 0, "alignItems": "stretch", "children": [ { "type": "rect", "id": "d1-dim", "width": "fill-container", "height": "fit-content", "text": "[维度1]", "fontSize": 14, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 }, { "type": "rect", "id": "d1-c1", "width": "fill-container", "height": "fit-content", "text": "[...]", "fontSize": 14, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 }, { "type": "rect", "id": "d1-c2", "width": "fill-container", "height": "fit-content", "text": "[...]", "fontSize": 14, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 }, { "type": "rect", "id": "d1-c3", "width": "fill-container", "height": "fit-content", "text": "[...]", "fontSize": 14, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 } ] }, { "type": "frame", "id": "data-row-2", "width": "fill-container", "height": "fit-content", "layout": "horizontal", "gap": 8, "padding": 0, "alignItems": "stretch", "children": [ { "type": "rect", "id": "d2-dim", "width": "fill-container", "height": "fit-content", "text": "[维度2]", "fontSize": 14, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 }, { "type": "rect", "id": "d2-c1", "width": "fill-container", "height": "fit-content", "text": "[...]", "fontSize": 14, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 }, { "type": "rect", "id": "d2-c2", "width": "fill-container", "height": "fit-content", "text": "[...]", "fontSize": 14, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 }, { "type": "rect", "id": "d2-c3", "width": "fill-container", "height": "fit-content", "text": "[...]", "fontSize": 14, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 } ] }, { "type": "frame", "id": "data-row-3", "width": "fill-container", "height": "fit-content", "layout": "horizontal", "gap": 8, "padding": 0, "alignItems": "stretch", "children": [ { "type": "rect", "id": "d3-dim", "width": "fill-container", "height": "fit-content", "text": "[维度3]", "fontSize": 14, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 }, { "type": "rect", "id": "d3-c1", "width": "fill-container", "height": "fit-content", "text": "[...]", "fontSize": 14, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 }, { "type": "rect", "id": "d3-c2", "width": "fill-container", "height": "fit-content", "text": "[...]", "fontSize": 14, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 }, { "type": "rect", "id": "d3-c3", "width": "fill-container", "height": "fit-content", "text": "[...]", "fontSize": 14, "textAlign": "center", "verticalAlign": "middle", "borderRadius": 8, "borderWidth": 2 } ] } ] } ] }对骨架的要点解读(结合 elements/schema.md 的节点语义):
- 顶层
nodes数组只有 1 个垂直 frame,其children依次为标题 text 和 4 个 horizontal frame(1 个表头行 + 3 个数据行)。 - 标题行中维度列单元格用
borderRadius: 0(直角),与数据列的borderRadius: 8形成区分——这正是"标题行(维度列)用与数据列不同的视觉处理"的具体实现。 - 表头行字号 15、数据行字号 14;表头用深色底白字(样式见下),数据格默认白底带边框。
- 每个单元格都是
rect节点,width: "fill-container"等分,height: "fit-content"自适应内容。 - 行间
gap: 12(外层 frame 的 gap),列间gap: 8(行 frame 的 gap),符合"行间 >= 12、列间 8-12"的规则。
需要增加维度时,复制一个>npx -y @larksuite/whiteboard-cli@^0.2.13 -i diagram.json -o diagram.png
渲染后按自查清单检查:信息完整?布局合理?配色协调?文字无截断?连线无交叉?发现问题按症状表修复(如"文字被截断 → height 改为 fit-content"、"节点重叠粘连 → 增大 gap"),最多重试 2 轮,2 轮后仍有严重问题可考虑走 Mermaid 路径兜底(见 routes/dsl.md)。
Step 3:写入画板
用 whiteboard-cli 将 DSL 转为 OpenAPI 格式并通过管道传给+update:
npx -y @larksuite/whiteboard-cli@^0.2.13 -i diagram.json --to openapi --format json \ | lark-cli whiteboard +update --whiteboard-token <board_token> \ --source - --input_format raw --idempotent-token <时间戳+标识> --as user写入空画板时无需--overwrite(默认 append 增量追加);若画板已有内容且需要整板替换,必须加--overwrite并先向用户确认会整板重建(见更新画板文档)。
+update 关键参数说明
lark-cli whiteboard +update的命令实现在 shortcuts/whiteboard/whiteboard_update.go 中,其参数校验逻辑与文档一致:
| 参数 | 必填 | 说明(含源码确认的细节) |
|---|---|---|
--whiteboard-token | 是 | 画板 token,需拥有画板编辑权限;含控制字符会被RejectDangerousCharsTyped拦截 |
--idempotent-token | 否 | 幂等 token,最少 10 个字符(源码len(itoken) < 10直接返回校验错误),建议时间戳 + 场景标识拼接(如1744800000-board-1)。同一次逻辑更新只生成一次,重试时原样复用,否则会重复写入 |
--overwrite | 否 | 写入模式:带上则覆盖更新(先删后写),省略则增量追加,默认 false |
--source | 是 | 输入内容,支持@path读文件或-读 stdin |
--input_format | 否 | raw/plantuml/mermaid/svg,默认raw(源码getFormat中空值回落 raw) |
从源码还可以看到两个实现事实:raw 格式走POST /open-apis/board/v1/whiteboards/{token}/nodes创建节点,plantuml/mermaid/svg 走POST /open-apis/board/v1/whiteboards/{token}/nodes/plantuml的图表导入接口(syntax_type分别映射 0/1/2/3);raw 路径解析的是 whiteboard-cli 的--to openapi输出格式(WbCliOutput),这也解释了为什么 DSL 产物必须先用 whiteboard-cli 转换。该命令需要board:whiteboard:node:create权限,支持--as user与--as bot两种身份,默认使用--as user。
陷阱清单:新手最容易翻车的七个点
场景指南末尾的陷阱清单是实战中最有价值的反模式,逐一对照:
- 行间距 8px 太紧:行间 gap 至少 12,8 会让行与行视觉粘连。这是最常被忽略的一条——行 frame 之间靠外层 frame 的
gap控制,务必显式写 12 或以上。 - 长文本居中:超过一行的文本改
textAlign: "left",居中多行文本可读性差。 - 列数太多导致每列太窄:对比对象建议 ≤ 5 列(含维度列),超过时合并维度或拆分为多张表。即 3 个对象列 + 1 个维度列已经接近上限。
- 列宽不等:所有数据列必须用
width: "fill-container"等分,不要给某列写固定宽度——固定宽度会破坏整张表的对齐。 - 行高不等:每行 frame 必须
alignItems: "stretch",否则同行单元格因文字行数不同而高矮不齐。 - 忘记维度标签列:第一列放维度名称,标题行(维度列)用与数据列不同的视觉处理(骨架中用
borderRadius: 0区分)。 - 单元格用固定 height:单元格必须
height: "fit-content",固定高度会导致文字截断(引擎不支持 overflow)。
对照 routes/dsl.md 的渲染前自查清单,对比图还额外要注意:frame 都要写layout属性、gap和padding显式设置、含文字节点高度用fit-content、connector 放顶层nodes(对比图通常无连线,可忽略)。
与其它场景指南的关系
对比图在 lark-whiteboard 的场景体系中与架构图共享"网格/分区"心智模型,但对比图更强调"行列对齐"而架构图更强调"层次分区";若对比场景中需要体现对象间的流程或依赖关系(比如在矩阵下方附加一条决策链),则属于 flowchart 或 Dagre 布局的范畴(见 elements/layout.md 的"布局选择指南")。所有场景共用的底层知识统一沉淀在 elements/schema.md(语法)、elements/layout.md(布局)、elements/style.md(配色)三个核心模块中,编写对比图 DSL 前建议先通读这三份文档。
【免费下载链接】cliThe official Lark/飞书 CLI tool, maintained by the larksuite team — built for humans and AI Agents. Covers core business domains including Messenger, Docs, Base, Sheets, Calendar, Mail, Tasks, Meetings, and more, with 200+ commands and 20+ AI Agent Skills.项目地址: https://gitcode.com/gh_mirrors/cli414/cli
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考