Files
smart-crop-ui/crop-x/src/components/ui/data-pagination.tsx

188 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from 'react';
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, MoreHorizontal } from 'lucide-react';
import { Button } from './button';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './select';
import { cn } from '@/lib/utils';
interface DataPaginationProps {
currentPage: number;
totalPages: number;
pageSize: number;
totalItems: number;
startIndex: number;
endIndex: number;
onPageChange: (page: number) => void;
onPageSizeChange: (size: number) => void;
canPreviousPage: boolean;
canNextPage: boolean;
pageSizeOptions?: number[];
}
export function DataPagination({
currentPage,
totalPages,
pageSize,
totalItems,
startIndex,
endIndex,
onPageChange,
onPageSizeChange,
canPreviousPage,
canNextPage,
pageSizeOptions = [10, 30, 50, 100],
}: DataPaginationProps) {
// 生成页码数组
const generatePageNumbers = () => {
const pages: (number | 'ellipsis')[] = [];
const maxVisiblePages = 5;
if (totalPages <= maxVisiblePages) {
// 如果总页数少,显示所有页码
for (let i = 1; i <= totalPages; i++) {
pages.push(i);
}
} else {
// 总是显示第一页
pages.push(1);
if (currentPage > 3) {
pages.push('ellipsis');
}
// 显示当前页附近的页码
const startPage = Math.max(2, currentPage - 1);
const endPage = Math.min(totalPages - 1, currentPage + 1);
for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}
if (currentPage < totalPages - 2) {
pages.push('ellipsis');
}
// 总是显示最后一页
if (totalPages > 1) {
pages.push(totalPages);
}
}
return pages;
};
const pageNumbers = generatePageNumbers();
if (totalItems === 0) {
return null;
}
return (
<div className="border-t bg-background">
<div className="flex flex-col gap-4 px-4 py-4 sm:flex-row sm:items-center sm:justify-between">
{/* 左侧:每页显示数量 */}
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground hidden sm:inline"></span>
<Select
value={pageSize.toString()}
onValueChange={(value) => onPageSizeChange(Number(value))}
>
<SelectTrigger className="h-9 w-[70px]">
<SelectValue />
</SelectTrigger>
<SelectContent side="top">
{pageSizeOptions.map((size) => (
<SelectItem key={size} value={size.toString()}>
{size}
</SelectItem>
))}
</SelectContent>
</Select>
<span className="text-sm text-muted-foreground"></span>
</div>
{/* 中间:分页器 */}
<div className="flex items-center gap-2">
{/* 第一页 */}
<Button
variant="outline"
size="sm"
onClick={() => onPageChange(1)}
disabled={!canPreviousPage}
className="h-9 w-9 p-0 hidden sm:flex"
>
<ChevronsLeft className="h-4 w-4" />
</Button>
{/* 上一页 */}
<Button
variant="outline"
size="sm"
onClick={() => onPageChange(currentPage - 1)}
disabled={!canPreviousPage}
className="h-9 w-9 p-0"
>
<ChevronLeft className="h-4 w-4" />
</Button>
{/* 页码 */}
<div className="flex items-center gap-1">
{pageNumbers.map((page, index) => (
<React.Fragment key={index}>
{page === 'ellipsis' ? (
<div className="flex h-9 w-9 items-center justify-center">
<MoreHorizontal className="h-4 w-4 text-muted-foreground" />
</div>
) : (
<Button
variant={currentPage === page ? 'default' : 'outline'}
size="sm"
onClick={() => onPageChange(page)}
className={cn(
'h-9 w-9 p-0',
currentPage === page && 'bg-green-600 hover:bg-green-700 text-white'
)}
>
{page}
</Button>
)}
</React.Fragment>
))}
</div>
{/* 下一页 */}
<Button
variant="outline"
size="sm"
onClick={() => onPageChange(currentPage + 1)}
disabled={!canNextPage}
className="h-9 w-9 p-0"
>
<ChevronRight className="h-4 w-4" />
</Button>
{/* 最后一页 */}
<Button
variant="outline"
size="sm"
onClick={() => onPageChange(totalPages)}
disabled={!canNextPage}
className="h-9 w-9 p-0 hidden sm:flex"
>
<ChevronsRight className="h-4 w-4" />
</Button>
</div>
{/* 右侧:统计信息 */}
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<span className="hidden sm:inline">
<span className="font-medium text-foreground">{startIndex}</span> {' '}
<span className="font-medium text-foreground">{endIndex}</span>
</span>
<span>
<span className="font-medium text-foreground">{totalItems}</span>
</span>
</div>
</div>
</div>
);
}