collabdocchat 2.0.4 → 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.
@@ -28,14 +28,14 @@ export function renderAdminDashboard(user, wsService) {
28
28
  <aside class="sidebar">
29
29
  <div class="sidebar-header">
30
30
  <h2>CollabDocChat</h2>
31
- <span class="badge-admin">管理员</span>
31
+ <span class="badge-admin">管理�?/span>
32
32
  </div>
33
33
 
34
34
  <div class="user-info">
35
35
  <div class="avatar">${user.username[0].toUpperCase()}</div>
36
36
  <div>
37
37
  <div class="username">${user.username}</div>
38
- <div class="user-role">管理员</div>
38
+ <div class="user-role">管理�?/div>
39
39
  </div>
40
40
  </div>
41
41
 
@@ -50,11 +50,9 @@ export function renderAdminDashboard(user, wsService) {
50
50
  <span class="icon">📄</span> 文档管理
51
51
  </button>
52
52
  <button class="nav-item" data-view="knowledge">
53
- <span class="icon">📚</span> 知识库
54
- </button>
53
+ <span class="icon">📚</span> 知识�? </button>
55
54
  <button class="nav-item" data-view="workflows">
56
- <span class="icon">🔄</span> 工作流
57
- </button>
55
+ <span class="icon">🔄</span> 工作�? </button>
58
56
  <button class="nav-item" data-view="files">
59
57
  <span class="icon">📎</span> 文件管理
60
58
  </button>
@@ -80,11 +78,11 @@ export function renderAdminDashboard(user, wsService) {
80
78
  <span class="icon">⚙️</span> 设置
81
79
  </button>
82
80
  <button class="nav-item" data-view="help">
83
- <span class="icon">❓</span> 帮助
81
+ <span class="icon">�?/span> 帮助
84
82
  </button>
85
83
  </div>
86
84
 
87
- <button class="btn-logout" id="logoutBtn">退出登录</button>
85
+ <button class="btn-logout" id="logoutBtn">退出登�?/button>
88
86
  </aside>
89
87
 
90
88
  <main class="main-content">
@@ -103,8 +101,7 @@ export function renderAdminDashboard(user, wsService) {
103
101
  });
104
102
  });
105
103
 
106
- // 退出登录
107
- document.getElementById('logoutBtn').addEventListener('click', () => {
104
+ // 退出登�? document.getElementById('logoutBtn').addEventListener('click', () => {
108
105
  authService.logout();
109
106
  });
110
107
 
@@ -125,8 +122,7 @@ export function renderAdminDashboard(user, wsService) {
125
122
  await renderOptimizedKnowledgeView(contentArea, currentGroup, apiService);
126
123
  break;
127
124
  case 'workflows':
128
- // 使用优化后的工作流视图(可视化创建,无需JSON代码)
129
- if (typeof window.renderOptimizedWorkflowView === 'function') {
125
+ // 使用优化后的工作流视图(可视化创建,无需JSON代码�? if (typeof window.renderOptimizedWorkflowView === 'function') {
130
126
  await window.renderOptimizedWorkflowView(contentArea, currentGroup, apiService);
131
127
  } else {
132
128
  await renderWorkflowsView(contentArea);
@@ -171,11 +167,11 @@ export function renderAdminDashboard(user, wsService) {
171
167
  <div class="groups-grid" id="groupsList"></div>
172
168
  <div id="createGroupModal" class="modal hidden">
173
169
  <div class="modal-content">
174
- <h3>创建新群组</h3>
170
+ <h3>创建新群�?/h3>
175
171
  <form id="createGroupForm">
176
172
  <div class="form-group">
177
173
  <label>群组名称</label>
178
- <input type="text" name="name" placeholder="请输入群组名称" required>
174
+ <input type="text" name="name" placeholder="请输入群组名�? required>
179
175
  </div>
180
176
  <div class="form-group">
181
177
  <label>群组描述</label>
@@ -184,7 +180,7 @@ export function renderAdminDashboard(user, wsService) {
184
180
  <div class="form-group">
185
181
  <label>添加成员(可选)</label>
186
182
  <div id="usersList" style="max-height: 200px; overflow-y: auto; border: 1px solid var(--border); border-radius: 8px; padding: 10px;">
187
- <p>加载中...</p>
183
+ <p>加载�?..</p>
188
184
  </div>
189
185
  </div>
190
186
  <div style="display: flex; gap: 10px;">
@@ -199,7 +195,7 @@ export function renderAdminDashboard(user, wsService) {
199
195
  <h3>管理成员</h3>
200
196
  <div id="currentMembers"></div>
201
197
  <div class="form-group">
202
- <label>添加新成员</label>
198
+ <label>添加新成�?/label>
203
199
  <div id="availableUsers"></div>
204
200
  </div>
205
201
  <button type="button" class="btn-secondary" id="closeMembersModal">关闭</button>
@@ -230,7 +226,7 @@ export function renderAdminDashboard(user, wsService) {
230
226
  btn.addEventListener('click', () => {
231
227
  currentGroup = groups.find(g => g._id === btn.dataset.id);
232
228
  wsService.joinGroup(currentGroup._id);
233
- alert(`已加入群组: ${currentGroup.name}`);
229
+ alert(`已加入群�? ${currentGroup.name}`);
234
230
  });
235
231
  });
236
232
 
@@ -265,7 +261,7 @@ export function renderAdminDashboard(user, wsService) {
265
261
  formData.get('description'),
266
262
  selectedUsers
267
263
  );
268
- alert('群组创建成功!');
264
+ alert('群组创建成功�?);
269
265
  await renderGroupsView(container);
270
266
  document.getElementById('createGroupModal').classList.add('hidden');
271
267
  } catch (error) {
@@ -283,7 +279,7 @@ export function renderAdminDashboard(user, wsService) {
283
279
  <label style="display: flex; align-items: center; gap: 10px; padding: 8px; cursor: pointer;">
284
280
  <input type="checkbox" value="${u._id}">
285
281
  <div class="avatar" style="width: 30px; height: 30px; font-size: 14px;">${u.username[0].toUpperCase()}</div>
286
- <span>${u.username} (${u.role === 'admin' ? '管理员' : '用户'})</span>
282
+ <span>${u.username} (${u.role === 'admin' ? '管理�? : '用户'})</span>
287
283
  </label>
288
284
  `).join('');
289
285
  } catch (error) {
@@ -306,7 +302,7 @@ export function renderAdminDashboard(user, wsService) {
306
302
  <div style="display: flex; align-items: center; justify-content: space-between; padding: 8px; border-bottom: 1px solid var(--border);">
307
303
  <div style="display: flex; align-items: center; gap: 10px;">
308
304
  <div class="avatar" style="width: 30px; height: 30px; font-size: 14px;">${member.username[0].toUpperCase()}</div>
309
- <span>${member.username} ${member._id.toString() === group.admin._id.toString() ? '(管理员)' : ''}</span>
305
+ <span>${member.username} ${member._id.toString() === group.admin._id.toString() ? '(管理�?' : ''}</span>
310
306
  </div>
311
307
  ${member._id.toString() !== group.admin._id.toString() ?
312
308
  `<button class="btn-secondary btn-sm" onclick="removeMember('${groupId}', '${member._id}')">移除</button>` :
@@ -321,7 +317,7 @@ export function renderAdminDashboard(user, wsService) {
321
317
 
322
318
  const availableUsersDiv = document.getElementById('availableUsers');
323
319
  if (availableUsers.length === 0) {
324
- availableUsersDiv.innerHTML = '<p>所有用户都已在群组中</p>';
320
+ availableUsersDiv.innerHTML = '<p>所有用户都已在群组�?/p>';
325
321
  } else {
326
322
  availableUsersDiv.innerHTML = availableUsers.map(u => `
327
323
  <div style="display: flex; align-items: center; justify-content: space-between; padding: 8px; border-bottom: 1px solid var(--border);">
@@ -341,11 +337,10 @@ export function renderAdminDashboard(user, wsService) {
341
337
  }
342
338
  }
343
339
 
344
- // 全局函数供按钮调用
345
- window.addMember = async (groupId, userId) => {
340
+ // 全局函数供按钮调�? window.addMember = async (groupId, userId) => {
346
341
  try {
347
342
  await apiService.addMember(groupId, userId);
348
- alert('成员添加成功!');
343
+ alert('成员添加成功�?);
349
344
  await showManageMembersModal(groupId);
350
345
  } catch (error) {
351
346
  alert('添加失败: ' + error.message);
@@ -356,7 +351,7 @@ export function renderAdminDashboard(user, wsService) {
356
351
  if (confirm('确定要移除该成员吗?')) {
357
352
  try {
358
353
  await apiService.removeMember(groupId, userId);
359
- alert('成员移除成功!');
354
+ alert('成员移除成功�?);
360
355
  await showManageMembersModal(groupId);
361
356
  } catch (error) {
362
357
  alert('移除失败: ' + error.message);
@@ -366,7 +361,7 @@ export function renderAdminDashboard(user, wsService) {
366
361
 
367
362
  async function renderTasksView(container) {
368
363
  if (!currentGroup) {
369
- container.innerHTML = '<div class="empty-state">请先选择一个群组</div>';
364
+ container.innerHTML = '<div class="empty-state">请先选择一个群�?/div>';
370
365
  return;
371
366
  }
372
367
 
@@ -380,15 +375,15 @@ export function renderAdminDashboard(user, wsService) {
380
375
  <div class="tasks-list" id="tasksList"></div>
381
376
  <div id="createTaskModal" class="modal hidden">
382
377
  <div class="modal-content">
383
- <h3>创建新任务</h3>
378
+ <h3>创建新任�?/h3>
384
379
  <form id="createTaskForm">
385
380
  <div class="form-group">
386
381
  <label>任务标题</label>
387
- <input type="text" name="title" placeholder="请输入任务标题" required>
382
+ <input type="text" name="title" placeholder="请输入任务标�? required>
388
383
  </div>
389
384
  <div class="form-group">
390
385
  <label>任务描述</label>
391
- <textarea name="description" placeholder="请输入任务描述"></textarea>
386
+ <textarea name="description" placeholder="请输入任务描�?></textarea>
392
387
  </div>
393
388
  <div class="form-group">
394
389
  <label>截止日期</label>
@@ -408,7 +403,7 @@ export function renderAdminDashboard(user, wsService) {
408
403
  <button class="modal-close" id="closeTaskDetailModal">&times;</button>
409
404
  </div>
410
405
  <div class="modal-body" id="taskDetailContent">
411
- <div class="loading">加载中...</div>
406
+ <div class="loading">加载�?..</div>
412
407
  </div>
413
408
  </div>
414
409
  </div>
@@ -431,16 +426,16 @@ export function renderAdminDashboard(user, wsService) {
431
426
  <div style="display: flex; justify-content: space-between; align-items: start;">
432
427
  <div style="flex: 1;">
433
428
  <h3>${task.title}</h3>
434
- <p>${task.description || '无描述'}</p>
429
+ <p>${task.description || '无描�?}</p>
435
430
  <div class="task-meta">
436
431
  <span class="status-badge">${getStatusText(task.status)}</span>
437
- <span>截止: ${task.deadline ? new Date(task.deadline).toLocaleDateString() : '无'}</span>
438
- <span>完成率: ${completionRate}% (${completedMembers}/${totalMembers})</span>
432
+ <span>截止: ${task.deadline ? new Date(task.deadline).toLocaleDateString() : '�?}</span>
433
+ <span>完成�? ${completionRate}% (${completedMembers}/${totalMembers})</span>
439
434
  </div>
440
435
  </div>
441
436
  <div style="display: flex; gap: 10px;">
442
437
  <button class="btn-primary btn-sm" data-id="${task._id}" data-action="view-detail" title="查看详细">📊 详情</button>
443
- <button class="btn-danger btn-sm" data-id="${task._id}" data-action="delete-task" title="删除任务" style="min-width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;">🗑️ 删除</button>
438
+ <button class="btn-danger btn-sm" data-id="${task._id}" data-action="delete-task" title="删除任务" style="min-width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;">🗑�?删除</button>
444
439
  </div>
445
440
  </div>
446
441
  `;
@@ -461,10 +456,10 @@ export function renderAdminDashboard(user, wsService) {
461
456
  btn.addEventListener('click', async (e) => {
462
457
  e.stopPropagation();
463
458
  const taskId = btn.dataset.id;
464
- if (confirm('确定要删除这个任务吗?删除后无法恢复!')) {
459
+ if (confirm('确定要删除这个任务吗?删除后无法恢复�?)) {
465
460
  try {
466
461
  await apiService.deleteTask(taskId);
467
- alert('任务删除成功!');
462
+ alert('任务删除成功�?);
468
463
  await renderTasksView(container);
469
464
  } catch (error) {
470
465
  console.error('删除任务错误:', error);
@@ -487,18 +482,16 @@ export function renderAdminDashboard(user, wsService) {
487
482
  e.preventDefault();
488
483
  const formData = new FormData(e.target);
489
484
  try {
490
- // 获取群组信息,自动分配给所有成员
491
- const groupResult = await apiService.getGroup(currentGroup._id);
485
+ // 获取群组信息,自动分配给所有成�? const groupResult = await apiService.getGroup(currentGroup._id);
492
486
  const memberIds = groupResult.group.members.map(m => m._id);
493
487
 
494
488
  await apiService.createTask({
495
489
  title: formData.get('title'),
496
490
  description: formData.get('description'),
497
491
  groupId: currentGroup._id,
498
- assignedTo: memberIds, // 分配给所有成员
499
- deadline: formData.get('deadline') || null
492
+ assignedTo: memberIds, // 分配给所有成�? deadline: formData.get('deadline') || null
500
493
  });
501
- alert('任务创建成功!已分配给所有群组成员');
494
+ alert('任务创建成功!已分配给所有群组成�?);
502
495
  await renderTasksView(container);
503
496
  document.getElementById('createTaskModal').classList.add('hidden');
504
497
  } catch (error) {
@@ -513,8 +506,7 @@ export function renderAdminDashboard(user, wsService) {
513
506
  const modal = document.getElementById('taskDetailModal');
514
507
  const content = document.getElementById('taskDetailContent');
515
508
 
516
- // 先设置关闭事件(只设置一次,避免重复)
517
- const closeBtn = document.getElementById('closeTaskDetailModal');
509
+ // 先设置关闭事件(只设置一次,避免重复�? const closeBtn = document.getElementById('closeTaskDetailModal');
518
510
  if (closeBtn && !closeBtn.dataset.listenerSet) {
519
511
  closeBtn.addEventListener('click', () => {
520
512
  modal.classList.add('hidden');
@@ -534,7 +526,7 @@ export function renderAdminDashboard(user, wsService) {
534
526
 
535
527
  try {
536
528
  modal.classList.remove('hidden');
537
- content.innerHTML = '<div class="loading">加载中...</div>';
529
+ content.innerHTML = '<div class="loading">加载�?..</div>';
538
530
 
539
531
  // 获取任务详情
540
532
  const taskResult = await apiService.getTask(taskId);
@@ -542,13 +534,12 @@ export function renderAdminDashboard(user, wsService) {
542
534
 
543
535
  // 验证必要数据
544
536
  if (!task) {
545
- throw new Error('任务数据不存在');
537
+ throw new Error('任务数据不存�?);
546
538
  }
547
539
 
548
- // 群组信息已经在task.group中(后端已populate
549
- const group = task.group;
540
+ // 群组信息已经在task.group中(后端已populate�? const group = task.group;
550
541
  if (!group || !group.members) {
551
- throw new Error('群组数据不完整');
542
+ throw new Error('群组数据不完�?);
552
543
  }
553
544
 
554
545
  // 计算完成情况
@@ -583,12 +574,11 @@ export function renderAdminDashboard(user, wsService) {
583
574
  };
584
575
  }).filter(m => m !== null);
585
576
 
586
- // 准备任务数据给美化组件
587
- const taskData = {
577
+ // 准备任务数据给美化组�? const taskData = {
588
578
  ...task,
589
579
  group: group.name,
590
580
  assignedTo: task.assignedTo && task.assignedTo.length > 0 && task.assignedTo[0].username ?
591
- task.assignedTo[0].username : '未分配',
581
+ task.assignedTo[0].username : '未分�?,
592
582
  members: assignedMembers,
593
583
  completedCount: completedMembers
594
584
  };
@@ -600,7 +590,7 @@ export function renderAdminDashboard(user, wsService) {
600
590
  console.error('加载任务详情失败:', error);
601
591
  content.innerHTML = `
602
592
  <div class="error-state">
603
- <h3>❌ 加载失败</h3>
593
+ <h3>�?加载失败</h3>
604
594
  <p>${error.message || '未知错误'}</p>
605
595
  <button class="btn-primary" onclick="document.getElementById('taskDetailModal').classList.add('hidden')">关闭</button>
606
596
  </div>
@@ -609,7 +599,7 @@ export function renderAdminDashboard(user, wsService) {
609
599
  }
610
600
  async function renderDocumentsView(container) {
611
601
  if (!currentGroup) {
612
- container.innerHTML = '<div class="empty-state">请先选择一个群组</div>';
602
+ container.innerHTML = '<div class="empty-state">请先选择一个群�?/div>';
613
603
  return;
614
604
  }
615
605
 
@@ -623,20 +613,20 @@ export function renderAdminDashboard(user, wsService) {
623
613
  <div class="documents-list" id="docsList"></div>
624
614
  <div id="createDocModal" class="modal hidden">
625
615
  <div class="modal-content">
626
- <h3>创建新文档</h3>
616
+ <h3>创建新文�?/h3>
627
617
  <form id="createDocForm">
628
618
  <div class="form-group">
629
619
  <label>文档标题</label>
630
- <input type="text" name="title" placeholder="请输入文档标题" required>
620
+ <input type="text" name="title" placeholder="请输入文档标�? required>
631
621
  </div>
632
622
  <div class="form-group">
633
623
  <label>文档内容</label>
634
- <textarea name="content" placeholder="请输入文档内容" rows="6"></textarea>
624
+ <textarea name="content" placeholder="请输入文档内�? rows="6"></textarea>
635
625
  </div>
636
626
  <div class="form-group">
637
627
  <label>权限设置</label>
638
628
  <select name="permission">
639
- <option value="editable">可编辑</option>
629
+ <option value="editable">可编�?/option>
640
630
  <option value="readonly">只读</option>
641
631
  </select>
642
632
  </div>
@@ -661,13 +651,13 @@ export function renderAdminDashboard(user, wsService) {
661
651
  <div style="flex: 1;">
662
652
  <h3>📄 ${doc.title}</h3>
663
653
  <div class="doc-meta">
664
- <span>创建者: ${doc.creator.username}</span>
665
- <span>${doc.permission === 'readonly' ? '🔒 只读' : '✏️ 可编辑'}</span>
654
+ <span>创建�? ${doc.creator.username}</span>
655
+ <span>${doc.permission === 'readonly' ? '🔒 只读' : '✏️ 可编�?}</span>
666
656
  </div>
667
657
  </div>
668
658
  <div style="display: flex; gap: 10px; align-items: center;">
669
659
  <button class="btn-edit" data-id="${doc._id}">编辑</button>
670
- <button class="btn-danger btn-sm" data-id="${doc._id}" data-action="delete-doc" title="删除文档" style="min-width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;">🗑️ 删除</button>
660
+ <button class="btn-danger btn-sm" data-id="${doc._id}" data-action="delete-doc" title="删除文档" style="min-width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;">🗑�?删除</button>
671
661
  </div>
672
662
  </div>
673
663
  `;
@@ -686,10 +676,10 @@ export function renderAdminDashboard(user, wsService) {
686
676
  btn.addEventListener('click', async (e) => {
687
677
  e.stopPropagation();
688
678
  const docId = btn.dataset.id;
689
- if (confirm('确定要删除这个文档吗?删除后无法恢复!')) {
679
+ if (confirm('确定要删除这个文档吗?删除后无法恢复�?)) {
690
680
  try {
691
681
  await apiService.deleteDocument(docId);
692
- alert('文档删除成功!');
682
+ alert('文档删除成功�?);
693
683
  await renderDocumentsView(container);
694
684
  } catch (error) {
695
685
  console.error('删除文档错误:', error);
@@ -718,7 +708,7 @@ export function renderAdminDashboard(user, wsService) {
718
708
  currentGroup._id,
719
709
  formData.get('permission')
720
710
  );
721
- alert('文档创建成功!');
711
+ alert('文档创建成功�?);
722
712
  await renderDocumentsView(container);
723
713
  document.getElementById('createDocModal').classList.add('hidden');
724
714
  } catch (error) {
@@ -734,7 +724,7 @@ export function renderAdminDashboard(user, wsService) {
734
724
 
735
725
  container.innerHTML = `
736
726
  <div class="view-header">
737
- <button class="btn-back" id="backBtn">← 返回</button>
727
+ <button class="btn-back" id="backBtn">�?返回</button>
738
728
  <h2>${doc.title}</h2>
739
729
  <span class="doc-status">${doc.permission === 'readonly' ? '🔒 只读模式' : '✏️ 编辑模式'}</span>
740
730
  </div>
@@ -747,13 +737,12 @@ export function renderAdminDashboard(user, wsService) {
747
737
  </div>
748
738
  <div id="editor"></div>
749
739
  <div class="editor-footer">
750
- <span>最后编辑: ${new Date(doc.updatedAt).toLocaleString()}</span>
740
+ <span>最后编�? ${new Date(doc.updatedAt).toLocaleString()}</span>
751
741
  </div>
752
742
  </div>
753
743
  `;
754
744
 
755
- // 初始化 Quill 编辑器
756
- const quill = new Quill('#editor', {
745
+ // 初始�?Quill 编辑�? const quill = new Quill('#editor', {
757
746
  theme: 'snow',
758
747
  modules: {
759
748
  toolbar: [
@@ -798,7 +787,7 @@ export function renderAdminDashboard(user, wsService) {
798
787
  try {
799
788
  const content = quill.root.innerHTML;
800
789
  await apiService.updateDocument(documentId, content);
801
- alert('保存成功!');
790
+ alert('保存成功�?);
802
791
  } catch (error) {
803
792
  alert('保存失败: ' + error.message);
804
793
  }
@@ -815,8 +804,7 @@ export function renderAdminDashboard(user, wsService) {
815
804
  }
816
805
  });
817
806
 
818
- // 监听打字状态
819
- wsService.on('typing', (data) => {
807
+ // 监听打字状�? wsService.on('typing', (data) => {
820
808
  if (data.documentId === documentId && data.userId !== user.id) {
821
809
  const onlineUsers = document.getElementById('onlineUsers');
822
810
  if (data.isTyping) {
@@ -835,7 +823,7 @@ export function renderAdminDashboard(user, wsService) {
835
823
 
836
824
  async function renderFilesView(container) {
837
825
  if (!currentGroup) {
838
- container.innerHTML = '<div class="empty-state">请先选择一个群组</div>';
826
+ container.innerHTML = '<div class="empty-state">请先选择一个群�?/div>';
839
827
  return;
840
828
  }
841
829
 
@@ -860,7 +848,7 @@ export function renderAdminDashboard(user, wsService) {
860
848
  <div class="form-group">
861
849
  <label>选择文件</label>
862
850
  <input type="file" id="fileInput" required>
863
- <small>支持图片、PDF、Word、Excel等,最大10MB</small>
851
+ <small>支持图片、PDF、Word、Excel等,最�?0MB</small>
864
852
  </div>
865
853
  <div class="form-group">
866
854
  <label>描述(可选)</label>
@@ -892,7 +880,7 @@ export function renderAdminDashboard(user, wsService) {
892
880
  <div class="file-info">
893
881
  <h4>${file.originalName}</h4>
894
882
  <div class="file-meta">
895
- <span>上传者: ${file.uploader.username}</span>
883
+ <span>上传�? ${file.uploader.username}</span>
896
884
  <span>大小: ${fileSize}</span>
897
885
  <span>时间: ${new Date(file.createdAt).toLocaleString()}</span>
898
886
  </div>
@@ -915,7 +903,7 @@ export function renderAdminDashboard(user, wsService) {
915
903
  const token = localStorage.getItem('token');
916
904
 
917
905
  // 使用 fetch 下载文件
918
- const response = await fetch(`http://localhost:8765/api/files/${fileId}/download`, {
906
+ const response = await fetch(`http://localhost:3000/api/files/${fileId}/download`, {
919
907
  method: 'GET',
920
908
  headers: {
921
909
  'Authorization': `Bearer ${token}`
@@ -949,10 +937,10 @@ export function renderAdminDashboard(user, wsService) {
949
937
  // 删除文件事件
950
938
  document.querySelectorAll('[data-action="delete-file"]').forEach(btn => {
951
939
  btn.addEventListener('click', async () => {
952
- if (confirm('确定要删除这个文件吗?')) {
940
+ if (confirm('确定要删除这个文件吗�?)) {
953
941
  try {
954
942
  await apiService.deleteFile(btn.dataset.id);
955
- alert('文件删除成功!');
943
+ alert('文件删除成功�?);
956
944
  await renderFilesView(container);
957
945
  } catch (error) {
958
946
  alert('删除失败: ' + error.message);
@@ -989,7 +977,7 @@ export function renderAdminDashboard(user, wsService) {
989
977
 
990
978
  try {
991
979
  await apiService.uploadFile(currentGroup._id, fileInput.files[0], description);
992
- alert('文件上传成功!');
980
+ alert('文件上传成功�?);
993
981
  document.getElementById('uploadFileModal').classList.add('hidden');
994
982
  document.getElementById('uploadFileForm').reset();
995
983
  await renderFilesView(container);
@@ -1009,7 +997,7 @@ export function renderAdminDashboard(user, wsService) {
1009
997
  }
1010
998
 
1011
999
  function getFileIcon(mimetype) {
1012
- if (mimetype.startsWith('image/')) return '🖼️';
1000
+ if (mimetype.startsWith('image/')) return '🖼�?;
1013
1001
  if (mimetype === 'application/pdf') return '📕';
1014
1002
  if (mimetype.includes('word') || mimetype.includes('document')) return '📘';
1015
1003
  if (mimetype.includes('excel') || mimetype.includes('spreadsheet')) return '📗';
@@ -1029,7 +1017,7 @@ export function renderAdminDashboard(user, wsService) {
1029
1017
 
1030
1018
  async function renderChatView(container) {
1031
1019
  if (!currentGroup) {
1032
- container.innerHTML = '<div class="empty-state">请先选择一个群组</div>';
1020
+ container.innerHTML = '<div class="empty-state">请先选择一个群�?/div>';
1033
1021
  return;
1034
1022
  }
1035
1023
 
@@ -1044,10 +1032,10 @@ export function renderAdminDashboard(user, wsService) {
1044
1032
  🤖 AI
1045
1033
  </button>
1046
1034
  <button class="btn-secondary" id="showCollabTools" title="协作工具">
1047
- 🛠️ 工具
1035
+ 🛠�?工具
1048
1036
  </button>
1049
- <button class="btn-secondary" id="markAllRead" title="全部标记为已读">
1050
- ✓ 已读
1037
+ <button class="btn-secondary" id="markAllRead" title="全部标记为已�?>
1038
+ �?已读
1051
1039
  </button>
1052
1040
  <button class="btn-secondary" id="muteAllBtn">全体禁言</button>
1053
1041
  <button class="btn-secondary" id="manageMuteBtn">个人禁言</button>
@@ -1059,7 +1047,7 @@ export function renderAdminDashboard(user, wsService) {
1059
1047
  <div class="chat-input">
1060
1048
  <button class="btn-emoji" id="emojiBtn">😊</button>
1061
1049
  <input type="text" id="messageInput" placeholder="输入消息... (使用 @ 提及用户)">
1062
- <button class="btn-primary" id="sendBtn">发送</button>
1050
+ <button class="btn-primary" id="sendBtn">发�?/button>
1063
1051
  </div>
1064
1052
  <emoji-picker id="emojiPicker" class="hidden"></emoji-picker>
1065
1053
  </div>
@@ -1074,25 +1062,22 @@ export function renderAdminDashboard(user, wsService) {
1074
1062
  <div class="modal-content">
1075
1063
  <h3>清除聊天记录</h3>
1076
1064
  <div style="padding: 20px;">
1077
- <p style="margin-bottom: 20px; color: var(--danger);">⚠️ 警告:此操作不可恢复!</p>
1065
+ <p style="margin-bottom: 20px; color: var(--danger);">⚠️ 警告:此操作不可恢复�?/p>
1078
1066
  <div class="form-group">
1079
1067
  <label>
1080
1068
  <input type="radio" name="clearType" value="all" checked>
1081
- 清除所有聊天记录
1082
- </label>
1069
+ 清除所有聊天记�? </label>
1083
1070
  </div>
1084
1071
  <div class="form-group">
1085
1072
  <label>
1086
1073
  <input type="radio" name="clearType" value="before">
1087
- 清除指定日期之前的记录
1088
- </label>
1074
+ 清除指定日期之前的记�? </label>
1089
1075
  <input type="date" id="clearBeforeDate" style="margin-left: 10px; margin-top: 10px;" disabled>
1090
1076
  </div>
1091
1077
  <div class="form-group">
1092
1078
  <label>
1093
1079
  <input type="radio" name="clearType" value="user">
1094
- 清除指定用户的消息
1095
- </label>
1080
+ 清除指定用户的消�? </label>
1096
1081
  <select id="clearUserId" style="margin-left: 10px; margin-top: 10px;" disabled>
1097
1082
  <option value="">选择用户</option>
1098
1083
  </select>
@@ -1152,8 +1137,7 @@ export function renderAdminDashboard(user, wsService) {
1152
1137
  }
1153
1138
  });
1154
1139
 
1155
- // 表情包功能
1156
- emojiBtn.addEventListener('click', () => {
1140
+ // 表情包功�? emojiBtn.addEventListener('click', () => {
1157
1141
  emojiPicker.classList.toggle('hidden');
1158
1142
  });
1159
1143
 
@@ -1163,8 +1147,7 @@ export function renderAdminDashboard(user, wsService) {
1163
1147
  emojiPicker.classList.add('hidden');
1164
1148
  });
1165
1149
 
1166
- // 点击外部关闭表情选择器
1167
- document.addEventListener('click', (e) => {
1150
+ // 点击外部关闭表情选择�? document.addEventListener('click', (e) => {
1168
1151
  if (!emojiBtn.contains(e.target) && !emojiPicker.contains(e.target)) {
1169
1152
  emojiPicker.classList.add('hidden');
1170
1153
  }
@@ -1192,8 +1175,7 @@ export function renderAdminDashboard(user, wsService) {
1192
1175
  const messagesResult = await apiService.getGroupMessages(currentGroup._id);
1193
1176
  if (messagesResult.messages) {
1194
1177
  messagesResult.messages.forEach(msg => {
1195
- // 使用增强的消息渲染
1196
- features.chatEnhancements.renderMessage(msg, messagesDiv, group.members);
1178
+ // 使用增强的消息渲�? features.chatEnhancements.renderMessage(msg, messagesDiv, group.members);
1197
1179
  });
1198
1180
  messagesDiv.scrollTop = messagesDiv.scrollHeight;
1199
1181
  }
@@ -1208,8 +1190,7 @@ export function renderAdminDashboard(user, wsService) {
1208
1190
  };
1209
1191
  refreshMuteButtons();
1210
1192
 
1211
- // 全体禁言(服务端生效)
1212
- document.getElementById('muteAllBtn').addEventListener('click', async () => {
1193
+ // 全体禁言(服务端生效�? document.getElementById('muteAllBtn').addEventListener('click', async () => {
1213
1194
  try {
1214
1195
  const next = !isMutedAll;
1215
1196
  const res = await apiService.setMuteAll(currentGroup._id, next);
@@ -1218,7 +1199,7 @@ export function renderAdminDashboard(user, wsService) {
1218
1199
 
1219
1200
  const notification = document.createElement('div');
1220
1201
  notification.className = 'notification';
1221
- notification.textContent = isMutedAll ? '已开启全体禁言(成员无法发言)' : '已取消全体禁言';
1202
+ notification.textContent = isMutedAll ? '已开启全体禁言(成员无法发言�? : '已取消全体禁言';
1222
1203
  messagesDiv.appendChild(notification);
1223
1204
  messagesDiv.scrollTop = messagesDiv.scrollHeight;
1224
1205
  } catch (e) {
@@ -1226,10 +1207,8 @@ export function renderAdminDashboard(user, wsService) {
1226
1207
  }
1227
1208
  });
1228
1209
 
1229
- // 个人禁言(服务端生效)
1230
- document.getElementById('manageMuteBtn').addEventListener('click', async () => {
1231
- // 重新拉取最新 group(避免成员变动不同步)
1232
- const latest = await apiService.getGroup(currentGroup._id);
1210
+ // 个人禁言(服务端生效�? document.getElementById('manageMuteBtn').addEventListener('click', async () => {
1211
+ // 重新拉取最�?group(避免成员变动不同步�? const latest = await apiService.getGroup(currentGroup._id);
1233
1212
  group = latest.group;
1234
1213
  mutedUsers = new Set((group.mutedUsers || []).map(String));
1235
1214
 
@@ -1274,8 +1253,7 @@ export function renderAdminDashboard(user, wsService) {
1274
1253
 
1275
1254
  // 清除聊天记录功能
1276
1255
  document.getElementById('clearChatBtn').addEventListener('click', async () => {
1277
- // 加载群组成员到下拉列表
1278
- const clearUserId = document.getElementById('clearUserId');
1256
+ // 加载群组成员到下拉列�? const clearUserId = document.getElementById('clearUserId');
1279
1257
  clearUserId.innerHTML = '<option value="">选择用户</option>';
1280
1258
  group.members.forEach(member => {
1281
1259
  clearUserId.innerHTML += `<option value="${member._id}">${member.username}</option>`;
@@ -1302,14 +1280,14 @@ export function renderAdminDashboard(user, wsService) {
1302
1280
 
1303
1281
  let confirmMsg = '';
1304
1282
  if (clearType === 'all') {
1305
- confirmMsg = '确定要清除所有聊天记录吗?此操作不可恢复!';
1283
+ confirmMsg = '确定要清除所有聊天记录吗?此操作不可恢复�?;
1306
1284
  } else if (clearType === 'before') {
1307
1285
  const date = document.getElementById('clearBeforeDate').value;
1308
1286
  if (!date) {
1309
1287
  alert('请选择日期');
1310
1288
  return;
1311
1289
  }
1312
- confirmMsg = `确定要清除 ${date} 之前的所有聊天记录吗?此操作不可恢复!`;
1290
+ confirmMsg = `确定要清�?${date} 之前的所有聊天记录吗?此操作不可恢复!`;
1313
1291
  } else if (clearType === 'user') {
1314
1292
  const userId = document.getElementById('clearUserId').value;
1315
1293
  if (!userId) {
@@ -1317,7 +1295,7 @@ export function renderAdminDashboard(user, wsService) {
1317
1295
  return;
1318
1296
  }
1319
1297
  const username = group.members.find(m => m._id === userId)?.username;
1320
- confirmMsg = `确定要清除用户 ${username} 的所有消息吗?此操作不可恢复!`;
1298
+ confirmMsg = `确定要清除用�?${username} 的所有消息吗?此操作不可恢复!`;
1321
1299
  }
1322
1300
 
1323
1301
  if (!confirm(confirmMsg)) {
@@ -1344,8 +1322,7 @@ export function renderAdminDashboard(user, wsService) {
1344
1322
  const messagesResult = await apiService.getGroupMessages(currentGroup._id);
1345
1323
  if (messagesResult.messages) {
1346
1324
  messagesResult.messages.forEach(msg => {
1347
- // 使用增强的消息渲染
1348
- features.chatEnhancements.renderMessage(msg, messagesDiv, group.members);
1325
+ // 使用增强的消息渲�? features.chatEnhancements.renderMessage(msg, messagesDiv, group.members);
1349
1326
  });
1350
1327
  }
1351
1328
  } catch (error) {
@@ -1356,16 +1333,13 @@ export function renderAdminDashboard(user, wsService) {
1356
1333
  // 监听消息
1357
1334
  wsService.on('chat_message', (data) => {
1358
1335
  if (data.groupId === currentGroup._id) {
1359
- // 使用增强的消息渲染
1360
- features.chatEnhancements.renderMessage(data, messagesDiv, group.members);
1336
+ // 使用增强的消息渲�? features.chatEnhancements.renderMessage(data, messagesDiv, group.members);
1361
1337
  messagesDiv.scrollTop = messagesDiv.scrollHeight;
1362
1338
 
1363
- // 显示通知(如果不是自己发送的消息)
1364
- if (data.userId !== currentUserId) {
1339
+ // 显示通知(如果不是自己发送的消息�? if (data.userId !== currentUserId) {
1365
1340
  features.notifications.showNewMessageNotification(data.username, data.content);
1366
1341
 
1367
- // 检查是否@了当前用户
1368
- if (features.chatEnhancements.checkMentionsMe(data.content, group.members)) {
1342
+ // 检查是否@了当前用�? if (features.chatEnhancements.checkMentionsMe(data.content, group.members)) {
1369
1343
  features.notifications.showMentionNotification(data.username, data.content);
1370
1344
  }
1371
1345
  }
@@ -1400,19 +1374,17 @@ export function renderAdminDashboard(user, wsService) {
1400
1374
  }
1401
1375
  });
1402
1376
 
1403
- // 发送被拦截提示(来自服务端)
1404
- wsService.on('chat_blocked', (data) => {
1377
+ // 发送被拦截提示(来自服务端�? wsService.on('chat_blocked', (data) => {
1405
1378
  if (data.groupId === currentGroup._id) {
1406
1379
  const notification = document.createElement('div');
1407
1380
  notification.className = 'notification';
1408
- notification.textContent = data.message || '消息发送失败';
1381
+ notification.textContent = data.message || '消息发送失�?;
1409
1382
  messagesDiv.appendChild(notification);
1410
1383
  messagesDiv.scrollTop = messagesDiv.scrollHeight;
1411
1384
  }
1412
1385
  });
1413
1386
 
1414
- // 发送消息
1415
- const sendMessage = () => {
1387
+ // 发送消�? const sendMessage = () => {
1416
1388
  const content = messageInput.value.trim();
1417
1389
  if (content) {
1418
1390
  // 解析@提及
@@ -1424,8 +1396,7 @@ export function renderAdminDashboard(user, wsService) {
1424
1396
  // 显示AI智能回复建议(如果启用)
1425
1397
  if (localStorage.getItem('ai_smartReplies') !== 'false') {
1426
1398
  features.aiAssistant.getSmartReplies(content).then(replies => {
1427
- // 可以在这里显示智能回复建议
1428
- console.log('智能回复建议:', replies);
1399
+ // 可以在这里显示智能回复建�? console.log('智能回复建议:', replies);
1429
1400
  });
1430
1401
  }
1431
1402
  }
@@ -1439,7 +1410,7 @@ export function renderAdminDashboard(user, wsService) {
1439
1410
 
1440
1411
  async function renderCallView(container) {
1441
1412
  if (!currentGroup) {
1442
- container.innerHTML = '<div class="empty-state">请先选择一个群组</div>';
1413
+ container.innerHTML = '<div class="empty-state">请先选择一个群�?/div>';
1443
1414
  return;
1444
1415
  }
1445
1416
 
@@ -1451,7 +1422,7 @@ export function renderAdminDashboard(user, wsService) {
1451
1422
  <div class="call-controls">
1452
1423
  <label>点名人数:</label>
1453
1424
  <input type="number" id="callCount" value="1" min="1" max="10">
1454
- <button class="btn-primary btn-large" id="randomCallBtn">🎲 开始点名</button>
1425
+ <button class="btn-primary btn-large" id="randomCallBtn">🎲 开始点�?/button>
1455
1426
  </div>
1456
1427
  <div id="callResult" class="call-result"></div>
1457
1428
  </div>
@@ -1496,9 +1467,9 @@ export function renderAdminDashboard(user, wsService) {
1496
1467
  <option value="title_edit">标题编辑</option>
1497
1468
  <option value="document_permission_change">权限修改</option>
1498
1469
  </select>
1499
- <input type="date" id="startDate" class="form-input" title="开始日期">
1470
+ <input type="date" id="startDate" class="form-input" title="开始日�?>
1500
1471
  <input type="date" id="endDate" class="form-input" title="结束日期">
1501
- <button class="btn-primary" id="applyFilters">筛选</button>
1472
+ <button class="btn-primary" id="applyFilters">筛�?/button>
1502
1473
  <button class="btn-secondary" id="exportLogs">导出</button>
1503
1474
  </div>
1504
1475
  </div>
@@ -1519,13 +1490,13 @@ export function renderAdminDashboard(user, wsService) {
1519
1490
  </div>
1520
1491
 
1521
1492
  <div class="audit-logs" id="auditLogs">
1522
- <div class="loading">加载中...</div>
1493
+ <div class="loading">加载�?..</div>
1523
1494
  </div>
1524
1495
 
1525
1496
  <div class="pagination" id="auditPagination" style="display: none;">
1526
- <button class="btn-secondary" id="prevPage">上一页</button>
1527
- <span id="pageInfo">第 1 页,共 1 页</span>
1528
- <button class="btn-secondary" id="nextPage">下一页</button>
1497
+ <button class="btn-secondary" id="prevPage">上一�?/button>
1498
+ <span id="pageInfo">�?1 页,�?1 �?/span>
1499
+ <button class="btn-secondary" id="nextPage">下一�?/button>
1529
1500
  </div>
1530
1501
 
1531
1502
  <div id="auditDetailModal" class="modal hidden">
@@ -1560,7 +1531,7 @@ export function renderAdminDashboard(user, wsService) {
1560
1531
  async function loadAuditLogs(page = 1, filters = {}) {
1561
1532
  try {
1562
1533
  const auditLogsDiv = document.getElementById('auditLogs');
1563
- auditLogsDiv.innerHTML = '<div class="loading">加载中...</div>';
1534
+ auditLogsDiv.innerHTML = '<div class="loading">加载�?..</div>';
1564
1535
 
1565
1536
  const options = { page, limit: 20 };
1566
1537
  const result = await apiService.getAuditLogs(filters, options);
@@ -1600,7 +1571,7 @@ export function renderAdminDashboard(user, wsService) {
1600
1571
  // 更新分页
1601
1572
  const pagination = document.getElementById('auditPagination');
1602
1573
  const pageInfo = document.getElementById('pageInfo');
1603
- pageInfo.textContent = `第 ${result.pagination.page} 页,共 ${result.pagination.pages} 页`;
1574
+ pageInfo.textContent = `�?${result.pagination.page} 页,�?${result.pagination.pages} 页`;
1604
1575
 
1605
1576
  document.getElementById('prevPage').disabled = result.pagination.page <= 1;
1606
1577
  document.getElementById('nextPage').disabled = result.pagination.page >= result.pagination.pages;
@@ -1647,22 +1618,20 @@ export function renderAdminDashboard(user, wsService) {
1647
1618
  const content = document.getElementById('auditDetailContent');
1648
1619
 
1649
1620
  modal.classList.remove('hidden');
1650
- content.innerHTML = '<div class="loading">加载中...</div>';
1621
+ content.innerHTML = '<div class="loading">加载�?..</div>';
1651
1622
 
1652
1623
  // 获取审计日志详情
1653
1624
  const result = await apiService.getAuditLogDetail(logId);
1654
1625
  const log = result.log;
1655
1626
 
1656
- // 格式化详细信息
1657
- let detailsHtml = '';
1627
+ // 格式化详细信�? let detailsHtml = '';
1658
1628
  if (log.details) {
1659
1629
  detailsHtml = Object.entries(log.details).map(([key, value]) => {
1660
1630
  let displayValue = value;
1661
1631
 
1662
1632
  // 特殊处理某些字段
1663
1633
  if (key === 'oldContent' || key === 'newContent') {
1664
- // 截断过长的内容
1665
- if (typeof value === 'string' && value.length > 200) {
1634
+ // 截断过长的内�? if (typeof value === 'string' && value.length > 200) {
1666
1635
  displayValue = value.substring(0, 200) + '...';
1667
1636
  }
1668
1637
  } else if (typeof value === 'object') {
@@ -1671,7 +1640,7 @@ export function renderAdminDashboard(user, wsService) {
1671
1640
 
1672
1641
  return `
1673
1642
  <div class="info-row">
1674
- <span class="info-label">${formatFieldName(key)}:</span>
1643
+ <span class="info-label">${formatFieldName(key)}�?/span>
1675
1644
  <span class="info-value">${displayValue || '-'}</span>
1676
1645
  </div>
1677
1646
  `;
@@ -1682,19 +1651,19 @@ export function renderAdminDashboard(user, wsService) {
1682
1651
  <div class="audit-detail">
1683
1652
  <div class="task-detail-info">
1684
1653
  <div class="info-row">
1685
- <span class="info-label">操作时间:</span>
1654
+ <span class="info-label">操作时间�?/span>
1686
1655
  <span class="info-value">${new Date(log.createdAt).toLocaleString()}</span>
1687
1656
  </div>
1688
1657
  <div class="info-row">
1689
- <span class="info-label">操作用户:</span>
1658
+ <span class="info-label">操作用户�?/span>
1690
1659
  <span class="info-value">${log.user?.username || '未知用户'}</span>
1691
1660
  </div>
1692
1661
  <div class="info-row">
1693
- <span class="info-label">用户角色:</span>
1694
- <span class="info-value">${log.user?.role === 'admin' ? '管理员' : '普通用户'}</span>
1662
+ <span class="info-label">用户角色�?/span>
1663
+ <span class="info-value">${log.user?.role === 'admin' ? '管理�? : '普通用�?}</span>
1695
1664
  </div>
1696
1665
  <div class="info-row">
1697
- <span class="info-label">操作类型:</span>
1666
+ <span class="info-label">操作类型�?/span>
1698
1667
  <span class="info-value">
1699
1668
  <span class="action-badge action-${log.action}">${getActionText(log.action)}</span>
1700
1669
  </span>
@@ -1704,15 +1673,15 @@ export function renderAdminDashboard(user, wsService) {
1704
1673
  <span class="info-value">${log.metadata?.groupId?.name || '-'}</span>
1705
1674
  </div>
1706
1675
  <div class="info-row">
1707
- <span class="info-label">资源类型:</span>
1676
+ <span class="info-label">资源类型�?/span>
1708
1677
  <span class="info-value">${log.resourceType || '-'}</span>
1709
1678
  </div>
1710
1679
  <div class="info-row">
1711
- <span class="info-label">资源标题:</span>
1680
+ <span class="info-label">资源标题�?/span>
1712
1681
  <span class="info-value">${log.resourceTitle || log.resourceId || '-'}</span>
1713
1682
  </div>
1714
1683
  <div class="info-row">
1715
- <span class="info-label">IP地址:</span>
1684
+ <span class="info-label">IP地址�?/span>
1716
1685
  <span class="info-value">${log.metadata?.ipAddress || '-'}</span>
1717
1686
  </div>
1718
1687
  </div>
@@ -1744,12 +1713,12 @@ export function renderAdminDashboard(user, wsService) {
1744
1713
  function formatFieldName(key) {
1745
1714
  const fieldNames = {
1746
1715
  'description': '描述',
1747
- 'oldTitle': '原标题',
1748
- 'newTitle': '新标题',
1749
- 'oldContent': '原内容',
1750
- 'newContent': '新内容',
1751
- 'oldPermission': '原权限',
1752
- 'newPermission': '新权限',
1716
+ 'oldTitle': '原标�?,
1717
+ 'newTitle': '新标�?,
1718
+ 'oldContent': '原内�?,
1719
+ 'newContent': '新内�?,
1720
+ 'oldPermission': '原权�?,
1721
+ 'newPermission': '新权�?,
1753
1722
  'contentLength': '内容长度',
1754
1723
  'changes': '变更内容',
1755
1724
  'reason': '原因'
@@ -1766,8 +1735,7 @@ export function renderAdminDashboard(user, wsService) {
1766
1735
  endDate: document.getElementById('endDate').value
1767
1736
  };
1768
1737
 
1769
- // 移除空值
1770
- Object.keys(currentFilters).forEach(key => {
1738
+ // 移除空�? Object.keys(currentFilters).forEach(key => {
1771
1739
  if (!currentFilters[key]) {
1772
1740
  delete currentFilters[key];
1773
1741
  }
@@ -1809,7 +1777,7 @@ export function renderAdminDashboard(user, wsService) {
1809
1777
  </div>
1810
1778
  <div class="search-container">
1811
1779
  <div class="search-box">
1812
- <input type="text" id="searchInput" placeholder="搜索消息、文档、任务...">
1780
+ <input type="text" id="searchInput" placeholder="搜索消息、文档、任�?..">
1813
1781
  <button class="btn-primary" id="searchBtn">搜索</button>
1814
1782
  </div>
1815
1783
  <div class="search-filters">
@@ -1844,7 +1812,7 @@ export function renderAdminDashboard(user, wsService) {
1844
1812
  tasks: document.getElementById('filterTasks').checked
1845
1813
  };
1846
1814
 
1847
- searchResults.innerHTML = '<div class="loading">搜索中...</div>';
1815
+ searchResults.innerHTML = '<div class="loading">搜索�?..</div>';
1848
1816
 
1849
1817
  try {
1850
1818
  const results = [];
@@ -1927,7 +1895,7 @@ export function renderAdminDashboard(user, wsService) {
1927
1895
 
1928
1896
  // 显示结果
1929
1897
  if (results.length === 0) {
1930
- searchResults.innerHTML = '<div class="empty-state">未找到相关结果</div>';
1898
+ searchResults.innerHTML = '<div class="empty-state">未找到相关结�?/div>';
1931
1899
  } else {
1932
1900
  searchResults.innerHTML = results.map(result => {
1933
1901
  const typeIcon = {
@@ -1944,7 +1912,7 @@ export function renderAdminDashboard(user, wsService) {
1944
1912
  <h4>${highlightText(result.title, query)}</h4>
1945
1913
  <p>${highlightText(result.content, query)}</p>
1946
1914
  ${result.group ? `<span class="result-group">群组: ${result.group}</span>` : ''}
1947
- ${result.status ? `<span class="result-status">状态: ${getStatusText(result.status)}</span>` : ''}
1915
+ ${result.status ? `<span class="result-status">状�? ${getStatusText(result.status)}</span>` : ''}
1948
1916
  </div>
1949
1917
  `;
1950
1918
  }).join('');
@@ -1968,10 +1936,10 @@ export function renderAdminDashboard(user, wsService) {
1968
1936
 
1969
1937
  function getStatusText(status) {
1970
1938
  const statusMap = {
1971
- 'pending': '待处理',
1972
- 'in_progress': '进行中',
1973
- 'completed': '已完成',
1974
- 'terminated': '已终止'
1939
+ 'pending': '待处�?,
1940
+ 'in_progress': '进行�?,
1941
+ 'completed': '已完�?,
1942
+ 'terminated': '已终�?
1975
1943
  };
1976
1944
  return statusMap[status] || status;
1977
1945
  }
@@ -1994,13 +1962,13 @@ export function renderAdminDashboard(user, wsService) {
1994
1962
  const token = localStorage.getItem('token');
1995
1963
 
1996
1964
  // 获取备份列表
1997
- const backupsResponse = await fetch('http://localhost:8765/api/backup/list', {
1965
+ const backupsResponse = await fetch('http://localhost:3000/api/backup/list', {
1998
1966
  headers: { 'Authorization': `Bearer ${token}` }
1999
1967
  });
2000
1968
  const backupsResult = await backupsResponse.json();
2001
1969
 
2002
1970
  // 获取备份配置
2003
- const configResponse = await fetch('http://localhost:8765/api/backup/config', {
1971
+ const configResponse = await fetch('http://localhost:3000/api/backup/config', {
2004
1972
  headers: { 'Authorization': `Bearer ${token}` }
2005
1973
  });
2006
1974
  const configResult = await configResponse.json();
@@ -2021,15 +1989,15 @@ export function renderAdminDashboard(user, wsService) {
2021
1989
  </div>
2022
1990
  <div class="stat-card">
2023
1991
  <h3>自动备份</h3>
2024
- <div class="stat-number">${configResult.data?.config?.autoBackup ? ' 已启用' : '❌ 已禁用'}</div>
1992
+ <div class="stat-number">${configResult.data?.config?.autoBackup ? '�?已启�? : '�?已禁�?}</div>
2025
1993
  </div>
2026
1994
  <div class="stat-card">
2027
1995
  <h3>备份频率</h3>
2028
- <div class="stat-number">${configResult.data?.config?.schedule || '未设置'}</div>
1996
+ <div class="stat-number">${configResult.data?.config?.schedule || '未设�?}</div>
2029
1997
  </div>
2030
1998
  <div class="stat-card">
2031
1999
  <h3>保留天数</h3>
2032
- <div class="stat-number">${configResult.data?.config?.retention || 30} 天</div>
2000
+ <div class="stat-number">${configResult.data?.config?.retention || 30} �?/div>
2033
2001
  </div>
2034
2002
  </div>
2035
2003
 
@@ -2040,7 +2008,7 @@ export function renderAdminDashboard(user, wsService) {
2040
2008
  <div>备份时间</div>
2041
2009
  <div>类型</div>
2042
2010
  <div>大小</div>
2043
- <div>状态</div>
2011
+ <div>状�?/div>
2044
2012
  <div>操作</div>
2045
2013
  </div>
2046
2014
  ${backupsResult.data.backups.map(backup => `
@@ -2054,7 +2022,7 @@ export function renderAdminDashboard(user, wsService) {
2054
2022
  <div>${formatFileSize(backup.size)}</div>
2055
2023
  <div>
2056
2024
  <span class="status-badge status-${backup.status}">
2057
- ${backup.status === 'completed' ? '✅ 完成' : backup.status === 'failed' ? '❌ 失败' : '⏳ 进行中'}
2025
+ ${backup.status === 'completed' ? '�?完成' : backup.status === 'failed' ? '�?失败' : '�?进行�?}
2058
2026
  </span>
2059
2027
  </div>
2060
2028
  <div class="backup-actions">
@@ -2067,7 +2035,7 @@ export function renderAdminDashboard(user, wsService) {
2067
2035
  </div>
2068
2036
  `).join('')}
2069
2037
  </div>
2070
- ` : '<div class="empty-state">暂无备份记录<br>点击"立即备份"创建第一个备份</div>'}
2038
+ ` : '<div class="empty-state">暂无备份记录<br>点击"立即备份"创建第一个备�?/div>'}
2071
2039
  </div>
2072
2040
 
2073
2041
  <!-- 创建备份模态框 -->
@@ -2082,7 +2050,7 @@ export function renderAdminDashboard(user, wsService) {
2082
2050
  <label>备份类型</label>
2083
2051
  <select id="backupType" required>
2084
2052
  <option value="full">完整备份(所有数据)</option>
2085
- <option value="incremental">增量备份(仅变更)</option>
2053
+ <option value="incremental">增量备份(仅变更�?/option>
2086
2054
  </select>
2087
2055
  </div>
2088
2056
  <div class="form-group">
@@ -2091,7 +2059,7 @@ export function renderAdminDashboard(user, wsService) {
2091
2059
  </div>
2092
2060
  <div class="form-actions">
2093
2061
  <button type="button" class="btn-secondary" id="cancelCreateBackup">取消</button>
2094
- <button type="submit" class="btn-primary">开始备份</button>
2062
+ <button type="submit" class="btn-primary">开始备�?/button>
2095
2063
  </div>
2096
2064
  </form>
2097
2065
  </div>
@@ -2113,16 +2081,16 @@ export function renderAdminDashboard(user, wsService) {
2113
2081
  </div>
2114
2082
  <div class="form-group">
2115
2083
  <label>备份频率(Cron表达式)</label>
2116
- <input type="text" id="backupSchedule" value="${configResult.data?.config?.schedule || '0 2 * * *'}" placeholder="0 2 * * * (每天凌晨2点)">
2117
- <small>示例:0 2 * * * (每天凌晨2点),0 */6 * * * (每6小时)</small>
2084
+ <input type="text" id="backupSchedule" value="${configResult.data?.config?.schedule || '0 2 * * *'}" placeholder="0 2 * * * (每天凌晨2�?">
2085
+ <small>示例�? 2 * * * (每天凌晨2�?�? */6 * * * (�?小时)</small>
2118
2086
  </div>
2119
2087
  <div class="form-group">
2120
2088
  <label>保留天数</label>
2121
2089
  <input type="number" id="backupRetention" value="${configResult.data?.config?.retention || 30}" min="1" max="365">
2122
- <small>超过此天数的备份将自动删除</small>
2090
+ <small>超过此天数的备份将自动删�?/small>
2123
2091
  </div>
2124
2092
  <div class="form-group">
2125
- <label>最大备份数量</label>
2093
+ <label>最大备份数�?/label>
2126
2094
  <input type="number" id="maxBackups" value="${configResult.data?.config?.maxBackups || 10}" min="1" max="100">
2127
2095
  </div>
2128
2096
  <div class="form-actions">
@@ -2153,7 +2121,7 @@ export function renderAdminDashboard(user, wsService) {
2153
2121
  const description = document.getElementById('backupDescription').value;
2154
2122
 
2155
2123
  try {
2156
- const response = await fetch('http://localhost:8765/api/backup/create', {
2124
+ const response = await fetch('http://localhost:3000/api/backup/create', {
2157
2125
  method: 'POST',
2158
2126
  headers: {
2159
2127
  'Content-Type': 'application/json',
@@ -2164,7 +2132,7 @@ export function renderAdminDashboard(user, wsService) {
2164
2132
 
2165
2133
  const result = await response.json();
2166
2134
  if (result.success) {
2167
- alert('备份创建成功!');
2135
+ alert('备份创建成功�?);
2168
2136
  document.getElementById('createBackupModal').classList.add('hidden');
2169
2137
  renderBackupView(container);
2170
2138
  } else {
@@ -2198,7 +2166,7 @@ export function renderAdminDashboard(user, wsService) {
2198
2166
  };
2199
2167
 
2200
2168
  try {
2201
- const response = await fetch('http://localhost:8765/api/backup/config', {
2169
+ const response = await fetch('http://localhost:3000/api/backup/config', {
2202
2170
  method: 'PUT',
2203
2171
  headers: {
2204
2172
  'Content-Type': 'application/json',
@@ -2209,7 +2177,7 @@ export function renderAdminDashboard(user, wsService) {
2209
2177
 
2210
2178
  const result = await response.json();
2211
2179
  if (result.success) {
2212
- alert('设置保存成功!');
2180
+ alert('设置保存成功�?);
2213
2181
  document.getElementById('configBackupModal').classList.add('hidden');
2214
2182
  renderBackupView(container);
2215
2183
  } else {
@@ -2224,20 +2192,20 @@ export function renderAdminDashboard(user, wsService) {
2224
2192
  document.querySelectorAll('[data-action="download-backup"]').forEach(btn => {
2225
2193
  btn.addEventListener('click', async () => {
2226
2194
  const backupId = btn.dataset.id;
2227
- window.location.href = `http://localhost:8765/api/backup/${backupId}/download?token=${token}`;
2195
+ window.location.href = `http://localhost:3000/api/backup/${backupId}/download?token=${token}`;
2228
2196
  });
2229
2197
  });
2230
2198
 
2231
2199
  // 恢复备份
2232
2200
  document.querySelectorAll('[data-action="restore-backup"]').forEach(btn => {
2233
2201
  btn.addEventListener('click', async () => {
2234
- if (!confirm('确定要恢复此备份吗?这将覆盖当前数据!')) {
2202
+ if (!confirm('确定要恢复此备份吗?这将覆盖当前数据�?)) {
2235
2203
  return;
2236
2204
  }
2237
2205
 
2238
2206
  const backupId = btn.dataset.id;
2239
2207
  try {
2240
- const response = await fetch(`http://localhost:8765/api/backup/${backupId}/restore`, {
2208
+ const response = await fetch(`http://localhost:3000/api/backup/${backupId}/restore`, {
2241
2209
  method: 'POST',
2242
2210
  headers: { 'Authorization': `Bearer ${token}` }
2243
2211
  });
@@ -2264,14 +2232,14 @@ export function renderAdminDashboard(user, wsService) {
2264
2232
 
2265
2233
  const backupId = btn.dataset.id;
2266
2234
  try {
2267
- const response = await fetch(`http://localhost:8765/api/backup/${backupId}`, {
2235
+ const response = await fetch(`http://localhost:3000/api/backup/${backupId}`, {
2268
2236
  method: 'DELETE',
2269
2237
  headers: { 'Authorization': `Bearer ${token}` }
2270
2238
  });
2271
2239
 
2272
2240
  const result = await response.json();
2273
2241
  if (result.success) {
2274
- alert('备份删除成功!');
2242
+ alert('备份删除成功�?);
2275
2243
  renderBackupView(container);
2276
2244
  } else {
2277
2245
  alert('删除失败: ' + result.error.message);
@@ -2296,20 +2264,20 @@ export function renderAdminDashboard(user, wsService) {
2296
2264
  // 知识库视图(管理员)
2297
2265
  async function renderKnowledgeView(container) {
2298
2266
  if (!currentGroup) {
2299
- container.innerHTML = '<div class="empty-state">请先选择一个群组</div>';
2267
+ container.innerHTML = '<div class="empty-state">请先选择一个群�?/div>';
2300
2268
  return;
2301
2269
  }
2302
2270
 
2303
2271
  try {
2304
2272
  const token = localStorage.getItem('token');
2305
- const response = await fetch(`http://localhost:8765/api/knowledge/group/${currentGroup._id}`, {
2273
+ const response = await fetch(`http://localhost:3000/api/knowledge/group/${currentGroup._id}`, {
2306
2274
  headers: { 'Authorization': `Bearer ${token}` }
2307
2275
  });
2308
2276
  const result = await response.json();
2309
2277
 
2310
2278
  container.innerHTML = `
2311
2279
  <div class="view-header">
2312
- <h2>📚 知识库管理 - ${currentGroup.name}</h2>
2280
+ <h2>📚 知识库管�?- ${currentGroup.name}</h2>
2313
2281
  <button class="btn-primary" id="createKnowledgeBtn">+ 新建文档</button>
2314
2282
  </div>
2315
2283
  <div class="knowledge-list" id="knowledgeList"></div>
@@ -2318,7 +2286,7 @@ export function renderAdminDashboard(user, wsService) {
2318
2286
  const knowledgeList = document.getElementById('knowledgeList');
2319
2287
 
2320
2288
  if (!result.data || !result.data.knowledgeList || result.data.knowledgeList.length === 0) {
2321
- knowledgeList.innerHTML = '<div class="empty-state">暂无知识库文档</div>';
2289
+ knowledgeList.innerHTML = '<div class="empty-state">暂无知识库文�?/div>';
2322
2290
  } else {
2323
2291
  result.data.knowledgeList.forEach(kb => {
2324
2292
  const kbCard = document.createElement('div');
@@ -2326,11 +2294,11 @@ export function renderAdminDashboard(user, wsService) {
2326
2294
  kbCard.innerHTML = `
2327
2295
  <div class="kb-header">
2328
2296
  <h3>${kb.title}</h3>
2329
- <span class="kb-status ${kb.status}">${kb.status === 'published' ? '已发布' : kb.status === 'draft' ? '草稿' : '已归档'}</span>
2297
+ <span class="kb-status ${kb.status}">${kb.status === 'published' ? '已发�? : kb.status === 'draft' ? '草稿' : '已归�?}</span>
2330
2298
  </div>
2331
2299
  <div class="kb-meta">
2332
- <span>📁 ${kb.category || '未分类'}</span>
2333
- <span>👁️ ${kb.views || 0} 浏览</span>
2300
+ <span>📁 ${kb.category || '未分�?}</span>
2301
+ <span>👁�?${kb.views || 0} 浏览</span>
2334
2302
  <span>👍 ${kb.likes ? kb.likes.length : 0} 点赞</span>
2335
2303
  </div>
2336
2304
  <div class="kb-actions">
@@ -2342,8 +2310,7 @@ export function renderAdminDashboard(user, wsService) {
2342
2310
  knowledgeList.appendChild(kbCard);
2343
2311
  });
2344
2312
 
2345
- // 查看、编辑、删除事件
2346
- document.querySelectorAll('[data-action="view-kb"]').forEach(btn => {
2313
+ // 查看、编辑、删除事�? document.querySelectorAll('[data-action="view-kb"]').forEach(btn => {
2347
2314
  btn.addEventListener('click', async () => {
2348
2315
  await showKnowledgeDetail(btn.dataset.id);
2349
2316
  });
@@ -2359,11 +2326,11 @@ export function renderAdminDashboard(user, wsService) {
2359
2326
  btn.addEventListener('click', async () => {
2360
2327
  if (confirm('确定要删除这个知识库文档吗?')) {
2361
2328
  try {
2362
- await fetch(`http://localhost:8765/api/knowledge/${btn.dataset.id}`, {
2329
+ await fetch(`http://localhost:3000/api/knowledge/${btn.dataset.id}`, {
2363
2330
  method: 'DELETE',
2364
2331
  headers: { 'Authorization': `Bearer ${token}` }
2365
2332
  });
2366
- alert('删除成功!');
2333
+ alert('删除成功�?);
2367
2334
  renderKnowledgeView(container);
2368
2335
  } catch (error) {
2369
2336
  alert('删除失败: ' + error.message);
@@ -2378,10 +2345,10 @@ export function renderAdminDashboard(user, wsService) {
2378
2345
  });
2379
2346
 
2380
2347
  } catch (error) {
2381
- console.error('加载知识库失败:', error);
2348
+ console.error('加载知识库失�?', error);
2382
2349
  container.innerHTML = `
2383
2350
  <div class="view-header">
2384
- <h2>📚 知识库管理</h2>
2351
+ <h2>📚 知识库管�?/h2>
2385
2352
  </div>
2386
2353
  <div class="empty-state">加载失败: ${error.message}</div>
2387
2354
  `;
@@ -2396,28 +2363,28 @@ export function renderAdminDashboard(user, wsService) {
2396
2363
  modal.innerHTML = `
2397
2364
  <div class="modal-content" style="max-width: 800px;">
2398
2365
  <div class="modal-header">
2399
- <h3>📚 创建知识库文档</h3>
2366
+ <h3>📚 创建知识库文�?/h3>
2400
2367
  <button class="close-btn" id="closeCreateKnowledge">&times;</button>
2401
2368
  </div>
2402
2369
  <form id="createKnowledgeForm">
2403
2370
  <div class="form-group">
2404
2371
  <label>标题 *</label>
2405
- <input type="text" id="kbTitle" required placeholder="请输入文档标题">
2372
+ <input type="text" id="kbTitle" required placeholder="请输入文档标�?>
2406
2373
  </div>
2407
2374
  <div class="form-group">
2408
2375
  <label>分类</label>
2409
2376
  <input type="text" id="kbCategory" placeholder="例如:技术文档、产品说明等">
2410
2377
  </div>
2411
2378
  <div class="form-group">
2412
- <label>标签(用逗号分隔)</label>
2413
- <input type="text" id="kbTags" placeholder="例如:前端,React,教程">
2379
+ <label>标签(用逗号分隔�?/label>
2380
+ <input type="text" id="kbTags" placeholder="例如:前�?React,教程">
2414
2381
  </div>
2415
2382
  <div class="form-group">
2416
2383
  <label>内容 *</label>
2417
- <textarea id="kbContent" rows="10" required placeholder="请输入文档内容"></textarea>
2384
+ <textarea id="kbContent" rows="10" required placeholder="请输入文档内�?></textarea>
2418
2385
  </div>
2419
2386
  <div class="form-group">
2420
- <label>状态</label>
2387
+ <label>状�?/label>
2421
2388
  <select id="kbStatus">
2422
2389
  <option value="draft">草稿</option>
2423
2390
  <option value="published">发布</option>
@@ -2428,15 +2395,15 @@ export function renderAdminDashboard(user, wsService) {
2428
2395
  <select id="kbReadPermission">
2429
2396
  <option value="group">群组成员可见</option>
2430
2397
  <option value="public">公开</option>
2431
- <option value="private">仅自己可见</option>
2398
+ <option value="private">仅自己可�?/option>
2432
2399
  </select>
2433
2400
  </div>
2434
2401
  <div class="form-group">
2435
2402
  <label>编辑权限</label>
2436
2403
  <select id="kbWritePermission">
2437
- <option value="author">仅作者</option>
2438
- <option value="admin">管理员</option>
2439
- <option value="all">所有成员</option>
2404
+ <option value="author">仅作�?/option>
2405
+ <option value="admin">管理�?/option>
2406
+ <option value="all">所有成�?/option>
2440
2407
  </select>
2441
2408
  </div>
2442
2409
  <div style="display: flex; gap: 10px; margin-top: 20px;">
@@ -2486,7 +2453,7 @@ export function renderAdminDashboard(user, wsService) {
2486
2453
 
2487
2454
  try {
2488
2455
  const token = localStorage.getItem('token');
2489
- const response = await fetch('http://localhost:8765/api/knowledge', {
2456
+ const response = await fetch('http://localhost:3000/api/knowledge', {
2490
2457
  method: 'POST',
2491
2458
  headers: {
2492
2459
  'Content-Type': 'application/json',
@@ -2495,7 +2462,7 @@ export function renderAdminDashboard(user, wsService) {
2495
2462
  body: JSON.stringify({
2496
2463
  title,
2497
2464
  content,
2498
- category: category || '未分类',
2465
+ category: category || '未分�?,
2499
2466
  tags,
2500
2467
  groupId: currentGroup._id,
2501
2468
  status,
@@ -2511,24 +2478,22 @@ export function renderAdminDashboard(user, wsService) {
2511
2478
  if (result.success) {
2512
2479
  alert('知识库文档创建成功!');
2513
2480
  modal.remove();
2514
- // 重新加载知识库列表
2515
- const contentArea = document.getElementById('contentArea');
2481
+ // 重新加载知识库列�? const contentArea = document.getElementById('contentArea');
2516
2482
  renderKnowledgeView(contentArea);
2517
2483
  } else {
2518
2484
  alert('创建失败: ' + (result.error?.message || result.message || '未知错误'));
2519
2485
  }
2520
2486
  } catch (error) {
2521
- console.error('创建知识库文档失败:', error);
2487
+ console.error('创建知识库文档失�?', error);
2522
2488
  alert('创建失败: ' + error.message);
2523
2489
  }
2524
2490
  });
2525
2491
  }
2526
2492
 
2527
- // 显示知识库文档详情
2528
- async function showKnowledgeDetail(knowledgeId) {
2493
+ // 显示知识库文档详�? async function showKnowledgeDetail(knowledgeId) {
2529
2494
  try {
2530
2495
  const token = localStorage.getItem('token');
2531
- const response = await fetch(`http://localhost:8765/api/knowledge/${knowledgeId}`, {
2496
+ const response = await fetch(`http://localhost:3000/api/knowledge/${knowledgeId}`, {
2532
2497
  headers: { 'Authorization': `Bearer ${token}` }
2533
2498
  });
2534
2499
  const result = await response.json();
@@ -2549,14 +2514,14 @@ export function renderAdminDashboard(user, wsService) {
2549
2514
  </div>
2550
2515
  <div style="padding: 20px;">
2551
2516
  <div style="display: flex; gap: 20px; margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid var(--border);">
2552
- <div><strong>分类:</strong>${kb.category}</div>
2553
- <div><strong>状态:</strong><span class="status-badge status-${kb.status}">${kb.status === 'published' ? '已发布' : kb.status === 'draft' ? '草稿' : '已归档'}</span></div>
2554
- <div><strong>浏览:</strong>${kb.views || 0}</div>
2555
- <div><strong>点赞:</strong>${kb.likes?.length || 0}</div>
2517
+ <div><strong>分类�?/strong>${kb.category}</div>
2518
+ <div><strong>状态:</strong><span class="status-badge status-${kb.status}">${kb.status === 'published' ? '已发�? : kb.status === 'draft' ? '草稿' : '已归�?}</span></div>
2519
+ <div><strong>浏览�?/strong>${kb.views || 0}</div>
2520
+ <div><strong>点赞�?/strong>${kb.likes?.length || 0}</div>
2556
2521
  </div>
2557
2522
  ${kb.tags && kb.tags.length > 0 ? `
2558
2523
  <div style="margin-bottom: 20px;">
2559
- <strong>标签:</strong>
2524
+ <strong>标签�?/strong>
2560
2525
  ${kb.tags.map(tag => `<span class="kb-tag" style="background: var(--bg-hover); padding: 4px 12px; border-radius: 12px; margin-right: 8px; font-size: 12px;">${tag}</span>`).join('')}
2561
2526
  </div>
2562
2527
  ` : ''}
@@ -2565,8 +2530,8 @@ export function renderAdminDashboard(user, wsService) {
2565
2530
  </div>
2566
2531
  <div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--border); color: var(--text-secondary); font-size: 13px;">
2567
2532
  <div>作者:${kb.author?.username || '未知'}</div>
2568
- <div>创建时间:${new Date(kb.createdAt).toLocaleString()}</div>
2569
- <div>更新时间:${new Date(kb.updatedAt).toLocaleString()}</div>
2533
+ <div>创建时间�?{new Date(kb.createdAt).toLocaleString()}</div>
2534
+ <div>更新时间�?{new Date(kb.updatedAt).toLocaleString()}</div>
2570
2535
  </div>
2571
2536
  </div>
2572
2537
  </div>
@@ -2584,7 +2549,7 @@ export function renderAdminDashboard(user, wsService) {
2584
2549
  }
2585
2550
  });
2586
2551
  } catch (error) {
2587
- console.error('加载知识库详情失败:', error);
2552
+ console.error('加载知识库详情失�?', error);
2588
2553
  alert('加载失败: ' + error.message);
2589
2554
  }
2590
2555
  }
@@ -2593,7 +2558,7 @@ export function renderAdminDashboard(user, wsService) {
2593
2558
  async function showEditKnowledgeModal(knowledgeId) {
2594
2559
  try {
2595
2560
  const token = localStorage.getItem('token');
2596
- const response = await fetch(`http://localhost:8765/api/knowledge/${knowledgeId}`, {
2561
+ const response = await fetch(`http://localhost:3000/api/knowledge/${knowledgeId}`, {
2597
2562
  headers: { 'Authorization': `Bearer ${token}` }
2598
2563
  });
2599
2564
  const result = await response.json();
@@ -2609,7 +2574,7 @@ export function renderAdminDashboard(user, wsService) {
2609
2574
  modal.innerHTML = `
2610
2575
  <div class="modal-content" style="max-width: 800px;">
2611
2576
  <div class="modal-header">
2612
- <h3>✏️ 编辑知识库文档</h3>
2577
+ <h3>✏️ 编辑知识库文�?/h3>
2613
2578
  <button class="close-btn" id="closeEditKnowledge">&times;</button>
2614
2579
  </div>
2615
2580
  <form id="editKnowledgeForm">
@@ -2622,7 +2587,7 @@ export function renderAdminDashboard(user, wsService) {
2622
2587
  <input type="text" id="editKbCategory" value="${kb.category || ''}">
2623
2588
  </div>
2624
2589
  <div class="form-group">
2625
- <label>标签(用逗号分隔)</label>
2590
+ <label>标签(用逗号分隔�?/label>
2626
2591
  <input type="text" id="editKbTags" value="${kb.tags ? kb.tags.join(', ') : ''}">
2627
2592
  </div>
2628
2593
  <div class="form-group">
@@ -2630,7 +2595,7 @@ export function renderAdminDashboard(user, wsService) {
2630
2595
  <textarea id="editKbContent" rows="10" required>${kb.content}</textarea>
2631
2596
  </div>
2632
2597
  <div class="form-group">
2633
- <label>状态</label>
2598
+ <label>状�?/label>
2634
2599
  <select id="editKbStatus">
2635
2600
  <option value="draft" ${kb.status === 'draft' ? 'selected' : ''}>草稿</option>
2636
2601
  <option value="published" ${kb.status === 'published' ? 'selected' : ''}>发布</option>
@@ -2642,15 +2607,15 @@ export function renderAdminDashboard(user, wsService) {
2642
2607
  <select id="editKbReadPermission">
2643
2608
  <option value="group" ${kb.permissions?.read === 'group' ? 'selected' : ''}>群组成员可见</option>
2644
2609
  <option value="public" ${kb.permissions?.read === 'public' ? 'selected' : ''}>公开</option>
2645
- <option value="private" ${kb.permissions?.read === 'private' ? 'selected' : ''}>仅自己可见</option>
2610
+ <option value="private" ${kb.permissions?.read === 'private' ? 'selected' : ''}>仅自己可�?/option>
2646
2611
  </select>
2647
2612
  </div>
2648
2613
  <div class="form-group">
2649
2614
  <label>编辑权限</label>
2650
2615
  <select id="editKbWritePermission">
2651
- <option value="author" ${kb.permissions?.write === 'author' ? 'selected' : ''}>仅作者</option>
2652
- <option value="admin" ${kb.permissions?.write === 'admin' ? 'selected' : ''}>管理员</option>
2653
- <option value="all" ${kb.permissions?.write === 'all' ? 'selected' : ''}>所有成员</option>
2616
+ <option value="author" ${kb.permissions?.write === 'author' ? 'selected' : ''}>仅作�?/option>
2617
+ <option value="admin" ${kb.permissions?.write === 'admin' ? 'selected' : ''}>管理�?/option>
2618
+ <option value="all" ${kb.permissions?.write === 'all' ? 'selected' : ''}>所有成�?/option>
2654
2619
  </select>
2655
2620
  </div>
2656
2621
  <div style="display: flex; gap: 10px; margin-top: 20px;">
@@ -2697,7 +2662,7 @@ export function renderAdminDashboard(user, wsService) {
2697
2662
  const tags = tagsInput ? tagsInput.split(',').map(tag => tag.trim()).filter(tag => tag) : [];
2698
2663
 
2699
2664
  try {
2700
- const updateResponse = await fetch(`http://localhost:8765/api/knowledge/${knowledgeId}`, {
2665
+ const updateResponse = await fetch(`http://localhost:3000/api/knowledge/${knowledgeId}`, {
2701
2666
  method: 'PUT',
2702
2667
  headers: {
2703
2668
  'Content-Type': 'application/json',
@@ -2706,7 +2671,7 @@ export function renderAdminDashboard(user, wsService) {
2706
2671
  body: JSON.stringify({
2707
2672
  title,
2708
2673
  content,
2709
- category: category || '未分类',
2674
+ category: category || '未分�?,
2710
2675
  tags,
2711
2676
  status,
2712
2677
  permissions: {
@@ -2722,19 +2687,18 @@ export function renderAdminDashboard(user, wsService) {
2722
2687
  if (updateResult.success) {
2723
2688
  alert('知识库文档更新成功!');
2724
2689
  modal.remove();
2725
- // 重新加载知识库列表
2726
- const contentArea = document.getElementById('contentArea');
2690
+ // 重新加载知识库列�? const contentArea = document.getElementById('contentArea');
2727
2691
  renderKnowledgeView(contentArea);
2728
2692
  } else {
2729
2693
  alert('更新失败: ' + (updateResult.error?.message || updateResult.message || '未知错误'));
2730
2694
  }
2731
2695
  } catch (error) {
2732
- console.error('更新知识库文档失败:', error);
2696
+ console.error('更新知识库文档失�?', error);
2733
2697
  alert('更新失败: ' + error.message);
2734
2698
  }
2735
2699
  });
2736
2700
  } catch (error) {
2737
- console.error('加载知识库文档失败:', error);
2701
+ console.error('加载知识库文档失�?', error);
2738
2702
  alert('加载失败: ' + error.message);
2739
2703
  }
2740
2704
  }
@@ -2742,13 +2706,13 @@ export function renderAdminDashboard(user, wsService) {
2742
2706
  // 工作流视图(管理员)
2743
2707
  async function renderWorkflowsView(container) {
2744
2708
  if (!currentGroup) {
2745
- container.innerHTML = '<div class="empty-state">请先选择一个群组</div>';
2709
+ container.innerHTML = '<div class="empty-state">请先选择一个群�?/div>';
2746
2710
  return;
2747
2711
  }
2748
2712
 
2749
2713
  try {
2750
2714
  const token = localStorage.getItem('token');
2751
- const response = await fetch(`http://localhost:8765/api/workflows/group/${currentGroup._id}`, {
2715
+ const response = await fetch(`http://localhost:3000/api/workflows/group/${currentGroup._id}`, {
2752
2716
  headers: { 'Authorization': `Bearer ${token}` }
2753
2717
  });
2754
2718
  const result = await response.json();
@@ -2756,36 +2720,33 @@ export function renderAdminDashboard(user, wsService) {
2756
2720
 
2757
2721
  container.innerHTML = `
2758
2722
  <div class="view-header">
2759
- <h2>🔄 工作流管理 - ${currentGroup.name}</h2>
2760
- <button class="btn-primary" id="createWorkflowBtn">+ 创建工作流</button>
2723
+ <h2>🔄 工作流管�?- ${currentGroup.name}</h2>
2724
+ <button class="btn-primary" id="createWorkflowBtn">+ 创建工作�?/button>
2761
2725
  </div>
2762
2726
 
2763
2727
  <div class="info-box" style="background: var(--bg-card); padding: 20px; border-radius: 12px; margin-bottom: 20px; border-left: 4px solid var(--primary);">
2764
2728
  <h3 style="margin-bottom: 10px;">💡 什么是工作流?</h3>
2765
2729
  <p style="color: var(--text-secondary); line-height: 1.6; margin-bottom: 15px;">
2766
- 工作流是一个<strong>自动化流程引擎</strong>,可以自动执行一系列预定义的操作,提高团队协作效率。
2767
- </p>
2730
+ 工作流是一�?strong>自动化流程引�?/strong>,可以自动执行一系列预定义的操作,提高团队协作效率�? </p>
2768
2731
  <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px; margin-top: 15px;">
2769
2732
  <div style="background: var(--bg-dark); padding: 15px; border-radius: 8px;">
2770
2733
  <div style="font-size: 24px; margin-bottom: 8px;">📋</div>
2771
2734
  <strong>文档审批流程</strong>
2772
2735
  <p style="font-size: 13px; color: var(--text-secondary); margin-top: 5px;">
2773
- 自动通知审批人 → 等待审批 → 审批通过后发布
2774
- </p>
2736
+ 自动通知审批�?�?等待审批 �?审批通过后发�? </p>
2775
2737
  </div>
2776
2738
  <div style="background: var(--bg-dark); padding: 15px; border-radius: 8px;">
2777
- <div style="font-size: 24px; margin-bottom: 8px;">✅</div>
2739
+ <div style="font-size: 24px; margin-bottom: 8px;">�?/div>
2778
2740
  <strong>任务自动分配</strong>
2779
2741
  <p style="font-size: 13px; color: var(--text-secondary); margin-top: 5px;">
2780
- 新任务创建 → 自动分配成员 → 发送通知
2742
+ 新任务创�?�?自动分配成员 �?发送通知
2781
2743
  </p>
2782
2744
  </div>
2783
2745
  <div style="background: var(--bg-dark); padding: 15px; border-radius: 8px;">
2784
- <div style="font-size: 24px; margin-bottom: 8px;">⏰</div>
2746
+ <div style="font-size: 24px; margin-bottom: 8px;">�?/div>
2785
2747
  <strong>定时报告生成</strong>
2786
2748
  <p style="font-size: 13px; color: var(--text-secondary); margin-top: 5px;">
2787
- 每天凌晨2 收集数据 → 生成报告 → 发送邮件
2788
- </p>
2749
+ 每天凌晨2�?�?收集数据 �?生成报告 �?发送邮�? </p>
2789
2750
  </div>
2790
2751
  </div>
2791
2752
  </div>
@@ -2796,17 +2757,17 @@ export function renderAdminDashboard(user, wsService) {
2796
2757
  <div class="modal hidden" id="workflowModal">
2797
2758
  <div class="modal-content" style="max-width: 720px;">
2798
2759
  <div class="modal-header">
2799
- <h3 id="workflowModalTitle">创建工作流</h3>
2760
+ <h3 id="workflowModalTitle">创建工作�?/h3>
2800
2761
  <button class="modal-close" id="closeWorkflowModal">&times;</button>
2801
2762
  </div>
2802
2763
  <form id="workflowForm">
2803
2764
  <div class="form-group">
2804
2765
  <label>名称 *</label>
2805
- <input type="text" id="wfName" required placeholder="例如:文档审批流程">
2766
+ <input type="text" id="wfName" required placeholder="例如:文档审批流�?>
2806
2767
  </div>
2807
2768
  <div class="form-group">
2808
2769
  <label>描述</label>
2809
- <textarea id="wfDescription" rows="2" placeholder="说明此工作流的用途"></textarea>
2770
+ <textarea id="wfDescription" rows="2" placeholder="说明此工作流的用�?></textarea>
2810
2771
  </div>
2811
2772
  <div class="form-group">
2812
2773
  <label>触发方式</label>
@@ -2817,17 +2778,17 @@ export function renderAdminDashboard(user, wsService) {
2817
2778
  </div>
2818
2779
  <div class="form-group" id="wfScheduleGroup">
2819
2780
  <label>定时表达式(可选)</label>
2820
- <input type="text" id="wfSchedule" placeholder="例如:0 2 * * * 表示每天凌晨2点">
2821
- <small>使用 Cron 表达式配置定时触发时间</small>
2781
+ <input type="text" id="wfSchedule" placeholder="例如�? 2 * * * 表示每天凌晨2�?>
2782
+ <small>使用 Cron 表达式配置定时触发时�?/small>
2822
2783
  </div>
2823
2784
  <div class="form-group">
2824
- <label>步骤配置(JSON 数组)</label>
2825
- <textarea id="wfSteps" rows="6" placeholder='例如:[{"name":"通知审批人","type":"notification","config":{"message":"有新文档需要审批"}}]'></textarea>
2826
- <small>高级用法:直接编辑 JSON,字段与后端 Workflow.steps 一致</small>
2785
+ <label>步骤配置(JSON 数组�?/label>
2786
+ <textarea id="wfSteps" rows="6" placeholder='例如:[{"name":"通知审批�?,"type":"notification","config":{"message":"有新文档需要审�?}}]'></textarea>
2787
+ <small>高级用法:直接编�?JSON,字段与后端 Workflow.steps 一�?/small>
2827
2788
  </div>
2828
2789
  <div class="form-actions">
2829
2790
  <button type="button" class="btn-secondary" id="cancelWorkflow">取消</button>
2830
- <button type="submit" class="btn-primary">保存工作流</button>
2791
+ <button type="submit" class="btn-primary">保存工作�?/button>
2831
2792
  </div>
2832
2793
  </form>
2833
2794
  </div>
@@ -2837,17 +2798,17 @@ export function renderAdminDashboard(user, wsService) {
2837
2798
  const workflowsList = document.getElementById('workflowsList');
2838
2799
 
2839
2800
  if (workflows.length === 0) {
2840
- workflowsList.innerHTML = '<div class="empty-state">暂无工作流<br><small style="color: var(--text-secondary);">点击"创建工作流"开始自动化您的工作流程</small></div>';
2801
+ workflowsList.innerHTML = '<div class="empty-state">暂无工作�?br><small style="color: var(--text-secondary);">点击"创建工作�?开始自动化您的工作流程</small></div>';
2841
2802
  } else {
2842
2803
  workflows.forEach(wf => {
2843
2804
  const wfCard = document.createElement('div');
2844
2805
  wfCard.className = 'workflow-card';
2845
2806
  const triggerType = wf.trigger && wf.trigger.type ? wf.trigger.type : 'manual';
2846
2807
  const triggerLabel = triggerType === 'manual'
2847
- ? '🖱️ 手动'
2808
+ ? '🖱�?手动'
2848
2809
  : triggerType === 'scheduled'
2849
- ? '⏰ 定时'
2850
- : '⚡ 事件';
2810
+ ? '�?定时'
2811
+ : '�?事件';
2851
2812
  const stepsCount = Array.isArray(wf.steps) ? wf.steps.length : 0;
2852
2813
  const totalExec = wf.stats && typeof wf.stats.totalExecutions === 'number'
2853
2814
  ? wf.stats.totalExecutions
@@ -2856,13 +2817,13 @@ export function renderAdminDashboard(user, wsService) {
2856
2817
  wfCard.innerHTML = `
2857
2818
  <div class="wf-header">
2858
2819
  <h3>${wf.name}</h3>
2859
- <span class="wf-status ${wf.status}">${wf.status === 'active' ? ' 已激活' : wf.status === 'inactive' ? '⏸️ 已停用' : '📝 草稿'}</span>
2820
+ <span class="wf-status ${wf.status}">${wf.status === 'active' ? '�?已激�? : wf.status === 'inactive' ? '⏸️ 已停�? : '📝 草稿'}</span>
2860
2821
  </div>
2861
2822
  <p>${wf.description || '暂无描述'}</p>
2862
2823
  <div class="wf-meta">
2863
- <span>触发器: ${triggerLabel}</span>
2824
+ <span>触发�? ${triggerLabel}</span>
2864
2825
  <span>步骤: ${stepsCount}</span>
2865
- <span>执行: ${totalExec} 次</span>
2826
+ <span>执行: ${totalExec} �?/span>
2866
2827
  </div>
2867
2828
  <div class="wf-actions">
2868
2829
  <button class="btn-primary btn-sm" data-id="${wf._id}" data-action="view-wf">查看详情</button>
@@ -2871,7 +2832,7 @@ export function renderAdminDashboard(user, wsService) {
2871
2832
  <button class="btn-secondary btn-sm" data-id="${wf._id}" data-action="trigger-wf">手动触发</button>
2872
2833
  <button class="btn-warning btn-sm" data-id="${wf._id}" data-action="deactivate-wf">停用</button>
2873
2834
  ` : `
2874
- <button class="btn-success btn-sm" data-id="${wf._id}" data-action="activate-wf">激活</button>
2835
+ <button class="btn-success btn-sm" data-id="${wf._id}" data-action="activate-wf">激�?/button>
2875
2836
  `}
2876
2837
  <button class="btn-danger btn-sm" data-id="${wf._id}" data-action="delete-wf">删除</button>
2877
2838
  </div>
@@ -2880,20 +2841,19 @@ export function renderAdminDashboard(user, wsService) {
2880
2841
  });
2881
2842
  }
2882
2843
 
2883
- // 详情查看(暂时简单提示,可后续扩展为真正详情面板)
2884
- document.querySelectorAll('[data-action="view-wf"]').forEach(btn => {
2844
+ // 详情查看(暂时简单提示,可后续扩展为真正详情面板�? document.querySelectorAll('[data-action="view-wf"]').forEach(btn => {
2885
2845
  const wf = workflows.find(w => w._id === btn.dataset.id);
2886
2846
  btn.addEventListener('click', () => {
2887
- alert(`工作流详情:\n\n名称:${wf.name}\n状态:${wf.status}\n触发方式:${wf.trigger?.type || 'manual'}\n步骤数:${(wf.steps || []).length}`);
2847
+ alert(`工作流详情:\n\n名称�?{wf.name}\n状态:${wf.status}\n触发方式�?{wf.trigger?.type || 'manual'}\n步骤数:${(wf.steps || []).length}`);
2888
2848
  });
2889
2849
  });
2890
2850
 
2891
2851
  // 手动触发
2892
2852
  document.querySelectorAll('[data-action="trigger-wf"]').forEach(btn => {
2893
2853
  btn.addEventListener('click', async () => {
2894
- if (confirm('确定要手动触发此工作流吗?')) {
2854
+ if (confirm('确定要手动触发此工作流吗�?)) {
2895
2855
  try {
2896
- await fetch(`http://localhost:8765/api/workflows/${btn.dataset.id}/trigger`, {
2856
+ await fetch(`http://localhost:3000/api/workflows/${btn.dataset.id}/trigger`, {
2897
2857
  method: 'POST',
2898
2858
  headers: {
2899
2859
  'Content-Type': 'application/json',
@@ -2901,7 +2861,7 @@ export function renderAdminDashboard(user, wsService) {
2901
2861
  },
2902
2862
  body: JSON.stringify({ triggerData: {} })
2903
2863
  });
2904
- alert('工作流已触发!');
2864
+ alert('工作流已触发�?);
2905
2865
  } catch (error) {
2906
2866
  alert('触发失败: ' + error.message);
2907
2867
  }
@@ -2909,12 +2869,12 @@ export function renderAdminDashboard(user, wsService) {
2909
2869
  });
2910
2870
  });
2911
2871
 
2912
- // 激活/停用
2872
+ // 激�?停用
2913
2873
  document.querySelectorAll('[data-action="activate-wf"], [data-action="deactivate-wf"]').forEach(btn => {
2914
2874
  btn.addEventListener('click', async () => {
2915
2875
  const action = btn.dataset.action === 'activate-wf' ? 'activate' : 'deactivate';
2916
2876
  try {
2917
- await fetch(`http://localhost:8765/api/workflows/${btn.dataset.id}/${action}`, {
2877
+ await fetch(`http://localhost:3000/api/workflows/${btn.dataset.id}/${action}`, {
2918
2878
  method: 'POST',
2919
2879
  headers: { 'Authorization': `Bearer ${token}` }
2920
2880
  });
@@ -2926,12 +2886,11 @@ export function renderAdminDashboard(user, wsService) {
2926
2886
  });
2927
2887
  });
2928
2888
 
2929
- // 删除工作流
2930
- document.querySelectorAll('[data-action="delete-wf"]').forEach(btn => {
2889
+ // 删除工作�? document.querySelectorAll('[data-action="delete-wf"]').forEach(btn => {
2931
2890
  btn.addEventListener('click', async () => {
2932
2891
  if (confirm('确定要删除这个工作流吗?删除后无法恢复!')) {
2933
2892
  try {
2934
- await fetch(`http://localhost:8765/api/workflows/${btn.dataset.id}`, {
2893
+ await fetch(`http://localhost:3000/api/workflows/${btn.dataset.id}`, {
2935
2894
  method: 'DELETE',
2936
2895
  headers: { 'Authorization': `Bearer ${token}` }
2937
2896
  });
@@ -2964,7 +2923,7 @@ export function renderAdminDashboard(user, wsService) {
2964
2923
 
2965
2924
  function openWorkflowModal(workflow) {
2966
2925
  editingWorkflow = workflow || null;
2967
- workflowModalTitle.textContent = workflow ? '编辑工作流' : '创建工作流';
2926
+ workflowModalTitle.textContent = workflow ? '编辑工作�? : '创建工作�?;
2968
2927
  nameInput.value = workflow?.name || '';
2969
2928
  descInput.value = workflow?.description || '';
2970
2929
  const type = workflow?.trigger?.type || 'manual';
@@ -2996,8 +2955,7 @@ export function renderAdminDashboard(user, wsService) {
2996
2955
  btn.addEventListener('click', () => openWorkflowModal(wf));
2997
2956
  });
2998
2957
 
2999
- // 保存工作流
3000
- workflowForm.addEventListener('submit', async (e) => {
2958
+ // 保存工作�? workflowForm.addEventListener('submit', async (e) => {
3001
2959
  e.preventDefault();
3002
2960
  const name = nameInput.value.trim();
3003
2961
  if (!name) {
@@ -3014,11 +2972,11 @@ export function renderAdminDashboard(user, wsService) {
3014
2972
  try {
3015
2973
  const parsed = JSON.parse(stepsText);
3016
2974
  if (!Array.isArray(parsed)) {
3017
- throw new Error('步骤配置必须是 JSON 数组');
2975
+ throw new Error('步骤配置必须�?JSON 数组');
3018
2976
  }
3019
2977
  steps = parsed;
3020
2978
  } catch (err) {
3021
- alert('步骤配置必须是合法的 JSON 数组:' + err.message);
2979
+ alert('步骤配置必须是合法的 JSON 数组�? + err.message);
3022
2980
  return;
3023
2981
  }
3024
2982
  }
@@ -3035,8 +2993,8 @@ export function renderAdminDashboard(user, wsService) {
3035
2993
  }
3036
2994
 
3037
2995
  const url = editingWorkflow
3038
- ? `http://localhost:8765/api/workflows/${editingWorkflow._id}`
3039
- : 'http://localhost:8765/api/workflows';
2996
+ ? `http://localhost:3000/api/workflows/${editingWorkflow._id}`
2997
+ : 'http://localhost:3000/api/workflows';
3040
2998
  const method = editingWorkflow ? 'PUT' : 'POST';
3041
2999
 
3042
3000
  try {
@@ -3061,10 +3019,10 @@ export function renderAdminDashboard(user, wsService) {
3061
3019
  });
3062
3020
 
3063
3021
  } catch (error) {
3064
- console.error('加载工作流失败:', error);
3022
+ console.error('加载工作流失�?', error);
3065
3023
  container.innerHTML = `
3066
3024
  <div class="view-header">
3067
- <h2>🔄 工作流管理</h2>
3025
+ <h2>🔄 工作流管�?/h2>
3068
3026
  </div>
3069
3027
  <div class="empty-state">加载失败: ${error.message}</div>
3070
3028
  `;
@@ -3073,10 +3031,10 @@ export function renderAdminDashboard(user, wsService) {
3073
3031
 
3074
3032
  function getStatusText(status) {
3075
3033
  const statusMap = {
3076
- 'pending': '待处理',
3077
- 'in_progress': '进行中',
3078
- 'completed': '已完成',
3079
- 'terminated': '已终止'
3034
+ 'pending': '待处�?,
3035
+ 'in_progress': '进行�?,
3036
+ 'completed': '已完�?,
3037
+ 'terminated': '已终�?
3080
3038
  };
3081
3039
  return statusMap[status] || status;
3082
3040
  }
@@ -3088,13 +3046,13 @@ export function renderAdminDashboard(user, wsService) {
3088
3046
  const token = localStorage.getItem('token');
3089
3047
 
3090
3048
  // 获取备份列表
3091
- const backupsResponse = await fetch('http://localhost:8765/api/backup/list', {
3049
+ const backupsResponse = await fetch('http://localhost:3000/api/backup/list', {
3092
3050
  headers: { 'Authorization': `Bearer ${token}` }
3093
3051
  });
3094
3052
  const backupsResult = await backupsResponse.json();
3095
3053
 
3096
3054
  // 获取备份配置
3097
- const configResponse = await fetch('http://localhost:8765/api/backup/config', {
3055
+ const configResponse = await fetch('http://localhost:3000/api/backup/config', {
3098
3056
  headers: { 'Authorization': `Bearer ${token}` }
3099
3057
  });
3100
3058
  const configResult = await configResponse.json();
@@ -3107,7 +3065,7 @@ export function renderAdminDashboard(user, wsService) {
3107
3065
  <h2>💾 备份管理</h2>
3108
3066
  <div style="display: flex; gap: 10px;">
3109
3067
  <button class="btn-primary" id="createBackupBtn">
3110
- <span style="font-size: 18px;">➕</span> 立即备份
3068
+ <span style="font-size: 18px;">�?/span> 立即备份
3111
3069
  </button>
3112
3070
  <button class="btn-secondary" id="configBackupBtn">
3113
3071
  <span style="font-size: 18px;">⚙️</span> 备份设置
@@ -3129,11 +3087,11 @@ export function renderAdminDashboard(user, wsService) {
3129
3087
 
3130
3088
  <div class="stat-card-modern">
3131
3089
  <div class="stat-icon" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%);">
3132
- ${config.autoBackup ? '✅' : '❌'}
3090
+ ${config.autoBackup ? '�? : '�?}
3133
3091
  </div>
3134
3092
  <div class="stat-content">
3135
3093
  <div class="stat-label">自动备份</div>
3136
- <div class="stat-value">${config.autoBackup ? '已启用' : '已禁用'}</div>
3094
+ <div class="stat-value">${config.autoBackup ? '已启�? : '已禁�?}</div>
3137
3095
  </div>
3138
3096
  </div>
3139
3097
 
@@ -3143,7 +3101,7 @@ export function renderAdminDashboard(user, wsService) {
3143
3101
  </div>
3144
3102
  <div class="stat-content">
3145
3103
  <div class="stat-label">备份频率</div>
3146
- <div class="stat-value">${config.schedule || '未设置'}</div>
3104
+ <div class="stat-value">${config.schedule || '未设�?}</div>
3147
3105
  </div>
3148
3106
  </div>
3149
3107
 
@@ -3153,7 +3111,7 @@ export function renderAdminDashboard(user, wsService) {
3153
3111
  </div>
3154
3112
  <div class="stat-content">
3155
3113
  <div class="stat-label">保留天数</div>
3156
- <div class="stat-value">${config.retention || 30} 天</div>
3114
+ <div class="stat-value">${config.retention || 30} �?/div>
3157
3115
  </div>
3158
3116
  </div>
3159
3117
  </div>
@@ -3161,14 +3119,13 @@ export function renderAdminDashboard(user, wsService) {
3161
3119
  <!-- 备份列表 -->
3162
3120
  <div class="backup-list-section">
3163
3121
  <h3 style="margin: 30px 0 20px; color: var(--text-primary); font-size: 20px;">
3164
- 📦 最近备份
3165
- </h3>
3122
+ 📦 最近备�? </h3>
3166
3123
  <div class="backup-cards-grid" id="backupCards">
3167
3124
  ${backups.length === 0 ? `
3168
3125
  <div class="empty-state-modern">
3169
3126
  <div class="empty-icon">📭</div>
3170
3127
  <h3>暂无备份记录</h3>
3171
- <p>点击"立即备份"创建第一个备份</p>
3128
+ <p>点击"立即备份"创建第一个备�?/p>
3172
3129
  <button class="btn-primary" onclick="document.getElementById('createBackupBtn').click()">
3173
3130
  立即备份
3174
3131
  </button>
@@ -3201,11 +3158,11 @@ export function renderAdminDashboard(user, wsService) {
3201
3158
  const typeConfig = {
3202
3159
  manual: { icon: '📦', label: '手动备份', color: '#6366f1' },
3203
3160
  scheduled: { icon: '🔄', label: '定时备份', color: '#10b981' },
3204
- auto: { icon: '⚡', label: '自动备份', color: '#f59e0b' }
3161
+ auto: { icon: '�?, label: '自动备份', color: '#f59e0b' }
3205
3162
  };
3206
3163
 
3207
3164
  const config = typeConfig[backup.type] || typeConfig.manual;
3208
- const statusIcon = backup.status === 'completed' ? '✅' : backup.status === 'failed' ? '❌' : '⏳';
3165
+ const statusIcon = backup.status === 'completed' ? '�? : backup.status === 'failed' ? '�? : '�?;
3209
3166
 
3210
3167
  return `
3211
3168
  <div class="backup-card-modern">
@@ -3226,8 +3183,8 @@ export function renderAdminDashboard(user, wsService) {
3226
3183
  <span class="info-value">${formatFileSize(backup.size)}</span>
3227
3184
  </div>
3228
3185
  <div class="backup-info-row">
3229
- <span class="info-label">状态</span>
3230
- <span class="info-value">${backup.status === 'completed' ? '完成' : backup.status === 'failed' ? '失败' : '进行中'}</span>
3186
+ <span class="info-label">状�?/span>
3187
+ <span class="info-value">${backup.status === 'completed' ? '完成' : backup.status === 'failed' ? '失败' : '进行�?}</span>
3231
3188
  </div>
3232
3189
  </div>
3233
3190
 
@@ -3240,13 +3197,13 @@ export function renderAdminDashboard(user, wsService) {
3240
3197
  <span>🔄</span> 恢复
3241
3198
  </button>
3242
3199
  <button class="btn-action btn-delete" data-id="${backup._id}" data-action="delete">
3243
- <span>🗑️</span> 删除
3200
+ <span>🗑�?/span> 删除
3244
3201
  </button>
3245
3202
  </div>
3246
3203
  ` : `
3247
3204
  <div class="backup-card-actions">
3248
3205
  <button class="btn-action btn-delete" data-id="${backup._id}" data-action="delete">
3249
- <span>🗑️</span> 删除
3206
+ <span>🗑�?/span> 删除
3250
3207
  </button>
3251
3208
  </div>
3252
3209
  `}
@@ -3268,7 +3225,7 @@ export function renderAdminDashboard(user, wsService) {
3268
3225
  <label>备份类型</label>
3269
3226
  <select id="backupType" required>
3270
3227
  <option value="full">完整备份(所有数据)</option>
3271
- <option value="incremental">增量备份(仅变更)</option>
3228
+ <option value="incremental">增量备份(仅变更�?/option>
3272
3229
  </select>
3273
3230
  </div>
3274
3231
  <div class="form-group">
@@ -3276,7 +3233,7 @@ export function renderAdminDashboard(user, wsService) {
3276
3233
  <textarea id="backupDescription" rows="3" placeholder="备份说明..."></textarea>
3277
3234
  </div>
3278
3235
  <div style="display: flex; gap: 10px; margin-top: 20px;">
3279
- <button type="submit" class="btn-primary">开始备份</button>
3236
+ <button type="submit" class="btn-primary">开始备�?/button>
3280
3237
  <button type="button" class="btn-secondary" id="cancelCreateBackup">取消</button>
3281
3238
  </div>
3282
3239
  </form>
@@ -3299,16 +3256,16 @@ export function renderAdminDashboard(user, wsService) {
3299
3256
  </div>
3300
3257
  <div class="form-group">
3301
3258
  <label>备份频率(Cron表达式)</label>
3302
- <input type="text" id="backupSchedule" value="${config.schedule || '0 2 * * *'}" placeholder="0 2 * * * (每天凌晨2点)">
3303
- <small>示例:0 2 * * * (每天凌晨2点),0 */6 * * * (每6小时)</small>
3259
+ <input type="text" id="backupSchedule" value="${config.schedule || '0 2 * * *'}" placeholder="0 2 * * * (每天凌晨2�?">
3260
+ <small>示例�? 2 * * * (每天凌晨2�?�? */6 * * * (�?小时)</small>
3304
3261
  </div>
3305
3262
  <div class="form-group">
3306
3263
  <label>保留天数</label>
3307
3264
  <input type="number" id="backupRetention" value="${config.retention || 30}" min="1" max="365">
3308
- <small>超过此天数的备份将自动删除</small>
3265
+ <small>超过此天数的备份将自动删�?/small>
3309
3266
  </div>
3310
3267
  <div class="form-group">
3311
- <label>最大备份数量</label>
3268
+ <label>最大备份数�?/label>
3312
3269
  <input type="number" id="maxBackups" value="${config.maxBackups || 10}" min="1" max="100">
3313
3270
  </div>
3314
3271
  <div style="display: flex; gap: 10px; margin-top: 20px;">
@@ -3567,7 +3524,7 @@ export function renderAdminDashboard(user, wsService) {
3567
3524
  const description = document.getElementById('backupDescription').value;
3568
3525
 
3569
3526
  try {
3570
- const response = await fetch('http://localhost:8765/api/backup/create', {
3527
+ const response = await fetch('http://localhost:3000/api/backup/create', {
3571
3528
  method: 'POST',
3572
3529
  headers: {
3573
3530
  'Content-Type': 'application/json',
@@ -3578,7 +3535,7 @@ export function renderAdminDashboard(user, wsService) {
3578
3535
 
3579
3536
  const result = await response.json();
3580
3537
  if (result.success) {
3581
- alert('备份创建成功!');
3538
+ alert('备份创建成功�?);
3582
3539
  document.getElementById('createBackupModal').classList.add('hidden');
3583
3540
  renderOptimizedBackupView(container);
3584
3541
  } else {
@@ -3600,7 +3557,7 @@ export function renderAdminDashboard(user, wsService) {
3600
3557
  };
3601
3558
 
3602
3559
  try {
3603
- const response = await fetch('http://localhost:8765/api/backup/config', {
3560
+ const response = await fetch('http://localhost:3000/api/backup/config', {
3604
3561
  method: 'PUT',
3605
3562
  headers: {
3606
3563
  'Content-Type': 'application/json',
@@ -3611,7 +3568,7 @@ export function renderAdminDashboard(user, wsService) {
3611
3568
 
3612
3569
  const result = await response.json();
3613
3570
  if (result.success) {
3614
- alert('设置保存成功!');
3571
+ alert('设置保存成功�?);
3615
3572
  document.getElementById('configBackupModal').classList.add('hidden');
3616
3573
  renderOptimizedBackupView(container);
3617
3574
  } else {
@@ -3626,17 +3583,17 @@ export function renderAdminDashboard(user, wsService) {
3626
3583
  document.querySelectorAll('[data-action="download"]').forEach(btn => {
3627
3584
  btn.addEventListener('click', () => {
3628
3585
  const backupId = btn.dataset.id;
3629
- window.location.href = `http://localhost:8765/api/backup/${backupId}/download?token=${token}`;
3586
+ window.location.href = `http://localhost:3000/api/backup/${backupId}/download?token=${token}`;
3630
3587
  });
3631
3588
  });
3632
3589
 
3633
3590
  document.querySelectorAll('[data-action="restore"]').forEach(btn => {
3634
3591
  btn.addEventListener('click', async () => {
3635
- if (!confirm('确定要恢复此备份吗?这将覆盖当前数据!')) return;
3592
+ if (!confirm('确定要恢复此备份吗?这将覆盖当前数据�?)) return;
3636
3593
 
3637
3594
  const backupId = btn.dataset.id;
3638
3595
  try {
3639
- const response = await fetch(`http://localhost:8765/api/backup/${backupId}/restore`, {
3596
+ const response = await fetch(`http://localhost:3000/api/backup/${backupId}/restore`, {
3640
3597
  method: 'POST',
3641
3598
  headers: { 'Authorization': `Bearer ${token}` }
3642
3599
  });
@@ -3660,14 +3617,14 @@ export function renderAdminDashboard(user, wsService) {
3660
3617
 
3661
3618
  const backupId = btn.dataset.id;
3662
3619
  try {
3663
- const response = await fetch(`http://localhost:8765/api/backup/${backupId}`, {
3620
+ const response = await fetch(`http://localhost:3000/api/backup/${backupId}`, {
3664
3621
  method: 'DELETE',
3665
3622
  headers: { 'Authorization': `Bearer ${token}` }
3666
3623
  });
3667
3624
 
3668
3625
  const result = await response.json();
3669
3626
  if (result.success) {
3670
- alert('备份删除成功!');
3627
+ alert('备份删除成功�?);
3671
3628
  renderOptimizedBackupView(container);
3672
3629
  } else {
3673
3630
  alert('删除失败: ' + (result.error?.message || '未知错误'));