Bläddra i källkod

feat(查堵数据): 新增查堵数据统计大屏、速率表、漏检统计表、每日行李查堵表等功能模块

新增查堵数据统计大屏页面,包含运行数据分析、查堵人员分析、培训质控分析和放行速率分析四个模块
新增速率表功能模块,支持查询、新增、修改、删除和导出速率统计数据
新增漏检统计表功能模块,支持查询、新增、修改、删除和导出漏检统计数据
新增每日行李查堵表功能模块,支持查询、新增、修改、删除和导出每日行李查堵数据
新增每日各时段查堵行李表功能模块,支持查询、新增、修改、删除和导出各时段查堵数据
huoyi 4 dagar sedan
förälder
incheckning
b026fd871d

+ 53 - 0
src/api/blockingData/dailyLuggageCheckInList.js

@@ -0,0 +1,53 @@
1
+import request from '@/utils/request'
2
+
3
+// 查询每日行李查缉列表
4
+export function listDailyLuggageCheckInList(query) {
5
+  return request({
6
+    url: '/dailyLuggageCheckInList/list',
7
+    method: 'get',
8
+    params: query
9
+  })
10
+}
11
+
12
+// 查询每日行李查缉详细
13
+export function getDailyLuggageCheckInList(id) {
14
+  return request({
15
+    url: '/dailyLuggageCheckInList/' + id,
16
+    method: 'get'
17
+  })
18
+}
19
+
20
+// 新增每日行李查缉
21
+export function addDailyLuggageCheckInList(data) {
22
+  return request({
23
+    url: '/dailyLuggageCheckInList',
24
+    method: 'post',
25
+    data: data
26
+  })
27
+}
28
+
29
+// 修改每日行李查缉
30
+export function updateDailyLuggageCheckInList(data) {
31
+  return request({
32
+    url: '/dailyLuggageCheckInList',
33
+    method: 'put',
34
+    data: data
35
+  })
36
+}
37
+
38
+// 删除每日行李查缉
39
+export function delDailyLuggageCheckInList(id) {
40
+  return request({
41
+    url: '/dailyLuggageCheckInList/' + id,
42
+    method: 'delete'
43
+  })
44
+}
45
+
46
+// 导出每日行李查缉
47
+export function exportDailyLuggageCheckInList(query) {
48
+  return request({
49
+    url: '/dailyLuggageCheckInList/export',
50
+    method: 'get',
51
+    params: query
52
+  })
53
+}

+ 53 - 0
src/api/blockingData/dailyLuggageInspectionScheduleByTime.js

@@ -0,0 +1,53 @@
1
+import request from '@/utils/request'
2
+
3
+// 查询每日各时段查堵行李列表
4
+export function listDailyLuggageInspectionScheduleByTime(query) {
5
+  return request({
6
+    url: '/dailyLuggageInspectionScheduleByTime/list',
7
+    method: 'get',
8
+    params: query
9
+  })
10
+}
11
+
12
+// 查询每日各时段查堵行李详细
13
+export function getDailyLuggageInspectionScheduleByTime(id) {
14
+  return request({
15
+    url: '/dailyLuggageInspectionScheduleByTime/' + id,
16
+    method: 'get'
17
+  })
18
+}
19
+
20
+// 新增每日各时段查堵行李
21
+export function addDailyLuggageInspectionScheduleByTime(data) {
22
+  return request({
23
+    url: '/dailyLuggageInspectionScheduleByTime',
24
+    method: 'post',
25
+    data: data
26
+  })
27
+}
28
+
29
+// 修改每日各时段查堵行李
30
+export function updateDailyLuggageInspectionScheduleByTime(data) {
31
+  return request({
32
+    url: '/dailyLuggageInspectionScheduleByTime',
33
+    method: 'put',
34
+    data: data
35
+  })
36
+}
37
+
38
+// 删除每日各时段查堵行李
39
+export function delDailyLuggageInspectionScheduleByTime(id) {
40
+  return request({
41
+    url: '/dailyLuggageInspectionScheduleByTime/' + id,
42
+    method: 'delete'
43
+  })
44
+}
45
+
46
+// 导出每日各时段查堵行李
47
+export function exportDailyLuggageInspectionScheduleByTime(query) {
48
+  return request({
49
+    url: '/dailyLuggageInspectionScheduleByTime/export',
50
+    method: 'get',
51
+    params: query
52
+  })
53
+}

+ 53 - 0
src/api/blockingData/missedInspection.js

@@ -0,0 +1,53 @@
1
+import request from '@/utils/request'
2
+
3
+// 查询漏检列表
4
+export function listMissedInspection(query) {
5
+  return request({
6
+    url: '/missedInspection/list',
7
+    method: 'get',
8
+    params: query
9
+  })
10
+}
11
+
12
+// 查询漏检详细
13
+export function getMissedInspection(id) {
14
+  return request({
15
+    url: '/missedInspection/' + id,
16
+    method: 'get'
17
+  })
18
+}
19
+
20
+// 新增漏检
21
+export function addMissedInspection(data) {
22
+  return request({
23
+    url: '/missedInspection',
24
+    method: 'post',
25
+    data: data
26
+  })
27
+}
28
+
29
+// 修改漏检
30
+export function updateMissedInspection(data) {
31
+  return request({
32
+    url: '/missedInspection',
33
+    method: 'put',
34
+    data: data
35
+  })
36
+}
37
+
38
+// 删除漏检
39
+export function delMissedInspection(id) {
40
+  return request({
41
+    url: '/missedInspection/' + id,
42
+    method: 'delete'
43
+  })
44
+}
45
+
46
+// 导出漏检
47
+export function exportMissedInspection(query) {
48
+  return request({
49
+    url: '/missedInspection/export',
50
+    method: 'get',
51
+    params: query
52
+  })
53
+}

+ 53 - 0
src/api/blockingData/rateList.js

@@ -0,0 +1,53 @@
1
+import request from '@/utils/request'
2
+
3
+// 查询速率统计列表
4
+export function listRate(query) {
5
+  return request({
6
+    url: '/rateList/list',
7
+    method: 'get',
8
+    params: query
9
+  })
10
+}
11
+
12
+// 查询速率统计详细
13
+export function getRate(id) {
14
+  return request({
15
+    url: '/rateList/' + id,
16
+    method: 'get'
17
+  })
18
+}
19
+
20
+// 新增速率统计
21
+export function addRate(data) {
22
+  return request({
23
+    url: '/rateList',
24
+    method: 'post',
25
+    data: data
26
+  })
27
+}
28
+
29
+// 修改速率统计
30
+export function updateRate(data) {
31
+  return request({
32
+    url: '/rateList',
33
+    method: 'put',
34
+    data: data
35
+  })
36
+}
37
+
38
+// 删除速率统计
39
+export function delRate(id) {
40
+  return request({
41
+    url: '/rateList/' + id,
42
+    method: 'delete'
43
+  })
44
+}
45
+
46
+// 导出速率统计
47
+export function exportRate(query) {
48
+  return request({
49
+    url: '/rateList/export',
50
+    method: 'get',
51
+    params: query
52
+  })
53
+}

+ 53 - 0
src/views/blockingData/blockingDataScreen/components/ModuleContainer.vue

@@ -0,0 +1,53 @@
1
+<template>
2
+  <div class="module-container">
3
+    <div class="module-header">
4
+      <span class="module-title">{{ title }}</span>
5
+    </div>
6
+    <div class="module-content">
7
+      <slot></slot>
8
+    </div>
9
+  </div>
10
+</template>
11
+
12
+<script setup>
13
+defineProps({
14
+  title: {
15
+    type: String,
16
+    default: ''
17
+  }
18
+})
19
+</script>
20
+
21
+<style lang="less" scoped>
22
+.module-container {
23
+  width: 100%;
24
+  height: 100%;
25
+  display: flex;
26
+  flex-direction: column;
27
+  background: rgba(13, 80, 122, 0.3);
28
+  border: 1px solid rgba(112, 207, 231, 0.3);
29
+  border-radius: 4px;
30
+}
31
+
32
+.module-header {
33
+  width: 100%;
34
+  height: 36px;
35
+  display: flex;
36
+  align-items: center;
37
+  padding: 0 15px;
38
+  background: linear-gradient(90deg, rgba(11, 88, 131, 0.8) 0%, rgba(13, 80, 122, 0.4) 100%);
39
+  border-bottom: 1px solid rgba(112, 207, 231, 0.3);
40
+}
41
+
42
+.module-title {
43
+  font-size: 16px;
44
+  font-weight: 600;
45
+  color: #fbffff;
46
+}
47
+
48
+.module-content {
49
+  flex: 1;
50
+  padding: 10px;
51
+  overflow: hidden;
52
+}
53
+</style>

+ 214 - 0
src/views/blockingData/blockingDataScreen/components/ModuleFour.vue

@@ -0,0 +1,214 @@
1
+<template>
2
+  <module-container title="放行速率分析">
3
+    <div class="module-four-content">
4
+      <div class="chart-row">
5
+        <div class="chart-item">
6
+          <div class="chart-title">T1航站楼放行速率</div>
7
+          <div ref="chart1" class="echarts"></div>
8
+        </div>
9
+        <div class="chart-item">
10
+          <div class="chart-title">T2航站楼放行速率</div>
11
+          <div ref="chart2" class="echarts"></div>
12
+        </div>
13
+      </div>
14
+      <div class="chart-row">
15
+        <div class="chart-item">
16
+          <div class="chart-title">大队总平均速率对比(国内)</div>
17
+          <div ref="chart3" class="echarts"></div>
18
+        </div>
19
+        <div class="chart-item">
20
+          <div class="chart-title">大队总平均速率对比(国际+中转)</div>
21
+          <div ref="chart4" class="echarts"></div>
22
+        </div>
23
+      </div>
24
+    </div>
25
+  </module-container>
26
+</template>
27
+
28
+<script setup>
29
+import { ref, onMounted, onBeforeUnmount } from 'vue'
30
+import * as echarts from 'echarts'
31
+import ModuleContainer from './ModuleContainer.vue'
32
+import { useEcharts } from '@/hooks/chart.js'
33
+
34
+const chart1 = ref(null)
35
+const chart2 = ref(null)
36
+const chart3 = ref(null)
37
+const chart4 = ref(null)
38
+
39
+const { setOption: setOption1 } = useEcharts(chart1)
40
+const { setOption: setOption2 } = useEcharts(chart2)
41
+const { setOption: setOption3 } = useEcharts(chart3)
42
+const { setOption: setOption4 } = useEcharts(chart4)
43
+
44
+const generateData = () => {
45
+  const data = []
46
+  for (let i = 0; i < 30; i++) {
47
+    data.push(Math.floor(Math.random() * 60) + 120)
48
+  }
49
+  return data
50
+}
51
+
52
+const xAxisData = []
53
+for (let i = 1; i <= 30; i++) {
54
+  xAxisData.push(i + '日')
55
+}
56
+
57
+const lineChartOption = (data1, data2, color1, color2) => ({
58
+  grid: {
59
+    left: '10%',
60
+    top: '15%',
61
+    right: '5%',
62
+    bottom: '15%',
63
+    containLabel: true
64
+  },
65
+  xAxis: {
66
+    type: 'category',
67
+    data: xAxisData,
68
+    axisLine: { lineStyle: { color: '#999' } },
69
+    axisLabel: { fontSize: 10, color: '#666' }
70
+  },
71
+  yAxis: {
72
+    type: 'value',
73
+    axisLine: { lineStyle: { color: '#999' } },
74
+    axisLabel: { fontSize: 10, color: '#666' },
75
+    splitLine: { lineStyle: { color: '#eee' } }
76
+  },
77
+  legend: {
78
+    top: 0,
79
+    right: 10,
80
+    textStyle: { fontSize: 10 }
81
+  },
82
+  series: [
83
+    {
84
+      name: '高峰期时段',
85
+      type: 'line',
86
+      smooth: true,
87
+      symbol: 'circle',
88
+      symbolSize: 6,
89
+      data: data1,
90
+      itemStyle: { color: color1 },
91
+      areaStyle: {
92
+        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
93
+          { offset: 0, color: color1 + '40' },
94
+          { offset: 1, color: color1 + '10' }
95
+        ])
96
+      }
97
+    },
98
+    {
99
+      name: '非高峰期时段',
100
+      type: 'line',
101
+      smooth: true,
102
+      symbol: 'circle',
103
+      symbolSize: 6,
104
+      data: data2,
105
+      itemStyle: { color: color2 },
106
+      areaStyle: {
107
+        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
108
+          { offset: 0, color: color2 + '40' },
109
+          { offset: 1, color: color2 + '10' }
110
+        ])
111
+      }
112
+    }
113
+  ]
114
+})
115
+
116
+const barLineChartOption = (data1, data2, data3, colors) => ({
117
+  grid: {
118
+    left: '10%',
119
+    top: '15%',
120
+    right: '5%',
121
+    bottom: '15%',
122
+    containLabel: true
123
+  },
124
+  xAxis: {
125
+    type: 'category',
126
+    data: xAxisData,
127
+    axisLine: { lineStyle: { color: '#999' } },
128
+    axisLabel: { fontSize: 10, color: '#666', rotate: 45 }
129
+  },
130
+  yAxis: {
131
+    type: 'value',
132
+    axisLine: { lineStyle: { color: '#999' } },
133
+    axisLabel: { fontSize: 10, color: '#666' },
134
+    splitLine: { lineStyle: { color: '#eee' } }
135
+  },
136
+  legend: {
137
+    top: 0,
138
+    right: 10,
139
+    textStyle: { fontSize: 10 }
140
+  },
141
+  series: [
142
+    {
143
+      name: '安检三大队',
144
+      type: 'bar',
145
+      data: data1,
146
+      itemStyle: { color: colors[0] },
147
+      barWidth: 8
148
+    },
149
+    {
150
+      name: '安检二大队',
151
+      type: 'line',
152
+      smooth: true,
153
+      symbol: 'circle',
154
+      symbolSize: 6,
155
+      data: data2,
156
+      itemStyle: { color: colors[1] }
157
+    },
158
+    {
159
+      name: '安检一大队',
160
+      type: 'line',
161
+      smooth: true,
162
+      symbol: 'circle',
163
+      symbolSize: 6,
164
+      data: data3,
165
+      itemStyle: { color: colors[2] }
166
+    }
167
+  ]
168
+})
169
+
170
+onMounted(() => {
171
+  setOption1(lineChartOption(generateData(), generateData(), '#3b82f6', '#22c55e'))
172
+  setOption2(lineChartOption(generateData(), generateData(), '#2563eb', '#16a34a'))
173
+  setOption3(barLineChartOption(generateData(), generateData(), generateData(), ['#3b82f6', '#22c55e', '#f97316']))
174
+  setOption4(barLineChartOption(generateData(), generateData(), generateData(), ['#3b82f6', '#22c55e', '#f97316']))
175
+})
176
+</script>
177
+
178
+<style lang="less" scoped>
179
+.module-four-content {
180
+  height: 100%;
181
+  display: flex;
182
+  flex-direction: column;
183
+  gap: 10px;
184
+}
185
+
186
+.chart-row {
187
+  flex: 1;
188
+  display: flex;
189
+  gap: 10px;
190
+}
191
+
192
+.chart-item {
193
+  flex: 1;
194
+  display: flex;
195
+  flex-direction: column;
196
+  background: #fff;
197
+  border-radius: 6px;
198
+  padding: 8px;
199
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
200
+}
201
+
202
+.chart-title {
203
+  font-size: 12px;
204
+  color: #333;
205
+  margin-bottom: 5px;
206
+  text-align: center;
207
+}
208
+
209
+.echarts {
210
+  flex: 1;
211
+  width: 100%;
212
+  min-height: 0;
213
+}
214
+</style>

+ 566 - 0
src/views/blockingData/blockingDataScreen/components/ModuleOne.vue

@@ -0,0 +1,566 @@
1
+<template>
2
+  <module-container title="运行数据分析">
3
+    <div class="module-one-content">
4
+      <!-- 新增:统计卡片行 -->
5
+      <div class="stats-row">
6
+        <div class="stat-card">
7
+          <div class="stat-card-inner">
8
+            <div class="stat-left">
9
+              <div class="stat-label">查堵总数(所有大队)</div>
10
+              <div class="stat-value">2327</div>
11
+            </div>
12
+            <div class="stat-right">
13
+              <div class="stat-title">查堵总数</div>
14
+              <div ref="chart0a" class="echarts-small"></div>
15
+            </div>
16
+          </div>
17
+        </div>
18
+        <div class="stat-card">
19
+          <div class="stat-card-inner">
20
+            <div class="stat-left">
21
+              <div class="stat-label">总查堵万分率</div>
22
+              <div class="stat-value">2.22</div>
23
+            </div>
24
+            <div class="stat-right">
25
+              <div class="stat-title">总查堵万分率</div>
26
+              <div ref="chart0b" class="echarts-small"></div>
27
+            </div>
28
+          </div>
29
+        </div>
30
+      </div>
31
+      
32
+      <!-- 第一行:每日查堵数量 -->
33
+      <div class="chart-row">
34
+        <div class="chart-item">
35
+          <div class="chart-title">每日查堵数量(总表)</div>
36
+          <div ref="chart1" class="echarts"></div>
37
+        </div>
38
+        <div class="chart-item">
39
+          <div class="chart-title">每日查堵数量(大队对比表)</div>
40
+          <div ref="chart2" class="echarts"></div>
41
+        </div>
42
+      </div>
43
+      
44
+      <!-- 第二行:每日查堵万分率 -->
45
+      <div class="chart-row">
46
+        <div class="chart-item">
47
+          <div class="chart-title">每日查堵万分率</div>
48
+          <div ref="chart3" class="echarts"></div>
49
+        </div>
50
+        <div class="chart-item">
51
+          <div class="chart-title">每日查堵万分率(大队对比表)</div>
52
+          <div ref="chart4" class="echarts"></div>
53
+        </div>
54
+      </div>
55
+      
56
+      <!-- 第三行:每日过检图像数 -->
57
+      <div class="chart-row">
58
+        <div class="chart-item">
59
+          <div class="chart-title">每日过检图像数(行检+旅检总表)</div>
60
+          <div ref="chart5" class="echarts"></div>
61
+        </div>
62
+        <div class="chart-item">
63
+          <div class="chart-title">每日过检图像数(行检+旅检大队对比表)</div>
64
+          <div ref="chart6" class="echarts"></div>
65
+        </div>
66
+      </div>
67
+      
68
+      <!-- 第四行:查堵物品分布和区域查堵数量分布 -->
69
+      <div class="chart-row">
70
+        <div class="chart-item">
71
+          <div class="chart-title">查堵物品分布</div>
72
+          <div ref="chart7" class="echarts"></div>
73
+        </div>
74
+        <div class="chart-item">
75
+          <div class="chart-title">区域查堵数量分布</div>
76
+          <div ref="chart8" class="echarts"></div>
77
+        </div>
78
+      </div>
79
+      
80
+      <!-- 第五行:查堵类型分布 -->
81
+      <div class="chart-row full-width">
82
+        <div class="chart-item">
83
+          <div class="chart-title">查堵类型分布</div>
84
+          <div ref="chart9" class="echarts"></div>
85
+        </div>
86
+      </div>
87
+      
88
+      <!-- 第六行:查堵时间段过检行李数及万分率 -->
89
+      <div class="chart-row full-width">
90
+        <div class="chart-item">
91
+          <div class="chart-title">查堵时间段过检行李数及万分率(总表)</div>
92
+          <div ref="chart10" class="echarts"></div>
93
+        </div>
94
+      </div>
95
+      
96
+      <!-- 第七行:每日过检行李数及万分率 -->
97
+      <div class="chart-row full-width">
98
+        <div class="chart-item">
99
+          <div class="chart-title">每日过检行李数及万分率</div>
100
+          <div ref="chart11" class="echarts"></div>
101
+        </div>
102
+      </div>
103
+      
104
+      <!-- 第八行:查堵-AI复查图像总数 -->
105
+      <div class="chart-row full-width">
106
+        <div class="chart-item">
107
+          <div class="chart-title">查堵-AI复查图像总数</div>
108
+          <div ref="chart12" class="echarts"></div>
109
+        </div>
110
+      </div>
111
+      
112
+      <!-- 第九行:AI漏判和误判图像总数 -->
113
+      <div class="chart-row">
114
+        <div class="chart-item">
115
+          <div class="chart-title">查堵-AI漏判图像总数</div>
116
+          <div ref="chart13" class="echarts"></div>
117
+        </div>
118
+        <div class="chart-item">
119
+          <div class="chart-title">查堵-AI误判图像总数</div>
120
+          <div ref="chart14" class="echarts"></div>
121
+        </div>
122
+      </div>
123
+    </div>
124
+  </module-container>
125
+</template>
126
+
127
+<script setup>
128
+import { ref, onMounted } from 'vue'
129
+import * as echarts from 'echarts'
130
+import ModuleContainer from './ModuleContainer.vue'
131
+import { useEcharts } from '@/hooks/chart.js'
132
+
133
+// 图表引用
134
+const chart0a = ref(null)
135
+const chart0b = ref(null)
136
+const chart1 = ref(null)
137
+const chart2 = ref(null)
138
+const chart3 = ref(null)
139
+const chart4 = ref(null)
140
+const chart5 = ref(null)
141
+const chart6 = ref(null)
142
+const chart7 = ref(null)
143
+const chart8 = ref(null)
144
+const chart9 = ref(null)
145
+const chart10 = ref(null)
146
+const chart11 = ref(null)
147
+const chart12 = ref(null)
148
+const chart13 = ref(null)
149
+const chart14 = ref(null)
150
+
151
+// 图表设置函数
152
+const { setOption: setOption0a } = useEcharts(chart0a)
153
+const { setOption: setOption0b } = useEcharts(chart0b)
154
+const { setOption: setOption1 } = useEcharts(chart1)
155
+const { setOption: setOption2 } = useEcharts(chart2)
156
+const { setOption: setOption3 } = useEcharts(chart3)
157
+const { setOption: setOption4 } = useEcharts(chart4)
158
+const { setOption: setOption5 } = useEcharts(chart5)
159
+const { setOption: setOption6 } = useEcharts(chart6)
160
+const { setOption: setOption7 } = useEcharts(chart7)
161
+const { setOption: setOption8 } = useEcharts(chart8)
162
+const { setOption: setOption9 } = useEcharts(chart9)
163
+const { setOption: setOption10 } = useEcharts(chart10)
164
+const { setOption: setOption11 } = useEcharts(chart11)
165
+const { setOption: setOption12 } = useEcharts(chart12)
166
+const { setOption: setOption13 } = useEcharts(chart13)
167
+const { setOption: setOption14 } = useEcharts(chart14)
168
+
169
+// 生成模拟数据
170
+const generateData = (count, min, max) => {
171
+  const data = []
172
+  for (let i = 0; i < count; i++) {
173
+    data.push(Math.floor(Math.random() * (max - min + 1)) + min)
174
+  }
175
+  return data
176
+}
177
+
178
+// X轴数据(30天)
179
+const xAxisData = []
180
+for (let i = 1; i <= 30; i++) {
181
+  xAxisData.push(i + '日')
182
+}
183
+
184
+// 小型柱状图配置(用于统计卡片)
185
+const smallBarOption = (data, color, isHorizontal = false) => ({
186
+  grid: {
187
+    left: '5%',
188
+    top: '5%',
189
+    right: '5%',
190
+    bottom: '5%',
191
+    containLabel: true
192
+  },
193
+  xAxis: {
194
+    type: isHorizontal ? 'value' : 'category',
195
+    data: isHorizontal ? null : ['1', '2', '3'],
196
+    show: false
197
+  },
198
+  yAxis: {
199
+    type: isHorizontal ? 'category' : 'value',
200
+    data: isHorizontal ? ['1', '2', '3'] : null,
201
+    show: false
202
+  },
203
+  series: [{
204
+    type: 'bar',
205
+    data: data,
206
+    itemStyle: { color: color },
207
+    barWidth: isHorizontal ? 8 : 12
208
+  }]
209
+})
210
+
211
+// 折线图配置(总表)
212
+const lineChartOption = (data, color, title) => ({
213
+  grid: {
214
+    left: '10%',
215
+    top: '15%',
216
+    right: '5%',
217
+    bottom: '15%',
218
+    containLabel: true
219
+  },
220
+  xAxis: {
221
+    type: 'category',
222
+    data: xAxisData,
223
+    axisLine: { lineStyle: { color: '#999' } },
224
+    axisLabel: { fontSize: 10, color: '#666' }
225
+  },
226
+  yAxis: {
227
+    type: 'value',
228
+    axisLine: { lineStyle: { color: '#999' } },
229
+    axisLabel: { fontSize: 10, color: '#666' },
230
+    splitLine: { lineStyle: { color: '#eee' } }
231
+  },
232
+  series: [{
233
+    name: title,
234
+    type: 'line',
235
+    smooth: true,
236
+    symbol: 'circle',
237
+    symbolSize: 6,
238
+    data: data,
239
+    itemStyle: { color: color },
240
+    lineStyle: { color: color }
241
+  }]
242
+})
243
+
244
+// 多折线图配置(大队对比)
245
+const multiLineChartOption = (dataList, colors, legends) => ({
246
+  grid: {
247
+    left: '10%',
248
+    top: '15%',
249
+    right: '5%',
250
+    bottom: '15%',
251
+    containLabel: true
252
+  },
253
+  xAxis: {
254
+    type: 'category',
255
+    data: xAxisData,
256
+    axisLine: { lineStyle: { color: '#999' } },
257
+    axisLabel: { fontSize: 10, color: '#666' }
258
+  },
259
+  yAxis: {
260
+    type: 'value',
261
+    axisLine: { lineStyle: { color: '#999' } },
262
+    axisLabel: { fontSize: 10, color: '#666' },
263
+    splitLine: { lineStyle: { color: '#eee' } }
264
+  },
265
+  legend: {
266
+    top: 0,
267
+    right: 10,
268
+    textStyle: { fontSize: 10 }
269
+  },
270
+  series: dataList.map((data, index) => ({
271
+    name: legends[index],
272
+    type: 'line',
273
+    smooth: true,
274
+    symbol: 'circle',
275
+    symbolSize: 6,
276
+    data: data,
277
+    itemStyle: { color: colors[index] },
278
+    lineStyle: { color: colors[index] }
279
+  }))
280
+})
281
+
282
+// 环形饼图配置
283
+const ringPieOption = (data, colors) => ({
284
+  color: colors,
285
+  series: [{
286
+    type: 'pie',
287
+    radius: ['45%', '70%'],
288
+    center: ['50%', '55%'],
289
+    data: data,
290
+    label: {
291
+      show: true,
292
+      formatter: '{b}\n{c}%',
293
+      fontSize: 10
294
+    },
295
+    labelLine: {
296
+      show: true,
297
+      length: 15,
298
+      length2: 10
299
+    }
300
+  }]
301
+})
302
+
303
+// 柱状图配置
304
+const barChartOption = (data, color, isHorizontal = false) => ({
305
+  grid: {
306
+    left: '15%',
307
+    top: '15%',
308
+    right: '5%',
309
+    bottom: '15%',
310
+    containLabel: true
311
+  },
312
+  xAxis: {
313
+    type: isHorizontal ? 'value' : 'category',
314
+    data: isHorizontal ? null : data.map(d => d.name),
315
+    axisLine: { lineStyle: { color: '#999' } },
316
+    axisLabel: { fontSize: 10, color: '#666' }
317
+  },
318
+  yAxis: {
319
+    type: isHorizontal ? 'category' : 'value',
320
+    data: isHorizontal ? data.map(d => d.name) : null,
321
+    axisLine: { lineStyle: { color: '#999' } },
322
+    axisLabel: { fontSize: 10, color: '#666' },
323
+    splitLine: { lineStyle: { color: '#eee' } }
324
+  },
325
+  series: [{
326
+    type: 'bar',
327
+    data: isHorizontal ? data.map(d => d.value) : data,
328
+    itemStyle: { color: color },
329
+    barWidth: isHorizontal ? 15 : 20
330
+  }]
331
+})
332
+
333
+// 双Y轴柱状图配置(用于行李数和万分率)
334
+const dualAxisBarChartOption = (data1, data2, color1, color2) => ({
335
+  grid: {
336
+    left: '10%',
337
+    top: '15%',
338
+    right: '10%',
339
+    bottom: '15%',
340
+    containLabel: true
341
+  },
342
+  xAxis: {
343
+    type: 'category',
344
+    data: xAxisData,
345
+    axisLine: { lineStyle: { color: '#999' } },
346
+    axisLabel: { fontSize: 10, color: '#666' }
347
+  },
348
+  yAxis: [
349
+    {
350
+      type: 'value',
351
+      name: '行李数',
352
+      axisLine: { lineStyle: { color: color1 } },
353
+      axisLabel: { fontSize: 10, color: color1 }
354
+    },
355
+    {
356
+      type: 'value',
357
+      name: '万分率',
358
+      axisLine: { lineStyle: { color: color2 } },
359
+      axisLabel: { fontSize: 10, color: color2 }
360
+    }
361
+  ],
362
+  series: [
363
+    {
364
+      name: '行李数',
365
+      type: 'bar',
366
+      data: data1,
367
+      itemStyle: { color: color1 },
368
+      barWidth: 15
369
+    },
370
+    {
371
+      name: '万分率',
372
+      type: 'line',
373
+      yAxisIndex: 1,
374
+      smooth: true,
375
+      data: data2,
376
+      itemStyle: { color: color2 },
377
+      lineStyle: { color: color2 }
378
+    }
379
+  ]
380
+})
381
+
382
+onMounted(() => {
383
+  // 统计卡片图表
384
+  setOption0a(smallBarOption([120, 80, 150], '#3b82f6'))
385
+  setOption0b(smallBarOption([2.2, 1.8, 2.5], '#3b82f6', true))
386
+  
387
+  // 第一行:每日查堵数量
388
+  setOption1(lineChartOption(generateData(30, 50, 150), '#3b82f6', '查堵数量'))
389
+  setOption2(multiLineChartOption(
390
+    [generateData(30, 20, 80), generateData(30, 15, 70), generateData(30, 10, 60)],
391
+    ['#3b82f6', '#22c55e', '#f97316'],
392
+    ['安检一大队', '安检二大队', '安检三大队']
393
+  ))
394
+  
395
+  // 第二行:每日查堵万分率
396
+  setOption3(lineChartOption(generateData(30, 0.5, 3.5), '#ec4899', '万分率'))
397
+  setOption4(multiLineChartOption(
398
+    [generateData(30, 0.3, 2.5), generateData(30, 0.2, 2.0), generateData(30, 0.1, 1.8)],
399
+    ['#3b82f6', '#22c55e', '#f97316'],
400
+    ['安检一大队', '安检二大队', '安检三大队']
401
+  ))
402
+  
403
+  // 第三行:每日过检图像数
404
+  setOption5(lineChartOption(generateData(30, 1000, 5000), '#8b5cf6', '过检图像数'))
405
+  setOption6(multiLineChartOption(
406
+    [generateData(30, 400, 2000), generateData(30, 300, 1800), generateData(30, 200, 1600)],
407
+    ['#3b82f6', '#22c55e', '#f97316'],
408
+    ['安检一大队', '安检二大队', '安检三大队']
409
+  ))
410
+  
411
+  // 第四行:查堵物品分布和区域查堵数量分布
412
+  setOption7(ringPieOption([
413
+    { name: '刀具', value: 25 },
414
+    { name: '液体', value: 20 },
415
+    { name: '电子产品', value: 18 },
416
+    { name: '其他', value: 15 },
417
+    { name: '打火机', value: 12 },
418
+    { name: '化妆品', value: 10 }
419
+  ], ['#3b82f6', '#22c55e', '#f97316', '#ec4899', '#8b5cf6', '#14b8a6']))
420
+  
421
+  setOption8(barChartOption([
422
+    { name: 'T1国内', value: 120 },
423
+    { name: 'T1国际', value: 80 },
424
+    { name: 'T2国内', value: 150 },
425
+    { name: 'T2国际', value: 60 },
426
+    { name: '中转区', value: 40 }
427
+  ], '#3b82f6', true))
428
+  
429
+  // 第五行:查堵类型分布
430
+  setOption9(barChartOption(generateData(6, 20, 100), '#3b82f6'))
431
+  
432
+  // 第六行:查堵时间段过检行李数及万分率
433
+  setOption10(dualAxisBarChartOption(
434
+    generateData(24, 100, 500), // 行李数
435
+    generateData(24, 0.5, 3.0), // 万分率
436
+    '#3b82f6', '#ec4899'
437
+  ))
438
+  
439
+  // 第七行:每日过检行李数及万分率
440
+  setOption11(dualAxisBarChartOption(
441
+    generateData(30, 1000, 5000), // 行李数
442
+    generateData(30, 0.5, 3.5),  // 万分率
443
+    '#3b82f6', '#ec4899'
444
+  ))
445
+  
446
+  // 第八行:查堵-AI复查图像总数
447
+  setOption12(lineChartOption(generateData(30, 50, 200), '#14b8a6', 'AI复查图像数'))
448
+  
449
+  // 第九行:AI漏判和误判图像总数
450
+  setOption13(lineChartOption(generateData(30, 5, 30), '#ef4444', 'AI漏判图像数'))
451
+  setOption14(lineChartOption(generateData(30, 3, 25), '#f97316', 'AI误判图像数'))
452
+})
453
+</script>
454
+
455
+<style lang="less" scoped>
456
+.module-one-content {
457
+  height: 100%;
458
+  display: flex;
459
+  flex-direction: column;
460
+  gap: 10px;
461
+  overflow-y: auto;
462
+  padding: 10px;
463
+}
464
+
465
+.stats-row {
466
+  display: flex;
467
+  gap: 10px;
468
+  flex-shrink: 0;
469
+}
470
+
471
+.stat-card {
472
+  flex: 1;
473
+  background: #fff;
474
+  border-radius: 6px;
475
+  padding: 15px;
476
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
477
+  min-height: 120px;
478
+}
479
+
480
+.stat-card-inner {
481
+  display: flex;
482
+  height: 100%;
483
+  align-items: center;
484
+}
485
+
486
+.stat-left {
487
+  flex: 1;
488
+  display: flex;
489
+  flex-direction: column;
490
+  align-items: flex-start;
491
+  padding-right: 15px;
492
+}
493
+
494
+.stat-label {
495
+  font-size: 12px;
496
+  color: #666;
497
+  margin-bottom: 8px;
498
+}
499
+
500
+.stat-value {
501
+  font-size: 24px;
502
+  font-weight: bold;
503
+  color: #3b82f6;
504
+}
505
+
506
+.stat-right {
507
+  flex: 1;
508
+  display: flex;
509
+  flex-direction: column;
510
+  align-items: flex-end;
511
+  height: 100%;
512
+}
513
+
514
+.stat-title {
515
+  font-size: 12px;
516
+  color: #333;
517
+  margin-bottom: 5px;
518
+  font-weight: 500;
519
+}
520
+
521
+.echarts-small {
522
+  width: 100%;
523
+  height: 60px;
524
+  min-height: 60px;
525
+}
526
+
527
+.chart-row {
528
+  display: flex;
529
+  gap: 10px;
530
+  flex-shrink: 0;
531
+}
532
+
533
+.chart-row.full-width {
534
+  flex-direction: column;
535
+}
536
+
537
+.chart-item {
538
+  flex: 1;
539
+  background: #fff;
540
+  border-radius: 6px;
541
+  padding: 10px;
542
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
543
+  display: flex;
544
+  flex-direction: column;
545
+  min-height: 200px;
546
+}
547
+
548
+.chart-row.full-width .chart-item {
549
+  flex: none;
550
+  height: 200px;
551
+}
552
+
553
+.chart-title {
554
+  font-size: 12px;
555
+  color: #333;
556
+  margin-bottom: 8px;
557
+  text-align: center;
558
+  font-weight: 500;
559
+}
560
+
561
+.echarts {
562
+  flex: 1;
563
+  width: 100%;
564
+  min-height: 0;
565
+}
566
+</style>

+ 97 - 0
src/views/blockingData/blockingDataScreen/components/ModuleThree.vue

@@ -0,0 +1,97 @@
1
+<template>
2
+  <module-container title="培训质控分析">
3
+    <div class="module-three-content">
4
+      <div class="chart-card">
5
+        <div class="chart-title">查堵-人员自测漏检次数(总累积)</div>
6
+        <div ref="chart1" class="echarts"></div>
7
+      </div>
8
+      <div class="chart-card">
9
+        <div class="chart-title">查堵培训-人员月考成绩(总累积)</div>
10
+        <div ref="chart2" class="echarts"></div>
11
+      </div>
12
+    </div>
13
+  </module-container>
14
+</template>
15
+
16
+<script setup>
17
+import { ref, onMounted } from 'vue'
18
+import * as echarts from 'echarts'
19
+import ModuleContainer from './ModuleContainer.vue'
20
+import { useEcharts } from '@/hooks/chart.js'
21
+
22
+const chart1 = ref(null)
23
+const chart2 = ref(null)
24
+
25
+const { setOption: setOption1 } = useEcharts(chart1)
26
+const { setOption: setOption2 } = useEcharts(chart2)
27
+
28
+const ringPieOption = (data, colors, centerOffset = ['50%', '55%']) => ({
29
+  color: colors,
30
+  series: [{
31
+    type: 'pie',
32
+    radius: ['45%', '70%'],
33
+    center: centerOffset,
34
+    data: data,
35
+    label: {
36
+      show: true,
37
+      formatter: '{b}\n{c}%',
38
+      fontSize: 11
39
+    },
40
+    labelLine: {
41
+      show: true,
42
+      length: 15,
43
+      length2: 10
44
+    }
45
+  }]
46
+})
47
+
48
+onMounted(() => {
49
+  setOption1(ringPieOption([
50
+    { name: '4次', value: 26 },
51
+    { name: '3次', value: 9 },
52
+    { name: '2次', value: 8 },
53
+    { name: '空白', value: 57 }
54
+  ], ['#3b82f6', '#22c55e', '#f97316', '#cbd5e1']))
55
+  
56
+  setOption2(ringPieOption([
57
+    { name: '本月未开展', value: 12 },
58
+    { name: '不合格', value: 8 },
59
+    { name: '合格', value: 13 },
60
+    { name: '优秀', value: 6 },
61
+    { name: '空白', value: 61 }
62
+  ], ['#64748b', '#ef4444', '#22c55e', '#3b82f6', '#cbd5e1']))
63
+})
64
+</script>
65
+
66
+<style lang="less" scoped>
67
+.module-three-content {
68
+  height: 100%;
69
+  display: flex;
70
+  gap: 10px;
71
+  padding: 10px;
72
+}
73
+
74
+.chart-card {
75
+  flex: 1;
76
+  background: #fff;
77
+  border-radius: 6px;
78
+  padding: 10px;
79
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
80
+  display: flex;
81
+  flex-direction: column;
82
+}
83
+
84
+.chart-title {
85
+  font-size: 12px;
86
+  color: #333;
87
+  margin-bottom: 8px;
88
+  text-align: center;
89
+  font-weight: 500;
90
+}
91
+
92
+.echarts {
93
+  flex: 1;
94
+  width: 100%;
95
+  min-height: 0;
96
+}
97
+</style>

+ 337 - 0
src/views/blockingData/blockingDataScreen/components/ModuleTwo.vue

@@ -0,0 +1,337 @@
1
+<template>
2
+  <module-container title="查堵人员分析">
3
+    <div class="module-two-content">
4
+      <div class="top-row">
5
+        <div class="table-card">
6
+          <div class="card-title">查堵-主管分管榜</div>
7
+          <div class="rank-table">
8
+            <div v-for="(item, index) in rankData1" :key="index" class="rank-row">
9
+              <span class="rank-number" :class="'rank-' + (index + 1)">NO.{{ index + 1 }}</span>
10
+              <span class="rank-name">{{ item.name }}</span>
11
+              <span class="rank-value">{{ item.value }}</span>
12
+            </div>
13
+          </div>
14
+        </div>
15
+        <div class="table-card">
16
+          <div class="card-title">查堵-班组排行</div>
17
+          <div class="rank-table">
18
+            <div v-for="(item, index) in rankData2" :key="index" class="rank-row">
19
+              <span class="rank-number" :class="'rank-' + (index + 1)">NO.{{ index + 1 }}</span>
20
+              <span class="rank-name">{{ item.name }}</span>
21
+              <span class="rank-value">{{ item.value }}</span>
22
+            </div>
23
+          </div>
24
+        </div>
25
+        <div class="table-card">
26
+          <div class="card-title">查堵-人员榜</div>
27
+          <div class="rank-table">
28
+            <div v-for="(item, index) in rankData3" :key="index" class="rank-row">
29
+              <span class="rank-number" :class="'rank-' + (index + 1)">NO.{{ index + 1 }}</span>
30
+              <span class="rank-name">{{ item.name }}</span>
31
+              <span class="rank-value">{{ item.value }}</span>
32
+            </div>
33
+          </div>
34
+        </div>
35
+      </div>
36
+      
37
+      <div class="middle-row">
38
+        <div class="chart-card">
39
+          <div class="card-title">查堵-物品位置分布</div>
40
+          <div ref="chart1" class="echarts"></div>
41
+        </div>
42
+        <div class="chart-card">
43
+          <div class="card-title">查堵-原因分类</div>
44
+          <div ref="chart2" class="echarts"></div>
45
+        </div>
46
+      </div>
47
+      
48
+      <div class="bottom-row">
49
+        <div class="chart-card">
50
+          <div class="card-title">查堵-开机人员年限分布</div>
51
+          <div ref="chart3" class="echarts"></div>
52
+        </div>
53
+        <div class="chart-card">
54
+          <div class="card-title">查堵-开机人员性别比例</div>
55
+          <div ref="chart4" class="echarts"></div>
56
+        </div>
57
+        <div class="chart-card">
58
+          <div class="card-title">查堵-图像难易程度比例</div>
59
+          <div ref="chart5" class="echarts"></div>
60
+        </div>
61
+      </div>
62
+      
63
+      <div class="extra-row">
64
+        <div class="chart-card">
65
+          <div class="card-title">大队开机人员查堵分布</div>
66
+          <div ref="chart6" class="echarts"></div>
67
+        </div>
68
+        <div class="chart-card">
69
+          <div class="card-title">大队开机人员查堵分布</div>
70
+          <div ref="chart7" class="echarts"></div>
71
+        </div>
72
+      </div>
73
+    </div>
74
+  </module-container>
75
+</template>
76
+
77
+<script setup>
78
+import { ref, onMounted, reactive } from 'vue'
79
+import * as echarts from 'echarts'
80
+import ModuleContainer from './ModuleContainer.vue'
81
+import { useEcharts } from '@/hooks/chart.js'
82
+
83
+const chart1 = ref(null)
84
+const chart2 = ref(null)
85
+const chart3 = ref(null)
86
+const chart4 = ref(null)
87
+const chart5 = ref(null)
88
+const chart6 = ref(null)
89
+const chart7 = ref(null)
90
+
91
+const { setOption: setOption1 } = useEcharts(chart1)
92
+const { setOption: setOption2 } = useEcharts(chart2)
93
+const { setOption: setOption3 } = useEcharts(chart3)
94
+const { setOption: setOption4 } = useEcharts(chart4)
95
+const { setOption: setOption5 } = useEcharts(chart5)
96
+const { setOption: setOption6 } = useEcharts(chart6)
97
+const { setOption: setOption7 } = useEcharts(chart7)
98
+
99
+const rankData1 = reactive([
100
+  { name: '李主管', value: 110 },
101
+  { name: '李海峰', value: 102 },
102
+  { name: '李华波', value: 94 },
103
+  { name: '方星星', value: 80 },
104
+  { name: '郭仁昌', value: 69 },
105
+  { name: '陈晓明', value: 67 },
106
+  { name: '万国', value: 62 },
107
+  { name: '杨高星', value: 60 },
108
+  { name: '符昌国', value: 59 },
109
+  { name: '李星国', value: 57 },
110
+  { name: '周良志', value: 56 },
111
+  { name: '杨旭', value: 53 }
112
+])
113
+
114
+const rankData2 = reactive([
115
+  { name: '李智辉', value: 44 },
116
+  { name: '林明玉', value: 42 },
117
+  { name: '吴帮文', value: 40 },
118
+  { name: '杨玉艳', value: 37 },
119
+  { name: '符玉玲', value: 37 },
120
+  { name: '黄登', value: 36 },
121
+  { name: '梁品俊', value: 35 },
122
+  { name: '陈勤丽', value: 35 },
123
+  { name: '潘政生', value: 34 },
124
+  { name: '周民伦', value: 34 },
125
+  { name: '王明星', value: 33 },
126
+  { name: '林文海', value: 33 }
127
+])
128
+
129
+const rankData3 = reactive([
130
+  { name: '梁翠兰', value: 21 },
131
+  { name: '陈秀强', value: 20 },
132
+  { name: '李国', value: 19 },
133
+  { name: '梁其佐', value: 18 },
134
+  { name: '王名标', value: 18 },
135
+  { name: '李书帆', value: 17 },
136
+  { name: '李亚峰', value: 17 },
137
+  { name: '杨燕钰', value: 16 },
138
+  { name: '刘燕萍', value: 16 },
139
+  { name: '庄伟', value: 16 },
140
+  { name: '黄富伦', value: 15 },
141
+  { name: '唐甸宇', value: 15 }
142
+])
143
+
144
+const pieOption = (data, colors, isRing = false) => ({
145
+  color: colors,
146
+  series: [{
147
+    type: 'pie',
148
+    radius: isRing ? ['50%', '70%'] : '60%',
149
+    center: ['50%', '55%'],
150
+    data: data,
151
+    label: {
152
+      show: true,
153
+      formatter: '{b}\n{c}%',
154
+      fontSize: 10
155
+    }
156
+  }]
157
+})
158
+
159
+const barOption = (data, colors) => ({
160
+  grid: { left: '15%', top: '10%', right: '5%', bottom: '10%', containLabel: true },
161
+  xAxis: { type: 'value', axisLabel: { fontSize: 9 } },
162
+  yAxis: { type: 'category', data: data.map(d => d.name), axisLabel: { fontSize: 9 } },
163
+  series: [{
164
+    type: 'bar',
165
+    data: data.map(d => d.value),
166
+    itemStyle: {
167
+      color: (params) => colors[params.dataIndex % colors.length]
168
+    },
169
+    barWidth: 15
170
+  }]
171
+})
172
+
173
+const stackBarOption = (data, colors) => ({
174
+  grid: { left: '15%', top: '10%', right: '5%', bottom: '15%', containLabel: true },
175
+  xAxis: { type: 'category', data: ['安检二大队', '安检三大队', '安检一大队'], axisLabel: { fontSize: 9 } },
176
+  yAxis: { type: 'value', axisLabel: { fontSize: 9 } },
177
+  legend: { top: 0, right: 10, textStyle: { fontSize: 9 } },
178
+  series: data.map((d, i) => ({
179
+    name: d.name,
180
+    type: 'bar',
181
+    stack: 'total',
182
+    data: d.data,
183
+    itemStyle: { color: colors[i] },
184
+    barWidth: 25
185
+  }))
186
+})
187
+
188
+onMounted(() => {
189
+  setOption1(pieOption([
190
+    { name: '包上', value: 10 },
191
+    { name: '包中', value: 15 },
192
+    { name: '包下', value: 12 },
193
+    { name: '左上', value: 18 },
194
+    { name: '左中', value: 20 },
195
+    { name: '左下', value: 15 },
196
+    { name: '右下', value: 10 }
197
+  ], ['#3b82f6', '#22c55e', '#f97316', '#ec4899', '#8b5cf6', '#14b8a6', '#64748b']))
198
+  setOption2(pieOption([
199
+    { name: '未注意到部位以及未仔细判图', value: 25 },
200
+    { name: '图像特征难以发现', value: 20 },
201
+    { name: '判图难度较大', value: 18 },
202
+    { name: '图像中判清物品', value: 15 },
203
+    { name: '判图不清,未能有效识别物品', value: 12 },
204
+    { name: '其他', value: 10 }
205
+  ], ['#ef4444', '#f97316', '#eab308', '#22c55e', '#3b82f6', '#64748b'], true))
206
+  setOption3(barOption([
207
+    { name: '6年及以上', value: 77 },
208
+    { name: '4-6年', value: 25 },
209
+    { name: '3年', value: 18 },
210
+    { name: '2年', value: 30 },
211
+    { name: '1年', value: 69 },
212
+    { name: '1年以下', value: 12 }
213
+  ], ['#3b82f6', '#60a5fa', '#93c5fd', '#bfdbfe', '#dbeafe', '#eff6ff']))
214
+  setOption4(pieOption([
215
+    { name: '男', value: 63 },
216
+    { name: '女', value: 37 }
217
+  ], ['#3b82f6', '#ec4899']))
218
+  setOption5(pieOption([
219
+    { name: '简单', value: 52 },
220
+    { name: '难', value: 30 },
221
+    { name: '未评判', value: 12 },
222
+    { name: '空白', value: 6 }
223
+  ], ['#93c5fd', '#3b82f6', '#64748b', '#e2e8f0']))
224
+  setOption6(stackBarOption([
225
+    { name: '6年及以上人数', data: [30, 25, 22] },
226
+    { name: '4-6年人数', data: [15, 20, 18] },
227
+    { name: '3年人数', data: [10, 12, 15] },
228
+    { name: '2年人数', data: [12, 15, 13] },
229
+    { name: '1年人数', data: [25, 30, 28] },
230
+    { name: '不足1年人数', data: [5, 8, 10] }
231
+  ], ['#3b82f6', '#22c55e', '#f97316', '#ec4899', '#8b5cf6', '#14b8a6']))
232
+  setOption7(stackBarOption([
233
+    { name: '安检一大队', data: [135, 120] },
234
+    { name: '安检二大队', data: [90, 110] },
235
+    { name: '安检三大队', data: [70, 130] }
236
+  ], ['#3b82f6', '#22c55e', '#f97316']))
237
+})
238
+</script>
239
+
240
+<style lang="less" scoped>
241
+.module-two-content {
242
+  height: 100%;
243
+  display: flex;
244
+  flex-direction: column;
245
+  gap: 8px;
246
+  overflow-y: auto;
247
+}
248
+
249
+.top-row,
250
+.middle-row,
251
+.bottom-row,
252
+.extra-row {
253
+  display: flex;
254
+  gap: 8px;
255
+  flex-shrink: 0;
256
+}
257
+
258
+.table-card {
259
+  flex: 1;
260
+  background: #fff;
261
+  border-radius: 6px;
262
+  padding: 8px;
263
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
264
+  display: flex;
265
+  flex-direction: column;
266
+  min-height: 200px;
267
+}
268
+
269
+.chart-card {
270
+  flex: 1;
271
+  background: #fff;
272
+  border-radius: 6px;
273
+  padding: 8px;
274
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
275
+  display: flex;
276
+  flex-direction: column;
277
+  min-height: 120px;
278
+}
279
+
280
+.card-title {
281
+  font-size: 11px;
282
+  color: #333;
283
+  margin-bottom: 5px;
284
+  text-align: center;
285
+  font-weight: 500;
286
+}
287
+
288
+.rank-table {
289
+  flex: 1;
290
+  overflow-y: auto;
291
+}
292
+
293
+.rank-row {
294
+  display: flex;
295
+  align-items: center;
296
+  padding: 4px 6px;
297
+  border-bottom: 1px solid #f0f0f0;
298
+  font-size: 11px;
299
+}
300
+
301
+.rank-row:last-child {
302
+  border-bottom: none;
303
+}
304
+
305
+.rank-number {
306
+  width: 45px;
307
+  font-weight: bold;
308
+  margin-right: 8px;
309
+}
310
+
311
+.rank-1 {
312
+  color: #eab308;
313
+}
314
+
315
+.rank-2 {
316
+  color: #94a3b8;
317
+}
318
+
319
+.rank-3 {
320
+  color: #f97316;
321
+}
322
+
323
+.rank-name {
324
+  flex: 1;
325
+  color: #333;
326
+}
327
+
328
+.rank-value {
329
+  color: #666;
330
+}
331
+
332
+.echarts {
333
+  flex: 1;
334
+  width: 100%;
335
+  min-height: 100px;
336
+}
337
+</style>

+ 109 - 0
src/views/blockingData/blockingDataScreen/index.vue

@@ -0,0 +1,109 @@
1
+<template>
2
+  <div class="blocking-data-screen">
3
+    <div class="screen-header">
4
+      <div class="header-title">查堵数据统计大屏(实时)</div>
5
+      <div class="header-nav">
6
+        <div class="nav-item active">运行数据分析</div>
7
+        <div class="nav-item">查堵人员分析</div>
8
+        <div class="nav-item">培训质控分析</div>
9
+        <div class="nav-item">放行速率分析</div>
10
+      </div>
11
+    </div>
12
+    
13
+    <div class="screen-content">
14
+      <div class="grid-layout">
15
+        <div class="grid-item">
16
+          <module-one />
17
+        </div>
18
+        <div class="grid-item">
19
+          <module-two />
20
+        </div>
21
+        <div class="grid-item">
22
+          <module-three />
23
+        </div>
24
+        <div class="grid-item">
25
+          <module-four />
26
+        </div>
27
+      </div>
28
+    </div>
29
+  </div>
30
+</template>
31
+
32
+<script setup>
33
+import ModuleOne from './components/ModuleOne.vue'
34
+import ModuleTwo from './components/ModuleTwo.vue'
35
+import ModuleThree from './components/ModuleThree.vue'
36
+import ModuleFour from './components/ModuleFour.vue'
37
+</script>
38
+
39
+<style lang="less" scoped>
40
+.blocking-data-screen {
41
+  width: 100%;
42
+  height: 100vh;
43
+  background: linear-gradient(180deg, #0D507A 0%, #054066 50%, #033055 100%);
44
+  display: flex;
45
+  flex-direction: column;
46
+  overflow: hidden;
47
+}
48
+
49
+.screen-header {
50
+  height: 60px;
51
+  display: flex;
52
+  align-items: center;
53
+  justify-content: space-between;
54
+  padding: 0 30px;
55
+  background: linear-gradient(90deg, rgba(5, 64, 102, 0.9) 0%, rgba(11, 88, 131, 0.9) 50%, rgba(5, 64, 102, 0.9) 100%);
56
+  border-bottom: 2px solid rgba(112, 207, 231, 0.5);
57
+}
58
+
59
+.header-title {
60
+  font-size: 24px;
61
+  font-weight: bold;
62
+  color: #fbffff;
63
+  text-shadow: 0 0 10px rgba(112, 207, 231, 0.5);
64
+}
65
+
66
+.header-nav {
67
+  display: flex;
68
+  gap: 15px;
69
+}
70
+
71
+.nav-item {
72
+  padding: 8px 20px;
73
+  background: rgba(112, 207, 231, 0.1);
74
+  border: 1px solid rgba(112, 207, 231, 0.3);
75
+  border-radius: 4px;
76
+  color: #B4E9FF;
77
+  font-size: 14px;
78
+  cursor: pointer;
79
+  transition: all 0.3s;
80
+}
81
+
82
+.nav-item:hover {
83
+  background: rgba(112, 207, 231, 0.2);
84
+}
85
+
86
+.nav-item.active {
87
+  background: linear-gradient(90deg, #3398BE, #0B5883);
88
+  border-color: #70CFE7;
89
+  color: #fff;
90
+}
91
+
92
+.screen-content {
93
+  flex: 1;
94
+  padding: 20px;
95
+  overflow: auto;
96
+}
97
+
98
+.grid-layout {
99
+  display: grid;
100
+  grid-template-columns: repeat(2, 1fr);
101
+  grid-template-rows: repeat(2, 1fr);
102
+  gap: 20px;
103
+  height: 100%;
104
+}
105
+
106
+.grid-item {
107
+  min-height: 200px;
108
+}
109
+</style>

+ 0 - 0
src/views/blockingData/blockingDataScreen/查堵大屏


+ 534 - 0
src/views/blockingData/dailyLuggageCheckInList/index.vue

@@ -0,0 +1,534 @@
1
+<template>
2
+  <div class="app-container">
3
+    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
4
+      <el-form-item label="日期" prop="date">
5
+        <el-date-picker clearable v-model="queryParams.date" type="date" placeholder="选择日期" value-format="YYYY-MM-DD"
6
+          style="width: 200px" />
7
+      </el-form-item>
8
+      <el-form-item label="班次" prop="shift">
9
+        <el-select v-model="queryParams.shift" placeholder="请选择班次" clearable style="width: 200px">
10
+          <el-option label="早班" value="早班" />
11
+          <el-option label="中班" value="中班" />
12
+          <el-option label="晚班" value="晚班" />
13
+        </el-select>
14
+      </el-form-item>
15
+      <el-form-item>
16
+        <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
17
+        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
18
+      </el-form-item>
19
+    </el-form>
20
+
21
+    <el-row :gutter="10" class="mb8">
22
+      <el-col :span="1.5">
23
+        <el-button type="primary" plain icon="Plus" @click="handleAdd"
24
+          v-hasPermi="['dailyLuggageCheckIn:dailyLuggageCheckIn:add']">新增</el-button>
25
+      </el-col>
26
+      <el-col :span="1.5">
27
+        <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
28
+          v-hasPermi="['dailyLuggageCheckIn:dailyLuggageCheckIn:remove']">删除</el-button>
29
+      </el-col>
30
+      <el-col :span="1.5">
31
+        <el-button type="warning" plain icon="Download" @click="handleExport"
32
+          v-hasPermi="['dailyLuggageCheckIn:dailyLuggageCheckIn:export']">导出</el-button>
33
+      </el-col>
34
+      <el-col :span="1.5">
35
+        <el-button type="info" plain icon="Upload" @click="handleImport"
36
+          v-hasPermi="['dailyLuggageCheckIn:dailyLuggageCheckIn:import']">导入</el-button>
37
+      </el-col>
38
+      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
39
+    </el-row>
40
+
41
+    <el-table v-loading="loading" :data="dailyLuggageCheckInList" @selection-change="handleSelectionChange">
42
+      <el-table-column type="selection" width="55" align="center" />
43
+      <el-table-column label="日期" align="center" prop="date" width="120" />
44
+      <el-table-column label="班次" align="center" prop="shift" width="100" />
45
+      <el-table-column label="当班大队" align="center" prop="dutyBrigade" width="120" />
46
+      <el-table-column label="t1T2旅检过检行李数" align="center" prop="t1T2LuggageCheckCount" width="150" />
47
+      <el-table-column label="过检行李合计" align="center" prop="totalLuggageCheckCount" width="120" />
48
+      <el-table-column label="T1旅检查堵件数" align="center" prop="t1LuggageCheckBlockCount" width="120" />
49
+      <el-table-column label="T1旅检万分率" align="center" prop="t1LuggageCheckBlockRate" width="120" />
50
+      <el-table-column label="T2旅检查堵件数" align="center" prop="t2LuggageCheckBlockCount" width="120" />
51
+      <el-table-column label="T2旅检万分率" align="center" prop="t2LuggageCheckBlockRate" width="120" />
52
+      <el-table-column label="T1行检过检行李数" align="center" prop="t1BaggageCheckCount" width="120" />
53
+      <el-table-column label="T2行检过检行李数" align="center" prop="t2BaggageCheckCount" width="120" />
54
+      <el-table-column label="T1行检查堵件数" align="center" prop="t1BaggageCheckBlockCount" width="120" />
55
+      <el-table-column label="T1行检万分率" align="center" prop="t1BaggageCheckBlockRate" width="120" />
56
+      <el-table-column label="T2行检查堵件数" align="center" prop="t2BaggageCheckBlockCount" width="120" />
57
+      <el-table-column label="T2行检万分率" align="center" prop="t2BaggageCheckBlockRate" width="120" />
58
+      <el-table-column label="查堵合计件数" align="center" prop="totalCheckBlockCount" width="120" />
59
+      <el-table-column label="当日查堵万分率" align="center" prop="dailyCheckBlockRate" width="120" />
60
+      <el-table-column label="T1复查图像总数" align="center" prop="t1ReviewImageTotal" width="120" />
61
+      <el-table-column label="T2-AI标记总数" align="center" prop="t2AiMarkTotal" width="120" />
62
+      <el-table-column label="T1-AI误判总数" align="center" prop="t1AiMisjudgeTotal" width="120" />
63
+      <el-table-column label="T1AI漏判总数" align="center" prop="t1AiMissTotal" width="120" />
64
+      <el-table-column label="T2复查图像总数" align="center" prop="t2ReviewImageTotal" width="120" />
65
+      <el-table-column label="T2-AI标记总数" align="center" prop="t2AiMarkTotal2" width="120" />
66
+      <el-table-column label="T2AI误判总数" align="center" prop="t2AiMisjudgeTotal" width="120" />
67
+      <el-table-column label="T2AI漏判总数" align="center" prop="t2AiMissTotal" width="120" />
68
+      <el-table-column label="其他(VP通道)数量" align="center" prop="vpChannelCount" width="140" />
69
+      <el-table-column label="AI复查图像总数" align="center" prop="aiReviewImageTotal" width="120" />
70
+      <el-table-column label="AI标记图像总数" align="center" prop="aiMarkImageTotal" width="120" />
71
+      <el-table-column label="AI漏判图像总数" align="center" prop="aiMissImageTotal" width="120" />
72
+      <el-table-column label="AI误判图像总数" align="center" prop="aiMisjudgeImageTotal" width="140" />
73
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="140">
74
+        <template #default="scope">
75
+          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
76
+            v-hasPermi="['dailyLuggageCheckIn:dailyLuggageCheckIn:edit']">修改</el-button>
77
+          <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
78
+            v-hasPermi="['dailyLuggageCheckIn:dailyLuggageCheckIn:remove']">删除</el-button>
79
+        </template>
80
+      </el-table-column>
81
+    </el-table>
82
+
83
+    <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
84
+      v-model:limit="queryParams.pageSize" @pagination="getList" />
85
+
86
+    <!-- 添加或修改每日行李查缉对话框 -->
87
+    <el-dialog :title="title" v-model="open" width="800px" append-to-body>
88
+      <el-form ref="dailyLuggageCheckInRef" :model="form" :rules="rules" label-width="140px">
89
+        <el-row :gutter="20">
90
+          <el-col :span="12">
91
+            <el-form-item label="日期" prop="date">
92
+              <el-date-picker v-model="form.date" type="date" placeholder="选择日期" value-format="YYYY-MM-DD"
93
+                style="width: 100%" />
94
+            </el-form-item>
95
+          </el-col>
96
+          <el-col :span="12">
97
+            <el-form-item label="班次" prop="shift">
98
+              <el-select v-model="form.shift" placeholder="请选择班次" style="width: 100%">
99
+                <el-option label="早班" value="早班" />
100
+                <el-option label="中班" value="中班" />
101
+                <el-option label="晚班" value="晚班" />
102
+              </el-select>
103
+            </el-form-item>
104
+          </el-col>
105
+        </el-row>
106
+        <el-form-item label="当班大队" prop="dutyBrigade">
107
+          <el-select v-model="form.dutyBrigade" placeholder="请选择当班大队" style="width: 100%">
108
+            <el-option v-for="item in brigadeOptions" :key="item.value" :label="item.label" :value="item.value" />
109
+          </el-select>
110
+        </el-form-item>
111
+        <el-row :gutter="20">
112
+          <el-col :span="12">
113
+            <el-form-item label="t1T2旅检过检行李数" prop="t1T2LuggageCheckCount">
114
+              <el-input-number v-model="form.t1T2LuggageCheckCount" :min="0" style="width: 100%" />
115
+            </el-form-item>
116
+          </el-col>
117
+          <el-col :span="12">
118
+            <el-form-item label="过检行李合计" prop="totalLuggageCheckCount">
119
+              <el-input-number v-model="form.totalLuggageCheckCount" :min="0" style="width: 100%" />
120
+            </el-form-item>
121
+          </el-col>
122
+        </el-row>
123
+        <el-row :gutter="20">
124
+          <el-col :span="12">
125
+            <el-form-item label="T1旅检查堵件数" prop="t1LuggageCheckBlockCount">
126
+              <el-input-number v-model="form.t1LuggageCheckBlockCount" :min="0" style="width: 100%" />
127
+            </el-form-item>
128
+          </el-col>
129
+          <el-col :span="12">
130
+            <el-form-item label="T1旅检万分率" prop="t1LuggageCheckBlockRate">
131
+              <el-input-number v-model="form.t1LuggageCheckBlockRate" :min="0" :precision="2" style="width: 100%" />
132
+            </el-form-item>
133
+          </el-col>
134
+        </el-row>
135
+        <el-row :gutter="20">
136
+          <el-col :span="12">
137
+            <el-form-item label="T2旅检查堵件数" prop="t2LuggageCheckBlockCount">
138
+              <el-input-number v-model="form.t2LuggageCheckBlockCount" :min="0" style="width: 100%" />
139
+            </el-form-item>
140
+          </el-col>
141
+          <el-col :span="12">
142
+            <el-form-item label="T2旅检万分率" prop="t2LuggageCheckBlockRate">
143
+              <el-input-number v-model="form.t2LuggageCheckBlockRate" :min="0" :precision="2" style="width: 100%" />
144
+            </el-form-item>
145
+          </el-col>
146
+        </el-row>
147
+        <el-row :gutter="20">
148
+          <el-col :span="12">
149
+            <el-form-item label="T1行检过检行李数" prop="t1BaggageCheckCount">
150
+              <el-input-number v-model="form.t1BaggageCheckCount" :min="0" style="width: 100%" />
151
+            </el-form-item>
152
+          </el-col>
153
+          <el-col :span="12">
154
+            <el-form-item label="T2行检过检行李数" prop="t2BaggageCheckCount">
155
+              <el-input-number v-model="form.t2BaggageCheckCount" :min="0" style="width: 100%" />
156
+            </el-form-item>
157
+          </el-col>
158
+        </el-row>
159
+        <el-row :gutter="20">
160
+          <el-col :span="12">
161
+            <el-form-item label="T1行检查堵件数" prop="t1BaggageCheckBlockCount">
162
+              <el-input-number v-model="form.t1BaggageCheckBlockCount" :min="0" style="width: 100%" />
163
+            </el-form-item>
164
+          </el-col>
165
+          <el-col :span="12">
166
+            <el-form-item label="T1行检万分率" prop="t1BaggageCheckBlockRate">
167
+              <el-input-number v-model="form.t1BaggageCheckBlockRate" :min="0" :precision="2" style="width: 100%" />
168
+            </el-form-item>
169
+          </el-col>
170
+        </el-row>
171
+        <el-row :gutter="20">
172
+          <el-col :span="12">
173
+            <el-form-item label="T2行检查堵件数" prop="t2BaggageCheckBlockCount">
174
+              <el-input-number v-model="form.t2BaggageCheckBlockCount" :min="0" style="width: 100%" />
175
+            </el-form-item>
176
+          </el-col>
177
+          <el-col :span="12">
178
+            <el-form-item label="T2行检万分率" prop="t2BaggageCheckBlockRate">
179
+              <el-input-number v-model="form.t2BaggageCheckBlockRate" :min="0" :precision="2" style="width: 100%" />
180
+            </el-form-item>
181
+          </el-col>
182
+        </el-row>
183
+        <el-row :gutter="20">
184
+          <el-col :span="12">
185
+            <el-form-item label="查堵合计件数" prop="totalCheckBlockCount">
186
+              <el-input-number v-model="form.totalCheckBlockCount" :min="0" style="width: 100%" />
187
+            </el-form-item>
188
+          </el-col>
189
+          <el-col :span="12">
190
+            <el-form-item label="当日查堵万分率" prop="dailyCheckBlockRate">
191
+              <el-input-number v-model="form.dailyCheckBlockRate" :min="0" :precision="2" style="width: 100%" />
192
+            </el-form-item>
193
+          </el-col>
194
+        </el-row>
195
+        <el-row :gutter="20">
196
+          <el-col :span="12">
197
+            <el-form-item label="T1复查图像总数" prop="t1ReviewImageTotal">
198
+              <el-input-number v-model="form.t1ReviewImageTotal" :min="0" style="width: 100%" />
199
+            </el-form-item>
200
+          </el-col>
201
+          <el-col :span="12">
202
+            <el-form-item label="T2-AI标记总数" prop="t2AiMarkTotal">
203
+              <el-input-number v-model="form.t2AiMarkTotal" :min="0" style="width: 100%" />
204
+            </el-form-item>
205
+          </el-col>
206
+        </el-row>
207
+        <el-row :gutter="20">
208
+          <el-col :span="12">
209
+            <el-form-item label="T1-AI误判总数" prop="t1AiMisjudgeTotal">
210
+              <el-input-number v-model="form.t1AiMisjudgeTotal" :min="0" style="width: 100%" />
211
+            </el-form-item>
212
+          </el-col>
213
+          <el-col :span="12">
214
+            <el-form-item label="T1AI漏判总数" prop="t1AiMissTotal">
215
+              <el-input-number v-model="form.t1AiMissTotal" :min="0" style="width: 100%" />
216
+            </el-form-item>
217
+          </el-col>
218
+        </el-row>
219
+        <el-row :gutter="20">
220
+          <el-col :span="12">
221
+            <el-form-item label="T2复查图像总数" prop="t2ReviewImageTotal">
222
+              <el-input-number v-model="form.t2ReviewImageTotal" :min="0" style="width: 100%" />
223
+            </el-form-item>
224
+          </el-col>
225
+          <el-col :span="12">
226
+            <el-form-item label="T2-AI标记总数" prop="t2AiMarkTotal2">
227
+              <el-input-number v-model="form.t2AiMarkTotal2" :min="0" style="width: 100%" />
228
+            </el-form-item>
229
+          </el-col>
230
+        </el-row>
231
+        <el-row :gutter="20">
232
+          <el-col :span="12">
233
+            <el-form-item label="T2AI误判总数" prop="t2AiMisjudgeTotal">
234
+              <el-input-number v-model="form.t2AiMisjudgeTotal" :min="0" style="width: 100%" />
235
+            </el-form-item>
236
+          </el-col>
237
+          <el-col :span="12">
238
+            <el-form-item label="T2AI漏判总数" prop="t2AiMissTotal">
239
+              <el-input-number v-model="form.t2AiMissTotal" :min="0" style="width: 100%" />
240
+            </el-form-item>
241
+          </el-col>
242
+        </el-row>
243
+        <el-form-item label="其他(VP通道)数量" prop="vpChannelCount">
244
+          <el-input-number v-model="form.vpChannelCount" :min="0" style="width: 100%" />
245
+        </el-form-item>
246
+        <el-row :gutter="20">
247
+          <el-col :span="12">
248
+            <el-form-item label="AI复查图像总数" prop="aiReviewImageTotal">
249
+              <el-input-number v-model="form.aiReviewImageTotal" :min="0" style="width: 100%" />
250
+            </el-form-item>
251
+          </el-col>
252
+          <el-col :span="12">
253
+            <el-form-item label="AI标记图像总数" prop="aiMarkImageTotal">
254
+              <el-input-number v-model="form.aiMarkImageTotal" :min="0" style="width: 100%" />
255
+            </el-form-item>
256
+          </el-col>
257
+        </el-row>
258
+        <el-row :gutter="20">
259
+          <el-col :span="12">
260
+            <el-form-item label="AI漏判图像总数" prop="aiMissImageTotal">
261
+              <el-input-number v-model="form.aiMissImageTotal" :min="0" style="width: 100%" />
262
+            </el-form-item>
263
+          </el-col>
264
+          <el-col :span="12">
265
+            <el-form-item label="AI误判图像总数" prop="aiMisjudgeImageTotal">
266
+              <el-input-number v-model="form.aiMisjudgeImageTotal" :min="0" style="width: 100%" />
267
+            </el-form-item>
268
+          </el-col>
269
+        </el-row>
270
+      </el-form>
271
+      <template #footer>
272
+        <div class="dialog-footer">
273
+          <el-button type="primary" @click="submitForm">确 定</el-button>
274
+          <el-button @click="cancel">取 消</el-button>
275
+        </div>
276
+      </template>
277
+    </el-dialog>
278
+
279
+    <!-- 导入对话框 -->
280
+    <el-dialog title="导入" v-model="importOpen" width="500px" append-to-body>
281
+      <el-upload ref="uploadRef" :auto-upload="false" :on-change="handleFileChange" :show-file-list="false"
282
+        accept=".xlsx,.xls">
283
+        <el-button type="primary">选取文件</el-button>
284
+        <div class="el-upload__tip">只能上传xls/xlsx文件</div>
285
+      </el-upload>
286
+      <template #footer>
287
+        <div class="dialog-footer">
288
+          <el-button type="primary" @click="submitImport">确 定</el-button>
289
+          <el-button @click="importOpen = false">取 消</el-button>
290
+        </div>
291
+      </template>
292
+    </el-dialog>
293
+  </div>
294
+</template>
295
+
296
+<script setup>
297
+import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
298
+import { listDailyLuggageCheckIn, getDailyLuggageCheckIn, delDailyLuggageCheckIn, addDailyLuggageCheckIn, updateDailyLuggageCheckIn, exportDailyLuggageCheckIn } from '@/api/blockingData/dailyLuggageCheckIn'
299
+import { listDept } from '@/api/system/dept'
300
+
301
+const { proxy } = getCurrentInstance()
302
+
303
+// 遮罩层
304
+const loading = ref(true)
305
+// 选中数组
306
+const ids = ref([])
307
+// 非单个禁用
308
+const single = ref(true)
309
+// 非多个禁用
310
+const multiple = ref(true)
311
+// 显示搜索条件
312
+const showSearch = ref(true)
313
+// 总条数
314
+const total = ref(0)
315
+// 每日行李查缉表格数据
316
+const dailyLuggageCheckInList = ref([])
317
+// 弹出层标题
318
+const title = ref('')
319
+// 是否显示弹出层
320
+const open = ref(false)
321
+// 是否显示导入弹出层
322
+const importOpen = ref(false)
323
+// 导入文件
324
+const importFile = ref(null)
325
+
326
+// 查询参数
327
+const queryParams = reactive({
328
+  pageNum: 1,
329
+  pageSize: 10,
330
+  date: null,
331
+  shift: null
332
+})
333
+
334
+// 表单参数
335
+const form = reactive({})
336
+
337
+// 表单校验
338
+const rules = {
339
+  date: [{ required: true, message: '日期不能为空', trigger: 'change' }],
340
+  shift: [{ required: true, message: '班次不能为空', trigger: 'change' }],
341
+  dutyBrigade: [{ required: true, message: '当班大队不能为空', trigger: 'change' }]
342
+}
343
+
344
+// 大队选项
345
+const brigadeOptions = ref([])
346
+
347
+/** 查询每日行李查缉列表 */
348
+function getList() {
349
+  loading.value = true
350
+  listDailyLuggageCheckIn(queryParams).then(response => {
351
+    dailyLuggageCheckInList.value = response.rows
352
+    total.value = response.total
353
+    loading.value = false
354
+  })
355
+}
356
+
357
+/** 获取部门列表 */
358
+function getDeptList() {
359
+  listDept({}).then(response => {
360
+    const deptList = response.data || []
361
+    brigadeOptions.value = deptList.filter(item => item.deptType === 'BRIGADE').map(item => ({
362
+      value: item.deptId,
363
+      label: item.deptName
364
+    }))
365
+  })
366
+}
367
+
368
+// 取消按钮
369
+function cancel() {
370
+  open.value = false
371
+  reset()
372
+}
373
+
374
+// 表单重置
375
+function reset() {
376
+  form.value = {
377
+    id: null,
378
+    date: null,
379
+    shift: null,
380
+    dutyBrigade: null,
381
+    t1T2LuggageCheckCount: 0,
382
+    totalLuggageCheckCount: 0,
383
+    t1LuggageCheckBlockCount: 0,
384
+    t1LuggageCheckBlockRate: 0,
385
+    t2LuggageCheckBlockCount: 0,
386
+    t2LuggageCheckBlockRate: 0,
387
+    t1BaggageCheckCount: 0,
388
+    t2BaggageCheckCount: 0,
389
+    t1BaggageCheckBlockCount: 0,
390
+    t1BaggageCheckBlockRate: 0,
391
+    t2BaggageCheckBlockCount: 0,
392
+    t2BaggageCheckBlockRate: 0,
393
+    totalCheckBlockCount: 0,
394
+    dailyCheckBlockRate: 0,
395
+    t1ReviewImageTotal: 0,
396
+    t2AiMarkTotal: 0,
397
+    t1AiMisjudgeTotal: 0,
398
+    t1AiMissTotal: 0,
399
+    t2ReviewImageTotal: 0,
400
+    t2AiMarkTotal2: 0,
401
+    t2AiMisjudgeTotal: 0,
402
+    t2AiMissTotal: 0,
403
+    vpChannelCount: 0,
404
+    aiReviewImageTotal: 0,
405
+    aiMarkImageTotal: 0,
406
+    aiMissImageTotal: 0,
407
+    aiMisjudgeImageTotal: 0
408
+  }
409
+  proxy.resetForm('dailyLuggageCheckInRef')
410
+}
411
+
412
+/** 搜索按钮操作 */
413
+function handleQuery() {
414
+  queryParams.pageNum = 1
415
+  getList()
416
+}
417
+
418
+/** 重置按钮操作 */
419
+function resetQuery() {
420
+  proxy.resetForm('queryRef')
421
+  handleQuery()
422
+}
423
+
424
+// 多选框选中数据
425
+function handleSelectionChange(selection) {
426
+  ids.value = selection.map(item => item.id)
427
+  single.value = selection.length !== 1
428
+  multiple.value = !selection.length
429
+}
430
+
431
+/** 新增按钮操作 */
432
+function handleAdd() {
433
+  reset()
434
+  open.value = true
435
+  title.value = '添加每日行李查缉'
436
+}
437
+
438
+/** 修改按钮操作 */
439
+function handleUpdate(row) {
440
+  reset()
441
+  const id = row.id || ids.value[0]
442
+  getDailyLuggageCheckIn(id).then(response => {
443
+    Object.assign(form, response.data)
444
+    open.value = true
445
+    title.value = '修改每日行李查缉'
446
+  })
447
+}
448
+
449
+/** 提交按钮 */
450
+function submitForm() {
451
+  proxy.$refs.dailyLuggageCheckInRef.validate(valid => {
452
+    if (valid) {
453
+      if (form.id != null) {
454
+        updateDailyLuggageCheckIn(form).then(response => {
455
+          proxy.$modal.msgSuccess('修改成功')
456
+          open.value = false
457
+          getList()
458
+        })
459
+      } else {
460
+        addDailyLuggageCheckIn(form).then(response => {
461
+          proxy.$modal.msgSuccess('新增成功')
462
+          open.value = false
463
+          getList()
464
+        })
465
+      }
466
+    }
467
+  })
468
+}
469
+
470
+/** 删除按钮操作 */
471
+function handleDelete(row) {
472
+  const id = row.id || ids.value
473
+  proxy.$modal.confirm('是否确认删除每日行李查缉编号为"' + id + '"的数据项?').then(function () {
474
+    return delDailyLuggageCheckIn(id)
475
+  }).then(() => {
476
+    getList()
477
+    proxy.$modal.msgSuccess('删除成功')
478
+  }).catch(() => { })
479
+}
480
+
481
+/** 导出按钮操作 */
482
+function handleExport() {
483
+  proxy.download('dailyLuggageCheckIn/export', {
484
+    ...queryParams
485
+  }, `每日行李查缉_${new Date().getTime()}.xlsx`)
486
+}
487
+
488
+/** 导入按钮操作 */
489
+function handleImport() {
490
+  importOpen.value = true
491
+  importFile.value = null
492
+}
493
+
494
+/** 文件选择 */
495
+function handleFileChange(file) {
496
+  importFile.value = file.raw
497
+}
498
+
499
+/** 提交导入 */
500
+function submitImport() {
501
+  if (!importFile.value) {
502
+    proxy.$modal.msgWarning('请选择要导入的文件')
503
+    return
504
+  }
505
+  const formData = new FormData()
506
+  formData.append('file', importFile.value)
507
+  proxy.upload('dailyLuggageCheckIn/importData', formData).then(response => {
508
+    proxy.$modal.msgSuccess(response.msg)
509
+    importOpen.value = false
510
+    getList()
511
+  })
512
+}
513
+
514
+onMounted(() => {
515
+  getList()
516
+  getDeptList()
517
+})
518
+</script>
519
+
520
+<style lang="less" scoped>
521
+.app-container {
522
+  padding: 20px;
523
+}
524
+
525
+:deep(.el-table) {
526
+  .el-table__header-wrapper {
527
+    th {
528
+      background-color: #303133;
529
+      color: #ffffff;
530
+      font-weight: 600;
531
+    }
532
+  }
533
+}
534
+</style>

+ 0 - 0
src/views/blockingData/dailyLuggageCheckInList/每日行李过检查堵表


+ 378 - 0
src/views/blockingData/dailyLuggageInspectionScheduleByTime/index.vue

@@ -0,0 +1,378 @@
1
+<template>
2
+  <div class="app-container">
3
+    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
4
+      <el-form-item label="大队" prop="brigadeId">
5
+        <el-select v-model="queryParams.brigadeId" placeholder="请选择大队" clearable style="width: 200px">
6
+          <el-option v-for="item in brigadeOptions" :key="item.value" :label="item.label" :value="item.value" />
7
+        </el-select>
8
+      </el-form-item>
9
+      <el-form-item label="日期" prop="date">
10
+        <el-date-picker clearable v-model="queryParams.date" type="date" placeholder="选择日期" value-format="YYYY-MM-DD"
11
+          style="width: 200px" />
12
+      </el-form-item>
13
+      <el-form-item>
14
+        <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
15
+        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
16
+      </el-form-item>
17
+    </el-form>
18
+
19
+    <el-row :gutter="10" class="mb8">
20
+      <el-col :span="1.5">
21
+        <el-button type="primary" plain icon="Plus" @click="handleAdd"
22
+          v-hasPermi="['dailyLuggageInspectionScheduleByTime:dailyLuggageInspectionScheduleByTime:add']">新增</el-button>
23
+      </el-col>
24
+      <el-col :span="1.5">
25
+        <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
26
+          v-hasPermi="['dailyLuggageInspectionScheduleByTime:dailyLuggageInspectionScheduleByTime:remove']">删除</el-button>
27
+      </el-col>
28
+      <el-col :span="1.5">
29
+        <el-button type="warning" plain icon="Download" @click="handleExport"
30
+          v-hasPermi="['dailyLuggageInspectionScheduleByTime:dailyLuggageInspectionScheduleByTime:export']">导出</el-button>
31
+      </el-col>
32
+      <el-col :span="1.5">
33
+        <el-button type="info" plain icon="Upload" @click="handleImport"
34
+          v-hasPermi="['dailyLuggageInspectionScheduleByTime:dailyLuggageInspectionScheduleByTime:import']">导入</el-button>
35
+      </el-col>
36
+      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
37
+    </el-row>
38
+
39
+    <el-table v-loading="loading" :data="dailyLuggageInspectionScheduleByTimeList" @selection-change="handleSelectionChange">
40
+      <el-table-column type="selection" width="55" align="center" />
41
+      <el-table-column label="日期" align="center" prop="date" width="120" />
42
+      <el-table-column label="当班大队" align="center" prop="dutyBrigade" width="120" />
43
+      <el-table-column label="时间段" align="center" prop="timePeriod" width="120" />
44
+      <el-table-column label="备注" align="center" prop="remark" width="120" />
45
+      <el-table-column label="T1行检箱包数" align="center" prop="t1BaggageCount" width="120" />
46
+      <el-table-column label="查堵数3" align="center" prop="checkBlockCount3" width="100" />
47
+      <el-table-column label="T2行检箱包数(国内+)" align="center" prop="t2BaggageCountDomestic" width="150" />
48
+      <el-table-column label="查堵数2" align="center" prop="checkBlockCount2" width="100" />
49
+      <el-table-column label="T1旅检箱包数" align="center" prop="t1LuggageCount" width="120" />
50
+      <el-table-column label="查堵数5" align="center" prop="checkBlockCount5" width="100" />
51
+      <el-table-column label="T2旅检箱包数(国内+)" align="center" prop="t2LuggageCountDomestic" width="150" />
52
+      <el-table-column label="查堵数" align="center" prop="checkBlockCount" width="100" />
53
+      <el-table-column label="过检行李数" align="center" prop="checkLuggageCount" width="120" />
54
+      <el-table-column label="时段查堵件数" align="center" prop="timePeriodCheckBlockCount" width="120" />
55
+      <el-table-column label="查堵万分率" align="center" prop="checkBlockRate" width="120" />
56
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="140">
57
+        <template #default="scope">
58
+          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
59
+            v-hasPermi="['dailyLuggageInspectionScheduleByTime:dailyLuggageInspectionScheduleByTime:edit']">修改</el-button>
60
+          <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
61
+            v-hasPermi="['dailyLuggageInspectionScheduleByTime:dailyLuggageInspectionScheduleByTime:remove']">删除</el-button>
62
+        </template>
63
+      </el-table-column>
64
+    </el-table>
65
+
66
+    <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
67
+      v-model:limit="queryParams.pageSize" @pagination="getList" />
68
+
69
+    <el-dialog :title="title" v-model="open" width="800px" append-to-body>
70
+      <el-form ref="dailyLuggageInspectionScheduleByTimeRef" :model="form" :rules="rules" label-width="180px">
71
+        <el-row :gutter="20">
72
+          <el-col :span="12">
73
+            <el-form-item label="日期" prop="date">
74
+              <el-date-picker v-model="form.date" type="date" placeholder="选择日期" value-format="YYYY-MM-DD"
75
+                style="width: 100%" />
76
+            </el-form-item>
77
+          </el-col>
78
+          <el-col :span="12">
79
+            <el-form-item label="当班大队" prop="dutyBrigade">
80
+              <el-select v-model="form.dutyBrigade" placeholder="请选择当班大队" style="width: 100%">
81
+                <el-option v-for="item in brigadeOptions" :key="item.value" :label="item.label" :value="item.value" />
82
+              </el-select>
83
+            </el-form-item>
84
+          </el-col>
85
+        </el-row>
86
+        <el-row :gutter="20">
87
+          <el-col :span="12">
88
+            <el-form-item label="时间段" prop="timePeriod">
89
+              <el-input v-model="form.timePeriod" placeholder="请输入时间段" style="width: 100%" />
90
+            </el-form-item>
91
+          </el-col>
92
+          <el-col :span="12">
93
+            <el-form-item label="备注" prop="remark">
94
+              <el-input v-model="form.remark" placeholder="请输入备注" style="width: 100%" />
95
+            </el-form-item>
96
+          </el-col>
97
+        </el-row>
98
+        <el-row :gutter="20">
99
+          <el-col :span="12">
100
+            <el-form-item label="T1行检箱包数" prop="t1BaggageCount">
101
+              <el-input-number v-model="form.t1BaggageCount" :min="0" style="width: 100%" />
102
+            </el-form-item>
103
+          </el-col>
104
+          <el-col :span="12">
105
+            <el-form-item label="查堵数3" prop="checkBlockCount3">
106
+              <el-input-number v-model="form.checkBlockCount3" :min="0" style="width: 100%" />
107
+            </el-form-item>
108
+          </el-col>
109
+        </el-row>
110
+        <el-row :gutter="20">
111
+          <el-col :span="12">
112
+            <el-form-item label="T2行检箱包数(国内+)" prop="t2BaggageCountDomestic">
113
+              <el-input-number v-model="form.t2BaggageCountDomestic" :min="0" style="width: 100%" />
114
+            </el-form-item>
115
+          </el-col>
116
+          <el-col :span="12">
117
+            <el-form-item label="查堵数2" prop="checkBlockCount2">
118
+              <el-input-number v-model="form.checkBlockCount2" :min="0" style="width: 100%" />
119
+            </el-form-item>
120
+          </el-col>
121
+        </el-row>
122
+        <el-row :gutter="20">
123
+          <el-col :span="12">
124
+            <el-form-item label="T1旅检箱包数" prop="t1LuggageCount">
125
+              <el-input-number v-model="form.t1LuggageCount" :min="0" style="width: 100%" />
126
+            </el-form-item>
127
+          </el-col>
128
+          <el-col :span="12">
129
+            <el-form-item label="查堵数5" prop="checkBlockCount5">
130
+              <el-input-number v-model="form.checkBlockCount5" :min="0" style="width: 100%" />
131
+            </el-form-item>
132
+          </el-col>
133
+        </el-row>
134
+        <el-row :gutter="20">
135
+          <el-col :span="12">
136
+            <el-form-item label="T2旅检箱包数(国内+)" prop="t2LuggageCountDomestic">
137
+              <el-input-number v-model="form.t2LuggageCountDomestic" :min="0" style="width: 100%" />
138
+            </el-form-item>
139
+          </el-col>
140
+          <el-col :span="12">
141
+            <el-form-item label="查堵数" prop="checkBlockCount">
142
+              <el-input-number v-model="form.checkBlockCount" :min="0" style="width: 100%" />
143
+            </el-form-item>
144
+          </el-col>
145
+        </el-row>
146
+        <el-row :gutter="20">
147
+          <el-col :span="12">
148
+            <el-form-item label="过检行李数" prop="checkLuggageCount">
149
+              <el-input-number v-model="form.checkLuggageCount" :min="0" style="width: 100%" />
150
+            </el-form-item>
151
+          </el-col>
152
+          <el-col :span="12">
153
+            <el-form-item label="时段查堵件数" prop="timePeriodCheckBlockCount">
154
+              <el-input-number v-model="form.timePeriodCheckBlockCount" :min="0" style="width: 100%" />
155
+            </el-form-item>
156
+          </el-col>
157
+        </el-row>
158
+        <el-form-item label="查堵万分率" prop="checkBlockRate">
159
+          <el-input-number v-model="form.checkBlockRate" :min="0" :precision="2" style="width: 100%" />
160
+        </el-form-item>
161
+      </el-form>
162
+      <template #footer>
163
+        <div class="dialog-footer">
164
+          <el-button type="primary" @click="submitForm">确 定</el-button>
165
+          <el-button @click="cancel">取 消</el-button>
166
+        </div>
167
+      </template>
168
+    </el-dialog>
169
+
170
+    <el-dialog title="导入" v-model="importOpen" width="500px" append-to-body>
171
+      <el-upload ref="uploadRef" :auto-upload="false" :on-change="handleFileChange" :show-file-list="false"
172
+        accept=".xlsx,.xls">
173
+        <el-button type="primary">选取文件</el-button>
174
+        <div class="el-upload__tip">只能上传xls/xlsx文件</div>
175
+      </el-upload>
176
+      <template #footer>
177
+        <div class="dialog-footer">
178
+          <el-button type="primary" @click="submitImport">确 定</el-button>
179
+          <el-button @click="importOpen = false">取 消</el-button>
180
+        </div>
181
+      </template>
182
+    </el-dialog>
183
+  </div>
184
+</template>
185
+
186
+<script setup>
187
+import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
188
+import { listDailyLuggageInspectionScheduleByTime, getDailyLuggageInspectionScheduleByTime, delDailyLuggageInspectionScheduleByTime, addDailyLuggageInspectionScheduleByTime, updateDailyLuggageInspectionScheduleByTime, exportDailyLuggageInspectionScheduleByTime } from '@/api/blockingData/dailyLuggageInspectionScheduleByTime'
189
+import { listDept } from '@/api/system/dept'
190
+
191
+const { proxy } = getCurrentInstance()
192
+
193
+const loading = ref(true)
194
+const ids = ref([])
195
+const single = ref(true)
196
+const multiple = ref(true)
197
+const showSearch = ref(true)
198
+const total = ref(0)
199
+const dailyLuggageInspectionScheduleByTimeList = ref([])
200
+const title = ref('')
201
+const open = ref(false)
202
+const importOpen = ref(false)
203
+const importFile = ref(null)
204
+
205
+const queryParams = reactive({
206
+  pageNum: 1,
207
+  pageSize: 10,
208
+  brigadeId: null,
209
+  date: null
210
+})
211
+
212
+const form = reactive({})
213
+
214
+const rules = {
215
+  date: [{ required: true, message: '日期不能为空', trigger: 'change' }],
216
+  dutyBrigade: [{ required: true, message: '当班大队不能为空', trigger: 'change' }]
217
+}
218
+
219
+const brigadeOptions = ref([])
220
+
221
+function getList() {
222
+  loading.value = true
223
+  listDailyLuggageInspectionScheduleByTime(queryParams).then(response => {
224
+    dailyLuggageInspectionScheduleByTimeList.value = response.rows
225
+    total.value = response.total
226
+    loading.value = false
227
+  })
228
+}
229
+
230
+function getDeptList() {
231
+  listDept({}).then(response => {
232
+    const deptList = response.data || []
233
+    brigadeOptions.value = deptList.filter(item => item.deptType === 'BRIGADE').map(item => ({
234
+      value: item.deptId,
235
+      label: item.deptName
236
+    }))
237
+  })
238
+}
239
+
240
+function cancel() {
241
+  open.value = false
242
+  reset()
243
+}
244
+
245
+function reset() {
246
+  form.value = {
247
+    id: null,
248
+    date: null,
249
+    dutyBrigade: null,
250
+    timePeriod: null,
251
+    remark: null,
252
+    t1BaggageCount: 0,
253
+    checkBlockCount3: 0,
254
+    t2BaggageCountDomestic: 0,
255
+    checkBlockCount2: 0,
256
+    t1LuggageCount: 0,
257
+    checkBlockCount5: 0,
258
+    t2LuggageCountDomestic: 0,
259
+    checkBlockCount: 0,
260
+    checkLuggageCount: 0,
261
+    timePeriodCheckBlockCount: 0,
262
+    checkBlockRate: 0
263
+  }
264
+  proxy.resetForm('dailyLuggageInspectionScheduleByTimeRef')
265
+}
266
+
267
+function handleQuery() {
268
+  queryParams.pageNum = 1
269
+  getList()
270
+}
271
+
272
+function resetQuery() {
273
+  proxy.resetForm('queryRef')
274
+  handleQuery()
275
+}
276
+
277
+function handleSelectionChange(selection) {
278
+  ids.value = selection.map(item => item.id)
279
+  single.value = selection.length !== 1
280
+  multiple.value = !selection.length
281
+}
282
+
283
+function handleAdd() {
284
+  reset()
285
+  open.value = true
286
+  title.value = '添加每日各时段查堵行李'
287
+}
288
+
289
+function handleUpdate(row) {
290
+  reset()
291
+  const id = row.id || ids.value[0]
292
+  getDailyLuggageInspectionScheduleByTime(id).then(response => {
293
+    Object.assign(form, response.data)
294
+    open.value = true
295
+    title.value = '修改每日各时段查堵行李'
296
+  })
297
+}
298
+
299
+function submitForm() {
300
+  proxy.$refs.dailyLuggageInspectionScheduleByTimeRef.validate(valid => {
301
+    if (valid) {
302
+      if (form.id != null) {
303
+        updateDailyLuggageInspectionScheduleByTime(form).then(response => {
304
+          proxy.$modal.msgSuccess('修改成功')
305
+          open.value = false
306
+          getList()
307
+        })
308
+      } else {
309
+        addDailyLuggageInspectionScheduleByTime(form).then(response => {
310
+          proxy.$modal.msgSuccess('新增成功')
311
+          open.value = false
312
+          getList()
313
+        })
314
+      }
315
+    }
316
+  })
317
+}
318
+
319
+function handleDelete(row) {
320
+  const id = row.id || ids.value
321
+  proxy.$modal.confirm('是否确认删除每日各时段查堵行李编号为"' + id + '"的数据项?').then(function () {
322
+    return delDailyLuggageInspectionScheduleByTime(id)
323
+  }).then(() => {
324
+    getList()
325
+    proxy.$modal.msgSuccess('删除成功')
326
+  }).catch(() => { })
327
+}
328
+
329
+function handleExport() {
330
+  proxy.download('dailyLuggageInspectionScheduleByTime/export', {
331
+    ...queryParams
332
+  }, `每日各时段查堵行李_${new Date().getTime()}.xlsx`)
333
+}
334
+
335
+function handleImport() {
336
+  importOpen.value = true
337
+  importFile.value = null
338
+}
339
+
340
+function handleFileChange(file) {
341
+  importFile.value = file.raw
342
+}
343
+
344
+function submitImport() {
345
+  if (!importFile.value) {
346
+    proxy.$modal.msgWarning('请选择要导入的文件')
347
+    return
348
+  }
349
+  const formData = new FormData()
350
+  formData.append('file', importFile.value)
351
+  proxy.upload('dailyLuggageInspectionScheduleByTime/importData', formData).then(response => {
352
+    proxy.$modal.msgSuccess(response.msg)
353
+    importOpen.value = false
354
+    getList()
355
+  })
356
+}
357
+
358
+onMounted(() => {
359
+  getList()
360
+  getDeptList()
361
+})
362
+</script>
363
+
364
+<style lang="less" scoped>
365
+.app-container {
366
+  padding: 20px;
367
+}
368
+
369
+:deep(.el-table) {
370
+  .el-table__header-wrapper {
371
+    th {
372
+      background-color: #303133;
373
+      color: #ffffff;
374
+      font-weight: 600;
375
+    }
376
+  }
377
+}
378
+</style>

+ 0 - 0
src/views/blockingData/dailyLuggageInspectionScheduleByTime/每日各时段查堵行李表


+ 512 - 0
src/views/blockingData/missedInspectionList/index.vue

@@ -0,0 +1,512 @@
1
+<template>
2
+  <div class="app-container">
3
+    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
4
+      <el-form-item label="大队" prop="brigadeId">
5
+        <el-select v-model="queryParams.brigadeId" placeholder="请选择大队" clearable style="width: 200px">
6
+          <el-option v-for="item in brigadeOptions" :key="item.value" :label="item.label" :value="item.value" />
7
+        </el-select>
8
+      </el-form-item>
9
+      <el-form-item label="回查日期" prop="checkDate">
10
+        <el-date-picker clearable v-model="dateRange" value-format="YYYY-MM-DD" type="daterange" range-separator="-"
11
+          start-placeholder="开始日期" end-placeholder="结束日期" style="width: 300px">
12
+        </el-date-picker>
13
+      </el-form-item>
14
+      <el-form-item label="被回查人" prop="checkedPerson">
15
+        <el-select v-model="queryParams.checkedPerson" placeholder="请选择被回查人" clearable style="width: 200px">
16
+          <el-option v-for="item in personOptions" :key="item.value" :label="item.label" :value="item.value" />
17
+        </el-select>
18
+      </el-form-item>
19
+      <el-form-item label="分管班组长" prop="teamLeaderId">
20
+        <el-select v-model="queryParams.teamLeaderId" placeholder="请选择分管班组长" clearable style="width: 200px">
21
+          <el-option v-for="item in teamLeaderOptions" :key="item.value" :label="item.label" :value="item.value" />
22
+        </el-select>
23
+      </el-form-item>
24
+      <el-form-item label="分管主管" prop="supervisorId">
25
+        <el-select v-model="queryParams.supervisorId" placeholder="请选择分管主管" clearable style="width: 200px">
26
+          <el-option v-for="item in supervisorOptions" :key="item.value" :label="item.label" :value="item.value" />
27
+        </el-select>
28
+      </el-form-item>
29
+      <el-form-item label="判别类型" prop="judgeType">
30
+        <el-select v-model="queryParams.judgeType" placeholder="请选择判别类型" clearable style="width: 200px">
31
+          <el-option v-for="item in judgeTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
32
+        </el-select>
33
+      </el-form-item>
34
+      <el-form-item>
35
+        <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
36
+        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
37
+      </el-form-item>
38
+    </el-form>
39
+
40
+    <el-row :gutter="10" class="mb8">
41
+      <el-col :span="1.5">
42
+        <el-button type="primary" plain icon="Plus" @click="handleAdd"
43
+          v-hasPermi="['missedInspection:missedInspection:add']">新增</el-button>
44
+      </el-col>
45
+      <el-col :span="1.5">
46
+        <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
47
+          v-hasPermi="['missedInspection:missedInspection:remove']">删除</el-button>
48
+      </el-col>
49
+      <el-col :span="1.5">
50
+        <el-button type="warning" plain icon="Download" @click="handleExport"
51
+          v-hasPermi="['missedInspection:missedInspection:export']">导出</el-button>
52
+      </el-col>
53
+      <el-col :span="1.5">
54
+        <el-button type="info" plain icon="Upload" @click="handleImport"
55
+          v-hasPermi="['missedInspection:missedInspection:import']">导入</el-button>
56
+      </el-col>
57
+      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
58
+    </el-row>
59
+
60
+    <el-table v-loading="loading" :data="missedInspectionList" @selection-change="handleSelectionChange">
61
+      <el-table-column type="selection" width="55" align="center" />
62
+      <el-table-column label="大队" align="center" prop="brigadeName" width="120" />
63
+      <el-table-column label="区域" align="center" prop="area" width="120" />
64
+      <el-table-column label="被回查人" align="center" prop="checkedPerson" width="120" />
65
+      <el-table-column label="回查日期" align="center" prop="checkDate" width="120" />
66
+      <el-table-column label="漏检时间" align="center" prop="missedTime" width="120" />
67
+      <el-table-column label="漏检时间段" align="center" prop="missedTimePeriod" width="150" />
68
+      <el-table-column label="上岗位置" align="center" prop="workPosition" width="120" />
69
+      <el-table-column label="分管主管" align="center" prop="supervisorName" width="120" />
70
+      <el-table-column label="代管主管" align="center" prop="actingSupervisorName" width="120" />
71
+      <el-table-column label="分管班组长" align="center" prop="teamLeaderName" width="120" />
72
+      <el-table-column label="物品位置" align="center" prop="itemPosition" width="120" />
73
+      <el-table-column label="简单/难" align="center" prop="difficulty" width="100" />
74
+      <el-table-column label="回查人" align="center" prop="checker" width="120" />
75
+      <el-table-column label="判别类型" align="center" prop="judgeType" width="100" />
76
+      <el-table-column label="是否追回" align="center" prop="isRecovered" width="100">
77
+        <template #default="scope">
78
+          <el-tag :type="scope.row.isRecovered === '是' ? 'success' : 'warning'">
79
+            {{ scope.row.isRecovered }}
80
+          </el-tag>
81
+        </template>
82
+      </el-table-column>
83
+      <el-table-column label="开机年限" align="center" prop="machineYears" width="100" />
84
+      <el-table-column label="证书级别" align="center" prop="certificateLevel" width="100" />
85
+      <el-table-column label="人员性别" align="center" prop="personGender" width="100" />
86
+      <el-table-column label="漏检原因分类" align="center" prop="missedReasonCategory" width="120" />
87
+      <el-table-column label="月考成绩" align="center" prop="monthlyExamScore" width="100" />
88
+      <el-table-column label="本月自测有无漏检" align="center" prop="monthlySelfTestMissed" width="120">
89
+        <template #default="scope">
90
+          <el-tag :type="scope.row.monthlySelfTestMissed === '是' ? 'warning' : 'success'">
91
+            {{ scope.row.monthlySelfTestMissed }}
92
+          </el-tag>
93
+        </template>
94
+      </el-table-column>
95
+      <el-table-column label="漏检物品" align="center" prop="missedItem" width="120" />
96
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="140">
97
+        <template #default="scope">
98
+          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
99
+            v-hasPermi="['missedInspection:missedInspection:edit']">修改</el-button>
100
+          <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
101
+            v-hasPermi="['missedInspection:missedInspection:remove']">删除</el-button>
102
+        </template>
103
+      </el-table-column>
104
+    </el-table>
105
+
106
+    <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
107
+      v-model:limit="queryParams.pageSize" @pagination="getList" />
108
+
109
+    <!-- 添加或修改漏检对话框 -->
110
+    <el-dialog :title="title" v-model="open" width="600px" append-to-body>
111
+      <el-form ref="missedInspectionRef" :model="form" :rules="rules" label-width="120px">
112
+        <el-form-item label="大队" prop="brigadeId">
113
+          <el-select v-model="form.brigadeId" placeholder="请选择大队" style="width: 100%">
114
+            <el-option v-for="item in brigadeOptions" :key="item.value" :label="item.label" :value="item.value" />
115
+          </el-select>
116
+        </el-form-item>
117
+        <el-form-item label="区域" prop="area">
118
+          <el-input v-model="form.area" placeholder="请输入区域" />
119
+        </el-form-item>
120
+        <el-form-item label="被回查人" prop="checkedPerson">
121
+          <el-select v-model="form.checkedPerson" placeholder="请选择被回查人" style="width: 100%">
122
+            <el-option v-for="item in personOptions" :key="item.value" :label="item.label" :value="item.value" />
123
+          </el-select>
124
+        </el-form-item>
125
+        <el-form-item label="回查日期" prop="checkDate">
126
+          <el-date-picker v-model="form.checkDate" type="date" placeholder="选择回查日期" value-format="YYYY-MM-DD"
127
+            style="width: 100%" />
128
+        </el-form-item>
129
+        <el-form-item label="漏检时间" prop="missedTime">
130
+          <el-input v-model="form.missedTime" placeholder="请输入漏检时间" />
131
+        </el-form-item>
132
+        <el-form-item label="漏检时间段" prop="missedTimePeriod">
133
+          <el-input v-model="form.missedTimePeriod" placeholder="请输入漏检时间段" />
134
+        </el-form-item>
135
+        <el-form-item label="上岗位置" prop="workPosition">
136
+          <el-input v-model="form.workPosition" placeholder="请输入上岗位置" />
137
+        </el-form-item>
138
+        <el-form-item label="分管主管" prop="supervisorId">
139
+          <el-select v-model="form.supervisorId" placeholder="请选择分管主管" style="width: 100%">
140
+            <el-option v-for="item in supervisorOptions" :key="item.value" :label="item.label" :value="item.value" />
141
+          </el-select>
142
+        </el-form-item>
143
+        <el-form-item label="代管主管" prop="actingSupervisorId">
144
+          <el-select v-model="form.actingSupervisorId" placeholder="请选择代管主管" style="width: 100%">
145
+            <el-option v-for="item in supervisorOptions" :key="item.value" :label="item.label" :value="item.value" />
146
+          </el-select>
147
+        </el-form-item>
148
+        <el-form-item label="分管班组长" prop="teamLeaderId">
149
+          <el-select v-model="form.teamLeaderId" placeholder="请选择分管班组长" style="width: 100%">
150
+            <el-option v-for="item in teamLeaderOptions" :key="item.value" :label="item.label" :value="item.value" />
151
+          </el-select>
152
+        </el-form-item>
153
+        <el-form-item label="物品位置" prop="itemPosition">
154
+          <el-input v-model="form.itemPosition" placeholder="请输入物品位置" />
155
+        </el-form-item>
156
+        <el-form-item label="简单/难" prop="difficulty">
157
+          <el-select v-model="form.difficulty" placeholder="请选择简单/难" style="width: 100%">
158
+            <el-option label="简单" value="简单" />
159
+            <el-option label="难" value="难" />
160
+          </el-select>
161
+        </el-form-item>
162
+        <el-form-item label="回查人" prop="checker">
163
+          <el-select v-model="form.checker" placeholder="请选择回查人" style="width: 100%">
164
+            <el-option v-for="item in personOptions" :key="item.value" :label="item.label" :value="item.value" />
165
+          </el-select>
166
+        </el-form-item>
167
+        <el-form-item label="判别类型" prop="judgeType">
168
+          <el-select v-model="form.judgeType" placeholder="请选择判别类型" style="width: 100%">
169
+            <el-option v-for="item in judgeTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
170
+          </el-select>
171
+        </el-form-item>
172
+        <el-form-item label="是否追回" prop="isRecovered">
173
+          <el-select v-model="form.isRecovered" placeholder="请选择是否追回" style="width: 100%">
174
+            <el-option label="是" value="是" />
175
+            <el-option label="否" value="否" />
176
+          </el-select>
177
+        </el-form-item>
178
+        <el-form-item label="开机年限" prop="machineYears">
179
+          <el-input v-model="form.machineYears" placeholder="请输入开机年限" />
180
+        </el-form-item>
181
+        <el-form-item label="证书级别" prop="certificateLevel">
182
+          <el-select v-model="form.certificateLevel" placeholder="请选择证书级别" style="width: 100%">
183
+            <el-option label="初级" value="初级" />
184
+            <el-option label="中级" value="中级" />
185
+            <el-option label="高级" value="高级" />
186
+          </el-select>
187
+        </el-form-item>
188
+        <el-form-item label="人员性别" prop="personGender">
189
+          <el-select v-model="form.personGender" placeholder="请选择人员性别" style="width: 100%">
190
+            <el-option label="男" value="男" />
191
+            <el-option label="女" value="女" />
192
+          </el-select>
193
+        </el-form-item>
194
+        <el-form-item label="漏检原因分类" prop="missedReasonCategory">
195
+          <el-input v-model="form.missedReasonCategory" placeholder="请输入漏检原因分类" />
196
+        </el-form-item>
197
+        <el-form-item label="月考成绩" prop="monthlyExamScore">
198
+          <el-input v-model="form.monthlyExamScore" placeholder="请输入月考成绩" />
199
+        </el-form-item>
200
+        <el-form-item label="本月自测有无漏检" prop="monthlySelfTestMissed">
201
+          <el-select v-model="form.monthlySelfTestMissed" placeholder="请选择本月自测有无漏检" style="width: 100%">
202
+            <el-option label="是" value="是" />
203
+            <el-option label="否" value="否" />
204
+          </el-select>
205
+        </el-form-item>
206
+        <el-form-item label="漏检物品" prop="missedItem">
207
+          <el-input v-model="form.missedItem" placeholder="请输入漏检物品" />
208
+        </el-form-item>
209
+      </el-form>
210
+      <template #footer>
211
+        <div class="dialog-footer">
212
+          <el-button type="primary" @click="submitForm">确 定</el-button>
213
+          <el-button @click="cancel">取 消</el-button>
214
+        </div>
215
+      </template>
216
+    </el-dialog>
217
+
218
+    <!-- 导入对话框 -->
219
+    <el-dialog title="导入" v-model="importOpen" width="500px" append-to-body>
220
+      <el-upload ref="uploadRef" :auto-upload="false" :on-change="handleFileChange" :show-file-list="false"
221
+        accept=".xlsx,.xls">
222
+        <el-button type="primary">选取文件</el-button>
223
+        <div class="el-upload__tip">只能上传xls/xlsx文件</div>
224
+      </el-upload>
225
+      <template #footer>
226
+        <div class="dialog-footer">
227
+          <el-button type="primary" @click="submitImport">确 定</el-button>
228
+          <el-button @click="importOpen = false">取 消</el-button>
229
+        </div>
230
+      </template>
231
+    </el-dialog>
232
+  </div>
233
+</template>
234
+
235
+<script setup>
236
+import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
237
+import { listMissedInspection, getMissedInspection, delMissedInspection, addMissedInspection, updateMissedInspection, exportMissedInspection } from '@/api/blockingData/missedInspection'
238
+import { listDept } from '@/api/system/dept'
239
+import { listUser } from '@/api/system/user'
240
+
241
+const { proxy } = getCurrentInstance()
242
+
243
+// 遮罩层
244
+const loading = ref(true)
245
+// 选中数组
246
+const ids = ref([])
247
+// 非单个禁用
248
+const single = ref(true)
249
+// 非多个禁用
250
+const multiple = ref(true)
251
+// 显示搜索条件
252
+const showSearch = ref(true)
253
+// 总条数
254
+const total = ref(0)
255
+// 漏检表格数据
256
+const missedInspectionList = ref([])
257
+// 弹出层标题
258
+const title = ref('')
259
+// 是否显示弹出层
260
+const open = ref(false)
261
+// 是否显示导入弹出层
262
+const importOpen = ref(false)
263
+// 日期范围
264
+const dateRange = ref([])
265
+// 导入文件
266
+const importFile = ref(null)
267
+
268
+// 查询参数
269
+const queryParams = reactive({
270
+  pageNum: 1,
271
+  pageSize: 10,
272
+  brigadeId: null,
273
+  checkDate: null,
274
+  checkedPerson: null,
275
+  teamLeaderId: null,
276
+  supervisorId: null,
277
+  judgeType: null
278
+})
279
+
280
+// 表单参数
281
+const form = reactive({})
282
+
283
+// 表单校验
284
+const rules = {
285
+  brigadeId: [{ required: true, message: '大队不能为空', trigger: 'change' }],
286
+  checkDate: [{ required: true, message: '回查日期不能为空', trigger: 'change' }]
287
+}
288
+
289
+// 大队选项
290
+const brigadeOptions = ref([])
291
+// 人员选项
292
+const personOptions = ref([])
293
+// 班组长选项
294
+const teamLeaderOptions = ref([])
295
+// 主管选项
296
+const supervisorOptions = ref([])
297
+// 判别类别选项
298
+const judgeTypeOptions = ref([
299
+  { label: '火灾', value: '火灾' },
300
+  { label: '打火机', value: '打火机' },
301
+  { label: '火柴', value: '火柴' },
302
+  { label: '火种', value: '火种' },
303
+  { label: '其他', value: '其他' }
304
+])
305
+
306
+/** 查询漏检列表 */
307
+function getList() {
308
+  loading.value = true
309
+  if (dateRange.value && dateRange.value.length === 2) {
310
+    queryParams.checkDateStart = dateRange.value[0]
311
+    queryParams.checkDateEnd = dateRange.value[1]
312
+  } else {
313
+    queryParams.checkDateStart = null
314
+    queryParams.checkDateEnd = null
315
+  }
316
+  listMissedInspection(queryParams).then(response => {
317
+    missedInspectionList.value = response.rows
318
+    total.value = response.total
319
+    loading.value = false
320
+  })
321
+}
322
+
323
+/** 获取部门列表 */
324
+function getDeptList() {
325
+  listDept({}).then(response => {
326
+    const deptList = response.data || []
327
+    brigadeOptions.value = deptList.filter(item => item.deptType === 'BRIGADE').map(item => ({
328
+      value: item.deptId,
329
+      label: item.deptName
330
+    }))
331
+    teamLeaderOptions.value = deptList.filter(item => item.deptType === 'TEAMS').map(item => ({
332
+      value: item.deptId,
333
+      label: item.deptName
334
+    }))
335
+    supervisorOptions.value = deptList.filter(item => item.deptType === 'MANAGER').map(item => ({
336
+      value: item.deptId,
337
+      label: item.deptName
338
+    }))
339
+  })
340
+}
341
+
342
+/** 获取用户列表 */
343
+function getUserList() {
344
+  listUser({ pageNum: 1, pageSize: 1000 }).then(response => {
345
+    personOptions.value = response.rows.map(item => ({
346
+      value: item.userId,
347
+      label: item.nickName
348
+    }))
349
+  })
350
+}
351
+
352
+// 取消按钮
353
+function cancel() {
354
+  open.value = false
355
+  reset()
356
+}
357
+
358
+// 表单重置
359
+function reset() {
360
+  form.value = {
361
+    id: null,
362
+    brigadeId: null,
363
+    area: null,
364
+    checkedPerson: null,
365
+    checkDate: null,
366
+    missedTime: null,
367
+    missedTimePeriod: null,
368
+    workPosition: null,
369
+    supervisorId: null,
370
+    actingSupervisorId: null,
371
+    teamLeaderId: null,
372
+    itemPosition: null,
373
+    difficulty: null,
374
+    checker: null,
375
+    judgeType: null,
376
+    isRecovered: null,
377
+    machineYears: null,
378
+    certificateLevel: null,
379
+    personGender: null,
380
+    missedReasonCategory: null,
381
+    monthlyExamScore: null,
382
+    monthlySelfTestMissed: null,
383
+    missedItem: null
384
+  }
385
+  proxy.resetForm('missedInspectionRef')
386
+}
387
+
388
+/** 搜索按钮操作 */
389
+function handleQuery() {
390
+  queryParams.pageNum = 1
391
+  getList()
392
+}
393
+
394
+/** 重置按钮操作 */
395
+function resetQuery() {
396
+  dateRange.value = []
397
+  proxy.resetForm('queryRef')
398
+  handleQuery()
399
+}
400
+
401
+// 多选框选中数据
402
+function handleSelectionChange(selection) {
403
+  ids.value = selection.map(item => item.id)
404
+  single.value = selection.length !== 1
405
+  multiple.value = !selection.length
406
+}
407
+
408
+/** 新增按钮操作 */
409
+function handleAdd() {
410
+  reset()
411
+  open.value = true
412
+  title.value = '添加漏检'
413
+}
414
+
415
+/** 修改按钮操作 */
416
+function handleUpdate(row) {
417
+  reset()
418
+  const id = row.id || ids.value[0]
419
+  getMissedInspection(id).then(response => {
420
+    Object.assign(form, response.data)
421
+    open.value = true
422
+    title.value = '修改漏检'
423
+  })
424
+}
425
+
426
+/** 提交按钮 */
427
+function submitForm() {
428
+  proxy.$refs.missedInspectionRef.validate(valid => {
429
+    if (valid) {
430
+      if (form.id != null) {
431
+        updateMissedInspection(form).then(response => {
432
+          proxy.$modal.msgSuccess('修改成功')
433
+          open.value = false
434
+          getList()
435
+        })
436
+      } else {
437
+        addMissedInspection(form).then(response => {
438
+          proxy.$modal.msgSuccess('新增成功')
439
+          open.value = false
440
+          getList()
441
+        })
442
+      }
443
+    }
444
+  })
445
+}
446
+
447
+/** 删除按钮操作 */
448
+function handleDelete(row) {
449
+  const id = row.id || ids.value
450
+  proxy.$modal.confirm('是否确认删除漏检编号为"' + id + '"的数据项?').then(function () {
451
+    return delMissedInspection(id)
452
+  }).then(() => {
453
+    getList()
454
+    proxy.$modal.msgSuccess('删除成功')
455
+  }).catch(() => { })
456
+}
457
+
458
+/** 导出按钮操作 */
459
+function handleExport() {
460
+  proxy.download('missedInspection/export', {
461
+    ...queryParams
462
+  }, `漏检_${new Date().getTime()}.xlsx`)
463
+}
464
+
465
+/** 导入按钮操作 */
466
+function handleImport() {
467
+  importOpen.value = true
468
+  importFile.value = null
469
+}
470
+
471
+/** 文件选择 */
472
+function handleFileChange(file) {
473
+  importFile.value = file.raw
474
+}
475
+
476
+/** 提交导入 */
477
+function submitImport() {
478
+  if (!importFile.value) {
479
+    proxy.$modal.msgWarning('请选择要导入的文件')
480
+    return
481
+  }
482
+  const formData = new FormData()
483
+  formData.append('file', importFile.value)
484
+  proxy.upload('missedInspection/importData', formData).then(response => {
485
+    proxy.$modal.msgSuccess(response.msg)
486
+    importOpen.value = false
487
+    getList()
488
+  })
489
+}
490
+
491
+onMounted(() => {
492
+  getList()
493
+  getDeptList()
494
+  getUserList()
495
+})
496
+</script>
497
+
498
+<style lang="less" scoped>
499
+.app-container {
500
+  padding: 20px;
501
+}
502
+
503
+:deep(.el-table) {
504
+  .el-table__header-wrapper {
505
+    th {
506
+      background-color: #303133;
507
+      color: #ffffff;
508
+      font-weight: 600;
509
+    }
510
+  }
511
+}
512
+</style>

+ 0 - 0
src/views/blockingData/missedInspectionList/漏检统计表


+ 352 - 0
src/views/blockingData/rateList/index.vue

@@ -0,0 +1,352 @@
1
+<template>
2
+  <div class="app-container">
3
+    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
4
+      <el-form-item label="大队" prop="brigadeId">
5
+        <el-select v-model="queryParams.brigadeId" placeholder="请选择大队" clearable style="width: 200px">
6
+          <el-option v-for="item in brigadeOptions" :key="item.value" :label="item.label" :value="item.value" />
7
+        </el-select>
8
+      </el-form-item>
9
+      <el-form-item label="日期" prop="date">
10
+        <el-date-picker clearable v-model="queryParams.date" type="date" placeholder="选择日期" value-format="YYYY-MM-DD"
11
+          style="width: 200px" />
12
+      </el-form-item>
13
+      <el-form-item label="班次" prop="shift">
14
+        <el-select v-model="queryParams.shift" placeholder="请选择班次" clearable style="width: 200px">
15
+          <el-option label="白班" value="白班" />
16
+          <el-option label="夜班" value="夜班" />
17
+        </el-select>
18
+      </el-form-item>
19
+      <el-form-item>
20
+        <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
21
+        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
22
+      </el-form-item>
23
+    </el-form>
24
+
25
+    <el-row :gutter="10" class="mb8">
26
+      <el-col :span="1.5">
27
+        <el-button type="primary" plain icon="Plus" @click="handleAdd"
28
+          v-hasPermi="['rateList:rateList:add']">新增</el-button>
29
+      </el-col>
30
+      <el-col :span="1.5">
31
+        <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
32
+          v-hasPermi="['rateList:rateList:remove']">删除</el-button>
33
+      </el-col>
34
+      <el-col :span="1.5">
35
+        <el-button type="warning" plain icon="Download" @click="handleExport"
36
+          v-hasPermi="['rateList:rateList:export']">导出</el-button>
37
+      </el-col>
38
+      <el-col :span="1.5">
39
+        <el-button type="info" plain icon="Upload" @click="handleImport"
40
+          v-hasPermi="['rateList:rateList:import']">导入</el-button>
41
+      </el-col>
42
+      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
43
+    </el-row>
44
+
45
+    <el-table v-loading="loading" :data="rateList" @selection-change="handleSelectionChange">
46
+      <el-table-column type="selection" width="55" align="center" />
47
+      <el-table-column label="日期" align="center" prop="date" width="120" />
48
+      <el-table-column label="旅检国内区域平均速率(高峰期时段)" align="center" prop="domesticAvgRate" width="220" />
49
+      <el-table-column label="当班大队" align="center" prop="dutyBrigade" width="120" />
50
+      <el-table-column label="T1-A速率(高峰期时段)" align="center" prop="t1ARate" width="180" />
51
+      <el-table-column label="T1-B速率(高峰期时段)" align="center" prop="t1BRate" width="180" />
52
+      <el-table-column label="T2国内速率(高峰期时段)" align="center" prop="t2DomesticRate" width="180" />
53
+      <el-table-column label="T2国际速率(高峰期时段)" align="center" prop="t2InternationalRate" width="180" />
54
+      <el-table-column label="班次" align="center" prop="shift" width="100" />
55
+      <el-table-column label="T2中转(高峰期时段)" align="center" prop="t2TransferRate" width="160" />
56
+      <el-table-column label="国际及中转区域平均速率(高峰期时段)" align="center" prop="internationalTransferAvgRate" width="240" />
57
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="140">
58
+        <template #default="scope">
59
+          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
60
+            v-hasPermi="['rateList:rateList:edit']">修改</el-button>
61
+          <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
62
+            v-hasPermi="['rateList:rateList:remove']">删除</el-button>
63
+        </template>
64
+      </el-table-column>
65
+    </el-table>
66
+
67
+    <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
68
+      v-model:limit="queryParams.pageSize" @pagination="getList" />
69
+
70
+    <el-dialog :title="title" v-model="open" width="800px" append-to-body>
71
+      <el-form ref="rateListRef" :model="form" :rules="rules" label-width="240px">
72
+        <el-row :gutter="20">
73
+          <el-col :span="12">
74
+            <el-form-item label="日期" prop="date">
75
+              <el-date-picker v-model="form.date" type="date" placeholder="选择日期" value-format="YYYY-MM-DD"
76
+                style="width: 100%" />
77
+            </el-form-item>
78
+          </el-col>
79
+          <el-col :span="12">
80
+            <el-form-item label="当班大队" prop="dutyBrigade">
81
+              <el-select v-model="form.dutyBrigade" placeholder="请选择当班大队" style="width: 100%">
82
+                <el-option v-for="item in brigadeOptions" :key="item.value" :label="item.label" :value="item.value" />
83
+              </el-select>
84
+            </el-form-item>
85
+          </el-col>
86
+        </el-row>
87
+        <el-row :gutter="20">
88
+          <el-col :span="12">
89
+            <el-form-item label="班次" prop="shift">
90
+              <el-select v-model="form.shift" placeholder="请选择班次" style="width: 100%">
91
+                <el-option label="白班" value="白班" />
92
+                <el-option label="夜班" value="夜班" />
93
+              </el-select>
94
+            </el-form-item>
95
+          </el-col>
96
+          <el-col :span="12">
97
+            <el-form-item label="旅检国内区域平均速率(高峰期时段)" prop="domesticAvgRate">
98
+              <el-input-number v-model="form.domesticAvgRate" :min="0" :precision="2" style="width: 100%" />
99
+            </el-form-item>
100
+          </el-col>
101
+        </el-row>
102
+        <el-row :gutter="20">
103
+          <el-col :span="12">
104
+            <el-form-item label="T1-A速率(高峰期时段)" prop="t1ARate">
105
+              <el-input-number v-model="form.t1ARate" :min="0" :precision="2" style="width: 100%" />
106
+            </el-form-item>
107
+          </el-col>
108
+          <el-col :span="12">
109
+            <el-form-item label="T1-B速率(高峰期时段)" prop="t1BRate">
110
+              <el-input-number v-model="form.t1BRate" :min="0" :precision="2" style="width: 100%" />
111
+            </el-form-item>
112
+          </el-col>
113
+        </el-row>
114
+        <el-row :gutter="20">
115
+          <el-col :span="12">
116
+            <el-form-item label="T2国内速率(高峰期时段)" prop="t2DomesticRate">
117
+              <el-input-number v-model="form.t2DomesticRate" :min="0" :precision="2" style="width: 100%" />
118
+            </el-form-item>
119
+          </el-col>
120
+          <el-col :span="12">
121
+            <el-form-item label="T2国际速率(高峰期时段)" prop="t2InternationalRate">
122
+              <el-input-number v-model="form.t2InternationalRate" :min="0" :precision="2" style="width: 100%" />
123
+            </el-form-item>
124
+          </el-col>
125
+        </el-row>
126
+        <el-row :gutter="20">
127
+          <el-col :span="12">
128
+            <el-form-item label="T2中转(高峰期时段)" prop="t2TransferRate">
129
+              <el-input-number v-model="form.t2TransferRate" :min="0" :precision="2" style="width: 100%" />
130
+            </el-form-item>
131
+          </el-col>
132
+          <el-col :span="12">
133
+            <el-form-item label="国际及中转区域平均速率(高峰期时段)" prop="internationalTransferAvgRate">
134
+              <el-input-number v-model="form.internationalTransferAvgRate" :min="0" :precision="2" style="width: 100%" />
135
+            </el-form-item>
136
+          </el-col>
137
+        </el-row>
138
+      </el-form>
139
+      <template #footer>
140
+        <div class="dialog-footer">
141
+          <el-button type="primary" @click="submitForm">确 定</el-button>
142
+          <el-button @click="cancel">取 消</el-button>
143
+        </div>
144
+      </template>
145
+    </el-dialog>
146
+
147
+    <el-dialog title="导入" v-model="importOpen" width="500px" append-to-body>
148
+      <el-upload ref="uploadRef" :auto-upload="false" :on-change="handleFileChange" :show-file-list="false"
149
+        accept=".xlsx,.xls">
150
+        <el-button type="primary">选取文件</el-button>
151
+        <div class="el-upload__tip">只能上传xls/xlsx文件</div>
152
+      </el-upload>
153
+      <template #footer>
154
+        <div class="dialog-footer">
155
+          <el-button type="primary" @click="submitImport">确 定</el-button>
156
+          <el-button @click="importOpen = false">取 消</el-button>
157
+        </div>
158
+      </template>
159
+    </el-dialog>
160
+  </div>
161
+</template>
162
+
163
+<script setup>
164
+import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
165
+import { listRate, getRate, delRate, addRate, updateRate, exportRate } from '@/api/blockingData/rateList'
166
+import { listDept } from '@/api/system/dept'
167
+
168
+const { proxy } = getCurrentInstance()
169
+
170
+const loading = ref(true)
171
+const ids = ref([])
172
+const single = ref(true)
173
+const multiple = ref(true)
174
+const showSearch = ref(true)
175
+const total = ref(0)
176
+const rateList = ref([])
177
+const title = ref('')
178
+const open = ref(false)
179
+const importOpen = ref(false)
180
+const importFile = ref(null)
181
+
182
+const queryParams = reactive({
183
+  pageNum: 1,
184
+  pageSize: 10,
185
+  brigadeId: null,
186
+  date: null,
187
+  shift: null
188
+})
189
+
190
+const form = reactive({})
191
+
192
+const rules = {
193
+  date: [{ required: true, message: '日期不能为空', trigger: 'change' }],
194
+  dutyBrigade: [{ required: true, message: '当班大队不能为空', trigger: 'change' }],
195
+  shift: [{ required: true, message: '班次不能为空', trigger: 'change' }]
196
+}
197
+
198
+const brigadeOptions = ref([])
199
+
200
+function getList() {
201
+  loading.value = true
202
+  listRate(queryParams).then(response => {
203
+    rateList.value = response.rows
204
+    total.value = response.total
205
+    loading.value = false
206
+  })
207
+}
208
+
209
+function getDeptList() {
210
+  listDept({}).then(response => {
211
+    const deptList = response.data || []
212
+    brigadeOptions.value = deptList.filter(item => item.deptType === 'BRIGADE').map(item => ({
213
+      value: item.deptId,
214
+      label: item.deptName
215
+    }))
216
+  })
217
+}
218
+
219
+function cancel() {
220
+  open.value = false
221
+  reset()
222
+}
223
+
224
+function reset() {
225
+  form.value = {
226
+    id: null,
227
+    date: null,
228
+    dutyBrigade: null,
229
+    shift: null,
230
+    domesticAvgRate: 0,
231
+    t1ARate: 0,
232
+    t1BRate: 0,
233
+    t2DomesticRate: 0,
234
+    t2InternationalRate: 0,
235
+    t2TransferRate: 0,
236
+    internationalTransferAvgRate: 0
237
+  }
238
+  proxy.resetForm('rateListRef')
239
+}
240
+
241
+function handleQuery() {
242
+  queryParams.pageNum = 1
243
+  getList()
244
+}
245
+
246
+function resetQuery() {
247
+  proxy.resetForm('queryRef')
248
+  handleQuery()
249
+}
250
+
251
+function handleSelectionChange(selection) {
252
+  ids.value = selection.map(item => item.id)
253
+  single.value = selection.length !== 1
254
+  multiple.value = !selection.length
255
+}
256
+
257
+function handleAdd() {
258
+  reset()
259
+  open.value = true
260
+  title.value = '添加速率统计'
261
+}
262
+
263
+function handleUpdate(row) {
264
+  reset()
265
+  const id = row.id || ids.value[0]
266
+  getRate(id).then(response => {
267
+    Object.assign(form, response.data)
268
+    open.value = true
269
+    title.value = '修改速率统计'
270
+  })
271
+}
272
+
273
+function submitForm() {
274
+  proxy.$refs.rateListRef.validate(valid => {
275
+    if (valid) {
276
+      if (form.id != null) {
277
+        updateRate(form).then(response => {
278
+          proxy.$modal.msgSuccess('修改成功')
279
+          open.value = false
280
+          getList()
281
+        })
282
+      } else {
283
+        addRate(form).then(response => {
284
+          proxy.$modal.msgSuccess('新增成功')
285
+          open.value = false
286
+          getList()
287
+        })
288
+      }
289
+    }
290
+  })
291
+}
292
+
293
+function handleDelete(row) {
294
+  const id = row.id || ids.value
295
+  proxy.$modal.confirm('是否确认删除速率统计编号为"' + id + '"的数据项?').then(function () {
296
+    return delRate(id)
297
+  }).then(() => {
298
+    getList()
299
+    proxy.$modal.msgSuccess('删除成功')
300
+  }).catch(() => { })
301
+}
302
+
303
+function handleExport() {
304
+  proxy.download('rateList/export', {
305
+    ...queryParams
306
+  }, `速率统计_${new Date().getTime()}.xlsx`)
307
+}
308
+
309
+function handleImport() {
310
+  importOpen.value = true
311
+  importFile.value = null
312
+}
313
+
314
+function handleFileChange(file) {
315
+  importFile.value = file.raw
316
+}
317
+
318
+function submitImport() {
319
+  if (!importFile.value) {
320
+    proxy.$modal.msgWarning('请选择要导入的文件')
321
+    return
322
+  }
323
+  const formData = new FormData()
324
+  formData.append('file', importFile.value)
325
+  proxy.upload('rateList/importData', formData).then(response => {
326
+    proxy.$modal.msgSuccess(response.msg)
327
+    importOpen.value = false
328
+    getList()
329
+  })
330
+}
331
+
332
+onMounted(() => {
333
+  getList()
334
+  getDeptList()
335
+})
336
+</script>
337
+
338
+<style lang="less" scoped>
339
+.app-container {
340
+  padding: 20px;
341
+}
342
+
343
+:deep(.el-table) {
344
+  .el-table__header-wrapper {
345
+    th {
346
+      background-color: #303133;
347
+      color: #ffffff;
348
+      font-weight: 600;
349
+    }
350
+  }
351
+}
352
+</style>

+ 0 - 0
src/views/blockingData/rateList/速率表