翻页样式完善

This commit is contained in:
2025-12-18 00:50:35 +08:00
parent e88716b1ea
commit 3d47e568f6
3 changed files with 111 additions and 86 deletions

View File

@@ -0,0 +1,87 @@
import React, { useState, useEffect } from 'react'
import { ChevronLeft, ChevronRight, ChevronDown } from 'lucide-react'
const Pagination = ({
currentPage,
totalItems,
pageSize,
onPageChange,
onPageSizeChange,
pageSizeOptions = [10, 20, 50, 100]
}) => {
const totalPages = Math.ceil(totalItems / pageSize)
const [inputPage, setInputPage] = useState(currentPage)
useEffect(() => {
setInputPage(currentPage)
}, [currentPage])
const handlePageSubmit = (e) => {
e.preventDefault()
let page = parseInt(inputPage)
if (isNaN(page)) page = 1
if (page < 1) page = 1
if (page > totalPages) page = totalPages
onPageChange(page)
setInputPage(page)
}
if (totalItems === 0) return null
return (
<div className="flex flex-col sm:flex-row items-center justify-between gap-4 py-4 border-t border-gray-100 mt-4">
{/* Info */}
<div className="text-sm text-gray-500">
显示 {Math.min((currentPage - 1) * pageSize + 1, totalItems)} - {Math.min(currentPage * pageSize, totalItems)} {totalItems}
</div>
<div className="flex items-center gap-2 sm:gap-4">
{/* Page Size Selector */}
<div className="relative group">
<select
value={pageSize}
onChange={(e) => onPageSizeChange(Number(e.target.value))}
className="appearance-none bg-white border border-gray-300 text-gray-700 py-2 pl-3 pr-8 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent cursor-pointer hover:border-gray-400 transition-colors"
>
{pageSizeOptions.map(size => (
<option key={size} value={size}>{size} /</option>
))}
</select>
<ChevronDown className="absolute right-2 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-500 pointer-events-none" />
</div>
{/* Navigation */}
<div className="flex items-center gap-2">
<button
onClick={() => onPageChange(Math.max(1, currentPage - 1))}
disabled={currentPage === 1}
className="p-2 border border-gray-300 rounded-lg hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
<ChevronLeft className="h-4 w-4" />
</button>
{/* Manual Input */}
<form onSubmit={handlePageSubmit} className="flex items-center">
<input
type="text"
value={inputPage}
onChange={(e) => setInputPage(e.target.value)}
className="w-12 text-center py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent mx-1"
/>
<span className="text-gray-500 text-sm mx-1">/ {totalPages}</span>
</form>
<button
onClick={() => onPageChange(Math.min(totalPages, currentPage + 1))}
disabled={currentPage === totalPages}
className="p-2 border border-gray-300 rounded-lg hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
<ChevronRight className="h-4 w-4" />
</button>
</div>
</div>
</div>
)
}
export default Pagination

View File

@@ -5,7 +5,8 @@ import React, { useState, useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import { mistakeAPI } from '../api/client'
import Layout from '../components/Layout'
import { XCircle, Loader, Trash2, BookOpen, Play, ChevronLeft, ChevronRight } from 'lucide-react'
import Pagination from '../components/Pagination'
import { XCircle, Loader, Trash2, BookOpen, Play } from 'lucide-react'
import toast from 'react-hot-toast'
import { getQuestionTypeText, formatRelativeTime } from '../utils/helpers'
@@ -185,48 +186,16 @@ export const MistakeList = () => {
})}
{/* Pagination */}
{total > limit && (
<div className="flex items-center justify-between pt-4">
<div className="text-sm text-gray-600">
显示 {Math.min((page - 1) * limit + 1, total)} - {Math.min(page * limit, total)} {total}
</div>
<div className="flex gap-2">
<button
onClick={() => setPage(p => Math.max(1, p - 1))}
disabled={page === 1}
className="p-2 border border-gray-300 rounded-lg hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
>
<ChevronLeft className="h-5 w-5" />
</button>
<span className="flex items-center px-4 border border-gray-300 rounded-lg bg-white">
{page}
</span>
<button
onClick={() => setPage(p => (p * limit < total ? p + 1 : p))}
disabled={page * limit >= total}
className="p-2 border border-gray-300 rounded-lg hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
>
<ChevronRight className="h-5 w-5" />
</button>
</div>
</div>
)}
{/* Limit Selector */}
<div className="flex justify-end pt-2">
<select
value={limit}
onChange={(e) => {
setLimit(Number(e.target.value))
setPage(1)
}}
className="text-sm border-gray-300 rounded-lg focus:ring-primary-500 focus:border-primary-500"
>
<option value={10}>10 /</option>
<option value={20}>20 /</option>
<option value={50}>50 /</option>
</select>
</div>
<Pagination
currentPage={page}
totalItems={total}
pageSize={limit}
onPageChange={setPage}
onPageSizeChange={(newLimit) => {
setLimit(newLimit)
setPage(1)
}}
/>
</div>
)}
</div>

View File

@@ -4,7 +4,8 @@
import React, { useState, useEffect } from 'react'
import { questionAPI } from '../api/client'
import Layout from '../components/Layout'
import { FileText, Loader, ChevronLeft, ChevronRight, Search } from 'lucide-react'
import Pagination from '../components/Pagination'
import { FileText, Loader, Search } from 'lucide-react'
import toast from 'react-hot-toast'
import { getQuestionTypeText, formatRelativeTime } from '../utils/helpers'
@@ -140,48 +141,16 @@ export const QuestionBank = () => {
</div>
{/* Pagination */}
{total > limit && (
<div className="flex items-center justify-between pt-4">
<div className="text-sm text-gray-600">
显示 {Math.min((page - 1) * limit + 1, total)} - {Math.min(page * limit, total)} {total}
</div>
<div className="flex gap-2">
<button
onClick={() => setPage(p => Math.max(1, p - 1))}
disabled={page === 1}
className="p-2 border border-gray-300 rounded-lg hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
>
<ChevronLeft className="h-5 w-5" />
</button>
<span className="flex items-center px-4 border border-gray-300 rounded-lg bg-white">
{page}
</span>
<button
onClick={() => setPage(p => (p * limit < total ? p + 1 : p))}
disabled={page * limit >= total}
className="p-2 border border-gray-300 rounded-lg hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
>
<ChevronRight className="h-5 w-5" />
</button>
</div>
</div>
)}
{/* Limit Selector */}
<div className="flex justify-end pt-2">
<select
value={limit}
onChange={(e) => {
setLimit(Number(e.target.value))
setPage(1)
}}
className="text-sm border-gray-300 rounded-lg focus:ring-primary-500 focus:border-primary-500"
>
<option value={10}>10 /</option>
<option value={20}>20 /</option>
<option value={50}>50 /</option>
</select>
</div>
<Pagination
currentPage={page}
totalItems={total}
pageSize={limit}
onPageChange={setPage}
onPageSizeChange={(newLimit) => {
setLimit(newLimit)
setPage(1)
}}
/>
</div>
</Layout>
)