Pārlūkot izejas kodu

feat(绩效分析): 添加大队分析面板并重构布局为双列显示

- 新增大队分析面板,包含折线图和明细表格
- 重构原有科室分析面板布局,改为与大队面板并排显示
- 添加大队相关数据请求、处理和展示逻辑
- 更新图表销毁逻辑以包含大队图表
huoyi 1 mēnesi atpakaļ
vecāks
revīzija
41f04ac2db
1 mainītis faili ar 209 papildinājumiem un 20 dzēšanām
  1. 209 20
      src/views/assistant/components/performanceAnalysis.vue

+ 209 - 20
src/views/assistant/components/performanceAnalysis.vue

@@ -63,26 +63,52 @@
63
 
63
 
64
     <!-- 绩效分析内容区域 -->
64
     <!-- 绩效分析内容区域 -->
65
     <div class="analysis-content">
65
     <div class="analysis-content">
66
-      <!-- 科室分析 - 单行面板 -->
67
-      <div class="panel-item full-width" :class="{ 'loading-overlay': departmentLoading }">
68
-        <div class="panel-header">
69
-          <h3>科室</h3>
70
-        </div>
71
-        <!-- 折线图 -->
72
-        <div class="chart-container">
73
-          <div ref="departmentChartRef" class="echarts-chart"></div>
74
-        </div>
75
-        <div class="panel-header">
76
-          <h3>明细</h3>
77
-          <span>得分(环比)</span>
66
+      <!-- 大队和科室分析 - 双列面板 -->
67
+      <div class="two-panel-layout">
68
+        <!-- 大队分析 -->
69
+        <div class="panel-item" :class="{ 'loading-overlay': brigadeLoading }">
70
+          <div class="panel-header">
71
+            <h3>大队</h3>
72
+          </div>
73
+          <!-- 折线图 -->
74
+          <div class="chart-container">
75
+            <div ref="brigadeChartRef" class="echarts-chart"></div>
76
+          </div>
77
+          <div class="panel-header">
78
+            <h3>明细</h3>
79
+            <span>得分(环比)</span>
80
+          </div>
81
+          <!-- 明细表格 -->
82
+          <div class="table-container">
83
+            <el-table :data="brigadeTableData" style="width: 100%" size="small">
84
+              <el-table-column prop="time" label="" width="150" />
85
+              <el-table-column v-for="column in brigadeTableColumn" :key="column.column" :prop="column.column"
86
+                :label="column.name" />
87
+            </el-table>
88
+          </div>
78
         </div>
89
         </div>
79
-        <!-- 明细表格 -->
80
-        <div class="table-container">
81
-          <el-table :data="departmentTableData" style="width: 100%" size="small">
82
-            <el-table-column prop="time" label="" width="150" />
83
-            <el-table-column v-for="column in departmentTableColumn" :key="column.column" :prop="column.column"
84
-              :label="column.name" />
85
-          </el-table>
90
+
91
+        <!-- 科室分析 -->
92
+        <div class="panel-item" :class="{ 'loading-overlay': departmentLoading }">
93
+          <div class="panel-header">
94
+            <h3>科室</h3>
95
+          </div>
96
+          <!-- 折线图 -->
97
+          <div class="chart-container">
98
+            <div ref="departmentChartRef" class="echarts-chart"></div>
99
+          </div>
100
+          <div class="panel-header">
101
+            <h3>明细</h3>
102
+            <span>得分(环比)</span>
103
+          </div>
104
+          <!-- 明细表格 -->
105
+          <div class="table-container">
106
+            <el-table :data="departmentTableData" style="width: 100%" size="small">
107
+              <el-table-column prop="time" label="" width="150" />
108
+              <el-table-column v-for="column in departmentTableColumn" :key="column.column" :prop="column.column"
109
+                :label="column.name" />
110
+            </el-table>
111
+          </div>
86
         </div>
112
         </div>
87
       </div>
113
       </div>
88
 
114
 
@@ -212,12 +238,15 @@ const personalSortOrder = ref('desc') // desc: 倒序, asc: 正序
212
 
238
 
213
 // 加载状态
239
 // 加载状态
214
 const loading = ref(false)
240
 const loading = ref(false)
241
+const brigadeLoading = ref(false)
215
 const departmentLoading = ref(false)
242
 const departmentLoading = ref(false)
216
 const teamLoading = ref(false)
243
 const teamLoading = ref(false)
217
 const personalLoading = ref(false)
244
 const personalLoading = ref(false)
218
 
245
 
219
 // 真实数据
246
 // 真实数据
220
 const realData = ref({
247
 const realData = ref({
248
+  brigade: [],
249
+  brigadeList: [],
221
   team: [],
250
   team: [],
222
   teamList: [],
251
   teamList: [],
223
   personal: [],
252
   personal: [],
@@ -322,11 +351,13 @@ const personalTabOptions = [
322
 ]
351
 ]
323
 
352
 
324
 // 图表容器引用
353
 // 图表容器引用
354
+const brigadeChartRef = ref(null)
325
 const departmentChartRef = ref(null)
355
 const departmentChartRef = ref(null)
326
 const teamChartRef = ref(null)
356
 const teamChartRef = ref(null)
327
 const personalChartRef = ref(null)
357
 const personalChartRef = ref(null)
328
 
358
 
329
 // 图表实例
359
 // 图表实例
360
+const { setOption: setBrigadeOption, dispose: disposeBrigade } = useEcharts(brigadeChartRef)
330
 const { setOption: setDepartmentOption, dispose: disposeDepartment } = useEcharts(departmentChartRef)
361
 const { setOption: setDepartmentOption, dispose: disposeDepartment } = useEcharts(departmentChartRef)
331
 const { setOption: setteamListOption, dispose: disposeTeam } = useEcharts(teamChartRef)
362
 const { setOption: setteamListOption, dispose: disposeTeam } = useEcharts(teamChartRef)
332
 const { setOption: setPersonalOption, dispose: disposePersonal } = useEcharts(personalChartRef)
363
 const { setOption: setPersonalOption, dispose: disposePersonal } = useEcharts(personalChartRef)
@@ -340,6 +371,10 @@ watch([() => queryForm.value.dateRangeType, () => queryForm.value.startMonth, ()
340
 })
371
 })
341
 
372
 
342
 // 表格数据
373
 // 表格数据
374
+const brigadeTableData = ref([
375
+
376
+])
377
+
343
 const departmentTableData = ref([
378
 const departmentTableData = ref([
344
 
379
 
345
 ])
380
 ])
@@ -352,6 +387,35 @@ const personalTableData = ref([
352
 
387
 
353
 ])
388
 ])
354
 
389
 
390
+// 大队图表配置
391
+const brigadeChartOptions = {
392
+  tooltip: {
393
+    trigger: 'axis'
394
+  },
395
+  legend: {
396
+    show: true,
397
+    icon: 'rect',
398
+  },
399
+  grid: {
400
+    left: '3%',
401
+    right: '4%',
402
+    bottom: '3%',
403
+    containLabel: true
404
+  },
405
+  xAxis: {
406
+    type: 'category',
407
+    boundaryGap: false,
408
+    data: []
409
+  },
410
+  yAxis: {
411
+    type: 'value',
412
+    name: '得分'
413
+  },
414
+  series: [
415
+
416
+  ]
417
+}
418
+
355
 // 科室图表配置
419
 // 科室图表配置
356
 const departmentChartOptions = {
420
 const departmentChartOptions = {
357
   tooltip: {
421
   tooltip: {
@@ -651,9 +715,82 @@ const buildPersonalApiParams = () => {
651
   return params
715
   return params
652
 }
716
 }
653
 
717
 
718
+// 构建大队API参数 (dimension = 4,没有dimensionType参数)
719
+const buildBrigadeApiParams = () => {
720
+  const params = {
721
+    resultField: performanceDimensionMap[queryForm.value.performanceDimension],
722
+    sortOrder: 1
723
+  }
724
+
725
+  // 处理时间范围
726
+  if (queryForm.value.dateRangeType === 'CUSTOM' && queryForm.value.startMonth && queryForm.value.endMonth) {
727
+    // 处理自定义月份范围选择
728
+    const startDate = new Date(queryForm.value.startMonth)
729
+    const endDate = new Date(queryForm.value.endMonth)
730
+
731
+    // 开始月的第一天
732
+    const startTime = new Date(startDate.getFullYear(), startDate.getMonth(), 1)
733
+    // 结束月的最后一天
734
+    const endTime = new Date(endDate.getFullYear(), endDate.getMonth() + 1, 0)
735
+
736
+    params.startTime = formatDate(startTime)
737
+    params.endTime = formatDate(endTime)
738
+  } else {
739
+    // 处理预设时间范围
740
+    const endDate = new Date()
741
+    endDate.setDate(0) // 设置为上个月最后一天,不包括本月
742
+    let startDate = new Date(endDate)
743
+
744
+    if (queryForm.value.dateRangeType === 'THREE_MONTHS') {
745
+      startDate.setMonth(endDate.getMonth() - 2)
746
+    } else if (queryForm.value.dateRangeType === 'SIX_MONTHS') {
747
+      startDate.setMonth(endDate.getMonth() - 5)
748
+    }
749
+
750
+    params.startTime = formatDate(startDate)
751
+    params.endTime = formatDate(endDate)
752
+  }
753
+
754
+  return params
755
+}
756
+
654
 
757
 
655
 
758
 
656
 // 数据请求函数
759
 // 数据请求函数
760
+// 获取大队数据
761
+const fetchBrigadeData = async () => {
762
+  brigadeLoading.value = true
763
+  try {
764
+    // 大队数据 (dimension = 4,没有dimensionType参数)
765
+    const brigadeChartParams = buildBrigadeApiParams()
766
+    brigadeChartParams.dimension = 4
767
+    const brigadeTableParams = buildBrigadeApiParams()
768
+    brigadeTableParams.dimension = 4
769
+
770
+    // 并行请求大队图表和表格数据
771
+    const [brigadeChartResponse, brigadeTableResponse] = await Promise.all([
772
+      getCalculateByTime(brigadeChartParams),
773
+      getCalculateByTimeList(brigadeTableParams)
774
+    ])
775
+
776
+    // 设置大队数据
777
+    if (brigadeChartResponse.data) {
778
+      realData.value.brigade = brigadeChartResponse.data || []
779
+    }
780
+    if (brigadeTableResponse.data) {
781
+      realData.value.brigadeList = brigadeTableResponse.data || []
782
+    }
783
+
784
+    // 请求完成后直接更新大队数据
785
+    updateBrigadeData()
786
+  } catch (error) {
787
+    console.error('大队数据请求失败:', error)
788
+    ElMessage.error('大队数据加载失败')
789
+  } finally {
790
+    brigadeLoading.value = false
791
+  }
792
+}
793
+
657
 // 获取科室数据
794
 // 获取科室数据
658
 const fetchDepartmentData = async () => {
795
 const fetchDepartmentData = async () => {
659
   departmentLoading.value = true
796
   departmentLoading.value = true
@@ -760,9 +897,10 @@ const fetchData = async () => {
760
   loading.value = true
897
   loading.value = true
761
 
898
 
762
   try {
899
   try {
763
-    // 并行请求科室、班组和个人数据
900
+    // 并行请求大队、科室、班组和个人数据
764
     // 数据更新已经在各个数据请求函数中完成
901
     // 数据更新已经在各个数据请求函数中完成
765
     await Promise.all([
902
     await Promise.all([
903
+      fetchBrigadeData(),
766
       fetchDepartmentData(),
904
       fetchDepartmentData(),
767
       fetchTeamData(),
905
       fetchTeamData(),
768
       fetchPersonalData()
906
       fetchPersonalData()
@@ -777,6 +915,8 @@ const fetchData = async () => {
777
 }
915
 }
778
 
916
 
779
 
917
 
918
+// 表格列定义
919
+const brigadeTableColumn = ref([])
780
 const departmentTableColumn = ref([])
920
 const departmentTableColumn = ref([])
781
 const teamTableColumn = ref([])
921
 const teamTableColumn = ref([])
782
 const personalTableColumn = ref([])
922
 const personalTableColumn = ref([])
@@ -809,6 +949,54 @@ const getColumnData = (arr, column) => {
809
   return res
949
   return res
810
 }
950
 }
811
 
951
 
952
+// 更新大队数据(包含图表和表格)
953
+const updateBrigadeData = () => {
954
+  // 更新大队图表
955
+  if (realData.value.brigade) {
956
+    const xAxisData = realData.value.brigade.timeAxis;
957
+    let seriesData = [];
958
+    let timeDataList = realData.value.brigade.timeDataList;
959
+    let arr = {};
960
+    timeDataList.forEach(item => {
961
+      item.dataItems.forEach(ele => {
962
+        if (!arr[ele.name]) {
963
+          arr[ele.name] = [];
964
+        }
965
+        arr[ele.name].push(ele.value);
966
+      })
967
+    })
968
+    Object.keys(arr).forEach((item, index) => {
969
+      seriesData.push({
970
+        name: item,
971
+        type: 'line',
972
+        symbol: 'circle',
973
+        symbolSize: 6,
974
+        data: arr[item],
975
+        itemStyle: { color: colorList[index % colorList.length] },
976
+        lineStyle: { color: colorList[index % colorList.length], width: 2 }
977
+      })
978
+    })
979
+
980
+    setBrigadeOption({
981
+      ...brigadeChartOptions,
982
+      xAxis: { ...brigadeChartOptions.xAxis, data: xAxisData },
983
+      series: seriesData
984
+    })
985
+  }
986
+
987
+  // 更新大队表格数据
988
+  if (realData.value.brigadeList) {
989
+    const timeDataList = realData.value.brigadeList.timeDataList;
990
+    brigadeTableColumn.value = []
991
+    if (timeDataList && timeDataList.length > 0 && timeDataList[0].dataItems) {
992
+      timeDataList[0].dataItems.forEach((item, index) => {
993
+        brigadeTableColumn.value.push({ name: item.name, column: `brigade${index}` })
994
+      })
995
+    }
996
+    brigadeTableData.value = getColumnData(timeDataList, brigadeTableColumn.value)
997
+  }
998
+}
999
+
812
 // 更新科室数据(包含图表和表格)
1000
 // 更新科室数据(包含图表和表格)
813
 const updateDepartmentData = () => {
1001
 const updateDepartmentData = () => {
814
   // 更新科室图表
1002
   // 更新科室图表
@@ -1144,6 +1332,7 @@ onMounted(() => {
1144
 
1332
 
1145
 // 组件卸载时销毁图表
1333
 // 组件卸载时销毁图表
1146
 onUnmounted(() => {
1334
 onUnmounted(() => {
1335
+  disposeBrigade()
1147
   disposeDepartment()
1336
   disposeDepartment()
1148
   disposeTeam()
1337
   disposeTeam()
1149
   disposePersonal()
1338
   disposePersonal()