gongchunyi
2 天以前 0c116b0f5624786bd06990b86c467be25e2411fd
src/views/financialManagement/financialStatements/index.vue
@@ -1,16 +1,16 @@
<template>
 <template>
  <div style="padding: 20px;">
    <!-- 页面标题和日期筛选 -->
    <!-- 页面标题和月份筛选 -->
    <div class="w-full md:w-auto flex items-center gap-3" style="margin-bottom: 20px;">
      <el-date-picker
        v-model="dateRange"
        type="daterange"
        format="YYYY-MM-DD"
        value-format="YYYY-MM-DD"
        type="monthrange"
        format="YYYY-MM"
        value-format="YYYY-MM"
        range-separator="至"
        start-placeholder="开始日期"
        end-placeholder="结束日期"
        :default-value="[new Date(firstDayOfMonth), new Date()]"
        start-placeholder="开始月份"
        end-placeholder="结束月份"
        :disabled-date="disabledDate"
        @change="handleDateChange"
        class="w-full md:w-auto"
        style="margin-right: 30px;"
@@ -28,117 +28,149 @@
    
    <main class="container mx-auto px-4 pb-10">
      <!-- 财务指标卡片 -->
      <div class="grid-container">
        <!-- 总收入 -->
        <el-card class="bg1">
          <p>总收入</p>
          <h3>
            ¥{{ pageInfo.totalIncome }}
          </h3>
        </el-card>
        <!-- 收入笔数 -->
        <el-card class="bg2">
          <p>收入笔数</p>
          <h3>
            {{ pageInfo.incomeNumber }}
          </h3>
        </el-card>
      <div class="stats-cards">
        <!-- 总营收 -->
        <div class="stat-card stat-card-blue">
          <div class="stat-icon">
            <img src="@/assets/icons/png/walletBlue@2x.png" alt="总营收" />
          </div>
          <div class="stat-content">
            <div class="stat-label">总营收</div>
            <div class="stat-value">{{ formatMoney(pageInfo.totalIncome || 0) }} 元</div>
          </div>
        </div>
        
        <!-- 总支出 -->
        <el-card class="bg3">
          <p>总支出</p>
          <h3>
            ¥{{ pageInfo.totalExpense }}
          </h3>
        </el-card>
        <div class="stat-card stat-card-orange">
          <div class="stat-icon">
            <img src="@/assets/icons/png/walletOrange@2x.png" alt="总支出" />
          </div>
          <div class="stat-content">
            <div class="stat-label">总支出</div>
            <div class="stat-value">{{ formatMoney(pageInfo.totalExpense || 0) }} 元</div>
          </div>
        </div>
        
        <!-- 支出笔数 -->
        <el-card class="bg4">
          <p>支出笔数</p>
          <h3>
            {{ pageInfo.expenseNumber }}
          </h3>
        </el-card>
        <!-- 总收入笔数 -->
        <div class="stat-card stat-card-green">
          <div class="stat-icon">
            <img src="@/assets/icons/png/walletGreen@2x.png" alt="总收入笔数" />
          </div>
          <div class="stat-content">
            <div class="stat-label">总收入笔数</div>
            <div class="stat-value">{{ pageInfo.incomeNumber || 0 }} 笔</div>
          </div>
        </div>
        <!-- 总支出笔数 -->
        <div class="stat-card stat-card-red">
          <div class="stat-icon">
            <img src="@/assets/icons/png/walletRed@2x.png" alt="总支出笔数" />
          </div>
          <div class="stat-content">
            <div class="stat-label">总支出笔数</div>
            <div class="stat-value">{{ pageInfo.expenseNumber || 0 }} 笔</div>
          </div>
        </div>
        
        <!-- 净收入 -->
        <el-card class="bg5">
          <p>净收入</p>
          <h3>
            ¥{{ pageInfo.netRevenue }}
          </h3>
        <div class="stat-card stat-card-yellow">
          <div class="stat-icon">
            <img src="@/assets/icons/png/walletYellow@2x.png" alt="净收入" />
          </div>
          <div class="stat-content">
            <div class="stat-label">净收入</div>
            <div class="stat-value">{{ formatMoney(pageInfo.netRevenue || 0) }} 元</div>
          </div>
        </div>
      </div>
      <!-- 中间图表区域 -->
      <div class="charts-row">
        <!-- 左侧:收入支出分析 -->
        <el-card class="chart-card">
          <h2 class="section-title">收入支出分析</h2>
          <div class="pie-chart-container">
            <Echarts
              :legend="pieLegendIncomeExpense"
              :chartStyle="chartStylePie"
              :series="pieSeriesIncomeExpense"
              :tooltip="pieTooltipIncomeExpense"
              style="height: 320px; width: 100%;">
            </Echarts>
            <div class="pie-stats">
              <div class="bar-stat-item">
                <span class="bar-stat-label">收入数量</span>
                <span class="bar-stat-value">{{ pageInfo.incomeNumber || 0 }}</span>
              </div>
              <div class="bar-stat-item">
                <span class="bar-stat-label">支出数量</span>
                <span class="bar-stat-value">{{ pageInfo.expenseNumber || 0 }}</span>
              </div>
            </div>
          </div>
        </el-card>
        <!-- 右侧:行项盈利分析 -->
        <el-card class="chart-card">
          <h2 class="section-title">行项盈利分析</h2>
          <div class="bar-chart-header">
            <div class="bar-stat-item">
              <span class="bar-stat-label">当前总个数</span>
              <span class="bar-stat-value">{{ allBarTypes.value?.length || 0 }}</span>
            </div>
            <div class="bar-stat-item">
              <span class="bar-stat-label">支出金额</span>
              <span class="bar-stat-value">{{ formatMoney(pageInfo.totalExpense || 0) }}</span>
            </div>
            <div class="bar-stat-item">
              <span class="bar-stat-label">收入金额</span>
              <span class="bar-stat-value">{{ formatMoney(pageInfo.totalIncome || 0) }}</span>
            </div>
          </div>
          <Echarts
            ref="barChart"
            :chartStyle="chartStyle"
            :grid="barGrid"
            :legend="barLegend"
            :series="barSeries"
            :tooltip="barTooltip"
            :xAxis="barXAxis"
            :yAxis="barYAxis"
            style="height: 300px; width: 100%;">
          </Echarts>
        </el-card>
      </div>
      
      <!-- 收入统计图表 -->
      <div class="grid-layout">
        <el-card style="margin-bottom: 20px;">
          <h2 class="section-title">收入统计(元)</h2>
          <div class="echarts">
            <Echarts :legend="pieLegend0" :chartStyle="chartStylePie"
                               :series="materialPieSeries0"
                               :tooltip="pieTooltip" style="height: 260px;width: 35%;">
                     <div class="chart-num">
                      <span style="font-size: 22px;">收入</span>
                      <span style="font-size: 36px;
    font-weight: 500;
    font-family: 'MyCustomFont', sans-serif;">{{ pageInfo.totalIncome }}</span>
                     </div>
                    </Echarts>
            <Echarts ref="chart"
                         :chartStyle="chartStyle"
                         :grid="grid"
                         :legend="lineLegend"
                         :series="lineSeries0"
                         :tooltip="tooltip"
                         :xAxis="xAxis0"
                         :yAxis="yAxis0"
                         style="height: 260px;width: 64%;"></Echarts>
          </div>
        </el-card>
        <!-- 支出统计图表 -->
        <el-card>
          <h2 class="section-title">支出统计(元)</h2>
          <div class="echarts">
            <Echarts ref="chart"
                    :legend="pieLegend1"
                    :chartStyle="chartStylePie"
                               :series="materialPieSeries1"
                               :tooltip="pieTooltip"
                     style="height: 260px;width: 35%;">
                     <div class="chart-num">
                      <span style="font-size: 22px;">支出</span>
                      <span style="font-size: 36px;
    font-weight: 500;
    font-family: 'MyCustomFont', sans-serif;">{{ pageInfo.totalExpense }}</span>
                     </div></Echarts>
            <Echarts ref="chart"
                         :chartStyle="chartStyle"
                         :grid="grid"
                         :legend="lineLegend"
                         :series="lineSeries1"
                         :tooltip="tooltip"
                         :xAxis="xAxis1"
                         :yAxis="yAxis1"
                         style="height: 260px;width: 64%;"></Echarts>
          </div>
        </el-card>
      </div>
      <!-- 底部:营收趋势分析 -->
      <el-card class="trend-chart-card">
        <h2 class="section-title">营收趋势分析</h2>
        <Echarts
          ref="trendChart"
          :chartStyle="chartStyle"
          :grid="grid"
          :legend="trendLegend"
          :series="trendSeries"
          :tooltip="tooltip"
          :xAxis="xAxis0"
          :yAxis="trendYAxis"
          style="height: 350px; width: 100%;">
        </Echarts>
      </el-card>
    </main>
  </div>
</template>
<script setup>
import { ref, computed, onMounted, reactive } from 'vue';
import { ref, computed, onMounted, reactive, nextTick, getCurrentInstance } from 'vue';
import 'element-plus/dist/index.css';
import Echarts from "@/components/Echarts/echarts.vue";
import { reportForms,reportIncome,reportExpense } from "@/api/financialManagement/financialStatements";
import dayjs from "dayjs";
// 日期范围
const dateRange = ref([]);
const firstDayOfMonth = ref(null);
const dateRange = ref(null);
const { proxy } = getCurrentInstance();
const chartStyle = {
   width: '100%',
   height: '100%', // 设置图表容器的高度
@@ -173,22 +205,35 @@
    return `<div>${axisLabel}</div><div>${rows}</div>`
  }
})
const months = ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'];
const lineSeries0 = ref([])
const lineSeries1 = ref([])
// 根据月份范围生成 x 轴数据
const generateMonthLabels = (startMonth, endMonth) => {
  const labels = [];
  let current = dayjs(startMonth);
  const end = dayjs(endMonth);
  while (current.isBefore(end) || current.isSame(end, 'month')) {
    labels.push(`${current.month() + 1}月`);
    current = current.add(1, 'month');
  }
  return labels;
};
const xAxis0 = ref([
  {
    type: 'category',
    axisTick: { show: true, alignWithLabel: true },
    data: months,
    data: [],
  },
]);
const xAxis1 = ref([
  {
    type: 'category',
    axisTick: { show: true, alignWithLabel: true },
    data: months,
    data: [],
  },
]);
const yAxis0 = [
@@ -233,9 +278,10 @@
  left: '60%',
  orient: 'vertical',
  icon: 'circle',
  data: pieData0.value.map(item => item.name),
  data: (pieData0.value || []).filter(item => item && item.name).map(item => item.name),
  formatter: function(name) {
    const item = pieData0.value.find(i => i.name === name);
    if (!name) return '';
    const item = pieData0.value.find(i => i && i.name === name);
    if (!item) return name;
    return `${name} | ${item.percent} ${item.amount}`;
  },
@@ -251,9 +297,10 @@
  left: '60%',
  orient: 'vertical',
  icon: 'circle',
  data: pieData1.value.map(item => item.name),
  data: (pieData1.value || []).filter(item => item && item.name).map(item => item.name),
  formatter: function(name) {
    const item = pieData1.value.find(i => i.name === name);
    if (!name) return '';
    const item = pieData1.value.find(i => i && i.name === name);
    if (!item) return name;
    return `${name} | ${item.percent} ${item.amount}`;
  },
@@ -277,7 +324,7 @@
    label: {
      show: false
    },
    data: pieData0.value,
    data: (pieData0.value || []).filter(item => item && item.name),
    color: pieColors
  }
]);
@@ -294,7 +341,7 @@
    label: {
      show: false
    },
    data: pieData1.value,
    data: (pieData1.value || []).filter(item => item && item.name),
    color: pieColors
  }
]);
@@ -319,50 +366,337 @@
const pageInfo = ref({
})
const getData = async () => {
  try {
    const {code,data} = await reportForms({entryDateStart:dateRange.value[0], entryDateEnd:dateRange.value[1]});
    if(code === 200) {
      pageInfo.value = data
      pieData0.value = data.incomeType.map(item=>({
        name:item.typeName,
        value:item.account,
        percent:`${item.proportion*100}%`,
        amount:`¥${item.account}`
      }))
      pieData1.value = data.expenseType.map(item=>({
        name:item.typeName,
        value:item.account,
        percent:`${item.proportion*100}%`,
        amount:`¥${item.account}`
      }))
// 格式化金额
const formatMoney = (value) => {
  if (!value && value !== 0) return '0';
  return Number(value).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
};
// 收入支出分析饼图
const pieDataIncomeExpense = computed(() => {
  const totalIncome = Number(pageInfo.value.totalIncome) || 0;
  const totalExpense = Number(pageInfo.value.totalExpense) || 0;
  const total = totalIncome + totalExpense;
  if (total === 0) {
    return [
      { name: '收入', value: 0, percent: '0%' },
      { name: '支出', value: 0, percent: '0%' }
    ];
  }
  const incomePercent = ((totalIncome / total) * 100).toFixed(0);
  const expensePercent = ((totalExpense / total) * 100).toFixed(0);
  return [
    { name: '收入', value: totalIncome, percent: `${incomePercent}%` },
    { name: '支出', value: totalExpense, percent: `${expensePercent}%` }
  ];
});
const pieLegendIncomeExpense = computed(() => ({
  show: false
}));
const pieTooltipIncomeExpense = reactive({
  trigger: 'item',
  formatter: function(params) {
    if (!params.data) return params.name;
    return `${params.name}占比 ${params.percent}%`;
  }
});
const pieSeriesIncomeExpense = computed(() => [
  {
    type: 'pie',
    radius: ['0%', '70%'],
    center: ['50%', '50%'],
    avoidLabelOverlap: true,
    itemStyle: {
      borderColor: '#fff',
      borderWidth: 2
    },
    label: {
      show: true,
      position: 'outside',
      formatter: function(params) {
        return `${params.name}占比 ${params.percent}%`;
      },
      fontSize: 14,
      color: '#333'
    },
    labelLine: {
      show: true,
      length: 15,
      length2: 10,
      lineStyle: {
        color: '#333'
      }
    },
    emphasis: {
      label: {
        show: true,
        fontSize: 16,
        fontWeight: 'bold'
      }
    },
    data: pieDataIncomeExpense.value,
    color: ['#1890FF', '#FACC14']
  }
]);
// 行项盈利分析柱状图
const barXAxis = computed(() => {
  return [{
    type: 'category',
    data: (allBarTypes.value && allBarTypes.value.length > 0) ? allBarTypes.value : ['项目1', '项目2', '项目3', '项目4', '项目5', '项目6', '项目7'],
    axisTick: { show: true, alignWithLabel: true },
  }];
});
const barYAxis = [{
  type: 'value',
  name: '单位: 元',
  position: 'left',
  min: 0,
  nameTextStyle: {
    color: '#000',
    fontSize: 14,
  },
}];
const barGrid = {
  left: '3%',
  right: '4%',
  bottom: '3%',
  containLabel: true
};
const barLegend = {
  show: true,
  top: 10,
  right: 10,
};
// 获取所有类型名称
const allBarTypes = computed(() => {
  const incomeTypes = (lineSeries0.value || []).map(item => item.name || item.typeName).filter(Boolean);
  const expenseTypes = (lineSeries1.value || []).map(item => item.name || item.typeName).filter(Boolean);
  return [...new Set([...incomeTypes, ...expenseTypes])];
});
const barSeries = computed(() => {
  if (allBarTypes.value.length === 0) {
    return [
      {
        name: '支出',
        type: 'bar',
        data: [],
        itemStyle: { color: '#1890FF' }
      },
      {
        name: '收入',
        type: 'bar',
        data: [],
        itemStyle: { color: '#13C2C2' }
      }
    ];
  }
  // 计算每个项目的总收入(汇总所有月份)
  const incomeData = allBarTypes.value.map(typeName => {
    const incomeItem = (lineSeries0.value || []).find(item => (item.name || item.typeName) === typeName);
    if (incomeItem && incomeItem.data && Array.isArray(incomeItem.data)) {
      return incomeItem.data.reduce((sum, val) => sum + (Number(val) || 0), 0);
    }
    return 0;
  });
  // 计算每个项目的总支出(汇总所有月份)
  const expenseData = allBarTypes.value.map(typeName => {
    const expenseItem = (lineSeries1.value || []).find(item => (item.name || item.typeName) === typeName);
    if (expenseItem && expenseItem.data && Array.isArray(expenseItem.data)) {
      return expenseItem.data.reduce((sum, val) => sum + (Number(val) || 0), 0);
    }
    return 0;
  });
  return [
    {
      name: '支出',
      type: 'bar',
      data: expenseData,
      itemStyle: { color: '#1890FF' }
    },
    {
      name: '收入',
      type: 'bar',
      data: incomeData,
      itemStyle: { color: '#13C2C2' }
    }
  ];
});
const barTooltip = reactive({
  trigger: 'axis',
  axisPointer: {
    type: 'shadow'
  },
  formatter: function (params) {
    if (!params || !params.length) return '';
    const axisLabel = params[0].axisValueLabel || params[0].axisValue || '';
    const rows = params
      .map(p => {
        const colorDot = `<span style="display:inline-block;margin-right:6px;width:8px;height:8px;border-radius:50%;background:${p.color}"></span>`;
        const value = typeof p.value === 'number' ? p.value.toFixed(2) : p.value;
        return `${colorDot}${p.seriesName} ${value}`;
      })
      .join('<br/>');
    return `<div>${axisLabel}</div><div>${rows}</div>`;
  }
});
// 营收趋势分析
const trendLegend = {
  show: true,
  top: 10,
  right: 10,
};
const trendYAxis = [{
  type: 'value',
  name: '单位: 元',
  position: 'left',
  min: 0,
  nameTextStyle: {
    color: '#000',
    fontSize: 14,
  },
}];
const trendSeries = computed(() => {
  // 汇总所有支出类型的数据
  let expenseTrend = [];
  if (lineSeries1.value.length > 0) {
    const monthCount = Math.max(...lineSeries1.value.map(item => item.data?.length || 0));
    expenseTrend = Array(monthCount).fill(0);
    lineSeries1.value.forEach(item => {
      if (item.data && Array.isArray(item.data)) {
        item.data.forEach((val, index) => {
          if (index < monthCount) {
            expenseTrend[index] += Number(val) || 0;
          }
        });
      }
    });
  }
  // 汇总所有收入类型的数据
  let incomeTrend = [];
  if (lineSeries0.value.length > 0) {
    const monthCount = Math.max(...lineSeries0.value.map(item => item.data?.length || 0));
    incomeTrend = Array(monthCount).fill(0);
    lineSeries0.value.forEach(item => {
      if (item.data && Array.isArray(item.data)) {
        item.data.forEach((val, index) => {
          if (index < monthCount) {
            incomeTrend[index] += Number(val) || 0;
          }
        });
      }
    });
  }
  return [
    {
      name: '支出',
      type: 'line',
      data: expenseTrend,
      itemStyle: { color: '#1890FF' },
      smooth: true
    },
    {
      name: '收入',
      type: 'line',
      data: incomeTrend,
      itemStyle: { color: '#13C2C2' },
      smooth: true
    }
  ];
});
// 获取最近六个月的范围
const getLastSixMonths = () => {
  const endMonth = dayjs().format('YYYY-MM');
  const startMonth = dayjs().subtract(5, 'month').format('YYYY-MM');
  return [startMonth, endMonth];
};
const getData = async () => {
  if (!dateRange.value || !Array.isArray(dateRange.value) || dateRange.value.length !== 2) {
    return;
  }
  const startDateStr = dateRange.value[0];
  const endDateStr = dateRange.value[1];
  if (!startDateStr || !endDateStr) {
    return;
  }
  // 验证日期格式并转换为完整日期
  const startDate = dayjs(startDateStr);
  const endDate = dayjs(endDateStr);
  if (!startDate.isValid() || !endDate.isValid()) {
    console.error('无效的日期格式');
    return;
  }
  // 更新 x 轴数据
  const monthLabels = generateMonthLabels(startDateStr, endDateStr);
  xAxis0.value[0].data = monthLabels;
  xAxis1.value[0].data = monthLabels;
  // 开始月份拼接第一天,结束月份拼接最后一天
  const entryDateStart = startDate.startOf('month').format('YYYY-MM-DD');
  const entryDateEnd = endDate.endOf('month').format('YYYY-MM-DD');
  try {
    const {code,data} = await reportForms({entryDateStart, entryDateEnd});
    if(code === 200 && data) {
      pageInfo.value = data || {};
      // 安全处理数据,过滤掉 null 或 undefined
      pieData0.value = (data.incomeType || []).filter(item => item && item.typeName).map(item=>({
        name:item.typeName || '',
        value:item.account || 0,
        percent:`${((item.proportion || 0) * 100).toFixed(2)}%`,
        amount:`¥${(item.account || 0).toFixed(2)}`
      }))
      pieData1.value = (data.expenseType || []).filter(item => item && item.typeName).map(item=>({
        name:item.typeName || '',
        value:item.account || 0,
        percent:`${((item.proportion || 0) * 100).toFixed(2)}%`,
        amount:`¥${(item.account || 0).toFixed(2)}`
      }))
    }
  } catch (error) {
    console.error('获取财务指标数据失败:', error);
  }
  try{
    const {code,data} = await reportIncome();
    if(code==200){
      lineSeries0.value = data.map(item=>({
        name:item.typeName,
    const {code,data} = await reportIncome({entryDateStart, entryDateEnd});
    if(code==200 && data && Array.isArray(data)){
      lineSeries0.value = data.filter(item => item && item.typeName).map(item=>({
        name:item.typeName || '',
        type: 'line',
        data:item.account.map(item=>Number(item))
        data:(item.account || []).map(val => Number(val) || 0)
      }))
    }
  }catch (error) {
    console.error('获取财务指标数据失败:', error);
  }
  try{
    const {code,data} = await reportExpense();
    if(code==200){
      lineSeries1.value = data.map(item=>({
        name:item.typeName,
    const {code,data} = await reportExpense({entryDateStart, entryDateEnd});
    if(code==200 && data && Array.isArray(data)){
      lineSeries1.value = data.filter(item => item && item.typeName).map(item=>({
        name:item.typeName || '',
        type: 'line',
        data:item.account.map(item=>Number(item))
        data:(item.account || []).map(val => Number(val) || 0)
      }))
    }
  }catch (error) {
    console.error('获取财务指标数据失败:', error);
@@ -370,30 +704,68 @@
};
// 初始化日期范围(默认当月)
// 初始化
onMounted(() => {
  const today = new Date();
  const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
  firstDayOfMonth.value = firstDay;
  dateRange.value = [dayjs(firstDay).format("YYYY-MM-DD"), dayjs(today).format("YYYY-MM-DD")];
  getData()
  // 设置默认值为最近六个月
  const defaultRange = getLastSixMonths();
  dateRange.value = defaultRange;
  // 使用 nextTick 确保组件完全渲染后再调用
  nextTick(() => {
    getData();
  });
});
// 处理日期范围变化
const handleDateChange = (newRange) => {
  if (newRange && newRange.length === 2) {
    dateRange.value = newRange;
    getData()
// 限制月份选择范围(最多12个月)
const disabledDate = (time) => {
  // 如果没有选择开始月份,不禁用任何日期
  if (!dateRange.value || !Array.isArray(dateRange.value) || !dateRange.value[0]) {
    return false;
  }
  const startMonth = dayjs(dateRange.value[0]);
  const currentMonth = dayjs(time);
  // 如果当前月份在开始月份之前,禁用
  if (currentMonth.isBefore(startMonth, 'month')) {
    return true;
  }
  // 计算最大允许的月份(开始月份 + 11个月 = 12个月)
  const maxMonth = startMonth.add(11, 'month');
  // 禁用超过12个月的月份
  return currentMonth.isAfter(maxMonth, 'month');
};
// 重置日期范围
// 处理月份范围变化
const handleDateChange = (newRange) => {
  if (!newRange || !Array.isArray(newRange) || newRange.length !== 2) {
    return;
  }
  // 验证月份范围不超过12个月
  const startDate = dayjs(newRange[0]);
  const endDate = dayjs(newRange[1]);
  const monthDiff = endDate.diff(startDate, 'month');
  if (monthDiff > 11) {
    proxy.$modal.msgWarning('最多只能选择12个月份');
    // 自动调整为12个月
    const adjustedEnd = startDate.add(11, 'month').format('YYYY-MM');
    dateRange.value = [newRange[0], adjustedEnd];
    getData();
    return;
  }
  dateRange.value = newRange;
  getData();
};
// 重置月份范围
const resetDateRange = () => {
  const today = new Date();
  const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
  dateRange.value = [dayjs(firstDay).format("YYYY-MM-DD"), dayjs(today).format("YYYY-MM-DD")];
  getData()
  // 重置为最近六个月
  dateRange.value = getLastSixMonths();
  getData();
};
</script>
@@ -403,111 +775,220 @@
:root {
  --el-color-primary: #4f46e5;
}
.el-card{
  position: relative;
  border-radius: 12px;
  padding: 14px 10px 10px 10px;
  box-shadow: 0 2px 8px #eee;
  :deep(.el-card__body){
    padding: 10px 20px !important;
  }
  &.bg1{
    background: url(@/assets/icons/png/1.png) no-repeat 100% 100% !important;
  }
  &.bg2{
    background: url(@/assets/icons/png/2.png) no-repeat 100% 100% !important;
  }
  &.bg3{
    background: url(@/assets/icons/png/3.png) no-repeat 100% 100% !important;
  }
  &.bg4{
    background: url(@/assets/icons/png/4.png) no-repeat 100% 100% !important;
  }
  &.bg5{
    background: url(@/assets/icons/png/5.png) no-repeat 100% 100% !important;
  }
}
.grid-container {
  /* grid 容器基础样式 */
/* 统计卡片样式 */
.stats-cards {
  display: grid;
  gap: 1rem; /* gap-4 对应 1rem (16px) */
  margin-bottom: 2rem; /* mb-8 对应 2rem (32px) */
  grid-template-columns: repeat(5, 1fr);
  gap: 20px;
  margin-bottom: 20px;
}
.stat-card {
  background: #fff;
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  padding: 20px;
  display: flex;
  align-items: center;
  gap: 15px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  transition: all 0.3s;
  
  p{
    font-size: 22px;
    margin-top: 0px;
    color: #fff;
  }
  h3{
    font-size: 36px;
    font-weight: 500;
    font-family: 'MyCustomFont', sans-serif;
    margin: 10px 0;
    color: #fff;
  &:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    transform: translateY(-2px);
  }
  
}
/* 移动端默认样式 (grid-cols-1) */
.grid-container {
  grid-template-columns: repeat(1, minmax(0, 1fr));
}
/* 小屏幕及以上 (sm:grid-cols-2) */
@media (min-width: 640px) {
  .grid-container {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  .stat-icon {
    width: 48px;
    height: 48px;
    flex-shrink: 0;
    img {
      width: 100%;
      height: 100%;
      object-fit: contain;
    }
  }
  .stat-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  .stat-label {
    font-size: 14px;
    color: #666;
    line-height: 1.2;
  }
  .stat-value {
    font-size: 24px;
    font-weight: 600;
    color: #333;
    line-height: 1.2;
  }
  .stat-trend {
    font-size: 12px;
    line-height: 1.2;
    &.trend-up {
      color: #f56c6c;
    }
    &.trend-down {
      color: #67c23a;
    }
  }
}
/* 大屏幕及以上 (lg:grid-cols-5) */
@media (min-width: 1024px) {
  .grid-container {
    grid-template-columns: repeat(5, minmax(0, 1fr));
/* 图表行布局 */
.charts-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin-bottom: 20px;
}
.chart-card {
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  :deep(.el-card__body) {
    padding: 20px !important;
  }
}
/* 卡片悬停效果增强 */
.el-card:hover {
  transform: translateY(-2px);
.trend-chart-card {
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  :deep(.el-card__body) {
    padding: 20px !important;
  }
}
.echarts{
/* 饼图容器 */
.pie-chart-container {
  position: relative;
  .pie-stats {
    display: flex;
    justify-content: space-between;
    gap: 20px;
    margin-top: 20px;
    .bar-stat-item {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 8px;
      padding: 15px;
      background: #f5f7fa;
      border-radius: 6px;
      flex: 1;
      .bar-stat-label {
        font-size: 14px;
        color: #666;
      }
      .bar-stat-value {
        font-size: 18px;
        font-weight: 600;
        color: #333;
      }
    }
  }
}
/* 柱状图头部统计 */
.bar-chart-header {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 20px;
  .bar-stat-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 15px;
    background: #f5f7fa;
    border-radius: 6px;
    flex: 1;
    .bar-stat-label {
      font-size: 14px;
      color: #666;
    }
    .bar-stat-value {
      font-size: 18px;
      font-weight: 600;
      color: #333;
    }
  }
}
/* 图表容器样式 */
.el-chart {
  width: 100%;
  height: 100%;
}
/* 标题样式 */
.section-title {
   position: relative;
   font-size: 18px;
   color: #333;
   padding-left: 10px;
   margin-bottom: 10px;
   font-weight: 700;
  position: relative;
  font-size: 18px;
  color: #333;
  padding-left: 12px;
  margin-bottom: 20px;
  font-weight: 700;
  &::before {
    position: absolute;
    left: 0;
    top: 2px;
    content: '';
    width: 4px;
    height: 18px;
    background-color: #002FA7;
    border-radius: 2px;
  }
}
.section-title::before {
   position: absolute;
   left: 0;
   top: 0px;
   content: '';
   width: 4px;
   height: 18px;
   background-color: #002FA7;
   border-radius: 2px;
/* 响应式设计 */
@media (max-width: 1400px) {
  .stats-cards {
    grid-template-columns: repeat(3, 1fr);
  }
}
.chart-num{
  position: absolute;
  z-index: 3;
  top: 92px;
  left: 92px;
  display: flex;
  flex-direction: column;
  justify-content: center;
@media (max-width: 1024px) {
  .stats-cards {
    grid-template-columns: repeat(2, 1fr);
  }
  .charts-row {
    grid-template-columns: 1fr;
  }
}
@media (max-width: 640px) {
  .stats-cards {
    grid-template-columns: 1fr;
  }
}
</style>