collabdocchat 1.2.13 → 2.0.1

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.
Files changed (63) hide show
  1. package/README.md +219 -218
  2. package/index.html +2 -0
  3. package/install-and-start.bat +5 -0
  4. package/install-and-start.sh +5 -0
  5. package/package.json +9 -2
  6. package/scripts/generate-docs.js +448 -0
  7. package/scripts/pre-publish-check.js +213 -0
  8. package/scripts/start-app.js +15 -15
  9. package/server/index.js +38 -6
  10. package/server/middleware/cache.js +115 -0
  11. package/server/middleware/errorHandler.js +209 -0
  12. package/server/models/Document.js +66 -59
  13. package/server/models/File.js +49 -43
  14. package/server/models/Group.js +6 -0
  15. package/server/models/KnowledgeBase.js +254 -0
  16. package/server/models/Message.js +43 -0
  17. package/server/models/Task.js +87 -55
  18. package/server/models/User.js +67 -60
  19. package/server/models/Workflow.js +249 -0
  20. package/server/routes/ai.js +327 -0
  21. package/server/routes/audit.js +245 -210
  22. package/server/routes/backup.js +108 -0
  23. package/server/routes/chunked-upload.js +343 -0
  24. package/server/routes/export.js +440 -0
  25. package/server/routes/files.js +294 -218
  26. package/server/routes/groups.js +182 -0
  27. package/server/routes/knowledge.js +509 -0
  28. package/server/routes/tasks.js +257 -110
  29. package/server/routes/workflows.js +380 -0
  30. package/server/utils/backup.js +439 -0
  31. package/server/utils/cache.js +223 -0
  32. package/server/utils/workflow-engine.js +479 -0
  33. package/server/websocket/enhanced.js +509 -0
  34. package/server/websocket/index.js +233 -1
  35. package/src/components/knowledge-modal.js +485 -0
  36. package/src/components/optimized-poll-detail.js +724 -0
  37. package/src/main.js +5 -0
  38. package/src/pages/admin-dashboard.js +2248 -44
  39. package/src/pages/optimized-backup-view.js +616 -0
  40. package/src/pages/optimized-knowledge-view.js +803 -0
  41. package/src/pages/optimized-task-detail.js +843 -0
  42. package/src/pages/optimized-workflow-view.js +806 -0
  43. package/src/pages/simplified-workflows.js +651 -0
  44. package/src/pages/user-dashboard.js +677 -58
  45. package/src/services/api.js +64 -0
  46. package/src/services/auth.js +1 -1
  47. package/src/services/websocket.js +124 -16
  48. package/src/styles/collaboration-modern.js +708 -0
  49. package/src/styles/enhancements.css +392 -0
  50. package/src/styles/main.css +620 -1420
  51. package/src/styles/responsive.css +1000 -0
  52. package/src/styles/sidebar-fix.css +60 -0
  53. package/src/utils/ai-assistant.js +1398 -0
  54. package/src/utils/chat-enhancements.js +509 -0
  55. package/src/utils/collaboration-enhancer.js +1151 -0
  56. package/src/utils/feature-integrator.js +1724 -0
  57. package/src/utils/onboarding-guide.js +734 -0
  58. package/src/utils/performance.js +394 -0
  59. package/src/utils/permission-manager.js +890 -0
  60. package/src/utils/responsive-handler.js +491 -0
  61. package/src/utils/theme-manager.js +811 -0
  62. package/src/utils/ui-enhancements-loader.js +329 -0
  63. package/USAGE.md +0 -298
@@ -0,0 +1,811 @@
1
+ /**
2
+ * 主题管理系统
3
+ * 支持多种主题切换和自定义主题
4
+ */
5
+
6
+ export class ThemeManager {
7
+ constructor() {
8
+ this.currentTheme = this.loadTheme();
9
+ this.themes = {
10
+ default: {
11
+ name: '默认深色',
12
+ colors: {
13
+ primary: '#6366f1',
14
+ primaryDark: '#4f46e5',
15
+ secondary: '#8b5cf6',
16
+ success: '#10b981',
17
+ danger: '#ef4444',
18
+ warning: '#f59e0b',
19
+ bgDark: '#0f172a',
20
+ bgCard: '#1e293b',
21
+ bgHover: '#334155',
22
+ textPrimary: '#f1f5f9',
23
+ textSecondary: '#94a3b8',
24
+ border: '#334155'
25
+ }
26
+ },
27
+ light: {
28
+ name: '明亮模式',
29
+ colors: {
30
+ primary: '#6366f1',
31
+ primaryDark: '#4f46e5',
32
+ secondary: '#8b5cf6',
33
+ success: '#10b981',
34
+ danger: '#ef4444',
35
+ warning: '#f59e0b',
36
+ bgDark: '#ffffff',
37
+ bgCard: '#f8fafc',
38
+ bgHover: '#f1f5f9',
39
+ textPrimary: '#0f172a',
40
+ textSecondary: '#64748b',
41
+ border: '#e2e8f0'
42
+ }
43
+ },
44
+ ocean: {
45
+ name: '海洋蓝',
46
+ colors: {
47
+ primary: '#0ea5e9',
48
+ primaryDark: '#0284c7',
49
+ secondary: '#06b6d4',
50
+ success: '#10b981',
51
+ danger: '#ef4444',
52
+ warning: '#f59e0b',
53
+ bgDark: '#0c4a6e',
54
+ bgCard: '#075985',
55
+ bgHover: '#0369a1',
56
+ textPrimary: '#f0f9ff',
57
+ textSecondary: '#bae6fd',
58
+ border: '#0369a1'
59
+ }
60
+ },
61
+ forest: {
62
+ name: '森林绿',
63
+ colors: {
64
+ primary: '#22c55e',
65
+ primaryDark: '#16a34a',
66
+ secondary: '#84cc16',
67
+ success: '#10b981',
68
+ danger: '#ef4444',
69
+ warning: '#f59e0b',
70
+ bgDark: '#14532d',
71
+ bgCard: '#166534',
72
+ bgHover: '#15803d',
73
+ textPrimary: '#f0fdf4',
74
+ textSecondary: '#bbf7d0',
75
+ border: '#15803d'
76
+ }
77
+ },
78
+ sunset: {
79
+ name: '日落橙',
80
+ colors: {
81
+ primary: '#f97316',
82
+ primaryDark: '#ea580c',
83
+ secondary: '#fb923c',
84
+ success: '#10b981',
85
+ danger: '#ef4444',
86
+ warning: '#f59e0b',
87
+ bgDark: '#7c2d12',
88
+ bgCard: '#9a3412',
89
+ bgHover: '#c2410c',
90
+ textPrimary: '#fff7ed',
91
+ textSecondary: '#fed7aa',
92
+ border: '#c2410c'
93
+ }
94
+ },
95
+ purple: {
96
+ name: '紫罗兰',
97
+ colors: {
98
+ primary: '#a855f7',
99
+ primaryDark: '#9333ea',
100
+ secondary: '#c084fc',
101
+ success: '#10b981',
102
+ danger: '#ef4444',
103
+ warning: '#f59e0b',
104
+ bgDark: '#581c87',
105
+ bgCard: '#6b21a8',
106
+ bgHover: '#7e22ce',
107
+ textPrimary: '#faf5ff',
108
+ textSecondary: '#e9d5ff',
109
+ border: '#7e22ce'
110
+ }
111
+ },
112
+ midnight: {
113
+ name: '午夜黑',
114
+ colors: {
115
+ primary: '#3b82f6',
116
+ primaryDark: '#2563eb',
117
+ secondary: '#60a5fa',
118
+ success: '#10b981',
119
+ danger: '#ef4444',
120
+ warning: '#f59e0b',
121
+ bgDark: '#000000',
122
+ bgCard: '#1a1a1a',
123
+ bgHover: '#2d2d2d',
124
+ textPrimary: '#ffffff',
125
+ textSecondary: '#a3a3a3',
126
+ border: '#404040'
127
+ }
128
+ },
129
+ rose: {
130
+ name: '玫瑰粉',
131
+ colors: {
132
+ primary: '#f43f5e',
133
+ primaryDark: '#e11d48',
134
+ secondary: '#fb7185',
135
+ success: '#10b981',
136
+ danger: '#ef4444',
137
+ warning: '#f59e0b',
138
+ bgDark: '#881337',
139
+ bgCard: '#9f1239',
140
+ bgHover: '#be123c',
141
+ textPrimary: '#fff1f2',
142
+ textSecondary: '#fecdd3',
143
+ border: '#be123c'
144
+ }
145
+ }
146
+ };
147
+ }
148
+
149
+ /**
150
+ * 加载保存的主题
151
+ */
152
+ loadTheme() {
153
+ return localStorage.getItem('theme') || 'default';
154
+ }
155
+
156
+ /**
157
+ * 保存主题设置
158
+ */
159
+ saveTheme(themeName) {
160
+ localStorage.setItem('theme', themeName);
161
+ }
162
+
163
+ /**
164
+ * 应用主题
165
+ */
166
+ applyTheme(themeName) {
167
+ if (!this.themes[themeName]) {
168
+ console.error('主题不存在:', themeName);
169
+ return;
170
+ }
171
+
172
+ const theme = this.themes[themeName];
173
+ const root = document.documentElement;
174
+
175
+ // 应用CSS变量
176
+ Object.entries(theme.colors).forEach(([key, value]) => {
177
+ const cssVar = key.replace(/([A-Z])/g, '-$1').toLowerCase();
178
+ root.style.setProperty(`--${cssVar}`, value);
179
+ });
180
+
181
+ // 保存当前主题
182
+ this.currentTheme = themeName;
183
+ this.saveTheme(themeName);
184
+
185
+ // 触发主题变更事件
186
+ window.dispatchEvent(new CustomEvent('themeChanged', {
187
+ detail: { theme: themeName }
188
+ }));
189
+ }
190
+
191
+ /**
192
+ * 获取当前主题
193
+ */
194
+ getCurrentTheme() {
195
+ return this.currentTheme;
196
+ }
197
+
198
+ /**
199
+ * 获取所有主题
200
+ */
201
+ getAllThemes() {
202
+ return Object.entries(this.themes).map(([key, theme]) => ({
203
+ id: key,
204
+ name: theme.name,
205
+ colors: theme.colors
206
+ }));
207
+ }
208
+
209
+ /**
210
+ * 创建自定义主题
211
+ */
212
+ createCustomTheme(name, colors) {
213
+ const themeId = `custom_${Date.now()}`;
214
+ this.themes[themeId] = {
215
+ name: name,
216
+ colors: colors
217
+ };
218
+
219
+ // 保存到localStorage
220
+ this.saveCustomThemes();
221
+
222
+ return themeId;
223
+ }
224
+
225
+ /**
226
+ * 保存自定义主题
227
+ */
228
+ saveCustomThemes() {
229
+ const customThemes = {};
230
+ Object.entries(this.themes).forEach(([key, theme]) => {
231
+ if (key.startsWith('custom_')) {
232
+ customThemes[key] = theme;
233
+ }
234
+ });
235
+ localStorage.setItem('customThemes', JSON.stringify(customThemes));
236
+ }
237
+
238
+ /**
239
+ * 加载自定义主题
240
+ */
241
+ loadCustomThemes() {
242
+ const saved = localStorage.getItem('customThemes');
243
+ if (saved) {
244
+ try {
245
+ const customThemes = JSON.parse(saved);
246
+ Object.assign(this.themes, customThemes);
247
+ } catch (error) {
248
+ console.error('加载自定义主题失败:', error);
249
+ }
250
+ }
251
+ }
252
+
253
+ /**
254
+ * 删除自定义主题
255
+ */
256
+ deleteCustomTheme(themeId) {
257
+ if (!themeId.startsWith('custom_')) {
258
+ console.error('只能删除自定义主题');
259
+ return false;
260
+ }
261
+
262
+ delete this.themes[themeId];
263
+ this.saveCustomThemes();
264
+
265
+ // 如果删除的是当前主题,切换到默认主题
266
+ if (this.currentTheme === themeId) {
267
+ this.applyTheme('default');
268
+ }
269
+
270
+ return true;
271
+ }
272
+
273
+ /**
274
+ * 渲染主题选择器(美化版)
275
+ */
276
+ renderThemeSelector(container) {
277
+ const themes = this.getAllThemes();
278
+
279
+ container.innerHTML = `
280
+ <div class="theme-selector-modern">
281
+ <div class="theme-header-modern">
282
+ <div class="theme-header-content">
283
+ <h2 class="theme-title-modern">🎨 主题设置</h2>
284
+ <p class="theme-subtitle-modern">选择您喜欢的主题风格,让界面更加个性化</p>
285
+ </div>
286
+ <button class="btn-create-theme-modern" id="createCustomThemeBtn">
287
+ <span class="btn-icon">✨</span>
288
+ <span>创建自定义主题</span>
289
+ </button>
290
+ </div>
291
+
292
+ <div class="theme-grid-modern">
293
+ ${themes.map(theme => `
294
+ <div class="theme-card-modern ${this.currentTheme === theme.id ? 'active' : ''}"
295
+ data-theme="${theme.id}">
296
+ <div class="theme-preview-modern" style="background: linear-gradient(135deg, ${theme.colors.bgDark} 0%, ${theme.colors.bgCard} 100%);">
297
+ <div class="theme-preview-header" style="background: ${theme.colors.bgCard};">
298
+ <div class="preview-dot" style="background: ${theme.colors.danger};"></div>
299
+ <div class="preview-dot" style="background: ${theme.colors.warning};"></div>
300
+ <div class="preview-dot" style="background: ${theme.colors.success};"></div>
301
+ </div>
302
+ <div class="theme-preview-content">
303
+ <div class="preview-sidebar" style="background: ${theme.colors.bgCard};"></div>
304
+ <div class="preview-main">
305
+ <div class="preview-card" style="background: ${theme.colors.bgCard};">
306
+ <div class="preview-line" style="background: ${theme.colors.primary};"></div>
307
+ <div class="preview-line" style="background: ${theme.colors.textSecondary}; width: 80%;"></div>
308
+ <div class="preview-line" style="background: ${theme.colors.textSecondary}; width: 60%;"></div>
309
+ </div>
310
+ <div class="preview-button" style="background: linear-gradient(135deg, ${theme.colors.primary} 0%, ${theme.colors.secondary} 100%);"></div>
311
+ </div>
312
+ </div>
313
+ <div class="theme-colors-modern">
314
+ <span class="color-dot" style="background: ${theme.colors.primary};" title="主色调"></span>
315
+ <span class="color-dot" style="background: ${theme.colors.secondary};" title="次要色"></span>
316
+ <span class="color-dot" style="background: ${theme.colors.success};" title="成功色"></span>
317
+ <span class="color-dot" style="background: ${theme.colors.danger};" title="危险色"></span>
318
+ </div>
319
+ </div>
320
+ <div class="theme-info-modern">
321
+ <div class="theme-name-modern">${theme.name}</div>
322
+ ${this.currentTheme === theme.id ?
323
+ '<div class="theme-badge-modern">✓ 当前使用</div>' :
324
+ '<div class="theme-action-modern">点击应用</div>'}
325
+ </div>
326
+ ${theme.id.startsWith('custom_') ?
327
+ `<button class="btn-delete-theme-modern" data-theme="${theme.id}" title="删除主题">
328
+ <span>🗑️</span>
329
+ </button>` :
330
+ ''}
331
+ </div>
332
+ `).join('')}
333
+ </div>
334
+ </div>
335
+ `;
336
+
337
+ // 添加样式
338
+ this.addThemeSelectorStyles();
339
+
340
+ // 添加事件监听
341
+ container.querySelectorAll('.theme-card-modern').forEach(card => {
342
+ card.addEventListener('click', (e) => {
343
+ if (!e.target.closest('.btn-delete-theme-modern')) {
344
+ const themeId = card.dataset.theme;
345
+ this.applyTheme(themeId);
346
+
347
+ // 更新选中状态
348
+ container.querySelectorAll('.theme-card-modern').forEach(c => c.classList.remove('active'));
349
+ card.classList.add('active');
350
+
351
+ // 更新徽章文字
352
+ container.querySelectorAll('.theme-badge-modern, .theme-action-modern').forEach(badge => {
353
+ badge.className = 'theme-action-modern';
354
+ badge.textContent = '点击应用';
355
+ });
356
+
357
+ const badge = card.querySelector('.theme-badge-modern, .theme-action-modern');
358
+ if (badge) {
359
+ badge.className = 'theme-badge-modern';
360
+ badge.textContent = '✓ 当前使用';
361
+ }
362
+ }
363
+ });
364
+ });
365
+
366
+ // 删除自定义主题
367
+ container.querySelectorAll('.btn-delete-theme-modern').forEach(btn => {
368
+ btn.addEventListener('click', (e) => {
369
+ e.stopPropagation();
370
+ const themeId = btn.dataset.theme;
371
+ if (confirm('确定要删除这个主题吗?')) {
372
+ this.deleteCustomTheme(themeId);
373
+ this.renderThemeSelector(container);
374
+ }
375
+ });
376
+ });
377
+
378
+ // 创建自定义主题
379
+ const createBtn = container.querySelector('#createCustomThemeBtn');
380
+ if (createBtn) {
381
+ createBtn.addEventListener('click', () => {
382
+ this.showCustomThemeDialog(container);
383
+ });
384
+ }
385
+ }
386
+
387
+ /**
388
+ * 显示自定义主题对话框
389
+ */
390
+ showCustomThemeDialog(container) {
391
+ const dialog = document.createElement('div');
392
+ dialog.className = 'modal';
393
+ dialog.innerHTML = `
394
+ <div class="modal-content" style="max-width: 600px;">
395
+ <h3>创建自定义主题</h3>
396
+ <form id="customThemeForm">
397
+ <div class="form-group">
398
+ <label>主题名称</label>
399
+ <input type="text" name="name" placeholder="我的主题" required>
400
+ </div>
401
+ <div class="form-group">
402
+ <label>主色调</label>
403
+ <input type="color" name="primary" value="#6366f1">
404
+ </div>
405
+ <div class="form-group">
406
+ <label>次要色</label>
407
+ <input type="color" name="secondary" value="#8b5cf6">
408
+ </div>
409
+ <div class="form-group">
410
+ <label>成功色</label>
411
+ <input type="color" name="success" value="#10b981">
412
+ </div>
413
+ <div class="form-group">
414
+ <label>危险色</label>
415
+ <input type="color" name="danger" value="#ef4444">
416
+ </div>
417
+ <div class="form-group">
418
+ <label>背景色</label>
419
+ <input type="color" name="bgDark" value="#0f172a">
420
+ </div>
421
+ <div class="form-group">
422
+ <label>卡片背景</label>
423
+ <input type="color" name="bgCard" value="#1e293b">
424
+ </div>
425
+ <div class="form-group">
426
+ <label>文字颜色</label>
427
+ <input type="color" name="textPrimary" value="#f1f5f9">
428
+ </div>
429
+ <div style="display: flex; gap: 10px; margin-top: 20px;">
430
+ <button type="submit" class="btn-primary">创建</button>
431
+ <button type="button" class="btn-secondary" id="cancelCustomTheme">取消</button>
432
+ </div>
433
+ </form>
434
+ </div>
435
+ `;
436
+
437
+ document.body.appendChild(dialog);
438
+
439
+ // 取消按钮
440
+ dialog.querySelector('#cancelCustomTheme').addEventListener('click', () => {
441
+ dialog.remove();
442
+ });
443
+
444
+ // 提交表单
445
+ dialog.querySelector('#customThemeForm').addEventListener('submit', (e) => {
446
+ e.preventDefault();
447
+ const formData = new FormData(e.target);
448
+
449
+ const colors = {
450
+ primary: formData.get('primary'),
451
+ primaryDark: this.darkenColor(formData.get('primary'), 10),
452
+ secondary: formData.get('secondary'),
453
+ success: formData.get('success'),
454
+ danger: formData.get('danger'),
455
+ warning: '#f59e0b',
456
+ bgDark: formData.get('bgDark'),
457
+ bgCard: formData.get('bgCard'),
458
+ bgHover: this.lightenColor(formData.get('bgCard'), 10),
459
+ textPrimary: formData.get('textPrimary'),
460
+ textSecondary: this.adjustOpacity(formData.get('textPrimary'), 0.7),
461
+ border: this.lightenColor(formData.get('bgCard'), 20)
462
+ };
463
+
464
+ const themeId = this.createCustomTheme(formData.get('name'), colors);
465
+ this.applyTheme(themeId);
466
+
467
+ dialog.remove();
468
+ this.renderThemeSelector(container);
469
+ });
470
+ }
471
+
472
+ /**
473
+ * 加深颜色
474
+ */
475
+ darkenColor(color, percent) {
476
+ const num = parseInt(color.replace('#', ''), 16);
477
+ const amt = Math.round(2.55 * percent);
478
+ const R = (num >> 16) - amt;
479
+ const G = (num >> 8 & 0x00FF) - amt;
480
+ const B = (num & 0x0000FF) - amt;
481
+ return '#' + (0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 +
482
+ (G < 255 ? G < 1 ? 0 : G : 255) * 0x100 +
483
+ (B < 255 ? B < 1 ? 0 : B : 255))
484
+ .toString(16).slice(1);
485
+ }
486
+
487
+ /**
488
+ * 提亮颜色
489
+ */
490
+ lightenColor(color, percent) {
491
+ const num = parseInt(color.replace('#', ''), 16);
492
+ const amt = Math.round(2.55 * percent);
493
+ const R = (num >> 16) + amt;
494
+ const G = (num >> 8 & 0x00FF) + amt;
495
+ const B = (num & 0x0000FF) + amt;
496
+ return '#' + (0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 +
497
+ (G < 255 ? G < 1 ? 0 : G : 255) * 0x100 +
498
+ (B < 255 ? B < 1 ? 0 : B : 255))
499
+ .toString(16).slice(1);
500
+ }
501
+
502
+ /**
503
+ * 调整透明度
504
+ */
505
+ adjustOpacity(color, opacity) {
506
+ const num = parseInt(color.replace('#', ''), 16);
507
+ const R = num >> 16;
508
+ const G = num >> 8 & 0x00FF;
509
+ const B = num & 0x0000FF;
510
+ return `rgba(${R}, ${G}, ${B}, ${opacity})`;
511
+ }
512
+
513
+ /**
514
+ * 添加主题选择器样式
515
+ */
516
+ addThemeSelectorStyles() {
517
+ if (document.getElementById('theme-selector-modern-styles')) return;
518
+
519
+ const style = document.createElement('style');
520
+ style.id = 'theme-selector-modern-styles';
521
+ style.textContent = `
522
+ .theme-selector-modern {
523
+ padding: 0;
524
+ animation: fadeInUp 0.5s ease;
525
+ }
526
+
527
+ .theme-header-modern {
528
+ display: flex;
529
+ justify-content: space-between;
530
+ align-items: center;
531
+ margin-bottom: 40px;
532
+ padding: 30px;
533
+ background: linear-gradient(135deg, var(--bg-card) 0%, rgba(99,102,241,0.05) 100%);
534
+ border-radius: 20px;
535
+ border: 1px solid var(--border);
536
+ }
537
+
538
+ .theme-header-content {
539
+ flex: 1;
540
+ }
541
+
542
+ .theme-title-modern {
543
+ margin: 0 0 8px;
544
+ font-size: 32px;
545
+ font-weight: 800;
546
+ color: var(--text-primary);
547
+ display: flex;
548
+ align-items: center;
549
+ gap: 12px;
550
+ }
551
+
552
+ .theme-subtitle-modern {
553
+ margin: 0;
554
+ font-size: 15px;
555
+ color: var(--text-secondary);
556
+ font-weight: 500;
557
+ }
558
+
559
+ .btn-create-theme-modern {
560
+ padding: 14px 28px;
561
+ background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
562
+ color: white;
563
+ border: none;
564
+ border-radius: 12px;
565
+ font-size: 15px;
566
+ font-weight: 600;
567
+ cursor: pointer;
568
+ transition: all 0.3s ease;
569
+ display: flex;
570
+ align-items: center;
571
+ gap: 8px;
572
+ box-shadow: 0 4px 12px rgba(99,102,241,0.3);
573
+ }
574
+
575
+ .btn-create-theme-modern:hover {
576
+ transform: translateY(-3px);
577
+ box-shadow: 0 8px 24px rgba(99,102,241,0.4);
578
+ }
579
+
580
+ .btn-icon {
581
+ font-size: 18px;
582
+ }
583
+
584
+ .theme-grid-modern {
585
+ display: grid;
586
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
587
+ gap: 24px;
588
+ padding: 0 30px 30px;
589
+ }
590
+
591
+ .theme-card-modern {
592
+ background: var(--bg-card);
593
+ border: 2px solid var(--border);
594
+ border-radius: 16px;
595
+ overflow: hidden;
596
+ cursor: pointer;
597
+ transition: all 0.3s ease;
598
+ position: relative;
599
+ }
600
+
601
+ .theme-card-modern:hover {
602
+ transform: translateY(-8px);
603
+ box-shadow: 0 12px 32px rgba(0,0,0,0.3);
604
+ border-color: var(--primary);
605
+ }
606
+
607
+ .theme-card-modern.active {
608
+ border-color: var(--primary);
609
+ box-shadow: 0 0 0 3px rgba(99,102,241,0.2);
610
+ }
611
+
612
+ .theme-card-modern.active::before {
613
+ content: '';
614
+ position: absolute;
615
+ top: 0;
616
+ left: 0;
617
+ right: 0;
618
+ height: 4px;
619
+ background: linear-gradient(90deg, var(--primary) 0%, var(--secondary) 100%);
620
+ z-index: 1;
621
+ }
622
+
623
+ .theme-preview-modern {
624
+ height: 200px;
625
+ padding: 12px;
626
+ display: flex;
627
+ flex-direction: column;
628
+ gap: 8px;
629
+ position: relative;
630
+ }
631
+
632
+ .theme-preview-header {
633
+ height: 24px;
634
+ border-radius: 8px 8px 0 0;
635
+ display: flex;
636
+ align-items: center;
637
+ gap: 6px;
638
+ padding: 0 10px;
639
+ }
640
+
641
+ .preview-dot {
642
+ width: 10px;
643
+ height: 10px;
644
+ border-radius: 50%;
645
+ }
646
+
647
+ .theme-preview-content {
648
+ flex: 1;
649
+ display: flex;
650
+ gap: 8px;
651
+ }
652
+
653
+ .preview-sidebar {
654
+ width: 50px;
655
+ border-radius: 8px;
656
+ }
657
+
658
+ .preview-main {
659
+ flex: 1;
660
+ display: flex;
661
+ flex-direction: column;
662
+ gap: 8px;
663
+ }
664
+
665
+ .preview-card {
666
+ flex: 1;
667
+ border-radius: 8px;
668
+ padding: 12px;
669
+ display: flex;
670
+ flex-direction: column;
671
+ gap: 6px;
672
+ }
673
+
674
+ .preview-line {
675
+ height: 6px;
676
+ border-radius: 3px;
677
+ width: 100%;
678
+ }
679
+
680
+ .preview-button {
681
+ height: 24px;
682
+ border-radius: 6px;
683
+ }
684
+
685
+ .theme-colors-modern {
686
+ display: flex;
687
+ gap: 8px;
688
+ justify-content: center;
689
+ padding: 8px 0;
690
+ }
691
+
692
+ .color-dot {
693
+ width: 24px;
694
+ height: 24px;
695
+ border-radius: 50%;
696
+ border: 2px solid rgba(255,255,255,0.2);
697
+ transition: all 0.3s ease;
698
+ cursor: pointer;
699
+ }
700
+
701
+ .color-dot:hover {
702
+ transform: scale(1.2);
703
+ border-color: rgba(255,255,255,0.5);
704
+ }
705
+
706
+ .theme-info-modern {
707
+ padding: 16px 20px;
708
+ background: var(--bg-dark);
709
+ display: flex;
710
+ justify-content: space-between;
711
+ align-items: center;
712
+ }
713
+
714
+ .theme-name-modern {
715
+ font-size: 16px;
716
+ font-weight: 700;
717
+ color: var(--text-primary);
718
+ }
719
+
720
+ .theme-badge-modern {
721
+ padding: 4px 12px;
722
+ background: linear-gradient(135deg, var(--success) 0%, #059669 100%);
723
+ color: white;
724
+ border-radius: 12px;
725
+ font-size: 12px;
726
+ font-weight: 600;
727
+ }
728
+
729
+ .theme-action-modern {
730
+ padding: 4px 12px;
731
+ background: var(--bg-hover);
732
+ color: var(--text-secondary);
733
+ border-radius: 12px;
734
+ font-size: 12px;
735
+ font-weight: 600;
736
+ }
737
+
738
+ .btn-delete-theme-modern {
739
+ position: absolute;
740
+ top: 16px;
741
+ right: 16px;
742
+ width: 36px;
743
+ height: 36px;
744
+ background: rgba(239,68,68,0.9);
745
+ border: none;
746
+ border-radius: 50%;
747
+ color: white;
748
+ font-size: 16px;
749
+ cursor: pointer;
750
+ transition: all 0.3s ease;
751
+ display: flex;
752
+ align-items: center;
753
+ justify-content: center;
754
+ z-index: 2;
755
+ opacity: 0;
756
+ }
757
+
758
+ .theme-card-modern:hover .btn-delete-theme-modern {
759
+ opacity: 1;
760
+ }
761
+
762
+ .btn-delete-theme-modern:hover {
763
+ background: var(--danger);
764
+ transform: scale(1.1) rotate(10deg);
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
+ .theme-header-modern {
780
+ flex-direction: column;
781
+ gap: 20px;
782
+ align-items: flex-start;
783
+ }
784
+
785
+ .btn-create-theme-modern {
786
+ width: 100%;
787
+ justify-content: center;
788
+ }
789
+
790
+ .theme-grid-modern {
791
+ grid-template-columns: 1fr;
792
+ padding: 0 20px 20px;
793
+ }
794
+ }
795
+ `;
796
+ document.head.appendChild(style);
797
+ }
798
+
799
+ /**
800
+ * 初始化主题
801
+ */
802
+ init() {
803
+ this.loadCustomThemes();
804
+ this.applyTheme(this.currentTheme);
805
+ }
806
+ }
807
+
808
+ // 导出单例
809
+ export const themeManager = new ThemeManager();
810
+
811
+