From b375f09a33904abc093e874495b15a23e196ccaa Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期二, 20 一月 2026 16:42:10 +0800
Subject: [PATCH] 优化人员数据分析页面,对接接口

---
 src/views/personnelManagement/analytics/index.vue |  216 ++++++++++++++++++++++++++----------------------------
 1 files changed, 104 insertions(+), 112 deletions(-)

diff --git a/src/views/personnelManagement/analytics/index.vue b/src/views/personnelManagement/analytics/index.vue
index 091156d..086dad8 100644
--- a/src/views/personnelManagement/analytics/index.vue
+++ b/src/views/personnelManagement/analytics/index.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="app-container analytics-container">
+  <div class="app-container analytics-container" v-loading="loading">
 
     <!-- 鍏抽敭鎸囨爣鍗$墖 -->
     <el-row :gutter="20" class="metrics-cards">
@@ -11,7 +11,7 @@
                 <component :is="item.icon" />
               </el-icon>
             </div>
-              <div class="card-info">
+            <div class="card-info">
               <div class="card-number">
                 <el-skeleton-item v-if="loading" variant="text" style="width: 60px; height: 32px;" />
                 <span v-else>{{ item.value }}{{ item.unit }}</span>
@@ -64,21 +64,6 @@
 
     <!-- 绗簩琛屽浘琛� -->
     <el-row :gutter="20" class="charts-section">
-      <!-- 缂栧埗杈炬垚鐜� -->
-      <el-col :span="12">
-        <el-card class="chart-card">
-          <template #header>
-            <div class="card-header">
-              <span>缂栧埗杈炬垚鐜�</span>
-              <el-tag type="warning">鍚勯儴闂ㄥ姣�</el-tag>
-            </div>
-          </template>
-          <div class="chart-container">
-            <div ref="staffingChartRef" class="chart"></div>
-          </div>
-        </el-card>
-      </el-col>
-
       <!-- 鍛樺伐娴佸け鍘熷洜鍒嗘瀽 -->
       <el-col :span="12">
         <el-card class="chart-card">
@@ -98,19 +83,15 @@
 </template>
 
 <script setup>
-import { ref, reactive, onMounted, onUnmounted } from 'vue'
+import { ref, onMounted, onUnmounted, nextTick } from 'vue'
 import { ElMessage } from 'element-plus'
-import { 
-  Refresh, 
-  User, 
-  TrendCharts, 
-  DataAnalysis, 
-  PieChart,
-  ArrowUp,
-  ArrowDown
-} from '@element-plus/icons-vue'
 import * as echarts from 'echarts'
-import { staffOnJobListPage } from '@/api/personnelManagement/staffOnJob.js'
+import {listDept} from "@/api/system/dept.js";
+import {
+  findStaffAnalysisMonthlyTurnoverRateFor12Months,
+  findStaffLeaveReasonAnalysis,
+  findStaffAnalysisTotalStatistic
+} from "@/api/personnelManagement/staffAnalytics.js";
 
 // 鍝嶅簲寮忔暟鎹�
 const loading = ref(false)
@@ -151,14 +132,6 @@
     trend: 0
   },
   {
-    label: '缂栧埗杈炬垚鐜�',
-    value: 0,
-    unit: '%',
-    icon: 'DataAnalysis',
-    type: 'success',
-    trend: 0
-  },
-  {
     label: '鍦ㄨ亴鍛樺伐鏁�',
     value: 0,
     unit: '浜�',
@@ -171,16 +144,56 @@
 
 // 閮ㄩ棬鏁版嵁
 const departmentData = ref([])
+// 鍛樺伐娴佸け鍘熷洜鍒嗘瀽鏁版嵁
+const staffLeaveReasons = ref([])
+// 12涓湀鍛樺伐娴佸姩娴佸け鐜囧垎鏋愭暟鎹�
+const turnoverRateStatistics = ref([])
 
-// 鑾峰彇鍦ㄨ亴鍛樺伐鏁�
-const getStaffCount = async () => {
+// 鑾峰彇閮ㄩ棬鏁版嵁
+const getDepartmentData = async () => {
   try {
-    const res = await staffOnJobListPage({ staffState: 1, current: 1, size: 1 })
+    const res = await listDept()
     if (res && res.data) {
-      keyMetrics.value[3].value = res.data.total || 0
+      departmentData.value = res.data
     }
   } catch (error) {
-    console.error('鑾峰彇鍦ㄨ亴鍛樺伐鏁板け璐�:', error)
+    console.error('鑾峰彇閮ㄩ棬鏁版嵁澶辫触:', error)
+  }
+}
+
+const getStaffLeaveReasonAnalysis = async () => {
+  try {
+    const res = await findStaffLeaveReasonAnalysis()
+    if (res && res.data) {
+      staffLeaveReasons.value = res.data || []
+    }
+  } catch (error) {
+    console.error('鑾峰彇鍛樺伐娴佸け鍘熷洜鍒嗘瀽澶辫触:', error)
+  }
+}
+
+// 淇敼涓鸿繑鍥濸romise鐨勫紓姝ュ嚱鏁�
+const getMonthlyTurnoverRateFor12Months = async () => {
+  try {
+    const res = await findStaffAnalysisMonthlyTurnoverRateFor12Months()
+    if (res && res.data) {
+      turnoverRateStatistics.value = res.data || []
+    }
+  } catch (error) {
+    console.error('鑾峰彇12涓湀鍛樺伐娴佸姩娴佸け鐜囧垎鏋愭暟鎹け璐�:', error)
+  }
+}
+
+const getStaffAnalysisTotalStatistic = async () => {
+  try {
+    const res = await findStaffAnalysisTotalStatistic()
+    if (res && res.data) {
+      keyMetrics.value[0].value = res.data.totalFlowRate || 0
+      keyMetrics.value[1].value = res.data.totalTurnoverRate || 0
+      keyMetrics.value[2].value = res.data.currentOnJobCount || 0
+    }
+  } catch (error) {
+    console.error('鑾峰彇鍛樺伐鍒嗘瀽鎬荤粺璁℃暟鎹け璐�:', error)
   }
 }
 
@@ -213,49 +226,28 @@
   }
 }
 
-// 鐢熸垚妯℃嫙鏁版嵁
-const generateMockData = () => {
-  // 鐢熸垚鍏抽敭鎸囨爣鏁版嵁
-  keyMetrics.value[0].value = (Math.random() * 5 + 2).toFixed(1)
-  keyMetrics.value[0].trend = (Math.random() * 3 - 1.5).toFixed(1)
-  
-  keyMetrics.value[1].value = (Math.random() * 3 + 1).toFixed(1)
-  keyMetrics.value[1].trend = (Math.random() * 2 - 1).toFixed(1)
-  
-  keyMetrics.value[2].value = (Math.random() * 15 + 85).toFixed(1)
-  keyMetrics.value[2].trend = (Math.random() * 3 - 1.5).toFixed(1)
-
-  // 鐢熸垚閮ㄩ棬鏁版嵁
-  const departments = ['鎶�鏈儴', '閿�鍞儴', '浜轰簨閮�', '璐㈠姟閮�', '鐢熶骇閮�', '甯傚満閮�']
-  departmentData.value = departments.map(dept => ({
-    department: dept,
-    currentStaff: Math.floor(Math.random() * 30 + 20),
-    plannedStaff: Math.floor(Math.random() * 10 + 35),
-    staffingRate: Math.floor(Math.random() * 20 + 80),
-    turnoverRate: (Math.random() * 4 + 1).toFixed(1),
-    attritionRate: (Math.random() * 2 + 0.5).toFixed(1),
-    newHires: Math.floor(Math.random() * 5 + 1),
-    resignations: Math.floor(Math.random() * 3 + 1),
-    status: Math.random() > 0.7 ? '寮傚父' : '姝e父'
-  }))
-}
-
-// 鍒锋柊鏁版嵁
+// 淇敼涓哄紓姝ュ嚱鏁帮紝纭繚鏁版嵁鍔犺浇瀹屾垚鍚庡啀娓叉煋鍥捐〃
 const refreshData = async () => {
-  loading.value = true
   try {
-    // 妯℃嫙API璋冪敤寤惰繜
-    await new Promise(resolve => setTimeout(resolve, 500))
-    
-    generateMockData()
+    loading.value = true
+
+    // 绛夊緟鎵�鏈夋暟鎹姞杞藉畬鎴�
+    await Promise.all([
+      getDepartmentData(),
+      getStaffLeaveReasonAnalysis(),
+      getMonthlyTurnoverRateFor12Months(),
+      getStaffAnalysisTotalStatistic()
+    ])
+
+    await nextTick()
     renderAllCharts()
-    
+
     if (!autoRefreshEnabled.value) {
       ElMessage.success('鏁版嵁鍒锋柊鎴愬姛')
     }
   } catch (error) {
-    console.error('鍒锋柊鏁版嵁澶辫触:', error)
-    ElMessage.error('鍒锋柊鏁版嵁澶辫触')
+    console.error('鏁版嵁鍒锋柊澶辫触:', error)
+    ElMessage.error('鏁版嵁鍒锋柊澶辫触')
   } finally {
     loading.value = false
   }
@@ -276,8 +268,9 @@
     if (attritionChartRef.value) {
       attritionChart = echarts.init(attritionChartRef.value)
     }
-    
-    renderAllCharts()
+
+    // 鍒濆鍖栨椂涔熷厛鍔犺浇鏁版嵁鍐嶆覆鏌撳浘琛�
+    refreshData()
   }, 300)
 }
 
@@ -289,14 +282,15 @@
   renderAttritionChart()
 }
 
-// 娓叉煋鍛樺伐娴佸姩鐜囪秼鍔垮浘
+// 淇敼涓轰娇鐢ˋPI杩斿洖鐨勫疄闄呮暟鎹�
 const renderTurnoverChart = () => {
   if (!turnoverChart) return
-  
-  const months = ['1鏈�', '2鏈�', '3鏈�', '4鏈�', '5鏈�', '6鏈�', '7鏈�', '8鏈�', '9鏈�', '10鏈�', '11鏈�', '12鏈�']
-  const turnoverData = months.map(() => (Math.random() * 5 + 2).toFixed(1))
-  const attritionData = months.map(() => (Math.random() * 3 + 1).toFixed(1))
-  
+
+  // 浣跨敤API杩斿洖鐨勫疄闄呮暟鎹�
+  const months = turnoverRateStatistics.value.map(item => item.month)
+  const turnoverData = turnoverRateStatistics.value.map(item => item.flowRate || 0)
+  const attritionData = turnoverRateStatistics.value.map(item => item.turnoverRate || 0)
+
   const option = {
     title: {
       text: '鍛樺伐娴佸姩鐜囪秼鍔�',
@@ -346,19 +340,19 @@
       }
     ]
   }
-  
+
   turnoverChart.setOption(option)
 }
 
 // 娓叉煋閮ㄩ棬浜哄憳鍒嗗竷鍥�
 const renderDepartmentChart = () => {
   if (!departmentChart) return
-  
+
   const data = departmentData.value.map(item => ({
-    name: item.department,
-    value: item.currentStaff
+    name: item.deptName,
+    value: item.staffCount
   }))
-  
+
   const option = {
     title: {
       text: '閮ㄩ棬浜哄憳鍒嗗竷',
@@ -391,17 +385,17 @@
       }
     ]
   }
-  
+
   departmentChart.setOption(option)
 }
 
 // 娓叉煋缂栧埗杈炬垚鐜囧浘
 const renderStaffingChart = () => {
   if (!staffingChart) return
-  
-  const departments = departmentData.value.map(item => item.department)
+
+  const departments = departmentData.value.map(item => item.deptName)
   const rates = departmentData.value.map(item => item.staffingRate)
-  
+
   const option = {
     title: {
       text: '缂栧埗杈炬垚鐜�',
@@ -445,17 +439,17 @@
       }
     ]
   }
-  
+
   staffingChart.setOption(option)
 }
 
 // 娓叉煋鍛樺伐娴佸け鍘熷洜鍒嗘瀽鍥�
 const renderAttritionChart = () => {
   if (!attritionChart) return
-  
-  const reasons = ['钖祫寰呴亣', '鑱屼笟鍙戝睍', '宸ヤ綔鐜', '涓汉鍘熷洜', '鍏朵粬']
-  const data = reasons.map(() => Math.floor(Math.random() * 20 + 5))
-  
+
+  const reasons = staffLeaveReasons.value.map(item => item.reasonText)
+  const data = staffLeaveReasons.value.map(item => item.count)
+
   const option = {
     title: {
       text: '鍛樺伐娴佸け鍘熷洜鍒嗘瀽',
@@ -491,14 +485,12 @@
       }
     ]
   }
-  
+
   attritionChart.setOption(option)
 }
- 
+
 // 鐢熷懡鍛ㄦ湡
 onMounted(() => {
-  generateMockData()
-  getStaffCount()
   initCharts()
   startAutoRefresh()
 })
@@ -663,32 +655,32 @@
   .analytics-container {
     padding: 10px;
   }
-  
+
   .page-header {
     padding: 15px;
   }
-  
+
   .page-header h2 {
     font-size: 24px;
   }
-  
+
   .header-controls {
     flex-direction: column;
     gap: 15px;
   }
-  
+
   .refresh-btn {
     margin-left: 0;
   }
-  
+
   .metrics-cards .el-col {
     margin-bottom: 15px;
   }
-  
+
   .charts-section .el-col {
     margin-bottom: 20px;
   }
-  
+
   .chart-container {
     height: 300px;
   }
@@ -698,11 +690,11 @@
   .page-header h2 {
     font-size: 20px;
   }
-  
+
   .card-number {
     font-size: 24px;
   }
-  
+
   .chart-container {
     height: 250px;
   }

--
Gitblit v1.9.3