<template> 
 | 
  <div class="app-container analytics-container"> 
 | 
  
 | 
    <!-- 关键指标卡片 --> 
 | 
    <el-row :gutter="20" class="metrics-cards"> 
 | 
      <el-col :span="6" v-for="(item, index) in keyMetrics" :key="index"> 
 | 
        <el-card class="metric-card" :class="item.type"> 
 | 
          <div class="card-content"> 
 | 
            <div class="card-icon"> 
 | 
              <el-icon :size="32"> 
 | 
                <component :is="item.icon" /> 
 | 
              </el-icon> 
 | 
            </div> 
 | 
              <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> 
 | 
              </div> 
 | 
              <div class="card-label">{{ item.label }}</div> 
 | 
              <div class="card-trend" :class="item.trend > 0 ? 'positive' : 'negative'" v-if="item.showTrend !== false"> 
 | 
                <el-icon> 
 | 
                  <component :is="item.trend > 0 ? 'ArrowUp' : 'ArrowDown'" /> 
 | 
                </el-icon> 
 | 
                {{ Math.abs(item.trend) }}% 
 | 
              </div> 
 | 
            </div> 
 | 
          </div> 
 | 
        </el-card> 
 | 
      </el-col> 
 | 
    </el-row> 
 | 
  
 | 
    <!-- 图表区域 --> 
 | 
    <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="info">近12个月</el-tag> 
 | 
            </div> 
 | 
          </template> 
 | 
          <div class="chart-container"> 
 | 
            <div ref="turnoverChartRef" class="chart"></div> 
 | 
          </div> 
 | 
        </el-card> 
 | 
      </el-col> 
 | 
  
 | 
      <!-- 部门人员分布 --> 
 | 
      <el-col :span="12"> 
 | 
        <el-card class="chart-card"> 
 | 
          <template #header> 
 | 
            <div class="card-header"> 
 | 
              <span>部门人员分布</span> 
 | 
              <el-tag type="success">当前状态</el-tag> 
 | 
            </div> 
 | 
          </template> 
 | 
          <div class="chart-container"> 
 | 
            <div ref="departmentChartRef" class="chart"></div> 
 | 
          </div> 
 | 
        </el-card> 
 | 
      </el-col> 
 | 
    </el-row> 
 | 
  
 | 
    <!-- 第二行图表 --> 
 | 
    <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"> 
 | 
          <template #header> 
 | 
            <div class="card-header"> 
 | 
              <span>员工流失原因分析</span> 
 | 
              <el-tag type="danger">年度统计</el-tag> 
 | 
            </div> 
 | 
          </template> 
 | 
          <div class="chart-container"> 
 | 
            <div ref="attritionChartRef" class="chart"></div> 
 | 
          </div> 
 | 
        </el-card> 
 | 
      </el-col> 
 | 
    </el-row> 
 | 
  </div> 
 | 
</template> 
 | 
  
 | 
<script setup> 
 | 
import { ref, reactive, onMounted, onUnmounted } 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/employeeRecord.js' 
 | 
  
 | 
// 响应式数据 
 | 
const loading = ref(false) 
 | 
const autoRefreshEnabled = ref(true) 
 | 
const autoRefreshInterval = ref(null) 
 | 
  
 | 
// 图表引用 
 | 
const turnoverChartRef = ref(null) 
 | 
const departmentChartRef = ref(null) 
 | 
const staffingChartRef = ref(null) 
 | 
const attritionChartRef = ref(null) 
 | 
  
 | 
// 图表实例 
 | 
let turnoverChart = null 
 | 
let departmentChart = null 
 | 
let staffingChart = null 
 | 
let attritionChart = null 
 | 
  
 | 
// 自动更新间隔(10分钟) 
 | 
const AUTO_REFRESH_INTERVAL = 10 * 60 * 1000 
 | 
  
 | 
// 关键指标数据 
 | 
const keyMetrics = ref([ 
 | 
  { 
 | 
    label: '员工流动率', 
 | 
    value: 0, 
 | 
    unit: '%', 
 | 
    icon: 'TrendCharts', 
 | 
    type: 'primary', 
 | 
    trend: 0 
 | 
  }, 
 | 
  { 
 | 
    label: '员工流失率', 
 | 
    value: 0, 
 | 
    unit: '%', 
 | 
    icon: 'User', 
 | 
    type: 'danger', 
 | 
    trend: 0 
 | 
  }, 
 | 
  { 
 | 
    label: '编制达成率', 
 | 
    value: 0, 
 | 
    unit: '%', 
 | 
    icon: 'DataAnalysis', 
 | 
    type: 'success', 
 | 
    trend: 0 
 | 
  }, 
 | 
  { 
 | 
    label: '在职员工数', 
 | 
    value: 0, 
 | 
    unit: '人', 
 | 
    icon: 'PieChart', 
 | 
    type: 'warning', 
 | 
    trend: 0, 
 | 
    showTrend: false 
 | 
  } 
 | 
]) 
 | 
  
 | 
// 部门数据 
 | 
const departmentData = ref([]) 
 | 
  
 | 
// 获取在职员工数 
 | 
const getStaffCount = async () => { 
 | 
  try { 
 | 
    const res = await staffOnJobListPage({ staffState: 1, current: 1, size: 1 }) 
 | 
    if (res && res.data) { 
 | 
      keyMetrics.value[3].value = res.data.total || 0 
 | 
    } 
 | 
  } catch (error) { 
 | 
    console.error('获取在职员工数失败:', error) 
 | 
  } 
 | 
} 
 | 
  
 | 
// 启动自动刷新 
 | 
const startAutoRefresh = () => { 
 | 
  if (autoRefreshInterval.value) { 
 | 
    clearInterval(autoRefreshInterval.value) 
 | 
  } 
 | 
  if (autoRefreshEnabled.value) { 
 | 
    autoRefreshInterval.value = setInterval(() => { 
 | 
      refreshData() 
 | 
    }, AUTO_REFRESH_INTERVAL) 
 | 
  } 
 | 
} 
 | 
  
 | 
// 停止自动刷新 
 | 
const stopAutoRefresh = () => { 
 | 
  if (autoRefreshInterval.value) { 
 | 
    clearInterval(autoRefreshInterval.value) 
 | 
    autoRefreshInterval.value = null 
 | 
  } 
 | 
} 
 | 
  
 | 
// 切换自动刷新状态 
 | 
const toggleAutoRefresh = (value) => { 
 | 
  if (value) { 
 | 
    startAutoRefresh() 
 | 
  } else { 
 | 
    stopAutoRefresh() 
 | 
  } 
 | 
} 
 | 
  
 | 
// 生成模拟数据 
 | 
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() 
 | 
    renderAllCharts() 
 | 
     
 | 
    if (!autoRefreshEnabled.value) { 
 | 
      ElMessage.success('数据刷新成功') 
 | 
    } 
 | 
  } catch (error) { 
 | 
    console.error('刷新数据失败:', error) 
 | 
    ElMessage.error('刷新数据失败') 
 | 
  } finally { 
 | 
    loading.value = false 
 | 
  } 
 | 
} 
 | 
  
 | 
// 初始化图表 
 | 
const initCharts = () => { 
 | 
  setTimeout(() => { 
 | 
    if (turnoverChartRef.value) { 
 | 
      turnoverChart = echarts.init(turnoverChartRef.value) 
 | 
    } 
 | 
    if (departmentChartRef.value) { 
 | 
      departmentChart = echarts.init(departmentChartRef.value) 
 | 
    } 
 | 
    if (staffingChartRef.value) { 
 | 
      staffingChart = echarts.init(staffingChartRef.value) 
 | 
    } 
 | 
    if (attritionChartRef.value) { 
 | 
      attritionChart = echarts.init(attritionChartRef.value) 
 | 
    } 
 | 
     
 | 
    renderAllCharts() 
 | 
  }, 300) 
 | 
} 
 | 
  
 | 
// 渲染所有图表 
 | 
const renderAllCharts = () => { 
 | 
  renderTurnoverChart() 
 | 
  renderDepartmentChart() 
 | 
  renderStaffingChart() 
 | 
  renderAttritionChart() 
 | 
} 
 | 
  
 | 
// 渲染员工流动率趋势图 
 | 
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)) 
 | 
   
 | 
  const option = { 
 | 
    title: { 
 | 
      text: '员工流动率趋势', 
 | 
      left: 'center', 
 | 
      textStyle: { fontSize: 16, fontWeight: 'normal' } 
 | 
    }, 
 | 
    tooltip: { 
 | 
      trigger: 'axis', 
 | 
      axisPointer: { type: 'cross' } 
 | 
    }, 
 | 
    legend: { 
 | 
      data: ['流动率', '流失率'], 
 | 
      bottom: 10 
 | 
    }, 
 | 
    grid: { 
 | 
      left: '3%', 
 | 
      right: '4%', 
 | 
      bottom: '15%', 
 | 
      top: '15%', 
 | 
      containLabel: true 
 | 
    }, 
 | 
    xAxis: { 
 | 
      type: 'category', 
 | 
      data: months, 
 | 
      boundaryGap: false 
 | 
    }, 
 | 
    yAxis: { 
 | 
      type: 'value', 
 | 
      axisLabel: { formatter: '{value}%' } 
 | 
    }, 
 | 
    series: [ 
 | 
      { 
 | 
        name: '流动率', 
 | 
        type: 'line', 
 | 
        data: turnoverData, 
 | 
        smooth: true, 
 | 
        lineStyle: { color: '#409EFF' }, 
 | 
        itemStyle: { color: '#409EFF' } 
 | 
      }, 
 | 
      { 
 | 
        name: '流失率', 
 | 
        type: 'line', 
 | 
        data: attritionData, 
 | 
        smooth: true, 
 | 
        lineStyle: { color: '#F56C6C' }, 
 | 
        itemStyle: { color: '#F56C6C' } 
 | 
      } 
 | 
    ] 
 | 
  } 
 | 
   
 | 
  turnoverChart.setOption(option) 
 | 
} 
 | 
  
 | 
// 渲染部门人员分布图 
 | 
const renderDepartmentChart = () => { 
 | 
  if (!departmentChart) return 
 | 
   
 | 
  const data = departmentData.value.map(item => ({ 
 | 
    name: item.department, 
 | 
    value: item.currentStaff 
 | 
  })) 
 | 
   
 | 
  const option = { 
 | 
    title: { 
 | 
      text: '部门人员分布', 
 | 
      left: 'center', 
 | 
      textStyle: { fontSize: 16, fontWeight: 'normal' } 
 | 
    }, 
 | 
    tooltip: { 
 | 
      trigger: 'item', 
 | 
      formatter: '{a} <br/>{b}: {c}人 ({d}%)' 
 | 
    }, 
 | 
    legend: { 
 | 
      orient: 'vertical', 
 | 
      left: 'left', 
 | 
      top: 'middle' 
 | 
    }, 
 | 
    series: [ 
 | 
      { 
 | 
        name: '人员数量', 
 | 
        type: 'pie', 
 | 
        radius: ['40%', '70%'], 
 | 
        center: ['60%', '50%'], 
 | 
        data: data, 
 | 
        emphasis: { 
 | 
          itemStyle: { 
 | 
            shadowBlur: 10, 
 | 
            shadowOffsetX: 0, 
 | 
            shadowColor: 'rgba(0, 0, 0, 0.5)' 
 | 
          } 
 | 
        } 
 | 
      } 
 | 
    ] 
 | 
  } 
 | 
   
 | 
  departmentChart.setOption(option) 
 | 
} 
 | 
  
 | 
// 渲染编制达成率图 
 | 
const renderStaffingChart = () => { 
 | 
  if (!staffingChart) return 
 | 
   
 | 
  const departments = departmentData.value.map(item => item.department) 
 | 
  const rates = departmentData.value.map(item => item.staffingRate) 
 | 
   
 | 
  const option = { 
 | 
    title: { 
 | 
      text: '编制达成率', 
 | 
      left: 'center', 
 | 
      textStyle: { fontSize: 16, fontWeight: 'normal' } 
 | 
    }, 
 | 
    tooltip: { 
 | 
      trigger: 'axis', 
 | 
      axisPointer: { type: 'shadow' } 
 | 
    }, 
 | 
    grid: { 
 | 
      left: '3%', 
 | 
      right: '4%', 
 | 
      bottom: '15%', 
 | 
      top: '15%', 
 | 
      containLabel: true 
 | 
    }, 
 | 
    xAxis: { 
 | 
      type: 'category', 
 | 
      data: departments, 
 | 
      axisLabel: { rotate: 45 } 
 | 
    }, 
 | 
    yAxis: { 
 | 
      type: 'value', 
 | 
      axisLabel: { formatter: '{value}%' }, 
 | 
      max: 100 
 | 
    }, 
 | 
    series: [ 
 | 
      { 
 | 
        name: '达成率', 
 | 
        type: 'bar', 
 | 
        data: rates, 
 | 
        itemStyle: { 
 | 
          color: function(params) { 
 | 
            const value = params.value 
 | 
            if (value >= 90) return '#67C23A' 
 | 
            if (value >= 80) return '#E6A23C' 
 | 
            return '#F56C6C' 
 | 
          } 
 | 
        } 
 | 
      } 
 | 
    ] 
 | 
  } 
 | 
   
 | 
  staffingChart.setOption(option) 
 | 
} 
 | 
  
 | 
// 渲染员工流失原因分析图 
 | 
const renderAttritionChart = () => { 
 | 
  if (!attritionChart) return 
 | 
   
 | 
  const reasons = ['薪资待遇', '职业发展', '工作环境', '个人原因', '其他'] 
 | 
  const data = reasons.map(() => Math.floor(Math.random() * 20 + 5)) 
 | 
   
 | 
  const option = { 
 | 
    title: { 
 | 
      text: '员工流失原因分析', 
 | 
      left: 'center', 
 | 
      textStyle: { fontSize: 16, fontWeight: 'normal' } 
 | 
    }, 
 | 
    tooltip: { 
 | 
      trigger: 'item', 
 | 
      formatter: '{a} <br/>{b}: {c}人 ({d}%)' 
 | 
    }, 
 | 
    legend: { 
 | 
      orient: 'vertical', 
 | 
      left: 'left', 
 | 
      top: 'middle' 
 | 
    }, 
 | 
    series: [ 
 | 
      { 
 | 
        name: '流失人数', 
 | 
        type: 'pie', 
 | 
        radius: '50%', 
 | 
        center: ['60%', '50%'], 
 | 
        data: reasons.map((reason, index) => ({ 
 | 
          name: reason, 
 | 
          value: data[index] 
 | 
        })), 
 | 
        emphasis: { 
 | 
          itemStyle: { 
 | 
            shadowBlur: 10, 
 | 
            shadowOffsetX: 0, 
 | 
            shadowColor: 'rgba(0, 0, 0, 0.5)' 
 | 
          } 
 | 
        } 
 | 
      } 
 | 
    ] 
 | 
  } 
 | 
   
 | 
  attritionChart.setOption(option) 
 | 
} 
 | 
  
 | 
// 生命周期 
 | 
onMounted(() => { 
 | 
  generateMockData() 
 | 
  getStaffCount() 
 | 
  initCharts() 
 | 
  startAutoRefresh() 
 | 
}) 
 | 
  
 | 
onUnmounted(() => { 
 | 
  stopAutoRefresh() 
 | 
}) 
 | 
</script> 
 | 
  
 | 
<style scoped> 
 | 
.analytics-container { 
 | 
  padding: 20px; 
 | 
  background-color: #f5f7fa; 
 | 
  min-height: 100vh; 
 | 
} 
 | 
  
 | 
.page-header { 
 | 
  text-align: center; 
 | 
  margin-bottom: 30px; 
 | 
  padding: 20px; 
 | 
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 
 | 
  border-radius: 12px; 
 | 
  color: white; 
 | 
} 
 | 
  
 | 
.page-header h2 { 
 | 
  color: white; 
 | 
  margin-bottom: 10px; 
 | 
  font-size: 28px; 
 | 
  font-weight: 600; 
 | 
} 
 | 
  
 | 
.page-header p { 
 | 
  color: rgba(255, 255, 255, 0.9); 
 | 
  font-size: 14px; 
 | 
  margin: 0 0 15px 0; 
 | 
} 
 | 
  
 | 
.header-controls { 
 | 
  display: flex; 
 | 
  justify-content: center; 
 | 
  align-items: center; 
 | 
  gap: 20px; 
 | 
} 
 | 
  
 | 
.refresh-btn { 
 | 
  margin-left: 20px; 
 | 
} 
 | 
  
 | 
.metrics-cards { 
 | 
  margin-bottom: 30px; 
 | 
} 
 | 
  
 | 
.metric-card { 
 | 
  border-radius: 12px; 
 | 
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); 
 | 
  transition: all 0.3s ease; 
 | 
  border: none; 
 | 
  overflow: hidden; 
 | 
} 
 | 
  
 | 
.metric-card:hover { 
 | 
  transform: translateY(-5px); 
 | 
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); 
 | 
} 
 | 
  
 | 
.metric-card.primary { 
 | 
  border-left: 4px solid #409EFF; 
 | 
  background: linear-gradient(135deg, #409EFF 0%, #36a3f7 100%); 
 | 
} 
 | 
  
 | 
.metric-card.danger { 
 | 
  border-left: 4px solid #F56C6C; 
 | 
  background: linear-gradient(135deg, #F56C6C 0%, #f78989 100%); 
 | 
} 
 | 
  
 | 
.metric-card.success { 
 | 
  border-left: 4px solid #67C23A; 
 | 
  background: linear-gradient(135deg, #67C23A 0%, #85ce61 100%); 
 | 
} 
 | 
  
 | 
.metric-card.warning { 
 | 
  border-left: 4px solid #E6A23C; 
 | 
  background: linear-gradient(135deg, #E6A23C 0%, #ebb563 100%); 
 | 
} 
 | 
  
 | 
.card-content { 
 | 
  display: flex; 
 | 
  align-items: center; 
 | 
  padding: 20px; 
 | 
} 
 | 
  
 | 
.card-icon { 
 | 
  margin-right: 20px; 
 | 
  color: white; 
 | 
} 
 | 
  
 | 
.card-info { 
 | 
  flex: 1; 
 | 
} 
 | 
  
 | 
.card-number { 
 | 
  font-size: 32px; 
 | 
  font-weight: 600; 
 | 
  color: white; 
 | 
  margin-bottom: 5px; 
 | 
} 
 | 
  
 | 
.card-label { 
 | 
  font-size: 14px; 
 | 
  color: rgba(255, 255, 255, 0.9); 
 | 
  margin-bottom: 5px; 
 | 
} 
 | 
  
 | 
.card-trend { 
 | 
  font-size: 12px; 
 | 
  display: flex; 
 | 
  align-items: center; 
 | 
  gap: 4px; 
 | 
} 
 | 
  
 | 
.card-trend.positive { 
 | 
  color: #67C23A; 
 | 
} 
 | 
  
 | 
.card-trend.negative { 
 | 
  color: #F56C6C; 
 | 
} 
 | 
  
 | 
.charts-section { 
 | 
  margin-bottom: 30px; 
 | 
} 
 | 
  
 | 
.chart-card { 
 | 
  border-radius: 12px; 
 | 
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); 
 | 
  border: none; 
 | 
} 
 | 
  
 | 
.card-header { 
 | 
  display: flex; 
 | 
  justify-content: space-between; 
 | 
  align-items: center; 
 | 
  font-weight: 600; 
 | 
  color: #303133; 
 | 
  padding: 15px 20px; 
 | 
  border-bottom: 1px solid #ebeef5; 
 | 
} 
 | 
  
 | 
.chart-container { 
 | 
  height: 350px; 
 | 
  padding: 20px; 
 | 
} 
 | 
  
 | 
.chart { 
 | 
  width: 100%; 
 | 
  height: 100%; 
 | 
} 
 | 
  
 | 
/* 响应式设计 */ 
 | 
@media (max-width: 768px) { 
 | 
  .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; 
 | 
  } 
 | 
} 
 | 
  
 | 
@media (max-width: 480px) { 
 | 
  .page-header h2 { 
 | 
    font-size: 20px; 
 | 
  } 
 | 
   
 | 
  .card-number { 
 | 
    font-size: 24px; 
 | 
  } 
 | 
   
 | 
  .chart-container { 
 | 
    height: 250px; 
 | 
  } 
 | 
} 
 | 
</style> 
 |