瀏覽代碼

feat: dashboard play

lihao16 7 月之前
父節點
當前提交
8549deca80

+ 5 - 3
smsb-admin/src/main/resources/application-dev.yml

@@ -23,8 +23,8 @@ snail-job:
   # token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
   token: "SJ_Wyz3dmsdbDOkDujOTSSoBjGQP1BMsVnj"
   server:
-    # host: 117.73.13.40
-    host: 127.0.0.1
+    host: 117.73.13.40
+    # host: 127.0.0.1
     port: 17888
   # 详见 script/sql/snail_job.sql `sj_namespace` 表
   #namespace: ${spring.profiles.active}
@@ -55,14 +55,16 @@ spring:
           # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
           # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
           url: jdbc:mysql://localhost:3306/smsb-plus?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+          # url: jdbc:mysql://117.73.3.135:3306/smsb-plus?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
           username: root
           password: password
+          # password: Hycpb@123
         # 从库数据源
         slave:
           lazy: true
           type: ${spring.datasource.type}
           driverClassName: com.mysql.cj.jdbc.Driver
-          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+          url: jdbc:mysql://localhost:3306/smsb-plus?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
           username:
           password:
       hikari:

+ 1 - 1
smsb-extend/smsb-snailjob-server/src/main/resources/application-dev.yml

@@ -41,7 +41,7 @@ snail-job:
 --- # 监控中心配置
 spring.boot.admin.client:
   # 增加客户端开关
-  enabled: false
+  enabled: true
   url: http://localhost:9090/admin
   instance:
     service-host-type: IP

+ 34 - 1
smsb-modules/smsb-source/src/main/java/com/inspur/source/controller/SmsbMinioDataController.java

@@ -1,7 +1,9 @@
 package com.inspur.source.controller;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.inspur.device.domain.vo.DeviceAlarmNumTopVo;
 import com.inspur.source.domain.bo.SmsbMinioDataBo;
+import com.inspur.source.domain.vo.DashBoardPlayVo;
 import com.inspur.source.domain.vo.FileStatisticsVo;
 import com.inspur.source.domain.vo.SmsbMinioDataVo;
 import com.inspur.source.service.ISmsbMinioDataService;
@@ -38,6 +40,8 @@ public class SmsbMinioDataController extends BaseController {
 
     private final ISmsbMinioDataService smsbMinioDataService;
 
+
+
     /**
      * 查询文件资源列表
      */
@@ -106,11 +110,40 @@ public class SmsbMinioDataController extends BaseController {
     }
 
     /**
-     * 获取文件资源数量统计
+     * 获取文件资源数量统计 by type
      *
      */
     @GetMapping("/statistics")
     public R<FileStatisticsVo> fileStatistics() {
         return R.ok(smsbMinioDataService.fileStatistics());
     }
+
+    /**
+     * 播放统计 资源数量统计 by tag
+     *
+     */
+    @GetMapping("/statistics/byTag")
+    public R<FileStatisticsVo> fileStatisticsByTag() {
+        return R.ok(smsbMinioDataService.fileStatisticsByTag());
+    }
+
+    /**
+     *播放统计 资源数量统计 by type and tag
+     *
+     */
+    @GetMapping("/statistics/byTypeAndTag")
+    public R<FileStatisticsVo> fileStatisticsByTypeAndTag() {
+        return R.ok(smsbMinioDataService.fileStatisticsByTypeAndTag());
+    }
+
+    /**
+     * 播放统计-内容新增折线图
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    @GetMapping("/dashboard/play/numLine")
+    public R<DashBoardPlayVo> numLine(@RequestParam String startTime, @RequestParam String endTime) {
+        return R.ok(smsbMinioDataService.numLine(startTime, endTime));
+    }
 }

+ 28 - 0
smsb-modules/smsb-source/src/main/java/com/inspur/source/domain/vo/DashBoardPlayVo.java

@@ -0,0 +1,28 @@
+package com.inspur.source.domain.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 播放统计 数据返回VO
+ * @author lihao16
+ */
+@Data
+public class DashBoardPlayVo {
+
+    private Integer sourceNum;
+
+    private String eventTime;
+
+    private Integer totalNum;
+
+    private Integer imageNum;
+
+    private Integer videoNum;
+
+    private List<String> timeList;
+
+    private List<Integer> numberList;
+
+}

+ 21 - 0
smsb-modules/smsb-source/src/main/java/com/inspur/source/domain/vo/FileStatisticsVo.java

@@ -2,6 +2,8 @@ package com.inspur.source.domain.vo;
 
 import lombok.Data;
 
+import java.util.List;
+
 /**
  * 文件资源统计
  * @author lihao16
@@ -17,4 +19,23 @@ public class FileStatisticsVo {
 
     private Integer otherNum;
 
+    /**
+     * 广告
+     */
+    private Integer ggNum;
+
+    /**
+     * 公益
+     */
+    private Integer gyNum;
+
+    /**
+     * 宣传
+     */
+    private Integer xcNum;
+
+    private List<Integer> imageTagList;
+
+    private List<Integer> videoTagList;
+
 }

+ 2 - 2
smsb-modules/smsb-source/src/main/java/com/inspur/source/domain/vo/SmsbMinioDataVo.java

@@ -65,9 +65,9 @@ public class SmsbMinioDataVo implements Serializable {
     private String screenshot;
 
     /**
-     * 分类1:广告,2:公益,3:地方宣传,4.垫片
+     * 分类1:广告,2:公益,3:宣传
      */
-    @ExcelProperty(value = "分类1:广告,2:公益,3:地方宣传,4.垫片", converter = ExcelDictConvert.class)
+    @ExcelProperty(value = "分类1:广告,2:公益,3:宣传", converter = ExcelDictConvert.class)
     @ExcelDictFormat(dictType = "smsb_source_classify")
     private Long tag;
 

+ 28 - 2
smsb-modules/smsb-source/src/main/java/com/inspur/source/mapper/SmsbMinioDataMapper.java

@@ -3,11 +3,14 @@ package com.inspur.source.mapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.inspur.source.domain.SmsbMinioData;
 import com.inspur.source.domain.bo.SmsbMinioDataBo;
+import com.inspur.source.domain.vo.DashBoardPlayVo;
 import com.inspur.source.domain.vo.FileStatisticsVo;
 import com.inspur.source.domain.vo.SmsbMinioDataVo;
 import org.apache.ibatis.annotations.Param;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 
+import java.util.List;
+
 /**
  * 文件资源Mapper接口
  *
@@ -17,16 +20,39 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 public interface SmsbMinioDataMapper extends BaseMapperPlus<SmsbMinioData, SmsbMinioDataVo> {
 
     /**
-     * 统计文件类型
+     * 统计文件类型 by type
      * @return
      */
     FileStatisticsVo statisticsByType();
 
     /**
      * 根据关联查询分页
+     *
      * @param bo
      * @param page
      * @return
      */
-    Page<SmsbMinioDataVo> selectVoPageByRels(@Param("page") Page<SmsbMinioDataVo> page, @Param("bo")SmsbMinioDataBo bo);
+    Page<SmsbMinioDataVo> selectVoPageByRels(@Param("page") Page<SmsbMinioDataVo> page, @Param("bo") SmsbMinioDataBo bo);
+
+    /**
+     * 播放统计-内容新增折线图
+     *
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    List<DashBoardPlayVo> numLine(@Param("startTime") String startTime, @Param("endTime") String endTime);
+
+    /**
+     * 统计文件类型 by tag
+     * @return
+     */
+    FileStatisticsVo statisticsByTag();
+
+    /**
+     * 统计文件类型 by type and tag
+     * @param type
+     * @return
+     */
+    FileStatisticsVo statisticsByTypeAndTag(@Param("type") Integer type);
 }

+ 17 - 0
smsb-modules/smsb-source/src/main/java/com/inspur/source/service/ISmsbMinioDataService.java

@@ -1,6 +1,7 @@
 package com.inspur.source.service;
 
 import com.inspur.source.domain.bo.SmsbMinioDataBo;
+import com.inspur.source.domain.vo.DashBoardPlayVo;
 import com.inspur.source.domain.vo.FileStatisticsVo;
 import com.inspur.source.domain.vo.SmsbMinioDataVo;
 import org.dromara.common.mybatis.core.page.PageQuery;
@@ -72,4 +73,20 @@ public interface ISmsbMinioDataService {
      * @return
      */
     FileStatisticsVo fileStatistics();
+
+    /**
+     * 获取播放次数统计
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    DashBoardPlayVo numLine(String startTime, String endTime);
+
+    /**
+     * 播放统计 资源数量统计 by tag
+     * @return
+     */
+    FileStatisticsVo fileStatisticsByTag();
+
+    FileStatisticsVo fileStatisticsByTypeAndTag();
 }

+ 36 - 0
smsb-modules/smsb-source/src/main/java/com/inspur/source/service/impl/SmsbMinioDataServiceImpl.java

@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.inspur.source.domain.*;
 import com.inspur.source.domain.bo.SmsbMinioDataBo;
+import com.inspur.source.domain.vo.DashBoardPlayVo;
 import com.inspur.source.domain.vo.FileStatisticsVo;
 import com.inspur.source.domain.vo.SmsbMinioDataVo;
 import com.inspur.source.domain.vo.SmsbSourceTreeVo;
@@ -255,4 +256,39 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
         fileStatisticsVo.setOtherNum(otherNum);
         return fileStatisticsVo;
     }
+
+    @Override
+    public FileStatisticsVo fileStatisticsByTag() {
+        FileStatisticsVo fileStatisticsVo = baseMapper.statisticsByTag();
+        return fileStatisticsVo;
+    }
+
+    @Override
+    public FileStatisticsVo fileStatisticsByTypeAndTag() {
+        FileStatisticsVo result = new FileStatisticsVo();
+        FileStatisticsVo imageVo = baseMapper.statisticsByTypeAndTag(1);
+        FileStatisticsVo videoVo = baseMapper.statisticsByTypeAndTag(2);
+        List<Integer> imageNumList = Arrays.asList(imageVo.getGgNum(), imageVo.getGyNum(), imageVo.getXcNum())
+            .stream().collect(Collectors.toList());
+        List<Integer> videoNumList = Arrays.asList(videoVo.getGgNum(), videoVo.getGyNum(), videoVo.getXcNum())
+            .stream().collect(Collectors.toList());
+        result.setImageTagList(imageNumList);
+        result.setVideoTagList(videoNumList);
+        return result;
+    }
+
+    @Override
+    public DashBoardPlayVo numLine(String startTime, String endTime) {
+
+        DashBoardPlayVo result = new DashBoardPlayVo();
+        List<DashBoardPlayVo> queryList = baseMapper.numLine(startTime, endTime);
+        if (CollectionUtil.isEmpty(queryList)) {
+            return result;
+        }
+        List<String> timeList = queryList.stream().map(DashBoardPlayVo::getEventTime).collect(Collectors.toList());
+        List<Integer> numList = queryList.stream().map(DashBoardPlayVo::getSourceNum).collect(Collectors.toList());
+        result.setNumberList(numList);
+        result.setTimeList(timeList);
+        return result;
+    }
 }

+ 39 - 0
smsb-modules/smsb-source/src/main/resources/mapper/SmsbMinioDataMapper.xml

@@ -10,6 +10,22 @@
         from smsb_minio_data where del_flag = 0
     </select>
 
+    <select id="statisticsByTag" resultType="com.inspur.source.domain.vo.FileStatisticsVo">
+        select count(1)                                            AS totalNum,
+               IFNULL(SUM(CASE WHEN tag = 1 THEN 1 ELSE 0 END), 0) AS ggNum,
+               IFNULL(SUM(CASE WHEN tag = 2 THEN 1 ELSE 0 END), 0) AS gyNum,
+               IFNULL(SUM(CASE WHEN tag = 3 THEN 1 ELSE 0 END), 0) AS xcNum
+        from smsb_minio_data where del_flag = 0
+    </select>
+
+    <select id="statisticsByTypeAndTag" resultType="com.inspur.source.domain.vo.FileStatisticsVo" parameterType="Integer">
+        select count(1)                                            AS totalNum,
+               IFNULL(SUM(CASE WHEN tag = 1 THEN 1 ELSE 0 END), 0) AS ggNum,
+               IFNULL(SUM(CASE WHEN tag = 2 THEN 1 ELSE 0 END), 0) AS gyNum,
+               IFNULL(SUM(CASE WHEN tag = 3 THEN 1 ELSE 0 END), 0) AS xcNum
+        from smsb_minio_data where del_flag = 0 and type = #{type}
+    </select>
+
     <select id="selectVoPageByRels" resultType="com.inspur.source.domain.vo.SmsbMinioDataVo"
             parameterType="com.inspur.source.domain.bo.SmsbMinioDataBo">
         SELECT * FROM smsb_minio_data md
@@ -29,4 +45,27 @@
             AND md.tag = #{bo.tag}
         </if>
     </select>
+    <select id="numLine" resultType="com.inspur.source.domain.vo.DashBoardPlayVo">
+        SELECT
+            date_range.stat_date AS eventTime,
+            IFNULL(COUNT(smsb_minio_data.id), 0) AS sourceNum
+        FROM (
+            SELECT
+                DATE_ADD(#{startTime}, INTERVAL t4.num DAY) AS stat_date
+            FROM (
+                SELECT
+                    (t3.num * 1000 + t2.num * 100 + t1.num * 10 + t0.num) AS num
+                FROM
+                    (SELECT 0 num UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0,
+                    (SELECT 0 num UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1,
+                    (SELECT 0 num UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2,
+                    (SELECT 0 num UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3
+                 ) t4
+            WHERE DATE_ADD(#{startTime}, INTERVAL t4.num DAY) BETWEEN #{startTime} AND #{endTime}) date_range
+            LEFT JOIN smsb_minio_data ON DATE(smsb_minio_data.create_time) = date_range.stat_date
+            AND smsb_minio_data.create_time BETWEEN #{startTime} AND #{endTime} + INTERVAL 1 DAY - INTERVAL 1 SECOND
+            AND smsb_minio_data.del_flag = 0
+        GROUP BY date_range.stat_date
+        ORDER BY date_range.stat_date
+    </select>
 </mapper>

+ 22 - 1
smsb-plus-ui/src/api/smsb/source/minioData.ts

@@ -1,6 +1,7 @@
 import request from '@/utils/request';
 import { AxiosPromise } from 'axios';
-import { MinioDataVO, MinioDataForm, MinioDataQuery } from '@/api/smsb/source/minioData_type';
+import { MinioDataVO, MinioDataForm, MinioDataQuery, SourceStatisticsVO } from '@/api/smsb/source/minioData_type';
+import { DeviceErrorRecordVO } from '@/api/smsb/device/errorRecord_type';
 
 /**
  * 查询文件资源列表
@@ -68,3 +69,23 @@ export const fileStatistics = () => {
     method: 'get'
   });
 };
+export const fileStatisticsByTag = () => {
+  return request({
+    url: '/source/minioData/statistics/byTag',
+    method: 'get'
+  });
+};
+
+export const statisticsByTypeAndTag = () => {
+  return request({
+    url: '/source/minioData/statistics/byTypeAndTag',
+    method: 'get'
+  });
+};
+export const numLine = (params?: any): AxiosPromise<SourceStatisticsVO> => {
+  return request({
+    url: '/source/minioData/dashboard/play/numLine',
+    method: 'get',
+    params: params
+  });
+};

+ 7 - 0
smsb-plus-ui/src/api/smsb/source/minioData_type.ts

@@ -103,3 +103,10 @@ export interface MinioDataQuery extends PageQuery {
    */
   params?: any;
 }
+export interface SourceStatisticsVO {
+  totalNum: number;
+  imageNum: number;
+  videoNum: number;
+  timeList: Array<string>;
+  numberList: Array<number>;
+}

+ 316 - 0
smsb-plus-ui/src/views/smsb/dashboard/play.vue

@@ -0,0 +1,316 @@
+<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>
+              </el-col>
+              <el-col :span="16">
+                <div ref="createLine" style="height: 150px"></div>
+              </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">
+                <h3>空间使用情况</h3>
+                <div ref="onlineTime" class="chart-placeholder"></div>
+              </el-card>
+            </el-col>
+            <el-col :span="12">
+              <el-card shadow="hover">
+                <h3>播放时长统计</h3>
+                <div ref="alarmNum" class="chart-placeholder"></div>
+              </el-card>
+            </el-col>
+          </el-row>
+        </el-col>
+        <el-col :span="6">
+          <el-card shadow="hover">
+            <h3>告警清单</h3>
+          </el-card>
+        </el-col>
+      </el-row>
+    </el-main>
+  </el-container>
+</template>
+
+<script setup lang="ts">
+import * as echarts from 'echarts';
+import { fileStatistics, fileStatisticsByTag, numLine, statisticsByTypeAndTag } from '@/api/smsb/source/minioData';
+
+const timeRadio = ref('week');
+const dateRange = ref(['2025-01-01', '2025-01-01']);
+const totalNum = ref(0);
+const imageNum = ref(0);
+const videoNum = ref(0);
+const createLine = ref();
+const typePie = ref();
+const tagPie = ref();
+const typeAndTag = ref();
+
+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' }]
+  });
+};
+
+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)'
+          }
+        }
+      }
+    ]
+  });
+};
+
+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)'
+          }
+        }
+      }
+    ]
+  });
+};
+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;
+    case 'month':
+      startDate.setMonth(today.getMonth() - 1);
+      break;
+    default:
+      throw new Error('Invalid range type');
+  }
+  dateRange.value = [formatDate(startDate), formatDate(endDate)];
+  getNumAndLine();
+};
+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}`;
+};
+onMounted(() => {
+  handleDateRangeChange();
+  getNumAndLine();
+  getNumByTag();
+  getNumByTypeAndTag();
+});
+</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;
+}
+</style>