功能新增:
- 实时要闻组件支持服务端分页,每次切换页码重新请求数据
- 分页控制器组件,支持数字页码、上下翻页、快速跳转
- 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>
142 lines
4.2 KiB
JavaScript
142 lines
4.2 KiB
JavaScript
// src/views/Community/components/DynamicNewsCard.js
|
|
// 横向滚动事件卡片组件(实时要闻·动态追踪)
|
|
|
|
import React, { forwardRef, useState, useEffect } from 'react';
|
|
import { useDispatch } from 'react-redux';
|
|
import {
|
|
Card,
|
|
CardHeader,
|
|
CardBody,
|
|
Box,
|
|
Flex,
|
|
VStack,
|
|
HStack,
|
|
Heading,
|
|
Text,
|
|
Badge,
|
|
Center,
|
|
Spinner,
|
|
useColorModeValue
|
|
} from '@chakra-ui/react';
|
|
import { TimeIcon } from '@chakra-ui/icons';
|
|
import EventScrollList from './DynamicNewsCard/EventScrollList';
|
|
import DynamicNewsDetailPanel from './DynamicNewsDetail';
|
|
import { fetchDynamicNews } from '../../../store/slices/communityDataSlice';
|
|
|
|
/**
|
|
* 实时要闻·动态追踪 - 横向滚动卡片组件
|
|
* @param {Array} events - 事件列表
|
|
* @param {boolean} loading - 加载状态
|
|
* @param {Object} pagination - 分页信息 { page, per_page, total, total_pages }
|
|
* @param {Date} lastUpdateTime - 最后更新时间
|
|
* @param {Function} onEventClick - 事件点击回调
|
|
* @param {Function} onViewDetail - 查看详情回调
|
|
* @param {Object} ref - 用于滚动的ref
|
|
*/
|
|
const DynamicNewsCard = forwardRef(({
|
|
events,
|
|
loading,
|
|
pagination = {},
|
|
lastUpdateTime,
|
|
onEventClick,
|
|
onViewDetail,
|
|
...rest
|
|
}, ref) => {
|
|
const dispatch = useDispatch();
|
|
const cardBg = useColorModeValue('white', 'gray.800');
|
|
const borderColor = useColorModeValue('gray.200', 'gray.700');
|
|
const [selectedEvent, setSelectedEvent] = useState(null);
|
|
|
|
const pageSize = 5; // 每页显示5个事件
|
|
const currentPage = pagination.page || 1;
|
|
const totalPages = pagination.total_pages || 1;
|
|
|
|
// 默认选中第一个事件
|
|
useEffect(() => {
|
|
if (events && events.length > 0 && !selectedEvent) {
|
|
setSelectedEvent(events[0]);
|
|
}
|
|
}, [events, selectedEvent]);
|
|
|
|
// 页码改变时,触发服务端分页请求
|
|
const handlePageChange = (newPage) => {
|
|
// 发起 Redux action 获取新页面数据
|
|
dispatch(fetchDynamicNews({ page: newPage, per_page: pageSize }));
|
|
|
|
// 重置选中事件(等新数据加载后自动选中第一个)
|
|
setSelectedEvent(null);
|
|
};
|
|
|
|
return (
|
|
<Card ref={ref} {...rest} bg={cardBg} borderColor={borderColor} mb={4}>
|
|
{/* 标题部分 */}
|
|
<CardHeader>
|
|
<Flex justify="space-between" align="center">
|
|
<VStack align="start" spacing={1}>
|
|
<Heading size="md">
|
|
<HStack>
|
|
<TimeIcon />
|
|
<Text>实时要闻·动态追踪</Text>
|
|
</HStack>
|
|
</Heading>
|
|
<HStack fontSize="sm" color="gray.500">
|
|
<Badge colorScheme="red">实时</Badge>
|
|
<Badge colorScheme="green">盘中</Badge>
|
|
<Badge colorScheme="blue">快讯</Badge>
|
|
</HStack>
|
|
</VStack>
|
|
<Text fontSize="xs" color="gray.500">
|
|
最后更新: {lastUpdateTime?.toLocaleTimeString() || '未知'}
|
|
</Text>
|
|
</Flex>
|
|
</CardHeader>
|
|
|
|
{/* 主体内容 */}
|
|
<CardBody position="relative">
|
|
{/* Loading 状态 */}
|
|
{loading && (
|
|
<Center py={10}>
|
|
<VStack>
|
|
<Spinner size="xl" color="blue.500" thickness="4px" />
|
|
<Text color="gray.500">正在加载最新事件...</Text>
|
|
</VStack>
|
|
</Center>
|
|
)}
|
|
|
|
{/* Empty 状态 */}
|
|
{!loading && (!events || events.length === 0) && (
|
|
<Center py={10}>
|
|
<VStack>
|
|
<Text fontSize="lg" color="gray.500">暂无事件数据</Text>
|
|
</VStack>
|
|
</Center>
|
|
)}
|
|
|
|
{/* 横向滚动事件列表 */}
|
|
{!loading && events && events.length > 0 && (
|
|
<EventScrollList
|
|
events={events}
|
|
selectedEvent={selectedEvent}
|
|
onEventSelect={setSelectedEvent}
|
|
borderColor={borderColor}
|
|
currentPage={currentPage}
|
|
totalPages={totalPages}
|
|
onPageChange={handlePageChange}
|
|
/>
|
|
)}
|
|
|
|
{/* 详情面板 */}
|
|
{!loading && events && events.length > 0 && (
|
|
<Box mt={6}>
|
|
<DynamicNewsDetailPanel event={selectedEvent} />
|
|
</Box>
|
|
)}
|
|
</CardBody>
|
|
</Card>
|
|
);
|
|
});
|
|
|
|
DynamicNewsCard.displayName = 'DynamicNewsCard';
|
|
|
|
export default DynamicNewsCard;
|