feat: 实现实时要闻服务端分页功能
功能新增:
- 实时要闻组件支持服务端分页,每次切换页码重新请求数据
- 分页控制器组件,支持数字页码、上下翻页、快速跳转
- Mock 数据量从 100 条增加到 200 条,支持分页测试
技术实现:
1. Redux 状态管理(communityDataSlice.js)
- fetchDynamicNews 接收分页参数 { page, per_page }
- 返回数据结构调整为 { events, pagination }
- initialState 新增 dynamicNewsPagination 字段
- Reducer 分别存储 events 和 pagination 信息
- Selector 返回完整的 pagination 数据
2. 组件层(index.js → DynamicNewsCard → EventScrollList)
- Community/index.js: 获取并传递 pagination 信息
- DynamicNewsCard.js: 管理分页状态,触发服务端请求
- EventScrollList.js: 接收服务端 totalPages,渲染当前页数据
- 页码切换时自动选中第一个事件
3. 分页控制器(PaginationControl.js)
- 精简版设计:移除首页/末页按钮
- 上一页/下一页按钮,边界状态自动禁用
- 智能页码列表(最多5个,使用省略号)
- 输入框跳转功能,支持回车键
- Toast 提示非法输入
- 全部使用 xs 尺寸,紧凑布局
4. Mock 数据(events.js)
- 总事件数从 100 增加到 200 条
- 支持服务端分页测试(40 页 × 5 条/页)
分页流程:
1. 初始加载:请求 page=1, per_page=5
2. 切换页码:dispatch(fetchDynamicNews({ page: 2, per_page: 5 }))
3. 后端返回:{ events: [5条], pagination: { page, total, total_pages } }
4. 前端更新:显示新页面数据,更新分页控制器状态
UI 优化:
- 紧凑的分页控制器布局
- 移除冗余元素(首页/末页/总页数提示)
- xs 尺寸按钮,减少视觉负担
- 保留核心功能(翻页、页码、跳转)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
// 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';
|
||||
|
||||
/**
|
||||
* 分页控制器组件
|
||||
* @param {number} currentPage - 当前页码
|
||||
* @param {number} totalPages - 总页数
|
||||
* @param {Function} onPageChange - 页码改变回调
|
||||
*/
|
||||
const PaginationControl = ({ 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>
|
||||
);
|
||||
};
|
||||
|
||||
export default PaginationControl;
|
||||
Reference in New Issue
Block a user