소스 검색

1. 重构播放统计页面前端;2. 修复了播放统计不可滚动的bug;3. 修复播放统计前端部分不可滚动的bug

Shinohara Haruna 6 달 전
부모
커밋
6d86095309
1개의 변경된 파일534개의 추가작업 그리고 451개의 파일을 삭제
  1. 534 451
      smsb-plus-ui/src/views/smsb/dashboard/play.vue

+ 534 - 451
smsb-plus-ui/src/views/smsb/dashboard/play.vue

@@ -1,162 +1,307 @@
 <template>
-  <el-container>
-    <el-header>
-      <el-card shadow="hover" style="margin-top: 10px">
-        <el-row justify="end" align="middle">
-          <el-col :span="19" style="text-align: right">
-            <el-radio-group v-model="timeRadio" size="small" @change="handleDateRangeChange">
-              <!--              <el-radio-button label="今日" value="today" />-->
-              <el-radio-button label="近7天" value="week" />
-              <el-radio-button label="近30天" value="month" />
-            </el-radio-group>
-          </el-col>
-          <el-col :span="5" style="text-align: right">
-            <el-date-picker v-model="dateRange" type="daterange" range-separator="-" start-placeholder="开始日期"
-              end-placeholder="结束日期" style="margin-left: 10px; margin-right: 30px" />
-          </el-col>
-        </el-row>
-      </el-card>
-    </el-header>
-
-    <el-main style="margin-top: 20px">
-      <el-row :gutter="20">
-        <el-col :span="12">
-          <el-card shadow="hover" class="stat-card">
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <h2 style="color: green; font-size: 30px">{{ totalNum }}</h2>
-                <p class="success">总内容量</p>
+  <div class="play-dashboard-page">
+    <el-container>
+      <el-main class="scrollable-main main-content">
+        <div class="play-dashboard-header">
+          <el-card shadow="hover" class="header-card">
+            <el-row justify="end" align="middle">
+              <el-col :span="19" class="text-right">
+                <el-radio-group v-model="timeRadio" size="small" @change="handleDateRangeChange">
+                  <el-radio-button label="近7天" value="week" />
+                  <el-radio-button label="近30天" value="month" />
+                </el-radio-group>
               </el-col>
-              <el-col :span="16">
-                <div ref="createLine" style="height: 150px"></div>
+              <el-col :span="5" class="text-right">
+                <el-date-picker v-model="dateRange" type="daterange" range-separator="-" start-placeholder="开始日期"
+                  end-placeholder="结束日期" class="date-picker" />
               </el-col>
             </el-row>
           </el-card>
-        </el-col>
-        <el-col :span="6">
-          <el-card shadow="hover" class="stat-card">
-            <h2 style="color: orange; font-size: 30px">{{ imageNum }}</h2>
-            <p class="warning">图片素材</p>
-          </el-card>
-        </el-col>
-        <el-col :span="6">
-          <el-card shadow="hover" class="stat-card">
-            <h2 style="color: green; font-size: 30px">{{ videoNum }}</h2>
-            <p class="success">视频素材</p>
-          </el-card>
-        </el-col>
-      </el-row>
-      <el-row :gutter="20" style="margin-top: 20px">
-        <el-col :span="18">
-          <el-row :gutter="20">
-            <el-col :span="8">
-              <el-card shadow="hover">
-                <h3>内容占比</h3>
-                <div ref="typePie" class="chart-placeholder"></div>
-              </el-card>
-            </el-col>
-            <el-col :span="8">
-              <el-card shadow="hover">
-                <h3>类型占比</h3>
-                <div ref="tagPie" class="chart-placeholder"></div>
-              </el-card>
-            </el-col>
-            <el-col :span="8">
-              <el-card shadow="hover">
-                <h3>素材统计</h3>
-                <div ref="typeAndTag" class="chart-placeholder"></div>
-              </el-card>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20" style="margin-top: 20px">
-            <el-col :span="12">
-              <el-card shadow="hover" style="height: 330px">
-                <h3>空间使用情况</h3>
-                <div style="margin-top: 50px">
-                  <h2>已用空间:{{ diskUsed }}GB/{{ diskTotal }}GB</h2>
-                </div>
-
-                <div class="disk-progress">
-                  <el-row>
-                    <el-col :span="2">
-                      <h3>图片:</h3>
-                    </el-col>
-                    <el-col :span="22">
-                      <el-progress :text-inside="true" :stroke-width="26" :percentage="diskImage" />
-                    </el-col>
-                  </el-row>
-                </div>
-                <div class="disk-progress">
-                  <el-row>
-                    <el-col :span="2">
-                      <h3>视频:</h3>
-                    </el-col>
-                    <el-col :span="22">
-                      <el-progress :text-inside="true" :stroke-width="26" status="success" :percentage="diskVideo" />
-                    </el-col>
-                  </el-row>
-                </div>
-              </el-card>
-            </el-col>
-            <el-col :span="12">
-              <el-card shadow="hover">
-                <h3>播放时长统计</h3>
-                <div ref="onlineTimeLine" class="chart-placeholder"></div>
-              </el-card>
-            </el-col>
-          </el-row>
-        </el-col>
-        <el-col :span="6">
-          <el-card shadow="hover">
-            <div>
-              <el-row :gutter="20">
-                <el-col :span="12">
-                  <h3>内容TOP5</h3>
-                </el-col>
-                <el-col :span="12">
-                  <el-select v-model="topType" @change="topTypeSelectChange">
-                    <el-option label="播放次数" value="1" />
-                    <el-option label="播放时长" value="2" />
-                  </el-select>
-                </el-col>
-              </el-row>
-            </div>
-            <div>
-              <el-row :gutter="20">
-                <el-col :span="4">
-                  <h3>图片</h3>
-                </el-col>
-                <el-col :span="4">
-                  <h3>{{ imageTopNum }}</h3>
-                </el-col>
-              </el-row>
-            </div>
-            <div ref="playImageTop" class="chart-placeholder"></div>
-            <div>
+        </div>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-card shadow="hover" class="stat-card">
               <el-row :gutter="20">
-                <el-col :span="4">
-                  <h3>视频</h3>
+                <el-col :span="8">
+                  <h2 class="stat-number success">{{ totalNum }}</h2>
+                  <p class="success">总内容量</p>
                 </el-col>
-                <el-col :span="4">
-                  <h3>{{ videoTopNum }}</h3>
+                <el-col :span="16">
+                  <div ref="createLine" class="chart-small"></div>
                 </el-col>
               </el-row>
-            </div>
-            <div ref="playVideoTop" class="chart-placeholder"></div>
-          </el-card>
-        </el-col>
-      </el-row>
-    </el-main>
-  </el-container>
+            </el-card>
+          </el-col>
+          <el-col :span="6">
+            <el-card shadow="hover" class="stat-card">
+              <h2 class="stat-number warning">{{ imageNum }}</h2>
+              <p class="warning">图片素材</p>
+            </el-card>
+          </el-col>
+          <el-col :span="6">
+            <el-card shadow="hover" class="stat-card">
+              <h2 class="stat-number success">{{ videoNum }}</h2>
+              <p class="success">视频素材</p>
+            </el-card>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20" class="margin-top-20">
+          <el-col :span="18">
+            <el-row :gutter="20">
+              <el-col :span="8">
+                <el-card shadow="hover">
+                  <h3>内容占比</h3>
+                  <div ref="typePie" class="chart-placeholder"></div>
+                </el-card>
+              </el-col>
+              <el-col :span="8">
+                <el-card shadow="hover">
+                  <h3>类型占比</h3>
+                  <div ref="tagPie" class="chart-placeholder"></div>
+                </el-card>
+              </el-col>
+              <el-col :span="8">
+                <el-card shadow="hover">
+                  <h3>素材统计</h3>
+                  <div ref="typeAndTag" class="chart-placeholder"></div>
+                </el-card>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20" class="margin-top-20">
+              <el-col :span="12">
+                <el-card shadow="hover" class="disk-card">
+                  <h3>空间使用情况</h3>
+                  <div class="disk-info">
+                    <h2>已用空间:{{ diskUsed }}GB/{{ diskTotal }}GB</h2>
+                  </div>
+
+                  <div class="disk-progress">
+                    <el-row align="middle">
+                      <el-col :span="3">
+                        <h3>图片:</h3>
+                      </el-col>
+                      <el-col :span="21">
+                        <el-progress :text-inside="true" :stroke-width="26" :percentage="diskImage" />
+                      </el-col>
+                    </el-row>
+                  </div>
+                  <div class="disk-progress">
+                    <el-row align="middle">
+                      <el-col :span="3">
+                        <h3>视频:</h3>
+                      </el-col>
+                      <el-col :span="21">
+                        <el-progress :text-inside="true" :stroke-width="26" status="success" :percentage="diskVideo" />
+                      </el-col>
+                    </el-row>
+                  </div>
+                </el-card>
+              </el-col>
+              <el-col :span="12">
+                <el-card shadow="hover">
+                  <h3>播放时长统计</h3>
+                  <div ref="onlineTimeLine" class="chart-placeholder"></div>
+                </el-card>
+              </el-col>
+            </el-row>
+          </el-col>
+          <el-col :span="6">
+            <el-card shadow="hover">
+              <div>
+                <el-row :gutter="20" align="middle">
+                  <el-col :span="12">
+                    <h3>内容TOP5</h3>
+                  </el-col>
+                  <el-col :span="12">
+                    <el-select v-model="topType" @change="getPlayTop">
+                      <el-option label="播放次数" value="1" />
+                      <el-option label="播放时长" value="2" />
+                    </el-select>
+                  </el-col>
+                </el-row>
+              </div>
+              <div class="top-stats">
+                <el-row :gutter="20">
+                  <el-col :span="12">
+                    <h3>图片</h3>
+                  </el-col>
+                  <el-col :span="12">
+                    <h3>{{ imageTopNum }}</h3>
+                  </el-col>
+                </el-row>
+              </div>
+              <div ref="playImageTop" class="chart-placeholder"></div>
+              <div class="top-stats">
+                <el-row :gutter="20">
+                  <el-col :span="12">
+                    <h3>视频</h3>
+                  </el-col>
+                  <el-col :span="12">
+                    <h3>{{ videoTopNum }}</h3>
+                  </el-col>
+                </el-row>
+              </div>
+              <div ref="playVideoTop" class="chart-placeholder"></div>
+            </el-card>
+          </el-col>
+        </el-row>
+      </el-main>
+    </el-container>
+  </div>
 </template>
 
+<style>
+html,
+body,
+#app {
+  height: 100%;
+}
+
+.play-dashboard-page {
+  height: 100%;
+}
+
+.el-container {
+  height: 100%;
+  flex-direction: column !important;
+  display: flex !important;
+}
+
+.el-main.main-content {
+  height: 100%;
+  overflow-y: auto;
+  padding-bottom: 70px;
+  /* 避免内容被底部元素遮挡,可根据实际调整 */
+}
+
+.play-dashboard-header {
+  margin-top: 0;
+  /* width: 98%; */
+  /* margin-left: 1%; */
+  /* margin-right: 1%; */
+  align-self: stretch;
+}
+
+.play-dashboard-page .navbar-fixed-full {
+  position: static !important;
+}
+
+.play-dashboard-page .app-wrapper,
+.play-dashboard-page .layout-body,
+.play-dashboard-page .main-container {
+  height: auto !important;
+  min-height: auto !important;
+  overflow: visible !important;
+  display: block !important;
+}
+
+.play-dashboard-page .layout-body,
+.play-dashboard-page .main-container {
+  height: auto !important;
+  min-height: auto !important;
+  overflow: visible !important;
+}
+
+.play-dashboard-page .layout-body {
+  margin-top: 0 !important;
+}
+</style>
+
+<style scoped>
+.el-container {
+  min-height: 100vh;
+}
+
+:deep(.navbar-fixed-full) {
+  position: static !important;
+}
+
+:deep(.layout-body) {
+  margin-top: 0 !important;
+}
+
+.header-card {
+  margin-top: 10px;
+}
+
+.text-right {
+  text-align: right;
+}
+
+.date-picker {
+  margin-left: 10px;
+  margin-right: 30px;
+}
+
+.main-content {
+  margin-top: 20px;
+}
+
+.stat-card {
+  text-align: center;
+  height: 170px;
+}
+
+.stat-number {
+  font-size: 30px;
+}
+
+.success {
+  color: green;
+}
+
+.danger {
+  color: red;
+}
+
+.warning {
+  color: orange;
+}
+
+.chart-small {
+  height: 150px;
+}
+
+.chart-placeholder {
+  height: 250px;
+  border-radius: 8px;
+}
+
+.margin-top-20 {
+  margin-top: 20px;
+}
+
+.disk-card {
+  height: 330px;
+}
+
+.disk-info {
+  margin-top: 50px;
+}
+
+.disk-progress {
+  margin-top: 20px;
+}
+
+.disk-progress .el-progress--line {
+  margin-bottom: 15px;
+  max-width: 100%;
+  /* Adjust as needed */
+}
+
+.top-stats {
+  margin-top: 10px;
+}
+</style>
+
 <script setup lang="ts">
 import * as echarts from 'echarts';
 import { diskUse, fileStatistics, fileStatisticsByTag, numLine, statisticsByTypeAndTag } from '@/api/smsb/source/minioData';
 import { playTopStatistics, sumOnlineTimeLine } from '@/api/smsb/source/play_record';
+import { ref, onMounted } from 'vue'; // Import ref and onMounted
 
 const timeRadio = ref('week');
-const dateRange = ref(['2025-01-01', '2025-01-01']);
+const dateRange = ref<[string, string]>(['', '']); // Explicitly type dateRange
 const totalNum = ref(0);
 const imageNum = ref(0);
 const videoNum = ref(0);
@@ -175,297 +320,262 @@ const diskTotal = ref();
 const diskImage = ref();
 const diskVideo = ref();
 
+// Helper function to format date
+const formatDate = (date: Date): string => {
+  const year = date.getFullYear();
+  const month = String(date.getMonth() + 1).padStart(2, '0');
+  const day = String(date.getDate()).padStart(2, '0');
+  return `${year}-${month}-${day}`;
+};
+
+// Set initial date range to the last 7 days
+const setInitialDateRange = () => {
+  const today = new Date();
+  const startDate = new Date();
+  startDate.setDate(today.getDate() - 7);
+  dateRange.value = [formatDate(startDate), formatDate(today)];
+};
+
 const getDiskUse = async () => {
-  const res = await diskUse();
-  diskTotal.value = res.data.total / 1024 / 1024;
-  diskUsed.value = (res.data.used / 1024 / 1024).toFixed(3);
-  diskImage.value = ((res.data.imageUse / res.data.total) * 100).toFixed(2);
-  diskVideo.value = ((res.data.videoUse / res.data.total) * 100).toFixed(2);
+  try {
+    const res = await diskUse();
+    diskTotal.value = (res.data.total / 1024 / 1024).toFixed(2); // Format to 2 decimal places
+    diskUsed.value = (res.data.used / 1024 / 1024).toFixed(3);
+    diskImage.value = ((res.data.imageUse / res.data.total) * 100).toFixed(2);
+    diskVideo.value = ((res.data.videoUse / res.data.total) * 100).toFixed(2);
+  } catch (error) {
+    console.error('Error fetching disk usage:', error);
+  }
 };
 
 const getOnlineTimeLine = async () => {
-  const params = {
-    startTime: dateRange.value[0],
-    endTime: dateRange.value[1]
-  };
-  const res = await sumOnlineTimeLine(params);
-
-  const alarmLevelInstance = echarts.init(onlineTimeLine.value, 'macaroons');
-  alarmLevelInstance.setOption({
-    title: {
-      text: ''
-    },
-    tooltip: {
-      trigger: 'axis'
-    },
-    legend: {
-      data: ['图片', '视频']
-    },
-    grid: {
-      left: '3%',
-      right: '4%',
-      bottom: '3%',
-      containLabel: true
-    },
-    toolbox: {
-      feature: {
-        saveAsImage: {}
-      }
-    },
-    xAxis: {
-      type: 'category',
-      boundaryGap: false,
-      data: res.data.timeList
-    },
-    yAxis: {
-      type: 'value'
-    },
-    series: [
-      {
-        name: '图片',
-        type: 'line',
-        stack: 'Total',
-        data: res.data.imageNumberList
-      },
-      {
-        name: '视频',
-        type: 'line',
-        stack: 'Total',
-        data: res.data.videoNumberList
-      }
-    ]
-  });
-};
+  try {
+    const params = {
+      startTime: dateRange.value[0],
+      endTime: dateRange.value[1]
+    };
+    const res = await sumOnlineTimeLine(params);
 
-const topTypeSelectChange = () => {
-  getPlayTop();
+    const alarmLevelInstance = echarts.init(onlineTimeLine.value, 'macaroons');
+    alarmLevelInstance.setOption({
+      title: { text: '' },
+      tooltip: { trigger: 'axis' },
+      legend: { data: ['图片', '视频'] },
+      grid: {
+        left: '3%',
+        right: '4%',
+        bottom: '3%',
+        containLabel: true
+      },
+      toolbox: { feature: { saveAsImage: {} } },
+      xAxis: {
+        type: 'category',
+        boundaryGap: false,
+        data: res.data.timeList
+      },
+      yAxis: { type: 'value' },
+      series: [
+        {
+          name: '图片',
+          type: 'line',
+          stack: 'Total',
+          data: res.data.imageNumberList
+        },
+        {
+          name: '视频',
+          type: 'line',
+          stack: 'Total',
+          data: res.data.videoNumberList
+        }
+      ]
+    });
+  } catch (error) {
+    console.error('Error fetching online time line:', error);
+  }
 };
 
 const getPlayTop = async () => {
-  const params = {
-    startTime: dateRange.value[0],
-    endTime: dateRange.value[1],
-    type: topType.value
-  };
-  const res = await playTopStatistics(params);
-  imageTopNum.value = res.data.imageNum;
-  videoTopNum.value = res.data.videoNum;
-  const playImageTopInstance = echarts.init(playImageTop.value, 'macaroons');
-  playImageTopInstance.setOption({
-    title: {
-      text: ''
-    },
-    tooltip: {
-      trigger: 'axis',
-      axisPointer: {
-        type: 'shadow'
-      }
-    },
-    legend: {},
-    grid: {
-      left: '3%',
-      right: '4%',
-      bottom: '3%',
-      containLabel: true
-    },
-    xAxis: {
-      type: 'value',
-      boundaryGap: [0, 0.01]
-    },
-    yAxis: {
-      type: 'category',
-      data: res.data.imageList
-    },
-    series: [
-      {
-        name: '',
-        type: 'bar',
-        data: res.data.imageNumberList
-      }
-    ]
-  });
-  const playVideoTopInstance = echarts.init(playVideoTop.value, 'macaroons');
-  playVideoTopInstance.setOption({
-    title: {
-      text: ''
-    },
-    tooltip: {
-      trigger: 'axis',
-      axisPointer: {
-        type: 'shadow'
-      }
-    },
-    legend: {},
-    grid: {
-      left: '3%',
-      right: '4%',
-      bottom: '3%',
-      containLabel: true
-    },
-    xAxis: {
-      type: 'value',
-      boundaryGap: [0, 0.01]
-    },
-    yAxis: {
-      type: 'category',
-      data: res.data.videoList
-    },
-    series: [
-      {
-        name: '',
-        type: 'bar',
-        data: res.data.videoNumberList
-      }
-    ]
-  });
+  try {
+    const params = {
+      startTime: dateRange.value[0],
+      endTime: dateRange.value[1],
+      type: topType.value
+    };
+    const res = await playTopStatistics(params);
+    imageTopNum.value = res.data.imageNum;
+    videoTopNum.value = res.data.videoNum;
+
+    const playImageTopInstance = echarts.init(playImageTop.value, 'macaroons');
+    playImageTopInstance.setOption({
+      title: { text: '' },
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: { type: 'shadow' }
+      },
+      legend: {},
+      grid: {
+        left: '3%',
+        right: '4%',
+        bottom: '3%',
+        containLabel: true
+      },
+      xAxis: { type: 'value', boundaryGap: [0, 0.01] },
+      yAxis: { type: 'category', data: res.data.imageList },
+      series: [{ name: '', type: 'bar', data: res.data.imageNumberList }]
+    });
+
+    const playVideoTopInstance = echarts.init(playVideoTop.value, 'macaroons');
+    playVideoTopInstance.setOption({
+      title: { text: '' },
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: { type: 'shadow' }
+      },
+      legend: {},
+      grid: {
+        left: '3%',
+        right: '4%',
+        bottom: '3%',
+        containLabel: true
+      },
+      xAxis: { type: 'value', boundaryGap: [0, 0.01] },
+      yAxis: { type: 'category', data: res.data.videoList },
+      series: [{ name: '', type: 'bar', data: res.data.videoNumberList }]
+    });
+  } catch (error) {
+    console.error('Error fetching play top statistics:', error);
+  }
 };
 
 const getNumByTypeAndTag = async () => {
-  const res = await statisticsByTypeAndTag();
-  const imageTagList = res.data.imageTagList;
-  const videoTagList = res.data.videoTagList;
-  const typeAndTagInstance = echarts.init(typeAndTag.value, 'macaroons');
-  typeAndTagInstance.setOption({
-    legend: {},
-    tooltip: {},
-    dataset: {
-      source: [['product', '广告', '公益', '视频'], ['图片'].concat(imageTagList), ['视频'].concat(videoTagList)]
-    },
-    xAxis: { type: 'category' },
-    yAxis: {},
-    series: [{ type: 'bar' }, { type: 'bar' }, { type: 'bar' }]
-  });
+  try {
+    const res = await statisticsByTypeAndTag();
+    const imageTagList = res.data.imageTagList;
+    const videoTagList = res.data.videoTagList;
+    const typeAndTagInstance = echarts.init(typeAndTag.value, 'macaroons');
+    typeAndTagInstance.setOption({
+      legend: {},
+      tooltip: {},
+      dataset: {
+        source: [['product', '广告', '公益', '宣传'], ['图片'].concat(imageTagList), ['视频'].concat(videoTagList)] // Changed '视频' to '宣传' based on pie chart data
+      },
+      xAxis: { type: 'category' },
+      yAxis: {},
+      series: [{ type: 'bar' }, { type: 'bar' }, { type: 'bar' }]
+    });
+  } catch (error) {
+    console.error('Error fetching statistics by type and tag:', error);
+  }
 };
 
 const getNumByTag = async () => {
-  const res = await fileStatisticsByTag();
-  const tagPieInstance = echarts.init(tagPie.value, 'macaroons');
-  tagPieInstance.setOption({
-    title: {
-      text: '',
-      subtext: '',
-      left: 'center'
-    },
-    tooltip: {
-      trigger: 'item'
-    },
-    legend: {
-      orient: 'vertical',
-      left: 'left'
-    },
-    series: [
-      {
-        name: '内容占比',
-        type: 'pie',
-        radius: '65%',
-        data: [
-          { value: (res.data.ggNum / res.data.totalNum) * 100, name: '广告' },
-          { value: (res.data.gyNum / res.data.totalNum) * 100, name: '公益' },
-          { value: (res.data.xcNum / res.data.totalNum) * 100, name: '宣传' }
-        ],
-        emphasis: {
-          itemStyle: {
-            shadowBlur: 10,
-            shadowOffsetX: 0,
-            shadowColor: 'rgba(0, 0, 0, 0.5)'
+  try {
+    const res = await fileStatisticsByTag();
+    const tagPieInstance = echarts.init(tagPie.value, 'macaroons');
+    tagPieInstance.setOption({
+      title: { text: '', subtext: '', left: 'center' },
+      tooltip: { trigger: 'item' },
+      legend: { orient: 'vertical', left: 'left' },
+      series: [
+        {
+          name: '内容占比',
+          type: 'pie',
+          radius: '65%',
+          data: [
+            { value: ((res.data.ggNum / res.data.totalNum) * 100).toFixed(2), name: '广告' }, // Format to 2 decimal places
+            { value: ((res.data.gyNum / res.data.totalNum) * 100).toFixed(2), name: '公益' },
+            { value: ((res.data.xcNum / res.data.totalNum) * 100).toFixed(2), name: '宣传' }
+          ],
+          emphasis: {
+            itemStyle: {
+              shadowBlur: 10,
+              shadowOffsetX: 0,
+              shadowColor: 'rgba(0, 0, 0, 0.5)'
+            }
           }
         }
-      }
-    ]
-  });
+      ]
+    });
+  } catch (error) {
+    console.error('Error fetching statistics by tag:', error);
+  }
 };
 
 const getNumAndLine = async () => {
-  const params = {
-    startTime: dateRange.value[0],
-    endTime: dateRange.value[1]
-  };
-  const res = await fileStatistics();
-  totalNum.value = res.data.totalNum;
-  imageNum.value = res.data.imageNum;
-  videoNum.value = res.data.videoNum;
-  const lineRes = await numLine(params);
-  const createLineInstance = echarts.init(createLine.value, 'macaroons');
-  createLineInstance.setOption({
-    title: {
-      text: ''
-    },
-    tooltip: {
-      trigger: 'axis'
-    },
-    legend: {
-      data: ['']
-    },
-    grid: {
-      left: '3%',
-      right: '4%',
-      bottom: '3%',
-      containLabel: true
-    },
-    toolbox: {
-      feature: {
-        saveAsImage: {}
-      }
-    },
-    xAxis: {
-      type: 'category',
-      boundaryGap: false,
-      data: lineRes.data.timeList
-    },
-    yAxis: {
-      type: 'value'
-    },
-    series: [
-      {
-        name: '',
-        type: 'line',
-        stack: 'Total',
-        data: lineRes.data.numberList
-      }
-    ]
-  });
-  const typePieInstance = echarts.init(typePie.value, 'macaroons');
-  typePieInstance.setOption({
-    title: {
-      text: '',
-      subtext: '',
-      left: 'center'
-    },
-    tooltip: {
-      trigger: 'item'
-    },
-    legend: {
-      orient: 'vertical',
-      left: 'left'
-    },
-    series: [
-      {
-        name: '内容占比',
-        type: 'pie',
-        radius: '65%',
-        data: [
-          { value: (res.data.imageNum / res.data.totalNum) * 100, name: '图片' },
-          { value: (res.data.videoNum / res.data.totalNum) * 100, name: '视频' }
-        ],
-        emphasis: {
-          itemStyle: {
-            shadowBlur: 10,
-            shadowOffsetX: 0,
-            shadowColor: 'rgba(0, 0, 0, 0.5)'
+  try {
+    const params = {
+      startTime: dateRange.value[0],
+      endTime: dateRange.value[1]
+    };
+    const res = await fileStatistics();
+    totalNum.value = res.data.totalNum;
+    imageNum.value = res.data.imageNum;
+    videoNum.value = res.data.videoNum;
+
+    const lineRes = await numLine(params);
+    const createLineInstance = echarts.init(createLine.value, 'macaroons');
+    createLineInstance.setOption({
+      title: { text: '' },
+      tooltip: { trigger: 'axis' },
+      legend: { data: [''] },
+      grid: {
+        left: '3%',
+        right: '4%',
+        bottom: '3%',
+        containLabel: true
+      },
+      toolbox: { feature: { saveAsImage: {} } },
+      xAxis: {
+        type: 'category',
+        boundaryGap: false,
+        data: lineRes.data.timeList
+      },
+      yAxis: { type: 'value' },
+      series: [
+        {
+          name: '',
+          type: 'line',
+          stack: 'Total',
+          data: lineRes.data.numberList
+        }
+      ]
+    });
+
+    const typePieInstance = echarts.init(typePie.value, 'macaroons');
+    typePieInstance.setOption({
+      title: { text: '', subtext: '', left: 'center' },
+      tooltip: { trigger: 'item' },
+      legend: { orient: 'vertical', left: 'left' },
+      series: [
+        {
+          name: '内容占比',
+          type: 'pie',
+          radius: '65%',
+          data: [
+            { value: ((res.data.imageNum / res.data.totalNum) * 100).toFixed(2), name: '图片' }, // Format to 2 decimal places
+            { value: ((res.data.videoNum / res.data.totalNum) * 100).toFixed(2), name: '视频' }
+          ],
+          emphasis: {
+            itemStyle: {
+              shadowBlur: 10,
+              shadowOffsetX: 0,
+              shadowColor: 'rgba(0, 0, 0, 0.5)'
+            }
           }
         }
-      }
-    ]
-  });
+      ]
+    });
+  } catch (error) {
+    console.error('Error fetching number and line data:', error);
+  }
 };
+
 const handleDateRangeChange = () => {
   const rangeType = timeRadio.value;
   const today = new Date();
   const startDate = new Date();
   const endDate = new Date();
   switch (rangeType) {
-    case 'today':
-      break;
     case 'week':
       startDate.setDate(today.getDate() - 7);
       break;
@@ -473,6 +583,14 @@ const handleDateRangeChange = () => {
       startDate.setMonth(today.getMonth() - 1);
       break;
     default:
+      // Handle custom date range from date picker
+      if (dateRange.value && dateRange.value[0] && dateRange.value[1]) {
+        // Date picker value is already set, no need to calculate
+        getNumAndLine();
+        getPlayTop();
+        getOnlineTimeLine();
+        return;
+      }
       throw new Error('Invalid range type');
   }
   dateRange.value = [formatDate(startDate), formatDate(endDate)];
@@ -480,55 +598,20 @@ const handleDateRangeChange = () => {
   getPlayTop();
   getOnlineTimeLine();
 };
-const formatDate = (date: Date) => {
-  const year = date.getFullYear();
-  const month = String(date.getMonth() + 1).padStart(2, '0');
-  const day = String(date.getDate()).padStart(2, '0');
-  return `${year}-${month}-${day}`;
-};
+
+// Watch for changes in dateRange and trigger data fetching
+watch(dateRange, (newDateRange, oldDateRange) => {
+  if (newDateRange && newDateRange[0] && newDateRange[1] && (newDateRange[0] !== oldDateRange?.[0] || newDateRange[1] !== oldDateRange?.[1])) {
+    getNumAndLine();
+    getPlayTop();
+    getOnlineTimeLine();
+  }
+});
+
 onMounted(() => {
-  handleDateRangeChange();
-  getNumAndLine();
+  setInitialDateRange(); // Set initial date range on mount
   getNumByTag();
   getNumByTypeAndTag();
-  getPlayTop();
-  getOnlineTimeLine();
   getDiskUse();
 });
 </script>
-
-<style scoped>
-.stat-card {
-  text-align: center;
-  height: 170px;
-}
-
-.number {
-  font-size: 24px;
-  font-weight: bold;
-}
-
-.success {
-  color: green;
-}
-
-.danger {
-  color: red;
-}
-
-.warning {
-  color: orange;
-}
-
-.chart-placeholder {
-  height: 250px;
-  /*background: #f5f5f5;*/
-  border-radius: 8px;
-}
-
-.disk-progress .el-progress--line {
-  margin-bottom: 15px;
-  max-width: 600px;
-  margin-top: 50px;
-}
-</style>