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