feat: 单排/双排列表模式切换
This commit is contained in:
@@ -25,7 +25,7 @@ import UnifiedSearchBox from './UnifiedSearchBox';
|
||||
import { fetchDynamicNews } from '../../../store/slices/communityDataSlice';
|
||||
|
||||
/**
|
||||
* 实时要闻·动态追踪 - 横向滚动卡片组件
|
||||
* 实时要闻·动态追踪 - 事件展示卡片组件
|
||||
* @param {Array} events - 事件列表
|
||||
* @param {boolean} loading - 加载状态
|
||||
* @param {Object} pagination - 分页信息 { page, per_page, total, total_pages }
|
||||
@@ -36,6 +36,7 @@ import { fetchDynamicNews } from '../../../store/slices/communityDataSlice';
|
||||
* @param {Function} onSearchFocus - 搜索框获得焦点回调
|
||||
* @param {Function} onEventClick - 事件点击回调
|
||||
* @param {Function} onViewDetail - 查看详情回调
|
||||
* @param {string} mode - 展示模式:'carousel'(单排轮播5个)| 'grid'(双排网格10个)
|
||||
* @param {Object} ref - 用于滚动的ref
|
||||
*/
|
||||
const DynamicNewsCard = forwardRef(({
|
||||
@@ -55,11 +56,25 @@ const DynamicNewsCard = forwardRef(({
|
||||
const cardBg = useColorModeValue('white', 'gray.800');
|
||||
const borderColor = useColorModeValue('gray.200', 'gray.700');
|
||||
const [selectedEvent, setSelectedEvent] = useState(null);
|
||||
const [mode, setMode] = useState('carousel'); // 'carousel' 或 'grid',默认单排
|
||||
|
||||
const pageSize = 5; // 每页显示5个事件
|
||||
// 根据模式决定每页显示数量
|
||||
const pageSize = mode === 'carousel' ? 5 : 10; // carousel: 5个, grid: 10个
|
||||
const currentPage = pagination.page || 1;
|
||||
const totalPages = pagination.total_pages || 1;
|
||||
|
||||
// 模式切换处理
|
||||
const handleModeToggle = (newMode) => {
|
||||
if (newMode !== mode) {
|
||||
setMode(newMode);
|
||||
// 切换模式时重置到第1页并重新请求数据
|
||||
const newPageSize = newMode === 'carousel' ? 5 : 10;
|
||||
dispatch(fetchDynamicNews({ page: 1, per_page: newPageSize }));
|
||||
// 清除当前选中的事件
|
||||
setSelectedEvent(null);
|
||||
}
|
||||
};
|
||||
|
||||
// 默认选中第一个事件
|
||||
useEffect(() => {
|
||||
if (events && events.length > 0 && !selectedEvent) {
|
||||
@@ -123,6 +138,8 @@ const DynamicNewsCard = forwardRef(({
|
||||
totalPages={totalPages}
|
||||
onPageChange={handlePageChange}
|
||||
loading={loading}
|
||||
mode={mode}
|
||||
onModeChange={handleModeToggle}
|
||||
/>
|
||||
) : !loading ? (
|
||||
/* Empty 状态 - 只在非加载且无数据时显示 */
|
||||
|
||||
@@ -5,7 +5,10 @@ import React, { useRef } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Grid,
|
||||
IconButton,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Center,
|
||||
VStack,
|
||||
Spinner,
|
||||
@@ -17,7 +20,7 @@ import DynamicNewsEventCard from '../EventCard/DynamicNewsEventCard';
|
||||
import PaginationControl from './PaginationControl';
|
||||
|
||||
/**
|
||||
* 横向滚动事件列表组件
|
||||
* 事件列表组件 - 支持两种展示模式
|
||||
* @param {Array} events - 当前页的事件列表(服务端已分页)
|
||||
* @param {Object} selectedEvent - 当前选中的事件
|
||||
* @param {Function} onEventSelect - 事件选择回调
|
||||
@@ -26,6 +29,8 @@ import PaginationControl from './PaginationControl';
|
||||
* @param {number} totalPages - 总页数(由服务端返回)
|
||||
* @param {Function} onPageChange - 页码改变回调
|
||||
* @param {boolean} loading - 加载状态
|
||||
* @param {string} mode - 展示模式:'carousel'(单排轮播)| 'grid'(双排网格)
|
||||
* @param {Function} onModeChange - 模式切换回调
|
||||
*/
|
||||
const EventScrollList = ({
|
||||
events,
|
||||
@@ -35,7 +40,9 @@ const EventScrollList = ({
|
||||
currentPage,
|
||||
totalPages,
|
||||
onPageChange,
|
||||
loading = false
|
||||
loading = false,
|
||||
mode = 'carousel',
|
||||
onModeChange
|
||||
}) => {
|
||||
const scrollContainerRef = useRef(null);
|
||||
|
||||
@@ -52,16 +59,35 @@ const EventScrollList = ({
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{/* 分页控制器 - 右上角相对定位 */}
|
||||
{/* 顶部控制栏:模式切换按钮(左)+ 分页控制器(右) */}
|
||||
<Flex justify="space-between" align="center" mb={2}>
|
||||
{/* 模式切换按钮 */}
|
||||
<ButtonGroup size="sm" isAttached>
|
||||
<Button
|
||||
onClick={() => onModeChange('carousel')}
|
||||
colorScheme="blue"
|
||||
variant={mode === 'carousel' ? 'solid' : 'outline'}
|
||||
>
|
||||
单排
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => onModeChange('grid')}
|
||||
colorScheme="blue"
|
||||
variant={mode === 'grid' ? 'solid' : 'outline'}
|
||||
>
|
||||
双排
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
{/* 分页控制器 */}
|
||||
{totalPages > 1 && (
|
||||
<Flex justify="flex-end" p={0} m={0} mb={2}>
|
||||
<PaginationControl
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
onPageChange={onPageChange}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
{/* 横向滚动区域 */}
|
||||
<Box position="relative">
|
||||
@@ -122,17 +148,16 @@ const EventScrollList = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 横向滚动容器 */}
|
||||
<Flex
|
||||
{/* 事件卡片容器 */}
|
||||
<Box
|
||||
ref={scrollContainerRef}
|
||||
overflowX="auto"
|
||||
overflowX={mode === 'carousel' ? 'auto' : 'hidden'}
|
||||
overflowY="hidden"
|
||||
gap={4}
|
||||
pt={0}
|
||||
pb={4}
|
||||
px={2}
|
||||
position="relative"
|
||||
css={{
|
||||
css={mode === 'carousel' ? {
|
||||
'&::-webkit-scrollbar': {
|
||||
height: '8px',
|
||||
},
|
||||
@@ -147,11 +172,9 @@ const EventScrollList = ({
|
||||
'&::-webkit-scrollbar-thumb:hover': {
|
||||
background: useColorModeValue('#555', '#718096'),
|
||||
},
|
||||
// 平滑滚动
|
||||
scrollBehavior: 'smooth',
|
||||
// 触摸设备优化
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
}}
|
||||
} : {}}
|
||||
>
|
||||
{/* 加载遮罩 */}
|
||||
{loading && (
|
||||
@@ -175,7 +198,9 @@ const EventScrollList = ({
|
||||
</Center>
|
||||
)}
|
||||
|
||||
{/* 事件卡片列表 */}
|
||||
{/* 模式1: 单排轮播模式 */}
|
||||
{mode === 'carousel' && (
|
||||
<Flex gap={4}>
|
||||
{events.map((event, index) => (
|
||||
<Box
|
||||
key={event.id}
|
||||
@@ -204,6 +229,41 @@ const EventScrollList = ({
|
||||
</Box>
|
||||
))}
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{/* 模式2: 双排网格模式 */}
|
||||
{mode === 'grid' && (
|
||||
<Grid
|
||||
templateRows="repeat(2, 1fr)"
|
||||
templateColumns="repeat(5, 1fr)"
|
||||
gap={4}
|
||||
autoFlow="column"
|
||||
>
|
||||
{events.map((event, index) => (
|
||||
<Box key={event.id}>
|
||||
<DynamicNewsEventCard
|
||||
event={event}
|
||||
index={index}
|
||||
isFollowing={false}
|
||||
followerCount={event.follower_count || 0}
|
||||
isSelected={selectedEvent?.id === event.id}
|
||||
onEventClick={(clickedEvent) => {
|
||||
onEventSelect(clickedEvent);
|
||||
}}
|
||||
onTitleClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onEventSelect(event);
|
||||
}}
|
||||
onToggleFollow={() => {}}
|
||||
timelineStyle={getTimelineBoxStyle()}
|
||||
borderColor={borderColor}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</Grid>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -196,6 +196,7 @@ const Community = () => {
|
||||
onSearchFocus={scrollToTimeline}
|
||||
onEventClick={handleEventClick}
|
||||
onViewDetail={handleViewDetail}
|
||||
mode="grid"
|
||||
/>
|
||||
|
||||
{/* 市场复盘 - 左右布局 */}
|
||||
|
||||
Reference in New Issue
Block a user