From b75a0052f926c7e36260c850be744c23a0f9dac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=B8=85=E7=88=BD?= Date: Tue, 22 Apr 2025 16:56:10 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B6=85=E7=AE=A1=E5=90=8E=E5=8F=B0=20-=20?= =?UTF-8?q?=E6=95=B4=E4=BD=93=E9=A1=B5=E9=9D=A2=E9=94=99=E8=AF=AF=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E3=80=81=E5=8F=8A=E8=8F=9C=E5=8D=95=E6=A0=8F=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E9=A2=9C=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SuperAdmin/app/dashboard/customers/page.tsx | 144 ++------------ SuperAdmin/app/dashboard/projects/page.tsx | 86 ++++----- .../components/layout/header.module.css | 3 + SuperAdmin/components/layout/header.tsx | 3 +- SuperAdmin/components/layout/sidebar.tsx | 20 +- .../components/ui/pagination-controls.tsx | 175 ++++++++++++++++++ 6 files changed, 253 insertions(+), 178 deletions(-) create mode 100644 SuperAdmin/components/layout/header.module.css create mode 100644 SuperAdmin/components/ui/pagination-controls.tsx diff --git a/SuperAdmin/app/dashboard/customers/page.tsx b/SuperAdmin/app/dashboard/customers/page.tsx index 4768840a..ee613e8f 100644 --- a/SuperAdmin/app/dashboard/customers/page.tsx +++ b/SuperAdmin/app/dashboard/customers/page.tsx @@ -18,6 +18,7 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Badge } from "@/components/ui/badge" import { getTrafficPoolList } from "@/lib/traffic-pool-api" import { Customer } from "@/lib/traffic-pool-api" +import { PaginationControls } from "@/components/ui/pagination-controls" // Sample customer data const customersData = [ @@ -123,8 +124,7 @@ export default function CustomersPage() { const [currentPage, setCurrentPage] = useState(1) const [totalPages, setTotalPages] = useState(1) const [totalItems, setTotalItems] = useState(0) - const [pageSize, setPageSize] = useState(10) - const [jumpToPage, setJumpToPage] = useState("") + const [pageSize, setPageSize] = useState(100) // 获取客户列表数据 useEffect(() => { @@ -140,10 +140,14 @@ export default function CustomersPage() { } else { setError(response.msg || "获取客户列表失败"); setCustomers([]); + setTotalItems(0); // Reset totals on error + setTotalPages(0); } } catch (err: any) { setError(err.message || "获取客户列表失败"); setCustomers([]); + setTotalItems(0); // Reset totals on error + setTotalPages(0); } finally { setIsLoading(false); } @@ -152,27 +156,10 @@ export default function CustomersPage() { fetchCustomers(); }, [currentPage, pageSize, searchTerm]); - // 切换页码 - const goToPage = (page: number) => { - if (page >= 1 && page <= totalPages) { - setCurrentPage(page); - } - }; - - // 处理页码跳转 - const handleJumpToPage = () => { - const page = parseInt(jumpToPage); - if (!isNaN(page) && page >= 1 && page <= totalPages) { - setCurrentPage(page); - setJumpToPage(""); - } - }; - - // 处理每页显示条数变化 - const handlePageSizeChange = (size: string) => { - const newSize = parseInt(size); + // 修改后的页面大小处理函数 + const handlePageSizeChange = (newSize: number) => { setPageSize(newSize); - setCurrentPage(1); // 重置为第一页 + setCurrentPage(1); // Reset to first page when page size changes }; // Filter customers based on search and filters (兼容示例数据) @@ -308,12 +295,12 @@ export default function CustomersPage() { {isLoading ? ( - 正在加载... + 加载中... ) : error ? ( - + {error} @@ -379,106 +366,15 @@ export default function CustomersPage() { - {/* 分页控件 */} - {!isLoading && !error && customers.length > 0 && ( -
-
-
- 共 {totalItems} 条记录,当前第 {currentPage}/{totalPages} 页 -
-
- 每页显示: - - -
-
-
- - - {/* 数字分页按钮 */} - {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => { - // 显示当前页码前后2页,以及第一页和最后一页 - const shouldShow = - page === 1 || - page === totalPages || - (page >= currentPage - 2 && page <= currentPage + 2); - - if (!shouldShow) { - // 显示省略号 - if (page === currentPage - 3 || page === currentPage + 3) { - return ( - - ... - - ); - } - return null; - } - - return ( - - ); - })} - - - - {/* 页码跳转 */} -
- 跳转到 - setJumpToPage(e.target.value)} - onKeyDown={(e) => e.key === "Enter" && handleJumpToPage()} - className="h-8 w-16 text-center" - min={1} - max={totalPages} - /> - - -
-
-
- )} + {/* 使用新的分页组件 */} + ) diff --git a/SuperAdmin/app/dashboard/projects/page.tsx b/SuperAdmin/app/dashboard/projects/page.tsx index 19cc14d2..f0b18610 100644 --- a/SuperAdmin/app/dashboard/projects/page.tsx +++ b/SuperAdmin/app/dashboard/projects/page.tsx @@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" -import { Plus, Search, MoreHorizontal, Edit, Eye, Trash, ChevronLeft, ChevronRight } from "lucide-react" +import { Plus, Search, MoreHorizontal, Edit, Eye, Trash } from "lucide-react" import { toast } from "sonner" import { Dialog, @@ -17,6 +17,7 @@ import { DialogTitle, } from "@/components/ui/dialog" import { Badge } from "@/components/ui/badge" +import { PaginationControls } from "@/components/ui/pagination-controls" interface Project { id: number @@ -36,7 +37,8 @@ export default function ProjectsPage() { const [isLoading, setIsLoading] = useState(true) const [currentPage, setCurrentPage] = useState(1) const [totalPages, setTotalPages] = useState(1) - const [pageSize] = useState(10) + const [pageSize, setPageSize] = useState(10) + const [totalItems, setTotalItems] = useState(0) const [deleteDialogOpen, setDeleteDialogOpen] = useState(false) const [deletingProjectId, setDeletingProjectId] = useState(null) const [isDeleting, setIsDeleting] = useState(false) @@ -51,12 +53,19 @@ export default function ProjectsPage() { if (data.code === 200) { setProjects(data.data.list) + setTotalItems(data.data.total) setTotalPages(Math.ceil(data.data.total / pageSize)) } else { toast.error(data.msg || "获取项目列表失败") + setProjects([]) + setTotalItems(0) + setTotalPages(0) } } catch (error) { toast.error("获取项目列表失败") + setProjects([]) + setTotalItems(0) + setTotalPages(0) } finally { setIsLoading(false) } @@ -65,11 +74,10 @@ export default function ProjectsPage() { fetchProjects() }, [currentPage, pageSize]) - // 切换页码 - const handlePageChange = (page: number) => { - if (page >= 1 && page <= totalPages) { - setCurrentPage(page) - } + // 处理页面大小变化 + const handlePageSizeChange = (newSize: number) => { + setPageSize(newSize) + setCurrentPage(1) } const handleDeleteClick = (projectId: number) => { @@ -96,8 +104,26 @@ export default function ProjectsPage() { if (data.code === 200) { toast.success("删除成功") - // 刷新项目列表 - window.location.reload() + // Fetch projects again after delete + const fetchProjects = async () => { + setIsLoading(true) + try { + const response = await fetch(`http://yishi.com/company/list?page=${currentPage}&limit=${pageSize}`) + const data = await response.json() + if (data.code === 200) { + setProjects(data.data.list) + setTotalItems(data.data.total) + setTotalPages(Math.ceil(data.data.total / pageSize)) + if (currentPage > Math.ceil(data.data.total / pageSize) && Math.ceil(data.data.total / pageSize) > 0) { + setCurrentPage(Math.ceil(data.data.total / pageSize)); + } + } else { + setProjects([]); setTotalItems(0); setTotalPages(0); + } + } catch (error) { setProjects([]); setTotalItems(0); setTotalPages(0); } + finally { setIsLoading(false); } + } + fetchProjects(); } else { toast.error(data.msg || "删除失败") } @@ -206,41 +232,15 @@ export default function ProjectsPage() { - {/* 分页控件 */} - {!isLoading && projects.length > 0 && ( -
- - - {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => ( - - ))} + - -
- )} - - {/* 删除确认对话框 */} diff --git a/SuperAdmin/components/layout/header.module.css b/SuperAdmin/components/layout/header.module.css new file mode 100644 index 00000000..50bb1fd5 --- /dev/null +++ b/SuperAdmin/components/layout/header.module.css @@ -0,0 +1,3 @@ +.contentHeader { + height: 3.81rem; +} \ No newline at end of file diff --git a/SuperAdmin/components/layout/header.tsx b/SuperAdmin/components/layout/header.tsx index 4b8c5321..2fda80b8 100644 --- a/SuperAdmin/components/layout/header.tsx +++ b/SuperAdmin/components/layout/header.tsx @@ -10,6 +10,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" +import styles from './header.module.css'; interface AdminInfo { id: number; @@ -39,7 +40,7 @@ export function Header() { } return ( -
+
diff --git a/SuperAdmin/components/layout/sidebar.tsx b/SuperAdmin/components/layout/sidebar.tsx index 211b1607..73993682 100644 --- a/SuperAdmin/components/layout/sidebar.tsx +++ b/SuperAdmin/components/layout/sidebar.tsx @@ -100,8 +100,8 @@ export function Sidebar() { onClick={() => toggleMenu(item.id)} className={`flex items-center justify-between px-4 py-2 rounded-md text-sm w-full text-left ${ isActive || isChildActive - ? "bg-primary text-primary-foreground" - : "hover:bg-accent hover:text-accent-foreground" + ? "text-white font-semibold" + : "hover:bg-blue-600" }`} >
@@ -125,8 +125,8 @@ export function Sidebar() { href={child.path} className={`flex items-center px-4 py-2 rounded-md text-sm ${ isChildItemActive - ? "text-primary font-medium" - : "hover:bg-accent hover:text-accent-foreground" + ? "text-white font-semibold bg-blue-700" + : "hover:bg-blue-600" }`} > {child.icon && getLucideIcon(child.icon)} @@ -143,8 +143,8 @@ export function Sidebar() { href={item.path} className={`flex items-center px-4 py-2 rounded-md text-sm ${ isActive - ? "bg-primary text-primary-foreground" - : "hover:bg-accent hover:text-accent-foreground" + ? "text-white font-semibold" + : "hover:bg-blue-600" }`} > {item.icon && getLucideIcon(item.icon)} @@ -156,8 +156,8 @@ export function Sidebar() { }; return ( -
-
+
+

超级管理员

@@ -166,7 +166,7 @@ export function Sidebar() { // 加载状态
{Array.from({ length: 5 }).map((_, i) => ( -
+
))}
) : menus.length > 0 ? ( @@ -176,7 +176,7 @@ export function Sidebar() { ) : ( // 无菜单数据 -
+

暂无菜单数据

)} diff --git a/SuperAdmin/components/ui/pagination-controls.tsx b/SuperAdmin/components/ui/pagination-controls.tsx new file mode 100644 index 00000000..38247ef9 --- /dev/null +++ b/SuperAdmin/components/ui/pagination-controls.tsx @@ -0,0 +1,175 @@ +"use client" + +import { useState, useEffect } from "react" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { ChevronLeft, ChevronRight } from "lucide-react" + +interface PaginationControlsProps { + currentPage: number + totalPages: number + pageSize: number + totalItems: number + onPageChange: (page: number) => void + onPageSizeChange: (size: number) => void +} + +export function PaginationControls({ + currentPage, + totalPages, + pageSize, + totalItems, + onPageChange, + onPageSizeChange, +}: PaginationControlsProps) { + const [jumpToPage, setJumpToPage] = useState("") + + useEffect(() => { + // Reset jump input when page changes externally + setJumpToPage(currentPage.toString()); + }, [currentPage]); + + const handleJumpToPage = () => { + const page = parseInt(jumpToPage) + if (!isNaN(page) && page >= 1 && page <= totalPages) { + onPageChange(page) + } + } + + const handleJumpInputChange = (e: React.ChangeEvent) => { + setJumpToPage(e.target.value); + }; + + const handleJumpInputKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + handleJumpToPage(); + } + }; + + const handleInternalPageSizeChange = (size: string) => { + const newSize = parseInt(size) + onPageSizeChange(newSize) + } + + // --- Page Number Logic --- (Same as customer pool) + const MAX_VISIBLE_PAGES = 5; + let startPage = 1; + let endPage = totalPages; + + if (totalPages > MAX_VISIBLE_PAGES) { + const halfVisible = Math.floor(MAX_VISIBLE_PAGES / 2); + if (currentPage <= halfVisible + 1) { + endPage = MAX_VISIBLE_PAGES; + } else if (currentPage >= totalPages - halfVisible) { + startPage = totalPages - MAX_VISIBLE_PAGES + 1; + } else { + startPage = currentPage - halfVisible; + endPage = currentPage + halfVisible; + } + } + + const pageNumbers = Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i); + // --- End Page Number Logic --- + + if (totalPages <= 0) { + return null; // Don't render pagination if there are no pages + } + + return ( +
+
+ 共 {totalItems} 条记录 +
+
+ + + + + {/* Page Numbers */} + {startPage > 1 && ( + + )} + {startPage > 2 && ( + ... + )} + + {pageNumbers.map((page) => ( + + ))} + + {endPage < totalPages -1 && ( + ... + )} + {endPage < totalPages && ( + + )} + {/* End Page Numbers */} + + + +
+ + +
+
+
+ ) +} \ No newline at end of file