Explorar o código

✨ feat(admin): add data table pagination component

- 创建DataTablePagination组件,实现高级分页功能
- 支持页码智能显示:总页数≤7时显示全部页码,否则显示当前页附近页码
- 包含省略号显示逻辑,优化长列表分页体验
- 在用户管理页面替换原有简单分页为新组件
- 显示总记录数信息,提升数据展示完整性
yourname hai 4 meses
pai
achega
890a30298a

+ 131 - 0
src/client/admin-shadcn/components/DataTablePagination.tsx

@@ -0,0 +1,131 @@
+import React from 'react';
+import {
+  Pagination,
+  PaginationContent,
+  PaginationEllipsis,
+  PaginationItem,
+  PaginationLink,
+  PaginationNext,
+  PaginationPrevious,
+} from '@/client/components/ui/pagination';
+
+interface DataTablePaginationProps {
+  currentPage: number;
+  totalCount: number;
+  pageSize: number;
+  onPageChange: (page: number, pageSize: number) => void;
+}
+
+export const DataTablePagination: React.FC<DataTablePaginationProps> = ({
+  currentPage,
+  totalCount,
+  pageSize,
+  onPageChange,
+}) => {
+  const totalPages = Math.ceil(totalCount / pageSize);
+
+  const getPageNumbers = () => {
+    const pages = [];
+    
+    if (totalPages <= 7) {
+      // 如果总页数小于等于7,显示所有页码
+      for (let i = 1; i <= totalPages; i++) {
+        pages.push(i);
+      }
+    } else {
+      // 显示当前页附近的页码
+      const startPage = Math.max(1, currentPage - 2);
+      const endPage = Math.min(totalPages, currentPage + 2);
+      
+      // 始终显示第一页
+      pages.push(1);
+      
+      // 添加省略号和中间页码
+      if (startPage > 2) {
+        pages.push('...');
+      }
+      
+      for (let i = Math.max(2, startPage); i <= Math.min(totalPages - 1, endPage); i++) {
+        pages.push(i);
+      }
+      
+      if (endPage < totalPages - 1) {
+        pages.push('...');
+      }
+      
+      // 始终显示最后一页
+      pages.push(totalPages);
+    }
+    
+    return pages;
+  };
+
+  const pageNumbers = getPageNumbers();
+
+  return (
+    <div className="flex justify-between items-center mt-4">
+      <div className="text-sm text-muted-foreground">
+        第 {currentPage} 页,共 {totalPages} 页,共 {totalCount} 条记录
+      </div>
+      <Pagination>
+        <PaginationContent>
+          <PaginationItem>
+            <PaginationPrevious
+              href="#"
+              onClick={(e) => {
+                e.preventDefault();
+                if (currentPage > 1) {
+                  onPageChange(currentPage - 1, pageSize);
+                }
+              }}
+              aria-disabled={currentPage <= 1}
+              className={currentPage <= 1 ? "pointer-events-none opacity-50" : ""}
+            >
+              上一页
+            </PaginationPrevious>
+          </PaginationItem>
+          
+          {pageNumbers.map((page, index) => {
+            if (page === '...') {
+              return (
+                <PaginationItem key={`ellipsis-${index}`}>
+                  <PaginationEllipsis />
+                </PaginationItem>
+              );
+            }
+            return (
+              <PaginationItem key={page}>
+                <PaginationLink
+                  href="#"
+                  isActive={page === currentPage}
+                  onClick={(e) => {
+                    e.preventDefault();
+                    onPageChange(page as number, pageSize);
+                  }}
+                >
+                  {page}
+                </PaginationLink>
+              </PaginationItem>
+            );
+          })}
+          
+          <PaginationItem>
+            <PaginationNext
+              href="#"
+              onClick={(e) => {
+                e.preventDefault();
+                if (currentPage < totalPages) {
+                  onPageChange(currentPage + 1, pageSize);
+                }
+              }}
+              aria-disabled={currentPage >= totalPages}
+              className={currentPage >= totalPages ? "pointer-events-none opacity-50" : ""}
+            >
+              下一页
+            </PaginationNext>
+          </PaginationItem>
+        </PaginationContent>
+      </Pagination>
+    </div>
+  );
+};

+ 7 - 23
src/client/admin-shadcn/pages/Users.tsx

@@ -11,6 +11,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@
 import { Badge } from '@/client/components/ui/badge';
 import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/client/components/ui/dialog';
 import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/client/components/ui/form';
+import { DataTablePagination } from '@/client/admin-shadcn/components/DataTablePagination';
 import { useForm } from 'react-hook-form';
 import { zodResolver } from '@hookform/resolvers/zod';
 import { toast } from 'sonner';
@@ -324,29 +325,12 @@ export const UsersPage = () => {
             </Table>
           </div>
 
-          <div className="flex justify-between items-center mt-4">
-            <div className="text-sm text-muted-foreground">
-              第 {searchParams.page} 页,共 {Math.ceil(totalCount / searchParams.limit)} 页
-            </div>
-            <div className="flex gap-2">
-              <Button
-                variant="outline"
-                size="sm"
-                disabled={searchParams.page <= 1}
-                onClick={() => handlePageChange(searchParams.page - 1, searchParams.limit)}
-              >
-                上一页
-              </Button>
-              <Button
-                variant="outline"
-                size="sm"
-                disabled={searchParams.page >= Math.ceil(totalCount / searchParams.limit)}
-                onClick={() => handlePageChange(searchParams.page + 1, searchParams.limit)}
-              >
-                下一页
-              </Button>
-            </div>
-          </div>
+          <DataTablePagination
+            currentPage={searchParams.page}
+            totalCount={totalCount}
+            pageSize={searchParams.limit}
+            onPageChange={handlePageChange}
+          />
         </CardContent>
       </Card>