Эх сурвалжийг харах

feat(data-kanban): 新增数据看板模块及优化广告管理API样式

- 新增数据看板API实现多项数据接口调用支持
- 新增数据看板Vue页面,实现数据展示及交互功能
- 优化数据看板表格样式和分页逻辑,提升用户体验
- 实现数据看板历史数据和趋势图表动态刷新功能
- 增加用户流失率统计及导出表格功能
- 细化充电度数趋势图及历史营业数据图表配置
- 优化广告管理API接口缩进和代码格式一致性
- 重构广告管理相关接口注释对齐和代码风格统一
zouzexu 1 долоо хоног өмнө
parent
commit
6bc95f6f07

+ 94 - 95
src/api/operationsManage/advertising-api.ts

@@ -3,113 +3,112 @@ import request from "@/utils/request";
 const ADVERTISING_BASE_URL = "/api/v1/advertising";
 
 const AdvertisingAPI = {
-    /** 获取广告管理分页数据 */
-    getPage(queryParams?: AdvertisingPageQuery) {
-        return request<any, PageResult<AdvertisingPageVO[]>>({
-            url: `${ADVERTISING_BASE_URL}/page`,
-            method: "get",
-            params: queryParams,
-        });
-    },
-    /**
-     * 获取广告管理表单数据
-     *
-     * @param id 广告管理ID
-     * @returns 广告管理表单数据
-     */
-    getFormData(id: number) {
-        return request<any, AdvertisingForm>({
-            url: `${ADVERTISING_BASE_URL}/${id}/form`,
-            method: "get",
-        });
-    },
+  /** 获取广告管理分页数据 */
+  getPage(queryParams?: AdvertisingPageQuery) {
+    return request<any, PageResult<AdvertisingPageVO[]>>({
+      url: `${ADVERTISING_BASE_URL}/page`,
+      method: "get",
+      params: queryParams,
+    });
+  },
+  /**
+   * 获取广告管理表单数据
+   *
+   * @param id 广告管理ID
+   * @returns 广告管理表单数据
+   */
+  getFormData(id: number) {
+    return request<any, AdvertisingForm>({
+      url: `${ADVERTISING_BASE_URL}/${id}/form`,
+      method: "get",
+    });
+  },
 
-    /**
-     *  添加广告管理
-     *
-     *  @param data 广告管理表单数据
-     */
-    create(data: AdvertisingForm) {
-        return request({
-            url: `${ADVERTISING_BASE_URL}`,
-            method: "post",
-            data,
-        });
-    },
+  /**
+   *  添加广告管理
+   *
+   *  @param data 广告管理表单数据
+   */
+  create(data: AdvertisingForm) {
+    return request({
+      url: `${ADVERTISING_BASE_URL}`,
+      method: "post",
+      data,
+    });
+  },
 
-    /**
-     * 更新广告管理
-     *
-     * @param id 广告管理ID
-     * @param data 广告管理表单数据
-     */
-     update(id: string, data: AdvertisingForm) {
-        return request({
-            url: `${ADVERTISING_BASE_URL}/${id}`,
-            method: "put",
-            data,
-        });
-    },
+  /**
+   * 更新广告管理
+   *
+   * @param id 广告管理ID
+   * @param data 广告管理表单数据
+   */
+  update(id: string, data: AdvertisingForm) {
+    return request({
+      url: `${ADVERTISING_BASE_URL}/${id}`,
+      method: "put",
+      data,
+    });
+  },
 
-    /**
-     * 批量删除广告管理,多个以英文逗号(,)分割
-     *
-     * @param ids 广告管理ID字符串,多个以英文逗号(,)分割
-     */
-     deleteByIds(ids: string) {
-        return request({
-            url: `${ADVERTISING_BASE_URL}/${ids}`,
-            method: "delete",
-        });
-    }
-}
+  /**
+   * 批量删除广告管理,多个以英文逗号(,)分割
+   *
+   * @param ids 广告管理ID字符串,多个以英文逗号(,)分割
+   */
+  deleteByIds(ids: string) {
+    return request({
+      url: `${ADVERTISING_BASE_URL}/${ids}`,
+      method: "delete",
+    });
+  },
+};
 
 export default AdvertisingAPI;
 
 /** 广告管理分页查询参数 */
-export interface AdvertisingPageQuery extends PageQuery {
-}
+export interface AdvertisingPageQuery extends PageQuery {}
 
 /** 广告管理表单对象 */
 export interface AdvertisingForm {
-    /** 主键 */
-    id?:  number;
-    /** 名称 */
-    name?:  string;
-    /** 状态 1.上线 2.下线 */
-    status?:  number;
-    /** 排序 */
-    sort?:  number;
-    /** 广告位置  1.首页弹框 */
-    advertisingPosition?:  number;
-    /** 图片 */
-    picture?:  string;
-    /** 跳转路径 */
-    skipUrl?:  string;
-    /** 创建时间 */
-    createTime?:  Date;
-    /** 更新时间 */
-    updateTime?:  Date;
+  /** 主键 */
+  id?: number;
+  /** 名称 */
+  name?: string;
+  /** 状态 1.上线 2.下线 */
+  status?: number;
+  /** 排序 */
+  sort?: number;
+  /** 广告位置  1.首页弹框 */
+  advertisingPosition?: number;
+  /** 图片 */
+  picture?: string;
+  /** 跳转路径 */
+  skipUrl?: string;
+  /** 创建时间 */
+  createTime?: Date;
+  /** 更新时间 */
+  updateTime?: Date;
 }
 
 /** 广告管理分页对象 */
 export interface AdvertisingPageVO {
-    /** 主键 */
-    id?: number;
-    /** 名称 */
-    name?: string;
-    /** 状态 1.上线 2.下线 */
-    status?: number;
-    /** 排序 */
-    sort?: number;
-    /** 广告位置  1.首页弹框 */
-    advertisingPosition?: number;
-    /** 图片 */
-    picture?: string;
-    /** 跳转路径 */
-    skipUrl?: string;
-    /** 创建时间 */
-    createTime?: Date;
-    /** 更新时间 */
-    updateTime?: Date;
+  /** 主键 */
+  id?: number;
+  /** 名称 */
+  name?: string;
+  /** 状态 1.上线 2.下线 */
+  status?: number;
+  /** 排序 */
+  sort?: number;
+  /** 广告位置  1.首页弹框 */
+  advertisingPosition?: number;
+  /** 图片 */
+  picture?: string;
+  /** 跳转路径 */
+  skipUrl?: string;
+  /** 创建时间 */
+  createTime?: Date;
+  /** 更新时间 */
+  updateTime?: Date;
 }

+ 298 - 0
src/api/operationsManage/data-kanban-api.ts

@@ -0,0 +1,298 @@
+import request from "@/utils/request";
+
+const dataBoard_BASE_URL = "/api/v1/dataBoard";
+
+const dataBoardApi = {
+  /** 获取数据看板总计信息 */
+  getRealTimeData() {
+    return request<any, PageResult<DataBoardRealTimeVO[]>>({
+      url: `${dataBoard_BASE_URL}/realTimeData`,
+      method: "get",
+    });
+  },
+  /** 获取数据看板今日实时数据 */
+  getTodayRealTimeData() {
+    return request<any, PageResult<DataBoardTodayVO[]>>({
+      url: `${dataBoard_BASE_URL}/todayData`,
+      method: "get",
+    });
+  },
+
+  /** 所有站点充电度数时段对比 */
+  getChargePowerTrend(queryParams?: ChargePowerTrendRequest) {
+    return request<any, PageResult<ChargePowerTrendVO[]>>({
+      url: `${dataBoard_BASE_URL}/chargePowerTrend`,
+      method: "get",
+      params: queryParams,
+    });
+  },
+
+  /** 历史营业数据 */
+  getHistoryBusinessData(queryParams?: historyBusinessDataRequest) {
+    return request<any, PageResult<HistoryBusinessDataVO[]>>({
+      url: `${dataBoard_BASE_URL}/historyBusinessData`,
+      method: "get",
+      params: queryParams,
+    });
+  },
+
+  /**获取站点排名表格数据*/
+  getStationRankData(queryParams?: StationRankDataRequest) {
+    return request<any, PageResult<StationRankListVO[]>>({
+      url: `${dataBoard_BASE_URL}/stationRankData`,
+      method: "get",
+      params: queryParams,
+    });
+  },
+
+  /**获取用户流失率统计*/
+  getUserChurnRate() {
+    return request<any, PageResult<UserChurnRateVO[]>>({
+      url: `${dataBoard_BASE_URL}/userChurnRate`,
+      method: "get",
+    });
+  },
+
+  /**导出指定用户流失率统计表*/
+  exportUserChurnRate(queryParams?: UserChurnRateRequest) {
+    return request<any, PageResult<UserChurnRateVO[]>>({
+      url: `${dataBoard_BASE_URL}/churnUsers/export`,
+      method: "get",
+      responseType: "blob",
+      params: queryParams,
+    });
+  },
+};
+
+export default dataBoardApi;
+
+/**返回数据*/
+export interface DataBoardRealTimeVO {
+  /**
+   * 总实付金额
+   */
+  totalActualPayAmount?: number;
+  /**
+   * 总充电金额
+   */
+  totalChargeAmount?: number;
+  /**
+   * 总充电度数
+   */
+  totalChargePower?: number;
+  /**
+   * 总分销佣金
+   */
+  totalCommissionAmount?: number;
+  /**
+   * 总优惠券减免
+   */
+  totalCouponAmount?: number;
+  /**
+   * 总抵扣券金额
+   */
+  totalDiscountAmount?: number;
+  /**
+   * 总企业专享价减
+   */
+  totalFirmDiscountAmount?: number;
+  /**
+   * 总首单金额
+   */
+  totalFirstOrderAmount?: number;
+  /**
+   * 总退款金额
+   */
+  totalRefundAmount?: number;
+  /**
+   * 总服务费金额
+   */
+  totalServiceFeeAmount?: number;
+  [property: string]: any;
+}
+export interface DataBoardTodayVO {
+  /**
+   * 今日实付金额
+   */
+  todayActualPayAmount?: number;
+  /**
+   * 今日充电金额
+   */
+  todayChargeAmount?: number;
+  /**
+   * 今日充电度数
+   */
+  todayChargePower?: number;
+  /**
+   * 今日分销佣金
+   */
+  todayCommissionAmount?: number;
+  /**
+   * 今日优惠券减免
+   */
+  todayCouponAmount?: number;
+  /**
+   * 今日抵扣券金额
+   */
+  todayDiscountAmount?: number;
+  /**
+   * 今日企业专享价减
+   */
+  todayFirmDiscountAmount?: number;
+  /**
+   * 今日首单金额
+   */
+  todayFirstOrderAmount?: number;
+  /**
+   * 今日退款金额
+   */
+  todayRefundAmount?: number;
+  /**
+   * 今日服务费金额
+   */
+  todayServiceFeeAmount?: number;
+  [property: string]: any;
+}
+export interface ChargePowerTrendVO {
+  /**
+   * 对比日数据
+   */
+  compareData?: number[];
+  /**
+   * 对比日期
+   */
+  compareDate?: string;
+  /**
+   * 时段列表(0-23小时)
+   */
+  hours?: number[];
+  /**
+   * 今日数据
+   */
+  todayData?: number[];
+  [property: string]: any;
+}
+export interface HistoryBusinessDataVO {
+  /**
+   * 数据类型:chargePower-充电度数,chargeAmount-充电金额,validOrders-有效订单,registerUsers-注册用户
+   */
+  dataType?: string;
+  /**
+   * 日期/时间标签列表
+   */
+  labels?: string[];
+  /**
+   * 时间维度:day-日趋势,month-月趋势
+   */
+  timeDimension?: string;
+  /**
+   * 数据值列表
+   */
+  values?: number[];
+  [property: string]: any;
+}
+export interface StationRankListVO {
+  /**
+   * 统计时间范围
+   */
+  dateRange?: string;
+  /**
+   * 波动充电站列表
+   */
+  fluctuationStations?: StationRankVO[];
+  /**
+   * 热门充电站列表
+   */
+  hotStations?: StationRankVO[];
+  [property: string]: any;
+}
+export interface UserChurnRateVO {
+  /**
+   * 近1个月流失率(百分比)
+   */
+  oneMonthChurnRate?: number;
+  /**
+   * 近七天流失率(百分比)
+   */
+  sevenDayChurnRate?: number;
+  /**
+   * 近3个月流失率(百分比)
+   */
+  threeMonthChurnRate?: number;
+}
+/**
+ * 数据看板-站点排名VO
+ */
+export interface StationRankVO {
+  /**
+   * 充电度数
+   */
+  chargePower?: number;
+  /**
+   * 波动幅度(百分比)
+   */
+  fluctuation?: number;
+  /**
+   * 波动方向:up-上升,down-下降
+   */
+  fluctuationDirection?: string;
+  /**
+   * 序号
+   */
+  rank?: number;
+  /**
+   * 站点ID
+   */
+  stationId?: number;
+  /**
+   * 电站名称
+   */
+  stationName?: string;
+  [property: string]: any;
+}
+
+/**请求参数*/
+export interface ChargePowerTrendRequest {
+  /**
+   * 对比日期(格式:yyyy-MM-dd)
+   */
+  compareDate?: string;
+}
+export interface historyBusinessDataRequest {
+  /**
+   * 数据类型:chargePower-充电度数,chargeAmount-充电金额,validOrders-有效订单,registerUsers-注册用户
+   */
+  dataType?: string;
+  /**
+   * 时间维度:day-日趋势,month-月趋势
+   */
+  timeDimension?: string;
+  /**
+   * 年月(格式:yyyy-MM,月趋势时使用)
+   */
+  yearMonth?: string;
+}
+export interface StationRankDataRequest {
+  /**
+   * 结束日期(自定义时间范围时使用,格式:yyyy-MM-dd)
+   */
+  endDate?: string;
+  /**
+   * 开始日期(自定义时间范围时使用,格式:yyyy-MM-dd)
+   */
+  startDate?: string;
+  /**
+   * 统计类型:chargePower-按充电度数,validOrders-按有效订单数
+   */
+  statisticType?: string;
+  /**
+   * 时间范围:today-今天,week-近7天,month-近30天,custom-自定义
+   */
+  timeRange?: string;
+}
+export interface UserChurnRateRequest {
+  /**
+   * 时段类型:7d-近7天,1m-近1个月,3m-近3个月 时段类型:7d-近7天,1m-近1个月,3m-近3个月
+   */
+  periodType: string;
+}

+ 355 - 124
src/views/operationsManage/data-kanban/index.vue

@@ -6,8 +6,14 @@
         <el-card shadow="hover">
           <div class="flex items-center gap-10px">
             <div class="flex items-center gap-6px">
-              <span class="font-bold text-24px">实时数据</span>
-              <el-icon color="#4080ff" size="20" class="cursor-pointer" @click="refreshData">
+              <span class="font-bold text-18px">实时数据</span>
+              <el-icon
+                color="#4080ff"
+                size="20"
+                class="cursor-pointer"
+                :class="isLoading ? 'is-loading' : ''"
+                @click="refreshData"
+              >
                 <Refresh />
               </el-icon>
             </div>
@@ -31,8 +37,14 @@
         <el-card shadow="hover">
           <div class="flex items-center gap-10px">
             <div class="flex items-center gap-6px">
-              <span class="font-bold text-24px">今日实时数据</span>
-              <el-icon color="#4080ff" size="20" class="cursor-pointer" @click="refreshData">
+              <span class="font-bold text-18px">今日实时数据</span>
+              <el-icon
+                color="#4080ff"
+                size="20"
+                class="cursor-pointer"
+                :class="isLoading ? 'is-loading' : ''"
+                @click="refreshData"
+              >
                 <Refresh />
               </el-icon>
             </div>
@@ -53,7 +65,7 @@
         <div class="h-16px"></div>
 
         <!-- 所有站点充电度数 日时段对比趋势 -->
-        <el-card shadow="hover">
+        <el-card v-loading="hourlyTrendLoading" shadow="hover">
           <div class="flex items-center gap-16px mb-16px">
             <span class="font-bold text-18px">所有站点充电度数 日时段对比趋势</span>
           </div>
@@ -83,7 +95,7 @@
         <div class="h-16px"></div>
 
         <!-- 历史营业数据(面积图) -->
-        <el-card shadow="hover">
+        <el-card v-loading="historyLoading" shadow="hover">
           <div class="flex items-center justify-between mb-16px">
             <span class="font-bold text-18px">历史营业数据</span>
           </div>
@@ -122,7 +134,7 @@
         <div class="h-16px"></div>
 
         <!-- 历史营业数据(表格) -->
-        <el-card shadow="hover">
+        <el-card v-loading="tableLoading" shadow="hover">
           <div class="flex items-center justify-between flex-wrap gap-16px mb-16px">
             <div class="flex items-center gap-8px">
               <el-radio-group v-model="tableDataType" size="small">
@@ -131,7 +143,7 @@
               </el-radio-group>
             </div>
             <div class="flex items-center gap-8px flex-wrap">
-              <el-radio-group v-model="tableDateRange" size="small">
+              <el-radio-group v-model="tableDateRange" size="small" @change="handleDateRangeChange">
                 <el-radio-button value="yesterday">昨天</el-radio-button>
                 <el-radio-button value="week">近七天</el-radio-button>
                 <el-radio-button value="month">近30天</el-radio-button>
@@ -144,6 +156,7 @@
                 value-format="YYYY-MM-DD"
                 size="small"
                 style="width: 130px"
+                @change="handleStartDateChange"
               />
               <span class="text-#999">至</span>
               <el-date-picker
@@ -154,7 +167,9 @@
                 value-format="YYYY-MM-DD"
                 size="small"
                 style="width: 130px"
+                @change="handleEndDateChange"
               />
+              <el-button type="primary" size="small" @click="handleQuery">查询</el-button>
               <span class="text-14px text-#999 ml-16px">已选时间: {{ selectedDateRange }}</span>
             </div>
           </div>
@@ -163,55 +178,55 @@
             <!-- 热门充电站 -->
             <div class="flex-1 min-w-400px">
               <div class="font-bold text-16px mb-12px">热门充电站</div>
-              <el-table :data="hotStationData" border stripe size="small">
+              <el-table
+                :data="hotStationData"
+                stripe
+                size="small"
+                class="custom-table"
+                :header-cell-style="{ background: '#F3F9FF', color: '#333' }"
+                :row-style="rowStyle"
+              >
                 <el-table-column type="index" label="序号" width="60" align="center" />
-                <el-table-column prop="name" label="电站名称" min-width="180" />
+                <el-table-column prop="stationName" label="电站名称" min-width="180" />
                 <el-table-column
-                  prop="value"
+                  prop="chargePower"
                   :label="tableDataType === 'degree' ? '充电度数' : '有效订单'"
                   width="120"
                   align="right"
                 />
               </el-table>
               <div class="flex items-center justify-between mt-12px">
-                <span class="text-12px text-#999">共{{ hotStationTotal }}条</span>
-                <el-pagination
-                  v-model:current-page="hotCurrentPage"
-                  v-model:page-size="hotPageSize"
-                  :page-sizes="[5, 10, 20]"
-                  :total="hotStationTotal"
-                  size="small"
-                  layout="sizes, prev, pager, next, jumper"
-                />
+                <span class="text-12px text-#999">共{{ hotStationData.length }}条</span>
               </div>
             </div>
 
             <!-- 波动充电站 -->
             <div class="flex-1 min-w-400px">
               <div class="font-bold text-16px mb-12px">波动充电站</div>
-              <el-table :data="fluctuationStationData" border stripe size="small">
+              <el-table
+                :data="fluctuationStationData"
+                stripe
+                size="small"
+                class="custom-table"
+                :header-cell-style="{ background: '#F3F9FF', color: '#333' }"
+                :row-style="rowStyle"
+              >
                 <el-table-column type="index" label="序号" width="60" align="center" />
-                <el-table-column prop="name" label="电站名称" min-width="180" />
-                <el-table-column prop="rate" label="涨幅 | 跌幅" width="120" align="right">
+                <el-table-column prop="stationName" label="电站名称" min-width="180" />
+                <el-table-column prop="fluctuation" label="涨幅 | 跌幅" width="120" align="right">
                   <template #default="{ row }">
-                    <span :class="row.rate >= 0 ? 'text-#F56C6C' : 'text-#67C23A'">
-                      <el-icon v-if="row.rate >= 0"><CaretTop /></el-icon>
+                    <span
+                      :class="row.fluctuationDirection === 'up' ? 'text-#F56C6C' : 'text-#67C23A'"
+                    >
+                      <el-icon v-if="row.fluctuationDirection === 'up'"><CaretTop /></el-icon>
                       <el-icon v-else><CaretBottom /></el-icon>
-                      {{ Math.abs(row.rate).toFixed(2) }}%
+                      {{ Math.abs(row.fluctuation ?? 0).toFixed(2) }}%
                     </span>
                   </template>
                 </el-table-column>
               </el-table>
               <div class="flex items-center justify-between mt-12px">
-                <span class="text-12px text-#999">共{{ fluctuationStationTotal }}条</span>
-                <el-pagination
-                  v-model:current-page="fluctuationCurrentPage"
-                  v-model:page-size="fluctuationPageSize"
-                  :page-sizes="[5, 10, 20]"
-                  :total="fluctuationStationTotal"
-                  size="small"
-                  layout="sizes, prev, pager, next, jumper"
-                />
+                <span class="text-12px text-#999">共{{ fluctuationStationData.length }}条</span>
               </div>
             </div>
           </div>
@@ -226,9 +241,14 @@
             <div class="flex items-center gap-200px bg-#F3F9FF rounded-8px p-32px">
               <div>
                 <div class="text-14px text-#666 mb-8px">近七天 流失率</div>
-                <div class="font-bold text-32px text-#333">{{ lossRate.sevenDay }}%</div>
+                <div class="font-bold text-32px text-#333">{{ lossRate?.sevenDayChurnRate }}%</div>
               </div>
-              <el-button type="primary" link @click="downloadReport('sevenDay')">
+              <el-button
+                type="primary"
+                link
+                :loading="downloadLoading === 'sevenDay'"
+                @click="downloadReport('sevenDay')"
+              >
                 <el-icon><Download /></el-icon>
                 下载表格
               </el-button>
@@ -236,9 +256,14 @@
             <div class="flex items-center gap-200px bg-#F3F9FF rounded-8px p-32px">
               <div>
                 <div class="text-14px text-#666 mb-8px">近1个月 流失率</div>
-                <div class="font-bold text-32px text-#333">{{ lossRate.oneMonth }}%</div>
+                <div class="font-bold text-32px text-#333">{{ lossRate?.oneMonthChurnRate }}%</div>
               </div>
-              <el-button type="primary" link @click="downloadReport('oneMonth')">
+              <el-button
+                type="primary"
+                link
+                :loading="downloadLoading === 'oneMonth'"
+                @click="downloadReport('oneMonth')"
+              >
                 <el-icon><Download /></el-icon>
                 下载表格
               </el-button>
@@ -246,9 +271,16 @@
             <div class="flex items-center gap-200px bg-#F3F9FF rounded-8px p-32px">
               <div>
                 <div class="text-14px text-#666 mb-8px">近3个月 流失率</div>
-                <div class="font-bold text-32px text-#333">{{ lossRate.threeMonth }}%</div>
+                <div class="font-bold text-32px text-#333">
+                  {{ lossRate?.threeMonthChurnRate }}%
+                </div>
               </div>
-              <el-button type="primary" link @click="downloadReport('threeMonth')">
+              <el-button
+                type="primary"
+                link
+                :loading="downloadLoading === 'threeMonth'"
+                @click="downloadReport('threeMonth')"
+              >
                 <el-icon><Download /></el-icon>
                 下载表格
               </el-button>
@@ -263,14 +295,33 @@
 </template>
 
 <script setup lang="ts">
-import { ref, computed, onMounted } from "vue";
+import { ref, computed, onMounted, watch } from "vue";
+import dataBoardApi from "@/api/operationsManage/data-kanban-api";
+import type {
+  DataBoardRealTimeVO,
+  DataBoardTodayVO,
+  ChargePowerTrendVO,
+  HistoryBusinessDataVO,
+  StationRankListVO,
+} from "@/api/operationsManage/data-kanban-api";
 import { dayjs, ElMessage } from "element-plus";
 import { CaretTop, CaretBottom, Download, Refresh } from "@element-plus/icons-vue";
 import type { TabsPaneContext } from "element-plus";
 import DataCard from "./components/DataCard.vue";
-
+onMounted(() => {
+  getTotalDataList();
+  getTodayDataList();
+  getChargePowerTrendData();
+  getHistoryBusinessData();
+  getStationRankData();
+  getUserLossRate();
+});
 const activeName = ref("first");
-
+const rowStyle = ({ row, rowIndex }) => {
+  return {
+    background: rowIndex % 2 === 0 ? "rgba(24,144,255,0.2)" : "rgba(24,144,255,0.05)",
+  };
+};
 // 最后更新时间
 const lastUpdateTime = ref(dayjs().format("MM-DD HH:mm"));
 
@@ -279,6 +330,29 @@ const selectedCompareDate = ref(dayjs().subtract(1, "day").format("YYYY-MM-DD"))
 // 对比日期显示格式
 const compareDate = computed(() => dayjs(selectedCompareDate.value).format("MM.DD"));
 
+// 充电度数趋势数据
+const chargePowerTrendData = ref<ChargePowerTrendVO>({});
+// 日时段对比趋势图表加载状态
+const hourlyTrendLoading = ref(false);
+
+// 获取充电度数趋势数据
+const getChargePowerTrendData = () => {
+  hourlyTrendLoading.value = true;
+  dataBoardApi
+    .getChargePowerTrend({ compareDate: selectedCompareDate.value })
+    .then((res) => {
+      chargePowerTrendData.value = res;
+    })
+    .finally(() => {
+      hourlyTrendLoading.value = false;
+    });
+};
+
+// 监听对比日期变化,重新获取数据
+watch(selectedCompareDate, () => {
+  getChargePowerTrendData();
+});
+
 // 禁用日期:只允许选择昨天之前的15天(含昨天)
 const disableCompareDate = (date: Date) => {
   const today = dayjs().startOf("day");
@@ -290,56 +364,86 @@ const disableCompareDate = (date: Date) => {
 };
 
 // 刷新数据
+const isLoading = ref(true);
 const refreshData = () => {
+  isLoading.value = true;
+  getTotalDataList();
+  getTodayDataList();
   lastUpdateTime.value = dayjs().format("MM-DD HH:mm");
-  ElMessage.success("数据已刷新");
 };
 
 // 数据指标配置
 const dataMetrics = [
-  { type: "cdds", label: "充电度数", icon: "cdds.png", unit: "度" },
-  { type: "cdje", label: "充电金额", icon: "cdje.png", unit: "元" },
-  { type: "dkqje", label: "抵扣券金额", icon: "dkqje.png", unit: "元" },
-  { type: "fwfje", label: "服务费金额", icon: "fwfje.png", unit: "元" },
-  { type: "tkje", label: "退款金额", icon: "tkje.png", unit: "元" },
-  { type: "sfje", label: "实付金额", icon: "sfje.png", unit: "元" },
-  { type: "sdje", label: "首单金额", icon: "sdje.png", unit: "元" },
-  { type: "yhqjm", label: "优惠券减免", icon: "yhqjm.png", unit: "元" },
-  { type: "qyzxjj", label: "企业专享价减", icon: "qyzxjj.png", unit: "元" },
-  { type: "fxyj", label: "分销佣金", icon: "fxyj.png", unit: "元" },
+  { type: "cdds", label: "充电度数", icon: "cdds.png", unit: "度", key: "ChargePower" },
+  { type: "cdje", label: "充电金额", icon: "cdje.png", unit: "元", key: "ChargeAmount" },
+  { type: "dkqje", label: "抵扣券金额", icon: "dkqje.png", unit: "元", key: "DiscountAmount" },
+  { type: "fwfje", label: "服务费金额", icon: "fwfje.png", unit: "元", key: "ServiceFeeAmount" },
+  { type: "tkje", label: "退款金额", icon: "tkje.png", unit: "元", key: "RefundAmount" },
+  { type: "sfje", label: "实付金额", icon: "sfje.png", unit: "元", key: "ActualPayAmount" },
+  { type: "sdje", label: "首单金额", icon: "sdje.png", unit: "元", key: "FirstOrderAmount" },
+  { type: "yhqjm", label: "优惠券减免", icon: "yhqjm.png", unit: "元", key: "CouponAmount" },
+  {
+    type: "qyzxjj",
+    label: "企业专享价减",
+    icon: "qyzxjj.png",
+    unit: "元",
+    key: "FirmDiscountAmount",
+  },
+  { type: "fxyj", label: "分销佣金", icon: "fxyj.png", unit: "元", key: "CommissionAmount" },
 ];
 
+// 总数据
+const totalData = ref<DataBoardRealTimeVO>({});
+
 // 总数据列表
 const totalDataList = computed(() =>
   dataMetrics.map((item) => ({
     ...item,
     label: `总${item.label}`,
-    value: "198,708.45",
+    value: formatNumber(totalData.value[`total${item.key}` as keyof DataBoardRealTimeVO]),
   }))
 );
 
+const getTotalDataList = () => {
+  dataBoardApi.getRealTimeData().then((res) => {
+    isLoading.value = false;
+    ElMessage.success("数据已刷新");
+    totalData.value = res;
+  });
+};
+
+// 今日数据
+const todayData = ref<DataBoardTodayVO>({});
+
 // 今日数据列表
 const todayDataList = computed(() =>
   dataMetrics.map((item) => ({
     ...item,
     label: `今日${item.label}`,
-    value: "198,708.45",
+    value: formatNumber(todayData.value[`today${item.key}` as keyof DataBoardTodayVO]),
   }))
 );
 
+const getTodayDataList = () => {
+  dataBoardApi.getTodayRealTimeData().then((res) => {
+    isLoading.value = false;
+    todayData.value = res;
+  });
+};
+
+// 格式化数字
+const formatNumber = (value: number | undefined): string => {
+  if (value === undefined || value === null) return "0.00";
+  return value.toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
+};
+
 // ==================== 日时段对比趋势图表 ====================
 const hourlyTrendChartOptions = computed(() => {
   const hours = Array.from({ length: 24 }, (_, i) => i);
-  // 模拟今日数据
-  const todayData = [
-    50, 80, 120, 150, 200, 280, 350, 450, 520, 580, 620, 580, 520, 480, 450, 420, 400, 450, 500,
-    480, 420, 350, 200, 100,
-  ];
-  // 模拟昨日数据
-  const yesterdayData = [
-    40, 70, 100, 130, 180, 250, 320, 420, 480, 550, 600, 560, 500, 460, 430, 400, 380, 420, 470,
-    450, 400, 320, 180, 80,
-  ];
+  // 今日数据
+  const todayData = chargePowerTrendData.value?.todayData ?? [];
+  // 对比日数据
+  const compareData = chargePowerTrendData.value?.compareData ?? [];
 
   return {
     tooltip: {
@@ -409,7 +513,7 @@ const hourlyTrendChartOptions = computed(() => {
       {
         name: compareDate.value,
         type: "line",
-        data: yesterdayData,
+        data: compareData,
         smooth: true,
         symbol: "circle",
         symbolSize: 6,
@@ -443,38 +547,67 @@ const historyTimeType = ref("day");
 const historyDateValue = ref(dayjs().format("YYYY-MM"));
 const historyMetric = ref("chargeDegree");
 
-// 监听时间维度变化,重置日期值
+// 历史营业数据
+const historyBusinessData = ref<HistoryBusinessDataVO>({});
+// 历史营业数据图表加载状态
+const historyLoading = ref(false);
+
+// 指标映射:前端值 -> API值
+const metricToApiMap: Record<string, string> = {
+  chargeDegree: "chargePower",
+  chargeAmount: "chargeAmount",
+  validOrder: "validOrders",
+  registerUser: "registerUsers",
+};
+
+// 指标标签映射
+const metricLabels: Record<string, string> = {
+  chargeDegree: "充电度数",
+  chargeAmount: "充电金额",
+  validOrder: "有效订单",
+  registerUser: "注册用户",
+};
+
+// 获取历史营业数据
+const getHistoryBusinessData = () => {
+  const dataType = metricToApiMap[historyMetric.value];
+  const timeDimension = historyTimeType.value;
+  const yearMonth = historyDateValue.value;
+
+  historyLoading.value = true;
+  dataBoardApi
+    .getHistoryBusinessData({
+      dataType,
+      timeDimension,
+      yearMonth,
+    })
+    .then((res) => {
+      historyBusinessData.value = res;
+    })
+    .finally(() => {
+      historyLoading.value = false;
+    });
+};
+
+// 监听时间维度变化,重置日期值并重新获取数据
 watch(historyTimeType, (newType) => {
   if (newType === "day") {
     historyDateValue.value = dayjs().format("YYYY-MM");
   } else {
     historyDateValue.value = dayjs().format("YYYY");
   }
+  getHistoryBusinessData();
 });
 
-const historyAreaChartOptions = computed(() => {
-  let dates: string[];
-  let mockData: number[];
-
-  if (historyTimeType.value === "day") {
-    // 日趋势:显示月份中的每一天
-    const daysInMonth = dayjs(historyDateValue.value).daysInMonth();
-    dates = Array.from({ length: daysInMonth }, (_, i) =>
-      dayjs(historyDateValue.value).startOf("month").add(i, "day").format("MM.DD")
-    );
-    mockData = Array.from({ length: daysInMonth }, () => Math.floor(Math.random() * 5000) + 2000);
-  } else {
-    // 月趋势:显示年份中的12个月
-    dates = Array.from({ length: 12 }, (_, i) => `${i + 1}月`);
-    mockData = Array.from({ length: 12 }, () => Math.floor(Math.random() * 50000) + 20000);
-  }
+// 监听日期和指标变化,重新获取数据
+watch([historyDateValue, historyMetric], () => {
+  getHistoryBusinessData();
+});
 
-  const metricLabels: Record<string, string> = {
-    chargeDegree: "充电度数",
-    chargeAmount: "充电金额",
-    validOrder: "有效订单",
-    registerUser: "注册用户",
-  };
+const historyAreaChartOptions = computed(() => {
+  // 使用API返回的数据
+  const dates = historyBusinessData.value?.labels ?? [];
+  const dataValues = historyBusinessData.value?.values ?? [];
 
   return {
     tooltip: {
@@ -506,7 +639,7 @@ const historyAreaChartOptions = computed(() => {
       {
         name: metricLabels[historyMetric.value],
         type: "line",
-        data: mockData,
+        data: dataValues,
         smooth: true,
         areaStyle: {
           color: {
@@ -539,15 +672,24 @@ const tableDateRange = ref("week");
 const tableStartDate = ref("");
 const tableEndDate = ref("");
 
-// 热门充电站分页
-const hotCurrentPage = ref(1);
-const hotPageSize = ref(5);
-const hotStationTotal = ref(50);
+// 表格加载状态
+const tableLoading = ref(false);
 
-// 波动充电站分页
-const fluctuationCurrentPage = ref(1);
-const fluctuationPageSize = ref(5);
-const fluctuationStationTotal = ref(50);
+// 站点排名数据
+const stationRankData = ref<StationRankListVO>({});
+
+// 统计类型映射:前端值 -> API值
+const statisticTypeMap: Record<string, string> = {
+  degree: "chargePower",
+  order: "validOrders",
+};
+
+// 时间范围映射:前端值 -> API值
+const timeRangeMap: Record<string, string> = {
+  yesterday: "today",
+  week: "week",
+  month: "month",
+};
 
 // 选中的日期范围显示
 const selectedDateRange = computed(() => {
@@ -568,30 +710,89 @@ const selectedDateRange = computed(() => {
 });
 
 // 热门充电站数据
-const hotStationData = computed(() => [
-  { name: "小关停车场B区-小站", value: 100.01 },
-  { name: "延安西路智慧停车楼", value: 99.01 },
-  { name: "花果园立交桥南广场停车场", value: 97.01 },
-  { name: "花果园立交北广场停车场", value: 97 },
-  { name: "轩宇智慧停车场", value: 96 },
-]);
+const hotStationData = computed(() => stationRankData.value?.hotStations ?? []);
 
 // 波动充电站数据
-const fluctuationStationData = computed(() => [
-  { name: "小关停车场B区-小站", rate: 6.29 },
-  { name: "延安西路智慧停车楼", rate: 5.34 },
-  { name: "花果园立交桥南广场停车场", rate: 4.21 },
-  { name: "花果园立交北广场停车场", rate: -3.52 },
-  { name: "轩宇智慧停车场", rate: -1.98 },
-]);
+const fluctuationStationData = computed(() => stationRankData.value?.fluctuationStations ?? []);
 
-// ==================== 用户流失率 ====================
-const lossRate = ref({
-  sevenDay: "59.99",
-  oneMonth: "70.12",
-  threeMonth: "70.12",
+// 获取站点排名数据
+const getStationRankData = () => {
+  const statisticType = statisticTypeMap[tableDataType.value];
+  let timeRange = timeRangeMap[tableDateRange.value];
+  let startDate: string | undefined;
+  let endDate: string | undefined;
+
+  // 自定义时间范围
+  if (tableStartDate.value && tableEndDate.value) {
+    timeRange = "custom";
+    startDate = tableStartDate.value;
+    endDate = tableEndDate.value;
+  }
+
+  tableLoading.value = true;
+  dataBoardApi
+    .getStationRankData({
+      statisticType,
+      timeRange,
+      startDate,
+      endDate,
+    })
+    .then((res) => {
+      stationRankData.value = res;
+    })
+    .finally(() => {
+      tableLoading.value = false;
+    });
+};
+
+// 监听表格数据类型变化,重新获取数据
+watch(tableDataType, () => {
+  getStationRankData();
 });
 
+// 日期范围按钮变化处理
+const handleDateRangeChange = () => {
+  // 清空手动选择的日期
+  tableStartDate.value = "";
+  tableEndDate.value = "";
+  getStationRankData();
+};
+
+// 开始日期变化处理
+const handleStartDateChange = () => {
+  // 清空按钮选择
+  tableDateRange.value = "";
+  // 如果结束日期已选,自动触发查询
+  if (tableEndDate.value) {
+    getStationRankData();
+  }
+};
+
+// 结束日期变化处理
+const handleEndDateChange = () => {
+  // 清空按钮选择
+  tableDateRange.value = "";
+  // 如果开始日期已选,自动触发查询
+  if (tableStartDate.value) {
+    getStationRankData();
+  }
+};
+
+// 查询按钮点击处理
+const handleQuery = () => {
+  getStationRankData();
+};
+
+// ==================== 用户流失率 ====================
+const lossRate = ref();
+const getUserLossRate = () => {
+  dataBoardApi.getUserChurnRate().then((res) => {
+    lossRate.value = res;
+  });
+};
+// 下载报表loading状态
+const downloadLoading = ref<string>("");
+
 // 下载报表
 const downloadReport = (type: string) => {
   const typeLabels: Record<string, string> = {
@@ -599,16 +800,43 @@ const downloadReport = (type: string) => {
     oneMonth: "近1个月",
     threeMonth: "近3个月",
   };
-  ElMessage.success(`正在下载${typeLabels[type]}流失率报表...`);
+
+  // 类型映射
+  const periodTypeMap: Record<string, string> = {
+    sevenDay: "7d",
+    oneMonth: "1m",
+    threeMonth: "3m",
+  };
+
+  downloadLoading.value = type;
+  dataBoardApi
+    .exportUserChurnRate({ periodType: periodTypeMap[type] })
+    .then((res: any) => {
+      // 创建Blob对象(res 是 axios response 对象,需要使用 res.data 获取 Blob 数据)
+      const blob = new Blob([res.data], {
+        type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+      });
+      // 创建下载链接
+      const link = document.createElement("a");
+      link.href = URL.createObjectURL(blob);
+      link.download = `用户流失率统计_${typeLabels[type]}_${dayjs().format("YYYY-MM-DD")}.xlsx`;
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link);
+      URL.revokeObjectURL(link.href);
+      ElMessage.success(`${typeLabels[type]}流失率报表下载成功`);
+    })
+    .catch(() => {
+      ElMessage.error("下载失败,请重试");
+    })
+    .finally(() => {
+      downloadLoading.value = "";
+    });
 };
 
 const handleClick = (tab: TabsPaneContext, event: Event) => {
   console.log(tab, event);
 };
-
-onMounted(() => {
-  // 初始化数据
-});
 </script>
 
 <style scoped lang="scss">
@@ -617,4 +845,7 @@ onMounted(() => {
     padding: 0;
   }
 }
+.custom-table {
+  background-color: #f3f9ff;
+}
 </style>