collabdocchat 2.1.4 → 2.2.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/package.json +1 -1
- package/scripts/add-missing-functions.js +66 -0
- package/scripts/add-more-features.js +427 -0
- package/scripts/add-user-functions.js +201 -0
- package/scripts/check-encoding.js +41 -0
- package/src/pages/admin-dashboard.js +409 -6
- package/src/pages/user-dashboard.js +154 -0
- package/src/utils/onboarding-guide.js +37 -37
- package/src/pages/optimized-backup-view.js +0 -614
- package/src/pages/optimized-knowledge-view.js +0 -797
- package/src/pages/optimized-workflow-view.js +0 -790
|
@@ -1,614 +0,0 @@
|
|
|
1
|
-
// 优化后的备份管理界面
|
|
2
|
-
// 使用卡片式布局,提升视觉效果和用户体验
|
|
3
|
-
|
|
4
|
-
export async function renderOptimizedBackupView(container) {
|
|
5
|
-
try {
|
|
6
|
-
const token = localStorage.getItem('token');
|
|
7
|
-
|
|
8
|
-
// 获取备份列表
|
|
9
|
-
const backupsResponse = await fetch('http://localhost:3000/api/backup/list', {
|
|
10
|
-
headers: { 'Authorization': `Bearer ${token}` }
|
|
11
|
-
});
|
|
12
|
-
const backupsResult = await backupsResponse.json();
|
|
13
|
-
|
|
14
|
-
// 获取备份配置
|
|
15
|
-
const configResponse = await fetch('http://localhost:3000/api/backup/config', {
|
|
16
|
-
headers: { 'Authorization': `Bearer ${token}` }
|
|
17
|
-
});
|
|
18
|
-
const configResult = await configResponse.json();
|
|
19
|
-
|
|
20
|
-
const backups = backupsResult.data?.backups || [];
|
|
21
|
-
const config = configResult.data?.config || {};
|
|
22
|
-
|
|
23
|
-
container.innerHTML = `
|
|
24
|
-
<div class="view-header">
|
|
25
|
-
<h2>💾 备份管理</h2>
|
|
26
|
-
<div style="display: flex; gap: 10px;">
|
|
27
|
-
<button class="btn-primary" id="createBackupBtn">
|
|
28
|
-
<span style="font-size: 18px;">�?/span> 立即备份
|
|
29
|
-
</button>
|
|
30
|
-
<button class="btn-secondary" id="configBackupBtn">
|
|
31
|
-
<span style="font-size: 18px;">⚙️</span> 备份设置
|
|
32
|
-
</button>
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
|
|
36
|
-
<!-- 统计卡片区域 -->
|
|
37
|
-
<div class="backup-stats-grid">
|
|
38
|
-
<div class="stat-card-modern">
|
|
39
|
-
<div class="stat-icon" style="background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);">
|
|
40
|
-
📊
|
|
41
|
-
</div>
|
|
42
|
-
<div class="stat-content">
|
|
43
|
-
<div class="stat-label">备份总数</div>
|
|
44
|
-
<div class="stat-value">${backups.length}</div>
|
|
45
|
-
</div>
|
|
46
|
-
</div>
|
|
47
|
-
|
|
48
|
-
<div class="stat-card-modern">
|
|
49
|
-
<div class="stat-icon" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%);">
|
|
50
|
-
${config.autoBackup ? '�? : '�?}
|
|
51
|
-
</div>
|
|
52
|
-
<div class="stat-content">
|
|
53
|
-
<div class="stat-label">自动备份</div>
|
|
54
|
-
<div class="stat-value">${config.autoBackup ? '已启�? : '已禁�?}</div>
|
|
55
|
-
</div>
|
|
56
|
-
</div>
|
|
57
|
-
|
|
58
|
-
<div class="stat-card-modern">
|
|
59
|
-
<div class="stat-icon" style="background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);">
|
|
60
|
-
🕐
|
|
61
|
-
</div>
|
|
62
|
-
<div class="stat-content">
|
|
63
|
-
<div class="stat-label">备份频率</div>
|
|
64
|
-
<div class="stat-value">${config.schedule || '未设�?}</div>
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
67
|
-
|
|
68
|
-
<div class="stat-card-modern">
|
|
69
|
-
<div class="stat-icon" style="background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);">
|
|
70
|
-
📅
|
|
71
|
-
</div>
|
|
72
|
-
<div class="stat-content">
|
|
73
|
-
<div class="stat-label">保留天数</div>
|
|
74
|
-
<div class="stat-value">${config.retention || 30} �?/div>
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
77
|
-
</div>
|
|
78
|
-
|
|
79
|
-
<!-- 备份列表 -->
|
|
80
|
-
<div class="backup-list-section">
|
|
81
|
-
<h3 style="margin: 30px 0 20px; color: var(--text-primary); font-size: 20px;">
|
|
82
|
-
📦 最近备�? </h3>
|
|
83
|
-
<div class="backup-cards-grid" id="backupCards">
|
|
84
|
-
${backups.length === 0 ? `
|
|
85
|
-
<div class="empty-state-modern">
|
|
86
|
-
<div class="empty-icon">📭</div>
|
|
87
|
-
<h3>暂无备份记录</h3>
|
|
88
|
-
<p>点击"立即备份"创建第一个备�?/p>
|
|
89
|
-
<button class="btn-primary" onclick="document.getElementById('createBackupBtn').click()">
|
|
90
|
-
立即备份
|
|
91
|
-
</button>
|
|
92
|
-
</div>
|
|
93
|
-
` : backups.map(backup => createBackupCard(backup)).join('')}
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
|
|
97
|
-
<!-- 模态框保持不变 -->
|
|
98
|
-
${createBackupModals(config)}
|
|
99
|
-
`;
|
|
100
|
-
|
|
101
|
-
// 添加样式
|
|
102
|
-
addBackupStyles();
|
|
103
|
-
|
|
104
|
-
// 绑定事件
|
|
105
|
-
setupBackupEvents(token, container);
|
|
106
|
-
|
|
107
|
-
} catch (error) {
|
|
108
|
-
console.error('加载备份管理失败:', error);
|
|
109
|
-
container.innerHTML = `
|
|
110
|
-
<div class="view-header">
|
|
111
|
-
<h2>💾 备份管理</h2>
|
|
112
|
-
</div>
|
|
113
|
-
<div class="empty-state">加载失败: ${error.message}</div>
|
|
114
|
-
`;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// 创建备份卡片
|
|
119
|
-
function createBackupCard(backup) {
|
|
120
|
-
const typeConfig = {
|
|
121
|
-
manual: { icon: '📦', label: '手动备份', color: '#6366f1' },
|
|
122
|
-
scheduled: { icon: '🔄', label: '定时备份', color: '#10b981' },
|
|
123
|
-
auto: { icon: '�?, label: '自动备份', color: '#f59e0b' }
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const config = typeConfig[backup.type] || typeConfig.manual;
|
|
127
|
-
const statusIcon = backup.status === 'completed' ? '�? : backup.status === 'failed' ? '�? : '�?;
|
|
128
|
-
|
|
129
|
-
return `
|
|
130
|
-
<div class="backup-card-modern">
|
|
131
|
-
<div class="backup-card-header">
|
|
132
|
-
<div class="backup-type-icon" style="background: ${config.color}20; color: ${config.color};">
|
|
133
|
-
${config.icon}
|
|
134
|
-
</div>
|
|
135
|
-
<div class="backup-card-title">
|
|
136
|
-
<h4>${config.label}</h4>
|
|
137
|
-
<span class="backup-time">${new Date(backup.createdAt).toLocaleString()}</span>
|
|
138
|
-
</div>
|
|
139
|
-
<div class="backup-status-icon">${statusIcon}</div>
|
|
140
|
-
</div>
|
|
141
|
-
|
|
142
|
-
<div class="backup-card-body">
|
|
143
|
-
<div class="backup-info-row">
|
|
144
|
-
<span class="info-label">大小</span>
|
|
145
|
-
<span class="info-value">${formatFileSize(backup.size)}</span>
|
|
146
|
-
</div>
|
|
147
|
-
<div class="backup-info-row">
|
|
148
|
-
<span class="info-label">状�?/span>
|
|
149
|
-
<span class="info-value">${backup.status === 'completed' ? '完成' : backup.status === 'failed' ? '失败' : '进行�?}</span>
|
|
150
|
-
</div>
|
|
151
|
-
</div>
|
|
152
|
-
|
|
153
|
-
${backup.status === 'completed' ? `
|
|
154
|
-
<div class="backup-card-actions">
|
|
155
|
-
<button class="btn-action btn-download" data-id="${backup._id}" data-action="download">
|
|
156
|
-
<span>⬇️</span> 下载
|
|
157
|
-
</button>
|
|
158
|
-
<button class="btn-action btn-restore" data-id="${backup._id}" data-action="restore">
|
|
159
|
-
<span>🔄</span> 恢复
|
|
160
|
-
</button>
|
|
161
|
-
<button class="btn-action btn-delete" data-id="${backup._id}" data-action="delete">
|
|
162
|
-
<span>🗑�?/span> 删除
|
|
163
|
-
</button>
|
|
164
|
-
</div>
|
|
165
|
-
` : `
|
|
166
|
-
<div class="backup-card-actions">
|
|
167
|
-
<button class="btn-action btn-delete" data-id="${backup._id}" data-action="delete">
|
|
168
|
-
<span>🗑�?/span> 删除
|
|
169
|
-
</button>
|
|
170
|
-
</div>
|
|
171
|
-
`}
|
|
172
|
-
</div>
|
|
173
|
-
`;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// 创建模态框HTML
|
|
177
|
-
function createBackupModals(config) {
|
|
178
|
-
return `
|
|
179
|
-
<!-- 创建备份模态框 -->
|
|
180
|
-
<div class="modal hidden" id="createBackupModal">
|
|
181
|
-
<div class="modal-content">
|
|
182
|
-
<div class="modal-header">
|
|
183
|
-
<h3>创建备份</h3>
|
|
184
|
-
<button class="close-btn" id="closeCreateBackup">×</button>
|
|
185
|
-
</div>
|
|
186
|
-
<form id="createBackupForm">
|
|
187
|
-
<div class="form-group">
|
|
188
|
-
<label>备份类型</label>
|
|
189
|
-
<select id="backupType" required>
|
|
190
|
-
<option value="full">完整备份(所有数据)</option>
|
|
191
|
-
<option value="incremental">增量备份(仅变更�?/option>
|
|
192
|
-
</select>
|
|
193
|
-
</div>
|
|
194
|
-
<div class="form-group">
|
|
195
|
-
<label>备份说明(可选)</label>
|
|
196
|
-
<textarea id="backupDescription" rows="3" placeholder="备份说明..."></textarea>
|
|
197
|
-
</div>
|
|
198
|
-
<div style="display: flex; gap: 10px; margin-top: 20px;">
|
|
199
|
-
<button type="submit" class="btn-primary">开始备�?/button>
|
|
200
|
-
<button type="button" class="btn-secondary" id="cancelCreateBackup">取消</button>
|
|
201
|
-
</div>
|
|
202
|
-
</form>
|
|
203
|
-
</div>
|
|
204
|
-
</div>
|
|
205
|
-
|
|
206
|
-
<!-- 备份配置模态框 -->
|
|
207
|
-
<div class="modal hidden" id="configBackupModal">
|
|
208
|
-
<div class="modal-content">
|
|
209
|
-
<div class="modal-header">
|
|
210
|
-
<h3>备份设置</h3>
|
|
211
|
-
<button class="close-btn" id="closeConfigBackup">×</button>
|
|
212
|
-
</div>
|
|
213
|
-
<form id="configBackupForm">
|
|
214
|
-
<div class="form-group">
|
|
215
|
-
<label>
|
|
216
|
-
<input type="checkbox" id="autoBackup" ${config.autoBackup ? 'checked' : ''}>
|
|
217
|
-
启用自动备份
|
|
218
|
-
</label>
|
|
219
|
-
</div>
|
|
220
|
-
<div class="form-group">
|
|
221
|
-
<label>备份频率(Cron表达式)</label>
|
|
222
|
-
<input type="text" id="backupSchedule" value="${config.schedule || '0 2 * * *'}" placeholder="0 2 * * * (每天凌晨2�?">
|
|
223
|
-
<small>示例�? 2 * * * (每天凌晨2�?�? */6 * * * (�?小时)</small>
|
|
224
|
-
</div>
|
|
225
|
-
<div class="form-group">
|
|
226
|
-
<label>保留天数</label>
|
|
227
|
-
<input type="number" id="backupRetention" value="${config.retention || 30}" min="1" max="365">
|
|
228
|
-
<small>超过此天数的备份将自动删�?/small>
|
|
229
|
-
</div>
|
|
230
|
-
<div class="form-group">
|
|
231
|
-
<label>最大备份数�?/label>
|
|
232
|
-
<input type="number" id="maxBackups" value="${config.maxBackups || 10}" min="1" max="100">
|
|
233
|
-
</div>
|
|
234
|
-
<div style="display: flex; gap: 10px; margin-top: 20px;">
|
|
235
|
-
<button type="submit" class="btn-primary">保存设置</button>
|
|
236
|
-
<button type="button" class="btn-secondary" id="cancelConfigBackup">取消</button>
|
|
237
|
-
</div>
|
|
238
|
-
</form>
|
|
239
|
-
</div>
|
|
240
|
-
</div>
|
|
241
|
-
`;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// 添加样式
|
|
245
|
-
function addBackupStyles() {
|
|
246
|
-
if (document.getElementById('backup-modern-styles')) return;
|
|
247
|
-
|
|
248
|
-
const style = document.createElement('style');
|
|
249
|
-
style.id = 'backup-modern-styles';
|
|
250
|
-
style.textContent = `
|
|
251
|
-
.backup-stats-grid {
|
|
252
|
-
display: grid;
|
|
253
|
-
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
254
|
-
gap: 20px;
|
|
255
|
-
margin: 20px 0 30px;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
.stat-card-modern {
|
|
259
|
-
background: linear-gradient(135deg, var(--bg-card) 0%, rgba(99,102,241,0.05) 100%);
|
|
260
|
-
border: 1px solid var(--border);
|
|
261
|
-
border-radius: 16px;
|
|
262
|
-
padding: 20px;
|
|
263
|
-
display: flex;
|
|
264
|
-
align-items: center;
|
|
265
|
-
gap: 16px;
|
|
266
|
-
transition: all 0.3s ease;
|
|
267
|
-
animation: fadeInUp 0.5s ease;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
.stat-card-modern:hover {
|
|
271
|
-
transform: translateY(-5px);
|
|
272
|
-
box-shadow: 0 12px 32px rgba(99,102,241,0.2);
|
|
273
|
-
border-color: var(--primary);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
.stat-icon {
|
|
277
|
-
width: 60px;
|
|
278
|
-
height: 60px;
|
|
279
|
-
border-radius: 12px;
|
|
280
|
-
display: flex;
|
|
281
|
-
align-items: center;
|
|
282
|
-
justify-content: center;
|
|
283
|
-
font-size: 28px;
|
|
284
|
-
flex-shrink: 0;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
.stat-content {
|
|
288
|
-
flex: 1;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
.stat-label {
|
|
292
|
-
font-size: 13px;
|
|
293
|
-
color: var(--text-secondary);
|
|
294
|
-
margin-bottom: 6px;
|
|
295
|
-
font-weight: 600;
|
|
296
|
-
text-transform: uppercase;
|
|
297
|
-
letter-spacing: 0.5px;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
.stat-value {
|
|
301
|
-
font-size: 24px;
|
|
302
|
-
font-weight: 800;
|
|
303
|
-
color: var(--text-primary);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
.backup-cards-grid {
|
|
307
|
-
display: grid;
|
|
308
|
-
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
|
309
|
-
gap: 20px;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
.backup-card-modern {
|
|
313
|
-
background: var(--bg-card);
|
|
314
|
-
border: 1px solid var(--border);
|
|
315
|
-
border-radius: 16px;
|
|
316
|
-
padding: 20px;
|
|
317
|
-
transition: all 0.3s ease;
|
|
318
|
-
animation: fadeInUp 0.5s ease;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
.backup-card-modern:hover {
|
|
322
|
-
transform: translateY(-5px);
|
|
323
|
-
box-shadow: 0 8px 24px rgba(99,102,241,0.15);
|
|
324
|
-
border-color: var(--primary);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
.backup-card-header {
|
|
328
|
-
display: flex;
|
|
329
|
-
align-items: center;
|
|
330
|
-
gap: 12px;
|
|
331
|
-
margin-bottom: 16px;
|
|
332
|
-
padding-bottom: 16px;
|
|
333
|
-
border-bottom: 1px solid var(--border);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
.backup-type-icon {
|
|
337
|
-
width: 48px;
|
|
338
|
-
height: 48px;
|
|
339
|
-
border-radius: 12px;
|
|
340
|
-
display: flex;
|
|
341
|
-
align-items: center;
|
|
342
|
-
justify-content: center;
|
|
343
|
-
font-size: 24px;
|
|
344
|
-
flex-shrink: 0;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
.backup-card-title {
|
|
348
|
-
flex: 1;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.backup-card-title h4 {
|
|
352
|
-
margin: 0 0 4px;
|
|
353
|
-
font-size: 16px;
|
|
354
|
-
color: var(--text-primary);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
.backup-time {
|
|
358
|
-
font-size: 12px;
|
|
359
|
-
color: var(--text-secondary);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
.backup-status-icon {
|
|
363
|
-
font-size: 24px;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
.backup-card-body {
|
|
367
|
-
margin-bottom: 16px;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
.backup-info-row {
|
|
371
|
-
display: flex;
|
|
372
|
-
justify-content: space-between;
|
|
373
|
-
padding: 8px 0;
|
|
374
|
-
font-size: 14px;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
.info-label {
|
|
378
|
-
color: var(--text-secondary);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
.info-value {
|
|
382
|
-
color: var(--text-primary);
|
|
383
|
-
font-weight: 600;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
.backup-card-actions {
|
|
387
|
-
display: flex;
|
|
388
|
-
gap: 8px;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
.btn-action {
|
|
392
|
-
flex: 1;
|
|
393
|
-
padding: 10px;
|
|
394
|
-
border: none;
|
|
395
|
-
border-radius: 8px;
|
|
396
|
-
font-size: 13px;
|
|
397
|
-
font-weight: 600;
|
|
398
|
-
cursor: pointer;
|
|
399
|
-
transition: all 0.3s ease;
|
|
400
|
-
display: flex;
|
|
401
|
-
align-items: center;
|
|
402
|
-
justify-content: center;
|
|
403
|
-
gap: 6px;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
.btn-download {
|
|
407
|
-
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
|
408
|
-
color: white;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
.btn-restore {
|
|
412
|
-
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
|
413
|
-
color: white;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
.btn-delete {
|
|
417
|
-
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
|
418
|
-
color: white;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
.btn-action:hover {
|
|
422
|
-
transform: translateY(-2px);
|
|
423
|
-
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
.empty-state-modern {
|
|
427
|
-
grid-column: 1 / -1;
|
|
428
|
-
text-align: center;
|
|
429
|
-
padding: 60px 20px;
|
|
430
|
-
background: linear-gradient(135deg, var(--bg-dark) 0%, rgba(99,102,241,0.03) 100%);
|
|
431
|
-
border-radius: 16px;
|
|
432
|
-
border: 2px dashed var(--border);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
.empty-icon {
|
|
436
|
-
font-size: 64px;
|
|
437
|
-
margin-bottom: 20px;
|
|
438
|
-
animation: bounce 2s infinite;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
.empty-state-modern h3 {
|
|
442
|
-
margin: 0 0 10px;
|
|
443
|
-
color: var(--text-primary);
|
|
444
|
-
font-size: 20px;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
.empty-state-modern p {
|
|
448
|
-
margin: 0 0 20px;
|
|
449
|
-
color: var(--text-secondary);
|
|
450
|
-
font-size: 14px;
|
|
451
|
-
}
|
|
452
|
-
`;
|
|
453
|
-
document.head.appendChild(style);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// 设置事件监听
|
|
457
|
-
function setupBackupEvents(token, container) {
|
|
458
|
-
// 创建备份按钮
|
|
459
|
-
document.getElementById('createBackupBtn')?.addEventListener('click', () => {
|
|
460
|
-
document.getElementById('createBackupModal').classList.remove('hidden');
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
// 配置备份按钮
|
|
464
|
-
document.getElementById('configBackupBtn')?.addEventListener('click', () => {
|
|
465
|
-
document.getElementById('configBackupModal').classList.remove('hidden');
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
// 关闭按钮
|
|
469
|
-
document.getElementById('closeCreateBackup')?.addEventListener('click', () => {
|
|
470
|
-
document.getElementById('createBackupModal').classList.add('hidden');
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
document.getElementById('cancelCreateBackup')?.addEventListener('click', () => {
|
|
474
|
-
document.getElementById('createBackupModal').classList.add('hidden');
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
document.getElementById('closeConfigBackup')?.addEventListener('click', () => {
|
|
478
|
-
document.getElementById('configBackupModal').classList.add('hidden');
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
document.getElementById('cancelConfigBackup')?.addEventListener('click', () => {
|
|
482
|
-
document.getElementById('configBackupModal').classList.add('hidden');
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
// 创建备份表单提交
|
|
486
|
-
document.getElementById('createBackupForm')?.addEventListener('submit', async (e) => {
|
|
487
|
-
e.preventDefault();
|
|
488
|
-
const type = document.getElementById('backupType').value;
|
|
489
|
-
const description = document.getElementById('backupDescription').value;
|
|
490
|
-
|
|
491
|
-
try {
|
|
492
|
-
const response = await fetch('http://localhost:3000/api/backup/create', {
|
|
493
|
-
method: 'POST',
|
|
494
|
-
headers: {
|
|
495
|
-
'Content-Type': 'application/json',
|
|
496
|
-
'Authorization': `Bearer ${token}`
|
|
497
|
-
},
|
|
498
|
-
body: JSON.stringify({ type, description })
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
const result = await response.json();
|
|
502
|
-
if (result.success) {
|
|
503
|
-
alert('备份创建成功�?);
|
|
504
|
-
document.getElementById('createBackupModal').classList.add('hidden');
|
|
505
|
-
renderOptimizedBackupView(container);
|
|
506
|
-
} else {
|
|
507
|
-
alert('备份失败: ' + (result.error?.message || '未知错误'));
|
|
508
|
-
}
|
|
509
|
-
} catch (error) {
|
|
510
|
-
alert('备份失败: ' + error.message);
|
|
511
|
-
}
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
// 配置备份表单提交
|
|
515
|
-
document.getElementById('configBackupForm')?.addEventListener('submit', async (e) => {
|
|
516
|
-
e.preventDefault();
|
|
517
|
-
const config = {
|
|
518
|
-
autoBackup: document.getElementById('autoBackup').checked,
|
|
519
|
-
schedule: document.getElementById('backupSchedule').value,
|
|
520
|
-
retention: parseInt(document.getElementById('backupRetention').value),
|
|
521
|
-
maxBackups: parseInt(document.getElementById('maxBackups').value)
|
|
522
|
-
};
|
|
523
|
-
|
|
524
|
-
try {
|
|
525
|
-
const response = await fetch('http://localhost:3000/api/backup/config', {
|
|
526
|
-
method: 'PUT',
|
|
527
|
-
headers: {
|
|
528
|
-
'Content-Type': 'application/json',
|
|
529
|
-
'Authorization': `Bearer ${token}`
|
|
530
|
-
},
|
|
531
|
-
body: JSON.stringify(config)
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
const result = await response.json();
|
|
535
|
-
if (result.success) {
|
|
536
|
-
alert('设置保存成功�?);
|
|
537
|
-
document.getElementById('configBackupModal').classList.add('hidden');
|
|
538
|
-
renderOptimizedBackupView(container);
|
|
539
|
-
} else {
|
|
540
|
-
alert('保存失败: ' + (result.error?.message || '未知错误'));
|
|
541
|
-
}
|
|
542
|
-
} catch (error) {
|
|
543
|
-
alert('保存失败: ' + error.message);
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
// 备份操作按钮
|
|
548
|
-
document.querySelectorAll('[data-action="download"]').forEach(btn => {
|
|
549
|
-
btn.addEventListener('click', () => {
|
|
550
|
-
const backupId = btn.dataset.id;
|
|
551
|
-
window.location.href = `http://localhost:3000/api/backup/${backupId}/download?token=${token}`;
|
|
552
|
-
});
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
document.querySelectorAll('[data-action="restore"]').forEach(btn => {
|
|
556
|
-
btn.addEventListener('click', async () => {
|
|
557
|
-
if (!confirm('确定要恢复此备份吗?这将覆盖当前数据�?)) return;
|
|
558
|
-
|
|
559
|
-
const backupId = btn.dataset.id;
|
|
560
|
-
try {
|
|
561
|
-
const response = await fetch(`http://localhost:3000/api/backup/${backupId}/restore`, {
|
|
562
|
-
method: 'POST',
|
|
563
|
-
headers: { 'Authorization': `Bearer ${token}` }
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
const result = await response.json();
|
|
567
|
-
if (result.success) {
|
|
568
|
-
alert('备份恢复成功!页面将刷新...');
|
|
569
|
-
setTimeout(() => window.location.reload(), 2000);
|
|
570
|
-
} else {
|
|
571
|
-
alert('恢复失败: ' + (result.error?.message || '未知错误'));
|
|
572
|
-
}
|
|
573
|
-
} catch (error) {
|
|
574
|
-
alert('恢复失败: ' + error.message);
|
|
575
|
-
}
|
|
576
|
-
});
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
document.querySelectorAll('[data-action="delete"]').forEach(btn => {
|
|
580
|
-
btn.addEventListener('click', async () => {
|
|
581
|
-
if (!confirm('确定要删除此备份吗?')) return;
|
|
582
|
-
|
|
583
|
-
const backupId = btn.dataset.id;
|
|
584
|
-
try {
|
|
585
|
-
const response = await fetch(`http://localhost:3000/api/backup/${backupId}`, {
|
|
586
|
-
method: 'DELETE',
|
|
587
|
-
headers: { 'Authorization': `Bearer ${token}` }
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
const result = await response.json();
|
|
591
|
-
if (result.success) {
|
|
592
|
-
alert('备份删除成功�?);
|
|
593
|
-
renderOptimizedBackupView(container);
|
|
594
|
-
} else {
|
|
595
|
-
alert('删除失败: ' + (result.error?.message || '未知错误'));
|
|
596
|
-
}
|
|
597
|
-
} catch (error) {
|
|
598
|
-
alert('删除失败: ' + error.message);
|
|
599
|
-
}
|
|
600
|
-
});
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
// 格式化文件大�?function formatFileSize(bytes) {
|
|
605
|
-
if (!bytes) return '0 B';
|
|
606
|
-
const k = 1024;
|
|
607
|
-
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
608
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
609
|
-
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
// 导出函数供其他模块使�?export { renderOptimizedBackupView };
|
|
613
|
-
|
|
614
|
-
|