@wecom/wecom-openclaw-plugin 2026.9.15 → 2026.10.19-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/package.json +2 -2
  2. package/skills/wecomcli-sheet/SKILL.md +64 -107
  3. package/skills/wecomcli-sheet/references/sheet-conditional-formatting.md +211 -0
  4. package/skills/wecomcli-sheet/references/sheet-filter.md +187 -0
  5. package/skills/wecomcli-sheet/references/sheet-float-image.md +248 -0
  6. package/skills/wecomcli-sheet/references/sheet-formulas.md +73 -0
  7. package/skills/wecomcli-sheet/references/sheet-history.md +125 -0
  8. package/skills/wecomcli-sheet/references/sheet-protection.md +180 -0
  9. package/skills/wecomcli-sheet/references/sheet-range-operations.md +323 -0
  10. package/skills/wecomcli-sheet/references/sheet-read-data.md +153 -0
  11. package/skills/wecomcli-sheet/references/sheet-search-replace.md +111 -0
  12. package/skills/wecomcli-sheet/references/sheet-structure-operations.md +416 -0
  13. package/skills/wecomcli-sheet/references/sheet-style.md +246 -0
  14. package/skills/wecomcli-sheet/references/sheet-validation.md +243 -0
  15. package/skills/wecomcli-sheet/references/sheet-workbook.md +271 -0
  16. package/skills/wecomcli-sheet/references/sheet-write-cells.md +332 -0
  17. package/skills/wecomcli-sheet/references/xlsx-process.md +328 -0
  18. package/skills/wecomcli-sheet/scripts/collect.py +868 -0
  19. package/skills/wecomcli-sheet/scripts/recalc.py +403 -0
  20. package/skills/wecomcli-sheet/references/sheet-contents-update.md +0 -49
  21. package/skills/wecomcli-sheet/references/sheet-ranges-get.md +0 -47
  22. package/skills/wecomcli-sheet/references/sheet-rows-append.md +0 -47
  23. package/skills/wecomcli-sheet/references/sheet-subsheets-add.md +0 -28
  24. package/skills/wecomcli-sheet/references/sheet-subsheets-delete.md +0 -22
@@ -0,0 +1,403 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """recalc.py — Excel 公式重算器。
4
+
5
+ 由 xlsx_recalc wheel(src/xlsx_recalc 的 _recalc.py + _soffice.py)还原而来,
6
+ 两个模块内联为单文件脚本,恢复 CLI 入口,agent 直接以子进程方式调用:
7
+
8
+ python3 recalc.py <file.xlsx> [timeout_seconds] [--skip-external-link-check]
9
+
10
+ 基于 LibreOffice 宏(RecalculateAndSave: calculateAll → store → close)显式重算
11
+ xlsx 全部公式并写入缓存值,**原地覆盖文件**;随后扫描全部单元格的 Excel 错误
12
+ (#REF!/#DIV/0!/#VALUE!/#NAME?/#NULL!/#NUM!/#N/A),按错误类型给出计数与位置。
13
+
14
+ 内置沙箱适配层:AF_UNIX socket 被封禁时自动编译并挂 LD_PRELOAD C shim
15
+ (lo_socket_shim.so),让 LibreOffice UNO socket 在受限沙箱里跑通。需要 gcc
16
+ (仅首次编译 shim;正常环境 socket 没被封则不需要 gcc)。
17
+
18
+ 前置条件:
19
+ - LibreOffice (soffice) 已安装且在 PATH。
20
+ - openpyxl 已安装(沙箱默认装)。
21
+ - 文件必须可写(宏 store() 原地覆盖)。agent 应先把 openpyxl 产出保存到
22
+ 沙箱可写目录(output/ 或 temp/),再调本脚本。
23
+ - 默认开外链闸门:检测到 externalLinks 直接返回 error,防止 store()
24
+ 静默剥缓存值致公式变 #NAME?。确认无外链或已拷值时传
25
+ --skip-external-link-check 跳过。
26
+
27
+ 输出 JSON 到 stdout;含 "error" 键时退出码 1,否则 0。
28
+ """
29
+
30
+ import argparse
31
+ import json
32
+ import os
33
+ import platform
34
+ import socket
35
+ import subprocess
36
+ import sys
37
+ import tempfile
38
+ import zipfile
39
+ from pathlib import Path
40
+
41
+ from openpyxl import load_workbook
42
+
43
+ MACRO_DIR_MACOS = "~/Library/Application Support/LibreOffice/4/user/basic/Standard"
44
+ MACRO_DIR_LINUX = "~/.config/libreoffice/4/user/basic/Standard"
45
+ MACRO_FILENAME = "Module1.xba"
46
+
47
+ # LibreOffice 宏:强制重算全部公式 → 原地保存 → 关闭。
48
+ # 比 soffice --convert-to xlsx 的"加载时重算"更显式可靠。
49
+ RECALCULATE_MACRO = """<?xml version="1.0" encoding="UTF-8"?>
50
+ <!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
51
+ <script:module xmlns:script="http://openoffice.org/2000/script" script:name="Module1" script:language="StarBasic">
52
+ Sub RecalculateAndSave()
53
+ ThisComponent.calculateAll()
54
+ ThisComponent.store()
55
+ ThisComponent.close(True)
56
+ End Sub
57
+ </script:module>"""
58
+
59
+ EXCEL_ERRORS = [
60
+ "#VALUE!", "#DIV/0!", "#REF!", "#NAME?", "#NULL!", "#NUM!", "#N/A",
61
+ ]
62
+
63
+
64
+ # --------------------------------------------------------------------------- #
65
+ # soffice 沙箱适配层(原 _soffice.py)
66
+ # --------------------------------------------------------------------------- #
67
+
68
+ _SHIM_SO = Path(tempfile.gettempdir()) / "lo_socket_shim.so"
69
+
70
+
71
+ def get_soffice_env() -> dict:
72
+ """返回跑 soffice 所需的 env dict。
73
+
74
+ - 固定设 SAL_USE_VCLPLUGIN=svp(无头渲染)。
75
+ - 若 AF_UNIX socket 被封禁(沙箱),编译并挂 lo_socket_shim.so 到 LD_PRELOAD。
76
+ """
77
+ env = os.environ.copy()
78
+ env["SAL_USE_VCLPLUGIN"] = "svp"
79
+
80
+ if _needs_shim():
81
+ shim = _ensure_shim()
82
+ env["LD_PRELOAD"] = str(shim)
83
+
84
+ return env
85
+
86
+
87
+ def _needs_shim() -> bool:
88
+ """AF_UNIX socket 能创建就不需要 shim。"""
89
+ try:
90
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
91
+ s.close()
92
+ return False
93
+ except OSError:
94
+ return True
95
+
96
+
97
+ def _ensure_shim() -> Path:
98
+ """编译(仅首次)并返回 shim .so 路径。需要 gcc。"""
99
+ if _SHIM_SO.exists():
100
+ return _SHIM_SO
101
+
102
+ src = Path(tempfile.gettempdir()) / "lo_socket_shim.c"
103
+ src.write_text(_SHIM_SOURCE)
104
+ subprocess.run(
105
+ ["gcc", "-shared", "-fPIC", "-o", str(_SHIM_SO), str(src), "-ldl"],
106
+ check=True,
107
+ capture_output=True,
108
+ )
109
+ src.unlink()
110
+ return _SHIM_SO
111
+
112
+
113
+ # LD_PRELOAD 挂的这个 C shim 把 LibreOffice 内部的 socket(AF_UNIX) 调用
114
+ # 重定向到 socketpair+pipe,绕开沙箱对 AF_UNIX 的封禁。仅 FD<1024 生效。
115
+ # 源自 xlsx 插件 office/soffice.py,逻辑未改动。
116
+ _SHIM_SOURCE = r"""
117
+ #define _GNU_SOURCE
118
+ #include <dlfcn.h>
119
+ #include <errno.h>
120
+ #include <signal.h>
121
+ #include <stdio.h>
122
+ #include <stdlib.h>
123
+ #include <sys/socket.h>
124
+ #include <unistd.h>
125
+
126
+ static int (*real_socket)(int, int, int);
127
+ static int (*real_socketpair)(int, int, int, int[2]);
128
+ static int (*real_listen)(int, int);
129
+ static int (*real_accept)(int, struct sockaddr *, socklen_t *);
130
+ static int (*real_close)(int);
131
+ static int (*real_read)(int, void *, size_t);
132
+
133
+ /* Per-FD bookkeeping (FDs >= 1024 are passed through unshimmed). */
134
+ static int is_shimmed[1024];
135
+ static int peer_of[1024];
136
+ static int wake_r[1024]; /* accept() blocks reading this */
137
+ static int wake_w[1024]; /* close() writes to this */
138
+ static int listener_fd = -1; /* FD that received listen() */
139
+
140
+ __attribute__((constructor))
141
+ static void init(void) {
142
+ real_socket = dlsym(RTLD_NEXT, "socket");
143
+ real_socketpair = dlsym(RTLD_NEXT, "socketpair");
144
+ real_listen = dlsym(RTLD_NEXT, "listen");
145
+ real_accept = dlsym(RTLD_NEXT, "accept");
146
+ real_close = dlsym(RTLD_NEXT, "close");
147
+ real_read = dlsym(RTLD_NEXT, "read");
148
+ for (int i = 0; i < 1024; i++) {
149
+ peer_of[i] = -1;
150
+ wake_r[i] = -1;
151
+ wake_w[i] = -1;
152
+ }
153
+ }
154
+
155
+ /* ---- socket ---------------------------------------------------------- */
156
+ int socket(int domain, int type, int protocol) {
157
+ if (domain == AF_UNIX) {
158
+ int fd = real_socket(domain, type, protocol);
159
+ if (fd >= 0) return fd;
160
+ /* socket(AF_UNIX) blocked - fall back to socketpair(). */
161
+ int sv[2];
162
+ if (real_socketpair(domain, type, protocol, sv) == 0) {
163
+ if (sv[0] >= 0 && sv[0] < 1024) {
164
+ is_shimmed[sv[0]] = 1;
165
+ peer_of[sv[0]] = sv[1];
166
+ int wp[2];
167
+ if (pipe(wp) == 0) {
168
+ wake_r[sv[0]] = wp[0];
169
+ wake_w[sv[0]] = wp[1];
170
+ }
171
+ }
172
+ return sv[0];
173
+ }
174
+ errno = EPERM;
175
+ return -1;
176
+ }
177
+ return real_socket(domain, type, protocol);
178
+ }
179
+
180
+ /* ---- listen ---------------------------------------------------------- */
181
+ int listen(int sockfd, int backlog) {
182
+ if (sockfd >= 0 && sockfd < 1024 && is_shimmed[sockfd]) {
183
+ listener_fd = sockfd;
184
+ return 0;
185
+ }
186
+ return real_listen(sockfd, backlog);
187
+ }
188
+
189
+ /* ---- accept ---------------------------------------------------------- */
190
+ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
191
+ if (sockfd >= 0 && sockfd < 1024 && is_shimmed[sockfd]) {
192
+ /* Block until close() writes to the wake pipe. */
193
+ if (wake_r[sockfd] >= 0) {
194
+ char buf;
195
+ real_read(wake_r[sockfd], &buf, 1);
196
+ }
197
+ errno = ECONNABORTED;
198
+ return -1;
199
+ }
200
+ return real_accept(sockfd, addr, addrlen);
201
+ }
202
+
203
+ /* ---- close ----------------------------------------------------------- */
204
+ int close(int fd) {
205
+ if (fd >= 0 && fd < 1024 && is_shimmed[fd]) {
206
+ int was_listener = (fd == listener_fd);
207
+ is_shimmed[fd] = 0;
208
+
209
+ if (wake_w[fd] >= 0) { /* unblock accept() */
210
+ char c = 0;
211
+ write(wake_w[fd], &c, 1);
212
+ real_close(wake_w[fd]);
213
+ wake_w[fd] = -1;
214
+ }
215
+ if (wake_r[fd] >= 0) { real_close(wake_r[fd]); wake_r[fd] = -1; }
216
+ if (peer_of[fd] >= 0) { real_close(peer_of[fd]); peer_of[fd] = -1; }
217
+
218
+ if (was_listener)
219
+ _exit(0); /* conversion done - exit */
220
+ }
221
+ return real_close(fd);
222
+ }
223
+ """
224
+
225
+
226
+ # --------------------------------------------------------------------------- #
227
+ # 重算核心(原 _recalc.py)
228
+ # --------------------------------------------------------------------------- #
229
+
230
+ def has_gtimeout() -> bool:
231
+ try:
232
+ subprocess.run(
233
+ ["gtimeout", "--version"], capture_output=True, timeout=1, check=False
234
+ )
235
+ return True
236
+ except (FileNotFoundError, subprocess.TimeoutExpired):
237
+ return False
238
+
239
+
240
+ def setup_libreoffice_macro() -> bool:
241
+ """确保 LibreOffice 用户配置里有 RecalculateAndSave 宏。"""
242
+ macro_dir = os.path.expanduser(
243
+ MACRO_DIR_MACOS if platform.system() == "Darwin" else MACRO_DIR_LINUX
244
+ )
245
+ macro_file = os.path.join(macro_dir, MACRO_FILENAME)
246
+
247
+ if os.path.exists(macro_file) and "RecalculateAndSave" in Path(macro_file).read_text():
248
+ return True
249
+
250
+ if not os.path.exists(macro_dir):
251
+ # 首次跑 soffice 让它生成用户配置目录树
252
+ subprocess.run(
253
+ ["soffice", "--headless", "--terminate_after_init"],
254
+ capture_output=True, timeout=10, check=False, env=get_soffice_env(),
255
+ )
256
+ os.makedirs(macro_dir, exist_ok=True)
257
+
258
+ try:
259
+ Path(macro_file).write_text(RECALCULATE_MACRO)
260
+ return True
261
+ except Exception:
262
+ return False
263
+
264
+
265
+ def _has_external_links(path: str) -> bool:
266
+ """检测 xlsx 内部是否存在外链(externalLinks)部件。"""
267
+ try:
268
+ with zipfile.ZipFile(path) as z:
269
+ return any("externallink" in n.lower() for n in z.namelist())
270
+ except Exception:
271
+ return False
272
+
273
+
274
+ def recalc(filename, timeout=30, check_external_links=True):
275
+ """用 LibreOffice 宏重算 xlsx 全部公式,原地保存并扫描错误。
276
+
277
+ 参数:
278
+ filename (str): xlsx 文件路径(必须可写——宏会 store() 原地覆盖)。
279
+ timeout (int): soffice 超时秒数,默认 30。
280
+ check_external_links (bool): 默认 True。检测到外链直接返回 error,
281
+ 防止 LibreOffice store() 静默剥掉外链缓存值致公式变 #NAME?。
282
+ 确认无外链依赖或已手动拷出值时可传 False 跳过。
283
+
284
+ 返回 dict:
285
+ 成功无错: {"status":"success","total_errors":0,"total_formulas":N}
286
+ 成功有错: {"status":"errors_found","total_errors":N,"total_formulas":N,
287
+ "error_summary":{"#REF!":{"count":2,"locations":[...]}, ...}}
288
+ 失败: {"error":"..."} (文件不存在/soffice 缺失/宏配置失败/超时等)
289
+ """
290
+ if not Path(filename).exists():
291
+ return {"error": f"File {filename} does not exist"}
292
+
293
+ abs_path = str(Path(filename).absolute())
294
+
295
+ # 外链前置闸门(默认开)。='[1]xxx'!$B$2 这类外链,store() 会剥缓存值。
296
+ if check_external_links and _has_external_links(abs_path):
297
+ return {"error": "检测到外链(externalLinks);先把外链单元格值拷出再重算"}
298
+
299
+ if not setup_libreoffice_macro():
300
+ return {"error": "Failed to setup LibreOffice macro"}
301
+
302
+ cmd = [
303
+ "soffice",
304
+ "--headless",
305
+ "--norestore",
306
+ "vnd.sun.star.script:Standard.Module1.RecalculateAndSave?language=Basic&location=application",
307
+ abs_path,
308
+ ]
309
+
310
+ if platform.system() == "Linux":
311
+ cmd = ["timeout", str(timeout)] + cmd
312
+ elif platform.system() == "Darwin" and has_gtimeout():
313
+ cmd = ["gtimeout", str(timeout)] + cmd
314
+
315
+ try:
316
+ result = subprocess.run(cmd, capture_output=True, text=True, env=get_soffice_env())
317
+ except FileNotFoundError:
318
+ return {"error": "soffice 未安装或不在 PATH;请先安装 LibreOffice"}
319
+
320
+ # returncode 124 = timeout 超时退出码(Linux timeout / macOS gtimeout)
321
+ if result.returncode != 0 and result.returncode != 124:
322
+ error_msg = result.stderr or "Unknown error during recalculation"
323
+ if "Module1" in error_msg or "RecalculateAndSave" not in error_msg:
324
+ return {"error": "LibreOffice macro not configured properly"}
325
+ return {"error": error_msg}
326
+
327
+ try:
328
+ wb = load_workbook(filename, data_only=True)
329
+
330
+ error_details = {err: [] for err in EXCEL_ERRORS}
331
+ total_errors = 0
332
+
333
+ for sheet_name in wb.sheetnames:
334
+ ws = wb[sheet_name]
335
+ for row in ws.iter_rows():
336
+ for cell in row:
337
+ if cell.value is not None and isinstance(cell.value, str):
338
+ for err in EXCEL_ERRORS:
339
+ if err in cell.value:
340
+ error_details[err].append(
341
+ f"{sheet_name}!{cell.coordinate}"
342
+ )
343
+ total_errors += 1
344
+ break
345
+
346
+ wb.close()
347
+
348
+ out = {
349
+ "status": "success" if total_errors == 0 else "errors_found",
350
+ "total_errors": total_errors,
351
+ "error_summary": {},
352
+ }
353
+
354
+ for err_type, locations in error_details.items():
355
+ if locations:
356
+ out["error_summary"][err_type] = {
357
+ "count": len(locations),
358
+ "locations": locations[:20], # 每类最多 20 个位置
359
+ }
360
+
361
+ wb_formulas = load_workbook(filename, data_only=False)
362
+ formula_count = 0
363
+ for sheet_name in wb_formulas.sheetnames:
364
+ ws = wb_formulas[sheet_name]
365
+ for row in ws.iter_rows():
366
+ for cell in row:
367
+ if (cell.value and isinstance(cell.value, str)
368
+ and cell.value.startswith("=")):
369
+ formula_count += 1
370
+ wb_formulas.close()
371
+
372
+ out["total_formulas"] = formula_count
373
+
374
+ return out
375
+
376
+ except Exception as e:
377
+ return {"error": str(e)}
378
+
379
+
380
+ # --------------------------------------------------------------------------- #
381
+ # CLI
382
+ # --------------------------------------------------------------------------- #
383
+
384
+ def main():
385
+ parser = argparse.ArgumentParser(
386
+ description="用 LibreOffice 宏重算 xlsx 全部公式,原地保存并扫描 Excel 错误。"
387
+ "输出 JSON 到 stdout。")
388
+ parser.add_argument("file", help="xlsx 文件路径(必须可写,宏 store() 原地覆盖)")
389
+ parser.add_argument("timeout", nargs="?", type=int, default=30,
390
+ help="soffice 超时秒数(默认 30)")
391
+ parser.add_argument("--skip-external-link-check", action="store_true",
392
+ help="跳过外链(externalLinks)前置闸门。"
393
+ "仅在确认无外链依赖或已手动拷出值时使用")
394
+ args = parser.parse_args()
395
+
396
+ result = recalc(args.file, timeout=args.timeout,
397
+ check_external_links=not args.skip_external_link_check)
398
+ print(json.dumps(result, indent=2, ensure_ascii=False))
399
+ sys.exit(1 if "error" in result else 0)
400
+
401
+
402
+ if __name__ == "__main__":
403
+ main()
@@ -1,49 +0,0 @@
1
- # 修改表格内容 — `wecom-cli sheet contents update`
2
-
3
- 修改**在线表格**指定区域的内容与格式,通过 `grid_data` 指定写入的起始位置与各单元格数据。
4
-
5
- ## 调用方式
6
-
7
- 使用 wecom-cli 工具按照下面格式调用
8
-
9
- ```
10
- wecom-cli sheet contents update --json '<JSON 参数>'
11
- ```
12
-
13
- ## 参数
14
-
15
- | 字段 | 类型 | 必填 | 默认值 | 语义 |
16
- |---|---|---|---|---|
17
- | `docid` | string | 是 | — | 在线表格 ID |
18
- | `sheet_id` | string | 是 | — | 工作表 ID;通过 `sheet get` 获取 |
19
- | `grid_data` | object | 是 | — | 写入区域的数据 |
20
- | `grid_data.start_row` | int | 是 | — | 起始行号,从 0 起 |
21
- | `grid_data.start_column` | int | 是 | — | 起始列号,从 0 起 |
22
- | `grid_data.rows` | array | 是 | — | 各行数据 |
23
-
24
- `grid_data.rows[].values[]` 对象结构:
25
-
26
- | 子字段 | 类型 | 说明 |
27
- |---|---|---|
28
- | `cell_value` | object | 单元格值,见下方「cell_value 类型选择」 |
29
- | `data_type` | string | 与 `cell_value` 对应的数据类型 |
30
- | `cell_format` | object | 单元格样式;传空对象 `{}` 表示默认样式 |
31
-
32
- ### cell_value 类型选择
33
-
34
- | 形态 | 结构 | 适用场景 |
35
- |---|---|---|
36
- | `text` | `{"text": "<纯文本>"}` | 纯文本内容(如姓名、说明、标签、编号字符串等) |
37
- | `number` | `{"number": 123.45}` | 数值,用于金额、数量、比率等需要参与公式计算或聚合的数据;值为 JSON 数字类型,不加引号 |
38
- | `formula` | `{"formula": "=SUM(A1,A2)"}` | 任何以 `=` 开头的公式,包括 `=SUM(...)`、`=A1+B1`、`=IF(...)`、`=VLOOKUP(...)` 等 |
39
- | `link` | `{"link": {"url": "<URL>", "text": "<显示文本>"}}` | 超链接 |
40
-
41
- ## 返回
42
-
43
- | 字段 | 类型 | 说明 |
44
- |---|---|---|
45
- | `grid_data` | object | 写入的数据,结构与入参 `grid_data` 一致 |
46
-
47
- ## 使用规则
48
-
49
- - **格式与已有内容对齐**:向已有内容的表格追加数据时,新行的样式应尽量与现有表格保持一致,避免出现字体、字号、对齐、边框、底色等风格突兀的行。
@@ -1,47 +0,0 @@
1
- # 读取子表数据 — `wecom-cli sheet ranges get`
2
-
3
- 根据 `docid`、`sheet_id` 读取**在线表格**指定子表的全部数据。可通过 `mode` 参数选择返回结构化的表格数据(含格式信息),或返回 CSV(内容或文件路径)。
4
-
5
- ## 调用方式
6
-
7
- 使用 wecom-cli 工具按照下面格式调用
8
-
9
- ```
10
- wecom-cli sheet ranges get --json '<JSON 参数>'
11
- ```
12
-
13
- ## 参数
14
-
15
- | 字段 | 类型 | 必填 | 默认值 | 语义 |
16
- |---|---|---|---|---|
17
- | `docid` | string | 是 | — | 在线表格 ID |
18
- | `sheet_id` | string | 是 | — | 工作表 ID;通过 `sheet get` 获取 |
19
- | `mode` | string | 否 | `"default"` | 返回格式选择:`"default"` 返回结构化 `grid_data`(含单元格格式);填 `"csv"` 返回 CSV(内容或文件路径) |
20
- | `range` | string | 条件必填 | — | 当 `mode="default"` 时**必填**,表示要读取的区域,形如 `"A1:A100"`;取值可从 `sheet get` 返回的 `sheets[].data_range` 拿到。`mode="csv"` 时忽略此字段 |
21
-
22
- ### `mode` 如何选择
23
-
24
- 默认一律使用 `"default"`,包括普通的读取、查看、展示数据等场景。此时必须同时传 `range`,返回 `grid_data`(含单元格值、格式、数据类型等完整信息)。
25
- 仅当用户明确表达需要对数据做统计、计算、聚合分析(例如"求和/平均/分组统计/透视/跑数据分析"等)时,才填 `"csv"`,便于直接把数据交给计算流程。
26
-
27
- ## 返回
28
-
29
- ### `mode` 为 `"default"`(默认)
30
-
31
- | 字段 | 类型 | 说明 |
32
- |---|---|---|
33
- | `grid_data` | object | 结构化表格数据,包含每个单元格的值(`cell_value`)、格式(`cell_format`,如字体、字号、颜色、对齐方式)和数据类型(`data_type`) |
34
-
35
- ### `mode` 为 `"csv"`
36
-
37
- 回包可能是以下两种形式之一(取决于数据大小):
38
-
39
- | 字段 | 类型 | 说明 |
40
- |---|---|---|
41
- | `content` | string | CSV 内容(直接返回) |
42
- | `file_path` | string | CSV 文件落盘后的绝对路径 |
43
-
44
- ## 使用规则
45
-
46
- - `mode="csv"` 且返回 `file_path` 时:必须再用 `read` 工具读取该文件内容,才能展示给用户或继续做分析。
47
- - `mode="csv"` 且返回 `content` 时:可直接消费,无需再次读取文件。
@@ -1,47 +0,0 @@
1
- # 追加一行数据 — `wecom-cli sheet rows append`
2
-
3
- 向**在线表格**的指定子工作表末尾自动追加一行数据,无需指定行号——数据将写到最末一行之后。
4
-
5
- ## 调用方式
6
-
7
- 使用 wecom-cli 工具按照下面格式调用
8
-
9
- ```
10
- wecom-cli sheet rows append --json '<JSON 参数>'
11
- ```
12
-
13
- ## 参数
14
-
15
- | 字段 | 类型 | 必填 | 默认值 | 语义 |
16
- |---|---|---|---|---|
17
- | `docid` | string | 是 | — | 在线表格 ID |
18
- | `sheet_id` | string | 是 | — | 工作表 ID;通过 `sheet get` 获取 |
19
- | `row` | object | 是 | — | 追加的一行数据 |
20
- | `row.values` | array | 是 | — | 单元格数组,按列顺序排列 |
21
-
22
- `row.values[]` 对象结构:
23
-
24
- | 子字段 | 类型 | 说明 |
25
- |---|---|---|
26
- | `cell_value` | object | 单元格值,见下方「cell_value 类型选择」|
27
- | `cell_format` | object | 单元格样式;传空对象 `{}` 表示默认样式 |
28
-
29
- ### cell_value 类型选择
30
-
31
- | 形态 | 结构 | 适用场景 |
32
- |---|---|---|
33
- | `text` | `{"text": "<纯文本>"}` | 纯文本内容(如姓名、说明、标签、编号字符串等) |
34
- | `number` | `{"number": 123.45}` | 数值,用于金额、数量、比率等需要参与公式计算或聚合的数据;值为 JSON 数字类型,不加引号 |
35
- | `formula` | `{"formula": "=SUM(A1,A2)"}` | 任何以 `=` 开头的公式,包括 `=SUM(...)`、`=A1+B1`、`=IF(...)`、`=VLOOKUP(...)` 等 |
36
- | `link` | `{"link": {"url": "<URL>", "text": "<显示文本>"}}` | 超链接 |
37
-
38
- ## 返回
39
-
40
- | 字段 | 类型 | 说明 |
41
- |---|---|---|
42
- | `row` | object | 写入的行数据,结构与入参 `row` 一致 |
43
-
44
- ## 使用规则
45
-
46
- - **逐行写入场景**:本接口会自动定位到子表最末一行之后追加;批量写不同区域请用 `sheet contents update`。
47
- - **格式与已有内容对齐**:向已有内容的表格追加数据时,新行的样式应尽量与现有表格保持一致,避免出现字体、字号、对齐、边框、底色等风格突兀的行。
@@ -1,28 +0,0 @@
1
- # 添加子工作表 — `wecom-cli sheet subsheets add`
2
-
3
- 向**在线表格**添加一个新的子工作表。
4
-
5
- ## 调用方式
6
-
7
- 使用 wecom-cli 工具按照下面格式调用
8
-
9
- ```
10
- wecom-cli sheet subsheets add --json '<JSON 参数>'
11
- ```
12
-
13
- ## 参数
14
-
15
- | 字段 | 类型 | 必填 | 默认值 | 语义 |
16
- |---|---|---|---|---|
17
- | `docid` | string | 是 | — | 在线表格 ID |
18
- | `sheet` | object | 是 | — | 子表信息 |
19
- | `sheet.title` | string | 是 | — | 工作表名称 |
20
- | `sheet.row_count` | int | 否 | — | 表格总行数 |
21
- | `sheet.column_count` | int | 否 | — | 表格总列数 |
22
- | `index` | int | 否 | — | 插入位置:`0` 表示插入到最后,`1` 表示插入到第一个位置 |
23
-
24
- ## 返回
25
-
26
- | 字段 | 类型 | 说明 |
27
- |---|---|---|
28
- | `sheet` | object | 新增的子表信息;含 `sheet_id`(唯一标识)/ `title` / `row_count` / `column_count` / `data_range`(新建时为空) |
@@ -1,22 +0,0 @@
1
- # 删除子工作表 — `wecom-cli sheet subsheets delete`
2
-
3
- 根据 `docid` 与 `sheet_id` 删除**在线表格**的指定子工作表。
4
-
5
- ## 调用方式
6
-
7
- 使用 wecom-cli 工具按照下面格式调用
8
-
9
- ```
10
- wecom-cli sheet subsheets delete --json '<JSON 参数>'
11
- ```
12
-
13
- ## 参数
14
-
15
- | 字段 | 类型 | 必填 | 默认值 | 语义 |
16
- |---|---|---|---|---|
17
- | `docid` | string | 是 | — | 在线表格 ID |
18
- | `sheet_id` | string | 是 | — | 要删除的工作表 ID;通过 `sheet get` 获取 |
19
-
20
- ## 返回
21
-
22
- 删除成功返回空对象。