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.
- 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/generate-docs.js +448 -0
- 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 +64 -0
- 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
|
@@ -0,0 +1,806 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 优化后的工作流管理界面
|
|
3
|
+
* 可视化创建工作流,无需编写JSON代码
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
async function renderOptimizedWorkflowView(container, currentGroup, apiService) {
|
|
7
|
+
if (!currentGroup) {
|
|
8
|
+
container.innerHTML = '<div class="empty-state">请先选择一个群组</div>';
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const token = localStorage.getItem('token');
|
|
14
|
+
|
|
15
|
+
// 获取工作流列表
|
|
16
|
+
const workflowsResponse = await fetch(`http://localhost:8765/api/workflows/group/${currentGroup._id}`, {
|
|
17
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
18
|
+
});
|
|
19
|
+
const workflowsResult = await workflowsResponse.json();
|
|
20
|
+
const workflows = workflowsResult.data?.workflows || [];
|
|
21
|
+
|
|
22
|
+
console.log('📋 工作流列表:', workflows);
|
|
23
|
+
|
|
24
|
+
container.innerHTML = `
|
|
25
|
+
<div class="view-header">
|
|
26
|
+
<h2>🔄 工作流管理 - ${currentGroup.name}</h2>
|
|
27
|
+
<button class="btn-primary" id="createWorkflowBtn">
|
|
28
|
+
<span style="font-size: 18px;">➕</span> 创建工作流
|
|
29
|
+
</button>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<!-- 工作流模板选择 -->
|
|
33
|
+
<div class="workflow-templates-section">
|
|
34
|
+
<h3 style="margin: 20px 0; color: var(--text-primary);">📋 快速开始 - 选择模板</h3>
|
|
35
|
+
<div class="workflow-templates-grid">
|
|
36
|
+
<div class="template-card" data-template="document-approval">
|
|
37
|
+
<div class="template-icon">📄</div>
|
|
38
|
+
<h4>文档审批流程</h4>
|
|
39
|
+
<p>创建文档后自动通知审批人</p>
|
|
40
|
+
<button class="btn-use-template">使用模板</button>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div class="template-card" data-template="task-notification">
|
|
44
|
+
<div class="template-icon">📋</div>
|
|
45
|
+
<h4>任务通知流程</h4>
|
|
46
|
+
<p>任务创建时自动通知相关人员</p>
|
|
47
|
+
<button class="btn-use-template">使用模板</button>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="template-card" data-template="auto-backup">
|
|
51
|
+
<div class="template-icon">💾</div>
|
|
52
|
+
<h4>自动备份流程</h4>
|
|
53
|
+
<p>定时自动备份重要数据</p>
|
|
54
|
+
<button class="btn-use-template">使用模板</button>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div class="template-card" data-template="custom">
|
|
58
|
+
<div class="template-icon">⚙️</div>
|
|
59
|
+
<h4>自定义流程</h4>
|
|
60
|
+
<p>从零开始创建自定义工作流</p>
|
|
61
|
+
<button class="btn-use-template">创建</button>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<!-- 现有工作流列表 -->
|
|
67
|
+
<div class="workflows-list-section">
|
|
68
|
+
<h3 style="margin: 30px 0 20px; color: var(--text-primary);">📦 现有工作流</h3>
|
|
69
|
+
<div class="workflows-cards-grid" id="workflowCards">
|
|
70
|
+
${workflows.length === 0 ? `
|
|
71
|
+
<div class="empty-state-modern">
|
|
72
|
+
<div class="empty-icon">📭</div>
|
|
73
|
+
<h3>暂无工作流</h3>
|
|
74
|
+
<p>选择上方模板快速创建工作流</p>
|
|
75
|
+
</div>
|
|
76
|
+
` : workflows.map(workflow => createWorkflowCard(workflow)).join('')}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<!-- 创建工作流模态框 -->
|
|
81
|
+
<div class="modal hidden" id="createWorkflowModal">
|
|
82
|
+
<div class="modal-content" style="max-width: 800px;">
|
|
83
|
+
<div class="modal-header">
|
|
84
|
+
<h3>创建工作流</h3>
|
|
85
|
+
<button class="close-btn" id="closeCreateWorkflow">×</button>
|
|
86
|
+
</div>
|
|
87
|
+
<form id="createWorkflowForm">
|
|
88
|
+
<div class="workflow-form-modern">
|
|
89
|
+
<!-- 基本信息 -->
|
|
90
|
+
<div class="form-section">
|
|
91
|
+
<h4>📝 基本信息</h4>
|
|
92
|
+
<div class="form-group">
|
|
93
|
+
<label>工作流名称 *</label>
|
|
94
|
+
<input type="text" id="workflowName" placeholder="例如:文档审批流程" required>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="form-group">
|
|
97
|
+
<label>描述</label>
|
|
98
|
+
<textarea id="workflowDescription" rows="3" placeholder="描述这个工作流的用途..."></textarea>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<!-- 触发条件 -->
|
|
103
|
+
<div class="form-section">
|
|
104
|
+
<h4>⚡ 触发条件</h4>
|
|
105
|
+
<div class="form-group">
|
|
106
|
+
<label>触发类型 *</label>
|
|
107
|
+
<select id="triggerType" required>
|
|
108
|
+
<option value="">请选择触发类型</option>
|
|
109
|
+
<option value="document_create">文档创建时</option>
|
|
110
|
+
<option value="document_update">文档更新时</option>
|
|
111
|
+
<option value="task_create">任务创建时</option>
|
|
112
|
+
<option value="task_complete">任务完成时</option>
|
|
113
|
+
<option value="message_send">消息发送时</option>
|
|
114
|
+
<option value="scheduled">定时触发</option>
|
|
115
|
+
</select>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="form-group hidden" id="scheduleGroup">
|
|
118
|
+
<label>定时规则(Cron表达式)</label>
|
|
119
|
+
<input type="text" id="scheduleRule" placeholder="例如:0 9 * * * (每天9点)">
|
|
120
|
+
<small>示例:0 9 * * * (每天9点),0 */6 * * * (每6小时)</small>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<!-- 执行动作 -->
|
|
125
|
+
<div class="form-section">
|
|
126
|
+
<h4>🎯 执行动作</h4>
|
|
127
|
+
<div class="form-group">
|
|
128
|
+
<label>动作类型 *</label>
|
|
129
|
+
<select id="actionType" required>
|
|
130
|
+
<option value="">请选择动作类型</option>
|
|
131
|
+
<option value="notification">发送通知</option>
|
|
132
|
+
<option value="email">发送邮件</option>
|
|
133
|
+
<option value="create_task">创建任务</option>
|
|
134
|
+
<option value="backup">执行备份</option>
|
|
135
|
+
<option value="webhook">调用Webhook</option>
|
|
136
|
+
</select>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<!-- 通知配置 -->
|
|
140
|
+
<div class="action-config hidden" id="notificationConfig">
|
|
141
|
+
<div class="form-group">
|
|
142
|
+
<label>通知消息</label>
|
|
143
|
+
<textarea id="notificationMessage" rows="2" placeholder="通知内容..."></textarea>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="form-group">
|
|
146
|
+
<label>通知对象</label>
|
|
147
|
+
<select id="notificationTarget">
|
|
148
|
+
<option value="all">所有成员</option>
|
|
149
|
+
<option value="admin">仅管理员</option>
|
|
150
|
+
<option value="creator">创建者</option>
|
|
151
|
+
</select>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<!-- 邮件配置 -->
|
|
156
|
+
<div class="action-config hidden" id="emailConfig">
|
|
157
|
+
<div class="form-group">
|
|
158
|
+
<label>邮件主题</label>
|
|
159
|
+
<input type="text" id="emailSubject" placeholder="邮件主题">
|
|
160
|
+
</div>
|
|
161
|
+
<div class="form-group">
|
|
162
|
+
<label>邮件内容</label>
|
|
163
|
+
<textarea id="emailBody" rows="3" placeholder="邮件内容..."></textarea>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<!-- 任务配置 -->
|
|
168
|
+
<div class="action-config hidden" id="taskConfig">
|
|
169
|
+
<div class="form-group">
|
|
170
|
+
<label>任务标题</label>
|
|
171
|
+
<input type="text" id="taskTitle" placeholder="任务标题">
|
|
172
|
+
</div>
|
|
173
|
+
<div class="form-group">
|
|
174
|
+
<label>任务描述</label>
|
|
175
|
+
<textarea id="taskDescription" rows="2" placeholder="任务描述..."></textarea>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
<!-- Webhook配置 -->
|
|
180
|
+
<div class="action-config hidden" id="webhookConfig">
|
|
181
|
+
<div class="form-group">
|
|
182
|
+
<label>Webhook URL</label>
|
|
183
|
+
<input type="url" id="webhookUrl" placeholder="https://example.com/webhook">
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<!-- 条件过滤 -->
|
|
189
|
+
<div class="form-section">
|
|
190
|
+
<h4>🔍 条件过滤(可选)</h4>
|
|
191
|
+
<div class="form-group">
|
|
192
|
+
<label>
|
|
193
|
+
<input type="checkbox" id="enableCondition">
|
|
194
|
+
启用条件过滤
|
|
195
|
+
</label>
|
|
196
|
+
</div>
|
|
197
|
+
<div class="condition-config hidden" id="conditionConfig">
|
|
198
|
+
<div class="form-group">
|
|
199
|
+
<label>条件字段</label>
|
|
200
|
+
<input type="text" id="conditionField" placeholder="例如:title">
|
|
201
|
+
</div>
|
|
202
|
+
<div class="form-group">
|
|
203
|
+
<label>条件值</label>
|
|
204
|
+
<input type="text" id="conditionValue" placeholder="例如:重要">
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<div style="display: flex; gap: 10px; margin-top: 20px; justify-content: center;">
|
|
211
|
+
<button type="submit" class="btn-primary btn-large">
|
|
212
|
+
<span>✅</span> 创建工作流
|
|
213
|
+
</button>
|
|
214
|
+
<button type="button" class="btn-secondary btn-large" id="cancelCreateWorkflow">
|
|
215
|
+
<span>❌</span> 取消
|
|
216
|
+
</button>
|
|
217
|
+
</div>
|
|
218
|
+
</form>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
`;
|
|
222
|
+
|
|
223
|
+
// 添加样式
|
|
224
|
+
addWorkflowStyles();
|
|
225
|
+
|
|
226
|
+
// 绑定事件
|
|
227
|
+
setupWorkflowEvents(token, container, currentGroup, apiService);
|
|
228
|
+
|
|
229
|
+
} catch (error) {
|
|
230
|
+
console.error('加载工作流管理失败:', error);
|
|
231
|
+
container.innerHTML = `
|
|
232
|
+
<div class="view-header">
|
|
233
|
+
<h2>🔄 工作流管理</h2>
|
|
234
|
+
</div>
|
|
235
|
+
<div class="empty-state">加载失败: ${error.message}</div>
|
|
236
|
+
`;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// 创建工作流卡片
|
|
241
|
+
function createWorkflowCard(workflow) {
|
|
242
|
+
const statusConfig = {
|
|
243
|
+
active: { icon: '✅', label: '运行中', color: '#10b981' },
|
|
244
|
+
inactive: { icon: '⏸️', label: '已暂停', color: '#f59e0b' },
|
|
245
|
+
error: { icon: '❌', label: '错误', color: '#ef4444' }
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const config = statusConfig[workflow.status] || statusConfig.inactive;
|
|
249
|
+
|
|
250
|
+
return `
|
|
251
|
+
<div class="workflow-card-modern">
|
|
252
|
+
<div class="workflow-card-header">
|
|
253
|
+
<div class="workflow-icon">🔄</div>
|
|
254
|
+
<div class="workflow-card-title">
|
|
255
|
+
<h4>${workflow.name}</h4>
|
|
256
|
+
<span class="workflow-time">${new Date(workflow.createdAt).toLocaleString()}</span>
|
|
257
|
+
</div>
|
|
258
|
+
<div class="workflow-status-badge" style="background: ${config.color}20; color: ${config.color};">
|
|
259
|
+
${config.icon} ${config.label}
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
<div class="workflow-card-body">
|
|
264
|
+
<p class="workflow-description">${workflow.description || '无描述'}</p>
|
|
265
|
+
<div class="workflow-info-row">
|
|
266
|
+
<span class="info-label">触发器</span>
|
|
267
|
+
<span class="info-value">${getTriggerText(workflow.trigger)}</span>
|
|
268
|
+
</div>
|
|
269
|
+
<div class="workflow-info-row">
|
|
270
|
+
<span class="info-label">执行次数</span>
|
|
271
|
+
<span class="info-value">${workflow.executionCount || 0} 次</span>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
|
|
275
|
+
<div class="workflow-card-actions">
|
|
276
|
+
<button class="btn-action btn-toggle" data-id="${workflow._id}" data-status="${workflow.status}">
|
|
277
|
+
<span>${workflow.status === 'active' ? '⏸️' : '▶️'}</span> ${workflow.status === 'active' ? '暂停' : '启动'}
|
|
278
|
+
</button>
|
|
279
|
+
<button class="btn-action btn-edit" data-id="${workflow._id}">
|
|
280
|
+
<span>✏️</span> 编辑
|
|
281
|
+
</button>
|
|
282
|
+
<button class="btn-action btn-delete" data-id="${workflow._id}">
|
|
283
|
+
<span>🗑️</span> 删除
|
|
284
|
+
</button>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// 获取触发器文本
|
|
291
|
+
function getTriggerText(trigger) {
|
|
292
|
+
if (!trigger) return '未知';
|
|
293
|
+
|
|
294
|
+
const typeMap = {
|
|
295
|
+
'manual': '手动触发',
|
|
296
|
+
'scheduled': '定时触发',
|
|
297
|
+
'event': '事件触发'
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const eventMap = {
|
|
301
|
+
'document_create': '文档创建',
|
|
302
|
+
'document_update': '文档更新',
|
|
303
|
+
'task_create': '任务创建',
|
|
304
|
+
'task_complete': '任务完成',
|
|
305
|
+
'message_send': '消息发送'
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
if (trigger.type === 'event' && trigger.event) {
|
|
309
|
+
return eventMap[trigger.event] || trigger.event;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return typeMap[trigger.type] || trigger.type;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// 添加样式
|
|
316
|
+
function addWorkflowStyles() {
|
|
317
|
+
if (document.getElementById('workflow-modern-styles')) return;
|
|
318
|
+
|
|
319
|
+
const style = document.createElement('style');
|
|
320
|
+
style.id = 'workflow-modern-styles';
|
|
321
|
+
style.textContent = `
|
|
322
|
+
.workflow-templates-grid {
|
|
323
|
+
display: grid;
|
|
324
|
+
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
|
325
|
+
gap: 20px;
|
|
326
|
+
margin-bottom: 30px;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.template-card {
|
|
330
|
+
background: linear-gradient(135deg, var(--bg-card) 0%, rgba(99,102,241,0.05) 100%);
|
|
331
|
+
border: 2px solid var(--border);
|
|
332
|
+
border-radius: 16px;
|
|
333
|
+
padding: 24px;
|
|
334
|
+
text-align: center;
|
|
335
|
+
transition: all 0.3s ease;
|
|
336
|
+
cursor: pointer;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.template-card:hover {
|
|
340
|
+
transform: translateY(-8px);
|
|
341
|
+
box-shadow: 0 12px 32px rgba(99,102,241,0.2);
|
|
342
|
+
border-color: var(--primary);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.template-icon {
|
|
346
|
+
font-size: 48px;
|
|
347
|
+
margin-bottom: 16px;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.template-card h4 {
|
|
351
|
+
margin: 0 0 8px;
|
|
352
|
+
font-size: 16px;
|
|
353
|
+
color: var(--text-primary);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.template-card p {
|
|
357
|
+
margin: 0 0 16px;
|
|
358
|
+
font-size: 13px;
|
|
359
|
+
color: var(--text-secondary);
|
|
360
|
+
line-height: 1.5;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.btn-use-template {
|
|
364
|
+
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
|
365
|
+
color: white;
|
|
366
|
+
border: none;
|
|
367
|
+
padding: 10px 20px;
|
|
368
|
+
border-radius: 8px;
|
|
369
|
+
font-weight: 600;
|
|
370
|
+
cursor: pointer;
|
|
371
|
+
transition: all 0.3s ease;
|
|
372
|
+
width: 100%;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.btn-use-template:hover {
|
|
376
|
+
transform: scale(1.05);
|
|
377
|
+
box-shadow: 0 4px 12px rgba(99,102,241,0.4);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.workflows-cards-grid {
|
|
381
|
+
display: grid;
|
|
382
|
+
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
|
383
|
+
gap: 20px;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.workflow-card-modern {
|
|
387
|
+
background: var(--bg-card);
|
|
388
|
+
border: 2px solid var(--border);
|
|
389
|
+
border-radius: 16px;
|
|
390
|
+
padding: 20px;
|
|
391
|
+
transition: all 0.3s ease;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.workflow-card-modern:hover {
|
|
395
|
+
transform: translateY(-5px);
|
|
396
|
+
box-shadow: 0 8px 24px rgba(99,102,241,0.15);
|
|
397
|
+
border-color: var(--primary);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.workflow-card-header {
|
|
401
|
+
display: flex;
|
|
402
|
+
align-items: center;
|
|
403
|
+
gap: 12px;
|
|
404
|
+
margin-bottom: 16px;
|
|
405
|
+
padding-bottom: 16px;
|
|
406
|
+
border-bottom: 1px solid var(--border);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.workflow-icon {
|
|
410
|
+
width: 48px;
|
|
411
|
+
height: 48px;
|
|
412
|
+
border-radius: 12px;
|
|
413
|
+
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
|
414
|
+
display: flex;
|
|
415
|
+
align-items: center;
|
|
416
|
+
justify-content: center;
|
|
417
|
+
font-size: 24px;
|
|
418
|
+
flex-shrink: 0;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.workflow-card-title {
|
|
422
|
+
flex: 1;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.workflow-card-title h4 {
|
|
426
|
+
margin: 0 0 4px;
|
|
427
|
+
font-size: 16px;
|
|
428
|
+
color: var(--text-primary);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.workflow-time {
|
|
432
|
+
font-size: 12px;
|
|
433
|
+
color: var(--text-secondary);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.workflow-status-badge {
|
|
437
|
+
padding: 6px 12px;
|
|
438
|
+
border-radius: 8px;
|
|
439
|
+
font-size: 12px;
|
|
440
|
+
font-weight: 600;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.workflow-card-body {
|
|
444
|
+
margin-bottom: 16px;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.workflow-description {
|
|
448
|
+
margin: 0 0 12px;
|
|
449
|
+
font-size: 14px;
|
|
450
|
+
color: var(--text-secondary);
|
|
451
|
+
line-height: 1.5;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.workflow-info-row {
|
|
455
|
+
display: flex;
|
|
456
|
+
justify-content: space-between;
|
|
457
|
+
padding: 8px 0;
|
|
458
|
+
font-size: 14px;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.workflow-card-actions {
|
|
462
|
+
display: flex;
|
|
463
|
+
gap: 8px;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.workflow-form-modern {
|
|
467
|
+
max-height: 60vh;
|
|
468
|
+
overflow-y: auto;
|
|
469
|
+
padding-right: 10px;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.form-section {
|
|
473
|
+
background: var(--bg-dark);
|
|
474
|
+
border: 1px solid var(--border);
|
|
475
|
+
border-radius: 12px;
|
|
476
|
+
padding: 20px;
|
|
477
|
+
margin-bottom: 20px;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.form-section h4 {
|
|
481
|
+
margin: 0 0 16px;
|
|
482
|
+
font-size: 16px;
|
|
483
|
+
color: var(--text-primary);
|
|
484
|
+
display: flex;
|
|
485
|
+
align-items: center;
|
|
486
|
+
gap: 8px;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.action-config,
|
|
490
|
+
.condition-config {
|
|
491
|
+
margin-top: 16px;
|
|
492
|
+
padding-top: 16px;
|
|
493
|
+
border-top: 1px solid var(--border);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.btn-large {
|
|
497
|
+
padding: 14px 28px;
|
|
498
|
+
font-size: 15px;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.empty-state-modern {
|
|
502
|
+
grid-column: 1 / -1;
|
|
503
|
+
text-align: center;
|
|
504
|
+
padding: 60px 20px;
|
|
505
|
+
background: linear-gradient(135deg, var(--bg-dark) 0%, rgba(99,102,241,0.03) 100%);
|
|
506
|
+
border-radius: 16px;
|
|
507
|
+
border: 2px dashed var(--border);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.empty-icon {
|
|
511
|
+
font-size: 64px;
|
|
512
|
+
margin-bottom: 20px;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.empty-state-modern h3 {
|
|
516
|
+
margin: 0 0 10px;
|
|
517
|
+
color: var(--text-primary);
|
|
518
|
+
font-size: 20px;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.empty-state-modern p {
|
|
522
|
+
margin: 0;
|
|
523
|
+
color: var(--text-secondary);
|
|
524
|
+
font-size: 14px;
|
|
525
|
+
}
|
|
526
|
+
`;
|
|
527
|
+
document.head.appendChild(style);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// 设置事件监听
|
|
531
|
+
function setupWorkflowEvents(token, container, currentGroup, apiService) {
|
|
532
|
+
// 模板选择
|
|
533
|
+
document.querySelectorAll('.template-card').forEach(card => {
|
|
534
|
+
card.addEventListener('click', () => {
|
|
535
|
+
const template = card.dataset.template;
|
|
536
|
+
openWorkflowModal(template);
|
|
537
|
+
});
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// 创建工作流按钮
|
|
541
|
+
document.getElementById('createWorkflowBtn')?.addEventListener('click', () => {
|
|
542
|
+
openWorkflowModal('custom');
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
// 关闭模态框
|
|
546
|
+
document.getElementById('closeCreateWorkflow')?.addEventListener('click', () => {
|
|
547
|
+
document.getElementById('createWorkflowModal').classList.add('hidden');
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
document.getElementById('cancelCreateWorkflow')?.addEventListener('click', () => {
|
|
551
|
+
document.getElementById('createWorkflowModal').classList.add('hidden');
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
// 触发类型变化
|
|
555
|
+
document.getElementById('triggerType')?.addEventListener('change', (e) => {
|
|
556
|
+
const scheduleGroup = document.getElementById('scheduleGroup');
|
|
557
|
+
if (e.target.value === 'scheduled') {
|
|
558
|
+
scheduleGroup.classList.remove('hidden');
|
|
559
|
+
} else {
|
|
560
|
+
scheduleGroup.classList.add('hidden');
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
// 动作类型变化
|
|
565
|
+
document.getElementById('actionType')?.addEventListener('change', (e) => {
|
|
566
|
+
// 隐藏所有配置
|
|
567
|
+
document.querySelectorAll('.action-config').forEach(el => el.classList.add('hidden'));
|
|
568
|
+
|
|
569
|
+
// 显示对应配置
|
|
570
|
+
const configMap = {
|
|
571
|
+
'notification': 'notificationConfig',
|
|
572
|
+
'email': 'emailConfig',
|
|
573
|
+
'create_task': 'taskConfig',
|
|
574
|
+
'webhook': 'webhookConfig'
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
const configId = configMap[e.target.value];
|
|
578
|
+
if (configId) {
|
|
579
|
+
document.getElementById(configId)?.classList.remove('hidden');
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
// 条件过滤开关
|
|
584
|
+
document.getElementById('enableCondition')?.addEventListener('change', (e) => {
|
|
585
|
+
const conditionConfig = document.getElementById('conditionConfig');
|
|
586
|
+
if (e.target.checked) {
|
|
587
|
+
conditionConfig.classList.remove('hidden');
|
|
588
|
+
} else {
|
|
589
|
+
conditionConfig.classList.add('hidden');
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
// 创建工作流表单提交
|
|
594
|
+
document.getElementById('createWorkflowForm')?.addEventListener('submit', async (e) => {
|
|
595
|
+
e.preventDefault();
|
|
596
|
+
await createWorkflow(token, container, currentGroup, apiService);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
// 工作流操作按钮
|
|
600
|
+
document.querySelectorAll('.btn-toggle').forEach(btn => {
|
|
601
|
+
btn.addEventListener('click', async () => {
|
|
602
|
+
const workflowId = btn.dataset.id;
|
|
603
|
+
const currentStatus = btn.dataset.status;
|
|
604
|
+
const newStatus = currentStatus === 'active' ? 'inactive' : 'active';
|
|
605
|
+
|
|
606
|
+
try {
|
|
607
|
+
await fetch(`http://localhost:8765/api/workflows/${workflowId}/status`, {
|
|
608
|
+
method: 'PUT',
|
|
609
|
+
headers: {
|
|
610
|
+
'Content-Type': 'application/json',
|
|
611
|
+
'Authorization': `Bearer ${token}`
|
|
612
|
+
},
|
|
613
|
+
body: JSON.stringify({ status: newStatus })
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
alert(`工作流已${newStatus === 'active' ? '启动' : '暂停'}!`);
|
|
617
|
+
renderOptimizedWorkflowView(container, currentGroup, apiService);
|
|
618
|
+
} catch (error) {
|
|
619
|
+
alert('操作失败: ' + error.message);
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
document.querySelectorAll('.btn-delete').forEach(btn => {
|
|
625
|
+
btn.addEventListener('click', async () => {
|
|
626
|
+
if (!confirm('确定要删除此工作流吗?')) return;
|
|
627
|
+
|
|
628
|
+
const workflowId = btn.dataset.id;
|
|
629
|
+
try {
|
|
630
|
+
await fetch(`http://localhost:8765/api/workflows/${workflowId}`, {
|
|
631
|
+
method: 'DELETE',
|
|
632
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
alert('工作流删除成功!');
|
|
636
|
+
renderOptimizedWorkflowView(container, currentGroup, apiService);
|
|
637
|
+
} catch (error) {
|
|
638
|
+
alert('删除失败: ' + error.message);
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// 打开工作流模态框并填充模板
|
|
645
|
+
function openWorkflowModal(template) {
|
|
646
|
+
const modal = document.getElementById('createWorkflowModal');
|
|
647
|
+
modal.classList.remove('hidden');
|
|
648
|
+
|
|
649
|
+
// 根据模板填充表单
|
|
650
|
+
const templates = {
|
|
651
|
+
'document-approval': {
|
|
652
|
+
name: '文档审批流程',
|
|
653
|
+
description: '文档创建后自动通知管理员审批',
|
|
654
|
+
trigger: 'document_create',
|
|
655
|
+
action: 'notification',
|
|
656
|
+
notificationMessage: '新文档待审批',
|
|
657
|
+
notificationTarget: 'admin'
|
|
658
|
+
},
|
|
659
|
+
'task-notification': {
|
|
660
|
+
name: '任务通知流程',
|
|
661
|
+
description: '任务创建时自动通知所有成员',
|
|
662
|
+
trigger: 'task_create',
|
|
663
|
+
action: 'notification',
|
|
664
|
+
notificationMessage: '新任务已分配',
|
|
665
|
+
notificationTarget: 'all'
|
|
666
|
+
},
|
|
667
|
+
'auto-backup': {
|
|
668
|
+
name: '自动备份流程',
|
|
669
|
+
description: '每天凌晨2点自动备份数据',
|
|
670
|
+
trigger: 'scheduled',
|
|
671
|
+
scheduleRule: '0 2 * * *',
|
|
672
|
+
action: 'notification'
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
const templateData = templates[template];
|
|
677
|
+
if (templateData) {
|
|
678
|
+
document.getElementById('workflowName').value = templateData.name;
|
|
679
|
+
document.getElementById('workflowDescription').value = templateData.description;
|
|
680
|
+
document.getElementById('triggerType').value = templateData.trigger;
|
|
681
|
+
document.getElementById('actionType').value = templateData.action;
|
|
682
|
+
|
|
683
|
+
// 触发类型变化事件
|
|
684
|
+
document.getElementById('triggerType').dispatchEvent(new Event('change'));
|
|
685
|
+
document.getElementById('actionType').dispatchEvent(new Event('change'));
|
|
686
|
+
|
|
687
|
+
if (templateData.scheduleRule) {
|
|
688
|
+
document.getElementById('scheduleRule').value = templateData.scheduleRule;
|
|
689
|
+
}
|
|
690
|
+
if (templateData.notificationMessage) {
|
|
691
|
+
document.getElementById('notificationMessage').value = templateData.notificationMessage;
|
|
692
|
+
}
|
|
693
|
+
if (templateData.notificationTarget) {
|
|
694
|
+
document.getElementById('notificationTarget').value = templateData.notificationTarget;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// 创建工作流
|
|
700
|
+
async function createWorkflow(token, container, currentGroup, apiService) {
|
|
701
|
+
const name = document.getElementById('workflowName').value;
|
|
702
|
+
const description = document.getElementById('workflowDescription').value;
|
|
703
|
+
const triggerType = document.getElementById('triggerType').value;
|
|
704
|
+
const actionType = document.getElementById('actionType').value;
|
|
705
|
+
|
|
706
|
+
// 构建触发器配置
|
|
707
|
+
const trigger = {};
|
|
708
|
+
|
|
709
|
+
// 判断触发类型
|
|
710
|
+
if (triggerType === 'scheduled') {
|
|
711
|
+
trigger.type = 'scheduled';
|
|
712
|
+
trigger.schedule = document.getElementById('scheduleRule').value;
|
|
713
|
+
} else {
|
|
714
|
+
trigger.type = 'event';
|
|
715
|
+
trigger.event = triggerType;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// 构建工作流数据
|
|
719
|
+
const workflowData = {
|
|
720
|
+
name,
|
|
721
|
+
description,
|
|
722
|
+
groupId: currentGroup._id,
|
|
723
|
+
trigger: trigger,
|
|
724
|
+
steps: []
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
// 构建动作步骤
|
|
728
|
+
const step = {
|
|
729
|
+
name: actionType === 'notification' ? '发送通知' :
|
|
730
|
+
actionType === 'email' ? '发送邮件' :
|
|
731
|
+
actionType === 'create_task' ? '创建任务' :
|
|
732
|
+
actionType === 'webhook' ? '调用Webhook' : '执行动作',
|
|
733
|
+
type: actionType,
|
|
734
|
+
order: 1,
|
|
735
|
+
config: {}
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
switch (actionType) {
|
|
739
|
+
case 'notification':
|
|
740
|
+
step.config.message = document.getElementById('notificationMessage').value;
|
|
741
|
+
step.config.notificationType = 'system';
|
|
742
|
+
// 根据目标设置接收者(这里简化处理,实际应该获取用户ID)
|
|
743
|
+
step.config.recipients = [];
|
|
744
|
+
break;
|
|
745
|
+
case 'email':
|
|
746
|
+
step.config.notificationType = 'email';
|
|
747
|
+
step.config.message = document.getElementById('emailSubject').value + '\n' + document.getElementById('emailBody').value;
|
|
748
|
+
break;
|
|
749
|
+
case 'create_task':
|
|
750
|
+
step.type = 'assignment';
|
|
751
|
+
step.config.message = document.getElementById('taskTitle').value;
|
|
752
|
+
break;
|
|
753
|
+
case 'webhook':
|
|
754
|
+
step.config.url = document.getElementById('webhookUrl').value;
|
|
755
|
+
step.config.method = 'POST';
|
|
756
|
+
break;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
workflowData.steps.push(step);
|
|
760
|
+
|
|
761
|
+
// 添加条件过滤
|
|
762
|
+
if (document.getElementById('enableCondition').checked) {
|
|
763
|
+
const conditionField = document.getElementById('conditionField').value;
|
|
764
|
+
const conditionValue = document.getElementById('conditionValue').value;
|
|
765
|
+
|
|
766
|
+
if (conditionField && conditionValue) {
|
|
767
|
+
workflowData.trigger.conditions = [{
|
|
768
|
+
field: conditionField,
|
|
769
|
+
operator: 'contains',
|
|
770
|
+
value: conditionValue
|
|
771
|
+
}];
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
console.log('📤 发送工作流数据:', workflowData);
|
|
776
|
+
|
|
777
|
+
try {
|
|
778
|
+
const response = await fetch('http://localhost:8765/api/workflows', {
|
|
779
|
+
method: 'POST',
|
|
780
|
+
headers: {
|
|
781
|
+
'Content-Type': 'application/json',
|
|
782
|
+
'Authorization': `Bearer ${token}`
|
|
783
|
+
},
|
|
784
|
+
body: JSON.stringify(workflowData)
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
const result = await response.json();
|
|
788
|
+
console.log('📥 服务器响应:', result);
|
|
789
|
+
|
|
790
|
+
if (result.success) {
|
|
791
|
+
alert('工作流创建成功!');
|
|
792
|
+
document.getElementById('createWorkflowModal').classList.add('hidden');
|
|
793
|
+
// 重新渲染工作流列表
|
|
794
|
+
await renderOptimizedWorkflowView(container, currentGroup, apiService);
|
|
795
|
+
} else {
|
|
796
|
+
alert('创建失败: ' + (result.error?.message || result.message || '未知错误'));
|
|
797
|
+
}
|
|
798
|
+
} catch (error) {
|
|
799
|
+
console.error('❌ 创建工作流失败:', error);
|
|
800
|
+
alert('创建失败: ' + error.message);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// 导出函数
|
|
805
|
+
export { renderOptimizedWorkflowView };
|
|
806
|
+
|