collabdocchat 1.2.13 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) 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/pre-publish-check.js +213 -0
  7. package/scripts/start-app.js +15 -15
  8. package/server/index.js +38 -6
  9. package/server/middleware/cache.js +115 -0
  10. package/server/middleware/errorHandler.js +209 -0
  11. package/server/models/Document.js +66 -59
  12. package/server/models/File.js +49 -43
  13. package/server/models/Group.js +6 -0
  14. package/server/models/KnowledgeBase.js +254 -0
  15. package/server/models/Message.js +43 -0
  16. package/server/models/Task.js +87 -55
  17. package/server/models/User.js +67 -60
  18. package/server/models/Workflow.js +249 -0
  19. package/server/routes/ai.js +327 -0
  20. package/server/routes/audit.js +245 -210
  21. package/server/routes/backup.js +108 -0
  22. package/server/routes/chunked-upload.js +343 -0
  23. package/server/routes/export.js +440 -0
  24. package/server/routes/files.js +294 -218
  25. package/server/routes/groups.js +182 -0
  26. package/server/routes/knowledge.js +509 -0
  27. package/server/routes/tasks.js +257 -110
  28. package/server/routes/workflows.js +380 -0
  29. package/server/utils/backup.js +439 -0
  30. package/server/utils/cache.js +223 -0
  31. package/server/utils/workflow-engine.js +479 -0
  32. package/server/websocket/enhanced.js +509 -0
  33. package/server/websocket/index.js +233 -1
  34. package/src/components/knowledge-modal.js +485 -0
  35. package/src/components/optimized-poll-detail.js +724 -0
  36. package/src/main.js +5 -0
  37. package/src/pages/admin-dashboard.js +2248 -44
  38. package/src/pages/optimized-backup-view.js +616 -0
  39. package/src/pages/optimized-knowledge-view.js +803 -0
  40. package/src/pages/optimized-task-detail.js +843 -0
  41. package/src/pages/optimized-workflow-view.js +806 -0
  42. package/src/pages/simplified-workflows.js +651 -0
  43. package/src/pages/user-dashboard.js +677 -58
  44. package/src/services/api.js +65 -1
  45. package/src/services/auth.js +1 -1
  46. package/src/services/websocket.js +124 -16
  47. package/src/styles/collaboration-modern.js +708 -0
  48. package/src/styles/enhancements.css +392 -0
  49. package/src/styles/main.css +620 -1420
  50. package/src/styles/responsive.css +1000 -0
  51. package/src/styles/sidebar-fix.css +60 -0
  52. package/src/utils/ai-assistant.js +1398 -0
  53. package/src/utils/chat-enhancements.js +509 -0
  54. package/src/utils/collaboration-enhancer.js +1151 -0
  55. package/src/utils/feature-integrator.js +1724 -0
  56. package/src/utils/onboarding-guide.js +734 -0
  57. package/src/utils/performance.js +394 -0
  58. package/src/utils/permission-manager.js +890 -0
  59. package/src/utils/responsive-handler.js +491 -0
  60. package/src/utils/theme-manager.js +811 -0
  61. package/src/utils/ui-enhancements-loader.js +329 -0
  62. package/USAGE.md +0 -298
@@ -0,0 +1,394 @@
1
+ /**
2
+ * 虚拟滚动实现
3
+ * 用于优化长列表渲染性能
4
+ */
5
+ export class VirtualScroller {
6
+ constructor(container, options = {}) {
7
+ this.container = container;
8
+ this.itemHeight = options.itemHeight || 60;
9
+ this.buffer = options.buffer || 5;
10
+ this.items = [];
11
+ this.renderItem = options.renderItem;
12
+ this.visibleStart = 0;
13
+ this.visibleEnd = 0;
14
+
15
+ this.init();
16
+ }
17
+
18
+ init() {
19
+ this.container.style.position = 'relative';
20
+ this.container.style.overflow = 'auto';
21
+
22
+ // 创建内容容器
23
+ this.content = document.createElement('div');
24
+ this.content.style.position = 'relative';
25
+ this.container.appendChild(this.content);
26
+
27
+ // 监听滚动事件
28
+ this.container.addEventListener('scroll', this.handleScroll.bind(this));
29
+
30
+ // 监听窗口大小变化
31
+ window.addEventListener('resize', this.handleResize.bind(this));
32
+ }
33
+
34
+ setItems(items) {
35
+ this.items = items;
36
+ this.content.style.height = `${items.length * this.itemHeight}px`;
37
+ this.render();
38
+ }
39
+
40
+ handleScroll() {
41
+ requestAnimationFrame(() => this.render());
42
+ }
43
+
44
+ handleResize() {
45
+ requestAnimationFrame(() => this.render());
46
+ }
47
+
48
+ render() {
49
+ const scrollTop = this.container.scrollTop;
50
+ const containerHeight = this.container.clientHeight;
51
+
52
+ // 计算可见范围
53
+ this.visibleStart = Math.max(0, Math.floor(scrollTop / this.itemHeight) - this.buffer);
54
+ this.visibleEnd = Math.min(
55
+ this.items.length,
56
+ Math.ceil((scrollTop + containerHeight) / this.itemHeight) + this.buffer
57
+ );
58
+
59
+ // 清空内容
60
+ this.content.innerHTML = '';
61
+
62
+ // 渲染可见项
63
+ for (let i = this.visibleStart; i < this.visibleEnd; i++) {
64
+ const item = this.items[i];
65
+ const element = this.renderItem(item, i);
66
+ element.style.position = 'absolute';
67
+ element.style.top = `${i * this.itemHeight}px`;
68
+ element.style.width = '100%';
69
+ element.style.height = `${this.itemHeight}px`;
70
+ this.content.appendChild(element);
71
+ }
72
+ }
73
+
74
+ scrollToIndex(index) {
75
+ this.container.scrollTop = index * this.itemHeight;
76
+ }
77
+
78
+ destroy() {
79
+ this.container.removeEventListener('scroll', this.handleScroll);
80
+ window.removeEventListener('resize', this.handleResize);
81
+ this.content.remove();
82
+ }
83
+ }
84
+
85
+ /**
86
+ * 图片懒加载
87
+ */
88
+ export class LazyLoader {
89
+ constructor(options = {}) {
90
+ this.rootMargin = options.rootMargin || '50px';
91
+ this.threshold = options.threshold || 0.01;
92
+ this.loadingClass = options.loadingClass || 'lazy-loading';
93
+ this.loadedClass = options.loadedClass || 'lazy-loaded';
94
+ this.errorClass = options.errorClass || 'lazy-error';
95
+
96
+ this.observer = null;
97
+ this.init();
98
+ }
99
+
100
+ init() {
101
+ if ('IntersectionObserver' in window) {
102
+ this.observer = new IntersectionObserver(
103
+ this.handleIntersection.bind(this),
104
+ {
105
+ rootMargin: this.rootMargin,
106
+ threshold: this.threshold
107
+ }
108
+ );
109
+ }
110
+ }
111
+
112
+ observe(element) {
113
+ if (!element || !this.observer) return;
114
+
115
+ // 添加加载中类
116
+ element.classList.add(this.loadingClass);
117
+
118
+ // 开始观察
119
+ this.observer.observe(element);
120
+ }
121
+
122
+ observeAll(selector = '[data-lazy-src]') {
123
+ const elements = document.querySelectorAll(selector);
124
+ elements.forEach(el => this.observe(el));
125
+ }
126
+
127
+ handleIntersection(entries) {
128
+ entries.forEach(entry => {
129
+ if (entry.isIntersecting) {
130
+ this.loadImage(entry.target);
131
+ this.observer.unobserve(entry.target);
132
+ }
133
+ });
134
+ }
135
+
136
+ loadImage(element) {
137
+ const src = element.dataset.lazySrc;
138
+ const srcset = element.dataset.lazySrcset;
139
+
140
+ if (!src) return;
141
+
142
+ const img = new Image();
143
+
144
+ img.onload = () => {
145
+ if (element.tagName === 'IMG') {
146
+ element.src = src;
147
+ if (srcset) element.srcset = srcset;
148
+ } else {
149
+ element.style.backgroundImage = `url(${src})`;
150
+ }
151
+
152
+ element.classList.remove(this.loadingClass);
153
+ element.classList.add(this.loadedClass);
154
+ };
155
+
156
+ img.onerror = () => {
157
+ element.classList.remove(this.loadingClass);
158
+ element.classList.add(this.errorClass);
159
+ console.error('图片加载失败:', src);
160
+ };
161
+
162
+ img.src = src;
163
+ }
164
+
165
+ destroy() {
166
+ if (this.observer) {
167
+ this.observer.disconnect();
168
+ }
169
+ }
170
+ }
171
+
172
+ /**
173
+ * 防抖函数
174
+ */
175
+ export function debounce(func, wait = 300) {
176
+ let timeout;
177
+ return function executedFunction(...args) {
178
+ const later = () => {
179
+ clearTimeout(timeout);
180
+ func(...args);
181
+ };
182
+ clearTimeout(timeout);
183
+ timeout = setTimeout(later, wait);
184
+ };
185
+ }
186
+
187
+ /**
188
+ * 节流函数
189
+ */
190
+ export function throttle(func, limit = 300) {
191
+ let inThrottle;
192
+ return function executedFunction(...args) {
193
+ if (!inThrottle) {
194
+ func(...args);
195
+ inThrottle = true;
196
+ setTimeout(() => inThrottle = false, limit);
197
+ }
198
+ };
199
+ }
200
+
201
+ /**
202
+ * 请求动画帧节流
203
+ */
204
+ export function rafThrottle(func) {
205
+ let rafId = null;
206
+ return function executedFunction(...args) {
207
+ if (rafId === null) {
208
+ rafId = requestAnimationFrame(() => {
209
+ func(...args);
210
+ rafId = null;
211
+ });
212
+ }
213
+ };
214
+ }
215
+
216
+ /**
217
+ * 代码分割 - 动态导入
218
+ */
219
+ export async function loadModule(modulePath) {
220
+ try {
221
+ const module = await import(modulePath);
222
+ return module;
223
+ } catch (error) {
224
+ console.error('模块加载失败:', modulePath, error);
225
+ throw error;
226
+ }
227
+ }
228
+
229
+ /**
230
+ * 资源预加载
231
+ */
232
+ export function preloadResource(url, type = 'fetch') {
233
+ const link = document.createElement('link');
234
+ link.rel = 'preload';
235
+ link.href = url;
236
+
237
+ switch (type) {
238
+ case 'script':
239
+ link.as = 'script';
240
+ break;
241
+ case 'style':
242
+ link.as = 'style';
243
+ break;
244
+ case 'image':
245
+ link.as = 'image';
246
+ break;
247
+ case 'font':
248
+ link.as = 'font';
249
+ link.crossOrigin = 'anonymous';
250
+ break;
251
+ default:
252
+ link.as = 'fetch';
253
+ link.crossOrigin = 'anonymous';
254
+ }
255
+
256
+ document.head.appendChild(link);
257
+ }
258
+
259
+ /**
260
+ * 性能监控
261
+ */
262
+ export class PerformanceMonitor {
263
+ constructor() {
264
+ this.marks = new Map();
265
+ this.measures = [];
266
+ }
267
+
268
+ mark(name) {
269
+ const timestamp = performance.now();
270
+ this.marks.set(name, timestamp);
271
+ performance.mark(name);
272
+ }
273
+
274
+ measure(name, startMark, endMark) {
275
+ try {
276
+ performance.measure(name, startMark, endMark);
277
+ const measure = performance.getEntriesByName(name, 'measure')[0];
278
+
279
+ this.measures.push({
280
+ name,
281
+ duration: measure.duration,
282
+ startTime: measure.startTime
283
+ });
284
+
285
+ return measure.duration;
286
+ } catch (error) {
287
+ console.error('性能测量失败:', error);
288
+ return 0;
289
+ }
290
+ }
291
+
292
+ getMetrics() {
293
+ const navigation = performance.getEntriesByType('navigation')[0];
294
+ const paint = performance.getEntriesByType('paint');
295
+
296
+ return {
297
+ // 页面加载时间
298
+ loadTime: navigation ? navigation.loadEventEnd - navigation.fetchStart : 0,
299
+ // DOM 解析时间
300
+ domParseTime: navigation ? navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart : 0,
301
+ // 首次绘制
302
+ firstPaint: paint.find(p => p.name === 'first-paint')?.startTime || 0,
303
+ // 首次内容绘制
304
+ firstContentfulPaint: paint.find(p => p.name === 'first-contentful-paint')?.startTime || 0,
305
+ // 自定义测量
306
+ customMeasures: this.measures
307
+ };
308
+ }
309
+
310
+ logMetrics() {
311
+ const metrics = this.getMetrics();
312
+ console.table(metrics);
313
+ }
314
+
315
+ clear() {
316
+ this.marks.clear();
317
+ this.measures = [];
318
+ performance.clearMarks();
319
+ performance.clearMeasures();
320
+ }
321
+ }
322
+
323
+ /**
324
+ * 内存优化 - 对象池
325
+ */
326
+ export class ObjectPool {
327
+ constructor(factory, reset, initialSize = 10) {
328
+ this.factory = factory;
329
+ this.reset = reset;
330
+ this.pool = [];
331
+
332
+ // 预创建对象
333
+ for (let i = 0; i < initialSize; i++) {
334
+ this.pool.push(this.factory());
335
+ }
336
+ }
337
+
338
+ acquire() {
339
+ return this.pool.length > 0 ? this.pool.pop() : this.factory();
340
+ }
341
+
342
+ release(obj) {
343
+ this.reset(obj);
344
+ this.pool.push(obj);
345
+ }
346
+
347
+ clear() {
348
+ this.pool = [];
349
+ }
350
+
351
+ get size() {
352
+ return this.pool.length;
353
+ }
354
+ }
355
+
356
+ /**
357
+ * 批量 DOM 更新
358
+ */
359
+ export class BatchUpdater {
360
+ constructor() {
361
+ this.updates = [];
362
+ this.scheduled = false;
363
+ }
364
+
365
+ add(updateFn) {
366
+ this.updates.push(updateFn);
367
+ this.schedule();
368
+ }
369
+
370
+ schedule() {
371
+ if (this.scheduled) return;
372
+
373
+ this.scheduled = true;
374
+ requestAnimationFrame(() => {
375
+ this.flush();
376
+ });
377
+ }
378
+
379
+ flush() {
380
+ const updates = this.updates.slice();
381
+ this.updates = [];
382
+ this.scheduled = false;
383
+
384
+ updates.forEach(update => {
385
+ try {
386
+ update();
387
+ } catch (error) {
388
+ console.error('批量更新失败:', error);
389
+ }
390
+ });
391
+ }
392
+ }
393
+
394
+