mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 22:24:31 +08:00
feat(sheets): styles 接受 halign/valign 等对齐字段别名
把模型常幻觉的 horizontal_align / halign / vertical_align / valign 映射到 规范字段 horizontal_alignment / vertical_alignment,覆盖 --styles 与 typed --cells;与规范字段冲突时报错而非静默择一。同步 lark-sheets skill 文档补 对齐字段说明 + --print-schema --flag-name styles 提示。
This commit is contained in:
@@ -2095,7 +2095,7 @@
|
||||
"kind": "own",
|
||||
"type": "string",
|
||||
"required": "optional",
|
||||
"desc": "Visual operations applied after the typed write, as JSON: top-level `{styles:[...]}`. Each item corresponds to one written sheet and must include `name`, plus at least one of `cell_styles` / `row_sizes` / `col_sizes` / `cell_merges`. `cell_styles` entries use +cells-set-style fields with a cell range; row/col sizes use dimension ranges plus type/size; merges use cell ranges plus optional merge_type. The styles array length/order/name must match the written sheets: with --sheets, match --sheets.sheets; with --dataframe (single sheet named Sheet1), pass exactly one styles item with name `Sheet1`.",
|
||||
"desc": "Visual operations applied after the typed write, as JSON: top-level `{styles:[...]}`. Each item corresponds to one written sheet and must include `name`, plus at least one of `cell_styles` / `row_sizes` / `col_sizes` / `cell_merges`. `cell_styles` entries use +cells-set-style fields with a cell range; row/col sizes use dimension ranges plus type/size; merges use cell ranges plus optional merge_type. The styles array length/order/name must match the written sheets: with --sheets, match --sheets.sheets; with --dataframe (single sheet named Sheet1), pass exactly one styles item with name `Sheet1`. Run `+table-put --print-schema --flag-name styles` for the full cell_styles field schema.",
|
||||
"input": [
|
||||
"file",
|
||||
"stdin"
|
||||
|
||||
@@ -10,6 +10,7 @@ package sheets
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/larksuite/cli/errs"
|
||||
@@ -335,6 +336,72 @@ func buildCellStyleFromFlags(runtime flagView) map[string]interface{} {
|
||||
return style
|
||||
}
|
||||
|
||||
// cellStyleAliases maps shorthand cell_styles field names that models commonly
|
||||
// hallucinate (Excel / openpyxl / CSS conventions) onto the canonical field
|
||||
// names the backend expects. Only the unambiguous alignment shorthands are
|
||||
// aliased — they are the high-frequency miss; ambiguous guesses (e.g. "color",
|
||||
// "bg_color", "text_align") are intentionally left out so a wrong guess still
|
||||
// surfaces as an error rather than being silently reinterpreted.
|
||||
var cellStyleAliases = []struct{ alias, canonical string }{
|
||||
{"horizontal_align", "horizontal_alignment"},
|
||||
{"halign", "horizontal_alignment"},
|
||||
{"vertical_align", "vertical_alignment"},
|
||||
{"valign", "vertical_alignment"},
|
||||
}
|
||||
|
||||
// normalizeCellStyleAliases renames known shorthand keys in a single
|
||||
// cell_styles map to their canonical equivalents, in place, so a model that
|
||||
// writes e.g. "horizontal_align" instead of "horizontal_alignment" still
|
||||
// applies the style instead of hitting an "unsupported field" error (--styles)
|
||||
// or having the field silently dropped by the backend (typed --cells). If both
|
||||
// the shorthand and its canonical key are present it returns a validation error
|
||||
// rather than picking one. path labels the map for the error message.
|
||||
func normalizeCellStyleAliases(style map[string]interface{}, path string) error {
|
||||
if len(style) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, a := range cellStyleAliases {
|
||||
v, ok := style[a.alias]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if _, exists := style[a.canonical]; exists {
|
||||
return common.ValidationErrorf("%s.%s conflicts with %s; pass only %s", path, a.alias, a.canonical, a.canonical)
|
||||
}
|
||||
style[a.canonical] = v
|
||||
delete(style, a.alias)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// normalizeTypedCellsStyleAliases walks a typed --cells 2D array and applies
|
||||
// normalizeCellStyleAliases to every cell's inline cell_styles object, so the
|
||||
// alignment shorthands are accepted on +cells-set the same as on --styles.
|
||||
// Structure is checked leniently to match the pass-through contract: any
|
||||
// element that isn't the expected shape is skipped, not rejected.
|
||||
func normalizeTypedCellsStyleAliases(cells []interface{}, path string) error {
|
||||
for r, rowRaw := range cells {
|
||||
row, ok := rowRaw.([]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for c, cellRaw := range row {
|
||||
cell, ok := cellRaw.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
st, ok := cell["cell_styles"].(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if err := normalizeCellStyleAliases(st, fmt.Sprintf("%s[%d][%d].cell_styles", path, r, c)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// borderStylesFromFlag parses --border-styles as a JSON object (top/bottom/
|
||||
// left/right with style sub-objects). Returns nil when the flag is empty.
|
||||
func borderStylesFromFlag(runtime flagView) (map[string]interface{}, error) {
|
||||
|
||||
199
shortcuts/sheets/lark_sheet_style_alias_test.go
Normal file
199
shortcuts/sheets/lark_sheet_style_alias_test.go
Normal file
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sheets
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestNormalizeCellStyleAliases pins the shorthand → canonical renaming for a
|
||||
// single cell_styles map: the alignment shorthands models commonly hallucinate
|
||||
// are rewritten in place, values are preserved, and a shorthand colliding with
|
||||
// its canonical key is a hard error rather than a silent pick.
|
||||
func TestNormalizeCellStyleAliases(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("renames *_align shorthands, keeps values and other fields", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
style := map[string]interface{}{
|
||||
"horizontal_align": "center",
|
||||
"vertical_align": "middle",
|
||||
"font_weight": "bold",
|
||||
}
|
||||
if err := normalizeCellStyleAliases(style, "x"); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if style["horizontal_alignment"] != "center" || style["vertical_alignment"] != "middle" {
|
||||
t.Errorf("alignment not renamed: %#v", style)
|
||||
}
|
||||
if _, ok := style["horizontal_align"]; ok {
|
||||
t.Errorf("shorthand horizontal_align should be removed: %#v", style)
|
||||
}
|
||||
if _, ok := style["vertical_align"]; ok {
|
||||
t.Errorf("shorthand vertical_align should be removed: %#v", style)
|
||||
}
|
||||
if style["font_weight"] != "bold" {
|
||||
t.Errorf("unrelated field font_weight dropped: %#v", style)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("renames halign/valign shorthands", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
style := map[string]interface{}{"halign": "left", "valign": "top"}
|
||||
if err := normalizeCellStyleAliases(style, "x"); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if style["horizontal_alignment"] != "left" || style["vertical_alignment"] != "top" {
|
||||
t.Errorf("halign/valign not renamed: %#v", style)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("shorthand colliding with canonical is an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
style := map[string]interface{}{
|
||||
"horizontal_align": "center",
|
||||
"horizontal_alignment": "left",
|
||||
}
|
||||
err := normalizeCellStyleAliases(style, "cell_styles[0]")
|
||||
if err == nil {
|
||||
t.Fatalf("expected conflict error, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "conflicts with horizontal_alignment") {
|
||||
t.Errorf("error should name the conflict: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("no shorthand leaves the map untouched", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
style := map[string]interface{}{"font_weight": "bold", "horizontal_alignment": "center"}
|
||||
if err := normalizeCellStyleAliases(style, "x"); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(style) != 2 || style["font_weight"] != "bold" || style["horizontal_alignment"] != "center" {
|
||||
t.Errorf("map should be unchanged: %#v", style)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty map is a no-op", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if err := normalizeCellStyleAliases(map[string]interface{}{}, "x"); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestNormalizeTypedCellsStyleAliases pins the 2D --cells walk: every cell's
|
||||
// inline cell_styles is normalized, malformed shapes are skipped (matching the
|
||||
// pass-through contract) rather than rejected, and a conflict propagates.
|
||||
func TestNormalizeTypedCellsStyleAliases(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("normalizes inline cell_styles across the grid", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
cells := []interface{}{
|
||||
[]interface{}{
|
||||
map[string]interface{}{
|
||||
"value": "x",
|
||||
"cell_styles": map[string]interface{}{"horizontal_align": "center"},
|
||||
},
|
||||
map[string]interface{}{"value": "y"}, // no cell_styles → untouched
|
||||
},
|
||||
}
|
||||
if err := normalizeTypedCellsStyleAliases(cells, "--cells"); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
row := cells[0].([]interface{})
|
||||
st := row[0].(map[string]interface{})["cell_styles"].(map[string]interface{})
|
||||
if st["horizontal_alignment"] != "center" {
|
||||
t.Errorf("cell_styles not normalized: %#v", st)
|
||||
}
|
||||
if _, ok := st["horizontal_align"]; ok {
|
||||
t.Errorf("shorthand should be removed: %#v", st)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("malformed shapes are skipped, not rejected", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
cells := []interface{}{
|
||||
"not-a-row",
|
||||
[]interface{}{
|
||||
"not-a-cell",
|
||||
map[string]interface{}{"cell_styles": "not-a-map"},
|
||||
},
|
||||
}
|
||||
if err := normalizeTypedCellsStyleAliases(cells, "--cells"); err != nil {
|
||||
t.Fatalf("lenient walk should not error on odd shapes: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("conflict inside a cell propagates", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
cells := []interface{}{
|
||||
[]interface{}{
|
||||
map[string]interface{}{
|
||||
"cell_styles": map[string]interface{}{
|
||||
"valign": "top",
|
||||
"vertical_alignment": "middle",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
err := normalizeTypedCellsStyleAliases(cells, "--cells")
|
||||
if err == nil {
|
||||
t.Fatalf("expected conflict error, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "--cells[0][0].cell_styles") {
|
||||
t.Errorf("error should carry the cell path: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestCellsSet_StyleAliasesNormalized is the end-to-end guard for +cells-set:
|
||||
// a typed --cells payload using alignment shorthands reaches set_cell_range
|
||||
// with canonical field names so the backend doesn't silently drop them.
|
||||
func TestCellsSet_StyleAliasesNormalized(t *testing.T) {
|
||||
t.Parallel()
|
||||
cells := `[[{"value":"Header","cell_styles":{"horizontal_align":"center","vertical_align":"middle","font_weight":"bold"}}]]`
|
||||
body := parseDryRunBody(t, CellsSet, []string{
|
||||
"--url", testURL, "--sheet-id", testSheetID,
|
||||
"--range", "A1", "--cells", cells,
|
||||
})
|
||||
input := decodeToolInput(t, body, "set_cell_range")
|
||||
raw, _ := json.Marshal(input["cells"])
|
||||
s := string(raw)
|
||||
if !strings.Contains(s, `"horizontal_alignment":"center"`) || !strings.Contains(s, `"vertical_alignment":"middle"`) {
|
||||
t.Errorf("alignment shorthands not normalized in cells: %s", s)
|
||||
}
|
||||
if strings.Contains(s, `"horizontal_align":`) || strings.Contains(s, `"vertical_align":`) {
|
||||
t.Errorf("shorthand keys leaked through to backend payload: %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWorkbookCreate_StyleAliasesNormalized is the end-to-end guard for
|
||||
// +workbook-create --styles: alignment shorthands in a cell_styles op are
|
||||
// accepted (no "unsupported style field" error) and emitted as canonical
|
||||
// field names merged into the fill cells.
|
||||
func TestWorkbookCreate_StyleAliasesNormalized(t *testing.T) {
|
||||
t.Parallel()
|
||||
calls := parseDryRunAPI(t, WorkbookCreate, []string{
|
||||
"--title", "Sales",
|
||||
"--values", `[["Name","Score"],["alice",95]]`,
|
||||
"--styles", `{"styles":[{"name":"Sheet1","cell_styles":[{"range":"A1:B2","horizontal_align":"center","vertical_align":"middle"}]}]}`,
|
||||
})
|
||||
if len(calls) != 2 {
|
||||
t.Fatalf("api calls = %d, want 2 (create + fill)", len(calls))
|
||||
}
|
||||
body, _ := calls[1].(map[string]interface{})["body"].(map[string]interface{})
|
||||
input := decodeToolInput(t, body, "set_cell_range")
|
||||
raw, _ := json.Marshal(input["cells"])
|
||||
s := string(raw)
|
||||
if c := strings.Count(s, `"horizontal_alignment":"center"`); c != 4 {
|
||||
t.Errorf("horizontal_alignment occurrences = %d, want 4 in 2x2 range; cells=%s", c, s)
|
||||
}
|
||||
if strings.Contains(s, `"horizontal_align":`) || strings.Contains(s, `"vertical_align":`) {
|
||||
t.Errorf("shorthand keys leaked through after normalization: %s", s)
|
||||
}
|
||||
}
|
||||
@@ -1192,6 +1192,9 @@ func normalizeWorkbookCreateStyleObject(in map[string]interface{}, path string)
|
||||
if len(in) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if err := normalizeCellStyleAliases(in, path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := map[string]interface{}{}
|
||||
cellStyle := map[string]interface{}{}
|
||||
for k, v := range in {
|
||||
|
||||
@@ -88,6 +88,9 @@ func cellsSetInput(runtime flagView, token, sheetID, sheetName string) (map[stri
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := normalizeTypedCellsStyleAliases(cells, "--cells"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
input := map[string]interface{}{
|
||||
"excel_id": token,
|
||||
"range": strings.TrimSpace(runtime.Str("range")),
|
||||
|
||||
@@ -140,7 +140,7 @@ _系统:`--dry-run`_
|
||||
| `--folder-token` | string | optional | 目标文件夹 token;省略时放在云空间根目录 |
|
||||
| `--values` | string + File + Stdin(简单 JSON) | optional | untyped 初始数据,一个 JSON 二维数组(表头并入第一行):`[["列A","列B"],["alice",95]]`;值原样写入、类型由飞书自动识别,走与 --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, ...}}`。Agents 通常用 `{**json.loads(df.to_json(orient="split")), "dtypes": df.dtypes.astype(str).to_dict()}` 一行构造。与 --values、--dataframe 互斥;新表默认子表复用为第一个子表,日期/数字类型保真。 |
|
||||
| `--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 忽略)。 |
|
||||
| `--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`。 |
|
||||
| `--dataframe` | string | optional | 单 sheet 类型保真表格的二进制入口,从一个 Arrow IPC 文件(Feather v2,pandas `df.to_feather()` 直接写出)读入,与 --values / --sheets 互斥。用 `@<path>` 传文件或 `-` 读二进制 stdin(同其他输入 flag 的约定)。Arrow 字节按原样读 —— 不做 TrimSpace / BOM strip,IPC magic 字节完整保留(区别于文本类输入 flag)。列类型从 Arrow schema 推导;每列的 `number_format` 可写在 Arrow Field metadata 里。建表后写入默认子表(`Sheet1` —— 直接复用,不残留空 Sheet1)。要多子表或换落点,请改用 `--sheets`。 |
|
||||
|
||||
### `+workbook-export`
|
||||
@@ -231,7 +231,7 @@ python prepare.py | lark-cli sheets +workbook-create --title "交易" --datafram
|
||||
|
||||
`--styles` 可在建表写入时同时写视觉处理。它和 `--sheets` 一样只有一种外层写法:顶层对象里放 `styles` 数组;数组每项对应一个子表,含 `name`,并按能力拆成四类可选数组:
|
||||
|
||||
- `cell_styles`:像 `+cells-set-style`,用 A1 单元格 `range` 加扁平样式字段(`font_weight` / `background_color` / `number_format` 等)和可选 `border_styles`;这些样式会随内容在同一次写入里一并应用。
|
||||
- `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`。
|
||||
@@ -245,7 +245,7 @@ lark-cli sheets +workbook-create --title "销售" \
|
||||
--styles '{
|
||||
"styles":[
|
||||
{"name":"Sheet1","cell_styles":[
|
||||
{"range":"A1:B1","font_weight":"bold","background_color":"#f5f5f5"},
|
||||
{"range":"A1:B1","font_weight":"bold","background_color":"#f5f5f5","horizontal_alignment":"center","vertical_alignment":"middle"},
|
||||
{"range":"B2:B3","number_format":"#,##0"}
|
||||
]}
|
||||
]
|
||||
|
||||
@@ -317,7 +317,7 @@ _公共:URL/token(无 sheet 定位) · 系统:`--dry-run`_
|
||||
| --- | --- | --- | --- |
|
||||
| `--sheets` | string + File + Stdin(复合 JSON) | xor | Typed 表格协议(pandas-DataFrame-shaped)JSON,与 `--dataframe` 互斥:顶层 sheets 数组,每项 `{name, start_cell?, mode?, header?, allow_overwrite?, columns:["colA","colB",...], data:[[...]], dtypes?:{colA:pandasDtype, ...}, formats?:{colA:numberFormat, ...}}`。Agents 通常用 `{**json.loads(df.to_json(orient="split")), "dtypes": df.dtypes.astype(str).to_dict()}` 一行构造。`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 列用文本格式 `@`。 |
|
||||
| `--dataframe` | string | xor | 单 sheet 类型保真表格的二进制入口,从一个 Arrow IPC 文件(即 Feather v2,pandas `df.to_feather()` 直接写出)读入,与 `--sheets` 互斥。用 `@<path>` 传文件或 `-` 读二进制 stdin(同其他输入 flag 的约定)。Arrow 字节按原样读 —— 不做 TrimSpace / BOM strip,IPC magic 字节完整保留(区别于文本类输入 flag)。列类型从 Arrow schema 推导(int*/uint*/float* → number,date32/date64/timestamp → date,utf8/large_utf8 → string,bool → bool);每列的 `number_format` 可写在 Arrow Field metadata 里(`pa.field("price", pa.float64(), metadata={b"number_format": b"$#,##0.00"})`)。子表走默认落点:名为 `Sheet1`(缺则新建),从 A1 起覆盖写并带表头。要换子表名 / 起始位置 / 写入方式,或要写多子表,请改用 `--sheets`。 |
|
||||
| `--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.sheets 对应;配 --dataframe(单子表,名为 Sheet1)时只给一个 name 为 `Sheet1` 的 styles 项。 |
|
||||
| `--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.sheets 对应;配 --dataframe(单子表,名为 Sheet1)时只给一个 name 为 `Sheet1` 的 styles 项。完整 cell_styles 字段结构跑 `+table-put --print-schema --flag-name styles`。 |
|
||||
|
||||
## Schemas
|
||||
|
||||
@@ -592,7 +592,7 @@ subprocess.run(["lark-cli","sheets","+table-put","--url",URL,"--dataframe","-"],
|
||||
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"}],
|
||||
"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}]}]}'
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user