소스 검색

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 주 전
부모
커밋
a9bbf84a1a
4개의 변경된 파일85개의 추가작업 그리고 54개의 파일을 삭제
  1. 3 1
      src/api/score/index.js
  2. 70 46
      src/views/ledger/import/index.vue
  3. 10 5
      src/views/score/dimension/index.vue
  4. 2 2
      src/views/score/radar/index.vue

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

@@ -70,7 +70,9 @@ export function importScoreEvent(file) {
70 70
   fd.append('file', file)
71 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 77
 export function radarTeamList() {
76 78
   return request({ url: '/score/radar/teamList', method: 'get' })

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

@@ -9,36 +9,39 @@
9 9
     <el-card class="combined-import-card" shadow="never">
10 10
       <div class="combined-inner">
11 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 15
           <div>
14 16
             <div class="combined-title">一键全量导入</div>
15 17
             <div class="combined-desc">上传「旅检三部"三三"数字管理平台.xlsx」,系统将自动识别20个Sheet并分别导入对应台账表</div>
16 18
           </div>
17 19
         </div>
18 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 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 32
             </el-button>
29 33
           </el-upload>
30 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 38
           </el-button>
33 39
         </div>
34 40
       </div>
35 41
       <div v-if="combinedResult" class="combined-result">
36 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 45
           {{ sheet }}:{{ msg }}
43 46
         </el-tag>
44 47
       </div>
@@ -48,31 +51,25 @@
48 51
     <el-card class="clear-card" shadow="never">
49 52
       <div class="clear-inner">
50 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 57
           <div>
53 58
             <div class="clear-title">清理台账数据</div>
54 59
             <div class="clear-desc">按导入时间范围删除全部20张台账表数据,同步清除台账来源的配分事项(手动录入不受影响)</div>
55 60
           </div>
56 61
         </div>
57 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 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 73
           </el-button>
77 74
         </div>
78 75
       </div>
@@ -92,30 +89,32 @@
92 89
       <div v-for="item in importItems" :key="item.key" class="import-card">
93 90
         <el-card shadow="hover" :class="['ledger-card', item.status]">
94 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 95
             <div class="card-title">{{ item.title }}</div>
97 96
           </div>
98 97
           <div class="card-desc">{{ item.desc }}</div>
99 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 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 106
               </el-button>
112 107
             </el-upload>
113 108
             <el-button size="small" text @click="downloadTemplate(item)">
114
-              <el-icon><Download /></el-icon> 下载模板
109
+              <el-icon>
110
+                <Download />
111
+              </el-icon> 下载模板
115 112
             </el-button>
116 113
           </div>
117 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 118
             {{ item.lastResult.msg }}
120 119
           </div>
121 120
         </el-card>
@@ -129,6 +128,7 @@ import { ref, reactive } from 'vue'
129 128
 import { ElMessage, ElMessageBox } from 'element-plus'
130 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 130
 import { importCombinedLedger, clearLedgerByTimeRange, downloadLedgerTemplate } from '@/api/ledger/index'
131
+import { syncLedgerAll } from '@/api/score/index'
132 132
 import {
133 133
   importSupervisionProblem,
134 134
   importPatrolInspection,
@@ -155,6 +155,22 @@ defineOptions({ name: 'LedgerImport' })
155 155
 const combinedLoading = ref(false)
156 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 175
 const clearBeginDate = ref('')
160 176
 const clearEndDate = ref('')
@@ -466,12 +482,14 @@ function triggerDownload(blob, fileName) {
466 482
 
467 483
 .page-header-card {
468 484
   margin-bottom: 20px;
485
+
469 486
   .page-title {
470 487
     font-size: 20px;
471 488
     font-weight: 600;
472 489
     color: #303133;
473 490
     margin-bottom: 6px;
474 491
   }
492
+
475 493
   .page-desc {
476 494
     font-size: 13px;
477 495
     color: #909399;
@@ -533,6 +551,11 @@ function triggerDownload(blob, fileName) {
533 551
     font-size: 14px;
534 552
   }
535 553
 
554
+  .sync-btn {
555
+    border-color: #409eff;
556
+    color: #409eff;
557
+  }
558
+
536 559
   .combined-result {
537 560
     margin-top: 14px;
538 561
     padding-top: 14px;
@@ -678,6 +701,7 @@ function triggerDownload(blob, fileName) {
678 701
       background: #f0f9eb;
679 702
       color: #67c23a;
680 703
     }
704
+
681 705
     &.error {
682 706
       background: #fef0f0;
683 707
       color: #f56c6c;

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

@@ -110,6 +110,11 @@
110 110
     <!-- 维度对话框 -->
111 111
     <el-dialog :title="dimDialogTitle" v-model="dimDialogVisible" width="480px" append-to-body>
112 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 118
         <el-form-item label="维度名称" prop="name">
114 119
           <el-input v-model="dimForm.name" placeholder="请输入维度名称" />
115 120
         </el-form-item>
@@ -191,7 +196,7 @@
191 196
 import { ref, reactive, computed, onMounted, getCurrentInstance } from 'vue'
192 197
 import { ElMessage, ElMessageBox } from 'element-plus'
193 198
 import {
194
-  listDimension, addDimension, updateDimension, delDimension,
199
+  getDimensionAll, listDimension, addDimension, updateDimension, delDimension,
195 200
   treeIndicator, addIndicator, updateIndicator, delIndicator
196 201
 } from '@/api/score/index'
197 202
 
@@ -200,7 +205,7 @@ defineOptions({ name: 'ScoreDimension' })
200 205
 const { proxy } = getCurrentInstance()
201 206
 const { score_level } = proxy.useDict('score_level')
202 207
 
203
-const scoreLevel = ref('')
208
+const scoreLevel = ref('4')
204 209
 
205 210
 // ===== 维度 =====
206 211
 const dimLoading = ref(false)
@@ -209,7 +214,7 @@ const selectedDim = ref(null)
209 214
 const dimDialogVisible = ref(false)
210 215
 const dimDialogTitle = ref('')
211 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 218
 const dimRules = {
214 219
   name: [{ required: true, message: '请输入维度名称', trigger: 'blur' }],
215 220
   weight: [{ required: true, message: '请输入权重', trigger: 'blur' }]
@@ -221,8 +226,8 @@ const totalWeight = computed(() => {
221 226
 
222 227
 async function loadDimensions() {
223 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 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 8
       </el-select>
9 9
       <div class="screen-title">班组成员画像大屏</div>
10 10
       <div class="header-right">
11
-        <el-button
11
+        <!-- <el-button
12 12
           v-hasPermi="['ledger:sync:all']"
13 13
           size="small" type="primary" plain
14 14
           :loading="syncing"
15 15
           class="sync-btn"
16 16
           @click="handleSync">
17 17
           同步台账
18
-        </el-button>
18
+        </el-button> -->
19 19
       </div>
20 20
     </div>
21 21