sheetnext 0.1.7 → 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 +1165 -151
- package/README.md +7 -6
- package/dist/sheetnext.css +1 -1
- package/dist/sheetnext.es.js +3 -3
- package/dist/sheetnext.umd.js +3 -3
- package/docs/demo.png +0 -0
- package/package.json +11 -4
- package/types/index.d.ts +0 -1
- package/AGENT.md +0 -330
- package/README.zh-CN.md +0 -100
package/DOCS.md
CHANGED
|
@@ -1,51 +1,159 @@
|
|
|
1
|
-
# SheetNext
|
|
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
|
-
|
|
10
|
-
- [Sheet Class](#Sheet) - 工作表管理:行列管理、合并单元格、区域遍历、排序、批量插入数据、图形对象管理等
|
|
11
|
-
- [Cell Class](#Cell) - 单元格管理:读写值/公式、样式设置、数字格式、超链接、数据验证等
|
|
12
|
-
- [Row Class](#Row) - 行管理:行高、批量行样式、获取行内单元格、显示/隐藏等
|
|
13
|
-
- [Col Class](#Col) - 列管理:列宽、批量列样式、获取列内单元格、显示/隐藏等
|
|
14
|
-
- [Drawing Class](#Drawing) - 图形管理:图表、图形、图片的增删改查、设置位置和尺寸、调整图层顺序等
|
|
15
|
-
- [Utils Class](#Utils) - 坐标转换:单元格字符串与数字坐标互转、列字母与数字互转、范围格式转换等
|
|
16
|
-
- [UndoRedo Class](#UndoRedo) - 历史管理:撤销/重做操作
|
|
14
|
+
SheetNext 提供多种安装方式,满足不同项目需求:
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
- **npm/yarn 安装** - 适用于现代前端项目(React、Vue、Angular 等)
|
|
17
|
+
- **浏览器直接引入** - 通过 CDN 或本地文件直接在 HTML 中使用
|
|
19
18
|
|
|
20
|
-
##
|
|
19
|
+
## 🔗 相关链接
|
|
21
20
|
|
|
22
|
-
|
|
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 安装和浏览器直接引入两种方式,满足不同场景需求。
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
## 📦 使用 npm 安装
|
|
25
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
|
+
```
|
|
26
37
|
```javascript
|
|
27
|
-
|
|
28
|
-
|
|
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>© SheetNext</div>' // 菜单栏右侧自定义内容
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 配置参数说明
|
|
78
|
+
|
|
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 自定义菜单示例
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
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[];
|
|
29
129
|
```
|
|
30
130
|
|
|
31
|
-
|
|
32
|
-
- `
|
|
33
|
-
- `
|
|
131
|
+
**注意事项:**
|
|
132
|
+
- `menuList` 和 `menuRight` 只能在初始化时配置,后续无法修改
|
|
133
|
+
- 如果不传入 `menuList`,将使用默认菜单(包含:文件、插入、公式、数据、视图、更多)
|
|
134
|
+
- AI 功能需要配置 `AI_URL` 才能使用,详见 AI 中转配置
|
|
135
|
+
|
|
136
|
+
# 工作簿级别
|
|
137
|
+
|
|
138
|
+
SheetNext 是主入口类,管理整个电子表格应用。
|
|
34
139
|
|
|
35
|
-
|
|
140
|
+
## 核心属性
|
|
36
141
|
|
|
37
142
|
| 属性 | 类型 | 说明 |
|
|
38
143
|
|------|------|------|
|
|
144
|
+
| `workbookName` | `string` | 工作簿名称(可读写,长度1-255字符) |
|
|
39
145
|
| `activeSheet` | `Sheet` | 当前激活的工作表(可读写) |
|
|
40
146
|
| `sheets` | `Sheet[]` | 所有工作表数组 |
|
|
41
147
|
| `sheetNames` | `string[]` | 工作表名称列表(只读) |
|
|
42
148
|
| `containerDom` | `HTMLElement` | 编辑器容器元素 |
|
|
43
149
|
| `namespace` | `string` | 实例的全局命名空间(如 `SN_0`) |
|
|
44
|
-
| `locked` | `boolean` |
|
|
150
|
+
| `locked` | `boolean` | 是否锁定工作表切换/操作 |
|
|
45
151
|
|
|
46
|
-
|
|
152
|
+
## 核心方法
|
|
153
|
+
|
|
154
|
+
### 新建工作表
|
|
155
|
+
`addSheet(name?: string): Sheet`
|
|
47
156
|
|
|
48
|
-
#### `addSheet(name?: string): Sheet`
|
|
49
157
|
添加新工作表,名称可选(自动生成 Sheet1、Sheet2 等)。
|
|
50
158
|
|
|
51
159
|
```javascript
|
|
@@ -55,66 +163,129 @@ const autoSheet = SN.addSheet(); // 自动命名
|
|
|
55
163
|
|
|
56
164
|
**规则:** 名称不重复、长度1-31字符、不含特殊符号 `: / \ * ? [ ]`
|
|
57
165
|
|
|
58
|
-
|
|
166
|
+
### 删除工作表
|
|
167
|
+
`delSheet(name: string): void`
|
|
168
|
+
|
|
59
169
|
删除指定工作表(至少保留一个可见工作表)。
|
|
60
170
|
|
|
61
171
|
```javascript
|
|
62
172
|
SN.delSheet("Sheet2");
|
|
63
173
|
```
|
|
64
174
|
|
|
65
|
-
|
|
175
|
+
### 根据名称获取工作表
|
|
176
|
+
`getSheetByName(name: string): Sheet | null`
|
|
177
|
+
|
|
66
178
|
根据名称获取工作表。
|
|
67
179
|
|
|
68
180
|
```javascript
|
|
69
181
|
const sheet = SN.getSheetByName("Sheet1");
|
|
70
182
|
```
|
|
71
183
|
|
|
72
|
-
|
|
184
|
+
### 根据索引获取可见工作表
|
|
185
|
+
`getVisibleSheetByIndex(index: number): Sheet`
|
|
186
|
+
|
|
73
187
|
获取可见工作表(按索引,隐藏工作表不计入)。
|
|
74
188
|
|
|
75
189
|
```javascript
|
|
76
190
|
const firstSheet = SN.getVisibleSheetByIndex(0);
|
|
77
191
|
```
|
|
78
192
|
|
|
79
|
-
###
|
|
193
|
+
### 手动重新渲染
|
|
194
|
+
`r(): void`
|
|
80
195
|
|
|
81
|
-
|
|
82
|
-
导入 Excel 文件(.xlsx 格式)。
|
|
196
|
+
手动触发画布重新渲染(批量修改后使用)。
|
|
83
197
|
|
|
84
198
|
```javascript
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
199
|
+
// 批量修改后刷新
|
|
200
|
+
for (let i = 0; i < 100; i++) {
|
|
201
|
+
sheet.getCell(i, 0).editVal = i;
|
|
202
|
+
}
|
|
203
|
+
SN.r();
|
|
88
204
|
```
|
|
89
205
|
|
|
90
|
-
|
|
91
|
-
|
|
206
|
+
### 获取工作簿数据
|
|
207
|
+
`getData(): object`
|
|
208
|
+
|
|
209
|
+
获取完整的工作簿数据(JSON格式),包含所有工作表、单元格数据、样式、公式、图表等。
|
|
92
210
|
|
|
93
211
|
```javascript
|
|
94
|
-
|
|
212
|
+
// 获取工作簿数据
|
|
213
|
+
const data = SN.getData();
|
|
95
214
|
```
|
|
96
215
|
|
|
97
|
-
|
|
216
|
+
**使用场景:**
|
|
217
|
+
- 数据备份和恢复
|
|
218
|
+
- 数据持久化到数据库或本地存储
|
|
219
|
+
- 数据分析和处理
|
|
220
|
+
- 跨系统数据传输
|
|
98
221
|
|
|
99
|
-
|
|
100
|
-
手动触发画布重新渲染(批量修改后使用)。
|
|
222
|
+
**示例:保存到 localStorage**
|
|
101
223
|
|
|
102
224
|
```javascript
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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);
|
|
108
232
|
```
|
|
109
233
|
|
|
110
|
-
|
|
111
|
-
|
|
234
|
+
### 加载工作簿数据
|
|
235
|
+
`setData(data: object): boolean`
|
|
236
|
+
|
|
237
|
+
加载完整的工作簿数据,替换当前所有工作表内容。
|
|
112
238
|
|
|
113
239
|
```javascript
|
|
114
|
-
|
|
240
|
+
SN.setData(data):boolean;
|
|
115
241
|
```
|
|
116
242
|
|
|
117
|
-
###
|
|
243
|
+
### 导入文件
|
|
244
|
+
`import(file: File): Promise<void>`
|
|
245
|
+
|
|
246
|
+
导入文件,支持 `.xlsx`、`.csv` 和 `.json` 格式。
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
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();
|
|
258
|
+
```
|
|
259
|
+
|
|
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
|
+
## 多实例支持
|
|
118
289
|
|
|
119
290
|
SheetNext 支持同一页面创建多个独立实例:
|
|
120
291
|
|
|
@@ -126,13 +297,11 @@ console.log(editor1.namespace); // "SN_0"
|
|
|
126
297
|
console.log(editor2.namespace); // "SN_1"
|
|
127
298
|
```
|
|
128
299
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
## Sheet
|
|
300
|
+
# 工作表级别
|
|
132
301
|
|
|
133
302
|
Sheet 类代表一个工作表。
|
|
134
303
|
|
|
135
|
-
|
|
304
|
+
## 属性
|
|
136
305
|
|
|
137
306
|
| 属性 | 类型 | 说明 |
|
|
138
307
|
|------|------|------|
|
|
@@ -147,41 +316,53 @@ Sheet 类代表一个工作表。
|
|
|
147
316
|
| `activeAreas` | `RangeNum[]` | 当前选中区域 |
|
|
148
317
|
| `rows` | `Row[]` | 所有行 |
|
|
149
318
|
| `cols` | `Col[]` | 所有列 |
|
|
319
|
+
| `rowCount` | `number` | 行数(只读) |
|
|
320
|
+
| `colCount` | `number` | 列数(只读) |
|
|
150
321
|
| `xSplit` | `number` | 冻结列数 |
|
|
151
322
|
| `ySplit` | `number` | 冻结行数 |
|
|
152
323
|
| `drawings` | `Drawing[]` | 图形对象列表(图表、图片等) |
|
|
153
324
|
|
|
154
|
-
|
|
325
|
+
## 基础方法
|
|
326
|
+
|
|
327
|
+
### 获取指定行
|
|
328
|
+
`getRow(r: number): Row`
|
|
155
329
|
|
|
156
|
-
#### `getRow(r: number): Row`
|
|
157
330
|
获取指定行对象。
|
|
158
331
|
|
|
159
332
|
```javascript
|
|
160
333
|
const row = sheet.getRow(0); // 获取第一行
|
|
161
334
|
```
|
|
162
335
|
|
|
163
|
-
|
|
336
|
+
### 获取指定列
|
|
337
|
+
`getCol(c: number): Col`
|
|
338
|
+
|
|
164
339
|
获取指定列对象。
|
|
165
340
|
|
|
166
341
|
```javascript
|
|
167
342
|
const col = sheet.getCol(0); // 获取第一列(A列)
|
|
168
343
|
```
|
|
169
344
|
|
|
170
|
-
|
|
345
|
+
### 获取指定单元格
|
|
346
|
+
`getCell(r: number, c: number): Cell`
|
|
347
|
+
|
|
171
348
|
获取指定单元格。
|
|
172
349
|
|
|
173
350
|
```javascript
|
|
174
351
|
const cell = sheet.getCell(0, 0); // 获取 A1 单元格
|
|
175
352
|
```
|
|
176
353
|
|
|
177
|
-
|
|
354
|
+
### 通过字符串获取单元格
|
|
355
|
+
`getCellByStr(cellStr: string): Cell`
|
|
356
|
+
|
|
178
357
|
通过字符串引用获取单元格。
|
|
179
358
|
|
|
180
359
|
```javascript
|
|
181
360
|
const cell = sheet.getCellByStr("A1");
|
|
182
361
|
```
|
|
183
362
|
|
|
184
|
-
|
|
363
|
+
### 范围字符串转数字
|
|
364
|
+
`rangeStrToNum(rangeStr: string): RangeNum`
|
|
365
|
+
|
|
185
366
|
将字符串范围转换为数字范围对象。
|
|
186
367
|
|
|
187
368
|
```javascript
|
|
@@ -189,9 +370,11 @@ const rangeNum = sheet.rangeStrToNum("A1:C3");
|
|
|
189
370
|
// 返回: {s:{r:0,c:0}, e:{r:2,c:2}}
|
|
190
371
|
```
|
|
191
372
|
|
|
192
|
-
|
|
373
|
+
## 区域遍历
|
|
374
|
+
|
|
375
|
+
### 遍历区域
|
|
376
|
+
`eachArea(rangeRef: RangeRef, callback: (r, c, index) => void, reverse?: boolean): void`
|
|
193
377
|
|
|
194
|
-
#### `eachArea(rangeRef: RangeRef, callback: (r, c, index) => void, reverse?: boolean): void`
|
|
195
378
|
遍历指定区域的每个单元格。
|
|
196
379
|
|
|
197
380
|
```javascript
|
|
@@ -209,23 +392,29 @@ sheet.eachArea("A:A", (r, c) => {
|
|
|
209
392
|
}, true);
|
|
210
393
|
```
|
|
211
394
|
|
|
212
|
-
|
|
395
|
+
## 行列操作
|
|
396
|
+
|
|
397
|
+
### 显示所有隐藏行
|
|
398
|
+
`showAllHidRows(): void`
|
|
213
399
|
|
|
214
|
-
#### `showAllHidRows(): void`
|
|
215
400
|
显示所有隐藏的行。
|
|
216
401
|
|
|
217
402
|
```javascript
|
|
218
403
|
sheet.showAllHidRows();
|
|
219
404
|
```
|
|
220
405
|
|
|
221
|
-
|
|
406
|
+
### 显示所有隐藏列
|
|
407
|
+
`showAllHidCols(): void`
|
|
408
|
+
|
|
222
409
|
显示所有隐藏的列。
|
|
223
410
|
|
|
224
411
|
```javascript
|
|
225
412
|
sheet.showAllHidCols();
|
|
226
413
|
```
|
|
227
414
|
|
|
228
|
-
|
|
415
|
+
### 插入行
|
|
416
|
+
`addRows(startR: number, num: number): void`
|
|
417
|
+
|
|
229
418
|
在指定位置插入行。
|
|
230
419
|
|
|
231
420
|
```javascript
|
|
@@ -234,30 +423,38 @@ sheet.addRows(5, 3); // 在第 5 行位置插入 3 行
|
|
|
234
423
|
|
|
235
424
|
**注意**:多个同时调用时,应反向遍历以避免索引混乱。
|
|
236
425
|
|
|
237
|
-
|
|
426
|
+
### 插入列
|
|
427
|
+
`addCols(startC: number, num: number): void`
|
|
428
|
+
|
|
238
429
|
在指定位置插入列。
|
|
239
430
|
|
|
240
431
|
```javascript
|
|
241
432
|
sheet.addCols(2, 2); // 在第 2 列位置插入 2 列
|
|
242
433
|
```
|
|
243
434
|
|
|
244
|
-
|
|
435
|
+
### 删除行
|
|
436
|
+
`delRows(startR: number, num: number): void`
|
|
437
|
+
|
|
245
438
|
删除指定行。
|
|
246
439
|
|
|
247
440
|
```javascript
|
|
248
441
|
sheet.delRows(5, 3); // 删除从第 5 行开始的 3 行
|
|
249
442
|
```
|
|
250
443
|
|
|
251
|
-
|
|
444
|
+
### 删除列
|
|
445
|
+
`delCols(startC: number, num: number): void`
|
|
446
|
+
|
|
252
447
|
删除指定列。
|
|
253
448
|
|
|
254
449
|
```javascript
|
|
255
450
|
sheet.delCols(2, 2); // 删除从第 2 列开始的 2 列
|
|
256
451
|
```
|
|
257
452
|
|
|
453
|
+
## 合并单元格
|
|
454
|
+
|
|
258
455
|
### 合并单元格
|
|
456
|
+
`mergeCells(rangeRef: RangeRef): void`
|
|
259
457
|
|
|
260
|
-
#### `mergeCells(rangeRef: RangeRef): void`
|
|
261
458
|
合并指定区域的单元格。
|
|
262
459
|
|
|
263
460
|
```javascript
|
|
@@ -266,16 +463,20 @@ sheet.mergeCells("A1:C3");
|
|
|
266
463
|
sheet.mergeCells({s:{r:0,c:0}, e:{r:2,c:2}});
|
|
267
464
|
```
|
|
268
465
|
|
|
269
|
-
|
|
466
|
+
### 取消合并单元格
|
|
467
|
+
`unMergeCells(cellRef: CellRef): void`
|
|
468
|
+
|
|
270
469
|
取消合并(传入区域内任意单元格引用)。
|
|
271
470
|
|
|
272
471
|
```javascript
|
|
273
472
|
sheet.unMergeCells("A1"); // 取消包含 A1 的合并区域
|
|
274
473
|
```
|
|
275
474
|
|
|
276
|
-
|
|
475
|
+
## 排序
|
|
476
|
+
|
|
477
|
+
### 区域排序
|
|
478
|
+
`rangeSort(sortItems: SortItem[], range?: RangeRef): void`
|
|
277
479
|
|
|
278
|
-
#### `rangeSort(sortItems: SortItem[], range?: RangeRef): void`
|
|
279
480
|
对指定区域进行排序。
|
|
280
481
|
|
|
281
482
|
**SortItem 接口:**
|
|
@@ -306,9 +507,11 @@ sheet.rangeSort(
|
|
|
306
507
|
);
|
|
307
508
|
```
|
|
308
509
|
|
|
510
|
+
## 批量插入数据
|
|
511
|
+
|
|
309
512
|
### 批量插入数据
|
|
513
|
+
`insertTable(data: (ICellConfig | string | number)[][], startCell: CellRef, globalConfig?: object): RangeNum`
|
|
310
514
|
|
|
311
|
-
#### `insertTable(data: (ICellConfig | string | number)[][], startCell: CellRef, globalConfig?: object): RangeNum`
|
|
312
515
|
在指定位置插入表格数据。
|
|
313
516
|
|
|
314
517
|
**ICellConfig 接口:**
|
|
@@ -365,9 +568,11 @@ SN.activeSheet.insertTable(t, "A1", {
|
|
|
365
568
|
- 对于合并单元格(`mr`/`mb`),需要添加相同数量的空字符串占位符,保持二维数组的矩形结构。
|
|
366
569
|
- 例如:`{ mr: 2 }, "", ""`
|
|
367
570
|
|
|
368
|
-
|
|
571
|
+
## 图形对象
|
|
572
|
+
|
|
573
|
+
### 添加图形对象
|
|
574
|
+
`addDrawing(config: object): Drawing`
|
|
369
575
|
|
|
370
|
-
#### `addDrawing(config: object): Drawing`
|
|
371
576
|
添加图形对象(图表、图片等)。
|
|
372
577
|
|
|
373
578
|
**示例:添加图表**
|
|
@@ -397,14 +602,18 @@ SN.activeSheet.addDrawing({
|
|
|
397
602
|
});
|
|
398
603
|
```
|
|
399
604
|
|
|
400
|
-
|
|
605
|
+
### 获取单元格的图形对象
|
|
606
|
+
`getDrawingsByCell(cellRef: CellRef): Drawing[]`
|
|
607
|
+
|
|
401
608
|
获取指定单元格位置的所有图形对象。
|
|
402
609
|
|
|
403
610
|
```javascript
|
|
404
611
|
const drawings = sheet.getDrawingsByCell("B2");
|
|
405
612
|
```
|
|
406
613
|
|
|
407
|
-
|
|
614
|
+
### 删除图形对象
|
|
615
|
+
`removeDrawing(id: string): void`
|
|
616
|
+
|
|
408
617
|
删除指定 ID 的图形对象。
|
|
409
618
|
|
|
410
619
|
```javascript
|
|
@@ -413,73 +622,490 @@ sheet.removeDrawing("drawing-id");
|
|
|
413
622
|
|
|
414
623
|
---
|
|
415
624
|
|
|
416
|
-
|
|
625
|
+
# 单元格级别
|
|
417
626
|
|
|
418
|
-
Cell
|
|
627
|
+
Cell 类代表单个单元格,提供完整的数据、样式、验证等功能。
|
|
419
628
|
|
|
420
|
-
|
|
629
|
+
## 核心属性
|
|
421
630
|
|
|
422
631
|
| 属性 | 类型 | 说明 |
|
|
423
632
|
|------|------|------|
|
|
424
|
-
| `
|
|
425
|
-
| `master` | `CellNum \| null` | 如果是合并单元格,指向主单元格 |
|
|
426
|
-
| `hyperlink` | `object` | 超链接:`{target, location, tooltip}` |
|
|
427
|
-
| `dataValidation` | `object` | 数据验证规则 |
|
|
428
|
-
| `editVal` | `string` | 编辑值或公式(可写) |
|
|
633
|
+
| `editVal` | `string` | 编辑值或公式(可读写) |
|
|
429
634
|
| `calcVal` | `any` | 计算值(只读) |
|
|
430
635
|
| `showVal` | `string` | 显示值(只读) |
|
|
431
|
-
| `
|
|
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
|
+
|------|------|------|
|
|
432
645
|
| `font` | `object` | 字体样式 |
|
|
433
646
|
| `alignment` | `object` | 对齐方式 |
|
|
434
647
|
| `border` | `object` | 边框样式 |
|
|
435
648
|
| `fill` | `object` | 填充样式 |
|
|
649
|
+
| `numFmt` | `string` | 数字格式 |
|
|
650
|
+
|
|
651
|
+
## 功能属性
|
|
652
|
+
|
|
653
|
+
| 属性 | 类型 | 说明 |
|
|
654
|
+
|------|------|------|
|
|
655
|
+
| `hyperlink` | `object` | 超链接配置 |
|
|
656
|
+
| `dataValidation` | `object` | 数据验证规则 |
|
|
657
|
+
| `validData` | `boolean` | 数据验证结果(只读) |
|
|
436
658
|
|
|
437
|
-
|
|
659
|
+
## font 对象结构
|
|
660
|
+
|
|
661
|
+
字体样式配置对象。
|
|
438
662
|
|
|
439
663
|
```typescript
|
|
440
664
|
{
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
error: string; // 错误内容
|
|
452
|
-
errorStyle: string; // 错误样式
|
|
453
|
-
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; // 字符集
|
|
454
675
|
}
|
|
455
676
|
```
|
|
456
677
|
|
|
457
|
-
|
|
678
|
+
**示例:**
|
|
458
679
|
|
|
459
680
|
```javascript
|
|
460
681
|
const cell = sheet.getCell(0, 0);
|
|
461
682
|
|
|
462
|
-
//
|
|
463
|
-
cell.
|
|
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
|
+
```
|
|
464
700
|
|
|
465
|
-
|
|
466
|
-
cell.editVal = "=SUM(A1:A10)";
|
|
701
|
+
## alignment 对象结构
|
|
467
702
|
|
|
468
|
-
|
|
469
|
-
console.log(cell.showVal);
|
|
703
|
+
对齐方式配置对象。
|
|
470
704
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
+
}
|
|
474
712
|
```
|
|
475
713
|
|
|
476
|
-
|
|
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 对象结构
|
|
477
740
|
|
|
478
|
-
|
|
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
|
+
# 行级别
|
|
479
1105
|
|
|
480
1106
|
Row 类代表行,提供行级别的属性和操作。
|
|
481
1107
|
|
|
482
|
-
|
|
1108
|
+
## 属性
|
|
483
1109
|
|
|
484
1110
|
| 属性 | 类型 | 说明 |
|
|
485
1111
|
|------|------|------|
|
|
@@ -493,9 +1119,11 @@ Row 类代表行,提供行级别的属性和操作。
|
|
|
493
1119
|
| `border` | `object` | 边框样式 |
|
|
494
1120
|
| `fill` | `object` | 填充样式 |
|
|
495
1121
|
|
|
496
|
-
|
|
1122
|
+
## 方法
|
|
1123
|
+
|
|
1124
|
+
### 获取该行的单元格
|
|
1125
|
+
`getCell(c: number): Cell`
|
|
497
1126
|
|
|
498
|
-
#### `getCell(c: number): Cell`
|
|
499
1127
|
获取该行的指定列单元格。
|
|
500
1128
|
|
|
501
1129
|
```javascript
|
|
@@ -503,7 +1131,7 @@ const row = sheet.getRow(0); // 获取第一行
|
|
|
503
1131
|
const cell = row.getCell(0); // 获取该行第一列的单元格
|
|
504
1132
|
```
|
|
505
1133
|
|
|
506
|
-
|
|
1134
|
+
## 示例
|
|
507
1135
|
|
|
508
1136
|
```javascript
|
|
509
1137
|
const row = sheet.getRow(5);
|
|
@@ -519,13 +1147,11 @@ row.fill = { fgColor: '#F0F0F0' };
|
|
|
519
1147
|
row.font = { bold: true };
|
|
520
1148
|
```
|
|
521
1149
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
## Col
|
|
1150
|
+
# 列级别
|
|
525
1151
|
|
|
526
1152
|
Col 类代表列,提供列级别的属性和操作。
|
|
527
1153
|
|
|
528
|
-
|
|
1154
|
+
## 属性
|
|
529
1155
|
|
|
530
1156
|
| 属性 | 类型 | 说明 |
|
|
531
1157
|
|------|------|------|
|
|
@@ -539,9 +1165,11 @@ Col 类代表列,提供列级别的属性和操作。
|
|
|
539
1165
|
| `border` | `object` | 边框样式 |
|
|
540
1166
|
| `fill` | `object` | 填充样式 |
|
|
541
1167
|
|
|
542
|
-
|
|
1168
|
+
## 方法
|
|
1169
|
+
|
|
1170
|
+
### 获取该列的单元格
|
|
1171
|
+
`getCell(r: number): Cell`
|
|
543
1172
|
|
|
544
|
-
#### `getCell(r: number): Cell`
|
|
545
1173
|
获取该列的指定行单元格。
|
|
546
1174
|
|
|
547
1175
|
```javascript
|
|
@@ -549,7 +1177,7 @@ const col = sheet.getCol(0); // 获取第一列(A列)
|
|
|
549
1177
|
const cell = col.getCell(0); // 获取该列第一行的单元格
|
|
550
1178
|
```
|
|
551
1179
|
|
|
552
|
-
|
|
1180
|
+
## 示例
|
|
553
1181
|
|
|
554
1182
|
```javascript
|
|
555
1183
|
const col = sheet.getCol(2); // 获取 C 列
|
|
@@ -565,30 +1193,36 @@ col.fill = { fgColor: '#E0E0E0' };
|
|
|
565
1193
|
col.alignment = { horizontal: 'center' };
|
|
566
1194
|
```
|
|
567
1195
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
## Drawing
|
|
1196
|
+
# 图形级别
|
|
571
1197
|
|
|
572
|
-
Drawing
|
|
1198
|
+
Drawing 类代表图表、图片或形状(包括连接线)等图形对象。
|
|
573
1199
|
|
|
574
|
-
|
|
1200
|
+
## 属性
|
|
575
1201
|
|
|
576
1202
|
| 属性 | 类型 | 说明 |
|
|
577
1203
|
|------|------|------|
|
|
578
|
-
| `id` | `string` |
|
|
579
|
-
| `type` | `string` | 类型:`chart`、`image`、`
|
|
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 时) |
|
|
580
1210
|
| `startCell` | `CellNum` | 起始单元格位置 |
|
|
581
|
-
| `offsetX` | `number` | X 轴偏移(默认
|
|
582
|
-
| `offsetY` | `number` | Y 轴偏移(默认
|
|
583
|
-
| `width` | `number` |
|
|
584
|
-
| `height` | `number` |
|
|
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=自动) |
|
|
585
1215
|
| `option` | `object` | 图表配置(type=chart 时,与 ECharts 配置相同) |
|
|
586
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`(固定) |
|
|
587
1219
|
| `updRender` | `boolean` | 是否更新渲染 |
|
|
588
1220
|
|
|
589
|
-
|
|
1221
|
+
## 方法
|
|
1222
|
+
|
|
1223
|
+
### 更新图层顺序
|
|
1224
|
+
`updIndex(direction: string): void`
|
|
590
1225
|
|
|
591
|
-
#### `updIndex(direction: string): void`
|
|
592
1226
|
更新图层顺序。
|
|
593
1227
|
|
|
594
1228
|
参数值:`"up"` | `"down"` | `"top"` | `"bottom"`
|
|
@@ -597,15 +1231,34 @@ Drawing 类代表图表、图片、连接线或形状等图形对象。
|
|
|
597
1231
|
drawing.updIndex("top"); // 移到最上层
|
|
598
1232
|
```
|
|
599
1233
|
|
|
600
|
-
|
|
1234
|
+
# 布局管理
|
|
1235
|
+
|
|
1236
|
+
Layout 类管理编辑器的界面布局,包括菜单栏、工具栏、公式栏、Sheet 标签栏和 AI 聊天面板等界面元素的显示与隐藏。
|
|
1237
|
+
|
|
1238
|
+
**说明:** Layout 类由 SheetNext 自动创建,通过 `SN.Layout` 访问。菜单栏相关配置请参见 [快速开始 - 初始化配置](#快速开始)。
|
|
1239
|
+
|
|
1240
|
+
## 属性
|
|
1241
|
+
|
|
1242
|
+
| 属性 | 类型 | 说明 |
|
|
1243
|
+
|------|------|------|
|
|
1244
|
+
| `showMenuBar` | `boolean` | 是否显示菜单栏(可读写) |
|
|
1245
|
+
| `showToolbar` | `boolean` | 是否显示工具栏(可读写) |
|
|
1246
|
+
| `showFormulaBar` | `boolean` | 是否显示公式栏(可读写) |
|
|
1247
|
+
| `showSheetTabBar` | `boolean` | 是否显示 Sheet 标签栏(可读写) |
|
|
1248
|
+
| `showAIChat` | `boolean` | 是否显示 AI 聊天面板(可读写) |
|
|
1249
|
+
| `showAIChatWindow` | `boolean` | 是否显示 AI 聊天小窗口模式(可读写) |
|
|
1250
|
+
| `isSmallWindow` | `boolean` | 当前是否为小窗口模式(宽度 < 900px)(只读) |
|
|
1251
|
+
| `menuConfig` | `object` | 菜单配置对象(只读) |
|
|
601
1252
|
|
|
602
|
-
|
|
1253
|
+
# 工具方法
|
|
603
1254
|
|
|
604
1255
|
Utils 类提供坐标转换等实用方法。
|
|
605
1256
|
|
|
606
|
-
|
|
1257
|
+
## 方法
|
|
1258
|
+
|
|
1259
|
+
### 数字转字母列标
|
|
1260
|
+
`numToChar(num: number): string`
|
|
607
1261
|
|
|
608
|
-
#### `numToChar(num: number): string`
|
|
609
1262
|
数字转字母列标。
|
|
610
1263
|
|
|
611
1264
|
```javascript
|
|
@@ -614,7 +1267,9 @@ SN.Utils.numToChar(25); // "Z"
|
|
|
614
1267
|
SN.Utils.numToChar(26); // "AA"
|
|
615
1268
|
```
|
|
616
1269
|
|
|
617
|
-
|
|
1270
|
+
### 字母列标转数字
|
|
1271
|
+
`charToNum(char: string): number`
|
|
1272
|
+
|
|
618
1273
|
字母列标转数字。
|
|
619
1274
|
|
|
620
1275
|
```javascript
|
|
@@ -623,50 +1278,86 @@ SN.Utils.charToNum("Z"); // 25
|
|
|
623
1278
|
SN.Utils.charToNum("AA"); // 26
|
|
624
1279
|
```
|
|
625
1280
|
|
|
626
|
-
|
|
1281
|
+
### 范围对象转字符串
|
|
1282
|
+
`rangeNumToStr(rangeNum: RangeNum): string`
|
|
1283
|
+
|
|
627
1284
|
范围对象转字符串。
|
|
628
1285
|
|
|
629
1286
|
```javascript
|
|
630
1287
|
SN.Utils.rangeNumToStr({s:{r:0,c:0}, e:{r:2,c:2}}); // "A1:C3"
|
|
631
1288
|
```
|
|
632
1289
|
|
|
633
|
-
|
|
1290
|
+
### 单元格字符串转数字对象
|
|
1291
|
+
`cellStrToNum(cellStr: string): CellNum`
|
|
1292
|
+
|
|
634
1293
|
单元格字符串转数字对象。
|
|
635
1294
|
|
|
636
1295
|
```javascript
|
|
637
1296
|
SN.Utils.cellStrToNum("A1"); // {r:0, c:0}
|
|
638
1297
|
```
|
|
639
1298
|
|
|
640
|
-
|
|
1299
|
+
### 单元格数字对象转字符串
|
|
1300
|
+
`cellNumToStr(cellNum: CellNum): string`
|
|
1301
|
+
|
|
641
1302
|
单元格数字对象转字符串。
|
|
642
1303
|
|
|
643
1304
|
```javascript
|
|
644
1305
|
SN.Utils.cellNumToStr({r:0, c:0}); // "A1"
|
|
645
1306
|
```
|
|
646
1307
|
|
|
647
|
-
|
|
1308
|
+
### 显示消息提示
|
|
1309
|
+
`msg(message: string): void`
|
|
1310
|
+
|
|
1311
|
+
显示临时消息提示(3秒后自动消失)。
|
|
1312
|
+
|
|
1313
|
+
```javascript
|
|
1314
|
+
SN.Utils.msg("操作成功!");
|
|
1315
|
+
```
|
|
1316
|
+
|
|
1317
|
+
### 显示弹窗
|
|
1318
|
+
`modal(options: object): Promise`
|
|
648
1319
|
|
|
649
|
-
|
|
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
|
+
# 历史记录
|
|
650
1337
|
|
|
651
1338
|
管理操作历史,支持撤销和重做功能。
|
|
652
1339
|
|
|
653
|
-
|
|
1340
|
+
## 方法
|
|
1341
|
+
|
|
1342
|
+
### 撤销操作
|
|
1343
|
+
`undo(): void`
|
|
654
1344
|
|
|
655
|
-
#### `undo(): void`
|
|
656
1345
|
撤销上一步操作。
|
|
657
1346
|
|
|
658
1347
|
```javascript
|
|
659
1348
|
SN.UndoRedo.undo();
|
|
660
1349
|
```
|
|
661
1350
|
|
|
662
|
-
|
|
1351
|
+
### 重做操作
|
|
1352
|
+
`redo(): void`
|
|
1353
|
+
|
|
663
1354
|
重做上一步操作。
|
|
664
1355
|
|
|
665
1356
|
```javascript
|
|
666
1357
|
SN.UndoRedo.redo();
|
|
667
1358
|
```
|
|
668
1359
|
|
|
669
|
-
|
|
1360
|
+
## 示例
|
|
670
1361
|
|
|
671
1362
|
```javascript
|
|
672
1363
|
// 执行一些操作
|
|
@@ -685,5 +1376,328 @@ SN.UndoRedo.redo();
|
|
|
685
1376
|
|
|
686
1377
|
**注意**:
|
|
687
1378
|
- 撤销/重做会自动记录大部分用户操作
|
|
688
|
-
- 通过 API 修改的内容也会被记录
|
|
689
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
|
+
```
|