ChartGroupCard.vue 7.5 KB
<template>
  <div class="md:flex">
    <template v-for="(item, index) in dataList" :key="item.title">
      <ChartCard
        :loading="loading"
        :title="item.title"
        :total="item.total"
        class="md:w-1/4 w-full !md:mt-0 !mt-4"
        :class="[index + 1 < 4 && '!md:mr-4']"
      >
        <template #action>
          <a-tooltip :title="item.tooltip">
            <Icon :icon="item.icon" :size="20" />
          </a-tooltip>
        </template>
        <div v-if="type === 'chart'">
          <div v-if="index === 0" class="p-2 px-4 flex justify-between">
            <span>平均问答次数</span>
            <span>{{ formatNumber(statistics.averageCount) }}</span>
          </div>
          <SingleLine v-if="index === 1" :option="lineOption" :chartData="dailyCountsData" :seriesColor="seriesColor" height="50px" />

          <Bar v-if="index === 2" :option="barOption" :chartData="dailyRejectedCountsData" :seriesColor="seriesColor" height="50px" />

          <Trend
            v-if="index === 3"
            :term="statistics.growthRate >= 0 ? '日增长' : '日下降'"
            :percentage="Math.abs(statistics.growthRate)"
            :type="statistics.growthRate >= 0"
          />
        </div>
        <div v-else>
          <SingleLine :seriesColor="seriesColor" v-if="index === 0" :option="option" height="50px" />

          <SingleLine :seriesColor="seriesColor" v-if="index === 1" :option="option" :chartData="dailyCountsData" height="50px" />

          <Bar :seriesColor="seriesColor" v-if="index === 2" :option="option" :chartData="dailyRejectedCountsData" height="50px" />

          <Progress v-if="index === 3" :percent="78" :show-info="false" />
        </div>
        <template #footer v-if="type === 'chart'">
          <span v-if="index !== 3"
            >{{ item.footer }}<span>{{ item.value }}</span></span
          >
          <!--          <Trend term="周同比" :percentage="12" v-if="index === 3" />-->
          <!--          <Trend term="日同比" :percentage="11" v-if="index === 3" :type="false" />-->
        </template>
        <template #footer v-else>
          <span
            >{{ item.footer }}<span>{{ item.value }}</span></span
          >
        </template>
      </ChartCard>
    </template>
  </div>
</template>
<script lang="ts" setup>
  import { ref, computed, watch, toRaw } from 'vue';
  import { Icon } from '/@/components/Icon';
  import { Progress } from 'ant-design-vue';
  import ChartCard from '/@/components/chart/ChartCard.vue';
  import Trend from '/@/components/chart/Trend.vue';
  import Bar from '/@/components/chart/Bar.vue';
  import SingleLine from '/@/components/chart/SingleLine.vue';
  import { bdcCardList } from '../data';
  import { useRootSetting } from '/@/hooks/setting/useRootSetting';

  const { getThemeColor } = useRootSetting();
  const props = defineProps({
    statistics: {
      type: Object,
      default: () => ({
        todayCount: 0,
        rejectedCount: 0,
        totalCount: 0,
        averageCount: 0,
        monthlyData: [],
      }),
    },
    loading: {
      type: Boolean,
    },
    type: {
      type: String,
      default: 'chart',
    },
  });
  const dailyCountsData = computed(() => {
    if (!props.statistics.dailyCounts) return [];
    return props.statistics.dailyCounts.map((item) => ({
      name: formatDayLabel(item.date),
      value: item.count,
    }));
  });

  const dailyRejectedCountsData = computed(() => {
    if (!props.statistics.dailyRejectedCounts) return [];
    return props.statistics.dailyRejectedCounts.map((item) => ({
      name: formatDayLabel(item.date),
      value: item.count,
    }));
  });

  const formatDayLabel = (dateStr: string) => {
    const date = new Date(dateStr);
    console.log('格式化日期:', dateStr);
    return `${date.getMonth() + 1}/${date.getDate()}`; // 格式: 月/日
  };
  const option = ref({
    xAxis: {
      show: true, // 显示X轴
      type: 'category',
      boundaryGap: false,
    },
    yAxis: {
      show: true, // 显示Y轴
      boundaryGap: false,
      min: 0, // 设置最小值
    },
    grid: {
      top: 5,
      bottom: 5,
      left: 5,
      right: 5,
    },
  });
  // 分离折线图和柱状图配置
  const lineOption = ref({
    tooltip: {
      trigger: 'axis',
      formatter: (params) => {
        const param = params[0];
        return `${param.name}<br/>问答次数: ${param.value}`;
      },
    },
    xAxis: {
      type: 'category',
      data: [], // 由组件内部填充
      axisLabel: { show: false },
      boundaryGap: false,
    },
    yAxis: {
      type: 'value',
      min: 0,
      axisLabel: { show: false },
    },
    grid: {
      top: 10,
      bottom: 10,
      left: 10,
      right: 10,
      containLabel: true,
    },
  });

  const barOption = ref({
    tooltip: {
      trigger: 'axis',
      formatter: (params) => {
        const param = params[0];
        return `${param.name}<br/>拒绝次数: ${param.value}`;
      },
    },
    xAxis: {
      type: 'category',
      data: [], // 由组件内部填充
      axisLabel: { show: false },
    },
    yAxis: {
      type: 'value',
      min: 0,
      axisLabel: { show: false },
    },
    grid: {
      top: 10,
      bottom: 10,
      left: 10,
      right: 10,
      containLabel: true,
    },
  });
  const seriesColor = computed(() => {
    return getThemeColor.value;
  });
  console.log('ChartGroupCard 组件渲染,接收到的 statistics:', toRaw(props.statistics));
  // 动态生成数据
  // 修复计算属性:使用props.statistics直接访问
  const dataList = computed(() => {
    if (props.type === 'dbc') {
      return bdcCardList;
    }

    // 使用解构确保响应式依赖
    const { todayCount, rejectedCount, totalCount, growthRate } = props.statistics;

    return [
      {
        title: '累计问答次数',
        icon: 'visit-count|svg',
        total: totalCount || 0,
        tooltip: '系统所有问答记录,从2025年7月1日开始的第一次问答至今所有累计:' + totalCount,
      },
      {
        title: '今日问答次数',
        icon: 'total-sales|svg',
        total: todayCount || 0,
        color: 'blue',
        tooltip: '系统今日进行回答的问题数量:' + todayCount,
      },
      {
        title: '拒绝回答次数',
        icon: 'download-count|svg',
        total: rejectedCount || 0,
        color: 'orange',
        tooltip: '系统拒绝回答的问题数量总计:' + rejectedCount,
      },
      {
        title: '对比前一日增长同比',
        icon: 'transaction|svg',
        total: formatGrowthRate(growthRate),
        tooltip: '系统对比今天和昨天的数据增长比,计算公式为(今日回答总量-前一日回答总量/前一日回答总量)* 100',
      },
    ];
  });
  // 添加数字格式化函数
  const formatNumber = (value: number) => {
    if (isNaN(value)) return '0';
    return value.toFixed(2);
  };

  // 格式化增长率显示
  function formatGrowthRate(rate: number): string {
    if (rate > 0) {
      return `+${rate.toFixed(2)}%`;
    } else if (rate < 0) {
      return `${rate.toFixed(2)}%`;
    }
    return '0.00%';
  }

  // 监测 props.statistics 的变化
  watch(
    () => props.statistics,
    (newStatistics) => {
      console.log('statistics 更新:', newStatistics);
      // 这里可以添加其他处理逻辑,如果有必要的话
    },
    { deep: true }
  );

  // 使用 toRaw 确保打印真实值
  watch(
    () => props.statistics,
    (newVal) => {
      console.log('子组件接收到新的 statistics:', toRaw(newVal));
    },
    { deep: true, immediate: true }
  );
</script>