File size: 9,062 Bytes
a21c316 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | import { ChevronLeft, ChevronRight } from 'lucide-react';
import { useTranslation } from 'react-i18next';
interface PaginationProps {
currentPage: number;
totalPages: number;
onPageChange: (page: number) => void;
totalItems: number;
itemsPerPage: number;
onPageSizeChange?: (pageSize: number) => void; // 新增:分页大小变更回调
pageSizeOptions?: number[]; // 新增:可选的分页大小选项
}
function Pagination({
currentPage,
totalPages,
onPageChange,
totalItems,
itemsPerPage,
onPageSizeChange,
pageSizeOptions = [10, 20, 50, 100]
}: PaginationProps) {
const { t } = useTranslation();
if (totalPages <= 1 && !onPageSizeChange) return null;
// 计算显示的页码范围 (最多显示 5 个页码)
let startPage = Math.max(1, currentPage - 2);
let endPage = Math.min(totalPages, startPage + 4);
if (endPage - startPage < 4) {
startPage = Math.max(1, endPage - 4);
}
const pages = [];
for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}
const startIndex = (currentPage - 1) * itemsPerPage + 1;
const endIndex = Math.min(currentPage * itemsPerPage, totalItems);
return (
<div className="flex items-center justify-between px-6 py-3">
{/* Mobile View */}
<div className="flex flex-1 justify-between sm:hidden">
<button
onClick={() => onPageChange(currentPage - 1)}
disabled={currentPage === 1}
className={`relative inline-flex items-center rounded-md border border-gray-300 dark:border-gray-600 px-4 py-2 text-sm font-medium ${currentPage === 1
? 'bg-gray-100 dark:bg-gray-800 text-gray-400 cursor-not-allowed'
: 'bg-white dark:bg-base-100 text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-base-200'
}`}
>
{t('common.prev_page')}
</button>
<button
onClick={() => onPageChange(currentPage + 1)}
disabled={currentPage === totalPages}
className={`relative ml-3 inline-flex items-center rounded-md border border-gray-300 dark:border-gray-600 px-4 py-2 text-sm font-medium ${currentPage === totalPages
? 'bg-gray-100 dark:bg-gray-800 text-gray-400 cursor-not-allowed'
: 'bg-white dark:bg-base-100 text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-base-200'
}`}
>
{t('common.next_page')}
</button>
</div>
{/* Desktop View */}
<div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between">
<div className="flex items-center gap-4">
<p className="text-sm text-gray-700 dark:text-gray-400">
{t('common.pagination_info', { start: startIndex, end: endIndex, total: totalItems })}
</p>
{/* 分页大小选择器 */}
{onPageSizeChange && (
<div className="flex items-center gap-2">
<span className="text-sm text-gray-600 dark:text-gray-400">{t('common.per_page')}</span>
<select
value={itemsPerPage}
onChange={(e) => onPageSizeChange(parseInt(e.target.value))}
className="px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-base-100 text-gray-900 dark:text-base-content focus:outline-none focus:ring-2 focus:ring-blue-500"
>
{pageSizeOptions.map(size => (
<option key={size} value={size}>{size} {t('common.items')}</option>
))}
</select>
</div>
)}
</div>
<div>
<nav className="isolate inline-flex -space-x-px rounded-md shadow-sm" aria-label="Pagination">
<button
onClick={() => onPageChange(currentPage - 1)}
disabled={currentPage === 1}
className={`relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 dark:ring-gray-600 hover:bg-gray-50 dark:hover:bg-base-200 focus:z-20 focus:outline-offset-0 ${currentPage === 1 ? 'cursor-not-allowed opacity-50' : ''
}`}
>
<span className="sr-only">{t('common.prev_page')}</span>
<ChevronLeft className="h-4 w-4" aria-hidden="true" />
</button>
{/* First Page Link if needed */}
{startPage > 1 && (
<>
<button
onClick={() => onPageChange(1)}
className="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-900 dark:text-gray-200 ring-1 ring-inset ring-gray-300 dark:ring-gray-600 hover:bg-gray-50 dark:hover:bg-base-200 focus:z-20 focus:outline-offset-0"
>
1
</button>
{startPage > 2 && (
<span className="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-700 dark:text-gray-400 ring-1 ring-inset ring-gray-300 dark:ring-gray-600 focus:outline-offset-0">
...
</span>
)}
</>
)}
{pages.map(page => (
<button
key={page}
onClick={() => onPageChange(page)}
aria-current={page === currentPage ? 'page' : undefined}
className={`relative inline-flex items-center px-4 py-2 text-sm font-semibold focus:z-20 focus:outline-offset-0 ${page === currentPage
? 'z-10 bg-blue-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600'
: 'text-gray-900 dark:text-gray-200 ring-1 ring-inset ring-gray-300 dark:ring-gray-600 hover:bg-gray-50 dark:hover:bg-base-200'
}`}
>
{page}
</button>
))}
{/* Last Page Link if needed */}
{endPage < totalPages && (
<>
{endPage < totalPages - 1 && (
<span className="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-700 dark:text-gray-400 ring-1 ring-inset ring-gray-300 dark:ring-gray-600 focus:outline-offset-0">
...
</span>
)}
<button
onClick={() => onPageChange(totalPages)}
className="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-900 dark:text-gray-200 ring-1 ring-inset ring-gray-300 dark:ring-gray-600 hover:bg-gray-50 dark:hover:bg-base-200 focus:z-20 focus:outline-offset-0"
>
{totalPages}
</button>
</>
)}
<button
onClick={() => onPageChange(currentPage + 1)}
disabled={currentPage === totalPages}
className={`relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 dark:ring-gray-600 hover:bg-gray-50 dark:hover:bg-base-200 focus:z-20 focus:outline-offset-0 ${currentPage === totalPages ? 'cursor-not-allowed opacity-50' : ''
}`}
>
<span className="sr-only">{t('common.next_page')}</span>
<ChevronRight className="h-4 w-4" aria-hidden="true" />
</button>
</nav>
</div>
</div>
</div>
);
}
export default Pagination;
|