collabdocchat 2.0.0 → 2.0.2

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.
@@ -1,803 +1,803 @@
1
- /**
2
- * 美化后的知识库视图
3
- * 现代化卡片布局,增强视觉效果
4
- */
5
-
6
- import { showCreateKnowledgeModal, showEditKnowledgeModal } from '../components/knowledge-modal.js';
7
-
8
- export async function renderOptimizedKnowledgeView(container, currentGroup, apiService, currentUserId) {
9
- if (!currentGroup) {
10
- container.innerHTML = `
11
- <div class="empty-state-modern">
12
- <div class="empty-icon">📚</div>
13
- <h3>请先选择一个群组</h3>
14
- <p>选择群组后即可查看和管理知识库文档</p>
15
- </div>
16
- `;
17
- return;
18
- }
19
-
20
- try {
21
- const token = localStorage.getItem('token');
22
- const response = await fetch(`http://localhost:8765/api/knowledge/group/${currentGroup._id}`, {
23
- headers: { 'Authorization': `Bearer ${token}` }
24
- });
25
- const result = await response.json();
26
-
27
- container.innerHTML = `
28
- <div class="knowledge-view-modern">
29
- <!-- 头部区域 -->
30
- <div class="knowledge-header-modern">
31
- <div class="header-left">
32
- <div class="header-icon">📚</div>
33
- <div class="header-content">
34
- <h2 class="header-title">知识库管理</h2>
35
- <p class="header-subtitle">${currentGroup.name} · ${result.data?.knowledgeList?.length || 0} 篇文档</p>
36
- </div>
37
- </div>
38
- <button class="btn-create-knowledge-modern" id="createKnowledgeBtn">
39
- <span class="btn-icon">✨</span>
40
- <span>新建文档</span>
41
- </button>
42
- </div>
43
-
44
- <!-- 统计卡片 -->
45
- <div class="knowledge-stats-modern">
46
- <div class="stat-card-kb">
47
- <div class="stat-icon-kb" style="background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);">📄</div>
48
- <div class="stat-content-kb">
49
- <div class="stat-label-kb">总文档数</div>
50
- <div class="stat-value-kb">${result.data?.knowledgeList?.length || 0}</div>
51
- </div>
52
- </div>
53
- <div class="stat-card-kb">
54
- <div class="stat-icon-kb" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%);">✅</div>
55
- <div class="stat-content-kb">
56
- <div class="stat-label-kb">已发布</div>
57
- <div class="stat-value-kb">${result.data?.knowledgeList?.filter(kb => kb.status === 'published').length || 0}</div>
58
- </div>
59
- </div>
60
- <div class="stat-card-kb">
61
- <div class="stat-icon-kb" style="background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);">📝</div>
62
- <div class="stat-content-kb">
63
- <div class="stat-label-kb">草稿</div>
64
- <div class="stat-value-kb">${result.data?.knowledgeList?.filter(kb => kb.status === 'draft').length || 0}</div>
65
- </div>
66
- </div>
67
- <div class="stat-card-kb">
68
- <div class="stat-icon-kb" style="background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);">👁️</div>
69
- <div class="stat-content-kb">
70
- <div class="stat-label-kb">总浏览量</div>
71
- <div class="stat-value-kb">${result.data?.knowledgeList?.reduce((sum, kb) => sum + (kb.views || 0), 0) || 0}</div>
72
- </div>
73
- </div>
74
- </div>
75
-
76
- <!-- 知识库列表 -->
77
- <div class="knowledge-list-modern" id="knowledgeList"></div>
78
- </div>
79
- `;
80
-
81
- // 添加样式
82
- addKnowledgeViewStyles();
83
-
84
- const knowledgeList = document.getElementById('knowledgeList');
85
-
86
- if (!result.data || !result.data.knowledgeList || result.data.knowledgeList.length === 0) {
87
- knowledgeList.innerHTML = `
88
- <div class="empty-state-modern">
89
- <div class="empty-icon">📚</div>
90
- <h3>暂无知识库文档</h3>
91
- <p>点击"新建文档"按钮创建第一篇文档</p>
92
- </div>
93
- `;
94
- } else {
95
- result.data.knowledgeList.forEach(kb => {
96
- const kbCard = document.createElement('div');
97
- kbCard.className = 'knowledge-card-modern';
98
-
99
- // 状态样式
100
- const statusConfig = {
101
- 'published': { text: '已发布', class: 'status-published', icon: '✅' },
102
- 'draft': { text: '草稿', class: 'status-draft', icon: '📝' },
103
- 'archived': { text: '已归档', class: 'status-archived', icon: '📦' }
104
- };
105
- const status = statusConfig[kb.status] || statusConfig['draft'];
106
-
107
- kbCard.innerHTML = `
108
- <div class="kb-card-header-modern">
109
- <div class="kb-status-badge-modern ${status.class}">
110
- <span class="status-icon">${status.icon}</span>
111
- <span>${status.text}</span>
112
- </div>
113
- <div class="kb-actions-menu">
114
- <button class="btn-menu-modern" data-id="${kb._id}">⋮</button>
115
- </div>
116
- </div>
117
-
118
- <div class="kb-card-body-modern">
119
- <h3 class="kb-title-modern">${kb.title}</h3>
120
- <div class="kb-category-modern">
121
- <span class="category-icon">📁</span>
122
- <span>${kb.category || '未分类'}</span>
123
- </div>
124
- ${kb.tags && kb.tags.length > 0 ? `
125
- <div class="kb-tags-modern">
126
- ${kb.tags.slice(0, 3).map(tag => `
127
- <span class="kb-tag-modern">#${tag}</span>
128
- `).join('')}
129
- ${kb.tags.length > 3 ? `<span class="kb-tag-more">+${kb.tags.length - 3}</span>` : ''}
130
- </div>
131
- ` : ''}
132
- </div>
133
-
134
- <div class="kb-card-footer-modern">
135
- <div class="kb-meta-modern">
136
- <div class="meta-item-kb">
137
- <span class="meta-icon">👁️</span>
138
- <span>${kb.views || 0}</span>
139
- </div>
140
- <div class="meta-item-kb">
141
- <span class="meta-icon">👍</span>
142
- <span>${kb.likes ? kb.likes.length : 0}</span>
143
- </div>
144
- <div class="meta-item-kb">
145
- <span class="meta-icon">💬</span>
146
- <span>${kb.comments ? kb.comments.length : 0}</span>
147
- </div>
148
- </div>
149
- <div class="kb-actions-modern">
150
- <button class="btn-kb-action view" data-id="${kb._id}" data-action="view-kb" title="查看">
151
- <span>👁️</span>
152
- </button>
153
- <button class="btn-kb-action edit" data-id="${kb._id}" data-action="edit-kb" title="编辑">
154
- <span>✏️</span>
155
- </button>
156
- <button class="btn-kb-action delete" data-id="${kb._id}" data-action="delete-kb" title="删除">
157
- <span>🗑️</span>
158
- </button>
159
- </div>
160
- </div>
161
- `;
162
- knowledgeList.appendChild(kbCard);
163
- });
164
-
165
- // 绑定事件
166
- setupKnowledgeEvents(container, currentGroup, apiService, currentUserId);
167
- }
168
-
169
- // 新建文档按钮
170
- document.getElementById('createKnowledgeBtn')?.addEventListener('click', () => {
171
- showCreateKnowledgeModal(currentGroup, apiService, container, currentUserId);
172
- });
173
-
174
- // 监听知识库更新事件
175
- const handleKnowledgeUpdate = (e) => {
176
- if (e.detail.groupId === currentGroup._id) {
177
- renderOptimizedKnowledgeView(container, currentGroup, apiService, currentUserId);
178
- }
179
- };
180
-
181
- // 移除旧的监听器(如果存在)
182
- window.removeEventListener('knowledgeUpdated', handleKnowledgeUpdate);
183
- // 添加新的监听器
184
- window.addEventListener('knowledgeUpdated', handleKnowledgeUpdate);
185
-
186
- } catch (error) {
187
- console.error('加载知识库失败:', error);
188
- container.innerHTML = `
189
- <div class="error-state-modern">
190
- <div class="error-icon">❌</div>
191
- <h3>加载失败</h3>
192
- <p>${error.message || '未知错误'}</p>
193
- <button class="btn-retry-modern" onclick="location.reload()">重试</button>
194
- </div>
195
- `;
196
- }
197
- }
198
-
199
- // 设置事件监听
200
- function setupKnowledgeEvents(container, currentGroup, apiService, currentUserId) {
201
- const token = localStorage.getItem('token');
202
-
203
- // 查看文档
204
- container.querySelectorAll('[data-action="view-kb"]').forEach(btn => {
205
- btn.addEventListener('click', async () => {
206
- await showKnowledgeDetail(btn.dataset.id, apiService, container);
207
- });
208
- });
209
-
210
- // 编辑文档
211
- container.querySelectorAll('[data-action="edit-kb"]').forEach(btn => {
212
- btn.addEventListener('click', async () => {
213
- await showEditKnowledgeModal(btn.dataset.id, currentGroup, apiService, container, currentUserId);
214
- });
215
- });
216
-
217
- // 删除文档
218
- container.querySelectorAll('[data-action="delete-kb"]').forEach(btn => {
219
- btn.addEventListener('click', async () => {
220
- if (confirm('确定要删除这个知识库文档吗?此操作不可撤销!')) {
221
- try {
222
- await fetch(`http://localhost:8765/api/knowledge/${btn.dataset.id}`, {
223
- method: 'DELETE',
224
- headers: { 'Authorization': `Bearer ${token}` }
225
- });
226
- alert('删除成功!');
227
- // 触发自定义事件通知刷新
228
- window.dispatchEvent(new CustomEvent('knowledgeUpdated', {
229
- detail: { groupId: currentGroup._id }
230
- }));
231
- } catch (error) {
232
- alert('删除失败: ' + error.message);
233
- }
234
- }
235
- });
236
- });
237
- }
238
-
239
- // 显示知识库详情
240
- async function showKnowledgeDetail(kbId, apiService, container) {
241
- try {
242
- const token = localStorage.getItem('token');
243
- const response = await fetch(`http://localhost:8765/api/knowledge/${kbId}`, {
244
- headers: { 'Authorization': `Bearer ${token}` }
245
- });
246
- const result = await response.json();
247
- const kb = result.data.knowledge;
248
-
249
- const modal = document.createElement('div');
250
- modal.className = 'modal';
251
- modal.id = 'knowledgeDetailModal';
252
-
253
- modal.innerHTML = `
254
- <div class="modal-content knowledge-modal-content">
255
- <div class="modal-header">
256
- <h3>📄 ${kb.title}</h3>
257
- <button class="modal-close" id="closeDetailModal">×</button>
258
- </div>
259
-
260
- <div class="kb-detail-content">
261
- <div class="kb-meta-detail">
262
- <span>👤 作者: ${kb.author.username}</span>
263
- <span>📁 分类: ${kb.category || '未分类'}</span>
264
- <span>👁️ 浏览: ${kb.views || 0}</span>
265
- <span>🕒 更新: ${new Date(kb.updatedAt).toLocaleString()}</span>
266
- </div>
267
-
268
- ${kb.tags && kb.tags.length > 0 ? `
269
- <div class="kb-tags-detail">
270
- ${kb.tags.map(tag => `<span class="tag-detail">#${tag}</span>`).join('')}
271
- </div>
272
- ` : ''}
273
-
274
- <div class="kb-content-detail">
275
- ${kb.content}
276
- </div>
277
- </div>
278
- </div>
279
- `;
280
-
281
- document.body.appendChild(modal);
282
-
283
- // 关闭模态框
284
- const closeModal = () => {
285
- modal.remove();
286
- };
287
-
288
- document.getElementById('closeDetailModal').addEventListener('click', closeModal);
289
- modal.addEventListener('click', (e) => {
290
- if (e.target === modal) closeModal();
291
- });
292
-
293
- // 添加详情样式
294
- if (!document.getElementById('kb-detail-styles')) {
295
- const style = document.createElement('style');
296
- style.id = 'kb-detail-styles';
297
- style.textContent = `
298
- .kb-detail-content {
299
- padding: 20px 0;
300
- }
301
-
302
- .kb-meta-detail {
303
- display: flex;
304
- flex-wrap: wrap;
305
- gap: 16px;
306
- padding: 16px;
307
- background: var(--bg-dark);
308
- border-radius: 12px;
309
- margin-bottom: 20px;
310
- font-size: 14px;
311
- color: var(--text-secondary);
312
- }
313
-
314
- .kb-tags-detail {
315
- display: flex;
316
- flex-wrap: wrap;
317
- gap: 8px;
318
- margin-bottom: 20px;
319
- }
320
-
321
- .tag-detail {
322
- padding: 6px 12px;
323
- background: rgba(99,102,241,0.1);
324
- color: var(--primary);
325
- border-radius: 8px;
326
- font-size: 13px;
327
- font-weight: 600;
328
- }
329
-
330
- .kb-content-detail {
331
- padding: 20px;
332
- background: var(--bg-card);
333
- border-radius: 12px;
334
- border: 1px solid var(--border);
335
- line-height: 1.8;
336
- color: var(--text-primary);
337
- }
338
-
339
- .kb-content-detail h1,
340
- .kb-content-detail h2,
341
- .kb-content-detail h3 {
342
- margin-top: 24px;
343
- margin-bottom: 12px;
344
- color: var(--text-primary);
345
- }
346
-
347
- .kb-content-detail p {
348
- margin-bottom: 16px;
349
- }
350
-
351
- .kb-content-detail code {
352
- background: var(--bg-dark);
353
- padding: 2px 6px;
354
- border-radius: 4px;
355
- font-family: 'Courier New', monospace;
356
- }
357
-
358
- .kb-content-detail pre {
359
- background: var(--bg-dark);
360
- padding: 16px;
361
- border-radius: 8px;
362
- overflow-x: auto;
363
- margin: 16px 0;
364
- }
365
- `;
366
- document.head.appendChild(style);
367
- }
368
-
369
- } catch (error) {
370
- alert('加载文档失败: ' + error.message);
371
- }
372
- }
373
-
374
- // 添加样式
375
- function addKnowledgeViewStyles() {
376
- if (document.getElementById('knowledge-view-modern-styles')) return;
377
-
378
- const style = document.createElement('style');
379
- style.id = 'knowledge-view-modern-styles';
380
- style.textContent = `
381
- .knowledge-view-modern {
382
- padding: 0;
383
- animation: fadeInUp 0.5s ease;
384
- }
385
-
386
- .knowledge-header-modern {
387
- display: flex;
388
- justify-content: space-between;
389
- align-items: center;
390
- padding: 30px;
391
- background: linear-gradient(135deg, var(--bg-card) 0%, rgba(99,102,241,0.05) 100%);
392
- border-radius: 20px;
393
- border: 1px solid var(--border);
394
- margin-bottom: 30px;
395
- }
396
-
397
- .header-left {
398
- display: flex;
399
- align-items: center;
400
- gap: 20px;
401
- }
402
-
403
- .header-icon {
404
- width: 60px;
405
- height: 60px;
406
- background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
407
- border-radius: 16px;
408
- display: flex;
409
- align-items: center;
410
- justify-content: center;
411
- font-size: 32px;
412
- box-shadow: 0 4px 12px rgba(99,102,241,0.3);
413
- }
414
-
415
- .header-content {
416
- flex: 1;
417
- }
418
-
419
- .header-title {
420
- margin: 0 0 6px;
421
- font-size: 28px;
422
- font-weight: 800;
423
- color: var(--text-primary);
424
- }
425
-
426
- .header-subtitle {
427
- margin: 0;
428
- font-size: 14px;
429
- color: var(--text-secondary);
430
- font-weight: 500;
431
- }
432
-
433
- .btn-create-knowledge-modern {
434
- padding: 14px 28px;
435
- background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
436
- color: white;
437
- border: none;
438
- border-radius: 12px;
439
- font-size: 15px;
440
- font-weight: 600;
441
- cursor: pointer;
442
- transition: all 0.3s ease;
443
- display: flex;
444
- align-items: center;
445
- gap: 8px;
446
- box-shadow: 0 4px 12px rgba(99,102,241,0.3);
447
- }
448
-
449
- .btn-create-knowledge-modern:hover {
450
- transform: translateY(-3px);
451
- box-shadow: 0 8px 24px rgba(99,102,241,0.4);
452
- }
453
-
454
- .btn-icon {
455
- font-size: 18px;
456
- }
457
-
458
- .knowledge-stats-modern {
459
- display: grid;
460
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
461
- gap: 20px;
462
- margin-bottom: 30px;
463
- padding: 0 30px;
464
- }
465
-
466
- .stat-card-kb {
467
- background: var(--bg-card);
468
- border: 2px solid var(--border);
469
- border-radius: 16px;
470
- padding: 20px;
471
- display: flex;
472
- align-items: center;
473
- gap: 16px;
474
- transition: all 0.3s ease;
475
- }
476
-
477
- .stat-card-kb:hover {
478
- transform: translateY(-5px);
479
- box-shadow: 0 8px 24px rgba(99,102,241,0.2);
480
- border-color: var(--primary);
481
- }
482
-
483
- .stat-icon-kb {
484
- width: 50px;
485
- height: 50px;
486
- border-radius: 12px;
487
- display: flex;
488
- align-items: center;
489
- justify-content: center;
490
- font-size: 24px;
491
- flex-shrink: 0;
492
- }
493
-
494
- .stat-content-kb {
495
- flex: 1;
496
- }
497
-
498
- .stat-label-kb {
499
- font-size: 13px;
500
- color: var(--text-secondary);
501
- margin-bottom: 6px;
502
- font-weight: 600;
503
- text-transform: uppercase;
504
- letter-spacing: 0.5px;
505
- }
506
-
507
- .stat-value-kb {
508
- font-size: 28px;
509
- font-weight: 800;
510
- color: var(--text-primary);
511
- }
512
-
513
- .knowledge-list-modern {
514
- display: grid;
515
- grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
516
- gap: 24px;
517
- padding: 0 30px 30px;
518
- }
519
-
520
- .knowledge-card-modern {
521
- background: var(--bg-card);
522
- border: 2px solid var(--border);
523
- border-radius: 16px;
524
- overflow: hidden;
525
- transition: all 0.3s ease;
526
- display: flex;
527
- flex-direction: column;
528
- }
529
-
530
- .knowledge-card-modern:hover {
531
- transform: translateY(-8px);
532
- box-shadow: 0 12px 32px rgba(0,0,0,0.3);
533
- border-color: var(--primary);
534
- }
535
-
536
- .kb-card-header-modern {
537
- padding: 16px 20px;
538
- background: var(--bg-dark);
539
- display: flex;
540
- justify-content: space-between;
541
- align-items: center;
542
- border-bottom: 1px solid var(--border);
543
- }
544
-
545
- .kb-status-badge-modern {
546
- padding: 6px 12px;
547
- border-radius: 12px;
548
- font-size: 13px;
549
- font-weight: 600;
550
- display: flex;
551
- align-items: center;
552
- gap: 6px;
553
- }
554
-
555
- .kb-status-badge-modern.status-published {
556
- background: linear-gradient(135deg, #10b981 0%, #059669 100%);
557
- color: white;
558
- }
559
-
560
- .kb-status-badge-modern.status-draft {
561
- background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
562
- color: white;
563
- }
564
-
565
- .kb-status-badge-modern.status-archived {
566
- background: linear-gradient(135deg, #6b7280 0%, #4b5563 100%);
567
- color: white;
568
- }
569
-
570
- .status-icon {
571
- font-size: 14px;
572
- }
573
-
574
- .btn-menu-modern {
575
- width: 32px;
576
- height: 32px;
577
- background: var(--bg-hover);
578
- border: none;
579
- border-radius: 8px;
580
- color: var(--text-secondary);
581
- font-size: 20px;
582
- cursor: pointer;
583
- transition: all 0.3s ease;
584
- display: flex;
585
- align-items: center;
586
- justify-content: center;
587
- }
588
-
589
- .btn-menu-modern:hover {
590
- background: var(--primary);
591
- color: white;
592
- }
593
-
594
- .kb-card-body-modern {
595
- padding: 20px;
596
- flex: 1;
597
- }
598
-
599
- .kb-title-modern {
600
- margin: 0 0 12px;
601
- font-size: 18px;
602
- font-weight: 700;
603
- color: var(--text-primary);
604
- line-height: 1.4;
605
- display: -webkit-box;
606
- -webkit-line-clamp: 2;
607
- -webkit-box-orient: vertical;
608
- overflow: hidden;
609
- }
610
-
611
- .kb-category-modern {
612
- display: flex;
613
- align-items: center;
614
- gap: 8px;
615
- margin-bottom: 12px;
616
- padding: 8px 12px;
617
- background: var(--bg-dark);
618
- border-radius: 8px;
619
- font-size: 14px;
620
- color: var(--text-secondary);
621
- width: fit-content;
622
- }
623
-
624
- .category-icon {
625
- font-size: 16px;
626
- }
627
-
628
- .kb-tags-modern {
629
- display: flex;
630
- flex-wrap: wrap;
631
- gap: 8px;
632
- margin-top: 12px;
633
- }
634
-
635
- .kb-tag-modern {
636
- padding: 4px 10px;
637
- background: rgba(99,102,241,0.1);
638
- color: var(--primary);
639
- border-radius: 8px;
640
- font-size: 12px;
641
- font-weight: 600;
642
- }
643
-
644
- .kb-tag-more {
645
- padding: 4px 10px;
646
- background: var(--bg-hover);
647
- color: var(--text-secondary);
648
- border-radius: 8px;
649
- font-size: 12px;
650
- font-weight: 600;
651
- }
652
-
653
- .kb-card-footer-modern {
654
- padding: 16px 20px;
655
- background: var(--bg-dark);
656
- border-top: 1px solid var(--border);
657
- display: flex;
658
- justify-content: space-between;
659
- align-items: center;
660
- }
661
-
662
- .kb-meta-modern {
663
- display: flex;
664
- gap: 16px;
665
- }
666
-
667
- .meta-item-kb {
668
- display: flex;
669
- align-items: center;
670
- gap: 6px;
671
- font-size: 13px;
672
- color: var(--text-secondary);
673
- font-weight: 600;
674
- }
675
-
676
- .meta-icon {
677
- font-size: 16px;
678
- }
679
-
680
- .kb-actions-modern {
681
- display: flex;
682
- gap: 8px;
683
- }
684
-
685
- .btn-kb-action {
686
- width: 36px;
687
- height: 36px;
688
- border: none;
689
- border-radius: 8px;
690
- font-size: 16px;
691
- cursor: pointer;
692
- transition: all 0.3s ease;
693
- display: flex;
694
- align-items: center;
695
- justify-content: center;
696
- }
697
-
698
- .btn-kb-action.view {
699
- background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
700
- color: white;
701
- }
702
-
703
- .btn-kb-action.edit {
704
- background: linear-gradient(135deg, #10b981 0%, #059669 100%);
705
- color: white;
706
- }
707
-
708
- .btn-kb-action.delete {
709
- background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
710
- color: white;
711
- }
712
-
713
- .btn-kb-action:hover {
714
- transform: scale(1.1);
715
- box-shadow: 0 4px 12px rgba(0,0,0,0.3);
716
- }
717
-
718
- .empty-state-modern,
719
- .error-state-modern {
720
- text-align: center;
721
- padding: 80px 20px;
722
- background: var(--bg-card);
723
- border-radius: 20px;
724
- border: 2px dashed var(--border);
725
- margin: 0 30px;
726
- }
727
-
728
- .empty-icon,
729
- .error-icon {
730
- font-size: 64px;
731
- margin-bottom: 20px;
732
- }
733
-
734
- .empty-state-modern h3,
735
- .error-state-modern h3 {
736
- margin: 0 0 12px;
737
- font-size: 24px;
738
- font-weight: 700;
739
- color: var(--text-primary);
740
- }
741
-
742
- .empty-state-modern p,
743
- .error-state-modern p {
744
- margin: 0;
745
- font-size: 15px;
746
- color: var(--text-secondary);
747
- }
748
-
749
- .btn-retry-modern {
750
- margin-top: 20px;
751
- padding: 12px 24px;
752
- background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
753
- color: white;
754
- border: none;
755
- border-radius: 12px;
756
- font-size: 15px;
757
- font-weight: 600;
758
- cursor: pointer;
759
- transition: all 0.3s ease;
760
- }
761
-
762
- .btn-retry-modern:hover {
763
- transform: translateY(-3px);
764
- box-shadow: 0 8px 24px rgba(99,102,241,0.4);
765
- }
766
-
767
- @keyframes fadeInUp {
768
- from {
769
- opacity: 0;
770
- transform: translateY(20px);
771
- }
772
- to {
773
- opacity: 1;
774
- transform: translateY(0);
775
- }
776
- }
777
-
778
- @media (max-width: 768px) {
779
- .knowledge-header-modern {
780
- flex-direction: column;
781
- gap: 20px;
782
- align-items: flex-start;
783
- }
784
-
785
- .btn-create-knowledge-modern {
786
- width: 100%;
787
- justify-content: center;
788
- }
789
-
790
- .knowledge-stats-modern {
791
- grid-template-columns: repeat(2, 1fr);
792
- }
793
-
794
- .knowledge-list-modern {
795
- grid-template-columns: 1fr;
796
- }
797
- }
798
- `;
799
- document.head.appendChild(style);
800
- }
801
-
802
-
803
-
1
+ /**
2
+ * 美化后的知识库视图
3
+ * 现代化卡片布局,增强视觉效果
4
+ */
5
+
6
+ import { showCreateKnowledgeModal, showEditKnowledgeModal } from '../components/knowledge-modal.js';
7
+
8
+ export async function renderOptimizedKnowledgeView(container, currentGroup, apiService, currentUserId) {
9
+ if (!currentGroup) {
10
+ container.innerHTML = `
11
+ <div class="empty-state-modern">
12
+ <div class="empty-icon">📚</div>
13
+ <h3>请先选择一个群组</h3>
14
+ <p>选择群组后即可查看和管理知识库文档</p>
15
+ </div>
16
+ `;
17
+ return;
18
+ }
19
+
20
+ try {
21
+ const token = localStorage.getItem('token');
22
+ const response = await fetch(`http://localhost:8765/api/knowledge/group/${currentGroup._id}`, {
23
+ headers: { 'Authorization': `Bearer ${token}` }
24
+ });
25
+ const result = await response.json();
26
+
27
+ container.innerHTML = `
28
+ <div class="knowledge-view-modern">
29
+ <!-- 头部区域 -->
30
+ <div class="knowledge-header-modern">
31
+ <div class="header-left">
32
+ <div class="header-icon">📚</div>
33
+ <div class="header-content">
34
+ <h2 class="header-title">知识库管理</h2>
35
+ <p class="header-subtitle">${currentGroup.name} · ${result.data?.knowledgeList?.length || 0} 篇文档</p>
36
+ </div>
37
+ </div>
38
+ <button class="btn-create-knowledge-modern" id="createKnowledgeBtn">
39
+ <span class="btn-icon">✨</span>
40
+ <span>新建文档</span>
41
+ </button>
42
+ </div>
43
+
44
+ <!-- 统计卡片 -->
45
+ <div class="knowledge-stats-modern">
46
+ <div class="stat-card-kb">
47
+ <div class="stat-icon-kb" style="background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);">📄</div>
48
+ <div class="stat-content-kb">
49
+ <div class="stat-label-kb">总文档数</div>
50
+ <div class="stat-value-kb">${result.data?.knowledgeList?.length || 0}</div>
51
+ </div>
52
+ </div>
53
+ <div class="stat-card-kb">
54
+ <div class="stat-icon-kb" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%);">✅</div>
55
+ <div class="stat-content-kb">
56
+ <div class="stat-label-kb">已发布</div>
57
+ <div class="stat-value-kb">${result.data?.knowledgeList?.filter(kb => kb.status === 'published').length || 0}</div>
58
+ </div>
59
+ </div>
60
+ <div class="stat-card-kb">
61
+ <div class="stat-icon-kb" style="background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);">📝</div>
62
+ <div class="stat-content-kb">
63
+ <div class="stat-label-kb">草稿</div>
64
+ <div class="stat-value-kb">${result.data?.knowledgeList?.filter(kb => kb.status === 'draft').length || 0}</div>
65
+ </div>
66
+ </div>
67
+ <div class="stat-card-kb">
68
+ <div class="stat-icon-kb" style="background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);">👁️</div>
69
+ <div class="stat-content-kb">
70
+ <div class="stat-label-kb">总浏览量</div>
71
+ <div class="stat-value-kb">${result.data?.knowledgeList?.reduce((sum, kb) => sum + (kb.views || 0), 0) || 0}</div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+
76
+ <!-- 知识库列表 -->
77
+ <div class="knowledge-list-modern" id="knowledgeList"></div>
78
+ </div>
79
+ `;
80
+
81
+ // 添加样式
82
+ addKnowledgeViewStyles();
83
+
84
+ const knowledgeList = document.getElementById('knowledgeList');
85
+
86
+ if (!result.data || !result.data.knowledgeList || result.data.knowledgeList.length === 0) {
87
+ knowledgeList.innerHTML = `
88
+ <div class="empty-state-modern">
89
+ <div class="empty-icon">📚</div>
90
+ <h3>暂无知识库文档</h3>
91
+ <p>点击"新建文档"按钮创建第一篇文档</p>
92
+ </div>
93
+ `;
94
+ } else {
95
+ result.data.knowledgeList.forEach(kb => {
96
+ const kbCard = document.createElement('div');
97
+ kbCard.className = 'knowledge-card-modern';
98
+
99
+ // 状态样式
100
+ const statusConfig = {
101
+ 'published': { text: '已发布', class: 'status-published', icon: '✅' },
102
+ 'draft': { text: '草稿', class: 'status-draft', icon: '📝' },
103
+ 'archived': { text: '已归档', class: 'status-archived', icon: '📦' }
104
+ };
105
+ const status = statusConfig[kb.status] || statusConfig['draft'];
106
+
107
+ kbCard.innerHTML = `
108
+ <div class="kb-card-header-modern">
109
+ <div class="kb-status-badge-modern ${status.class}">
110
+ <span class="status-icon">${status.icon}</span>
111
+ <span>${status.text}</span>
112
+ </div>
113
+ <div class="kb-actions-menu">
114
+ <button class="btn-menu-modern" data-id="${kb._id}">⋮</button>
115
+ </div>
116
+ </div>
117
+
118
+ <div class="kb-card-body-modern">
119
+ <h3 class="kb-title-modern">${kb.title}</h3>
120
+ <div class="kb-category-modern">
121
+ <span class="category-icon">📁</span>
122
+ <span>${kb.category || '未分类'}</span>
123
+ </div>
124
+ ${kb.tags && kb.tags.length > 0 ? `
125
+ <div class="kb-tags-modern">
126
+ ${kb.tags.slice(0, 3).map(tag => `
127
+ <span class="kb-tag-modern">#${tag}</span>
128
+ `).join('')}
129
+ ${kb.tags.length > 3 ? `<span class="kb-tag-more">+${kb.tags.length - 3}</span>` : ''}
130
+ </div>
131
+ ` : ''}
132
+ </div>
133
+
134
+ <div class="kb-card-footer-modern">
135
+ <div class="kb-meta-modern">
136
+ <div class="meta-item-kb">
137
+ <span class="meta-icon">👁️</span>
138
+ <span>${kb.views || 0}</span>
139
+ </div>
140
+ <div class="meta-item-kb">
141
+ <span class="meta-icon">👍</span>
142
+ <span>${kb.likes ? kb.likes.length : 0}</span>
143
+ </div>
144
+ <div class="meta-item-kb">
145
+ <span class="meta-icon">💬</span>
146
+ <span>${kb.comments ? kb.comments.length : 0}</span>
147
+ </div>
148
+ </div>
149
+ <div class="kb-actions-modern">
150
+ <button class="btn-kb-action view" data-id="${kb._id}" data-action="view-kb" title="查看">
151
+ <span>👁️</span>
152
+ </button>
153
+ <button class="btn-kb-action edit" data-id="${kb._id}" data-action="edit-kb" title="编辑">
154
+ <span>✏️</span>
155
+ </button>
156
+ <button class="btn-kb-action delete" data-id="${kb._id}" data-action="delete-kb" title="删除">
157
+ <span>🗑️</span>
158
+ </button>
159
+ </div>
160
+ </div>
161
+ `;
162
+ knowledgeList.appendChild(kbCard);
163
+ });
164
+
165
+ // 绑定事件
166
+ setupKnowledgeEvents(container, currentGroup, apiService, currentUserId);
167
+ }
168
+
169
+ // 新建文档按钮
170
+ document.getElementById('createKnowledgeBtn')?.addEventListener('click', () => {
171
+ showCreateKnowledgeModal(currentGroup, apiService, container, currentUserId);
172
+ });
173
+
174
+ // 监听知识库更新事件
175
+ const handleKnowledgeUpdate = (e) => {
176
+ if (e.detail.groupId === currentGroup._id) {
177
+ renderOptimizedKnowledgeView(container, currentGroup, apiService, currentUserId);
178
+ }
179
+ };
180
+
181
+ // 移除旧的监听器(如果存在)
182
+ window.removeEventListener('knowledgeUpdated', handleKnowledgeUpdate);
183
+ // 添加新的监听器
184
+ window.addEventListener('knowledgeUpdated', handleKnowledgeUpdate);
185
+
186
+ } catch (error) {
187
+ console.error('加载知识库失败:', error);
188
+ container.innerHTML = `
189
+ <div class="error-state-modern">
190
+ <div class="error-icon">❌</div>
191
+ <h3>加载失败</h3>
192
+ <p>${error.message || '未知错误'}</p>
193
+ <button class="btn-retry-modern" onclick="location.reload()">重试</button>
194
+ </div>
195
+ `;
196
+ }
197
+ }
198
+
199
+ // 设置事件监听
200
+ function setupKnowledgeEvents(container, currentGroup, apiService, currentUserId) {
201
+ const token = localStorage.getItem('token');
202
+
203
+ // 查看文档
204
+ container.querySelectorAll('[data-action="view-kb"]').forEach(btn => {
205
+ btn.addEventListener('click', async () => {
206
+ await showKnowledgeDetail(btn.dataset.id, apiService, container);
207
+ });
208
+ });
209
+
210
+ // 编辑文档
211
+ container.querySelectorAll('[data-action="edit-kb"]').forEach(btn => {
212
+ btn.addEventListener('click', async () => {
213
+ await showEditKnowledgeModal(btn.dataset.id, currentGroup, apiService, container, currentUserId);
214
+ });
215
+ });
216
+
217
+ // 删除文档
218
+ container.querySelectorAll('[data-action="delete-kb"]').forEach(btn => {
219
+ btn.addEventListener('click', async () => {
220
+ if (confirm('确定要删除这个知识库文档吗?此操作不可撤销!')) {
221
+ try {
222
+ await fetch(`http://localhost:8765/api/knowledge/${btn.dataset.id}`, {
223
+ method: 'DELETE',
224
+ headers: { 'Authorization': `Bearer ${token}` }
225
+ });
226
+ alert('删除成功!');
227
+ // 触发自定义事件通知刷新
228
+ window.dispatchEvent(new CustomEvent('knowledgeUpdated', {
229
+ detail: { groupId: currentGroup._id }
230
+ }));
231
+ } catch (error) {
232
+ alert('删除失败: ' + error.message);
233
+ }
234
+ }
235
+ });
236
+ });
237
+ }
238
+
239
+ // 显示知识库详情
240
+ async function showKnowledgeDetail(kbId, apiService, container) {
241
+ try {
242
+ const token = localStorage.getItem('token');
243
+ const response = await fetch(`http://localhost:8765/api/knowledge/${kbId}`, {
244
+ headers: { 'Authorization': `Bearer ${token}` }
245
+ });
246
+ const result = await response.json();
247
+ const kb = result.data.knowledge;
248
+
249
+ const modal = document.createElement('div');
250
+ modal.className = 'modal';
251
+ modal.id = 'knowledgeDetailModal';
252
+
253
+ modal.innerHTML = `
254
+ <div class="modal-content knowledge-modal-content">
255
+ <div class="modal-header">
256
+ <h3>📄 ${kb.title}</h3>
257
+ <button class="modal-close" id="closeDetailModal">×</button>
258
+ </div>
259
+
260
+ <div class="kb-detail-content">
261
+ <div class="kb-meta-detail">
262
+ <span>👤 作者: ${kb.author.username}</span>
263
+ <span>📁 分类: ${kb.category || '未分类'}</span>
264
+ <span>👁️ 浏览: ${kb.views || 0}</span>
265
+ <span>🕒 更新: ${new Date(kb.updatedAt).toLocaleString()}</span>
266
+ </div>
267
+
268
+ ${kb.tags && kb.tags.length > 0 ? `
269
+ <div class="kb-tags-detail">
270
+ ${kb.tags.map(tag => `<span class="tag-detail">#${tag}</span>`).join('')}
271
+ </div>
272
+ ` : ''}
273
+
274
+ <div class="kb-content-detail">
275
+ ${kb.content}
276
+ </div>
277
+ </div>
278
+ </div>
279
+ `;
280
+
281
+ document.body.appendChild(modal);
282
+
283
+ // 关闭模态框
284
+ const closeModal = () => {
285
+ modal.remove();
286
+ };
287
+
288
+ document.getElementById('closeDetailModal').addEventListener('click', closeModal);
289
+ modal.addEventListener('click', (e) => {
290
+ if (e.target === modal) closeModal();
291
+ });
292
+
293
+ // 添加详情样式
294
+ if (!document.getElementById('kb-detail-styles')) {
295
+ const style = document.createElement('style');
296
+ style.id = 'kb-detail-styles';
297
+ style.textContent = `
298
+ .kb-detail-content {
299
+ padding: 20px 0;
300
+ }
301
+
302
+ .kb-meta-detail {
303
+ display: flex;
304
+ flex-wrap: wrap;
305
+ gap: 16px;
306
+ padding: 16px;
307
+ background: var(--bg-dark);
308
+ border-radius: 12px;
309
+ margin-bottom: 20px;
310
+ font-size: 14px;
311
+ color: var(--text-secondary);
312
+ }
313
+
314
+ .kb-tags-detail {
315
+ display: flex;
316
+ flex-wrap: wrap;
317
+ gap: 8px;
318
+ margin-bottom: 20px;
319
+ }
320
+
321
+ .tag-detail {
322
+ padding: 6px 12px;
323
+ background: rgba(99,102,241,0.1);
324
+ color: var(--primary);
325
+ border-radius: 8px;
326
+ font-size: 13px;
327
+ font-weight: 600;
328
+ }
329
+
330
+ .kb-content-detail {
331
+ padding: 20px;
332
+ background: var(--bg-card);
333
+ border-radius: 12px;
334
+ border: 1px solid var(--border);
335
+ line-height: 1.8;
336
+ color: var(--text-primary);
337
+ }
338
+
339
+ .kb-content-detail h1,
340
+ .kb-content-detail h2,
341
+ .kb-content-detail h3 {
342
+ margin-top: 24px;
343
+ margin-bottom: 12px;
344
+ color: var(--text-primary);
345
+ }
346
+
347
+ .kb-content-detail p {
348
+ margin-bottom: 16px;
349
+ }
350
+
351
+ .kb-content-detail code {
352
+ background: var(--bg-dark);
353
+ padding: 2px 6px;
354
+ border-radius: 4px;
355
+ font-family: 'Courier New', monospace;
356
+ }
357
+
358
+ .kb-content-detail pre {
359
+ background: var(--bg-dark);
360
+ padding: 16px;
361
+ border-radius: 8px;
362
+ overflow-x: auto;
363
+ margin: 16px 0;
364
+ }
365
+ `;
366
+ document.head.appendChild(style);
367
+ }
368
+
369
+ } catch (error) {
370
+ alert('加载文档失败: ' + error.message);
371
+ }
372
+ }
373
+
374
+ // 添加样式
375
+ function addKnowledgeViewStyles() {
376
+ if (document.getElementById('knowledge-view-modern-styles')) return;
377
+
378
+ const style = document.createElement('style');
379
+ style.id = 'knowledge-view-modern-styles';
380
+ style.textContent = `
381
+ .knowledge-view-modern {
382
+ padding: 0;
383
+ animation: fadeInUp 0.5s ease;
384
+ }
385
+
386
+ .knowledge-header-modern {
387
+ display: flex;
388
+ justify-content: space-between;
389
+ align-items: center;
390
+ padding: 30px;
391
+ background: linear-gradient(135deg, var(--bg-card) 0%, rgba(99,102,241,0.05) 100%);
392
+ border-radius: 20px;
393
+ border: 1px solid var(--border);
394
+ margin-bottom: 30px;
395
+ }
396
+
397
+ .header-left {
398
+ display: flex;
399
+ align-items: center;
400
+ gap: 20px;
401
+ }
402
+
403
+ .header-icon {
404
+ width: 60px;
405
+ height: 60px;
406
+ background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
407
+ border-radius: 16px;
408
+ display: flex;
409
+ align-items: center;
410
+ justify-content: center;
411
+ font-size: 32px;
412
+ box-shadow: 0 4px 12px rgba(99,102,241,0.3);
413
+ }
414
+
415
+ .header-content {
416
+ flex: 1;
417
+ }
418
+
419
+ .header-title {
420
+ margin: 0 0 6px;
421
+ font-size: 28px;
422
+ font-weight: 800;
423
+ color: var(--text-primary);
424
+ }
425
+
426
+ .header-subtitle {
427
+ margin: 0;
428
+ font-size: 14px;
429
+ color: var(--text-secondary);
430
+ font-weight: 500;
431
+ }
432
+
433
+ .btn-create-knowledge-modern {
434
+ padding: 14px 28px;
435
+ background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
436
+ color: white;
437
+ border: none;
438
+ border-radius: 12px;
439
+ font-size: 15px;
440
+ font-weight: 600;
441
+ cursor: pointer;
442
+ transition: all 0.3s ease;
443
+ display: flex;
444
+ align-items: center;
445
+ gap: 8px;
446
+ box-shadow: 0 4px 12px rgba(99,102,241,0.3);
447
+ }
448
+
449
+ .btn-create-knowledge-modern:hover {
450
+ transform: translateY(-3px);
451
+ box-shadow: 0 8px 24px rgba(99,102,241,0.4);
452
+ }
453
+
454
+ .btn-icon {
455
+ font-size: 18px;
456
+ }
457
+
458
+ .knowledge-stats-modern {
459
+ display: grid;
460
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
461
+ gap: 20px;
462
+ margin-bottom: 30px;
463
+ padding: 0 30px;
464
+ }
465
+
466
+ .stat-card-kb {
467
+ background: var(--bg-card);
468
+ border: 2px solid var(--border);
469
+ border-radius: 16px;
470
+ padding: 20px;
471
+ display: flex;
472
+ align-items: center;
473
+ gap: 16px;
474
+ transition: all 0.3s ease;
475
+ }
476
+
477
+ .stat-card-kb:hover {
478
+ transform: translateY(-5px);
479
+ box-shadow: 0 8px 24px rgba(99,102,241,0.2);
480
+ border-color: var(--primary);
481
+ }
482
+
483
+ .stat-icon-kb {
484
+ width: 50px;
485
+ height: 50px;
486
+ border-radius: 12px;
487
+ display: flex;
488
+ align-items: center;
489
+ justify-content: center;
490
+ font-size: 24px;
491
+ flex-shrink: 0;
492
+ }
493
+
494
+ .stat-content-kb {
495
+ flex: 1;
496
+ }
497
+
498
+ .stat-label-kb {
499
+ font-size: 13px;
500
+ color: var(--text-secondary);
501
+ margin-bottom: 6px;
502
+ font-weight: 600;
503
+ text-transform: uppercase;
504
+ letter-spacing: 0.5px;
505
+ }
506
+
507
+ .stat-value-kb {
508
+ font-size: 28px;
509
+ font-weight: 800;
510
+ color: var(--text-primary);
511
+ }
512
+
513
+ .knowledge-list-modern {
514
+ display: grid;
515
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
516
+ gap: 24px;
517
+ padding: 0 30px 30px;
518
+ }
519
+
520
+ .knowledge-card-modern {
521
+ background: var(--bg-card);
522
+ border: 2px solid var(--border);
523
+ border-radius: 16px;
524
+ overflow: hidden;
525
+ transition: all 0.3s ease;
526
+ display: flex;
527
+ flex-direction: column;
528
+ }
529
+
530
+ .knowledge-card-modern:hover {
531
+ transform: translateY(-8px);
532
+ box-shadow: 0 12px 32px rgba(0,0,0,0.3);
533
+ border-color: var(--primary);
534
+ }
535
+
536
+ .kb-card-header-modern {
537
+ padding: 16px 20px;
538
+ background: var(--bg-dark);
539
+ display: flex;
540
+ justify-content: space-between;
541
+ align-items: center;
542
+ border-bottom: 1px solid var(--border);
543
+ }
544
+
545
+ .kb-status-badge-modern {
546
+ padding: 6px 12px;
547
+ border-radius: 12px;
548
+ font-size: 13px;
549
+ font-weight: 600;
550
+ display: flex;
551
+ align-items: center;
552
+ gap: 6px;
553
+ }
554
+
555
+ .kb-status-badge-modern.status-published {
556
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
557
+ color: white;
558
+ }
559
+
560
+ .kb-status-badge-modern.status-draft {
561
+ background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
562
+ color: white;
563
+ }
564
+
565
+ .kb-status-badge-modern.status-archived {
566
+ background: linear-gradient(135deg, #6b7280 0%, #4b5563 100%);
567
+ color: white;
568
+ }
569
+
570
+ .status-icon {
571
+ font-size: 14px;
572
+ }
573
+
574
+ .btn-menu-modern {
575
+ width: 32px;
576
+ height: 32px;
577
+ background: var(--bg-hover);
578
+ border: none;
579
+ border-radius: 8px;
580
+ color: var(--text-secondary);
581
+ font-size: 20px;
582
+ cursor: pointer;
583
+ transition: all 0.3s ease;
584
+ display: flex;
585
+ align-items: center;
586
+ justify-content: center;
587
+ }
588
+
589
+ .btn-menu-modern:hover {
590
+ background: var(--primary);
591
+ color: white;
592
+ }
593
+
594
+ .kb-card-body-modern {
595
+ padding: 20px;
596
+ flex: 1;
597
+ }
598
+
599
+ .kb-title-modern {
600
+ margin: 0 0 12px;
601
+ font-size: 18px;
602
+ font-weight: 700;
603
+ color: var(--text-primary);
604
+ line-height: 1.4;
605
+ display: -webkit-box;
606
+ -webkit-line-clamp: 2;
607
+ -webkit-box-orient: vertical;
608
+ overflow: hidden;
609
+ }
610
+
611
+ .kb-category-modern {
612
+ display: flex;
613
+ align-items: center;
614
+ gap: 8px;
615
+ margin-bottom: 12px;
616
+ padding: 8px 12px;
617
+ background: var(--bg-dark);
618
+ border-radius: 8px;
619
+ font-size: 14px;
620
+ color: var(--text-secondary);
621
+ width: fit-content;
622
+ }
623
+
624
+ .category-icon {
625
+ font-size: 16px;
626
+ }
627
+
628
+ .kb-tags-modern {
629
+ display: flex;
630
+ flex-wrap: wrap;
631
+ gap: 8px;
632
+ margin-top: 12px;
633
+ }
634
+
635
+ .kb-tag-modern {
636
+ padding: 4px 10px;
637
+ background: rgba(99,102,241,0.1);
638
+ color: var(--primary);
639
+ border-radius: 8px;
640
+ font-size: 12px;
641
+ font-weight: 600;
642
+ }
643
+
644
+ .kb-tag-more {
645
+ padding: 4px 10px;
646
+ background: var(--bg-hover);
647
+ color: var(--text-secondary);
648
+ border-radius: 8px;
649
+ font-size: 12px;
650
+ font-weight: 600;
651
+ }
652
+
653
+ .kb-card-footer-modern {
654
+ padding: 16px 20px;
655
+ background: var(--bg-dark);
656
+ border-top: 1px solid var(--border);
657
+ display: flex;
658
+ justify-content: space-between;
659
+ align-items: center;
660
+ }
661
+
662
+ .kb-meta-modern {
663
+ display: flex;
664
+ gap: 16px;
665
+ }
666
+
667
+ .meta-item-kb {
668
+ display: flex;
669
+ align-items: center;
670
+ gap: 6px;
671
+ font-size: 13px;
672
+ color: var(--text-secondary);
673
+ font-weight: 600;
674
+ }
675
+
676
+ .meta-icon {
677
+ font-size: 16px;
678
+ }
679
+
680
+ .kb-actions-modern {
681
+ display: flex;
682
+ gap: 8px;
683
+ }
684
+
685
+ .btn-kb-action {
686
+ width: 36px;
687
+ height: 36px;
688
+ border: none;
689
+ border-radius: 8px;
690
+ font-size: 16px;
691
+ cursor: pointer;
692
+ transition: all 0.3s ease;
693
+ display: flex;
694
+ align-items: center;
695
+ justify-content: center;
696
+ }
697
+
698
+ .btn-kb-action.view {
699
+ background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
700
+ color: white;
701
+ }
702
+
703
+ .btn-kb-action.edit {
704
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
705
+ color: white;
706
+ }
707
+
708
+ .btn-kb-action.delete {
709
+ background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
710
+ color: white;
711
+ }
712
+
713
+ .btn-kb-action:hover {
714
+ transform: scale(1.1);
715
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
716
+ }
717
+
718
+ .empty-state-modern,
719
+ .error-state-modern {
720
+ text-align: center;
721
+ padding: 80px 20px;
722
+ background: var(--bg-card);
723
+ border-radius: 20px;
724
+ border: 2px dashed var(--border);
725
+ margin: 0 30px;
726
+ }
727
+
728
+ .empty-icon,
729
+ .error-icon {
730
+ font-size: 64px;
731
+ margin-bottom: 20px;
732
+ }
733
+
734
+ .empty-state-modern h3,
735
+ .error-state-modern h3 {
736
+ margin: 0 0 12px;
737
+ font-size: 24px;
738
+ font-weight: 700;
739
+ color: var(--text-primary);
740
+ }
741
+
742
+ .empty-state-modern p,
743
+ .error-state-modern p {
744
+ margin: 0;
745
+ font-size: 15px;
746
+ color: var(--text-secondary);
747
+ }
748
+
749
+ .btn-retry-modern {
750
+ margin-top: 20px;
751
+ padding: 12px 24px;
752
+ background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
753
+ color: white;
754
+ border: none;
755
+ border-radius: 12px;
756
+ font-size: 15px;
757
+ font-weight: 600;
758
+ cursor: pointer;
759
+ transition: all 0.3s ease;
760
+ }
761
+
762
+ .btn-retry-modern:hover {
763
+ transform: translateY(-3px);
764
+ box-shadow: 0 8px 24px rgba(99,102,241,0.4);
765
+ }
766
+
767
+ @keyframes fadeInUp {
768
+ from {
769
+ opacity: 0;
770
+ transform: translateY(20px);
771
+ }
772
+ to {
773
+ opacity: 1;
774
+ transform: translateY(0);
775
+ }
776
+ }
777
+
778
+ @media (max-width: 768px) {
779
+ .knowledge-header-modern {
780
+ flex-direction: column;
781
+ gap: 20px;
782
+ align-items: flex-start;
783
+ }
784
+
785
+ .btn-create-knowledge-modern {
786
+ width: 100%;
787
+ justify-content: center;
788
+ }
789
+
790
+ .knowledge-stats-modern {
791
+ grid-template-columns: repeat(2, 1fr);
792
+ }
793
+
794
+ .knowledge-list-modern {
795
+ grid-template-columns: 1fr;
796
+ }
797
+ }
798
+ `;
799
+ document.head.appendChild(style);
800
+ }
801
+
802
+
803
+