forked from AxTps/Starlight_Lancher
feat:移除了弹窗,服务器添加sls
This commit is contained in:
234
.claude/skills/lark-sheets/SKILL.md
Normal file
234
.claude/skills/lark-sheets/SKILL.md
Normal file
@ -0,0 +1,234 @@
|
||||
---
|
||||
name: lark-sheets
|
||||
version: 3.0.2
|
||||
description: "飞书电子表格:创建和操作电子表格。支持创建表格、管理工作表与行列结构(增删/合并/调整尺寸/隐藏/冻结)、读写单元格(值/公式/样式/批注/单元格图片)、查找替换、多操作原子批量更新,以及图表、透视表、条件格式、筛选器、迷你图、浮动图片等对象的创建与维护。当用户需要创建电子表格、管理工作表、批量读写或编辑数据、统计汇总与可视化、表格美化、公式计算(含 Excel 公式迁移)、金融/财务建模(DCF、三张表、预算、Sensitivity 等)等任务时使用。若用户是想按名称或关键词搜索云空间(云盘/云存储)里的表格文件,请改用 lark-drive 的 drive +search 先定位资源。当用户给出 doubao.com 的 /sheets/ URL/token 时,也应直接使用本 skill,不要因为域名不是飞书而回退到 WebFetch;路由依据是 URL 路径模式和 token,而不是域名。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["lark-cli"]
|
||||
siblings: ["lark-shared"]
|
||||
cliHelp: "lark-cli sheets --help"
|
||||
---
|
||||
|
||||
# sheets
|
||||
|
||||
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md),其中包含认证、权限处理。**
|
||||
|
||||
## 术语约定
|
||||
|
||||
下列词在本 skill 各文档中可能交替出现,但**指同一对象**;解析用户口语时按此映射,不要当成不同概念:
|
||||
|
||||
| 标准用语 | 同义 / 口语(均指同一对象) | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 工作表(sheet) | 子表、tab、标签页 | spreadsheet 内的单张表;`sheet_id` 是其稳定标识 |
|
||||
| 电子表格(spreadsheet) | 工作簿、表格 | 顶层容器;由 `--url` 或 `--spreadsheet-token` 定位 |
|
||||
| reference_id | id | **表内对象**的稳定标识,即各对象主键 flag 接受的值(见下表)。⚠️ 与 `lark-sheets-float-image` 的 `--image-uri`(图片上传句柄)不是一回事,后者不属于 reference_id |
|
||||
|
||||
每类对象用各自的主键 flag 定位(命名不统一,按此表对照,不要凭直觉拼):
|
||||
|
||||
| 对象 | 主键 flag | 对象 | 主键 flag |
|
||||
| --- | --- | --- | --- |
|
||||
| 工作表 sheet | `--sheet-id` | 条件格式规则 | `--rule-id` |
|
||||
| 图表 chart | `--chart-id` | 筛选视图 | `--view-id` |
|
||||
| 透视表 pivot | `--pivot-table-id` | 迷你图(按组) | `--group-id` |
|
||||
| 浮动图片 | `--float-image-id` | | |
|
||||
|
||||
## 飞书表格编辑准则(动手前必守,所有编辑类任务一律生效)
|
||||
|
||||
下列准则横切所有飞书表格任务,**动手前先过一遍**——即使你是被索引直接路由进某个工具参考也一律生效。每条只给一句话纲要,展开与边界见括注的 reference。
|
||||
|
||||
1. **最小改动**:除任务要改的单元格 / 列外,原表其它单元格、行列结构、Sheet 名、合并区、格式 1:1 保持;中间结果放原数据右侧或新建空白 Sheet,**禁止删 / 改名 / 隐藏 / 移动已存在 Sheet**;改写类任务精确圈定行列,不该转的原值 1:1 保留。
|
||||
2. **真实写回 + 回读校验**:交付必须是对在线表格的真实写入,写完用 `+csv-get` / `+cells-get` / `+<对象>-list` 回读确认实际生效——**写操作返回 `ok` 只代表请求被接受、不代表结果符合预期**;写公式后查错误码、筛选 / 排序后核对前几行、删除 / 清空后确认已空。禁止只在文本里声称"已完成"。
|
||||
3. **读全再写**:批量填充 / 补齐 / 修正类任务先确认真实数据末行再写,只探前 N 行会漏写表尾(确定末行流程见 `lark-sheets-read-data`)。
|
||||
4. **公式优先于硬编码**:能用公式表达的计算(总计 / 占比 / 增长率 / 提取 / 查找)一律写公式而非静态值;**凡可由表内其它单元格推导的派生值默认就用公式,即使用户没说"联动 / 自动更新"**;写任何飞书公式前先读 `lark-sheets-formula-translation`,而且**只要公式真实写入表格,收尾默认就要继续跑 `lark-sheets-formula-verify` 的 `+formula-verify`,直到 `status='success'`**。
|
||||
5. **续写 / 扩展继承样式**:续写、补齐、复制区块、新增行列时禁止只读值只写值,必须连带 `cell_styles` + `border_styles` + 合并 + 行高一起继承(清单见 `lark-sheets-write-cells`,四边框最易漏)。
|
||||
6. **多步写入合并 `+batch-update`**:多个连续写入、或同一工具对多区域重复调用,合并为单次原子 `+batch-update`(语义见 `lark-sheets-batch-update`)。
|
||||
7. **分组汇总用透视表**:"按 X 统计 Y / 分组汇总 / 各类数量金额"用 `+pivot-{create|update|delete}`,禁止用 SUMIF / 本地脚本拼一张假透视表。
|
||||
8. **拆成可验证 checklist**:落地前把指令拆成所有"独立可验证子要点",逐点 `assert` 全过才交付(多维排序每维一点、多目标每目标一点、范围类核起 / 末 / 边界);只做第一个要点属违规。
|
||||
9. **全量处理前置断言条数**:翻译 / 打标 / 批量公式落地等逐条任务,先把预期条数硬编码再 `assert actual == expected`,禁止输出"已完成前 N 条,剩余继续"的半成品。
|
||||
|
||||
> 上述准则的实操展开——读取路径、原生工具优先级、脚本配合、易漏陷阱——见下方「执行要点」节;端到端工作流为:了解结构(`+workbook-info`)→ 读数据 → 理解语义 → 原生工具优先 → 写入 → 回读验证。
|
||||
|
||||
## 场景 → 命令速查(拿不准命令名先查这里,别按直觉拼)
|
||||
|
||||
把高频意图映射到**真实存在**的 shortcut / flag。agent 常从 Excel / Google Sheets / 飞书 OpenAPI 误迁移命令名或 flag,先对照本表,避免一次必然失败的试错。完整 shortcut 见各工具参考。**选定命令后别急着写——先读「动手前读」列指向的 reference 再动手**:命令名对得上不代表用法对,写入 / 清除 / 透视类尤其容易漏掉 reference 里的防错、类型与样式继承规则。
|
||||
|
||||
| 你要做的事 | ✅ 正确写法 | 动手前读 | ❌ 不存在(会被 cobra 拒) |
|
||||
| --- | --- | --- | --- |
|
||||
| 读数据(纯值 / CSV) | `+csv-get`(范围用 `--range`) | `lark-sheets-read-data` | `+get-range`、`+range-get`、`+cells-read` |
|
||||
| 读值 + 公式 / 样式 / 批注 | `+cells-get --include value,formula,style,comment,data_validation` | `lark-sheets-read-data` | `+get-cell`、`+cell-get`、`--with-styles`、`--with-merges`、`--include-merged-cells` |
|
||||
| 写纯文本值(整块 CSV 平铺;列里**没有**需字面保真的数值 / 日期标签 / 编号——点分日期 `12.10`、编号 `001` 会被 csv-put 数值化,不算纯文本) | `+csv-put`(定位用 `--start-cell`,单个左上角锚点格;也接受 `--range` 别名,区间自动取左上角) | `lark-sheets-write-cells` | 把含点分日期(`12.10`)/编号(`001`)的列裸灌 `+csv-put`——会被数值化(`12.10`→`12.1`、`001`→`1`,尾零/前导零丢失),改用 `+table-put` 声明 `dtypes:object` |
|
||||
| 写带类型的数据到**已有**表(列里有数字 / 金额 / 百分比 / 日期 / 计数等**本质是量值**的数据——不看当下要不要排序 / 求和,量值一律走这里) | `+table-put --sheets` 完整 payload `{"sheets":[{...}]}`(列名走 `columns`、二维数据走 `data`、列 pandas dtype 走 `dtypes`、列展示格式走 `formats`;来源不限 DataFrame——Counter / dict / list 同理;要同时美化加 `--styles` 一步带样式(区域底色 / 边框 / 列宽 / 行高 / 合并),不必事后再刷;payload 里不存在的 sheet 名会自动建子表,详见 write-cells) | `lark-sheets-write-cells` | 在本地把数字拼成 `"$1,234"` / `"30.5%"` 字符串再 `+csv-put`(会落成文本、丢失计算能力;常见借口见下方 ⚠️) |
|
||||
| **新建**电子表格并写带类型的数据(类型保真需求同上,但目标表还不存在) | `+workbook-create --sheets`(协议与 `+table-put` 同构、一步建表 + typed 写入,无需先建空表再 `+table-put`;date / number 不丢;`--styles` 同样可在建表同一步带全套样式,详见 workbook) | `lark-sheets-workbook` | 用 `--values` 灌日期 / 数字(会落成文本、丢类型) |
|
||||
| 写公式 / 富写入(样式 · 批注 · 图片 · 富文本),或需精确矩形定位的值 | `+cells-set`(定位用 `--range`;批注 / 图片 / 富文本只能用它,公式也可;**公式落表后继续 `+formula-verify` 收尾**) | `lark-sheets-write-cells` | — |
|
||||
| 插图:图片**绑定到某条记录**、随行走(凭证 / 证件照 / 商品图 / 头像 / 二维码 / 每行配图) | `+cells-set-image`(单格 `--range`,嵌入单元格内) | `lark-sheets-write-cells` | — |
|
||||
| 插图:**自由摆放、不绑数据**的装饰 / 标识(logo / 水印 / 封面大图 / banner) | `+float-image-create`(浮动图片,自由定位 + 尺寸 + 层级) | `lark-sheets-float-image` | — |
|
||||
| 查找 / 替换文本 | `+cells-search`(找,关键字用 `--find`)、`+cells-replace`(替换) | `lark-sheets-search-replace` | `+cells-find`、`+find`、`--query` |
|
||||
| 看子表结构(合并 / 行高列宽 / 冻结 / 隐藏) | `+sheet-info` | `lark-sheets-sheet-structure` | `+sheet-get`、`+structure-get`、`+sheet-structure-get` |
|
||||
| 看工作簿 / 子表清单 | `+workbook-info` | `lark-sheets-workbook` | `+sheet-list`、`+workbook-get`、`+workbook-list` |
|
||||
| 复核某次(AI)编辑改了什么 / 取两个版本间的变更 | `+changeset-get --start-revision <编辑前版本>`(省略 `--end-revision` 取到最新;版本差 ≤ 20) | `lark-sheets-changeset` | — |
|
||||
| 取当前文档 revision(版本号) | `+revision-get` | `lark-sheets-workbook` | — |
|
||||
| 导出 xlsx / 单表 csv | `+workbook-export` | `lark-sheets-workbook` | — |
|
||||
| 导入本地 xlsx/xls/csv 文件为飞书电子表格 | `+workbook-import --file ./x.xlsx`(本地表格文件 → 飞书电子表格的正解;仅要导成多维表格 bitable 时才用 `drive +import --type bitable`) | `lark-sheets-workbook` | `drive +import`(导电子表格时绕了 drive 通道、还要多给 `--type`,应直接用 `+workbook-import`)、把 .xlsx 在本地读成数据再 `+workbook-create` 重灌(多此一举,应直接 `+workbook-import`)、要把文件并入某个**已有在线工作簿**(给它加子表)却用它——import 只会新建独立表,加子表应走 `+sheet-copy` / `+sheet-create` |
|
||||
| 参考某个**已有在线表**、把多个本地文件 / 数据各作为一张子表**追加**进去(不另起独立表) | 先 `+workbook-info` 拿模板子表 `sheet_id` → `+sheet-copy` 逐张复制模板子表(公式 / 合并 / 分组底色 / 列宽 / 条件格式全继承)再用 `+cells-*` 只改数据;无模板可继承时 `+sheet-create` 建空子表 + `+table-put --sheets/--styles` 写入 | `lark-sheets-workbook` | 把文件 `+workbook-import` / `+workbook-create` 另起一张**独立新表**(目标是并入已有工作簿时就跑偏了;这两条只产新表、不接受已有表定位) |
|
||||
| 清除内容 / 格式 | `+cells-clear`(范围维度用 `--scope`,取值 content / formats / all) | `lark-sheets-range-operations` | `--type` |
|
||||
| 批量清除多区域 | `+cells-batch-clear`(`--scope`) | `lark-sheets-batch-update` | `--target` |
|
||||
| 调整列宽 / 行高 | `+cols-resize` / `+rows-resize`(行、列是两个独立命令) | `lark-sheets-range-operations` | `--dimension`(无此 flag) |
|
||||
| 分组汇总 / 透视 | `+pivot-create`(默认不传落点 flag → 自动新建子表,零覆盖) | `lark-sheets-pivot-table` | 用 SUMIF / 本地脚本拼一张假透视表 |
|
||||
| 画图表 / 可视化(柱 / 折线 / 饼 / 条 / 散点 / 组合…) | `+chart-create` | `lark-sheets-chart` | matplotlib / 本地画图再贴图(原生图表可交互、随数据更新) |
|
||||
| 条件高亮 / 数据条 / 色阶 / 重复值标记 | `+cond-format-create` | `lark-sheets-conditional-format` | `+highlight`、`+conditional-format`、逐格 `+cells-set-style` 硬凑 |
|
||||
| 筛选 / 只看符合条件的行 | `+filter-create` | `lark-sheets-filter` | pandas filter 后覆盖写回(会毁原数据;要保存多份筛选状态用 `+filter-view-create`) |
|
||||
|
||||
> ⚠️ **动手前的触发式必读(按动作判定,不看主场景)**:本次操作只要**涉及样式 / 美化**(底色 / 边框 / 字号 / 对齐 / 数字格式 / 汇总行 / 配色 / 列宽行高),动手前先读 `lark-sheets-visual-standards`;只要**要写飞书公式**,动手前先读 `lark-sheets-formula-translation`(飞书函数与 Excel 有差异,凭直觉迁移易错),**写完后再读 `lark-sheets-formula-verify` 并执行 `+formula-verify` 收尾**。哪怕主任务是"建表 / 展开数据 / 录入",只要动作里含美化或写公式就适用——别因"这不算专门的美化 / 公式任务"而跳过。
|
||||
> ⚠️ **两种图片别选错**:图若**绑定某条记录、要随行排序 / 筛选 / 增删**(凭证 / 证件照 / 每行配图,话里带「对应 / 每行 / 这列」等绑定词)→ 单元格图片 `+cells-set-image`;只是自由摆放的装饰(logo / 水印 / 封面)→ 浮动图片 `+float-image-create`。别因「浮动图更好控制 / 更熟」默认选浮动图。
|
||||
> ⚠️ **纯文本还是数值语义(看数据本质,不看当下用途)**:金额 / 百分比 / 比率 / 计数 / 日期等**本质是量值**的数据 → 一律数值写入,常规二维表用 `+table-put`(`dtypes` 声明类型 + `formats` 设展示格式),版式装不下(多级 / 合并表头的宽表 leaderboard 等)改用 `+cells-set` 传数字(百分比传小数 `0.4`)+ `number_format`,照样显示 `40%` 且数值无损。只有编号 / 身份证 / 单据号这类**本质是标识符**、要字面保真的才用 `+csv-put` 平铺。**几个常见借口都不成立**——"只是 leaderboard / 报表展示不用算""版式复杂""样式以后再刷、先铺文本"都不是把百分比写成 `"40%"` 字符串灌 `+csv-put` 的理由(展示不改变它是数值;类型不能后补,落成文本就回不来)。判据与操作展开见 `lark-sheets-write-cells`「数字还是文本」。
|
||||
> ⚠️ **要新建子表 / 整表美化 → 别默认「`+csv-put` 写值再事后刷样式」**:`+table-put` / `+workbook-create` 的 `--styles` 能在写数据的**同一步**带全套样式(区域底色 / 边框 / 列宽 / 行高 / 合并),且 `+table-put` 的 payload 里若 sheet 名不在工作簿中会自动新建子表——**纯文本表要新建子表 + 美化时同样走这里**(`--styles` 与列是否 typed 无关),比「`+csv-put` 写值 + 多次 `+cells-batch-set-style` / `+*-resize` 刷样式」少好几次调用(冻结行列等 sheet 级属性仍需 `+dim-freeze` 单独一步)。
|
||||
> ⚠️ **定位 flag**:`+cells-get` / `+cells-set` / `+csv-get` 用 `--range`;`+csv-put` 规范用 `--start-cell`(单个左上角锚点格),也接受 `--range` 别名(区间自动取左上角),二者择一即可。
|
||||
> ⚠️ **读取附加信息**一律走 `+cells-get --include …`,**没有** `--with-styles` 这类 flag;**看合并单元格**用 `+sheet-info` 的 `merged_cells`,不要在 `+cells-get` 里找 merge flag。
|
||||
|
||||
## 执行要点(读取 / 原生工具 / 陷阱)
|
||||
|
||||
准则的实操展开。端到端工作流:了解结构 → 读数据 → 理解语义 → 原生工具优先 → 写入 → 回读验证。
|
||||
|
||||
### 读取:按需求选路径(细则见 `lark-sheets-read-data`)
|
||||
|
||||
| 用户需求 | 读取路径 |
|
||||
|---|---|
|
||||
| "完善 / 补齐 / 填空 / 修正所有 XX"、分析 / 清洗 / 大数据 | 原生优先(公式 / `+pivot` / `+filter`);表达不了再分批 `+csv-get` 导出 + 脚本处理 + 分批回写(默认覆盖所有对应数据行,不以用户选区为准) |
|
||||
| "查一下 / 看看 / 统计 / 汇总"等只读 | `+csv-get` 读到上下文 |
|
||||
| 需要公式 / 样式 / 批注 | `+cells-get` |
|
||||
| 续写 / 扩展已有内容 | `+csv-get` 看结构 + `+cells-get` 读源区样式 + `+sheet-info --include row_heights,merges`(见准则 5) |
|
||||
|
||||
> "补齐 / 填空"类用只读路径探 10 行就写会漏写表尾——写入前先按 `lark-sheets-read-data` 确认真实数据末行(准则 3)。
|
||||
|
||||
### 计算:原生工具优先,代码兜底(强化准则 7)
|
||||
|
||||
| 用户需求 | 用原生 | 禁止的替代 |
|
||||
|---|---|---|
|
||||
| 按 X 统计 Y、分组汇总 | `+pivot-{create\|update\|delete}` | pandas groupby → 写值 |
|
||||
| 求和 / 计数 / 平均 / 占比 | 公式 | Python 算 → 写静态值 |
|
||||
| 图表 / 可视化 | `+chart-*` | matplotlib |
|
||||
| 条件高亮 / 色阶 | `+cond-format-*` | 逐格设样式 |
|
||||
| 筛选 | `+filter-*` | pandas filter → 覆盖写入 |
|
||||
| 文本提取 / 转换 / 查找 | 公式(REGEXEXTRACT / TEXT / VLOOKUP 等) | Python → 写静态值 |
|
||||
|
||||
只有多步清洗、统计建模、公式试错 3 次仍失败时才用代码。
|
||||
|
||||
### 用脚本配合 CLI 时
|
||||
|
||||
- **只读 stdout**:CLI 数据走 stdout、诊断走 stderr;解析 JSON 别 `2>&1`(警告混入会解析失败),用管道或单独重定向 stdout。
|
||||
- **喂 CLI 的 CSV / JSON 用 UTF-8 无 BOM**;临时文件放系统临时目录、勿落项目目录。
|
||||
- **命令失败先读 stderr 再调整**,别原样重发。
|
||||
- **回写纯单元格值**:剥离 `值(V-Align: bottom)` 这类"值(样式)"串与残留引号再写;排序优先 `+range-sort` 原生工具,别"读出本地排完再整列写回"。
|
||||
|
||||
### 易漏陷阱
|
||||
|
||||
- **`+dim-insert` 不继承行高**:只继承值 / 公式 / 边框,新行回落默认高度截断长文本;插行填长文本前读相邻行 `row_height`,用 `+batch-update` 合 `+rows-resize` 补齐。
|
||||
- **公式容错**:日期 / 查找 / 数值转换公式用 `IFERROR` 包裹;写完读结果列首末各 5 行查 `#VALUE!` / `#REF!` / `#DIV/0!`,然后继续跑 `+formula-verify` 直到 `status='success'`;同一方案试错上限 3 次。
|
||||
- **循环引用**:聚合公式引用范围不能含目标 cell 自身或其传递依赖。
|
||||
- **隐藏行列**:`+csv-get` 默认含隐藏行列;设 `--skip-hidden=true` 只看可见,但返回行序号与实际行号不再对应。
|
||||
- **跨 sheet 对象**:图表 / 条件格式 / 透视表 / 浮动图片可能分布在多个子表,操作前先 `+workbook-info` 掌握全局。
|
||||
- **NLP 任务分批**:语义理解 / 翻译 / 改写 / 分类等用 NLP 处理(代码只做分批 / 行号映射 / 写回);数据量大必须分批(通常 30 行 / 批),每批处理完即时写回,单批生成通常 ≤ 300 行,多批用 `+batch-update`。
|
||||
|
||||
## References
|
||||
|
||||
本 skill 的 reference 分两组:先读**通用方法与规范**(横切所有任务的样式、公式规则,不含具体 shortcut),它们规定了"怎么做对";再按操作对象进入**工具参考**查具体 shortcut 与调用细节。编辑类任务务必先过一遍通用方法与规范,连同上方「飞书表格编辑准则」对所有工具参考一律生效。
|
||||
|
||||
### 通用方法与规范(先读,横切所有任务,不含具体 shortcut)
|
||||
|
||||
| Reference | 描述 |
|
||||
| --- | --- |
|
||||
| [飞书表格样式与配色规范](references/lark-sheets-visual-standards.md) | 飞书表格样式与配色规范:表头/数据区/汇总行的颜色、字号、对齐、边框、数字格式等取值标准,以及从零新建表格的版式美化、新增汇总行、追加行列继承原表风格、已有区域美化等典型场景的决策流程与样式要点。工具调用参数细节请参考对应的 lark-sheets-write-cells / lark-sheets-range-operations / lark-sheets-batch-update。条件格式(高亮、标红、数据条、色阶)请使用 lark-sheets-conditional-format。 |
|
||||
| [飞书表格公式生成规则](references/lark-sheets-formula-translation.md) | Excel 公式到飞书表格公式的迁移与生成规则。核心目标不是保留 Excel 原语法,而是按飞书表格可执行规则重写公式,并在结果上尽量对齐 Excel。当用户要求把 Excel 公式改写成飞书表格公式,或需要生成飞书公式(尤其涉及 ARRAYFORMULA、原生数组函数、INDEX/OFFSET、MAP/LAMBDA、日期差、多层范围结果与二次展开)时使用。本文只负责把公式写对,落表后的强制收尾请接 `lark-sheets-formula-verify`。 |
|
||||
|
||||
### 按对象的工具参考(含 shortcut)
|
||||
|
||||
| Reference | 描述 |
|
||||
| --- | --- |
|
||||
| [Lark Sheet Formula Verify](references/lark-sheets-formula-verify.md) | 公式写入 / 批量填充 / `--copy-to-range` 扩展 / 导入含公式工作簿后的强制自检入口。对指定子表(或整本工作簿)扫描公式与单元格值,聚合所有 Excel 错误(#REF! / #DIV/0! / #VALUE! / #NAME? / #NULL! / #NUM! / #N/A),同时合并最近一次写入留下的编译失败(formula_errors),输出统一 JSON 让 AI 一次拿到完整健康度报告。只要任务涉及写公式,落表后就应调用 +formula-verify 收敛到 zero-error;`status='errors_found'` 或 `status='partial'` 时禁止把链路标为完成。 |
|
||||
| [Lark Sheet Workbook](references/lark-sheets-workbook.md) | 管理飞书表格的工作簿结构(子表列表及元数据)。当用户提到"看看这个表格有什么"、"表格结构"、"有哪些 sheet"、"新建一个 sheet"、"删除这个工作表"、"重命名"、"复制一份"、"移动到前面"时使用。 |
|
||||
| [Lark Sheet Sheet Structure](references/lark-sheets-sheet-structure.md) | 管理飞书表格的子表结构与布局。适用场景:查看行高、列宽、隐藏行列、合并单元格等布局信息,以及"插入一行"、"删除这列"、"隐藏行"、"冻结表头"、行列分组(大纲折叠/展开)等操作。行列大纲仅在用户明确提到"行分组"、"列分组"、"大纲"、"outline"时才触发,"按XXX分组"等数据分组场景请使用 lark-sheets-pivot-table。如需在表尾追加数据,应先通过此 skill 插入行,再通过 lark-sheets-write-cells 写入。 |
|
||||
| [Lark Sheet Read Data](references/lark-sheets-read-data.md) | 读取飞书表格中的单元格数据。当用户需要"看看数据"、"分析数据"、"统计/汇总"时使用;也适用于需要查看公式、样式、批注等详细信息的场景。 |
|
||||
| [Lark Sheet Search & Replace](references/lark-sheets-search-replace.md) | 在飞书表格中搜索和替换文本,支持限定范围、大小写匹配、精确匹配、正则表达式。当用户需要"查找"、"搜索"、"定位"某个值,或"替换"、"批量修改文本"、"把 A 改成 B"时使用。不要用于理解表格结构(应读取数据)、不要用于数据分析(应读取数据后计算)、不要把用户操作动作中的关键词(如"汇总金额""统计数量")当作搜索词。 |
|
||||
| [Lark Sheet Write Cells](references/lark-sheets-write-cells.md) | 向飞书表格的指定区域批量写入值、公式、样式、批注或单元格图片。适用场景:填写数据、设置公式、修改格式、添加批注、嵌入单元格图片(如需操作浮动图片,请使用 lark-sheets-float-image);若只需把一块 CSV 批量铺到表格上(值或公式,不带样式/批注),直接使用 `+csv-put` 更短更快。追加数据需先通过 lark-sheets-sheet-structure 插入行列。只要这次写入真实落了公式,收尾默认继续执行 `lark-sheets-formula-verify`。 |
|
||||
| [Lark Sheet Range Operations](references/lark-sheets-range-operations.md) | 对飞书表格中指定区域执行结构性操作(不涉及写入单元格数据值)。适用场景:清除内容或格式("清空"、"删除内容"、"去掉格式")、合并/取消合并单元格、调整行高列宽("加宽列"、"自适应列宽")、移动/复制/填充/排序数据("移动数据"、"复制到"、"自动填充"、"按某列排序")。写入单元格数据请使用 lark-sheets-write-cells。 |
|
||||
| [Lark Sheet Batch Update](references/lark-sheets-batch-update.md) | 将多个飞书表格写入操作合并为一次批量执行,按顺序依次完成。适合需要连续执行多个写入操作的场景(如先修改结构再写入数据)。 |
|
||||
| [Lark Sheet Chart](references/lark-sheets-chart.md) | 管理飞书表格中的图表(柱形图、折线图、饼图、条形图、面积图、散点图、组合图、雷达图等)。当用户需要创建图表、修改图表样式或数据源、查看已有图表配置、删除图表时使用。也适用于用户提到"数据可视化"、"画个图"、"趋势分析"、"对比图"、"占比分析"、"做个图表"等数据可视化相关场景。 |
|
||||
| [Lark Sheet Pivot Table](references/lark-sheets-pivot-table.md) | 管理飞书表格中的数据透视表。当用户需要创建透视表、修改透视表的行列字段/聚合方式/筛选条件、查看已有透视表配置、删除透视表时使用。也适用于用户提到"分组汇总"、"交叉分析"、"按XXX统计"、"按字段分组"、"再分下组"、"多维分析"、"数据透视"等场景。 |
|
||||
| [Lark Sheet Conditional Format](references/lark-sheets-conditional-format.md) | 管理飞书表格中的条件格式规则(重复值高亮、单元格值比较、数据条、色阶、排名、自定义公式等)。当用户需要创建条件格式、修改已有规则的范围或样式、查看当前条件格式配置、删除规则时使用。也适用于用户提到"高亮"、"标红"、"颜色标记"、"数据条"、"色阶"、"条件样式"等场景。 |
|
||||
| [Lark Sheet Filter](references/lark-sheets-filter.md) | 管理飞书表格中的筛选器(filter)。当用户需要筛选数据(按文本/数值/颜色/日期条件过滤行)、查看已有筛选配置、修改或删除筛选器时使用。也适用于"只看"、"筛选出"、"仅保留符合条件的"等场景。 |
|
||||
| [Lark Sheet Filter View](references/lark-sheets-filter-view.md) | 管理飞书表格中的筛选视图(filter view)。当用户需要"建一个 XX 视图"、"保存这个筛选状态"、"切换不同筛选"、维护一个 sheet 上多份独立筛选配置时使用。视图与筛选器(filter)相互独立,可在同一 sheet 共存;视图的隐藏行仅在用户进入该视图时本地生效,不影响其他协作者。 |
|
||||
| [Lark Sheet Sparkline](references/lark-sheets-sparkline.md) | 管理飞书表格中的迷你图(折线迷你图、柱形迷你图、胜负迷你图)。当用户需要在单元格内嵌入小型图表来展示数据趋势时使用。也适用于"趋势线"、"单元格内图表"、"迷你图"等场景。注意:不等同于被禁用的 SPARKLINE() 公式函数。 |
|
||||
| [Lark Sheet Float Image](references/lark-sheets-float-image.md) | 管理飞书表格中的浮动图片。当用户需要在表格中插入浮动图片、调整图片位置和大小、查看已有浮动图片、删除图片时使用。也适用于"插入图片"、"添加 logo"、"放一张图"等场景。注意:如果用户需要将图片嵌入到某个单元格内部(单元格图片),请阅读 lark-sheets-write-cells。 |
|
||||
| [Lark Sheet History](references/lark-sheets-history.md) | 查询飞书表格的历史版本并回滚到指定版本。当用户需要查看一张表的编辑历史版本列表、回滚到某个历史版本、或查询回滚的异步状态(进行中/成功/失败)时使用。回滚为异步操作,发起后通过状态查询轮询结果。仅针对飞书表格。 |
|
||||
| [Lark Sheet Changeset](references/lark-sheets-changeset.md) | 读取两个版本(CS revision)之间的 changeset(原始变更操作清单),用于复核某次编辑——尤其是 AI 编辑——是否真实满足用户诉求。传入起始版本(编辑前基线),可选结束版本(省略取最新),版本差上限 20;返回里最外层带当前表格最新版本号。当用户需要"看看这次改了什么"、"核对 AI 改动"、"对比两个版本的变更"时使用。 |
|
||||
|
||||
## 公共 flag 速查
|
||||
|
||||
各 reference 的每个 shortcut 标题下用一行徽章标注该 shortcut 支持的公共 / 系统 flag,例如:
|
||||
|
||||
- `_公共四件套 · 系统:--dry-run_` — URL/token + sheet 定位(两组各**必给一个**,详见下方「公共 flag」),加 `--dry-run`
|
||||
- `_公共:URL/token(无 sheet 定位) · 系统:--yes、--dry-run_` — 只接 URL/token,常见于 `+batch-update` 等不强制 sheet 定位的 shortcut
|
||||
|
||||
徽章里只列名字。type / 必填 / 描述都在本段统一声明:
|
||||
|
||||
### 公共 flag(定位资源)
|
||||
|
||||
**公共四件套** = `--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`,分成两组 XOR,**每组都必须给且只能给一个**(XOR = 二选一必填,不是"可选"):
|
||||
|
||||
1. **spreadsheet 定位(必填)**:`--url` 与 `--spreadsheet-token` 二选一,**必须给其中之一**。两个都不给 → 校验报错 `specify at least one of --url or --spreadsheet-token`;两个都给 → 互斥冲突。
|
||||
- **`--url` 解析 `/sheets/`、`/spreadsheets/` 与 `/wiki/` 三种链接**(从路径里抽出 token;也可以直接把裸 token 传给 `--spreadsheet-token`)。其它形态的链接不会被解析成表格 token。
|
||||
- **`/wiki/` 知识库链接可直接传 `--url`**:会自动定位到链接背后的电子表格;若该链接背后不是电子表格(而是文档 / 多维表格等),则报错。
|
||||
- **例外**:`+workbook-create`(新建表 + 可选写入数据)与 `+workbook-import`(把本地文件导入为新表)都产出一张**还不存在**的表格,**不接受任何 spreadsheet / sheet 定位 flag**——`+workbook-create` 只有 `--title` / `--folder-token` / `--values` / `--styles` / `--sheets`,`+workbook-import` 只有 `--file`(必填)/ `--folder-token` / `--name`。
|
||||
2. **sheet 定位(公共四件套 shortcut 必填)**:`--sheet-id` 与 `--sheet-name` 二选一,**必须给其中之一**。两个都不给 → 校验报错 `specify at least one of --sheet-id or --sheet-name`。
|
||||
- ⚠️ **不确定 sheet 名时禁止直接猜 `Sheet1`**:除非用户对话明确说出 sheet 名 / id,或上下文(之前的工具调用 / URL 锚点 `?sheet=xxx`)已经出现过具体值,否则**第一步先调 `+workbook-info --url "..."`**(或 `--spreadsheet-token`)拿 `sheets[].sheet_id` / `sheets[].title` 列表再选。中文环境下子表常叫"数据" / "Sheet"(无数字)/ "工作表 1" / 业务名,猜 `Sheet1` 大概率撞 `sheet not found`,比先查多耗一次失败调用 + 重试。
|
||||
- ⚠️ **`--range` 里的 `Sheet1!` 前缀不能替代 sheet 定位**:即使写了 `--range 'Sheet1!A1:B2'`,仍**必须**额外传 `--sheet-id` 或 `--sheet-name`,否则照样报上面的错。
|
||||
- ⚠️ **A1 reference 含 `!`**(`--source` / `--range` / `--ranges`)**:整段用单引号包裹**,如 `--range 'Sheet1!A1:B2'`——单引号能挡住 bash 的 history expansion(`!` 被拦成 `event not found`;双引号挡不住;别改用 `set +H`,原因见下方「复合 JSON / 大入参」)。sheet 名含特殊字符(`-` / 空格 / 非 ASCII)需在内部按 A1 标准再包一层单引号时,用 `'\''` 转义保持外层单引号,如 `--source ''\''Sales-2025'\''!A1:D100'`。
|
||||
- **例外**:徽章标为 `_公共:URL/token(无 sheet 定位)…_` 的 shortcut(如 `+workbook-info` / `+workbook-export` / `+batch-update` / `+dropdown-update|delete` / `+cells-batch-set-style` / `+cells-batch-clear` / `+sheet-create`)**不接受也不需要** sheet 定位,只给一组 spreadsheet 定位即可。`+pivot-create` 用 `--target-sheet-id` / `--target-sheet-name`(XOR,可都不传,落点细节见 `lark-sheets-pivot-table`)。
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--url` | string | 二选一必填(与 `--spreadsheet-token`) | spreadsheet 或 wiki URL |
|
||||
| `--spreadsheet-token` | string | 二选一必填(与 `--url`) | spreadsheet token |
|
||||
| `--sheet-id` | string | 二选一必填(与 `--sheet-name`;仅公共四件套 shortcut) | 工作表 reference_id |
|
||||
| `--sheet-name` | string | 二选一必填(与 `--sheet-id`;仅公共四件套 shortcut) | 工作表名称 |
|
||||
|
||||
**统一调用范式**(公共四件套 shortcut 的所有示例都遵循此形状,两组定位缺一不可):
|
||||
|
||||
```bash
|
||||
lark-cli sheets <shortcut> <workbook 定位> <sheet 定位> <其它 flag>
|
||||
# workbook 定位:--url "..." 或 --spreadsheet-token "..." (二选一,必给)
|
||||
# sheet 定位: --sheet-id "$SID" 或 --sheet-name "<真实表名>" (二选一,必给;占位符不要原样填)
|
||||
# 例:lark-cli sheets +csv-get --url "https://.../sheets/shtXXX" --sheet-name "<真实表名>" --range "A1:F30"
|
||||
# 注意:真实表名不要直接填 "Sheet1"——大多数表的子表不叫这个;先 +workbook-info 拿 sheets[].title 再代入。
|
||||
```
|
||||
|
||||
### 系统 flag
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--dry-run` | bool | 否 | 零副作用:仅打印请求路径与参数模板,不发起调用;多步操作会输出每个子操作的请求模板 |
|
||||
| `--yes` | bool | 是(仅 `high-risk-write`) | 二次确认;不带时退出码 10。详见 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md) 高风险审批协议 |
|
||||
| `--print-schema` | bool | 否 | 本地打印复合 JSON flag 的 JSON Schema 并退出,不发起任何调用、不需要其它 required flag。与 `--flag-name <name>` 搭配指定要查哪个 flag;省略 `--flag-name` 时列出该 shortcut 所有可查询的 flag。**仅在 shortcut 含复合 JSON flag 时有效**——判断方法:该 shortcut 的 Flags 表里出现类型标注为「复合 JSON」的 flag(如 `--cells` / `--properties` / `--operations` / `--border-styles` / `--sort-keys` / `--options`)即支持;纯标量 flag 的 shortcut 不支持。 |
|
||||
| `--flag-name` | string | 否 | 配合 `--print-schema` 使用,指定要打印 JSON Schema 的 flag 名(不带 `--` 前缀,如 `cells` / `properties` / `operations`)。 |
|
||||
|
||||
**Agent 使用提示**:写复合 JSON flag(`--cells` / `--properties` / `--operations` / `--border-styles` / `--sort-keys` / `--options` 等)时,如果对结构不确定,先跑 `lark-cli sheets <shortcut> --print-schema --flag-name <name>` 把完整 JSON Schema 读出来再构造 payload,比靠 reference 的速查表更精确,也避免因为字段拼写或缺失被服务端拒绝。reference 的 `## Schemas` 段只给一层结构,深层只能靠 `--print-schema` 或 `## Examples` 的真实示例。
|
||||
|
||||
### flag 内容类型与输出约定(术语速记)
|
||||
|
||||
- flag 表里 JSON 类入参标三类:**复合 JSON** = 深层嵌套对象(用 `--print-schema` 取完整结构);**简单 JSON** = 一维 / 二维标量数组(如 `["sheet1!A1:B2",...]` / `[["alice",95]]`,结构简单无需 print-schema);**非 JSON 文本** = 原样文本(如 CSV)。`--print-schema` 只对**复合 JSON** flag 有效(同一 shortcut 的简单 JSON flag 如 `--colors` 不在此列)。
|
||||
- **envelope**:所有 shortcut 返回统一外层结构 `{ok, identity, data, ...}`。正文里 `envelope.data` 指业务数据层(如 `+csv-get` 的 `annotated_csv`);写操作不会自动回读,如需校验请自行调用对应的 `+*-list` / `+*-get` / `+cells-get`。
|
||||
|
||||
## 复合 JSON / 大入参:优先 stdin
|
||||
|
||||
flag 帮助里标注支持 **Stdin** 的入参,当 payload 较大、含换行 / 引号等特殊字符,或已经落在某个文件里时,优先用 stdin(`-`)传入,避免命令行超长与 shell 转义问题。
|
||||
|
||||
推荐写法:payload 写到用户项目目录之外的临时文件(放系统临时目录,避免污染项目),再用 stdin 喂进去:
|
||||
|
||||
```bash
|
||||
# TMPFILE 指向系统临时目录下的 payload 文件(脚本里用 tempfile.gettempdir() / os.tmpdir() 等取临时目录)
|
||||
lark-cli sheets +cells-set --url "..." --sheet-name "Sheet1" --range "A1:B2" --cells - < "$TMPFILE"
|
||||
```
|
||||
|
||||
**参数含特殊字符(`!` / 引号 / 空格 / 非 ASCII)时,用单引号包裹该参数即可,不要起手 `set +H` 之类的 shell 开关来防转义。** `set +H`(关 bash history expansion)在 `sh` / `dash` 下是非法选项(`set: Illegal option -H`)、会让整条命令直接失败;而单引号挡得住 `!` 的 history expansion(否则报 `event not found`),对 bash 与 `sh` / `dash` 一致安全。参数本身含单引号、或 payload 较大时,按上文走 stdin。
|
||||
|
||||
**`@file` 接绝对路径会被拒,且被拒后不要照报错提示做。** `@file` 出于安全只接受 cwd 下的相对路径,传 cwd 之外的绝对路径会被拒。此时报错会建议"先 cd 到目标目录,或改用相对路径"——**两条都不要照做**:cd 过去、或把临时文件写进用户项目目录,都会污染工作目录。正解是改用 stdin(`--<flag> - < 文件`)。
|
||||
@ -0,0 +1,200 @@
|
||||
# Lark Sheet Batch Update
|
||||
|
||||
## 写入边界 + 回读校验
|
||||
|
||||
`+batch-update` 把多次写入打包成单次请求,但每个子操作仍受编辑类任务硬性默认规则约束:
|
||||
|
||||
1. **目标 range 必须落在用户授权范围内**:除用户明示要修改的区域外,子操作禁止扩张到无关单元格 / 列 / Sheet。规划 range 时先确认每个子操作的边界。
|
||||
2. **批次完成后必须回读校验**:整个 `+batch-update` 执行成功后,用 `+csv-get` 或 `+cells-get` 抽样回读受影响区域,至少校验 3-5 个代表性单元格(首 / 中 / 末),与本地脚本预先计算的预期值对照。
|
||||
3. **预期条数前置断言**:涉及"批量填充 N 行"或"对 M 个区域分别写入"时,先把 N、M 硬编码进代码,回读后断言实际等于预期;不一致就再发一轮 `+batch-update` 补齐,禁止交付半成品。
|
||||
|
||||
若本次 `+batch-update` 的任一子操作写入了公式、复制了公式模板、或导入了含公式的数据块,**回读校验之后还必须继续执行 `+formula-verify`**。`+batch-update` 的原子提交只保证“写入动作都执行了”,不保证整批公式运行结果 zero-error。
|
||||
|
||||
## 使用场景
|
||||
|
||||
写入。批量执行多个写入工具操作。将多个工具调用合并为一次请求,按顺序依次执行。适合需要连续执行多个写入操作的场景(如先修改结构再写入数据)。注意:不支持嵌套 `+batch-update`。
|
||||
|
||||
**不可放进 `--operations` 的写 shortcut**(`shortcut` 枚举不含它们,强行写入会被校验拒):`+cells-set-image`(需本地上传图片)、`+dropdown-update` / `+dropdown-delete` / `+cells-batch-set-style` / `+cells-batch-clear`(自身已是批量入口,不可再嵌套)、`+dim-move`。这些操作需在 `+batch-update` 之外单独调用。
|
||||
|
||||
**⚠️ 何时必须使用 `+batch-update`(硬性要求)**:
|
||||
- 需要对**多个**不同区域执行 `+cells-{merge|unmerge}` 时(如按分组合并多列相同内容)
|
||||
- 需要先插入行列再写入数据时(`+dim-{insert|delete|hide|unhide|freeze|group|ungroup}` + `+cells-set`)
|
||||
- 需要对多个区域执行不同写入操作时(多次 `+cells-set` + `+cells-clear` 等组合)
|
||||
|
||||
**行高列宽批量不走这里**:多行 / 多列不同尺寸直接用 `+rows-resize --heights` / `+cols-resize --widths` 的 map 形态(如 `--widths '{"A":100,"C:E":120}'`,见 `lark-sheets-range-operations`),一次调用原子完成;map 形态不可作为 `--operations` 子操作嵌入(子操作里仍可用单区间形态 `range` + `height`/`width`)。
|
||||
|
||||
当同一工具需要对多个区域重复调用时,**必须**改用 `+batch-update` 合并为单次请求——`+batch-update` 是原子提交(要么全成功要么整批回滚);逐个调用非原子,中途失败会留下半成品。
|
||||
|
||||
**公式相关批处理的默认闭环**:
|
||||
- 写前:先读 `lark-sheets-formula-translation`,把公式改写成飞书可执行语义。
|
||||
- 写时:用 `+batch-update` 一次性完成插行/写公式/复制模板等原子动作。
|
||||
- 写后:抽样回读之外,继续跑 `lark-sheets-formula-verify`,直到 `+formula-verify` 返回 `status='success'`。
|
||||
|
||||
**`+dropdown-update` 的选项模式(`--options` / `--source-range` 二选一)+ 配色规则**(`--colors` 长度可短不能长、必须配 `--highlight=true` 才生效、不传按内置 10 色色板循环补色)见 [`lark-sheets-write-cells`](./lark-sheets-write-cells.md) 的「Dropdown 选项 + 配色」节,本文不重复。`+dropdown-delete` 不涉及这些 flag。
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+batch-update` | high-risk-write | 批量 |
|
||||
| `+cells-batch-set-style` | write | 批量 |
|
||||
| `+dropdown-update` | write | 对象 |
|
||||
| `+dropdown-delete` | high-risk-write | 对象 |
|
||||
| `+cells-batch-clear` | high-risk-write | 批量 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+batch-update`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--yes`、`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--operations` | string + File + Stdin(复合 JSON) | required | JSON 数组:[{"shortcut":"+xxx-yyy","input":{...}}, ...]。shortcut 用 CLI 名;input 是该 shortcut 的入参集——含子表定位 sheet_id(或 sheet_name),但不含 spreadsheet token/url(后者只在顶层 --url/--spreadsheet-token 给一次;+batch-update 顶层没有 --sheet-id);input 的键是该 shortcut 的 flag 展平成 JSON(如 "range":"A11:B12"),不是再套一层嵌套。基础 flag 查 --help,复合 JSON flag 查 --print-schema --flag-name <flag>;不要手填 operation 字段(由 CLI 按 shortcut 自动注入)。默认严格事务(首个失败即整批中断),传 --continue-on-error 切换为软批量(遇失败仍继续);不支持嵌套;按数组顺序串行执行 |
|
||||
| `--continue-on-error` | bool | optional | 遇子操作失败时继续执行剩余操作;默认 false(首个失败即整批中断) |
|
||||
|
||||
### `+cells-batch-set-style`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--ranges` | string + File + Stdin(简单 JSON) | required | 目标范围 JSON 数组(最多 100 个),每项必须带 sheet 前缀(如 `["Sheet1!A1:B2","Sheet2!D1:D10"]`,前缀裸写不加引号);前缀必须与 sheet 真实显示名完全一致(含大小写),不接受 sheet reference_id;支持跨 sheet;所有 range 应用同一组 style |
|
||||
| `--background-color` | string | optional | 背景颜色(十六进制,如 `#ffffff`) |
|
||||
| `--font-color` | string | optional | 字体颜色(十六进制,如 `#000000`) |
|
||||
| `--font-family` | string | optional | 字体名称(如 `Arial`、`微软雅黑`) |
|
||||
| `--font-size` | float64 | optional | 字体大小(px,例:10、12、14) |
|
||||
| `--font-style` | string | optional | 字体样式(可选值:`normal` / `italic`) |
|
||||
| `--font-weight` | string | optional | 字重(可选值:`normal` / `bold`) |
|
||||
| `--font-line` | string | optional | 字体线条样式(可选值:`none` / `underline` / `line-through`) |
|
||||
| `--horizontal-alignment` | string | optional | 水平对齐(可选值:`left` / `center` / `right`) |
|
||||
| `--vertical-alignment` | string | optional | 垂直对齐(可选值:`top` / `middle` / `bottom`) |
|
||||
| `--word-wrap` | string | optional | 换行策略(可选值:`overflow` / `auto-wrap` / `word-clip`) |
|
||||
| `--number-format` | string | optional | 数字格式(例:文本 `@`、数字 `0.00`、货币 `$#,##0.00`、日期 `mm/dd/yyyy`) |
|
||||
| `--border-styles` | string + File + Stdin(复合 JSON) | optional | 边框配置 JSON(结构同 +cells-set-style) |
|
||||
|
||||
### `+dropdown-update`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--ranges` | string + File + Stdin(简单 JSON) | required | 目标范围 JSON 数组(最多 100 个,如 `["Sheet1!A2:A100","Sheet1!C2:C100"]`,前缀裸写不加引号),每项必须带 sheet 前缀;前缀必须与 sheet 真实显示名完全一致(含大小写),不接受 sheet reference_id |
|
||||
| `--options` | string + File + Stdin(复合 JSON) | xor | 下拉选项 JSON 数组,例如 `["opt1","opt2"]`。服务端不限制选项数量,也不限制单个选项长度;含逗号的选项可以接受(写入时会自动转义)。大量选项建议改用 `--source-range`。 |
|
||||
| `--colors` | string + File + Stdin(简单 JSON) | optional | 下拉胶囊背景色,RGB hex 数组(如 `["#1FB6C1","#F006C2"]`)。长度可短不可长——超长 Validate 拦截(`--colors length (N) must not exceed dropdown source size (M)`),未指定项按内置 10 色色板循环补色。**单独传即生效**;`--highlight=false` 时被忽略。 |
|
||||
| `--multiple` | bool | optional | 启用多选 |
|
||||
| `--highlight` | bool | optional | 下拉胶囊背景色高亮开关。**不传 = 开**(按内置 10 色色板循环上色);`--highlight=false` 关闭得到纯白下拉。配色用 `--colors` 覆盖。 |
|
||||
| `--source-range` | string | xor | listFromRange 模式的下拉源 range,A1 表示法 + sheet 前缀(如 `'Sheet1'!T1:T3`)。映射到 server `data_validation.range`,搭配 server `data_validation.type='listFromRange'` 自动生效。跟 `--options` 二选一:传 `--options` 走 inline 列表(type=list),传本 flag 走 range 引用(type=listFromRange)。`--colors` 长度规则不变(≤ 源 range 单元格数),`--highlight` / `--multiple` 行为相同。当 `--highlight` 开启且 source 覆盖单元格数超过 2000 时,服务端会将该下拉判为 option-error(这是不支持的组合);CLI 会向 stderr 输出 warning。如需取消,传 `--highlight=false`。 |
|
||||
|
||||
### `+dropdown-delete`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--yes`、`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--ranges` | string + File + Stdin(简单 JSON) | required | 目标范围 JSON 数组(最多 100 个,如 `["Sheet1!E2:E6"]`,前缀裸写不加引号),每项必须带 sheet 前缀;前缀必须与 sheet 真实显示名完全一致(含大小写),不接受 sheet reference_id |
|
||||
|
||||
### `+cells-batch-clear`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--yes`、`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--ranges` | string + File + Stdin(简单 JSON) | required | 目标范围 JSON 数组(最多 100 个),每项必须带 sheet 前缀(如 `["Sheet1!A2:Z1000","Sheet2!A2:Z1000"]`,前缀裸写不加引号);前缀必须与 sheet 真实显示名完全一致(含大小写),不接受 sheet reference_id;支持跨 sheet;对所有 range 执行同一 scope 的清除 |
|
||||
| `--scope` | string | optional | 清除范围 enum:`content`(默认,仅清内容)/ `formats`(仅清格式)/ `all`(清内容 + 格式)(可选值:`content` / `formats` / `all`) |
|
||||
|
||||
## Schemas
|
||||
|
||||
> 复合 JSON flag 字段速查(只列顶层 + 一层嵌套)。深层结构看下方 `## Examples`,或用 `--print-schema` 读完整 JSON Schema(用法见 SKILL.md「公共 flag 速查」与「Agent 使用提示」)。
|
||||
|
||||
### `+batch-update` `--operations`
|
||||
|
||||
_要批量执行的 CLI shortcut 操作列表,按声明顺序串行执行;任一失败立即中断_
|
||||
|
||||
**数组项**(类型 object):
|
||||
- `shortcut` (enum) — CLI shortcut 名(不是底层 MCP tool 名) [+cells-set / +cells-set-style / +cells-clear / +cells-merge / +cells-unmerge / +cells-replace / +csv-put / +dropdown-set / +dim-insert / +dim-delete / +dim-hide / +dim-unhide / +dim-freeze / +dim-group / +dim-ungroup / +rows-resize / +cols-resize / +range-move / +range-copy / +range-fill / +range-sort / +sheet-create / +sheet-delete / +sheet-rename / +sheet-move / +sheet-copy / +sheet-hide / +sheet-unhide / +sheet-set-tab-color / +sheet-show-gridline / +sheet-hide-gridline / +chart-create / +chart-update / +chart-delete / +pivot-create / +pivot-update / +pivot-delete / +cond-format-create / +cond-format-update / +cond-format-delete / +filter-create / +filter-update / +filter-delete / +filter-view-create / +filter-view-update / +filter-view-delete / +sparkline-create / +sparkline-update / +sparkline-delete / +float-image-create / +float-image-update / +float-image-delete]
|
||||
- `input` (object) — 该 shortcut 的入参集——含子表定位 sheet_id(或 sheet_name),但不含 spreadsheet token/url(后者只在顶层 …
|
||||
|
||||
### `+cells-batch-set-style` `--border-styles`
|
||||
|
||||
_单元格边框配置,含 top/bottom/left/right 四个方向,每个方向的结构相同(见 top)_
|
||||
|
||||
**顶层字段**:
|
||||
- `top` (object?) { style?: enum, weight?: enum, color?: string }
|
||||
- `bottom` (object?) { style?: enum, weight?: enum, color?: string }
|
||||
- `left` (object?) { style?: enum, weight?: enum, color?: string }
|
||||
- `right` (object?) { style?: enum, weight?: enum, color?: string }
|
||||
|
||||
### `+dropdown-update` `--options`
|
||||
|
||||
_列表选项_
|
||||
|
||||
**数组项**(类型 string):
|
||||
- 标量:string
|
||||
|
||||
## Examples
|
||||
|
||||
公共四件套:`--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`(前两者 XOR;`+batch-update` 本身不强制 sheet-id,子操作各自携带)。
|
||||
|
||||
### `+batch-update`
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
lark-cli sheets +batch-update --url "https://example.feishu.cn/sheets/shtXXX" --yes \
|
||||
--operations @ops.json
|
||||
|
||||
# ops.json (array<{shortcut, input}>,shortcut 用 CLI 名):
|
||||
# [
|
||||
# {"shortcut": "+dim-insert", "input": {"sheet_id":"...","position":10,"count":3}},
|
||||
# {"shortcut": "+cells-set", "input": {"sheet_id":"...","range":"A11:B12","cells":[[{"value":"a"},{"value":"b"}],[{"value":"c"},{"value":"d"}]]}}
|
||||
# ]
|
||||
```
|
||||
|
||||
> ⚠️ **子操作定位规则**:
|
||||
> - spreadsheet 定位(`--url` / `--spreadsheet-token`)**只在顶层给一次**;`+batch-update` 顶层**没有** `--sheet-id` / `--sheet-name`,在顶层传不生效。
|
||||
> - **每个子操作的子表定位 `sheet_id`(或 `sheet_name`)写进它自己的 `input`**(见上方 ops.json 每个 item)。
|
||||
> - `input` 的键是该 shortcut 的 flag **展平**成 JSON(`"range":"A11:B12"`、`"position":11`),不要把整组 `--operations` 再套一层嵌套 JSON。
|
||||
|
||||
> **常见组合:插列 + 写表头 + 整列回填**——一次原子提交,不要拆成 N 次独立调用。批量回填同一列 **只需一次** `+cells-set`(range 写整列范围、cells 写 N×1 矩阵),不需要逐行循环。
|
||||
>
|
||||
> ```jsonc
|
||||
> // 在 C 列前插入新列 → 写表头 C1 → 回填 C2:C100 共 99 行
|
||||
> [
|
||||
> {"shortcut": "+dim-insert",
|
||||
> "input": {"sheet_name": "Sheet1", "position": "C", "count": 1}},
|
||||
> {"shortcut": "+cells-set",
|
||||
> "input": {"sheet_name": "Sheet1", "range": "C1:C100",
|
||||
> "cells": [[{"value":"score"}], [{"value":95}], [{"value":87}], /* ... 97 more rows ... */ ]}}
|
||||
> ]
|
||||
> ```
|
||||
|
||||
### `+cells-batch-set-style`
|
||||
|
||||
多 range 应用同一组 style(服务端走 `+batch-update` 原子事务):
|
||||
|
||||
```bash
|
||||
# 表头行 + 汇总行同时刷成蓝底白字
|
||||
lark-cli sheets +cells-batch-set-style --url "..." \
|
||||
--ranges '["sheet1!A1:F1","sheet1!A30:F30"]' \
|
||||
--background-color "#1E5BC6" --font-color "#FFFFFF" --font-weight bold
|
||||
```
|
||||
|
||||
### `+cells-batch-clear`
|
||||
|
||||
多 range 一次性清除(服务端走 `+batch-update` 原子事务);`--scope` 同 `+cells-clear`(`content` / `formats` / `all`,默认 `content`),`high-risk-write` 强制 `--yes`:
|
||||
|
||||
```bash
|
||||
# dry-run 先看清除范围
|
||||
lark-cli sheets +cells-batch-clear --url "..." \
|
||||
--ranges '["sheet1!A2:Z1000","sheet2!A2:Z1000"]' --scope all --dry-run
|
||||
# 执行
|
||||
lark-cli sheets +cells-batch-clear --url "..." \
|
||||
--ranges '["sheet1!A2:Z1000","sheet2!A2:Z1000"]' --scope all --yes
|
||||
```
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate`:`+batch-update` 的 `--operations` 必须合法 JSON,且为非空数组;逐个子操作 `shortcut` / `input` 字段必填校验;**禁止嵌套 `+batch-update`**。`+cells-batch-set-style` 的 `--ranges` 必须 JSON 数组、每项带 sheet 前缀;样式 flag 至少一个非空(或带 `--border-styles`)。`+cells-batch-clear` 的 `--ranges` 同样必须 JSON 数组、每项带 sheet 前缀,`high-risk-write` 强制 `--yes` 或 `--dry-run`(`--scope` 默认 `content`)。
|
||||
- `DryRun`:按顺序输出每个子操作的目标 API + 请求 body 模板;首个失败则整批 fail-fast(不实际执行任何后续)。
|
||||
- `Execute`:按声明顺序串行执行;任一子操作失败立即中断并回滚到该子操作前状态(具体回滚能力取决于子操作类型,沿用 `+batch-update` 的语义)。
|
||||
105
.claude/skills/lark-sheets/references/lark-sheets-changeset.md
Normal file
105
.claude/skills/lark-sheets/references/lark-sheets-changeset.md
Normal file
@ -0,0 +1,105 @@
|
||||
# Lark Sheet Changeset
|
||||
|
||||
## 使用场景
|
||||
|
||||
读取两个版本之间的 **changeset(变更操作清单)**,用于**复核某次编辑(尤其是 AI 编辑)是否真实满足用户诉求**。
|
||||
|
||||
典型场景:AI agent 对表格做了一批编辑后,想确认它"说做的"和"真正落到表格上的"是否一致——拉取编辑前版本到编辑后版本之间的 changeset,逐条核对 action 是否覆盖了用户要求的修改、有没有多改 / 漏改。
|
||||
|
||||
## 版本(revision)语义
|
||||
|
||||
- 这里的"版本"指表格的 **CS revision**(每次提交单调递增的修订号),不是文档历史里的命名版本。
|
||||
- `--start-revision` 是复核基线,即你认定的"编辑前"版本。
|
||||
- `--end-revision` 是"编辑后"版本;**省略时默认取最新 revision**,返回从 start 到最新的全部 changeset。
|
||||
- **版本差上限 20**:`end - start + 1 ≤ 20`,超出会被拒绝(服务端同样以 20 兜底)。复核大跨度变更时请分段拉取。
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+changeset-get` | read | 变更记录 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+changeset-get`
|
||||
|
||||
_公共:URL/token(无 sheet 定位)_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--start-revision` | int | required | 起始版本(编辑前基线,>= 1) |
|
||||
| `--end-revision` | int | optional | 结束版本(省略取最新) |
|
||||
|
||||
## 返回结构
|
||||
|
||||
返回一个 JSON 对象,`changesets` 数组按版本顺序排列,每个元素是一次提交的**原始 action 列表**与元信息:
|
||||
|
||||
```json
|
||||
{
|
||||
"spreadsheet_token": "shtcnXXXX",
|
||||
"latest_revision": 142,
|
||||
"start_revision": 120,
|
||||
"end_revision": 135,
|
||||
"changesets": [
|
||||
{
|
||||
"revision": 121,
|
||||
"create_time": "2026-06-12T10:00:00Z",
|
||||
"actions": [
|
||||
{ "action": "setCellRange", "sheetId": "...", "value": { /* ... */ } }
|
||||
],
|
||||
"is_self_edit": false,
|
||||
"is_ai_edit": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- 最外层 `latest_revision` 是**当前表格的最新版本号**(与查询区间无关),便于判断表格当前停在哪个版本、`--start-revision` 该取多少。
|
||||
- `actions` 是**未经语义渲染的原始操作对象**,按提交内的执行顺序排列。复核时逐条比对:每个 action 改了哪个 sheet、哪个区域、改成什么,是否对应用户的诉求。
|
||||
- `revision` / `create_time` 用于判断"这次改动属于哪个版本、什么时候做的"。
|
||||
- `is_self_edit` 表示该 changeset 是否由当前请求用户提交(committer 与请求用户相同),即"是不是我自己提交的编辑"。
|
||||
- `is_ai_edit` 表示该 changeset 是否由 AI 客户端提交(`member_id` 为 10 / 11)。复核时 `is_ai_edit=true` 即为 AI 写入的编辑(而非用户手动编辑),是核对 AI 是否完成诉求的主要对象。
|
||||
|
||||
## 复核工作流(判断 AI 是否真实完成诉求)
|
||||
|
||||
1. 记下 AI 开始编辑前的 revision(编辑前 `+workbook-info` 或上一次工具返回的 revision 即可作为 `--start-revision`)。
|
||||
2. AI 编辑完成后,跑 `+changeset-get --url <表格> --start-revision <编辑前版本>`(不传 end → 取到最新)。
|
||||
3. 遍历 `changesets[].actions`,核对:
|
||||
- 用户要求的每一处修改是否都有对应 action;
|
||||
- 有没有越权 / 多余的修改(动了用户没让动的 sheet / 区域);
|
||||
- action 的目标区域、值是否与诉求一致。
|
||||
4. 若版本跨度可能 > 20,分段拉取(如 `start..start+19`、`start+20..` …)。
|
||||
|
||||
## 注意
|
||||
|
||||
- `+changeset-get` 是**只读**操作,不改动表格。
|
||||
- 大跨度 / 大批量编辑的 changeset 可能体积较大;输出在传输层已 gzip。必要时缩小版本区间。
|
||||
- 该工具走只读 scope `sheets:spreadsheet:read`,需要对表格有查看权限。
|
||||
|
||||
## Examples
|
||||
|
||||
### `+changeset-get`
|
||||
|
||||
公共:`--url` / `--spreadsheet-token`(二选一,无 sheet 定位)。changeset 是工作簿级历史,不接受 sheet 定位 flag。
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
# 只传起始版本 → 返回从该版本到最新的全部 changeset(最常用:复核 AI 编辑前后的差异)
|
||||
lark-cli sheets +changeset-get --url "https://example.feishu.cn/sheets/shtXXX" --start-revision 120
|
||||
|
||||
# 传起始 + 结束版本(版本差 end-start+1 ≤ 20)
|
||||
lark-cli sheets +changeset-get --spreadsheet-token shtXXX --start-revision 120 --end-revision 135
|
||||
```
|
||||
|
||||
输出契约(envelope.data):
|
||||
|
||||
- `latest_revision` — 当前表格最新版本号(与查询区间无关)
|
||||
- `start_revision` / `end_revision` — 实际查询区间(省略 `--end-revision` 时 `end_revision` = 最新版本)
|
||||
- `changesets[]` — 按版本顺序排列;每项含 `revision` / `create_time` / `actions`(原始操作列表)/ `is_self_edit` / `is_ai_edit`
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate` 阶段只做 XOR 检查(`--url` / `--spreadsheet-token` 二选一)与版本上限校验(`--start-revision ≥ 1`,传了 `--end-revision` 时 `end ≥ start` 且 `end - start + 1 ≤ 20`);**禁止**联网。
|
||||
- `DryRun` 输出请求模板,不实际拉取 changeset。
|
||||
- `Execute` 阶段才发起 changeset 查询;省略 `--end-revision` 时由服务端解析为最新 revision。
|
||||
332
.claude/skills/lark-sheets/references/lark-sheets-chart.md
Normal file
332
.claude/skills/lark-sheets/references/lark-sheets-chart.md
Normal file
@ -0,0 +1,332 @@
|
||||
# Lark Sheet Chart
|
||||
|
||||
## 真对象硬约束
|
||||
|
||||
当用户要求"画个图 / 数据可视化 / 趋势图 / 对比图 / 占比图"时,**必须**通过 `+chart-{create|update|delete}` 创建真实的图表对象。**禁止**用本地脚本调 matplotlib / seaborn 生成图片再插入到表格代替——静态图片无法随源数据更新,且失去交互能力。判断标准:交付后 `+chart-list` 必须能返回该对象。
|
||||
|
||||
## 使用场景
|
||||
|
||||
读写图表对象。本 reference 覆盖 4 个 shortcut:
|
||||
|
||||
| 操作需求 | 使用工具 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 查看已有图表 | `+chart-list` | 获取图表的类型、数据源和样式配置 |
|
||||
| 创建/更新/删除图表 | `+chart-{create|update|delete}` | 对图表对象执行写入操作 |
|
||||
|
||||
典型工作流:先读取现有图表了解配置 → 执行创建/更新/删除 → 再次读取验证结果。
|
||||
|
||||
## 需求→图表类型映射(创建前必查)
|
||||
|
||||
| 用户说 | 图表类型 | 备注 |
|
||||
|--------|---------|------|
|
||||
| "占比"、"比例"、"各XX占多少" | 饼图(pie) | 单维度占比首选 |
|
||||
| "对比"、"各XX的YY" | 柱形图(column,纵向) | 多类别数值对比;横向条形用 `bar` |
|
||||
| "趋势"、"变化"、"走势" | 折线图(line) | 时间序列首选 |
|
||||
| "堆积"、"组成构成" | 堆积柱形图(column + stack) | 多系列累加 |
|
||||
| "分布"、"相关性" | 散点图(scatter) | 两变量关系 |
|
||||
|
||||
**多图表需求**:当用户同时提到多种分析(如"统计占比 + 对比数量"),必须创建多个图表,每个对应一种类型,不要只做一个。
|
||||
|
||||
**`--properties` 结构锚点(构造前必读)**:`--properties` 顶层只有 `position` / `offset` / `size` / `snapshot` 四个字段,**没有**顶层 `data`,也没有再嵌一层 `properties`。图表数据配置全部挂在 `snapshot.data` 下——下文及示例里出现的 `refs` / `headerMode` / `dim1` / `dim2` / `nameRef` 一律指 `snapshot.data.refs` / `snapshot.data.headerMode` / `snapshot.data.dim1` / `snapshot.data.dim2`(及其下的 `serie.nameRef` / `series[].nameRef`);样式 / 堆叠 / 数据标签等在 `snapshot.plotArea` 下。完整结构以 `lark-cli sheets +chart-create --print-schema --flag-name properties` 为准。
|
||||
|
||||
**常见配置错误(必须注意)**:
|
||||
- **图表类型选择错误**:用户说"堆积柱形图/百分比堆积"时,应在 `properties.snapshot.plotArea.plot.extra.stack` 中配置堆叠;百分比堆叠需在该 stack 下设置 `percentage: true`。用户说"占比/比例"时,优先考虑饼图或百分比堆积图。注意区分 `column`(柱形图,纵向)与 `bar`(条形图,横向)是两个不同的 type 取值,"对比/各 XX" 类纵向柱默认用 `column`
|
||||
- **数据标签开关**:`plotArea.plot.labels` 对象的**存在性即开关**——
|
||||
- 用户需要看到具体数值/类别时:传入 `labels` 并配置 `value` / `category` / `series` / `percentage` 等显示位。
|
||||
- 用户明确说"不要数据标签 / 关掉标签"时:**整个 `labels` 字段省略**。不要用 `labels: { value: false, category: false, series: false }` 这种"全部置 false"的写法关闭——只要传了 `labels`,系统就会显示数据标签(且默认兜底显示 value)。
|
||||
- **数据源范围与系列名来源要对齐**:
|
||||
- **默认情况(inline 模式)**:`refs` 范围**应包含表头行**(首行/首列即系列名),且范围要精确覆盖目标数据,不要多选或少选。
|
||||
- **合并标题行要跳过**:如果表格在表头上方存在合并的标题行(如"员工统计表"横跨多列的大标题),`refs` 必须跳过标题行、从真正的列标题行开始。例如表头在第 3 行、数据在第 4-20 行,则 `refs` 应为 `A3:G20` 而非 `A1:G20`。包含合并标题行会导致列名识别错误、表头被当作数据参与聚合计算。
|
||||
- **数据与表头分离时必须用 detached 模式**:当 `refs` 只覆盖完整数据的一个子集(按筛选/分组只画其中一段),而真正的语义表头在该子集之外时,**必须**设置 `snapshot.data.headerMode='detached'`:refs 仅传纯数据范围,维度名/系列名通过 `snapshot.data.dim1.serie.nameRef` / `snapshot.data.dim2.series[].nameRef` 指向真正的表头单元格。详见下文"硬性规则:数据与表头分离场景必须使用 detached 模式"。
|
||||
- **axes[].label 不接受 `format` / `number_format` 字段**:想给坐标轴数值加千分位、百分号等格式化时,不要在 `axes[i].label` 里传 `format` 或 `number_format`(schema 未定义,会报 `unexpected property "format" is not defined in schema`)。数值格式化统一在源数据单元格的 `cell_styles.number_format` 里设置(写 `+cells-set` 时),图表会沿用单元格格式。**日期轴同理**:横轴显示成 `45297` 这类 Excel 序列号,是因为源日期列没设日期格式——给源列设 `number_format="yyyy-mm-dd"` 后横轴才会显示成日期(反例:折线图横轴日期显示为序列号)。大数值轴显示科学计数法同理,给源列设整数 / 千分位格式(反例:透视表数值轴显示科学计数法)。
|
||||
- **轴口径要对齐用户要的指标**:用户要"占比 / 比例"时,**纵轴应是百分比**——用饼图,或柱 / 条形图设 `stack.percentage: true` 让纵轴变 %,并把数据源指向占比列 / 让数据标签显示百分比;不要交付纵轴仍是原始计数的图(反例:要求看各类占比,却用普通堆积柱、纵轴是 0–350 的人数而非百分比)。
|
||||
- **创建后必须验证**:图表创建后必须调用 `+chart-list` 验证配置是否正确
|
||||
|
||||
> **⚠️ 硬性规则:当用户通过列标题名称(而非列索引)指定横轴/纵轴系列时,必须先读取表格首行(表头)来确定列名与列索引的对应关系,再设置 `dim1`/`dim2` 的 `index`。**
|
||||
> 例如用户说"横轴为车型系列,纵轴为Q1-Q4的销量",你不能猜测列索引,必须先通过读取表格数据源范围的首行内容(使用 `lark-sheets-read-data` 的 `+cells-get` 或其他读取单元格的工具),确认"车型系列"是第几列、"Q1"~"Q4"分别是第几列,然后再将正确的列索引填入 `dim1.serie.index` 和 `dim2.series[].index`。
|
||||
|
||||
> **⚠️ 硬性规则:数据与表头分离场景必须使用 detached 模式。** 当 `refs` 仅覆盖数据的一个子集,而真正的语义表头行/列位于该子集之外时,**必须** `snapshot.data.headerMode='detached'` 并配上 `nameRef`。不能用 inline 模式 + 把 refs 多带 1 行兜底表头来替代——那种写法已废弃。否则图表会把错误的首行/首列当系列名,或图例显示成"系列1/系列2"等默认名,或者 refs 里混入相邻分组的数据。
|
||||
>
|
||||
> **触发该规则的典型信号**(满足任意一条都必须走 detached):
|
||||
> - 用户要求"针对 X 类的数据画图"、"只看某个分组"、"只画筛选后的部分",而 X 类对应的行段在数据中间或末尾,与表头不连续;
|
||||
> - 用户要求"按 X 分别画图"、"按某个维度(部门/品类/地区/时间段等)拆图"——**多张图共享同一组表头**;
|
||||
> - `refs` 起始行 > 表头行(如表头在第 1 行,但 `refs` 从第 11 行开始);
|
||||
> - `refs` 起始列 > 表头列(如表头在 A 列,但 `refs` 从 C 列开始)。
|
||||
>
|
||||
> **正确做法**:
|
||||
> 1. 在 `data` 下显式设置 `"headerMode": "detached"`;
|
||||
> 2. `refs` **只覆盖该子集的纯数据**,不要向上/向左多带 1 行/列,也不要把全局表头整段并进来(否则会把其它分组的数据混进图);
|
||||
> 3. **`nameRef` 必填**:给 `dim1.serie.nameRef` 写真正表头中"类别名"那一格的 A1 引用(如 `'Sheet2'!A1`,sheet 名按 A1 标准单引号包裹),给每个 `dim2.series[i].nameRef` 写对应数值列的 A1 引用(如 `'Sheet2'!C1`、`'Sheet2'!D1`)。任一缺失会被校验拦下并报 `headerMode=detached requires ... nameRef`;
|
||||
> 4. `refs[i].value` 必须是单元格或普通矩形范围(CELL / NORMAL),不接受整行/整列/开区间;`direction='column'` 时起始行必须 > 0,`direction='row'` 时起始列必须 > 0;
|
||||
> 5. `index` 仍按 `refs` 内的列/行号填,从 1 开始。
|
||||
>
|
||||
> **两种场景对照(互斥,二选一)**:
|
||||
>
|
||||
> | 场景 | 何时命中 | 写法 |
|
||||
> |---|---|---|
|
||||
> | A. 表头与数据连在一起 | 单张图、refs 首行/首列就是表头(典型整段画图) | **省略 headerMode**(默认 inline),refs 含表头,**不写 nameRef** |
|
||||
> | B. 表头与数据分离 | 上面 4 条信号任一命中(数据子集、按维度拆图等) | **`headerMode='detached'`**,refs 仅纯数据,**`nameRef` 必填** |
|
||||
>
|
||||
> **反向约束**:场景 A 下不要写 `nameRef`——首行命名已经生效,多写反而冗余。`nameRef` 仅在场景 B 下使用(且必填)。
|
||||
|
||||
## ⚠️ chart 数据源引用 pivot 时必须排除总计行
|
||||
|
||||
当 chart 要基于刚创建的 pivot 产物画图时,**禁止凭猜写 `refs`**。pivot 默认启用 `show_row_grand_total` / `show_col_grand_total`,产物最后一行/一列通常是"总计"。如果 `refs` 把总计行一并框进去:
|
||||
- **柱形图**末尾会多一根天文数字柱子(=所有数据求和),把其他柱子压扁到看不见
|
||||
- **饼图**会多一个"总计"扇区占 33%+,真实类别的比例完全失真
|
||||
|
||||
**正确流程**:
|
||||
1. `+pivot-create create` 返回 `sheet_id` + `pivot_table_id`
|
||||
2. 调 `+csv-get(sheet_id, 'A1:E30')` 或 `+pivot-list` 读 pivot 产物的**实际数据范围**
|
||||
3. 识别并排除"总计"/"小计"行(通常最后一行;嵌套 pivot 还要排除中间层小计)
|
||||
4. `+chart-create create` 时 `snapshot.data.refs` 精确到数据行(如 pivot 占 A1:D9、总计在 row9 → chart 用 `A1:D8`)
|
||||
|
||||
## 图表位置选择(创建前必做)
|
||||
|
||||
凭感觉挑列号/行号会被 API 拒(`position is out of sheet range`)。按以下四步走:
|
||||
|
||||
1. **查尺寸**:`+workbook-info` 拿该 sheet 的 `row_count` / `column_count`(下文记为 rowCount / columnCount;`+sheet-info` 只返回布局,不含行列总数)。
|
||||
2. **估跨度**:默认单元格 **105 px 宽 × 27 px 高**,`needCols = ceil(width/105)`,`needRows = ceil(height/27)`。
|
||||
3. **校验**:`position.row + needRows ≤ rowCount` 且 `col_idx + needCols ≤ columnCount`(`position.row` 为 **0-based**:首行 = `row:0`,与 A1 区间 / `+dim-insert --position` 的 1-based 行号不同;col 按 A=0、B=1、…、Z=25、AA=26… 换算)。
|
||||
4. **不够就先扩表**,二选一,禁止硬塞越界位置:
|
||||
- **优先**放数据下方空区:`position = {row: data_end_row + 2, col: "A"}`;
|
||||
- 否则先调 `+dim-insert`(`lark-sheets-sheet-structure`)扩行/列,再 create。
|
||||
|
||||
⚠️ **图表落点禁止压在已有数据矩形内**——必须落在数据区**右侧或下方的空白**,否则图表浮层会遮挡原始数据被判失败(反例:折线图落在数据区中间,遮挡了下方原始数据)。
|
||||
|
||||
**示例**:21 列 sheet 放 600×400 图 → `needCols=6, needRows=15`
|
||||
- ❌ `{row: 0, col: "W"}` — col=22 越界
|
||||
- ✅ `{row: 42, col: "A"}` — 放数据下方
|
||||
- ✅ 先 `+dim-insert --position V --count 6`(在 V 列前插 6 列,即 U 列之后),再放图到 `{row: 0, col: "V"}`
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+chart-list` | read | 对象 |
|
||||
| `+chart-create` | write | 对象 |
|
||||
| `+chart-update` | write | 对象 |
|
||||
| `+chart-delete` | high-risk-write | 对象 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+chart-list`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--chart-id` | string | optional | 指定单个图表 reference_id 过滤 |
|
||||
|
||||
### `+chart-create`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--properties` | string + File + Stdin(复合 JSON) | required | 图表完整配置 JSON。顶层字段为 `position` / `offset` / `size` / `snapshot`(无顶层 `data`,也无再嵌一层 `properties`);图表数据配置在 `snapshot.data` 下(含 `refs` / `headerMode` / `dim1` / `dim2`);必须至少含 `snapshot.data.dim1.serie.index` 或 `dim2.series[].index` 之一,否则 server 拒。结构嵌套深,完整结构跑 `--print-schema --flag-name properties` |
|
||||
|
||||
### `+chart-update`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--chart-id` | string | required | 目标图表 reference_id |
|
||||
| `--properties` | string + File + Stdin(复合 JSON) | required | 完整或足够完整的图表配置 JSON(先 `+chart-list` 回读再 patch) |
|
||||
|
||||
### `+chart-delete`
|
||||
|
||||
_公共四件套 · 系统:`--yes`、`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--chart-id` | string | required | 目标图表 reference_id |
|
||||
|
||||
## Schemas
|
||||
|
||||
> 复合 JSON flag 字段速查(只列顶层 + 一层嵌套)。深层结构看下方 `## Examples`,或用 `--print-schema` 读完整 JSON Schema(用法见 SKILL.md「公共 flag 速查」与「Agent 使用提示」)。
|
||||
|
||||
### `+chart-create` `--properties` / `+chart-update` `--properties`
|
||||
|
||||
_创建/更新的图表属性_
|
||||
|
||||
**顶层字段**:
|
||||
- `position` (object?) — 必填 { row: number, col: string }
|
||||
- `offset` (object?) — 可选 { row_offset?: number, col_offset?: number }
|
||||
- `size` (object?) — 必填 { width: number, height: number }
|
||||
- `snapshot` (object?) — 图表快照配置 { title?: object, subTitle?: object, style?: object, legend?: oneOf, plotArea: object, …共 6 项 }
|
||||
|
||||
## Examples
|
||||
|
||||
公共四件套:所有 shortcut 顶部排列 `--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`(XOR 规则同 `+csv-get`)。
|
||||
|
||||
### `+chart-list`
|
||||
|
||||
输出契约:返回按工作表分组的图表列表,每个图表含 `chart_id` / `position` / `details.snapshot` 等。
|
||||
|
||||
### `+chart-create`
|
||||
|
||||
> **`snapshot.data` 必填 `dim1.serie.index` 或 `dim2.series[].index` 之一**(1-based,对应 `refs.value` 范围内的列序)。schema 允许传空 `{}` 但 server 运行时强制:缺则被拒为 `snapshot.data.dim1.serie.index and dim2.series[].index are both missing; at least one must be set`,即便侥幸通过也只会渲染空图。
|
||||
|
||||
> ⚠️ **含 `'Sheet'!` 前缀的 `--properties` 必须走 stdin 或 `@file`,不要用 inline 单引号**。`refs` / `nameRef` 里的 sheet 前缀带单引号(`'Sheet1'!A1`),若塞进 inline 的 `--properties '{...}'`,bash 会把内层那对单引号吃掉(sheet 名带空格还会被拆成多个词),JSON 直接被破坏。下面示例统一用 `--properties - <<'JSON' … JSON`(heredoc 定界符加引号 = 不做 shell 替换),或 `--properties @file.json`(`@` 只接 cwd 下相对路径)。
|
||||
|
||||
最小可用列图(inline 模式:refs 含表头行):
|
||||
|
||||
```bash
|
||||
lark-cli sheets +chart-create --url "https://example.feishu.cn/sheets/shtXXX" \
|
||||
--sheet-name "Sheet1" --properties - <<'JSON'
|
||||
{
|
||||
"position":{"row":42,"col":"A"},
|
||||
"size":{"width":600,"height":400},
|
||||
"snapshot":{
|
||||
"data":{
|
||||
"refs":[{"value":"'Sheet1'!A1:B10"}],
|
||||
"dim1":{"serie":{"index":1}},
|
||||
"dim2":{"series":[{"index":2}]}
|
||||
},
|
||||
"plotArea":{"plot":{"type":"column"}}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
|
||||
# 或落到 cwd 下相对路径文件再用 @file
|
||||
lark-cli sheets +chart-create --url "..." --sheet-name "Sheet1" --properties @chart-config.json
|
||||
```
|
||||
|
||||
**饼图专属示例**(`sectors` 必须嵌在 `plotArea.plot.series[i].sectors.sector[]`,且 `sector[].index` 1-based):
|
||||
|
||||
饼图比 column / bar 更复杂:`sectors` 是 object,里面再包一个**单数** `sector` 数组——CLI 不替你 normalize,写错路径会被 server schema 直接拒。
|
||||
|
||||
```bash
|
||||
lark-cli sheets +chart-create --url "..." --sheet-name "Sheet1" --properties - <<'JSON'
|
||||
{
|
||||
"position":{"row":24,"col":"F"},
|
||||
"size":{"width":600,"height":450},
|
||||
"snapshot":{
|
||||
"title":{"text":"各部门员工人数占比"},
|
||||
"plotArea":{"plot":{
|
||||
"type":"pie",
|
||||
"series":[{
|
||||
"index":1,
|
||||
"sectors":{"sector":[{"index":1,"offsetRadius":0.05}]}
|
||||
}]
|
||||
}},
|
||||
"data":{
|
||||
"refs":[{"value":"'Sheet1'!A1:B11"}],
|
||||
"dim1":{"serie":{"index":1,"aggregate":true}},
|
||||
"dim2":{"series":[{"index":2,"aggregateType":"sum"}]}
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
```
|
||||
|
||||
**数据与表头分离(必须用 `detached` + `nameRef`)**:
|
||||
|
||||
场景:周度销量明细表,真实表头在第 1 行(A1=周次、C1=订单量、D1=退款量),数据按 B 列"店铺"分段;用户只要"3 号店"那一段(第 11–17 行)。
|
||||
|
||||
```bash
|
||||
lark-cli sheets +chart-create --url "..." --sheet-name "Sheet2" --properties - <<'JSON'
|
||||
{
|
||||
"position":{"row":7,"col":"F"},
|
||||
"size":{"width":600,"height":360},
|
||||
"snapshot":{
|
||||
"title":{"text":"3 号店周度订单/退款"},
|
||||
"plotArea":{"plot":{"type":"column"}},
|
||||
"data":{
|
||||
"headerMode":"detached",
|
||||
"direction":"column",
|
||||
"refs":[{"value":"'Sheet2'!A11:D17"}],
|
||||
"dim1":{"serie":{"index":1,"nameRef":"'Sheet2'!A1"}},
|
||||
"dim2":{"series":[
|
||||
{"index":3,"nameRef":"'Sheet2'!C1"},
|
||||
{"index":4,"nameRef":"'Sheet2'!D1"}
|
||||
]}
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
```
|
||||
|
||||
约束:
|
||||
- `refs` 只覆盖纯数据 `A11:D17`,**不要**把表头行 A1 并进来
|
||||
- `nameRef` 在 detached 模式下**必填**,缺了被校验报 `headerMode=detached requires ... nameRef`
|
||||
- `index` 按 refs 内的列序算(A=1、B=2、C=3、D=4),**不是**全表列号
|
||||
- `nameRef` 必须配对应的 `index`;单写 `nameRef` 不传 `index` 直接报参数错
|
||||
|
||||
**多张图共享同一组表头(按维度拆图,必须用 detached)**:
|
||||
|
||||
场景:销售明细表头在 A1:E1(月份/区域/销售额/订单数/客单价),数据按区域分 3 段(华北 A2:E9、华东 A10:E17、华南 A18:E25),要分别画 3 张图。
|
||||
|
||||
❌ 常见错误:
|
||||
|
||||
```jsonc
|
||||
// 错误 1:refs 含全局表头但跨段 —— 多个区域被混进同一张图
|
||||
{"data":{"refs":[{"value":"'Sheet'!A1:E17"}], ... }} // 华东图混进华北 8 行
|
||||
// 错误 2:inline + refs 只取数据段、不写 detached/nameRef —— 图例显示成具体数据值
|
||||
{"data":{"refs":[{"value":"'Sheet'!A10:E17"}],"dim1":{"serie":{"index":1}}, ... }}
|
||||
```
|
||||
|
||||
✅ 正确模式:3 张图各自 detached、refs 干净不重叠:
|
||||
|
||||
```jsonc
|
||||
// 图 1:华北
|
||||
{"data":{
|
||||
"headerMode":"detached","direction":"column",
|
||||
"refs":[{"value":"'Sheet'!A2:E9"}],
|
||||
"dim1":{"serie":{"index":1,"nameRef":"'Sheet'!A1"}},
|
||||
"dim2":{"series":[
|
||||
{"index":3,"nameRef":"'Sheet'!C1"},
|
||||
{"index":4,"nameRef":"'Sheet'!D1"}
|
||||
]}
|
||||
}}
|
||||
// 图 2:华东 —— refs 改 'Sheet'!A10:E17,其余同上
|
||||
// 图 3:华南 —— refs 改 'Sheet'!A18:E25,其余同上
|
||||
```
|
||||
|
||||
> `--properties` JSON 关键字段:
|
||||
> - `position.row` / `position.col` 必须留足空间,越界会被 API 拒(按本文件"图表位置选择"四步走)
|
||||
> - `snapshot.data.headerMode`:默认 inline;当 refs 仅覆盖数据子集而语义表头在子集之外,必须 `detached` + `nameRef`
|
||||
> - chart 引用 pivot 输出时,`snapshot.data.refs` 必须排除总计 / 小计行
|
||||
|
||||
### `+chart-update`
|
||||
|
||||
**Update 三步法**(缺一步会丢字段):
|
||||
|
||||
1. `+chart-list --chart-id <id>` 拿到完整 snapshot
|
||||
2. 在拿到的 snapshot 上**局部**修改要改的字段,其余保持不变
|
||||
3. 把**完整 snapshot** 整个回写到 `--properties.snapshot`
|
||||
|
||||
```bash
|
||||
lark-cli sheets +chart-update --url "..." --sheet-id "$SID" --chart-id "chrXXX" \
|
||||
--properties '{
|
||||
"position":{"row":0,"col":"A"},
|
||||
"size":{"width":480,"height":320},
|
||||
"snapshot": <完整快照(由 +chart-list 取回后局部修改)>
|
||||
}'
|
||||
```
|
||||
|
||||
> 关键:**不能只提交局部 snapshot**,否则未传字段会被还原为默认值。`+chart-update` 的语义是 PUT(整体覆盖),不是 PATCH。
|
||||
|
||||
### `+chart-delete`
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
# dry-run 先看会删什么(sheet 定位必填)
|
||||
lark-cli sheets +chart-delete --url "https://example.feishu.cn/sheets/shtXXX" --sheet-id "$SID" \
|
||||
--chart-id "chrXXX" --dry-run
|
||||
|
||||
# 真正执行
|
||||
lark-cli sheets +chart-delete --url "https://example.feishu.cn/sheets/shtXXX" --sheet-id "$SID" \
|
||||
--chart-id "chrXXX" --yes
|
||||
```
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate`:XOR 公共四件套;`+chart-create` / `+chart-update` 的 `--properties` 必须能解析为合法 JSON;`+chart-delete`(high-risk-write)校验 `--yes` 或 `--dry-run` 至少一个。
|
||||
- `DryRun`:`+chart-create` / `+chart-update` 输出"将要 POST 的 body 模板";`+chart-delete` 输出"将要删除的 chart_id 及隶属 sheet",零网络副作用。
|
||||
- `Execute`:写操作执行后不自动回读;如需确认,自行调用 `+chart-list` 比对结果。
|
||||
|
||||
> `+chart-create` / `+chart-update` 是 write 级别,按需可用 `--dry-run` 预览,不要求 `--yes`。只有 `+chart-delete`(high-risk-write)必须 `--yes`。
|
||||
@ -0,0 +1,181 @@
|
||||
# Lark Sheet Conditional Format
|
||||
|
||||
## 真对象硬约束 + 触发词清单
|
||||
|
||||
用户出现以下口语指令时,**强制**走 `+cond-format-{create|update|delete}`,**禁止**用 `+cells-set` 写静态背景色 / 字体色代替:
|
||||
|
||||
- **颜色动作**:"标红 / 标黄 / 标绿 / 上色 / 染色 / 涂色 / 表红色 / 表黄色"
|
||||
- **视觉强调**:"高亮 / 突出 / 标记 / 标注 / 区分"
|
||||
- **条件触发**:"重复的标出来 / 异常的圈出来 / 过期的染红 / 大于 X 的标黄 / 不达标的标红"
|
||||
- **联动语义**:"颜色随数据变 / 联动 / 自动更新 / 改了数据颜色也跟着变"
|
||||
- **数值可视化**:"数据条 / 色阶 / 渐变色 / 进度条样式"
|
||||
|
||||
飞书表格的"颜色标记"语义 = 条件格式规则 ≠ 静态背景色。如果用 `+cells-set` 写静态,源数据变化时颜色不会跟着变(典型反例:用户要求"过期单元格标红"时,模型用静态填充——日期变化后单元格颜色不再准确反映过期状态)。
|
||||
|
||||
**判断标准**:交付后 `+cond-format-list` 必须能返回该规则;否则视为违规。
|
||||
|
||||
**大数据量首选**:当数据量 > 1000 行时,条件格式是首选——它由飞书自身渲染,比"本地脚本逐行计算 + `+cells-set` 写静态背景色"更高效、更稳(颜色还能随源数据自动联动)。
|
||||
|
||||
## 使用场景
|
||||
|
||||
读写条件格式对象。本 reference 覆盖 4 个 shortcut:
|
||||
|
||||
| 操作需求 | 使用工具 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 查看已有条件格式 | `+cond-format-list` | 获取规则类型、范围和样式配置 |
|
||||
| 创建/更新/删除条件格式 | `+cond-format-{create|update|delete}` | 对条件格式规则执行写入操作 |
|
||||
|
||||
典型工作流:先读取现有条件格式了解配置 → 执行创建/更新/删除 → **必须再次读取验证结果**。
|
||||
|
||||
**常见配置错误(必须注意)**:
|
||||
- **创建后必须验证**:条件格式创建后必须调用 `+cond-format-list` 验证规则是否生效。如果验证发现规则未生效或配置不正确,应立即修复并重试
|
||||
- **范围要精确**:条件格式的应用范围必须精确覆盖用户指定的列/行,不要遗漏
|
||||
- **`style.back_color` vs `style.fore_color` 的中文语义**:用户中文语境下的"**标红/高亮/染色/标记**"指**单元格背景色**,用 `back_color`;"**文字红/字体红/把字变红**"才用 `fore_color`。默认无说明时选 `back_color`。把过期数据涂红、重复值高亮等都应该是 `back_color: "#FFE6E6"`(或类似浅红)配合可选的 `fore_color` 加深字体
|
||||
- **日期/空值比较必须防空**:用户说"过期的标红"时,除了 `TODAY()`,公式必须排除空单元格,否则空白格也会被误判为"早于今天"而全表标红。正确公式:`=AND(E1<>"", E1<=TODAY())`;错误公式:`=E1<=TODAY()`(空值会被当作 0 判为过期)
|
||||
- **公式条件注意引用方式**:自定义公式条件中的单元格引用需要根据实际场景选择相对/绝对引用(如 `=E1<=TODAY()` 而非 `=$E$1<=TODAY()`,后者只比较一个格)
|
||||
|
||||
⚠️ **用户明确要求"辅助列+条件格式"两步走时,禁止用 `expression` 绕过**:当用户说以下任意一种表达时,必须按两步走(先建辅助列 → 再基于辅助列做条件格式),**禁止**直接用一个 `rule_type: "expression"` 公式一步完成:
|
||||
|
||||
- "**增加辅助列**,再/然后标记……"
|
||||
- "**先计算/判断** XX **是否** YY,**再**标记……"
|
||||
- "**新建一列**放结果,再用结果染色"
|
||||
- 明确要求用 "辅助列"、"辅助字段"、"判断列"、"标记列"
|
||||
|
||||
**正确做法(两步走)**:
|
||||
|
||||
Step 1 的 `+cells-set` 及 `--copy-to-range` 等 flag 以 `lark-sheets-write-cells` 为准。
|
||||
|
||||
```
|
||||
Step 1: `+cells-set` 在新列写判断公式(形成"是/否"或布尔辅助列)
|
||||
range="H2", cells=[[{formula: "=IF(A2>B2, \"是\", \"否\")"}]], --copy-to-range="H2:H100"
|
||||
|
||||
Step 2: 基于辅助列值做条件格式(用 cellIs 或引用辅助列的 expression)
|
||||
`+cond-format-{create|update|delete}` create
|
||||
rule_type: "expression"
|
||||
ranges: ["A2:H100"] // 整行高亮
|
||||
attrs: [{formula: ["=$H2=\"是\""]}] // 引用辅助列
|
||||
style: {back_color: "#FFECEC"}
|
||||
```
|
||||
|
||||
**错误做法(一步走绕过辅助列)**:
|
||||
|
||||
```
|
||||
`+cond-format-{create|update|delete}` create
|
||||
rule_type: "expression"
|
||||
ranges: ["2:145"]
|
||||
attrs: [{formula: ["=$O2>$H2"]}] ← 虽然逻辑等价,但产物里缺辅助列 → 不满足用户明确要求的"辅助列"诉求
|
||||
```
|
||||
|
||||
为什么禁止一步走:用户明确要求辅助列是有**业务意图**的——让人肉眼能在表里看到"是/否"列;条件格式只是视觉辅助。一步 expression 虽然效果对了,但用户打开表格看不到辅助列,被视为"操作不完整/未采用公式"。
|
||||
|
||||
`expression` 单独使用的场景是:用户**没有**明确要求辅助列、只要"标红符合条件的行"时。
|
||||
|
||||
⚠️ **创建条件格式前必须读数据行确认列对应**:仅读首行表头(`+csv-get range="A1:Z1"`)不够——如果表头语义含糊(比如"时间"、"日期"这种多列同义词),formula 里引用的列字母可能张冠李戴。必须再读 3-5 行**数据样本**(如 `range="A2:Z6"`)确认:①列名对应的实际值;②字段含义匹配用户描述;③数据类型是日期/数字/文本。特别是比较类条件格式(`=$A2>$B2` 这种),列字母选错整条规则就废了。
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+cond-format-list` | read | 对象 |
|
||||
| `+cond-format-create` | write | 对象 |
|
||||
| `+cond-format-update` | write | 对象 |
|
||||
| `+cond-format-delete` | high-risk-write | 对象 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+cond-format-list`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--rule-id` | string | optional | 按规则 id 过滤 |
|
||||
|
||||
### `+cond-format-create`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--properties` | string + File + Stdin(复合 JSON) | required | 规则配置 JSON,含 `style`(命中样式,必填)和 `attrs?`(规则参数列表,因 `rule_type` 不同结构而异)/ `has_ref?`。`rule_type` 和 `ranges` 已拎为独立 flag |
|
||||
| `--rule-type` | string | required | 条件格式规则类型;优先级高于 `--properties` 中同名字段(可选值:`duplicateValues` / `uniqueValues` / `cellIs` / `containsText` / `timePeriod` / `containsBlanks` / `notContainsBlanks` / `dataBar` / `colorScale` / `rank` / `aboveAverage` / `expression` / `iconSet`) |
|
||||
| `--ranges` | string + File + Stdin(简单 JSON) | required | 应用条件格式的 A1 范围 JSON 数组(如 `["A1:A100","C2:C50"]`);优先级高于 `--properties` 中同名字段 |
|
||||
|
||||
### `+cond-format-update`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--rule-id` | string | required | 目标规则 id |
|
||||
| `--properties` | string + File + Stdin(复合 JSON) | required | 规则配置 JSON,结构同 `+cond-format-create` 的 `--properties`;update 是整组覆盖式 |
|
||||
| `--rule-type` | string | required | 条件格式规则类型;优先级高于 `--properties` 中同名字段(可选值:`duplicateValues` / `uniqueValues` / `cellIs` / `containsText` / `timePeriod` / `containsBlanks` / `notContainsBlanks` / `dataBar` / `colorScale` / `rank` / `aboveAverage` / `expression` / `iconSet`) |
|
||||
| `--ranges` | string + File + Stdin(简单 JSON) | required | 应用条件格式的 A1 范围 JSON 数组(如 `["A1:A100","C2:C50"]`);优先级高于 `--properties` 中同名字段 |
|
||||
|
||||
### `+cond-format-delete`
|
||||
|
||||
_公共四件套 · 系统:`--yes`、`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--rule-id` | string | required | 目标规则 id |
|
||||
|
||||
## Schemas
|
||||
|
||||
> 复合 JSON flag 字段速查(只列顶层 + 一层嵌套)。深层结构看下方 `## Examples`,或用 `--print-schema` 读完整 JSON Schema(用法见 SKILL.md「公共 flag 速查」与「Agent 使用提示」)。
|
||||
|
||||
### `+cond-format-create` `--properties` / `+cond-format-update` `--properties`
|
||||
|
||||
_创建/更新的条件格式属性_
|
||||
|
||||
**顶层字段**:
|
||||
- `rule_type` (enum) — 条件格式规则类型 [duplicateValues / uniqueValues / cellIs / containsText / timePeriod / containsBlanks / notContainsBlanks / dataBar / colorScale / rank / aboveAverage / expression / iconSet] — ⚠️ 已拎为独立 flag `--rule-type`,请勿在此 JSON 内重复填写(同名以独立 flag 为准)
|
||||
- `ranges` (array<string>) — 应用条件格式的 A1 范围列表 — ⚠️ 已拎为独立 flag `--ranges`,请勿在此 JSON 内重复填写(同名以独立 flag 为准)
|
||||
- `style` (object) — 命中规则时应用的单元格样式 { back_color?: string, fore_color?: string, text_decoration?: enum, font?: enum }
|
||||
- `attrs` (array<oneOf>?) — 规则参数列表
|
||||
- `has_ref` (boolean?) — 可选
|
||||
|
||||
## Examples
|
||||
|
||||
公共四件套:所有 shortcut 顶部排列 `--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`(XOR)。
|
||||
|
||||
### `+cond-format-list`
|
||||
|
||||
```bash
|
||||
# 列出当前 sheet 全部条件格式规则(拿 rule_id 供 update/delete)
|
||||
lark-cli sheets +cond-format-list --url "..." --sheet-id "$SID"
|
||||
```
|
||||
|
||||
### `+cond-format-create`
|
||||
|
||||
`--rule-type` / `--ranges` 是独立 flag(不要再放 `--properties`);`style` / `attrs` 等结构走 `--properties`:
|
||||
|
||||
```bash
|
||||
# 重复值高亮
|
||||
lark-cli sheets +cond-format-create --url "..." --sheet-id "$SID" \
|
||||
--rule-type duplicateValues --ranges '["A1:A100"]' \
|
||||
--properties '{"style":{"back_color":"#FFD7D7"}}'
|
||||
|
||||
# 数据条
|
||||
lark-cli sheets +cond-format-create --url "..." --sheet-id "$SID" \
|
||||
--rule-type dataBar --ranges '["B2:B100"]' \
|
||||
--properties @rule.json
|
||||
```
|
||||
|
||||
### `+cond-format-update`
|
||||
|
||||
整组覆盖式:先 `+cond-format-list --rule-id <id>` 拿当前完整配置,改后整组传回。
|
||||
|
||||
### `+cond-format-delete`
|
||||
|
||||
```bash
|
||||
lark-cli sheets +cond-format-delete --url "..." --sheet-id "$SID" --rule-id "$RULE_ID" --yes
|
||||
```
|
||||
|
||||
> 一次只删一个 `--rule-id`。要删**多个**条件格式时,先 `+cond-format-list` 拿到各 `rule-id`,再用 `+batch-update` 把多个 `+cond-format-delete` 合并为单次原子提交,不要逐个调用。
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate`:XOR 公共四件套;`--rule-type` / `--ranges` 必填;`--properties` 必须能解析为合法 JSON;按 `--rule-type` 检查必填子字段(`cellIs` 需 `attrs.operator` + `attrs.value`、`expression` 需 `attrs.formula`、`colorScale` 需 `min/mid/max` 配色等);`+cond-format-delete` 强制 `--yes` 或 `--dry-run`。
|
||||
- `DryRun`:写操作输出"将要 POST/PATCH/DELETE 的 conditional_format 请求模板"。
|
||||
- `Execute`:写后不自动回读;如需确认,自行调用 `+cond-format-list --rule-id <id>` 比对规则 / 范围 / 样式。
|
||||
137
.claude/skills/lark-sheets/references/lark-sheets-filter-view.md
Normal file
137
.claude/skills/lark-sheets/references/lark-sheets-filter-view.md
Normal file
@ -0,0 +1,137 @@
|
||||
# Lark Sheet Filter View
|
||||
|
||||
## 概念回顾
|
||||
|
||||
筛选视图是 sheet 内的多份独立筛选配置,每个视图持有自己的 `range` 和 `rules`,由独立 `view_id`(10 位随机字符串)标识。一个 sheet 可有多个视图,视图的隐藏行仅在用户进入该视图时本地生效,不影响其他协作者,也不与该 sheet 上可能并存的筛选器(filter)互相影响。
|
||||
|
||||
`+filter-view-{create|update|delete}` 负责视图本身的 CRUD(create / update / delete);视图的"进入 / 退出"(激活态)是本地状态,不在工具语义内。
|
||||
|
||||
## 使用场景
|
||||
|
||||
读写筛选视图对象。本 reference 覆盖 4 个 shortcut:
|
||||
|
||||
| 操作需求 | 使用工具 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 查看已有筛选视图 | `+filter-view-list` | 获取 sheet 上所有视图(视图名、范围、规则) |
|
||||
| 创建 / 更新 / 删除筛选视图 | `+filter-view-{create|update|delete}` | create / update / delete 三个独立 shortcut |
|
||||
|
||||
典型工作流:先读取现有视图了解配置 → 执行创建 / 更新 / 删除 → **必须再次读取验证结果**。
|
||||
|
||||
**常见配置错误(必须注意)**:
|
||||
- **视图范围必须覆盖表头行**:视图的 range 必须从表头行开始(如 `A1:F100`),不能只包含数据行
|
||||
- **更新前先读取**:用户说"调整这个视图"时,先用 `+filter-view-list` 拉到目标视图当前 rules,**只改差异列**再回写
|
||||
- **多次 create 不能复用 view_id**:复用应走 `update`,重复 `create` 会产生新视图
|
||||
- **筛选不支持正则表达式**:飞书表格筛选器不支持正则表达式,传入正则会当成普通文本处理
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+filter-view-list` | read | 对象 |
|
||||
| `+filter-view-create` | write | 对象 |
|
||||
| `+filter-view-update` | write | 对象 |
|
||||
| `+filter-view-delete` | high-risk-write | 对象 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+filter-view-list`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--view-id` | string | optional | 按筛选视图 reference_id 过滤(命中即只返回单个视图) |
|
||||
|
||||
### `+filter-view-create`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--properties` | string + File + Stdin(复合 JSON) | required | 筛选视图规则 JSON,含 `rules?`(列级筛选规则数组)和 `filtered_columns?`。`range` 和 `view_name` 是独立 flag |
|
||||
| `--range` | string | required | 筛选视图作用的单元格范围(A1 表示法,如 `A1:F1000`);优先级高于 `--properties` 中同名字段;create 必填,必须覆盖表头行 |
|
||||
| `--view-name` | string | optional | 筛选视图名称;不传时系统自动分配;优先级高于 `--properties` 中同名字段 |
|
||||
|
||||
### `+filter-view-update`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--view-id` | string | required | 目标筛选视图 reference_id |
|
||||
| `--properties` | string + File + Stdin(复合 JSON) | required | 筛选视图规则 JSON,含 `rules?` 和 `filtered_columns?`;update 是整组覆盖式(先 `+filter-view-list` 回读再 patch;传空 `rules: []` 清空)。`range` 和 `view_name` 是独立 flag |
|
||||
| `--range` | string | optional | 筛选视图作用的单元格范围(A1 表示法,如 `A1:F1000`);优先级高于 `--properties` 中同名字段;update 时省略表示保留当前 range |
|
||||
| `--view-name` | string | optional | 筛选视图名称;create 不传时系统自动分配,update 不传时保留原名;优先级高于 `--properties` 中同名字段 |
|
||||
|
||||
### `+filter-view-delete`
|
||||
|
||||
_公共四件套 · 系统:`--yes`、`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--view-id` | string | required | 目标筛选视图 reference_id |
|
||||
|
||||
## Schemas
|
||||
|
||||
> 复合 JSON flag 字段速查(只列顶层 + 一层嵌套)。深层结构看下方 `## Examples`,或用 `--print-schema` 读完整 JSON Schema(用法见 SKILL.md「公共 flag 速查」与「Agent 使用提示」)。
|
||||
|
||||
### `+filter-view-create` `--properties` / `+filter-view-update` `--properties`
|
||||
|
||||
_create / update 的视图属性_
|
||||
|
||||
**顶层字段**:
|
||||
- `view_name` (string?) — 可选 — ⚠️ 已拎为独立 flag `--view-name`,请勿在此 JSON 内重复填写(同名以独立 flag 为准)
|
||||
- `range` (string?) — 视图作用的单元格范围(A1 表示法) — ⚠️ 已拎为独立 flag `--range`,请勿在此 JSON 内重复填写(同名以独立 flag 为准)
|
||||
- `rules` (array<object>?) — 列级筛选规则列表,每一项对应一个具体列的筛选条件 each: { column_index: string, conditions: array<oneOf>, filtered_rows?: array<number> }
|
||||
- `filtered_columns` (array<string>?) — 可选
|
||||
|
||||
## Examples
|
||||
|
||||
公共四件套:所有 shortcut 顶部排列 `--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`(XOR)。`view_id` 是 10 位随机字符串,每个 sheet 可有多个视图。
|
||||
|
||||
### `+filter-view-list`
|
||||
|
||||
```bash
|
||||
# 列出某个 sheet 的全部筛选视图
|
||||
lark-cli sheets +filter-view-list --url "..." --sheet-id "$SID"
|
||||
|
||||
# 按 view_id 精确定位
|
||||
lark-cli sheets +filter-view-list --url "..." --sheet-id "$SID" --view-id vAbcde1234
|
||||
```
|
||||
|
||||
### `+filter-view-create`
|
||||
|
||||
`--range`(必填)/ `--view-name`(可选)是独立 flag;`rules` 走 `--properties`:
|
||||
|
||||
```bash
|
||||
lark-cli sheets +filter-view-create --url "..." --sheet-id "$SID" \
|
||||
--view-name "活跃用户" --range "A1:F1000" \
|
||||
--properties '{"rules":[{"column_index":"C","conditions":[{"type":"number","compare_type":"greaterThan","values":[100]}]}]}'
|
||||
```
|
||||
|
||||
**`conditions[].type` × `compare_type` 取值**(`type` 决定可用的 `compare_type`;两者均必填):
|
||||
|
||||
| `type` | 可用 `compare_type` | `values` |
|
||||
|---|---|---|
|
||||
| `text` | `contains` / `doesNotContain` / `beginsWith` / `doesNotBeginWith` / `endsWith` / `doesNotEndWith` / `equals` / `notEquals` | 字符串数组 |
|
||||
| `number` | `equal` / `notEqual` / `greaterThan` / `greaterThanOrEqual` / `lessThan` / `lessThanOrEqual` / `between` / `notBetween` | 数值(或数值字符串)数组;`between` / `notBetween` 传两个边界 |
|
||||
| `multiValue` | `equal` / `notEqual` | 字符串数组(精确匹配其中任一值) |
|
||||
| `color` | `backgroundColor` / `foregroundColor` | 不传 `values`(按单元格颜色筛选) |
|
||||
|
||||
> ⚠️ `text` 用 `equals` / `notEquals`(**带 s**),`number` / `multiValue` 用 `equal` / `notEqual`(**不带 s**)——别混。完整 schema 跑 `+filter-view-create --print-schema --flag-name properties`。
|
||||
|
||||
> `--range` **必须覆盖表头行**(如 `A1:F1000`),不能只包含数据行;`--view-name` 重名时服务端自动改名。
|
||||
|
||||
### `+filter-view-update`
|
||||
|
||||
> ⚠️ update 是整组覆盖(PUT 语义):`--properties` **必传**,未在请求里出现的 rules / filtered_columns 会被清空。如要保留已有 rules,先 `+filter-view-list` 读回再合并写回。`--range` 变更会丢弃已有筛选规则属预期行为(rules 跟当前 range 绑定)。重复 `+filter-view-create` 不会复用 view_id,会产生新视图。
|
||||
|
||||
### `+filter-view-delete`
|
||||
|
||||
> ⚠️ 删除**已存在**的视图不可逆;目标 view_id **不存在**时按幂等成功返回(不报错)。先 `--dry-run` 看 view_id 确认。
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate`:XOR 公共四件套;`+filter-view-create` 校验 `--range` 起始行为表头(第一行);`+filter-view-update` 必须先 `+filter-view-list` 确认 view 存在,`--properties` 必传(整组覆盖式);`+filter-view-delete` 强制 `--yes` 或 `--dry-run`。
|
||||
- `DryRun`:输出"将要 POST/PATCH/DELETE 的 view 请求模板",零网络副作用;`--sheet-name` 在 dry-run 输出里生成为 `<resolve:Sheet1>` 占位符。
|
||||
- `Execute`:写后不自动回读;如需确认,自行调用 `+filter-view-list --view-id <new>` 比对当前 range + rules。
|
||||
130
.claude/skills/lark-sheets/references/lark-sheets-filter.md
Normal file
130
.claude/skills/lark-sheets/references/lark-sheets-filter.md
Normal file
@ -0,0 +1,130 @@
|
||||
# Lark Sheet Filter
|
||||
|
||||
## 真对象硬约束 + 数量校验
|
||||
|
||||
1. **真对象**:当用户要求"筛选 / 只看 / 仅保留 X"时,**必须**通过 `+filter-{create|update|delete}` 创建真实的筛选器对象。**禁止**用"删除不符合条件的行" / "新建子表只放符合条件的行" / 用 `+cells-set` 覆盖原表来代替——这些做法会让原数据丢失或不可恢复。
|
||||
2. **筛选数量必校**:执行筛选后**必须**回读,断言 `len(visible_rows) == expected_count`。`expected_count` 来自先用本地脚本在源数据上独立复现该筛选条件得到的结果数。两者不一致时禁止交付,需排查筛选条件 / 数据列类型问题。
|
||||
3. **混合文本列禁止字面比较**:筛选 key 是公式文本(如 `1000+200=1200`)或带单位的混合文本时,先在辅助列里抽出纯数值再筛选;不能直接用文本比较。
|
||||
|
||||
## 使用场景
|
||||
|
||||
读写筛选器对象。本 reference 覆盖 4 个 shortcut:
|
||||
|
||||
| 操作需求 | 使用工具 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 查看已有筛选器 | `+filter-list` | 获取筛选器的范围、规则和条件配置 |
|
||||
| 创建/更新/删除筛选器 | `+filter-{create|update|delete}` | 对筛选器执行写入操作 |
|
||||
|
||||
典型工作流:先读取现有筛选器了解配置 → 执行创建/更新/删除 → **必须再次读取验证结果**。
|
||||
|
||||
**只读场景例外**:用户只是想知道哪些数据满足条件、并不要求修改表格展示时,可以走 `lark-sheets-read-data` 读后文本回答,不必创建筛选器。
|
||||
|
||||
**常见配置错误(必须注意)**:
|
||||
- **筛选范围必须覆盖表头行**:筛选器的 range 必须从表头行开始(如 `A1:F100`),不能只包含数据行。缺少表头会导致筛选条件无法正确匹配列
|
||||
- **更新已有筛选器前先读取**:如果子表上已存在筛选器,直接创建会报错或覆盖原有配置。应先用 `+filter-list` 查看是否存在筛选器,存在时使用 update 而非 create
|
||||
- **筛选条件的列索引要精确**:筛选条件中的列标识必须与实际数据列精确对应,不要凭猜测填写
|
||||
- **”调整筛选逻辑”要先读旧配置**:用户说”调整筛选”时,先读取现有筛选器的完整配置,理解当前规则后再修改,不要从零创建
|
||||
- **创建后必须验证**:调用 `+filter-list` 确认筛选器配置正确且生效
|
||||
- **筛选不支持正则表达式**:飞书表格筛选器不支持正则表达式,传入正则会当成普通文本处理。
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+filter-list` | read | 对象 |
|
||||
| `+filter-create` | write | 对象 |
|
||||
| `+filter-update` | write | 对象 |
|
||||
| `+filter-delete` | high-risk-write | 对象 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+filter-list`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
_仅含公共 / 系统 flag。_
|
||||
|
||||
### `+filter-create`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | 筛选范围(A1 表示法,含表头行,如 `A1:F1000`);不要重复写入 `--properties` 中的 range 字段 |
|
||||
| `--properties` | string + File + Stdin(复合 JSON) | optional | 筛选规则 JSON:`rules`(列级筛选规则数组)+ `filtered_columns?`(激活列索引提示)。`--properties` 整体可选——传它时 `rules` 不可为空;不传则只在 `--range` 上建立空筛选器(无列条件)。`range` 是独立 flag(不要再放此 JSON 里) |
|
||||
|
||||
### `+filter-update`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--properties` | string + File + Stdin(复合 JSON) | required | 筛选规则 JSON,含 `rules` 和 `filtered_columns?`;update 是整组覆盖式(传空 `rules: []` 清空)。`range` 已拎为独立 flag |
|
||||
| `--range` | string | required | 筛选作用的单元格范围(A1 表示法,如 `A1:F1000`);优先级高于 `--properties` 中同名字段 |
|
||||
|
||||
### `+filter-delete`
|
||||
|
||||
_公共四件套 · 系统:`--yes`、`--dry-run`_
|
||||
|
||||
_仅含公共 / 系统 flag。_
|
||||
|
||||
## Schemas
|
||||
|
||||
> 复合 JSON flag 字段速查(只列顶层 + 一层嵌套)。深层结构看下方 `## Examples`,或用 `--print-schema` 读完整 JSON Schema(用法见 SKILL.md「公共 flag 速查」与「Agent 使用提示」)。
|
||||
|
||||
### `+filter-create` `--properties` / `+filter-update` `--properties`
|
||||
|
||||
_创建/更新的筛选器属性_
|
||||
|
||||
**顶层字段**:
|
||||
- `range` (string) — 筛选对象作用的单元格范围(A1 表示法) — ⚠️ 已拎为独立 flag `--range`,请勿在此 JSON 内重复填写(同名以独立 flag 为准)
|
||||
- `rules` (array<object>) — 列级筛选规则列表,每一项对应一个具体列的筛选条件 each: { column_index: string, conditions: array<oneOf>, filtered_rows?: array<number> }
|
||||
- `filtered_columns` (array<string>?) — 可选
|
||||
|
||||
## Examples
|
||||
|
||||
公共四件套:所有 shortcut 顶部排列 `--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`(XOR)。`filter_id` 等同于 `sheet_id`(每个工作表至多一个筛选器)。
|
||||
|
||||
### `+filter-list`
|
||||
|
||||
```bash
|
||||
# 查看当前 sheet 的筛选器配置(filter_id 等于 sheet_id)
|
||||
lark-cli sheets +filter-list --url "..." --sheet-id "$SID"
|
||||
```
|
||||
|
||||
### `+filter-create`
|
||||
|
||||
`--range` 是独立 flag(含表头行);`rules` 走 `--properties`:
|
||||
|
||||
```bash
|
||||
lark-cli sheets +filter-create --url "..." --sheet-id "$SID" \
|
||||
--range "A1:F1000" \
|
||||
--properties '{"rules":[{"column_index":"B","conditions":[{"type":"multiValue","compare_type":"equal","values":["北京","上海"]}]}]}'
|
||||
```
|
||||
|
||||
**`conditions[].type` × `compare_type` 取值**(`type` 决定可用的 `compare_type`;两者均必填):
|
||||
|
||||
| `type` | 可用 `compare_type` | `values` |
|
||||
|---|---|---|
|
||||
| `text` | `contains` / `doesNotContain` / `beginsWith` / `doesNotBeginWith` / `endsWith` / `doesNotEndWith` / `equals` / `notEquals` | 字符串数组 |
|
||||
| `number` | `equal` / `notEqual` / `greaterThan` / `greaterThanOrEqual` / `lessThan` / `lessThanOrEqual` / `between` / `notBetween` | 数值(或数值字符串)数组;`between` / `notBetween` 传两个边界 |
|
||||
| `multiValue` | `equal` / `notEqual` | 字符串数组(精确匹配其中任一值) |
|
||||
| `color` | `backgroundColor` / `foregroundColor` | 不传 `values`(按单元格颜色筛选) |
|
||||
|
||||
> ⚠️ `text` 用 `equals` / `notEquals`(**带 s**),`number` / `multiValue` 用 `equal` / `notEqual`(**不带 s**)——别混。完整 schema 跑 `+filter-create --print-schema --flag-name properties`。
|
||||
|
||||
### `+filter-update`
|
||||
|
||||
> ⚠️ update 是覆盖式:`--properties` 中传新 `rules` 会替换旧组。如只想加一条,要带上已有的全部条件再追加。必填 `--range`。
|
||||
|
||||
### `+filter-delete`
|
||||
|
||||
```bash
|
||||
lark-cli sheets +filter-delete --url "..." --sheet-id "$SID" --yes
|
||||
```
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate`:XOR 公共四件套;`+filter-create` 校验 `--range` 至少 2 行(表头 + 至少 1 行数据);`+filter-update` 必须先 `+filter-list` 确认目标存在;`+filter-delete` 强制 `--yes` 或 `--dry-run`。
|
||||
- `DryRun`:输出"将要 POST/PATCH/DELETE 的 filter 请求模板"。
|
||||
- `Execute`:写后不自动回读;如需确认,自行调用 `+filter-list` 查看当前筛选条件 + 已过滤行数。
|
||||
159
.claude/skills/lark-sheets/references/lark-sheets-float-image.md
Normal file
159
.claude/skills/lark-sheets/references/lark-sheets-float-image.md
Normal file
@ -0,0 +1,159 @@
|
||||
# Lark Sheet Float Image
|
||||
|
||||
> **选浮动图还是单元格图?只看一条**:这张图是不是**属于某条记录、要随那行一起排序 / 筛选 / 增删**?
|
||||
> - **是 → 单元格图片**(不在本 reference):嵌进单元格、随行走。用 `+cells-set-image`(或 `+cells-set` 的 `rich_text` + `type: "embed-image"`,见 lark-sheets-write-cells)。典型:凭证 / 证件照 / 商品图 / 头像 / 二维码 / 每行配图;话里带「对应 / 每行 / 每条 / 这列」等绑定词即属此类。
|
||||
> - **否 → 浮动图片**(本 reference):自由摆放、不绑数据的装饰 / 标识(logo / 水印 / 封面大图 / banner)。
|
||||
> - ⚠️ 别凭"浮动图位置尺寸更好控制 / 更熟"就选它——那是按操作便利选,不是按场景选;用浮动图承载"对应某记录"的图会在增删行 / 排序后错位。
|
||||
|
||||
## 真对象硬约束
|
||||
|
||||
当用户要求"插入图片 / 添加 logo / 放一张图"时,**必须**通过 `+float-image-{create|update|delete}`(浮动图片)或 `+cells-set-image` / `+cells-set` 的 `embed-image`(单元格图片)创建真实的图片对象。**禁止**只在文本回复中给出图片链接 / 描述图片内容代替插入。判断标准:交付后 `+float-image-list` 或单元格 `rich_text` 必须能读到该图片对象。
|
||||
|
||||
## 使用场景
|
||||
|
||||
读写**浮动图片**对象(悬浮在单元格上方的图片,不属于单元格内容)。本 reference 覆盖 4 个 shortcut:
|
||||
|
||||
| 操作需求 | 使用工具 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 查看已有浮动图片 | `+float-image-list` | 获取浮动图片的位置、大小和层级配置 |
|
||||
| 创建/更新/删除浮动图片 | `+float-image-{create|update|delete}` | 对浮动图片执行写入操作 |
|
||||
|
||||
典型工作流:先读取现有浮动图片了解配置 → 执行创建/更新/删除 → **必须再次读取验证结果**。
|
||||
|
||||
**常见配置错误(必须注意)**:
|
||||
- **单元格图片 vs 浮动图片选择错误(最易选错)**:图与某条记录一一对应、要随行排序 / 筛选 / 增删时,应走 `+cells-set-image`(见顶部判别),用浮动图会错位。
|
||||
- **图片位置参数要精确**:锚点单元格的行列索引和偏移量决定了图片位置,设置不当会导致图片遮挡数据
|
||||
- **创建后必须验证**:调用 `+float-image-list` 确认图片位置和大小正确
|
||||
|
||||
图片来源有三种方式,`+float-image-create` 上三者 **XOR、必给其一**(`--image` / `--image-token` / `--image-uri`):
|
||||
|
||||
- **`--image <本地路径>`(首选,最省事)**:直接给本地图片文件路径(PNG/JPEG/GIF/BMP/HEIC 等)。CLI 会自动把它以 `parent_type=sheet_image` 上传,拿到 file_token 后创建浮动图,**不用你手动上传 / 取 token**。路径规则同其它本地文件 flag:必须是当前工作目录内的相对路径(绝对路径会被 Validate 拒,`--dry-run` 也会拦)。
|
||||
- `--image-token`:复用**已存在**的图片 file_token。常见来源:① `+float-image-list` 返回的 `image_token`(适合"换皮不换位置"复用同一张图);② `+cells-set-image` 成功返回里的 `file_token`(它也是 `sheet_image` 上传句柄)。适合"同一张图复用到多处",省去重复上传。
|
||||
- `--image-uri`:图片 URI(上传链路返回的句柄),**非**表内对象 reference_id;由系统自动转 file_token。
|
||||
|
||||
> ⚠️ **`--image` 仅 `+float-image-create` 支持**。`+float-image-update` 换图仍只接受 `--image-token` / `--image-uri`,而且**图片源是 update 唯一可省的部分**——三者全不传则保留原图。但 `--image-name` / `--position-{row,col}` / `--size-{width,height}` 在 update 时和 create 一样**必填**(`+float-image-update` 强制要求这套核心字段,且 `+float-image-list` 不回传 `image_name` 供 CLI 回填)。要在 update 里换一张本地新图,先用 `+cells-set-image` 上传到任意临时单元格、从返回取 `file_token`,再把它传给 update 的 `--image-token`;用完清除该临时单元格,避免残留多余图片。
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+float-image-list` | read | 对象 |
|
||||
| `+float-image-create` | write | 对象 |
|
||||
| `+float-image-update` | write | 对象 |
|
||||
| `+float-image-delete` | high-risk-write | 对象 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+float-image-list`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--float-image-id` | string | optional | 按 id 过滤;省略时列工作表全部 |
|
||||
|
||||
### `+float-image-create`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--image-name` | string | required | 图片名称,含扩展名(如 `logo.png`) |
|
||||
| `--image-token` | string | xor | 图片 file_token(与 `--image-uri` 二选一)。常见来源:`+float-image-list` 返回的 `image_token` |
|
||||
| `--image-uri` | string | xor | 图片 URI(上传链路返回的句柄,非表内对象 reference_id;与 `--image-token` 二选一);系统自动转换为 file_token |
|
||||
| `--position-row` | int | required | 图片左上角所在行(0-based) |
|
||||
| `--position-col` | string | required | 图片左上角所在列(列字母,如 `A` / `B`) |
|
||||
| `--size-width` | int | required | 图片宽度(像素) |
|
||||
| `--size-height` | int | required | 图片高度(像素) |
|
||||
| `--offset-row` | int | optional | 在 `--position-row` 基础上的行内偏移(像素) |
|
||||
| `--offset-col` | int | optional | 在 `--position-col` 基础上的列内偏移(像素) |
|
||||
| `--z-index` | int | optional | 图片 Z 轴层级,控制重叠顺序 |
|
||||
| `--image` | string | xor | 本地图片路径(PNG/JPEG 等);CLI 自动上传为 sheet_image 并用返回的 file_token,省去手动拿 token(与 --image-token / --image-uri 三选一) |
|
||||
|
||||
### `+float-image-update`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--float-image-id` | string | required | 目标图片 id |
|
||||
| `--image-name` | string | required | 图片名称,含扩展名(如 `logo.png`) |
|
||||
| `--image-token` | string | optional | 可选图片 file_token;与 `--image-uri` 互斥,二者均省略时保留原图。常见来源:`+float-image-list` 返回的 `image_token` |
|
||||
| `--image-uri` | string | optional | 可选图片 URI(上传链路返回的句柄,非表内对象 reference_id);与 `--image-token` 互斥,二者均省略时保留原图;系统自动转换为 file_token |
|
||||
| `--position-row` | int | required | 图片左上角所在行(0-based) |
|
||||
| `--position-col` | string | required | 图片左上角所在列(列字母,如 `A` / `B`) |
|
||||
| `--size-width` | int | required | 图片宽度(像素) |
|
||||
| `--size-height` | int | required | 图片高度(像素) |
|
||||
| `--offset-row` | int | optional | 在 `--position-row` 基础上的行内偏移(像素) |
|
||||
| `--offset-col` | int | optional | 在 `--position-col` 基础上的列内偏移(像素) |
|
||||
| `--z-index` | int | optional | 图片 Z 轴层级,控制重叠顺序 |
|
||||
|
||||
### `+float-image-delete`
|
||||
|
||||
_公共四件套 · 系统:`--yes`、`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--float-image-id` | string | required | 目标图片 id |
|
||||
|
||||
## Examples
|
||||
|
||||
公共四件套:所有 shortcut 顶部排列 `--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`(XOR)。浮动图片是 sheet 级对象——和单元格内嵌图片不同(后者走 `+cells-set`)。
|
||||
|
||||
### `+float-image-list`
|
||||
|
||||
```bash
|
||||
lark-cli sheets +float-image-list --url "..." --sheet-id "$SID"
|
||||
```
|
||||
|
||||
### `+float-image-create`
|
||||
|
||||
所有字段拍平为独立 flag:图片来源 `--image` / `--image-token` / `--image-uri`(三选一 XOR)/ `--image-name` / `--position-{row,col}` / `--size-{width,height}` / `--offset-{row,col}` / `--z-index`。
|
||||
|
||||
```bash
|
||||
# 首选:直接给本地图片路径,CLI 自动上传(无需手动拿 token)
|
||||
# 注意:--image-name 是 required(即使路径 basename 已经是 logo.png 也要显式传)
|
||||
lark-cli sheets +float-image-create --url "..." --sheet-id "$SID" \
|
||||
--image ./logo.png --image-name "logo.png" \
|
||||
--position-row 2 --position-col B --size-width 300 --size-height 200 --z-index 1
|
||||
|
||||
# 用已有 file_token(从 +float-image-list 的 image_token 或 +cells-set-image 返回的 file_token)
|
||||
lark-cli sheets +float-image-create --url "..." --sheet-id "$SID" \
|
||||
--image-name "logo.png" --image-token "$TOKEN" \
|
||||
--position-row 0 --position-col A --size-width 200 --size-height 150
|
||||
|
||||
# 用 image URI(上传链路返回的句柄,非表内对象 reference_id;与 --image-token 二选一)
|
||||
lark-cli sheets +float-image-create --url "..." --sheet-id "$SID" \
|
||||
--image-name "logo.png" --image-uri "$IMAGE_URI" \
|
||||
--position-row 2 --position-col B --size-width 300 --size-height 200 --z-index 1
|
||||
```
|
||||
|
||||
### `+float-image-update`
|
||||
|
||||
> **update ≈ create,只有图片源可省**:`+float-image-update` 的 update 要求和 create 相同的核心字段——`--image-name`、`--position-{row,col}`、`--size-{width,height}` **全部必填**;唯一区别是**图片源(`--image-token` / `--image-uri`)可以全省**,省略即保留原图。这**不是**"只发改动字段"的 patch:缺任一核心字段会被拒绝(`+float-image-list` 不回传 `image_name`,CLI 无法替你回填)。
|
||||
>
|
||||
> 推荐流程:先 `+float-image-list --float-image-id <id>` 回读当前 position / size,再带上 `--image-name` 和完整的 position / size 调一次 `+float-image-update`。
|
||||
|
||||
```bash
|
||||
# 调整位置 + 尺寸,保留原图(不传图片源)
|
||||
lark-cli sheets +float-image-update --url "..." --sheet-id "$SID" \
|
||||
--float-image-id "$IMG_ID" --image-name "logo.png" \
|
||||
--position-row 5 --position-col C --size-width 300 --size-height 200
|
||||
|
||||
# 换图:额外带 --image-token,核心字段同样要给全
|
||||
lark-cli sheets +float-image-update --url "..." --sheet-id "$SID" \
|
||||
--float-image-id "$IMG_ID" --image-name "new-logo.png" --image-token "$NEW_TOKEN" \
|
||||
--position-row 5 --position-col C --size-width 300 --size-height 200
|
||||
```
|
||||
|
||||
### `+float-image-delete`
|
||||
|
||||
```bash
|
||||
lark-cli sheets +float-image-delete --url "..." --sheet-id "$SID" --float-image-id "$IMG_ID" --yes
|
||||
```
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate`:XOR 公共四件套;`+float-image-create` 要求 `--image` / `--image-token` / `--image-uri` **恰好给一个**,`--position-row/col` 与 `--size-width/height` 必填且为合法整数;传 `--image` 时还会校验路径安全(绝对路径 / 越出工作目录会被拒,`--dry-run` 同样拦)。`+float-image-update` 必须 `--float-image-id`,并和 create 一样必填 `--image-name` / `--position-{row,col}` / `--size-{width,height}`(缺任一核心字段本地直接报错,不会静默发 0);图片源 `--image-token` / `--image-uri` 可省(省略保留原图),给则二选一;`+float-image-delete` 强制 `--yes` 或 `--dry-run`。
|
||||
- `DryRun`:写操作输出"将要 POST/PATCH/DELETE 的 float_image 请求模板";传 `--image` 时会多打印一步本地图片上传(`POST /open-apis/drive/v1/medias/upload_all`,`parent_type=sheet_image`)。
|
||||
- `Execute`:写后不自动回读;如需确认,自行调用 `+float-image-list --float-image-id <id>` 比对新位置 / 尺寸。
|
||||
@ -0,0 +1,276 @@
|
||||
# 飞书表格公式生成规则
|
||||
|
||||
> **本文定位**:飞书公式正确性的**唯一权威**——书写任何飞书公式、或把 Excel 公式迁移到飞书前,先读本文。涵盖公式书写约定(绝对引用、范围语法)、投影 vs spill、ARRAYFORMULA / 数组语义、高风险引用函数、日期差、不支持函数清单。
|
||||
> **边界**:本文只讲"公式怎么写对";公式**怎么写入表格**(`+cells-set` / 模板单元格 + `--copy-to-range` / 容错回读)见 `lark-sheets-write-cells`。**公式写入完成后的强制收尾**见 `lark-sheets-formula-verify`:不要把"翻译对了"误当成"已经交付完成"。本文不含 shortcut,通用编辑准则见主 SKILL.md「飞书表格编辑准则」。
|
||||
|
||||
**核心原则:飞书不像 Excel 365 那样默认 spill(溢出展开)。飞书普通公式遇到区域时默认"投影"(只取当前行/列对应的单个值),必须显式使用 `ARRAYFORMULA` 或原生数组函数才能逐项展开。**
|
||||
|
||||
## 公式书写约定(写任何公式都先满足)
|
||||
|
||||
- **绝对引用 `$`**:向下 / 向右填充前判断哪些引用要锁定——用户指定的固定 cell(`$C$3`)、要固定的数据范围(`$A$2:$B$5`)、锁列不锁行(`$A2`)、锁行不锁列(`B$1`)。填充前检查是否需固定汇率 / 税率 / 查找表 / 权重表,以及同列 / 同行公式结构是否一致。
|
||||
- **公式字符串用飞书范围语法**:写 `H:H`、`A2:B5`,**禁止** `H2:H` / `2:2`。要在公式里引用整行,用显式范围(如 `$A2:$Z2`)替代禁用的 `2:2`。这与 CLI 工具参数(如 `--range` / `--copy-to-range`)的 A1 表示法写法不同:参数侧合法的 `D3:D`、`1:1`、`3:6` 在公式串里反而非法。**公式串 ≠ CLI 参数**,两套规则别互相照搬,混用会导致调用失败或公式报错。
|
||||
|
||||
## 翻译后必做:代码复现校验
|
||||
|
||||
公式语法翻译完之后,**必须**用本地脚本在源数据上独立复现一份"等价计算结果"再写入。流程:
|
||||
|
||||
1. **挑 3-5 个代表性输入行**(首行 / 中段 / 末行 / 含空值 / 含异常格式各一)
|
||||
2. **用 Python 复现 Excel 原公式的语义**(不是飞书译文的语义,而是用户原本想要的结果)
|
||||
3. **写入飞书译文公式后回读这几行的实际值**
|
||||
4. **三方对照**:`Excel 原公式语义 == Python 复现 == 飞书译文回读值`,全部一致才交付;不一致先排查(数组语义?日期差?范围引用?)
|
||||
|
||||
**理由**:Excel→飞书的语法翻译很容易在 spill / 数组 / 日期差 / 范围引用上出现等价性偏差,仅靠语法转换通过不足以保证业务结果正确。
|
||||
|
||||
## 落表后的默认交接
|
||||
|
||||
本文解决的是"公式怎么写对",不是"写进表里后一定能零错误运行"。因此:
|
||||
|
||||
1. 按本文完成公式改写后,用 `lark-sheets-write-cells` / `lark-sheets-batch-update` 把公式真实写入表格。
|
||||
2. 公式一旦落表,就默认进入 `lark-sheets-formula-verify` 的收尾阶段。
|
||||
3. 最终必须跑 `+formula-verify` 收敛到 `status='success'`;`errors_found` / `partial` 都不算完成。
|
||||
|
||||
## 决策流程
|
||||
|
||||
1. 最终结果是**标量**(单值)→ 通常不需要 `ARRAYFORMULA`
|
||||
2. 最终结果是**一维或二维数组**:
|
||||
- 公式中**包含**飞书原生数组函数(如 FILTER、XLOOKUP、MAP 等)→ 无需加 `ARRAYFORMULA`,数组语义会自动传播到整个公式,包括原生数组函数外层接的标量运算(如 `+1`、`*100`)
|
||||
- 公式中**不包含**任何原生数组函数,但在对区域做标量计算 → 加 `ARRAYFORMULA(<整个表达式>)`
|
||||
3. Excel 依赖 `ROW(range)` 逐项驱动 `SUBTOTAL/INDIRECT/OFFSET` → 改用 `MAP(ARRAYFORMULA(ROW(...)), LAMBDA(r, ...))`
|
||||
4. 内层 `INDEX/INDIRECT/OFFSET` 返回范围,外层 `SUMIF/COUNTIF/SUMIFS` 还要继续吃这些范围 → 改用 `MAP(..., LAMBDA(...))` 或 `REDUCE(..., LAMBDA(...))`
|
||||
5. 公式意图是"对多个区域分别计算再汇总"(例如用 INDIRECT/OFFSET 对每行生成一个范围,再对所有范围聚合)→ 飞书不能直接返回"区域的列表",必须明确降维:用 `VSTACK` 垂直合并、`HSTACK` 水平合并、`TOCOL/TOROW` 展平,或 `REDUCE` 归约成标量
|
||||
6. 算日期差 → 不要写 `DAY(end-start)`,用 `DAYS`、`DATEDIF` 或直接 `end-start`
|
||||
|
||||
## 飞书的投影行为(不是默认 spill)
|
||||
|
||||
飞书普通公式对引用区域默认"投影"而不是"spill":
|
||||
|
||||
- 单列区域 → 按当前公式所在行取值
|
||||
- 单行区域 → 按当前公式所在列取值
|
||||
- 二维区域 → 只有当前公式位置能映射到该区域时才取值,否则报错
|
||||
- 数组常量 `{...}` 或函数返回矩阵,在普通标量上下文里通常只取左上角
|
||||
|
||||
因此:
|
||||
- `=A1:A2` 在飞书普通公式里不会 spill,只会投影到当前行
|
||||
- `=ABS(A2:B2)` 不会得到一整行,要写 `=ARRAYFORMULA(ABS(A2:B2))`
|
||||
- `=TRUNC({1.1111,2.222},{1,2})` 要得到一整行,写 `=ARRAYFORMULA(TRUNC({1.1111,2.222},{1,2}))`
|
||||
|
||||
## ARRAYFORMULA 使用规则
|
||||
|
||||
**前提:以下规则适用于公式中没有任何原生数组函数的情况。** 若公式中已有原生数组函数(如 FILTER、XLOOKUP、MAP 等),数组语义会自动传播到整个公式的求值过程,后续标量运算无需额外包 `ARRAYFORMULA`(见下一节)。
|
||||
|
||||
需要加 `ARRAYFORMULA` 的典型场景(公式中无原生数组函数时):
|
||||
|
||||
- 算术运算:`+ - * / ^ %`
|
||||
- 比较运算:`= <> > >= < <=`
|
||||
- 标量数学函数:`ABS ROUND INT TRUNC MOD LOG LN SQRT SIN COS TAN ...`
|
||||
- 文本函数:`LEN LEFT RIGHT MID UPPER LOWER TRIM TEXT VALUE ...`
|
||||
- 日期函数:`YEAR MONTH DAY DATE TIME EDATE EOMONTH ...`
|
||||
- 条件函数:`IF IFS IFERROR IFNA NOT ISNUMBER ISTEXT ISBLANK ...`
|
||||
- 引用函数(高风险):`INDEX OFFSET COLUMN ROW MATCH`
|
||||
|
||||
### 公式中有原生数组函数时,整个公式已进入数组模式
|
||||
|
||||
飞书的数组语义会在整个公式求值过程中累积传播:一旦某个原生数组函数运行,后续所有运算符和函数也会自动逐元素处理,无论它们出现在哪一层。
|
||||
|
||||
因此,以下写法**无需**额外包 `ARRAYFORMULA`:
|
||||
|
||||
- `=FILTER(A2:A10,B2:B10="x")+1` ✓
|
||||
- `=XLOOKUP(E2:E10,A2:A10,B2:B10)*100` ✓
|
||||
- `=ABS(FILTER(A2:A10,B2:B10>0))` ✓
|
||||
- `=MAP(A2:A10,LAMBDA(x,x*2))-1` ✓
|
||||
|
||||
对比:**没有原生数组函数**时必须加:
|
||||
|
||||
- `=A2:A100*B2:B100` → `=ARRAYFORMULA(A2:A100*B2:B100)` ✓
|
||||
- `=IF(A2:A100>0,B2:B100,"")` → `=ARRAYFORMULA(IF(A2:A100>0,B2:B100,""))` ✓
|
||||
|
||||
## 飞书原生数组函数清单
|
||||
|
||||
以下函数按数组语义工作,通常**不需要额外包 `ARRAYFORMULA`**:
|
||||
|
||||
`ARRAYFORMULA` `ARRAY_CONSTRAIN` `BYCOL` `BYROW` `CELL` `CHOOSECOLS` `CHOOSEROWS` `DROP` `EXPAND` `FILTER` `FLATTEN` `FREQUENCY` `GROWTH` `HSTACK` `IMPORTDATA` `IMPORTFEED` `IMPORTHTML` `IMPORTRANGE` `IMPORTXML` `LINEST` `LOGEST` `LOOKUP` `MAKEARRAY` `MAP` `MINVERSE` `MMULT` `MUNIT` `QUERY` `RANDARRAY` `REDUCE` `REGEXEXTRACT` `SCAN` `SEQUENCE` `SORT` `SORTBY` `SORTN` `SPLIT` `SUMPRODUCT` `SWITCH` `TAKE` `TEXTSPLIT` `TOCOL` `TOROW` `TRANSPOSE` `TREND` `UNIQUE` `VSTACK` `WRAPCOLS` `WRAPROWS` `XLOOKUP`
|
||||
|
||||
> **注意:`SWITCH` 在飞书里被当作原生数组函数处理,这与 Excel 行为不同,不需要额外包 `ARRAYFORMULA`。**
|
||||
|
||||
## IMPORTRANGE 跨工作簿引用限制
|
||||
|
||||
用 `IMPORTRANGE` 跨电子表格引用数据时有两条硬上限:
|
||||
|
||||
- **嵌套最多 5 层**:被引用的表里若又用 `IMPORTRANGE` 继续引下一张表,整条引用链最多 5 层。
|
||||
- **每个工作表最多 100 个 `IMPORTRANGE` 引用**。
|
||||
|
||||
超限会让引用失效或报错。设计大量跨表汇总前先估算引用数,必要时先把数据落地到本表再计算。
|
||||
|
||||
## INDEX / OFFSET / COLUMN / ROW / MATCH 是高风险函数
|
||||
|
||||
这组函数容易让人误以为会自动把多值铺开,但在飞书里不能这样假设。
|
||||
|
||||
**高风险信号:**
|
||||
|
||||
- 行号 / 列号 / 偏移量本身是数组
|
||||
- 结果本来应该是一行或一块二维区域
|
||||
- 外层还有算术、比较、`IF` 等继续处理它
|
||||
|
||||
更稳的写法:
|
||||
|
||||
- `=ARRAYFORMULA(INDEX(...))`
|
||||
- `=ARRAYFORMULA(OFFSET(...))`
|
||||
- `=ARRAYFORMULA(COLUMN(...))`
|
||||
- `=ARRAYFORMULA(ROW(...))`
|
||||
|
||||
**例外:** 如果返回值只是立刻交给聚合函数消费,不需要额外包:
|
||||
|
||||
- `=SUM(INDEX(A1:B2,0,1))` ✓
|
||||
|
||||
## Excel 隐式逐项求值,飞书里要显式写 MAP
|
||||
|
||||
**典型特征:**
|
||||
|
||||
- 外层是 `SUMPRODUCT`、`SUM` 等聚合
|
||||
- 内层用了 `SUBTOTAL`、`INDIRECT`、`OFFSET` 等更偏"单值/单引用"的函数
|
||||
- Excel 会把中间结果逐项带进去算
|
||||
- 飞书里直接照抄,往往不能得到同样的逐项语义
|
||||
|
||||
同类本质也包括:`INDEX/INDIRECT/OFFSET` 先返回范围,外层再把这些范围交给 `SUMIF`、`COUNTIF`、`AVERAGEIF`、`SUMIFS` 等范围感知函数 —— 飞书里这些外层函数不会自动二次展开内层范围。
|
||||
|
||||
这时不要只会补 `ARRAYFORMULA`,要显式写"遍历"。最常用模板:
|
||||
|
||||
```excel
|
||||
=SUMPRODUCT(
|
||||
MAP(
|
||||
ARRAYFORMULA(ROW(目标范围)),
|
||||
LAMBDA(r, 单行计算逻辑)
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
同类场景也优先考虑 `MAP`:
|
||||
|
||||
- `INDIRECT("A"&ROW(...))`
|
||||
- `OFFSET(...,ROW(...)-ROW(...),...)`
|
||||
- `SUBTOTAL(...)`
|
||||
- `SUMIF(内层返回范围, ...)`
|
||||
- `COUNTIF(内层返回范围, ...)`
|
||||
- `SUMIFS(内层返回范围, ...)`
|
||||
- 任何"希望对每一行 / 每一列各算一次"的模式
|
||||
|
||||
## 多层范围结果与三维以上结果
|
||||
|
||||
飞书公式结果只能是二维区域,不能是"数组的数组"。
|
||||
|
||||
### 多层范围不能自动二次展开
|
||||
|
||||
内层 `INDEX/INDIRECT/OFFSET` 返回的是二维范围,外层还想继续对这些范围做范围计算时,不要假设飞书会"再展开一层"。改用:
|
||||
|
||||
- `MAP(..., LAMBDA(...))` 显式逐项算
|
||||
- `REDUCE(..., LAMBDA(...))` 显式累加/归约
|
||||
|
||||
### 真正的三维或更高维结果不能直接返回
|
||||
|
||||
典型触发场景:想把多个不同区域或不同条件的结果合并展示,例如:
|
||||
- 对 A 列、B 列、C 列分别做 FILTER,想把三列结果并排展示
|
||||
- 对多个月份分别生成数据行,想把所有月份上下堆叠展示
|
||||
|
||||
飞书无法直接返回"多个区域的集合",必须先决定降维方式:
|
||||
|
||||
- 上下堆叠:`=VSTACK(slice1, slice2, slice3)`
|
||||
- 左右拼接:`=HSTACK(slice1, slice2, slice3)`
|
||||
- 压成单列:`=TOCOL(...)`
|
||||
- 压成单行:`=TOROW(...)`
|
||||
- 只保留聚合值:`=REDUCE(slice1, {slice2,slice3}, LAMBDA(acc,x,acc+x))`
|
||||
|
||||
不要替用户"偷定"第三维展示方式;如果用户没有明确说明怎么展示,至少先把结果改写成可见的二维形状。
|
||||
|
||||
## 不能机械照抄的 Excel 语法
|
||||
|
||||
### `@` 隐式交叉
|
||||
|
||||
Excel:`=@A1:A10`(强制单值,取当前行对应的值)
|
||||
|
||||
飞书没有 `@` 运算符。飞书普通公式对引用区域默认就有投影语义,去掉 `@` 即可:
|
||||
|
||||
- Excel: `=@A1:A10`
|
||||
- 飞书: `=A1:A10`
|
||||
|
||||
### `#` spill range
|
||||
|
||||
Excel:`=A1#`(引用 A1 公式溢出的整片区域)
|
||||
|
||||
飞书没有此语法,迁移方式:
|
||||
|
||||
- spill 区域已知 → 改成明确范围
|
||||
- spill 区域未知 → 回到源公式重写,或用 `TAKE` / `DROP` / `ARRAY_CONSTRAIN`
|
||||
|
||||
### 结构化引用
|
||||
|
||||
Excel:`=SUM(Table1[Amount])`
|
||||
|
||||
飞书不支持结构化引用,改成显式 A1 区域:`=SUM(A2:A100)`
|
||||
|
||||
### 老式 CSE 花括号
|
||||
|
||||
Excel:`{=A1:A10*B1:B10}`(Ctrl+Shift+Enter 输入)
|
||||
|
||||
飞书改为:`=ARRAYFORMULA(A1:A10*B1:B10)`
|
||||
|
||||
## 日期序列与日期差
|
||||
|
||||
飞书日期序列:`0 = 1899-12-30`,`1 = 1899-12-31`,没有 Excel 的 1900 年闰年兼容问题。
|
||||
|
||||
**错误写法(不要用):**
|
||||
|
||||
- `=DAY(B2-A2)` ✗ — 差值会被当成日期序列号再拆字段
|
||||
- `=MONTH(B2-A2)` ✗
|
||||
- `=YEAR(B2-A2)` ✗
|
||||
|
||||
**正确写法:**
|
||||
|
||||
- 天数差:`=DAYS(B2,A2)` 或 `=DATEDIF(A2,B2,"D")` 或 `=B2-A2`
|
||||
- 月份差:`=DATEDIF(A2,B2,"M")`
|
||||
- 年份差:`=DATEDIF(A2,B2,"Y")`
|
||||
- 工作日差:`=NETWORKDAYS(A2,B2)`
|
||||
|
||||
## 飞书不支持的函数
|
||||
|
||||
> 本段是"飞书不支持函数"的**唯一权威清单**。以下函数在飞书里不存在或被禁用,禁止主动使用;用户明确要求时应拒绝并提供替代方案:
|
||||
|
||||
- `STOCKHISTORY` — 实时股票数据,飞书无等价函数,需手动导入数据
|
||||
- `WEBSERVICE` — 外部 HTTP 请求,飞书无等价函数
|
||||
- CUBE 系列(`CUBEVALUE`、`CUBEMEMBER`、`CUBESET`、`CUBERANK` 等)— OLAP cube 函数,飞书不支持
|
||||
- `GOOGLEFINANCE`、`GOOGLETRANSLATE` 等 Google 特有函数 — 无等价函数
|
||||
- `FORECAST.ETS` 系列(`FORECAST.ETS`、`FORECAST.ETS.STAT` 等)— 飞书不支持
|
||||
- `INFO`、`RTD` — 系统信息 / 实时数据函数,飞书不支持
|
||||
- `PIVOT` — 用 `+pivot-{create|update|delete}` 透视表对象替代
|
||||
- `AMORDEGRC`、`PHONETIC`、`DETECTLANGUAGE` — 飞书不支持
|
||||
- `LET`、命名自定义函数(名称管理器里定义的 LAMBDA)、独立调用的 `LAMBDA`(如 `=LAMBDA(x,x+1)(5)`)— 会报 `#NAME?`;改用嵌套 IF / 辅助列。**例外**:`LAMBDA` 作为 `MAP` / `REDUCE` / `BYROW` / `BYCOL` / `SCAN` / `MAKEARRAY` 的内联参数时**支持**(见上方「飞书原生数组函数清单」)
|
||||
|
||||
## 代表性改写示例
|
||||
|
||||
- 基础逐项计算
|
||||
- Excel: `=A2:A100*B2:B100`
|
||||
- 飞书: `=ARRAYFORMULA(A2:A100*B2:B100)`
|
||||
- 条件判断
|
||||
- Excel: `=IF(A2:A100>0,B2:B100,"")`
|
||||
- 飞书: `=ARRAYFORMULA(IF(A2:A100>0,B2:B100,""))`
|
||||
- 原生数组函数(无需改动)
|
||||
- Excel: `=FILTER(A2:C100,B2:B100="East")`
|
||||
- 飞书: `=FILTER(A2:C100,B2:B100="East")`
|
||||
- 原生数组函数 + 标量运算(无需改动,数组语义自动传播)
|
||||
- Excel: `=XLOOKUP(E2:E10,A2:A10,B2:B10)*100`
|
||||
- 飞书: `=XLOOKUP(E2:E10,A2:A10,B2:B10)*100`
|
||||
- 高风险引用函数
|
||||
- Excel: `=INDEX(A1:D2,{2,1},0)`
|
||||
- 飞书: `=ARRAYFORMULA(INDEX(A1:D2,{2,1},0))`
|
||||
- 日期差
|
||||
- 错误: `=DAY(B2-A2)`
|
||||
- 推荐: `=DAYS(B2,A2)` 或 `=DATEDIF(A2,B2,"D")` 或 `=B2-A2`
|
||||
- Excel 隐式逐项求值
|
||||
- Excel: `=SUMPRODUCT(SUBTOTAL(103,INDIRECT("E"&ROW($E$16:$E$387))))`
|
||||
- 飞书: `=SUMPRODUCT(MAP(ARRAYFORMULA(ROW($E$16:$E$387)),LAMBDA(row,SUBTOTAL(103,INDIRECT("E"&row)))))`
|
||||
- 多层范围 / 二次展开
|
||||
- 错误思路: `=SUMIF(INDIRECT("E"&ROW($E$16:$E$387)),">0")`
|
||||
- 飞书: `=MAP(ARRAYFORMULA(ROW($E$16:$E$387)),LAMBDA(r,SUMIF(INDIRECT("E"&r),">0")))`
|
||||
- 三维降二维(保留所有层)
|
||||
- 飞书: `=VSTACK(slice1,slice2,slice3)` 或 `=HSTACK(slice1,slice2,slice3)`
|
||||
- 三维降二维(只保留聚合值)
|
||||
- 飞书: `=REDUCE(slice1,{slice2,slice3},LAMBDA(acc,x,acc+x))`
|
||||
@ -0,0 +1,77 @@
|
||||
# Lark Sheet Formula Verify(+formula-verify)
|
||||
|
||||
> **本文定位**:飞书表格"公式写入后是否真的零错误"的自检入口,也是所有写公式任务的**强制收尾步骤**。公式的书写规则与 Excel→飞书迁移的语义规则一律以 `lark-sheets-formula-translation` 为唯一权威,本文不重复;本文聚焦"写完了之后怎么用一次调用确认 zero-error"。
|
||||
>
|
||||
> **边界**:本文不讲公式怎么写(去 `lark-sheets-formula-translation`),也不讲公式怎么写入表格(去 `lark-sheets-write-cells` / `lark-sheets-batch-update`)。本文只讲一件事:**只要任务里发生了公式落表、批量填充公式、`--copy-to-range` 扩展公式、导入含公式 workbook,收尾就必须用 `+formula-verify` 自检到 zero-error 才能交付**。
|
||||
|
||||
## 为什么需要自检
|
||||
|
||||
飞书在线表格已经实时算好结果,但"算出来"和"算对了"是两件事。常见缺口:
|
||||
|
||||
- 公式编译失败 → 单元格落成文本(写入类 shortcut 返回的 `formula_errors[]` 是**编译失败**信号)。
|
||||
- 公式编译成功但**运行时错误**:`#REF!` / `#DIV/0!` / `#VALUE!` / `#NAME?` / `#NULL!` / `#NUM!` / `#N/A`——这一类只看 `formula_errors[]` 看不到,必须扫单元格值。
|
||||
|
||||
`+formula-verify` 把两路信号合并成一份统一 JSON:一次调用聚合全表错误清单 + 编译失败清单 + 每类错误的定位与样本,AI 一眼就能定位修复,链路也能据 `status` 强制收敛到 `success`。
|
||||
|
||||
## 调用契约
|
||||
|
||||
最小调用形态:
|
||||
|
||||
| 入参 | 含义 |
|
||||
|---|---|
|
||||
| `--url` / `--spreadsheet-token` | 表格定位(XOR 二选一,必填) |
|
||||
| `--sheet-id` / `--sheet-name` | 限定子表(mutually exclusive;省略则扫全部可见子表) |
|
||||
| `--range` | 限定 A1 范围;省略则用各 sheet 的 `current_region` |
|
||||
| `--max-locations` | 每类错误样本上限,默认 20 |
|
||||
| `--exit-on-error` | `status='errors_found'` 时返回非 0 退出码(CI 网关用) |
|
||||
|
||||
返回核心字段:
|
||||
|
||||
- `status` ∈ `success` / `errors_found` / `partial`——**唯一可机读的健康度判据**。
|
||||
- `total_errors` / `total_formulas` / `scanned_cells`——本次扫描规模指标。
|
||||
- `has_more`——为 true 表示扫描被内部上限截断(详见后文「截断与续读」),未覆盖完整范围。
|
||||
- `error_summary[<错误类型>]`——每类错误的 `count` / `locations[]` / `samples[].{address,formula,depends_on}`。
|
||||
- `compile_errors[]`——合并最近一次写入留下的编译失败清单,与运行时错误并存时同时出现。
|
||||
- `warning_message`——仅在 `has_more=true` 时出现,告知调用方需要缩小 `--range` / 拆 `--sheet-id` 续读。
|
||||
|
||||
## 写入收尾收敛规则
|
||||
|
||||
任何批量公式 / 含公式列写入完成后调用 `+formula-verify` 直到 `status='success'` 才能交付。不要等用户显式说"校验一下公式"才想到这里;**只要任务动作包含写公式,这一步默认就该做**。触发场景:
|
||||
|
||||
- `+cells-set` / `+csv-put`
|
||||
- `+cells-set --copy-to-range` / 模板单元格向整列或整块扩展公式
|
||||
- `+workbook-import`
|
||||
- `+batch-update` 中含写入子操作
|
||||
- `+table-put`(任意列含公式时)
|
||||
- `+workbook-import`(导入的 xlsx 含公式时)
|
||||
|
||||
收敛规则:
|
||||
|
||||
1. `status='success'` → 通过;可以把链路标完成。
|
||||
2. `status='partial'` → 扫描被内部上限截断。先缩小 `--range` 或拆 `--sheet-id` 续扫,**不允许**把 `partial` 当作 `success`。
|
||||
3. `status='errors_found'` 且 `compile_errors[]` 非空 → **先解决编译失败**:根据 `compile_errors[].reason` 修正公式语法(飞书函数名 / 范围语法 / 引用样式),用 `+cells-set` 重写后再调一次 `+formula-verify`。
|
||||
4. `status='errors_found'` 且只剩运行时错误 → 按 `error_summary` 的 `samples[].formula` + `depends_on` 排查根因(零除?空值参与运算?引用越界?日期差写法?数组语义?),修复后重新自检。
|
||||
5. 同一处错误连续修复 3 次仍未通过 → 改用 `IFERROR` 包裹兜底,或退回纯值写入;不要在 `errors_found` 状态下扩展 `+cells-set --copy-to-range`、追加批量写入。
|
||||
|
||||
注意:
|
||||
|
||||
- 在 `status='errors_found'` 的状态下调用 `+cells-set --copy-to-range` 继续扩展会把错误复制放大。
|
||||
- "编译失败但运行时无报错"不是 zero-error(编译失败的单元格此刻是文本不是公式,源数据一变就再也算不出值)。
|
||||
- 跳过自检直接交付、靠肉眼读首末 5 行确认是不可靠的——表中段、隐藏行、合并区里的错误这样根本看不到。
|
||||
|
||||
## 截断与续读
|
||||
|
||||
后端有一个内部硬上限对总扫描单元格数做截断(不暴露给调用方),超过后立即返回 `has_more=true` + `warning_message`,`error_summary` / `compile_errors` 仅覆盖已扫描部分。处理路径:
|
||||
|
||||
- 把工作簿按 `--sheet-id` / `--sheet-name` 拆成多次调用。
|
||||
- 同 sheet 内按 `--range` 切片(如先 `A1:Z200` 再 `AA1:AZ200`),逐块自检。
|
||||
- 每块都跑到 `has_more=false` 且 `status='success'` 才算通过。
|
||||
|
||||
## 常见陷阱
|
||||
|
||||
| 坑 | 应对 |
|
||||
|---|---|
|
||||
| 错误字符串本地化 | 后端按内部 `error_kind` / `compute_status` 字段识别错误类别,不走字符串匹配;调用方拿到的 7 类英文错误代码由后端统一规范输出,与 locale 无关。 |
|
||||
| `formatted_value` 可能隐藏错误 | 某些条件格式 / 自定义数字格式会把 `#DIV/0!` 显示成空白。后端直接读 cell `error_kind`,不依赖 `formatted_value`,绕开此类被遮蔽。 |
|
||||
| 把 `partial` 当 `success` | `partial` 仅表示**已扫描部分**无错误,剩余区域未知。必须续扫直到 `has_more=false` 且 `status='success'` 才能算通过。 |
|
||||
| 编译失败 vs 运行时错误 | 同一份报告里 `compile_errors[]` 与 `error_summary` 并存。语义层先解决 `compile_errors[]`、再做运行时自检。 |
|
||||
93
.claude/skills/lark-sheets/references/lark-sheets-history.md
Normal file
93
.claude/skills/lark-sheets/references/lark-sheets-history.md
Normal file
@ -0,0 +1,93 @@
|
||||
# Lark Sheet History
|
||||
|
||||
## 概念回顾
|
||||
|
||||
每张飞书电子表格保留一串历史版本(`minor_histories`)。每个版本由 `history_version_id` 标识,并附带创建时间(`create_time`)、动作(`action`)与块修订信息(`all_block_revision`)。历史是**工作簿级**的(针对整张电子表格,不针对单个子表)。
|
||||
|
||||
回滚(revert)把电子表格的当前内容覆盖回某个历史版本——这是一个**高风险写入**操作,且为**异步**:发起后立即返回受理标识,真正的回滚在后台进行,需通过状态查询轮询最终结果(进行中 / 成功 / 失败)。
|
||||
|
||||
`+history-list` 读取版本列表以挑选目标;`+history-revert` 发起回滚;`+history-revert-status` 轮询回滚结果。若只是想拿**当前文档版本号(revision)**当作 recover / undo / `+changeset-get` 的起点锚点,直接用 `+revision-get` 更轻量。
|
||||
|
||||
## 使用场景
|
||||
|
||||
读取历史版本、发起回滚、查询回滚状态。本 reference 覆盖 3 个 shortcut:
|
||||
|
||||
| 操作需求 | 使用工具 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 查看历史版本列表 | `+history-list` | 返回 `minor_histories`,每条含 `history_version_id` / `create_time` / `action` / `all_block_revision` 四个字段;支持向前分页(可选 `--end-version`) |
|
||||
| 回滚到指定历史版本 | `+history-revert` | 传入 `--history-version-id`;异步受理,返回可查询标识 |
|
||||
| 查询回滚状态 | `+history-revert-status` | 传入 `--transaction-id`(取自 `+history-revert` 的异步受理标识);轮询某次回滚的进行中 / 成功 / 失败状态 |
|
||||
|
||||
典型工作流:`+history-list` 拿到目标版本的 `history_version_id`(必要时翻页拉取更早历史)→ `+history-revert` 发起回滚并取回 `transaction_id` → `+history-revert-status --transaction-id <transaction_id>` 轮询直到成功或失败。
|
||||
|
||||
**注意事项(必须了解)**:
|
||||
- **回滚是高风险写入操作**:会用历史版本内容覆盖当前表格,执行前应明确告知用户影响。
|
||||
- **回滚是异步的**:`+history-revert` 返回的是 `transaction_id`(受理标识),不代表回滚已完成;必须用 `+history-revert-status --transaction-id <transaction_id>` 确认最终结果。
|
||||
- **`history_version_id` 与 `transaction_id` 不是同一个**:`history_version_id` 用于 `+history-revert`(取自 `+history-list`);`transaction_id` 用于 `+history-revert-status`(取自 `+history-revert` 的输出)。
|
||||
- **历史是工作簿级**:定位只需 `--url` / `--spreadsheet-token`(XOR),不需要子表选择器。
|
||||
- **`+history-list` 倒序分页**:首次查省略 `--end-version`,返回最新一页;若响应里附带 `next_end_version` 与 `has_more=true`,把 `next_end_version` 作为下一次的 `--end-version` 即可继续向更早翻页;当响应**不包含**这两个字段时表示已到最早一页,不必再翻。
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+history-list` | read | 历史版本 |
|
||||
| `+history-revert` | high-risk-write | 历史版本 |
|
||||
| `+history-revert-status` | read | 历史版本 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+history-list`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--end-version` | int | optional | 分页查询的最大版本(倒序);首次查询省略,下一页传上一页返回的 next_end_version。 |
|
||||
|
||||
### `+history-revert`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--history-version-id` | string | required | 要回滚到的历史版本(取自 +history-list) |
|
||||
|
||||
### `+history-revert-status`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--transaction-id` | string | required | 异步回滚的受理标识(取自 +history-revert) |
|
||||
|
||||
## Examples
|
||||
|
||||
公共定位:所有 shortcut 顶部排列 `--url` / `--spreadsheet-token`(XOR,二选一)。`+history-revert` 用 `--history-version-id`(取自 `+history-list`);`+history-revert-status` 用 `--transaction-id`(取自 `+history-revert` 的异步受理标识)。
|
||||
|
||||
### `+history-list`
|
||||
|
||||
```bash
|
||||
# 列出某张电子表格的最新一页历史版本
|
||||
lark-cli sheets +history-list --url "https://sample.feishu.cn/sheets/SHTxxxxxx"
|
||||
|
||||
# 用原始 spreadsheet token 定位
|
||||
lark-cli sheets +history-list --spreadsheet-token "SHTxxxxxx"
|
||||
|
||||
# 翻到下一页:把上次响应里的 next_end_version 作为 --end-version 传入
|
||||
lark-cli sheets +history-list --url "https://sample.feishu.cn/sheets/SHTxxxxxx" --end-version 12345
|
||||
```
|
||||
|
||||
### `+history-revert`
|
||||
|
||||
```bash
|
||||
# 回滚到指定历史版本(异步受理)
|
||||
lark-cli sheets +history-revert --url "https://sample.feishu.cn/sheets/SHTxxxxxx" --history-version-id "<id-from-history-list>"
|
||||
```
|
||||
|
||||
### `+history-revert-status`
|
||||
|
||||
```bash
|
||||
# 查询某次回滚的当前状态(进行中 / 成功 / 失败)
|
||||
lark-cli sheets +history-revert-status --url "https://sample.feishu.cn/sheets/SHTxxxxxx" --transaction-id "<transaction-id-from-history-revert>"
|
||||
```
|
||||
171
.claude/skills/lark-sheets/references/lark-sheets-pivot-table.md
Normal file
171
.claude/skills/lark-sheets/references/lark-sheets-pivot-table.md
Normal file
@ -0,0 +1,171 @@
|
||||
# Lark Sheet Pivot Table
|
||||
|
||||
## 真对象硬约束
|
||||
|
||||
当用户要求"透视表 / 分组汇总 / 交叉分析 / 按 X 统计 Y"时,**必须**通过 `+pivot-{create|update|delete}` 创建真实的透视表对象。**禁止**用 `SUMIFS` / `COUNTIFS` 等普通公式 + `+cells-set` 在原表中拼一张"看起来像透视表的汇总表"来代替。判断标准:交付后 `+pivot-list` 必须能返回该对象。
|
||||
|
||||
## 使用场景
|
||||
|
||||
读写透视表对象。本 reference 覆盖 4 个 shortcut:
|
||||
|
||||
| 操作需求 | 使用工具 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 查看已有透视表 | `+pivot-list` | 获取透视表的结构、数据源和配置 |
|
||||
| 创建/更新/删除透视表 | `+pivot-{create|update|delete}` | 对透视表执行写入操作 |
|
||||
|
||||
典型工作流:先读取现有透视表了解配置 → 执行创建/更新/删除 → **必须再次读取验证结果**。
|
||||
|
||||
## 行/值字段映射(创建前必做)
|
||||
|
||||
创建透视表前先识别用户需求中的分组维度和聚合指标,**不要搞反**:
|
||||
|
||||
- **rows(行字段)** = 分组维度,即"按什么分组"。例:部门、地区、医生、产品类别
|
||||
- **values(值字段)** = 聚合指标,即"统计什么数值"。例:销售额(聚合方式 `sum`)、订单数(聚合方式 `count`)
|
||||
- **columns(列字段)** = 交叉维度(可选),即"再按什么横向展开"。例:月份、性别
|
||||
|
||||
| 用户说 | rows | values | columns |
|
||||
|--------|------|--------|---------|
|
||||
| "按部门统计人数" | 部门 | 姓名(`summarize_by: "count"`) | — |
|
||||
| "按医生统计费用和结余" | 主管医生 | 费用(`"sum"`)、结余(`"sum"`) | — |
|
||||
| "各部门男女人数" | 部门 | 姓名(`"count"`) | 性别 |
|
||||
|
||||
**常见配置错误(必须注意)**:
|
||||
- **数据源范围必须精确**:透视表的数据源范围必须包含表头行,且精确覆盖全部数据行列。范围过大(包含空行/空列)或过小(遗漏数据列)都会导致透视表结果错误
|
||||
- **行列字段选择要匹配用户意图**:用户说"按商品统计金额"→ 行字段=商品,值字段=金额(`summarize_by: "sum"`)。不要把行列字段搞反
|
||||
- **聚合类型要匹配**:用户说"统计数量"→ `summarize_by: "count"`;"统计总额"→ `"sum"`;"统计平均"→ `"average"`。完整合法值:`sum` / `count` / `average` / `max` / `min` / `product` / `countNums` / `stdDev` / `stdDevp` / `var` / `varp` / `distinct` / `median`。按用户意图选聚合方式,不要拿 `count` 顶替 `sum`
|
||||
- **参数长度限制**:如果透视表配置 JSON 过长(数据源范围跨越大量行列),可能导致工具调用失败。此时应先确认数据范围的精确边界,避免传入过大的 range
|
||||
- **落点不能覆盖任何已有数据(不只是 `--source` 范围)**:透视表创建后会向右下**展开**,展开区域哪怕只盖到一个已有单元格(即便已避开源数据),也会报「目标位置不能与数据源重叠」并产生 `#REF!`。创建前无法精确预知展开尺寸,故**强烈优先默认策略**(不传 `--target-sheet-id/-name` 与 `--target-position`/`--range`,后端自动新建空白子表),零覆盖风险;非要落到已有子表,必须挑一片足够大的纯空白区
|
||||
- **创建后必须校验(用 `info` 读取展开后的真实占用区域)**:创建后调用 `+pivot-list` 读 `info.error_state` 与 `info.content_range`/`page_range`——`error_state` 非 `None`(如 `Cover` 盖到其它内容 / `Shrink` 展不开)说明落点冲突,应删除后重建到空白区;`content_range`/`page_range` 是展开后**实际占用区域**,可用 `+csv-get` 抽查其边缘外有没有盖掉原有数据,确认结构正确
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+pivot-list` | read | 对象 |
|
||||
| `+pivot-create` | write | 对象 |
|
||||
| `+pivot-update` | write | 对象 |
|
||||
| `+pivot-delete` | high-risk-write | 对象 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+pivot-list`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--pivot-table-id` | string | optional | 按 id 过滤 |
|
||||
|
||||
### `+pivot-create`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--properties` | string + File + Stdin(复合 JSON) | required | JSON:{"rows":[...],"columns":[...],"values":[...],"filters":[...],"show_row_grand_total":true,"show_col_grand_total":true}(数据源走 --source,不要再放进 properties.source) |
|
||||
| `--target-position` | string | optional | 透视表落点子表内的起始 cell(A1 格式,如 `A1`),映射到顶层 `target_position`,默认 `A1`(值为 A1 时不下发)。它与 `--range` 都表达落点但落在不同 wire 字段,避免两者同时给冲突值 |
|
||||
| `--target-sheet-id` | string | xor | 透视表落点目标子表的 reference_id(与 `--target-sheet-name` 互斥,优先于 --target-sheet-name;都不传时自动新建一张子表放置透视表——推荐)。与数据源 sheet 区分:数据源 sheet 写在 --source 的 A1 引用里(带 sheet 前缀,形如 `'Sheet1'!A1:D100`)。 |
|
||||
| `--target-sheet-name` | string | xor | 透视表落点目标子表的名称(与 `--target-sheet-id` 互斥;都不传时自动新建一张子表放置透视表——推荐)。与数据源 sheet 区分:数据源 sheet 写在 --source 的 A1 引用里(带 sheet 前缀,形如 `'Sheet1'!A1:D100`)。 |
|
||||
| `--source` | string | required | 透视表源数据区域(A1 表示法,格式 `'SheetName'!StartCell:EndCell`,如 `'Sheet1'!A1:D100`) |
|
||||
| `--range` | string | optional | 透视表左上角放置位置(A1 单值,如 `F1`,仅 create 生效),映射到 `properties.range`;省略时放在落点子表(默认新建子表)的左上角。它与 `--target-position` 都表达落点但落在不同 wire 字段,避免两者同时给冲突值 |
|
||||
|
||||
### `+pivot-update`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--pivot-table-id` | string | required | 目标透视表 id |
|
||||
| `--properties` | string + File + Stdin(复合 JSON) | required | 完整或足够完整的配置(先 `+pivot-list --pivot-table-id <id>` 回读再 patch) |
|
||||
|
||||
### `+pivot-delete`
|
||||
|
||||
_公共四件套 · 系统:`--yes`、`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--pivot-table-id` | string | required | 目标透视表 id |
|
||||
|
||||
## Schemas
|
||||
|
||||
> 复合 JSON flag 字段速查(只列顶层 + 一层嵌套)。深层结构看下方 `## Examples`,或用 `--print-schema` 读完整 JSON Schema(用法见 SKILL.md「公共 flag 速查」与「Agent 使用提示」)。
|
||||
|
||||
### `+pivot-create` `--properties` / `+pivot-update` `--properties`
|
||||
|
||||
_创建/更新的透视表属性_
|
||||
|
||||
**顶层字段**:
|
||||
- `range` (string?) — 放置透视表的左上角单元格 A1 地址(例如:'F1')(仅 create 时有效) — ⚠️ 已拎为独立 flag `--range`,请勿在此 JSON 内重复填写(同名以独立 flag 为准)
|
||||
- `source` (string?) — 源数据区域地址,格式为 'SheetName!StartCell:EndCell'(例如:'Sheet1!A1:D100') — ⚠️ 已拎为独立 flag `--source`,请勿在此 JSON 内重复填写(同名以独立 flag 为准)
|
||||
- `rows` (array<object>?) — 纵向分组字段(行字段) each: { field: string, display_name?: string, sort?: object, filter?: object, condition_filter?: object, …共 6 项 }
|
||||
- `columns` (array<object>?) — 横向分组字段(列字段) each: { field: string, display_name?: string, sort?: object, filter?: object, condition_filter?: object, …共 6 项 }
|
||||
- `filters` (array<object>?) — 筛选区域字段(页字段) each: { field: string, display_name?: string, filter?: object, condition_filter?: object, group?: object }
|
||||
- `values` (array<object>?) — 要汇总的字段(至少需要 1 个) each: { field: string, display_name?: string, summarize_by?: enum, show_data_as?: enum, base_field?: string }
|
||||
- `auto_fit_col` (boolean?) — 是否自动调整列宽以适应内容
|
||||
- `show_row_grand_total` (boolean?) — 是否显示行总计(默认 true)
|
||||
- `show_col_grand_total` (boolean?) — 是否显示列总计(默认 true)
|
||||
- `show_subtotals` (boolean?) — 是否显示分类小计(默认 true,应用于所有字段)
|
||||
- `repeat_row_labels` (boolean?) — 是否显示重复项标签
|
||||
- `calculated_fields` (array<object>?) — 计算字段列表 each: { name: string, formula: string, summarize_by?: enum }
|
||||
- `collapse` (object?) — 行字段展开/折叠状态:字段名 -> 要折叠的项目列表
|
||||
|
||||
## Examples
|
||||
|
||||
公共四件套:所有 shortcut 顶部排列 `--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`,其中 `--sheet-id` / `--sheet-name` 在 `+pivot-update` / `+pivot-delete` / `+pivot-list` 上是公共四件套语义(定位透视表所在 sheet,XOR 必传一个)。
|
||||
|
||||
**`+pivot-create` 例外**:placement 选择器用 `--target-sheet-id` / `--target-sheet-name`(XOR,两个都不传时后端自动新建子表存放产物,强烈推荐,绝不碰源数据)。数据源 sheet 写在 `--source` 的 `'SheetName'!Range` 里,不靠 sheet 选择器 flag。
|
||||
|
||||
### `+pivot-list`
|
||||
|
||||
```bash
|
||||
lark-cli sheets +pivot-list --url "..." --sheet-id "$SID"
|
||||
```
|
||||
|
||||
> **返回值含 `info`(展开后的占用区域与状态)**:每个透视表对象除 `position` / `snapshot` 外,还返回 `info`,标明它在 sheet 上的平铺区域与状态——`info.page_range`(筛选/分页区 A1)、`info.content_range`(主体数据区 A1)、`info.span_range`(空表合并区 A1)、`info.error_state`(错误状态,如 `None`/`Cover`/`Shrink`/`Loading`)、`info.is_empty` / `info.is_hidden`、`info.row`/`info.col`(锚点)等。
|
||||
> **用途 1(判断改值还是改配置)**:当用户描述某个单元格要改动时,先 `+pivot-list` 拿到 `info`,判断该单元格是否落在 `page_range` / `content_range` 内——**落在区域内 = 属于透视表,应走 `+pivot-update` 改配置**(透视表单元格不能直接 `+cells-set` 改值);**落在区域外 = 普通单元格,正常 `+cells-set` 改值**。
|
||||
> **用途 2(创建后校验覆盖)**:建完透视表用 `info.error_state` 判断有没有冲突(非 `None` 即落点/展开区与已有数据重叠或展不开),用 `info.content_range`/`page_range` 拿到展开后真实占用区域再核对是否盖到原有数据。
|
||||
|
||||
### `+pivot-create`
|
||||
|
||||
> 数据源 `--source` 必须从表头行开始;空行 / 汇总行会被当作数据参与聚合,需提前用 `+csv-get` 确认起止边界。`--source` 和 `--range` 是独立 flag(不要再放 `--properties`);`rows` / `columns` / `values` 等数组字段走 `--properties`。
|
||||
>
|
||||
> **先理清 `+pivot-create` 上 4 个位置类入参(语义不同,别混)**:
|
||||
> - `--source`(**必填**):**源数据**区域,须自带 `Sheet!` 前缀(如 `'Sheet1'!A1:D100`,sheet 名按 A1 标准单引号包裹)。源 sheet 的名字在 `--source` 字符串里,**不**通过单独 flag 传。
|
||||
> - `--target-sheet-id` / `--target-sheet-name`:**透视表的落点 sheet**(即产物放哪张子表)。两个互斥(最多传一个),都不传时后端自动新建子表存放产物(强烈推荐)。
|
||||
> - `--target-position`(可选,A1 表示法,默认 `A1`):落点 sheet 内的起始 cell,映射到顶层 `target_position`。
|
||||
> - `--range`(可选,A1 单值,仅 create 生效):跟 `--target-position` 表达同一意图但映射到 `properties.range`,**两者不要同时给**。
|
||||
>
|
||||
> **落点 3 种策略(互斥,选其一)**:
|
||||
> 1. **默认(强烈推荐)**:`--target-sheet-id` / `--target-sheet-name` / `--target-position` / `--range` **全都不传** → 服务端**自动新建子表**存放产物,绝不碰任何已有数据。
|
||||
> 2. **放进指定的已有子表**:传 `--target-sheet-id <落点子表 id>`(或 `--target-sheet-name`),可选 `--target-position <子表内起点 cell>`。⚠️ **若落点子表就是源数据所在的 sheet**,必须配 `--target-position` 或 `--range` 指向源数据范围**之外**的位置,否则产物默认从 A1 起会盖在源数据上。
|
||||
> 3. **`--range`**:跟策略 2 等价(同样需要 `--target-sheet-id` / `--target-sheet-name` 指定落点子表,不然落到自动新建子表),只是用 `properties.range` 那条 wire 路径表达位置。同样的覆盖风险,同样需要避开源数据范围。
|
||||
>
|
||||
> 一般用策略 1(默认新建子表)即可,零覆盖风险,无需任何 `--target-*` / `--range` flag。
|
||||
|
||||
```bash
|
||||
# 策略 1(强烈推荐):不传任何落点 flag → 后端自动新建子表,零覆盖风险
|
||||
lark-cli sheets +pivot-create --url "..." \
|
||||
--source "'Sheet1'!A1:D100" --properties @pivot.json
|
||||
|
||||
# 策略 2:落进指定的已有目标子表(注意目标 sheet ≠ 源 sheet,否则要配 --target-position 避开源数据)
|
||||
lark-cli sheets +pivot-create --url "..." \
|
||||
--source "'Sheet1'!A1:D100" --target-sheet-id "$DEST_SID" --target-position "A1" --properties @pivot.json
|
||||
```
|
||||
|
||||
### `+pivot-update`
|
||||
|
||||
> 不允许改 `--source` / `--range`(透视表创建后位置/数据源固定);只能用 `--properties` 改 rows / columns / values / filters 等。先 `+pivot-list --pivot-table-id <id>` 回读再 patch,避免漏字段。
|
||||
|
||||
### `+pivot-delete`
|
||||
|
||||
```bash
|
||||
lark-cli sheets +pivot-delete --url "..." --sheet-id "$SID" --pivot-table-id "$PID" --yes
|
||||
```
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate`:`--url` / `--spreadsheet-token` XOR 必填;`+pivot-{update,delete,list}` 的 `--sheet-id` / `--sheet-name` XOR 必填一个;`+pivot-create` 例外(用 `--target-sheet-id` / `--target-sheet-name` 表达落点,两个都可空时触发 backend auto-create 子表,两个都给则报 mutually exclusive);`+pivot-create` 的 `--source` 必填且必须含表头行;`--properties` 中 `rows` / `columns` / `values` 至少非空之一;`+pivot-delete` 强制 `--yes` 或 `--dry-run`。
|
||||
- `DryRun`:写操作输出"将要 POST/PATCH/DELETE 的 pivot 请求模板"+ 预估输出尺寸(行数 × 列数)。
|
||||
- `Execute`:写后不自动回读;如需确认,自行调用 `+pivot-list --pivot-table-id <id>` 并用 `+csv-get` 抽样读透视产物核对输出尺寸 + 总计行位置。
|
||||
|
||||
> ⚠️ pivot 输出包含总计 / 小计行;后续 chart 引用 pivot 时,`snapshot.data.refs` 必须排除这些行(见 `lark-sheets-chart` 的「⚠️ chart 数据源引用 pivot 时必须排除总计行」段)。
|
||||
@ -0,0 +1,297 @@
|
||||
# Lark Sheet Range Operations
|
||||
|
||||
## 结构性操作影响面预检(清除 / 合并 / 排序 / 移动前必做)
|
||||
|
||||
`+cells-clear`、`+cells-{merge|unmerge}`、`+range-{move|copy|fill|sort}`(移动 / 复制 / 排序 / 自动填充)都会让既有引用关系发生偏移或失效。**操作前必须**先确认以下两点;否则禁止执行:
|
||||
|
||||
1. **打印当前合并单元格 + 公式引用 + 数据验证范围**:用 `+sheet-info --include merges` + `+cells-get` 抽样目标区域和它周边的公式 / 透视表 / 图表 / 条件格式 / 筛选器的数据源;评估操作后这些引用是否仍指向正确数据。
|
||||
2. **`+cells-clear` 不得侵入用户授权范围之外**:清除范围只能是用户明示要清的区域;不要顺手清除"看起来没用"的相邻单元格。
|
||||
|
||||
排序场景的存储类型识别 + 辅助列抽数值的细则见下方「sort 操作前必读」章节。
|
||||
|
||||
## 使用场景
|
||||
|
||||
写入。对指定区域执行结构性操作。本 reference 覆盖 9 个 shortcut,按 4 类用途组织:
|
||||
|
||||
| 操作需求 | 使用工具 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 清除内容/格式 | `+cells-clear` | "清空"、"删除内容"、"去掉格式" |
|
||||
| 合并/取消合并单元格 | `+cells-{merge|unmerge}` | "合并单元格"、"取消合并" |
|
||||
| 调整行高/列宽 | `+rows-resize / +cols-resize` | "加宽列"、"调整行高"、"自适应列宽" |
|
||||
| 移动/复制/填充/排序 | `+range-{move|copy|fill|sort}` | "移动数据"、"复制到"、"自动填充"、"按某列排序" |
|
||||
|
||||
注意:
|
||||
|
||||
- **`--range` 两种语法别混**:`+cells-clear` / `+cells-{merge|unmerge}` / `+range-*` 用单元格 A1 矩形(如 `A2:A10`);`+rows-resize` / `+cols-resize` 用纯行 / 列区间(行 `2:10`、列 `A:C`),不要给 resize 传 `A2:A10`
|
||||
- 用户说"这行 / 整行 / 首行"时,优先使用整行范围如 `1:1`;"这列 / 整列"时使用 `J:J`。不要截断为局部矩形
|
||||
- 合并后只保留左上角单元格的内容,其余清除。写入合并区域用 `+cells-set` 对左上角单元格操作
|
||||
- 调整行高列宽时,先读取相邻行列尺寸再决定像素值,不要随意猜测
|
||||
- `--copy-to-range`(`+cells-set` 的参数)复制的是值/公式/样式,不含行高列宽。需要统一尺寸时另行调用 `+rows-resize / +cols-resize`
|
||||
|
||||
## 写入后列宽自适应(防内容遮挡)
|
||||
|
||||
写入文本 / 数值后**必须**主动检查列宽是否适配,否则会出现"内容被截断 / 长数字显示为科学计数法 / 文本溢出被相邻列遮挡"等用户感知问题:
|
||||
|
||||
1. **写入后回读最长内容字符数**:用 `+csv-get` 读目标列的实际写入内容,统计最长单元格的字符数(`max(len(cell) for cell in col)`)。汉字按 2 字符宽度估算,半角字母数字按 1 字符。
|
||||
2. **判定阈值**:当前列宽(用 `+sheet-info --include row_heights,col_widths` 拿)≥ 最长字符数 × 字体宽度系数 + buffer 才算适配。默认列宽 11 通常只够 11 个半角字符或 5-6 个汉字,写长文本前必扩宽。
|
||||
3. **修复二选一**:
|
||||
- **扩列宽**:用 `+rows-resize / +cols-resize` 把目标列宽设为 `max(表头字符数, 内容采样最长字符数) × 8 + 16` 像素(经验值)
|
||||
- **自动换行**:在 `+cells-set` 时给单元格设置 `cell_styles.word_wrap="auto-wrap"`(可选值:`overflow` / `auto-wrap` / `word-clip`;`cell_styles` 字段见 `lark-sheets-write-cells`),并用 `+rows-resize / +cols-resize` 调高对应行的行高
|
||||
4. **新增列默认列宽规则**:新增列宽度 ≥ `max(表头字符数, 内容采样最长字符数) × 8 + 16` 像素,**禁止**用默认 11 直接交付。
|
||||
|
||||
**典型反例**:默认列宽 11 但内容含 12+ 字符的中文 / 含单位的数值(如 `109.10μmol/L`)/ 长数字未设 `number_format` 显示为科学计数法 —— 用户在结果表里看不到完整原值。
|
||||
|
||||
**打印场景控制总宽(用户说"适合打印 / A4 / 打印范围"时必做)**:扩单列宽防截断的同时,**所有列宽之和要落在纸张可打印宽度内**——A4 横向约 ≤ 102 个半角字符(约 1000px),纵向约 ≤ 70 个字符。超宽时不要无限加宽,改用 `cell_styles.word_wrap="auto-wrap"` + 调高行高,或缩窄非关键列,让整表在一页内(反例:总列宽远超 A4 可打印宽度,且长文本行高不够被截断)。
|
||||
|
||||
**只加宽承载新内容的列,不改动原有列的列宽**:列宽自适应**只针对新增 / 真正放不下新内容的列**;原表已有列的列宽**禁止重新计算、禁止缩小**——即便你估算的"理想宽度"与原值不同,只要原内容没被截断就不要动它。无差别地把所有列重设一遍宽度(哪怕只 ±1)都属于破坏原文件视觉格式(反例:填完数据后顺手把原有列的列宽从 16 改成 17,与原附件不一致,破坏了原视觉格式)。
|
||||
|
||||
**⚠️ 合并单元格安全操作规则**(`+cells-{merge|unmerge}` 必读):
|
||||
|
||||
1. **先读后写**:操作前必须用 `+sheet-info --include merges` 或 `+cells-get` 识别已有合并区域(特征:多个连续单元格中只有左上角有值,其余为空)。
|
||||
2. **不要对已合并区域重复 merge**:对已合并的区域再次调用 merge 会报错或产生不可预期结果。
|
||||
3. **修改合并区域的正确顺序**:先 `unmerge` → 修改内容/样式 → 再 `merge`。
|
||||
4. **对合并区域设置样式**:只对完整 range 设置一次 `cell_styles`(写在左上角单元格),其余位置用 `{}` 占位。
|
||||
5. **新增合并时数据保护**:合并前确认目标区域只有左上角有数据,其余单元格为空,否则合并会导致非左上角的数据丢失。
|
||||
6. **批量取消合并一次调用即可**:当一个范围(整列 `A:A`、整行 `3:3`、矩形 `A1:D100`)内存在多个合并区域,直接调一次 `+cells-unmerge` 传入这个大范围,会一次性取消该范围内所有合并区域;**不要**为每个合并区域单独调用 unmerge,也不要用 `+batch-update` 拆成多次 unmerge。
|
||||
|
||||
**⚠️ 批量操作必须用 `+batch-update`**:对**多个**不同区域执行 `+cells-merge` 时,禁止逐个调用,合并为单次原子 `+batch-update`(语义与 `--operations` 入参格式见 `lark-sheets-batch-update`)。行高列宽**不需要** `+batch-update`:多行 / 多列不同尺寸直接用 `+rows-resize --heights` / `+cols-resize --widths` 的 map 形态,一次调用原子完成。
|
||||
|
||||
**唯一例外**:`+cells-unmerge` 原生支持传一个大 range 一次性取消其中所有合并区域,应直接单次调用,**不要**拆进 `+batch-update`。
|
||||
|
||||
**⚠️ sort 操作前必读:确认目标列的数据类型**
|
||||
|
||||
排序按单元格的**存储类型**比较:纯数字按数值排序;文本字符串按**字典序**(`"1000"` 排在 `"999"` 之前,与数值相反);日期按时间戳排序。
|
||||
|
||||
以下形态**看起来像数字但实际是字符串**,直接 sort 会得到错误结果:
|
||||
|
||||
| 示例 | 说明 |
|
||||
|------|------|
|
||||
| `843688.69+20042.35=863731.04` | 表达式文本(无前导 `=` 不是公式,整串按字典序比较) |
|
||||
| `¥1,234.56` / `$1,234` | 带货币符号 |
|
||||
| `1.2万` / `3.5亿` / `100kg` | 带中文 / 英文单位 |
|
||||
| 前后含空格或不可见字符的数字串 | 被当文本 |
|
||||
| 同列混文本和数字 | 排序后分块 |
|
||||
|
||||
**硬性流程**:
|
||||
|
||||
1. sort 前先用 `+csv-get` 抽样目标列的前 3–5 行确认原始值形态,不要只看列名和用户问题就直接排。
|
||||
2. 若是纯数字或日期 → 直接 sort。
|
||||
3. 若是带符号 / 表达式 / 单位的文本 → **不要直接排**:
|
||||
- 简单场景(货币、千分位、单位前缀):新增辅助列,用公式提取数值(如 `=VALUE(SUBSTITUTE(SUBSTITUTE(A2,"¥",""),",",""))`),按辅助列排序,排完可按需清除辅助列。
|
||||
- 复杂场景(多段表达式、中文单位、混合格式):分批 `+csv-get` 读到本地,按数值排序后用 `+csv-put` / `+cells-set` 分批回写。
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+cells-clear` | high-risk-write | 单元格 |
|
||||
| `+cells-merge` | write | 单元格 |
|
||||
| `+cells-unmerge` | write | 单元格 |
|
||||
| `+rows-resize` | write | 工作表 |
|
||||
| `+cols-resize` | write | 工作表 |
|
||||
| `+range-move` | write | 区域 |
|
||||
| `+range-copy` | write | 区域 |
|
||||
| `+range-fill` | write | 区域 |
|
||||
| `+range-sort` | write | 区域 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+cells-clear`
|
||||
|
||||
_公共四件套 · 系统:`--yes`、`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | 清除范围(A1 格式) |
|
||||
| `--scope` | string | optional | 清除范围 enum:`content`(默认,仅清内容)/ `formats`(仅清格式)/ `all`(清内容 + 格式)(可选值:`content` / `formats` / `all`) |
|
||||
|
||||
### `+cells-merge`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | 待合并 / 取消合并的范围(A1 格式) |
|
||||
| `--merge-type` | string | optional | 合并方向(仅 `+cells-merge`)(可选值:`all` / `rows` / `columns`)(默认 `all`) |
|
||||
|
||||
### `+cells-unmerge`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | 待合并 / 取消合并的范围(A1 格式) |
|
||||
|
||||
### `+rows-resize`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--height` | int | xor | 统一行高(像素,例:30 / 40 / 60;不是磅/points),配 `--range` 使用。传了 `--height` 就是像素模式,可以省略 `--type`;显式 `--type pixel` 也行(等价)。多行不同高用 `--heights` |
|
||||
| `--heights` | string + File + Stdin(复合 JSON) | xor | 差异化行高 map,一次原子调用给多行设置不同高度:键为单行(`"1"`)或行闭区间(`"2:20"`),值为像素高(如 30 / 50)、`"auto"`(自适应内容)或 `"standard"`(重置默认)。⚠️ 单位是像素,不是磅/points。与 `--range` / `--height` / `--type` 互斥 |
|
||||
| `--type` | string | xor | 尺寸方式 enum:`pixel`(需配 `--height`)/ `standard`(重置为默认行高)/ `auto`(自动适应内容)。常规写法直接给 `--height` 即可省略本 flag;`--type standard` / `--type auto` 不能与 `--height` 同时给(可选值:`pixel` / `standard` / `auto`) |
|
||||
| `--range` | string | xor | 要调整行高的行闭区间;1-based 行号如 `2:10` 或单行 `5`。统一尺寸形态必填(配 `--height` 或 `--type`);map 形态(`--heights`)不传 |
|
||||
|
||||
### `+cols-resize`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--width` | int | xor | 统一列宽(像素,例:80 / 120 / 200;不是 Excel 字符单位),配 `--range` 使用。传了 `--width` 就是像素模式,可以省略 `--type`;显式 `--type pixel` 也行(等价)。多列不同宽用 `--widths` |
|
||||
| `--widths` | string + File + Stdin(复合 JSON) | xor | 差异化列宽 map,一次原子调用给多列设置不同宽度:键为单列(`"A"`)或列闭区间(`"C:E"`),值为像素宽(如 80 / 120 / 200)或 `"standard"`(重置默认)。⚠️ 单位是像素,不是 Excel 字符单位(像素 ≈ 字符数×8+16)。与 `--range` / `--width` / `--type` 互斥 |
|
||||
| `--type` | string | xor | 尺寸方式 enum:`pixel`(需配 `--width`)/ `standard`(重置为默认列宽)。常规写法直接给 `--width` 即可省略本 flag;`--type standard` 不能与 `--width` 同时给(可选值:`pixel` / `standard`) |
|
||||
| `--range` | string | xor | 要调整列宽的列闭区间;列字母如 `A:E` 或单列 `C`。统一尺寸形态必填(配 `--width` 或 `--type`);map 形态(`--widths`)不传 |
|
||||
|
||||
### `+range-move`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--source-range` | string | required | 源 A1 范围 |
|
||||
| `--target-sheet-id` | string | optional | 目标子表 id;省略时同源 sheet |
|
||||
| `--target-range` | string | required | 目标 A1 范围(传起点 cell 即可,按源尺寸自动推断) |
|
||||
|
||||
### `+range-copy`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--source-range` | string | required | 源 A1 范围 |
|
||||
| `--target-sheet-id` | string | optional | 目标子表 id;省略时同源 sheet |
|
||||
| `--target-range` | string | required | 目标 A1 范围(传起点 cell 即可,按源尺寸自动推断) |
|
||||
| `--paste-type` | string | optional | 粘贴内容(仅 `+range-copy`)(可选值:`values` / `formulas` / `formats` / `all`)(默认 `all`) |
|
||||
|
||||
### `+range-fill`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--source-range` | string | required | 填充模板范围(系列起始 cells) |
|
||||
| `--target-range` | string | required | 目标填充范围(A1 格式) |
|
||||
| `--series-type` | string | optional | 填充序列类型(可选值:`auto` / `linear` / `growth` / `date` / `copy`)(默认 `auto`) |
|
||||
|
||||
### `+range-sort`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | 排序范围(A1 格式;含或不含表头由 `--has-header` 决定) |
|
||||
| `--sort-keys` | string + File + Stdin(复合 JSON) | required | JSON 数组:`[{"column":"<列字母>","ascending":<bool>}, ...]` |
|
||||
| `--has-header` | bool | optional | 第一行是表头不参与排序,默认 false |
|
||||
|
||||
## Schemas
|
||||
|
||||
> 复合 JSON flag 字段速查(只列顶层 + 一层嵌套)。深层结构看下方 `## Examples`,或用 `--print-schema` 读完整 JSON Schema(用法见 SKILL.md「公共 flag 速查」与「Agent 使用提示」)。
|
||||
|
||||
### `+rows-resize` `--heights`
|
||||
|
||||
_行 → 高度 map_
|
||||
- type: object
|
||||
|
||||
### `+cols-resize` `--widths`
|
||||
|
||||
_列 → 宽度 map_
|
||||
- type: object
|
||||
|
||||
### `+range-sort` `--sort-keys`
|
||||
|
||||
_排序条件列表(仅 sort 操作)_
|
||||
|
||||
**数组项**(类型 object):
|
||||
- `column` (string) — 排序依据的列字母(如 "C"、"D"),必须在 range 范围内
|
||||
- `ascending` (boolean) — 是否升序排序
|
||||
|
||||
## Examples
|
||||
|
||||
> ⚠️ 本 reference 派生的 shortcut 跨 3 个分组:`+rows-resize` / `+cols-resize` → 工作表,`+cells-*` → 单元格,`+range-*` → 区域。这里统一从区域操作视角讲解。
|
||||
|
||||
公共四件套:所有 shortcut 顶部排列 `--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`(XOR)。
|
||||
|
||||
### `+cells-clear`
|
||||
|
||||
> ⚠️ **`--scope all` 清整表是不可逆的大范围破坏**:会一并抹掉该区域的合并单元格、原公式,以及图表 / 透视表引用的数据源列(这类列常在主数据区右侧,视觉上"看着没用"却被图例 / 系列引用)。**"美化 / 规范化一张已有表"永远不需要 clear 原表再重写**——若你打算"清空原表 → 写入重排后的版本",说明走错了路径,应改为原地只刷样式(见 `lark-sheets-visual-standards` 场景三)。
|
||||
|
||||
> **删不掉嵌入对象**:`+cells-clear`(任何 `--scope`,含 `all`)只清单元格的值 / 格式,**删不掉**压在范围内的透视表 / 图表等嵌入对象——后端会报 `can not find embedded block`。删透视表用 `+pivot-delete`、删图表用 `+chart-delete`(先用 `+pivot-list` / `+chart-list` 拿对象 id)。
|
||||
|
||||
> 需要一次清除**多个不连续 range**(如把内容搬走后批量去掉散落各处的边框/底色)时,改用 `lark-sheets-batch-update` 的 `+cells-batch-clear`,避免对 `+cells-clear` 逐个 range 调用。
|
||||
|
||||
```bash
|
||||
# dry-run 先看
|
||||
lark-cli sheets +cells-clear --url "..." --sheet-id "$SID" --range "A2:Z1000" --scope all --dry-run
|
||||
# 执行
|
||||
lark-cli sheets +cells-clear --url "..." --sheet-id "$SID" --range "A2:Z1000" --scope all --yes
|
||||
```
|
||||
|
||||
### `+cells-merge` / `+cells-unmerge`
|
||||
|
||||
```bash
|
||||
# 合并 A1:C1(可选 --merge-type all/rows/columns)
|
||||
lark-cli sheets +cells-merge --url "..." --sheet-id "$SID" --range "A1:C1"
|
||||
# 取消合并:传大 range 一次性取消其中所有合并区域
|
||||
lark-cli sheets +cells-unmerge --url "..." --sheet-id "$SID" --range "A1:C100"
|
||||
```
|
||||
|
||||
### `+rows-resize` / `+cols-resize`
|
||||
|
||||
行高列宽分两条 shortcut,避免行 / 列在底层 schema 的差异(行支持 `auto`,列不支持)混在一起。两种形态:
|
||||
|
||||
- **统一尺寸**:`--range` + `--height`/`--width <px>`(省略 `--type`,等价于 `--type pixel`)。非像素模式走 `--type standard` / `--type auto`,此时不能再带像素值。
|
||||
- **差异化尺寸**:`--heights`/`--widths` 一个 JSON map,键为单行/列或闭区间、值为像素或模式字符串,**一次调用原子完成多行 / 多列不同尺寸**——不要拆多次调用,也不要用 `+batch-update`。
|
||||
|
||||
```bash
|
||||
# 统一尺寸:把第 2-10 行设为固定 30 px
|
||||
lark-cli sheets +rows-resize --url "..." --sheet-id "$SID" --range "2:10" --height 30
|
||||
|
||||
# 统一尺寸:把 A-C 列设为固定 120 px
|
||||
lark-cli sheets +cols-resize --url "..." --sheet-id "$SID" --range "A:C" --width 120
|
||||
|
||||
# 差异化尺寸:多列不同宽,一次调用(值可混用 "standard" 重置某列)
|
||||
lark-cli sheets +cols-resize --url "..." --sheet-id "$SID" \
|
||||
--widths '{"A": 100, "B": 358, "C:E": 120, "G": "standard"}'
|
||||
|
||||
# 差异化尺寸:多行不同高,值可混用 "auto" / "standard"
|
||||
lark-cli sheets +rows-resize --url "..." --sheet-id "$SID" \
|
||||
--heights '{"1": 50, "2:20": 30, "21": "auto"}'
|
||||
|
||||
# 第 1 行行高自动适应内容(列宽不支持 auto)
|
||||
lark-cli sheets +rows-resize --url "..." --sheet-id "$SID" --range "1" --type auto
|
||||
|
||||
# 重置 A-E 列为默认列宽
|
||||
lark-cli sheets +cols-resize --url "..." --sheet-id "$SID" --range "A:E" --type standard
|
||||
```
|
||||
|
||||
**⚠️ 单位是像素,不是 Excel 字符单位 / 磅**:列宽常见 60~400px;如果你按 Excel 字符单位(openpyxl / xlsxwriter 的 `width`)心算,先换算 `px ≈ 字符数 × 8 + 16`——写 `{"A": 10}` 得到的是 10px 的不可用窄列(CLI 会拒绝 < 20px 的列宽并提示换算)。行高是像素不是磅(points),默认行高约 24px。
|
||||
|
||||
**列宽没有 auto-fit**:需要"列宽自适应内容"时,按"写入后列宽自适应"一节的公式估算像素值(`max(表头字符数, 内容最长字符数) × 8 + 16`)后用 `--widths` 显式设置。
|
||||
|
||||
> 同时出现在 `lark-sheets-sheet-structure.md` —— 行高 / 列宽调整也算行列结构层动作。
|
||||
|
||||
### `+range-move` / `+range-copy`
|
||||
|
||||
> `+range-move` 会**清空源区域**(move = copy + clear_source);`+range-copy` 不动源。
|
||||
|
||||
### `+range-fill`
|
||||
|
||||
```bash
|
||||
# 用 A1:A2 的序列规律向下填充到 A3:A100(target 区域不能与 source 重叠,否则后端报 source overlaps destination)
|
||||
lark-cli sheets +range-fill --url "..." --sheet-id "$SID" --source-range "A1:A2" --target-range "A3:A100" --series-type auto
|
||||
```
|
||||
|
||||
### `+range-sort`
|
||||
|
||||
```bash
|
||||
# 按 C 列降序排 A1:E100(首行为表头不参与)
|
||||
lark-cli sheets +range-sort --url "..." --sheet-id "$SID" --range "A1:E100" --has-header --sort-keys '[{"column":"C","ascending":false}]'
|
||||
```
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate`:XOR 公共四件套;`+cells-clear` 强制 `--yes` 或 `--dry-run`;`+range-*` 校验源 / 目标 range 在同一 spreadsheet;`+range-sort` 的 `--sort-keys` 必须合法 JSON 数组且 col 都在 `--range` 内;`+rows-resize` / `+cols-resize` 两种形态二选一——统一形态必须给 `--range` 且至少给 `--height`/`--width` 或 `--type` 之一(`--type standard`/`auto` 不能与像素 flag 同给,`--type pixel` 共存 OK),map 形态(`--heights`/`--widths`)不能与 `--range`/`--height`/`--width`/`--type` 混用,map 键必须与命令维度一致(行数字 / 列字母)、不得重复,值为正整数像素或模式字符串;列宽 < 20px 拒绝(疑似 Excel 字符单位);`+cols-resize` 不接受 `auto`(列宽不支持自适应)。map 形态在 `+batch-update` 子操作里不可用(它本身就是原子批量)。
|
||||
- `DryRun`:所有写操作输出"将要 PATCH 的 range + 受影响 cell 数估算"。
|
||||
- `Execute`:写后不自动回读;如需确认,自行调用 `+cells-get --range <影响范围>` 抽样比对。
|
||||
235
.claude/skills/lark-sheets/references/lark-sheets-read-data.md
Normal file
235
.claude/skills/lark-sheets/references/lark-sheets-read-data.md
Normal file
@ -0,0 +1,235 @@
|
||||
# Lark Sheet Read Data
|
||||
|
||||
## 列格式多样性预探(写公式 / 排序 / 筛选前必做)
|
||||
|
||||
> 本节给出"写公式 / 排序 / 筛选前先探清列格式多样性"的正确流程,是主 SKILL.md「飞书表格编辑准则」准则 3(读全再写)在 read_data 工具层的落地。
|
||||
|
||||
对参与后续**计算 / 排序 / 筛选 / 公式提取**的列,**必须**先 sample **至少 50 行**(小表则全量),识别该列所有值类型变体后再设计公式 / 条件。只看前 10 行不够,因为下列差异通常潜伏在表尾或中段:
|
||||
|
||||
- **日期列同时出现多种格式**:`YYYYMM`、`YYYY-MM-DD`、`YYYY/M/D`、带时间戳、文本"未知"
|
||||
- **数值列混入公式文本 / 单位 / 注释**:`1000+200=1200`、`100元`、`/(合同未明确)`、`#N/A`
|
||||
- **空值与 0 / "0" 混杂**
|
||||
- **大小写 / 全角半角差异**("办公费" vs "办公费 "、"Sales" vs "sales")
|
||||
|
||||
预探后必须在公式 / 筛选条件里用 `IFERROR` / `IFS` / 提取数值的辅助列处理所有变体;不能为了通过 head(10) 的样本就直接落地。一旦设计的逻辑只覆盖 sample 中出现的格式,就属于违规。
|
||||
|
||||
⚠️ **大数字(15 位以上的身份证 / 参考号 / 流水号)做去重 / 比较时禁止用 `+csv-get` 的显示值**:`+csv-get` 返回的是**格式化显示值**,15 位以上数字会被显示成 `1.04E+14` 这类科学计数法——多个本不相同的号在显示层全变成同一个 `1.04E+14`,拿去判重会**整列误判为重复**。比较 / 去重 / 匹配大数字时必须改用 `+cells-get`(取原始精确值)或把该列读为文本,禁止用 csv-get 的科学计数显示值(反例:大批长参考号被显示成科学计数后,互不相同的号全变成同一个值,被当成整列重复并错误高亮)。
|
||||
|
||||
## 使用场景
|
||||
|
||||
读取。从飞书表格中读取单元格数据。本 reference 覆盖 4 个 shortcut,按读取目的选择:
|
||||
|
||||
| 读取目的 | 用这个 shortcut | 数据去向 | 说明 |
|
||||
|---------|----------------|---------|------|
|
||||
| 快速查看纯值数据、批量处理 | `+csv-get` | 对话上下文 | 返回 CSV 文本(每行带 `[row=N]` 前缀);大表请按 `--range` 行窗口分批读(截断时看 `has_more`) |
|
||||
| 按列类型结构化读出(喂 DataFrame / round-trip 回 `+table-put`) | `+table-get` | 对话上下文 | 返回 typed 协议(`columns:[列名]` + `data` + `dtypes`/`formats` + `range`),输出形状对齐 pandas split;可一行 `pd.DataFrame(sheet["data"], columns=sheet["columns"]).astype(sheet["dtypes"])` 还原 DataFrame,或直接 round-trip 回 `+table-put`。不带 `--range` 时读**完整 used range**(跨过表中部空行 / 空列),每个子表回传实际读取范围 `range` 供完整性校验。注意这与下文 `current_region` "遇表中部空行截断"不矛盾:`+table-get` 读的是子表物理 used range(飞书记录的已用矩形,含中间空行),`current_region` 是从锚点连通扩展、遇整行空行就断 |
|
||||
| 查看公式、样式、批注、数据验证 | `+cells-get` | 对话上下文 | 返回单元格完整信息,token 开销较大 |
|
||||
| 查看某区域的下拉框(数据验证)选项 | `+dropdown-get` | 对话上下文 | 返回该 A1 范围已配置的下拉列表选项 |
|
||||
|
||||
**选择原则**:
|
||||
- 只看值或做数据处理 → `+csv-get`;大表分批读取,避免一次拉全表撑爆上下文
|
||||
- 要按列类型结构化读出(喂 DataFrame / round-trip 回 `+table-put`)→ `+table-get`
|
||||
- 需要公式/样式/批注 → `+cells-get`
|
||||
- 只想知道某区域下拉框有哪些选项 → `+dropdown-get`
|
||||
|
||||
⚠️ **大数据优先落盘、别灌进上下文**:`+csv-get` / `+cells-get` 都受调用方 Bash / 终端的单命令 stdout 输出上限约束(常见默认约 30000 字符,超过会被截断或转存为文件)。纯值分析优先 `+csv-get --format csv` 按 `--range` 行窗口(`A1:Z500` / `A501:Z1000` …)分批重定向到文件 + 本地脚本处理 + `+csv-put` 分批回写;若确实要让结果直接进上下文又不想触发转存,给任一命令把 `--max-chars`(默认 500000)调小到略低于该上限(如 `25000`),CLI 改为优雅截断 + `has_more` 分页。
|
||||
|
||||
**`+csv-get` 返回值核心设计**:
|
||||
- `annotated_csv` — **CSV 数据唯一入口**。每一逻辑行前加 `[row=N] ` 前缀(N = 真实表格行号)。任何需要行号的下游操作(合并、写入、清空、格式化、插入/删除、条件格式、筛选、图表/透视表范围、搜索替换等),**行号一律直接从 `[row=N]` 读取**。若需要纯 CSV(如喂给本地脚本做解析),去前缀即可:`line.replace(/^\[row=\d+\] /, '')`。
|
||||
- `col_indices` — **定位列字母唯一入口**。在表头中找到目标字段是第 j 个(0-based),用 `col_indices[j]` 取列字母。**禁止手数逗号**——列数超过 10 时极易 off-by-one(例如把 W 误判为 X)。
|
||||
- `row_indices` — 程序化引用的备用数组。LLM 推理请用 `annotated_csv` 的前缀,不要查这个数组里的 index(把行号当数值用容易心算出错)。
|
||||
- `current_region` — 从请求范围扩展到被空行空列包围的连续数据区域(等价于 Excel Ctrl+Shift+*),适合先读少量行探表头。⚠️ 它**遇表中部整行空行 / 整列空列就截断**,可能小于真实数据范围(漏掉空行之后的行);**不能**直接当整表末行用,判断整表是否读全要拿 `+workbook-info` 的物理 `row_count` / `column_count` 当上界交叉核对(见下方「按 row_count 盲读空行」与「确定数据范围的正确流程」)。
|
||||
|
||||
注意:
|
||||
|
||||
- `+csv-get` 和 `+cells-get` 支持分页/截断,注意检查 `has_more` / `truncated` 标志;两者在处理返回数据之前都必须先读 `warning_message`(上游 schema 要求先读它再用其它字段,内含定位与截断续读提示),`+cells-get` 还要用每个 range 的 `actual_range` / `row_indices` / `col_indices` 判断真实位置
|
||||
- 隐藏行列默认包含在返回结果中(`--skip-hidden=false`),如需只看可见数据设为 `true`。读取原语本身不标注哪些行列被隐藏:若要识别隐藏区间(以决定是否过滤、或如何解读混入的隐藏数据),用 `+sheet-info --include hidden_rows,hidden_cols` 取隐藏行列集合,再结合 `+csv-get` / `+cells-get` 返回的 `row_indices` / `col_indices` 判断每行 / 每列是否隐藏
|
||||
|
||||
**常见配置错误(必须注意)**:
|
||||
- **全量读取导致上下文溢出**:不要对大表(数百行以上)直接用 `+csv-get` 或 `+cells-get` 读取全部数据到上下文。大表场景必须分批读取:用 `--range` 切行窗口逐块读(`+csv-get` / `+cells-get` 单次返回量由 `--max-chars` 自动兜底,截断时返回 `has_more`);过大时考虑导出到本地文件后用脚本处理再分批回写
|
||||
- **了解结构 ≠ 读取全量数据**:探表不用读全表,但必须同时探两个方向的表头:
|
||||
- **横向(列头)**:先读前几行,且**列范围必须覆盖所有列**——用 `+workbook-info` 拿总列数,`range` 末列填到最后一列(例如总列数是 N,则 `range: "A1:[列N]10"`)。列范围截短会遗漏右侧字段、后续写入列定位错误。
|
||||
- **纵向(行标)**:若左侧 1-2 列是行标签(日期/类别/编号枚举每行含义,典型交叉表/透视布局),**必须再读 `A:A` 或 `A:B` 把行标列读到底**,拿全部行标。只读前几行会看不全表尾的行,导致批量写入漏改——这是"只改前 N 行、其余未更新"的主要成因。扁平列表(每行独立记录、列是字段)可跳过这一步,但仍要按下方「确定数据范围的正确流程」用 `+workbook-info` 的物理 `row_count` 交叉核对末行(`current_region` 遇空行会截断,不能单独兜底)。
|
||||
- 数据量大或会进入上下文上限时,分批读 + 本地处理 + 分批回写,不要一口气拉全表到上下文。
|
||||
- **`+cells-get` 滥用**:当只需要数据值时,使用 `+csv-get`(token 开销约为 `+cells-get` 的 1/5)。只有确实需要公式、样式或批注时才用 `+cells-get`
|
||||
- **忽略分页标志**:读取返回 `has_more=true` 时,说明还有更多数据。如果任务需要完整数据,必须继续分页读取,不能只处理第一页就开始写入
|
||||
- **直接按 `+cells-get` 返回二维数组下标推导真实位置**:`ranges[n].cells[i][j]` 里的 `i/j` 只是返回数组下标,不等于真实表格行列。定位真实行号必须用 `ranges[n].row_indices[i]`,定位真实列字母必须用 `ranges[n].col_indices[j]`;若 `--skip-hidden=true`、请求范围越界被裁剪,或最后一行是部分返回,错误地自己数下标会立刻错位
|
||||
- **CSV 行号计数错误**:`+csv-get` 返回的 CSV 遵循 RFC 4180 标准,被双引号 `"..."` 包裹的字段中的换行符属于**字段内容的一部分**(即单元格内换行),不代表新的一行。计算行号时必须按**逻辑记录**计数,而非按物理换行符 `\n` 计数
|
||||
- **手动数列确定列号**:禁止通过在 CSV 表头中手动数逗号/字段来确定目标列的列字母。当列数超过 10 时,手动计数极易产生 off-by-one 偏移(例如把 W 列误判为 X 列)。**必须使用 `col_indices`**:先在 CSV 表头中找到目标字段名是第 j 个字段(0-based),再用 `col_indices[j]` 获取该列的实际列字母
|
||||
- **用数据列的值推导行号(常被巧合掩盖)**:CSV 中常见"序号 / ID / 编号 / No."等形似行号的列,其值与实际表格行号**没有任何绑定关系**——序号可能跳号(1,2,3,5,6...)、可能从非 1 开始、可能有重复或被中途重置。此规则适用于**所有需要行号的下游操作**:合并单元格、区间写入/清空/格式化、插入/删除行、条件格式范围、筛选器范围、图表数据源、透视表范围、搜索替换范围等等——**凡是要把行号填进任何工具参数的场景,行号一律从 `annotated_csv` 中目标行开头的 `[row=N]` 前缀直接读取**,禁止用"序号=行号"、"表头占 1 行所以数据从第 2 行开始"、"第 N 个序号就在第 N+1 行"等心算,也禁止先心算再"事后核对"。**危险特征**:前几十行中序号恰好等于表格行号(典型成因:表头 +1 与一次跳号 -1 的偏移互相抵消形成巧合),模型一旦把这个巧合当作规律,会在后续所有行沿用;而中间再出现跳号时,从该行起整块区域全部错位,且错位不自查很难发现。**正确工作流**:①在 `annotated_csv` 里定位目标逻辑行(按字段内容匹配);②直接读取该行开头的 `[row=N]` 前缀得到真实表格行号;③把这个行号填进下游工具参数。区间操作时,起始行用 start 行的 `[row=N]`、结束行用 end 行的 `[row=N]`。**自检**:动手前,在 `annotated_csv` 靠后位置再抽 1~2 行,核对 `[row=N]` 是否与首列"序号"一致——不一致(典型:`[row=57] 58,...`)即说明有跳号/隐藏行,更要严格从 `[row=N]` 取值,不要被序号列迷惑
|
||||
- **`row_count` 与 `current_region` 都不能单独定末行**:`+workbook-info` 的 `row_count` 是 sheet 的**网格物理行数**(常是 200 / 1000 等默认值),通常**大于**真实数据末行——直接按它把 `--range` 拉到 `S200` 会读回大片空行,浪费上下文。反过来,`+csv-get` 返回的 `current_region` 是从锚点扩展、被空行空列围住的连续块,**遇表中部整行空行就截断**,可能**小于**真实数据范围(漏掉空行之后的行,典型反例:1–80 行有数据、81 行空、82 行起还有数据,`current_region` 只到 80,82 行起整段被漏读)。正确做法:把 `row_count` 当**上界**、`current_region` 当**起点参考**,在二者之间按下方「确定数据范围的正确流程」确认真实末行(含跨过中间空行的核对),不要只信其一。
|
||||
- **current_region 当作纯数据范围**:`current_region` 返回的是从请求范围向四周扩展到被空行空列包围的**连续非空区域**,等价于 Excel 的 Ctrl+Shift+\*。它包含该区域内**所有非空行**——不仅包含数据行,还可能包含标题行、汇总行(如"总计")、签名行(如"编制人/审批人")、脚注等非数据内容。**严禁直接将 `current_region` 的末尾行作为数据范围的结束行**。正确做法见下方「确定数据范围的正确流程」
|
||||
|
||||
### 确定数据范围的正确流程(排序、筛选、批量写入等操作前必做)
|
||||
|
||||
当后续操作需要精确的数据范围(如排序、筛选、删除、批量写入)时,仅靠 `current_region` 探测到的范围是不够的——它**两头都可能不准**:表中部有整行空行时会被截断(末行偏小、漏数据),表尾有汇总 / 签名行时又会偏大。必须同时确认数据的**起始行**和**结束行**。具体步骤:
|
||||
|
||||
1. **确认起始行**:读取前 5~10 行,识别表头行位置,数据起始行 = 表头行 + 1
|
||||
2. **确认结束行**(关键步骤,不可跳过):
|
||||
- **先防截断(漏数据)**:拿 `+workbook-info` 的物理 `row_count` 当上界,与 `current_region` 末行对比。若 `current_region` 末行 **远小于** `row_count`(差出很多空间),不要直接采信——在 `current_region` 末行之后再探一段(如往下读到 `row_count`,或分段扫到首个连续空白区),确认空行之后确实没有数据;典型反例:`row_count=327`、`current_region` 只到第 80 行,第 81 行空、82 行起还有数据,只读到 80 就漏了一大段。
|
||||
- **再排尾部非数据行**:读取确认到的末行附近若干行(建议末尾 5~10 行),逐行排除:
|
||||
- **汇总行**:内容为"合计"、"总计"、"小计"、"总计:"等
|
||||
- **签名/审批行**:内容为"编制人"、"审核人"、"部门负责人"等
|
||||
- **空行或分隔行**:整行为空或仅有边框
|
||||
- **备注/脚注行**:注释性文字、说明文字等
|
||||
3. **最终数据范围** = 起始行 ~ 最后一条有效数据行(跨过中间空行、排除尾部非数据行)
|
||||
|
||||
**示例**:`current_region` 返回 `A1:N51`,读取 Row 48~51 发现:
|
||||
|
||||
- Row 49: 序号=47, 姓名=xxx, 有正常数据 → ✅ 数据行
|
||||
- Row 50: "总计", 有合并单元格 → ❌ 汇总行
|
||||
- Row 51: "总经理:...", "编制人:..." → ❌ 签名行
|
||||
- **正确数据范围 = A3:N49**(而非 A3:N51)
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+cells-get` | read | 单元格 |
|
||||
| `+dropdown-get` | read | 对象 |
|
||||
| `+csv-get` | read | 单元格 |
|
||||
| `+table-get` | read | 单元格 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+cells-get`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | A1 范围,如 `A1:F10`(不带 sheet 前缀;用 `--sheet-id` / `--sheet-name` 指定 sheet) |
|
||||
| `--include` | string_slice | optional | 要返回的信息类别,逗号分隔多个(可选值:`value` / `formula` / `style` / `comment` / `data_validation`) |
|
||||
| `--max-chars` | int | optional | 单次返回字符上限,默认 500000(兜底防爆)。大数据通常宜重定向落盘做分析;仅当要让结果直接进上下文、又不触发文件转存时才调小(如 25000),以 has_more 分页 |
|
||||
| `--skip-hidden` | bool | optional | 跳过隐藏行列,默认 `false` |
|
||||
|
||||
### `+dropdown-get`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | A1 范围,如 `A2:A100`(不带 sheet 前缀;用 `--sheet-id` / `--sheet-name` 指定 sheet) |
|
||||
|
||||
### `+csv-get`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | A1 范围,如 `A1:F30`(不带 sheet 前缀;用 `--sheet-id` / `--sheet-name` 指定 sheet) |
|
||||
| `--max-chars` | int | optional | 单次返回字符上限,默认 500000(兜底防爆)。大数据通常宜重定向落盘做分析;仅当要让结果直接进上下文、又不触发文件转存时才调小(如 25000),以 has_more 分页 |
|
||||
| `--include-row-prefix` | bool | optional | 是否在每行前加 `[row=N]` 前缀,默认 `true` |
|
||||
| `--skip-hidden` | bool | optional | 跳过隐藏行列,默认 `false` |
|
||||
|
||||
### `+table-get`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--sheet-id` | string | optional | 只读该子表(按 id);省略则读所有子表 |
|
||||
| `--sheet-name` | string | optional | 只读该子表(按名);省略则读所有子表 |
|
||||
| `--range` | string | optional | 读取的 A1 范围;省略则读每个子表的完整 used range(会跨过表中部的整行空行 / 整列空列,不会被截断) |
|
||||
| `--no-header` | bool | optional | 把第一行当数据而非表头(列名取 col1/col2 …) |
|
||||
|
||||
## Examples
|
||||
|
||||
### `+csv-get`
|
||||
|
||||
公共四件套:`--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`(前两者 XOR,后两者 XOR)。
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
# 简单读(sheet 定位必填:--sheet-name 或 --sheet-id 必给一个;range 的 Sheet1! 前缀不能替代它)
|
||||
lark-cli sheets +csv-get --url "https://example.feishu.cn/sheets/shtXXX" --sheet-name "Sheet1" --range "A1:F30"
|
||||
|
||||
# 用 sheet-name 模糊定位(运行时框架会先解析到 sheet-id)
|
||||
lark-cli sheets +csv-get --spreadsheet-token shtXXX --sheet-name "销售明细" --range "A1:F30"
|
||||
```
|
||||
|
||||
输出契约(envelope.data):
|
||||
|
||||
- `annotated_csv` — 含 `[row=N]` 前缀的 CSV 主入口
|
||||
- `col_indices` / `row_indices` — 列字母 / 行号映射数组
|
||||
- `current_region` — 从锚点扩展到被空行空列包围的连续区域的 A1 范围。⚠️ **它不是整表真实边界**:遇表中部整行空行 / 整列空列会截断、可能小于真实数据范围;表尾的汇总 / 签名 / 脚注又可能让它大于纯数据范围。判断整表是否读全须拿 `+workbook-info` 的物理 `row_count` 当上界交叉核对(见上方「`row_count` 与 `current_region` 都不能单独定末行」)
|
||||
- `row_count` / `col_count` — **本次返回的行 / 列数**(= `actual_range` 的尺寸,随 `--range` 变),**不是整表物理总行列数**;整表物理尺寸取 `+workbook-info`
|
||||
- `has_more` — 当前 `--range` 是否因 `--max-chars` 被截断(截断后续读接着用 `--range`);它**只反映本次 range 内是否读完**,`has_more=false` **不代表整表已读全**(range 之外的数据不在判断内)
|
||||
|
||||
> 要按列类型结构化读出(喂 DataFrame、或 round-trip 回 `+table-put`)用 `+table-get`(见下);`+csv-get` 给的是带 `[row=N]` 前缀的纯值快照,下游需要行号/列坐标时直接从前缀与 `col_indices` 取。
|
||||
|
||||
### `+cells-get`
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
# 读 A1:F10 的公式 + 样式(sheet 定位必填)
|
||||
lark-cli sheets +cells-get --url "https://example.feishu.cn/sheets/shtXXX" --sheet-name "Sheet1" \
|
||||
--range "A1:F10" --include formula,style
|
||||
```
|
||||
|
||||
> ⚠️ 调用方在 `cells[i][j]` 中**不能**用下标推真实行列:必须读 `ranges[n].row_indices[i]` / `ranges[n].col_indices[j]`。
|
||||
|
||||
### `+table-get`(飞书 → DataFrame,类型保真读出)
|
||||
|
||||
`+table-put`(写入侧,见 write-cells reference)的镜像:把表格读回与 `--sheets` 完全同构的 typed 协议(`sheets[]` + `columns:[列名]` + `data:[[行]]` + `dtypes:{列名:pandas_dtype}` + `formats?:{列名:number_format}` + `range`),可直接喂回 `+table-put` 或一行还原 DataFrame。
|
||||
|
||||
**默认(不带 `--range`)读取整张子表的完整 used range**:会跨过表中部的整行空行 / 整列空列,覆盖到真实数据边界。每个子表都回传实际读取的 `range`(如 `A1:F10`)——`+table-get` 不返回分页 / 截断标志,这个 `range` 是判断是否读全的唯一信号:拿它和源 xlsx 行列数、关键末行 / 末日期交叉核对,确认读取完整。仍要精确控制范围时显式传 `--range`。
|
||||
|
||||
列类型从每列 `number_format` 推断(日期格式→`date`/`datetime64[ns]`、数值→`number`/`float64`、bool→`bool`),`date` 列的序列号转回 ISO `yyyy-mm-dd`——日期、数字往返不丢类型。**列类型只在该列所有非空值一致时才定(`number` / `date` / `bool`);一列混了类型(如数字列混入「暂无」、日期列混入裸数字)会降为 `string`(dtypes 输出 `object`),让 `dtypes` 与 `data` 里每个值自洽——能 round-trip 回 `+table-put`、不让 pandas `astype` 崩。降级是无损的(脏值原样保留为文本);若要把零星脏值转成数值列,交给调用方在 pandas 侧做(`to_numeric(errors='coerce')`),那里原始值仍在、可追溯。** 默认读所有子表、第一行当表头(`--no-header` 把首行当数据、列名取 `col1` / `col2` …)。
|
||||
|
||||
```bash
|
||||
# 默认读所有子表 → sheets[](与 +table-put 的 --sheets 同构,可喂回或转 DataFrame)
|
||||
lark-cli sheets +table-get --url "<表URL>"
|
||||
# 可选:--sheet-name / --sheet-id 限定只读某一个子表(不给则读全部)
|
||||
lark-cli sheets +table-get --url "<表URL>" --sheet-name "销售"
|
||||
```
|
||||
|
||||
#### 输出 → DataFrame(用 `sheet_to_df` helper)
|
||||
|
||||
输出形状对齐 pandas split:`columns` 是列名数组、`data` 是二维数据、`dtypes` 是 `{列名: pandas_dtype_str}` 映射。直接喂给 `pd.DataFrame(...).astype(...)` 就能一次性还原所有列类型(不必逐列 `to_datetime` / `to_numeric`)。本 skill 把这段 2 行 helper 打包成可 import 的 [`scripts/sheets_df.py`](../scripts/sheets_df.py)(含 `df_to_sheet` 和 `sheet_to_df`,写入 / 读回成对):
|
||||
|
||||
```python
|
||||
from sheets_df import sheet_to_df
|
||||
|
||||
# 单 sheet
|
||||
df = sheet_to_df(out["data"]["sheets"][0])
|
||||
|
||||
# 多 sheet——按名字取
|
||||
sheets = {s["name"]: sheet_to_df(s) for s in out["data"]["sheets"]}
|
||||
df_sales = sheets["销售"]
|
||||
```
|
||||
|
||||
> 显示格式(千分位、百分比、自定义日期)在 `sheet["formats"]`,pandas 不消费;改完数据 round-trip 回去时透传给 `+table-put` 即可,飞书侧显示不变。
|
||||
|
||||
#### round-trip:读 → 改 → 写回(写读对偶)
|
||||
|
||||
`sheet_to_df` 和 `df_to_sheet` 一对镜像 helper([`scripts/sheets_df.py`](../scripts/sheets_df.py))让 round-trip 三段读 / 改 / 写各一行:
|
||||
|
||||
```python
|
||||
import json, subprocess
|
||||
from sheets_df import df_to_sheet, sheet_to_df
|
||||
|
||||
# 1. 读
|
||||
out = json.loads(subprocess.check_output(
|
||||
["lark-cli","sheets","+table-get","--url",URL,"--sheet-name","销售"]))
|
||||
sheet = out["data"]["sheets"][0]
|
||||
df = sheet_to_df(sheet)
|
||||
|
||||
# 2. 改(pandas 操作)
|
||||
df["营收"] = df["营收"] * 1.1
|
||||
|
||||
# 3. 写回(formats 是飞书侧显示格式,pandas 不消费,透传保留显示)
|
||||
payload = {"sheets": [df_to_sheet(df, sheet["name"], formats=sheet.get("formats"))]}
|
||||
subprocess.run(["lark-cli","sheets","+table-put","--url",URL,"--sheets","-"],
|
||||
input=json.dumps(payload).encode(), check=True)
|
||||
```
|
||||
|
||||
`sheet_to_df(sheet)` 消费 `(columns, data, dtypes)`,`df_to_sheet(df, name, formats=...)` 重新生成同样三个字段——读 / 写完全对偶,只有 `formats` 需要手工透传一次。
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate` 阶段只做 XOR 检查、Enum 合法性、防爆参数上限校验;**禁止**联网(如不能用 `--sheet-name` 提前去查 `sheet-id`)。
|
||||
- `DryRun` 输出请求模板:`--sheet-name` 在 dry-run 输出里生成为 `<resolve:销售明细>` 占位符,不实际解析。
|
||||
- `Execute` 阶段才进行 sheet-name → sheet-id 解析与 API 调用。
|
||||
@ -0,0 +1,111 @@
|
||||
# Lark Sheet Search & Replace
|
||||
|
||||
## 替换前 dry-run + 范围明确(替换前必做)
|
||||
|
||||
`+cells-replace` 的副作用是不可逆的(除非另写代码回滚)。执行前必须:
|
||||
|
||||
1. **明确替换范围**:必须显式说明"只替换 X 列 / X 区域,还是全表替换"。**禁止**默认全表替换——容易误改无关列。范围应由用户指令决定,模糊时主动询问。
|
||||
2. **dry-run 命中数量**:先用 `+cells-search` 在同一范围、同一关键词、同一匹配选项(大小写 / 精确 / 正则)下统计命中数量。把数量和**期望命中数**(用户明示的或基于业务理解推断的)对照——一致才进入 `+cells-replace`,不一致先排查(关键词太宽?范围太大?)。
|
||||
3. **替换后回读校验**:执行后再次 `+cells-search` 旧关键词,预期为 0;并对替换后的若干代表性单元格回读确认值符合预期。
|
||||
|
||||
## 使用场景
|
||||
|
||||
读写。在飞书表格中搜索和替换文本。本 reference 覆盖 2 个 shortcut:
|
||||
|
||||
| 操作需求 | 使用工具 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 搜索/定位文本 | `+cells-search` | 返回匹配的单元格位置,支持正则、精确匹配等 |
|
||||
| 查找并替换文本 | `+cells-replace` | 批量替换文本;`--regex` 模式下 `--replacement` 可用 `$1`、`$2` 引用 `--find` 的捕获组 |
|
||||
|
||||
**常见配置错误(必须注意)**:
|
||||
- **不要把操作动词当搜索词**:用户说"汇总金额"是一个操作动作(求和),不是要搜索"汇总金额"这个文本。只有当确实需要定位某个文本值的位置时才用 `+cells-search`
|
||||
- **不要用搜索来了解表格结构**:要了解表头和数据结构时,应使用 `+csv-get` 读取前几行,而不是用 `+cells-search` 逐个猜测字段名
|
||||
- **注意正则特殊字符**:使用正则匹配时,`.`、`*`、`(`、`)` 等特殊字符需要转义
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+cells-search` | read | 单元格 |
|
||||
| `+cells-replace` | write | 单元格 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+cells-search`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--find` | string | required | 待查找文本(与 `--regex` 配合时按正则解释) |
|
||||
| `--range` | string | optional | 查找范围(A1 格式);省略时整表 |
|
||||
| `--match-case` | bool | optional | 大小写敏感 |
|
||||
| `--match-entire-cell` | bool | optional | 完全匹配整个单元格 |
|
||||
| `--regex` | bool | optional | 把 `--find` 按正则解释 |
|
||||
| `--include-formulas` | bool | optional | 也在公式文本中搜索 |
|
||||
| `--max-matches` | int | optional | 防爆,默认 5000(隐藏 flag:不在 `--help` 列出,但可正常传入) |
|
||||
| `--offset` | int | optional | 跳过前 N 个匹配(分页用),默认 0 |
|
||||
|
||||
### `+cells-replace`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--find` | string | required | 待替换文本 |
|
||||
| `--replacement` | string | required | 替换为;传空字符串 `""` 等价于「删除内容」 |
|
||||
| `--range` | string | optional | 替换范围(A1 格式);省略时整表 |
|
||||
| `--match-case` | bool | optional | 大小写敏感 |
|
||||
| `--match-entire-cell` | bool | optional | 完全匹配整个单元格 |
|
||||
| `--regex` | bool | optional | 把 `--find` 按正则解释 |
|
||||
| `--include-formulas` | bool | optional | 也在公式文本中替换 |
|
||||
|
||||
## Examples
|
||||
|
||||
公共四件套:所有 shortcut 顶部排列 `--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`(XOR 规则)。
|
||||
|
||||
### `+cells-search`
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
# 普通查找
|
||||
lark-cli sheets +cells-search --url "https://example.feishu.cn/sheets/shtXXX" \
|
||||
--sheet-name "Sheet1" --find "张三"
|
||||
|
||||
# 正则 + 范围限定
|
||||
lark-cli sheets +cells-search --spreadsheet-token shtXXX --sheet-id "$SID" \
|
||||
--find "^[A-Z]{2}-\\d{4}$" --regex --range "A2:A1000"
|
||||
```
|
||||
|
||||
输出契约(envelope.data):
|
||||
|
||||
- `matches` — 命中 cell 列表,每条含 `address`(A1)+ `value` + `sheet_id`
|
||||
- `total_matches` — 匹配总数
|
||||
- `has_more` / `next_offset` — 分页游标(命中数超过单页上限时用于继续读取)
|
||||
|
||||
### `+cells-replace`
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
# 先 dry-run 预览
|
||||
lark-cli sheets +cells-replace --url "https://example.feishu.cn/sheets/shtXXX" \
|
||||
--sheet-name "Sheet1" --find "v1" --replacement "v2" --dry-run
|
||||
|
||||
# 确认后执行
|
||||
lark-cli sheets +cells-replace --url "https://example.feishu.cn/sheets/shtXXX" \
|
||||
--sheet-name "Sheet1" --find "v1" --replacement "v2"
|
||||
|
||||
# 正则捕获组:把 "2026-03" 重排成 "03/2026"($1/$2 引用 --find 的捕获组)
|
||||
lark-cli sheets +cells-replace --url "https://example.feishu.cn/sheets/shtXXX" \
|
||||
--sheet-name "Sheet1" --regex --find "(\\d{4})-(\\d{2})" --replacement "$2/$1" --dry-run
|
||||
```
|
||||
|
||||
> `+cells-replace` 虽然 Risk = write,但范围大或正则错可能改一堆。**强烈推荐工作流**:先 `+cells-search` 看匹配数,再 `+cells-replace --dry-run` 预览,最后真正执行。
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate`:XOR 公共四件套;`--find` 非空;正则模式下 `--find` 必须是合法正则。
|
||||
- `DryRun`:`+cells-search` 输出请求模板;`+cells-replace` 额外返回预估替换数(`would_replace_count`)。
|
||||
- `Execute`:写后不自动回读;如需确认,自行用 `+cells-search` 复查旧值是否已不再命中。
|
||||
@ -0,0 +1,212 @@
|
||||
# Lark Sheet Sheet Structure
|
||||
|
||||
## 结构性操作影响面预检(插入 / 删除行列前必做)
|
||||
|
||||
插入 / 删除行列、隐藏 / 取消隐藏、冻结、行列分组都会让原表的引用关系发生偏移。**操作前必须**先打印以下三类信息,并评估操作是否会让它们失效;否则禁止执行:
|
||||
|
||||
1. **当前合并单元格范围**(来自 `+sheet-info` 的 `merged_cells`):插入行 / 列时,跨过插入位置的合并区域可能扩张或断裂;删除行 / 列时合并区域可能直接消失。
|
||||
2. **现有公式的引用范围**(用 `+cells-get` 抽样附近行 + 跨表引用 + 透视表 / 图表 / 条件格式 / 筛选器的数据源 range):插入 / 删除会导致 `=SUM(B4:B13)` 这种相对引用偏移;如果操作发生在引用范围内部,可能产生 `#REF!`。
|
||||
3. **数据验证(下拉列表)规则的应用范围**:列表来源是某个区域时,区域被部分删除会让规则失效。
|
||||
|
||||
不可逆的影响必须先在回复中告知用户,得到确认再执行。
|
||||
|
||||
## 使用场景
|
||||
|
||||
读写。管理子表结构与布局。本 reference 覆盖 9 个 shortcut(按用途分两类):
|
||||
|
||||
| 操作需求 | 使用工具 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 查看子表布局 | `+sheet-info` | 获取行高、列宽、隐藏行列、行列分组、合并单元格等信息 |
|
||||
| 变更子表结构 | `+dim-{insert|delete|hide|unhide|freeze|group|ungroup|move}` | 插入/删除/隐藏/取消隐藏/冻结/分组/移动行列 |
|
||||
|
||||
注意:
|
||||
|
||||
- 当表格存在合并单元格时,应结合返回的 `merged_cells` 判断表头、分组标题和区域语义
|
||||
- 不要把合并区域中非左上角的空白单元格理解为"无内容";通常应将左上角单元格的内容视为整个合并区域的语义内容
|
||||
- 插入用 `+dim-insert`:`--position`(插入位置;行用 1-based 行号如 `3`,列用字母如 `C`,新行/列插在此位置**之前**)+ `--count`(插入数量,>0)。新行/列样式继承用 `--inherit-style`(`before`/`after`/`none`)
|
||||
- 例如"在第 20 行后新增 116 行":`--position 21 --count 116`("第 20 行后"即 1-based 行号 21)
|
||||
|
||||
**区间表达统一为 A1 风格**:所有涉及"一段连续行/列"的 shortcut 都用同一套 A1 闭区间字符串语法,**不存在 inclusive / exclusive / 0-based / 1-based 跨命令差异**:
|
||||
|
||||
| 命令 | 用什么 flag 表达区间 / 位置 | 例子 |
|
||||
| --- | --- | --- |
|
||||
| `+dim-insert` | `--position` + `--count` | `--position 3 --count 5`(在第 3 行前插 5 行)/ `--position C --count 2`(在 C 列前插 2 列) |
|
||||
| `+dim-delete` / `+dim-hide` / `+dim-unhide` / `+dim-group` / `+dim-ungroup` / `+rows-resize` / `+cols-resize` | `--range` | `"3:7"`(第 3-7 行,闭区间)/ `"C:F"`(C-F 列,闭区间)/ `"5"` 或 `"C"`(单行/列) |
|
||||
| `+dim-move` | `--source-range`(源区间)+ `--target`(目标位置) | `--source-range "3:7" --target 12`(把第 3-7 行移到第 12 行前)/ `--source-range "C:F" --target H` |
|
||||
|
||||
行用 1-based 数字、列用字母——跟 Excel / 飞书 UI 看到的行号、列字母完全一致。
|
||||
|
||||
**常见配置错误(必须注意)**:
|
||||
- **插入列直接用字母**:`+dim-insert` 的 `--position` 在列场景直接传字母(如 `C`),不要把列字母换算成 0-based 索引
|
||||
- **插入后引用偏移**:插入行/列后,原有数据的行号 / 列字母会发生偏移。如果插入后还需要对原有区域执行写入操作,必须重新计算偏移后的位置
|
||||
- **删除行列前先确认范围**:删除操作不可逆,执行前应确认 `--range` 精确无误。可先用 `+csv-get` 读取目标区域验证内容(`+csv-get` / `+cells-get` 见 `lark-sheets-read-data`)
|
||||
- **"在 D 列左侧新增一列"的正确写法**:`--position D --count 1`(新列插在 D 列之前);要继承左侧列样式加 `--inherit-style before`
|
||||
- **`+dim-move` 同维度约束**:`--source-range` 是行区间时 `--target` 必须是行号(数字),是列区间时 `--target` 必须是列字母——不可一行一列混用
|
||||
- **插入列后必须检查多行表头合并区域**:很多表格有 2-3 行的合并表头。插入列后,原有的合并区域不会自动扩展到新列。必须先用 `+sheet-info --include merges` 读取合并区域,插入后将跨越插入位置的合并区域重新设置(用 `+cells-{merge|unmerge}`),否则新列的表头会是空的、格式不连续
|
||||
- **公式写入范围跳过表头行**:写入公式时从数据行开始(不是第 1 行)。先确认表头占几行(可能 1-3 行),公式的起始行 = 表头行数 + 1
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+sheet-info` | read | 工作表 |
|
||||
| `+dim-insert` | write | 工作表 |
|
||||
| `+dim-delete` | high-risk-write | 工作表 |
|
||||
| `+dim-hide` | write | 工作表 |
|
||||
| `+dim-unhide` | write | 工作表 |
|
||||
| `+dim-freeze` | write | 工作表 |
|
||||
| `+dim-group` | write | 工作表 |
|
||||
| `+dim-ungroup` | write | 工作表 |
|
||||
| `+dim-move` | write | 工作表 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+sheet-info`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--include` | string_slice | optional | 要返回的结构信息类别,逗号分隔多个(可选值:`merges` / `row_heights` / `col_widths` / `hidden_rows` / `hidden_cols` / `groups` / `frozen`) |
|
||||
| `--range` | string | optional | 限定只返回该 A1 范围的结构信息;省略时返回整表 |
|
||||
|
||||
### `+dim-insert`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--inherit-style` | string | optional | 新行/列样式继承策略 enum:`before`(继承前一行/列)/ `after`(继承后一行/列)/ `none`(默认)(可选值:`before` / `after` / `none`) |
|
||||
| `--position` | string | required | 插入位置(在此行/列**之前**插入):行用 1-based 行号如 `3`;列用字母如 `C` |
|
||||
| `--count` | int | required | 插入数量(>0) |
|
||||
|
||||
### `+dim-delete`
|
||||
|
||||
_公共四件套 · 系统:`--yes`、`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | 要删除的行/列闭区间;行用 1-based 数字如 `3:7` 或单行 `5`,列用字母如 `C:F` 或单列 `C` |
|
||||
|
||||
### `+dim-hide`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | 要隐藏的行/列闭区间;行如 `3:7`,列如 `C:F` |
|
||||
|
||||
### `+dim-unhide`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | 要取消隐藏的行/列闭区间;行如 `3:7`,列如 `C:F` |
|
||||
|
||||
### `+dim-freeze`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--dimension` | string | required | 维度方向(行或列)(可选值:`row` / `column`) |
|
||||
| `--count` | int | required | 冻结前 N 行/列;传 0 解除冻结 |
|
||||
|
||||
### `+dim-group`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--depth` | int | optional | 嵌套分组的层级(创建到第几层),默认 1 |
|
||||
| `--group-state` | string | optional | 分组初始展开状态(可选值:`expand` / `fold`)(默认 `expand`) |
|
||||
| `--range` | string | required | 要创建分组的行/列闭区间;行如 `3:7`,列如 `C:F` |
|
||||
|
||||
### `+dim-ungroup`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--depth` | int | optional | 要取消的分组层级,默认 1(1=最外层,数字越大越内层) |
|
||||
| `--range` | string | required | 要取消分组的行/列闭区间;行如 `3:7`,列如 `C:F` |
|
||||
|
||||
### `+dim-move`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--source-range` | string | required | 要移动的源行/列闭区间;行如 `3:7`,列如 `C:F` |
|
||||
| `--target` | string | required | 目标位置(移到此行/列**之前**):行用 1-based 行号如 `12`,列用字母如 `H`。必须与 `--source-range` 同维度(行/列) |
|
||||
|
||||
## Examples
|
||||
|
||||
公共四件套:所有 shortcut 顶部排列 `--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`(XOR)。
|
||||
|
||||
### `+sheet-info`
|
||||
|
||||
输出契约:返回子表的行高 / 列宽 / 隐藏 / 合并 / 分组等布局元信息。
|
||||
|
||||
### `+dim-insert`
|
||||
|
||||
```bash
|
||||
# 在第 10 行前插 3 行,继承上方样式
|
||||
lark-cli sheets +dim-insert --url "https://example.feishu.cn/sheets/shtXXX" \
|
||||
--sheet-id "$SID" --position 10 --count 3 --inherit-style before
|
||||
|
||||
# 在 C 列前插 2 列
|
||||
lark-cli sheets +dim-insert --url "..." --sheet-id "$SID" --position C --count 2
|
||||
```
|
||||
|
||||
### `+dim-delete`
|
||||
|
||||
```bash
|
||||
# 删除第 5-7 行
|
||||
lark-cli sheets +dim-delete --url "..." --sheet-id "$SID" --range "5:7" --yes
|
||||
|
||||
# 删除 D-F 列
|
||||
lark-cli sheets +dim-delete --url "..." --sheet-id "$SID" --range "D:F" --yes
|
||||
```
|
||||
|
||||
### `+dim-hide` / `+dim-unhide`
|
||||
|
||||
```bash
|
||||
lark-cli sheets +dim-hide --url "..." --sheet-id "$SID" --range "5:7"
|
||||
lark-cli sheets +dim-unhide --url "..." --sheet-id "$SID" --range "5:7"
|
||||
lark-cli sheets +dim-hide --url "..." --sheet-id "$SID" --range "C:F"
|
||||
```
|
||||
|
||||
### `+dim-move`
|
||||
|
||||
```bash
|
||||
# 把第 3-7 行移到第 12 行前
|
||||
lark-cli sheets +dim-move --url "..." --sheet-id "$SID" --source-range "3:7" --target 12
|
||||
|
||||
# 把 C-F 列移到 H 列前
|
||||
lark-cli sheets +dim-move --url "..." --sheet-id "$SID" --source-range "C:F" --target H
|
||||
```
|
||||
|
||||
### `+rows-resize` / `+cols-resize`
|
||||
|
||||
> ⚠️ 这两条 shortcut 来自 `lark-sheets-range-operations` 的 `+rows-resize / +cols-resize` tool(分组在"工作表"是为了发现性)。详细参数和示例在 `lark-sheets-range-operations.md`。
|
||||
>
|
||||
> 常规写法:行高走 `--range` + `--height <px>`、列宽走 `--range` + `--width <px>`,无需再传 `--type`(等价于 `--type pixel`);多行 / 多列不同尺寸用 map 形态 `--heights` / `--widths`(如 `--widths '{"A":100,"C:E":120}'`)一次原子完成,不要拆多次调用或走 `+batch-update`。`--type standard` / `--type auto` 用于非像素模式,不能与像素 flag 同给。`+cols-resize.--type` 不接受 `auto`(列宽不支持自动适应)。⚠️ 单位是像素(不是 Excel 字符单位 / 磅)。
|
||||
|
||||
### `+dim-freeze`
|
||||
|
||||
```bash
|
||||
# 冻结前 1 行(--count 传 0 解除冻结)
|
||||
lark-cli sheets +dim-freeze --url "..." --sheet-id "$SID" --dimension row --count 1
|
||||
```
|
||||
|
||||
### `+dim-group` / `+dim-ungroup`(大纲)
|
||||
|
||||
> 仅当用户明确说"行分组 / 列分组 / 大纲 / outline"时触发;按字段做数据分组用 `+pivot-create`。
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate`:XOR 公共四件套;`--range` / `--source-range` 必须是合法 A1 闭区间(行用数字、列用字母,不可混用);`+dim-insert` 的 `--count` > 0;`+dim-move` 的 `--target` 必须与 `--source-range` 同维度(行 vs 列);`+dim-delete` 强制 `--yes` 或 `--dry-run`;`+rows-resize` / `+cols-resize` 的统一形态(`--range` + `--height`/`--width` 或 `--type`)与 map 形态(`--heights`/`--widths`)二选一、不可混用;详见 `lark-sheets-range-operations.md`。
|
||||
- `DryRun`:写操作输出"将要 PATCH 的目标范围 + 目标参数"。
|
||||
- `Execute`:写后不自动回读;如需确认,自行调用 `+sheet-info --include row_heights,col_widths,hidden_rows,hidden_cols,groups,frozen` 查看受影响的范围。
|
||||
149
.claude/skills/lark-sheets/references/lark-sheets-sparkline.md
Normal file
149
.claude/skills/lark-sheets/references/lark-sheets-sparkline.md
Normal file
@ -0,0 +1,149 @@
|
||||
# Lark Sheet Sparkline
|
||||
|
||||
## 真对象硬约束
|
||||
|
||||
当用户要求"迷你图 / 趋势线 / 单元格内图表"时,**必须**通过 `+sparkline-{create|update|delete}` 创建真实的迷你图对象。**禁止**用文本字符(如 `▁▂▃▅▇`)拼接在单元格里、或用 `SPARKLINE()` 公式函数(已禁用)代替。判断标准:交付后 `+sparkline-list` 必须能返回该对象。
|
||||
|
||||
## 使用场景
|
||||
|
||||
读写迷你图对象。本 reference 覆盖 4 个 shortcut:
|
||||
|
||||
| 操作需求 | 使用工具 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 查看已有迷你图 | `+sparkline-list` | 获取迷你图的类型、数据源和样式配置 |
|
||||
| 创建/更新/删除迷你图 | `+sparkline-{create|update|delete}` | 对迷你图执行写入操作 |
|
||||
|
||||
典型工作流:先读取现有迷你图了解配置 → 执行创建/更新/删除 → **必须再次读取验证结果**。
|
||||
|
||||
**常见配置错误(必须注意)**:
|
||||
- **数据源范围要精确**:迷你图的数据源范围必须与实际数据行列精确对应,范围偏移会导致图形展示错误
|
||||
- **不要与 SPARKLINE() 公式混淆**:飞书表格的 `SPARKLINE()` 公式函数已被禁用,迷你图只能通过 `+sparkline-{create|update|delete}` 的对象方式创建
|
||||
- **创建后必须验证**:调用 `+sparkline-list` 确认迷你图配置正确
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+sparkline-list` | read | 对象 |
|
||||
| `+sparkline-create` | write | 对象 |
|
||||
| `+sparkline-update` | write | 对象 |
|
||||
| `+sparkline-delete` | high-risk-write | 对象 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+sparkline-list`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--group-id` | string | optional | 按 group_id 过滤 |
|
||||
|
||||
### `+sparkline-create`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--properties` | string + File + Stdin(复合 JSON) | required | JSON:`{config(共享样式配置), sparklines(迷你图数组)}`;完整字段结构跑 `--print-schema` |
|
||||
|
||||
### `+sparkline-update`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--group-id` | string | required | 目标组 id |
|
||||
| `--properties` | string + File + Stdin(复合 JSON) | required | JSON:`{config, sparklines}`;先 `+sparkline-list --group-id <id>` 回读再 patch;完整字段结构跑 `--print-schema` |
|
||||
|
||||
### `+sparkline-delete`
|
||||
|
||||
_公共四件套 · 系统:`--yes`、`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--group-id` | string | required | 目标组 id |
|
||||
|
||||
## Schemas
|
||||
|
||||
> 复合 JSON flag 字段速查(只列顶层 + 一层嵌套)。深层结构看下方 `## Examples`,或用 `--print-schema` 读完整 JSON Schema(用法见 SKILL.md「公共 flag 速查」与「Agent 使用提示」)。
|
||||
|
||||
### `+sparkline-create` `--properties` / `+sparkline-update` `--properties`
|
||||
|
||||
_创建/更新/部分删除的迷你图属性_
|
||||
|
||||
**顶层字段**:
|
||||
- `config` (object?) — 迷你图样式配置, 相同 groupId 的迷你图共享相同的样式 { theme_type?: enum, non_num_show_as?: enum, empty_show_as?: enum, contain_hidden_cells?: boolean, series_color?: string, …共 13 项 }
|
||||
- `sparklines` (array<object>?) — 迷你图项列表 each: { sparkline_id?: string, position?: object, source?: string, source_range?: object }
|
||||
|
||||
## Examples
|
||||
|
||||
公共四件套:所有 shortcut 顶部排列 `--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`(XOR)。迷你图用 **两层 id** 管理——`group_id` 选组(一组同形态的迷你图共享类型 / 样式 / 数据源映射),`sparkline_id` 在组内选具体某一项。注意:不等同于已禁用的 `SPARKLINE()` 公式函数。
|
||||
|
||||
> **何时需要先 `+sparkline-list`:**
|
||||
> - `+sparkline-update`:**总是**需要——拿到组内每一项的 `sparkline_id`,回填到 `properties.sparklines[i]`,server 用它做映射。
|
||||
> - `+sparkline-delete`:**不需要** `sparkline_id`——CLI 仅支持按 `--group-id` 整组删除(该 shortcut 没有 `--properties`)。
|
||||
|
||||
### `+sparkline-list`
|
||||
|
||||
```bash
|
||||
# 列出整张子表的所有迷你图组
|
||||
lark-cli sheets +sparkline-list --url "..." --sheet-id "$SID"
|
||||
|
||||
# 钉到单组:返回该组每一项的 sparkline_id(update / partial-delete 必需)
|
||||
lark-cli sheets +sparkline-list --url "..." --sheet-id "$SID" --group-id "grpA"
|
||||
```
|
||||
|
||||
### `+sparkline-create`
|
||||
|
||||
> `--properties` 顶层只有 `config`(同组共享样式,如 `line_width` / `points` / `extremum_max` / `extremum_min`)和 `sparklines`(迷你图项数组)两个字段。`sparklines[i]` 每项必须含 `position`(落点 cell,`row` + `col`)+ `source`(数据 A1 范围,与 `source_range` 二选一);create 时 `sparkline_id` 可省略,由系统生成。
|
||||
|
||||
```bash
|
||||
lark-cli sheets +sparkline-create --url "..." --sheet-id "$SID" --properties @sparkline.json
|
||||
```
|
||||
|
||||
`sparkline.json` 示例(在 F 列嵌入两行折线迷你图,数据分别来自 A2:E2 和 A3:E3):
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"config": { "line_width": 2 },
|
||||
"sparklines": [
|
||||
{"position": {"row": 1, "col": "F"}, "source": "'Sheet1'!A2:E2"},
|
||||
{"position": {"row": 2, "col": "F"}, "source": "'Sheet1'!A3:E3"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `+sparkline-update`
|
||||
|
||||
> 两步式:先 `+sparkline-list --group-id <id>` 拿当前组的 `sparkline_id` 列表,再构造 `properties.sparklines[]`——**每项必须带 `sparkline_id`**。只改样式可只传 `properties.config`(不带 `sparklines`,整组样式覆盖式更新)。
|
||||
|
||||
```bash
|
||||
# 假设 +sparkline-list 已返回 group_id=grpA,组内 sparkline_id=sl_1 / sl_2
|
||||
lark-cli sheets +sparkline-update --url "..." --sheet-id "$SID" --group-id "grpA" --properties '{
|
||||
"sparklines": [
|
||||
{"sparkline_id":"sl_1","source":"'Sheet1'!A2:A20"},
|
||||
{"sparkline_id":"sl_2","source":"'Sheet1'!B2:B20"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### `+sparkline-delete`
|
||||
|
||||
> CLI 仅支持**整组删除**:传 `--group-id` 删掉该组全部迷你图。该 shortcut **没有** `--properties`,无法只删组内单项(需求上要"留一部分"时,改用 `+sparkline-update` 重写该组的 `sparklines` 列表,而不是 delete)。强制 `--yes` 或 `--dry-run`;先 `--dry-run` 确认要删的目标组。
|
||||
|
||||
```bash
|
||||
# 删整组
|
||||
lark-cli sheets +sparkline-delete --url "..." --sheet-id "$SID" --group-id "grpA" --yes
|
||||
```
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate`:
|
||||
- XOR 公共四件套;`+sparkline-{update,delete}` 必须 `--group-id`。
|
||||
- **`+sparkline-update`**:当 `properties.sparklines` 非空时,每一项必须含 `sparkline_id`(CLI 预检,错误信息会指回 `+sparkline-list`,避免命中服务端的不可读拒绝);只传 `properties.config`(config-only update)合法、不触发 sparkline_id 检查。
|
||||
- **`+sparkline-delete`**:只接 `--group-id`(整组删除),**没有** `--properties`,无法删组内单项。
|
||||
- `--properties`(仅 `+sparkline-create` / `+sparkline-update`)顶层只接 `config`(同组共享样式)和 `sparklines`(迷你图项数组);`+sparkline-create` 要求每个 `sparklines[i]` 含 `position` 与 `source`(或 `source_range`,二选一)。
|
||||
- `+sparkline-delete` 强制 `--yes` 或 `--dry-run`。
|
||||
- `DryRun`:写操作输出"将要 POST/PATCH/DELETE 的 sparkline group 请求模板"。
|
||||
- `Execute`:写后不自动回读;如需确认,自行调用 `+sparkline-list --group-id <id>` 查看 `config` / `sparklines`。
|
||||
@ -0,0 +1,205 @@
|
||||
# 飞书表格样式与配色规范
|
||||
|
||||
> **本文定位**:飞书表格"正确视觉输出"的取值标准与美化决策流——配色、表头、对齐、数值格式、斑马纹、列宽行高、图表展示,以及新增 / 继承 / 美化已有区域三类场景的做法。
|
||||
> **边界**:本文只讲"样式长什么样、怎么决策";**怎么调用工具写入样式**(`cell_styles` / `border_styles` 字段、合并、resize 等参数)见 `lark-sheets-write-cells` / `lark-sheets-range-operations` / `lark-sheets-batch-update`。**条件格式**(高亮 / 标红 / 数据条 / 色阶)见 `lark-sheets-conditional-format`。本文不含 shortcut,通用编辑准则见主 SKILL.md「飞书表格编辑准则」。
|
||||
|
||||
## 最高优先级原则
|
||||
|
||||
- **用户指令优先**:用户明确提出的格式要求(如"使用红色背景")具有最高权重,即使与通用审美冲突。
|
||||
- **继承原表风格**:编辑前先采样原文件视觉特征(色系、边框、对齐、数字格式),新增内容必须与之对齐。严禁对已有风格的文件强行施加通用标准化格式。
|
||||
- **扩展而非覆盖**:新增行列或追加数据时,目标是"扩展原模板"——继承邻近区域的表头风格、条纹节奏、边框层级、对齐方式、数字格式和列宽/行高策略。
|
||||
- **美化只动样式属性,不动数据**:对**已有区域**做美化时,**只能**修改 `font` / `fill` / `border` / `alignment` / `number_format` 这 5 类样式属性。**禁止**改动原始单元格的 `value` / `formula`、合并区域、行列结构、Sheet 名称。如果美化需求需要改变数据布局(例如"汇总行加进表里"),必须把"加汇总行"和"美化"拆成两步,前者属于编辑动作、需另行得到用户授权。
|
||||
- **不可见视觉属性也属保护对象**:原表的**合并范围、对齐方式(H-Align/V-Align)、行高列宽、数字格式**是用户能感知但不一定会明示的视觉属性。即使用户没说"保留这些",**禁止**因写入新内容而修改它们;写公式 / 写值 / 写新列时只传 `value` / `formula`,不要重置 `alignment` / `number_format` 等字段为默认值(重置等同于改动)。**例外**:用户明示要修改这些属性时(如"调整对齐 / 合并 / 列宽")才能动。
|
||||
- **美化范围必须覆盖所有用户语义目标**:用户说"给表格加边框 / 美化整个表"时,范围 = 实际数据区域**含所有数据行**(含汇总行、总计行、表尾备注行),不能停在"看起来主体内容结束"的地方。落地前先用 `current_region` + 末尾 5~10 行核对真实末行(同 `lark-sheets-read-data` 的「确定数据范围的正确流程」),再设置美化范围。范围漏掉用户提到的目标行 / 列**直接判失败**。
|
||||
|
||||
## 美化任务 5 维度 checklist(用户说"美化 / 整理 / 让表更清晰 / 适合打印"时必做)
|
||||
|
||||
当用户用"美化 / 整理表格 / 让表清晰 / 适合打印 / 调整样式"等口语表达**主动美化需求**时,**必须**遍历以下 5 个维度逐一落地,**只动一处就交付**(如只加边框)属于违规:
|
||||
|
||||
1. **表头格式区分**:表头行加粗 + 背景色填充(与数据行有色差)+ 居中对齐;多行表头时全部行同步处理
|
||||
2. **对齐方式**:文本列左对齐、数值 / 货币 / 百分比列右对齐、日期 / 分类列居中;垂直方向统一居中
|
||||
3. **数值格式**:每列统一小数位 + 千分位(用 `number_format`);金额列统一货币符号;同一列内**禁止**出现 0 位 / 1 位 / 2 位小数混杂
|
||||
4. **边框**:覆盖范围按上方「美化范围必须覆盖所有用户语义目标」规则(含汇总 / 总计 / 表尾说明行),内外框线清晰
|
||||
5. **列宽 + 行高 + 自动换行**:详细规则见 `lark-sheets-range-operations` 的「写入后列宽自适应」章节(按最长字符数扩列宽 / 长文本设置 `cell_styles.word_wrap="auto-wrap"` + 调高行高 / 长数字设置 `number_format` 防科学计数法)
|
||||
|
||||
**差异化标注场景**:用户要求"重复行 / 异常值 / 重要项视觉区分"时,标注列 / 行必须设置与普通数据**显著不同**的 `cell_styles`(背景色 + 加粗 + 字体色至少改一项),不能与普通数据格式完全一致。
|
||||
|
||||
**显式要求边框 / 表头 / 对齐时同样按上面标准落地**(不必等用户说"美化"):① 用户说"给某矩形区域加边框"必须**整个矩形含表头行、数据行、汇总行全部加内外框**,落地后核起 / 末行、末列三边界(反例:要求加边框的区域实际无任何边框);② **新建表头前先确认哪一行才是表头**——别把已有的第一行数据误当表头刷成蓝底白字,真正该加的表头列也要建出来(反例:把第一行数据误设成了表头样式);③ 新增 / 编辑区域的字号必须与原表一致,禁止 13 号与 14 号、10 号与 11 号混杂(反例:新列字号与原表不一致)。
|
||||
|
||||
## 通用样式规范
|
||||
|
||||
> 以下取值标准都在「最高优先级原则」的**继承原表风格 / 扩展而非覆盖**前提下生效:凡涉及"沿用原表"的条目,遵循该原则即可,本节不再逐条复述。
|
||||
|
||||
### 1. 表头样式
|
||||
|
||||
- 表头/汇总行须与数据区域有明确视觉区分。
|
||||
- 使用低饱和度背景色搭配字体颜色(如深蓝 + 白字,浅蓝 + 黑字),文字加粗、水平居中。
|
||||
- 表头覆盖多列时使用合并单元格。
|
||||
|
||||
### 2. 数据区域样式
|
||||
|
||||
- 减少垂直线条,优先使用水平浅灰细线。
|
||||
- **对齐方式**:文本左对齐,数值/货币/百分比右对齐,日期或分类居中,所有内容垂直居中。
|
||||
- 次要信息(备注、次要日期等)使用缩小字号或浅灰色。
|
||||
- **Zebra Stripes**:数据行 > 10 行时可使用交替背景色引导视线。
|
||||
- 设置前先清理原区域背景色为白色(#FFFFFF),再设置斑马纹色,避免新旧混杂。
|
||||
- 优先直接设置单元格背景色,而非条件格式(除非用户要求)。
|
||||
- 推荐配色:奇数行 #FFFFFF,偶数行 #F3F4F6 或 #EBF1F8。
|
||||
|
||||
### 3. 数值格式
|
||||
|
||||
- 百分比使用 `%` 符号,适当注明单位和货币符号(¥、$)。
|
||||
- 大于 1000 的数字使用千分位符,保留一致的小数位数(1–2 位)。
|
||||
- 涉及数据检索的须注明数据来源。
|
||||
- 可使用数据条/色阶/条件格式增强可视化。
|
||||
|
||||
### 4. 整体结构
|
||||
|
||||
- 长表/宽表考虑冻结行列,方便滚动查看。
|
||||
- **长文本处理**:启用自动换行,行高合理调整以确保阅读舒适,添加适当垂直留白,目标是清晰、专业、不拥挤的布局。
|
||||
- 保持表格简洁,合理分组(可用合并单元格展示分组),在适当位置添加合计或汇总行。
|
||||
- **区域分隔**:多阶段或多类别时,使用柔和背景色块进行逻辑分区,而非简单边框。
|
||||
- **增删行列的样式规则**:
|
||||
- 新增整列继承同组列的表头样式、列宽、对齐和数字格式;新增整行继承同层级数据行或汇总行风格,避免写成表头风格。追加列时需判断是否应加入已有合并单元格(常见于顶部标题行)。
|
||||
- 若追加位置紧邻汇总行、说明区或空白分隔区,先判断真实数据区域边界再操作,避免破坏原有结构。
|
||||
- **Zebra Stripes 维护**:插入或删除行后若影响后续行奇偶性,须从受影响行往后重建条纹(先清理再重设)。少量增删用局部重建,大量变动用全局清理+统一重建。
|
||||
- 具体采样与复制流程见下方「场景二:从已有区域继承美化」。
|
||||
- **列宽 / 行高调整**(飞书 `+cols-resize` / `+rows-resize` 直接给像素值:统一尺寸用 `--range` + `--width`/`--height <px>`,多列 / 多行不同尺寸用 `--widths`/`--heights` map 一次原子完成,如 `--widths '{"A":100,"C:E":120}'`):
|
||||
- 禁止硬编码固定列宽,须根据该列实际内容长度估算像素。
|
||||
- 经验估算:中文每字约 15-18px,英文/数字每字约 7-9px,外加 10-16px padding。
|
||||
- 上下限建议 80~400px;超上限启用自动换行(`word_wrap: auto-wrap`)+ 调整行高,而非无限加宽。
|
||||
- 合并单元格不参与列宽计算,避免撑宽单列。
|
||||
- 复制自原文件的列优先沿用原列宽,不重新计算覆盖。
|
||||
|
||||
### 5. 配色
|
||||
|
||||
- 优先沿用原表色板与明暗层级(见「继承原表风格」),新增区域不凭空换色,确保视觉连续。
|
||||
- 背景填充选择柔和色(如浅蓝 `#DDEBF7`),区分颜色时优先同一主题色不同深浅,避免超过 3 种主题色。
|
||||
|
||||
### 6. 图表展示
|
||||
|
||||
- 遵循用户指令选择图表类型,或匹配用户意图(饼图/环形图 → 占比,折线图 → 趋势)。
|
||||
- 包含必要元素:标题、图例、数据标签、坐标轴标题。
|
||||
- 调整至合适大小,避免数据和标签过多堆叠。
|
||||
- **图表放置防重叠**:新增图表前须计算放置区域,避免与已有图表重叠。具体步骤:
|
||||
1. 调用 `+chart-list` 获取当前工作表所有已有图表的 `position`(锚点单元格:`col` 是列字母如 "A"/"B"、`row` 是 1-based 行号;以 `+chart-list` 实际返回字段为准)、`offset`(锚点内偏移:`row_offset`、`col_offset`,单位像素)以及 `size`(`width`、`height`,单位像素)。
|
||||
2. 获取工作表的行高和列宽信息(像素)。
|
||||
3. 根据每个图表的锚点 `position.row`/`position.col` + 偏移 `offset.row_offset`/`offset.col_offset` + 尺寸 `size.width`/`size.height`,结合行高列宽,计算出每个已有图表覆盖的像素矩形区域 `(x_min, y_min, x_max, y_max)`。
|
||||
4. 为新图表选定大小后,候选放置位置应避开所有已有矩形区域;若存在重叠则向下或向右偏移,直至找到无冲突位置。
|
||||
5. 若工作表已无足够空间,优先向下方空白区域放置,保持图表间至少 1 行或 1 列的间距。
|
||||
|
||||
> 飞书表格中颜色需带 `#` 前缀(如 `#0070C0`),与 openpyxl 的无前缀写法不同。
|
||||
> 具体工具调用参数格式,请读取对应工具 skill(`lark-sheets-write-cells`、`lark-sheets-conditional-format`、`lark-sheets-range-operations` 等)。
|
||||
|
||||
---
|
||||
|
||||
## 场景化操作指南
|
||||
|
||||
### 场景一:新增独立样式
|
||||
|
||||
> 适用情况:在表格中创建全新的、具有独立视觉特征的区域,如汇总行、新表头、独立数据表等。
|
||||
|
||||
#### 1A. 添加汇总行 / 表头行
|
||||
|
||||
**决策流程:**
|
||||
1. 先用 `+cells-get` 读取目标位置上方的数据区域,确认数据边界和已有样式(背景色、字体大小等)
|
||||
2. 如果需要新增空行,先用 `+dim-{insert|delete|hide|unhide|freeze|group|ungroup}` 插入行
|
||||
3. 用 `+cells-set` 写入汇总公式 + 特殊样式(背景色区分 + 加粗 + 边框)
|
||||
4. 如果汇总行标题需要跨列显示,追加 `+cells-{merge|unmerge}` 合并标题区域
|
||||
|
||||
**样式要点:**
|
||||
- 汇总行使用比数据区域更深的同色系背景(如数据区 #EBF1F8 → 汇总行 #D6E4F0 或 #4472C4 + 白字)
|
||||
- 必须加粗,水平对齐方式与数据列一致(数值列右对齐,文本列左对齐)
|
||||
- 上方加一条较粗的边框线,与数据区域形成视觉分隔
|
||||
|
||||
#### 1B. 添加独立数据表/独立区域
|
||||
|
||||
**决策流程:**
|
||||
|
||||
1. 新建 sheet,或用 `+cells-get` 或 `+workbook-info` 确认已有表格的占用范围,找到空闲区域
|
||||
2. 用 `+cells-get` 采样已有表格的表头样式(背景色、字体大小、字重、对齐方式)和数据区域样式
|
||||
3. 新表头复用已有表头的配色和字体参数(保持风格统一),但内容和列宽可独立
|
||||
4. 新数据区域复用已有数据区域的对齐规则、边框风格、数字格式
|
||||
5. 用 `+cells-set` 一次性写入新表头 + 数据
|
||||
|
||||
**样式要点:**
|
||||
- 必须复用:背景色色系、字体大小、字重、边框风格
|
||||
- 可以独立:列宽、行高、具体数字格式(根据新数据的类型调整)
|
||||
- 新旧表格之间至少留 1~2 行空白作为视觉分隔
|
||||
|
||||
### 场景二:从已有区域继承美化
|
||||
|
||||
> 适用情况:新增的行/列/区域与已有内容性质相同(数据类型、层级一致),需要无缝衔接已有格式。
|
||||
|
||||
#### 2A. 继续补充行/列(数据性质与已有内容一致)
|
||||
|
||||
**核心规则**:采样紧邻 2 行 → 判断并延续 Zebra Stripes 奇偶性 → 按 write-cells 的继承清单带齐样式写入。
|
||||
|
||||
**斑马纹延续要点**(本节只管"奇偶判断"这一标准,"带哪些样式字段写入"的机制见下方指针):
|
||||
|
||||
- 至少读 2 行(末行 + 倒数第二行)才能判断是否有斑马纹交替色
|
||||
- 若倒数两行背景色不同(如 #FFFFFF 与 #F3F4F6),新行按奇偶延续,不要固定一个色
|
||||
|
||||
> 具体继承哪些字段、怎么采样与写入(`+cells-get` 读源行 `cell_styles` + `border_styles`、`+sheet-info --include row_heights,merges` 读行高合并、带齐 6 类样式写入)见 `lark-sheets-write-cells` 的「新增列 / 新增行的样式继承」章节——`border_styles` 四边最易遗漏,以那里为准。
|
||||
|
||||
#### 2B. 基于模板区域的修改(copy 保留所有格式)
|
||||
|
||||
**核心思路:三步分层法**
|
||||
|
||||
```
|
||||
Step 1 — 格式铺开:`+batch-update` + `+range-copy`(或 `+range-fill`)
|
||||
└── 将模板行/区域的 **全部格式**(样式、边框、数字格式、数据验证等)复制到目标区域
|
||||
└── 推荐用 `+range-copy --paste-type formats`(仅复制格式,目标值/公式保留),即"格式刷"
|
||||
└── 若需连带公式平移填充(如公式列结构一致),改用 `+range-fill --series-type copy` 或 `+range-copy --paste-type all`
|
||||
|
||||
Step 2 — 内容覆写:`+batch-update` + `+cells-set`(仅传 value/formula,不传任何样式)
|
||||
└── 将每行的实际数据写入,cell_styles 全部省略,因为格式已在 Step 1 中就位
|
||||
|
||||
Step 3 — 微调收尾:`+rows-resize --heights` / `+cols-resize --widths`(行高列宽 map 一次原子完成)、`+batch-update` + `+cells-{merge|unmerge}` 等
|
||||
└── 调整行高列宽、处理合并单元格、扩展条件格式范围等边缘情况
|
||||
```
|
||||
|
||||
**关键注意事项:**
|
||||
- Step 1 用 `+range-copy --paste-type formats` 时只铺格式、不动值/公式,Step 2 再用 `+cells-set` 写值即可(`+cells-set` 默认覆盖,无需额外 flag);若 Step 1 用 `--paste-type all` 连带复制了值/公式,Step 2 写入同样会覆盖(默认行为)
|
||||
- `+range-fill --series-type auto`(或 `linear`/`date`)会自动递增数字序列(1→2→3)和日期序列,`+range-fill --series-type copy` 则原样复制值但公式引用会自动平移
|
||||
- 如果模板区域存在合并单元格,copy/fill 不会复制合并状态,必须在 Step 3 中用 `+cells-{merge|unmerge}` 补全
|
||||
- 如果模板区域有条件格式,需要在 Step 3 中通过 `+cond-format-update` 扩展 ranges
|
||||
|
||||
**场景:纯"格式刷"(用户说"把 A 列样式应用到 B 列"、"格式复制过去"、"只刷格式不改数据")**
|
||||
|
||||
单步即可,无需三步分层:调用 `+range-copy --paste-type formats`,`--source-range` 为样式来源、`--target-range` 为目标起点。参数细节见 `lark-sheets-range-operations`。
|
||||
|
||||
### 场景三:已有区域格式美化
|
||||
|
||||
> 适用情况:对已存在数据的区域进行格式美化(不改变数据内容),重点处理表头、汇总行等特殊行的识别与格式设置,需特别注意合并单元格的安全操作。
|
||||
|
||||
#### 整体操作流程
|
||||
|
||||
```
|
||||
1. 探查阶段
|
||||
├── `+workbook-info` → 获取子表列表、行列数、冻结位置
|
||||
├── `+sheet-info --include merges` → 获取合并区域
|
||||
├── `+cells-get`(前几行 + 末尾几行,`--include style`)→ 采样表头/数据区/汇总行样式
|
||||
└── 分析结果 → 建立区域地图(表头行号、数据起止行号、汇总行号、合并区域列表)
|
||||
|
||||
2. 规划阶段
|
||||
├── 判断表头行:通常第 1 行或前 2 行,特征为加粗/背景色/合并/居中
|
||||
├── 判断汇总行:通常最后 1~2 行,特征为加粗/SUM/AVERAGE 公式/更深背景色
|
||||
├── 判断合并区域:从 `+cells-get` 返回中识别(多个单元格同值且样式相同通常暗示合并)
|
||||
└── 制定美化方案:按区域分别设置样式
|
||||
|
||||
3. 执行阶段(按顺序)
|
||||
├── 先处理合并单元格(如需取消合并再重新合并,必须先 unmerge 再 merge)
|
||||
├── 设置表头样式
|
||||
├── 设置数据区域样式
|
||||
├── 设置汇总行样式
|
||||
└── 调整列宽行高
|
||||
```
|
||||
|
||||
#### 美化中的合并单元格要点
|
||||
|
||||
- 编辑前先识别已有合并区域(见探查阶段),避免破坏原有语义分区。
|
||||
- 美化表头/分组标题时,若需修改合并区域的范围或样式,遵循"先 `unmerge` → 修改 → 再 `merge`"顺序。
|
||||
- 合并区域样式只写左上角,不要对合并内的其他单元格重复写入样式。
|
||||
|
||||
> 合并单元格完整的安全操作规则(含数据保护、样式占位等 5 条)见 `lark-sheets-range-operations` 的 `+cells-{merge|unmerge}` 章节。
|
||||
420
.claude/skills/lark-sheets/references/lark-sheets-workbook.md
Normal file
420
.claude/skills/lark-sheets/references/lark-sheets-workbook.md
Normal file
@ -0,0 +1,420 @@
|
||||
# Lark Sheet Workbook
|
||||
|
||||
## Sheet 结构变更保守化(编辑类任务必做)
|
||||
|
||||
`+sheet-{create|delete|rename|move|copy|hide|unhide|set-tab-color}` 会改变原表的物理结构,是高副作用动作。执行前必须遵守:
|
||||
|
||||
1. **删除 / 重命名 / 隐藏 / 移动原 Sheet 需用户明示**:除非用户明示要这些操作,**禁止**擅自对**已存在**的 Sheet 执行 delete / rename / hide / move。新建 Sheet 是允许的(用于承载中间结果或透视表 / 图表对象),但应优先在原表右侧加列;只有当中间结果数量较大或会与原数据混淆时,才新建空白 Sheet(同 R1)。
|
||||
2. **Sheet 级操作前先列清单**:调用 `+sheet-{create|delete|rename|move|copy|hide|unhide|set-tab-color}` 之前,必须先调用 `+workbook-info`,把"当前所有 Sheet 名 + 可见性 + 行列数"列出来,再决定是否操作。禁止跳过列清单直接 create / delete / rename。
|
||||
3. **删除 / 重命名前向用户确认**:删除是不可逆的,重命名会让其他公式 / 透视表 / 图表的数据源失效——执行前必须在回复里确认"将删除 / 改名 X,影响 Y 个引用"。
|
||||
|
||||
## 使用场景
|
||||
|
||||
读写。管理工作簿结构。本 reference 覆盖 14 个 shortcut:
|
||||
|
||||
| 操作需求 | 使用工具 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 查看工作簿结构 | `+workbook-info` | 获取子表列表、名称、行列数、冻结位置等元数据 |
|
||||
| 获取当前 revision | `+revision-get` | 获取当前文档 revision(版本号),可作为 recover / undo / changeset 复核的版本锚点 |
|
||||
| 新建工作簿(可预填数据) | `+workbook-create` | 从内存数据建一张新表(`--values` / `--sheets` typed) |
|
||||
| 导入本地文件为新表 | `+workbook-import` | 把本地 `.xlsx` / `.xls` / `.csv` 导入为新的飞书电子表格 |
|
||||
| 导出工作簿到本地 | `+workbook-export` | 导出为本地 `.xlsx`(整簿)或单子表 `.csv` |
|
||||
| 变更工作簿结构 | `+sheet-{create|delete|rename|move|copy|hide|unhide|set-tab-color}` | 新建/删除/移动/重命名/复制/隐藏子表、修改标签颜色 |
|
||||
| 切换子表网格线显隐 | `+sheet-show-gridline` / `+sheet-hide-gridline` | 显示 / 隐藏单个子表的网格线 |
|
||||
|
||||
注意:
|
||||
|
||||
- 如果用户请求包含多个动作,例如"先重命名,再新建工作表",请按顺序发起多次调用,覆盖全部动作
|
||||
- `create` 时若用户指定了工作表名称,应显式传入 `sheet_name`;不要省略后依赖默认命名
|
||||
- 若 `+workbook-info` 返回包含 `warning_message`,说明部分 `sheet_id` 已失效(被删除/改名或输入错误),应停止复用这些 id,重新不带 `sheet_ids` 全量获取结构后再继续操作
|
||||
|
||||
**常见配置错误(必须注意)**:
|
||||
- **获取结构是第一步**:任何表格操作前必须先调用 `+workbook-info`,不要跳过直接操作。返回的行列数、子表列表是后续所有操作的基础
|
||||
- **sheet_id 不要写错**:从 `+workbook-info` 返回值中精确获取 `sheet_id`,不要手动拼写或从 URL 中猜测
|
||||
- **优先使用 `sheet_id`**:虽然飞书表格不允许子表重名,但 `sheet_id` 是稳定标识符,跨多轮操作时不会因用户中途重命名而失效
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+workbook-info` | read | 工作簿 |
|
||||
| `+revision-get` | read | 工作簿 |
|
||||
| `+sheet-create` | write | 工作簿 |
|
||||
| `+sheet-delete` | high-risk-write | 工作簿 |
|
||||
| `+sheet-rename` | write | 工作簿 |
|
||||
| `+sheet-move` | write | 工作簿 |
|
||||
| `+sheet-copy` | write | 工作簿 |
|
||||
| `+sheet-hide` | write | 工作簿 |
|
||||
| `+sheet-unhide` | write | 工作簿 |
|
||||
| `+sheet-set-tab-color` | write | 工作簿 |
|
||||
| `+sheet-hide-gridline` | write | 工作簿 |
|
||||
| `+sheet-show-gridline` | write | 工作簿 |
|
||||
| `+workbook-create` | write | 工作簿 |
|
||||
| `+workbook-export` | read | 工作簿 |
|
||||
| `+workbook-import` | write | 工作簿 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+workbook-info`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
|
||||
_仅含公共 / 系统 flag。_
|
||||
|
||||
### `+revision-get`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
|
||||
_仅含公共 / 系统 flag。_
|
||||
|
||||
### `+sheet-create`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--title` | string | required | 新工作表名称 |
|
||||
| `--index` | int | optional | 插入位置(0-based);省略时附加到末尾 |
|
||||
| `--row-count` | int | optional | 初始行数(默认 200,上限 50000) |
|
||||
| `--col-count` | int | optional | 初始列数(默认 20,上限 200) |
|
||||
| `--type` | string | optional | 新子表类型:sheet(电子表格);默认 sheet(可选值:`sheet`) |
|
||||
|
||||
### `+sheet-delete`
|
||||
|
||||
_公共四件套 · 系统:`--yes`、`--dry-run`_
|
||||
|
||||
_仅含公共 / 系统 flag。_
|
||||
|
||||
### `+sheet-rename`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--title` | string | required | 新名称 |
|
||||
|
||||
### `+sheet-move`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--index` | int | required | 目标位置(0-based) |
|
||||
| `--source-index` | int | optional | 源位置(0-based);standalone 调用时可选,未传时由 CLI runtime 根据 `--sheet-id` / `--sheet-name` 当前在工作簿中的 index 自动派生。但在 `+batch-update` 内不可省(须显式传)——batch 中途无法发起结构查询自动派生 |
|
||||
|
||||
### `+sheet-copy`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--title` | string | optional | 副本名称;省略时由服务端生成 |
|
||||
| `--index` | int | optional | 副本插入位置(0-based);省略时附加到末尾 |
|
||||
|
||||
### `+sheet-hide`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
_仅含公共 / 系统 flag。_
|
||||
|
||||
### `+sheet-unhide`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
_仅含公共 / 系统 flag。_
|
||||
|
||||
### `+sheet-set-tab-color`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--color` | string | required | Hex 色值如 `#FF0000`,传空 `""` 清除 |
|
||||
|
||||
### `+sheet-hide-gridline`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
_仅含公共 / 系统 flag。_
|
||||
|
||||
### `+sheet-show-gridline`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
_仅含公共 / 系统 flag。_
|
||||
|
||||
### `+workbook-create`
|
||||
|
||||
_系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--title` | string | required | 新 spreadsheet 标题 |
|
||||
| `--folder-token` | string | optional | 目标文件夹 token;省略时放在云空间根目录 |
|
||||
| `--values` | string + File + Stdin(简单 JSON) | optional | untyped 初始数据,一个 JSON 二维数组(表头并入第一行):`[["列A","列B"],["alice",95]]`;值原样写入、类型由飞书自动识别(日期 / 数字会落成文本,需类型保真改用 --sheets),走与 --sheets 相同的分批 `+cells-set`;配 --styles 控制格式/颜色/合并/行列尺寸 |
|
||||
| `--sheets` | string + File + Stdin(复合 JSON) | optional | 建表后写入的 typed 表格协议 JSON(同 +table-put):顶层 `{"sheets":[...]}`,每个数组项是一张子表 `{name, start_cell?, mode?, header?, allow_overwrite?, columns:["colA","colB",...], data:[[...]], dtypes?:{colA:pandasDtype, ...}, formats?:{colA:numberFormat, ...}}` —— `name` 与外层 `sheets` 数组都不可省。Agents 用 `scripts/sheets_df.py` 的 `df_to_sheet(df, name)` 把 DataFrame 转成一项再包 `{"sheets":[...]}`。与 --values 互斥;新表默认子表复用为第一个子表,日期/数字类型保真。 |
|
||||
| `--styles` | string + File + Stdin(复合 JSON) | optional | 建表时同时写入的视觉处理操作 JSON:顶层 `{styles:[...]}`,每项对应一个目标子表、含 `name`,并至少给 `cell_styles` / `row_sizes` / `col_sizes` / `cell_merges` 之一。`cell_styles` 用 A1 单元格 range + 扁平样式字段(字段同 +cells-set-style,含 number_format / 颜色 / 对齐 / border_styles);row/col sizes 用行/列范围 + type/size;merges 用单元格 range + 可选 merge_type。与 --sheets 搭配时 styles 数组长度/顺序/name 必须与 --sheets.sheets 对应;与 --values 搭配时只给一个 styles 项(其 name 忽略)。完整 cell_styles 字段结构跑 `+workbook-create --print-schema --flag-name styles`。 |
|
||||
|
||||
### `+workbook-export`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--file-extension` | string | optional | 导出文件格式;`csv` 模式必须配 `--sheet-id`(可选值:`xlsx` / `csv`)(默认 `xlsx`) |
|
||||
| `--sheet-id` | string | optional | 仅 csv 模式必填:指定要导出哪张 sheet 为 CSV。这是 `+workbook-export` 专有 flag,与公共四件套的 sheet 定位无关(本 shortcut 不接受公共 sheet 定位) |
|
||||
| `--output-path` | string | optional | 本地保存路径;省略时**只触发并轮询导出任务、不下载文件**(返回 file_token / status,便于稍后续传)。要落盘传具体路径(如 `./out.xlsx`)或目录(如 `.`,服务端给的文件名落在该目录下)。注意:对应的 `lark-cli drive +export --doc-type sheet` 走 `--output-dir` / `--file-name` / `--overwrite` 三 flag 且默认下载到当前目录——本 wrapper 把它们合成单一 `--output-path` 简化常见用例,但默认不下载,需要的话也可改用 `drive +export`。 |
|
||||
|
||||
### `+workbook-import`
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--file` | string | required | 本地文件路径(.xlsx / .xls / .csv) |
|
||||
| `--folder-token` | string | optional | 目标文件夹 token;省略则导入到云空间根目录 |
|
||||
| `--name` | string | optional | 导入后表格名称;省略则用本地文件名(去掉扩展名) |
|
||||
|
||||
## Schemas
|
||||
|
||||
> 复合 JSON flag 字段速查(只列顶层 + 一层嵌套)。深层结构看下方 `## Examples`,或用 `--print-schema` 读完整 JSON Schema(用法见 SKILL.md「公共 flag 速查」与「Agent 使用提示」)。
|
||||
|
||||
### `+workbook-create` `--sheets`
|
||||
|
||||
_一个或多个子表的 typed 数据,每个数组元素写入一张子表;支持多 DataFrame → 多子表一次写入_
|
||||
|
||||
**数组项**(类型 object):
|
||||
- `name` (string) — 目标子表名
|
||||
- `start_cell` (string?) — 写入起点单元格(A1 记法,如 "B2"),默认 "A1"
|
||||
- `mode` (enum?) — overwrite(默认):从 start_cell 起写「表头 + 数据」块;append:把数据追加到子表已有数据下方(默认不重复表头) [overwrite / append]
|
||||
- `header` (boolean?) — 是否写一行列名表头
|
||||
- `allow_overwrite` (boolean?) — 为 false 时,若写入会落在非空单元格则拒写以保护原数据(返回 partial_success)
|
||||
- `columns` (array<string>) — 列名字符串数组,顺序与 `data` 中每行取值一一对应
|
||||
- `data` (array<array<string|number|boolean|null>>) — 数据行;每行是一个数组,长度必须等于 `columns` 数
|
||||
- `dtypes` (object?) — 可选
|
||||
- `formats` (object?) — 可选
|
||||
|
||||
### `+workbook-create` `--styles`
|
||||
|
||||
|
||||
**数组项**(类型 object):
|
||||
- `cell_merges` (array<object>?) — 单元格合并操作数组;range 使用 A1 单元格范围,merge_type 默认 all each: { merge_type?: enum, range: string }
|
||||
- `cell_styles` (array<object>?) — 单元格样式操作数组;每项用 A1 单元格 range 指定范围,字段名与 +cells-set-style 对齐 each: { background_color?: string, border_styles?: object, font_color?: string, font_family?: string, font_line?: enum, …共 13 项 }
|
||||
- `col_sizes` (array<object>?) — 列宽操作数组;range 使用列范围如 A:C,type 为 pixel/standard,pixel 需要 size each: { range: string, size?: number, type: enum }
|
||||
- `name` (string) — 子表名
|
||||
- `row_sizes` (array<object>?) — 行高操作数组;range 使用行范围如 1:3,type 为 pixel/standard/auto,pixel 需要 size each: { range: string, size?: number, type: enum }
|
||||
|
||||
## Examples
|
||||
|
||||
公共四件套:所有 shortcut 顶部排列 `--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`(XOR)。`+workbook-info` 只用前两者;`+sheet-*` 系列对单个工作表操作,需 `--sheet-id` 或 `--sheet-name`。
|
||||
|
||||
### `+workbook-info`
|
||||
|
||||
输出契约:返回 `sheets[]`,每个含 `sheet_id` / `title`(工作表显示名;旧 payload 用 `sheet_name`,读取时优先取 `title`、缺失再回退 `sheet_name`)/ `index` / `resource_type` / `row_count` / `column_count` / `is_hidden`,以及计数字段 `merged_cells_count` / `chart_count` / `pivot_table_count` / `float_image_count`(无 `frozen_*` 字段,冻结信息请用 `+sheet-info` 读取)。是操作飞书表格的第一步——任何后续 sheet 级动作都需要先拿这里的 sheet_id。
|
||||
|
||||
> **子表类型 `resource_type`**:`sheet`(普通网格子表)/ `bitable`(内嵌的多维表格子表)/ `#UNSUPPORTED_TYPE`(其它暂不支持的嵌入子表)。
|
||||
> - 网格类操作(读写单元格 / 区域 / 样式 / CSV / 筛选 / 透视 / 图表等)**仅适用于 `sheet`**。对 `bitable` / `#UNSUPPORTED_TYPE` 子表执行网格操作会被直接拒绝并返回明确报错,不再静默出错。
|
||||
> - 要操作 `bitable` 子表里的数据:该子表条目会附带 `bitable_app_token` + `bitable_table_id` 两个字段,直接用多维表格命令操作,例如 `lark-cli base +record-list --base-token <bitable_app_token> --table-id <bitable_table_id>`(记录增删改查、字段、视图等整套 `lark-cli base` 命令均可用)。不要走 sheets 网格命令。
|
||||
> - `bitable` / `#UNSUPPORTED_TYPE` 子表条目**只含** `sheet_id` / `sheet_name` / `index` / `resource_type`(bitable 另加上述两个 token)以及 `is_hidden` / `tab_color`;**不输出** `row_count` / `column_count` / `merged_cells_count` / `chart_count` / `pivot_table_count` / `float_image_count` / `frozen_*` 等网格指标(对非网格子表无意义)。
|
||||
> - tab 管理类操作(`+sheet-rename` / `+sheet-move` / `+sheet-delete` / `+sheet-hide` 等)对任意 `resource_type` 的子表都合法,不受此限制。
|
||||
|
||||
### `+revision-get`
|
||||
|
||||
输出契约:返回单个 `revision` 字段,即当前文档版本号。它是 recover / undo / `+changeset-get` 的版本锚点:如果刚执行过一次读写操作,也可以直接复用那次响应里的 `revision`;当只想单独取当前版本号、且不需要其它结构信息时,用 `+revision-get` 最直接。
|
||||
|
||||
### `+workbook-create`
|
||||
|
||||
新建电子表格,可选预填数据。两种数据入口(untyped `--values` / typed `--sheets` JSON)**互斥**,按需选一——两者都走同一条分批写入:
|
||||
|
||||
```bash
|
||||
# 1) untyped:--values(一个二维数组,表头并入第一行;值原样写、类型由飞书自动识别,
|
||||
# 日期会落成文本,配 --styles 控制格式)
|
||||
lark-cli sheets +workbook-create --title "销售" \
|
||||
--values '[["门店","销售额"],["北京",259874]]'
|
||||
|
||||
# 2) typed JSON:--sheets(一步建表 + 类型保真)。date 列落成真日期(可排序/透视)、
|
||||
# number 不丢精度、string 列保前导零(如订单号 00123);多子表一次建。
|
||||
lark-cli sheets +workbook-create --title "交易" --sheets '{
|
||||
"sheets":[
|
||||
{"name":"明细",
|
||||
"columns":["日期","金额","单号"],
|
||||
"dtypes":{"日期":"datetime64[ns]","金额":"float64","单号":"object"},
|
||||
"formats":{"金额":"#,##0.00"},
|
||||
"data":[["2024-01-15",1234.5,"00123"]]}
|
||||
]}'
|
||||
```
|
||||
|
||||
`--sheets` 协议与 `+table-put` 完全同构(字段含义见 lark-sheets-write-cells 的 `+table-put`,大 payload 走 stdin / `@file`)。关键差异:**新建工作簿的默认子表会被复用为第一个子表**(重命名后承载数据),不会残留空 `Sheet1`;其余子表按需新建。它把 `+table-put` 单独做不到的"建表 + typed 写入"合到一条命令,是「pandas 算完直接落地一张带真日期的新表」的首选。回读校验用 `+table-get`(与 `--sheets` 同构、可 round-trip)。
|
||||
|
||||
> 💡 pandas DataFrame 走 `--sheets` 时直接 `from sheets_df import df_to_sheet`([`scripts/sheets_df.py`](../scripts/sheets_df.py),与 `+table-put` 共用同一份 helper),多子表场景 helper 优势更明显:
|
||||
> ```python
|
||||
> payload = {"sheets": [df_to_sheet(income, "Income Statement"),
|
||||
> df_to_sheet(balance, "Balance Sheet"),
|
||||
> df_to_sheet(cashflow, "Cash Flow")]}
|
||||
> ```
|
||||
|
||||
`--styles` 可在建表写入时同时写视觉处理。它和 `--sheets` 一样只有一种外层写法:顶层对象里放 `styles` 数组;数组每项对应一个子表,含 `name`,并按能力拆成四类可选数组:
|
||||
|
||||
- `cell_styles`:像 `+cells-set-style`,用 A1 单元格 `range` 加扁平样式字段(`font_weight` / `background_color` / `horizontal_alignment` / `vertical_alignment` / `number_format` 等)和可选 `border_styles`;这些样式会随内容在同一次写入里一并应用。完整字段跑 `+workbook-create --print-schema --flag-name styles`。
|
||||
- `cell_merges`:用 A1 单元格 `range` 设置合并,`merge_type` 默认为 `all`,可选 `rows` / `columns`。
|
||||
- `row_sizes`:用行范围(如 `1:3`)设置行高,`type` 为 `pixel` / `standard` / `auto`;`pixel` 需要 `size`。
|
||||
- `col_sizes`:用列范围(如 `A:C`)设置列宽,`type` 为 `pixel` / `standard`;`pixel` 需要 `size`。
|
||||
|
||||
同一单元格命中多个 `cell_styles` 项时,后面的操作继续合并覆盖已传字段。`cell_merges` / `row_sizes` / `col_sizes` 在内容写入后顺序执行。
|
||||
|
||||
```bash
|
||||
# 3) untyped:仍用 {"styles":[...]},只有一个子表样式项(name 忽略);range 覆盖 --values 初始区域
|
||||
lark-cli sheets +workbook-create --title "销售" \
|
||||
--values '[["门店","销售额"],["北京",259874],["上海",198320]]' \
|
||||
--styles '{
|
||||
"styles":[
|
||||
{"name":"Sheet1","cell_styles":[
|
||||
{"range":"A1:B1","font_weight":"bold","background_color":"#f5f5f5","horizontal_alignment":"center","vertical_alignment":"middle"},
|
||||
{"range":"B2:B3","number_format":"#,##0"}
|
||||
]}
|
||||
]
|
||||
}'
|
||||
|
||||
# 4) typed 单子表:--styles.styles[0].name 必须对应 --sheets.sheets[0].name
|
||||
lark-cli sheets +workbook-create --title "交易" --sheets '{
|
||||
"sheets":[
|
||||
{"name":"明细",
|
||||
"columns":["日期","金额"],
|
||||
"dtypes":{"日期":"datetime64[ns]","金额":"float64"},
|
||||
"formats":{"金额":"#,##0.00"},
|
||||
"data":[["2024-01-15",1234.5]]}
|
||||
]}' --styles '{
|
||||
"styles":[
|
||||
{"name":"明细",
|
||||
"cell_styles":[
|
||||
{"range":"A1:B1","font_weight":"bold","background_color":"#f5f5f5",
|
||||
"border_styles":{"bottom":{"style":"solid","weight":"thin","color":"#000000"}}},
|
||||
{"range":"A2:A2","number_format":"yyyy-mm-dd"},
|
||||
{"range":"B2:B2","number_format":"#,##0.00","font_color":"#0f7b0f"}
|
||||
],
|
||||
"cell_merges":[{"range":"A1:B1"}],
|
||||
"col_sizes":[{"range":"A:B","type":"pixel","size":120}],
|
||||
"row_sizes":[{"range":"1:1","type":"pixel","size":28}]}
|
||||
]
|
||||
}'
|
||||
|
||||
# 5) typed 多子表:styles 数组和 sheets 数组长度、顺序、name 都必须一致
|
||||
lark-cli sheets +workbook-create --title "经营看板" --sheets '{
|
||||
"sheets":[
|
||||
{"name":"收入","columns":["月份","收入"],"dtypes":{"收入":"int64"},"formats":{"收入":"#,##0"},"data":[["2026-05",1200000]]},
|
||||
{"name":"成本","columns":["月份","成本"],"dtypes":{"成本":"int64"},"formats":{"成本":"#,##0"},"data":[["2026-05",730000]]}
|
||||
]}' --styles '{
|
||||
"styles":[
|
||||
{"name":"收入","cell_styles":[
|
||||
{"range":"A1:B1","font_weight":"bold","background_color":"#f0f7ff"},
|
||||
{"range":"B2:B2","font_color":"#0f7b0f"}
|
||||
]},
|
||||
{"name":"成本","cell_styles":[
|
||||
{"range":"A1:B1","font_weight":"bold","background_color":"#fff7ed"},
|
||||
{"range":"B2:B2","font_color":"#b42318"}
|
||||
]}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
> ⚠️ **`+workbook-create` 是把内存里的数据写成新表;要把已有的本地 Excel/CSV 文件原样导入成新表,用 `+workbook-import`**(见下),不要先在本地读出文件再 `+workbook-create` 重灌。
|
||||
|
||||
### `+workbook-import`
|
||||
|
||||
把已有的本地 `.xlsx` / `.xls` / `.csv` 文件导入为一个**新的**飞书电子表格(异步任务 + 内置轮询),与 `+workbook-export`(导出)对称,固定导入为电子表格类型。
|
||||
|
||||
```bash
|
||||
# 导入到云空间根目录;表格名默认取本地文件名(去掉扩展名)
|
||||
lark-cli sheets +workbook-import --file ./data.xlsx
|
||||
|
||||
# 指定目标文件夹与导入后表格名
|
||||
lark-cli sheets +workbook-import --file ./report.csv --folder-token <FOLDER_TOKEN> --name "月度报表"
|
||||
```
|
||||
|
||||
- **不接受任何 spreadsheet / sheet 定位 flag**(它是新建,不操作已有表):只有 `--file`(必填)/ `--folder-token` / `--name`。
|
||||
- 本地表格文件 → 飞书电子表格一律用本命令,**不要**用 `drive +import` 导电子表格——它是 sheets 之外的通用导入、还需额外指定 `--type`,绕路且更易错。只有要把本地表格导入成**多维表格**(bitable)时,才改用 `lark-cli drive +import --type bitable`。
|
||||
- 返回 `token` / `url`(导入完成的新表格)/ `ticket` / `ready` / `job_status`;未在内置轮询窗口内完成时返回 `timed_out=true` 与续查命令 `next_command`。
|
||||
|
||||
### `+workbook-export`
|
||||
|
||||
把飞书电子表格导出为本地 `.xlsx`(整工作簿)或单子表 `.csv`(异步任务 + 内置轮询 + 可选下载)。
|
||||
|
||||
```bash
|
||||
# 1) 只创建并轮询导出任务,不下载(默认):返回 file_token / status 便于稍后续传
|
||||
lark-cli sheets +workbook-export --url "https://example.feishu.cn/sheets/shtXXX"
|
||||
|
||||
# 2) 下载到具体文件名
|
||||
lark-cli sheets +workbook-export --url "..." --output-path ./report.xlsx
|
||||
|
||||
# 3) 下载到目录(保留服务端给的文件名)
|
||||
lark-cli sheets +workbook-export --url "..." --output-path ./downloads/
|
||||
|
||||
# 4) csv 模式必须传 --sheet-id(API 一次只导一张子表)
|
||||
lark-cli sheets +workbook-export --url "..." --file-extension csv --sheet-id "$SID" --output-path ./sheet.csv
|
||||
```
|
||||
|
||||
> ⚠️ **默认不下载**:省略 `--output-path` 时只触发并轮询导出任务,不写本地文件——给「先排队再续传」用例留出口。要落盘必须显式给 `--output-path`。
|
||||
>
|
||||
> **与 `drive +export --doc-type sheet` 的关系**:本 wrapper 是它的特化封装,固定 `--doc-type sheet`,并把 drive 的 `--output-dir` / `--file-name` / `--overwrite` 三 flag 折叠成单一 `--output-path` 简化常见用例。代价是默认值不同:`drive +export` 默认下载到当前目录、本 wrapper 默认不下载。需要细控目录/文件名/是否覆盖的,回退到 `drive +export --doc-type sheet`。
|
||||
|
||||
### `+sheet-create`
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
lark-cli sheets +sheet-create --url "https://example.feishu.cn/sheets/shtXXX" \
|
||||
--title "汇总" --index 0
|
||||
```
|
||||
|
||||
> 💡 `+sheet-create` 只建一张**空子表**。要在已有工作簿里建子表并一步写入 typed 数据和/或样式,用 `+table-put`(payload 里命名的子表缺则自动新建)配合它的 `--sheets` / `--styles`,省掉先建表再 `+cells-set` / `+cells-set-style` 的二次往返。
|
||||
|
||||
### `+sheet-delete`
|
||||
|
||||
> ⚠️ 工作表删除不可逆;先 `--dry-run` 看输出 sheet_id + title 确认是要删的那张。
|
||||
|
||||
### `+sheet-rename`
|
||||
|
||||
```bash
|
||||
lark-cli sheets +sheet-rename --url "..." --sheet-id "$SID" --title "汇总"
|
||||
```
|
||||
|
||||
### `+sheet-move`
|
||||
|
||||
standalone 路径在缺 `--source-index` / 只给 `--sheet-name` 时会自动发起一次 `+workbook-info` 读把它们解出来。
|
||||
|
||||
> ⚠️ **在 `+batch-update` 内调用 `+sheet-move`**:必须同时显式传 `--sheet-id`、`--source-index` 和 `--index`(目标位置)。batch 中途无法发起结构查询,且 `--index` 不显式给会静默落到默认位置 0,所以 batch translator 强制要求三者都显式。
|
||||
|
||||
### `+sheet-copy`
|
||||
|
||||
```bash
|
||||
# --title 省略时由服务端生成副本名
|
||||
lark-cli sheets +sheet-copy --url "..." --sheet-id "$SID" --title "副本"
|
||||
```
|
||||
|
||||
> 💡 `+sheet-copy` 连**公式 / 合并 / 分组底色 / 列宽 / 条件格式**一起整表复制。"照一张现成子表批量造结构相同的新子表"(如参考模板给每份数据各建一张同构子表)时,先 `+sheet-copy` 复制模板再用 `+cells-*` 只改数据,比从零 `+sheet-create` + 重建公式 / 样式省一大截,也天然满足"公式 / 分组 / 颜色照搬"。要把本地文件 / 数据并入**已有工作簿**当子表时走它(或 `+sheet-create`),别用 `+workbook-import` / `+workbook-create`——那两条只会新建独立表。
|
||||
|
||||
### `+sheet-hide` / `+sheet-unhide`
|
||||
|
||||
```bash
|
||||
lark-cli sheets +sheet-hide --url "..." --sheet-id "$SID"
|
||||
lark-cli sheets +sheet-unhide --url "..." --sheet-id "$SID"
|
||||
```
|
||||
|
||||
### `+sheet-set-tab-color`
|
||||
|
||||
```bash
|
||||
# Hex 色值;传空字符串 "" 清除标签色
|
||||
lark-cli sheets +sheet-set-tab-color --url "..." --sheet-id "$SID" --color "#FF0000"
|
||||
```
|
||||
|
||||
### `+sheet-show-gridline` / `+sheet-hide-gridline`
|
||||
|
||||
```bash
|
||||
# 切换子表网格线显隐;二态语义在命令名里,无需额外参数(同 +sheet-hide/+sheet-unhide)
|
||||
lark-cli sheets +sheet-show-gridline --url "..." --sheet-id "$SID"
|
||||
lark-cli sheets +sheet-hide-gridline --url "..." --sheet-id "$SID"
|
||||
```
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate`:XOR 公共四件套;`+sheet-create` 校验 `--title` 非空、`--row-count` ≤ 50000、`--col-count` ≤ 200;`+sheet-delete` 必须 `--yes` 或 `--dry-run`;`+workbook-create` 的 `--sheets` 与 `--values` **互斥**,给了 `--sheets` 则按 typed 协议校验 payload(其余约束同 `+table-put`)。
|
||||
- `DryRun`:`+sheet-*` 写操作输出"将要 PATCH 的 sheet metadata";`--sheet-name` 在 dry-run 输出里生成为 `<resolve:Sheet1>` 占位符,不实际解析为 sheet-id。
|
||||
- `Execute`:写操作不自动回读;如需确认目标 sheet 的新状态,自行调用 `+workbook-info`。
|
||||
575
.claude/skills/lark-sheets/references/lark-sheets-write-cells.md
Normal file
575
.claude/skills/lark-sheets/references/lark-sheets-write-cells.md
Normal file
@ -0,0 +1,575 @@
|
||||
# Lark Sheet Write Cells
|
||||
|
||||
## 写入边界 + 回读校验(编辑类任务必做)
|
||||
|
||||
1. **明确写入边界**:写入前必须能回答"目标 range 的起止行列号是多少?是否落在用户授权范围内?"。除用户明示要修改的区域外,禁止扩张到原数据列以外或新建 Sheet。
|
||||
2. **完整性断言**:批量写入前先把"预期写入条数"硬编码到代码里(如要填 106 条翻译 → `expected = 106`),写完后回读断言 `actual == expected`。少于预期就继续写,禁止交付半成品。
|
||||
3. **回读抽样校验**:写完关键值 / 公式后,用 `+csv-get` 或 `+cells-get` 重新读取写入区域,至少抽样 3-5 个代表性单元格(首 / 中 / 末),核对值与预期一致(与本地脚本计算的预期值对照)。公式特定的"先验证模板再 --copy-to-range / 修完再读回"细则见下方相关章节。
|
||||
4. **护原表 · 派生产物落点(写排名 / 标记 / 汇总 / 改写列时易丢数据)**:派生结果一律写到**真实末列 +1 的全新空列**或新建子表,**禁止复用任何已有原数据列**——哪怕该列看起来"空",也要先 `+csv-get` 回读确认整列无原始数据再写。三条准则:① 不把新公式 / 新值写进原数据列(典型反例:把新算的排名公式写进了原本存放另一份原始数据的列,整列原始数据被覆盖丢失);② 不改写、不合并原表头字段名(典型反例:把几个独立表头字段合并成一列,原字段名丢失);③ 慎用 `--allow-overwrite`:它一旦让写入区盖到相邻原始列 / 行就是不可逆数据丢失,加它之前必须用 `+sheet-info` / `+csv-get` 核清目标 range 不含任何原始数据。
|
||||
|
||||
## 新增列 / 新增行的样式继承(防止视觉风格不一致)
|
||||
|
||||
新增列 / 新增行**必须**先用 `+cells-get` 读相邻原列 / 原行的完整样式作为模板,**禁止**只传 `value` 期望默认样式与原表一致——飞书新单元格默认对齐通常是 `H:right, V:bottom`,与多数原表的 `H:center, V:middle` 不一致。
|
||||
|
||||
**完整继承清单**(写新列 / 新行时 cells 数组必须同时携带):
|
||||
|
||||
1. `cell_styles.font_family` / `cell_styles.font_size` / `cell_styles.font_weight` / `cell_styles.font_color` / `cell_styles.font_style`(字体名称 / 字号 / 粗细 / 颜色 / 斜体等)
|
||||
2. `cell_styles.horizontal_alignment` / `cell_styles.vertical_alignment`(H-Align / V-Align)—— 漏继承会导致新列对齐与原列不一致(常见)
|
||||
3. `cell_styles.number_format`(小数位 / 千分位 / 百分比 / 日期格式)—— 漏继承会导致同列数值格式混乱
|
||||
4. `cell_styles.background_color`(背景色)
|
||||
5. `border_styles`(四边框)
|
||||
6. **`merged_cells`(合并范围)**——续写场景必查:用 `+sheet-info --include merges` 读原数据区域的合并信息。**原行有跨列合并**(如标题行 `A1:G1` 合并)时,新行**必须**用 `+cells-{merge|unmerge}` 工具复制相同合并模式到新行(如续写第 3 个周报块的标题行 `A23:G23` 必须合并)。仅传 cells 数组的 5 类样式不够——合并范围要单独靠 `+cells-{merge|unmerge}` 工具落地(典型反例:续写多周记录表时,新增周次的标题行未合并,视觉上与原前几周风格不一致)
|
||||
|
||||
**采样模板的正确做法**:
|
||||
- 表头新列 → 读相邻表头单元格(如新加 D1 → 读 A1/B1/C1 任一)
|
||||
- 数据新列 → 读相邻数据行单元格(如新加 M5:M100 → 读 L5 / L6 / L7)
|
||||
- 续写新行 → 读最近一行已有数据(如续写第 20 行 → 读 19 行所有列)
|
||||
|
||||
**反模式**(违规):
|
||||
- 只传 `{"value": "四级菜单"}` 给 D1,不传 `cell_styles` → D1 默认非加粗、非居中,与 A1/B1/C1 风格断裂
|
||||
- 新列 M5 写入 `=SUM(F5:L5)` 时只传 `formula`,不传 `cell_styles.horizontal_alignment / vertical_alignment / number_format` → M 列对齐变 `H:right`,数字格式变默认
|
||||
|
||||
## 长数字防科学计数法(数值列写入必查)
|
||||
|
||||
写入或计算结果可能产生长数字(≥ 12 位整数 / 高精度小数)的列,**必须**在 `cell_styles.number_format` 显式设置非通用格式,否则飞书会自动用科学计数法显示,用户看到的就是"内容被截断 / 看不清原值"。
|
||||
|
||||
| 场景 | 必加的 `number_format` |
|
||||
|---|---|
|
||||
| 长整数(订单号 / 身份证 / 单据号) | `"0"` 或 `"@"`(强制文本,避免精度丢失) |
|
||||
| 金额 / 千分位 | `"#,##0.00"` |
|
||||
| 百分比 | `"0.00%"` |
|
||||
| 数量 / 计数 | `"0"`(整数) |
|
||||
| 日期 | `"yyyy-mm-dd"` 或 `"yyyy/m/d"` |
|
||||
|
||||
**典型反例**:长数字列(如审批单号、流水号)未设 `number_format`,飞书显示为 `1.23E+15`,用户复制出来已经丢失精度。
|
||||
|
||||
> **数字还是文本,按"数据本质是量值还是标识符"二选一 —— 不看当下要不要计算**:金额 / 百分比 / 比率 / 计数 / 度量这类**本质是量值**的数据,一律以**数字类型**写入(百分比存小数 `0.54` 配 `number_format:"0%"`),**不要**设 `@` 文本格式。**这与"用户当下是否要排序 / 求和"无关**——数据类型由数据本质决定、不由当下用途决定:表格数据几乎总会被后续排序 / 图表 / 二次计算复用,`"54%"` 文本与数值列混排本就破坏一致性,且数字 + `number_format` 显示效果与文本**完全相同**,没有任何理由选文本。**最常见的误判就是"这只是 leaderboard / 报表 / 看板展示,又不用算,写成 `54%` 字符串就行"——这是错的,展示用途不改变"百分比是数值"的事实。**(`+table-put` 用 `dtypes` 声明 `int64` / `float64`;版式 `+table-put` 装不下时用 `+cells-set` 传数字 + `number_format`;都别在本地拼成带 `$` / `%` 的字符串走 `+csv-put`。)反过来,编号 `001`、规格 `3-1`、身份证 / 电话 / 单据号等**本质是标识符 / 标签**、要原样保留不被飞书自动解释的内容(否则 `001`→`1`、`3-1`→日期、点分日期 `12.10`→`12.1`(尾零丢失)、长号→科学计数),才以**字符串类型**写入(`dtypes` 设 `object`)并把 `number_format` 设为 `"@"`(文本格式),字面保真。
|
||||
|
||||
## 使用场景
|
||||
|
||||
写入。向飞书表格的单元格区域写入值、公式、样式、批注、图片或下拉,也可批量写入 CSV / DataFrame。本 reference 覆盖 6 个 shortcut,按数据来源 + 内容形态选:
|
||||
|
||||
| 场景 | 用这个 shortcut | 原因 |
|
||||
|------|----------------|------|
|
||||
| 模型手里已经有 CSV 文本(小规模手动构造、从 `+csv-get` 取到后简单加工) | `+csv-put` | 直接传 CSV 文本 + `--start-cell`,不用自己拼二维 cells 数组;必要时自动扩容行列 |
|
||||
| 列里有数值语义的数据(数字 / 金额 / 百分比 / 日期 / 计数)→ 飞书,要类型保真(来源不限:DataFrame、Counter、dict、list 都算) | `+table-put` | typed 协议(外层 `{"sheets":[{"name":"…","columns":[...],"data":[[...]],"dtypes":{...},"formats":{...}}]}`,**只有这四件套字段**):`dtypes` 用 pandas dtype 串声明列类型(`int64` / `float64` / `datetime64[ns]` / `bool` / `object`),`formats` 给每列展示格式(千分位 / 百分比 / 日期)。**date 落真日期、金额 / 百分比 / 计数等数值列保精度且带 `number_format`(可排序 / 求和 / 入图表)**、string 保前导零,多 sheet 一次写 |
|
||||
| 写入含样式、批注、图片、数据校验等任意富写入 | `+cells-set` | 唯一支持完整富字段的 shortcut(公式 `+csv-put` 也能写) |
|
||||
| 只改已有 cell 的样式,不动 value/formula | `+cells-set-style` | 拍平 10 个样式字段为独立 flag;不触发不必要的值写入 |
|
||||
| 单 cell 嵌入图片 | `+cells-set-image` | 比 `+cells-set` 参数更简短 |
|
||||
| 在**已有区域**局部补表头样式/边框 | 先用 `+csv-put` 写值,再用 `+cells-set-style` 补样式 | 分工配合,入参最短 |
|
||||
| **新建子表 / 整表成套美化**(哪怕全是纯文本) | `+table-put --sheets … --styles …` 一步带值 + 全套样式(区域底色 / 边框 / 列宽 / 行高 / 合并;payload 里不存在的 sheet 名自动建子表) | `--styles` 与列是否 typed 无关,纯文本同样适用;比「写值 + 多次刷样式」少好几次调用 |
|
||||
|
||||
**选命令按内容形态分流(不设"默认首选")**:① 列有数值语义(金额 / 百分比 / 日期 / 计数)→ `+table-put`(`dtypes` 声明类型 + `formats` 设展示格式),版式装不下时 → `+cells-set` 传数字 + `number_format`;② 要样式 / 批注 / 图片 / 富文本 → `+cells-set`;③ **仅**全文本、无数值语义的内容平铺 → `+csv-put`(入参最短)。判据详见上方「数字还是文本」。
|
||||
|
||||
⚠️ `+csv-put` 可写值或公式:以 `=` 开头的单元格会被当作公式计算(读回时 `formula` 字段保留、`value` 为计算结果)。**公式内部含逗号 / 引号 / 换行时必须按 RFC 4180 转义**——含逗号的字段整格用双引号包裹、字段内部的引号再翻倍:如 `=COUNTIF(D5:D22,"及格")` 必须写成 `"=COUNTIF(D5:D22,""及格"")"`(外层双引号包裹整格,内部 `"及格"` 的引号翻倍成 `""及格""`)。漏转义会被 CSV 解析器按逗号拆列、整块写入区域错位(如本该 `G4:H6` 错成 `G4:K4`),详见下方 `+csv-put` 示例。**因此含逗号 / 引号 / 换行的公式优先改用 `+cells-set`(JSON 二维数组)写入——`cells[r][c].formula` 字段直接放公式串,零 CSV 转义负担,从根上避免拆列错位**(`+table-put` 的 typed 协议只接受 `columns / data / dtypes / formats` 四件套、没有 `formula` 字段,公式写入只能走 `+cells-set` / `+csv-put`)。此外 `+csv-put` **不会**携带样式/批注/图片,也无法把 `=` 开头的内容当字面量文本写入;需要样式/批注/图片用 `+cells-set`(或"写值 + 补样式"两步法)。
|
||||
|
||||
⚠️ **`+csv-put` 会把数值落成文本**:把金额 / 百分比 / 计数等在本地拼成带 `$` / `%` / 千分位的字符串(如 `"$1,234.50"` / `"+30.5%"`)再 `+csv-put` 灌进去,单元格就是**文本**——丢失排序 / 求和 / 图表能力,且与数值列混排无法参与计算。数值该怎么写、何时 `+table-put`、版式装不下时何时退 `+cells-set` 传数字 + `number_format`,判据与分流见上方「数字还是文本」;核心一句:**准备把数字 format 成字符串再写时就是走错了路,数值一律以数字写入 + `number_format` 控制显示。**
|
||||
|
||||
⚠️ **`+csv-put` 也会把「看着像数字」的字段静默数值化**(与上一条相反的另一半坑):CSV 里语义是**日期标签 / 编号 / 标识符**、内容却全是数字字符的列,会被按数值解析——`12.10`→`12.1`(点分日期尾零丢失)、`3.0`→`3`、`001`→`1`、长号→科学计数。**这类列即使已攒好 CSV 文本也不能裸走 `+csv-put`**:优先 `+table-put` 把该列 `dtypes` 声明为 `object`(无年份的点分标签如 `12.10` / `3-1` 字面保真)或 `datetime64[ns]`(完整真日期),版式装不下再退 `+cells-set` + `number_format:"@"`。此类失真在「抽样首 / 中 / 末」回读时易被掩盖(`12.10` / `12.20` 等尾零行常不落在抽样窗口),日期 / 编号列回读要专挑带尾零 / 前导零的代表值核对。
|
||||
|
||||
⚠️ 大数据回写走"`+csv-get` 按 `--range` 行窗口分批读到本地 + 本地脚本处理 + `+csv-put` 分批回写"。
|
||||
|
||||
## `+cells-set` 写入要点(常用模式 / 公式 / 样式)
|
||||
|
||||
> 以下是用 `+cells-set`(及 `+cells-set-style`)做富写入时的常用模式与准则;选哪个 shortcut 见上方「使用场景」。
|
||||
|
||||
`+cells-set` 为一块区域设置值 / 公式 / 批注 / 样式,也支持 `rich_text` 的 `type: "embed-image"` 嵌入单元格图片。**关键:`cells` 二维数组的行列维度必须与 `range`(闭区间)严格一致,否则触发 `InvalidCellRangeError`**——维度计算示例见文末 `## Schemas` 的 `--cells`。
|
||||
|
||||
> **单元格图片 vs 浮动图片(最易选错)**:图若**属于某条记录、要随那行排序 / 筛选 / 增删**(凭证 / 证件照 / 每行配图,话里带「对应 / 每行 / 这列」等绑定词)→ **单元格图片**(本工具):用 `+cells-set-image`(最短)或 `+cells-set` 的 `rich_text` + `type: "embed-image"`。只是自由摆放的装饰(logo / 水印 / 封面)→ 浮动图片,见 lark-sheets-float-image。别因「浮动图更好控制 / 更熟」默认选浮动图——它承载"对应某记录"的图会随增删行 / 排序错位。
|
||||
|
||||
常用模式(**必须遵守,禁止逐行写入替代**):
|
||||
|
||||
- 整列公式:先在 `H2` 写一个公式,再用 `--copy-to-range "H2:H100"` 或 `--copy-to-range "H:H"` 向下填充。**禁止对每一行单独调用 `+cells-set` 写入相同结构的公式**
|
||||
- 整列格式:先在 `J1` 写一个带样式的模板单元格,再用 `--copy-to-range "J:J"`
|
||||
- 首行样式:先在 `A1` 写一个模板单元格,再用 `--copy-to-range "1:1"`
|
||||
- 用户说”这列 / 整列 / 这行 / 首行 / 向下复制”时,**必须**使用模板单元格 + `--copy-to-range`
|
||||
- 多区域写入相同格式/公式结构时,优先写一个模板,再用 `--copy-to-range` 复制到所有目标区域
|
||||
|
||||
⚠️ **模板 `--range` 从数据行起算、别把表头圈进去**:`--copy-to-range` 会把 `--range` 模板按目标区尺寸周期性平铺,模板里若含了表头行,表头会每隔几行重复铺进数据区。整列填充时模板只取一格数据样式(如 `H2`),不要取成 `H1:H2`。
|
||||
|
||||
⚠️ **逐行写入公式是常见低效写法**:对每一行单独调用 `+cells-set` 写公式(如 26 次)既慢又易错,且不会自动平移公式引用。正确做法是 1 次模板写入 + 1 次 `--copy-to-range`(公式引用自动平移)。
|
||||
|
||||
💡 **写入公式前先按迁移规则改写**:如果公式来自 Excel 或包含数组场景,先读取并遵循 `lark-sheets-formula-translation` 的规则完成改写,再把最终公式写入 `formula` 字段。
|
||||
|
||||
💡 **内容与样式分离写入(推荐)**:当需要同时写入内容和样式时,`cells` 中每个单元格都带上 `cell_styles` / `border_styles` 会导致入参非常冗长。由于同一区域的样式通常高度重复(如整列统一背景色、统一边框),推荐拆成两步:
|
||||
1. **先写内容**:`+cells-set` 只传 `value` / `formula`,不带样式,`cells` 入参精简。⚠️ 这里"不带样式"指暂不带 `cell_styles`,**不是**降级用 `+csv-put` 铺文本——数值列(百分比 / 金额 / 计数)仍必须以数字写入(百分比传 `0.44`):样式能后补,数据类型不能后补(见上方「数字还是文本」)。
|
||||
2. **再批量刷样式**:对区域中的一个单元格写入目标样式作为模板,再用 `--copy-to-range` 将样式扩展到整列 / 整行 / 整个区域(`--copy-to-range` 会复制值、公式和样式,所以模板单元格应已包含正确的值)
|
||||
|
||||
示例:要对 A2:A100 写入数据并统一设置蓝色背景 + 边框:
|
||||
```
|
||||
Step 1: `+cells-set` — range="A2:A100", cells 只含 value(无样式,入参短)
|
||||
Step 2: `+cells-set` — range="A2", cells 含 value + cell_styles + border_styles(单个模板), --copy-to-range="A2:A100"
|
||||
```
|
||||
这比在 99 个单元格中都重复写样式 JSON 高效得多。
|
||||
|
||||
💡 **样式更新是「部分合并」,不是整体覆盖**:`+cells-set-style` / `+cells-batch-set-style`(以及 `+cells-set` 的 `cell_styles` / `border_styles`)只改你**显式传入**的样式属性,未传的属性保留原值。两个实用推论:
|
||||
- **可分层叠加**:对同一区域先刷字体色、再单独刷背景色、再单独刷边框,后一步不会清掉前一步——美化已有区域时无需一次带齐所有字段,可拆成多次窄调用。
|
||||
- **`border_styles` 按边合并**:只传 `{"top":{...}}` 只更新上边框,`bottom` / `left` / `right` 保留原状;不必为了「只改一条边」而把四边全部重传。(例外见上方「新增行的边框/样式禁止用 `{}` 跳过」:**全新行**底子里没有边框,仍需把要显示的边都显式传出。)
|
||||
|
||||
💡 **大批量数据分批写入(推荐)**:当需要写入大量行(如几十行以上)时,不要试图在一次调用中生成全部 `cells` 数据——`cells` 数组过大会让单次生成的内容过长,容易出错或被截断。应将数据拆分为多批,每批 20-50 行,分多次调用 `+cells-set` 逐批生成并写入(如先写 `A2:D21`,再写 `A22:D41`,依此类推)。每次只生成当前批次的数据,控制单次生成量。
|
||||
|
||||
注意:
|
||||
|
||||
- 不要把 `cells` 写成字符串化 JSON
|
||||
- `+cells-set` 默认即覆盖非空 cell(`--allow-overwrite` 默认 true);若要**保护**非空 cell 不被覆盖,显式传 `--allow-overwrite=false`(遇非空 cell 报错)
|
||||
- 若目标区域涉及合并单元格,不要向合并区域中的非左上角单元格写入数据;如需写入,应改写合并区域左上角单元格,或先调整/取消合并区域
|
||||
- **构造 `range` 时行号必须基于逻辑行号**:如果之前通过 `+csv-get` 读取了数据,CSV 中被双引号包裹的多行字段(如 `"2026年3月2日\n星期一"`)是**一个单元格**,不是两行。写入时的行号必须按逻辑记录计算,不能按物理换行符计数,否则 `range` 会整体偏移导致写入到错误位置
|
||||
|
||||
> 用户说"样式和原表一致 / 保持原表格式 / 边框继承"时同理:`cell_styles` 只覆盖字体和对齐、**不含边框**,边框必须用独立 `border_styles` 字段传——完整继承清单见上方「新增列 / 新增行的样式继承」。
|
||||
|
||||
⚠️ **公式写入必须自己校验结果(后端不会报语法错)**:`+cells-set` 写公式时,即便公式有括号不配对(如 `=IFERROR(VALUE(REGEXEXTRACT(D5, "\d+"))), 0)` 比 IFERROR 多一个 `)`)或用了飞书不支持的函数(如 `GOOGLETRANSLATE` / `CUBEVALUE`),**后端工具也会返回 `updated_cells_count=N, rc=0` 的"成功"**——错误会静默写进单元格显示为 `#VALUE!` / `#NAME?` / `#REF!`。因此:
|
||||
1. **写完立即读回**:`+cells-set` 后紧跟 `+csv-get`(或 `+cells-get`)读目标范围前几行,检查是否出现 `#VALUE!` / `#NAME?` / `#REF!` / `#N/A` / `#DIV/0!` / `#NUM!`
|
||||
2. **看到 `#` 开头的错误值**立即修公式:`#NAME?` 多半是函数名拼错或用了飞书不支持的函数(如 `GOOGLETRANSLATE` / CUBE 系列;注意 `UNIQUE` / `FILTER` / `SPLIT` 飞书是支持的);`#VALUE!` 多半是类型不匹配或括号错位;`#REF!` 是引用错误;`~CIRCULAR~REF~` 是循环引用(公式引用了自身或会闭环)
|
||||
3. **`--copy-to-range` 扩展前先验证模板**:模板单元格公式自己都算错,`--copy-to-range` 复制到 100 行就是 100 个错误
|
||||
4. **去重 / 筛选函数**:飞书**支持** `UNIQUE` / `FILTER` / `SPLIT`(原生数组函数,详见 `lark-sheets-formula-translation`),可直接用;`DISTINCT` 不是飞书函数,去重用 `UNIQUE`。大数据量去重 / 分组也可用透视表(`+pivot-{create|update|delete}`,值字段聚合方式选 count)
|
||||
5. **循环引用预检**:写聚合公式(SUM / AVERAGE / COUNT 等)前必须明确**引用范围不包含目标单元格自身或其传递依赖**。典型反例:在 C3 写 `=SUMIF(B:B,LEFT(B3,9)&"*",C:C)`,B 列匹配 B3 前 9 位时 C3 自己也命中,导致 C3 自引用 → `~CIRCULAR~REF~`。修法:用辅助列 / 显式排除自身(`SUMIFS(C:C, B:B, ..., A:A, "<>"&A3)`)/ 缩小范围避开自己
|
||||
6. **REGEX 模式覆盖率验证**:公式里的 `REGEXEXTRACT` / `REGEXMATCH` / `REGEXREPLACE` 等正则模式落地前必须用本地脚本在源列上跑一遍命中率统计(`df[col].str.contains(pattern).mean()`);命中率 < 100% 时必须扩展 pattern 或加多分支(IFS / 多个 IFERROR 串联)兜底,**禁止**只覆盖样本前 N 行就交付(典型反例:用 `REGEXEXTRACT(D5,"长(\d+)")` 只匹配带"长"前缀的尺寸文本,对"宽×高"、"×"、"*"等其它分隔符直接漏匹配)
|
||||
7. **公式范围与用户指令字面对齐**:用户说"对 F 至 L 列求和"就必须写 `SUM(F2:L2)` 或 `F2+G2+H2+I2+J2+K2+L2`,**不能漏列、多列、错列**。写完用 `+cells-get` 拿回 `formula` 字符串,与用户原话逐字对照(参与求和的列名一致 / 起止列号一致 / 运算符一致),不一致就是违规
|
||||
8. **量纲 / 单位换算 / 数量乘项预检(公式不报错但结果整体偏倍数)**:从文本提取数字做计算前,先核对**单位是否统一、是否漏乘数量、口径是否一致**——这类错误公式能跑通、无 `#` 报错,回读也看不出(值"像对的")。必须用本地脚本对 3–5 个代表行**离线手算一遍预期值**,与公式结果逐格比对量级:① 单位不一致先统一再算(典型反例:尺寸 `320CM*337CM` 直接取数相乘除以 1e6 得 0.11,正确是 CM→MM 换算后得 10.78,**差 100 倍**);② 按"单件×数量"的量必须乘数量列(典型反例:侧面板面积漏乘 F 列数量,F=2 的行只算了一半);③ 标准值口径对齐(典型反例:营养成分 mg/kg 与 g/100g 口径混用,整列放大 100 倍)。**口径 / 单位 / 数量任一项错,整列计算结果就是错的;这类错误公式不报错、回读也不易看出,必须靠离线手算对照。**
|
||||
|
||||
⚠️ **公式写入的默认收尾不是停在回读,而是继续跑 `+formula-verify`**:`+csv-get` / `+cells-get` 的抽样回读只能帮你快速发现明显错误,但它覆盖不到整列中段、隐藏行、被条件格式遮蔽的错误,也看不到 `partial` 截断。**只要这次 `+cells-set` / `--copy-to-range` / `+csv-put` 实际写入了公式,收尾默认就是转到 `lark-sheets-formula-verify` 跑 `+formula-verify`,直到 `status='success'`。** 不要等用户补一句“再验证下公式”才做。
|
||||
|
||||
⚠️ **收到 `formula_errors` 反馈后不要只打补丁**:`+cells-set` 返回值里若出现 `formula_errors: [{cell, formula, error_type, detail}]`,说明某些 cell 公式编译失败(`error_type=compile_failed` 通常是函数语法错如 `SPLIT(x)[1]` 的下标取值飞书不支持(SPLIT 本身支持,取第 N 项用 `INDEX(SPLIT(...),N)`);`non_formula` 是 `=` 开头但解析不通过)。此时**禁止只聚焦修报错点的局部语法**(如仅把 `[1]` 换成 `INDEX(..,1)`),必须:
|
||||
|
||||
1. **重新审视整条公式的完整性**:被 formula_errors 标出的那一行,公式除了下标语法错,还可能有其他先天缺陷(字符清洗不全、IFERROR 兜底漏条件、引用列写错),修完语法错后立即整体复核
|
||||
2. **同步对称修复所有相似列**:如果同一任务涉及多列相似处理(如"算 H 列面积"用 D 列尺寸、"算 I 列面积"用 E 列尺寸),**修完一列必须把同样的清洗/兜底逻辑同步到所有相似列**,禁止出现 H 列用 `SUBSTITUTE(长)+SUBSTITUTE(高)+SUBSTITUTE(×)` 而 I 列只用 `SUBSTITUTE(×)` 这种不对称处理——会导致一列编译通过有值、另一列编译通过但 IFERROR 全返回空,用户看到的是"数据为空"而非"公式错"
|
||||
3. **修完再读回验证**:不只看 `formula_errors` 为空(这只证明编译通过,不证明运行时有值),必须 `+csv-get` 读目标列前 3-5 行,确认**非空源数据对应的目标列有非空计算结果**
|
||||
4. **核心心智**:`formula_errors` 是"帮你暴露编译错"的工具,不是"修掉它就收工"的通行证。编译通过 + 运行时 IFERROR 兜底空 = 用户视角的"没算出来"
|
||||
|
||||
⚠️ **新增行的边框/样式禁止用 `{}` 跳过**:`cells` 数组里 `{}` 的语义是"**此单元格不做任何修改、保留原状态**"。这在写入**已有行**时是安全的(原有边框/样式保持不变),但在写入**新行**(比如表尾追加汇总行、扩展行)时是灾难:新行底子里本来就没边框,`{}` 不修改 = 保留无边框状态,导致该 cell 视觉断裂。
|
||||
|
||||
⚠️ **"汇总行"识别 → 读 `lark-sheets-visual-standards` 拿完整样式规范**:下述双重条件**同时满足**才是汇总行,禁止仅凭"有 AVERAGE"就判定:
|
||||
- **语义信号**(二选一):用户 prompt 含"合计/汇总/总计/统计/各科平均分/最下面加一行算…/底部总计"等意图词;或上下文明确是"表尾追加一行做聚合"
|
||||
- **结构信号**:新行全行都在做聚合(含 `=SUM/AVERAGE/COUNT/MAX/MIN/SUBTOTAL(...)`,支持 IFERROR 包裹),**不是**单个 cell 算个参考值或每行都算的派生列
|
||||
|
||||
满足上述时,**不要在本文里猜样式**,直接去读 `lark-sheets-visual-standards` 的「场景一 → 1A. 添加汇总行 / 表头行」章节,按那里的样式要点配齐 `font.bold / horizontal_alignment / background_color / border_styles`。
|
||||
|
||||
反例(**不是**汇总行,禁止自动加粗):
|
||||
- 用户说"在 H5 帮我算个 AVERAGE 参考"→ 单 cell 计算
|
||||
- 每行都有 `=AVERAGE(本行区间)` 的派生列 → 属数据列
|
||||
- 用户明确说"不要加粗/样式和数据行保持一致"→ 遵循用户意图
|
||||
|
||||
**正确做法**(二选一):
|
||||
|
||||
- **做法 A(推荐)**:按上方「内容与样式分离写入」两步法——先用模板单元格 + `--copy-to-range` 铺**完整样式**(`cell_styles` + `border_styles` 都要,不能只铺 border,否则新行字体 / 对齐 / 背景色全裸奔),再单独 `+cells-set` 写 value / formula。汇总行的 `cell_styles` 要点(bold / 背景色 / 上边框)见 `lark-sheets-visual-standards` 的「场景一 → 1A. 添加汇总行 / 表头行」。
|
||||
- **做法 B**:一次写入,但每个 cell(含空白格)都显式带 `cell_styles` + `border_styles`,**不能用 `{}`**。
|
||||
|
||||
**判断是不是"新行"**:写入 range 超出 `+csv-get` 返回的 `current_region` 右 / 下边界(如 `current_region=A1:H10`、写 `A11:H11`)即新行,必须按上述做法补边框。
|
||||
|
||||
## 富文本单元格:超链接 / @人 / @文档(`rich_text`)
|
||||
|
||||
带显示文本的超链接、@人、@文档这类富内容**必须**走 `+cells-set` 的 `rich_text` 字段(`cells[].rich_text` 数组,每段一个对象、带 `type`),**不能**直接传普通字符串——纯字符串只会被当作纯文本存进单元格。完整字段跑 `lark-cli sheets +cells-set --print-schema --flag-name cells`,常用段类型:
|
||||
|
||||
- **超链接(带显示文本)**:`{"type":"link","text":"飞书","link":"https://www.feishu.cn"}`。纯 URL 不需要 `rich_text`,直接写普通字符串即可。
|
||||
- **@人**:`{"type":"mention","mention_token":"<userId>","notify":false}`。**仅支持同租户用户,单次写入最多 50 人。** `notify` **默认 `true`**(会给被 @ 的人发通知),不想发务必显式传 `false`。
|
||||
- **@文档**:同样 `"type":"mention"`,`mention_token` 传文档 token(如 `shtXXX`)。
|
||||
|
||||
`mention_type`(类型编号)等可选字段以 `--print-schema` 输出为准。
|
||||
|
||||
> ⚠️ `rich_text` 一旦设置会**忽略**同一 cell 的 `value`;它与 `formula` / `multiple_values` 三者只能选其一作为内容字段(可叠加 `cell_styles` / `note` 等)。
|
||||
|
||||
## Dropdown 选项 + 配色(`+dropdown-set` / `+dropdown-update`)
|
||||
|
||||
### 选项怎么来:`--options` 与 `--source-range` 二选一
|
||||
|
||||
| flag | 选项来源 | 适用场景 |
|
||||
|---|---|---|
|
||||
| `--options '["a","b","c"]'` | 写在命令里的固定列表 | 选项集是常量、不需要事后维护 |
|
||||
| `--source-range ''\''Sheet1'\''!T1:T3'` | 已有单元格里的值 | 选项要跟数据动态同步;想维护一张「枚举值」列后多处引用 |
|
||||
|
||||
两个 flag **必须传一个、且只能传一个**——同时传或都不传,CLI 会立刻报错。`--source-range` 用 A1 + sheet 前缀写法(如 `'Sheet1'!T1:T3`,sheet 名按 A1 标准单引号包裹),可以指同 sheet 也可以指其它 sheet(如 `'Refs'!A1:A10`)。
|
||||
|
||||
### 配色:默认即上色,三种意图三条线
|
||||
|
||||
下拉**默认带胶囊高亮**——什么 flag 都不传时,所有选项按内置 10 色色板循环上色,跟 UI 手动配下拉的默认行为对齐。三种意图:
|
||||
|
||||
| 想要的效果 | 怎么传 |
|
||||
|---|---|
|
||||
| 默认色板循环上色 | 都不传 `--highlight` / `--colors` |
|
||||
| 按选项指定具体颜色 | 只传 `--colors '["#hex",...]'`(不需要再传 `--highlight`) |
|
||||
| 纯白下拉、不要高亮 | 传 `--highlight=false`(注意 `=false` 不能省,单写 `--highlight` 在 cobra 里等价于 true) |
|
||||
|
||||
`--colors` 长度**可以短于**选项数(list 模式短于 `--options` 长度,listFromRange 模式短于 `--source-range` 的单元格数),未指定的选项按内置色板循环补色;但**不能长于**——CLI 在 Validate 阶段就会拦截,错误形如 `--colors length (4) must not exceed dropdown source size (3)`。
|
||||
|
||||
当 `--highlight=false` 显式关闭高亮时,`--colors` 即使传了也会被忽略(语义自相矛盾,但不报错)。
|
||||
|
||||
### 最小用例
|
||||
|
||||
**`--options` 模式 — 默认色板(最常见)**:
|
||||
|
||||
```
|
||||
lark-cli sheets +dropdown-set \
|
||||
--url https://... --sheet-id <id> \
|
||||
--range A2:A100 \
|
||||
--options '["待开始","进行中","已完成","已取消"]'
|
||||
```
|
||||
|
||||
**`--options` 模式 — 指定颜色**(4 个选项配 3 个颜色,第 4 个按色板补):
|
||||
|
||||
```
|
||||
lark-cli sheets +dropdown-set \
|
||||
--url https://... --sheet-id <id> \
|
||||
--range A2:A100 \
|
||||
--options '["待开始","进行中","已完成","已取消"]' \
|
||||
--colors '["#bff7d9","#FFE699","#bacefd"]'
|
||||
```
|
||||
|
||||
**`--source-range` 模式**(先在 `'Sheet1'!T1:T3` 维护「男/女/保密」三行,再让 `B2:B21` 引用它):
|
||||
|
||||
```
|
||||
lark-cli sheets +dropdown-set \
|
||||
--url https://... --sheet-id <id> \
|
||||
--range B2:B21 \
|
||||
--source-range ''\''Sheet1'\''!T1:T3' \
|
||||
--colors '["#cce8ff","#ffd6e7","#e6e6e6"]'
|
||||
```
|
||||
|
||||
**纯白下拉**(明确告诉用户"不要彩色"时才用):
|
||||
|
||||
```
|
||||
lark-cli sheets +dropdown-set \
|
||||
--url https://... --sheet-id <id> \
|
||||
--range A2:A100 \
|
||||
--options '["低","中","高"]' \
|
||||
--highlight=false
|
||||
```
|
||||
|
||||
> ⚠️ **`--source-range` 必须带 sheet 前缀**(即使跟 `--range` 同 sheet)。注意一个坑:回读这种 listFromRange 下拉单元格时,`data_validation.range` 看起来不带 sheet 前缀(形如 `$T$1:$T$3`),如果要把读出来的 range 反过来写回 `--source-range`,**必须自己重新补上 sheet 前缀**,否则会被拒。
|
||||
>
|
||||
> ⚠️ **`--ranges` 类批量 flag 的 sheet 前缀必须「裸写」**——`+cells-batch-set-style` / `+cells-batch-clear` / `+dropdown-update` / `+dropdown-delete` 的 `--ranges` 解析器不接受引号:表名含点或空格(如 `2025.9`、`一月份`)也直接写 `2025.9!A1`,写成 `'2025.9'!A1` 会被当成表名一部分、报 `sheet not found`。**但 `--source-range`、透视表 `--source`、`--range` 走 A1 标准**:sheet 名带单引号(如 `'Sheet1'!A1:B2`)是标准写法、裸写也接受,回读统一返回带引号形式——别把 `--ranges` 的裸写要求套到这些 flag 上。
|
||||
|
||||
`+dropdown-update`(多 range 批量更新)的所有 flag 语义与 `+dropdown-set` 完全一致;只是目标 `--ranges` 由单值变成 JSON 数组(每项带 sheet 前缀),同一份选项 + 配色应用到所有 range。
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| Shortcut | Risk | 分组 |
|
||||
| --- | --- | --- |
|
||||
| `+cells-set` | write | 单元格 |
|
||||
| `+cells-set-style` | write | 单元格 |
|
||||
| `+cells-set-image` | write | 单元格 |
|
||||
| `+dropdown-set` | write | 对象 |
|
||||
| `+csv-put` | write | 单元格 |
|
||||
| `+table-put` | write | 单元格 |
|
||||
|
||||
## Flags
|
||||
|
||||
### `+cells-set`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | 写入区域(A1 格式) |
|
||||
| `--cells` | string + File + Stdin(复合 JSON) | required | JSON:2D 数组 `[[{cell},...],...]`,维度与 `--range` 完全一致;每个 cell 可含 `value` / `formula` / `cell_styles` / `note` / `rich_text`(含 `type="embed-image"` 单元格嵌图)等,完整字段跑 `--print-schema` |
|
||||
| `--allow-overwrite` | bool | optional | 允许覆盖非空 cell(默认 true);设为 false 时遇非空 cell 报错 |
|
||||
| `--max-cells` | int | optional | 防爆,默认 50000(隐藏 flag:不在 `--help` 列出,但可正常传入) |
|
||||
| `--copy-to-range` | string | optional | 复制范围(A1 表示法):把 --range 中 --cells 写入的内容(值/公式/样式,取决于实际传入字段)复制到该区域,公式引用自动平移(如 C2=B2 → C3=B3)。适合先写一行/一块模板再扩展填充整列/整区域(如 --range A1:G1 写模板、--copy-to-range A1:G100 填充 100 行)。支持整行 3:6、整列 C:E、到列尾 D3:D、到行尾 D3:3;支持英文逗号分隔多个目标区域,如 C1:D2,E5:F6 |
|
||||
|
||||
### `+cells-set-style`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | 目标范围(A1 格式,如 `A1:B2`) |
|
||||
| `--background-color` | string | optional | 背景颜色(十六进制,如 `#ffffff`) |
|
||||
| `--font-color` | string | optional | 字体颜色(十六进制,如 `#000000`) |
|
||||
| `--font-family` | string | optional | 字体名称(如 `Arial`、`微软雅黑`) |
|
||||
| `--font-size` | float64 | optional | 字体大小(px,例:10、12、14) |
|
||||
| `--font-style` | string | optional | 字体样式(可选值:`normal` / `italic`) |
|
||||
| `--font-weight` | string | optional | 字重(可选值:`normal` / `bold`) |
|
||||
| `--font-line` | string | optional | 字体线条样式(可选值:`none` / `underline` / `line-through`) |
|
||||
| `--horizontal-alignment` | string | optional | 水平对齐(可选值:`left` / `center` / `right`) |
|
||||
| `--vertical-alignment` | string | optional | 垂直对齐(可选值:`top` / `middle` / `bottom`) |
|
||||
| `--word-wrap` | string | optional | 换行策略(可选值:`overflow` / `auto-wrap` / `word-clip`) |
|
||||
| `--number-format` | string | optional | 数字格式(例:文本 `@`、数字 `0.00`、货币 `$#,##0.00`、日期 `mm/dd/yyyy`) |
|
||||
| `--border-styles` | string + File + Stdin(复合 JSON) | optional | 边框配置 JSON:`{ top: {style,color,weight}, bottom: ..., left: ..., right: ... }`;4 方向结构相同 |
|
||||
|
||||
### `+cells-set-image`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | 目标单元格(A1 格式,必须单 cell,如 `A1`;起止 cell 须相同) |
|
||||
| `--image` | string | required | 本地图片路径(支持 PNG / JPEG / JPG / GIF / BMP / JFIF / EXIF / TIFF / BPG / HEIC) |
|
||||
| `--name` | string | optional | 图片文件名(含扩展名);省略时取 `--image` 的 basename |
|
||||
|
||||
### `+dropdown-set`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--range` | string | required | 目标范围(A1 格式,如 `A2:A100`) |
|
||||
| `--options` | string + File + Stdin(复合 JSON) | xor | 下拉选项 JSON 数组,例如 `["opt1","opt2"]`。服务端不限制选项数量,也不限制单个选项长度;含逗号的选项可以接受(写入时会自动转义)。大量选项建议改用 `--source-range`。 |
|
||||
| `--colors` | string + File + Stdin(简单 JSON) | optional | 下拉胶囊背景色,RGB hex 数组(如 `["#1FB6C1","#F006C2"]`)。长度可短不可长——超长 Validate 拦截(`--colors length (N) must not exceed dropdown source size (M)`),未指定项按内置 10 色色板循环补色。**单独传即生效**;`--highlight=false` 时被忽略。 |
|
||||
| `--multiple` | bool | optional | 启用多选;默认 `false` |
|
||||
| `--highlight` | bool | optional | 下拉胶囊背景色高亮开关。**不传 = 开**(按内置 10 色色板循环上色);`--highlight=false` 关闭得到纯白下拉。配色用 `--colors` 覆盖。 |
|
||||
| `--source-range` | string | xor | listFromRange 模式的下拉源 range,A1 表示法 + sheet 前缀(如 `'Sheet1'!T1:T3`)。映射到 server `data_validation.range`,搭配 server `data_validation.type='listFromRange'` 自动生效。跟 `--options` 二选一:传 `--options` 走 inline 列表(type=list),传本 flag 走 range 引用(type=listFromRange)。`--colors` 长度规则不变(≤ 源 range 单元格数),`--highlight` / `--multiple` 行为相同。当 `--highlight` 开启且 source 覆盖单元格数超过 2000 时,服务端会将该下拉判为 option-error(这是不支持的组合);CLI 会向 stderr 输出 warning。如需取消,传 `--highlight=false`。 |
|
||||
|
||||
### `+csv-put`
|
||||
|
||||
_公共四件套 · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--start-cell` | string | required | 目标区域起点 A1(如 `A1`、`B5`,不带 sheet 前缀;用 `--sheet-id` / `--sheet-name` 指定 sheet);必须是单个单元格,不接受范围写法;终点按 CSV 实际行列数自动推断 |
|
||||
| `--csv` | string + File + Stdin(非 JSON 文本) | required | RFC 4180 CSV 文本;可写值或公式(以 = 开头的单元格按公式计算);不带样式 / 批注 / 图片,需要这些用 +cells-set。 |
|
||||
| `--allow-overwrite` | bool | optional | 允许覆盖(默认 true);设为 false 时若目标非空报错 |
|
||||
| `--range` | string | optional | --start-cell 的别名(与 +csv-get / +cells-set 一致,用 --range 定位);传区间(如 A1:H17)时自动取其左上角单元格(隐藏 flag:不在 `--help` 列出,但可正常传入) |
|
||||
|
||||
### `+table-put`
|
||||
|
||||
_公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
|
||||
| Flag | Type | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--sheets` | string + File + Stdin(复合 JSON) | required | Typed 表格协议(pandas-DataFrame-shaped)JSON:顶层 `{"sheets":[...]}`,每个数组项是一张子表 `{name, start_cell?, mode?, header?, allow_overwrite?, columns:["colA","colB",...], data:[[...]], dtypes?:{colA:pandasDtype, ...}, formats?:{colA:numberFormat, ...}}` —— `name` 与外层 `sheets` 数组都不可省。Agents 用 `scripts/sheets_df.py` 的 `df_to_sheet(df, name)` 一行把 DataFrame 转成一项(多子表就 list 拼起来再包 `{"sheets":[...]}`)。`dtypes` 值是 pandas dtype 字符串(`int64`、`float64`、`Int64`、`bool`、`boolean`、`datetime64[ns]`、`object`、...),CLI 端映射成内部 string/number/date/bool —— 省略 `dtypes` 时该列按文本写入(适合原始 CSV-shaped 数据)。`formats[col]` 是 Excel number_format 字符串(如 `#,##0.00`、`0.0%`、`yyyy-mm`);缺省时 date 列用 `yyyy-mm-dd`,string 列用文本格式 `@`。 |
|
||||
| `--styles` | string + File + Stdin(复合 JSON) | optional | 类型保真写入后再应用的视觉处理操作 JSON:顶层 `{styles:[...]}`,每项对应一个被写入的子表、含 `name`,并至少给 `cell_styles` / `row_sizes` / `col_sizes` / `cell_merges` 之一。`cell_styles` 用 A1 单元格 range + 扁平样式字段(字段同 +cells-set-style,含 number_format / 颜色 / 对齐 / border_styles);row/col sizes 用行/列范围 + type/size;merges 用单元格 range + 可选 merge_type。styles 数组的长度/顺序/name 必须与被写入的子表对应(与 --sheets.sheets 一一对应)。完整 cell_styles 字段结构跑 `+table-put --print-schema --flag-name styles`。 |
|
||||
|
||||
## Schemas
|
||||
|
||||
> 复合 JSON flag 字段速查(只列顶层 + 一层嵌套)。深层结构看下方 `## Examples`,或用 `--print-schema` 读完整 JSON Schema(用法见 SKILL.md「公共 flag 速查」与「Agent 使用提示」)。
|
||||
|
||||
### `+cells-set` `--cells`
|
||||
|
||||
_【维度】行列数必须与 range 完全一致:'A1:C2'→[[_,_,_],[_,_,_]](2行×3列),'B5:B7'→[[_],[_],[_]](3行×1列),'A1'→[[_]](1×1)_
|
||||
|
||||
**二维数组项**(类型 object):
|
||||
- `value` (oneOf?) — 静态单元格值(文本、数字、布尔)
|
||||
- `formula` (string?) — 以 '=' 开头的单元格公式(例如:'=SUM(A1:A10)')
|
||||
- `note` (string?) — 单元格批注/备注
|
||||
- `cell_styles` (object?) — 单元格样式属性,包括字体、颜色、对齐方式和数字格式 { font_color?: string, font_family?: string, font_size?: number, font_weight?: enum, font_style?: enum, …共 11 项 }
|
||||
- `border_styles` (object?) — 单元格边框配置,含 top/bottom/left/right 四个方向,每个方向的结构相同(见 top) { top?: object, bottom?: object, left?: object, right?: object }
|
||||
- `rich_text` (array<object>?) — 富文本内容 each: { type: enum, text: string, style?: object, link?: string, mention_token?: string, …共 17 项 }
|
||||
- `multiple_values` (array<object>?) — 多值内容,用于支持多选的列表验证单元格 each: { value: oneOf, format?: string }
|
||||
- `data_validation` (object?) — 数据验证配置 { type: enum, items?: array<string>, range?: string, operator?: enum, values?: array<oneOf>, …共 9 项 }
|
||||
|
||||
### `+cells-set-style` `--border-styles`
|
||||
|
||||
_单元格边框配置,含 top/bottom/left/right 四个方向,每个方向的结构相同(见 top)_
|
||||
|
||||
**顶层字段**:
|
||||
- `top` (object?) { style?: enum, weight?: enum, color?: string }
|
||||
- `bottom` (object?) { style?: enum, weight?: enum, color?: string }
|
||||
- `left` (object?) { style?: enum, weight?: enum, color?: string }
|
||||
- `right` (object?) { style?: enum, weight?: enum, color?: string }
|
||||
|
||||
### `+dropdown-set` `--options`
|
||||
|
||||
_列表选项_
|
||||
|
||||
**数组项**(类型 string):
|
||||
- 标量:string
|
||||
|
||||
### `+table-put` `--sheets`
|
||||
|
||||
_一个或多个子表的 typed 数据,每个数组元素写入一张子表;支持多 DataFrame → 多子表一次写入_
|
||||
|
||||
**数组项**(类型 object):
|
||||
- `name` (string) — 目标子表名
|
||||
- `start_cell` (string?) — 写入起点单元格(A1 记法,如 "B2"),默认 "A1"
|
||||
- `mode` (enum?) — overwrite(默认):从 start_cell 起写「表头 + 数据」块;append:把数据追加到子表已有数据下方(默认不重复表头) [overwrite / append]
|
||||
- `header` (boolean?) — 是否写一行列名表头
|
||||
- `allow_overwrite` (boolean?) — 为 false 时,若写入会落在非空单元格则拒写以保护原数据(返回 partial_success)
|
||||
- `columns` (array<string>) — 列名字符串数组,顺序与 `data` 中每行取值一一对应
|
||||
- `data` (array<array<string|number|boolean|null>>) — 数据行;每行是一个数组,长度必须等于 `columns` 数
|
||||
- `dtypes` (object?) — 可选
|
||||
- `formats` (object?) — 可选
|
||||
|
||||
### `+table-put` `--styles`
|
||||
|
||||
|
||||
**数组项**(类型 object):
|
||||
- `cell_merges` (array<object>?) — 单元格合并操作数组;range 使用 A1 单元格范围,merge_type 默认 all each: { merge_type?: enum, range: string }
|
||||
- `cell_styles` (array<object>?) — 单元格样式操作数组;每项用 A1 单元格 range 指定范围,字段名与 +cells-set-style 对齐 each: { background_color?: string, border_styles?: object, font_color?: string, font_family?: string, font_line?: enum, …共 13 项 }
|
||||
- `col_sizes` (array<object>?) — 列宽操作数组;range 使用列范围如 A:C,type 为 pixel/standard,pixel 需要 size each: { range: string, size?: number, type: enum }
|
||||
- `name` (string) — 子表名
|
||||
- `row_sizes` (array<object>?) — 行高操作数组;range 使用行范围如 1:3,type 为 pixel/standard/auto,pixel 需要 size each: { range: string, size?: number, type: enum }
|
||||
|
||||
## Examples
|
||||
|
||||
公共四件套:所有 shortcut 顶部排列 `--url` / `--spreadsheet-token` / `--sheet-id` / `--sheet-name`(XOR)。
|
||||
|
||||
### `+cells-set` 的拆分与转介绍
|
||||
|
||||
"工具选择"段已讲清纯值(`+csv-put`)vs 富写入(`+cells-set`)。下表补 CLI 侧的 `+cells-set` **兄弟拆分**,以及不属于本 reference 的**跨 reference 转介绍**——避免 agent 用 `+cells-set` 硬扛所有写入场景。
|
||||
|
||||
| 写入场景 | 用这个 | 不要用 |
|
||||
|---------|--------|--------|
|
||||
| 只改**已有 cell 的样式**,不动 value/formula | `+cells-set-style` | `+cells-set`(会触发不必要的值写入) |
|
||||
| 把**单张图片嵌入**到某个 cell | `+cells-set-image` | `+cells-set`(参数更繁琐) |
|
||||
| **插行/列 + 写入** 这种多步组合,且要原子 | `+batch-update`(见 lark-sheets-batch-update) | 多次独立 `+cells-set`(非原子;插入会扰动后续 range) |
|
||||
| 在**多个不连续 range** 上应用同一组样式 | `+cells-batch-set-style`(见 lark-sheets-batch-update) | 多次 `+cells-set-style`(非原子) |
|
||||
|
||||
### `+cells-set`
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
# 纯值(数组形态);默认即覆盖非空 cell,无需显式传 --allow-overwrite
|
||||
lark-cli sheets +cells-set --url "https://example.feishu.cn/sheets/shtXXX" \
|
||||
--sheet-name "Sheet1" --range "A1:B2" \
|
||||
--cells '[[{"value":"name"},{"value":"score"}],[{"value":"alice"},{"value":95}]]'
|
||||
|
||||
# 富 cell(公式 + 样式,cells 是二维矩阵每元素一个 cell schema)
|
||||
lark-cli sheets +cells-set --spreadsheet-token shtXXX --sheet-id "$SID" \
|
||||
--range "C2:C10" --cells @rich-cells.json
|
||||
```
|
||||
|
||||
`--cells` 富格式见 `## Schemas` 段(cells 元素含 value / formula / cell_styles / border_styles / data_validation / multiple_values / note / rich_text);值 / 公式 / 样式 / 批注 / 嵌入图片可同一次写入混合提交。
|
||||
|
||||
> 中间想跳过的 cell 用空对象 `{}` 占位(底层语义为"保留原值不变"),`--cells` 维度仍须与 `--range` 完全一致。例:`--range A1:A5 --cells '[[{"value":1}],[{}],[{}],[{}],[{"value":5}]]'` 只写 A1 和 A5。
|
||||
>
|
||||
> 跨多个不连续区域散点写入(如 `D2` + `F7` + `J15`)不属于 `+cells-set` 的能力范围——请用 `+batch-update` 把多次 `+cells-set` 打包成单次原子请求。
|
||||
|
||||
### `+cells-set-style`
|
||||
|
||||
只改样式,不动 value / formula。10 个 cell_styles 字段拍平为独立 flag,边框走 `--border-styles` JSON。
|
||||
|
||||
```bash
|
||||
# 加粗 + 黄底
|
||||
lark-cli sheets +cells-set-style --url "..." --sheet-name "Sheet1" \
|
||||
--range "A1:B2" --font-weight bold --background-color "#FFFF00"
|
||||
|
||||
# 配套边框
|
||||
lark-cli sheets +cells-set-style --url "..." --sheet-id "$SID" \
|
||||
--range "A1:D10" --font-size 12 --horizontal-alignment center \
|
||||
--border-styles '{"top":{"style":"solid","color":"#000","weight":"thin"},"bottom":{"style":"solid","color":"#000","weight":"thin"}}'
|
||||
```
|
||||
|
||||
### `+cells-set-image`
|
||||
|
||||
把单张图片嵌入 cell(必须单 cell 范围):
|
||||
|
||||
```bash
|
||||
lark-cli sheets +cells-set-image --url "..." --sheet-name "Sheet1" \
|
||||
--range "A1" --image ./logo.png
|
||||
```
|
||||
|
||||
### `+csv-put`
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
# 内联 CSV
|
||||
lark-cli sheets +csv-put --url "https://example.feishu.cn/sheets/shtXXX" \
|
||||
--sheet-name "Sheet1" --start-cell "A1" \
|
||||
--csv $'name,score\nalice,95\nbob,87'
|
||||
|
||||
# 从文件
|
||||
lark-cli sheets +csv-put --spreadsheet-token shtXXX --sheet-id "$SID" \
|
||||
--start-cell "A1" --csv @data.csv
|
||||
```
|
||||
|
||||
> `+csv-put` 比 `+cells-set` 短得多——批量灌值或公式时优先用它。需要样式/批注/图片才换 `+cells-set`。
|
||||
>
|
||||
> ✅ `=` 开头的单元格会被当作公式计算(不是字面量文本):
|
||||
>
|
||||
> ```bash
|
||||
> lark-cli sheets +csv-put --url "..." --sheet-name "Sheet1" \
|
||||
> --start-cell "A1" \
|
||||
> --csv $'name,score\nalice,=SUM(B2:B10)'
|
||||
> # ↑ B2 写入公式 =SUM(B2:B10),读回 formula 保留、value 为计算结果。
|
||||
> # 反过来:无法用 +csv-put 写「= 开头的字面量文本」(会被当公式);样式/批注/图片仍用 +cells-set。
|
||||
> ```
|
||||
>
|
||||
> ⚠️ **公式内部含逗号 / 引号必须 RFC 4180 转义**:CSV 用逗号分隔字段,公式里的逗号(如 `COUNTIF(D5:D22,"及格")` 的参数分隔逗号)会被解析器当成字段分隔符,把一格拆成多格、整块二维结构压扁错位。规则:**含逗号的字段整格用双引号包裹,字段内部的引号再翻倍**:
|
||||
>
|
||||
> ```bash
|
||||
> # 从 G4 写一个 2 列 3 行的统计块;=COUNTIF 含逗号 + 内部引号,必须转义
|
||||
> lark-cli sheets +csv-put --url "..." --sheet-name "Sheet1" \
|
||||
> --start-cell "G4" \
|
||||
> --csv $'统计项,结果\n成绩总和,=SUM(C5:C22)\n及格人数,"=COUNTIF(D5:D22,""及格"")"'
|
||||
> # ↑ "=COUNTIF(D5:D22,""及格"")":外层双引号包裹整格,内部 "及格" 的引号翻倍成 ""及格""。
|
||||
> # 裸写 =COUNTIF(D5:D22,"及格") 会被 CSV 按逗号拆成两格、写入区域从 G4:H6 错位成 G4:K4。
|
||||
> ```
|
||||
>
|
||||
> 💡 **含逗号 / 引号 / 换行的公式优先用 `+cells-set`(JSON 二维数组)写入**——`cells[r][c].formula` 字段直接放公式串,没有 CSV 转义负担,从根上杜绝拆列错位。`+table-put` 的 typed 协议只有 `columns / data / dtypes / formats` 四件套、没有 `formula` 字段,公式写入用 `+cells-set` 或 `+csv-put`。准备给 `+csv-put` 的公式加逗号时,先考虑换 `+cells-set`:
|
||||
>
|
||||
> ```bash
|
||||
> # 同样的统计块,结构化写入无需任何转义
|
||||
> lark-cli sheets +cells-set --url "..." --sheet-name "Sheet1" --range "G4:H6" \
|
||||
> --cells '[[{"value":"统计项"},{"value":"结果"}],[{"value":"成绩总和"},{"formula":"=SUM(C5:C22)"}],[{"value":"及格人数"},{"formula":"=COUNTIF(D5:D22,\"及格\")"}]]'
|
||||
> ```
|
||||
|
||||
> **定位 + 写入边界(关键,避免误覆盖)**:
|
||||
> - 定位用 `--start-cell`(锚点 = 左上角单元格);也接受 `--range` 别名(与 `+csv-get` / `+cells-set` 一致,传区间会自动取左上角)。
|
||||
> - ⚠️ `--start-cell` / `--range` **只定左上角、不限制写入大小**:CSV 从锚点按自身行列数 auto-expand 铺开。给一个"小 range"**不会**截断数据——超出部分照写,且默认覆盖。这与 `+cells-set --range`(精确矩形、`--cells` 必须与 range 同维)语义相反,别把那套心智搬过来。
|
||||
> - dry-run 与成功响应都回显 `writes_range`(实际落区,如 `B2:D4`):**写前先 `--dry-run` 看一眼落区**,确认不会盖到相邻数据。
|
||||
> - 要保护非空 cell:`--allow-overwrite=false`(落区内出现非空 cell 即报错)。
|
||||
|
||||
### `+table-put`(DataFrame → 飞书,类型保真写入)
|
||||
|
||||
把结构化数据(DataFrame、list of dict、Counter)类型保真写入**已有**表(写入语义同 `+cells-set`)。协议形状**对齐 pandas `to_json(orient="split")`**:`columns:[列名]` + `data:[[行...]]`,可选 `dtypes:{列名:pandas_dtype}` 决定每列类型(number 保精度、date 落真日期),可选 `formats:{列名:number_format}` 覆盖显示格式(千分位 / 百分比 / 自定义日期)。dtypes 缺失时整张表按 string 写入(带 `@` 文本格式,邮编 / 订单号等含前导零的 id 保真)。
|
||||
|
||||
只写入**已有**表(`--url` / `--spreadsheet-token` 二选一必填),不新建工作簿——**要新建表格直接用 `+workbook-create --sheets`**(同协议、一步建表 + 类型保真写入,详见 workbook reference)。读回用镜像命令 `+table-get`(见 read-data reference),输出与 `--sheets` 同构、可 round-trip。
|
||||
|
||||
```bash
|
||||
# sheet 按 name 匹配、缺则新建;多 DataFrame 经 stdin 一次写多 sheet
|
||||
python export.py | lark-cli sheets +table-put --url "<表URL>" --sheets -
|
||||
# 某 sheet 带 "mode":"append" 追加到已有数据末尾、默认不重复表头
|
||||
lark-cli sheets +table-put --spreadsheet-token "<token>" --sheets @payload.json
|
||||
```
|
||||
|
||||
每个 sheet 还可带 `"allow_overwrite": false`(遇非空拒写、保护原数据)、`"header": false`(只写数据不写表头)。完整字段跑 `+table-put --print-schema --flag-name sheets`。
|
||||
|
||||
#### DataFrame → 协议(用 `df_to_sheet` helper)
|
||||
|
||||
pandas 的 `df.to_json(orient="split", date_format="iso")` 一步完成所有清洗(NaN→null、Timestamp→ISO 字符串、numpy 标量→原生数字),把 dtypes 拼上即可。本 skill 把这段 5 行 helper 打包成可 import 的 [`scripts/sheets_df.py`](../scripts/sheets_df.py)(含 `df_to_sheet` 和 `sheet_to_df`,写入 / 读回成对):
|
||||
|
||||
```python
|
||||
from sheets_df import df_to_sheet
|
||||
|
||||
# 单 sheet(显式 format 覆盖默认显示)
|
||||
payload = {"sheets": [df_to_sheet(df, "销售", {"营收": "#,##0.00", "毛利率": "0.0%"})]}
|
||||
|
||||
# 多 sheet——helper 让每个 sheet 一行,不再重复 boilerplate
|
||||
payload = {"sheets": [df_to_sheet(df1, "销售"),
|
||||
df_to_sheet(df2, "成本"),
|
||||
df_to_sheet(df3, "利润")]}
|
||||
```
|
||||
|
||||
> **CSV-shaped 全文本数据**(不需要类型保真、含前导零的 id 也要保留)省掉 dtypes 即可,inline 一行写完,不必走 helper(注意保留 `date_format="iso"`,否则 datetime 列会被序列化成 epoch 毫秒数字,CLI 拒绝):
|
||||
> ```python
|
||||
> payload = {"sheets": [{"name": "原始",
|
||||
> **json.loads(df.to_json(orient="split", date_format="iso"))}]}
|
||||
> ```
|
||||
> **别把 `to_json + json.loads` 换成 `df.to_dict(orient="split")`**:会留 `numpy.int64` 让 `json.dumps` 后续报 "not serializable"——这一步是清洗的关键。
|
||||
|
||||
不用 pandas 也行——typed 协议就是纯 JSON。手写场景:
|
||||
|
||||
```python
|
||||
# Counter / dict / 手拼数据:直接写 columns + data,按需加 dtypes/formats
|
||||
payload = {"sheets": [{
|
||||
"name": "渠道",
|
||||
"columns": ["channel", "count", "rate"],
|
||||
"data": [["app", 1240, 0.62], ["web", 760, 0.38]],
|
||||
"dtypes": {"count": "int64", "rate": "float64"},
|
||||
"formats": {"rate": "0.0%"},
|
||||
}]}
|
||||
```
|
||||
|
||||
> **dtype 速查**:`int64`/`float64`(数值)、`Int64`(含空值的整数,nullable)、`bool`/`boolean`、`datetime64[ns]`(date,默认 `yyyy-mm-dd`)、`object`(string)。pandas dtype 字符串原样塞进 dtypes 即可,CLI 端按前缀匹配(`int*`/`uint*`/`Int*`/`float*` → number 等)。未识别 dtype 兜底为 string。
|
||||
|
||||
#### `--styles`(写入时同时套样式)
|
||||
|
||||
`--styles` 在 typed 写入后顺带应用视觉处理,省掉一次 `+cells-set-style` 往返。协议与 `+workbook-create --styles` **完全同构**(详见 workbook reference):顶层 `{styles:[...]}`,数组每项对应一个被写入的子表、含 `name`,并按能力拆成四类可选数组——`cell_styles`(A1 单元格 range + 扁平样式字段,含 `number_format` / 颜色 / 对齐 / `border_styles`,随内容在同一次写入里一并应用)、`cell_merges`、`row_sizes`、`col_sizes`。styles 数组的长度 / 顺序 / name 必须与被写入的子表对应(与 `--sheets.sheets` 一一对应)。
|
||||
|
||||
```bash
|
||||
lark-cli sheets +table-put --url "<表URL>" \
|
||||
--sheets '{"sheets":[{"name":"明细","columns":["日期","金额"],"dtypes":{"日期":"datetime64[ns]","金额":"float64"},"formats":{"金额":"#,##0.00"},"data":[["2024-01-15",1234.5]]}]}' \
|
||||
--styles '{"styles":[{"name":"明细",
|
||||
"cell_styles":[{"range":"A1:B1","font_weight":"bold","background_color":"#f5f5f5","horizontal_alignment":"center"}],
|
||||
"cell_merges":[{"range":"A1:B1"}],
|
||||
"col_sizes":[{"range":"A:B","type":"pixel","size":120}]}]}'
|
||||
```
|
||||
|
||||
完整字段跑 `+table-put --print-schema --flag-name styles`。
|
||||
|
||||
### Validate / DryRun / Execute 约束
|
||||
|
||||
- `Validate`:XOR 公共四件套;`+cells-set` 的 `--cells` 必须能解析为 JSON 二维矩阵且行列数与 `--range` 完全一致;`+cells-set-style` 的样式 flag 至少一个非空(或带 `--border-styles`);`+cells-set-image` 的 `--range` 必须是单 cell(起止 cell 相同);`+csv-put` 的 `--csv` 必须能按 RFC 4180 解析;`+table-put` 给了 `--styles` 则按子表名 / 顺序 / 数量与 `--sheets.sheets` 对齐校验;防爆参数上限校验。
|
||||
- `DryRun`:输出目标 range + 推断尺寸 + 是否覆盖非空 cell 警告,零网络副作用。
|
||||
- `Execute`:写后不自动回读;如需确认,自行调用 `+cells-get --range <写入区域> --include value,formula` 抽样核对。
|
||||
32
.claude/skills/lark-sheets/scripts/sheets_df.py
Normal file
32
.claude/skills/lark-sheets/scripts/sheets_df.py
Normal file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""DataFrame ↔ Feishu Sheet typed-JSON helpers.
|
||||
|
||||
This is the same 7-line snippet the skill docs already inline (see
|
||||
`lark-sheets-write-cells` "DataFrame → 协议(5 行 helper)" and
|
||||
`lark-sheets-read-data` "输出 → DataFrame(2 行 helper)"), pulled out
|
||||
so callers can `import` it instead of copy-pasting:
|
||||
|
||||
from sheets_df import df_to_sheet, sheet_to_df
|
||||
|
||||
Callers run lark-cli themselves; this file is a library, not a CLI.
|
||||
"""
|
||||
import json
|
||||
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def df_to_sheet(df, name, formats=None):
|
||||
"""Pack one DataFrame into one entry of a `+table-put --sheets` payload."""
|
||||
return {
|
||||
"name": name,
|
||||
**json.loads(df.to_json(orient="split", date_format="iso")),
|
||||
"dtypes": df.dtypes.astype(str).to_dict(),
|
||||
**({"formats": formats} if formats else {}),
|
||||
}
|
||||
|
||||
|
||||
def sheet_to_df(sheet):
|
||||
"""Restore one `+table-get` sheet dict into a typed DataFrame."""
|
||||
return pd.DataFrame(sheet["data"], columns=sheet["columns"]).astype(sheet["dtypes"])
|
||||
Reference in New Issue
Block a user