Преглед изворни кода

feat(运行数据): 添加生产运行数据展示页面

实现运行数据展示页面,包含旅检、行检、货检和车辆过检数据统计
添加多个ECharts图表展示各类数据趋势和分布
支持响应式布局适配不同屏幕尺寸
huoyi пре 1 месец
родитељ
комит
7f2890d372
1 измењених фајлова са 601 додато и 0 уклоњено
  1. 601 0
      src/views/runData/runScreen/index.vue

+ 601 - 0
src/views/runData/runScreen/index.vue

@@ -0,0 +1,601 @@
1
+<template>
2
+  <div class="run-screen-container">
3
+    <!-- 页面标题 -->
4
+    <div class="page-title">
5
+      <h1>盛世鹰眸质控系统[生产运行](安检站)</h1>
6
+    </div>
7
+
8
+    <!-- 模块一:运行数据 -->
9
+    <div class="module-section">
10
+      <div class="module-title">模块一:运行数据</div>
11
+      
12
+      <!-- 第一行 -->
13
+      <el-row :gutter="20" class="row-margin">
14
+        <!-- 左边50%:两个div -->
15
+        <el-col :span="12">
16
+          <el-row :gutter="20">
17
+            <el-col :span="12">
18
+              <div class="data-card">
19
+                <div class="card-title">旅检过检总人数</div>
20
+                <div class="card-value">{{ runData.travelInspectionTotal }}人</div>
21
+              </div>
22
+            </el-col>
23
+            <el-col :span="12">
24
+              <div class="data-card">
25
+                <div class="card-title">行检过检总行李数</div>
26
+                <div class="card-value">{{ runData.luggageInspectionTotal }}件</div>
27
+              </div>
28
+            </el-col>
29
+          </el-row>
30
+        </el-col>
31
+        <!-- 右边50%:T1旅检过检人数折线图 -->
32
+        <el-col :span="12">
33
+          <div class="chart-card">
34
+            <div class="chart-title">T1旅检过检人数</div>
35
+            <div ref="t1TravelChart" class="chart-container"></div>
36
+          </div>
37
+        </el-col>
38
+      </el-row>
39
+
40
+      <!-- 第二行 -->
41
+      <el-row :gutter="20" class="row-margin">
42
+        <el-col :span="12">
43
+          <div class="chart-card">
44
+            <div class="chart-title">T2旅检过检人数</div>
45
+            <div ref="t2TravelChart" class="chart-container"></div>
46
+          </div>
47
+        </el-col>
48
+        <el-col :span="12">
49
+          <div class="chart-card">
50
+            <div class="chart-title">行检过检数</div>
51
+            <div ref="luggageChart" class="chart-container"></div>
52
+          </div>
53
+        </el-col>
54
+      </el-row>
55
+
56
+      <!-- 第三行 -->
57
+      <el-row :gutter="20" class="row-margin">
58
+        <el-col :span="8">
59
+          <div class="data-card">
60
+            <div class="card-title">国内货站总过检数</div>
61
+            <div class="card-value">{{ runData.domesticCargoTotal }}件</div>
62
+          </div>
63
+        </el-col>
64
+        <el-col :span="8">
65
+          <div class="data-card">
66
+            <div class="card-title">国际货站总过检数</div>
67
+            <div class="card-value">{{ runData.internationalCargoTotal }}件</div>
68
+          </div>
69
+        </el-col>
70
+        <el-col :span="8">
71
+          <div class="data-card">
72
+            <div class="card-title">道口车辆过检数</div>
73
+            <div class="card-value">{{ runData.vehicleInspectionTotal }}辆</div>
74
+          </div>
75
+        </el-col>
76
+      </el-row>
77
+
78
+      <!-- 第四行 -->
79
+      <el-row :gutter="20" class="row-margin">
80
+        <el-col :span="12">
81
+          <div class="chart-card">
82
+            <div class="chart-title">货物过检数</div>
83
+            <div ref="cargoChart" class="chart-container"></div>
84
+          </div>
85
+        </el-col>
86
+        <el-col :span="12">
87
+          <div class="chart-card">
88
+            <div class="chart-title">车辆过检数</div>
89
+            <div ref="vehicleChart" class="chart-container"></div>
90
+          </div>
91
+        </el-col>
92
+      </el-row>
93
+    </div>
94
+
95
+    <!-- 模块二:查获/收缴数据 -->
96
+    <div class="module-section">
97
+      <div class="module-title">模块二:查获/收缴数据</div>
98
+      
99
+      <!-- 第一行 -->
100
+      <el-row :gutter="20" class="row-margin">
101
+        <el-col :span="12">
102
+          <div class="chart-card">
103
+            <div class="chart-title">查获数据</div>
104
+            <div ref="seizePieChart" class="chart-container"></div>
105
+          </div>
106
+        </el-col>
107
+        <el-col :span="12">
108
+          <div class="chart-card">
109
+            <div class="chart-title">大队查获数对比图</div>
110
+            <div ref="teamSeizeBarChart" class="chart-container"></div>
111
+          </div>
112
+        </el-col>
113
+      </el-row>
114
+
115
+      <!-- 第二行 -->
116
+      <el-row :gutter="20" class="row-margin">
117
+        <el-col :span="12">
118
+          <div class="chart-card">
119
+            <div class="chart-title">T1区域各大队查获数对比图</div>
120
+            <div ref="t1TeamBarChart" class="chart-container"></div>
121
+          </div>
122
+        </el-col>
123
+        <el-col :span="12">
124
+          <div class="chart-card">
125
+            <div class="chart-title">T2区域各大队查获数对比图</div>
126
+            <div ref="t2TeamBarChart" class="chart-container"></div>
127
+          </div>
128
+        </el-col>
129
+      </el-row>
130
+
131
+      <!-- 第三行 -->
132
+      <el-row :gutter="20" class="row-margin">
133
+        <el-col :span="12">
134
+          <div class="chart-card">
135
+            <div class="chart-title">不合格充电宝劝阻数组</div>
136
+            <div ref="powerBankPieChart" class="chart-container"></div>
137
+          </div>
138
+        </el-col>
139
+        <el-col :span="12">
140
+          <div class="chart-card">
141
+            <div class="chart-title">待检区收缴禁限带物品数据表</div>
142
+            <div ref="waitingAreaPieChart" class="chart-container"></div>
143
+          </div>
144
+        </el-col>
145
+      </el-row>
146
+    </div>
147
+  </div>
148
+</template>
149
+
150
+<script setup name="RunScreen">
151
+import { ref, onMounted, onUnmounted, nextTick } from 'vue'
152
+import * as echarts from 'echarts'
153
+
154
+// 图表引用
155
+const t1TravelChart = ref(null)
156
+const t2TravelChart = ref(null)
157
+const luggageChart = ref(null)
158
+const cargoChart = ref(null)
159
+const vehicleChart = ref(null)
160
+const seizePieChart = ref(null)
161
+const teamSeizeBarChart = ref(null)
162
+const t1TeamBarChart = ref(null)
163
+const t2TeamBarChart = ref(null)
164
+const powerBankPieChart = ref(null)
165
+const waitingAreaPieChart = ref(null)
166
+
167
+// 图表实例
168
+let t1TravelChartInstance = null
169
+let t2TravelChartInstance = null
170
+let luggageChartInstance = null
171
+let cargoChartInstance = null
172
+let vehicleChartInstance = null
173
+let seizePieChartInstance = null
174
+let teamSeizeBarChartInstance = null
175
+let t1TeamBarChartInstance = null
176
+let t2TeamBarChartInstance = null
177
+let powerBankPieChartInstance = null
178
+let waitingAreaPieChartInstance = null
179
+
180
+// 运行数据
181
+const runData = ref({
182
+  travelInspectionTotal: 12580,
183
+  luggageInspectionTotal: 8920,
184
+  domesticCargoTotal: 1560,
185
+  internationalCargoTotal: 980,
186
+  vehicleInspectionTotal: 450
187
+})
188
+
189
+// 初始化所有图表
190
+function initCharts() {
191
+  // T1旅检过检人数折线图
192
+  if (t1TravelChart.value) {
193
+    t1TravelChartInstance = echarts.init(t1TravelChart.value)
194
+    t1TravelChartInstance.setOption({
195
+      tooltip: { trigger: 'axis' },
196
+      xAxis: {
197
+        type: 'category',
198
+        data: ['08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00']
199
+      },
200
+      yAxis: { type: 'value' },
201
+      series: [{
202
+        data: [820, 932, 901, 934, 1290, 1330, 1320],
203
+        type: 'line',
204
+        smooth: true,
205
+        lineStyle: { color: '#5470c6' },
206
+        areaStyle: { color: 'rgba(84, 112, 198, 0.2)' }
207
+      }]
208
+    })
209
+  }
210
+
211
+  // T2旅检过检人数折线图
212
+  if (t2TravelChart.value) {
213
+    t2TravelChartInstance = echarts.init(t2TravelChart.value)
214
+    t2TravelChartInstance.setOption({
215
+      tooltip: { trigger: 'axis' },
216
+      xAxis: {
217
+        type: 'category',
218
+        data: ['08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00']
219
+      },
220
+      yAxis: { type: 'value' },
221
+      series: [{
222
+        data: [620, 732, 701, 734, 890, 930, 920],
223
+        type: 'line',
224
+        smooth: true,
225
+        lineStyle: { color: '#91cc75' },
226
+        areaStyle: { color: 'rgba(145, 204, 117, 0.2)' }
227
+      }]
228
+    })
229
+  }
230
+
231
+  // 行检过检数折线图
232
+  if (luggageChart.value) {
233
+    luggageChartInstance = echarts.init(luggageChart.value)
234
+    luggageChartInstance.setOption({
235
+      tooltip: { trigger: 'axis' },
236
+      xAxis: {
237
+        type: 'category',
238
+        data: ['08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00']
239
+      },
240
+      yAxis: { type: 'value' },
241
+      series: [{
242
+        data: [620, 832, 701, 834, 1090, 1130, 1120],
243
+        type: 'line',
244
+        smooth: true,
245
+        lineStyle: { color: '#fac858' },
246
+        areaStyle: { color: 'rgba(250, 200, 88, 0.2)' }
247
+      }]
248
+    })
249
+  }
250
+
251
+  // 货物过检数折线图
252
+  if (cargoChart.value) {
253
+    cargoChartInstance = echarts.init(cargoChart.value)
254
+    cargoChartInstance.setOption({
255
+      tooltip: { trigger: 'axis' },
256
+      xAxis: {
257
+        type: 'category',
258
+        data: ['08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00']
259
+      },
260
+      yAxis: { type: 'value' },
261
+      series: [{
262
+        data: [120, 232, 201, 234, 290, 330, 320],
263
+        type: 'line',
264
+        smooth: true,
265
+        lineStyle: { color: '#ee6666' },
266
+        areaStyle: { color: 'rgba(238, 102, 102, 0.2)' }
267
+      }]
268
+    })
269
+  }
270
+
271
+  // 车辆过检数折线图
272
+  if (vehicleChart.value) {
273
+    vehicleChartInstance = echarts.init(vehicleChart.value)
274
+    vehicleChartInstance.setOption({
275
+      tooltip: { trigger: 'axis' },
276
+      xAxis: {
277
+        type: 'category',
278
+        data: ['08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00']
279
+      },
280
+      yAxis: { type: 'value' },
281
+      series: [{
282
+        data: [30, 52, 41, 54, 70, 83, 80],
283
+        type: 'line',
284
+        smooth: true,
285
+        lineStyle: { color: '#73c0de' },
286
+        areaStyle: { color: 'rgba(115, 192, 222, 0.2)' }
287
+      }]
288
+    })
289
+  }
290
+
291
+  // 查获数据环形图
292
+  if (seizePieChart.value) {
293
+    seizePieChartInstance = echarts.init(seizePieChart.value)
294
+    seizePieChartInstance.setOption({
295
+      tooltip: { trigger: 'item' },
296
+      legend: { orient: 'vertical', right: 10, top: 'center' },
297
+      series: [{
298
+        name: '查获数据',
299
+        type: 'pie',
300
+        radius: ['40%', '70%'],
301
+        center: ['40%', '50%'],
302
+        data: [
303
+          { value: 1048, name: '火种' },
304
+          { value: 735, name: '刀具' },
305
+          { value: 580, name: '警械器具' },
306
+          { value: 484, name: '烟花爆竹' },
307
+          { value: 300, name: '其他' }
308
+        ],
309
+        emphasis: {
310
+          itemStyle: {
311
+            shadowBlur: 10,
312
+            shadowOffsetX: 0,
313
+            shadowColor: 'rgba(0, 0, 0, 0.5)'
314
+          }
315
+        }
316
+      }]
317
+    })
318
+  }
319
+
320
+  // 大队查获数对比柱状图
321
+  if (teamSeizeBarChart.value) {
322
+    teamSeizeBarChartInstance = echarts.init(teamSeizeBarChart.value)
323
+    teamSeizeBarChartInstance.setOption({
324
+      tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
325
+      xAxis: {
326
+        type: 'category',
327
+        data: ['一大队', '二大队', '三大队', '四大队', '五大队', '六大队']
328
+      },
329
+      yAxis: { type: 'value' },
330
+      series: [{
331
+        data: [120, 200, 150, 80, 70, 110],
332
+        type: 'bar',
333
+        itemStyle: { color: '#5470c6' }
334
+      }]
335
+    })
336
+  }
337
+
338
+  // T1区域各大队查获数对比柱状图
339
+  if (t1TeamBarChart.value) {
340
+    t1TeamBarChartInstance = echarts.init(t1TeamBarChart.value)
341
+    t1TeamBarChartInstance.setOption({
342
+      tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
343
+      xAxis: {
344
+        type: 'category',
345
+        data: ['一大队', '二大队', '三大队', '四大队']
346
+      },
347
+      yAxis: { type: 'value' },
348
+      series: [{
349
+        data: [80, 120, 90, 60],
350
+        type: 'bar',
351
+        itemStyle: { color: '#91cc75' }
352
+      }]
353
+    })
354
+  }
355
+
356
+  // T2区域各大队查获数对比柱状图
357
+  if (t2TeamBarChart.value) {
358
+    t2TeamBarChartInstance = echarts.init(t2TeamBarChart.value)
359
+    t2TeamBarChartInstance.setOption({
360
+      tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
361
+      xAxis: {
362
+        type: 'category',
363
+        data: ['五大队', '六大队', '七大队', '八大队']
364
+      },
365
+      yAxis: { type: 'value' },
366
+      series: [{
367
+        data: [70, 90, 80, 50],
368
+        type: 'bar',
369
+        itemStyle: { color: '#fac858' }
370
+      }]
371
+    })
372
+  }
373
+
374
+  // 不合格充电宝劝阻数组环形图
375
+  if (powerBankPieChart.value) {
376
+    powerBankPieChartInstance = echarts.init(powerBankPieChart.value)
377
+    powerBankPieChartInstance.setOption({
378
+      tooltip: { trigger: 'item' },
379
+      legend: { orient: 'vertical', right: 10, top: 'center' },
380
+      series: [{
381
+        name: '不合格充电宝',
382
+        type: 'pie',
383
+        radius: ['40%', '70%'],
384
+        center: ['40%', '50%'],
385
+        data: [
386
+          { value: 320, name: '无3C标识' },
387
+          { value: 240, name: '标识不清' },
388
+          { value: 180, name: '超规数量' },
389
+          { value: 150, name: '其他' }
390
+        ],
391
+        emphasis: {
392
+          itemStyle: {
393
+            shadowBlur: 10,
394
+            shadowOffsetX: 0,
395
+            shadowColor: 'rgba(0, 0, 0, 0.5)'
396
+          }
397
+        }
398
+      }]
399
+    })
400
+  }
401
+
402
+  // 待检区收缴禁限带物品数据表环形图
403
+  if (waitingAreaPieChart.value) {
404
+    waitingAreaPieChartInstance = echarts.init(waitingAreaPieChart.value)
405
+    waitingAreaPieChartInstance.setOption({
406
+      tooltip: { trigger: 'item' },
407
+      legend: { orient: 'vertical', right: 10, top: 'center' },
408
+      series: [{
409
+        name: '禁限带物品',
410
+        type: 'pie',
411
+        radius: ['40%', '70%'],
412
+        center: ['40%', '50%'],
413
+        data: [
414
+          { value: 420, name: '火种' },
415
+          { value: 280, name: '液态物品' },
416
+          { value: 190, name: '其他物品' },
417
+          { value: 120, name: '其他' }
418
+        ],
419
+        emphasis: {
420
+          itemStyle: {
421
+            shadowBlur: 10,
422
+            shadowOffsetX: 0,
423
+            shadowColor: 'rgba(0, 0, 0, 0.5)'
424
+          }
425
+        }
426
+      }]
427
+    })
428
+  }
429
+}
430
+
431
+// 窗口大小变化时重新调整图表大小
432
+function handleResize() {
433
+  const charts = [
434
+    t1TravelChartInstance, t2TravelChartInstance, luggageChartInstance,
435
+    cargoChartInstance, vehicleChartInstance, seizePieChartInstance,
436
+    teamSeizeBarChartInstance, t1TeamBarChartInstance, t2TeamBarChartInstance,
437
+    powerBankPieChartInstance, waitingAreaPieChartInstance
438
+  ]
439
+  
440
+  charts.forEach(chart => {
441
+    if (chart) {
442
+      chart.resize()
443
+    }
444
+  })
445
+}
446
+
447
+// 组件挂载时初始化图表
448
+onMounted(() => {
449
+  nextTick(() => {
450
+    initCharts()
451
+    window.addEventListener('resize', handleResize)
452
+  })
453
+})
454
+
455
+// 组件卸载时销毁图表
456
+onUnmounted(() => {
457
+  const charts = [
458
+    t1TravelChartInstance, t2TravelChartInstance, luggageChartInstance,
459
+    cargoChartInstance, vehicleChartInstance, seizePieChartInstance,
460
+    teamSeizeBarChartInstance, t1TeamBarChartInstance, t2TeamBarChartInstance,
461
+    powerBankPieChartInstance, waitingAreaPieChartInstance
462
+  ]
463
+  
464
+  charts.forEach(chart => {
465
+    if (chart) {
466
+      chart.dispose()
467
+    }
468
+  })
469
+  
470
+  window.removeEventListener('resize', handleResize)
471
+})
472
+</script>
473
+
474
+<style scoped>
475
+.run-screen-container {
476
+  padding: 20px;
477
+  background: #f0f2f5;
478
+  min-height: 100vh;
479
+}
480
+
481
+.page-title {
482
+  text-align: center;
483
+  margin-bottom: 30px;
484
+}
485
+
486
+.page-title h1 {
487
+  font-size: 28px;
488
+  color: #333;
489
+  font-weight: bold;
490
+  margin: 0;
491
+}
492
+
493
+.module-section {
494
+  margin-bottom: 30px;
495
+  background: #fff;
496
+  border-radius: 8px;
497
+  padding: 20px;
498
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
499
+}
500
+
501
+.module-title {
502
+  font-size: 20px;
503
+  font-weight: bold;
504
+  color: #333;
505
+  margin-bottom: 20px;
506
+  padding-bottom: 10px;
507
+  border-bottom: 2px solid #409eff;
508
+}
509
+
510
+.row-margin {
511
+  margin-bottom: 20px;
512
+}
513
+
514
+.data-card {
515
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
516
+  border-radius: 8px;
517
+  padding: 20px;
518
+  color: white;
519
+  text-align: center;
520
+  height: 120px;
521
+  display: flex;
522
+  flex-direction: column;
523
+  justify-content: center;
524
+  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
525
+}
526
+
527
+.card-title {
528
+  font-size: 16px;
529
+  margin-bottom: 10px;
530
+  opacity: 0.9;
531
+}
532
+
533
+.card-value {
534
+  font-size: 28px;
535
+  font-weight: bold;
536
+}
537
+
538
+.chart-card {
539
+  background: #fff;
540
+  border-radius: 8px;
541
+  padding: 15px;
542
+  border: 1px solid #e6e8eb;
543
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
544
+  height: 300px;
545
+}
546
+
547
+.chart-title {
548
+  font-size: 16px;
549
+  font-weight: bold;
550
+  color: #333;
551
+  margin-bottom: 15px;
552
+  text-align: center;
553
+}
554
+
555
+.chart-container {
556
+  width: 100%;
557
+  height: calc(100% - 30px);
558
+}
559
+
560
+/* 响应式设计 */
561
+@media (max-width: 1200px) {
562
+  .data-card {
563
+    height: 100px;
564
+  }
565
+  
566
+  .card-value {
567
+    font-size: 24px;
568
+  }
569
+  
570
+  .chart-card {
571
+    height: 250px;
572
+  }
573
+}
574
+
575
+@media (max-width: 768px) {
576
+  .run-screen-container {
577
+    padding: 10px;
578
+  }
579
+  
580
+  .module-section {
581
+    padding: 15px;
582
+  }
583
+  
584
+  .data-card {
585
+    height: 80px;
586
+    padding: 15px;
587
+  }
588
+  
589
+  .card-title {
590
+    font-size: 14px;
591
+  }
592
+  
593
+  .card-value {
594
+    font-size: 20px;
595
+  }
596
+  
597
+  .chart-card {
598
+    height: 200px;
599
+  }
600
+}
601
+</style>