sheetnext 0.1.4 → 0.1.6
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/AGENT.md +119 -192
- package/DOCS.md +0 -12
- package/README.md +32 -32
- package/dist/sheetnext.css +1 -1
- package/dist/sheetnext.es.js +9 -106680
- package/dist/sheetnext.umd.js +5 -48
- package/package.json +2 -2
package/AGENT.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# SheetNext AI
|
|
1
|
+
# SheetNext AI 中转配置指南
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 一句话总结超级简单:写一个接口将前端传入的message消息分发给你想对接的大模型,然后在前端配置好接口地址即可开始工作!
|
|
4
4
|
|
|
5
5
|
## 文档导航
|
|
6
6
|
|
|
@@ -11,12 +11,20 @@
|
|
|
11
11
|
|
|
12
12
|
## 目录
|
|
13
13
|
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
14
|
+
- [SheetNext AI 中转配置指南](#sheetnext-ai-中转配置指南)
|
|
15
|
+
- [文档导航](#文档导航)
|
|
16
|
+
- [目录](#目录)
|
|
17
|
+
- [功能说明](#功能说明)
|
|
18
|
+
- [核心功能](#核心功能)
|
|
19
|
+
- [核心架构](#核心架构)
|
|
20
|
+
- [工作流程](#工作流程)
|
|
21
|
+
- [完整示例](#完整示例)
|
|
22
|
+
- [安装依赖](#安装依赖)
|
|
23
|
+
- [完整代码](#完整代码)
|
|
24
|
+
- [配置说明](#配置说明)
|
|
25
|
+
- [消息格式](#消息格式)
|
|
26
|
+
- [请求格式](#请求格式)
|
|
27
|
+
- [响应格式](#响应格式)
|
|
20
28
|
|
|
21
29
|
---
|
|
22
30
|
|
|
@@ -30,14 +38,6 @@ AI 服务中转层是连接 SheetNext 前端与大模型 API 的桥梁,主要
|
|
|
30
38
|
2. **流式数据处理** - 实现 AI 响应的流式接收与转发,提升用户交互体验
|
|
31
39
|
3. **安全隔离** - 在服务端隐藏真实的 API Key,避免密钥泄露风险
|
|
32
40
|
4. **使用统计** - 企业可在中转层统计 Token 消耗、请求次数等关键数据
|
|
33
|
-
5. **多模态支持** - 处理文本和图片(Base64)输入,要求对接的大模型必须支持视觉能力
|
|
34
|
-
|
|
35
|
-
### 关键要点
|
|
36
|
-
|
|
37
|
-
- ✅ 中转接口编写完毕后,将请求地址在前端编辑器初始化时通过 `AI_URL` 填入
|
|
38
|
-
- ✅ 编辑器在对话时会调用此 API,将整个对话历史以 `messages` 数组传入后端
|
|
39
|
-
- ✅ 后端根据 `messages` 对大模型进行分发、格式转换和流式处理
|
|
40
|
-
- ✅ 中转层可统计使用数据,保障 API Key 安全
|
|
41
41
|
|
|
42
42
|
---
|
|
43
43
|
|
|
@@ -63,107 +63,112 @@ AI 服务中转层是连接 SheetNext 前端与大模型 API 的桥梁,主要
|
|
|
63
63
|
2. **格式转换** - 中转服务器将通用格式转换为目标大模型的专用格式
|
|
64
64
|
3. **API 调用** - 使用服务端存储的 API Key 调用大模型 API
|
|
65
65
|
4. **流式响应** - 接收大模型的流式响应,转换后通过 SSE (Server-Sent Events) 返回前端
|
|
66
|
-
5. **数据统计** - 在中转层记录使用情况(可选)
|
|
67
66
|
|
|
68
67
|
---
|
|
69
68
|
|
|
70
69
|
## 完整示例
|
|
71
70
|
|
|
72
|
-
|
|
71
|
+
通用中转完整实现示例:
|
|
73
72
|
|
|
74
73
|
### 安装依赖
|
|
75
74
|
|
|
76
75
|
```bash
|
|
77
|
-
npm install @anthropic-ai/sdk
|
|
76
|
+
npm install @anthropic-ai/sdk openai
|
|
78
77
|
```
|
|
79
78
|
|
|
80
79
|
### 完整代码
|
|
81
80
|
|
|
82
81
|
```javascript
|
|
83
82
|
/**
|
|
84
|
-
* SheetNext AI
|
|
85
|
-
*
|
|
83
|
+
* SheetNext AI & claude/openai 中转服务器示例 Node.js 版本
|
|
84
|
+
* 2025.10.17 v1.0.0
|
|
86
85
|
*/
|
|
87
86
|
|
|
88
87
|
const http = require('http');
|
|
89
88
|
const Anthropic = require('@anthropic-ai/sdk');
|
|
89
|
+
const OpenAI = require('openai');
|
|
90
90
|
|
|
91
91
|
// ======= 配置 =======
|
|
92
92
|
const CONFIG = {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
model: 'claude-sonnet-4-5-20250929', // 设置模型名称,自动判断使用 claude 还是 openai
|
|
94
|
+
claude: {
|
|
95
|
+
apiKey: 'sk-xWp4TFA81arQCudIbLRmE0h1TtmM0lQWz4Lt7lKryUhk5HhN',
|
|
96
|
+
baseURL: 'https://m5.aitoo.fun/'
|
|
97
|
+
},
|
|
98
|
+
openai: {
|
|
99
|
+
apiKey: 'sk-UWq0SWmDIEFTI5hswtg5uONjQ95ECUneWtp46Pp9Kdujg9py',
|
|
100
|
+
baseURL: 'https://m5.aitoo.fun/v1'
|
|
101
|
+
}
|
|
96
102
|
};
|
|
97
103
|
|
|
98
|
-
const anthropic = new Anthropic({
|
|
99
|
-
|
|
100
|
-
baseURL: CONFIG.baseURL
|
|
101
|
-
});
|
|
104
|
+
const anthropic = new Anthropic({ apiKey: CONFIG.claude.apiKey, baseURL: CONFIG.claude.baseURL });
|
|
105
|
+
const openai = new OpenAI({ apiKey: CONFIG.openai.apiKey, baseURL: CONFIG.openai.baseURL });
|
|
102
106
|
|
|
103
|
-
// =======
|
|
107
|
+
// ======= message默认是openai格式,claude请求时转为它适配格式 =======
|
|
104
108
|
const convertToClaudeMessages = (messages) => {
|
|
105
|
-
const system = []
|
|
109
|
+
const system = [];
|
|
110
|
+
const claudeMessages = [];
|
|
111
|
+
let isFirstSystem = true;
|
|
112
|
+
|
|
113
|
+
// 转换内容部分的辅助函数
|
|
114
|
+
const convertContent = (content) => {
|
|
115
|
+
const parts = Array.isArray(content) ? content : [{ type: 'text', text: content }];
|
|
116
|
+
return parts.map(part => {
|
|
117
|
+
if (part.type === 'text') {
|
|
118
|
+
return { type: 'text', text: part.text };
|
|
119
|
+
}
|
|
120
|
+
if (part.type === 'image_url') {
|
|
121
|
+
const [, mediaType, base64Data] = part.image_url.url.match(/data:(.*?);base64,(.*)/) || [];
|
|
122
|
+
if (base64Data) {
|
|
123
|
+
return { type: 'image', source: { type: 'base64', media_type: mediaType || 'image/jpeg', data: base64Data } };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}).filter(Boolean);
|
|
128
|
+
};
|
|
106
129
|
|
|
107
130
|
for (const msg of messages) {
|
|
108
131
|
if (msg.role === 'system') {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
? msg.content
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
});
|
|
132
|
+
if (isFirstSystem) {
|
|
133
|
+
// 第一个 system:提取文本作为 system 参数(约定无图片)
|
|
134
|
+
const text = typeof msg.content === 'string' ? msg.content : msg.content[0]?.text || '';
|
|
135
|
+
if (text) system.push({ type: 'text', text });
|
|
136
|
+
isFirstSystem = false;
|
|
137
|
+
} else {
|
|
138
|
+
// 其他 system:转为 user
|
|
139
|
+
claudeMessages.push({ role: 'user', content: convertContent(msg.content) });
|
|
140
|
+
}
|
|
135
141
|
} else {
|
|
136
|
-
//
|
|
137
|
-
|
|
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 });
|
|
142
|
+
// user/assistant 消息
|
|
143
|
+
claudeMessages.push({ role: msg.role, content: convertContent(msg.content) });
|
|
155
144
|
}
|
|
156
145
|
}
|
|
157
146
|
|
|
158
|
-
return { system, messages:
|
|
147
|
+
return { system, messages: claudeMessages };
|
|
159
148
|
};
|
|
160
149
|
|
|
161
|
-
// ======= Claude SDK
|
|
162
|
-
async function callClaudeSDK(messages, onChunk) {
|
|
150
|
+
// ======= Claude SDK =======
|
|
151
|
+
async function callClaudeSDK(messages, model, onChunk) {
|
|
163
152
|
const { system, messages: claudeMessages } = convertToClaudeMessages(messages);
|
|
164
153
|
|
|
154
|
+
// 打印请求结构(省略 base64 数据)
|
|
155
|
+
const printableRequest = {
|
|
156
|
+
system: system.map(s => s.type === 'image'
|
|
157
|
+
? { type: 'image', source: { ...s.source, data: `[${s.source.data?.length || 0} chars]` } }
|
|
158
|
+
: s
|
|
159
|
+
),
|
|
160
|
+
messages: claudeMessages.map(msg => ({
|
|
161
|
+
role: msg.role,
|
|
162
|
+
content: typeof msg.content === 'string' ? msg.content :
|
|
163
|
+
msg.content.map(c => c.type === 'image'
|
|
164
|
+
? { type: 'image', source: { ...c.source, data: `[${c.source.data?.length || 0} chars]` } }
|
|
165
|
+
: c
|
|
166
|
+
)
|
|
167
|
+
}))
|
|
168
|
+
};
|
|
169
|
+
|
|
165
170
|
const stream = await anthropic.messages.create({
|
|
166
|
-
model:
|
|
171
|
+
model: model,
|
|
167
172
|
max_tokens: 8192,
|
|
168
173
|
system,
|
|
169
174
|
messages: claudeMessages,
|
|
@@ -174,21 +179,33 @@ async function callClaudeSDK(messages, onChunk) {
|
|
|
174
179
|
for await (const event of stream) {
|
|
175
180
|
if (event.type === 'content_block_delta') {
|
|
176
181
|
const { delta } = event;
|
|
177
|
-
// 思考过程
|
|
178
182
|
if (delta?.type === 'thinking_delta' && delta.thinking) {
|
|
179
183
|
onChunk({ type: 'think', delta: delta.thinking });
|
|
180
|
-
}
|
|
181
|
-
// 文本响应
|
|
182
|
-
else if (delta?.type === 'text_delta') {
|
|
184
|
+
} else if (delta?.type === 'text_delta') {
|
|
183
185
|
onChunk({ type: 'text', delta: delta.text });
|
|
184
186
|
}
|
|
185
187
|
}
|
|
186
188
|
}
|
|
187
189
|
}
|
|
188
190
|
|
|
191
|
+
// ======= OpenAI SDK =======
|
|
192
|
+
async function callOpenAISDK(messages, model, onChunk) {
|
|
193
|
+
const stream = await openai.chat.completions.create({
|
|
194
|
+
model: model,
|
|
195
|
+
messages: messages, // 直接使用 OpenAI 格式的 messages
|
|
196
|
+
stream: true
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
for await (const chunk of stream) {
|
|
200
|
+
const delta = chunk.choices[0]?.delta;
|
|
201
|
+
if (delta?.content) {
|
|
202
|
+
onChunk({ type: 'text', delta: delta.content });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
189
207
|
// ======= HTTP 处理 =======
|
|
190
208
|
async function handleChat(messages, res) {
|
|
191
|
-
// 设置 SSE 响应头
|
|
192
209
|
res.writeHead(200, {
|
|
193
210
|
'Content-Type': 'text/event-stream',
|
|
194
211
|
'Cache-Control': 'no-cache',
|
|
@@ -198,14 +215,19 @@ async function handleChat(messages, res) {
|
|
|
198
215
|
|
|
199
216
|
let ended = false;
|
|
200
217
|
const write = (data) => !ended && !res.writableEnded && res.write(data);
|
|
201
|
-
const onChunk = (chunk) => write(`data: ${JSON.stringify(chunk)}
|
|
218
|
+
const onChunk = (chunk) => write(`data: ${JSON.stringify(chunk)}\n\n`);
|
|
202
219
|
|
|
203
220
|
try {
|
|
204
|
-
|
|
205
|
-
|
|
221
|
+
// 根据模型名称自动判断使用哪个 provider
|
|
222
|
+
const provider = CONFIG.model.toLowerCase().includes('claude') ? 'claude' : 'openai';
|
|
223
|
+
if (provider === 'openai') {
|
|
224
|
+
await callOpenAISDK(messages, CONFIG.model, onChunk);
|
|
225
|
+
} else {
|
|
226
|
+
await callClaudeSDK(messages, CONFIG.model, onChunk);
|
|
227
|
+
}
|
|
228
|
+
write(`data: [DONE]\n\n`);
|
|
206
229
|
} catch (error) {
|
|
207
|
-
|
|
208
|
-
write(`data: ${JSON.stringify({ error: error.message })}\\n\\n`);
|
|
230
|
+
write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
|
|
209
231
|
} finally {
|
|
210
232
|
ended = true;
|
|
211
233
|
res.end();
|
|
@@ -217,25 +239,21 @@ http.createServer(async (req, res) => {
|
|
|
217
239
|
const corsHeaders = {
|
|
218
240
|
'Access-Control-Allow-Origin': '*',
|
|
219
241
|
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
220
|
-
'Access-Control-Allow-Headers': 'Content-Type
|
|
242
|
+
'Access-Control-Allow-Headers': 'Content-Type'
|
|
221
243
|
};
|
|
222
244
|
|
|
223
|
-
// 处理 CORS 预检请求
|
|
224
245
|
if (req.method === 'OPTIONS') {
|
|
225
246
|
res.writeHead(200, corsHeaders);
|
|
226
247
|
return res.end();
|
|
227
248
|
}
|
|
228
249
|
|
|
229
|
-
// 处理 AI 请求
|
|
230
250
|
if (req.url === '/sheetnextAI' && req.method === 'POST') {
|
|
231
251
|
let body = '';
|
|
232
252
|
req.on('data', chunk => body += chunk);
|
|
233
253
|
req.on('end', async () => {
|
|
234
254
|
try {
|
|
235
255
|
const { messages } = JSON.parse(body);
|
|
236
|
-
if (!Array.isArray(messages))
|
|
237
|
-
throw new Error('Invalid messages format');
|
|
238
|
-
}
|
|
256
|
+
if (!Array.isArray(messages)) throw new Error('Invalid messages');
|
|
239
257
|
await handleChat(messages, res);
|
|
240
258
|
} catch (error) {
|
|
241
259
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
@@ -246,20 +264,14 @@ http.createServer(async (req, res) => {
|
|
|
246
264
|
res.writeHead(404);
|
|
247
265
|
res.end('Not Found');
|
|
248
266
|
}
|
|
249
|
-
}).listen(3000, () =>
|
|
250
|
-
console.log('🚀 SheetNext AI 中转服务器运行在 http://localhost:3000');
|
|
251
|
-
console.log('📡 AI 端点: http://localhost:3000/sheetnextAI');
|
|
252
|
-
});
|
|
267
|
+
}).listen(3000, () => console.log('🚀 Server running on http://localhost:3000'));
|
|
253
268
|
```
|
|
254
269
|
|
|
255
|
-
###
|
|
270
|
+
### 配置说明
|
|
256
271
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
AI_TOKEN: "your-optional-token" // 可选,用于鉴权
|
|
261
|
-
});
|
|
262
|
-
```
|
|
272
|
+
**判断规则:**
|
|
273
|
+
- 如果模型名称包含 `claude`(不区分大小写) → 使用 Claude SDK
|
|
274
|
+
- 其他情况 → 使用 OpenAI SDK
|
|
263
275
|
|
|
264
276
|
---
|
|
265
277
|
|
|
@@ -289,7 +301,7 @@ SheetNext 发送的请求体格式:
|
|
|
289
301
|
"content": [
|
|
290
302
|
{
|
|
291
303
|
"type": "text",
|
|
292
|
-
"text": "
|
|
304
|
+
"text": "某区域图片"
|
|
293
305
|
},
|
|
294
306
|
{
|
|
295
307
|
"type": "image_url",
|
|
@@ -316,88 +328,3 @@ data: {"type":"text","delta":"帮"}
|
|
|
316
328
|
|
|
317
329
|
data: [DONE]
|
|
318
330
|
```
|
|
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
CHANGED
|
@@ -2,11 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
> 详细的类、方法和属性说明
|
|
4
4
|
|
|
5
|
-
## 文档导航
|
|
6
|
-
|
|
7
|
-
- [← 返回 README](./README.md) - 快速开始和特点介绍
|
|
8
|
-
- [→ AI 配置指南](./AGENT.md) - AI 中转服务搭建
|
|
9
|
-
|
|
10
5
|
---
|
|
11
6
|
|
|
12
7
|
## 目录
|
|
@@ -699,10 +694,3 @@ SN.UndoRedo.redo();
|
|
|
699
694
|
- 撤销/重做会自动记录大部分用户操作
|
|
700
695
|
- 通过 API 修改的内容也会被记录
|
|
701
696
|
- 历史记录栈有大小限制,过旧的操作会被清除
|
|
702
|
-
|
|
703
|
-
---
|
|
704
|
-
|
|
705
|
-
## 相关文档
|
|
706
|
-
|
|
707
|
-
- [← 返回 README](./README.md) - 快速开始和特点介绍
|
|
708
|
-
- [→ AI 配置指南](./AGENT.md) - AI 中转服务搭建
|
package/README.md
CHANGED
|
@@ -1,37 +1,51 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<div><img src="docs/logo.png" alt="SheetNext Logo" width="100" style="vertical-align: middle;"/></div>
|
|
3
|
+
<div>✨ 数行代码可集成一个纯前端高性能 Excel 编辑器。</div>
|
|
4
|
+
<div>🤖 内置超级AI工作流程,目标用 AI 操纵 Excel 表格完成所有任务。</div>
|
|
5
|
+
<div>
|
|
6
|
+
<a href="https://www.sheetnext.com/">🏠 官网</a> |
|
|
7
|
+
<a href="https://www.sheetnext.com/editor">🎯 在线体验</a> |
|
|
8
|
+
<a href="./AGENT.md">🤖 AI中转文档</a> |
|
|
9
|
+
<a href="./DOCS.md">📖 编辑器操作文档</a>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
2
12
|
|
|
3
|
-
|
|
13
|
+
---
|
|
4
14
|
|
|
5
|
-
## 特点
|
|
15
|
+
## ✨ 特点
|
|
6
16
|
|
|
7
|
-
- **电子表格功能** -
|
|
8
|
-
- **AI 工作流** - 内置 AI
|
|
9
|
-
- **导入导出** - 原生支持 Excel (.xlsx)
|
|
17
|
+
- 📊 **电子表格功能** - 支持电子表格核心功能如:单元格编辑、样式、公式引擎、图表、排序、筛选等
|
|
18
|
+
- 🤖 **AI 工作流** - 内置 AI 全自动操作工作流,简单配置可以生成模板、数据分析、公式编写、跨表逻辑操纵等
|
|
19
|
+
- 📁 **导入导出** - 原生支持 Excel (.xlsx) 文件的导入和导出,无需插件前端秒操作
|
|
20
|
+
- 🚀 **开箱即用** - 不用单独配置任何库和插件,安装后简单配置即用所有功能
|
|
21
|
+
- 🔄 **快速迭代** - 版本快速迭代更新,有问题可随时联系或提交 issue
|
|
10
22
|
|
|
11
|
-
## 快速开始
|
|
23
|
+
## 🚀 快速开始
|
|
12
24
|
|
|
13
|
-
### 使用 npm 安装
|
|
25
|
+
### 📦 使用 npm 安装
|
|
14
26
|
|
|
15
27
|
```bash
|
|
16
28
|
npm install sheetnext
|
|
17
29
|
```
|
|
18
|
-
|
|
30
|
+
```html
|
|
31
|
+
<div id="SNContainer" style="width:100vw;height:100vh;padding:0 7px 7px"></div>
|
|
32
|
+
```
|
|
19
33
|
```javascript
|
|
20
34
|
import SheetNext from 'sheetnext';
|
|
21
|
-
import 'sheetnext
|
|
35
|
+
import 'sheetnext.css';
|
|
22
36
|
|
|
23
37
|
// 注意设置容器#SNContainer宽高
|
|
24
38
|
const SN = new SheetNext(document.querySelector('#SNContainer'));
|
|
25
39
|
```
|
|
26
40
|
|
|
27
|
-
### 浏览器直接引入
|
|
41
|
+
### 🌐 浏览器直接引入
|
|
28
42
|
|
|
29
43
|
```html
|
|
30
44
|
<!-- 引入样式 -->
|
|
31
45
|
<link rel="stylesheet" href="dist/sheetnext.css">
|
|
32
46
|
|
|
33
47
|
<!-- 编辑器容器 -->
|
|
34
|
-
<div id="SNContainer" style="width: 100vw; height: 100vh;"></div>
|
|
48
|
+
<div id="SNContainer" style="width: 100vw; height: 100vh;padding:0 7px 7px"></div>
|
|
35
49
|
|
|
36
50
|
<!-- 引入脚本 -->
|
|
37
51
|
<script src="dist/sheetnext.umd.js"></script>
|
|
@@ -42,30 +56,16 @@ const SN = new SheetNext(document.querySelector('#SNContainer'));
|
|
|
42
56
|
</script>
|
|
43
57
|
```
|
|
44
58
|
|
|
45
|
-
## 初始化配置
|
|
59
|
+
## ⚙️ 初始化配置
|
|
46
60
|
|
|
47
61
|
```javascript
|
|
48
62
|
const SN = new SheetNext(document.querySelector('#container'), {
|
|
49
|
-
AI_URL: "http://localhost:3000/sheetnextAI", // AI
|
|
50
|
-
AI_TOKEN: "your-token"
|
|
51
|
-
showMenuBar: true, // 显示菜单栏
|
|
52
|
-
showToolbar: true, // 显示工具栏
|
|
53
|
-
showAIChat: true, // 启用 AI 聊天
|
|
54
|
-
showFormulaBar: true, // 显示公式栏
|
|
55
|
-
showSheetTabBar: true, // 显示工作表标签栏
|
|
56
|
-
showAIChatWindow: false // 显示 AI 聊天窗口
|
|
63
|
+
AI_URL: "http://localhost:3000/sheetnextAI", // AI 中转地址(可选)
|
|
64
|
+
AI_TOKEN: "your-token" // 中转 token(可选)
|
|
57
65
|
});
|
|
58
66
|
```
|
|
59
67
|
|
|
60
|
-
##
|
|
61
|
-
|
|
62
|
-
- **[完整 API 文档 (DOCS.md)](./DOCS.md)** - 详细的类、方法和属性说明
|
|
63
|
-
- **[AI 配置指南 (AGENT.md)](./AGENT.md)** - AI 中转服务搭建和配置
|
|
64
|
-
|
|
65
|
-
## License
|
|
66
|
-
|
|
67
|
-
MIT
|
|
68
|
-
|
|
69
|
-
## 相关链接
|
|
68
|
+
## 🔗 相关链接
|
|
70
69
|
|
|
71
|
-
- [
|
|
70
|
+
- 🏠 [官网](https://www.sheetnext.com)
|
|
71
|
+
- 📦 [npm 包地址](https://www.npmjs.com/package/sheetnext)
|