Просмотр исходного кода

feat(score): add dimension api and optimize related pages

1. add getDimensionAll api in score api module
2. add配分层级 select component in dimension page
3. replace listDimension with getDimensionAll in dimension page
4. add sync ledger button in ledger import page
5. comment out old sync button in radar page
6. fix date picker disabled logic in clear card
huoyi недель назад: 3
Родитель
Сommit
a9bbf84a1a

+ 3 - 1
src/api/score/index.js

@@ -70,7 +70,9 @@ export function importScoreEvent(file) {
70
   fd.append('file', file)
70
   fd.append('file', file)
71
   return request({ url: '/score/event/import', method: 'post', data: fd })
71
   return request({ url: '/score/event/import', method: 'post', data: fd })
72
 }
72
 }
73
-
73
+export function getDimensionAll(params) {
74
+  return request({ url: `/score/dimension/all`, method: 'get', params: params })
75
+}
74
 // ===== 雷达图大屏 =====
76
 // ===== 雷达图大屏 =====
75
 export function radarTeamList() {
77
 export function radarTeamList() {
76
   return request({ url: '/score/radar/teamList', method: 'get' })
78
   return request({ url: '/score/radar/teamList', method: 'get' })

+ 70 - 46
src/views/ledger/import/index.vue

@@ -9,36 +9,39 @@
9
     <el-card class="combined-import-card" shadow="never">
9
     <el-card class="combined-import-card" shadow="never">
10
       <div class="combined-inner">
10
       <div class="combined-inner">
11
         <div class="combined-info">
11
         <div class="combined-info">
12
-          <el-icon class="combined-icon"><Files /></el-icon>
12
+          <el-icon class="combined-icon">
13
+            <Files />
14
+          </el-icon>
13
           <div>
15
           <div>
14
             <div class="combined-title">一键全量导入</div>
16
             <div class="combined-title">一键全量导入</div>
15
             <div class="combined-desc">上传「旅检三部"三三"数字管理平台.xlsx」,系统将自动识别20个Sheet并分别导入对应台账表</div>
17
             <div class="combined-desc">上传「旅检三部"三三"数字管理平台.xlsx」,系统将自动识别20个Sheet并分别导入对应台账表</div>
16
           </div>
18
           </div>
17
         </div>
19
         </div>
18
         <div class="combined-btns">
20
         <div class="combined-btns">
19
-          <el-upload
20
-            :action="''"
21
-            :auto-upload="false"
22
-            :show-file-list="false"
23
-            :on-change="handleCombinedChange"
24
-            accept=".xlsx,.xls"
25
-          >
21
+          <el-button v-hasPermi="['ledger:sync:all']" type="primary" size="large" :loading="syncing"
22
+            class="combined-btn" @click="handleSync">
23
+            同步台账
24
+          </el-button>
25
+
26
+          <el-upload :action="''" :auto-upload="false" :show-file-list="false" :on-change="handleCombinedChange"
27
+            accept=".xlsx,.xls">
26
             <el-button type="primary" size="large" :loading="combinedLoading" class="combined-btn">
28
             <el-button type="primary" size="large" :loading="combinedLoading" class="combined-btn">
27
-              <el-icon><Upload /></el-icon> 选择文件并全量导入
29
+              <el-icon>
30
+                <Upload />
31
+              </el-icon> 选择文件并全量导入
28
             </el-button>
32
             </el-button>
29
           </el-upload>
33
           </el-upload>
30
           <el-button size="large" class="combined-tpl-btn" @click="downloadCombinedTemplate">
34
           <el-button size="large" class="combined-tpl-btn" @click="downloadCombinedTemplate">
31
-            <el-icon><Download /></el-icon> 下载合并模板
35
+            <el-icon>
36
+              <Download />
37
+            </el-icon> 下载合并模板
32
           </el-button>
38
           </el-button>
33
         </div>
39
         </div>
34
       </div>
40
       </div>
35
       <div v-if="combinedResult" class="combined-result">
41
       <div v-if="combinedResult" class="combined-result">
36
         <div class="result-title">导入结果:</div>
42
         <div class="result-title">导入结果:</div>
37
-        <el-tag
38
-          v-for="(msg, sheet) in combinedResult" :key="sheet"
39
-          :type="msg.includes('失败') || msg.includes('错误') ? 'danger' : 'success'"
40
-          class="result-tag"
41
-        >
43
+        <el-tag v-for="(msg, sheet) in combinedResult" :key="sheet"
44
+          :type="msg.includes('失败') || msg.includes('错误') ? 'danger' : 'success'" class="result-tag">
42
           {{ sheet }}:{{ msg }}
45
           {{ sheet }}:{{ msg }}
43
         </el-tag>
46
         </el-tag>
44
       </div>
47
       </div>
@@ -48,31 +51,25 @@
48
     <el-card class="clear-card" shadow="never">
51
     <el-card class="clear-card" shadow="never">
49
       <div class="clear-inner">
52
       <div class="clear-inner">
50
         <div class="clear-info">
53
         <div class="clear-info">
51
-          <el-icon class="clear-icon"><DeleteFilled /></el-icon>
54
+          <el-icon class="clear-icon">
55
+            <DeleteFilled />
56
+          </el-icon>
52
           <div>
57
           <div>
53
             <div class="clear-title">清理台账数据</div>
58
             <div class="clear-title">清理台账数据</div>
54
             <div class="clear-desc">按导入时间范围删除全部20张台账表数据,同步清除台账来源的配分事项(手动录入不受影响)</div>
59
             <div class="clear-desc">按导入时间范围删除全部20张台账表数据,同步清除台账来源的配分事项(手动录入不受影响)</div>
55
           </div>
60
           </div>
56
         </div>
61
         </div>
57
         <div class="clear-actions">
62
         <div class="clear-actions">
58
-          <el-date-picker
59
-            v-model="clearBeginDate"
60
-            type="date"
61
-            value-format="YYYY-MM-DD"
62
-            placeholder="开始日期"
63
-            style="width: 150px"
64
-          />
63
+          <el-date-picker v-model="clearBeginDate" type="date" value-format="YYYY-MM-DD" placeholder="开始日期"
64
+            style="width: 150px" />
65
           <span style="margin: 0 6px; color: #909399;">至</span>
65
           <span style="margin: 0 6px; color: #909399;">至</span>
66
-          <el-date-picker
67
-            v-model="clearEndDate"
68
-            type="date"
69
-            value-format="YYYY-MM-DD"
70
-            placeholder="结束日期"
71
-            :disabled-date="(d) => clearBeginDate && d < new Date(clearBeginDate)"
72
-            style="width: 150px"
73
-          />
74
-          <el-button type="danger" :loading="clearLoading" :disabled="!clearBeginDate || !clearEndDate" @click="handleClear">
75
-            <el-icon><Delete /></el-icon> 清理
66
+          <el-date-picker v-model="clearEndDate" type="date" value-format="YYYY-MM-DD" placeholder="结束日期"
67
+            :disabled-date="(d) => clearBeginDate && d <= new Date(new Date(clearBeginDate).getTime() - 24 * 60 * 60 * 1000)" style="width: 150px" />
68
+          <el-button type="danger" :loading="clearLoading" :disabled="!clearBeginDate || !clearEndDate"
69
+            @click="handleClear">
70
+            <el-icon>
71
+              <Delete />
72
+            </el-icon> 清理
76
           </el-button>
73
           </el-button>
77
         </div>
74
         </div>
78
       </div>
75
       </div>
@@ -92,30 +89,32 @@
92
       <div v-for="item in importItems" :key="item.key" class="import-card">
89
       <div v-for="item in importItems" :key="item.key" class="import-card">
93
         <el-card shadow="hover" :class="['ledger-card', item.status]">
90
         <el-card shadow="hover" :class="['ledger-card', item.status]">
94
           <div class="card-header">
91
           <div class="card-header">
95
-            <el-icon class="card-icon"><component :is="item.icon" /></el-icon>
92
+            <el-icon class="card-icon">
93
+              <component :is="item.icon" />
94
+            </el-icon>
96
             <div class="card-title">{{ item.title }}</div>
95
             <div class="card-title">{{ item.title }}</div>
97
           </div>
96
           </div>
98
           <div class="card-desc">{{ item.desc }}</div>
97
           <div class="card-desc">{{ item.desc }}</div>
99
           <div class="card-actions">
98
           <div class="card-actions">
100
-            <el-upload
101
-              ref="uploadRefs"
102
-              :action="''"
103
-              :auto-upload="false"
104
-              :show-file-list="false"
105
-              :before-upload="(file) => beforeUpload(file, item)"
106
-              :on-change="(file) => handleFileChange(file, item)"
107
-              accept=".xlsx,.xls"
108
-            >
99
+            <el-upload ref="uploadRefs" :action="''" :auto-upload="false" :show-file-list="false"
100
+              :before-upload="(file) => beforeUpload(file, item)" :on-change="(file) => handleFileChange(file, item)"
101
+              accept=".xlsx,.xls">
109
               <el-button type="primary" size="small" :loading="item.loading">
102
               <el-button type="primary" size="small" :loading="item.loading">
110
-                <el-icon><Upload /></el-icon> 选择文件上传
103
+                <el-icon>
104
+                  <Upload />
105
+                </el-icon> 选择文件上传
111
               </el-button>
106
               </el-button>
112
             </el-upload>
107
             </el-upload>
113
             <el-button size="small" text @click="downloadTemplate(item)">
108
             <el-button size="small" text @click="downloadTemplate(item)">
114
-              <el-icon><Download /></el-icon> 下载模板
109
+              <el-icon>
110
+                <Download />
111
+              </el-icon> 下载模板
115
             </el-button>
112
             </el-button>
116
           </div>
113
           </div>
117
           <div v-if="item.lastResult" class="last-result" :class="item.lastResult.success ? 'success' : 'error'">
114
           <div v-if="item.lastResult" class="last-result" :class="item.lastResult.success ? 'success' : 'error'">
118
-            <el-icon><component :is="item.lastResult.success ? 'CircleCheck' : 'CircleClose'" /></el-icon>
115
+            <el-icon>
116
+              <component :is="item.lastResult.success ? 'CircleCheck' : 'CircleClose'" />
117
+            </el-icon>
119
             {{ item.lastResult.msg }}
118
             {{ item.lastResult.msg }}
120
           </div>
119
           </div>
121
         </el-card>
120
         </el-card>
@@ -129,6 +128,7 @@ import { ref, reactive } from 'vue'
129
 import { ElMessage, ElMessageBox } from 'element-plus'
128
 import { ElMessage, ElMessageBox } from 'element-plus'
130
 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'
129
 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'
131
 import { importCombinedLedger, clearLedgerByTimeRange, downloadLedgerTemplate } from '@/api/ledger/index'
130
 import { importCombinedLedger, clearLedgerByTimeRange, downloadLedgerTemplate } from '@/api/ledger/index'
131
+import { syncLedgerAll } from '@/api/score/index'
132
 import {
132
 import {
133
   importSupervisionProblem,
133
   importSupervisionProblem,
134
   importPatrolInspection,
134
   importPatrolInspection,
@@ -155,6 +155,22 @@ defineOptions({ name: 'LedgerImport' })
155
 const combinedLoading = ref(false)
155
 const combinedLoading = ref(false)
156
 const combinedResult = ref(null)
156
 const combinedResult = ref(null)
157
 
157
 
158
+// ── 台账同步 ──────────────────────────────────────────
159
+const syncing = ref(false)
160
+
161
+async function handleSync() {
162
+  syncing.value = true
163
+  try {
164
+    const r = await syncLedgerAll()
165
+    const d = r.data || {}
166
+    ElMessage.success(`同步完成:新增 ${d.inserted ?? 0} 条,跳过 ${d.skipped ?? 0} 条`)
167
+  } catch (e) {
168
+    ElMessage.error('同步失败,请查看后端日志')
169
+  } finally {
170
+    syncing.value = false
171
+  }
172
+}
173
+
158
 // ── 台账数据清理 ──────────────────────────────────────
174
 // ── 台账数据清理 ──────────────────────────────────────
159
 const clearBeginDate = ref('')
175
 const clearBeginDate = ref('')
160
 const clearEndDate = ref('')
176
 const clearEndDate = ref('')
@@ -466,12 +482,14 @@ function triggerDownload(blob, fileName) {
466
 
482
 
467
 .page-header-card {
483
 .page-header-card {
468
   margin-bottom: 20px;
484
   margin-bottom: 20px;
485
+
469
   .page-title {
486
   .page-title {
470
     font-size: 20px;
487
     font-size: 20px;
471
     font-weight: 600;
488
     font-weight: 600;
472
     color: #303133;
489
     color: #303133;
473
     margin-bottom: 6px;
490
     margin-bottom: 6px;
474
   }
491
   }
492
+
475
   .page-desc {
493
   .page-desc {
476
     font-size: 13px;
494
     font-size: 13px;
477
     color: #909399;
495
     color: #909399;
@@ -533,6 +551,11 @@ function triggerDownload(blob, fileName) {
533
     font-size: 14px;
551
     font-size: 14px;
534
   }
552
   }
535
 
553
 
554
+  .sync-btn {
555
+    border-color: #409eff;
556
+    color: #409eff;
557
+  }
558
+
536
   .combined-result {
559
   .combined-result {
537
     margin-top: 14px;
560
     margin-top: 14px;
538
     padding-top: 14px;
561
     padding-top: 14px;
@@ -678,6 +701,7 @@ function triggerDownload(blob, fileName) {
678
       background: #f0f9eb;
701
       background: #f0f9eb;
679
       color: #67c23a;
702
       color: #67c23a;
680
     }
703
     }
704
+
681
     &.error {
705
     &.error {
682
       background: #fef0f0;
706
       background: #fef0f0;
683
       color: #f56c6c;
707
       color: #f56c6c;

+ 10 - 5
src/views/score/dimension/index.vue

@@ -110,6 +110,11 @@
110
     <!-- 维度对话框 -->
110
     <!-- 维度对话框 -->
111
     <el-dialog :title="dimDialogTitle" v-model="dimDialogVisible" width="480px" append-to-body>
111
     <el-dialog :title="dimDialogTitle" v-model="dimDialogVisible" width="480px" append-to-body>
112
       <el-form ref="dimFormRef" :model="dimForm" :rules="dimRules" label-width="90px">
112
       <el-form ref="dimFormRef" :model="dimForm" :rules="dimRules" label-width="90px">
113
+        <el-form-item label="配分层级" prop="org">
114
+          <el-select v-model="dimForm.org" placeholder="请选择配分层级" clearable style="width:100%">
115
+            <el-option v-for="dict in score_level" :key="dict.value" :label="dict.label" :value="dict.value" />
116
+          </el-select>
117
+        </el-form-item>
113
         <el-form-item label="维度名称" prop="name">
118
         <el-form-item label="维度名称" prop="name">
114
           <el-input v-model="dimForm.name" placeholder="请输入维度名称" />
119
           <el-input v-model="dimForm.name" placeholder="请输入维度名称" />
115
         </el-form-item>
120
         </el-form-item>
@@ -191,7 +196,7 @@
191
 import { ref, reactive, computed, onMounted, getCurrentInstance } from 'vue'
196
 import { ref, reactive, computed, onMounted, getCurrentInstance } from 'vue'
192
 import { ElMessage, ElMessageBox } from 'element-plus'
197
 import { ElMessage, ElMessageBox } from 'element-plus'
193
 import {
198
 import {
194
-  listDimension, addDimension, updateDimension, delDimension,
199
+  getDimensionAll, listDimension, addDimension, updateDimension, delDimension,
195
   treeIndicator, addIndicator, updateIndicator, delIndicator
200
   treeIndicator, addIndicator, updateIndicator, delIndicator
196
 } from '@/api/score/index'
201
 } from '@/api/score/index'
197
 
202
 
@@ -200,7 +205,7 @@ defineOptions({ name: 'ScoreDimension' })
200
 const { proxy } = getCurrentInstance()
205
 const { proxy } = getCurrentInstance()
201
 const { score_level } = proxy.useDict('score_level')
206
 const { score_level } = proxy.useDict('score_level')
202
 
207
 
203
-const scoreLevel = ref('')
208
+const scoreLevel = ref('4')
204
 
209
 
205
 // ===== 维度 =====
210
 // ===== 维度 =====
206
 const dimLoading = ref(false)
211
 const dimLoading = ref(false)
@@ -209,7 +214,7 @@ const selectedDim = ref(null)
209
 const dimDialogVisible = ref(false)
214
 const dimDialogVisible = ref(false)
210
 const dimDialogTitle = ref('')
215
 const dimDialogTitle = ref('')
211
 const dimFormRef = ref(null)
216
 const dimFormRef = ref(null)
212
-const dimForm = reactive({ id: null, name: '', weight: 0, baseScore: 80, sortOrder: 0, status: '0', remark: '' })
217
+const dimForm = reactive({ id: null, name: '', weight: 0, baseScore: 80, sortOrder: 0, status: '0', remark: '', org: '' })
213
 const dimRules = {
218
 const dimRules = {
214
   name: [{ required: true, message: '请输入维度名称', trigger: 'blur' }],
219
   name: [{ required: true, message: '请输入维度名称', trigger: 'blur' }],
215
   weight: [{ required: true, message: '请输入权重', trigger: 'blur' }]
220
   weight: [{ required: true, message: '请输入权重', trigger: 'blur' }]
@@ -221,8 +226,8 @@ const totalWeight = computed(() => {
221
 
226
 
222
 async function loadDimensions() {
227
 async function loadDimensions() {
223
   dimLoading.value = true
228
   dimLoading.value = true
224
-  const r = await listDimension({ pageNum: 1, pageSize: 100 }).finally(() => dimLoading.value = false)
225
-  dimList.value = r.rows || []
229
+  const r = await getDimensionAll({ pageNum: 1, pageSize: 100 }).finally(() => dimLoading.value = false)
230
+  dimList.value = r.data || []
226
   if (!selectedDim.value && dimList.value.length) selectDimension(dimList.value[0])
231
   if (!selectedDim.value && dimList.value.length) selectDimension(dimList.value[0])
227
 }
232
 }
228
 
233
 

+ 2 - 2
src/views/score/radar/index.vue

@@ -8,14 +8,14 @@
8
       </el-select>
8
       </el-select>
9
       <div class="screen-title">班组成员画像大屏</div>
9
       <div class="screen-title">班组成员画像大屏</div>
10
       <div class="header-right">
10
       <div class="header-right">
11
-        <el-button
11
+        <!-- <el-button
12
           v-hasPermi="['ledger:sync:all']"
12
           v-hasPermi="['ledger:sync:all']"
13
           size="small" type="primary" plain
13
           size="small" type="primary" plain
14
           :loading="syncing"
14
           :loading="syncing"
15
           class="sync-btn"
15
           class="sync-btn"
16
           @click="handleSync">
16
           @click="handleSync">
17
           同步台账
17
           同步台账
18
-        </el-button>
18
+        </el-button> -->
19
       </div>
19
       </div>
20
     </div>
20
     </div>
21
 
21