Explorar o código

feat(performance/monthlyAssessSum): 新增大队维度的考核数据统计与图表展示

1.  新增获取部门列表接口,拉取大队数据并按大队维度拆分接口请求
2.  重构遍历数据逻辑,支持多大队的考核统计、待改进和不称职人数分布图表
3.  更新汇总表格标题,动态显示对应大队的统计信息
4.  优化职能部门的图表渲染逻辑,修复数据匹配问题
5.  调整样式代码格式,统一换行和缩进规范
huoyi hai 1 mes
pai
achega
ef7aacaaf1
Modificáronse 1 ficheiros con 137 adicións e 93 borrados
  1. 137 93
      src/views/performanceManage/monthlyAssessSum/index.vue

+ 137 - 93
src/views/performanceManage/monthlyAssessSum/index.vue

@@ -6,16 +6,16 @@
6 6
         <div class="filter-container">
7 7
           <el-form :model="queryParams" ref="queryFormRef" :inline="true" class="search-form">
8 8
             <el-form-item label="考核月份" prop="assessmentMonth">
9
-              <el-date-picker v-model="queryParams.assessmentMonth" type="month" placeholder="请选择考核月份" 
9
+              <el-date-picker v-model="queryParams.assessmentMonth" type="month" placeholder="请选择考核月份"
10 10
                 value-format="YYYY-MM" style="width: 200px" />
11 11
             </el-form-item>
12
-            
12
+
13 13
             <el-form-item>
14 14
               <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
15 15
               <el-button icon="Refresh" @click="resetQuery">重置</el-button>
16 16
             </el-form-item>
17 17
           </el-form>
18
-          
18
+
19 19
           <div class="export-button">
20 20
             <el-button type="warning" icon="Document" @click="handleExport">导出文档</el-button>
21 21
           </div>
@@ -26,7 +26,7 @@
26 26
     <!-- 非干部月度考核分数汇总 -->
27 27
     <div class="main-section">
28 28
       <h2 class="section-title">非干部月度考核分数汇总</h2>
29
-      
29
+
30 30
       <!-- 第一行:两个区块 -->
31 31
       <div class="chart-row">
32 32
         <!-- 整体分值分布柱状图 -->
@@ -34,7 +34,7 @@
34 34
           <div class="chart-header">整体分值分布柱状图</div>
35 35
           <div ref="overallBarChart" class="chart-container"></div>
36 36
         </el-card>
37
-        
37
+
38 38
         <!-- 参与人数占比饼图 -->
39 39
         <el-card class="chart-card">
40 40
           <div class="chart-header">参与人数占比饼图</div>
@@ -49,14 +49,15 @@
49 49
           <div class="chart-header">各部门分值分布对比图</div>
50 50
           <div ref="departmentComparisonChart" class="chart-container"></div>
51 51
         </el-card>
52
-        
52
+
53 53
         <!-- 汇总表 -->
54 54
         <el-card class="chart-card">
55 55
           <div class="chart-header">汇总表</div>
56 56
           <el-table :data="summaryTableData" border style="width: 100%; margin-top: 10px;">
57 57
             <el-table-column prop="rangeLabel" label="区间" align="center" min-width="100" />
58 58
             <el-table-column prop="totalCount" :label="summaryTableTitle" align="center" min-width="150" />
59
-            <el-table-column v-for="col in summaryTableColumns" :key="col.deptId" :prop="'team' + col.deptId" :label="col.deptName" align="center" min-width="100" />
59
+            <el-table-column v-for="col in summaryTableColumns" :key="col.deptId" :prop="'team' + col.deptId"
60
+              :label="col.deptName" align="center" min-width="100" />
60 61
           </el-table>
61 62
         </el-card>
62 63
       </div>
@@ -65,7 +66,7 @@
65 66
     <!-- 非干部月度考核分类结果汇总 -->
66 67
     <div class="main-section">
67 68
       <h2 class="section-title">非干部月度考核分类结果汇总</h2>
68
-      
69
+
69 70
       <!-- 汇总统计表格 -->
70 71
       <el-card class="summary-table-card">
71 72
         <div class="chart-header">汇总统计</div>
@@ -91,7 +92,7 @@
91 92
             <div ref="brigadePieChart2" class="pie-chart"></div>
92 93
           </div>
93 94
         </el-card>
94
-        
95
+
95 96
         <!-- 岗位分布统计图 -->
96 97
         <el-card class="chart-card">
97 98
           <div class="chart-header">岗位分布统计图</div>
@@ -122,15 +123,15 @@
122 123
               <el-table-column prop="actualIncompetentCount" label="实际不称职人数" align="center" min-width="140" />
123 124
             </el-table>
124 125
           </div>
125
-          
126
+
126 127
           <!-- 右边两个饼状图 -->
127 128
           <div class="chart-section">
128 129
             <div class="pie-chart-container">
129
-              <div class="pie-chart-title">考核结果分布</div>
130
+              <div class="pie-chart-title">{{ item.title }}实际待改进人数分布</div>
130 131
               <div :ref="el => setTraversalChartRef(el, `pieChart1_${index}`)" class="pie-chart"></div>
131 132
             </div>
132 133
             <div class="pie-chart-container">
133
-              <div class="pie-chart-title">改进情况分布</div>
134
+              <div class="pie-chart-title">{{ item.title }}实际不称职人数分布</div>
134 135
               <div :ref="el => setTraversalChartRef(el, `pieChart2_${index}`)" class="pie-chart"></div>
135 136
             </div>
136 137
           </div>
@@ -158,7 +159,7 @@
158 159
               <el-table-column prop="actualUnqualified" label="实际不称职人数" align="center" min-width="140" />
159 160
             </el-table>
160 161
           </div>
161
-          
162
+
162 163
           <!-- 右边柱状图和饼状图 -->
163 164
           <div class="chart-section">
164 165
             <div class="bar-chart-container">
@@ -198,6 +199,7 @@ import {
198 199
   getFunctionalDeptPersonnelDistribution,
199 200
   getFunctionalDeptDistributionPie
200 201
 } from '@/api/performance/monthlyAssessSum.js'
202
+import { listDept } from '@/api/system/dept.js'
201 203
 
202 204
 // 响应式数据
203 205
 const loading = ref(false)
@@ -240,11 +242,11 @@ const classificationTableData = ref([
240 242
 
241 243
 // 遍历数据
242 244
 const traversalData1 = ref([
243
-  
245
+
244 246
 ])
245 247
 
246 248
 const traversalData2 = ref([
247
-  
249
+
248 250
 ])
249 251
 
250 252
 // 图表实例
@@ -478,6 +480,11 @@ const handleResize = () => {
478 480
 const getList = async () => {
479 481
   loading.value = true
480 482
   try {
483
+    // 获取大队列表
484
+    const brigadeListRes = await listDept()
485
+    const brigadeList = (brigadeListRes.data || []).filter(item => item.deptType === 'BRIGADE' && item.isFunctionalDept == 0)
486
+
487
+    // 其他接口调用
481 488
     const results = await Promise.allSettled([
482 489
       getScoreDistribution(queryParams),
483 490
       getDeptParticipation(queryParams),
@@ -487,15 +494,50 @@ const getList = async () => {
487 494
       getActualIncompetentDistribution(queryParams),
488 495
       getAssessmentTeamImprovementDistribution(queryParams),
489 496
       getAssessmentTeamIncompetentDistribution(queryParams),
490
-      getDeptAssessmentTeamStatistics(queryParams),
491
-      getBrigadeImprovementDistribution(queryParams),
492
-      getBrigadeIncompetentDistribution(queryParams),
493 497
       getFunctionalDeptSummary(queryParams),
494 498
       getFunctionalDeptPersonnelDistribution(queryParams),
495 499
       getFunctionalDeptDistributionPie(queryParams)
496 500
     ])
497 501
 
498
-    const [scoreDistRes, deptPartRes, deptScoreRes, assessmentSumRes, actualImproveRes, actualIncompRes, teamImproveRes, teamIncompRes, deptTeamStatRes, brigadeImproveRes, brigadeIncompRes, functionalDeptRes, functionalDeptPersonnelRes, functionalDeptPieRes] = results
502
+    const [scoreDistRes, deptPartRes, deptScoreRes, assessmentSumRes, actualImproveRes, actualIncompRes, teamImproveRes, teamIncompRes, functionalDeptRes, functionalDeptPersonnelRes, functionalDeptPieRes] = results
503
+
504
+    // 对每个大队调用三个接口
505
+    const brigadeDataPromises = brigadeList.map(async (brigade) => {
506
+      const brigadeParams = { ...queryParams, deptId: brigade.deptId }
507
+      const brigadeResults = await Promise.allSettled([
508
+        getDeptAssessmentTeamStatistics(brigadeParams),
509
+        getBrigadeImprovementDistribution(brigadeParams),
510
+        getBrigadeIncompetentDistribution(brigadeParams)
511
+      ])
512
+      
513
+      const [deptTeamStatRes, brigadeImproveRes, brigadeIncompRes] = brigadeResults
514
+      
515
+      const statisticsData = deptTeamStatRes.status === 'fulfilled' ? (deptTeamStatRes.value.data || []) : []
516
+      const brigadeImproveData = brigadeImproveRes.status === 'fulfilled' ? (brigadeImproveRes.value.data || []) : []
517
+      const brigadeIncompData = brigadeIncompRes.status === 'fulfilled' ? (brigadeIncompRes.value.data || []) : []
518
+      
519
+      // 解析表格数据
520
+      let tableData = []
521
+      if (statisticsData.length > 0 && statisticsData[0].assessmentTeams) {
522
+        tableData = statisticsData[0].assessmentTeams
523
+      }
524
+      
525
+      // 计算总计用于标题
526
+      const totalImprovement = tableData.reduce((sum, item) => sum + (item.actualImprovementCount || 0), 0)
527
+      const totalIncompetent = tableData.reduce((sum, item) => sum + (item.actualIncompetentCount || 0), 0)
528
+      
529
+      return {
530
+        title: brigade.deptName,
531
+        deptId: brigade.deptId,
532
+        tableData,
533
+        improveData: brigadeImproveData,
534
+        incompData: brigadeIncompData,
535
+        totalImprovement,
536
+        totalIncompetent
537
+      }
538
+    })
539
+
540
+    traversalData1.value = await Promise.all(brigadeDataPromises)
499 541
 
500 542
     if (scoreDistRes.status === 'fulfilled' && scoreDistRes.value.data) {
501 543
       const data = scoreDistRes.value.data
@@ -521,7 +563,7 @@ const getList = async () => {
521 563
       if (departmentComparisonChartInstance && Array.isArray(data)) {
522 564
         const xAxisData = data.map(item => item.rangeLabel)
523 565
         const firstDeptCounts = data.length > 0 ? data[0].deptCounts : []
524
-        
566
+
525 567
         const series = firstDeptCounts.map(dept => ({
526 568
           name: dept.deptName,
527 569
           type: 'bar',
@@ -594,85 +636,82 @@ const getList = async () => {
594 636
       }
595 637
     }
596 638
 
597
-    if (deptTeamStatRes.status === 'fulfilled' && deptTeamStatRes.value.data) {
598
-      const statistics = deptTeamStatRes.value.data || []
599
-      const brigadeImproveData = brigadeImproveRes.status === 'fulfilled' ? (brigadeImproveRes.value.data || []) : []
600
-      const brigadeIncompData = brigadeIncompRes.status === 'fulfilled' ? (brigadeIncompRes.value.data || []) : []
601
-      
602
-      const brigadeMap = {}
603
-      brigadeImproveData.forEach(item => {
604
-        if (!brigadeMap[item.deptName]) {
605
-          brigadeMap[item.deptName] = { brigade: item.deptName, assessmentGroup: '', improvementCount: 0, exemption1: 0, unqualifiedCount: 0, exemption2: 0 }
639
+    // 初始化traversalData1对应的图表
640
+    nextTick(() => {
641
+      traversalData1.value.forEach((item, index) => {
642
+        const chartKey1 = `pieChart1_${index}`
643
+        const chartKey2 = `pieChart2_${index}`
644
+
645
+        if (traversalChartsRefs.value[chartKey1] && item.improveData) {
646
+          const pieChart1 = echarts.init(traversalChartsRefs.value[chartKey1])
647
+          pieChart1.setOption({
648
+            tooltip: { trigger: 'item' },
649
+            series: [{
650
+              type: 'pie',
651
+              radius: '70%',
652
+              data: item.improveData.map(d => ({ name: d.assessmentTeam, value: d.count }))
653
+            }]
654
+          })
606 655
         }
607
-        brigadeMap[item.deptName].improvementCount += item.count
608
-        brigadeMap[item.deptName].exemption1 += (item.exemptedCount || 0)
609
-      })
610
-      brigadeIncompData.forEach(item => {
611
-        if (!brigadeMap[item.deptName]) {
612
-          brigadeMap[item.deptName] = { brigade: item.deptName, assessmentGroup: '', improvementCount: 0, exemption1: 0, unqualifiedCount: 0, exemption2: 0 }
656
+
657
+        if (traversalChartsRefs.value[chartKey2] && item.incompData) {
658
+          const pieChart2 = echarts.init(traversalChartsRefs.value[chartKey2])
659
+          pieChart2.setOption({
660
+            tooltip: { trigger: 'item' },
661
+            series: [{
662
+              type: 'pie',
663
+              radius: '70%',
664
+              data: item.incompData.map(d => ({ name: d.assessmentTeam, value: d.count }))
665
+            }]
666
+          })
613 667
         }
614
-        brigadeMap[item.deptName].unqualifiedCount += item.count
615
-        brigadeMap[item.deptName].exemption2 += (item.exemptedCount || 0)
616 668
       })
617
-      
618
-      const groupCount = statistics.reduce((sum, item) => sum + (item.count || 0), 0)
619
-      const calculatedImprovement = statistics.reduce((sum, item) => sum + (item.calculatedImprovementCount || 0), 0)
620
-      const actualImprovement = statistics.reduce((sum, item) => sum + (item.actualImprovementCount || 0), 0)
621
-      const actualUnqualified = statistics.reduce((sum, item) => sum + (item.actualIncompetentCount || 0), 0)
622
-      
623
-      const tableData = Object.values(brigadeMap).map(item => ({
624
-        ...item,
625
-        groupCount,
626
-        calculatedImprovement,
627
-        actualImprovement,
628
-        actualUnqualified
629
-      }))
630
-      
631
-      if (tableData.length > 0) {
632
-        traversalData1.value = [{ title: '大队考核统计', tableData }]
633
-      }
634
-    }
669
+    })
635 670
 
636 671
     if (functionalDeptRes.status === 'fulfilled' && functionalDeptRes.value.data) {
637 672
       const summaryData = functionalDeptRes.value.data || []
638
-      
639
-      traversalData2.value = summaryData.map(item => ({
640
-        title: item.deptName,
641
-        deptId: item.deptId,
642
-        tableData: item.assessmentTeams || []
643
-      }))
644
-      
645 673
       const personnelData = functionalDeptPersonnelRes.status === 'fulfilled' ? (functionalDeptPersonnelRes.value.data || []) : []
646 674
       const pieData = functionalDeptPieRes.status === 'fulfilled' ? (functionalDeptPieRes.value.data || []) : []
647
-      
648
-      personnelData.forEach(item => {
649
-        const deptItem = traversalData2.value.find(d => d.deptId === item.deptId)
650
-        if (deptItem) {
651
-          deptItem.personnelCount = item.assessmentTeamCount || 0
652
-          deptItem.actualImprovement = item.actualImprovementCount || 0
653
-          deptItem.actualUnqualified = item.actualIncompetentCount || 0
675
+
676
+      traversalData2.value = summaryData.map(item => {
677
+        // 匹配人员数据
678
+        const personnelItem = personnelData.find(p => p.deptId === item.deptId)
679
+        // 匹配饼图数据
680
+        const deptPieData = pieData.filter(p => p.deptId === item.deptId)
681
+
682
+        return {
683
+          title: item.deptName,
684
+          deptId: item.deptId,
685
+          tableData: item.assessmentTeams || [],
686
+          barData: personnelItem ? [
687
+            { name: '考核组人数', value: personnelItem.assessmentTeamCount || 0 },
688
+            { name: '实际待改进人数', value: personnelItem.actualImprovementCount || 0 },
689
+            { name: '实际不称职人数', value: personnelItem.actualIncompetentCount || 0 }
690
+          ] : [],
691
+          pieData: deptPieData.map(p => ({ name: p.assessmentTeam, value: p.count }))
654 692
         }
655 693
       })
656
-      
694
+
657 695
       nextTick(() => {
658
-        pieData.forEach(pieItem => {
659
-          const deptIndex = traversalData2.value.findIndex(d => d.deptId === pieItem.deptId)
660
-          const chartKey = `barChart_${deptIndex}`
661
-          const pieChartKey = `pieChart3_${deptIndex}`
662
-          
663
-          const filteredData = pieData.filter(p => p.deptId === pieItem.deptId).map(p => ({ name: p.assessmentTeam, value: p.count }))
664
-          
665
-          if (traversalChartsRefs.value[chartKey]) {
696
+        traversalData2.value.forEach((item, index) => {
697
+          const chartKey = `barChart_${index}`
698
+          const pieChartKey = `pieChart3_${index}`
699
+
700
+          if (traversalChartsRefs.value[chartKey] && item.barData.length > 0) {
666 701
             const barChart = echarts.init(traversalChartsRefs.value[chartKey])
667 702
             barChart.setOption({
668
-              series: [{ data: filteredData }]
703
+              tooltip: { trigger: 'axis' },
704
+              xAxis: { type: 'category', data: item.barData.map(d => d.name) },
705
+              yAxis: { type: 'value' },
706
+              series: [{ type: 'bar', data: item.barData.map(d => d.value), itemStyle: { color: '#3b82f6' } }]
669 707
             })
670 708
           }
671
-          
672
-          if (traversalChartsRefs.value[pieChartKey]) {
709
+
710
+          if (traversalChartsRefs.value[pieChartKey] && item.pieData.length > 0) {
673 711
             const pieChart = echarts.init(traversalChartsRefs.value[pieChartKey])
674 712
             pieChart.setOption({
675
-              series: [{ data: filteredData }]
713
+              tooltip: { trigger: 'item' },
714
+              series: [{ type: 'pie', radius: '70%', data: item.pieData }]
676 715
             })
677 716
           }
678 717
         })
@@ -857,7 +896,8 @@ onUnmounted(() => {
857 896
   height: 400px;
858 897
 }
859 898
 
860
-.pie-chart-container, .bar-chart-container {
899
+.pie-chart-container,
900
+.bar-chart-container {
861 901
   display: flex;
862 902
   flex-direction: column;
863 903
   border: 1px solid #e4e7ed;
@@ -866,7 +906,8 @@ onUnmounted(() => {
866 906
   background-color: #fff;
867 907
 }
868 908
 
869
-.pie-chart-title, .chart-title {
909
+.pie-chart-title,
910
+.chart-title {
870 911
   font-size: 14px;
871 912
   font-weight: 600;
872 913
   color: #606266;
@@ -874,7 +915,8 @@ onUnmounted(() => {
874 915
   text-align: center;
875 916
 }
876 917
 
877
-.pie-chart, .bar-chart {
918
+.pie-chart,
919
+.bar-chart {
878 920
   flex: 1;
879 921
   min-height: 300px;
880 922
 }
@@ -884,22 +926,23 @@ onUnmounted(() => {
884 926
   .chart-row {
885 927
     grid-template-columns: 1fr;
886 928
   }
887
-  
929
+
888 930
   .pie-charts-container {
889 931
     grid-template-columns: 1fr;
890 932
   }
891
-  
933
+
892 934
   .traversal-content {
893 935
     grid-template-columns: 1fr;
894 936
     gap: 15px;
895 937
   }
896
-  
938
+
897 939
   .chart-section {
898 940
     grid-template-columns: 1fr;
899 941
     height: auto;
900 942
   }
901
-  
902
-  .pie-chart, .bar-chart {
943
+
944
+  .pie-chart,
945
+  .bar-chart {
903 946
     min-height: 250px;
904 947
   }
905 948
 }
@@ -908,12 +951,13 @@ onUnmounted(() => {
908 951
   .traversal-content {
909 952
     padding: 15px;
910 953
   }
911
-  
954
+
912 955
   .chart-section {
913 956
     gap: 10px;
914 957
   }
915
-  
916
-  .pie-chart-container, .bar-chart-container {
958
+
959
+  .pie-chart-container,
960
+  .bar-chart-container {
917 961
     padding: 8px;
918 962
   }
919 963
 }