sheetnext 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/AGENT.md +403 -0
  2. package/DOCS.md +708 -0
  3. package/README.md +71 -0
  4. package/package.json +6 -2
package/AGENT.md ADDED
@@ -0,0 +1,403 @@
1
+ # SheetNext AI 配置指南
2
+
3
+ > AI 中转服务搭建和配置说明
4
+
5
+ ## 文档导航
6
+
7
+ - [← 返回 README](./README.md) - 快速开始和特点介绍
8
+ - [← API 文档](./DOCS.md) - 详细的类、方法和属性说明
9
+
10
+ ---
11
+
12
+ ## 目录
13
+
14
+ - [功能说明](#功能说明)
15
+ - [核心架构](#核心架构)
16
+ - [完整示例](#完整示例)
17
+ - [消息格式](#消息格式)
18
+ - [其他大模型对接](#其他大模型对接)
19
+ - [部署建议](#部署建议)
20
+
21
+ ---
22
+
23
+ ## 功能说明
24
+
25
+ AI 服务中转层是连接 SheetNext 前端与大模型 API 的桥梁,主要负责以下核心功能:
26
+
27
+ ### 核心功能
28
+
29
+ 1. **消息格式转换** - 将 SheetNext 提供的通用消息结构转换为目标大模型(如 Claude、GPT 等)所需的标准格式
30
+ 2. **流式数据处理** - 实现 AI 响应的流式接收与转发,提升用户交互体验
31
+ 3. **安全隔离** - 在服务端隐藏真实的 API Key,避免密钥泄露风险
32
+ 4. **使用统计** - 企业可在中转层统计 Token 消耗、请求次数等关键数据
33
+ 5. **多模态支持** - 处理文本和图片(Base64)输入,要求对接的大模型必须支持视觉能力
34
+
35
+ ### 关键要点
36
+
37
+ - ✅ 中转接口编写完毕后,将请求地址在前端编辑器初始化时通过 `AI_URL` 填入
38
+ - ✅ 编辑器在对话时会调用此 API,将整个对话历史以 `messages` 数组传入后端
39
+ - ✅ 后端根据 `messages` 对大模型进行分发、格式转换和流式处理
40
+ - ✅ 中转层可统计使用数据,保障 API Key 安全
41
+
42
+ ---
43
+
44
+ ## 核心架构
45
+
46
+ ```
47
+ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐
48
+ │ │ │ │ │ │
49
+ │ SheetNext │────────▶│ 中转服务器 │────────▶│各种大模型API│
50
+ │ 前端 │ HTTP │ (您的服务器) │ HTTPS │ (Claude等) │
51
+ │ │◀────────│ │◀────────│ │
52
+ └─────────────┘ SSE流 └──────────────┘ Stream └─────────────┘
53
+
54
+
55
+ ┌──────────────┐
56
+ │ 使用统计/日志 │
57
+ └──────────────┘
58
+ ```
59
+
60
+ ### 工作流程
61
+
62
+ 1. **前端请求** - SheetNext 发送包含 `messages` 数组的 POST 请求到中转服务器
63
+ 2. **格式转换** - 中转服务器将通用格式转换为目标大模型的专用格式
64
+ 3. **API 调用** - 使用服务端存储的 API Key 调用大模型 API
65
+ 4. **流式响应** - 接收大模型的流式响应,转换后通过 SSE (Server-Sent Events) 返回前端
66
+ 5. **数据统计** - 在中转层记录使用情况(可选)
67
+
68
+ ---
69
+
70
+ ## 完整示例
71
+
72
+ 以下是 **Node.js + Claude API** 的完整实现示例:
73
+
74
+ ### 安装依赖
75
+
76
+ ```bash
77
+ npm install @anthropic-ai/sdk
78
+ ```
79
+
80
+ ### 完整代码
81
+
82
+ ```javascript
83
+ /**
84
+ * SheetNext AI 中转服务器 - Claude 流式接收/发送示例
85
+ * 功能: 消息格式转换、流式响应、安全中转
86
+ */
87
+
88
+ const http = require('http');
89
+ const Anthropic = require('@anthropic-ai/sdk');
90
+
91
+ // ======= 配置 =======
92
+ const CONFIG = {
93
+ apiKey: 'your-api-key-here', // 您的 API Key
94
+ model: 'claude-sonnet-4-5-20250929', // 模型名称
95
+ baseURL: 'https://api.anthropic.com' // API 基础地址
96
+ };
97
+
98
+ const anthropic = new Anthropic({
99
+ apiKey: CONFIG.apiKey,
100
+ baseURL: CONFIG.baseURL
101
+ });
102
+
103
+ // ======= 消息转换器 =======
104
+ const convertToClaudeMessages = (messages) => {
105
+ const system = [], userMessages = [];
106
+
107
+ for (const msg of messages) {
108
+ if (msg.role === 'system') {
109
+ // 处理系统消息
110
+ const parts = Array.isArray(msg.content)
111
+ ? msg.content
112
+ : [{ type: 'text', text: msg.content }];
113
+
114
+ parts.forEach(part => {
115
+ if (part.type === 'text') {
116
+ system.push({
117
+ type: 'text',
118
+ text: part.text,
119
+ ...(part.cacheable && { cache_control: { type: 'ephemeral' } })
120
+ });
121
+ } else if (part.type === 'image_url') {
122
+ // 转换 Base64 图片
123
+ const [, mediaType, base64Data] =
124
+ part.image_url.url.match(/data:(.*?);base64,(.*)/) || [];
125
+ system.push({
126
+ type: 'image',
127
+ source: {
128
+ type: 'base64',
129
+ media_type: mediaType || 'image/jpeg',
130
+ data: base64Data
131
+ }
132
+ });
133
+ }
134
+ });
135
+ } else {
136
+ // 处理用户/助手消息
137
+ const content = typeof msg.content === 'string'
138
+ ? msg.content
139
+ : msg.content.map(part => {
140
+ if (part.type === 'image_url') {
141
+ const [, mediaType, base64Data] =
142
+ part.image_url.url.match(/data:(.*?);base64,(.*)/) || [];
143
+ return {
144
+ type: 'image',
145
+ source: {
146
+ type: 'base64',
147
+ media_type: mediaType || 'image/jpeg',
148
+ data: base64Data
149
+ }
150
+ };
151
+ }
152
+ return part;
153
+ });
154
+ userMessages.push({ role: msg.role, content });
155
+ }
156
+ }
157
+
158
+ return { system, messages: userMessages };
159
+ };
160
+
161
+ // ======= Claude SDK 调用 =======
162
+ async function callClaudeSDK(messages, onChunk) {
163
+ const { system, messages: claudeMessages } = convertToClaudeMessages(messages);
164
+
165
+ const stream = await anthropic.messages.create({
166
+ model: CONFIG.model,
167
+ max_tokens: 8192,
168
+ system,
169
+ messages: claudeMessages,
170
+ stream: true,
171
+ thinking: { type: "enabled", budget_tokens: 2000 }
172
+ });
173
+
174
+ for await (const event of stream) {
175
+ if (event.type === 'content_block_delta') {
176
+ const { delta } = event;
177
+ // 思考过程
178
+ if (delta?.type === 'thinking_delta' && delta.thinking) {
179
+ onChunk({ type: 'think', delta: delta.thinking });
180
+ }
181
+ // 文本响应
182
+ else if (delta?.type === 'text_delta') {
183
+ onChunk({ type: 'text', delta: delta.text });
184
+ }
185
+ }
186
+ }
187
+ }
188
+
189
+ // ======= HTTP 处理 =======
190
+ async function handleChat(messages, res) {
191
+ // 设置 SSE 响应头
192
+ res.writeHead(200, {
193
+ 'Content-Type': 'text/event-stream',
194
+ 'Cache-Control': 'no-cache',
195
+ 'Connection': 'keep-alive',
196
+ 'Access-Control-Allow-Origin': '*'
197
+ });
198
+
199
+ let ended = false;
200
+ const write = (data) => !ended && !res.writableEnded && res.write(data);
201
+ const onChunk = (chunk) => write(`data: ${JSON.stringify(chunk)}\\n\\n`);
202
+
203
+ try {
204
+ await callClaudeSDK(messages, onChunk);
205
+ write(`data: [DONE]\\n\\n`);
206
+ } catch (error) {
207
+ console.error('AI 调用错误:', error);
208
+ write(`data: ${JSON.stringify({ error: error.message })}\\n\\n`);
209
+ } finally {
210
+ ended = true;
211
+ res.end();
212
+ }
213
+ }
214
+
215
+ // ======= HTTP 服务器 =======
216
+ http.createServer(async (req, res) => {
217
+ const corsHeaders = {
218
+ 'Access-Control-Allow-Origin': '*',
219
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
220
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization'
221
+ };
222
+
223
+ // 处理 CORS 预检请求
224
+ if (req.method === 'OPTIONS') {
225
+ res.writeHead(200, corsHeaders);
226
+ return res.end();
227
+ }
228
+
229
+ // 处理 AI 请求
230
+ if (req.url === '/sheetnextAI' && req.method === 'POST') {
231
+ let body = '';
232
+ req.on('data', chunk => body += chunk);
233
+ req.on('end', async () => {
234
+ try {
235
+ const { messages } = JSON.parse(body);
236
+ if (!Array.isArray(messages)) {
237
+ throw new Error('Invalid messages format');
238
+ }
239
+ await handleChat(messages, res);
240
+ } catch (error) {
241
+ res.writeHead(400, { 'Content-Type': 'application/json' });
242
+ res.end(JSON.stringify({ error: error.message }));
243
+ }
244
+ });
245
+ } else {
246
+ res.writeHead(404);
247
+ res.end('Not Found');
248
+ }
249
+ }).listen(3000, () => {
250
+ console.log('🚀 SheetNext AI 中转服务器运行在 http://localhost:3000');
251
+ console.log('📡 AI 端点: http://localhost:3000/sheetnextAI');
252
+ });
253
+ ```
254
+
255
+ ### 前端配置
256
+
257
+ ```javascript
258
+ const SN = new SheetNext(document.querySelector('#container'), {
259
+ AI_URL: "http://localhost:3000/sheetnextAI",
260
+ AI_TOKEN: "your-optional-token" // 可选,用于鉴权
261
+ });
262
+ ```
263
+
264
+ ---
265
+
266
+ ## 消息格式
267
+
268
+ ### 请求格式
269
+
270
+ SheetNext 发送的请求体格式:
271
+
272
+ ```json
273
+ {
274
+ "messages": [
275
+ {
276
+ "role": "system",
277
+ "content": "你是一个电子表格助手..."
278
+ },
279
+ {
280
+ "role": "user",
281
+ "content": "帮我分析销售数据"
282
+ },
283
+ {
284
+ "role": "assistant",
285
+ "content": "好的,我来帮您分析..."
286
+ },
287
+ {
288
+ "role": "user",
289
+ "content": [
290
+ {
291
+ "type": "text",
292
+ "text": "这是表格截图"
293
+ },
294
+ {
295
+ "type": "image_url",
296
+ "image_url": {
297
+ "url": "data:image/png;base64,iVBORw0KGgoAAAANS..."
298
+ }
299
+ }
300
+ ]
301
+ }
302
+ ]
303
+ }
304
+ ```
305
+
306
+ ### 响应格式
307
+
308
+ 您的服务器应该返回 SSE 流:
309
+
310
+ ```
311
+ data: {"type":"text","delta":"我"}
312
+
313
+ data: {"type":"text","delta":"来"}
314
+
315
+ data: {"type":"text","delta":"帮"}
316
+
317
+ data: [DONE]
318
+ ```
319
+
320
+ **响应对象格式:**
321
+
322
+ ```typescript
323
+ {
324
+ type: "text" | "think" | "error", // 消息类型
325
+ delta: string // 增量文本
326
+ }
327
+ ```
328
+
329
+ ---
330
+
331
+ ## 其他大模型对接
332
+
333
+ ### OpenAI GPT
334
+
335
+ ```javascript
336
+ const OpenAI = require('openai');
337
+
338
+ const openai = new OpenAI({
339
+ apiKey: 'your-openai-key'
340
+ });
341
+
342
+ async function callOpenAI(messages, onChunk) {
343
+ const stream = await openai.chat.completions.create({
344
+ model: 'gpt-4',
345
+ messages: messages, // OpenAI 格式已兼容
346
+ stream: true
347
+ });
348
+
349
+ for await (const chunk of stream) {
350
+ const delta = chunk.choices[0]?.delta?.content;
351
+ if (delta) {
352
+ onChunk({ type: 'text', delta });
353
+ }
354
+ }
355
+ }
356
+ ```
357
+
358
+ ### 其他模型
359
+
360
+ 只需要:
361
+ 1. 将 `messages` 转换为目标 API 的格式
362
+ 2. 调用 API 获取流式响应
363
+ 3. 将响应转换为 `{type, delta}` 格式
364
+ 4. 通过 `onChunk` 回调返回
365
+
366
+ ---
367
+
368
+ ### 安全建议
369
+
370
+ 1. **使用环境变量存储 API Key**
371
+ ```javascript
372
+ const CONFIG = {
373
+ apiKey: process.env.ANTHROPIC_API_KEY,
374
+ // ...
375
+ };
376
+ ```
377
+
378
+ 2. **添加请求鉴权**
379
+ ```javascript
380
+ const authToken = req.headers.authorization;
381
+ if (authToken !== process.env.AUTH_TOKEN) {
382
+ res.writeHead(401);
383
+ return res.end('Unauthorized');
384
+ }
385
+ ```
386
+
387
+ 3. **限制请求频率**
388
+ ```javascript
389
+ const rateLimit = require('express-rate-limit');
390
+ // 使用限流中间件
391
+ ```
392
+
393
+ 4. **记录使用日志**
394
+ ```javascript
395
+ console.log(`[${new Date().toISOString()}] Token使用: ${usage.total_tokens}`);
396
+ ```
397
+
398
+ ---
399
+
400
+ ## 相关文档
401
+
402
+ - [← 返回 README](./README.md) - 快速开始和特点介绍
403
+ - [← API 文档](./DOCS.md) - 详细的类、方法和属性说明
package/DOCS.md ADDED
@@ -0,0 +1,708 @@
1
+ # SheetNext API 文档
2
+
3
+ > 详细的类、方法和属性说明
4
+
5
+ ## 文档导航
6
+
7
+ - [← 返回 README](./README.md) - 快速开始和特点介绍
8
+ - [→ AI 配置指南](./AGENT.md) - AI 中转服务搭建
9
+
10
+ ---
11
+
12
+ ## 目录
13
+
14
+ - [SheetNext Class](#SheetNext) - 工作簿管理:实例化编辑器、添加/删除/切换工作表、导入/导出 Excel、多实例支持等
15
+ - [Sheet Class](#Sheet) - 工作表管理:行列管理、合并单元格、区域遍历、排序、批量插入数据、图形对象管理等
16
+ - [Cell Class](#Cell) - 单元格管理:读写值/公式、样式设置、数字格式、超链接、数据验证等
17
+ - [Row Class](#Row) - 行管理:行高、批量行样式、获取行内单元格、显示/隐藏等
18
+ - [Col Class](#Col) - 列管理:列宽、批量列样式、获取列内单元格、显示/隐藏等
19
+ - [Drawing Class](#Drawing) - 图形管理:图表、图形、图片的增删改查、设置位置和尺寸、调整图层顺序等
20
+ - [Utils Class](#Utils) - 坐标转换:单元格字符串与数字坐标互转、列字母与数字互转、范围格式转换等
21
+ - [UndoRedo Class](#UndoRedo) - 历史管理:撤销/重做操作
22
+
23
+ ---
24
+
25
+ ## SheetNext
26
+
27
+ SheetNext 是主入口类,管理整个电子表格应用。
28
+
29
+ ### 构造函数
30
+
31
+ ```javascript
32
+ const SN = new SheetNext(dom: HTMLElement, options?: object)
33
+ // 下面所有示例使用此实例进行操作
34
+ ```
35
+
36
+ **参数:**
37
+ - `dom`: 容器 DOM 元素(必需)
38
+ - `options`: 可选配置对象(详见 [README 初始化配置](./README.md#初始化配置))
39
+
40
+ ### 核心属性
41
+
42
+ | 属性 | 类型 | 说明 |
43
+ |------|------|------|
44
+ | `activeSheet` | `Sheet` | 当前激活的工作表(可读写) |
45
+ | `sheets` | `Sheet[]` | 所有工作表数组 |
46
+ | `sheetNames` | `string[]` | 工作表名称列表(只读) |
47
+ | `containerDom` | `HTMLElement` | 编辑器容器元素 |
48
+ | `namespace` | `string` | 实例的全局命名空间(如 `SN_0`) |
49
+ | `locked` | `boolean` | 是否锁定工作表切换 |
50
+
51
+ ### 工作表管理方法
52
+
53
+ #### `addSheet(name?: string): Sheet`
54
+ 添加新工作表,名称可选(自动生成 Sheet1、Sheet2 等)。
55
+
56
+ ```javascript
57
+ const newSheet = SN.addSheet("销售数据");
58
+ const autoSheet = SN.addSheet(); // 自动命名
59
+ ```
60
+
61
+ **规则:** 名称不重复、长度1-31字符、不含特殊符号 `: / \ * ? [ ]`
62
+
63
+ #### `delSheet(name: string): void`
64
+ 删除指定工作表(至少保留一个可见工作表)。
65
+
66
+ ```javascript
67
+ SN.delSheet("Sheet2");
68
+ ```
69
+
70
+ #### `getSheetByName(name: string): Sheet | null`
71
+ 根据名称获取工作表。
72
+
73
+ ```javascript
74
+ const sheet = SN.getSheetByName("Sheet1");
75
+ ```
76
+
77
+ #### `getVisibleSheetByIndex(index: number): Sheet`
78
+ 获取可见工作表(按索引,隐藏工作表不计入)。
79
+
80
+ ```javascript
81
+ const firstSheet = SN.getVisibleSheetByIndex(0);
82
+ ```
83
+
84
+ ### 文件操作
85
+
86
+ #### `import(file: File): Promise<void>`
87
+ 导入 Excel 文件(.xlsx 格式)。
88
+
89
+ ```javascript
90
+ fileInput.addEventListener('change', (e) => {
91
+ SN.import(e.target.files[0]);
92
+ });
93
+ ```
94
+
95
+ #### `export(type: string): void`
96
+ 导出电子表格(目前支持 `"XLSX"`)。
97
+
98
+ ```javascript
99
+ SN.export('XLSX');
100
+ ```
101
+
102
+ ### 其他方法
103
+
104
+ #### `r(): void`
105
+ 手动触发画布重新渲染(批量修改后使用)。
106
+
107
+ ```javascript
108
+ // 批量修改后刷新
109
+ for (let i = 0; i < 100; i++) {
110
+ sheet.getCell(i, 0).editVal = i;
111
+ }
112
+ SN.r();
113
+ ```
114
+
115
+ #### `activateLicense(licenseKey: string)`
116
+ 激活 License 授权。
117
+
118
+ ```javascript
119
+ SN.activateLicense("your-license-key");
120
+ ```
121
+
122
+ #### `getLicenseInfo()`
123
+ 获取 License 信息。
124
+
125
+ ```javascript
126
+ const info = SN.getLicenseInfo();
127
+ ```
128
+
129
+ ### 多实例支持
130
+
131
+ SheetNext 支持同一页面创建多个独立实例:
132
+
133
+ ```javascript
134
+ const editor1 = new SheetNext(document.querySelector('#container1'));
135
+ const editor2 = new SheetNext(document.querySelector('#container2'));
136
+
137
+ console.log(editor1.namespace); // "SN_0"
138
+ console.log(editor2.namespace); // "SN_1"
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Sheet
144
+
145
+ Sheet 类代表一个工作表。
146
+
147
+ ### 属性
148
+
149
+ | 属性 | 类型 | 说明 |
150
+ |------|------|------|
151
+ | `name` | `string` | 工作表名称 |
152
+ | `hidden` | `boolean` | 是否隐藏 |
153
+ | `merges` | `RangeNum[]` | 合并单元格区域列表 |
154
+ | `defaultColWidth` | `number` | 默认列宽(像素) |
155
+ | `defaultRowHeight` | `number` | 默认行高(像素) |
156
+ | `showGridLines` | `boolean` | 是否显示网格线 |
157
+ | `showRowColHeaders` | `boolean` | 是否显示行列标题 |
158
+ | `activeCell` | `CellNum` | 当前活动单元格 |
159
+ | `activeAreas` | `RangeNum[]` | 当前选中区域 |
160
+ | `rows` | `Row[]` | 所有行 |
161
+ | `cols` | `Col[]` | 所有列 |
162
+ | `xSplit` | `number` | 冻结列数 |
163
+ | `ySplit` | `number` | 冻结行数 |
164
+ | `drawings` | `Drawing[]` | 图形对象列表(图表、图片等) |
165
+
166
+ ### 基础方法
167
+
168
+ #### `getRow(r: number): Row`
169
+ 获取指定行对象。
170
+
171
+ ```javascript
172
+ const row = sheet.getRow(0); // 获取第一行
173
+ ```
174
+
175
+ #### `getCol(c: number): Col`
176
+ 获取指定列对象。
177
+
178
+ ```javascript
179
+ const col = sheet.getCol(0); // 获取第一列(A列)
180
+ ```
181
+
182
+ #### `getCell(r: number, c: number): Cell`
183
+ 获取指定单元格。
184
+
185
+ ```javascript
186
+ const cell = sheet.getCell(0, 0); // 获取 A1 单元格
187
+ ```
188
+
189
+ #### `getCellByStr(cellStr: string): Cell`
190
+ 通过字符串引用获取单元格。
191
+
192
+ ```javascript
193
+ const cell = sheet.getCellByStr("A1");
194
+ ```
195
+
196
+ #### `rangeStrToNum(rangeStr: string): RangeNum`
197
+ 将字符串范围转换为数字范围对象。
198
+
199
+ ```javascript
200
+ const rangeNum = sheet.rangeStrToNum("A1:C3");
201
+ // 返回: {s:{r:0,c:0}, e:{r:2,c:2}}
202
+ ```
203
+
204
+ ### 区域遍历
205
+
206
+ #### `eachArea(rangeRef: RangeRef, callback: (r, c, index) => void, reverse?: boolean): void`
207
+ 遍历指定区域的每个单元格。
208
+
209
+ ```javascript
210
+ // 正向遍历
211
+ sheet.eachArea("A1:C3", (r, c, index) => {
212
+ const cell = sheet.getCell(r, c);
213
+ console.log(cell.showVal);
214
+ });
215
+
216
+ // 反向遍历(用于删除操作,避免索引混乱)
217
+ sheet.eachArea("A:A", (r, c) => {
218
+ if (sheet.getCell(r, c).showVal === "") {
219
+ sheet.delRows(r, 1);
220
+ }
221
+ }, true);
222
+ ```
223
+
224
+ ### 行列操作
225
+
226
+ #### `showAllHidRows(): void`
227
+ 显示所有隐藏的行。
228
+
229
+ ```javascript
230
+ sheet.showAllHidRows();
231
+ ```
232
+
233
+ #### `showAllHidCols(): void`
234
+ 显示所有隐藏的列。
235
+
236
+ ```javascript
237
+ sheet.showAllHidCols();
238
+ ```
239
+
240
+ #### `addRows(startR: number, num: number): void`
241
+ 在指定位置插入行。
242
+
243
+ ```javascript
244
+ sheet.addRows(5, 3); // 在第 5 行位置插入 3 行
245
+ ```
246
+
247
+ **注意**:多个同时调用时,应反向遍历以避免索引混乱。
248
+
249
+ #### `addCols(startC: number, num: number): void`
250
+ 在指定位置插入列。
251
+
252
+ ```javascript
253
+ sheet.addCols(2, 2); // 在第 2 列位置插入 2 列
254
+ ```
255
+
256
+ #### `delRows(startR: number, num: number): void`
257
+ 删除指定行。
258
+
259
+ ```javascript
260
+ sheet.delRows(5, 3); // 删除从第 5 行开始的 3 行
261
+ ```
262
+
263
+ #### `delCols(startC: number, num: number): void`
264
+ 删除指定列。
265
+
266
+ ```javascript
267
+ sheet.delCols(2, 2); // 删除从第 2 列开始的 2 列
268
+ ```
269
+
270
+ ### 合并单元格
271
+
272
+ #### `mergeCells(rangeRef: RangeRef): void`
273
+ 合并指定区域的单元格。
274
+
275
+ ```javascript
276
+ sheet.mergeCells("A1:C3");
277
+ // 或
278
+ sheet.mergeCells({s:{r:0,c:0}, e:{r:2,c:2}});
279
+ ```
280
+
281
+ #### `unMergeCells(cellRef: CellRef): void`
282
+ 取消合并(传入区域内任意单元格引用)。
283
+
284
+ ```javascript
285
+ sheet.unMergeCells("A1"); // 取消包含 A1 的合并区域
286
+ ```
287
+
288
+ ### 排序
289
+
290
+ #### `rangeSort(sortItems: SortItem[], range?: RangeRef): void`
291
+ 对指定区域进行排序。
292
+
293
+ **SortItem 接口:**
294
+
295
+ ```typescript
296
+ interface SortItem {
297
+ type: "column" | "row" | "custom";
298
+ order?: "asc" | "desc" | "value"; // type="custom" 时省略
299
+ index: string; // 列/行标签,行从 1 开始,列从 A 开始
300
+ sortData?: any[]; // order="value" 时使用,基于此数据排序
301
+ cb?: (rowsArray: Cell[][], sortIndex: number) => Cell[][]; // type="custom" 时使用
302
+ }
303
+ ```
304
+
305
+ **示例:按自定义顺序排序**
306
+
307
+ ```javascript
308
+ const sheet = SN.activeSheet;
309
+ // 除标题外,按 C 列字母顺序排序:A V U T
310
+ sheet.rangeSort(
311
+ [{
312
+ type: 'column',
313
+ order: 'value',
314
+ index: 'C',
315
+ sortData: ["A", "V", "U", "T"]
316
+ }],
317
+ {s:{c:0,r:1}, e:{c:sheet.colCount, r:sheet.rowCount}}
318
+ );
319
+ ```
320
+
321
+ ### 批量插入数据
322
+
323
+ #### `insertTable(data: (ICellConfig | string | number)[][], startCell: CellRef, globalConfig?: object): RangeNum`
324
+ 在指定位置插入表格数据。
325
+
326
+ **ICellConfig 接口:**
327
+
328
+ ```typescript
329
+ interface ICellConfig {
330
+ v?: string; // 单元格值
331
+ w?: number; // 列宽(像素),仅在首行设置
332
+ h?: number; // 行高(像素),仅在首列设置
333
+ b?: boolean; // 是否粗体
334
+ s?: number; // 字体大小
335
+ fg?: string; // 背景色
336
+ a?: 'l' | 'r' | 'c'; // 对齐方式(left/right/center)
337
+ c?: string; // 文本颜色
338
+ mr?: number; // 向右合并单元格数(不包括自身)
339
+ mb?: number; // 向下合并单元格数(不包括自身)
340
+ }
341
+ ```
342
+
343
+ **globalConfig 参数:**
344
+ - `a`: 对齐方式
345
+ - `border`: 是否显示边框
346
+ - `w`: 默认列宽
347
+ - `h`: 默认行高
348
+ - `fg`: 背景色
349
+ - `c`: 文本颜色
350
+
351
+ **示例:生成会议记录模板**
352
+
353
+ ```javascript
354
+ const t = [
355
+ [
356
+ { v: "Meeting Minutes", s: 16, mr: 3, fg: "#eee", h: 45, b: true },
357
+ { w: 160 }, "", { w: 160 }
358
+ ],
359
+ ["Time", "", "Location", ""],
360
+ ["Host", "", "Recorder", ""],
361
+ ["Expected", "", "Present", ""],
362
+ ["Absent Members", { mr: 2 }, "", ""],
363
+ ["Topic", { mr: 2 }, "", ""],
364
+ [{ v: "Content", h: 280 }, { mr: 2 }, "", ""],
365
+ [{ v: "Remarks", h: 80 }, { mr: 2 }, "", ""]
366
+ ]; // 必须是矩形矩阵
367
+
368
+ SN.activeSheet.insertTable(t, "A1", {
369
+ border: true,
370
+ a: "c",
371
+ h: 35,
372
+ w: 140
373
+ });
374
+ ```
375
+
376
+ **注意**:
377
+ - 对于合并单元格(`mr`/`mb`),需要添加相同数量的空字符串占位符,保持二维数组的矩形结构。
378
+ - 例如:`{ mr: 2 }, "", ""`
379
+
380
+ ### 图形对象
381
+
382
+ #### `addDrawing(config: object): Drawing`
383
+ 添加图形对象(图表、图片等)。
384
+
385
+ **示例:添加图表**
386
+
387
+ ```javascript
388
+ SN.activeSheet.addDrawing({
389
+ type: 'chart',
390
+ startCell: 'B2',
391
+ option: {
392
+ title: { text: '销售趋势图' },
393
+ legend: {
394
+ data: ['销量'] // 或使用引用: `${sheet.name}!B3`
395
+ },
396
+ xAxis: {
397
+ type: 'category',
398
+ data: ['一月', '二月', '三月'] // 或引用: `${sheet.name}!C2:E2`
399
+ },
400
+ yAxis: { type: 'value' },
401
+ series: [
402
+ {
403
+ name: '销量',
404
+ type: 'line',
405
+ data: [820, 932, 901] // 或引用: `${sheet.name}!C3:E3`
406
+ }
407
+ ]
408
+ }
409
+ });
410
+ ```
411
+
412
+ #### `getDrawingsByCell(cellRef: CellRef): Drawing[]`
413
+ 获取指定单元格位置的所有图形对象。
414
+
415
+ ```javascript
416
+ const drawings = sheet.getDrawingsByCell("B2");
417
+ ```
418
+
419
+ #### `removeDrawing(id: string): void`
420
+ 删除指定 ID 的图形对象。
421
+
422
+ ```javascript
423
+ sheet.removeDrawing("drawing-id");
424
+ ```
425
+
426
+ ---
427
+
428
+ ## Cell
429
+
430
+ Cell 类代表单个单元格。
431
+
432
+ ### 属性
433
+
434
+ | 属性 | 类型 | 说明 |
435
+ |------|------|------|
436
+ | `isMerged` | `boolean` | 是否为合并单元格 |
437
+ | `master` | `CellNum \| null` | 如果是合并单元格,指向主单元格 |
438
+ | `hyperlink` | `object` | 超链接:`{target, location, tooltip}` |
439
+ | `dataValidation` | `object` | 数据验证规则 |
440
+ | `editVal` | `string` | 编辑值或公式(可写) |
441
+ | `calcVal` | `any` | 计算值(只读) |
442
+ | `showVal` | `string` | 显示值(只读) |
443
+ | `numFmt` | `string` | 数字格式 |
444
+ | `font` | `object` | 字体样式 |
445
+ | `alignment` | `object` | 对齐方式 |
446
+ | `border` | `object` | 边框样式 |
447
+ | `fill` | `object` | 填充样式 |
448
+
449
+ ### dataValidation 对象结构
450
+
451
+ ```typescript
452
+ {
453
+ type: string; // 验证类型
454
+ operator: string; // 操作符
455
+ allowBlank: boolean; // 是否允许空白
456
+ formula1: any; // 公式1(type=list 时为数组)
457
+ formula2: any; // 公式2
458
+ showInputMessage: boolean; // 是否显示输入提示
459
+ promptTitle: string; // 提示标题
460
+ prompt: string; // 提示内容
461
+ showErrorMessage: boolean; // 是否显示错误提示
462
+ errorTitle: string; // 错误标题
463
+ error: string; // 错误内容
464
+ errorStyle: string; // 错误样式
465
+ showDropDown: boolean; // 是否显示下拉框
466
+ }
467
+ ```
468
+
469
+ ### 示例
470
+
471
+ ```javascript
472
+ const cell = sheet.getCell(0, 0);
473
+
474
+ // 设置单元格值
475
+ cell.editVal = "Hello World";
476
+
477
+ // 设置公式
478
+ cell.editVal = "=SUM(A1:A10)";
479
+
480
+ // 读取显示值
481
+ console.log(cell.showVal);
482
+
483
+ // 设置样式
484
+ cell.fill = { fgColor: '#FFFF00' };
485
+ cell.font = { bold: true, size: 14 };
486
+ ```
487
+
488
+ ---
489
+
490
+ ## Row
491
+
492
+ Row 类代表行,提供行级别的属性和操作。
493
+
494
+ ### 属性
495
+
496
+ | 属性 | 类型 | 说明 |
497
+ |------|------|------|
498
+ | `cells` | `Cell[]` | 该行的所有单元格 |
499
+ | `height` | `number` | 行高(像素) |
500
+ | `hidden` | `boolean` | 是否隐藏 |
501
+ | `rIndex` | `number` | 行索引 |
502
+ | `numFmt` | `string` | 数字格式 |
503
+ | `font` | `object` | 字体样式 |
504
+ | `alignment` | `object` | 对齐方式 |
505
+ | `border` | `object` | 边框样式 |
506
+ | `fill` | `object` | 填充样式 |
507
+
508
+ ### 方法
509
+
510
+ #### `getCell(c: number): Cell`
511
+ 获取该行的指定列单元格。
512
+
513
+ ```javascript
514
+ const row = sheet.getRow(0); // 获取第一行
515
+ const cell = row.getCell(0); // 获取该行第一列的单元格
516
+ ```
517
+
518
+ ### 示例
519
+
520
+ ```javascript
521
+ const row = sheet.getRow(5);
522
+
523
+ // 设置行高
524
+ row.height = 30;
525
+
526
+ // 隐藏行
527
+ row.hidden = true;
528
+
529
+ // 设置行样式
530
+ row.fill = { fgColor: '#F0F0F0' };
531
+ row.font = { bold: true };
532
+ ```
533
+
534
+ ---
535
+
536
+ ## Col
537
+
538
+ Col 类代表列,提供列级别的属性和操作。
539
+
540
+ ### 属性
541
+
542
+ | 属性 | 类型 | 说明 |
543
+ |------|------|------|
544
+ | `cells` | `Cell[]` | 该列的所有单元格 |
545
+ | `hidden` | `boolean` | 是否隐藏 |
546
+ | `width` | `number` | 列宽(像素) |
547
+ | `cIndex` | `number` | 列索引 |
548
+ | `numFmt` | `string` | 数字格式 |
549
+ | `font` | `object` | 字体样式 |
550
+ | `alignment` | `object` | 对齐方式 |
551
+ | `border` | `object` | 边框样式 |
552
+ | `fill` | `object` | 填充样式 |
553
+
554
+ ### 方法
555
+
556
+ #### `getCell(r: number): Cell`
557
+ 获取该列的指定行单元格。
558
+
559
+ ```javascript
560
+ const col = sheet.getCol(0); // 获取第一列(A列)
561
+ const cell = col.getCell(0); // 获取该列第一行的单元格
562
+ ```
563
+
564
+ ### 示例
565
+
566
+ ```javascript
567
+ const col = sheet.getCol(2); // 获取 C 列
568
+
569
+ // 设置列宽
570
+ col.width = 120;
571
+
572
+ // 隐藏列
573
+ col.hidden = true;
574
+
575
+ // 设置列样式
576
+ col.fill = { fgColor: '#E0E0E0' };
577
+ col.alignment = { horizontal: 'center' };
578
+ ```
579
+
580
+ ---
581
+
582
+ ## Drawing
583
+
584
+ Drawing 类代表图表、图片、连接线或形状等图形对象。
585
+
586
+ ### 属性
587
+
588
+ | 属性 | 类型 | 说明 |
589
+ |------|------|------|
590
+ | `id` | `string` | 唯一标识符 |
591
+ | `type` | `string` | 类型:`chart`、`image`、`connector`、`shape` |
592
+ | `startCell` | `CellNum` | 起始单元格位置 |
593
+ | `offsetX` | `number` | X 轴偏移(默认 0) |
594
+ | `offsetY` | `number` | Y 轴偏移(默认 0) |
595
+ | `width` | `number` | 宽度(默认 480) |
596
+ | `height` | `number` | 高度(默认 288) |
597
+ | `option` | `object` | 图表配置(type=chart 时,与 ECharts 配置相同) |
598
+ | `imageBase64` | `string` | Base64 图片数据(type=image 时) |
599
+ | `updRender` | `boolean` | 是否更新渲染 |
600
+
601
+ ### 方法
602
+
603
+ #### `updIndex(direction: string): void`
604
+ 更新图层顺序。
605
+
606
+ 参数值:`"up"` | `"down"` | `"top"` | `"bottom"`
607
+
608
+ ```javascript
609
+ drawing.updIndex("top"); // 移到最上层
610
+ ```
611
+
612
+ ---
613
+
614
+ ## Utils
615
+
616
+ Utils 类提供坐标转换等实用方法。
617
+
618
+ ### 方法
619
+
620
+ #### `numToChar(num: number): string`
621
+ 数字转字母列标。
622
+
623
+ ```javascript
624
+ SN.Utils.numToChar(0); // "A"
625
+ SN.Utils.numToChar(25); // "Z"
626
+ SN.Utils.numToChar(26); // "AA"
627
+ ```
628
+
629
+ #### `charToNum(char: string): number`
630
+ 字母列标转数字。
631
+
632
+ ```javascript
633
+ SN.Utils.charToNum("A"); // 0
634
+ SN.Utils.charToNum("Z"); // 25
635
+ SN.Utils.charToNum("AA"); // 26
636
+ ```
637
+
638
+ #### `rangeNumToStr(rangeNum: RangeNum): string`
639
+ 范围对象转字符串。
640
+
641
+ ```javascript
642
+ SN.Utils.rangeNumToStr({s:{r:0,c:0}, e:{r:2,c:2}}); // "A1:C3"
643
+ ```
644
+
645
+ #### `cellStrToNum(cellStr: string): CellNum`
646
+ 单元格字符串转数字对象。
647
+
648
+ ```javascript
649
+ SN.Utils.cellStrToNum("A1"); // {r:0, c:0}
650
+ ```
651
+
652
+ #### `cellNumToStr(cellNum: CellNum): string`
653
+ 单元格数字对象转字符串。
654
+
655
+ ```javascript
656
+ SN.Utils.cellNumToStr({r:0, c:0}); // "A1"
657
+ ```
658
+
659
+ ---
660
+
661
+ ## UndoRedoClass
662
+
663
+ 管理操作历史,支持撤销和重做功能。
664
+
665
+ ### 方法
666
+
667
+ #### `undo(): void`
668
+ 撤销上一步操作。
669
+
670
+ ```javascript
671
+ SN.UndoRedo.undo();
672
+ ```
673
+
674
+ #### `redo(): void`
675
+ 重做上一步操作。
676
+
677
+ ```javascript
678
+ SN.UndoRedo.redo();
679
+ ```
680
+
681
+ ### 示例
682
+
683
+ ```javascript
684
+ // 执行一些操作
685
+ sheet.getCell(0, 0).editVal = "Hello";
686
+ sheet.mergeCells("A1:B1");
687
+
688
+ // 撤销合并操作
689
+ SN.UndoRedo.undo();
690
+
691
+ // 撤销编辑操作
692
+ SN.UndoRedo.undo();
693
+
694
+ // 重做编辑操作
695
+ SN.UndoRedo.redo();
696
+ ```
697
+
698
+ **注意**:
699
+ - 撤销/重做会自动记录大部分用户操作
700
+ - 通过 API 修改的内容也会被记录
701
+ - 历史记录栈有大小限制,过旧的操作会被清除
702
+
703
+ ---
704
+
705
+ ## 相关文档
706
+
707
+ - [← 返回 README](./README.md) - 快速开始和特点介绍
708
+ - [→ AI 配置指南](./AGENT.md) - AI 中转服务搭建
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # SheetNext
2
+
3
+ > 一个纯前端高性能 Excel 编辑器,数行代码可集成表格全部基础功能。且作为新一代智能表格工具,内置AI工作流,简单配置即可全自动操作表格。
4
+
5
+ ## 特点
6
+
7
+ - **电子表格功能** - 支持电子表格核心功能如:单元格编辑、样式、公式引擎、图表、排序、筛选等。
8
+ - **AI 工作流** - 内置 AI 全自动操作工作流,简单配置可以生成模板/数据/分析/公式等。
9
+ - **导入导出** - 原生支持 Excel (.xlsx) 文件的导入和导出,无需插件前端秒操作。
10
+
11
+ ## 快速开始
12
+
13
+ ### 使用 npm 安装
14
+
15
+ ```bash
16
+ npm install sheetnext
17
+ ```
18
+
19
+ ```javascript
20
+ import SheetNext from 'sheetnext';
21
+ import 'sheetnext/dist/sheetnext.css';
22
+
23
+ // 注意设置容器#SNContainer宽高
24
+ const SN = new SheetNext(document.querySelector('#SNContainer'));
25
+ ```
26
+
27
+ ### 浏览器直接引入
28
+
29
+ ```html
30
+ <!-- 引入样式 -->
31
+ <link rel="stylesheet" href="dist/sheetnext.css">
32
+
33
+ <!-- 编辑器容器 -->
34
+ <div id="SNContainer" style="width: 100vw; height: 100vh;"></div>
35
+
36
+ <!-- 引入脚本 -->
37
+ <script src="dist/sheetnext.umd.js"></script>
38
+
39
+ <!-- 初始化,注意设置宽高 -->
40
+ <script>
41
+ const SN = new SheetNext(document.querySelector('#SNContainer'));
42
+ </script>
43
+ ```
44
+
45
+ ## 初始化配置
46
+
47
+ ```javascript
48
+ const SN = new SheetNext(document.querySelector('#container'), {
49
+ AI_URL: "http://localhost:3000/sheetnextAI", // AI 中转地址
50
+ AI_TOKEN: "your-token", // 可选的认证 token
51
+ showMenuBar: true, // 显示菜单栏
52
+ showToolbar: true, // 显示工具栏
53
+ showAIChat: true, // 启用 AI 聊天
54
+ showFormulaBar: true, // 显示公式栏
55
+ showSheetTabBar: true, // 显示工作表标签栏
56
+ showAIChatWindow: false // 显示 AI 聊天窗口
57
+ });
58
+ ```
59
+
60
+ ## 文档导航
61
+
62
+ - **[完整 API 文档 (DOCS.md)](./DOCS.md)** - 详细的类、方法和属性说明
63
+ - **[AI 配置指南 (AGENT.md)](./AGENT.md)** - AI 中转服务搭建和配置
64
+
65
+ ## License
66
+
67
+ MIT
68
+
69
+ ## 相关链接
70
+
71
+ - [npm 包地址](https://www.npmjs.com/package/sheetnext)
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "sheetnext",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "A powerful spreadsheet editor library",
5
+ "homepage": "https://www.sheetnext.com",
5
6
  "main": "dist/sheetnext.umd.js",
6
7
  "module": "dist/sheetnext.es.js",
7
8
  "exports": {
@@ -12,7 +13,10 @@
12
13
  "./dist/sheetnext.css": "./dist/sheetnext.css"
13
14
  },
14
15
  "files": [
15
- "dist"
16
+ "dist",
17
+ "README.md",
18
+ "DOCS.md",
19
+ "AGENT.md"
16
20
  ],
17
21
  "scripts": {
18
22
  "dev": "vite",