Browse Source

Add user export functionality to UserListPage

- Implemented an export feature to download user data as a desensitized Excel file.
- Introduced a new button with a loading state for the export action.
- Updated the user service to handle the export request via a new API endpoint.
0es 3 weeks ago
parent
commit
3d66a6011d
2 changed files with 58 additions and 1 deletions
  1. 50 0
      src/app/(dashboard)/user/list/page.tsx
  2. 8 1
      src/services/user.ts

+ 50 - 0
src/app/(dashboard)/user/list/page.tsx

@@ -2,6 +2,7 @@
 
 import {
   DownOutlined,
+  DownloadOutlined,
   EditOutlined,
   EyeOutlined,
   LockOutlined,
@@ -39,6 +40,7 @@ import AudioUpload from "@/components/AudioUpload";
 import ImageUpload from "@/components/ImageUpload";
 import { useGlobalConsts } from "@/contexts/GlobalConstsContext";
 import {
+  exportUserExcel,
   getUserDetail,
   getUserPage,
   lockOrUnlockUser,
@@ -66,6 +68,7 @@ const UserListPage: React.FC = () => {
 
   // State management
   const [loading, setLoading] = useState(false);
+  const [exportLoading, setExportLoading] = useState(false);
   const [dataSource, setDataSource] = useState<UserInfoAdminDTO[]>([]);
   const [total, setTotal] = useState(0);
   const [queryParams, setQueryParams] = useState<UserInfoAdminQuery>({
@@ -142,6 +145,46 @@ const UserListPage: React.FC = () => {
     loadPageData();
   };
 
+  // Export excel (desensitized)
+  const handleExport = async () => {
+    setExportLoading(true);
+    try {
+      const exportQuery: UserInfoAdminQuery = {
+        ...queryParams,
+        pageSize: 0,
+        pageIndex: 0,
+      };
+
+      const { blob, filename } = await exportUserExcel(exportQuery);
+
+      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 fallbackName = `user_list_${ts}.xlsx`;
+
+      const url = window.URL.createObjectURL(blob);
+      const a = document.createElement("a");
+      a.href = url;
+      a.download = filename || fallbackName;
+      document.body.appendChild(a);
+      a.click();
+      a.remove();
+      window.URL.revokeObjectURL(url);
+
+      message.success("导出成功");
+    } catch (error) {
+      console.error("Failed to export user excel:", error);
+      message.error("导出失败");
+    } finally {
+      setExportLoading(false);
+    }
+  };
+
   // Pagination change handler
   const handleTableChange = (pagination: TablePaginationConfig) => {
     setQueryParams({
@@ -572,6 +615,13 @@ const UserListPage: React.FC = () => {
               >
                 刷新
               </Button>
+              <Button
+                icon={<DownloadOutlined />}
+                onClick={() => void handleExport()}
+                loading={exportLoading}
+              >
+                导出 (脱敏)
+              </Button>
             </Space>
           </Form.Item>
         </Form>

+ 8 - 1
src/services/user.ts

@@ -2,7 +2,7 @@
  * User management API service
  */
 
-import { post } from "@/lib/request";
+import { post, postBlob } from "@/lib/request";
 import type {
   PagerUserRandomProfileAdminDTO,
   UserRandomProfileAddAdminDTO,
@@ -95,3 +95,10 @@ export async function updateUserRandomProfile(
 export async function deleteUserRandomProfiles(ids: string[]): Promise<void> {
   await post<unknown>("/user/random-profile/delete", { ids });
 }
+
+/**
+ * Export user list as Excel (desensitized)
+ */
+export async function exportUserExcel(query: UserInfoAdminQuery) {
+  return postBlob("/user/user-export-excel", query);
+}