Browse Source

营销数据的裂变分析页面

ylong 7 months ago
parent
commit
22dec8c6ef
1 changed files with 619 additions and 470 deletions
  1. 619 470
      src/views/marketing/ipMrakup/fission/index.vue

+ 619 - 470
src/views/marketing/ipMrakup/fission/index.vue

@@ -1,506 +1,655 @@
 <template>
-  <div class="fission-analysis-container">
-    <el-card class="analysis-card mt-4" shadow="never">
-      <div class="date-selector-container">
-        <!-- Top Navigation Section -->
-        <div class="date-period-selector">
-          <el-button
-            :class="{ active: activePeriod === 'yesterday' }"
-            @click="changePeriod('yesterday')"
-            >昨日</el-button
-          >
-          <el-button
-            :class="{ active: activePeriod === '7d' }"
-            @click="changePeriod('7d')"
-            >7日</el-button
-          >
-          <el-button
-            :class="{ active: activePeriod === '15d' }"
-            @click="changePeriod('15d')"
-            >15日</el-button
-          >
-          <el-button
-            :class="{ active: activePeriod === '30d' }"
-            @click="changePeriod('30d')"
-            >30日</el-button
-          >
-
-          <el-date-picker
-            v-model="dateRange"
-            type="date"
-            placeholder="选择日期"
-            format="YYYY-MM-DD"
-            value-format="YYYY-MM-DD"
-            :clearable="false"
-            :disabled-date="(date) => false"
-            @change="fetchData"
-          />
-          <div class="date-action-buttons">
-            <el-button type="primary" @click="fetchData">提交</el-button>
-            <el-button @click="resetFilters">重置</el-button>
-          </div>
-        </div>
-      </div>
-
-      <!-- Metrics Cards -->
-      <div class="metrics-cards">
-        <el-row :gutter="20">
-          <el-col :span="6">
-            <el-card shadow="never" class="metric-card">
-              <div class="metric-title">今日助力人数</div>
-              <div class="metric-date">{{ currentDate }}</div>
-              <div class="metric-value-container">
-                <div class="metric-label">合计</div>
-                <div class="metric-value">{{ metrics.totalUsers }}人</div>
-              </div>
-              <div class="metric-comparison"
-                >环比: {{ metrics.totalUsersChange }}↓</div
-              >
-            </el-card>
-          </el-col>
-          <el-col :span="6">
-            <el-card shadow="never" class="metric-card">
-              <div class="metric-title">今日人均助力次数</div>
-              <div class="metric-date">{{ currentDate }}</div>
-              <div class="metric-value-container">
-                <div class="metric-label">合计</div>
-                <div class="metric-value">{{ metrics.avgHelps }}人</div>
-              </div>
-              <div class="metric-comparison"
-                >环比: {{ metrics.avgHelpsChange }}↓</div
-              >
-            </el-card>
-          </el-col>
-          <el-col :span="6">
-            <el-card shadow="never" class="metric-card">
-              <div class="metric-title">活动书单扫描/参与占比</div>
-              <div class="metric-date">{{ currentDate }}</div>
-              <div class="metric-value-container">
-                <div class="metric-label">合计</div>
-                <div class="metric-value">{{ metrics.scanRate }}</div>
-              </div>
-              <div class="metric-comparison"
-                >环比: {{ metrics.scanRateChange }}↓</div
-              >
-            </el-card>
-          </el-col>
-          <el-col :span="6">
-            <el-card shadow="never" class="metric-card">
-              <div class="metric-title">今日助力新用户占比</div>
-              <div class="metric-date">{{ currentDate }}</div>
-              <div class="metric-value-container">
-                <div class="metric-label">合计</div>
-                <div class="metric-value">{{ metrics.newUserRate }}</div>
-              </div>
-              <div class="metric-comparison"
-                >环比: {{ metrics.newUserRateChange }}↓</div
-              >
-            </el-card>
-          </el-col>
-        </el-row>
-      </div>
-
-      <!-- Numbers Cards -->
-      <div class="number-metrics">
-        <el-row :gutter="20">
-          <el-col :span="4">
-            <el-card shadow="hover" class="number-card">
-              <div class="number-value">{{ stats.participants }}</div>
-              <div class="number-label">参与用户数</div>
-            </el-card>
-          </el-col>
-          <el-col :span="4">
-            <el-card shadow="hover" class="number-card">
-              <div class="number-value">{{ stats.helps }}</div>
-              <div class="number-label">助力次数</div>
-            </el-card>
-          </el-col>
-          <el-col :span="4">
-            <el-card shadow="hover" class="number-card">
-              <div class="number-value">{{ stats.ordersCount }}</div>
-              <div class="number-label">订单总数</div>
-            </el-card>
-          </el-col>
-          <el-col :span="4">
-            <el-card shadow="hover" class="number-card">
-              <div class="number-value">{{ stats.totalOrderAmount }}</div>
-              <div class="number-label">订单预估总金额</div>
-            </el-card>
-          </el-col>
-          <el-col :span="4">
-            <el-card shadow="hover" class="number-card">
-              <div class="number-value">{{ stats.totalAddedPoints }}</div>
-              <div class="number-label">加价积分总金额</div>
-            </el-card>
-          </el-col>
-          <el-col :span="4">
-            <el-card shadow="hover" class="number-card">
-              <div class="number-value">{{ stats.totalSaleAmount }}</div>
-              <div class="number-label">实际成价总金额</div>
-            </el-card>
-          </el-col>
-        </el-row>
-      </div>
-
-      <!-- Chart Section -->
-      <div class="trend-chart-section">
-        <div class="chart-title">30日分享人数趋势</div>
-        <div class="chart-container" ref="chartRef"></div>
-      </div>
-    </el-card>
-  </div>
+    <div class="fission-analysis-container">
+        <el-card class="analysis-card mt-4" shadow="never">
+            <div class="date-selector-container">
+                <!-- Top Navigation Section -->
+                <div class="date-period-selector">
+                    <el-button
+                        :class="{ active: activePeriod === 'yesterday' }"
+                        @click="changePeriod('yesterday')"
+                        >昨日</el-button
+                    >
+                    <el-button
+                        :class="{ active: activePeriod === '7d' }"
+                        @click="changePeriod('7d')"
+                        >7日</el-button
+                    >
+                    <el-button
+                        :class="{ active: activePeriod === '15d' }"
+                        @click="changePeriod('15d')"
+                        >15日</el-button
+                    >
+                    <el-button
+                        :class="{ active: activePeriod === '30d' }"
+                        @click="changePeriod('30d')"
+                        >30日</el-button
+                    >
+
+                    <el-date-picker
+                        v-model="dateRange"
+                        type="daterange"
+                        range-separator="至"
+                        start-placeholder="开始日期"
+                        end-placeholder="结束日期"
+                        format="YYYY-MM-DD"
+                        value-format="YYYY-MM-DD"
+                        :clearable="false"
+                        :disabled-date="(date) => false"
+                        @change="handleDateRangeChange"
+                    />
+                    <div class="date-action-buttons">
+                        <el-button type="primary" @click="fetchData"
+                            >搜索</el-button
+                        >
+                        <el-button @click="resetFilters">重置</el-button>
+                    </div>
+                </div>
+            </div>
+
+            <!-- Metrics Cards -->
+            <div class="metrics-cards">
+                <el-row :gutter="20" class="metrics-row">
+                    <el-col v-for="(item, index) in metricsCardsData" :key="index" class="metric-col">
+                        <el-card shadow="never" class="metric-card">
+                            <div class="metric-title">{{ item.title }}</div>
+                            <div class="metric-date">{{ displayDateRange }}</div>
+                            <div class="metric-value-container">
+                                <div class="metric-label">合计</div>
+                                <div class="metric-value">{{ item.value }}</div>
+                            </div>
+                            <div class="metric-comparison">
+                                环比 {{ item.change }}%
+                                <template v-if="parseFloat(item.change) > 0"><span class="arrow-up">↑</span></template>
+                                <template v-else-if="parseFloat(item.change) < 0"><span class="arrow-down">↓</span></template>
+                            </div>
+                        </el-card>
+                    </el-col>
+                </el-row>
+            </div>
+
+            <!-- Numbers Cards -->
+            <div class="number-metrics">
+                <el-row :gutter="20">
+                    <el-col :span="4" v-for="(item, index) in numberMetricsData" :key="index">
+                        <el-card shadow="hover" class="number-card">
+                            <div class="number-value">{{ item.value }}</div>
+                            <div class="number-label">{{ item.label }}</div>
+                        </el-card>
+                    </el-col>
+                </el-row>
+            </div>
+
+            <!-- Chart Section -->
+            <div class="trend-chart-section">
+                <div class="chart-title">30日分享人数趋势</div>
+                <div class="chart-container" ref="chartRef"></div>
+            </div>
+        </el-card>
+    </div>
 </template>
 
 <script setup>
-  import { ref, reactive, onMounted, computed, watch } from 'vue';
-  import { ArrowDown } from '@element-plus/icons-vue';
-  import * as echarts from 'echarts';
-  import dayjs from 'dayjs';
-
-  // State
-  const activePeriod = ref('yesterday');
-  const timeSliderValue = ref(12);
-  const datePopoverVisible = ref(false);
-  const dateDisplayOption = ref('1');
-  const chartRef = ref(null);
-  let chart = null;
-  // Initialize dateRange with current date
-  const dateRange = ref(dayjs().toDate());
-
-  // Mock data
-  const metrics = reactive({
-    totalUsers: '266',
-    totalUsersChange: '56%',
-    avgHelps: '266',
-    avgHelpsChange: '56%',
-    scanRate: '55%',
-    scanRateChange: '56%',
-    newUserRate: '55%',
-    newUserRateChange: '56%'
-  });
-
-  const stats = reactive({
-    participants: '44351316',
-    helps: '44351316',
-    ordersCount: '44351316',
-    totalOrderAmount: '44351316',
-    totalAddedPoints: '48415555',
-    totalSaleAmount: '48415555'
-  });
-
-  const currentDate = computed(() => {
-    return dayjs().format('YYYY-MM-DD');
-  });
-
-  // Methods
-  const formatTooltip = (val) => {
-    return `${val}:00`;
-  };
-
-  const changePeriod = (period) => {
-    activePeriod.value = period;
-
-    // Update dateRange based on the selected period
-    switch (period) {
-      case 'yesterday':
-        dateRange.value = dayjs().subtract(1, 'day').toDate();
-        break;
-      case '7d':
-        dateRange.value = dayjs().subtract(7, 'day').toDate();
-        break;
-      case '15d':
-        dateRange.value = dayjs().subtract(15, 'day').toDate();
-        break;
-      case '30d':
-        dateRange.value = dayjs().subtract(30, 'day').toDate();
-        break;
-      default:
-        dateRange.value = dayjs().toDate();
-    }
-
-    fetchData();
-  };
-
-  const fetchData = () => {
-    // Here you would fetch real data from your API
-    console.log('Fetching data for period:', activePeriod.value);
-    console.log('Selected date:', dayjs(dateRange.value).format('YYYY-MM-DD'));
-    console.log('Time:', timeSliderValue.value);
-
-    // Mock API call and data update
-    // In a real app, you would make an API request and update the state
-  };
-
-  const resetFilters = () => {
-    activePeriod.value = 'yesterday';
-    dateRange.value = dayjs().subtract(1, 'day').toDate(); // Reset dateRange to yesterday
-    timeSliderValue.value = 12;
-    fetchData();
-  };
-
-  const initChart = () => {
-    if (chartRef.value) {
-      chart = echarts.init(chartRef.value);
-
-      // Sample data for the chart
-      const option = {
-        tooltip: {
-          trigger: 'axis',
-          axisPointer: {
-            type: 'shadow'
-          }
-        },
-        grid: {
-          left: '3%',
-          right: '4%',
-          bottom: '3%',
-          containLabel: true
-        },
-        xAxis: {
-          type: 'category',
-          data: ['1月', '2月', '3月', '4月', '5月'],
-          boundaryGap: false
-        },
-        yAxis: {
-          type: 'value',
-          min: 0,
-          max: 250,
-          interval: 50
-        },
-        series: [
-          {
-            name: '分享人数',
-            type: 'line',
-            data: [100, 140, 220, 120, 130],
-            smooth: true,
-            lineStyle: {
-              color: '#5470c6',
-              width: 2
+    import { ref, onMounted, computed, watch } from 'vue';
+    import * as echarts from 'echarts';
+    import dayjs from 'dayjs';
+    import request from '@/utils/request';
+    import { ElMessage } from 'element-plus';
+
+    // State
+    const activePeriod = ref('7d');
+    const chartRef = ref(null);
+    let chart = null;
+    // Track if custom date range is used
+    const isCustomDateRange = ref(false);
+    // Initialize dateRange with 7 days range
+    const dateRange = ref([
+        dayjs().subtract(7, 'day').format('YYYY-MM-DD'),
+        dayjs().format('YYYY-MM-DD')
+    ]);
+
+    // API data
+    const statisticData = ref(null);
+
+    // Metrics computed from API response
+    const metrics = computed(() => {
+        if (!statisticData.value) {
+            return {
+                totalUsers: '',
+                totalUsersChange: '0',
+                orders: '',
+                ordersChange: '0',
+                scanNum: '',
+                scanNumChange: '0',
+                scanRate: '',
+                scanRateChange: '0',
+                newUserRate: '',
+                newUserRateChange: '0'
+            };
+        }
+
+        const data = statisticData.value.data;
+
+        // Helper function to format comparison values consistently
+        const formatComparison = (value) => {
+            if (value === null || value === undefined) return '0';
+            // Remove % if present and return just the number as string
+            return String(value).replace('%', '');
+        };
+
+        return {
+            totalUsers: data.upsellHelpNum || '0',
+            totalUsersChange: formatComparison(data.upsellHelpNumComparison),
+            orders: data.upsellOrderNum || '0',
+            ordersChange: formatComparison(data.upsellOrderNumComparison),
+            scanNum: data.upsellScanNum || '0',
+            scanNumChange: formatComparison(data.upsellScanNumComparison),
+            scanRate: data.scanJoinRate ? `${data.scanJoinRate}%` : '0%',
+            scanRateChange: formatComparison(data.scanJoinRateComparison),
+            newUserRate: data.newUserRate ? `${data.newUserRate}%` : '0%',
+            newUserRateChange: formatComparison(data.newUserRateComparison)
+        };
+    });
+
+    const stats = computed(() => {
+        if (!statisticData.value) {
+            return {
+                participants: '0',
+                helps: '0',
+                ordersCount: '0',
+                totalOrderAmount: '0',
+                totalAddedPoints: '0',
+                totalSaleAmount: '0'
+            };
+        }
+
+        const data = statisticData.value.data;
+
+        return {
+            participants: data.upsellShareNum || '0',
+            helps: data.upsellHelpTimes || '0',
+            ordersCount: data.orderBookNum || '0',
+            totalOrderAmount: data.orderTotalMoney || '0',
+            totalAddedPoints: data.upsellTotalMoney || '0',
+            totalSaleAmount: data.upsellFinalTotalMoney || '0'
+        };
+    });
+
+    // 数字指标卡片数据
+    const numberMetricsData = computed(() => {
+        return [
+            { label: '参与用户数', value: stats.value.participants },
+            { label: '助力次数', value: stats.value.helps },
+            { label: '订单总数', value: stats.value.ordersCount },
+            { label: '订单预估总金额', value: stats.value.totalOrderAmount },
+            { label: '加价预估总金额', value: stats.value.totalAddedPoints },
+            { label: '实际加价金额', value: stats.value.totalSaleAmount }
+        ];
+    });
+
+    // 度量指标卡片数据
+    const metricsCardsData = computed(() => {
+        return [
+            { 
+                title: '助力人数', 
+                value: `${metrics.value.totalUsers}人`, 
+                change: metrics.value.totalUsersChange 
+            },
+            { 
+                title: '参与活动订单数', 
+                value: `${metrics.value.orders}单`, 
+                change: metrics.value.ordersChange 
+            },
+            { 
+                title: '活动书单扫描次数', 
+                value: `${metrics.value.scanNum}次`, 
+                change: metrics.value.scanNumChange 
             },
-            symbol: 'circle',
-            symbolSize: 8,
-            itemStyle: {
-              color: '#5470c6'
+            { 
+                title: '活动书单扫描/参与占比', 
+                value: metrics.value.scanRate, 
+                change: metrics.value.scanRateChange 
+            },
+            { 
+                title: '助力新用户占比', 
+                value: metrics.value.newUserRate, 
+                change: metrics.value.newUserRateChange 
             }
-          }
-        ]
-      };
-
-      chart.setOption(option);
-
-      // Handle resize
-      window.addEventListener('resize', () => {
-        chart.resize();
-      });
-
-      // Use ResizeObserver to detect and respond to size changes of the chart container
-      // This will ensure the chart resizes properly when tabs change
-      const resizeObserver = new ResizeObserver(() => {
-        chart.resize();
-      });
+        ];
+    });
+
+    // Chart data
+    const chartData = computed(() => {
+        if (!statisticData.value || !statisticData.value.data.chartList) {
+            return {
+                dates: [],
+                values: []
+            };
+        }
+
+        const chartList = statisticData.value.data.chartList;
+        return {
+            dates: chartList.map((item) => item.date),
+            values: chartList.map((item) => item.shareNum)
+        };
+    });
+
+    const currentDate = computed(() => {
+        return dayjs().format('YYYY-MM-DD');
+    });
+
+    const displayDateRange = computed(() => {
+        if (Array.isArray(dateRange.value) && dateRange.value.length === 2) {
+            return `${dateRange.value[0]} - ${dateRange.value[1]}`;
+        } else {
+            return currentDate.value;
+        }
+    });
+
+    // Method to update date range based on period
+    const updateDateRangeByPeriod = (period) => {
+        isCustomDateRange.value = false;
+
+        switch (period) {
+            case 'yesterday':
+                dateRange.value = [
+                    dayjs().subtract(1, 'day').format('YYYY-MM-DD'),
+                    dayjs().subtract(1, 'day').format('YYYY-MM-DD')
+                ];
+                break;
+            case '7d':
+                dateRange.value = [
+                    dayjs().subtract(7, 'day').format('YYYY-MM-DD'),
+                    dayjs().format('YYYY-MM-DD')
+                ];
+                break;
+            case '15d':
+                dateRange.value = [
+                    dayjs().subtract(15, 'day').format('YYYY-MM-DD'),
+                    dayjs().format('YYYY-MM-DD')
+                ];
+                break;
+            case '30d':
+                dateRange.value = [
+                    dayjs().subtract(30, 'day').format('YYYY-MM-DD'),
+                    dayjs().format('YYYY-MM-DD')
+                ];
+                break;
+            default:
+                dateRange.value = [
+                    dayjs().subtract(7, 'day').format('YYYY-MM-DD'),
+                    dayjs().format('YYYY-MM-DD')
+                ];
+        }
+    };
+
+    // Method to handle period change button clicks
+    const changePeriod = (period) => {
+        activePeriod.value = period;
+        updateDateRangeByPeriod(period);
+        fetchData();
+    };
+
+    // Handle date picker change
+    const handleDateRangeChange = () => {
+        isCustomDateRange.value = true;
+        activePeriod.value = '';
+        fetchData();
+    };
+
+    const getDateRangeForAPI = () => {
+        let startDate, endDate;
+
+        if (Array.isArray(dateRange.value) && dateRange.value.length === 2) {
+            startDate = dayjs(dateRange.value[0]).format('YYYY-MM-DD 00:00:00');
+            endDate = dayjs(dateRange.value[1]).format('YYYY-MM-DD 23:59:59');
+        } else {
+            // Fallback if dateRange is not an array
+            startDate = dayjs().subtract(7, 'day').format('YYYY-MM-DD 00:00:00');
+            endDate = dayjs().format('YYYY-MM-DD 23:59:59');
+        }
+
+        return { startDate, endDate };
+    };
+
+    const fetchData = async () => {
+        const { startDate, endDate } = getDateRangeForAPI();
+
+        try {
+            const { data } = await request.get(
+                '/activity/activityUpsellInfo/statistic',
+                {
+                    params: {
+                        startDate,
+                        endDate
+                    }
+                }
+            );
+
+            statisticData.value = data;
+            updateChart();
+        } catch (error) {
+            ElMessage.error('获取数据失败');
+            console.error('Failed to fetch statistic data:', error);
+        }
+    };
+
+    const resetFilters = () => {
+        isCustomDateRange.value = false;
+        activePeriod.value = '7d';
+        updateDateRangeByPeriod('7d');
+        fetchData();
+    };
+
+    const updateChart = () => {
+        if (!chart || !chartData.value.dates.length) return;
+
+        const option = {
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'shadow'
+                }
+            },
+            grid: {
+                left: '3%',
+                right: '4%',
+                bottom: '3%',
+                containLabel: true
+            },
+            xAxis: {
+                type: 'category',
+                data: chartData.value.dates,
+                boundaryGap: false
+            },
+            yAxis: {
+                type: 'value',
+                min: 0
+            },
+            series: [
+                {
+                    name: '分享人数',
+                    type: 'line',
+                    data: chartData.value.values,
+                    smooth: true,
+                    lineStyle: {
+                        color: '#5470c6',
+                        width: 2
+                    },
+                    symbol: 'circle',
+                    symbolSize: 8,
+                    itemStyle: {
+                        color: '#5470c6'
+                    }
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    };
+
+    const initChart = () => {
+        if (chartRef.value) {
+            chart = echarts.init(chartRef.value);
+
+            // Initialize with empty data, will be updated after API call
+            const option = {
+                tooltip: {
+                    trigger: 'axis',
+                    axisPointer: {
+                        type: 'shadow'
+                    }
+                },
+                grid: {
+                    left: '3%',
+                    right: '4%',
+                    bottom: '3%',
+                    containLabel: true
+                },
+                xAxis: {
+                    type: 'category',
+                    data: [],
+                    boundaryGap: false
+                },
+                yAxis: {
+                    type: 'value',
+                    min: 0
+                },
+                series: [
+                    {
+                        name: '分享人数',
+                        type: 'line',
+                        data: [],
+                        smooth: true,
+                        lineStyle: {
+                            color: '#5470c6',
+                            width: 2
+                        },
+                        symbol: 'circle',
+                        symbolSize: 8,
+                        itemStyle: {
+                            color: '#5470c6'
+                        }
+                    }
+                ]
+            };
+
+            chart.setOption(option);
+
+            // Handle resize
+            window.addEventListener('resize', () => {
+                chart.resize();
+            });
+
+            // Use ResizeObserver to detect and respond to size changes of the chart container
+            const resizeObserver = new ResizeObserver(() => {
+                chart.resize();
+            });
+
+            resizeObserver.observe(chartRef.value);
+        }
+    };
+
+    // Method to manually resize chart - can be called from parent components
+    const resizeChart = () => {
+        if (chart) {
+            chart.resize();
+        }
+    };
+
+    // Watch for changes in dateRange to update activePeriod
+    watch(dateRange, (newValue) => {
+        if (isCustomDateRange.value) {
+            activePeriod.value = '';
+        }
+    });
+
+    onMounted(() => {
+        initChart();
+        activePeriod.value = '7d';
+        updateDateRangeByPeriod('7d');
+        fetchData();
+        
+        // Force a resize after component is mounted
+        setTimeout(() => {
+            resizeChart();
+        }, 300);
+    });
+
+    // Expose the resizeChart method to parent components
+    defineExpose({
+        resizeChart
+    });
+</script>
 
-      resizeObserver.observe(chartRef.value);
+<style lang="scss" scoped>
+    .date-selector-container {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        margin-bottom: 20px;
+        border-bottom: 1px solid #ebeef5;
+        padding-bottom: 16px;
     }
-  };
 
-  // Method to manually resize chart - can be called from parent components
-  const resizeChart = () => {
-    if (chart) {
-      chart.resize();
+    .arrow-up {
+        color: #67C23A; /* Element Plus success color (green) */
     }
-  };
-
-  // Watch for changes in dateRange to update activePeriod
-  watch(dateRange, (newValue) => {
-    const selectedDate = dayjs(newValue);
-    const today = dayjs();
-    const yesterday = dayjs().subtract(1, 'day');
-    const days7ago = dayjs().subtract(7, 'day');
-    const days15ago = dayjs().subtract(15, 'day');
-    const days30ago = dayjs().subtract(30, 'day');
-
-    // Check if selected date matches any of the predefined periods
-    if (selectedDate.isSame(yesterday, 'day')) {
-      activePeriod.value = 'yesterday';
-    } else if (selectedDate.isSame(days7ago, 'day')) {
-      activePeriod.value = '7d';
-    } else if (selectedDate.isSame(days15ago, 'day')) {
-      activePeriod.value = '15d';
-    } else if (selectedDate.isSame(days30ago, 'day')) {
-      activePeriod.value = '30d';
-    } else {
-      // If it doesn't match any predefined period, clear the active selection
-      activePeriod.value = '';
+
+    .arrow-down {
+        color: #F56C6C; /* Element Plus danger color (red) */
     }
-  });
 
-  onMounted(() => {
-    initChart();
+    .date-period-selector {
+        display: flex;
+        gap: 10px;
 
-    // Initialize dateRange based on the default activePeriod (yesterday)
-    if (activePeriod.value === 'yesterday') {
-      dateRange.value = dayjs().subtract(1, 'day').toDate();
-    }
+        .el-button {
+            border-radius: 4px;
+        }
 
-    // Force a resize after component is mounted
-    // This ensures the chart properly fills its container
-    setTimeout(() => {
-      resizeChart();
-    }, 300); // Small delay to ensure rendering is complete
-  });
-
-  // Expose the resizeChart method to parent components
-  defineExpose({
-    resizeChart
-  });
-</script>
+        .active {
+            color: #409eff;
+            border-color: #409eff;
+        }
+    }
 
-<style lang="scss" scoped>
-  .date-selector-container {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    margin-bottom: 20px;
-    border-bottom: 1px solid #ebeef5;
-    padding-bottom: 16px;
-  }
-
-  .date-period-selector {
-    display: flex;
-    gap: 10px;
-
-    .el-button {
-      border-radius: 4px;
+    .custom-date-range {
+        display: flex;
+        align-items: center;
+        gap: 16px;
     }
 
-    .active {
-      color: #409eff;
-      border-color: #409eff;
+    .time-range-selector {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+
+        .time-label {
+            font-size: 14px;
+            color: #606266;
+        }
     }
-  }
-
-  .custom-date-range {
-    display: flex;
-    align-items: center;
-    gap: 16px;
-  }
-
-  .time-range-selector {
-    display: flex;
-    align-items: center;
-    gap: 8px;
-
-    .time-label {
-      font-size: 14px;
-      color: #606266;
+
+    .time-slider {
+        width: 200px;
     }
-  }
-
-  .time-slider {
-    width: 200px;
-  }
-
-  .date-action-buttons {
-    display: flex;
-    gap: 8px;
-  }
-
-  .date-display {
-    display: flex;
-    align-items: center;
-    gap: 4px;
-    cursor: pointer;
-    padding: 6px 12px;
-    border: 1px solid #dcdfe6;
-    border-radius: 4px;
-    margin-bottom: 20px;
-  }
-
-  .metrics-cards {
-    margin-bottom: 24px;
-  }
-
-  .metric-card {
-    border: 1px solid #ebeef5;
-    height: 150px;
-
-    .metric-title {
-      font-size: 16px;
-      color: #303133;
-      margin-bottom: 4px;
+
+    .date-action-buttons {
+        display: flex;
+        gap: 8px;
     }
 
-    .metric-date {
-      font-size: 12px;
-      color: #909399;
-      margin-bottom: 8px;
+    .date-display {
+        display: flex;
+        align-items: center;
+        gap: 4px;
+        cursor: pointer;
+        padding: 6px 12px;
+        border: 1px solid #dcdfe6;
+        border-radius: 4px;
+        margin-bottom: 20px;
     }
 
-    .metric-value-container {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      margin-bottom: 4px;
-
-      .metric-label {
-        font-size: 14px;
-        color: #606266;
-      }
-
-      .metric-value {
-        font-size: 26px;
-        font-weight: bold;
-        color: #303133;
-      }
+    .metrics-cards {
+        margin-bottom: 24px;
     }
 
-    .metric-comparison {
-      font-size: 14px;
-      color: #606266;
+    .metrics-row {
+        display: flex;
+        flex-wrap: nowrap;
     }
-  }
-
-  .number-metrics {
-    margin-bottom: 24px;
-  }
-
-  .number-card {
-    text-align: center;
-    padding: 16px;
-    height: 120px;
-
-    .number-value {
-      font-size: 22px;
-      font-weight: bold;
-      color: #f6a623;
-      margin-bottom: 8px;
+
+    .metric-col {
+        flex: 1;
     }
 
-    .number-label {
-      font-size: 14px;
-      color: #606266;
+    .metric-card {
+        border: 1px solid #ebeef5;
+        height: 150px;
+        position: relative;
+
+        &:first-child {
+            border-left: 3px solid #409eff;
+        }
+
+        .metric-title {
+            font-size: 14px;
+            color: #303133;
+            margin-bottom: 4px;
+        }
+
+        .metric-date {
+            font-size: 12px;
+            color: #909399;
+        }
+
+        .metric-value-container {
+            display: flex;
+            margin-bottom: 4px;
+            align-items: center;
+
+            .metric-label {
+                font-size: 14px;
+                color: #606266;
+                margin-right: 10px;
+            }
+
+            .metric-value {
+                font-size: 32px;
+                font-weight: bold;
+                color: #303133;
+            }
+        }
+
+        .metric-comparison {
+            font-size: 12px;
+            color: #606266;
+            position: absolute;
+            bottom: 12px;
+        }
     }
-  }
 
-  .trend-chart-section {
-    margin-top: 24px;
-    width: 100%;
+    .number-metrics {
+        margin-bottom: 24px;
+    }
 
-    .chart-title {
-      font-size: 16px;
-      color: #303133;
-      margin-bottom: 16px;
+    .number-card {
+        text-align: center;
+        padding: 16px;
+        height: 120px;
+
+        .number-value {
+            font-size: 22px;
+            font-weight: bold;
+            color: #f6a623;
+            margin-bottom: 8px;
+        }
+
+        .number-label {
+            font-size: 14px;
+            color: #606266;
+        }
     }
 
-    .chart-container {
-      height: 350px;
-      width: 100%;
-      position: relative;
-      overflow: hidden;
+    .trend-chart-section {
+        margin-top: 24px;
+        width: 100%;
+
+        .chart-title {
+            font-size: 16px;
+            color: #303133;
+            margin-bottom: 16px;
+        }
+
+        .chart-container {
+            height: 350px;
+            width: 100%;
+            position: relative;
+            overflow: hidden;
+        }
     }
-  }
 
-  .date-popover-content {
-    padding: 12px;
-  }
+    .date-popover-content {
+        padding: 12px;
+    }
 </style>