ModuleBrigadeThree.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. <template>
  2. <module-container title="模块三 培训质控分析">
  3. <div class="module-brigade-content">
  4. <div class="chart-row">
  5. <div class="chart-item">
  6. <div class="chart-title">查堵-人员自测漏检次数</div>
  7. <div ref="chart1" class="echarts"></div>
  8. </div>
  9. <div class="chart-item">
  10. <div class="chart-title">查堵人员月考成绩</div>
  11. <div ref="chart2" class="echarts"></div>
  12. </div>
  13. </div>
  14. </div>
  15. </module-container>
  16. </template>
  17. <script setup>
  18. import { ref, onMounted, watch } from 'vue'
  19. import * as echarts from 'echarts'
  20. import ModuleContainer from './ModuleContainer.vue'
  21. import { useEcharts } from '@/hooks/chart.js'
  22. import { formatDateHy } from '@/utils/index.js'
  23. import {
  24. selfTestHasMissCheck,
  25. monthlyAssessment
  26. } from '@/api/blockingData/blockingDataScreen'
  27. const props = defineProps({
  28. filterParams: {
  29. type: Object,
  30. default: () => ({})
  31. }
  32. })
  33. const loading = ref(false)
  34. const processFilterParams = (params) => {
  35. const { dateRange, ...rest } = params
  36. const processed = { ...rest }
  37. if (dateRange && Array.isArray(dateRange) && dateRange.length === 2) {
  38. processed.startTime = formatDateHy(dateRange[0])
  39. processed.endTime = formatDateHy(dateRange[1])
  40. }
  41. if (processed.brigadeId == 'all') {
  42. delete processed.brigadeId
  43. }
  44. if (processed.terminalId == 'all') {
  45. delete processed.terminalId
  46. }
  47. return processed
  48. }
  49. const chart1 = ref(null)
  50. const chart2 = ref(null)
  51. const { setOption: setOption1 } = useEcharts(chart1)
  52. const { setOption: setOption2 } = useEcharts(chart2)
  53. const fetchData = async () => {
  54. loading.value = true
  55. try {
  56. const processedParams = processFilterParams(props.filterParams)
  57. const [missCheckRes, assessmentRes] = await Promise.all([
  58. selfTestHasMissCheck(processedParams),
  59. monthlyAssessment(processedParams)
  60. ])
  61. if (missCheckRes.data) {
  62. const colors = ['#22c55e', '#3b82f6', '#f97316']
  63. const missCheckData = missCheckRes.data.map(item => ({
  64. name: item.name || item.level || '未知',
  65. value: item.total || item.count || 0
  66. }))
  67. setOption1(ringChartOption(missCheckData, colors))
  68. }
  69. if (assessmentRes.data) {
  70. const colors = ['#22c55e', '#3b82f6', '#fbbf24', '#ef4444']
  71. const assessmentData = assessmentRes.data.map(item => ({
  72. name: item.level || item.name || '',
  73. value: item.total || 0
  74. }))
  75. setOption2(ringChartOption(assessmentData, colors))
  76. }
  77. } catch (error) {
  78. console.error('获取数据失败:', error)
  79. } finally {
  80. loading.value = false
  81. }
  82. }
  83. watch(() => props.filterParams, () => {
  84. fetchData()
  85. }, { deep: true })
  86. const ringChartOption = (data, colors) => ({
  87. color: colors,
  88. tooltip: { trigger: 'item', formatter: '{b}: {c}' },
  89. legend: {
  90. data: data.map(item => item.name),
  91. top: '0%',
  92. textStyle: { fontSize: 10 }
  93. },
  94. series: [{
  95. type: 'pie',
  96. radius: ['35%', '45%'],
  97. center: ['50%', '55%'],
  98. data: data,
  99. label: {
  100. show: true,
  101. formatter: (params) => {
  102. const total = data.reduce((sum, item) => sum + item.value, 0)
  103. const percentage = ((params.value / total) * 100).toFixed(2)
  104. return `${params.name}\n${params.value}(${percentage}%)`
  105. },
  106. fontSize: 10
  107. }
  108. }]
  109. })
  110. onMounted(() => {
  111. fetchData()
  112. })
  113. </script>
  114. <style lang="less" scoped>
  115. .module-brigade-content {
  116. height: 100%;
  117. display: flex;
  118. flex-direction: column;
  119. gap: 10px;
  120. overflow-y: auto;
  121. }
  122. .chart-row {
  123. display: flex;
  124. gap: 10px;
  125. flex-shrink: 0;
  126. }
  127. .chart-item {
  128. flex: 1;
  129. background: #fff;
  130. border-radius: 6px;
  131. padding: 10px;
  132. border: 1px solid #eee;
  133. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  134. display: flex;
  135. flex-direction: column;
  136. min-height: 400px;
  137. }
  138. .chart-title {
  139. font-size: 17px;
  140. color: black;
  141. margin-bottom: 5px;
  142. text-align: left;
  143. }
  144. .echarts {
  145. flex: 1;
  146. width: 100%;
  147. min-height: 0;
  148. }
  149. </style>