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 +1150 -273
- package/README.md +3 -6
- package/dist/sheetnext.css +1 -1
- package/dist/sheetnext.es.js +3 -3
- package/dist/sheetnext.umd.js +3 -3
- package/package.json +1 -2
- package/types/index.d.ts +0 -1
- package/AGENT.md +0 -330
package/DOCS.md
CHANGED
|
@@ -1,52 +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
|
-
- [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
|
-
##
|
|
19
|
+
## 🔗 相关链接
|
|
22
20
|
|
|
23
|
-
|
|
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>© 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(
|
|
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
|
-
- `
|
|
34
|
-
- `
|
|
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
|
-
|
|
166
|
+
### 删除工作表
|
|
167
|
+
`delSheet(name: string): void`
|
|
168
|
+
|
|
60
169
|
删除指定工作表(至少保留一个可见工作表)。
|
|
61
170
|
|
|
62
171
|
```javascript
|
|
63
172
|
SN.delSheet("Sheet2");
|
|
64
173
|
```
|
|
65
174
|
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
导入 Excel 文件(.xlsx 格式)。
|
|
196
|
+
手动触发画布重新渲染(批量修改后使用)。
|
|
84
197
|
|
|
85
198
|
```javascript
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
92
|
-
|
|
206
|
+
### 获取工作簿数据
|
|
207
|
+
`getData(): object`
|
|
208
|
+
|
|
209
|
+
获取完整的工作簿数据(JSON格式),包含所有工作表、单元格数据、样式、公式、图表等。
|
|
93
210
|
|
|
94
211
|
```javascript
|
|
95
|
-
|
|
212
|
+
// 获取工作簿数据
|
|
213
|
+
const data = SN.getData();
|
|
96
214
|
```
|
|
97
215
|
|
|
98
|
-
|
|
216
|
+
**使用场景:**
|
|
217
|
+
- 数据备份和恢复
|
|
218
|
+
- 数据持久化到数据库或本地存储
|
|
219
|
+
- 数据分析和处理
|
|
220
|
+
- 跨系统数据传输
|
|
99
221
|
|
|
100
|
-
|
|
101
|
-
手动触发画布重新渲染(批量修改后使用)。
|
|
222
|
+
**示例:保存到 localStorage**
|
|
102
223
|
|
|
103
224
|
```javascript
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
112
|
-
|
|
243
|
+
### 导入文件
|
|
244
|
+
`import(file: File): Promise<void>`
|
|
245
|
+
|
|
246
|
+
导入文件,支持 `.xlsx`、`.csv` 和 `.json` 格式。
|
|
113
247
|
|
|
114
248
|
```javascript
|
|
115
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
406
|
+
### 显示所有隐藏列
|
|
407
|
+
`showAllHidCols(): void`
|
|
408
|
+
|
|
223
409
|
显示所有隐藏的列。
|
|
224
410
|
|
|
225
411
|
```javascript
|
|
226
412
|
sheet.showAllHidCols();
|
|
227
413
|
```
|
|
228
414
|
|
|
229
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
625
|
+
# 单元格级别
|
|
418
626
|
|
|
419
|
-
Cell
|
|
627
|
+
Cell 类代表单个单元格,提供完整的数据、样式、验证等功能。
|
|
420
628
|
|
|
421
|
-
|
|
629
|
+
## 核心属性
|
|
422
630
|
|
|
423
631
|
| 属性 | 类型 | 说明 |
|
|
424
632
|
|------|------|------|
|
|
425
|
-
| `
|
|
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
|
-
| `
|
|
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
|
-
|
|
661
|
+
字体样式配置对象。
|
|
439
662
|
|
|
440
663
|
```typescript
|
|
441
664
|
{
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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.
|
|
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
|
-
|
|
474
|
-
|
|
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
|
-
|
|
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`、`
|
|
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 轴偏移(默认
|
|
583
|
-
| `offsetY` | `number` | Y 轴偏移(默认
|
|
584
|
-
| `width` | `number` |
|
|
585
|
-
| `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=自动) |
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
```
|