| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- <template>
- <module-container title="模块二 查堵人员分析">
- <div class="module-brigade-content">
- <div class="chart-row">
- <div class="chart-item">
- <div class="chart-title">查堵-主管排行榜</div>
- <rank-list :rank-data="rankData1" title="查堵-主管排行榜" header-label="排名" header-name="分管主管" header-count="计数" />
- </div>
- <div class="chart-item">
- <div class="chart-title">查堵-班组长排行榜</div>
- <rank-list :rank-data="rankData2" title="查堵-班组长排行榜" header-label="排名" header-name="分管班组长" header-count="计数" />
- </div>
- <div class="chart-item">
- <div class="chart-title">查堵-员工排行榜</div>
- <rank-list :rank-data="rankData3" title="查堵-员工排行榜" header-label="排名" header-name="被回查人" header-count="计数" />
- </div>
- </div>
- <div class="chart-row">
- <div class="chart-item">
- <div class="chart-title">查堵男女比例</div>
- <div ref="chart4" class="echarts"></div>
- </div>
- <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 class="chart-row">
- <div class="chart-item">
- <div class="chart-title">查堵物品位置</div>
- <div ref="chart7" class="echarts"></div>
- </div>
- <div class="chart-item">
- <div class="chart-title">查堵-困难图像数</div>
- <div class="number-display">
- <div class="number-value">{{ difficultValue || 0 }}</div>
- <div class="number-unit">幅</div>
- </div>
- </div>
- <div class="chart-item">
- <div class="chart-title">查堵-简单图像数</div>
- <div class="number-display">
- <div class="number-value">{{ simpleValue || 0 }}</div>
- <div class="number-unit">幅</div>
- </div>
- </div>
- </div>
- <div class="chart-row">
- <div class="chart-item">
- <div class="chart-title">查堵-主管分管次数</div>
- <div ref="chart10" class="echarts"></div>
- </div>
- <div class="chart-item">
- <div class="chart-title">查堵原因分类</div>
- <div ref="chart11" class="echarts"></div>
- </div>
- </div>
- <div class="chart-row">
- <div class="chart-item">
- <div class="chart-title">查堵人员开机年限分布</div>
- <div ref="chart12" class="echarts"></div>
- </div>
- <div class="chart-item">
- <div class="chart-title">大队开机年限人员分布</div>
- <div ref="chart13" class="echarts"></div>
- </div>
- </div>
- </div>
- </module-container>
- </template>
- <script setup>
- import { ref, onMounted, reactive, watch } from 'vue'
- import * as echarts from 'echarts'
- import ModuleContainer from './ModuleContainer.vue'
- import RankList from './RankList.vue'
- import { useEcharts } from '@/hooks/chart.js'
- import { formatDateHy } from '@/utils/index.js'
- import {
- brigadeSupervisorRanking,
- brigadeTeamLeaderRanking,
- brigadeReviewedUserRanking,
- brigadeGenderDistribution,
- brigadeCertificateDistributionDetail,
- personnelCertificateBase,
- brigadeItemLocationDistribution,
- brigadeDifficultyDistribution,
- brigadeSupervisorDistribution,
- brigadeMissCheckReasonDistribution,
- brigadeOperatingYearsDistribution,
- tenureListAll
- } from '@/api/blockingData/blockingDataScreen'
- const props = defineProps({
- filterParams: {
- type: Object,
- default: () => ({})
- }
- })
- const loading = ref(false)
- const rankData1 = reactive([])
- const rankData2 = reactive([])
- const rankData3 = reactive([])
- const difficultValue = ref(0)
- const simpleValue = 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 chart4 = ref(null)
- const chart5 = ref(null)
- const chart6 = ref(null)
- const chart7 = ref(null)
- const chart10 = ref(null)
- const chart11 = ref(null)
- const chart12 = ref(null)
- const chart13 = ref(null)
- const { setOption: setOption4 } = useEcharts(chart4)
- const { setOption: setOption5 } = useEcharts(chart5)
- const { setOption: setOption6 } = useEcharts(chart6)
- const { setOption: setOption7 } = useEcharts(chart7)
- const { setOption: setOption10 } = useEcharts(chart10)
- const { setOption: setOption11 } = useEcharts(chart11)
- const { setOption: setOption12 } = useEcharts(chart12)
- const { setOption: setOption13 } = useEcharts(chart13)
- const fetchData = async () => {
- loading.value = true
- try {
- const processedParams = processFilterParams(props.filterParams)
- const [supervisorRes, teamRes, userRes, genderRes, certDetailRes, certBaseRes, locationRes, difficultyRes, supervisorDistRes, reasonRes, yearsRes, tenureRes] = await Promise.allSettled([
- brigadeSupervisorRanking(processedParams),
- brigadeTeamLeaderRanking(processedParams),
- brigadeReviewedUserRanking(processedParams),
- brigadeGenderDistribution(processedParams),
- brigadeCertificateDistributionDetail(processedParams),
- personnelCertificateBase(processedParams),
- brigadeItemLocationDistribution(processedParams),
- brigadeDifficultyDistribution(processedParams),
- brigadeSupervisorDistribution(processedParams),
- brigadeMissCheckReasonDistribution(processedParams),
- brigadeOperatingYearsDistribution(processedParams),
- tenureListAll(processedParams)
- ])
- if (supervisorRes.value?.data) {
- rankData1.length = 0
- supervisorRes.value.data.slice(0, 10).forEach(item => {
- rankData1.push({
- name: item.supervisorName || item.name || '',
- value: item.count || item.value || 0
- })
- })
- }
- if (teamRes.value?.data) {
- rankData2.length = 0
- teamRes.value.data.slice(0, 10).forEach(item => {
- rankData2.push({
- name: item.teamLeaderName || item.name || '',
- value: item.count || item.value || 0
- })
- })
- }
- if (userRes.value?.data) {
- rankData3.length = 0
- userRes.value.data.slice(0, 10).forEach(item => {
- rankData3.push({
- name: item.userName || item.name || '',
- value: item.count || item.value || 0
- })
- })
- }
- if (genderRes.value?.data) {
- const genderData = genderRes.value.data.map(item => ({
- name: item.gender || item.name || '',
- value: item.percentage || item.value || 0
- }))
- setOption4(pieChartOption(genderData, ['#ec4899', '#3b82f6']))
- }
- if (certDetailRes.value?.data) {
- const certData = certDetailRes.value.data.map(item => ({
- name: item.certificateLevel || item.name || '',
- value: item.percentage || item.value || 0
- }))
- setOption5(pieChartOption(certData, ['#22c55e', '#3b82f6']))
- }
- if (certBaseRes.value?.data) {
- const baseData = certBaseRes.value.data.map(item => ({
- name: item.certificateLevel || item.name || '',
- value: item.percentage || item.value || 0
- }))
- setOption6(pieChartOption(baseData, ['#22c55e', '#3b82f6']))
- }
- if (locationRes.value?.data) {
- const colors = ['#8b5cf6', '#ec4899', '#f97316', '#22c55e', '#3b82f6', '#14b8a6', '#ef4444', '#fbbf24', '#6366f1', '#06b6d4', '#84cc16', '#e11d48']
- const locationData = locationRes.value.data.map(item => ({
- name: item.itemLocation || item.name || '',
- value: item.percentage || item.value || 0
- }))
- setOption7(donutChartOption(locationData, colors))
- }
- if (difficultyRes.value?.data) {
- let difficult = 0
- let simple = 0
- difficultyRes.value.data.forEach(item => {
- if (item.difficultyLevel === '难') {
- difficult = item.count || item.value || 0
- } else if (item.difficultyLevel === '简单') {
- simple = item.count || item.value || 0
- }
- })
- difficultValue.value = difficult
- simpleValue.value = simple
- }
- if (supervisorDistRes.value?.data) {
- const colors = ['#3b82f6', '#22c55e', '#f97316', '#ec4899', '#8b5cf6', '#14b8a6', '#ef4444', '#fbbf24', '#6366f1', '#06b6d4', '#84cc16', '#e11d48', '#a855f7', '#f59e0b']
- const supervisorData = supervisorDistRes.value.data.map(item => ({
- name: item.supervisorName || item.name || '',
- value: item.percentage || item.value || 0
- }))
- setOption10(donutChartOption(supervisorData, colors))
- }
- if (reasonRes.value?.data) {
- const colors = ['#3b82f6', '#22c55e', '#f97316', '#ec4899', '#8b5cf6', '#14b8a6', '#ef4444', '#fbbf24']
- const reasonData = reasonRes.value.data.map(item => ({
- name: item.missCheckReasonCategory || '',
- value: item.count || 0
- }))
- setOption11(pieChartOption(reasonData, colors))
- }
- if (yearsRes.value?.data) {
- const colors = ['#3b82f6', '#22c55e', '#f97316', '#ec4899', '#8b5cf6', '#14b8a6']
- const yearsData = yearsRes.value.data.map(item => ({
- name: item.operatingYears || '',
- value: item.count || 0
- }))
- setOption12(pieChartOption(yearsData, colors))
- }
- if (tenureRes.value?.data) {
- const xAxisData = tenureRes.value.data.map(item => item.brigadeName || '')
- const seriesData = [
- {
- name: '5年及以上',
- data: tenureRes.value.data.map(item => item.cntGe5Year || 0),
- color: '#3b82f6'
- },
- {
- name: '4年',
- data: tenureRes.value.data.map(item => item.cnt4Years || 0),
- color: '#22c55e'
- },
- {
- name: '3年',
- data: tenureRes.value.data.map(item => item.cnt3Years || 0),
- color: '#f97316'
- },
- {
- name: '2年',
- data: tenureRes.value.data.map(item => item.cnt2Years || 0),
- color: '#ec4899'
- },
- {
- name: '1年',
- data: tenureRes.value.data.map(item => item.cnt1Years || 0),
- color: '#8b5cf6'
- },
- {
- name: '不足1年',
- data: tenureRes.value.data.map(item => item.cntLt1Year || 0),
- color: '#14b8a6'
- }
- ]
- setOption13(multiSeriesBarChartOption(xAxisData, seriesData))
- }
- } catch (error) {
- console.error('获取数据失败:', error)
- } finally {
- loading.value = false
- }
- }
- watch(() => props.filterParams, () => {
- fetchData()
- }, { deep: true })
- const pieChartOption = (data, colors) => ({
- color: colors,
- tooltip: { trigger: 'item', formatter: '{b}: {c}' },
- legend: {
- data: data.map(item => item.name),
- top: '0%',
- textStyle: { fontSize: 10 }
- },
- series: [{
- type: 'pie',
- radius: ['40%', '65%'],
- center: ['50%', '55%'],
- data: data,
- label: {
- show: true,
- formatter: (params) => {
- const total = data.reduce((sum, item) => sum + item.value, 0)
- const percentage = ((params.value / total) * 100).toFixed(2)
- return `${params.name}\n${params.value}(${percentage}%)`
- },
- fontSize: 10
- }
- }]
- })
- const donutChartOption = (data, colors) => ({
- color: colors,
- tooltip: { trigger: 'item', formatter: '{b}: {c}' },
- legend: {
- data: data.map(item => item.name),
- top: '0%',
- textStyle: { fontSize: 10 }
- },
- series: [{
- type: 'pie',
- radius: ['30%', '55%'],
- center: ['50%', '55%'],
- data: data,
- label: {
- show: true,
- position: 'outside',
- formatter: (params) => {
- const total = data.reduce((sum, item) => sum + item.value, 0)
- const percentage = ((params.value / total) * 100).toFixed(2)
- return `${params.name} ${params.value}(${percentage}%)`
- },
- fontSize: 10
- }
- }]
- })
- const multiBarChartOption = (data, color) => ({
- grid: { left: '15%', top: '15%', right: '5%', bottom: '15%', containLabel: true },
- xAxis: { type: 'category', data: data.map(d => d.name), 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' } } },
- series: [{ type: 'bar', data: data.map(d => d.value), itemStyle: { color: color }, barWidth: 20 }]
- })
- const multiSeriesBarChartOption = (xAxisData, series) => ({
- grid: { left: '10%', top: '15%', right: '5%', bottom: '15%', containLabel: true },
- 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: 'bar',
- data: s.data,
- itemStyle: { color: s.color },
- barWidth: 15
- }))
- })
- onMounted(() => {
- fetchData()
- })
- </script>
- <style lang="less" scoped>
- .module-brigade-content {
- height: 100%;
- display: flex;
- flex-direction: column;
- gap: 10px;
- overflow-y: auto;
- }
- .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: 280px;
- }
- .chart-title {
- font-size: 17px;
- color: black;
- margin-bottom: 5px;
- text-align: left;
- }
- .echarts {
- flex: 1;
- width: 100%;
- min-height: 0;
- }
- .number-display {
- flex: 1;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- }
- .number-value {
- font-size: 48px;
- font-weight: bold;
- color: #3b82f6;
- }
- .number-unit {
- font-size: 14px;
- color: #3b82f6;
- margin-top: 5px;
- }
- </style>
|