sheetnext 0.1.8 → 0.1.9

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.
package/DOCS.md CHANGED
@@ -1,52 +1,159 @@
1
- # SheetNext API 文档
1
+ # SheetNext 简介
2
2
 
3
- > 详细的类、方法和属性说明
3
+ ## ✨ 核心特点
4
4
 
5
- ---
5
+ - **📊 完整的电子表格功能** - 支持单元格编辑、样式设置、公式引擎、图表绘制、数据排序、筛选等核心功能
6
+ - **🤖 AI 智能工作流** - 内置 AI 全自动操作流程,轻松实现模板生成、数据分析、公式编写、跨表逻辑操作等
7
+ - **📁 原生文件支持** - 原生支持 Excel (.xlsx)、CSV、JSON 文件的导入导出,无需额外插件
8
+ - **🚀 开箱即用** - 零配置开始使用,所有功能内置,无需单独安装依赖库
9
+ - **⚡ 高性能渲染** - 基于 Canvas 的虚拟滚动技术,轻松处理大数据量表格
10
+ - **🔄 快速迭代** - 版本持续更新,积极响应用户反馈和问题
6
11
 
7
- ## 目录
12
+ ## 📦 安装方式
8
13
 
9
- - [SheetNext Class](#SheetNext) - 工作簿管理:实例化编辑器、添加/删除/切换工作表、导入/导出 Excel、多实例支持等
10
- - [Sheet Class](#Sheet) - 工作表管理:行列管理、合并单元格、区域遍历、排序、批量插入数据、图形对象管理等
11
- - [Cell Class](#Cell) - 单元格管理:读写值/公式、样式设置、数字格式、超链接、数据验证等
12
- - [Row Class](#Row) - 行管理:行高、批量行样式、获取行内单元格、显示/隐藏等
13
- - [Col Class](#Col) - 列管理:列宽、批量列样式、获取列内单元格、显示/隐藏等
14
- - [Drawing Class](#Drawing) - 图形管理:图表、图形、图片的增删改查、设置位置和尺寸、调整图层顺序等
15
- - [Layout Class](#Layout) - 布局管理:显示/隐藏菜单栏、工具栏、公式栏、AI 聊天面板等界面元素
16
- - [Utils Class](#Utils) - 坐标转换:单元格字符串与数字坐标互转、列字母与数字互转、范围格式转换等
17
- - [UndoRedo Class](#UndoRedo) - 历史管理:撤销/重做操作
14
+ SheetNext 提供多种安装方式,满足不同项目需求:
18
15
 
19
- ---
16
+ - **npm/yarn 安装** - 适用于现代前端项目(React、Vue、Angular 等)
17
+ - **浏览器直接引入** - 通过 CDN 或本地文件直接在 HTML 中使用
20
18
 
21
- ## SheetNext
19
+ ## 🔗 相关链接
22
20
 
23
- SheetNext 是主入口类,管理整个电子表格应用。
21
+ - 🏠 [官网](https://www.sheetnext.com)
22
+ - 🎯 [在线体验](https://www.sheetnext.com/editor)
23
+ - 📦 [npm 包](https://www.npmjs.com/package/sheetnext)
24
+
25
+ # 快速开始
26
+
27
+ 只需几行代码,即可将 SheetNext 集成到您的项目中。支持 npm 安装和浏览器直接引入两种方式,满足不同场景需求。
28
+
29
+ ## 📦 使用 npm 安装
30
+
31
+ ```bash
32
+ npm install sheetnext
33
+ ```
34
+ ```html
35
+ <div id="SNContainer" style="width:100vw;height:100vh;padding:0 7px 7px"></div>
36
+ ```
37
+ ```javascript
38
+ import SheetNext from 'sheetnext';
39
+ import 'sheetnext/dist/sheetnext.css';
40
+
41
+ // 注意设置容器#SNContainer宽高
42
+ const SN = new SheetNext(document.querySelector('#SNContainer'));
43
+ ```
44
+
45
+ ## 🌐 浏览器直接引入
46
+
47
+ ```html
48
+ <!-- 引入样式 -->
49
+ <link rel="stylesheet" href="dist/sheetnext.css">
50
+
51
+ <!-- 编辑器容器 -->
52
+ <div id="SNContainer" style="width: 100vw; height: 100vh;padding:0 7px 7px"></div>
53
+
54
+ <!-- 引入脚本 -->
55
+ <!-- <script src="dist/sheetnext.umd.js"></script> -->
56
+
57
+ <!-- 初始化,注意设置宽高 -->
58
+ <!-- <script>
59
+ const SN = new SheetNext(document.querySelector('#SNContainer'));
60
+ </script> -->
61
+ ```
62
+
63
+ ## ⚙️ 初始化配置
64
+
65
+ SheetNext 支持多种可选配置参数,用于定制编辑器的功能和外观。
66
+
67
+ ```javascript
68
+ const SN = new SheetNext(document.querySelector('#container'), {
69
+ AI_URL: "http://localhost:3000/sheetnextAI", // AI 中转地址
70
+ AI_TOKEN: "your-token", // AI 中转 token
71
+ licenseKey: "your-license-key", // 授权密钥
72
+ menuList: (defaultList) => { /* ... */ }, // 自定义菜单栏
73
+ menuRight: '<div>&copy SheetNext</div>' // 菜单栏右侧自定义内容
74
+ });
75
+ ```
76
+
77
+ ## 配置参数说明
24
78
 
25
- ### 构造函数
79
+ | 参数 | 类型 | 说明 |
80
+ |------|------|------|
81
+ | `AI_URL` | `string` | AI 服务中转地址,用于配置 AI 功能的后端接口 |
82
+ | `AI_TOKEN` | `string` | AI 服务访问令牌,用于鉴权认证 |
83
+ | `licenseKey` | `string` | 商业版授权密钥,社区版可不填 |
84
+ | `menuList` | `function` | 自定义顶部菜单栏,接收默认菜单并返回修改后的菜单 |
85
+ | `menuRight` | `string` | 菜单栏右侧区域的自定义 HTML 内容 |
86
+
87
+ ## menuList 自定义菜单示例
26
88
 
27
89
  ```javascript
28
- const SN = new SheetNext(dom: HTMLElement, options?: object)
29
- // 下面所有示例使用此实例进行操作
90
+ const SN = new SheetNext(document.querySelector('#container'), {
91
+ menuList: (defaultList) => {
92
+ // 在"文件"菜单末尾添加自定义项
93
+ defaultList[0].items.push({
94
+ label: '我的自定义功能',
95
+ handler: () => alert('这是自定义菜单项!')
96
+ });
97
+
98
+ // 添加新的顶级菜单
99
+ defaultList.push({
100
+ label: '帮助',
101
+ items: [
102
+ { label: '使用文档', handler: () => window.open('https://www.sheetnext.com/docs') },
103
+ { label: '关于', handler: () => alert('SheetNext v1.0') }
104
+ ]
105
+ });
106
+
107
+ return defaultList;
108
+ }
109
+ });
110
+ ```
111
+
112
+ **MenuList 结构定义:**
113
+
114
+ ```typescript
115
+ interface MenuItem {
116
+ label: string; // 菜单项标签
117
+ handler?: () => void; // 点击处理函数
118
+ disabled?: boolean; // 是否禁用
119
+ tip?: string; // 提示信息(右侧显示)
120
+ divider?: boolean; // 是否为分隔线
121
+ }
122
+
123
+ interface Menu {
124
+ label: string; // 菜单标签
125
+ items: MenuItem[]; // 菜单项列表
126
+ }
127
+
128
+ type MenuList = Menu[];
30
129
  ```
31
130
 
32
- **参数:**
33
- - `dom`: 容器 DOM 元素(必需)
34
- - `options`: 可选配置对象(详见 [README 初始化配置](https://github.com/wyyazlz/sheetnext/blob/master/README.md#初始化配置))
131
+ **注意事项:**
132
+ - `menuList` `menuRight` 只能在初始化时配置,后续无法修改
133
+ - 如果不传入 `menuList`,将使用默认菜单(包含:文件、插入、公式、数据、视图、更多)
134
+ - AI 功能需要配置 `AI_URL` 才能使用,详见 AI 中转配置
135
+
136
+ # 工作簿级别
137
+
138
+ SheetNext 是主入口类,管理整个电子表格应用。
35
139
 
36
- ### 核心属性
140
+ ## 核心属性
37
141
 
38
142
  | 属性 | 类型 | 说明 |
39
143
  |------|------|------|
144
+ | `workbookName` | `string` | 工作簿名称(可读写,长度1-255字符) |
40
145
  | `activeSheet` | `Sheet` | 当前激活的工作表(可读写) |
41
146
  | `sheets` | `Sheet[]` | 所有工作表数组 |
42
147
  | `sheetNames` | `string[]` | 工作表名称列表(只读) |
43
148
  | `containerDom` | `HTMLElement` | 编辑器容器元素 |
44
149
  | `namespace` | `string` | 实例的全局命名空间(如 `SN_0`) |
45
- | `locked` | `boolean` | 是否锁定工作表切换 |
150
+ | `locked` | `boolean` | 是否锁定工作表切换/操作 |
46
151
 
47
- ### 工作表管理方法
152
+ ## 核心方法
153
+
154
+ ### 新建工作表
155
+ `addSheet(name?: string): Sheet`
48
156
 
49
- #### `addSheet(name?: string): Sheet`
50
157
  添加新工作表,名称可选(自动生成 Sheet1、Sheet2 等)。
51
158
 
52
159
  ```javascript
@@ -56,66 +163,129 @@ const autoSheet = SN.addSheet(); // 自动命名
56
163
 
57
164
  **规则:** 名称不重复、长度1-31字符、不含特殊符号 `: / \ * ? [ ]`
58
165
 
59
- #### `delSheet(name: string): void`
166
+ ### 删除工作表
167
+ `delSheet(name: string): void`
168
+
60
169
  删除指定工作表(至少保留一个可见工作表)。
61
170
 
62
171
  ```javascript
63
172
  SN.delSheet("Sheet2");
64
173
  ```
65
174
 
66
- #### `getSheetByName(name: string): Sheet | null`
175
+ ### 根据名称获取工作表
176
+ `getSheetByName(name: string): Sheet | null`
177
+
67
178
  根据名称获取工作表。
68
179
 
69
180
  ```javascript
70
181
  const sheet = SN.getSheetByName("Sheet1");
71
182
  ```
72
183
 
73
- #### `getVisibleSheetByIndex(index: number): Sheet`
184
+ ### 根据索引获取可见工作表
185
+ `getVisibleSheetByIndex(index: number): Sheet`
186
+
74
187
  获取可见工作表(按索引,隐藏工作表不计入)。
75
188
 
76
189
  ```javascript
77
190
  const firstSheet = SN.getVisibleSheetByIndex(0);
78
191
  ```
79
192
 
80
- ### 文件操作
193
+ ### 手动重新渲染
194
+ `r(): void`
81
195
 
82
- #### `import(file: File): Promise<void>`
83
- 导入 Excel 文件(.xlsx 格式)。
196
+ 手动触发画布重新渲染(批量修改后使用)。
84
197
 
85
198
  ```javascript
86
- fileInput.addEventListener('change', (e) => {
87
- SN.import(e.target.files[0]);
88
- });
199
+ // 批量修改后刷新
200
+ for (let i = 0; i < 100; i++) {
201
+ sheet.getCell(i, 0).editVal = i;
202
+ }
203
+ SN.r();
89
204
  ```
90
205
 
91
- #### `export(type: string): void`
92
- 导出电子表格(目前支持 `"XLSX"`)。
206
+ ### 获取工作簿数据
207
+ `getData(): object`
208
+
209
+ 获取完整的工作簿数据(JSON格式),包含所有工作表、单元格数据、样式、公式、图表等。
93
210
 
94
211
  ```javascript
95
- SN.export('XLSX');
212
+ // 获取工作簿数据
213
+ const data = SN.getData();
96
214
  ```
97
215
 
98
- ### 其他方法
216
+ **使用场景:**
217
+ - 数据备份和恢复
218
+ - 数据持久化到数据库或本地存储
219
+ - 数据分析和处理
220
+ - 跨系统数据传输
99
221
 
100
- #### `r(): void`
101
- 手动触发画布重新渲染(批量修改后使用)。
222
+ **示例:保存到 localStorage**
102
223
 
103
224
  ```javascript
104
- // 批量修改后刷新
105
- for (let i = 0; i < 100; i++) {
106
- sheet.getCell(i, 0).editVal = i;
107
- }
108
- SN.r();
225
+ // 保存数据
226
+ const data = SN.getData();
227
+ localStorage.setItem('sheetData', JSON.stringify(data));
228
+
229
+ // 读取数据
230
+ const savedData = JSON.parse(localStorage.getItem('sheetData'));
231
+ SN.setData(savedData);
232
+ ```
233
+
234
+ ### 加载工作簿数据
235
+ `setData(data: object): boolean`
236
+
237
+ 加载完整的工作簿数据,替换当前所有工作表内容。
238
+
239
+ ```javascript
240
+ SN.setData(data):boolean;
109
241
  ```
110
242
 
111
- #### `getLicenseInfo()`
112
- 获取 License 信息。
243
+ ### 导入文件
244
+ `import(file: File): Promise<void>`
245
+
246
+ 导入文件,支持 `.xlsx`、`.csv` 和 `.json` 格式。
113
247
 
114
248
  ```javascript
115
- const info = SN.getLicenseInfo();
249
+ // 通过文件选择器导入
250
+ const fileInput = document.createElement('input');
251
+ fileInput.type = 'file';
252
+ fileInput.accept = '.xlsx,.csv,.json';
253
+ fileInput.onchange = (e) => {
254
+ const file = e.target.files[0];
255
+ SN.import(file);
256
+ };
257
+ fileInput.click();
116
258
  ```
117
259
 
118
- ### 多实例支持
260
+ **支持格式:**
261
+ - `.xlsx` - Excel 工作簿(完整支持)
262
+ - `.csv` - 逗号分隔值文件(导入为单个工作表)
263
+ - `.json` - SheetNext JSON 格式(包含样式、公式、图表等)
264
+
265
+ ### 从URL导入文件
266
+ `importFromUrl(url: String): Promise<void>`
267
+
268
+ 通过在线地址导入 Excel 文件(.xlsx 格式)。
269
+
270
+ ```javascript
271
+ await SN.importFromUrl('https://example.com/data.xlsx');
272
+ ```
273
+
274
+ ### 导出文件
275
+ `export(type: string): void`
276
+
277
+ 导出电子表格,支持 `"XLSX"`、`"CSV"` 和 `"JSON"` 格式。
278
+
279
+ ```javascript
280
+ SN.export('XLSX'); // 导出为 Excel 文件
281
+ ```
282
+
283
+ **格式说明:**
284
+ - `XLSX` - Excel 工作簿格式,支持多工作表、样式、公式、图表等
285
+ - `CSV` - 纯文本格式,仅导出当前激活工作表的数据(不含样式)
286
+ - `JSON` - SheetNext 专用格式,完整保存工作簿结构、样式、公式、图表等,适合数据备份和快速加载
287
+
288
+ ## 多实例支持
119
289
 
120
290
  SheetNext 支持同一页面创建多个独立实例:
121
291
 
@@ -127,13 +297,11 @@ console.log(editor1.namespace); // "SN_0"
127
297
  console.log(editor2.namespace); // "SN_1"
128
298
  ```
129
299
 
130
- ---
131
-
132
- ## Sheet
300
+ # 工作表级别
133
301
 
134
302
  Sheet 类代表一个工作表。
135
303
 
136
- ### 属性
304
+ ## 属性
137
305
 
138
306
  | 属性 | 类型 | 说明 |
139
307
  |------|------|------|
@@ -148,41 +316,53 @@ Sheet 类代表一个工作表。
148
316
  | `activeAreas` | `RangeNum[]` | 当前选中区域 |
149
317
  | `rows` | `Row[]` | 所有行 |
150
318
  | `cols` | `Col[]` | 所有列 |
319
+ | `rowCount` | `number` | 行数(只读) |
320
+ | `colCount` | `number` | 列数(只读) |
151
321
  | `xSplit` | `number` | 冻结列数 |
152
322
  | `ySplit` | `number` | 冻结行数 |
153
323
  | `drawings` | `Drawing[]` | 图形对象列表(图表、图片等) |
154
324
 
155
- ### 基础方法
325
+ ## 基础方法
326
+
327
+ ### 获取指定行
328
+ `getRow(r: number): Row`
156
329
 
157
- #### `getRow(r: number): Row`
158
330
  获取指定行对象。
159
331
 
160
332
  ```javascript
161
333
  const row = sheet.getRow(0); // 获取第一行
162
334
  ```
163
335
 
164
- #### `getCol(c: number): Col`
336
+ ### 获取指定列
337
+ `getCol(c: number): Col`
338
+
165
339
  获取指定列对象。
166
340
 
167
341
  ```javascript
168
342
  const col = sheet.getCol(0); // 获取第一列(A列)
169
343
  ```
170
344
 
171
- #### `getCell(r: number, c: number): Cell`
345
+ ### 获取指定单元格
346
+ `getCell(r: number, c: number): Cell`
347
+
172
348
  获取指定单元格。
173
349
 
174
350
  ```javascript
175
351
  const cell = sheet.getCell(0, 0); // 获取 A1 单元格
176
352
  ```
177
353
 
178
- #### `getCellByStr(cellStr: string): Cell`
354
+ ### 通过字符串获取单元格
355
+ `getCellByStr(cellStr: string): Cell`
356
+
179
357
  通过字符串引用获取单元格。
180
358
 
181
359
  ```javascript
182
360
  const cell = sheet.getCellByStr("A1");
183
361
  ```
184
362
 
185
- #### `rangeStrToNum(rangeStr: string): RangeNum`
363
+ ### 范围字符串转数字
364
+ `rangeStrToNum(rangeStr: string): RangeNum`
365
+
186
366
  将字符串范围转换为数字范围对象。
187
367
 
188
368
  ```javascript
@@ -190,9 +370,11 @@ const rangeNum = sheet.rangeStrToNum("A1:C3");
190
370
  // 返回: {s:{r:0,c:0}, e:{r:2,c:2}}
191
371
  ```
192
372
 
193
- ### 区域遍历
373
+ ## 区域遍历
374
+
375
+ ### 遍历区域
376
+ `eachArea(rangeRef: RangeRef, callback: (r, c, index) => void, reverse?: boolean): void`
194
377
 
195
- #### `eachArea(rangeRef: RangeRef, callback: (r, c, index) => void, reverse?: boolean): void`
196
378
  遍历指定区域的每个单元格。
197
379
 
198
380
  ```javascript
@@ -210,23 +392,29 @@ sheet.eachArea("A:A", (r, c) => {
210
392
  }, true);
211
393
  ```
212
394
 
213
- ### 行列操作
395
+ ## 行列操作
396
+
397
+ ### 显示所有隐藏行
398
+ `showAllHidRows(): void`
214
399
 
215
- #### `showAllHidRows(): void`
216
400
  显示所有隐藏的行。
217
401
 
218
402
  ```javascript
219
403
  sheet.showAllHidRows();
220
404
  ```
221
405
 
222
- #### `showAllHidCols(): void`
406
+ ### 显示所有隐藏列
407
+ `showAllHidCols(): void`
408
+
223
409
  显示所有隐藏的列。
224
410
 
225
411
  ```javascript
226
412
  sheet.showAllHidCols();
227
413
  ```
228
414
 
229
- #### `addRows(startR: number, num: number): void`
415
+ ### 插入行
416
+ `addRows(startR: number, num: number): void`
417
+
230
418
  在指定位置插入行。
231
419
 
232
420
  ```javascript
@@ -235,30 +423,38 @@ sheet.addRows(5, 3); // 在第 5 行位置插入 3 行
235
423
 
236
424
  **注意**:多个同时调用时,应反向遍历以避免索引混乱。
237
425
 
238
- #### `addCols(startC: number, num: number): void`
426
+ ### 插入列
427
+ `addCols(startC: number, num: number): void`
428
+
239
429
  在指定位置插入列。
240
430
 
241
431
  ```javascript
242
432
  sheet.addCols(2, 2); // 在第 2 列位置插入 2 列
243
433
  ```
244
434
 
245
- #### `delRows(startR: number, num: number): void`
435
+ ### 删除行
436
+ `delRows(startR: number, num: number): void`
437
+
246
438
  删除指定行。
247
439
 
248
440
  ```javascript
249
441
  sheet.delRows(5, 3); // 删除从第 5 行开始的 3 行
250
442
  ```
251
443
 
252
- #### `delCols(startC: number, num: number): void`
444
+ ### 删除列
445
+ `delCols(startC: number, num: number): void`
446
+
253
447
  删除指定列。
254
448
 
255
449
  ```javascript
256
450
  sheet.delCols(2, 2); // 删除从第 2 列开始的 2 列
257
451
  ```
258
452
 
453
+ ## 合并单元格
454
+
259
455
  ### 合并单元格
456
+ `mergeCells(rangeRef: RangeRef): void`
260
457
 
261
- #### `mergeCells(rangeRef: RangeRef): void`
262
458
  合并指定区域的单元格。
263
459
 
264
460
  ```javascript
@@ -267,16 +463,20 @@ sheet.mergeCells("A1:C3");
267
463
  sheet.mergeCells({s:{r:0,c:0}, e:{r:2,c:2}});
268
464
  ```
269
465
 
270
- #### `unMergeCells(cellRef: CellRef): void`
466
+ ### 取消合并单元格
467
+ `unMergeCells(cellRef: CellRef): void`
468
+
271
469
  取消合并(传入区域内任意单元格引用)。
272
470
 
273
471
  ```javascript
274
472
  sheet.unMergeCells("A1"); // 取消包含 A1 的合并区域
275
473
  ```
276
474
 
277
- ### 排序
475
+ ## 排序
476
+
477
+ ### 区域排序
478
+ `rangeSort(sortItems: SortItem[], range?: RangeRef): void`
278
479
 
279
- #### `rangeSort(sortItems: SortItem[], range?: RangeRef): void`
280
480
  对指定区域进行排序。
281
481
 
282
482
  **SortItem 接口:**
@@ -307,9 +507,11 @@ sheet.rangeSort(
307
507
  );
308
508
  ```
309
509
 
510
+ ## 批量插入数据
511
+
310
512
  ### 批量插入数据
513
+ `insertTable(data: (ICellConfig | string | number)[][], startCell: CellRef, globalConfig?: object): RangeNum`
311
514
 
312
- #### `insertTable(data: (ICellConfig | string | number)[][], startCell: CellRef, globalConfig?: object): RangeNum`
313
515
  在指定位置插入表格数据。
314
516
 
315
517
  **ICellConfig 接口:**
@@ -366,9 +568,11 @@ SN.activeSheet.insertTable(t, "A1", {
366
568
  - 对于合并单元格(`mr`/`mb`),需要添加相同数量的空字符串占位符,保持二维数组的矩形结构。
367
569
  - 例如:`{ mr: 2 }, "", ""`
368
570
 
369
- ### 图形对象
571
+ ## 图形对象
572
+
573
+ ### 添加图形对象
574
+ `addDrawing(config: object): Drawing`
370
575
 
371
- #### `addDrawing(config: object): Drawing`
372
576
  添加图形对象(图表、图片等)。
373
577
 
374
578
  **示例:添加图表**
@@ -398,14 +602,18 @@ SN.activeSheet.addDrawing({
398
602
  });
399
603
  ```
400
604
 
401
- #### `getDrawingsByCell(cellRef: CellRef): Drawing[]`
605
+ ### 获取单元格的图形对象
606
+ `getDrawingsByCell(cellRef: CellRef): Drawing[]`
607
+
402
608
  获取指定单元格位置的所有图形对象。
403
609
 
404
610
  ```javascript
405
611
  const drawings = sheet.getDrawingsByCell("B2");
406
612
  ```
407
613
 
408
- #### `removeDrawing(id: string): void`
614
+ ### 删除图形对象
615
+ `removeDrawing(id: string): void`
616
+
409
617
  删除指定 ID 的图形对象。
410
618
 
411
619
  ```javascript
@@ -414,73 +622,490 @@ sheet.removeDrawing("drawing-id");
414
622
 
415
623
  ---
416
624
 
417
- ## Cell
625
+ # 单元格级别
418
626
 
419
- Cell 类代表单个单元格。
627
+ Cell 类代表单个单元格,提供完整的数据、样式、验证等功能。
420
628
 
421
- ### 属性
629
+ ## 核心属性
422
630
 
423
631
  | 属性 | 类型 | 说明 |
424
632
  |------|------|------|
425
- | `isMerged` | `boolean` | 是否为合并单元格 |
426
- | `master` | `CellNum \| null` | 如果是合并单元格,指向主单元格 |
427
- | `hyperlink` | `object` | 超链接:`{target, location, tooltip}` |
428
- | `dataValidation` | `object` | 数据验证规则 |
429
- | `editVal` | `string` | 编辑值或公式(可写) |
633
+ | `editVal` | `string` | 编辑值或公式(可读写) |
430
634
  | `calcVal` | `any` | 计算值(只读) |
431
635
  | `showVal` | `string` | 显示值(只读) |
432
- | `numFmt` | `string` | 数字格式 |
636
+ | `type` | `string` | 单元格类型(只读):`string/number/date/time/dateTime/boolean/error` |
637
+ | `isFormula` | `boolean` | 是否为公式(只读) |
638
+ | `isMerged` | `boolean` | 是否为合并单元格 |
639
+ | `master` | `CellNum \| null` | 如果是合并单元格,指向主单元格 |
640
+
641
+ ## 样式属性
642
+
643
+ | 属性 | 类型 | 说明 |
644
+ |------|------|------|
433
645
  | `font` | `object` | 字体样式 |
434
646
  | `alignment` | `object` | 对齐方式 |
435
647
  | `border` | `object` | 边框样式 |
436
648
  | `fill` | `object` | 填充样式 |
649
+ | `numFmt` | `string` | 数字格式 |
650
+
651
+ ## 功能属性
652
+
653
+ | 属性 | 类型 | 说明 |
654
+ |------|------|------|
655
+ | `hyperlink` | `object` | 超链接配置 |
656
+ | `dataValidation` | `object` | 数据验证规则 |
657
+ | `validData` | `boolean` | 数据验证结果(只读) |
658
+
659
+ ## font 对象结构
437
660
 
438
- ### dataValidation 对象结构
661
+ 字体样式配置对象。
439
662
 
440
663
  ```typescript
441
664
  {
442
- type: string; // 验证类型
443
- operator: string; // 操作符
444
- allowBlank: boolean; // 是否允许空白
445
- formula1: any; // 公式1(type=list 时为数组)
446
- formula2: any; // 公式2
447
- showInputMessage: boolean; // 是否显示输入提示
448
- promptTitle: string; // 提示标题
449
- prompt: string; // 提示内容
450
- showErrorMessage: boolean; // 是否显示错误提示
451
- errorTitle: string; // 错误标题
452
- error: string; // 错误内容
453
- errorStyle: string; // 错误样式
454
- showDropDown: boolean; // 是否显示下拉框
665
+ name?: string; // 字体名称,如 'Arial', '微软雅黑'
666
+ size?: number; // 字体大小,默认 11
667
+ bold?: boolean; // 是否粗体
668
+ italic?: boolean; // 是否斜体
669
+ underline?: string; // 下划线:'single' | 'double' | 'none'
670
+ strike?: boolean; // 是否删除线
671
+ color?: string; // 字体颜色,格式:'#RRGGBB'
672
+ vertAlign?: string; // 上下标:'superscript' | 'subscript'
673
+ outline?: boolean; // 是否轮廓(Mac专用)
674
+ charset?: number; // 字符集
455
675
  }
456
676
  ```
457
677
 
458
- ### 示例
678
+ **示例:**
459
679
 
460
680
  ```javascript
461
681
  const cell = sheet.getCell(0, 0);
462
682
 
463
- // 设置单元格值
464
- cell.editVal = "Hello World";
683
+ // 基础字体设置
684
+ cell.font = {
685
+ name: '微软雅黑',
686
+ size: 14,
687
+ bold: true
688
+ };
689
+
690
+ // 带颜色和下划线
691
+ cell.font = {
692
+ size: 12,
693
+ color: '#FF0000',
694
+ underline: 'single'
695
+ };
696
+
697
+ // 删除线
698
+ cell.font = { strike: true };
699
+ ```
465
700
 
466
- // 设置公式
467
- cell.editVal = "=SUM(A1:A10)";
701
+ ## alignment 对象结构
468
702
 
469
- // 读取显示值
470
- console.log(cell.showVal);
703
+ 对齐方式配置对象。
471
704
 
472
- // 设置样式
473
- cell.fill = { fgColor: '#FFFF00' };
474
- cell.font = { bold: true, size: 14 };
705
+ ```typescript
706
+ {
707
+ horizontal?: string; // 水平对齐:'left' | 'center' | 'right' | 'fill' | 'justify' | 'distributed'
708
+ vertical?: string; // 垂直对齐:'top' | 'middle' | 'bottom' | 'justify' | 'distributed'
709
+ wrapText?: boolean; // 是否自动换行
710
+ indent?: number; // 缩进级别(仅 horizontal='left'/'right' 时有效)
711
+ }
475
712
  ```
476
713
 
477
- ---
714
+ **示例:**
715
+
716
+ ```javascript
717
+ // 水平居中,垂直居中
718
+ cell.alignment = {
719
+ horizontal: 'center',
720
+ vertical: 'middle'
721
+ };
722
+
723
+ // 自动换行
724
+ cell.alignment = { wrapText: true };
725
+
726
+ // 左对齐并缩进2级
727
+ cell.alignment = {
728
+ horizontal: 'left',
729
+ indent: 2
730
+ };
731
+
732
+ // 两端对齐
733
+ cell.alignment = {
734
+ horizontal: 'justify',
735
+ vertical: 'top'
736
+ };
737
+ ```
738
+
739
+ ## border 对象结构
478
740
 
479
- ## Row
741
+ 边框样式配置对象,可单独设置四个方向的边框。
742
+
743
+ ```typescript
744
+ {
745
+ top?: {
746
+ style: string; // 边框样式
747
+ color?: string; // 边框颜色,格式:'#RRGGBB',默认 '#000000'
748
+ };
749
+ right?: { style: string; color?: string; };
750
+ bottom?: { style: string; color?: string; };
751
+ left?: { style: string; color?: string; };
752
+ diagonal?: { style: string; color?: string; };
753
+ }
754
+ ```
755
+
756
+ **边框样式 (style):**
757
+ - `thin` - 细线(最常用)
758
+ - `medium` - 中等
759
+ - `thick` - 粗线
760
+ - `dotted` - 点线
761
+ - `dashed` - 虚线
762
+ - `double` - 双线
763
+ - `hair` - 极细线
764
+ - `dashDot` - 点划线
765
+ - `dashDotDot` - 双点划线
766
+ - `mediumDashed` - 中等虚线
767
+ - `mediumDashDot` - 中等点划线
768
+ - `mediumDashDotDot` - 中等双点划线
769
+ - `slantDashDot` - 斜点划线
770
+
771
+ **示例:**
772
+
773
+ ```javascript
774
+ // 设置所有边框
775
+ cell.border = {
776
+ top: { style: 'thin' },
777
+ right: { style: 'thin' },
778
+ bottom: { style: 'thin' },
779
+ left: { style: 'thin' }
780
+ };
781
+
782
+ // 设置单边框
783
+ cell.border = {
784
+ bottom: { style: 'medium', color: '#FF0000' }
785
+ };
786
+
787
+ // 添加对角线
788
+ cell.border = {
789
+ diagonal: { style: 'thin', color: '#0000FF' }
790
+ };
791
+
792
+ // 清空边框
793
+ cell.border = {};
794
+ ```
795
+
796
+ ## fill 对象结构
797
+
798
+ 填充样式配置对象,支持纯色填充和渐变填充。
799
+
800
+ ### 纯色填充
801
+
802
+ ```typescript
803
+ {
804
+ type: 'pattern'; // 填充类型
805
+ pattern: string; // 图案类型
806
+ fgColor?: string; // 前景色,格式:'#RRGGBB'
807
+ bgColor?: string; // 背景色,格式:'#RRGGBB'
808
+ }
809
+ ```
810
+
811
+ **图案类型 (pattern):**
812
+ - `solid` - 纯色(最常用)
813
+ - `darkGray` - 深灰
814
+ - `mediumGray` - 中灰
815
+ - `lightGray` - 浅灰
816
+ - `gray125` - 12.5% 灰
817
+ - `gray0625` - 6.25% 灰
818
+ - `darkHorizontal` - 深色横线
819
+ - `darkVertical` - 深色竖线
820
+ - `darkDown` - 深色斜线(左上到右下)
821
+ - `darkUp` - 深色斜线(左下到右上)
822
+ - `darkGrid` - 深色网格
823
+ - `darkTrellis` - 深色斜网格
824
+ - `lightHorizontal` - 浅色横线
825
+ - `lightVertical` - 浅色竖线
826
+ - `lightDown` - 浅色斜线(左上到右下)
827
+ - `lightUp` - 浅色斜线(左下到右上)
828
+ - `lightGrid` - 浅色网格
829
+ - `lightTrellis` - 浅色斜网格
830
+
831
+ ### 渐变填充
832
+
833
+ ```typescript
834
+ {
835
+ type: 'gradient'; // 填充类型
836
+ gradientType?: string; // 渐变类型:'linear' | 'path'
837
+ degree?: number; // 线性渐变角度(0-360)
838
+ left?: number; // 左侧偏移(0-1)
839
+ right?: number; // 右侧偏移(0-1)
840
+ top?: number; // 顶部偏移(0-1)
841
+ bottom?: number; // 底部偏移(0-1)
842
+ stops: Array<{ // 渐变色停止点
843
+ position: number; // 位置(0-1)
844
+ color: string; // 颜色,格式:'#RRGGBB'
845
+ }>;
846
+ }
847
+ ```
848
+
849
+ **示例:**
850
+
851
+ ```javascript
852
+ // 纯色背景(最常用)
853
+ cell.fill = {
854
+ type: 'pattern',
855
+ pattern: 'solid',
856
+ fgColor: '#FFFF00'
857
+ };
858
+
859
+ // 图案填充
860
+ cell.fill = {
861
+ type: 'pattern',
862
+ pattern: 'lightGrid',
863
+ fgColor: '#FF0000',
864
+ bgColor: '#FFFFFF'
865
+ };
866
+
867
+ // 线性渐变(从红到蓝)
868
+ cell.fill = {
869
+ type: 'gradient',
870
+ gradientType: 'linear',
871
+ degree: 90,
872
+ stops: [
873
+ { position: 0, color: '#FF0000' },
874
+ { position: 1, color: '#0000FF' }
875
+ ]
876
+ };
877
+
878
+ // 路径渐变
879
+ cell.fill = {
880
+ type: 'gradient',
881
+ gradientType: 'path',
882
+ left: 0.5,
883
+ right: 0.5,
884
+ top: 0.5,
885
+ bottom: 0.5,
886
+ stops: [
887
+ { position: 0, color: '#FFFFFF' },
888
+ { position: 1, color: '#000000' }
889
+ ]
890
+ };
891
+
892
+ // 清空填充
893
+ cell.fill = {};
894
+ ```
895
+
896
+ ## numFmt 数字格式
897
+
898
+ 数字格式字符串,用于控制数值、日期、时间的显示格式。
899
+
900
+ ### 常用格式
901
+
902
+ | 格式字符串 | 说明 | 示例 |
903
+ |-----------|------|------|
904
+ | `0` | 整数 | 1234 |
905
+ | `0.00` | 保留2位小数 | 1234.50 |
906
+ | `#,##0` | 千分位分隔符 | 1,234 |
907
+ | `#,##0.00` | 千分位+2位小数 | 1,234.50 |
908
+ | `0%` | 百分比 | 50% |
909
+ | `0.00%` | 百分比+2位小数 | 50.25% |
910
+ | `0.00E+00` | 科学计数法 | 1.23E+03 |
911
+ | `# ?/?` | 分数 | 1 1/4 |
912
+ | `¥#,##0.00` | 货币格式 | ¥1,234.50 |
913
+ | `yyyy/m/d` | 日期(年/月/日) | 2024/1/15 |
914
+ | `m/d/yyyy` | 日期(月/日/年) | 1/15/2024 |
915
+ | `yyyy-mm-dd` | 日期(年-月-日) | 2024-01-15 |
916
+ | `h:mm` | 时间(时:分) | 14:30 |
917
+ | `h:mm:ss` | 时间(时:分:秒) | 14:30:25 |
918
+ | `yyyy/m/d h:mm:ss` | 日期时间 | 2024/1/15 14:30:25 |
919
+ | `[Red]0.00` | 负数显示红色 | -12.34 |
920
+ | `0.00;[Red]-0.00` | 正数黑色,负数红色 | 12.34 或 -12.34 |
921
+
922
+ ### 格式代码说明
923
+
924
+ **数字部分:**
925
+ - `0` - 占位符,不足补0
926
+ - `#` - 占位符,不足不显示
927
+ - `,` - 千分位分隔符
928
+ - `.` - 小数点
929
+ - `%` - 百分比
930
+ - `?` - 空格占位(用于对齐)
931
+
932
+ **日期部分:**
933
+ - `yyyy` - 四位年份(2024)
934
+ - `yy` - 两位年份(24)
935
+ - `m` - 月份(1-12)
936
+ - `mm` - 月份补0(01-12)
937
+ - `mmm` - 月份简写(Jan-Dec)
938
+ - `mmmm` - 月份全称(January-December)
939
+ - `d` - 日期(1-31)
940
+ - `dd` - 日期补0(01-31)
941
+ - `ddd` - 星期简写(Sun-Sat)
942
+ - `dddd` - 星期全称(Sunday-Saturday)
943
+
944
+ **时间部分:**
945
+ - `h` - 小时(0-23)
946
+ - `hh` - 小时补0(00-23)
947
+ - `m` - 分钟(0-59)
948
+ - `mm` - 分钟补0(00-59)
949
+ - `s` - 秒(0-59)
950
+ - `ss` - 秒补0(00-59)
951
+ - `AM/PM` - 12小时制
952
+
953
+ **示例:**
954
+
955
+ ```javascript
956
+ const cell = sheet.getCell(0, 0);
957
+
958
+ // 数字格式
959
+ cell.editVal = 1234.5;
960
+ cell.numFmt = '#,##0.00'; // 显示:1,234.50
961
+
962
+ // 百分比
963
+ cell.editVal = 0.258;
964
+ cell.numFmt = '0.00%'; // 显示:25.80%
965
+
966
+ // 货币
967
+ cell.editVal = 1234.5;
968
+ cell.numFmt = '¥#,##0.00'; // 显示:¥1,234.50
969
+
970
+ // 日期
971
+ cell.editVal = '2024/1/15';
972
+ cell.numFmt = 'yyyy-mm-dd'; // 显示:2024-01-15
973
+
974
+ // 时间
975
+ cell.editVal = '14:30:25';
976
+ cell.numFmt = 'h:mm:ss'; // 显示:14:30:25
977
+
978
+ // 自定义格式(正数、负数、零、文本)
979
+ cell.numFmt = '0.00;[Red]-0.00;"零";@';
980
+
981
+ // 清除格式(恢复常规)
982
+ cell.numFmt = null;
983
+ ```
984
+
985
+ ## hyperlink 对象结构
986
+
987
+ 超链接配置对象。
988
+
989
+ ```typescript
990
+ {
991
+ target?: string; // 外部链接 URL(如 'https://example.com')
992
+ location?: string; // 内部链接位置(如 'Sheet2!A1')
993
+ tooltip?: string; // 鼠标悬停提示文本
994
+ }
995
+ ```
996
+
997
+ **示例:**
998
+
999
+ ```javascript
1000
+ // 外部链接
1001
+ cell.editVal = '访问官网';
1002
+ cell.hyperlink = {
1003
+ target: 'https://www.sheetnext.com',
1004
+ tooltip: '点击访问 SheetNext 官网'
1005
+ };
1006
+
1007
+ // 内部链接(跳转到其他工作表)
1008
+ cell.editVal = '查看数据';
1009
+ cell.hyperlink = {
1010
+ location: 'Sheet2!A1',
1011
+ tooltip: '跳转到 Sheet2 的 A1 单元格'
1012
+ };
1013
+
1014
+ // 移除超链接
1015
+ cell.hyperlink = {};
1016
+ ```
1017
+
1018
+ ## dataValidation 对象结构
1019
+
1020
+ 数据验证规则配置对象,用于限制单元格可输入的内容。
1021
+
1022
+ ```typescript
1023
+ {
1024
+ type: string; // 验证类型:'list' | 'whole' | 'decimal' | 'date' | 'time' | 'textLength' | 'custom'
1025
+ operator?: string; // 操作符:'between' | 'notBetween' | 'equal' | 'notEqual' | 'greaterThan' | 'lessThan' | 'greaterThanOrEqual' | 'lessThanOrEqual'
1026
+ allowBlank?: boolean; // 是否允许空白,默认 false
1027
+ formula1: any; // 公式1(type='list' 时为数组)
1028
+ formula2?: any; // 公式2(范围验证时使用)
1029
+ showInputMessage?: boolean; // 是否显示输入提示,默认 true
1030
+ promptTitle?: string; // 输入提示标题
1031
+ prompt?: string; // 输入提示内容
1032
+ showErrorMessage?: boolean; // 是否显示错误提示,默认 true
1033
+ errorTitle?: string; // 错误提示标题
1034
+ error?: string; // 错误提示内容
1035
+ errorStyle?: string; // 错误样式:'stop' | 'warning' | 'information'
1036
+ showDropDown?: boolean; // 是否显示下拉框(type='list' 时),默认 true
1037
+ }
1038
+ ```
1039
+
1040
+ **示例:**
1041
+
1042
+ ```javascript
1043
+ // 下拉列表
1044
+ cell.dataValidation = {
1045
+ type: 'list',
1046
+ formula1: ['优秀', '良好', '及格', '不及格'],
1047
+ showDropDown: true,
1048
+ promptTitle: '请选择',
1049
+ prompt: '请从列表中选择一个等级'
1050
+ };
1051
+
1052
+ // 整数范围(1-100)
1053
+ cell.dataValidation = {
1054
+ type: 'whole',
1055
+ operator: 'between',
1056
+ formula1: 1,
1057
+ formula2: 100,
1058
+ errorTitle: '输入错误',
1059
+ error: '请输入 1 到 100 之间的整数'
1060
+ };
1061
+
1062
+ // 小数验证(大于0)
1063
+ cell.dataValidation = {
1064
+ type: 'decimal',
1065
+ operator: 'greaterThan',
1066
+ formula1: 0,
1067
+ errorTitle: '输入错误',
1068
+ error: '请输入大于 0 的数字'
1069
+ };
1070
+
1071
+ // 日期范围
1072
+ cell.dataValidation = {
1073
+ type: 'date',
1074
+ operator: 'between',
1075
+ formula1: '2024/1/1',
1076
+ formula2: '2024/12/31',
1077
+ errorTitle: '日期错误',
1078
+ error: '请输入 2024 年的日期'
1079
+ };
1080
+
1081
+ // 时间验证
1082
+ cell.dataValidation = {
1083
+ type: 'time',
1084
+ operator: 'between',
1085
+ formula1: '9:00:00',
1086
+ formula2: '18:00:00',
1087
+ errorTitle: '时间错误',
1088
+ error: '请输入工作时间(9:00-18:00)'
1089
+ };
1090
+
1091
+ // 文本长度限制
1092
+ cell.dataValidation = {
1093
+ type: 'textLength',
1094
+ operator: 'lessThanOrEqual',
1095
+ formula1: 20,
1096
+ errorTitle: '文本过长',
1097
+ error: '最多输入 20 个字符'
1098
+ };
1099
+
1100
+ // 移除验证
1101
+ cell.dataValidation = {};
1102
+ ```
1103
+
1104
+ # 行级别
480
1105
 
481
1106
  Row 类代表行,提供行级别的属性和操作。
482
1107
 
483
- ### 属性
1108
+ ## 属性
484
1109
 
485
1110
  | 属性 | 类型 | 说明 |
486
1111
  |------|------|------|
@@ -494,9 +1119,11 @@ Row 类代表行,提供行级别的属性和操作。
494
1119
  | `border` | `object` | 边框样式 |
495
1120
  | `fill` | `object` | 填充样式 |
496
1121
 
497
- ### 方法
1122
+ ## 方法
1123
+
1124
+ ### 获取该行的单元格
1125
+ `getCell(c: number): Cell`
498
1126
 
499
- #### `getCell(c: number): Cell`
500
1127
  获取该行的指定列单元格。
501
1128
 
502
1129
  ```javascript
@@ -504,7 +1131,7 @@ const row = sheet.getRow(0); // 获取第一行
504
1131
  const cell = row.getCell(0); // 获取该行第一列的单元格
505
1132
  ```
506
1133
 
507
- ### 示例
1134
+ ## 示例
508
1135
 
509
1136
  ```javascript
510
1137
  const row = sheet.getRow(5);
@@ -520,13 +1147,11 @@ row.fill = { fgColor: '#F0F0F0' };
520
1147
  row.font = { bold: true };
521
1148
  ```
522
1149
 
523
- ---
524
-
525
- ## Col
1150
+ # 列级别
526
1151
 
527
1152
  Col 类代表列,提供列级别的属性和操作。
528
1153
 
529
- ### 属性
1154
+ ## 属性
530
1155
 
531
1156
  | 属性 | 类型 | 说明 |
532
1157
  |------|------|------|
@@ -540,9 +1165,11 @@ Col 类代表列,提供列级别的属性和操作。
540
1165
  | `border` | `object` | 边框样式 |
541
1166
  | `fill` | `object` | 填充样式 |
542
1167
 
543
- ### 方法
1168
+ ## 方法
1169
+
1170
+ ### 获取该列的单元格
1171
+ `getCell(r: number): Cell`
544
1172
 
545
- #### `getCell(r: number): Cell`
546
1173
  获取该列的指定行单元格。
547
1174
 
548
1175
  ```javascript
@@ -550,7 +1177,7 @@ const col = sheet.getCol(0); // 获取第一列(A列)
550
1177
  const cell = col.getCell(0); // 获取该列第一行的单元格
551
1178
  ```
552
1179
 
553
- ### 示例
1180
+ ## 示例
554
1181
 
555
1182
  ```javascript
556
1183
  const col = sheet.getCol(2); // 获取 C 列
@@ -566,30 +1193,36 @@ col.fill = { fgColor: '#E0E0E0' };
566
1193
  col.alignment = { horizontal: 'center' };
567
1194
  ```
568
1195
 
569
- ---
570
-
571
- ## Drawing
1196
+ # 图形级别
572
1197
 
573
- Drawing 类代表图表、图片、连接线或形状等图形对象。
1198
+ Drawing 类代表图表、图片或形状(包括连接线)等图形对象。
574
1199
 
575
- ### 属性
1200
+ ## 属性
576
1201
 
577
1202
  | 属性 | 类型 | 说明 |
578
1203
  |------|------|------|
579
- | `id` | `string` | 唯一标识符 |
580
- | `type` | `string` | 类型:`chart`、`image`、`connector`、`shape` |
1204
+ | `id` | `string` | 唯一标识符(只读) |
1205
+ | `type` | `string` | 类型:`chart`、`image`、`shape` |
1206
+ | `shapeType` | `string` | 形状类型(type=shape 时):`rect`、`line`、`straightConnector1` 等 |
1207
+ | `isConnector` | `boolean` | 是否为连接线(只读) |
1208
+ | `shapeStyle` | `object` | 形状样式(type=shape 时):`{fill, stroke, strokeWidth, startArrow, endArrow}` |
1209
+ | `shapeText` | `string` | 形状内的文本(type=shape 时) |
581
1210
  | `startCell` | `CellNum` | 起始单元格位置 |
582
- | `offsetX` | `number` | X 轴偏移(默认 0) |
583
- | `offsetY` | `number` | Y 轴偏移(默认 0) |
584
- | `width` | `number` | 宽度(默认 480) |
585
- | `height` | `number` | 高度(默认 288) |
1211
+ | `offsetX` | `number` | X 轴偏移(默认 5) |
1212
+ | `offsetY` | `number` | Y 轴偏移(默认 5) |
1213
+ | `width` | `number` | 宽度(默认:chart=460, shape=100, image=自动) |
1214
+ | `height` | `number` | 高度(默认:chart=260, shape=100, image=自动) |
586
1215
  | `option` | `object` | 图表配置(type=chart 时,与 ECharts 配置相同) |
587
1216
  | `imageBase64` | `string` | Base64 图片数据(type=image 时) |
1217
+ | `area` | `object` | 图形对象的实际覆盖区域(只读):`{s:{r,c,offsetX,offsetY}, e:{r,c,offsetX,offsetY}}` |
1218
+ | `anchorType` | `string` | 锚点类型:`twoCell`(随单元格移动+缩放)、`oneCell`(仅随移动)、`absolute`(固定) |
588
1219
  | `updRender` | `boolean` | 是否更新渲染 |
589
1220
 
590
- ### 方法
1221
+ ## 方法
1222
+
1223
+ ### 更新图层顺序
1224
+ `updIndex(direction: string): void`
591
1225
 
592
- #### `updIndex(direction: string): void`
593
1226
  更新图层顺序。
594
1227
 
595
1228
  参数值:`"up"` | `"down"` | `"top"` | `"bottom"`
@@ -598,26 +1231,13 @@ Drawing 类代表图表、图片、连接线或形状等图形对象。
598
1231
  drawing.updIndex("top"); // 移到最上层
599
1232
  ```
600
1233
 
601
- ---
602
-
603
- ## Layout
1234
+ # 布局管理
604
1235
 
605
1236
  Layout 类管理编辑器的界面布局,包括菜单栏、工具栏、公式栏、Sheet 标签栏和 AI 聊天面板等界面元素的显示与隐藏。
606
1237
 
607
- ### 构造函数
1238
+ **说明:** Layout 类由 SheetNext 自动创建,通过 `SN.Layout` 访问。菜单栏相关配置请参见 [快速开始 - 初始化配置](#快速开始)。
608
1239
 
609
- Layout 类由 SheetNext 自动创建,通过 `SN.Layout` 访问。
610
-
611
- ```javascript
612
- const SN = new SheetNext(dom, {
613
- menuList: [...], // 自定义菜单配置(可选)
614
- menuRight: {...} // 自定义右侧菜单(可选)
615
- });
616
- ```
617
-
618
- **注意:** `menuList` 和 `menuRight` 只能在初始化时传入,后续无法修改。
619
-
620
- ### 属性
1240
+ ## 属性
621
1241
 
622
1242
  | 属性 | 类型 | 说明 |
623
1243
  |------|------|------|
@@ -630,119 +1250,15 @@ const SN = new SheetNext(dom, {
630
1250
  | `isSmallWindow` | `boolean` | 当前是否为小窗口模式(宽度 < 900px)(只读) |
631
1251
  | `menuConfig` | `object` | 菜单配置对象(只读) |
632
1252
 
633
- ### 菜单配置(初始化时)
634
-
635
- #### menuList 配置
636
-
637
- `menuList` 用于自定义顶部菜单栏,只能在初始化时传入。
638
-
639
- **MenuList 结构:**
640
-
641
- ```typescript
642
- interface MenuItem {
643
- label: string; // 菜单项标签
644
- handler?: () => void; // 点击处理函数
645
- disabled?: boolean; // 是否禁用
646
- tip?: string; // 提示信息(右侧显示)
647
- divider?: boolean; // 是否为分隔线
648
- }
649
-
650
- interface Menu {
651
- label: string; // 菜单标签
652
- items: MenuItem[]; // 菜单项列表
653
- }
654
-
655
- type MenuList = Menu[];
656
- ```
657
-
658
- **完整示例:**
659
-
660
- ```javascript
661
- const SN = new SheetNext(document.querySelector('#container'), {
662
- // 配置后覆盖原配置
663
- menuList: [
664
- {
665
- label: '文件',
666
- items: [
667
- { label: '导入 XLSX', handler: () => SN.containerDom.querySelector('.sn-upload').click() },
668
- { divider: true },
669
- { label: '导出 XLSX', handler: () => SN.export('XLSX') }
670
- ]
671
- },
672
- {
673
- label: '插入',
674
- items: [
675
- { label: '插入行', handler: () => SN.AI.chatInput('在表最后新插入10行') },
676
- { label: '插入列', handler: () => SN.AI.chatInput('在表最后新插入10列') },
677
- { label: '超链接', handler: () => SN.AI.chatInput('在A1插入超链接,文本:SN 地址:https://www.sheetnext.com') },
678
- { label: '图片', disabled: true, tip: '待开放' },
679
- { label: '图表', handler: () => SN.AI.chatInput('根据表中数据生成柱状图') },
680
- { label: '图形', disabled: true, tip: '待开放' },
681
- { label: '数据透视表', disabled: true, tip: '待开放' },
682
- { label: '公式', handler: () => SN.AI.chatInput('在A1编写公式,求F18:G21的平均值') }
683
- ]
684
- },
685
- {
686
- label: '视图',
687
- items: [
688
- { label: '显示行列标', handler: () => { SN.activeSheet.showRowColHeaders = true; SN.r(); } },
689
- { label: '隐藏行列标', handler: () => { SN.activeSheet.showRowColHeaders = false; SN.r(); } },
690
- { label: '显示网格线', handler: () => { SN.activeSheet.showGridLines = true; SN.r(); } },
691
- { label: '隐藏网格线', handler: () => { SN.activeSheet.showGridLines = false; SN.r(); } },
692
- { label: '冻结首行', handler: () => { SN.activeSheet.ySplit = 1; SN.r(); } },
693
- { label: '冻结首列', handler: () => { SN.activeSheet.xSplit = 1; SN.r(); } }
694
- ]
695
- },
696
- {
697
- label: '更多',
698
- items: [
699
- { label: 'SheetNext Pro', handler: () => window.open('https://www.sheetnext.com/pro') },
700
- { label: '开发文档', handler: () => window.open('https://www.github.com/wyyazlz/sheetnext') },
701
- ]
702
- }
703
- ]
704
- });
705
- ```
706
-
707
- **默认菜单:**
708
-
709
- 如果不传入 `menuList`,将使用默认菜单配置(包含:文件、插入、视图、更多)。
710
-
711
- #### menuRight 配置
712
-
713
- `menuRight` 用于自定义菜单栏右侧区域,只能在初始化时传入。
714
-
715
- **MenuRight 结构:**
716
-
717
- ```typescript
718
- interface MenuRight {
719
- html: string; // HTML 内容
720
- handler?: () => void; // 点击处理函数
721
- }
722
- ```
723
-
724
- **示例:**
725
-
726
- ```javascript
727
- const SN = new SheetNext(document.querySelector('#container'), {
728
- menuRight: {
729
- html: '<span style="color: #666;">我的应用 v1.0</span>',
730
- handler: () => {
731
- alert('欢迎使用!');
732
- }
733
- }
734
- });
735
- ```
736
-
737
- ---
738
-
739
- ## Utils
1253
+ # 工具方法
740
1254
 
741
1255
  Utils 类提供坐标转换等实用方法。
742
1256
 
743
- ### 方法
1257
+ ## 方法
1258
+
1259
+ ### 数字转字母列标
1260
+ `numToChar(num: number): string`
744
1261
 
745
- #### `numToChar(num: number): string`
746
1262
  数字转字母列标。
747
1263
 
748
1264
  ```javascript
@@ -751,7 +1267,9 @@ SN.Utils.numToChar(25); // "Z"
751
1267
  SN.Utils.numToChar(26); // "AA"
752
1268
  ```
753
1269
 
754
- #### `charToNum(char: string): number`
1270
+ ### 字母列标转数字
1271
+ `charToNum(char: string): number`
1272
+
755
1273
  字母列标转数字。
756
1274
 
757
1275
  ```javascript
@@ -760,50 +1278,86 @@ SN.Utils.charToNum("Z"); // 25
760
1278
  SN.Utils.charToNum("AA"); // 26
761
1279
  ```
762
1280
 
763
- #### `rangeNumToStr(rangeNum: RangeNum): string`
1281
+ ### 范围对象转字符串
1282
+ `rangeNumToStr(rangeNum: RangeNum): string`
1283
+
764
1284
  范围对象转字符串。
765
1285
 
766
1286
  ```javascript
767
1287
  SN.Utils.rangeNumToStr({s:{r:0,c:0}, e:{r:2,c:2}}); // "A1:C3"
768
1288
  ```
769
1289
 
770
- #### `cellStrToNum(cellStr: string): CellNum`
1290
+ ### 单元格字符串转数字对象
1291
+ `cellStrToNum(cellStr: string): CellNum`
1292
+
771
1293
  单元格字符串转数字对象。
772
1294
 
773
1295
  ```javascript
774
1296
  SN.Utils.cellStrToNum("A1"); // {r:0, c:0}
775
1297
  ```
776
1298
 
777
- #### `cellNumToStr(cellNum: CellNum): string`
1299
+ ### 单元格数字对象转字符串
1300
+ `cellNumToStr(cellNum: CellNum): string`
1301
+
778
1302
  单元格数字对象转字符串。
779
1303
 
780
1304
  ```javascript
781
1305
  SN.Utils.cellNumToStr({r:0, c:0}); // "A1"
782
1306
  ```
783
1307
 
784
- ---
1308
+ ### 显示消息提示
1309
+ `msg(message: string): void`
785
1310
 
786
- ## UndoRedoClass
1311
+ 显示临时消息提示(3秒后自动消失)。
1312
+
1313
+ ```javascript
1314
+ SN.Utils.msg("操作成功!");
1315
+ ```
1316
+
1317
+ ### 显示弹窗
1318
+ `modal(options: object): Promise`
1319
+
1320
+ 显示模态弹窗,返回 Promise(确定时 resolve,取消时 reject)。
1321
+
1322
+ ```javascript
1323
+ // 基础用法
1324
+ SN.Utils.modal({
1325
+ title: '提示',
1326
+ content: '确定要删除吗?',
1327
+ confirmText: '确定',
1328
+ cancelText: '取消'
1329
+ }).then(() => {
1330
+ console.log('用户点击了确定');
1331
+ }).catch(() => {
1332
+ console.log('用户取消了');
1333
+ });
1334
+ ```
1335
+
1336
+ # 历史记录
787
1337
 
788
1338
  管理操作历史,支持撤销和重做功能。
789
1339
 
790
- ### 方法
1340
+ ## 方法
1341
+
1342
+ ### 撤销操作
1343
+ `undo(): void`
791
1344
 
792
- #### `undo(): void`
793
1345
  撤销上一步操作。
794
1346
 
795
1347
  ```javascript
796
1348
  SN.UndoRedo.undo();
797
1349
  ```
798
1350
 
799
- #### `redo(): void`
1351
+ ### 重做操作
1352
+ `redo(): void`
1353
+
800
1354
  重做上一步操作。
801
1355
 
802
1356
  ```javascript
803
1357
  SN.UndoRedo.redo();
804
1358
  ```
805
1359
 
806
- ### 示例
1360
+ ## 示例
807
1361
 
808
1362
  ```javascript
809
1363
  // 执行一些操作
@@ -822,5 +1376,328 @@ SN.UndoRedo.redo();
822
1376
 
823
1377
  **注意**:
824
1378
  - 撤销/重做会自动记录大部分用户操作
825
- - 通过 API 修改的内容也会被记录
826
1379
  - 历史记录栈有大小限制,过旧的操作会被清除
1380
+
1381
+ # AI 功能
1382
+
1383
+ 通过 `SN.AI` 访问 AI 辅助功能
1384
+
1385
+ ## 方法
1386
+
1387
+ ### 监听 AI 请求状态
1388
+ `listenRequestStatus(callback: Function): Function`
1389
+
1390
+ 监听 AI 请求的 HTTP 状态码(如 200、401、500 等),返回取消监听的函数。
1391
+
1392
+ ```javascript
1393
+ // 添加监听器
1394
+ const unsubscribe = SN.AI.listenRequestStatus((httpStatus) => {
1395
+ if (httpStatus === 200) {
1396
+ console.log('AI 请求成功');
1397
+ } else if (httpStatus === 401) {
1398
+ console.log('未授权,请检查 AI_TOKEN');
1399
+ }
1400
+ });
1401
+
1402
+ // 取消监听
1403
+ unsubscribe();
1404
+ ```
1405
+
1406
+ # AI 中转配置
1407
+
1408
+ 写一个接口将前端传入的message消息分发给你想对接的大模型,然后在前端配置好接口地址即可开始工作!
1409
+
1410
+ ## 功能说明
1411
+
1412
+ AI 服务中转层是连接 SheetNext 前端与大模型 API 的桥梁,主要负责以下核心功能:
1413
+
1414
+ **核心功能:**
1415
+
1416
+ 1. **消息格式转换** - 将 SheetNext 提供的通用消息结构转换为目标大模型(如 Claude、GPT 等)所需的标准格式
1417
+ 2. **流式数据处理** - 实现 AI 响应的流式接收与转发,提升用户交互体验
1418
+ 3. **安全隔离** - 在服务端隐藏真实的 API Key,避免密钥泄露风险
1419
+ 4. **使用统计** - 企业可在中转层统计 Token 消耗、请求次数等关键数据
1420
+
1421
+ ## 核心架构
1422
+
1423
+ ```
1424
+ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐
1425
+ │ │ │ │ │ │
1426
+ │ SheetNext │────────▶│ 中转服务器 │──────▶│各种大模型API │
1427
+ │ 前端 │ HTTP │ (您的服务器) │ HTTPS │ (Claude等) │
1428
+ │ │◀────────│ │◀──────│ │
1429
+ └─────────────┘ SSE流 └──────────────┘ Stream └─────────────┘
1430
+
1431
+
1432
+ ┌──────────────┐
1433
+ │ 使用统计/日志 │
1434
+ └──────────────┘
1435
+ ```
1436
+
1437
+ **工作流程:**
1438
+
1439
+ 1. **前端请求** - SheetNext 发送包含 `messages` 数组的 POST 请求到中转服务器
1440
+ 2. **格式转换** - 中转服务器将通用格式转换为目标大模型的专用格式
1441
+ 3. **API 调用** - 使用服务端存储的 API Key 调用大模型 API
1442
+ 4. **流式响应** - 接收大模型的流式响应,转换后通过 SSE (Server-Sent Events) 返回前端
1443
+
1444
+ ## 完整示例
1445
+
1446
+ 通用中转完整实现示例:
1447
+
1448
+ **安装依赖:**
1449
+
1450
+ ```bash
1451
+ npm install @anthropic-ai/sdk openai
1452
+ ```
1453
+
1454
+ **完整代码:**
1455
+
1456
+ ```javascript
1457
+ /**
1458
+ * SheetNext AI & claude/openai 中转服务器示例 Node.js 版本
1459
+ * 2025.10.17 v1.0.0
1460
+ */
1461
+
1462
+ const http = require('http');
1463
+ const Anthropic = require('@anthropic-ai/sdk');
1464
+ const OpenAI = require('openai');
1465
+
1466
+ // ======= 配置 =======
1467
+ const CONFIG = {
1468
+ model: 'claude-sonnet-4-5-20250929', // 设置模型名称,自动判断使用 claude 还是 openai
1469
+ claude: {
1470
+ apiKey: 'your-apiKey',
1471
+ baseURL: 'https://xx.xx.xx/'
1472
+ },
1473
+ openai: {
1474
+ apiKey: 'your-apiKey',
1475
+ baseURL: 'https://xx.xx.xx/v1'
1476
+ }
1477
+ };
1478
+
1479
+ const anthropic = new Anthropic({ apiKey: CONFIG.claude.apiKey, baseURL: CONFIG.claude.baseURL });
1480
+ const openai = new OpenAI({ apiKey: CONFIG.openai.apiKey, baseURL: CONFIG.openai.baseURL });
1481
+
1482
+ // ======= message默认是openai格式,claude请求时转为它适配格式 =======
1483
+ const convertToClaudeMessages = (messages) => {
1484
+ const system = [];
1485
+ const claudeMessages = [];
1486
+ let isFirstSystem = true;
1487
+
1488
+ // 转换内容部分的辅助函数
1489
+ const convertContent = (content) => {
1490
+ const parts = Array.isArray(content) ? content : [{ type: 'text', text: content }];
1491
+ return parts.map(part => {
1492
+ if (part.type === 'text') {
1493
+ return { type: 'text', text: part.text };
1494
+ }
1495
+ if (part.type === 'image_url') {
1496
+ const [, mediaType, base64Data] = part.image_url.url.match(/data:(.*?);base64,(.*)/) || [];
1497
+ if (base64Data) {
1498
+ return { type: 'image', source: { type: 'base64', media_type: mediaType || 'image/jpeg', data: base64Data } };
1499
+ }
1500
+ }
1501
+ return null;
1502
+ }).filter(Boolean);
1503
+ };
1504
+
1505
+ for (const msg of messages) {
1506
+ if (msg.role === 'system') {
1507
+ if (isFirstSystem) {
1508
+ // 第一个 system:提取文本作为 system 参数(约定无图片)
1509
+ const text = typeof msg.content === 'string' ? msg.content : msg.content[0]?.text || '';
1510
+ if (text) system.push({ type: 'text', text });
1511
+ isFirstSystem = false;
1512
+ } else {
1513
+ // 其他 system:转为 user
1514
+ claudeMessages.push({ role: 'user', content: convertContent(msg.content) });
1515
+ }
1516
+ } else {
1517
+ // user/assistant 消息
1518
+ claudeMessages.push({ role: msg.role, content: convertContent(msg.content) });
1519
+ }
1520
+ }
1521
+
1522
+ return { system, messages: claudeMessages };
1523
+ };
1524
+
1525
+ // ======= Claude SDK =======
1526
+ async function callClaudeSDK(messages, model, onChunk) {
1527
+ const { system, messages: claudeMessages } = convertToClaudeMessages(messages);
1528
+
1529
+ // 打印请求结构(省略 base64 数据)
1530
+ const printableRequest = {
1531
+ system: system.map(s => s.type === 'image'
1532
+ ? { type: 'image', source: { ...s.source, data: `[${s.source.data?.length || 0} chars]` } }
1533
+ : s
1534
+ ),
1535
+ messages: claudeMessages.map(msg => ({
1536
+ role: msg.role,
1537
+ content: typeof msg.content === 'string' ? msg.content :
1538
+ msg.content.map(c => c.type === 'image'
1539
+ ? { type: 'image', source: { ...c.source, data: `[${c.source.data?.length || 0} chars]` } }
1540
+ : c
1541
+ )
1542
+ }))
1543
+ };
1544
+
1545
+ const stream = await anthropic.messages.create({
1546
+ model: model,
1547
+ max_tokens: 8192,
1548
+ system,
1549
+ messages: claudeMessages,
1550
+ stream: true,
1551
+ thinking: { type: "enabled", budget_tokens: 2000 }
1552
+ });
1553
+
1554
+ for await (const event of stream) {
1555
+ if (event.type === 'content_block_delta') {
1556
+ const { delta } = event;
1557
+ if (delta?.type === 'thinking_delta' && delta.thinking) {
1558
+ onChunk({ type: 'think', delta: delta.thinking });
1559
+ } else if (delta?.type === 'text_delta') {
1560
+ onChunk({ type: 'text', delta: delta.text });
1561
+ }
1562
+ }
1563
+ }
1564
+ }
1565
+
1566
+ // ======= OpenAI SDK =======
1567
+ async function callOpenAISDK(messages, model, onChunk) {
1568
+ const stream = await openai.chat.completions.create({
1569
+ model: model,
1570
+ messages: messages, // 直接使用 OpenAI 格式的 messages
1571
+ stream: true
1572
+ });
1573
+
1574
+ for await (const chunk of stream) {
1575
+ const delta = chunk.choices[0]?.delta;
1576
+ if (delta?.content) {
1577
+ onChunk({ type: 'text', delta: delta.content });
1578
+ }
1579
+ }
1580
+ }
1581
+
1582
+ // ======= HTTP 处理 =======
1583
+ async function handleChat(messages, res) {
1584
+ res.writeHead(200, {
1585
+ 'Content-Type': 'text/event-stream',
1586
+ 'Cache-Control': 'no-cache',
1587
+ 'Connection': 'keep-alive',
1588
+ 'Access-Control-Allow-Origin': '*'
1589
+ });
1590
+
1591
+ let ended = false;
1592
+ const write = (data) => !ended && !res.writableEnded && res.write(data);
1593
+ const onChunk = (chunk) => write(`data: ${JSON.stringify(chunk)}\n\n`);
1594
+
1595
+ try {
1596
+ // 根据模型名称自动判断使用哪个 provider
1597
+ const provider = CONFIG.model.toLowerCase().includes('claude') ? 'claude' : 'openai';
1598
+ if (provider === 'openai') {
1599
+ await callOpenAISDK(messages, CONFIG.model, onChunk);
1600
+ } else {
1601
+ await callClaudeSDK(messages, CONFIG.model, onChunk);
1602
+ }
1603
+ write(`data: [DONE]\n\n`);
1604
+ } catch (error) {
1605
+ write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
1606
+ } finally {
1607
+ ended = true;
1608
+ res.end();
1609
+ }
1610
+ }
1611
+
1612
+ // ======= HTTP 服务器 =======
1613
+ http.createServer(async (req, res) => {
1614
+ const corsHeaders = {
1615
+ 'Access-Control-Allow-Origin': '*',
1616
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
1617
+ 'Access-Control-Allow-Headers': 'Content-Type'
1618
+ };
1619
+
1620
+ if (req.method === 'OPTIONS') {
1621
+ res.writeHead(200, corsHeaders);
1622
+ return res.end();
1623
+ }
1624
+
1625
+ if (req.url === '/sheetnextAI' && req.method === 'POST') {
1626
+ let body = '';
1627
+ req.on('data', chunk => body += chunk);
1628
+ req.on('end', async () => {
1629
+ try {
1630
+ const { messages } = JSON.parse(body);
1631
+ if (!Array.isArray(messages)) throw new Error('Invalid messages');
1632
+ await handleChat(messages, res);
1633
+ } catch (error) {
1634
+ res.writeHead(400, { 'Content-Type': 'application/json' });
1635
+ res.end(JSON.stringify({ error: error.message }));
1636
+ }
1637
+ });
1638
+ } else {
1639
+ res.writeHead(404);
1640
+ res.end('Not Found');
1641
+ }
1642
+ }).listen(3000, () => console.log('🚀 Server running on http://localhost:3000'));
1643
+ ```
1644
+
1645
+ **配置说明:**
1646
+
1647
+ **判断规则:**
1648
+ - 如果模型名称包含 `claude`(不区分大小写) → 使用 Claude SDK
1649
+ - 其他情况 → 使用 OpenAI SDK
1650
+
1651
+ ## 消息格式
1652
+
1653
+ **请求格式:**
1654
+
1655
+ SheetNext 发送的请求体格式:
1656
+
1657
+ ```json
1658
+ {
1659
+ "messages": [
1660
+ {
1661
+ "role": "system",
1662
+ "content": "你是一个电子表格助手..."
1663
+ },
1664
+ {
1665
+ "role": "user",
1666
+ "content": "帮我分析销售数据"
1667
+ },
1668
+ {
1669
+ "role": "assistant",
1670
+ "content": "好的,我来帮您分析..."
1671
+ },
1672
+ {
1673
+ "role": "user",
1674
+ "content": [
1675
+ {
1676
+ "type": "text",
1677
+ "text": "某区域图片"
1678
+ },
1679
+ {
1680
+ "type": "image_url",
1681
+ "image_url": {
1682
+ "url": "data:image/png;base64,iVBORw0KGgoAAAANS..."
1683
+ }
1684
+ }
1685
+ ]
1686
+ }
1687
+ ]
1688
+ }
1689
+ ```
1690
+
1691
+ **响应格式:**
1692
+
1693
+ 您的服务器应该返回 SSE 流:
1694
+
1695
+ ```
1696
+ data: {"type":"text","delta":"我"}
1697
+
1698
+ data: {"type":"text","delta":"来"}
1699
+
1700
+ data: {"type":"text","delta":"帮"}
1701
+
1702
+ data: [DONE]
1703
+ ```