Procházet zdrojové kódy

Add export functionality for user, order, and playmate data in StatsDashboardPage

- Integrated export buttons for user, order, and playmate data tables, allowing users to download statistics as Excel files.
- Implemented loading states for each export action to enhance user feedback during the export process.
- Added corresponding export functions in the statistics service to handle data retrieval and file generation.
- Updated the UI to include download icons and improved error handling for export failures.
0es před 3 týdny
rodič
revize
75a42e3fcc

+ 113 - 3
src/app/(dashboard)/statistics/dashboard/page.tsx

@@ -1,8 +1,10 @@
 "use client";
 
 import { DualAxes } from "@ant-design/charts";
+import { DownloadOutlined } from "@ant-design/icons";
 import {
   App,
+  Button,
   Card,
   Col,
   DatePicker,
@@ -17,6 +19,9 @@ import type { ColumnsType } from "antd/es/table";
 import type { Dayjs } from "dayjs";
 import { useCallback, useEffect, useState } from "react";
 import {
+  exportOrderDataStatExcel,
+  exportPlaymateDataStatExcel,
+  exportUserDataStatExcel,
   fetchOrderDataStatPage,
   fetchPlaymateDataStatPage,
   fetchUserDataStatPage,
@@ -221,6 +226,75 @@ const StatsDashboardPage: React.FC = () => {
     void loadData(getDefaultRange(reportType));
   }, [reportType]);
 
+  // ─── 导出 ────────────────────────────────────────────────────
+  const [exportUserLoading, setExportUserLoading] = useState(false);
+  const [exportOrderLoading, setExportOrderLoading] = useState(false);
+  const [exportPlaymateLoading, setExportPlaymateLoading] = useState(false);
+
+  const triggerDownload = (blob: Blob, filename: string | undefined, fallback: string) => {
+    if (!blob || blob.size === 0) {
+      message.error("导出失败:文件为空");
+      return;
+    }
+    const now = new Date();
+    const pad = (n: number) => String(n).padStart(2, "0");
+    const ts = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
+    const url = window.URL.createObjectURL(blob);
+    const a = document.createElement("a");
+    a.href = url;
+    a.download = filename || `${fallback}_${ts}.xlsx`;
+    document.body.appendChild(a);
+    a.click();
+    a.remove();
+    window.URL.revokeObjectURL(url);
+    message.success("导出成功");
+  };
+
+  const handleExportUser = async () => {
+    setExportUserLoading(true);
+    try {
+      const { blob, filename } = await exportUserDataStatExcel(
+        buildQuery(effectiveRange),
+      );
+      triggerDownload(blob, filename, "user_stat");
+    } catch (err) {
+      console.error("Failed to export user stat excel:", err);
+      message.error("用户数据导出失败");
+    } finally {
+      setExportUserLoading(false);
+    }
+  };
+
+  const handleExportOrder = async () => {
+    setExportOrderLoading(true);
+    try {
+      const { blob, filename } = await exportOrderDataStatExcel(
+        buildQuery(effectiveRange),
+      );
+      triggerDownload(blob, filename, "order_stat");
+    } catch (err) {
+      console.error("Failed to export order stat excel:", err);
+      message.error("订单数据导出失败");
+    } finally {
+      setExportOrderLoading(false);
+    }
+  };
+
+  const handleExportPlaymate = async () => {
+    setExportPlaymateLoading(true);
+    try {
+      const { blob, filename } = await exportPlaymateDataStatExcel(
+        buildQuery(effectiveRange),
+      );
+      triggerDownload(blob, filename, "playmate_stat");
+    } catch (err) {
+      console.error("Failed to export playmate stat excel:", err);
+      message.error("陪玩师数据导出失败");
+    } finally {
+      setExportPlaymateLoading(false);
+    }
+  };
+
   const handleRangeChange = (values: [Dayjs | null, Dayjs | null] | null) => {
     if (!values || !values[0] || !values[1]) {
       // User cleared the picker → fall back to default range
@@ -594,7 +668,19 @@ const StatsDashboardPage: React.FC = () => {
           </Row>
 
           {/* ── 用户数据表格 ── */}
-          <Card title="用户数据">
+          <Card
+            title="用户数据"
+            extra={
+              <Button
+                icon={<DownloadOutlined />}
+                size="small"
+                loading={exportUserLoading}
+                onClick={() => void handleExportUser()}
+              >
+                导出
+              </Button>
+            }
+          >
             <Table<UserDataStatAdminDTO>
               rowKey="id"
               columns={userColumns}
@@ -607,7 +693,19 @@ const StatsDashboardPage: React.FC = () => {
           </Card>
 
           {/* ── 订单数据表格 ── */}
-          <Card title="订单数据">
+          <Card
+            title="订单数据"
+            extra={
+              <Button
+                icon={<DownloadOutlined />}
+                size="small"
+                loading={exportOrderLoading}
+                onClick={() => void handleExportOrder()}
+              >
+                导出
+              </Button>
+            }
+          >
             <Table<OrderDataStatAdminDTO>
               rowKey="id"
               columns={orderColumns}
@@ -620,7 +718,19 @@ const StatsDashboardPage: React.FC = () => {
           </Card>
 
           {/* ── 陪玩师数据表格 ── */}
-          <Card title="陪玩师数据">
+          <Card
+            title="陪玩师数据"
+            extra={
+              <Button
+                icon={<DownloadOutlined />}
+                size="small"
+                loading={exportPlaymateLoading}
+                onClick={() => void handleExportPlaymate()}
+              >
+                导出
+              </Button>
+            }
+          >
             <Table<PlaymateDataStatAdminDTO>
               rowKey="id"
               columns={playmateColumns}

+ 13 - 1
src/services/statisticsDashboard.ts

@@ -1,4 +1,4 @@
-import { post } from "@/lib/request";
+import { post, postBlob } from "@/lib/request";
 import type {
   PagerOrderDataStatAdminDTO,
   PagerPlaymateDataStatAdminDTO,
@@ -32,3 +32,15 @@ export async function fetchPlaymateDataStatPage(
     query,
   );
 }
+
+export async function exportUserDataStatExcel(query: StatAdminQuery) {
+  return postBlob("/stat/mananger/userDataStat-export-excel", query);
+}
+
+export async function exportOrderDataStatExcel(query: StatAdminQuery) {
+  return postBlob("/stat/mananger/orderDataStat-export-excel", query);
+}
+
+export async function exportPlaymateDataStatExcel(query: StatAdminQuery) {
+  return postBlob("/stat/mananger/playmateDataStat-export-excel", query);
+}