collabdocchat 2.0.3 → 2.0.5
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 +1 -1
- package/scripts/generate-docs.js +1 -0
- package/scripts/pre-publish-check.js +1 -0
- package/src/components/knowledge-modal.js +15 -17
- package/src/components/optimized-poll-detail.js +17 -20
- package/src/main.js +1 -1
- package/src/pages/admin-dashboard.js +281 -324
- package/src/pages/login.js +7 -7
- package/src/pages/optimized-backup-view.js +32 -35
- package/src/pages/optimized-knowledge-view.js +34 -41
- package/src/pages/optimized-task-detail.js +36 -42
- package/src/pages/optimized-workflow-view.js +64 -81
- package/src/pages/simplified-workflows.js +66 -66
- package/src/pages/user-dashboard.js +95 -111
- package/src/services/api.js +3 -6
- package/src/services/auth.js +1 -1
- package/src/services/websocket.js +11 -14
- package/src/styles/collaboration-modern.js +8 -10
- package/src/utils/ai-assistant.js +74 -89
- package/src/utils/chat-enhancements.js +18 -18
- package/src/utils/collaboration-enhancer.js +84 -126
- package/src/utils/feature-integrator.js +78 -93
- package/src/utils/onboarding-guide.js +82 -93
- package/src/utils/performance.js +9 -9
- package/src/utils/permission-manager.js +39 -58
- package/src/utils/responsive-handler.js +22 -22
- package/src/utils/theme-manager.js +37 -49
- package/src/utils/ui-enhancements-loader.js +30 -40
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 协作增强功能
|
|
3
|
-
*
|
|
4
|
-
*/
|
|
3
|
+
* 提供更多协作工具和功�? */
|
|
5
4
|
|
|
6
5
|
export class CollaborationEnhancer {
|
|
7
6
|
constructor(apiService, wsService) {
|
|
@@ -13,8 +12,7 @@ export class CollaborationEnhancer {
|
|
|
13
12
|
* 协作白板
|
|
14
13
|
*/
|
|
15
14
|
createWhiteboard(container, groupId, username, isAdmin = false) {
|
|
16
|
-
//
|
|
17
|
-
const adminControls = isAdmin ? `
|
|
15
|
+
// 管理员控制按�? const adminControls = isAdmin ? `
|
|
18
16
|
<button class="btn-warning" id="toggleCollaboration">🔒 禁止协作</button>
|
|
19
17
|
<button class="btn-success" id="sendToChat">📤 发送到群聊</button>
|
|
20
18
|
` : '';
|
|
@@ -25,7 +23,7 @@ export class CollaborationEnhancer {
|
|
|
25
23
|
<button class="tool-btn active" data-tool="pen">✏️ 画笔</button>
|
|
26
24
|
<button class="tool-btn" data-tool="eraser">🧹 橡皮</button>
|
|
27
25
|
<button class="tool-btn" data-tool="text">📝 文字</button>
|
|
28
|
-
<button class="tool-btn" data-tool="shape"
|
|
26
|
+
<button class="tool-btn" data-tool="shape">�?形状</button>
|
|
29
27
|
<input type="color" id="colorPicker" value="#6366f1" title="选择颜色">
|
|
30
28
|
<input type="range" id="brushSize" min="1" max="20" value="3" title="画笔粗细">
|
|
31
29
|
<button class="btn-secondary" id="clearBoard">清空</button>
|
|
@@ -38,7 +36,7 @@ export class CollaborationEnhancer {
|
|
|
38
36
|
<span class="user-badge">👤 在线用户: <span id="userCount">1</span></span>
|
|
39
37
|
</div>
|
|
40
38
|
<div class="whiteboard-status" id="whiteboardStatus">
|
|
41
|
-
<span class="status-badge status-active"
|
|
39
|
+
<span class="status-badge status-active">�?协作模式</span>
|
|
42
40
|
</div>
|
|
43
41
|
</div>
|
|
44
42
|
</div>
|
|
@@ -76,14 +74,13 @@ export class CollaborationEnhancer {
|
|
|
76
74
|
usersEl.innerHTML = `👤 在线用户 (${onlineUsers.size}): ${userList}`;
|
|
77
75
|
};
|
|
78
76
|
|
|
79
|
-
//
|
|
80
|
-
const updateCollaborationStatus = () => {
|
|
77
|
+
// 更新协作状态显�? const updateCollaborationStatus = () => {
|
|
81
78
|
const statusEl = container.querySelector('#whiteboardStatus');
|
|
82
79
|
if (statusEl) {
|
|
83
80
|
if (collaborationEnabled) {
|
|
84
|
-
statusEl.innerHTML = '<span class="status-badge status-active"
|
|
81
|
+
statusEl.innerHTML = '<span class="status-badge status-active">�?协作模式</span>';
|
|
85
82
|
} else {
|
|
86
|
-
statusEl.innerHTML = '<span class="status-badge status-locked">🔒
|
|
83
|
+
statusEl.innerHTML = '<span class="status-badge status-locked">🔒 仅管理员可编�?/span>';
|
|
87
84
|
}
|
|
88
85
|
}
|
|
89
86
|
};
|
|
@@ -111,12 +108,9 @@ export class CollaborationEnhancer {
|
|
|
111
108
|
let lastX = 0;
|
|
112
109
|
let lastY = 0;
|
|
113
110
|
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (!collaborationEnabled) {
|
|
118
|
-
return false; // 协作被禁用,普通用户不能绘画
|
|
119
|
-
}
|
|
111
|
+
// 检查是否可以绘�? const canDraw = () => {
|
|
112
|
+
if (isAdmin) return true; // 管理员始终可以绘�? if (!collaborationEnabled) {
|
|
113
|
+
return false; // 协作被禁用,普通用户不能绘�? }
|
|
120
114
|
return true;
|
|
121
115
|
};
|
|
122
116
|
|
|
@@ -142,11 +136,9 @@ export class CollaborationEnhancer {
|
|
|
142
136
|
const x = e.clientX - rect.left;
|
|
143
137
|
const y = e.clientY - rect.top;
|
|
144
138
|
|
|
145
|
-
//
|
|
146
|
-
this.drawLine(ctx, lastX, lastY, x, y, currentTool, currentColor, brushSize);
|
|
139
|
+
// 绘制到本地画�? this.drawLine(ctx, lastX, lastY, x, y, currentTool, currentColor, brushSize);
|
|
147
140
|
|
|
148
|
-
//
|
|
149
|
-
this.wsService.send({
|
|
141
|
+
// 广播绘画数据到其他用�? this.wsService.send({
|
|
150
142
|
type: 'whiteboard_draw',
|
|
151
143
|
groupId: groupId,
|
|
152
144
|
username: username,
|
|
@@ -174,10 +166,10 @@ export class CollaborationEnhancer {
|
|
|
174
166
|
// 清空画布
|
|
175
167
|
container.querySelector('#clearBoard').addEventListener('click', () => {
|
|
176
168
|
if (!isAdmin && !collaborationEnabled) {
|
|
177
|
-
alert('
|
|
169
|
+
alert('管理员已禁止协作,无法清空画�?);
|
|
178
170
|
return;
|
|
179
171
|
}
|
|
180
|
-
if (confirm('
|
|
172
|
+
if (confirm('确定要清空画布吗�?)) {
|
|
181
173
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
182
174
|
this.wsService.send({
|
|
183
175
|
type: 'whiteboard_clear',
|
|
@@ -211,18 +203,16 @@ export class CollaborationEnhancer {
|
|
|
211
203
|
toggleBtn.className = 'btn-success';
|
|
212
204
|
}
|
|
213
205
|
|
|
214
|
-
//
|
|
215
|
-
updateCollaborationStatus();
|
|
206
|
+
// 更新状态显�? updateCollaborationStatus();
|
|
216
207
|
|
|
217
|
-
//
|
|
218
|
-
this.wsService.send({
|
|
208
|
+
// 广播协作状态变�? this.wsService.send({
|
|
219
209
|
type: 'whiteboard_collaboration_toggle',
|
|
220
210
|
groupId: groupId,
|
|
221
211
|
enabled: collaborationEnabled,
|
|
222
212
|
username: username
|
|
223
213
|
});
|
|
224
214
|
|
|
225
|
-
alert(collaborationEnabled ? '
|
|
215
|
+
alert(collaborationEnabled ? '已允许所有用户协作绘�? : '已禁止普通用户绘画,仅管理员可编�?);
|
|
226
216
|
});
|
|
227
217
|
}
|
|
228
218
|
|
|
@@ -253,36 +243,32 @@ export class CollaborationEnhancer {
|
|
|
253
243
|
// 获取文件查看URL(用于在线预览)
|
|
254
244
|
const fileViewUrl = this.apiService.getFileViewUrl(uploadResult.file._id);
|
|
255
245
|
|
|
256
|
-
//
|
|
257
|
-
const message = `📋 ${username} 分享了白板内容\n<img src="${fileViewUrl}" alt="白板" class="chat-image" data-file-id="${uploadResult.file._id}">`;
|
|
246
|
+
// 发送包含图片的消息到群�? const message = `📋 ${username} 分享了白板内容\n<img src="${fileViewUrl}" alt="白板" class="chat-image" data-file-id="${uploadResult.file._id}">`;
|
|
258
247
|
this.wsService.sendChatMessage(groupId, username, message);
|
|
259
248
|
|
|
260
|
-
alert('
|
|
249
|
+
alert('白板已发送到群聊�?);
|
|
261
250
|
sendBtn.disabled = false;
|
|
262
251
|
sendBtn.textContent = '📤 发送到群聊';
|
|
263
252
|
} catch (error) {
|
|
264
|
-
console.error('
|
|
265
|
-
alert('
|
|
253
|
+
console.error('发送白板失�?', error);
|
|
254
|
+
alert('发送失�? ' + error.message);
|
|
266
255
|
sendBtn.disabled = false;
|
|
267
256
|
sendBtn.textContent = '📤 发送到群聊';
|
|
268
257
|
}
|
|
269
258
|
}, 'image/png');
|
|
270
259
|
} catch (error) {
|
|
271
|
-
console.error('
|
|
272
|
-
alert('
|
|
260
|
+
console.error('发送白板失�?', error);
|
|
261
|
+
alert('发送失�? ' + error.message);
|
|
273
262
|
}
|
|
274
263
|
});
|
|
275
264
|
}
|
|
276
265
|
|
|
277
|
-
//
|
|
278
|
-
this.wsService.on('whiteboard_draw', (data) => {
|
|
266
|
+
// 监听其他用户的绘�? this.wsService.on('whiteboard_draw', (data) => {
|
|
279
267
|
if (data.groupId === groupId) {
|
|
280
|
-
//
|
|
281
|
-
this.drawLine(ctx, data.data.from.x, data.data.from.y, data.data.to.x, data.data.to.y,
|
|
268
|
+
// 绘制其他用户的笔�? this.drawLine(ctx, data.data.from.x, data.data.from.y, data.data.to.x, data.data.to.y,
|
|
282
269
|
data.data.tool, data.data.color, data.data.size);
|
|
283
270
|
|
|
284
|
-
//
|
|
285
|
-
if (data.username && data.username !== username) {
|
|
271
|
+
// 显示绘画者信息(短暂提示�? if (data.username && data.username !== username) {
|
|
286
272
|
this.showDrawingNotification(container, data.username);
|
|
287
273
|
}
|
|
288
274
|
}
|
|
@@ -328,21 +314,18 @@ export class CollaborationEnhancer {
|
|
|
328
314
|
}
|
|
329
315
|
});
|
|
330
316
|
|
|
331
|
-
//
|
|
332
|
-
this.wsService.send({
|
|
317
|
+
// 通知其他用户自己加入了白�? this.wsService.send({
|
|
333
318
|
type: 'whiteboard_user_join',
|
|
334
319
|
groupId: groupId,
|
|
335
320
|
username: username
|
|
336
321
|
});
|
|
337
322
|
|
|
338
|
-
//
|
|
339
|
-
updateOnlineUsers();
|
|
323
|
+
// 初始化显�? updateOnlineUsers();
|
|
340
324
|
updateCollaborationStatus();
|
|
341
325
|
}
|
|
342
326
|
|
|
343
327
|
/**
|
|
344
|
-
*
|
|
345
|
-
*/
|
|
328
|
+
* 绘制线条的辅助方�? */
|
|
346
329
|
drawLine(ctx, fromX, fromY, toX, toY, tool, color, size) {
|
|
347
330
|
ctx.beginPath();
|
|
348
331
|
ctx.moveTo(fromX, fromY);
|
|
@@ -396,10 +379,10 @@ export class CollaborationEnhancer {
|
|
|
396
379
|
container.innerHTML = `
|
|
397
380
|
<div class="poll-creator">
|
|
398
381
|
<h3>创建投票任务</h3>
|
|
399
|
-
<p class="info-text"
|
|
382
|
+
<p class="info-text">投票将作为任务分配给所有群组成员,成员可以�?我的任务"中查看并投票</p>
|
|
400
383
|
<div class="form-group">
|
|
401
384
|
<label>投票标题</label>
|
|
402
|
-
<input type="text" id="pollTitle" placeholder="
|
|
385
|
+
<input type="text" id="pollTitle" placeholder="请输入投票标�?>
|
|
403
386
|
</div>
|
|
404
387
|
<div class="form-group">
|
|
405
388
|
<label>投票说明</label>
|
|
@@ -416,8 +399,7 @@ export class CollaborationEnhancer {
|
|
|
416
399
|
<div class="form-group">
|
|
417
400
|
<label>
|
|
418
401
|
<input type="checkbox" id="allowMultiple">
|
|
419
|
-
|
|
420
|
-
</label>
|
|
402
|
+
允许多�? </label>
|
|
421
403
|
</div>
|
|
422
404
|
<div class="form-group">
|
|
423
405
|
<label>截止时间</label>
|
|
@@ -432,7 +414,7 @@ export class CollaborationEnhancer {
|
|
|
432
414
|
const optionsDiv = container.querySelector('#pollOptions');
|
|
433
415
|
const optionCount = optionsDiv.querySelectorAll('.poll-option').length + 1;
|
|
434
416
|
if (optionCount > 10) {
|
|
435
|
-
alert('
|
|
417
|
+
alert('最多只能添�?0个选项');
|
|
436
418
|
return;
|
|
437
419
|
}
|
|
438
420
|
const input = document.createElement('input');
|
|
@@ -469,15 +451,13 @@ export class CollaborationEnhancer {
|
|
|
469
451
|
totalVotes: 0
|
|
470
452
|
};
|
|
471
453
|
|
|
472
|
-
//
|
|
473
|
-
await this.apiService.createTask({
|
|
454
|
+
// 创建为任�? await this.apiService.createTask({
|
|
474
455
|
title: `📊 ${title}`,
|
|
475
|
-
description: description || '
|
|
456
|
+
description: description || '请在下方选项中投�?,
|
|
476
457
|
groupId: groupId,
|
|
477
458
|
assignedTo: memberIds,
|
|
478
459
|
deadline: deadline || null,
|
|
479
|
-
type: 'poll', //
|
|
480
|
-
pollData: pollData // 投票数据
|
|
460
|
+
type: 'poll', // 标记为投票类�? pollData: pollData // 投票数据
|
|
481
461
|
});
|
|
482
462
|
|
|
483
463
|
alert('投票任务创建成功!所有成员可以在"我的任务"中查看并投票');
|
|
@@ -485,10 +465,10 @@ export class CollaborationEnhancer {
|
|
|
485
465
|
// 显示成功信息
|
|
486
466
|
container.innerHTML = `
|
|
487
467
|
<div class="success-message">
|
|
488
|
-
<h3
|
|
489
|
-
<p
|
|
490
|
-
<p
|
|
491
|
-
<p>分配给:${memberIds.length}
|
|
468
|
+
<h3>�?投票任务创建成功�?/h3>
|
|
469
|
+
<p>投票标题�?{title}</p>
|
|
470
|
+
<p>选项数量�?{options.length}</p>
|
|
471
|
+
<p>分配给:${memberIds.length} 名成�?/p>
|
|
492
472
|
<p>所有成员可以在"我的任务"中查看并投票</p>
|
|
493
473
|
<button class="btn-primary" id="closeSuccessMsg">关闭</button>
|
|
494
474
|
</div>
|
|
@@ -558,7 +538,7 @@ export class CollaborationEnhancer {
|
|
|
558
538
|
votes: selected
|
|
559
539
|
});
|
|
560
540
|
|
|
561
|
-
alert('
|
|
541
|
+
alert('投票成功�?);
|
|
562
542
|
});
|
|
563
543
|
}
|
|
564
544
|
|
|
@@ -580,7 +560,7 @@ export class CollaborationEnhancer {
|
|
|
580
560
|
`;
|
|
581
561
|
|
|
582
562
|
container.querySelector('#stopShare').addEventListener('click', () => {
|
|
583
|
-
container.innerHTML = '<p
|
|
563
|
+
container.innerHTML = '<p>屏幕共享已停�?/p>';
|
|
584
564
|
});
|
|
585
565
|
} catch (error) {
|
|
586
566
|
alert('屏幕共享失败: ' + error.message);
|
|
@@ -602,10 +582,10 @@ export class CollaborationEnhancer {
|
|
|
602
582
|
<option value="html">HTML</option>
|
|
603
583
|
<option value="css">CSS</option>
|
|
604
584
|
</select>
|
|
605
|
-
<button class="btn-secondary" id="formatCode"
|
|
585
|
+
<button class="btn-secondary" id="formatCode">格式�?/button>
|
|
606
586
|
<button class="btn-primary" id="runCode">运行</button>
|
|
607
587
|
</div>
|
|
608
|
-
<textarea id="codeEditor" placeholder="
|
|
588
|
+
<textarea id="codeEditor" placeholder="在这里编写代�?.."></textarea>
|
|
609
589
|
<div class="code-output" id="codeOutput">
|
|
610
590
|
<h4>输出</h4>
|
|
611
591
|
<pre id="outputContent"></pre>
|
|
@@ -616,10 +596,8 @@ export class CollaborationEnhancer {
|
|
|
616
596
|
const editor = container.querySelector('#codeEditor');
|
|
617
597
|
const output = container.querySelector('#outputContent');
|
|
618
598
|
|
|
619
|
-
//
|
|
620
|
-
|
|
621
|
-
// 简单的格式化(实际应使用专业的格式化库)
|
|
622
|
-
const code = editor.value;
|
|
599
|
+
// 格式化代�? container.querySelector('#formatCode').addEventListener('click', () => {
|
|
600
|
+
// 简单的格式化(实际应使用专业的格式化库�? const code = editor.value;
|
|
623
601
|
const formatted = code.split('\n').map(line => line.trim()).join('\n');
|
|
624
602
|
editor.value = formatted;
|
|
625
603
|
});
|
|
@@ -652,15 +630,14 @@ export class CollaborationEnhancer {
|
|
|
652
630
|
}
|
|
653
631
|
|
|
654
632
|
/**
|
|
655
|
-
* 思维导图 -
|
|
656
|
-
*/
|
|
633
|
+
* 思维导图 - 增强�? */
|
|
657
634
|
createMindMap(container) {
|
|
658
635
|
container.innerHTML = `
|
|
659
636
|
<div class="mindmap-container">
|
|
660
637
|
<div class="mindmap-toolbar">
|
|
661
|
-
<button class="btn-secondary" id="addChildNode"
|
|
662
|
-
<button class="btn-secondary" id="addSiblingNode"
|
|
663
|
-
<button class="btn-danger" id="deleteNode"
|
|
638
|
+
<button class="btn-secondary" id="addChildNode">�?添加子节�?/button>
|
|
639
|
+
<button class="btn-secondary" id="addSiblingNode">�?添加同级节点</button>
|
|
640
|
+
<button class="btn-danger" id="deleteNode">🗑�?删除节点</button>
|
|
664
641
|
<button class="btn-secondary" id="toggleLines">🔗 显示/隐藏连线</button>
|
|
665
642
|
<button class="btn-secondary" id="autoLayout">📐 自动布局</button>
|
|
666
643
|
<button class="btn-primary" id="saveMindMap">💾 保存</button>
|
|
@@ -670,18 +647,18 @@ export class CollaborationEnhancer {
|
|
|
670
647
|
<svg class="mindmap-lines" id="mindmapLines"></svg>
|
|
671
648
|
<div class="mindmap-node root" data-id="root" data-level="0" style="left: 400px; top: 50px;">
|
|
672
649
|
<div class="node-actions">
|
|
673
|
-
<button class="node-action-btn add" title="
|
|
650
|
+
<button class="node-action-btn add" title="添加子节�?>+</button>
|
|
674
651
|
</div>
|
|
675
652
|
<div class="node-content" contenteditable="true">中心主题</div>
|
|
676
653
|
</div>
|
|
677
654
|
<div class="mindmap-legend">
|
|
678
655
|
<div class="mindmap-legend-item">
|
|
679
656
|
<div class="legend-color" style="background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);"></div>
|
|
680
|
-
<span
|
|
657
|
+
<span>根节�?/span>
|
|
681
658
|
</div>
|
|
682
659
|
<div class="mindmap-legend-item">
|
|
683
660
|
<div class="legend-color" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%);"></div>
|
|
684
|
-
<span
|
|
661
|
+
<span>一级节�?/span>
|
|
685
662
|
</div>
|
|
686
663
|
<div class="mindmap-legend-item">
|
|
687
664
|
<div class="legend-color" style="background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);"></div>
|
|
@@ -703,16 +680,14 @@ export class CollaborationEnhancer {
|
|
|
703
680
|
nodes.set('root', { element: rootNode, parent: null, children: [] });
|
|
704
681
|
this.makeDraggable(rootNode, () => this.updateLines(container, connections, showLines));
|
|
705
682
|
|
|
706
|
-
//
|
|
707
|
-
const addChildNode = (parentNode) => {
|
|
683
|
+
// 添加子节�? const addChildNode = (parentNode) => {
|
|
708
684
|
const canvas = container.querySelector('#mindmapCanvas');
|
|
709
685
|
const parentId = parentNode.dataset.id;
|
|
710
686
|
const parentLevel = parseInt(parentNode.dataset.level || 0);
|
|
711
687
|
const childLevel = parentLevel + 1;
|
|
712
688
|
const nodeId = `node_${++nodeCount}`;
|
|
713
689
|
|
|
714
|
-
//
|
|
715
|
-
let nodeClass = 'mindmap-node';
|
|
690
|
+
// 确定节点样式�? let nodeClass = 'mindmap-node';
|
|
716
691
|
if (childLevel === 1) nodeClass += ' child';
|
|
717
692
|
else if (childLevel >= 2) nodeClass += ' grandchild';
|
|
718
693
|
|
|
@@ -731,10 +706,10 @@ export class CollaborationEnhancer {
|
|
|
731
706
|
node.style.top = top + 'px';
|
|
732
707
|
node.innerHTML = `
|
|
733
708
|
<div class="node-actions">
|
|
734
|
-
<button class="node-action-btn add" title="
|
|
709
|
+
<button class="node-action-btn add" title="添加子节�?>+</button>
|
|
735
710
|
<button class="node-action-btn delete" title="删除节点">×</button>
|
|
736
711
|
</div>
|
|
737
|
-
<div class="node-content" contenteditable="true"
|
|
712
|
+
<div class="node-content" contenteditable="true">新节�?/div>
|
|
738
713
|
`;
|
|
739
714
|
|
|
740
715
|
canvas.appendChild(node);
|
|
@@ -760,15 +735,13 @@ export class CollaborationEnhancer {
|
|
|
760
735
|
return node;
|
|
761
736
|
};
|
|
762
737
|
|
|
763
|
-
//
|
|
764
|
-
this.bindNodeActions(rootNode, container, nodes, connections, showLines);
|
|
738
|
+
// 绑定根节点操作按�? this.bindNodeActions(rootNode, container, nodes, connections, showLines);
|
|
765
739
|
|
|
766
|
-
//
|
|
767
|
-
container.querySelector('#addChildNode').addEventListener('click', () => {
|
|
740
|
+
// 添加子节点按�? container.querySelector('#addChildNode').addEventListener('click', () => {
|
|
768
741
|
if (selectedNode) {
|
|
769
742
|
addChildNode(selectedNode);
|
|
770
743
|
} else {
|
|
771
|
-
alert('
|
|
744
|
+
alert('请先选择一个节�?);
|
|
772
745
|
}
|
|
773
746
|
});
|
|
774
747
|
|
|
@@ -780,7 +753,7 @@ export class CollaborationEnhancer {
|
|
|
780
753
|
addChildNode(parentNode);
|
|
781
754
|
}
|
|
782
755
|
} else {
|
|
783
|
-
alert('
|
|
756
|
+
alert('根节点没有同级节�?);
|
|
784
757
|
}
|
|
785
758
|
});
|
|
786
759
|
|
|
@@ -791,7 +764,7 @@ export class CollaborationEnhancer {
|
|
|
791
764
|
this.updateLines(container, connections, showLines);
|
|
792
765
|
selectedNode = null;
|
|
793
766
|
} else {
|
|
794
|
-
alert('
|
|
767
|
+
alert('请选择要删除的节点(根节点不能删除�?);
|
|
795
768
|
}
|
|
796
769
|
});
|
|
797
770
|
|
|
@@ -835,34 +808,30 @@ export class CollaborationEnhancer {
|
|
|
835
808
|
|
|
836
809
|
console.log('思维导图数据:', data);
|
|
837
810
|
|
|
838
|
-
//
|
|
839
|
-
|
|
840
|
-
alert('思维导图已保存到本地!');
|
|
811
|
+
// 保存到本地存�? localStorage.setItem('mindmap_data', JSON.stringify(data));
|
|
812
|
+
alert('思维导图已保存到本地�?);
|
|
841
813
|
});
|
|
842
814
|
|
|
843
|
-
//
|
|
844
|
-
container.querySelector('#exportImage').addEventListener('click', async () => {
|
|
815
|
+
// 导出为图�? container.querySelector('#exportImage').addEventListener('click', async () => {
|
|
845
816
|
try {
|
|
846
817
|
const canvas = container.querySelector('#mindmapCanvas');
|
|
847
818
|
|
|
848
|
-
// 使用 html2canvas
|
|
849
|
-
if (typeof html2canvas !== 'undefined') {
|
|
819
|
+
// 使用 html2canvas 库(如果可用�? if (typeof html2canvas !== 'undefined') {
|
|
850
820
|
const canvasElement = await html2canvas(canvas);
|
|
851
821
|
const link = document.createElement('a');
|
|
852
822
|
link.download = `mindmap_${Date.now()}.png`;
|
|
853
823
|
link.href = canvasElement.toDataURL();
|
|
854
824
|
link.click();
|
|
855
825
|
} else {
|
|
856
|
-
alert('
|
|
826
|
+
alert('导出图片功能需�?html2canvas 库支持。\n您可以使用浏览器的截图功能来保存思维导图�?);
|
|
857
827
|
}
|
|
858
828
|
} catch (error) {
|
|
859
829
|
console.error('导出失败:', error);
|
|
860
|
-
alert('
|
|
830
|
+
alert('导出失败,请使用浏览器截图功�?);
|
|
861
831
|
}
|
|
862
832
|
});
|
|
863
833
|
|
|
864
|
-
//
|
|
865
|
-
this.updateLines(container, connections, showLines);
|
|
834
|
+
// 初始化连�? this.updateLines(container, connections, showLines);
|
|
866
835
|
}
|
|
867
836
|
|
|
868
837
|
/**
|
|
@@ -899,10 +868,10 @@ export class CollaborationEnhancer {
|
|
|
899
868
|
newNode.style.top = top + 'px';
|
|
900
869
|
newNode.innerHTML = `
|
|
901
870
|
<div class="node-actions">
|
|
902
|
-
<button class="node-action-btn add" title="
|
|
871
|
+
<button class="node-action-btn add" title="添加子节�?>+</button>
|
|
903
872
|
<button class="node-action-btn delete" title="删除节点">×</button>
|
|
904
873
|
</div>
|
|
905
|
-
<div class="node-content" contenteditable="true"
|
|
874
|
+
<div class="node-content" contenteditable="true">新节�?/div>
|
|
906
875
|
`;
|
|
907
876
|
|
|
908
877
|
canvas.appendChild(newNode);
|
|
@@ -947,8 +916,7 @@ export class CollaborationEnhancer {
|
|
|
947
916
|
// 删除所有子节点
|
|
948
917
|
info.children.forEach(childId => deleteRecursive(childId));
|
|
949
918
|
|
|
950
|
-
//
|
|
951
|
-
if (info.parent) {
|
|
919
|
+
// 从父节点的子节点列表中移�? if (info.parent) {
|
|
952
920
|
const parentInfo = nodes.get(info.parent);
|
|
953
921
|
if (parentInfo) {
|
|
954
922
|
parentInfo.children = parentInfo.children.filter(cid => cid !== id);
|
|
@@ -963,16 +931,14 @@ export class CollaborationEnhancer {
|
|
|
963
931
|
info.element.remove();
|
|
964
932
|
}
|
|
965
933
|
|
|
966
|
-
// 从nodes
|
|
967
|
-
nodes.delete(id);
|
|
934
|
+
// 从nodes中删�? nodes.delete(id);
|
|
968
935
|
};
|
|
969
936
|
|
|
970
937
|
deleteRecursive(nodeId);
|
|
971
938
|
}
|
|
972
939
|
|
|
973
940
|
/**
|
|
974
|
-
* 更新连线 -
|
|
975
|
-
*/
|
|
941
|
+
* 更新连线 - 性能优化�? */
|
|
976
942
|
updateLines(container, connections, showLines) {
|
|
977
943
|
const svg = container.querySelector('#mindmapLines');
|
|
978
944
|
if (!svg) return;
|
|
@@ -1013,8 +979,7 @@ export class CollaborationEnhancer {
|
|
|
1013
979
|
const x2 = childRect.left - canvasRect.left;
|
|
1014
980
|
const y2 = childRect.top - canvasRect.top + childRect.height / 2;
|
|
1015
981
|
|
|
1016
|
-
//
|
|
1017
|
-
const line = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
982
|
+
// 创建贝塞尔曲�? const line = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1018
983
|
const midX = (x1 + x2) / 2;
|
|
1019
984
|
const d = `M ${x1} ${y1} C ${midX} ${y1}, ${midX} ${y2}, ${x2} ${y2}`;
|
|
1020
985
|
line.setAttribute('d', d);
|
|
@@ -1026,8 +991,7 @@ export class CollaborationEnhancer {
|
|
|
1026
991
|
tempSvg.appendChild(line);
|
|
1027
992
|
});
|
|
1028
993
|
|
|
1029
|
-
//
|
|
1030
|
-
svg.innerHTML = tempSvg.innerHTML;
|
|
994
|
+
// 一次性替换所有内�? svg.innerHTML = tempSvg.innerHTML;
|
|
1031
995
|
});
|
|
1032
996
|
}
|
|
1033
997
|
|
|
@@ -1038,12 +1002,10 @@ export class CollaborationEnhancer {
|
|
|
1038
1002
|
const rootNode = nodes.get('root');
|
|
1039
1003
|
if (!rootNode) return;
|
|
1040
1004
|
|
|
1041
|
-
//
|
|
1042
|
-
rootNode.element.style.left = '50px';
|
|
1005
|
+
// 设置根节点位�? rootNode.element.style.left = '50px';
|
|
1043
1006
|
rootNode.element.style.top = '50px';
|
|
1044
1007
|
|
|
1045
|
-
//
|
|
1046
|
-
const layoutChildren = (parentId, startX, startY, verticalSpacing = 100) => {
|
|
1008
|
+
// 递归布局子节�? const layoutChildren = (parentId, startX, startY, verticalSpacing = 100) => {
|
|
1047
1009
|
const parentInfo = nodes.get(parentId);
|
|
1048
1010
|
if (!parentInfo || parentInfo.children.length === 0) return;
|
|
1049
1011
|
|
|
@@ -1056,8 +1018,7 @@ export class CollaborationEnhancer {
|
|
|
1056
1018
|
childInfo.element.style.left = startX + 'px';
|
|
1057
1019
|
childInfo.element.style.top = currentY + 'px';
|
|
1058
1020
|
|
|
1059
|
-
//
|
|
1060
|
-
layoutChildren(childId, startX + 250, currentY, verticalSpacing);
|
|
1021
|
+
// 递归布局子节点的子节�? layoutChildren(childId, startX + 250, currentY, verticalSpacing);
|
|
1061
1022
|
|
|
1062
1023
|
currentY += verticalSpacing;
|
|
1063
1024
|
});
|
|
@@ -1065,12 +1026,11 @@ export class CollaborationEnhancer {
|
|
|
1065
1026
|
|
|
1066
1027
|
layoutChildren('root', 300, 50);
|
|
1067
1028
|
|
|
1068
|
-
alert('
|
|
1029
|
+
alert('自动布局完成�?);
|
|
1069
1030
|
}
|
|
1070
1031
|
|
|
1071
1032
|
/**
|
|
1072
|
-
* 使元素可拖动 -
|
|
1073
|
-
*/
|
|
1033
|
+
* 使元素可拖动 - 性能优化�? */
|
|
1074
1034
|
makeDraggable(element, onDragEnd) {
|
|
1075
1035
|
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
|
|
1076
1036
|
let isDragging = false;
|
|
@@ -1113,7 +1073,7 @@ export class CollaborationEnhancer {
|
|
|
1113
1073
|
pos3 = e.clientX;
|
|
1114
1074
|
pos4 = e.clientY;
|
|
1115
1075
|
|
|
1116
|
-
// 直接使用 transform
|
|
1076
|
+
// 直接使用 transform 而不�?top/left,性能更好
|
|
1117
1077
|
const newTop = element.offsetTop - pos2;
|
|
1118
1078
|
const newLeft = element.offsetLeft - pos1;
|
|
1119
1079
|
|
|
@@ -1127,8 +1087,7 @@ export class CollaborationEnhancer {
|
|
|
1127
1087
|
document.onmouseup = null;
|
|
1128
1088
|
document.onmousemove = null;
|
|
1129
1089
|
|
|
1130
|
-
//
|
|
1131
|
-
if (animationFrameId) {
|
|
1090
|
+
// 取消未完成的动画�? if (animationFrameId) {
|
|
1132
1091
|
cancelAnimationFrame(animationFrameId);
|
|
1133
1092
|
animationFrameId = null;
|
|
1134
1093
|
}
|
|
@@ -1138,8 +1097,7 @@ export class CollaborationEnhancer {
|
|
|
1138
1097
|
element.style.zIndex = '';
|
|
1139
1098
|
element.style.transition = ''; // 恢复过渡动画
|
|
1140
1099
|
|
|
1141
|
-
//
|
|
1142
|
-
if (typeof onDragEnd === 'function') {
|
|
1100
|
+
// 拖动结束后才更新连线(而不是拖动过程中�? if (typeof onDragEnd === 'function') {
|
|
1143
1101
|
// 使用 setTimeout 确保 DOM 更新完成
|
|
1144
1102
|
setTimeout(() => {
|
|
1145
1103
|
onDragEnd();
|