216 lines
5.7 KiB
JavaScript
216 lines
5.7 KiB
JavaScript
// src/views/Community/components/DynamicNewsCard/PaginationControl.js
|
||
// 分页控制器组件
|
||
|
||
import React, { useState } from 'react';
|
||
import {
|
||
Box,
|
||
HStack,
|
||
Button,
|
||
Input,
|
||
Text,
|
||
IconButton,
|
||
useColorModeValue,
|
||
useToast,
|
||
} from '@chakra-ui/react';
|
||
import {
|
||
ChevronLeftIcon,
|
||
ChevronRightIcon,
|
||
} from '@chakra-ui/icons';
|
||
|
||
/**
|
||
* 分页控制器组件(使用 React.memo 优化,避免不必要的重新渲染)
|
||
* @param {number} currentPage - 当前页码
|
||
* @param {number} totalPages - 总页数
|
||
* @param {Function} onPageChange - 页码改变回调
|
||
*/
|
||
const PaginationControl = React.memo(({ currentPage, totalPages, onPageChange }) => {
|
||
const [jumpPage, setJumpPage] = useState('');
|
||
const toast = useToast();
|
||
|
||
const buttonBg = useColorModeValue('white', 'gray.700');
|
||
const activeBg = useColorModeValue('blue.500', 'blue.400');
|
||
const activeColor = useColorModeValue('white', 'white');
|
||
const borderColor = useColorModeValue('gray.300', 'gray.600');
|
||
const hoverBg = useColorModeValue('gray.100', 'gray.600');
|
||
|
||
// 生成页码数字列表(智能省略)
|
||
const getPageNumbers = () => {
|
||
const pageNumbers = [];
|
||
const maxVisible = 5; // 最多显示5个页码(精简版)
|
||
|
||
if (totalPages <= maxVisible) {
|
||
// 总页数少,显示全部
|
||
for (let i = 1; i <= totalPages; i++) {
|
||
pageNumbers.push(i);
|
||
}
|
||
} else {
|
||
// 总页数多,使用省略号
|
||
if (currentPage <= 3) {
|
||
// 当前页在前面
|
||
for (let i = 1; i <= 4; i++) {
|
||
pageNumbers.push(i);
|
||
}
|
||
pageNumbers.push('...');
|
||
pageNumbers.push(totalPages);
|
||
} else if (currentPage >= totalPages - 2) {
|
||
// 当前页在后面
|
||
pageNumbers.push(1);
|
||
pageNumbers.push('...');
|
||
for (let i = totalPages - 3; i <= totalPages; i++) {
|
||
pageNumbers.push(i);
|
||
}
|
||
} else {
|
||
// 当前页在中间
|
||
pageNumbers.push(1);
|
||
pageNumbers.push('...');
|
||
pageNumbers.push(currentPage);
|
||
pageNumbers.push('...');
|
||
pageNumbers.push(totalPages);
|
||
}
|
||
}
|
||
|
||
return pageNumbers;
|
||
};
|
||
|
||
// 处理页码跳转
|
||
const handleJump = () => {
|
||
const page = parseInt(jumpPage, 10);
|
||
|
||
if (isNaN(page)) {
|
||
toast({
|
||
title: '请输入有效的页码',
|
||
status: 'warning',
|
||
duration: 2000,
|
||
isClosable: true,
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (page < 1 || page > totalPages) {
|
||
toast({
|
||
title: `页码范围:1 - ${totalPages}`,
|
||
status: 'warning',
|
||
duration: 2000,
|
||
isClosable: true,
|
||
});
|
||
return;
|
||
}
|
||
|
||
onPageChange(page);
|
||
setJumpPage('');
|
||
};
|
||
|
||
// 处理回车键
|
||
const handleKeyPress = (e) => {
|
||
if (e.key === 'Enter') {
|
||
handleJump();
|
||
}
|
||
};
|
||
|
||
const pageNumbers = getPageNumbers();
|
||
|
||
return (
|
||
<Box mb={3}>
|
||
<HStack spacing={1.5} justify="center" flexWrap="wrap">
|
||
{/* 上一页按钮 */}
|
||
<IconButton
|
||
icon={<ChevronLeftIcon />}
|
||
size="xs"
|
||
onClick={() => onPageChange(currentPage - 1)}
|
||
isDisabled={currentPage === 1}
|
||
bg={buttonBg}
|
||
borderWidth="1px"
|
||
borderColor={borderColor}
|
||
_hover={{ bg: hoverBg }}
|
||
aria-label="上一页"
|
||
title="上一页"
|
||
/>
|
||
|
||
{/* 数字页码列表 */}
|
||
{pageNumbers.map((page, index) => {
|
||
if (page === '...') {
|
||
return (
|
||
<Text
|
||
key={`ellipsis-${index}`}
|
||
px={1}
|
||
fontSize="xs"
|
||
color="gray.500"
|
||
>
|
||
...
|
||
</Text>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<Button
|
||
key={page}
|
||
size="xs"
|
||
onClick={() => onPageChange(page)}
|
||
bg={currentPage === page ? activeBg : buttonBg}
|
||
color={currentPage === page ? activeColor : undefined}
|
||
borderWidth="1px"
|
||
borderColor={currentPage === page ? activeBg : borderColor}
|
||
_hover={{
|
||
bg: currentPage === page ? activeBg : hoverBg,
|
||
}}
|
||
minW="28px"
|
||
>
|
||
{page}
|
||
</Button>
|
||
);
|
||
})}
|
||
|
||
{/* 下一页按钮 */}
|
||
<IconButton
|
||
icon={<ChevronRightIcon />}
|
||
size="xs"
|
||
onClick={() => onPageChange(currentPage + 1)}
|
||
isDisabled={currentPage === totalPages}
|
||
bg={buttonBg}
|
||
borderWidth="1px"
|
||
borderColor={borderColor}
|
||
_hover={{ bg: hoverBg }}
|
||
aria-label="下一页"
|
||
title="下一页"
|
||
/>
|
||
|
||
{/* 分隔线 */}
|
||
<Box w="1px" h="20px" bg={borderColor} mx={1.5} />
|
||
|
||
{/* 输入框跳转 */}
|
||
<HStack spacing={1.5}>
|
||
<Text fontSize="xs" color="gray.600">
|
||
跳转到
|
||
</Text>
|
||
<Input
|
||
size="xs"
|
||
width="50px"
|
||
type="number"
|
||
min={1}
|
||
max={totalPages}
|
||
value={jumpPage}
|
||
onChange={(e) => setJumpPage(e.target.value)}
|
||
onKeyPress={handleKeyPress}
|
||
placeholder="页"
|
||
bg={buttonBg}
|
||
borderColor={borderColor}
|
||
/>
|
||
<Button
|
||
size="xs"
|
||
colorScheme="blue"
|
||
onClick={handleJump}
|
||
>
|
||
跳转
|
||
</Button>
|
||
</HStack>
|
||
</HStack>
|
||
</Box>
|
||
);
|
||
}, (prevProps, nextProps) => {
|
||
// 自定义比较函数:只有当 currentPage 或 totalPages 变化时才重新渲染
|
||
return prevProps.currentPage === nextProps.currentPage &&
|
||
prevProps.totalPages === nextProps.totalPages;
|
||
});
|
||
|
||
export default PaginationControl;
|