飞书画板(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
导读
里程碑时间线(Milestone Timeline)是产品版本演进、项目排期、公司大事记等场景中最常用的可视化形式之一。在飞书 CLI 的 lark-whiteboard 技能中,这一场景由 scenes/milestone.md 场景指南定义了一套完整的 DSL 绘制规范:包括节点数量约束、两种布局选型、箭头形年份条 + 虚线卡片的视觉结构,以及可直接套用的 JSON 骨架模板。读完本文,你将掌握如何在飞书画板中通过layout: "none"绝对定位 + connector 时间轴的方式,从零构建一张专业、严格对齐的里程碑时间线图,并了解如何结合 whiteboard-cli 与lark-cli whiteboard +update将其写入真实画板。
一、里程碑时间线:场景定位与核心约束
在 routes/dsl.md 的场景指南索引中,里程碑(scenes/milestone.md)被明确标注为适用于「时间线、版本演进」类图表,与架构图、泳道图、鱼骨图等并列,是 DSL 路径下的一种标准场景范式。
该场景的Content 约束(内容约束)非常明确:
- 节点数量 4-8 个:里程碑数量有严格上下限,太少撑不起时间线的叙事,太多则容易拥挤(下文「陷阱」一节会专门说明超限处理);
- 每个节点由三部分组成:标题 + 日期 + 可选描述;
- 时间语义从左到右递增:节点在画布上的水平位置(x 坐标)承载时间先后关系,越靠右越晚。
这意味着里程碑图的信息结构是「一维序列」,天然适合把 x 坐标当作时间的映射轴——这也是该场景选择绝对定位而非 Flex/Dagre 的根本原因(对应 elements/layout.md 中「节点位置本身有含义(拓扑图、地图、时间线轴)时用绝对定位」的布局决策原则)。
二、两种布局选型:横向时间线 vs 交替上下
原文档给出了两种按需选择的布局方案,它们对应不同节点数量下的最优视觉表现:
| 方案 | 实现方式 | 适用场景 |
|---|---|---|
| 横向时间线 | horizontal frame,节点等分 | 节点较少(默认推荐),结构规整 |
| 交替上下 | 绝对定位,节点交替分布在时间轴上下方 | 节点较多时更紧凑,避免单侧堆积 |
选型判断:当节点数接近上限(如 6-8 个)时,若全部排在一侧,卡片会纵向撑得很高或横向挤得很密;采用上下交替布局可以让左右相邻节点的卡片错开,压缩整体纵向高度。从 elements/content.md 的分组与精简原则来看,超过 5 个节点的横排已经属于「一行放不下」的范畴,需要主动考虑布局策略调整。
三、结构特征:一张标准里程碑图由哪些元素构成
原文档对里程碑图的结构特征做了精确定义,这是判断渲染结果是否符合预期的视觉验收标准:
- 标题居中:图表标题(如「产品 2024 年度里程碑」)放在画布顶部居中;
- 年份/时间轴条:使用箭头形色块承载年份(如 2024、2025),按时间从左到右递增排列;
- 里程碑卡片:时间轴下方放置虚线圆角卡片,承载里程碑标题与描述;
- 严格对齐:年份条与对应卡片等宽、左右对齐(两者的 x 与 width 必须完全一致);
- 文字层级:标题加粗在上(如 fontSize 16),描述文字更小更浅在下(如 fontSize 13),均居中对齐——这与 elements/typography.md 的「字号层级表」完全吻合(H3 15-16 用于卡片标题、Caption 13 用于辅助说明,且同张图不超过 3 个字号层级)。
箭头形年份条的实现在 DSL 中借助SVG 节点完成。按 elements/schema.md 的 SVG 渲染规范,内联 SVG 必须包含viewBox与xmlns属性,且只能使用纯几何绘制元素——里程碑场景使用的<polygon points="0,0 170,0 190,18 170,36 0,36"/>正是允许的图形之一,通过三个点的坐标描绘出右侧带箭头的色块形状。
四、Layout 规则:绝对定位下的坐标纪律
原文档对布局规则的描述是里程碑图能够「严格对齐」的关键,逐条拆解如下:
- 绝对定位为主:整个画布容器使用
layout: "none",节点位置(x/y)直接承载时间序列含义; - 先定数量,再算坐标:先确定里程碑数量 N,再计算等距的 x 坐标序列——例如画布宽 1200、卡片宽 190 时,x 依次取 50、290、530…(步长 = 卡片宽 + 间距);
- 时间轴用 connector 贯穿所有节点:用一条贯穿的连线把整个时间序列串起来;
- 节点与时间轴用短竖线连接:每个里程碑卡片通过短的竖直连线挂到时间轴上;
- 节点间水平间距一致:等距分布保证时间刻度均匀;
- 年份条宽度 = 卡片宽度,垂直间距统一:保证上下视觉对位;
- 标题与年份区域保留足够留白:标题区(y≈12-44)与年份条(y≈56)之间、年份条与卡片(y≈132)之间留有清晰间隔。
从 elements/layout.md 的绝对定位规则可以印证两点实现细节:
layout: "none"的容器必须有固定宽高:骨架示例中外层 frame 显式声明了"width": 1200, "height": 360,绝不能写成fit-content,否则子节点绝对定位会错乱;- flex 容器内的 x/y 会被完全忽略:这也解释了为什么里程碑图必须用
layout: "none"而非 horizontal frame——x 坐标是时间轴的核心语义,不能被 Flex 接管。
关于时间轴连线,elements/connectors.md 还给出了一个与本场景强相关的约束:绘制坐标轴/数轴必须使用lineShape: 'straight',因为polyline/rightAngle的自动避障机制可能在刻度元素触发时把线条绕弯,破坏时间轴的笔直性。同时 connector 必须放在根nodes数组(与顶层 frame 平级),不能嵌套在children中。
五、骨架示例精解:可直接复用的完整 DSL
以下是原文档给出的完整骨架示例(双节点版,实际使用时按节点数复制扩展):
{ "version": 2, "nodes": [ { "type": "frame", "x": 0, "y": 0, "width": 1200, "height": 360, "layout": "none", "children": [ { "type": "text", "x": 300, "y": 12, "width": 600, "height": "fit-content", "text": [{ "content": "{{CHART_TITLE}}", "bold": true, "fontSize": 24 }], "textAlign": "center" }, { "type": "svg", "x": 50, "y": 56, "width": 190, "height": 36, "svg": { "code": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 190 36\"><polygon points=\"0,0 170,0 190,18 170,36 0,36\"/></svg>" } }, { "type": "text", "x": 50, "y": 64, "width": 190, "height": "fit-content", "text": "{{DATE_1}}", "textAlign": "center" }, { "type": "rect", "x": 50, "y": 132, "width": 190, "height": 120, "borderDash": "dashed", "borderRadius": 8 }, { "type": "text", "x": 50, "y": 150, "width": 190, "height": "fit-content", "text": [{ "content": "{{MILESTONE_1_TITLE}}", "bold": true, "fontSize": 16 }], "textAlign": "center" }, { "type": "text", "x": 50, "y": 180, "width": 190, "height": "fit-content", "text": "{{MILESTONE_1_DESC}}", "fontSize": 13, "textAlign": "center" }, { "type": "svg", "x": 290, "y": 56, "width": 190, "height": 36, "svg": { "code": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 190 36\"><polygon points=\"0,0 170,0 190,18 170,36 0,36\"/></svg>" } }, { "type": "text", "x": 290, "y": 64, "width": 190, "height": "fit-content", "text": "{{DATE_2}}", "textAlign": "center" }, { "type": "rect", "x": 290, "y": 132, "width": 190, "height": 120, "borderDash": "dashed", "borderRadius": 8 }, { "type": "text", "x": 290, "y": 150, "width": 190, "height": "fit-content", "text": [{ "content": "{{MILESTONE_2_TITLE}}", "bold": true, "fontSize": 16 }], "textAlign": "center" }, { "type": "text", "x": 290, "y": 180, "width": 190, "height": "fit-content", "text": "{{MILESTONE_2_DESC}}", "fontSize": 13, "textAlign": "center" } ] } ] }对模板中的占位符与坐标规律做逐一说明(便于扩展到 8 个节点):
| 占位符 | 含义 | 取值建议 |
|---|---|---|
{{CHART_TITLE}} | 图表标题 | 加粗、fontSize 24,居中于画布顶部 |
{{DATE_N}} | 第 N 个里程碑的年份/日期 | 如 2024 Q1,fontSize 13-14 |
{{MILESTONE_N_TITLE}} | 第 N 个里程碑标题 | 加粗、fontSize 16 |
{{MILESTONE_N_DESC}} | 第 N 个里程碑描述(可选) | fontSize 13,更小更浅 |
坐标递推规律(对应原文档「先确定里程碑数量,计算等距的 x 坐标序列」):
- 年份条与卡片统一宽度
190,x 步长为240(190 卡片宽 + 50 间距),即第 N 个节点的x = 50 + (N-1) × 240; - 同一节点的年份条(y=56)、日期文字(y=64)、卡片(y=132)、标题(y=150)、描述(y=180)纵向层叠,垂直间距统一;
- 日期文字与箭头色块共用 x/width,保证「年份条与卡片等宽、左右对齐」。
需要补充的还有两个模板未展开的要素:
- 时间轴 connector:在根
nodes数组中追加一条lineShape: 'straight'的连线贯穿所有节点(如from: {x: 50, y: 100}, to: {x: 770, y: 100}),每个节点再配一条短竖线(如从{x: 145, y: 100}到{x: 145, y: 132})连接卡片;连线必须放在根 nodes 数组末尾,不能放进 frame 的 children(见 elements/connectors.md); - 文字高度用
fit-content:所有含文字节点(标题、日期、卡片标题、描述)的 height 均为"fit-content",因为引擎不支持 overflow,写死高度会截断文字(见 elements/layout.md 注意事项第 5 条)。
六、陷阱清单:四个必须避开的坑
原文档以「陷阱」小节收尾,这些是渲染审查(对应 routes/dsl.md Step 3 的检查清单)时必须重点排查的点:
- 节点太多时太拥挤:超过 6 个节点时,应切换为交替上下布局(节点交替分布在时间轴上下方),或增大画布宽度(画布宽度常用范围 1000-1400px,见 elements/layout.md 的常用间距表);
- 右侧节点与时间轴末端重叠:最后一个节点的
x + width不要超出画布边界——例如画布 1200 宽、卡片 190 宽时,最后一个节点的 x 应满足x + 190 ≤ 1200,即 x 最大约 1010; - 年份条与卡片不对齐:年份条和卡片的x、width 必须完全一致——这是本场景最容易出现的视觉瑕疵,根源往往是复制扩展模板时只改了 x 忘了同步 width,或年份条与卡片各自采用了不同的步长;
- 连线形状误用:时间轴必须用
straight直线,若误用polyline/rightAngle,刻度附近的自动避障可能把时间轴绕出弧度(由 elements/connectors.md 的坐标轴规则引申)。
七、端到端落地:从 DSL 到真实画板
骨架模板产出diagram.json后,按 references/lark-whiteboard-workflow.md 的「渲染 & 写入画板」流程执行(完整命令语法见 references/lark-whiteboard-update.md):
Step 1:本地渲染审查
npx -y @larksuite/whiteboard-cli@^0.2.13 -i diagram.json -o diagram.png用 PNG 做预览验证:信息是否完整、布局是否合理、文字有无截断、年份条与卡片是否对齐、时间轴是否笔直。发现问题按上述陷阱清单修复后重新渲染(最多两轮)。
Step 2:转换为 OpenAPI 格式并写入画板
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关键参数说明(详见 lark-whiteboard-update.md):
--whiteboard-token:目标画板 token(wbcnXXX格式),需拥有画板编辑权限;--input_format raw:DSL 产物必须先用 whiteboard-cli 转成 OpenAPI 原生节点格式再以raw写入;--idempotent-token:幂等 token,最少 10 个字符,建议使用时间戳 + 场景标识拼接(如1744800000-milestone-1);同一次逻辑更新只生成一次,重试时原样复用,切勿每次重试都重新生成,否则会重复写入;--as user:画板操作默认以用户身份执行(参见 SKILL.md 的快速决策);- 首次写入空白画板时无需
--overwrite;若写入已有内容画板并需覆盖,则要附加--overwrite并确认会整板重建。
结语
里程碑时间线是 lark-whiteboard DSL 路径中最典型的「绝对定位承载语义」场景:x 坐标即时间、layout: "none"容器 + 固定宽高是前提、箭头 SVG + 虚线卡片是视觉骨架、等距步长与严格对齐是质量底线。掌握 scenes/milestone.md 的约束与骨架模板,再结合 schema.md、layout.md、connectors.md 的底层规则,即可稳定产出可写入飞书画板的版本演进时间线,也能轻松迁移到组织大事记、项目排期等同类时间序列图表。
【免费下载链接】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),仅供参考