| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- <template>
- <module-container title="模块一 运行数据分析">
- <div class="module-brigade-content">
- <div class="combined-row">
- <div class="stat-card">
- <div class="stat-card-inner">
- <div class="stat-left">
- <div class="stat-label">查堵总数</div>
- <div class="stat-value">{{ statValue || 0 }}</div>
- <div class="stat-unit">起</div>
- </div>
- </div>
- </div>
- <div class="chart-item">
- <div class="chart-title">两楼每日查堵走势</div>
- <div ref="chart1" class="echarts"></div>
- </div>
- <div class="chart-item">
- <div class="chart-title">两楼每日过检图像数</div>
- <div ref="chart2" class="echarts"></div>
- </div>
- </div>
-
- <div class="chart-row">
- <div class="chart-item">
- <div class="chart-title">两楼查堵数(包含行检)</div>
- <div ref="chart3" class="echarts"></div>
- </div>
- <div class="chart-item">
- <div class="chart-title">查堵时间段过检行李数</div>
- <div ref="chart4" class="echarts"></div>
- </div>
- </div>
-
- <div class="chart-row">
- <div class="chart-item">
- <div class="chart-title">两楼每日查堵万分率</div>
- <div ref="chart5" class="echarts"></div>
- </div>
- <div class="chart-item">
- <div class="chart-title">查堵物品分布</div>
- <div ref="chart6" class="echarts"></div>
- </div>
- </div>
- </div>
- </module-container>
- </template>
- <script setup>
- import { ref, onMounted, watch } from 'vue'
- import * as echarts from 'echarts'
- import ModuleContainer from './ModuleContainer.vue'
- import { useEcharts } from '@/hooks/chart.js'
- import { formatDateHy } from '@/utils/index.js'
- import {
- dailyTerminalTrend,
- dailyTerminalLuggageTrend,
- terminalBlockedStats,
- timePeriodLuggageStats,
- dailyTerminalRateTrend,
- brigadeItemDistribution
- } from '@/api/blockingData/blockingDataScreen'
- const props = defineProps({
- filterParams: {
- type: Object,
- default: () => ({})
- }
- })
- const loading = ref(false)
- const statValue = ref(0)
- const processFilterParams = (params) => {
- const { dateRange, ...rest } = params
- const processed = { ...rest }
- if (dateRange && Array.isArray(dateRange) && dateRange.length === 2) {
- processed.startTime = formatDateHy(dateRange[0])
- processed.endTime = formatDateHy(dateRange[1])
- }
- if (processed.brigadeId == 'all') {
- delete processed.brigadeId
- }
- if (processed.terminalId == 'all') {
- delete processed.terminalId
- }
- return processed
- }
- const chart1 = ref(null)
- const chart2 = ref(null)
- const chart3 = ref(null)
- const chart4 = ref(null)
- const chart5 = ref(null)
- const chart6 = ref(null)
- const { setOption: setOption1 } = useEcharts(chart1)
- const { setOption: setOption2 } = useEcharts(chart2)
- const { setOption: setOption3 } = useEcharts(chart3)
- const { setOption: setOption4 } = useEcharts(chart4)
- const { setOption: setOption5 } = useEcharts(chart5)
- const { setOption: setOption6 } = useEcharts(chart6)
- const fetchData = async () => {
- loading.value = true
- try {
- const processedParams = processFilterParams(props.filterParams)
- const [trendRes, luggageRes, blockedRes, timeRes, rateRes, itemRes] = await Promise.allSettled([
- dailyTerminalTrend(processedParams),
- dailyTerminalLuggageTrend(processedParams),
- terminalBlockedStats(processedParams),
- timePeriodLuggageStats(processedParams),
- dailyTerminalRateTrend(processedParams),
- brigadeItemDistribution(processedParams)
- ])
- if (trendRes.value?.data) {
- const dates = trendRes.value.data.map(item => item.statDate?.split('T')[0] || item.date || item.name || '')
- const t1TravelData = trendRes.value.data.map(item => item.t1TravelBlockedCount || 0)
- const t2TravelData = trendRes.value.data.map(item => item.t2TravelBlockedCount || 0)
- const t1WalkData = trendRes.value.data.map(item => item.t1WalkBlockedCount || 0)
- const t2WalkData = trendRes.value.data.map(item => item.t2WalkBlockedCount || 0)
- setOption1(multiLineChartOption(dates, [
- { name: 'T1旅检查堵件数', data: t1TravelData, color: '#3b82f6' },
- { name: 'T2旅检查堵件数', data: t2TravelData, color: '#22c55e' },
- { name: 'T1行检查堵件数', data: t1WalkData, color: '#f97316' },
- { name: 'T2行检查堵件数', data: t2WalkData, color: '#ec4899' }
- ]))
- }
- if (luggageRes.value?.data) {
- const dates = luggageRes.value.data.map(item => item.statDate?.split('T')[0] || item.date || item.name || '')
- const t1TravelData = luggageRes.value.data.map(item => item.t1TravelBagCount || 0)
- const t2TravelData = luggageRes.value.data.map(item => item.t2TravelBagCount || 0)
- const t1WalkData = luggageRes.value.data.map(item => item.t1WalkBagCount || 0)
- const t2WalkData = luggageRes.value.data.map(item => item.t2WalkBagCount || 0)
- setOption2(multiLineChartOption(dates, [
- { name: 'T1旅检过检行李数', data: t1TravelData, color: '#3b82f6' },
- { name: 'T2旅检过检行李数', data: t2TravelData, color: '#22c55e' },
- { name: 'T1行检过检行李数', data: t1WalkData, color: '#f97316' },
- { name: 'T2行检过检行李数', data: t2WalkData, color: '#ec4899' }
- ]))
- }
- if (blockedRes.value?.data) {
- const blockedData = [
- { name: 'T1', value: blockedRes.value.data.t1BlockedCount || 0 },
- { name: 'T2', value: blockedRes.value.data.t2BlockedCount || 0 }
- ]
- setOption3(horizontalBarChartOption(blockedData, '#3b82f6'))
- const total = blockedRes.value.data.totalBlockedCount || 0
- statValue.value = total
- }
- if (timeRes.value?.data) {
- const timePeriods = timeRes.value.data.map(item => item.timePeriod || '')
- const avgLuggageData = timeRes.value.data.map(item => item.avgLuggageCount || 0)
- const avgBlockedData = timeRes.value.data.map(item => item.avgBlockedCount || 0)
- setOption4(barLineChartOption(timePeriods, avgLuggageData, avgBlockedData))
- }
- if (rateRes.value?.data) {
- const dates = rateRes.value.data.map(item => item.statDate?.split('T')[0] || item.date || item.name || '')
- const t1TravelRate = rateRes.value.data.map(item => item.t1TravelBlockRate || 0)
- const t2TravelRate = rateRes.value.data.map(item => item.t2TravelBlockRate || 0)
- const t1WalkRate = rateRes.value.data.map(item => item.t1WalkBlockRate || 0)
- const t2WalkRate = rateRes.value.data.map(item => item.t2WalkBlockRate || 0)
- setOption5(multiLineChartOption(dates, [
- { name: 'T1旅检万分率', data: t1TravelRate, color: '#3b82f6' },
- { name: 'T2旅检万分率', data: t2TravelRate, color: '#22c55e' },
- { name: 'T1行检万分率', data: t1WalkRate, color: '#f97316' },
- { name: 'T2行检万分率', data: t2WalkRate, color: '#ec4899' }
- ]))
- }
- if (itemRes.value?.data) {
- const colors = ['#3b82f6', '#22c55e', '#f97316', '#ec4899', '#8b5cf6', '#10b981', '#f59e0b', '#ef4444']
- const itemData = itemRes.value.data.map((item, index) => ({
- name: item.missCheckItem ,
- value: item.count
- }))
- setOption6(pieChartOption(itemData, colors))
- }
- } catch (error) {
- console.error('获取数据失败:', error)
- } finally {
- loading.value = false
- }
- }
- watch(() => props.filterParams, () => {
- fetchData()
- }, { deep: true })
- const xAxisData = Array.from({ length: 30 }, (_, i) => `${i + 1}日`)
- const generateData = (count, min, max) => {
- const data = []
- for (let i = 0; i < count; i++) {
- data.push(Math.floor(Math.random() * (max - min + 1)) + min)
- }
- return data
- }
- const multiLineChartOption = (xAxisData, series) => ({
- grid: { left: '10%', top: '15%', right: '5%', bottom: '15%', containLabel: true },
- tooltip: {
- trigger: 'axis',
- axisPointer: { type: 'cross' }
- },
- xAxis: { type: 'category', data: xAxisData, axisLine: { lineStyle: { color: '#999' } }, axisLabel: { fontSize: 10, color: '#666' } },
- yAxis: { type: 'value', axisLine: { lineStyle: { color: '#999' } }, axisLabel: { fontSize: 10, color: '#666' }, splitLine: { lineStyle: { color: '#eee' } } },
- legend: {
- data: series.map(s => s.name),
- top: '0%',
- textStyle: { fontSize: 10 }
- },
- series: series.map(s => ({
- name: s.name,
- type: 'line',
- smooth: true,
- symbol: 'circle',
- symbolSize: 6,
- data: s.data,
- itemStyle: { color: s.color },
- lineStyle: { color: s.color },
- label: { show: true, position: 'top', fontSize: 9, color: s.color }
- }))
- })
- const horizontalBarChartOption = (data, color) => ({
- grid: { left: '20%', top: '15%', right: '5%', bottom: '15%', containLabel: true },
- tooltip: {
- trigger: 'axis',
- axisPointer: { type: 'shadow' }
- },
- xAxis: { type: 'value', axisLine: { lineStyle: { color: '#999' } }, axisLabel: { fontSize: 10, color: '#666' }, splitLine: { lineStyle: { color: '#eee' } } },
- yAxis: { type: 'category', data: data.map(d => d.name), axisLine: { lineStyle: { color: '#999' } }, axisLabel: { fontSize: 10, color: '#666' } },
- series: [{
- type: 'bar',
- data: data.map(d => d.value),
- itemStyle: { color: color },
- barWidth: 15,
- label: { show: true, position: 'right', fontSize: 10, color: color }
- }]
- })
- const barLineChartOption = (xAxisData, barData, lineData) => ({
- grid: { left: '10%', top: '15%', right: '5%', bottom: '15%', containLabel: true },
- tooltip: {
- trigger: 'axis',
- axisPointer: { type: 'cross' }
- },
- xAxis: { type: 'category', data: xAxisData, axisLine: { lineStyle: { color: '#999' } }, axisLabel: { fontSize: 10, color: '#666' } },
- yAxis: { type: 'value', axisLine: { lineStyle: { color: '#999' } }, axisLabel: { fontSize: 10, color: '#666' }, splitLine: { lineStyle: { color: '#eee' } } },
- legend: {
- data: ['平均过检行李数', '平均查堵件数'],
- top: '0%',
- textStyle: { fontSize: 10 }
- },
- series: [
- {
- name: '平均过检行李数',
- type: 'bar',
- data: barData,
- itemStyle: { color: '#3b82f6' },
- barWidth: 20,
- label: { show: true, position: 'top', fontSize: 9, color: '#3b82f6' }
- },
- {
- name: '平均查堵件数',
- type: 'line',
- smooth: true,
- symbol: 'circle',
- symbolSize: 6,
- data: lineData,
- itemStyle: { color: '#ec4899' },
- lineStyle: { color: '#ec4899' },
- label: { show: true, position: 'top', fontSize: 9, color: '#ec4899' }
- }
- ]
- })
- const pieChartOption = (data, colors) => ({
- color: colors,
- legend: {
- show: true,
- textStyle: { fontSize: 10 }
- },
- tooltip: { trigger: 'item' },
- series: [{
- type: 'pie',
- radius: '65%',
- center: ['50%', '55%'],
- data: data,
- label: { show: true, formatter: '{b}\n{c} ({d}%)', fontSize: 10 }
- }]
- })
- onMounted(() => {
- fetchData()
-
- })
- </script>
- <style lang="less" scoped>
- .module-brigade-content {
- height: 100%;
- display: flex;
- flex-direction: column;
- gap: 10px;
- overflow-y: auto;
- }
- .stats-row {
- display: flex;
- gap: 10px;
- flex-shrink: 0;
- }
- .stat-card {
- flex: 1;
- background: #fff;
- border-radius: 6px;
- padding: 15px;
- border: 1px solid #eee;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
- min-height: 120px;
- }
- .stat-card-inner {
- display: flex;
- height: 100%;
- align-items: center;
- justify-content: center;
- }
- .stat-left {
- display: flex;
- flex-direction: column;
- align-items: center;
- }
- .stat-label {
- font-size: 14px;
- color: #666;
- margin-bottom: 8px;
- }
- .stat-value {
- font-size: 48px;
- font-weight: bold;
- color: #3b82f6;
- }
- .stat-unit {
- font-size: 14px;
- color: #666;
- margin-top: 5px;
- }
- .combined-row {
- display: flex;
- gap: 10px;
- flex-shrink: 0;
- }
- .combined-row .stat-card {
- flex: 0.5;
- min-height: 200px;
- }
- .combined-row .chart-item {
- flex: 1;
- }
- .chart-row {
- display: flex;
- gap: 10px;
- flex-shrink: 0;
- }
- .chart-item {
- flex: 1;
- background: #fff;
- border-radius: 6px;
- padding: 15px;
- border: 1px solid #eee;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
- display: flex;
- flex-direction: column;
- min-height: 300px;
- }
- .chart-title {
- font-size: 17px;
- color: black;
- margin-bottom: 5px;
- text-align: left;
- }
- .echarts {
- flex: 1;
- width: 100%;
- min-height: 0;
- }
- </style>
|