|
|
@@ -12,7 +12,7 @@
|
|
12
|
12
|
<el-icon class="combined-icon"><Files /></el-icon>
|
|
13
|
13
|
<div>
|
|
14
|
14
|
<div class="combined-title">一键全量导入</div>
|
|
15
|
|
- <div class="combined-desc">上传「旅检三部"三三"数字管理平台.xlsx」,系统将自动识别15个Sheet并分别导入对应台账表</div>
|
|
|
15
|
+ <div class="combined-desc">上传「旅检三部"三三"数字管理平台.xlsx」,系统将自动识别20个Sheet并分别导入对应台账表</div>
|
|
16
|
16
|
</div>
|
|
17
|
17
|
</div>
|
|
18
|
18
|
<el-upload
|
|
|
@@ -39,6 +39,48 @@
|
|
39
|
39
|
</div>
|
|
40
|
40
|
</el-card>
|
|
41
|
41
|
|
|
|
42
|
+ <!-- 按时间范围清理台账数据 -->
|
|
|
43
|
+ <el-card class="clear-card" shadow="never">
|
|
|
44
|
+ <div class="clear-inner">
|
|
|
45
|
+ <div class="clear-info">
|
|
|
46
|
+ <el-icon class="clear-icon"><DeleteFilled /></el-icon>
|
|
|
47
|
+ <div>
|
|
|
48
|
+ <div class="clear-title">清理台账数据</div>
|
|
|
49
|
+ <div class="clear-desc">按导入时间范围删除全部20张台账表数据,同步清除台账来源的配分事项(手动录入不受影响)</div>
|
|
|
50
|
+ </div>
|
|
|
51
|
+ </div>
|
|
|
52
|
+ <div class="clear-actions">
|
|
|
53
|
+ <el-date-picker
|
|
|
54
|
+ v-model="clearBeginDate"
|
|
|
55
|
+ type="date"
|
|
|
56
|
+ value-format="YYYY-MM-DD"
|
|
|
57
|
+ placeholder="开始日期"
|
|
|
58
|
+ style="width: 150px"
|
|
|
59
|
+ />
|
|
|
60
|
+ <span style="margin: 0 6px; color: #909399;">至</span>
|
|
|
61
|
+ <el-date-picker
|
|
|
62
|
+ v-model="clearEndDate"
|
|
|
63
|
+ type="date"
|
|
|
64
|
+ value-format="YYYY-MM-DD"
|
|
|
65
|
+ placeholder="结束日期"
|
|
|
66
|
+ :disabled-date="(d) => clearBeginDate && d < new Date(clearBeginDate)"
|
|
|
67
|
+ style="width: 150px"
|
|
|
68
|
+ />
|
|
|
69
|
+ <el-button type="danger" :loading="clearLoading" :disabled="!clearBeginDate || !clearEndDate" @click="handleClear">
|
|
|
70
|
+ <el-icon><Delete /></el-icon> 清理
|
|
|
71
|
+ </el-button>
|
|
|
72
|
+ </div>
|
|
|
73
|
+ </div>
|
|
|
74
|
+ <div v-if="clearResult" class="clear-result">
|
|
|
75
|
+ <div class="result-title">清理结果(按导入时间 {{ clearBeginDate }} ~ {{ clearEndDate }}):</div>
|
|
|
76
|
+ <div class="result-table">
|
|
|
77
|
+ <span v-for="(count, name) in clearResult" :key="name" class="result-item">
|
|
|
78
|
+ <el-tag :type="count > 0 ? 'warning' : 'info'" size="small">{{ name }}:{{ count }} 条</el-tag>
|
|
|
79
|
+ </span>
|
|
|
80
|
+ </div>
|
|
|
81
|
+ </div>
|
|
|
82
|
+ </el-card>
|
|
|
83
|
+
|
|
42
|
84
|
<el-divider content-position="left">或按类型单独导入</el-divider>
|
|
43
|
85
|
|
|
44
|
86
|
<div class="import-grid">
|
|
|
@@ -79,9 +121,9 @@
|
|
79
|
121
|
|
|
80
|
122
|
<script setup>
|
|
81
|
123
|
import { ref, reactive } from 'vue'
|
|
82
|
|
-import { ElMessage } from 'element-plus'
|
|
83
|
|
-import { Upload, Download, Document, DocumentChecked, Warning, Trophy, UserFilled, Ticket, DataAnalysis, Histogram, Medal, Memo, Money, Calendar, Flag, Files } from '@element-plus/icons-vue'
|
|
84
|
|
-import { importCombinedLedger } from '@/api/ledger/index'
|
|
|
124
|
+import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
125
|
+import { Upload, Download, Document, DocumentChecked, Warning, Trophy, UserFilled, Ticket, DataAnalysis, Histogram, Medal, Memo, Money, Calendar, Flag, Files, Reading, Management, FirstAidKit, House, Bell, Delete, DeleteFilled } from '@element-plus/icons-vue'
|
|
|
126
|
+import { importCombinedLedger, clearLedgerByTimeRange } from '@/api/ledger/index'
|
|
85
|
127
|
import {
|
|
86
|
128
|
importSupervisionProblem,
|
|
87
|
129
|
importPatrolInspection,
|
|
|
@@ -94,7 +136,12 @@ import {
|
|
94
|
136
|
importSeizureStats,
|
|
95
|
137
|
importTerminalBonus,
|
|
96
|
138
|
importExamScore,
|
|
97
|
|
- importRewardApproval
|
|
|
139
|
+ importRewardApproval,
|
|
|
140
|
+ importDailyTraining,
|
|
|
141
|
+ importLeaderDuty,
|
|
|
142
|
+ importHealthSoldier,
|
|
|
143
|
+ importDormFireSafety,
|
|
|
144
|
+ importTrainingIssue
|
|
98
|
145
|
} from '@/api/ledger/index'
|
|
99
|
146
|
|
|
100
|
147
|
defineOptions({ name: 'LedgerImport' })
|
|
|
@@ -103,6 +150,49 @@ defineOptions({ name: 'LedgerImport' })
|
|
103
|
150
|
const combinedLoading = ref(false)
|
|
104
|
151
|
const combinedResult = ref(null)
|
|
105
|
152
|
|
|
|
153
|
+// ── 台账数据清理 ──────────────────────────────────────
|
|
|
154
|
+const clearBeginDate = ref('')
|
|
|
155
|
+const clearEndDate = ref('')
|
|
|
156
|
+const clearLoading = ref(false)
|
|
|
157
|
+const clearResult = ref(null)
|
|
|
158
|
+
|
|
|
159
|
+async function handleClear() {
|
|
|
160
|
+ if (!clearBeginDate.value || !clearEndDate.value) {
|
|
|
161
|
+ ElMessage.warning('请先选择清理时间范围')
|
|
|
162
|
+ return
|
|
|
163
|
+ }
|
|
|
164
|
+ if (clearBeginDate.value > clearEndDate.value) {
|
|
|
165
|
+ ElMessage.warning('开始日期不能晚于结束日期')
|
|
|
166
|
+ return
|
|
|
167
|
+ }
|
|
|
168
|
+ const beginTime = clearBeginDate.value
|
|
|
169
|
+ const endTime = clearEndDate.value
|
|
|
170
|
+ try {
|
|
|
171
|
+ await ElMessageBox.confirm(
|
|
|
172
|
+ `确认删除导入时间在 ${beginTime} ~ ${endTime} 之间的全部台账数据?\n此操作不可恢复,请谨慎操作!`,
|
|
|
173
|
+ '危险操作确认',
|
|
|
174
|
+ { type: 'warning', confirmButtonText: '确认清理', cancelButtonText: '取消', confirmButtonClass: 'el-button--danger' }
|
|
|
175
|
+ )
|
|
|
176
|
+ } catch {
|
|
|
177
|
+ return
|
|
|
178
|
+ }
|
|
|
179
|
+ clearLoading.value = true
|
|
|
180
|
+ clearResult.value = null
|
|
|
181
|
+ try {
|
|
|
182
|
+ const res = await clearLedgerByTimeRange({ beginTime, endTime })
|
|
|
183
|
+ if (res.code === 200) {
|
|
|
184
|
+ clearResult.value = res.data || {}
|
|
|
185
|
+ ElMessage.success(res.msg || '清理完成')
|
|
|
186
|
+ } else {
|
|
|
187
|
+ ElMessage.error(res.msg || '清理失败')
|
|
|
188
|
+ }
|
|
|
189
|
+ } catch (e) {
|
|
|
190
|
+ ElMessage.error('清理失败,请查看后端日志')
|
|
|
191
|
+ } finally {
|
|
|
192
|
+ clearLoading.value = false
|
|
|
193
|
+ }
|
|
|
194
|
+}
|
|
|
195
|
+
|
|
106
|
196
|
async function handleCombinedChange(uploadFile) {
|
|
107
|
197
|
const file = uploadFile.raw
|
|
108
|
198
|
if (!file) return
|
|
|
@@ -238,6 +328,51 @@ const importItems = reactive([
|
|
238
|
328
|
api: importRewardApproval,
|
|
239
|
329
|
loading: false,
|
|
240
|
330
|
lastResult: null
|
|
|
331
|
+ },
|
|
|
332
|
+ {
|
|
|
333
|
+ key: 'dailyTraining',
|
|
|
334
|
+ title: '日常培训记录',
|
|
|
335
|
+ desc: '月度培训任务及完成情况记录',
|
|
|
336
|
+ icon: 'Reading',
|
|
|
337
|
+ api: importDailyTraining,
|
|
|
338
|
+ loading: false,
|
|
|
339
|
+ lastResult: null
|
|
|
340
|
+ },
|
|
|
341
|
+ {
|
|
|
342
|
+ key: 'leaderDuty',
|
|
|
343
|
+ title: '组长履职情况记录',
|
|
|
344
|
+ desc: '队室组长本班点评及问题处置',
|
|
|
345
|
+ icon: 'Management',
|
|
|
346
|
+ api: importLeaderDuty,
|
|
|
347
|
+ loading: false,
|
|
|
348
|
+ lastResult: null
|
|
|
349
|
+ },
|
|
|
350
|
+ {
|
|
|
351
|
+ key: 'healthSoldier',
|
|
|
352
|
+ title: '健康锐兵',
|
|
|
353
|
+ desc: '员工身心健康状况台账记录',
|
|
|
354
|
+ icon: 'FirstAidKit',
|
|
|
355
|
+ api: importHealthSoldier,
|
|
|
356
|
+ loading: false,
|
|
|
357
|
+ lastResult: null
|
|
|
358
|
+ },
|
|
|
359
|
+ {
|
|
|
360
|
+ key: 'dormFireSafety',
|
|
|
361
|
+ title: '宿舍消防安全专项自查',
|
|
|
362
|
+ desc: '宿舍消防隐患自查情况记录',
|
|
|
363
|
+ icon: 'House',
|
|
|
364
|
+ api: importDormFireSafety,
|
|
|
365
|
+ loading: false,
|
|
|
366
|
+ lastResult: null
|
|
|
367
|
+ },
|
|
|
368
|
+ {
|
|
|
369
|
+ key: 'trainingIssue',
|
|
|
370
|
+ title: '培训台账问题通报',
|
|
|
371
|
+ desc: '培训台账发现问题及整改通报',
|
|
|
372
|
+ icon: 'Bell',
|
|
|
373
|
+ api: importTrainingIssue,
|
|
|
374
|
+ loading: false,
|
|
|
375
|
+ lastResult: null
|
|
241
|
376
|
}
|
|
242
|
377
|
])
|
|
243
|
378
|
|
|
|
@@ -373,6 +508,72 @@ function downloadTemplate(item) {
|
|
373
|
508
|
}
|
|
374
|
509
|
}
|
|
375
|
510
|
|
|
|
511
|
+.clear-card {
|
|
|
512
|
+ margin-bottom: 16px;
|
|
|
513
|
+ border: 1px solid #fde2e2;
|
|
|
514
|
+ background: #fff8f8;
|
|
|
515
|
+
|
|
|
516
|
+ .clear-inner {
|
|
|
517
|
+ display: flex;
|
|
|
518
|
+ align-items: center;
|
|
|
519
|
+ justify-content: space-between;
|
|
|
520
|
+ gap: 16px;
|
|
|
521
|
+ flex-wrap: wrap;
|
|
|
522
|
+ }
|
|
|
523
|
+
|
|
|
524
|
+ .clear-info {
|
|
|
525
|
+ display: flex;
|
|
|
526
|
+ align-items: flex-start;
|
|
|
527
|
+ gap: 12px;
|
|
|
528
|
+ }
|
|
|
529
|
+
|
|
|
530
|
+ .clear-icon {
|
|
|
531
|
+ font-size: 36px;
|
|
|
532
|
+ color: #f56c6c;
|
|
|
533
|
+ flex-shrink: 0;
|
|
|
534
|
+ margin-top: 2px;
|
|
|
535
|
+ }
|
|
|
536
|
+
|
|
|
537
|
+ .clear-title {
|
|
|
538
|
+ font-size: 17px;
|
|
|
539
|
+ font-weight: 600;
|
|
|
540
|
+ color: #f56c6c;
|
|
|
541
|
+ margin-bottom: 4px;
|
|
|
542
|
+ }
|
|
|
543
|
+
|
|
|
544
|
+ .clear-desc {
|
|
|
545
|
+ font-size: 13px;
|
|
|
546
|
+ color: #909399;
|
|
|
547
|
+ max-width: 520px;
|
|
|
548
|
+ }
|
|
|
549
|
+
|
|
|
550
|
+ .clear-actions {
|
|
|
551
|
+ display: flex;
|
|
|
552
|
+ gap: 10px;
|
|
|
553
|
+ align-items: center;
|
|
|
554
|
+ flex-wrap: wrap;
|
|
|
555
|
+ }
|
|
|
556
|
+
|
|
|
557
|
+ .clear-result {
|
|
|
558
|
+ margin-top: 14px;
|
|
|
559
|
+ padding-top: 14px;
|
|
|
560
|
+ border-top: 1px dashed #fde2e2;
|
|
|
561
|
+
|
|
|
562
|
+ .result-title {
|
|
|
563
|
+ font-size: 13px;
|
|
|
564
|
+ color: #606266;
|
|
|
565
|
+ margin-bottom: 8px;
|
|
|
566
|
+ font-weight: 500;
|
|
|
567
|
+ }
|
|
|
568
|
+
|
|
|
569
|
+ .result-table {
|
|
|
570
|
+ display: flex;
|
|
|
571
|
+ flex-wrap: wrap;
|
|
|
572
|
+ gap: 6px;
|
|
|
573
|
+ }
|
|
|
574
|
+ }
|
|
|
575
|
+}
|
|
|
576
|
+
|
|
376
|
577
|
.import-grid {
|
|
377
|
578
|
display: grid;
|
|
378
|
579
|
grid-template-columns: repeat(2, 1fr);
|