huminmin
13 小时以前 b375f09a33904abc093e874495b15a23e196ccaa
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)
  }
}
// 修改为返回Promise的异步函数
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 ? '异常' : '正常'
  }))
}
// 刷新数据
// 修改为异步函数,确保数据加载完成后再渲染图表
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()
}
// 渲染员工流动率趋势图
// 修改为使用API返回的实际数据
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;
  }