collabdocchat 2.1.4 → 2.2.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "collabdocchat",
3
- "version": "2.1.4",
3
+ "version": "2.2.0",
4
4
  "description": "开源的实时协作文档聊天平台 - 集成任务管理、多人文档编辑、智能点名功能",
5
5
  "main": "./server/index.js",
6
6
  "type": "module",
@@ -0,0 +1,66 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const filePath = path.join(__dirname, '../src/pages/admin-dashboard.js');
9
+
10
+ console.log('读取文件...');
11
+ let content = fs.readFileSync(filePath, 'utf8');
12
+
13
+ // 找到最后一个 renderView('groups'); 之前插入新函数
14
+ const insertPoint = content.lastIndexOf(" renderView('groups');");
15
+
16
+ if (insertPoint === -1) {
17
+ console.error('❌ 找不到插入点');
18
+ process.exit(1);
19
+ }
20
+
21
+ const newFunctions = `
22
+ // 知识库管理
23
+ async function renderKnowledgeView(container) {
24
+ if (!currentGroup) {
25
+ container.innerHTML = '<div class="empty-state">请先选择一个群组</div>';
26
+ return;
27
+ }
28
+ container.innerHTML = '<div class="empty-state">知识库功能开发中...</div>';
29
+ }
30
+
31
+ // 工作流管理
32
+ async function renderWorkflowView(container) {
33
+ if (!currentGroup) {
34
+ container.innerHTML = '<div class="empty-state">请先选择一个群组</div>';
35
+ return;
36
+ }
37
+ container.innerHTML = '<div class="empty-state">工作流功能开发中...</div>';
38
+ }
39
+
40
+ // 备份管理
41
+ async function renderBackupView(container) {
42
+ container.innerHTML = '<div class="empty-state">备份功能开发中...</div>';
43
+ }
44
+
45
+ // AI助手
46
+ async function renderAIView(container) {
47
+ container.innerHTML = '<div class="empty-state">AI助手功能开发中...</div>';
48
+ }
49
+
50
+ // 数据导出
51
+ async function renderExportView(container) {
52
+ container.innerHTML = '<div class="empty-state">数据导出功能开发中...</div>';
53
+ }
54
+
55
+ `;
56
+
57
+ const before = content.substring(0, insertPoint);
58
+ const after = content.substring(insertPoint);
59
+
60
+ content = before + newFunctions + after;
61
+
62
+ console.log('写入文件...');
63
+ fs.writeFileSync(filePath, content, 'utf8');
64
+
65
+ console.log('✅ 完成!已添加缺失的函数');
66
+
@@ -0,0 +1,427 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const filePath = path.join(__dirname, '../src/pages/admin-dashboard.js');
9
+
10
+ console.log('读取文件...');
11
+ let content = fs.readFileSync(filePath, 'utf8');
12
+
13
+ // 1. 在导出按钮后添加新的菜单项
14
+ console.log('添加新菜单项...');
15
+ const navMenuPattern = /(data-view="export">[\s\S]*?数据导出[\s\S]*?<\/button>)/;
16
+ const navMenuReplacement = `$1
17
+ <button class="nav-item" data-view="whiteboard">
18
+ <span class="icon">🎨</span> 协作白板
19
+ </button>
20
+ <button class="nav-item" data-view="integrations">
21
+ <span class="icon">🔌</span> 集成管理
22
+ </button>`;
23
+
24
+ content = content.replace(navMenuPattern, navMenuReplacement);
25
+
26
+ // 2. 在退出登录按钮前添加设置和帮助按钮
27
+ console.log('添加底部菜单...');
28
+ const logoutPattern = /(<button class="btn-logout" id="logoutBtn">退出登录<\/button>)/;
29
+ const logoutReplacement = `<div class="sidebar-footer">
30
+ <button class="sidebar-footer-btn" id="settingsBtn">
31
+ <span class="icon">⚙️</span> 设置
32
+ </button>
33
+ <button class="sidebar-footer-btn" id="helpBtn">
34
+ <span class="icon">❓</span> 帮助
35
+ </button>
36
+ </div>
37
+ $1`;
38
+
39
+ content = content.replace(logoutPattern, logoutReplacement);
40
+
41
+ // 3. 在 switch 语句中添加新的 case
42
+ console.log('添加 switch case...');
43
+ const switchPattern = /(case 'export':[\s\S]*?await renderExportView\(contentArea\);[\s\S]*?break;)/;
44
+ const switchReplacement = `$1
45
+ case 'whiteboard':
46
+ await renderWhiteboardView(contentArea);
47
+ break;
48
+ case 'integrations':
49
+ await renderIntegrationsView(contentArea);
50
+ break;`;
51
+
52
+ content = content.replace(switchPattern, switchReplacement);
53
+
54
+ // 4. 在退出登录事件后添加设置和帮助按钮事件
55
+ console.log('添加按钮事件...');
56
+ const logoutEventPattern = /(document\.getElementById\('logoutBtn'\)\.addEventListener\('click', \(\) => \{[\s\S]*?authService\.logout\(\);[\s\S]*?\}\);)/;
57
+ const logoutEventReplacement = `$1
58
+
59
+ // 设置按钮
60
+ document.getElementById('settingsBtn').addEventListener('click', () => {
61
+ renderView('settings');
62
+ });
63
+
64
+ // 帮助按钮
65
+ document.getElementById('helpBtn').addEventListener('click', () => {
66
+ renderView('help');
67
+ });`;
68
+
69
+ content = content.replace(logoutEventPattern, logoutEventReplacement);
70
+
71
+ // 5. 在 switch 中添加设置和帮助的 case
72
+ const exportCasePattern = /(case 'export':[\s\S]*?await renderExportView\(contentArea\);[\s\S]*?break;[\s\S]*?case 'whiteboard':[\s\S]*?await renderWhiteboardView\(contentArea\);[\s\S]*?break;[\s\S]*?case 'integrations':[\s\S]*?await renderIntegrationsView\(contentArea\);[\s\S]*?break;)/;
73
+ const exportCaseReplacement = `$1
74
+ case 'settings':
75
+ await renderSettingsView(contentArea);
76
+ break;
77
+ case 'help':
78
+ await renderHelpView(contentArea);
79
+ break;`;
80
+
81
+ content = content.replace(exportCasePattern, exportCaseReplacement);
82
+
83
+ // 6. 在文件末尾添加新函数
84
+ console.log('添加新函数...');
85
+ const insertPoint = content.lastIndexOf(" renderView('groups');");
86
+
87
+ if (insertPoint === -1) {
88
+ console.error('❌ 找不到插入点');
89
+ process.exit(1);
90
+ }
91
+
92
+ const newFunctions = `
93
+ // 协作白板
94
+ async function renderWhiteboardView(container) {
95
+ if (!currentGroup) {
96
+ container.innerHTML = '<div class="empty-state">请先选择一个群组</div>';
97
+ return;
98
+ }
99
+
100
+ container.innerHTML = \`
101
+ <div class="view-header">
102
+ <h2>🎨 协作白板 - \${currentGroup.name}</h2>
103
+ <div style="display: flex; gap: 10px;">
104
+ <button class="btn-secondary" id="clearCanvas">清空画布</button>
105
+ <button class="btn-primary" id="saveCanvas">保存白板</button>
106
+ </div>
107
+ </div>
108
+ <div class="whiteboard-container">
109
+ <div class="whiteboard-toolbar">
110
+ <button class="tool-btn active" data-tool="pen">✏️ 画笔</button>
111
+ <button class="tool-btn" data-tool="eraser">🧹 橡皮擦</button>
112
+ <button class="tool-btn" data-tool="text">📝 文字</button>
113
+ <button class="tool-btn" data-tool="shape">⬜ 形状</button>
114
+ <input type="color" id="colorPicker" value="#000000" title="颜色">
115
+ <input type="range" id="brushSize" min="1" max="20" value="3" title="画笔大小">
116
+ </div>
117
+ <canvas id="whiteboard" width="1200" height="600" style="border: 1px solid var(--border); background: white; cursor: crosshair;"></canvas>
118
+ <div class="whiteboard-users" id="whiteboardUsers">
119
+ <span class="user-badge">👤 \${user.username}</span>
120
+ </div>
121
+ </div>
122
+ \`;
123
+
124
+ const canvas = document.getElementById('whiteboard');
125
+ const ctx = canvas.getContext('2d');
126
+ let isDrawing = false;
127
+ let currentTool = 'pen';
128
+ let currentColor = '#000000';
129
+ let brushSize = 3;
130
+
131
+ // 工具切换
132
+ document.querySelectorAll('.tool-btn').forEach(btn => {
133
+ btn.addEventListener('click', () => {
134
+ document.querySelectorAll('.tool-btn').forEach(b => b.classList.remove('active'));
135
+ btn.classList.add('active');
136
+ currentTool = btn.dataset.tool;
137
+ });
138
+ });
139
+
140
+ document.getElementById('colorPicker').addEventListener('change', (e) => {
141
+ currentColor = e.target.value;
142
+ });
143
+
144
+ document.getElementById('brushSize').addEventListener('input', (e) => {
145
+ brushSize = e.target.value;
146
+ });
147
+
148
+ // 绘画功能
149
+ let lastX = 0;
150
+ let lastY = 0;
151
+
152
+ canvas.addEventListener('mousedown', (e) => {
153
+ isDrawing = true;
154
+ const rect = canvas.getBoundingClientRect();
155
+ lastX = e.clientX - rect.left;
156
+ lastY = e.clientY - rect.top;
157
+ });
158
+
159
+ canvas.addEventListener('mousemove', (e) => {
160
+ if (!isDrawing) return;
161
+
162
+ const rect = canvas.getBoundingClientRect();
163
+ const x = e.clientX - rect.left;
164
+ const y = e.clientY - rect.top;
165
+
166
+ ctx.beginPath();
167
+ ctx.moveTo(lastX, lastY);
168
+ ctx.lineTo(x, y);
169
+ ctx.strokeStyle = currentTool === 'eraser' ? '#ffffff' : currentColor;
170
+ ctx.lineWidth = brushSize;
171
+ ctx.lineCap = 'round';
172
+ ctx.stroke();
173
+
174
+ lastX = x;
175
+ lastY = y;
176
+
177
+ // 发送绘画数据到其他用户
178
+ wsService.sendWhiteboardData(currentGroup._id, {
179
+ tool: currentTool,
180
+ color: currentColor,
181
+ size: brushSize,
182
+ from: { x: lastX, y: lastY },
183
+ to: { x, y }
184
+ });
185
+ });
186
+
187
+ canvas.addEventListener('mouseup', () => {
188
+ isDrawing = false;
189
+ });
190
+
191
+ canvas.addEventListener('mouseleave', () => {
192
+ isDrawing = false;
193
+ });
194
+
195
+ // 清空画布
196
+ document.getElementById('clearCanvas').addEventListener('click', () => {
197
+ if (confirm('确定要清空画布吗?')) {
198
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
199
+ }
200
+ });
201
+
202
+ // 保存白板
203
+ document.getElementById('saveCanvas').addEventListener('click', () => {
204
+ const dataURL = canvas.toDataURL('image/png');
205
+ const link = document.createElement('a');
206
+ link.download = \`whiteboard-\${Date.now()}.png\`;
207
+ link.href = dataURL;
208
+ link.click();
209
+ alert('白板已保存!');
210
+ });
211
+
212
+ // 监听其他用户的绘画
213
+ wsService.on('whiteboard_draw', (data) => {
214
+ if (data.groupId === currentGroup._id && data.userId !== currentUserId) {
215
+ ctx.beginPath();
216
+ ctx.moveTo(data.from.x, data.from.y);
217
+ ctx.lineTo(data.to.x, data.to.y);
218
+ ctx.strokeStyle = data.tool === 'eraser' ? '#ffffff' : data.color;
219
+ ctx.lineWidth = data.size;
220
+ ctx.lineCap = 'round';
221
+ ctx.stroke();
222
+ }
223
+ });
224
+ }
225
+
226
+ // 集成管理
227
+ async function renderIntegrationsView(container) {
228
+ container.innerHTML = \`
229
+ <div class="view-header">
230
+ <h2>🔌 集成管理</h2>
231
+ <button class="btn-primary" id="addIntegrationBtn">添加集成</button>
232
+ </div>
233
+ <div class="integrations-grid">
234
+ <div class="integration-card">
235
+ <div class="integration-icon">📧</div>
236
+ <h3>邮件通知</h3>
237
+ <p>接收重要事件的邮件通知</p>
238
+ <button class="btn-secondary">配置</button>
239
+ </div>
240
+ <div class="integration-card">
241
+ <div class="integration-icon">🔔</div>
242
+ <h3>Webhook</h3>
243
+ <p>自定义 Webhook 集成</p>
244
+ <button class="btn-secondary">配置</button>
245
+ </div>
246
+ <div class="integration-card">
247
+ <div class="integration-icon">📱</div>
248
+ <h3>钉钉机器人</h3>
249
+ <p>发送消息到钉钉群</p>
250
+ <button class="btn-secondary">配置</button>
251
+ </div>
252
+ <div class="integration-card">
253
+ <div class="integration-icon">💬</div>
254
+ <h3>企业微信</h3>
255
+ <p>发送消息到企业微信</p>
256
+ <button class="btn-secondary">配置</button>
257
+ </div>
258
+ <div class="integration-card">
259
+ <div class="integration-icon">🤖</div>
260
+ <h3>Slack</h3>
261
+ <p>连接到 Slack 工作区</p>
262
+ <button class="btn-secondary">配置</button>
263
+ </div>
264
+ <div class="integration-card">
265
+ <div class="integration-icon">📊</div>
266
+ <h3>数据分析</h3>
267
+ <p>导出数据到分析平台</p>
268
+ <button class="btn-secondary">配置</button>
269
+ </div>
270
+ </div>
271
+ \`;
272
+
273
+ document.getElementById('addIntegrationBtn').addEventListener('click', () => {
274
+ alert('添加自定义集成功能开发中...');
275
+ });
276
+ }
277
+
278
+ // 设置
279
+ async function renderSettingsView(container) {
280
+ container.innerHTML = \`
281
+ <div class="view-header">
282
+ <h2>⚙️ 设置</h2>
283
+ </div>
284
+ <div class="settings-container">
285
+ <div class="settings-section">
286
+ <h3>个人设置</h3>
287
+ <div class="setting-item">
288
+ <label>用户名</label>
289
+ <input type="text" value="\${user.username}" disabled>
290
+ </div>
291
+ <div class="setting-item">
292
+ <label>邮箱</label>
293
+ <input type="email" value="\${user.email || '未设置'}" placeholder="请输入邮箱">
294
+ </div>
295
+ <div class="setting-item">
296
+ <label>修改密码</label>
297
+ <button class="btn-secondary" id="changePasswordBtn">修改密码</button>
298
+ </div>
299
+ </div>
300
+
301
+ <div class="settings-section">
302
+ <h3>通知设置</h3>
303
+ <div class="setting-item">
304
+ <label>
305
+ <input type="checkbox" id="emailNotifications" checked>
306
+ 邮件通知
307
+ </label>
308
+ </div>
309
+ <div class="setting-item">
310
+ <label>
311
+ <input type="checkbox" id="desktopNotifications" checked>
312
+ 桌面通知
313
+ </label>
314
+ </div>
315
+ <div class="setting-item">
316
+ <label>
317
+ <input type="checkbox" id="soundNotifications" checked>
318
+ 声音提示
319
+ </label>
320
+ </div>
321
+ </div>
322
+
323
+ <div class="settings-section">
324
+ <h3>系统设置</h3>
325
+ <div class="setting-item">
326
+ <label>主题</label>
327
+ <select id="themeSelect">
328
+ <option value="light">浅色</option>
329
+ <option value="dark">深色</option>
330
+ <option value="auto">跟随系统</option>
331
+ </select>
332
+ </div>
333
+ <div class="setting-item">
334
+ <label>语言</label>
335
+ <select id="languageSelect">
336
+ <option value="zh-CN">简体中文</option>
337
+ <option value="en-US">English</option>
338
+ </select>
339
+ </div>
340
+ </div>
341
+
342
+ <div class="settings-actions">
343
+ <button class="btn-primary" id="saveSettingsBtn">保存设置</button>
344
+ </div>
345
+ </div>
346
+ \`;
347
+
348
+ document.getElementById('changePasswordBtn').addEventListener('click', () => {
349
+ alert('修改密码功能开发中...');
350
+ });
351
+
352
+ document.getElementById('saveSettingsBtn').addEventListener('click', () => {
353
+ alert('设置已保存!');
354
+ });
355
+ }
356
+
357
+ // 帮助
358
+ async function renderHelpView(container) {
359
+ container.innerHTML = \`
360
+ <div class="view-header">
361
+ <h2>❓ 帮助中心</h2>
362
+ </div>
363
+ <div class="help-container">
364
+ <div class="help-section">
365
+ <h3>📖 快速开始</h3>
366
+ <ul>
367
+ <li><a href="#" class="help-link">如何创建群组?</a></li>
368
+ <li><a href="#" class="help-link">如何邀请成员?</a></li>
369
+ <li><a href="#" class="help-link">如何创建文档?</a></li>
370
+ <li><a href="#" class="help-link">如何使用协作白板?</a></li>
371
+ </ul>
372
+ </div>
373
+
374
+ <div class="help-section">
375
+ <h3>🔧 功能说明</h3>
376
+ <ul>
377
+ <li><a href="#" class="help-link">群组管理</a></li>
378
+ <li><a href="#" class="help-link">任务管理</a></li>
379
+ <li><a href="#" class="help-link">文档协作</a></li>
380
+ <li><a href="#" class="help-link">知识库</a></li>
381
+ <li><a href="#" class="help-link">工作流引擎</a></li>
382
+ <li><a href="#" class="help-link">AI 助手</a></li>
383
+ </ul>
384
+ </div>
385
+
386
+ <div class="help-section">
387
+ <h3>❓ 常见问题</h3>
388
+ <ul>
389
+ <li><a href="#" class="help-link">如何重置密码?</a></li>
390
+ <li><a href="#" class="help-link">如何导出数据?</a></li>
391
+ <li><a href="#" class="help-link">如何备份数据?</a></li>
392
+ <li><a href="#" class="help-link">如何联系技术支持?</a></li>
393
+ </ul>
394
+ </div>
395
+
396
+ <div class="help-section">
397
+ <h3>📞 联系我们</h3>
398
+ <p>如果您有任何问题或建议,请通过以下方式联系我们:</p>
399
+ <ul>
400
+ <li>📧 邮箱: support@collabdocchat.com</li>
401
+ <li>🌐 GitHub: <a href="https://github.com/shijinghao/collabdocchat" target="_blank">github.com/shijinghao/collabdocchat</a></li>
402
+ <li>📦 npm: <a href="https://www.npmjs.com/package/collabdocchat" target="_blank">npmjs.com/package/collabdocchat</a></li>
403
+ </ul>
404
+ </div>
405
+
406
+ <div class="help-section">
407
+ <h3>ℹ️ 关于</h3>
408
+ <p><strong>CollabDocChat</strong> v2.1.5</p>
409
+ <p>开源的实时协作文档聊天平台</p>
410
+ <p>© 2026 CollabDocChat. All rights reserved.</p>
411
+ </div>
412
+ </div>
413
+ \`;
414
+ }
415
+
416
+ `;
417
+
418
+ const before = content.substring(0, insertPoint);
419
+ const after = content.substring(insertPoint);
420
+
421
+ content = before + newFunctions + after;
422
+
423
+ console.log('写入文件...');
424
+ fs.writeFileSync(filePath, content, 'utf8');
425
+
426
+ console.log('✅ 完成!已添加协作白板、集成管理、设置和帮助功能');
427
+