Explorar o código

Merge branch 'blockingData' into dev

huoyi hai 1 mes
pai
achega
54efaf1a00

+ 1 - 1
src/utils/dict.js

@@ -14,7 +14,7 @@ export function useDict(...args) {
14 14
         res.value[dictType] = dicts
15 15
       } else {
16 16
         getDicts(dictType).then(resp => {
17
-          res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }))
17
+          res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass, remark: p.remark }))
18 18
           useDictStore().setDict(dictType, res.value[dictType])
19 19
         })
20 20
       }

+ 16 - 8
src/utils/index.js

@@ -5,12 +5,12 @@ import { parseTime } from './ruoyi'
5 5
  */
6 6
 export function formatDate(cellValue) {
7 7
   if (cellValue == null || cellValue == "") return ""
8
-  var date = new Date(cellValue) 
8
+  var date = new Date(cellValue)
9 9
   var year = date.getFullYear()
10 10
   var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
11
-  var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() 
12
-  var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() 
13
-  var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() 
11
+  var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
12
+  var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
13
+  var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
14 14
   var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
15 15
   return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
16 16
 }
@@ -218,7 +218,7 @@ export function getTime(type) {
218 218
 export function debounce(func, wait, immediate) {
219 219
   let timeout, args, context, timestamp, result
220 220
 
221
-  const later = function() {
221
+  const later = function () {
222 222
     // 据上一次触发时间间隔
223 223
     const last = +new Date() - timestamp
224 224
 
@@ -235,7 +235,7 @@ export function debounce(func, wait, immediate) {
235 235
     }
236 236
   }
237 237
 
238
-  return function(...args) {
238
+  return function (...args) {
239 239
     context = this
240 240
     timestamp = +new Date()
241 241
     const callNow = immediate && !timeout
@@ -330,7 +330,7 @@ export function makeMap(str, expectsLowerCase) {
330 330
     ? val => map[val.toLowerCase()]
331 331
     : val => map[val]
332 332
 }
333
- 
333
+
334 334
 export const exportDefault = 'export default '
335 335
 
336 336
 export const beautifierConf = {
@@ -387,4 +387,12 @@ export function camelCase(str) {
387 387
 export function isNumberStr(str) {
388 388
   return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
389 389
 }
390
- 
390
+export function formatDateHy(dateString) {
391
+  if (!dateString) return ''
392
+  const date = new Date(dateString)
393
+  const year = date.getFullYear()
394
+  const month = String(date.getMonth() + 1).padStart(2, '0')
395
+  const day = String(date.getDate()).padStart(2, '0')
396
+  return `${year}-${month}-${day}`
397
+}
398
+

+ 383 - 137
src/views/blockingData/blockingDataScreen/components/ModuleOne.vue

@@ -7,7 +7,10 @@
7 7
           <div class="stat-card-inner">
8 8
             <div class="stat-left">
9 9
               <div class="stat-label">查堵总数(所有大队)</div>
10
-              <div class="stat-value">2327</div>
10
+              <div class="stat-content">
11
+                <div class="stat-value">{{ totalBlockedCount }}</div>
12
+                <div class="stat-unit">起</div>
13
+              </div>
11 14
             </div>
12 15
             <div class="stat-right">
13 16
               <div class="stat-title">查堵总数</div>
@@ -19,7 +22,10 @@
19 22
           <div class="stat-card-inner">
20 23
             <div class="stat-left">
21 24
               <div class="stat-label">总查堵万分率</div>
22
-              <div class="stat-value">2.22</div>
25
+              <div class="stat-content">
26
+                <div class="stat-value">{{ totalBlockedRate }}</div>
27
+                <div class="stat-unit">‱</div>
28
+              </div>
23 29
             </div>
24 30
             <div class="stat-right">
25 31
               <div class="stat-title">总查堵万分率</div>
@@ -28,7 +34,7 @@
28 34
           </div>
29 35
         </div>
30 36
       </div>
31
-      
37
+
32 38
       <!-- 第一行:每日查堵数量 -->
33 39
       <div class="chart-row">
34 40
         <div class="chart-item">
@@ -40,7 +46,7 @@
40 46
           <div ref="chart2" class="echarts"></div>
41 47
         </div>
42 48
       </div>
43
-      
49
+
44 50
       <!-- 第二行:每日查堵万分率 -->
45 51
       <div class="chart-row">
46 52
         <div class="chart-item">
@@ -52,7 +58,7 @@
52 58
           <div ref="chart4" class="echarts"></div>
53 59
         </div>
54 60
       </div>
55
-      
61
+
56 62
       <!-- 第三行:每日过检图像数 -->
57 63
       <div class="chart-row">
58 64
         <div class="chart-item">
@@ -64,7 +70,7 @@
64 70
           <div ref="chart6" class="echarts"></div>
65 71
         </div>
66 72
       </div>
67
-      
73
+
68 74
       <!-- 第四行:查堵物品分布和区域查堵数量分布 -->
69 75
       <div class="chart-row">
70 76
         <div class="chart-item">
@@ -76,7 +82,7 @@
76 82
           <div ref="chart8" class="echarts"></div>
77 83
         </div>
78 84
       </div>
79
-      
85
+
80 86
       <!-- 第五行:查堵类型分布 -->
81 87
       <div class="chart-row full-width">
82 88
         <div class="chart-item">
@@ -84,7 +90,7 @@
84 90
           <div ref="chart9" class="echarts"></div>
85 91
         </div>
86 92
       </div>
87
-      
93
+
88 94
       <!-- 第六行:查堵时间段过检行李数及万分率 -->
89 95
       <div class="chart-row full-width">
90 96
         <div class="chart-item">
@@ -92,7 +98,7 @@
92 98
           <div ref="chart10" class="echarts"></div>
93 99
         </div>
94 100
       </div>
95
-      
101
+
96 102
       <!-- 第七行:每日过检行李数及万分率 -->
97 103
       <div class="chart-row full-width">
98 104
         <div class="chart-item">
@@ -100,7 +106,7 @@
100 106
           <div ref="chart11" class="echarts"></div>
101 107
         </div>
102 108
       </div>
103
-      
109
+
104 110
       <!-- 第八行:查堵-AI复查图像总数 -->
105 111
       <div class="chart-row full-width">
106 112
         <div class="chart-item">
@@ -108,7 +114,7 @@
108 114
           <div ref="chart12" class="echarts"></div>
109 115
         </div>
110 116
       </div>
111
-      
117
+
112 118
       <!-- 第九行:AI漏判和误判图像总数 -->
113 119
       <div class="chart-row">
114 120
         <div class="chart-item">
@@ -125,10 +131,31 @@
125 131
 </template>
126 132
 
127 133
 <script setup>
128
-import { ref, onMounted } from 'vue'
134
+import { ref, onMounted, watch } from 'vue'
129 135
 import * as echarts from 'echarts'
136
+import { formatDateHy } from '@/utils/index.js'
130 137
 import ModuleContainer from './ModuleContainer.vue'
131 138
 import { useEcharts } from '@/hooks/chart.js'
139
+import {
140
+  totalCount,
141
+  brigadeChart,
142
+  totalRate,
143
+  brigadeRateChart,
144
+  dailyTrend,
145
+  dailyBrigadeComparison,
146
+  dailyRateTrend,
147
+  dailyBrigadeRateComparison,
148
+  dailyLuggageTrend,
149
+  dailyBrigadeLuggageComparison,
150
+  itemDistribution,
151
+  areaDistribution,
152
+  discriminationDistribution,
153
+  timePeriodStats,
154
+  dailyLuggageAndRate,
155
+  aiImageStats,
156
+  aiMissImageStats,
157
+  aiErrorImageStats
158
+} from '@/api/blockingData/blockingDataScreen.js'
132 159
 
133 160
 // 接收筛选参数
134 161
 const props = defineProps({
@@ -137,6 +164,28 @@ const props = defineProps({
137 164
     default: () => ({})
138 165
   }
139 166
 })
167
+const totalBlockedCount = ref(0)
168
+const totalBlockedRate = ref(0)
169
+// 处理filterParams,将dateRange拆分为startTime和endTime
170
+const processFilterParams = (params) => {
171
+  const { dateRange, ...rest } = params
172
+  const processed = { ...rest }
173
+
174
+  if (dateRange && Array.isArray(dateRange) && dateRange.length === 2) {
175
+    processed.startTime = formatDateHy(dateRange[0])
176
+    processed.endTime = formatDateHy(dateRange[1])
177
+  }
178
+  if (processed.brigadeId == 'all') {
179
+    delete processed.brigadeId
180
+  }
181
+  if (processed.terminalId == 'all') {
182
+    delete processed.terminalId
183
+  }
184
+
185
+  return processed
186
+}
187
+
188
+
140 189
 
141 190
 // 图表引用
142 191
 const chart0a = ref(null)
@@ -174,50 +223,282 @@ const { setOption: setOption12 } = useEcharts(chart12)
174 223
 const { setOption: setOption13 } = useEcharts(chart13)
175 224
 const { setOption: setOption14 } = useEcharts(chart14)
176 225
 
177
-// 生成模拟数据
178
-const generateData = (count, min, max) => {
179
-  const data = []
180
-  for (let i = 0; i < count; i++) {
181
-    data.push(Math.floor(Math.random() * (max - min + 1)) + min)
226
+// 加载所有数据
227
+const loadData = async () => {
228
+  const queryParams = processFilterParams(props.filterParams)
229
+
230
+  try {
231
+    // 统计卡片数据
232
+    const [totalCountRes, totalRateRes] = await Promise.allSettled([
233
+      totalCount(queryParams),
234
+      totalRate(queryParams)
235
+    ])
236
+
237
+    totalBlockedCount.value = totalCountRes.value?.data?.totalBlockedCount || 0
238
+    totalBlockedRate.value = totalRateRes.value?.data?.totalBlockedRate || 0
239
+
240
+
241
+    const [brigadeChartRes, brigadeRateRes] = await Promise.allSettled([
242
+      brigadeChart(queryParams),
243
+      brigadeRateChart(queryParams)
244
+    ])
245
+
246
+    const brigadeChartData = brigadeChartRes.value?.data || []
247
+    const brigadeRateData = brigadeRateRes.value?.data || []
248
+
249
+    const chart0aOption = {
250
+      grid: {
251
+        left: '5%',
252
+        top: '5%',
253
+        right: '5%',
254
+        bottom: '5%',
255
+        containLabel: true
256
+      },
257
+      xAxis: {
258
+        type: 'category',
259
+        data: brigadeChartData.map(d => d.brigadeName),
260
+        show: true,
261
+        axisLine: { lineStyle: { color: '#999' } },
262
+        axisLabel: { fontSize: 10, color: '#666' }
263
+      },
264
+      yAxis: {
265
+        type: 'value',
266
+        show: true,
267
+        axisLine: { lineStyle: { color: '#999' } },
268
+        axisLabel: { fontSize: 10, color: '#666' }
269
+      },
270
+      series: [{
271
+        type: 'bar',
272
+        data: brigadeChartData.map(d => d.totalBlockedCount),
273
+        itemStyle: { color: '#3b82f6' },
274
+        barWidth: 12,
275
+        label: {
276
+          show: true,
277
+          position: 'top',
278
+          fontSize: 10
279
+        }
280
+      }]
281
+    }
282
+
283
+    const chart0bOption = {
284
+      grid: {
285
+        left: '5%',
286
+        top: '5%',
287
+        right: '5%',
288
+        bottom: '5%',
289
+        containLabel: true
290
+      },
291
+      xAxis: {
292
+        type: 'value',
293
+        show: true,
294
+        axisLine: { lineStyle: { color: '#999' } },
295
+        axisLabel: { fontSize: 10, color: '#666' }
296
+      },
297
+      yAxis: {
298
+        type: 'category',
299
+        data: brigadeRateData.map(d => d.brigadeName),
300
+        show: true,
301
+        axisLine: { lineStyle: { color: '#999' } },
302
+        axisLabel: { fontSize: 10, color: '#666' }
303
+      },
304
+      series: [{
305
+        type: 'bar',
306
+        data: brigadeRateData.map(d => d.avgBlockedRate),
307
+        itemStyle: { color: '#3b82f6' },
308
+        barWidth: 8,
309
+        label: {
310
+          show: true,
311
+          position: 'right',
312
+          fontSize: 10
313
+        }
314
+      }]
315
+    }
316
+
317
+    setOption0a(chart0aOption)
318
+    setOption0b(chart0bOption)
319
+
320
+    // 每日查堵数量
321
+    const [dailyTrendRes, dailyBrigadeRes] = await Promise.allSettled([
322
+      dailyTrend(queryParams),
323
+      dailyBrigadeComparison(queryParams)
324
+    ])
325
+    console.log(dailyTrendRes, dailyBrigadeRes, 1)
326
+
327
+    const trendData = dailyTrendRes?.value?.data || []
328
+    const brigadeData = dailyBrigadeRes?.value?.data || []
329
+
330
+    setOption1(lineChartOption(
331
+      trendData.map(d => d.totalBlockedCount),
332
+      '#3b82f6',
333
+      '查堵数量',
334
+      trendData.map(d => d.statDate ? d.statDate.split('T')[0] : '')
335
+    ))
336
+
337
+    const uniqueBrigades = [...new Set(brigadeData.map(d => d.brigadeName))]
338
+    const allDates = [...new Set(brigadeData.map(d => d.statDate ? d.statDate.split('T')[0] : ''))].sort()
339
+    const brigadeDatasets = uniqueBrigades.map(brigadeName => {
340
+      return allDates.map(date => {
341
+        const found = brigadeData.find(d =>
342
+          d.brigadeName === brigadeName && d.statDate && d.statDate.split('T')[0] === date
343
+        )
344
+        return found ? found.totalBlockedCount : 0
345
+      })
346
+    })
347
+
348
+    setOption2(multiLineChartOption(
349
+      brigadeDatasets,
350
+      ['#3b82f6', '#22c55e', '#f97316'],
351
+      uniqueBrigades,
352
+      allDates
353
+    ))
354
+
355
+    // 每日查堵万分率
356
+    const [dailyRateTrendRes, dailyBrigadeRateRes] = await Promise.allSettled([
357
+      dailyRateTrend(queryParams),
358
+      dailyBrigadeRateComparison(queryParams)
359
+    ])
360
+    console.log(dailyRateTrendRes, dailyBrigadeRateRes, 3)
361
+    const rateTrendData = dailyRateTrendRes?.value?.data || []
362
+    const rateBrigadeData = dailyBrigadeRateRes?.value?.data || []
363
+
364
+    setOption3(lineChartOption(
365
+      rateTrendData.map(d => d.avgBlockedRate),
366
+      '#ec4899',
367
+      '万分率',
368
+      rateTrendData.map(d => d.statDate ? d.statDate.split('T')[0] : '')
369
+    ))
370
+
371
+    const rateAllDates = [...new Set(rateBrigadeData.map(d => d.statDate ? d.statDate.split('T')[0] : ''))].sort()
372
+    const rateUniqueBrigades = [...new Set(rateBrigadeData.map(d => d.brigadeName))]
373
+    const rateBrigadeDatasets = rateUniqueBrigades.map(brigadeName => {
374
+      return rateAllDates.map(date => {
375
+        const found = rateBrigadeData.find(d =>
376
+          d.brigadeName === brigadeName && d.statDate && d.statDate.split('T')[0] === date
377
+        )
378
+        return found ? found.avgBlockedRate : 0
379
+      })
380
+    })
381
+
382
+    setOption4(multiLineChartOption(
383
+      rateBrigadeDatasets,
384
+      ['#3b82f6', '#22c55e', '#f97316'],
385
+      rateUniqueBrigades,
386
+      rateAllDates
387
+    ))
388
+
389
+    // 每日过检图像数
390
+    const [dailyLuggageTrendRes, dailyBrigadeLuggageRes] = await Promise.allSettled([
391
+      dailyLuggageTrend(queryParams),
392
+      dailyBrigadeLuggageComparison(queryParams)
393
+    ])
394
+
395
+    const luggageTrendData = dailyLuggageTrendRes?.value?.data || []
396
+    const luggageBrigadeData = dailyBrigadeLuggageRes?.value?.data || []
397
+    console.log(luggageTrendData, luggageBrigadeData, 555)
398
+    if (luggageTrendData.length > 0) {
399
+      setOption5(lineChartOption(
400
+        luggageTrendData.map(d => d.totalLuggageCount),
401
+        '#8b5cf6',
402
+        '过检图像数',
403
+        luggageTrendData.map(d => d.statDate ? d.statDate.split('T')[0] : '')
404
+      ))
405
+    }
406
+
407
+
408
+    const luggageAllDates = [...new Set(luggageBrigadeData.map(d => d.statDate ? d.statDate.split('T')[0] : ''))].sort()
409
+    const luggageUniqueBrigades = [...new Set(luggageBrigadeData.map(d => d.brigadeName))]
410
+    const luggageBrigadeDatasets = luggageUniqueBrigades.map(brigadeName => {
411
+      return luggageAllDates.map(date => {
412
+        const found = luggageBrigadeData.find(d =>
413
+          d.brigadeName === brigadeName && d.statDate && d.statDate.split('T')[0] === date
414
+        )
415
+        return found ? found.totalLuggageCount : 0
416
+      })
417
+    })
418
+    if (luggageBrigadeDatasets.length > 0) {
419
+      setOption6(multiLineChartOption(
420
+        luggageBrigadeDatasets,
421
+        ['#3b82f6', '#22c55e', '#f97316'],
422
+        luggageUniqueBrigades,
423
+        luggageAllDates
424
+      ))
425
+    }
426
+
427
+    // 查堵物品分布和区域查堵数量分布
428
+    const [itemDistRes, areaDistRes] = await Promise.allSettled([
429
+      itemDistribution(queryParams),
430
+      areaDistribution(queryParams)
431
+    ])
432
+    console.log(itemDistRes, areaDistRes, 55555)
433
+    const itemDistData = itemDistRes?.value?.data || []
434
+    if (itemDistData.length > 0) {
435
+      setOption7(ringPieOption(
436
+        itemDistData.map(d => ({ name: d.name, value: d.value })),
437
+        ['#3b82f6', '#22c55e', '#f97316', '#ec4899', '#8b5cf6', '#14b8a6']
438
+      ))
439
+    }
440
+
441
+
442
+    const areaDistData = areaDistRes?.value?.data || []
443
+    if (areaDistData.length > 0) {
444
+      setOption8(ringPieOption(
445
+        areaDistData.map(d => ({ name: d.name, value: d.value })),
446
+        ['#3b82f6', '#22c55e', '#f97316', '#ec4899', '#8b5cf6', '#14b8a6']
447
+      ))
448
+    }
449
+
450
+
451
+    // 查堵类型分布
452
+    const discDistRes = await discriminationDistribution(queryParams)
453
+    const discDistData = discDistRes.data || []
454
+    setOption9(barChartOption(discDistData, '#3b82f6'))
455
+
456
+    // 查堵时间段过检行李数及万分率
457
+    const timePeriodRes = await timePeriodStats(queryParams)
458
+    const timePeriodData = timePeriodRes.data || []
459
+    setOption10(dualAxisBarChartOption(
460
+      timePeriodData.map(d => d.luggageCount),
461
+      timePeriodData.map(d => d.rate),
462
+      '#3b82f6',
463
+      '#ec4899'
464
+    ))
465
+
466
+    // 每日过检行李数及万分率
467
+    const dailyLuggageRes = await dailyLuggageAndRate(queryParams)
468
+    const dailyLuggageData = dailyLuggageRes.data || []
469
+    setOption11(dualAxisBarChartOption(
470
+      dailyLuggageData.map(d => d.luggageCount),
471
+      dailyLuggageData.map(d => d.rate),
472
+      '#3b82f6',
473
+      '#ec4899'
474
+    ))
475
+
476
+    // AI复查图像总数
477
+    const aiImageRes = await aiImageStats(queryParams)
478
+    const aiImageData = aiImageRes.data || []
479
+    setOption12(lineChartOption(aiImageData.map(d => d.value), '#14b8a6', 'AI复查图像数'))
480
+
481
+    // AI漏判和误判图像总数
482
+    const [aiMissRes, aiErrorRes] = await Promise.all([
483
+      aiMissImageStats(queryParams),
484
+      aiErrorImageStats(queryParams)
485
+    ])
486
+
487
+    const aiMissData = aiMissRes.data || []
488
+    const aiErrorData = aiErrorRes.data || []
489
+
490
+    setOption13(lineChartOption(aiMissData.map(d => d.value), '#ef4444', 'AI漏判图像数'))
491
+    setOption14(lineChartOption(aiErrorData.map(d => d.value), '#f97316', 'AI误判图像数'))
492
+
493
+  } catch (error) {
494
+    console.error('加载数据失败:', error)
182 495
   }
183
-  return data
184 496
 }
185 497
 
186
-// X轴数据(30天)
187
-const xAxisData = []
188
-for (let i = 1; i <= 30; i++) {
189
-  xAxisData.push(i + '日')
190
-}
191 498
 
192
-// 小型柱状图配置(用于统计卡片)
193
-const smallBarOption = (data, color, isHorizontal = false) => ({
194
-  grid: {
195
-    left: '5%',
196
-    top: '5%',
197
-    right: '5%',
198
-    bottom: '5%',
199
-    containLabel: true
200
-  },
201
-  xAxis: {
202
-    type: isHorizontal ? 'value' : 'category',
203
-    data: isHorizontal ? null : ['1', '2', '3'],
204
-    show: false
205
-  },
206
-  yAxis: {
207
-    type: isHorizontal ? 'category' : 'value',
208
-    data: isHorizontal ? ['1', '2', '3'] : null,
209
-    show: false
210
-  },
211
-  series: [{
212
-    type: 'bar',
213
-    data: data,
214
-    itemStyle: { color: color },
215
-    barWidth: isHorizontal ? 8 : 12
216
-  }]
217
-})
218 499
 
219 500
 // 折线图配置(总表)
220
-const lineChartOption = (data, color, title) => ({
501
+const lineChartOption = (data, color, title, xAxisData = []) => ({
221 502
   grid: {
222 503
     left: '10%',
223 504
     top: '15%',
@@ -229,7 +510,7 @@ const lineChartOption = (data, color, title) => ({
229 510
     type: 'category',
230 511
     data: xAxisData,
231 512
     axisLine: { lineStyle: { color: '#999' } },
232
-    axisLabel: { fontSize: 10, color: '#666' }
513
+    axisLabel: { fontSize: 10, color: '#666', rotate: 0 }
233 514
   },
234 515
   yAxis: {
235 516
     type: 'value',
@@ -250,7 +531,7 @@ const lineChartOption = (data, color, title) => ({
250 531
 })
251 532
 
252 533
 // 多折线图配置(大队对比)
253
-const multiLineChartOption = (dataList, colors, legends) => ({
534
+const multiLineChartOption = (dataList, colors, legends, xAxisData = []) => ({
254 535
   grid: {
255 536
     left: '10%',
256 537
     top: '15%',
@@ -262,7 +543,7 @@ const multiLineChartOption = (dataList, colors, legends) => ({
262 543
     type: 'category',
263 544
     data: xAxisData,
264 545
     axisLine: { lineStyle: { color: '#999' } },
265
-    axisLabel: { fontSize: 10, color: '#666' }
546
+    axisLabel: { fontSize: 10, color: '#666', rotate: 0 }
266 547
   },
267 548
   yAxis: {
268 549
     type: 'value',
@@ -349,7 +630,7 @@ const dualAxisBarChartOption = (data1, data2, color1, color2) => ({
349 630
   },
350 631
   xAxis: {
351 632
     type: 'category',
352
-    data: xAxisData,
633
+    data: [],
353 634
     axisLine: { lineStyle: { color: '#999' } },
354 635
     axisLabel: { fontSize: 10, color: '#666' }
355 636
   },
@@ -387,76 +668,15 @@ const dualAxisBarChartOption = (data1, data2, color1, color2) => ({
387 668
   ]
388 669
 })
389 670
 
671
+
672
+
673
+// 监听filterParams变化
674
+watch(() => props.filterParams, () => {
675
+  loadData()
676
+}, { deep: true })
677
+
390 678
 onMounted(() => {
391
-  // 统计卡片图表
392
-  setOption0a(smallBarOption([120, 80, 150], '#3b82f6'))
393
-  setOption0b(smallBarOption([2.2, 1.8, 2.5], '#3b82f6', true))
394
-  
395
-  // 第一行:每日查堵数量
396
-  setOption1(lineChartOption(generateData(30, 50, 150), '#3b82f6', '查堵数量'))
397
-  setOption2(multiLineChartOption(
398
-    [generateData(30, 20, 80), generateData(30, 15, 70), generateData(30, 10, 60)],
399
-    ['#3b82f6', '#22c55e', '#f97316'],
400
-    ['安检一大队', '安检二大队', '安检三大队']
401
-  ))
402
-  
403
-  // 第二行:每日查堵万分率
404
-  setOption3(lineChartOption(generateData(30, 0.5, 3.5), '#ec4899', '万分率'))
405
-  setOption4(multiLineChartOption(
406
-    [generateData(30, 0.3, 2.5), generateData(30, 0.2, 2.0), generateData(30, 0.1, 1.8)],
407
-    ['#3b82f6', '#22c55e', '#f97316'],
408
-    ['安检一大队', '安检二大队', '安检三大队']
409
-  ))
410
-  
411
-  // 第三行:每日过检图像数
412
-  setOption5(lineChartOption(generateData(30, 1000, 5000), '#8b5cf6', '过检图像数'))
413
-  setOption6(multiLineChartOption(
414
-    [generateData(30, 400, 2000), generateData(30, 300, 1800), generateData(30, 200, 1600)],
415
-    ['#3b82f6', '#22c55e', '#f97316'],
416
-    ['安检一大队', '安检二大队', '安检三大队']
417
-  ))
418
-  
419
-  // 第四行:查堵物品分布和区域查堵数量分布
420
-  setOption7(ringPieOption([
421
-    { name: '刀具', value: 25 },
422
-    { name: '液体', value: 20 },
423
-    { name: '电子产品', value: 18 },
424
-    { name: '其他', value: 15 },
425
-    { name: '打火机', value: 12 },
426
-    { name: '化妆品', value: 10 }
427
-  ], ['#3b82f6', '#22c55e', '#f97316', '#ec4899', '#8b5cf6', '#14b8a6']))
428
-  
429
-  setOption8(barChartOption([
430
-    { name: 'T1国内', value: 120 },
431
-    { name: 'T1国际', value: 80 },
432
-    { name: 'T2国内', value: 150 },
433
-    { name: 'T2国际', value: 60 },
434
-    { name: '中转区', value: 40 }
435
-  ], '#3b82f6', true))
436
-  
437
-  // 第五行:查堵类型分布
438
-  setOption9(barChartOption(generateData(6, 20, 100), '#3b82f6'))
439
-  
440
-  // 第六行:查堵时间段过检行李数及万分率
441
-  setOption10(dualAxisBarChartOption(
442
-    generateData(24, 100, 500), // 行李数
443
-    generateData(24, 0.5, 3.0), // 万分率
444
-    '#3b82f6', '#ec4899'
445
-  ))
446
-  
447
-  // 第七行:每日过检行李数及万分率
448
-  setOption11(dualAxisBarChartOption(
449
-    generateData(30, 1000, 5000), // 行李数
450
-    generateData(30, 0.5, 3.5),  // 万分率
451
-    '#3b82f6', '#ec4899'
452
-  ))
453
-  
454
-  // 第八行:查堵-AI复查图像总数
455
-  setOption12(lineChartOption(generateData(30, 50, 200), '#14b8a6', 'AI复查图像数'))
456
-  
457
-  // 第九行:AI漏判和误判图像总数
458
-  setOption13(lineChartOption(generateData(30, 5, 30), '#ef4444', 'AI漏判图像数'))
459
-  setOption14(lineChartOption(generateData(30, 3, 25), '#f97316', 'AI误判图像数'))
679
+  loadData()
460 680
 })
461 681
 </script>
462 682
 
@@ -467,7 +687,7 @@ onMounted(() => {
467 687
   flex-direction: column;
468 688
   gap: 10px;
469 689
   overflow-y: auto;
470
-  
690
+
471 691
 }
472 692
 
473 693
 .stats-row {
@@ -478,11 +698,8 @@ onMounted(() => {
478 698
 
479 699
 .stat-card {
480 700
   flex: 1;
481
-  background: #fff;
482
-  border-radius: 6px;
483
-  padding: 15px;
484
-  border: 1px solid #eee;
485
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
701
+
702
+
486 703
   min-height: 120px;
487 704
 }
488 705
 
@@ -490,47 +707,76 @@ onMounted(() => {
490 707
   display: flex;
491 708
   height: 100%;
492 709
   align-items: center;
710
+  gap: 10px;
711
+
493 712
 }
494 713
 
495 714
 .stat-left {
715
+  background: #fff;
716
+  border-radius: 6px;
717
+  border: 1px solid #eee;
718
+  padding: 15px;
719
+  height: 100%;
720
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
496 721
   flex: 1;
497 722
   display: flex;
723
+  align-items: center;
498 724
   flex-direction: column;
499
-  align-items: flex-start;
725
+
500 726
   padding-right: 15px;
501 727
 }
502 728
 
503 729
 .stat-label {
504
-  font-size: 12px;
505
-  color: #666;
730
+  width: 100%;
731
+  font-size: 17px;
732
+  color: black;
506 733
   margin-bottom: 8px;
507 734
 }
508 735
 
736
+.stat-content {
737
+  margin-top: 15%;
738
+  display: flex;
739
+  flex-direction: column;
740
+  align-items: center;
741
+}
742
+
509 743
 .stat-value {
744
+  font-size: 80px;
745
+  font-weight: bold;
746
+  color: #3b82f6;
747
+
748
+}
749
+
750
+.stat-unit {
510 751
   font-size: 24px;
511 752
   font-weight: bold;
512 753
   color: #3b82f6;
513 754
 }
514 755
 
515 756
 .stat-right {
757
+  background: #fff;
758
+  border-radius: 6px;
759
+  border: 1px solid #eee;
760
+  padding: 15px;
761
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
516 762
   flex: 1;
517 763
   display: flex;
518 764
   flex-direction: column;
519
-  align-items: flex-end;
765
+  align-items: flex-start;
520 766
   height: 100%;
521 767
 }
522 768
 
523 769
 .stat-title {
524
-  font-size: 12px;
525
-  color: #333;
770
+  font-size: 17px;
771
+  color: black;
526 772
   margin-bottom: 5px;
527 773
   font-weight: 500;
528 774
 }
529 775
 
530 776
 .echarts-small {
531 777
   width: 100%;
532
-  height: 60px;
533
-  min-height: 60px;
778
+  height: 300px;
779
+  min-height: 300px;
534 780
 }
535 781
 
536 782
 .chart-row {

+ 33 - 20
src/views/blockingData/blockingDataScreen/index.vue

@@ -28,23 +28,19 @@
28 28
         </div>
29 29
         <div class="filter-item">
30 30
           <span class="filter-label">航站楼:</span>
31
-          <el-select v-model="filterParams.terminal" placeholder="请选择区域" clearable style="width: 200px">
31
+          <el-select v-model="filterParams.terminalId" placeholder="请选择区域" clearable style="width: 200px"
32
+            @change="terminalChange">
32 33
             <el-option v-for="item in terminalOptions" :key="item.value" :label="item.label" :value="item.value" />
33 34
           </el-select>
34 35
         </div>
35 36
 
36 37
         <div class="filter-item">
37 38
           <span class="filter-label">漏检物品:</span>
38
-          <el-tree-select
39
-            v-model="filterParams.missCheckItem"
40
-            :data="missedItemOptions"
41
-            :props="{ value: 'id', label: 'name', children: 'children' }"
42
-            value-key="id"
43
-            placeholder="请选择漏检物品"
44
-            check-strictly
45
-            style="width: 200px"
46
-          />
39
+          <el-tree-select v-model="filterParams.missCheckItem" :data="missedItemOptions"
40
+            :props="{ value: 'id', label: 'name', children: 'children', disabled: data => data.children && data.children.length > 0 }"
41
+            value-key="id" placeholder="请选择漏检物品" check-strictly style="width: 200px" />
47 42
         </div>
43
+
48 44
         <div class="filter-item">
49 45
           <span class="filter-label">分大队:</span>
50 46
           <el-select v-model="filterParams.brigadeId" placeholder="请选择大队" clearable style="width: 200px">
@@ -116,8 +112,9 @@ const filterParams = reactive({
116 112
   supervisorId: '',
117 113
   teamLeaderId: '',
118 114
   terminal: '',
115
+  terminalId: '',
119 116
   missCheckItem: '',
120
-  brigadeId: '全站'
117
+  brigadeId: 'all'
121 118
 })
122 119
 
123 120
 // 选项数据
@@ -133,16 +130,19 @@ function getMissedItemOptions() {
133 130
     missedItemOptions.value = handleTree(response.data, "id", "parentId")
134 131
   })
135 132
 }
133
+const terminalChange = (value) => {
134
+  filterParams.terminal = terminalOptions.value.find(item => item.value === value).label
135
+}
136 136
 
137 137
 // 树形数据处理函数
138 138
 function handleTree(data, id, parentId) {
139 139
   const result = []
140 140
   const map = {}
141
-  
141
+
142 142
   data.forEach(item => {
143 143
     map[item[id]] = { ...item, children: [] }
144 144
   })
145
-  
145
+
146 146
   data.forEach(item => {
147 147
     const parent = map[item[parentId]]
148 148
     if (parent) {
@@ -151,7 +151,7 @@ function handleTree(data, id, parentId) {
151 151
       result.push(map[item[id]])
152 152
     }
153 153
   })
154
-  
154
+
155 155
   return result
156 156
 }
157 157
 
@@ -163,7 +163,7 @@ function getTerminalOptions() {
163 163
       label: item.name
164 164
     }))
165 165
     // 添加"整体"选项到最前面
166
-    terminalOptions.value.unshift({ label: '整体', value: null })
166
+    terminalOptions.value.unshift({ label: '整体', value: 'all' })
167 167
   })
168 168
 }
169 169
 function getTeamLeaderOptions() {
@@ -195,21 +195,33 @@ function getBrigadeOptions() {
195 195
       label: item.deptName
196 196
     }))
197 197
     // 添加"全站"选项到最前面
198
-    brigadeOptions.value.unshift({ label: '全站', value: null })
198
+    brigadeOptions.value.unshift({ label: '全站', value: 'all' })
199 199
   })
200 200
 }
201 201
 const setDefaultDateRange = () => {
202 202
   const now = new Date()
203
-  const startOfYear = new Date(now.getFullYear(), 0, 1, 0, 0, 0)
203
+  const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
204 204
   const endOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59)
205 205
 
206
-  filterParams.dateRange = [startOfYear, endOfToday]
206
+  // 格式化日期为 YYYY-MM-DD HH:mm:ss 格式
207
+  const formatDate = (date) => {
208
+    const year = date.getFullYear()
209
+    const month = String(date.getMonth() + 1).padStart(2, '0')
210
+    const day = String(date.getDate()).padStart(2, '0')
211
+    const hours = String(date.getHours()).padStart(2, '0')
212
+    const minutes = String(date.getMinutes()).padStart(2, '0')
213
+    const seconds = String(date.getSeconds()).padStart(2, '0')
214
+    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
215
+  }
216
+
217
+  filterParams.dateRange = [formatDate(startOfMonth), formatDate(endOfToday)]
207 218
 }
208 219
 
209 220
 // 处理筛选
210 221
 const handleFilter = () => {
211 222
   console.log('筛选参数:', filterParams)
212
-  // 这里可以调用API或更新图表数据
223
+  // 强制触发响应式更新,确保子组件能监听到变化
224
+  filterParams.dateRange = [...filterParams.dateRange]
213 225
 }
214 226
 
215 227
 // 重置筛选
@@ -220,7 +232,8 @@ const resetFilter = () => {
220 232
     teamLeaderId: '',
221 233
     terminal: '',
222 234
     missCheckItem: '',
223
-    brigadeId: '全站'
235
+    terminalId: '',
236
+    brigadeId: 'all'
224 237
   })
225 238
   setDefaultDateRange()
226 239
 }

+ 3 - 4
src/views/blockingData/dailyLuggageCheckInList/index.vue

@@ -2,7 +2,7 @@
2 2
   <div class="app-container">
3 3
     <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"> <el-form-item label="大队"
4 4
         prop="brigadeId">
5
-        <el-select v-model="queryParams.brigadeId" placeholder="请选择大队" clearable style="width: 200px">
5
+        <el-select v-model="queryParams.brigadeId" placeholder="请选择大队" filterable clearable style="width: 200px">
6 6
           <el-option v-for="item in brigadeOptions" :key="item.value" :label="item.label" :value="item.value" />
7 7
         </el-select>
8 8
       </el-form-item>
@@ -94,16 +94,15 @@
94 94
           </el-col>
95 95
           <el-col :span="12">
96 96
             <el-form-item label="班次" prop="shiftType">
97
-              <el-select v-model="form.shiftType" placeholder="请选择班次" style="width: 100%">
97
+              <el-select v-model="form.shiftType" placeholder="请选择班次" filterable style="width: 100%">
98 98
                 <el-option label="早班" value="早班" />
99
-                <el-option label="中班" value="中班" />
100 99
                 <el-option label="晚班" value="晚班" />
101 100
               </el-select>
102 101
             </el-form-item>
103 102
           </el-col>
104 103
         </el-row>
105 104
         <el-form-item label="当班大队" prop="brigadeId">
106
-          <el-select v-model="form.brigadeId" placeholder="请选择当班大队" style="width: 100%">
105
+          <el-select v-model="form.brigadeId" placeholder="请选择当班大队" filterable style="width: 100%">
107 106
             <el-option v-for="item in brigadeOptions" :key="item.value" :label="item.label" :value="item.value" />
108 107
           </el-select>
109 108
         </el-form-item>

+ 3 - 3
src/views/blockingData/dailyLuggageInspectionScheduleByTime/index.vue

@@ -2,7 +2,7 @@
2 2
   <div class="app-container">
3 3
     <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
4 4
       <el-form-item label="大队" prop="brigadeId">
5
-        <el-select v-model="queryParams.brigadeId" placeholder="请选择大队" clearable style="width: 200px">
5
+        <el-select v-model="queryParams.brigadeId" placeholder="请选择大队" filterable clearable style="width: 200px">
6 6
           <el-option v-for="item in brigadeOptions" :key="item.value" :label="item.label" :value="item.value" />
7 7
         </el-select>
8 8
       </el-form-item>
@@ -78,7 +78,7 @@
78 78
           </el-col>
79 79
           <el-col :span="12">
80 80
             <el-form-item label="当班大队" prop="brigadeId">
81
-              <el-select v-model="form.brigadeId" placeholder="请选择当班大队" style="width: 100%">
81
+              <el-select v-model="form.brigadeId" placeholder="请选择当班大队" filterable style="width: 100%">
82 82
                 <el-option v-for="item in brigadeOptions" :key="item.value" :label="item.label" :value="item.value" />
83 83
               </el-select>
84 84
             </el-form-item>
@@ -87,7 +87,7 @@
87 87
         <el-row :gutter="20">
88 88
           <el-col :span="12">
89 89
             <el-form-item label="时间段" prop="timePeriod">
90
-              <el-select v-model="form.timePeriod" placeholder="请选择时间段" style="width: 100%">
90
+              <el-select v-model="form.timePeriod" placeholder="请选择时间段" filterable style="width: 100%">
91 91
                 <el-option v-for="dict in blocked_time_period" :key="dict.value" :label="dict.label"
92 92
                   :value="dict.value" />
93 93
               </el-select>

+ 57 - 67
src/views/blockingData/missedInspectionList/index.vue

@@ -2,7 +2,7 @@
2 2
   <div class="app-container">
3 3
     <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
4 4
       <el-form-item label="大队" prop="brigadeId">
5
-        <el-select v-model="queryParams.brigadeId" placeholder="请选择大队" clearable style="width: 200px">
5
+        <el-select v-model="queryParams.brigadeId" placeholder="请选择大队" filterable clearable style="width: 200px">
6 6
           <el-option v-for="item in brigadeOptions" :key="item.value" :label="item.label" :value="item.value" />
7 7
         </el-select>
8 8
       </el-form-item>
@@ -12,22 +12,23 @@
12 12
         </el-date-picker>
13 13
       </el-form-item>
14 14
       <el-form-item label="被回查人" prop="reviewedUserId">
15
-        <el-select v-model="queryParams.reviewedUserId" placeholder="请选择被回查人" clearable style="width: 200px">
15
+        <el-select v-model="queryParams.reviewedUserId" placeholder="请选择被回查人" filterable clearable style="width: 200px">
16 16
           <el-option v-for="item in personOptions" :key="item.value" :label="item.label" :value="item.value" />
17 17
         </el-select>
18 18
       </el-form-item>
19 19
       <el-form-item label="分管班组长" prop="teamLeaderId">
20
-        <el-select v-model="queryParams.teamLeaderId" placeholder="请选择分管班组长" clearable style="width: 200px">
20
+        <el-select v-model="queryParams.teamLeaderId" placeholder="请选择分管班组长" filterable clearable style="width: 200px">
21 21
           <el-option v-for="item in teamLeaderOptions" :key="item.value" :label="item.label" :value="item.value" />
22 22
         </el-select>
23 23
       </el-form-item>
24 24
       <el-form-item label="分管主管" prop="supervisorId">
25
-        <el-select v-model="queryParams.supervisorId" placeholder="请选择分管主管" clearable style="width: 200px">
25
+        <el-select v-model="queryParams.supervisorId" placeholder="请选择分管主管" filterable clearable style="width: 200px">
26 26
           <el-option v-for="item in supervisorOptions" :key="item.value" :label="item.label" :value="item.value" />
27 27
         </el-select>
28 28
       </el-form-item>
29 29
       <el-form-item label="判别类型" prop="discriminationType">
30
-        <el-select v-model="queryParams.discriminationType" placeholder="请选择判别类型" clearable style="width: 200px">
30
+        <el-select v-model="queryParams.discriminationType" placeholder="请选择判别类型" filterable clearable
31
+          style="width: 200px">
31 32
           <el-option v-for="item in discrimination_type" :key="item.value" :label="item.label" :value="item.value" />
32 33
         </el-select>
33 34
       </el-form-item>
@@ -60,7 +61,7 @@
60 61
     <el-table v-loading="loading" :data="missedInspectionList" @selection-change="handleSelectionChange">
61 62
       <el-table-column type="selection" width="55" align="center" />
62 63
       <el-table-column label="大队" align="center" prop="brigadeName" min-width="120" />
63
-      <el-table-column label="区域" align="center" prop="areaName" min-width="120" />
64
+      <el-table-column label="航站楼" align="center" prop="areaName" min-width="120" />
64 65
       <el-table-column label="被回查人" align="center" prop="reviewedUserName" min-width="120" />
65 66
       <el-table-column label="回查日期" align="center" prop="reviewDate" min-width="120" />
66 67
       <el-table-column label="漏检时间" align="center" prop="missCheckTime" min-width="180" />
@@ -83,11 +84,8 @@
83 84
       <el-table-column label="人员性别" align="center" prop="gender" min-width="100" />
84 85
       <el-table-column label="漏检原因分类" align="center" prop="missCheckReasonCategory" min-width="220" />
85 86
       <el-table-column label="月考成绩" align="center" prop="monthlyAssessment" min-width="100" />
86
-      <el-table-column label="本月自测有无漏检" align="center" prop="selfTestHasMissCheck" min-width="160">
87
-        <template #default="scope">
88
-            {{ scope.row.selfTestHasMissCheck ? '有' : '无' }}
89
-        </template>
90
-      </el-table-column>
87
+      <el-table-column label="本月自测有无漏检" align="center" prop="selfTestHasMissCheck" min-width="160" />
88
+
91 89
       <el-table-column label="漏检物品" align="center" prop="missCheckItem" min-width="120" />
92 90
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="140" fixed="right">
93 91
         <template #default="scope">
@@ -108,15 +106,15 @@
108 106
         <el-row :gutter="20">
109 107
           <el-col :span="12">
110 108
             <el-form-item label="大队" prop="brigadeId">
111
-              <el-select v-model="form.brigadeId" placeholder="请选择大队" style="width: 100%">
109
+              <el-select v-model="form.brigadeId" placeholder="请选择大队" filterable style="width: 100%">
112 110
                 <el-option v-for="item in brigadeOptions" :key="item.value" :label="item.label" :value="item.value" />
113 111
               </el-select>
114 112
             </el-form-item>
115 113
           </el-col>
116 114
           <el-col :span="12">
117
-            <el-form-item label="区域" prop="areaId">
118
-              <el-select v-model="form.areaId" placeholder="请选择区域" style="width: 100%">
119
-                <el-option v-for="item in areaOptions" :key="item.id" :label="item.name" :value="item.id" />
115
+            <el-form-item label="航站楼" prop="areaId">
116
+              <el-select v-model="form.areaId" placeholder="请选择航站楼" filterable style="width: 100%">
117
+                <el-option v-for="item in channelOptions" :key="item.id" :label="item.name" :value="item.id" />
120 118
               </el-select>
121 119
             </el-form-item>
122 120
           </el-col>
@@ -124,7 +122,7 @@
124 122
         <el-row :gutter="20">
125 123
           <el-col :span="12">
126 124
             <el-form-item label="被回查人" prop="reviewedUserId">
127
-              <el-select v-model="form.reviewedUserId" placeholder="请选择被回查人" style="width: 100%"
125
+              <el-select v-model="form.reviewedUserId" placeholder="请选择被回查人" filterable style="width: 100%"
128 126
                 @change="handleReviewedUserChange">
129 127
                 <el-option v-for="item in personOptions" :key="item.value" :label="item.label" :value="item.value" />
130 128
               </el-select>
@@ -146,7 +144,7 @@
146 144
           </el-col>
147 145
           <el-col :span="12">
148 146
             <el-form-item label="漏检时间段" prop="missCheckTimePeriod">
149
-              <el-select v-model="form.missCheckTimePeriod" placeholder="请选择漏检时间段" style="width: 100%">
147
+              <el-select v-model="form.missCheckTimePeriod" placeholder="请选择漏检时间段" filterable style="width: 100%">
150 148
                 <el-option v-for="item in blocked_time_period" :key="item.value" :label="item.label"
151 149
                   :value="item.value" />
152 150
               </el-select>
@@ -156,22 +154,16 @@
156 154
         <el-row :gutter="20">
157 155
           <el-col :span="12">
158 156
             <el-form-item label="上岗位置" prop="channelId">
159
-              <el-select v-model="form.channelId" placeholder="请选择上岗位置" style="width: 100%">
160
-                <el-option v-for="item in channelOptions" :key="item.id" :label="item.name" :value="item.id" />
157
+              <el-select v-model="form.channelId" placeholder="请选择上岗位置" filterable style="width: 100%">
158
+                <el-option v-for="item in areaOptions" :key="item.id" :label="item.name" :value="item.id" />
161 159
               </el-select>
162 160
             </el-form-item>
163 161
           </el-col>
164 162
           <el-col :span="12">
165 163
             <el-form-item label="漏检物品" prop="missCheckItem">
166
-              <el-tree-select
167
-                v-model="form.missCheckItem"
168
-                :data="missCheckItemOptions"
169
-                :props="{ value: 'name', label: 'name', children: 'children' }"
170
-                value-key="name"
171
-                placeholder="请选择漏检物品"
172
-                check-strictly
173
-                style="width: 100%"
174
-              />
164
+              <el-tree-select v-model="form.missCheckItem" :data="missCheckItemOptions"
165
+                :props="{ value: 'name', label: 'name', children: 'children', disabled: data => data.children && data.children.length > 0 }"
166
+                value-key="name" placeholder="请选择漏检物品" check-strictly style="width: 100%" />
175 167
             </el-form-item>
176 168
           </el-col>
177 169
 
@@ -179,7 +171,7 @@
179 171
         <el-row :gutter="20">
180 172
           <el-col :span="12">
181 173
             <el-form-item label="分管主管" prop="supervisorId">
182
-              <el-select v-model="form.supervisorId" placeholder="请选择分管主管" style="width: 100%">
174
+              <el-select v-model="form.supervisorId" placeholder="请选择分管主管" filterable style="width: 100%">
183 175
                 <el-option v-for="item in supervisorOptions" :key="item.value" :label="item.label"
184 176
                   :value="item.value" />
185 177
               </el-select>
@@ -187,7 +179,7 @@
187 179
           </el-col>
188 180
           <el-col :span="12">
189 181
             <el-form-item label="代管主管" prop="actingSupervisorId">
190
-              <el-select v-model="form.actingSupervisorId" placeholder="请选择代管主管" style="width: 100%">
182
+              <el-select v-model="form.actingSupervisorId" placeholder="请选择代管主管" filterable style="width: 100%">
191 183
                 <el-option v-for="item in supervisorOptions" :key="item.value" :label="item.label"
192 184
                   :value="item.value" />
193 185
               </el-select>
@@ -198,7 +190,7 @@
198 190
         <el-row :gutter="20">
199 191
           <el-col :span="12">
200 192
             <el-form-item label="分管班组长" prop="teamLeaderId">
201
-              <el-select v-model="form.teamLeaderId" placeholder="请选择分管班组长" style="width: 100%">
193
+              <el-select v-model="form.teamLeaderId" placeholder="请选择分管班组长" filterable style="width: 100%">
202 194
                 <el-option v-for="item in teamLeaderOptions" :key="item.value" :label="item.label"
203 195
                   :value="item.value" />
204 196
               </el-select>
@@ -206,7 +198,7 @@
206 198
           </el-col>
207 199
           <el-col :span="12">
208 200
             <el-form-item label="物品位置" prop="itemLocation">
209
-              <el-select v-model="form.itemLocation" placeholder="请选择物品位置" style="width: 100%">
201
+              <el-select v-model="form.itemLocation" placeholder="请选择物品位置" filterable style="width: 100%">
210 202
                 <el-option v-for="item in blocked_item_position" :key="item.value" :label="item.label"
211 203
                   :value="item.value" />
212 204
               </el-select>
@@ -217,7 +209,7 @@
217 209
         <el-row :gutter="20">
218 210
           <el-col :span="12">
219 211
             <el-form-item label="简单/难" prop="difficultyLevel">
220
-              <el-select v-model="form.difficultyLevel" placeholder="请选择简单/难" style="width: 100%">
212
+              <el-select v-model="form.difficultyLevel" placeholder="请选择简单/难" filterable style="width: 100%">
221 213
                 <el-option label="简单" value="简单" />
222 214
                 <el-option label="难" value="难" />
223 215
               </el-select>
@@ -225,7 +217,7 @@
225 217
           </el-col>
226 218
           <el-col :span="12">
227 219
             <el-form-item label="回查人" prop="reviewUserId">
228
-              <el-select v-model="form.reviewUserId" placeholder="请选择回查人" style="width: 100%">
220
+              <el-select v-model="form.reviewUserId" placeholder="请选择回查人" filterable style="width: 100%">
229 221
                 <el-option v-for="item in personOptions" :key="item.value" :label="item.label" :value="item.value" />
230 222
               </el-select>
231 223
             </el-form-item>
@@ -235,7 +227,7 @@
235 227
         <el-row :gutter="20">
236 228
           <el-col :span="12">
237 229
             <el-form-item label="判别类型" prop="discriminationType">
238
-              <el-select v-model="form.discriminationType" placeholder="请选择判别类型" style="width: 100%">
230
+              <el-select v-model="form.discriminationType" placeholder="请选择判别类型" filterable style="width: 100%">
239 231
                 <el-option v-for="item in discrimination_type" :key="item.value" :label="item.label"
240 232
                   :value="item.value" />
241 233
               </el-select>
@@ -243,7 +235,7 @@
243 235
           </el-col>
244 236
           <el-col :span="12">
245 237
             <el-form-item label="是否追回" prop="isRecovered">
246
-              <el-select v-model="form.isRecovered" placeholder="请选择是否追回" style="width: 100%">
238
+              <el-select v-model="form.isRecovered" placeholder="请选择是否追回" filterable style="width: 100%">
247 239
                 <el-option label="是" value="1" />
248 240
                 <el-option label="否" value="0" />
249 241
               </el-select>
@@ -254,7 +246,7 @@
254 246
         <el-row :gutter="20">
255 247
           <el-col :span="12">
256 248
             <el-form-item label="开机年限" prop="machineOperatingYears">
257
-              <el-select v-model="form.machineOperatingYears" placeholder="请选择开机年限" style="width: 100%">
249
+              <el-select v-model="form.machineOperatingYears" placeholder="请选择开机年限" filterable style="width: 100%">
258 250
                 <el-option v-for="item in blocked_operating_years" :key="item.value" :label="item.label"
259 251
                   :value="item.value" />
260 252
               </el-select>
@@ -262,7 +254,11 @@
262 254
           </el-col>
263 255
           <el-col :span="12">
264 256
             <el-form-item label="证书级别" prop="certificateLevel">
265
-              <el-input v-model="form.certificateLevel" placeholder="请输入证书级别" />
257
+              <el-select v-model="form.certificateLevel" placeholder="请输入证书级别" filterable style="width: 100%">
258
+                <el-option label="高级" value="高级" />
259
+                <el-option label="中级" value="中级" />
260
+                <el-option label="初级" value="初级" />
261
+              </el-select>
266 262
             </el-form-item>
267 263
           </el-col>
268 264
 
@@ -270,7 +266,7 @@
270 266
         <el-row :gutter="20">
271 267
           <el-col :span="12">
272 268
             <el-form-item label="人员性别" prop="gender">
273
-              <el-select v-model="form.gender" placeholder="请选择人员性别" style="width: 100%">
269
+              <el-select v-model="form.gender" placeholder="请选择人员性别" filterable style="width: 100%">
274 270
                 <el-option label="男" value="男" />
275 271
                 <el-option label="女" value="女" />
276 272
               </el-select>
@@ -278,7 +274,7 @@
278 274
           </el-col>
279 275
           <el-col :span="12">
280 276
             <el-form-item label="漏检原因分类" prop="missCheckReasonCategory">
281
-              <el-select v-model="form.missCheckReasonCategory" placeholder="请选择漏检原因分类" style="width: 100%">
277
+              <el-select v-model="form.missCheckReasonCategory" placeholder="请选择漏检原因分类" filterable style="width: 100%">
282 278
                 <el-option v-for="item in blocked_miss_check_reason" :key="item.value" :label="item.label"
283 279
                   :value="item.value" />
284 280
               </el-select>
@@ -289,7 +285,7 @@
289 285
         <el-row :gutter="20">
290 286
           <el-col :span="12">
291 287
             <el-form-item label="月考成绩" prop="monthlyAssessment">
292
-              <el-select v-model="form.monthlyAssessment" placeholder="请选择月考成绩" style="width: 100%">
288
+              <el-select v-model="form.monthlyAssessment" placeholder="请选择月考成绩" filterable style="width: 100%">
293 289
                 <el-option v-for="item in blocked_monthly_exam_result" :key="item.value" :label="item.label"
294 290
                   :value="item.value" />
295 291
               </el-select>
@@ -297,8 +293,8 @@
297 293
           </el-col>
298 294
           <el-col :span="12">
299 295
             <el-form-item label="本月自测有无漏检" prop="selfTestHasMissCheck">
300
-              <el-input-number v-model="form.selfTestHasMissCheck" :min="0" :max="2" :precision="0"
301
-                placeholder="请输入0-2的整数" style="width: 100%" />
296
+              <el-input-number v-model="form.selfTestHasMissCheck" :min="0" :precision="0" placeholder="请输入0-2的整数"
297
+                style="width: 100%" />
302 298
             </el-form-item>
303 299
           </el-col>
304 300
 
@@ -342,7 +338,7 @@ import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
342 338
 import { listPosition } from '@/api/system/position'
343 339
 import { listMissedInspection, getMissedInspection, delMissedInspection, addMissedInspection, updateMissedInspection, exportMissedInspection, downloadTemplate } from '@/api/blockingData/missedInspection'
344 340
 import { listDept } from '@/api/system/dept'
345
-import { listUser, selectUserLeaderListByCondition } from '@/api/system/user'
341
+import { listUser, selectUserLeaderListByCondition, getUser } from '@/api/system/user'
346 342
 import { listCategory } from '@/api/system/category'
347 343
 import { useDict } from '@/utils/dict'
348 344
 import { getToken } from '@/utils/auth'
@@ -398,7 +394,7 @@ const form = reactive({})
398 394
 // 表单校验
399 395
 const rules = {
400 396
   brigadeId: [{ required: true, message: '大队不能为空', trigger: 'change' }],
401
-  areaId: [{ required: true, message: '区域不能为空', trigger: 'change' }],
397
+  areaId: [{ required: true, message: '航站楼不能为空', trigger: 'change' }],
402 398
   reviewedUserId: [{ required: true, message: '被回查人不能为空', trigger: 'change' }],
403 399
   reviewDate: [{ required: true, message: '回查日期不能为空', trigger: 'change' }],
404 400
   missCheckTime: [{ required: true, message: '漏检时间不能为空', trigger: 'change' }],
@@ -419,24 +415,13 @@ const rules = {
419 415
   monthlyAssessment: [{ required: true, message: '月考成绩不能为空', trigger: 'change' }],
420 416
   selfTestHasMissCheck: [
421 417
     { required: true, message: '本月自测有无漏检不能为空', trigger: 'change' },
422
-    {
423
-      validator: (rule, value, callback) => {
424
-        if (value === null || value === undefined || value === '') {
425
-          callback(new Error('本月自测有无漏检不能为空'))
426
-        } else if (![0, 1, 2].includes(Number(value))) {
427
-          callback(new Error('本月自测有无漏检只能输入0、1、2'))
428
-        } else {
429
-          callback()
430
-        }
431
-      },
432
-      trigger: 'change'
433
-    }
418
+
434 419
   ]
435 420
   // actingSupervisorId 不设置必填校验
436 421
 }
437 422
 
438 423
 // 字典数据
439
-const { discrimination_type, blocked_time_period, blocked_item_position, blocked_monthly_exam_result, blocked_operating_years, blocked_miss_check_reason } = useDict('discrimination_type', 'blocked_time_period', 'blocked_item_position', 'blocked_monthly_exam_result', 'blocked_operating_years', 'blocked_miss_check_reason')
424
+const { discrimination_type, blocked_time_period, blocked_item_position, blocked_monthly_exam_result, blocked_operating_years, blocked_miss_check_reason, sys_user_qualification_level } = useDict('discrimination_type', 'blocked_time_period', 'blocked_item_position', 'blocked_monthly_exam_result', 'blocked_operating_years', 'blocked_miss_check_reason', 'sys_user_qualification_level')
440 425
 
441 426
 // 大队选项
442 427
 const brigadeOptions = ref([])
@@ -446,15 +431,15 @@ const personOptions = ref([])
446 431
 const teamLeaderOptions = ref([])
447 432
 // 主管选项
448 433
 const supervisorOptions = ref([])
449
-// 区域选项
434
+// 航站楼选项
450 435
 const areaOptions = ref([])
451 436
 // 上岗位置选项
452 437
 const channelOptions = ref([])
453 438
 
454 439
 /** 获取职位列表 */
455 440
 function getPositionList() {
456
-  // 获取区域选项
457
-  listPosition({ positionType: 'REGIONAL' }).then(response => {
441
+  // 获取航站楼选项
442
+  listPosition({ positionType: 'CHANNEL' }).then(response => {
458 443
     areaOptions.value = response.data || []
459 444
   })
460 445
 
@@ -468,8 +453,8 @@ function getPositionList() {
468 453
 function getList() {
469 454
   loading.value = true
470 455
   if (dateRange.value && dateRange.value.length === 2) {
471
-    queryParams.reviewDateStart = dateRange.value[0]
472
-    queryParams.reviewDateEnd = dateRange.value[1]
456
+    queryParams.startDate = dateRange.value[0]
457
+    queryParams.endDate = dateRange.value[1]
473 458
   } else {
474 459
     queryParams.reviewDateStart = null
475 460
     queryParams.reviewDateEnd = null
@@ -533,6 +518,11 @@ function handleReviewedUserChange(userId) {
533 518
         form.supervisorId = data.userId
534 519
       }
535 520
     })
521
+    getUser(userId).then(response => {
522
+      const data = response?.data;
523
+      form.certificateLevel = sys_user_qualification_level.value.find(item => item.value == data?.qualificationLevel).remark
524
+      form.gender = data?.sex == '0' ? '男' : '女'
525
+    })
536 526
   } else {
537 527
     form.supervisorId = null
538 528
     form.teamLeaderId = null
@@ -583,7 +573,7 @@ function reset() {
583 573
     gender: null,
584 574
     missCheckReasonCategory: null,
585 575
     monthlyAssessment: null,
586
-    selfTestHasMissCheck: null,
576
+    selfTestHasMissCheck: 0,
587 577
     missCheckItem: null
588 578
   })
589 579
   proxy.resetForm('missedInspectionRef')
@@ -641,9 +631,9 @@ function submitForm() {
641 631
         form.brigadeName = brigade ? brigade.label : null
642 632
       }
643 633
 
644
-      // 处理区域ID和名称映射
634
+      // 处理航站楼ID和名称映射
645 635
       if (form.areaId) {
646
-        
636
+
647 637
         const area = areaOptions.value.find(item => item.id === form.areaId)
648 638
         form.areaName = area ? area.name : null
649 639
       }
@@ -668,7 +658,7 @@ function submitForm() {
668 658
 
669 659
       // 处理代管主管ID和名称映射
670 660
       if (form.actingSupervisorId) {
671
-        
661
+
672 662
         const actingSupervisor = supervisorOptions.value.find(item => item.value === form.actingSupervisorId)
673 663
         form.actingSupervisorName = actingSupervisor ? actingSupervisor.label : null
674 664
       }

+ 13 - 3
src/views/blockingData/rateList/index.vue

@@ -51,11 +51,15 @@
51 51
       <el-table-column label="T1-B区速率(高峰期时段)" align="center" prop="t1BAreaRatePeak" />
52 52
       <el-table-column label="T2-国内速率(高峰期时段)" align="center" prop="t2DomesticRatePeak" />
53 53
       <el-table-column label="T2-国际速率(高峰期时段)" align="center" prop="t2InternationalRatePeak" />
54
-      <el-table-column label="班次" align="center" prop="shiftDesc" />
54
+      <el-table-column label="班次" align="center" prop="shift" min-width="100">
55
+        <template #default="scope">
56
+          {{ getDictLabel(shift, scope.row.shift) }}
57
+        </template>
58
+      </el-table-column>
55 59
       <el-table-column label="T2-中转(高峰期时段)" align="center" prop="t2TransferRatePeak" />
56 60
       <el-table-column label="国际及中转区域平均速率(高峰期时段)" align="center" prop="internationalTransferAvgRatePeak"
57 61
         min-width="120" />
58
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
62
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" min-width="180">
59 63
         <template #default="scope">
60 64
           <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
61 65
             v-hasPermi="['rateList:rateList:edit']">修改</el-button>
@@ -194,7 +198,7 @@ const upload = reactive({
194 198
   open: false,
195 199
   isUploading: false,
196 200
   headers: { Authorization: 'Bearer ' + getToken() },
197
-  url: import.meta.env.VITE_APP_BASE_API + 'blocked/rate/importData'
201
+  url: import.meta.env.VITE_APP_BASE_API + '/blocked/rate/importData'
198 202
 })
199 203
 
200 204
 const queryParams = reactive({
@@ -231,6 +235,12 @@ function getList() {
231 235
   })
232 236
 }
233 237
 
238
+const getDictLabel = (dictList, value) => {
239
+  if (!dictList || !value) return ''
240
+  const dict = dictList.find(item => item.value === value)
241
+  return dict ? dict.label : value
242
+}
243
+
234 244
 function getDeptList() {
235 245
   listDept({}).then(response => {
236 246
     const deptList = response.data || []