Bladeren bron

Merge branch 'employeeScreen'

huoyi 6 dagen geleden
bovenliggende
commit
cc7c89a744
37 gewijzigde bestanden met toevoegingen van 741 en 1505 verwijderingen
  1. 13 0
      src/api/ledger/index.js
  2. 3 3
      src/router/index.js
  3. 180 66
      src/views/assistant/index.vue
  4. 2 2
      src/views/ledger/bannerLetter/index.vue
  5. 2 2
      src/views/ledger/channelPassRate/index.vue
  6. 2 2
      src/views/ledger/complaint/index.vue
  7. 2 2
      src/views/ledger/dailyTraining/index.vue
  8. 2 2
      src/views/ledger/dormFireSafety/index.vue
  9. 2 2
      src/views/ledger/examScore/index.vue
  10. 11 2
      src/views/ledger/import/index.vue
  11. 4 4
      src/views/ledger/leaveSpecial/index.vue
  12. 2 2
      src/views/ledger/patrolInspection/index.vue
  13. 169 0
      src/views/ledger/qualificationCertificateStatus/index.vue
  14. 0 0
      src/views/ledger/qualificationCertificateStatus/职业资格证书情况
  15. 5 5
      src/views/ledger/realtimeInterception/index.vue
  16. 3 3
      src/views/ledger/rewardApproval/index.vue
  17. 2 2
      src/views/ledger/rewardPenalty/index.vue
  18. 2 2
      src/views/ledger/securityTest/index.vue
  19. 2 2
      src/views/ledger/seizureStats/index.vue
  20. 5 5
      src/views/ledger/servicePatrol/index.vue
  21. 5 5
      src/views/ledger/supervisionProblem/index.vue
  22. 3 3
      src/views/ledger/terminalBonus/index.vue
  23. 2 2
      src/views/ledger/trainingIssue/index.vue
  24. 2 2
      src/views/ledger/unsafeEvent/index.vue
  25. 1 1
      src/views/portraitManagement/components/ChannelCheckChart.vue
  26. 21 0
      src/views/portraitManagement/components/ProfileBasicDistribution.vue
  27. 23 2
      src/views/portraitManagement/components/ProfilePositionDistribution.vue
  28. 68 4
      src/views/portraitManagement/components/ProfileRadar.vue
  29. 1 0
      src/views/portraitManagement/components/SeizedItems.vue
  30. 5 1
      src/views/portraitManagement/components/SeizedNum.vue
  31. 1 1
      src/views/portraitManagement/components/SupervisionDistribution.vue
  32. 1 1
      src/views/portraitManagement/deptProfile/component/runData.vue
  33. 95 8
      src/views/portraitManagement/employeeProfile/index.vue
  34. 0 1306
      src/views/portraitManagement/employeeProfile/indexOld.vue
  35. 1 1
      src/views/portraitManagement/teamProfile/component/runData.vue
  36. 18 4
      src/views/score/event/index.vue
  37. 81 56
      src/views/warningPage/index.vue

+ 13 - 0
src/api/ledger/index.js

@@ -285,3 +285,16 @@ export function getTrainingIssue(id) {
285 285
 export function exportTrainingIssue(query) {
286 286
   return request({ url: '/ledger/trainingIssue/export', method: 'post', params: query, responseType: 'blob' })
287 287
 }
288
+
289
+// ===== 资格证书状态 =====
290
+export function listQualificationCertificateStatus(query) {
291
+  return request({ url: '/ledger/qualificationLevel/list', method: 'get', params: query })
292
+}
293
+//导入职业资格等级
294
+export function importQualificationLevel(data) {
295
+  return request({ url: '/ledger/import/qualificationLevel', method: 'post', data })
296
+}
297
+//通过用户id获取职业资格等级获取时间信息
298
+export function getQualificationLevelTime(query) {
299
+  return request({ url: '/ledger/qualificationLevel/user', method: 'get',params: query })
300
+}

+ 3 - 3
src/router/index.js

@@ -92,9 +92,9 @@ export const constantRoutes = [
92 92
     children: [
93 93
       {
94 94
         path: '/index',
95
-        component: () => import('@/views/warningPage'),
96
-        name: 'WarningPage',
97
-        meta: { title: '预警页面', icon: 'dashboard' }
95
+        component: () => import('@/views/assistant'),
96
+        name: 'Assistant',
97
+        meta: { title: 'AI助手', icon: 'dashboard' }
98 98
       },
99 99
       // {
100 100
       //   path: '/dashboard-work',

+ 180 - 66
src/views/assistant/index.vue

@@ -60,6 +60,17 @@
60 60
                       min-width="100" />
61 61
                   </el-table>
62 62
                   <div v-else-if="msg.result.rows" class="no-data">暂无数据</div>
63
+                  
64
+                  <!-- SQL 折叠 -->
65
+                  <div v-if="msg.result.sql" class="sql-block">
66
+                    <div class="sql-toggle" @click="toggleSql(msg.id)">
67
+                      <span class="sql-label">SQL</span>
68
+                      <span class="sql-arrow">{{ msg.showSql ? '▲' : '▼' }}</span>
69
+                    </div>
70
+                    <div v-if="msg.showSql" class="sql-code">
71
+                      <pre>{{ msg.result.sql }}</pre>
72
+                    </div>
73
+                  </div>
63 74
                 </div>
64 75
 
65 76
                 <!-- 耗时 -->
@@ -81,6 +92,11 @@
81 92
         </div>
82 93
       </div>
83 94
 
95
+      <!-- 意图澄清 -->
96
+      <div v-if="clarifyQuestion" class="clarify-bar">
97
+        <span class="clarify-text">{{ clarifyQuestion }}</span>
98
+      </div>
99
+
84 100
       <!-- 底部输入区 -->
85 101
       <div class="chat-container">
86 102
         <div class="quick-action">
@@ -88,10 +104,9 @@
88 104
           <el-button class="report-btn" @click="handlePerformanceClick">绩效分析报告</el-button>
89 105
           <el-button class="report-btn" @click="handleUseReportClick">使用报表</el-button>
90 106
         </div>
91
-        <!--请输入您的问题,按 Enter 发送...-->
92 107
         <div class="input-container">
93
-          <el-input v-model="inputMessage" type="textarea" :rows="3" placeholder="功能开发中,请敬请期待"
94
-            class="chat-input" @keydown.enter.exact.prevent="handleSend" :disabled="true" />
108
+          <el-input v-model="inputMessage" type="textarea" :rows="3" placeholder="请输入您的问题,按 Enter 发送..."
109
+            class="chat-input" @keydown.enter.exact.prevent="handleSend" />
95 110
           <el-button :disabled="!inputMessage.trim() || isLoading" circle class="send-btn" @click="handleSend">
96 111
             <svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
97 112
               <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" />
@@ -114,7 +129,9 @@ import PerformanceAnalysis from './components/performanceAnalysis.vue'
114 129
 import useUserStore from '@/store/modules/user'
115 130
 import UseReports from './components/useReports.vue'
116 131
 const userStore = useUserStore()
117
-const AI_SERVICE_URL = import.meta.env.VITE_AI_SERVICE_URL || 'http://localhost:8000'
132
+
133
+const AI_BASE = import.meta.env.VITE_AI_SERVICE_URL || 'http://airport-test.samsundot.com:9015'
134
+const AI_URL = AI_BASE + '/api/query-stream'
118 135
 
119 136
 const inputMessage = ref('')
120 137
 const isLoading = ref(false)
@@ -123,6 +140,9 @@ const messagesRef = ref(null)
123 140
 const showDataBoard = ref(false)
124 141
 const showPerformanceAnalysis = ref(false)
125 142
 const showUseReports = ref(false)
143
+const sessionId = ref('app_' + Date.now())
144
+const history = ref([])
145
+const clarifyQuestion = ref('')
126 146
 
127 147
 onMounted(() => resetState())
128 148
 onActivated(() => resetState())
@@ -135,6 +155,9 @@ const resetState = () => {
135 155
   messages.value = []
136 156
   showUseReports.value = false
137 157
   isLoading.value = false
158
+  sessionId.value = 'app_' + Date.now()
159
+  history.value = []
160
+  clarifyQuestion.value = ''
138 161
 }
139 162
 
140 163
 const scrollToBottom = async () => {
@@ -144,11 +167,84 @@ const scrollToBottom = async () => {
144 167
   }
145 168
 }
146 169
 
170
+const getUserId = () => {
171
+  return String(userStore.id || 1)
172
+}
173
+
174
+const toggleSql = (msgId) => {
175
+  const msg = messages.value.find(m => m.id === msgId)
176
+  if (msg) {
177
+    msg.showSql = !msg.showSql
178
+  }
179
+}
180
+
181
+const handleEvent = (aiMsg, data) => {
182
+  if (data.step !== undefined && data.name) {
183
+    // 进度步骤
184
+    const steps = [...(aiMsg.steps || [])]
185
+    const si = steps.findIndex(s => s.name === data.name)
186
+    const step = { 
187
+      step: si >= 0 ? si : steps.length, 
188
+      name: data.name, 
189
+      status: data.status, 
190
+      duration: data.duration 
191
+    }
192
+    if (si >= 0) {
193
+      steps[si] = step
194
+    } else {
195
+      steps.push(step)
196
+    }
197
+    aiMsg.steps = steps
198
+    messages.value = [...messages.value]
199
+    return
200
+  }
201
+
202
+  if (data.question) {
203
+    // 意图澄清
204
+    clarifyQuestion.value = data.question
205
+    aiMsg.status = 'done'
206
+    messages.value = [...messages.value]
207
+    return
208
+  }
209
+
210
+  if (data.columns !== undefined || data.answer !== undefined) {
211
+    // 最终结果
212
+    aiMsg.steps.forEach(s => { if (s.status === 'running') s.status = 'done' })
213
+    
214
+    // 适配数据结构
215
+    aiMsg.result = {
216
+      query_type: data.answer ? 'knowledge' : 'data',
217
+      answer: data.answer || '',
218
+      message: data.message || '',
219
+      sql: data.sql || '',
220
+      columns: data.columns || [],
221
+      rows: data.rows || [],
222
+      sources: data.sources || [],
223
+      timing: { total: 0 },
224
+      success: data.success !== false
225
+    }
226
+    aiMsg.showSql = false
227
+    aiMsg.isError = !data.success
228
+    aiMsg.status = 'done'
229
+    messages.value = [...messages.value]
230
+    
231
+    // 记录历史
232
+    const prevUserMsg = messages.value.find(m => m.id < aiMsg.id && m.type === 'user')
233
+    if (prevUserMsg) {
234
+      history.value.push({
235
+        question: prevUserMsg.text || '',
236
+        sql: data.sql || '',
237
+      })
238
+    }
239
+  }
240
+}
241
+
147 242
 const handleSend = async () => {
148 243
   const text = inputMessage.value.trim()
149 244
   if (!text || isLoading.value) return
150 245
 
151 246
   inputMessage.value = ''
247
+  clarifyQuestion.value = ''
152 248
   isLoading.value = true
153 249
 
154 250
   // 添加用户消息
@@ -162,25 +258,27 @@ const handleSend = async () => {
162 258
     status: 'loading',
163 259
     steps: [],
164 260
     result: null,
165
-    errorText: ''
261
+    errorText: '',
262
+    showSql: false,
263
+    isError: false
166 264
   }
167 265
   messages.value.push(assistantMsg)
168 266
   await scrollToBottom()
169 267
 
170 268
   try {
171
-    const response = await fetch(`${AI_SERVICE_URL}/api/smart-query-stream`, {
269
+    const response = await fetch(AI_URL, {
172 270
       method: 'POST',
173 271
       headers: { 'Content-Type': 'application/json' },
174 272
       body: JSON.stringify({
175 273
         question: text,
176
-        user_id: String(userStore.id || 'anonymous'),
177
-        llm_type: 'claude'
178
-      })
274
+        user_id: getUserId(),
275
+        llm_type: 'qwen',
276
+        session_id: sessionId.value,
277
+        history: history.value.slice(-3),
278
+      }),
179 279
     })
180 280
 
181
-    if (!response.ok) {
182
-      throw new Error(`服务请求失败 (${response.status})`)
183
-    }
281
+    if (!response.ok) throw new Error('请求失败 ' + response.status)
184 282
 
185 283
     const reader = response.body.getReader()
186 284
     const decoder = new TextDecoder()
@@ -191,66 +289,27 @@ const handleSend = async () => {
191 289
       if (done) break
192 290
 
193 291
       buffer += decoder.decode(value, { stream: true })
194
-      // SSE 事件以两个换行符分隔
195
-      const blocks = buffer.split('\n\n')
196
-      buffer = blocks.pop() // 保留未完整的块
197
-
198
-      for (const block of blocks) {
199
-        if (!block.trim()) continue
200
-        const lines = block.split('\n')
201
-        let eventType = ''
202
-        let dataStr = ''
203
-        for (const line of lines) {
204
-          if (line.startsWith('event: ')) eventType = line.slice(7).trim()
205
-          else if (line.startsWith('data: ')) dataStr = line.slice(6).trim()
206
-        }
207
-        if (!dataStr) continue
208
-
209
-        let data
210
-        try { data = JSON.parse(dataStr) } catch { continue }
211
-
212
-        if (eventType === 'progress') {
213
-          const existing = assistantMsg.steps.find(s => s.step === data.step)
214
-          if (existing) {
215
-            Object.assign(existing, data)
216
-          } else {
217
-            assistantMsg.steps.push({ ...data })
218
-          }
219
-          // 触发响应式更新
220
-          messages.value = [...messages.value]
221
-          await scrollToBottom()
222
-
223
-        } else if (eventType === 'result') {
224
-          // 将所有仍在 running 的步骤标记为 done
225
-          assistantMsg.steps.forEach(s => { if (s.status === 'running') s.status = 'done' })
226
-          assistantMsg.result = data
227
-          assistantMsg.status = 'done'
228
-          messages.value = [...messages.value]
229
-          await scrollToBottom()
230
-
231
-        } else if (eventType === 'error') {
232
-          assistantMsg.status = 'error'
233
-          assistantMsg.errorText = data.message || '请求失败,请稍后重试'
234
-          messages.value = [...messages.value]
292
+      const lines = buffer.split('\n')
293
+      buffer = lines.pop()
294
+
295
+      for (const line of lines) {
296
+        if (line.startsWith('data: ')) {
297
+          try {
298
+            const data = JSON.parse(line.slice(6))
299
+            handleEvent(assistantMsg, data)
300
+            await scrollToBottom()
301
+          } catch (e) {}
235 302
         }
236 303
       }
237 304
     }
238
-
239
-    // 若未收到 result 事件
240
-    if (assistantMsg.status === 'loading') {
241
-      assistantMsg.status = 'error'
242
-      assistantMsg.errorText = '未收到服务响应,请检查 AI 服务是否正常运行'
243
-      messages.value = [...messages.value]
244
-    }
245
-
246
-  } catch (e) {
305
+  } catch (err) {
247 306
     assistantMsg.status = 'error'
248
-    assistantMsg.errorText = e.message || '网络错误,请检查 AI 服务连接'
307
+    assistantMsg.errorText = '请求失败,请检查网络后重试'
249 308
     messages.value = [...messages.value]
250
-  } finally {
251
-    isLoading.value = false
252
-    await scrollToBottom()
253 309
   }
310
+
311
+  isLoading.value = false
312
+  await scrollToBottom()
254 313
 }
255 314
 
256 315
 const handleReportClick = () => { showDataBoard.value = true }
@@ -459,6 +518,61 @@ const handleBack = () => {
459 518
   font-size: 13px;
460 519
 }
461 520
 
521
+/* ===== SQL 折叠样式 ===== */
522
+.sql-block {
523
+  margin-top: 12px;
524
+}
525
+
526
+.sql-toggle {
527
+  display: flex;
528
+  align-items: center;
529
+  gap: 8px;
530
+  cursor: pointer;
531
+  color: #557DDB;
532
+  font-size: 13px;
533
+}
534
+
535
+.sql-label {
536
+  background: #e6f0ff;
537
+  padding: 2px 8px;
538
+  border-radius: 4px;
539
+  font-weight: 600;
540
+}
541
+
542
+.sql-arrow {
543
+  font-size: 12px;
544
+}
545
+
546
+.sql-code {
547
+  margin-top: 8px;
548
+  background: #f5f7fa;
549
+  padding: 12px;
550
+  border-radius: 8px;
551
+  overflow-x: auto;
552
+}
553
+
554
+.sql-code pre {
555
+  margin: 0;
556
+  font-size: 12px;
557
+  color: #333;
558
+  white-space: pre-wrap;
559
+  word-break: break-all;
560
+}
561
+
562
+/* ===== 意图澄清 ===== */
563
+.clarify-bar {
564
+  background: #fff3cd;
565
+  border: 1px solid #ffc107;
566
+  padding: 12px 16px;
567
+  border-radius: 8px;
568
+  margin-bottom: 16px;
569
+}
570
+
571
+.clarify-text {
572
+  color: #856404;
573
+  font-size: 14px;
574
+}
575
+
462 576
 /* ===== 底部输入区 ===== */
463 577
 .chat-container {
464 578
   flex-shrink: 0;

+ 2 - 2
src/views/ledger/bannerLetter/index.vue

@@ -38,8 +38,8 @@
38 38
 
39 39
     <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
40 40
       <el-table-column type="selection" width="55" align="center" />
41
-      <el-table-column label="记录日期" align="center" prop="recordDate" width="110">
42
-        <template #default="{ row }">{{ parseTime(row.recordDate, '{y}-{m}-{d}') }}</template>
41
+      <el-table-column label="记录日期" align="center" prop="recordDate" width="170">
42
+        <template #default="{ row }">{{ row.recordDate }}</template>
43 43
       </el-table-column>
44 44
       <el-table-column label="部门名称" align="center" prop="deptName" />
45 45
       <el-table-column label="队室/班组" align="center" prop="teamName" />

+ 2 - 2
src/views/ledger/channelPassRate/index.vue

@@ -23,8 +23,8 @@
23 23
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
24 24
     </el-row>
25 25
     <el-table v-loading="loading" :data="list">
26
-      <el-table-column label="记录日期" align="center" prop="recordDate" width="110">
27
-        <template #default="{ row }">{{ parseTime(row.recordDate, '{y}-{m}-{d}') }}</template>
26
+      <el-table-column label="记录日期" align="center" prop="recordDate" width="170">
27
+        <template #default="{ row }">{{ row.recordDate }}</template>
28 28
       </el-table-column>
29 29
       <el-table-column label="部门名称" align="center" prop="deptName" />
30 30
       <el-table-column label="队室/班组" align="center" prop="teamName" />

+ 2 - 2
src/views/ledger/complaint/index.vue

@@ -32,8 +32,8 @@
32 32
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
33 33
     </el-row>
34 34
     <el-table v-loading="loading" :data="list">
35
-      <el-table-column label="记录日期" align="center" prop="recordDate" width="110">
36
-        <template #default="{ row }">{{ parseTime(row.recordDate, '{y}-{m}-{d}') }}</template>
35
+      <el-table-column label="记录日期" align="center" prop="recordDate" width="170">
36
+        <template #default="{ row }">{{ row.recordDate }}</template>
37 37
       </el-table-column>
38 38
       <el-table-column label="部门名称" align="center" prop="deptName" />
39 39
       <el-table-column label="队室/班组" align="center" prop="teamName" />

+ 2 - 2
src/views/ledger/dailyTraining/index.vue

@@ -23,8 +23,8 @@
23 23
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
24 24
     </el-row>
25 25
     <el-table v-loading="loading" :data="list">
26
-      <el-table-column label="培训日期" align="center" prop="recordDate" width="110">
27
-        <template #default="{ row }">{{ parseTime(row.recordDate, '{y}-{m}-{d}') }}</template>
26
+      <el-table-column label="培训日期" align="center" prop="recordDate" width="170">
27
+        <template #default="{ row }">{{ row.recordDate }}</template>
28 28
       </el-table-column>
29 29
       <el-table-column label="班组" align="center" prop="teamName" width="120" />
30 30
       <el-table-column label="项目名称" align="center" prop="projectName" min-width="150" show-overflow-tooltip />

+ 2 - 2
src/views/ledger/dormFireSafety/index.vue

@@ -26,8 +26,8 @@
26 26
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
27 27
     </el-row>
28 28
     <el-table v-loading="loading" :data="list">
29
-      <el-table-column label="检查日期" align="center" prop="checkDate" width="110">
30
-        <template #default="{ row }">{{ parseTime(row.checkDate, '{y}-{m}-{d}') }}</template>
29
+      <el-table-column label="检查日期" align="center" prop="checkDate" width="170">
30
+        <template #default="{ row }">{{ row.checkDate }}</template>
31 31
       </el-table-column>
32 32
       <el-table-column label="寝室所在位置" align="center" prop="dormLocation" width="120" />
33 33
       <el-table-column label="寝室号" align="center" prop="dormNo" width="90" />

+ 2 - 2
src/views/ledger/examScore/index.vue

@@ -35,8 +35,8 @@
35 35
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
36 36
     </el-row>
37 37
     <el-table v-loading="loading" :data="list">
38
-      <el-table-column label="考试日期" align="center" prop="examDate" width="110">
39
-        <template #default="{ row }">{{ parseTime(row.examDate, '{y}-{m}-{d}') }}</template>
38
+      <el-table-column label="考试日期" align="center" prop="examDate" width="170">
39
+        <template #default="{ row }">{{ row.examDate }}</template>
40 40
       </el-table-column>
41 41
       <el-table-column label="部门名称" align="center" prop="deptName" />
42 42
       <el-table-column label="队室/班组" align="center" prop="teamName" />

+ 11 - 2
src/views/ledger/import/index.vue

@@ -147,7 +147,8 @@ import {
147 147
   importLeaderDuty,
148 148
   importHealthSoldier,
149 149
   importDormFireSafety,
150
-  importTrainingIssue
150
+  importTrainingIssue,
151
+  importQualificationLevel
151 152
 } from '@/api/ledger/index'
152 153
 
153 154
 defineOptions({ name: 'LedgerImport' })
@@ -360,7 +361,15 @@ const importItems = reactive([
360 361
     loading: false,
361 362
     lastResult: null
362 363
   },
363
-
364
+  {
365
+    key: 'qualificationLevel',
366
+    title: '职业资格等级',
367
+    desc: '职业资格等级流程记录',
368
+    icon: 'Medal',
369
+    api: importQualificationLevel,
370
+    loading: false,
371
+    lastResult: null
372
+  },
364 373
   // {
365 374
   //   key: 'dailyTraining',
366 375
   //   title: '日常培训记录',

+ 4 - 4
src/views/ledger/leaveSpecial/index.vue

@@ -38,11 +38,11 @@
38 38
       <el-table-column label="队室/班组" align="center" prop="teamName" />
39 39
       <el-table-column label="姓名" align="center" prop="personName" />
40 40
       <el-table-column label="假期类型" align="center" prop="leaveType" />
41
-      <el-table-column label="开始日期" align="center" prop="startDate" width="110">
42
-        <template #default="{ row }">{{ parseTime(row.startDate, '{y}-{m}-{d}') }}</template>
41
+      <el-table-column label="开始日期" align="center" prop="startDate" width="170">
42
+        <template #default="{ row }">{{ row.startDate }}</template>
43 43
       </el-table-column>
44
-      <el-table-column label="结束日期" align="center" prop="endDate" width="110">
45
-        <template #default="{ row }">{{ parseTime(row.endDate, '{y}-{m}-{d}') }}</template>
44
+      <el-table-column label="结束日期" align="center" prop="endDate" width="170">
45
+        <template #default="{ row }">{{ row.endDate }}</template>
46 46
       </el-table-column>
47 47
       <el-table-column label="天数" align="center" prop="days" />
48 48
       <el-table-column label="扣分" align="center" prop="deductScore">

+ 2 - 2
src/views/ledger/patrolInspection/index.vue

@@ -27,8 +27,8 @@
27 27
     </el-row>
28 28
 
29 29
     <el-table v-loading="loading" :data="list">
30
-      <el-table-column label="巡查日期" align="center" prop="recordDate" width="110">
31
-        <template #default="{ row }">{{ parseTime(row.recordDate, '{y}-{m}-{d}') }}</template>
30
+      <el-table-column label="巡查日期" align="center" prop="recordDate" width="170">
31
+        <template #default="{ row }">{{ row.recordDate }}</template>
32 32
       </el-table-column>
33 33
       <el-table-column label="部门名称" align="center" prop="deptName" />
34 34
       <el-table-column label="队室/班组" align="center" prop="teamName" />

+ 169 - 0
src/views/ledger/qualificationCertificateStatus/index.vue

@@ -0,0 +1,169 @@
1
+<template>
2
+  <div class="app-container">
3
+    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
4
+      <el-form-item label="部门名称" prop="deptId">
5
+        <el-select v-model="queryParams.deptId" placeholder="请选择部门" style="width:150px" clearable filterable
6
+          @change="handleQueryDeptChange">
7
+          <el-option v-for="d in queryDeptOptions" :key="d.deptId" :label="d.deptName" :value="d.deptId" />
8
+        </el-select>
9
+      </el-form-item>
10
+      <el-form-item label="队室/班组" prop="teamId">
11
+        <el-select v-model="queryParams.teamId" placeholder="请选择队室/班组" style="width:150px" clearable filterable
12
+          @change="handleQueryTeamChange">
13
+          <el-option v-for="t in queryTeamOptions" :key="t.id" :label="t.label" :value="t.id" />
14
+        </el-select>
15
+      </el-form-item>
16
+      <el-form-item label="小组" prop="groupId">
17
+        <el-select v-model="queryParams.groupId" placeholder="请选择小组" style="width:150px" clearable filterable
18
+          @change="handleQueryGroupChange">
19
+          <el-option v-for="g in queryGroupOptions" :key="g.id" :label="g.label" :value="g.id" />
20
+        </el-select>
21
+      </el-form-item>
22
+      <el-form-item label="员工姓名" prop="personId">
23
+        <el-select v-model="queryParams.personId" placeholder="请选择员工" style="width:150px" clearable filterable>
24
+          <el-option v-for="p in queryPersonOptions" :key="p.userId" :label="p.nickName" :value="p.userId" />
25
+        </el-select>
26
+      </el-form-item>
27
+      <el-form-item label="录入时间">
28
+        <el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-"
29
+          start-placeholder="开始日期" end-placeholder="结束日期" clearable />
30
+      </el-form-item>
31
+      <el-form-item>
32
+        <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
33
+        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
34
+      </el-form-item>
35
+    </el-form>
36
+
37
+    <!-- <el-row :gutter="10" class="mb8">
38
+      <el-col :span="1.5">
39
+        <el-button type="warning" plain icon="Download" @click="handleExport"
40
+          v-hasPermi="['ledger:qualification:export']">导出</el-button>
41
+      </el-col>
42
+      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
43
+    </el-row> -->
44
+
45
+    <el-table v-loading="loading" :data="list" style="width: 100%;" fit="true"
46
+      :scrollbar-always-on="true">
47
+      <el-table-column label="部门名称" align="center" prop="deptName" min-width="120" resizable />
48
+      <el-table-column label="队室/班组" align="center" prop="teamName" min-width="120" resizable />
49
+      <el-table-column label="小组" align="center" prop="groupName" min-width="120" resizable />
50
+      <el-table-column label="姓名" align="center" prop="personName" width="80" resizable />
51
+      <el-table-column label="一级发证时间" align="center" prop="levelOneTime" width="120" resizable>
52
+        <template #default="{ row }">{{ row.levelOneTime || '-' }}</template>
53
+      </el-table-column>
54
+      <el-table-column label="二级发证时间" align="center" prop="levelTwoTime" width="120" resizable>
55
+        <template #default="{ row }">{{ row.levelTwoTime || '-' }}</template>
56
+      </el-table-column>
57
+      <el-table-column label="三级发证时间" align="center" prop="levelThreeTime" width="120" resizable>
58
+        <template #default="{ row }">{{ row.levelThreeTime || '-' }}</template>
59
+      </el-table-column>
60
+      <el-table-column label="四级发证时间" align="center" prop="levelFourTime" width="120" resizable>
61
+        <template #default="{ row }">{{ row.levelFourTime || '-' }}</template>
62
+      </el-table-column>
63
+      <el-table-column label="五级发证时间" align="center" prop="levelFiveTime" width="120" resizable>
64
+        <template #default="{ row }">{{ row.levelFiveTime || '-' }}</template>
65
+      </el-table-column>
66
+      <el-table-column label="录入时间" align="center" prop="createTime" width="170" resizable>
67
+        <template #default="{ row }">{{ row.createTime }}</template>
68
+      </el-table-column>
69
+    </el-table>
70
+    <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
71
+      v-model:limit="queryParams.pageSize" @pagination="getList" />
72
+  </div>
73
+</template>
74
+
75
+<script setup>
76
+import { ref, reactive, onMounted } from 'vue'
77
+import { listDept } from '@/api/system/dept'
78
+import { deptTreeSelect } from '@/api/system/user'
79
+import { listUser } from '@/api/system/user'
80
+import { listQualificationCertificateStatus } from '@/api/ledger/index'
81
+
82
+defineOptions({ name: 'LedgerQualificationCertificateStatus' })
83
+
84
+const loading = ref(false), list = ref([]), total = ref(0), showSearch = ref(true)
85
+const dateRange = ref([]), queryRef = ref(null)
86
+
87
+const queryParams = reactive({
88
+  pageNum: 1,
89
+  pageSize: 10,
90
+  deptId: null,
91
+  teamId: null,
92
+  groupId: null,
93
+  personId: null
94
+})
95
+
96
+const queryDeptOptions = ref([])
97
+const queryTeamOptions = ref([])
98
+const queryGroupOptions = ref([])
99
+const queryPersonOptions = ref([])
100
+
101
+async function loadQueryDepts() {
102
+  const r = await listDept()
103
+  queryDeptOptions.value = (r.data || []).filter(d => d.deptType === 'BRIGADE')
104
+}
105
+
106
+async function handleQueryDeptChange(deptId) {
107
+  queryParams.teamId = null; queryParams.groupId = null; queryParams.personId = null
108
+  queryTeamOptions.value = []; queryGroupOptions.value = []; queryPersonOptions.value = []
109
+  if (deptId) {
110
+    const r = await deptTreeSelect({ parentId: deptId })
111
+    queryTeamOptions.value = r.data || []
112
+  }
113
+}
114
+
115
+async function handleQueryTeamChange(val) {
116
+  queryParams.groupId = null; queryParams.personId = null
117
+  queryGroupOptions.value = []; queryPersonOptions.value = []
118
+  if (val) {
119
+    const r = await deptTreeSelect({ parentId: val })
120
+    queryGroupOptions.value = r.data || []
121
+  }
122
+}
123
+
124
+async function handleQueryGroupChange(val) {
125
+  queryParams.personId = null
126
+  queryPersonOptions.value = []
127
+  if (val) {
128
+    const r = await listUser({ deptId: val, pageSize: 9999 })
129
+    queryPersonOptions.value = r.rows || []
130
+  }
131
+}
132
+
133
+function getList() {
134
+  loading.value = true
135
+  const p = { ...queryParams }
136
+  if (dateRange.value?.length === 2) {
137
+    p.beginTime = dateRange.value[0]
138
+    p.endTime = dateRange.value[1]
139
+  }
140
+  listQualificationCertificateStatus(p).then(r => {
141
+    list.value = r.rows
142
+    total.value = r.total
143
+  }).finally(() => loading.value = false)
144
+}
145
+
146
+function handleQuery() { queryParams.pageNum = 1; getList() }
147
+
148
+function resetQuery() {
149
+  dateRange.value = []
150
+  Object.assign(queryParams, {
151
+    pageNum: 1,
152
+    pageSize: 10,
153
+    deptId: null,
154
+    teamId: null,
155
+    groupId: null,
156
+    personId: null
157
+  })
158
+  queryTeamOptions.value = []; queryGroupOptions.value = []; queryPersonOptions.value = []
159
+  queryRef.value?.resetFields()
160
+  handleQuery()
161
+}
162
+
163
+
164
+
165
+onMounted(() => {
166
+  loadQueryDepts()
167
+  getList()
168
+})
169
+</script>

+ 0 - 0
src/views/ledger/qualificationCertificateStatus/职业资格证书情况


+ 5 - 5
src/views/ledger/realtimeInterception/index.vue

@@ -7,8 +7,8 @@
7 7
       <el-form-item label="队室/班组" prop="teamName">
8 8
         <el-input v-model="queryParams.teamName" placeholder="请输入队室/班组" clearable @keyup.enter="handleQuery" />
9 9
       </el-form-item>
10
-      <el-form-item label="查获人" prop="inspectorName">
11
-        <el-input v-model="queryParams.inspectorName" placeholder="请输入查获人" clearable @keyup.enter="handleQuery" />
10
+      <el-form-item label="责任人" prop="inspectorName">
11
+        <el-input v-model="queryParams.inspectorName" placeholder="请输入责任人" clearable @keyup.enter="handleQuery" />
12 12
       </el-form-item>
13 13
 
14 14
       <el-form-item label="记录日期">
@@ -30,12 +30,12 @@
30 30
     </el-row>
31 31
 
32 32
     <el-table v-loading="loading" :data="list">
33
-      <el-table-column label="记录日期" align="center" prop="recordDate" width="110">
34
-        <template #default="{ row }">{{ parseTime(row.recordDate, '{y}-{m}-{d}') }}</template>
33
+      <el-table-column label="记录日期" align="center" prop="recordDate" width="170">
34
+        <template #default="{ row }">{{ row.recordDate }}</template>
35 35
       </el-table-column>
36 36
       <el-table-column label="部门名称" align="center" prop="deptName" />
37 37
       <el-table-column label="队室/班组" align="center" prop="teamName" />
38
-      <el-table-column label="查获人" align="center" prop="inspectorName" />
38
+      <el-table-column label="责任人" align="center" prop="inspectorName" />
39 39
       <el-table-column label="通道号" align="center" prop="channelNo" />
40 40
       <el-table-column label="旅客姓名" align="center" prop="passengerName" />
41 41
       <el-table-column label="航班号" align="center" prop="flightNo" />

+ 3 - 3
src/views/ledger/rewardApproval/index.vue

@@ -10,7 +10,7 @@
10 10
       <el-form-item label="姓名" prop="personName">
11 11
         <el-input v-model="queryParams.personName" placeholder="请输入姓名" clearable @keyup.enter="handleQuery" />
12 12
       </el-form-item>
13
-      <el-form-item label="审批日期">
13
+      <el-form-item label="查获(事件)时间">
14 14
         <el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD"
15 15
           range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" clearable />
16 16
       </el-form-item>
@@ -26,8 +26,8 @@
26 26
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
27 27
     </el-row>
28 28
     <el-table v-loading="loading" :data="list">
29
-      <el-table-column label="审批日期" align="center" prop="approveDate" width="110">
30
-        <template #default="{ row }">{{ parseTime(row.approveDate, '{y}-{m}-{d}') }}</template>
29
+      <el-table-column label="查获(事件)时间" align="center" prop="approveDate" width="170">
30
+        <template #default="{ row }">{{ row.approveDate }}</template>
31 31
       </el-table-column>
32 32
       <el-table-column label="部门名称" align="center" prop="deptName" />
33 33
       <el-table-column label="队室/班组" align="center" prop="teamName" />

+ 2 - 2
src/views/ledger/rewardPenalty/index.vue

@@ -38,8 +38,8 @@
38 38
 
39 39
     <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
40 40
       <el-table-column type="selection" width="55" align="center" />
41
-      <el-table-column label="记录日期" align="center" prop="recordDate" width="110">
42
-        <template #default="{ row }">{{ parseTime(row.recordDate, '{y}-{m}-{d}') }}</template>
41
+      <el-table-column label="记录日期" align="center" prop="recordDate" width="170">
42
+        <template #default="{ row }">{{ row.recordDate }}</template>
43 43
       </el-table-column>
44 44
       <el-table-column label="部门名称" align="center" prop="deptName" />
45 45
       <el-table-column label="队室/班组" align="center" prop="teamName" />

+ 2 - 2
src/views/ledger/securityTest/index.vue

@@ -26,8 +26,8 @@
26 26
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
27 27
     </el-row>
28 28
     <el-table v-loading="loading" :data="list">
29
-      <el-table-column label="测试日期" align="center" prop="recordDate" width="110">
30
-        <template #default="{ row }">{{ parseTime(row.recordDate, '{y}-{m}-{d}') }}</template>
29
+      <el-table-column label="测试日期" align="center" prop="recordDate" width="170">
30
+        <template #default="{ row }">{{ row.recordDate }}</template>
31 31
       </el-table-column>
32 32
       <el-table-column label="部门名称" align="center" prop="deptName" />
33 33
       <el-table-column label="队室/班组" align="center" prop="teamName" />

+ 2 - 2
src/views/ledger/seizureStats/index.vue

@@ -23,8 +23,8 @@
23 23
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
24 24
     </el-row>
25 25
     <el-table v-loading="loading" :data="list">
26
-      <el-table-column label="查获日期" align="center" prop="recordDate" width="110">
27
-        <template #default="{ row }">{{ parseTime(row.recordDate, '{y}-{m}-{d}') }}</template>
26
+      <el-table-column label="查获日期" align="center" prop="recordDate" width="170">
27
+        <template #default="{ row }">{{ row.recordDate }}</template>
28 28
       </el-table-column>
29 29
       <el-table-column label="部门名称" align="center" prop="deptName" />
30 30
       <el-table-column label="队室/班组" align="center" prop="teamName" />

+ 5 - 5
src/views/ledger/servicePatrol/index.vue

@@ -7,8 +7,8 @@
7 7
       <el-form-item label="队室/班组" prop="teamName">
8 8
         <el-input v-model="queryParams.teamName" placeholder="请输入队室/班组" clearable @keyup.enter="handleQuery" />
9 9
       </el-form-item>
10
-      <el-form-item label="被查人" prop="inspectedName">
11
-        <el-input v-model="queryParams.inspectedName" placeholder="请输入被查人" clearable @keyup.enter="handleQuery" />
10
+      <el-form-item label="责任人" prop="inspectedName">
11
+        <el-input v-model="queryParams.inspectedName" placeholder="请输入责任人" clearable @keyup.enter="handleQuery" />
12 12
       </el-form-item>
13 13
 
14 14
       <el-form-item label="记录日期">
@@ -30,13 +30,13 @@
30 30
     </el-row>
31 31
 
32 32
     <el-table v-loading="loading" :data="list">
33
-      <el-table-column label="巡查日期" align="center" prop="recordDate" width="110">
34
-        <template #default="{ row }">{{ parseTime(row.recordDate, '{y}-{m}-{d}') }}</template>
33
+      <el-table-column label="巡查日期" align="center" prop="recordDate" width="170">
34
+        <template #default="{ row }">{{ row.recordDate }}</template>
35 35
       </el-table-column>
36 36
       <el-table-column label="部门名称" align="center" prop="deptName" />
37 37
       <el-table-column label="队室/班组" align="center" prop="teamName" />
38 38
       <el-table-column label="巡查人" align="center" prop="inspectorName" />
39
-      <el-table-column label="被查人" align="center" prop="inspectedName" />
39
+      <el-table-column label="责任人" align="center" prop="inspectedName" />
40 40
       <el-table-column label="服务类型" align="center" prop="serviceType" />
41 41
       <el-table-column label="问题描述" align="center" prop="problemDesc" show-overflow-tooltip />
42 42
       <el-table-column label="扣分" align="center" prop="deductScore">

+ 5 - 5
src/views/ledger/supervisionProblem/index.vue

@@ -10,8 +10,8 @@
10 10
       <el-form-item label="巡查人" prop="inspectorName">
11 11
         <el-input v-model="queryParams.inspectorName" placeholder="请输入巡查人" clearable @keyup.enter="handleQuery" />
12 12
       </el-form-item>
13
-      <el-form-item label="被查人" prop="inspectedName">
14
-        <el-input v-model="queryParams.inspectedName" placeholder="请输入被查人" clearable @keyup.enter="handleQuery" />
13
+      <el-form-item label="责任人" prop="inspectedName">
14
+        <el-input v-model="queryParams.inspectedName" placeholder="请输入责任人" clearable @keyup.enter="handleQuery" />
15 15
       </el-form-item>
16 16
       <el-form-item label="记录日期">
17 17
         <el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD"
@@ -32,14 +32,14 @@
32 32
     </el-row>
33 33
 
34 34
     <el-table v-loading="loading" :data="list">
35
-      <el-table-column label="记录日期" align="center" prop="recordDate" width="110">
36
-        <template #default="{ row }">{{ parseTime(row.recordDate, '{y}-{m}-{d}') }}</template>
35
+      <el-table-column label="记录日期" align="center" prop="recordDate" width="170">
36
+        <template #default="{ row }">{{ row.recordDate }}</template>
37 37
       </el-table-column>
38 38
       <el-table-column label="部门名称" align="center" prop="deptName" />
39 39
       <el-table-column label="队室/班组" align="center" prop="teamName" />
40 40
       <el-table-column label="小组" align="center" prop="groupName" />
41 41
       <el-table-column label="巡查人" align="center" prop="inspectorName" />
42
-      <el-table-column label="被查人" align="center" prop="inspectedName" />
42
+      <el-table-column label="责任人" align="center" prop="inspectedName" />
43 43
       <el-table-column label="问题类型" align="center" prop="problemType" />
44 44
       <el-table-column label="问题描述" align="center" prop="problemDesc" show-overflow-tooltip />
45 45
       <el-table-column label="问题地点" align="center" prop="location" />

+ 3 - 3
src/views/ledger/terminalBonus/index.vue

@@ -10,7 +10,7 @@
10 10
       <el-form-item label="姓名" prop="personName">
11 11
         <el-input v-model="queryParams.personName" placeholder="请输入姓名" clearable @keyup.enter="handleQuery" />
12 12
       </el-form-item>
13
-      <el-form-item label="审核日期">
13
+      <el-form-item label="计分时间">
14 14
         <el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD"
15 15
           range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" clearable />
16 16
       </el-form-item>
@@ -26,8 +26,8 @@
26 26
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
27 27
     </el-row>
28 28
     <el-table v-loading="loading" :data="list">
29
-      <el-table-column label="审核日期" align="center" prop="approveDate" width="110">
30
-        <template #default="{ row }">{{ parseTime(row.approveDate, '{y}-{m}-{d}') }}</template>
29
+      <el-table-column label="计分时间" align="center" prop="scoreDate" width="170">
30
+        <template #default="{ row }">{{ row.scoreDate }}</template>
31 31
       </el-table-column>
32 32
       <el-table-column label="部门名称" align="center" prop="deptName" />
33 33
       <el-table-column label="队室/班组" align="center" prop="teamName" />

+ 2 - 2
src/views/ledger/trainingIssue/index.vue

@@ -30,8 +30,8 @@
30 30
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
31 31
     </el-row>
32 32
     <el-table v-loading="loading" :data="list">
33
-      <el-table-column label="问题台账日期" align="center" prop="recordDate" width="120">
34
-        <template #default="{ row }">{{ parseTime(row.recordDate, '{y}-{m}-{d}') }}</template>
33
+      <el-table-column label="问题台账日期" align="center" prop="recordDate" width="170">
34
+        <template #default="{ row }">{{ row.recordDate }}</template>
35 35
       </el-table-column>
36 36
       <el-table-column label="班组" align="center" prop="teamName" width="120" />
37 37
       <el-table-column label="处理人" align="center" prop="handler" width="100" />

+ 2 - 2
src/views/ledger/unsafeEvent/index.vue

@@ -26,8 +26,8 @@
26 26
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
27 27
     </el-row>
28 28
     <el-table v-loading="loading" :data="list">
29
-      <el-table-column label="事件日期" align="center" prop="recordDate" width="110">
30
-        <template #default="{ row }">{{ parseTime(row.recordDate, '{y}-{m}-{d}') }}</template>
29
+      <el-table-column label="事件日期" align="center" prop="recordDate" width="170">
30
+        <template #default="{ row }">{{ row.recordDate }}</template>
31 31
       </el-table-column>
32 32
       <el-table-column label="部门名称" align="center" prop="deptName" />
33 33
       <el-table-column label="队室/班组" align="center" prop="teamName" />

+ 1 - 1
src/views/portraitManagement/components/ChannelCheckChart.vue

@@ -1,5 +1,5 @@
1 1
 <template>
2
-  <Card title="每日通道过检率">
2
+  <Card title="通道高峰过检率">
3 3
     <div ref="bar" style="width: 100%; height: 100%;"></div>
4 4
   </Card>
5 5
 </template>

+ 21 - 0
src/views/portraitManagement/components/ProfileBasicDistribution.vue

@@ -96,6 +96,13 @@ const updateGenderChart = (data) => {
96 96
       textStyle: { color: '#fff' },
97 97
       formatter: '{b}: {c} ({d}%)'
98 98
     },
99
+    legend: {
100
+      show: true,
101
+      textStyle: { color: '#fff' },
102
+      top: 0,
103
+      itemWidth: 10,
104
+      itemHeight: 10
105
+    },
99 106
     series: [{
100 107
       type: 'pie',
101 108
       radius: ['38%', '55%'],
@@ -120,6 +127,13 @@ const updateNationChart = (data) => {
120 127
       textStyle: { color: '#fff' },
121 128
       formatter: '{b}: {c} ({d}%)'
122 129
     },
130
+    legend: {
131
+      show: true,
132
+      textStyle: { color: '#fff' },
133
+      top: 0,
134
+      itemWidth: 10,
135
+      itemHeight: 10
136
+    },
123 137
     series: [{
124 138
       type: 'pie',
125 139
       radius: ['38%', '55%'],
@@ -144,6 +158,13 @@ const updatePoliticalChart = (data) => {
144 158
       textStyle: { color: '#fff' },
145 159
       formatter: '{b}: {c} ({d}%)'
146 160
     },
161
+    legend: {
162
+      show: true,
163
+      textStyle: { color: '#fff' },
164
+      top: 0,
165
+      itemWidth: 10,
166
+      itemHeight: 10
167
+    },
147 168
     series: [{
148 169
       type: 'pie',
149 170
       radius: ['38%', '55%'],

+ 23 - 2
src/views/portraitManagement/components/ProfilePositionDistribution.vue

@@ -96,6 +96,12 @@ const updateSkillChart = (data) => {
96 96
       textStyle: { color: '#fff' },
97 97
       formatter: '{b}: {c}'
98 98
     },
99
+    legend: {
100
+      show: true,
101
+      textStyle: { color: '#fff' },
102
+      top: 0,
103
+      data: ['职业资格等级']
104
+    },
99 105
     xAxis: {
100 106
       type: 'category',
101 107
       data: chartData.categories,
@@ -110,6 +116,7 @@ const updateSkillChart = (data) => {
110 116
     },
111 117
     series: [{
112 118
       type: 'bar',
119
+      name: '职业资格等级',
113 120
       data: chartData.values,
114 121
       itemStyle: {
115 122
         color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
@@ -137,10 +144,16 @@ const updateOperateChart = (data) => {
137 144
       textStyle: { color: '#fff' },
138 145
       formatter: '{b}: {c}'
139 146
     },
147
+    legend: {
148
+      show: true,
149
+      textStyle: { color: '#fff' },
150
+      top: 0,
151
+      data: ['开机年限']
152
+    },
140 153
     xAxis: {
141 154
       type: 'category',
142 155
       data: chartData.categories,
143
-      axisLabel: { color: '#a0c4ff', fontSize: 10 },
156
+      axisLabel: { color: '#a0c4ff', fontSize: 10,rotate: 30 },
144 157
       axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } }
145 158
     },
146 159
     yAxis: {
@@ -151,6 +164,7 @@ const updateOperateChart = (data) => {
151 164
     },
152 165
     series: [{
153 166
       type: 'bar',
167
+      name: '开机年限',
154 168
       data: chartData.values,
155 169
       itemStyle: {
156 170
         color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
@@ -177,10 +191,16 @@ const updatePostChart = (data) => {
177 191
       textStyle: { color: '#fff' },
178 192
       formatter: '{b}: {c}'
179 193
     },
194
+    legend: {
195
+      show: true,
196
+      textStyle: { color: '#fff' },
197
+      top: 0,
198
+      data: ['岗位资质']
199
+    },
180 200
     xAxis: {
181 201
       type: 'category',
182 202
       data: chartData.categories,
183
-      axisLabel: { color: '#a0c4ff', fontSize: 10 },
203
+      axisLabel: { color: '#a0c4ff', fontSize: 10,rotate: 30 },
184 204
       axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } }
185 205
     },
186 206
     yAxis: {
@@ -191,6 +211,7 @@ const updatePostChart = (data) => {
191 211
     },
192 212
     series: [{
193 213
       type: 'bar',
214
+      name: '岗位资质',
194 215
       data: chartData.values,
195 216
       itemStyle: {
196 217
         color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [

+ 68 - 4
src/views/portraitManagement/components/ProfileRadar.vue

@@ -1,17 +1,25 @@
1 1
 <template>
2 2
   <InfoCard :title="title">
3 3
     <div class="radar-section">
4
-      <div ref="radarChartRef" class="radar-chart"></div>
4
+      <div>
5
+        <div class="chart-legend">
6
+          <div class="legend-item legend-warning"><span></span>预警线(低于75分)</div>
7
+          <!-- <div class="legend-item legend-normal"><span></span>正常线(75~90分)</div> -->
8
+          <div class="legend-item legend-excellent"><span></span>优秀线(高于90分)</div>
9
+          <div class="legend-item legend-current"><span></span>当前分值</div>
10
+        </div>
11
+        <div ref="radarChartRef" class="radar-chart"></div>
12
+      </div>
5 13
       <div class="radar-list">
6 14
         <div class="radar-item" v-for="(item, index) in computedRadarData" :key="index">
7 15
           <span class="item-label">{{ item.name }}</span>
8 16
           <div class="progress-row">
9 17
             <div class="progress-bar">
10
-            <div class="progress-fill"
11
-              :style="{ width: ((item.finalScore || 0) / (computedMaxScore || 1) * 100) + '%', background: `linear-gradient(90deg, ${item.color}33, ${item.color})` }">
18
+              <div class="progress-fill"
19
+                :style="{ width: ((item.finalScore || 0) / (computedMaxScore || 1) * 100) + '%', background: `linear-gradient(90deg, ${item.color}33, ${item.color})` }">
12 20
                 <span class="progress-end" :style="{ background: item.color }"></span>
21
+              </div>
13 22
             </div>
14
-          </div>
15 23
             <span class="item-value">{{ item.finalScore || 0 }}</span>
16 24
           </div>
17 25
         </div>
@@ -181,6 +189,62 @@ onUnmounted(() => {
181 189
   gap: 20px;
182 190
   min-height: 400px;
183 191
 
192
+  .chart-legend {
193
+    display: flex;
194
+    justify-content: center;
195
+    align-items: center;
196
+    flex-wrap: wrap;
197
+    gap: 10px 18px;
198
+    padding-top: 8px;
199
+    color: #fff;
200
+    font-size: 14px;
201
+  }
202
+
203
+  .legend-item {
204
+    display: flex;
205
+    align-items: center;
206
+    gap: 6px;
207
+
208
+    span {
209
+      width: 24px;
210
+      height: 10px;
211
+      border: 2px solid currentColor;
212
+      border-radius: 2px;
213
+    }
214
+  }
215
+
216
+  .legend-warning {
217
+    color: #fe4322;
218
+
219
+    span {
220
+      background-color: #fe4322;
221
+    }
222
+  }
223
+
224
+  .legend-normal {
225
+    color: #fff;
226
+
227
+    span {
228
+      background-color: #fff;
229
+    }
230
+  }
231
+
232
+  .legend-excellent {
233
+    color: #8EC742;
234
+
235
+    span {
236
+      background-color: #8EC742;
237
+    }
238
+  }
239
+
240
+  .legend-current {
241
+    color: #1890ff;
242
+
243
+    span {
244
+      background-color: #1890ff;
245
+    }
246
+  }
247
+
184 248
   .radar-chart {
185 249
     flex: 1;
186 250
     min-width: 300px;

+ 1 - 0
src/views/portraitManagement/components/SeizedItems.vue

@@ -31,6 +31,7 @@ const setChartsOptionsBar = computed(() => {
31 31
       textStyle: { color: '#fff' }
32 32
     },
33 33
     legend: {
34
+      type: 'scroll',
34 35
       top: 'center',
35 36
       left: '0%',
36 37
       orient: 'vertical',

+ 5 - 1
src/views/portraitManagement/components/SeizedNum.vue

@@ -1,5 +1,5 @@
1 1
 <template>
2
-  <Card title="每日查获数量(个人对比)">
2
+  <Card :title="title">
3 3
     <div ref="bar" style="width: 100%; height: 100%;"></div>
4 4
   </Card>
5 5
 </template>
@@ -10,6 +10,10 @@
10 10
   import { useECharts } from '@/hooks/useEcharts.js';
11 11
   const bar = ref(null)
12 12
   const props = defineProps({
13
+    title: {
14
+      type: String,
15
+      default: '每日查获数量(个人对比)'
16
+    },
13 17
     chartsData: {
14 18
       type: Array,
15 19
       default: () => []

+ 1 - 1
src/views/portraitManagement/components/SupervisionDistribution.vue

@@ -33,7 +33,7 @@ const setChartsOptionsBar = computed(() => {
33 33
     xAxis: {
34 34
       type: 'category',
35 35
       data: labels,
36
-      axisLabel: { color: '#fff', fontSize: 14 },
36
+      axisLabel: { color: '#fff', fontSize: 13, rotate: 30 },
37 37
       axisLine: { lineStyle: { color: '#344067' } }
38 38
     },
39 39
     yAxis: [

+ 1 - 1
src/views/portraitManagement/deptProfile/component/runData.vue

@@ -16,7 +16,7 @@
16 16
         <SeizedNumAll :chartsData="countSeizureTotalQuantityData"/>
17 17
       </div>
18 18
       <div class="col">
19
-        <SeizedNum :chartsData="countSeizureSingleQuantityData" />
19
+        <SeizedNum :chartsData="countSeizureSingleQuantityData" :title="'每日查获数量(班组对比)'"/>
20 20
       </div>
21 21
     </div>
22 22
     <div class="row">

+ 95 - 8
src/views/portraitManagement/employeeProfile/index.vue

@@ -65,7 +65,10 @@
65 65
                   </div>
66 66
                   <div class="info-item-content">
67 67
                     <div class="info-item-label">技能等级:</div>
68
-                    <div class="info-item-value">{{ portrait.qualificationLevelText || '-' }}</div>
68
+                    <div class="info-item-value">
69
+                      {{ portrait.qualificationLevelText || '-' }}
70
+                      <span class="info-item-tag" style="margin-left: 10px;margin-top: 2px;" v-if="!!portrait.xrayOperatorStarttime">X射线安检仪操作岗位</span>
71
+                    </div>
69 72
                   </div>
70 73
                 </div>
71 74
                 <div class="info-item">
@@ -84,19 +87,20 @@
84 87
                 <div class="score-progress">
85 88
                   <el-progress type="circle" :width="160" :stroke-width="18" color="#5BE39E" :percentage="(portrait.totalScore || -2) + 2">
86 89
                     <div class="percentage-content">
87
-                      <span class="percentage-value">{{ (portrait.totalScore || -2) + 2 }}</span>
90
+                      <span class="percentage-value">{{ (portrait.totalScore || 0)  }}</span>
88 91
                       <span class="percentage-text">综合得分</span>
89 92
                     </div>
90 93
                   </el-progress>
91 94
                 </div>
92 95
                 <div class="score-box">
93
-                  <div class="score-row">
96
+                  <!-- <div class="score-row">
94 97
                     <span class="score-col">评分:</span>
95 98
                     <span class="score-col-2">{{ portrait.totalScore || 0 }}</span>
96
-                  </div>
99
+                  </div> -->
97 100
                   <div class="score-row">
98
-                    <span class="score-col">标签得分:</span>
99
-                    <span class="score-col-2">{{ tagScoreData != null ? (typeof tagScoreData === 'object' ? (tagScoreData.totalScore ?? tagScoreData.score ?? tagScoreData) : tagScoreData) : 0 }}</span>
101
+                    <span class="score-col">附加分:</span>
102
+                    <span class="score-col-2">{{ portrait?.userTags?.split(',').length || 0 }}分</span>
103
+                    <!-- <span class="score-col-2">{{ tagScoreData != null ? (typeof tagScoreData === 'object' ? (tagScoreData.totalScore ?? tagScoreData.score ?? tagScoreData) : tagScoreData) : 0 }}</span> -->
100 104
                   </div>
101 105
                 </div>
102 106
               </div>
@@ -126,9 +130,21 @@
126 130
                 </div>
127 131
               </div>
128 132
             </Card>
133
+            <Card title="职业资格证书情况">
134
+              <div class="cert-info">
135
+                <span class="cert-name">证书名称:{{ portrait?.qualificationLevelText || '-' }} 时间:{{ getQualificationTime|| '-' }}</span>
136
+              
137
+              </div>
138
+            </Card>
129 139
           </div>
130 140
           <div class="content-bottom-center">
131 141
             <Card title="个人能力">
142
+              <div class="chart-legend">
143
+                <div class="legend-item legend-warning"><span></span>预警线(低于75分)</div>
144
+                <!-- <div class="legend-item legend-normal"><span></span>正常线(75~90分)</div> -->
145
+                <div class="legend-item legend-excellent"><span></span>优秀线(高于90分)</div>
146
+                <div class="legend-item legend-current"><span></span>当前员工分值</div>
147
+              </div>
132 148
               <div ref="abilityChart" class="chart-box" @click="goToWarningPage" />
133 149
             </Card>
134 150
           </div>
@@ -225,6 +241,7 @@ import { getEmployeePortrait } from '@/api/score/index'
225 241
 import { countTagScore } from '@/api/portraitManagement/portraitManagement'
226 242
 import { onMounted, onUnmounted, reactive, ref, computed, watch } from 'vue'
227 243
 import { useDict } from '@/utils/dict'
244
+import { getQualificationLevelTime } from '@/api/ledger/index'
228 245
 import { useECharts } from '@/hooks/useEcharts'
229 246
 import useUserStore from '@/store/modules/user'
230 247
 import { useRouter } from 'vue-router'
@@ -243,6 +260,7 @@ const abilityChart = ref(null)
243 260
 const activeDimName = ref(null)
244 261
 const tagScoreData = ref(null)
245 262
 const currentQuery = ref(null)
263
+const qualificationData = ref(null)
246 264
 
247 265
 const scoreDetails = computed(() => portrait.value?.scoreDetails || [])
248 266
 
@@ -279,6 +297,25 @@ const deductTotal = computed(() => {
279 297
   return s.toFixed(2)
280 298
 })
281 299
 
300
+// 职业资格等级枚举
301
+const qualificationLevelMap = [
302
+  { label: '一级', field: 'levelOneTime' },
303
+  { label: '二级', field: 'levelTwoTime' },
304
+  { label: '三级', field: 'levelThreeTime' },
305
+  { label: '四级', field: 'levelFourTime' },
306
+  { label: '五级', field: 'levelFiveTime' }
307
+]
308
+
309
+const getQualificationTime = computed(() => {
310
+  const certName = portrait.value?.qualificationLevelText || '-';
311
+  
312
+  const item = qualificationLevelMap.find(l => l.label === certName)
313
+  if (item) {
314
+    return qualificationData.value?.[item.field] || '-'
315
+  }
316
+  return '-'
317
+})
318
+
282 319
 
283 320
 const getSchooling = (schooling) => {
284 321
   const result = (sys_user_schooling.value || []).find(item => item.value === schooling) || { label: schooling }
@@ -345,7 +382,12 @@ const invokerEmployeePortrait = (query) => {
345 382
       tagScoreData.value = data
346 383
     }
347 384
   })
348
-  return Promise.all([portraitPromise, tagPromise]).finally(() => {
385
+  const qualificationPromise = getQualificationLevelTime({ userId }).then(res => {
386
+    qualificationData.value = res.data || null
387
+  }).catch(() => {
388
+    qualificationData.value = null
389
+  })
390
+  return Promise.all([portraitPromise, tagPromise, qualificationPromise]).finally(() => {
349 391
     loading.value = false
350 392
   })
351 393
 }
@@ -764,9 +806,54 @@ onUnmounted(() => {
764 806
       & > * {
765 807
         height: 100%;
766 808
       }
809
+      .chart-legend {
810
+        display: flex;
811
+        justify-content: center;
812
+        align-items: center;
813
+        flex-wrap: wrap;
814
+        gap: 10px 18px;
815
+        padding-top: 8px;
816
+        color: #fff;
817
+        font-size: 14px;
818
+      }
819
+      .legend-item {
820
+        display: flex;
821
+        align-items: center;
822
+        gap: 6px;
823
+        span {
824
+          width: 24px;
825
+          height: 10px;
826
+          border: 2px solid currentColor;
827
+          border-radius: 2px;
828
+        }
829
+      }
830
+      .legend-warning {
831
+        color: #fe4322;
832
+        span { 
833
+          background-color: #fe4322;
834
+        }
835
+      }
836
+      .legend-normal {
837
+        color: #fff;
838
+        span { 
839
+          background-color: #fff;
840
+        }
841
+      }
842
+      .legend-excellent {
843
+        color: #8EC742;
844
+        span { 
845
+          background-color: #8EC742;
846
+        }
847
+      }
848
+      .legend-current {
849
+        color: #1890ff;
850
+        span { 
851
+          background-color: #1890ff;
852
+        }
853
+      }
767 854
       .chart-box {
768 855
         width: 100%;
769
-        height: 100%;
856
+        height: calc(100% - 34px);
770 857
         overflow: hidden;
771 858
       }
772 859
     }

File diff suppressed because it is too large
+ 0 - 1306
src/views/portraitManagement/employeeProfile/indexOld.vue


+ 1 - 1
src/views/portraitManagement/teamProfile/component/runData.vue

@@ -16,7 +16,7 @@
16 16
         <SeizedNumAll :chartsData="countSeizureTotalQuantityData" />
17 17
       </div>
18 18
       <div class="col">
19
-        <SeizedNum :chartsData="countSeizureSingleQuantityData" />
19
+        <SeizedNum :chartsData="countSeizureSingleQuantityData" :title="'每日查获数量(小组对比)'"/>
20 20
       </div>
21 21
     </div>
22 22
     <div class="row">

+ 18 - 4
src/views/score/event/index.vue

@@ -96,14 +96,14 @@
96 96
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
97 97
     </el-row>
98 98
 
99
-    <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange" style="width: 100%;" fit="true"
100
-      :scrollbar-always-on="true">
99
+    <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange" @sort-change="handleSortChange" style="width: 100%;" fit="true"
100
+      :scrollbar-always-on="true" :default-sort="{ prop: sortField, order: sortOrder }">
101 101
       <el-table-column type="selection" width="55" align="center" resizable />
102 102
       <el-table-column label="配分层级" align="center" prop="org" width="110" resizable>
103 103
         <template #default="{ row }"><dict-tag :options="score_level" :value="row.org" /></template>
104 104
       </el-table-column>
105
-      <el-table-column label="事件时间" align="center" prop="eventTime" width="120" resizable>
106
-        <template #default="{ row }">{{ parseTime(row.eventTime, '{y}-{m}-{d}') }}</template>
105
+      <el-table-column label="事件时间" align="center" prop="eventTime" width="170" resizable sortable="custom">
106
+        <template #default="{ row }">{{ row.eventTime }}</template>
107 107
       </el-table-column>
108 108
       <el-table-column label="维度" align="center" prop="dimensionName" width="150" resizable />
109 109
       <el-table-column label="二级指标" align="center" prop="level2Name" min-width="250" show-overflow-tooltip resizable />
@@ -332,6 +332,8 @@ const channelOptions = ref([])
332 332
 const postTreeData = ref([])
333 333
 
334 334
 const queryParams = reactive({ pageNum: 1, pageSize: 10, personId: null, personName: '', deptId: null, deptName: '', teamId: null, teamName: '', groupId: null, groupName: '', dimensionId: null, sourceType: '', org: '', scoreType: null, regionalId: null, channelId: null, postId: null })
335
+const sortField = ref('eventTime')
336
+const sortOrder = ref('descending')
335 337
 const form = reactive({ id: null, dimensionId: null, dimensionName: '', indicatorId: null, level2Id: null, level2Name: '', level3Id: null, level3Name: '', level4Id: null, level4Name: '', eventTime: '', location: '', personId: null, deptName: '', deptId: null, teamId: null, groupId: null, scoreValue: 0, cascadeScore: 0, eventDesc: '', remark: '', org: '', regionalId: null, channelId: null, postId: null })
336 338
 const formChannelOptions = ref([])
337 339
 const rules = computed(() => {
@@ -540,9 +542,21 @@ function getList() {
540 542
   loading.value = true
541 543
   const p = { ...queryParams }
542 544
   if (dateRange.value?.length === 2) { p['params[beginTime]'] = dateRange.value[0]; p['params[endTime]'] = dateRange.value[1] }
545
+  // 添加排序参数
546
+  if (sortField.value && sortOrder.value) {
547
+    const orderType = sortOrder.value === 'ascending' ? 'asc' : 'desc'
548
+    p[`sorts[${sortField.value}]`] = orderType
549
+  }
543 550
   listScoreEvent(p).then(r => { list.value = r.rows; total.value = r.total }).finally(() => loading.value = false)
544 551
 }
545 552
 
553
+function handleSortChange({ prop, order }) {
554
+  sortField.value = prop
555
+  sortOrder.value = order
556
+  queryParams.pageNum = 1
557
+  getList()
558
+}
559
+
546 560
 function handleQuery() { queryParams.pageNum = 1; getList() }
547 561
 function resetQuery() {
548 562
   dateRange.value = []

+ 81 - 56
src/views/warningPage/index.vue

@@ -74,52 +74,56 @@
74 74
             </div>
75 75
             <div class="employee-card">
76 76
                 <div style="overflow-x: auto;">
77
-                    <table class="data-table" style="width:100%;">
78
-                        <thead>
79
-                            <tr>
80
-                                <th>员工ID</th>
81
-                                <th>姓名</th>
82
-                                <th>所属部门</th>
83
-                                <th>综合评估得分</th>
84
-                                <th>预警等级</th>
85
-                                <th>核心风险/优秀事迹</th>
86
-                                <th>状态标签</th>
87
-                            </tr>
88
-                        </thead>
89
-                        <tbody>
90
-                            <tr v-for="emp in filteredEmployees" :key="emp.id" :class="getRowClass(emp.overallScore)">
91
-                                <td>{{ emp.userId }}</td>
92
-                                <td><strong>{{ emp.nickName }}</strong></td>
93
-                                <td>{{ emp.deptName }}</td>
94
-                                <td>
95
-                                    <span v-if="emp.overallScore < 75" class="score-danger">{{ emp.overallScore }}
96
-                                        分</span>
97
-                                    <span v-else-if="emp.overallScore >= 90" class="score-excellent">{{ emp.overallScore
98
-                                        }}
99
-                                        分</span>
100
-                                    <span v-else style="font-weight:600;">{{ emp.overallScore }} 分</span>
101
-                                </td>
102
-                                <td>
103
-                                    <span v-if="emp.overallScore < 75" class="status-badge"
104
-                                        style="animation: subtlePulse 1s infinite;"><i
105
-                                            class="fas fa-exclamation-triangle"></i> 红色预警</span>
106
-                                    <span v-else-if="emp.overallScore >= 90" class="status-excellent"
107
-                                        style="background:#d1fae5; color:#065f46;"><i class="fas fa-star"></i>
108
-                                        优秀标杆</span>
109
-                                    <span v-else class="status-warning">正常范围</span>
110
-                                </td>
111
-                                <td style="font-size:0.75rem;">{{ emp.coreRisksOrOutstandingAchievements }}</td>
112
-                                <td>
113
-                                    <span v-if="emp.overallScore < 75" style="color:#b91c1c;"><i
114
-                                            class="fas fa-bell"></i>
115
-                                        {{ getAlertLabel(emp.statusLabel) }}</span>
116
-                                    <span v-else-if="emp.overallScore >= 90" style="color:#15803d;"><i
117
-                                            class="fas fa-crown"></i> {{ getAlertLabel(emp.statusLabel) }}</span>
118
-                                    <span v-else>{{ getAlertLabel(emp.statusLabel) }}</span>
119
-                                </td>
120
-                            </tr>
121
-                        </tbody>
122
-                    </table>
77
+                    <el-table :data="filteredEmployees" :row-class-name="getRowClass" style="width:100%;" border stripe>
78
+                        <el-table-column prop="userId" label="员工ID" />
79
+                        <el-table-column prop="nickName" label="姓名">
80
+                            <template #default="{ row }">
81
+                                <strong>{{ row.nickName }}</strong>
82
+                            </template>
83
+                        </el-table-column>
84
+                        <el-table-column prop="deptName" label="所属部门" />
85
+                        <el-table-column prop="eventTime" label="事件时间" sortable width="180" />
86
+                        <el-table-column prop="overallScore" label="综合评估得分" sortable>
87
+                            <template #default="{ row }">
88
+                                <span v-if="row.overallScore < 75" class="score-danger">{{ row.overallScore }} 分</span>
89
+                                <span v-else-if="row.overallScore >= 90" class="score-excellent">{{ row.overallScore }} 分</span>
90
+                                <span v-else style="font-weight:600;">{{ row.overallScore }} 分</span>
91
+                            </template>
92
+                        </el-table-column>
93
+                        <el-table-column prop="overallScore" label="预警等级">
94
+                            <template #default="{ row }">
95
+                                <span v-if="row.overallScore < 75" class="status-badge"
96
+                                    style="animation: subtlePulse 1s infinite;"><i
97
+                                        class="fas fa-exclamation-triangle"></i> 红色预警</span>
98
+                                <span v-else-if="row.overallScore >= 90" class="status-excellent"
99
+                                    style="background:#d1fae5; color:#065f46;"><i class="fas fa-star"></i>
100
+                                    优秀标杆</span>
101
+                                <span v-else class="status-warning">正常范围</span>
102
+                            </template>
103
+                        </el-table-column>
104
+                        <el-table-column prop="coreRisksOrOutstandingAchievements" label="核心风险/优秀事迹" />
105
+                        <el-table-column prop="statusLabel" label="状态标签">
106
+                            <template #default="{ row }">
107
+                                <span v-if="row.overallScore < 75" style="color:#b91c1c;"><i
108
+                                        class="fas fa-bell"></i>
109
+                                    {{ getAlertLabel(row.statusLabel) }}</span>
110
+                                <span v-else-if="row.overallScore >= 90" style="color:#15803d;"><i
111
+                                        class="fas fa-crown"></i> {{ getAlertLabel(row.statusLabel) }}</span>
112
+                                <span v-else>{{ getAlertLabel(row.statusLabel) }}</span>
113
+                            </template>
114
+                        </el-table-column>
115
+                    </el-table>
116
+                    <el-pagination
117
+                        v-show="total > 0"
118
+                        :total="total"
119
+                        v-model:current-page="queryParams.pageNum"
120
+                        v-model:page-size="queryParams.pageSize"
121
+                        :page-sizes="[10, 20, 50, 100]"
122
+                        layout="total, sizes, prev, pager, next, jumper"
123
+                        @size-change="handleSizeChange"
124
+                        @current-change="handlePageChange"
125
+                        style="margin-top: 16px; justify-content: flex-end;"
126
+                    />
123 127
                 </div>
124 128
                 <div class="warning-summary">
125 129
                     <div><i class="fas fa-circle" style="color:#ef4444;"></i> <strong>红色预警</strong> (综合评估 &lt; 75分) 共计
@@ -190,6 +194,10 @@ const coreRisksOrOutstandingAchievementsList = [
190 194
 ]
191 195
 
192 196
 const employeesData = ref([])
197
+const queryParams = reactive({
198
+  pageNum: 1,
199
+  pageSize: 10
200
+})
193 201
 
194 202
 for (let i = 0; i < newNames.length; i++) {
195 203
     employeesData.value.push({
@@ -250,10 +258,8 @@ const getSelectedInfo = (selectedValue) => {
250 258
     return findNodeByValue(cascadeOptions.value, selectedValue)
251 259
 }
252 260
 
253
-const filteredEmployees = computed(() => {
254
-    let result = employeesData.value.ledgerWarningDetailItemList
255
-
256
-
261
+const allFilteredEmployees = computed(() => {
262
+    let result = employeesData.value.ledgerWarningDetailItemList || []
257 263
 
258 264
     // 预警等级筛选
259 265
     if (selectedAlertLevel.value) {
@@ -272,11 +278,26 @@ const filteredEmployees = computed(() => {
272 278
         }
273 279
     }
274 280
 
275
-
276
-
277 281
     return result
278 282
 })
279 283
 
284
+const filteredEmployees = computed(() => {
285
+    const start = (queryParams.pageNum - 1) * queryParams.pageSize
286
+    const end = start + queryParams.pageSize
287
+    return allFilteredEmployees.value.slice(start, end)
288
+})
289
+
290
+const total = computed(() => allFilteredEmployees.value.length)
291
+
292
+function handlePageChange(newPage) {
293
+    queryParams.pageNum = newPage
294
+}
295
+
296
+function handleSizeChange(newSize) {
297
+    queryParams.pageSize = newSize
298
+    queryParams.pageNum = 1
299
+}
300
+
280 301
 const redAlertCount = computed(() => {
281 302
     return employeesData.value?.redAlertNum || 0
282 303
 })
@@ -289,9 +310,9 @@ const avgScore = computed(() => {
289 310
     return employeesData.value?.averageComprehensiveScore || 0
290 311
 })
291 312
 
292
-const getRowClass = (score) => {
293
-    if (score < 75) return "employee-warning-row"
294
-    if (score >= 90) return "employee-excellent-row"
313
+const getRowClass = ({ row }) => {
314
+    if (row.overallScore < 75) return "employee-warning-row"
315
+    if (row.overallScore >= 90) return "employee-excellent-row"
295 316
     return ""
296 317
 }
297 318
 
@@ -348,7 +369,7 @@ const warningDataMap = {
348 369
 }
349 370
 
350 371
 const fetchWarningData = async () => {
351
-    
372
+    queryParams.pageNum = 1
352 373
     let params = {}
353 374
     if (startDate.value && endDate.value) {
354 375
         params.startDate = formatDate(startDate.value)
@@ -419,6 +440,10 @@ watch(cascadeOptions, (val) => {
419 440
     }
420 441
 })
421 442
 
443
+watch(selectedAlertLevel, () => {
444
+    queryParams.pageNum = 1
445
+})
446
+
422 447
 // 监听路由参数变化,回显到级联选择器
423 448
 watch(() => route.query, (query) => {
424 449
     const { id } = query