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.
- package/README.md +219 -218
- package/index.html +2 -0
- package/install-and-start.bat +5 -0
- package/install-and-start.sh +5 -0
- package/package.json +9 -2
- package/scripts/pre-publish-check.js +213 -0
- package/scripts/start-app.js +15 -15
- package/server/index.js +38 -6
- package/server/middleware/cache.js +115 -0
- package/server/middleware/errorHandler.js +209 -0
- package/server/models/Document.js +66 -59
- package/server/models/File.js +49 -43
- package/server/models/Group.js +6 -0
- package/server/models/KnowledgeBase.js +254 -0
- package/server/models/Message.js +43 -0
- package/server/models/Task.js +87 -55
- package/server/models/User.js +67 -60
- package/server/models/Workflow.js +249 -0
- package/server/routes/ai.js +327 -0
- package/server/routes/audit.js +245 -210
- package/server/routes/backup.js +108 -0
- package/server/routes/chunked-upload.js +343 -0
- package/server/routes/export.js +440 -0
- package/server/routes/files.js +294 -218
- package/server/routes/groups.js +182 -0
- package/server/routes/knowledge.js +509 -0
- package/server/routes/tasks.js +257 -110
- package/server/routes/workflows.js +380 -0
- package/server/utils/backup.js +439 -0
- package/server/utils/cache.js +223 -0
- package/server/utils/workflow-engine.js +479 -0
- package/server/websocket/enhanced.js +509 -0
- package/server/websocket/index.js +233 -1
- package/src/components/knowledge-modal.js +485 -0
- package/src/components/optimized-poll-detail.js +724 -0
- package/src/main.js +5 -0
- package/src/pages/admin-dashboard.js +2248 -44
- package/src/pages/optimized-backup-view.js +616 -0
- package/src/pages/optimized-knowledge-view.js +803 -0
- package/src/pages/optimized-task-detail.js +843 -0
- package/src/pages/optimized-workflow-view.js +806 -0
- package/src/pages/simplified-workflows.js +651 -0
- package/src/pages/user-dashboard.js +677 -58
- package/src/services/api.js +65 -1
- package/src/services/auth.js +1 -1
- package/src/services/websocket.js +124 -16
- package/src/styles/collaboration-modern.js +708 -0
- package/src/styles/enhancements.css +392 -0
- package/src/styles/main.css +620 -1420
- package/src/styles/responsive.css +1000 -0
- package/src/styles/sidebar-fix.css +60 -0
- package/src/utils/ai-assistant.js +1398 -0
- package/src/utils/chat-enhancements.js +509 -0
- package/src/utils/collaboration-enhancer.js +1151 -0
- package/src/utils/feature-integrator.js +1724 -0
- package/src/utils/onboarding-guide.js +734 -0
- package/src/utils/performance.js +394 -0
- package/src/utils/permission-manager.js +890 -0
- package/src/utils/responsive-handler.js +491 -0
- package/src/utils/theme-manager.js +811 -0
- package/src/utils/ui-enhancements-loader.js +329 -0
- package/USAGE.md +0 -298
package/src/services/api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const API_URL = 'http://localhost:
|
|
1
|
+
const API_URL = 'http://localhost:8765/api';
|
|
2
2
|
|
|
3
3
|
export class ApiService {
|
|
4
4
|
constructor() {
|
|
@@ -95,6 +95,48 @@ export class ApiService {
|
|
|
95
95
|
return await this.request(`/groups/${groupId}/messages`);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
// 清除聊天记录(管理员)
|
|
99
|
+
async clearChatMessages(groupId, options = {}) {
|
|
100
|
+
return await this.request(`/groups/${groupId}/messages`, {
|
|
101
|
+
method: 'DELETE',
|
|
102
|
+
body: JSON.stringify(options)
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 清除指定用户的消息(管理员)
|
|
107
|
+
async clearUserMessages(groupId, userId) {
|
|
108
|
+
return await this.request(`/groups/${groupId}/messages/user/${userId}`, {
|
|
109
|
+
method: 'DELETE'
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 撤回消息
|
|
114
|
+
async recallMessage(groupId, messageId) {
|
|
115
|
+
return await this.request(`/groups/${groupId}/messages/${messageId}/recall`, {
|
|
116
|
+
method: 'POST'
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 标记消息为已读
|
|
121
|
+
async markMessageAsRead(groupId, messageId) {
|
|
122
|
+
return await this.request(`/groups/${groupId}/messages/${messageId}/read`, {
|
|
123
|
+
method: 'POST'
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 批量标记消息为已读
|
|
128
|
+
async markMessagesAsRead(groupId, messageIds) {
|
|
129
|
+
return await this.request(`/groups/${groupId}/messages/read-all`, {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
body: JSON.stringify({ messageIds })
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 获取未读消息数量
|
|
136
|
+
async getUnreadCount(groupId) {
|
|
137
|
+
return await this.request(`/groups/${groupId}/messages/unread-count`);
|
|
138
|
+
}
|
|
139
|
+
|
|
98
140
|
async randomCall(groupId, count = 1) {
|
|
99
141
|
return await this.request(`/groups/${groupId}/call`, {
|
|
100
142
|
method: 'POST',
|
|
@@ -131,6 +173,14 @@ export class ApiService {
|
|
|
131
173
|
});
|
|
132
174
|
}
|
|
133
175
|
|
|
176
|
+
// 投票相关
|
|
177
|
+
async submitPollVote(taskId, selectedOptions) {
|
|
178
|
+
return await this.request(`/tasks/${taskId}/vote`, {
|
|
179
|
+
method: 'POST',
|
|
180
|
+
body: JSON.stringify({ selectedOptions })
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
134
184
|
// 文档相关
|
|
135
185
|
async getDocuments(groupId) {
|
|
136
186
|
return await this.request(`/documents/group/${groupId}`);
|
|
@@ -217,12 +267,22 @@ export class ApiService {
|
|
|
217
267
|
return await this.request(`/audit/group/${groupId}${queryString ? '?' + queryString : ''}`);
|
|
218
268
|
}
|
|
219
269
|
|
|
270
|
+
// 获取单个任务详情
|
|
271
|
+
async getTask(taskId) {
|
|
272
|
+
return await this.request(`/tasks/${taskId}`);
|
|
273
|
+
}
|
|
274
|
+
|
|
220
275
|
async getAuditSummary(filters = {}) {
|
|
221
276
|
const params = new URLSearchParams(filters);
|
|
222
277
|
const queryString = params.toString();
|
|
223
278
|
return await this.request(`/audit/stats/summary${queryString ? '?' + queryString : ''}`);
|
|
224
279
|
}
|
|
225
280
|
|
|
281
|
+
// 获取审计日志详情
|
|
282
|
+
async getAuditLogDetail(logId) {
|
|
283
|
+
return await this.request(`/audit/${logId}`);
|
|
284
|
+
}
|
|
285
|
+
|
|
226
286
|
// 文件相关
|
|
227
287
|
async uploadFile(groupId, file, description = '') {
|
|
228
288
|
const formData = new FormData();
|
|
@@ -261,5 +321,9 @@ export class ApiService {
|
|
|
261
321
|
getFileDownloadUrl(fileId) {
|
|
262
322
|
return `${API_URL}/files/${fileId}/download?token=${this.token}`;
|
|
263
323
|
}
|
|
324
|
+
|
|
325
|
+
getFileViewUrl(fileId) {
|
|
326
|
+
return `${API_URL}/files/${fileId}/view?token=${this.token}`;
|
|
327
|
+
}
|
|
264
328
|
}
|
|
265
329
|
|
package/src/services/auth.js
CHANGED
|
@@ -2,29 +2,122 @@ export class WebSocketService {
|
|
|
2
2
|
constructor() {
|
|
3
3
|
this.ws = null;
|
|
4
4
|
this.listeners = new Map();
|
|
5
|
+
this.token = null;
|
|
6
|
+
this.reconnectAttempts = 0;
|
|
7
|
+
this.maxReconnectAttempts = 10;
|
|
8
|
+
this.reconnectDelay = 1000;
|
|
9
|
+
this.heartbeatInterval = null;
|
|
10
|
+
this.isConnecting = false;
|
|
11
|
+
this.isAuthenticated = false;
|
|
5
12
|
}
|
|
6
13
|
|
|
7
14
|
connect(token) {
|
|
8
|
-
this.
|
|
15
|
+
if (this.isConnecting) {
|
|
16
|
+
console.log('⏳ 正在连接中,请稍候...');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
9
19
|
|
|
10
|
-
this.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
this.token = token;
|
|
21
|
+
this.isConnecting = true;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
this.ws = new WebSocket('ws://localhost:8765');
|
|
25
|
+
|
|
26
|
+
this.ws.onopen = () => {
|
|
27
|
+
console.log('✅ WebSocket 连接成功');
|
|
28
|
+
this.isConnecting = false;
|
|
29
|
+
this.reconnectAttempts = 0;
|
|
30
|
+
this.reconnectDelay = 1000;
|
|
31
|
+
this.send({ type: 'auth', token });
|
|
32
|
+
this.startHeartbeat();
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
this.ws.onmessage = (event) => {
|
|
36
|
+
try {
|
|
37
|
+
const data = JSON.parse(event.data);
|
|
38
|
+
|
|
39
|
+
if (data.type === 'auth_success') {
|
|
40
|
+
this.isAuthenticated = true;
|
|
41
|
+
console.log('✅ WebSocket 认证成功');
|
|
42
|
+
} else if (data.type === 'pong') {
|
|
43
|
+
// 心跳响应
|
|
44
|
+
console.log('💓 心跳正常');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.notifyListeners(data.type, data);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('❌ 解析消息失败:', error);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
this.ws.onerror = (error) => {
|
|
54
|
+
console.error('❌ WebSocket 错误:', error);
|
|
55
|
+
this.isConnecting = false;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
this.ws.onclose = (event) => {
|
|
59
|
+
console.log(`🔌 WebSocket 连接关闭 (代码: ${event.code}, 原因: ${event.reason || '未知'})`);
|
|
60
|
+
this.isConnecting = false;
|
|
61
|
+
this.isAuthenticated = false;
|
|
62
|
+
this.stopHeartbeat();
|
|
63
|
+
this.handleReconnect();
|
|
64
|
+
};
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error('❌ WebSocket 连接失败:', error);
|
|
67
|
+
this.isConnecting = false;
|
|
68
|
+
this.handleReconnect();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
handleReconnect() {
|
|
73
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
74
|
+
console.error('❌ 达到最大重连次数,停止重连');
|
|
75
|
+
this.notifyListeners('max_reconnect_reached', {
|
|
76
|
+
message: '无法连接到服务器,请刷新页面重试'
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
14
80
|
|
|
15
|
-
this.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
81
|
+
this.reconnectAttempts++;
|
|
82
|
+
const delay = Math.min(this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1), 30000);
|
|
83
|
+
|
|
84
|
+
console.log(`🔄 ${delay / 1000}秒后尝试第 ${this.reconnectAttempts} 次重连...`);
|
|
85
|
+
|
|
86
|
+
setTimeout(() => {
|
|
87
|
+
if (this.token) {
|
|
88
|
+
this.connect(this.token);
|
|
89
|
+
}
|
|
90
|
+
}, delay);
|
|
91
|
+
}
|
|
19
92
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
93
|
+
startHeartbeat() {
|
|
94
|
+
this.stopHeartbeat();
|
|
95
|
+
|
|
96
|
+
this.heartbeatInterval = setInterval(() => {
|
|
97
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
98
|
+
this.send({ type: 'ping', timestamp: Date.now() });
|
|
99
|
+
}
|
|
100
|
+
}, 30000); // 每30秒发送一次心跳
|
|
101
|
+
}
|
|
23
102
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
103
|
+
stopHeartbeat() {
|
|
104
|
+
if (this.heartbeatInterval) {
|
|
105
|
+
clearInterval(this.heartbeatInterval);
|
|
106
|
+
this.heartbeatInterval = null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
disconnect() {
|
|
111
|
+
this.stopHeartbeat();
|
|
112
|
+
this.reconnectAttempts = this.maxReconnectAttempts; // 阻止自动重连
|
|
113
|
+
|
|
114
|
+
if (this.ws) {
|
|
115
|
+
this.ws.close(1000, '用户主动断开');
|
|
116
|
+
this.ws = null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
this.isAuthenticated = false;
|
|
120
|
+
console.log('👋 WebSocket 已断开');
|
|
28
121
|
}
|
|
29
122
|
|
|
30
123
|
send(data) {
|
|
@@ -75,6 +168,21 @@ export class WebSocketService {
|
|
|
75
168
|
sendTyping(documentId, username, isTyping) {
|
|
76
169
|
this.send({ type: 'typing', documentId, username, isTyping });
|
|
77
170
|
}
|
|
171
|
+
|
|
172
|
+
// 撤回消息
|
|
173
|
+
recallMessage(messageId) {
|
|
174
|
+
this.send({ type: 'recall_message', messageId });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// 标记消息为已读
|
|
178
|
+
markMessageAsRead(messageId) {
|
|
179
|
+
this.send({ type: 'message_read', messageId });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// 发送用户在线状态
|
|
183
|
+
sendUserOnline(username) {
|
|
184
|
+
this.send({ type: 'user_online', username });
|
|
185
|
+
}
|
|
78
186
|
}
|
|
79
187
|
|
|
80
188
|
|