feat: 添加 EventScrollList.js 组件
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
// src/views/Community/components/DynamicNewsCard.js
|
// src/views/Community/components/DynamicNewsCard.js
|
||||||
// 横向滚动事件卡片组件(实时要闻·动态追踪)
|
// 横向滚动事件卡片组件(实时要闻·动态追踪)
|
||||||
|
|
||||||
import React, { forwardRef, useRef, useState, useEffect } from 'react';
|
import React, { forwardRef, useState, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
@@ -13,13 +13,12 @@ import {
|
|||||||
Heading,
|
Heading,
|
||||||
Text,
|
Text,
|
||||||
Badge,
|
Badge,
|
||||||
IconButton,
|
|
||||||
Center,
|
Center,
|
||||||
Spinner,
|
Spinner,
|
||||||
useColorModeValue
|
useColorModeValue
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { ChevronLeftIcon, ChevronRightIcon, TimeIcon } from '@chakra-ui/icons';
|
import { TimeIcon } from '@chakra-ui/icons';
|
||||||
import DynamicNewsEventCard from './EventCard/DynamicNewsEventCard';
|
import EventScrollList from './DynamicNewsCard/EventScrollList';
|
||||||
import DynamicNewsDetailPanel from './DynamicNewsDetail';
|
import DynamicNewsDetailPanel from './DynamicNewsDetail';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,9 +40,6 @@ const DynamicNewsCard = forwardRef(({
|
|||||||
}, ref) => {
|
}, ref) => {
|
||||||
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 scrollContainerRef = useRef(null);
|
|
||||||
const [showLeftArrow, setShowLeftArrow] = useState(false);
|
|
||||||
const [showRightArrow, setShowRightArrow] = useState(true);
|
|
||||||
const [selectedEvent, setSelectedEvent] = useState(null);
|
const [selectedEvent, setSelectedEvent] = useState(null);
|
||||||
|
|
||||||
// 默认选中第一个事件
|
// 默认选中第一个事件
|
||||||
@@ -53,48 +49,6 @@ const DynamicNewsCard = forwardRef(({
|
|||||||
}
|
}
|
||||||
}, [events, selectedEvent]);
|
}, [events, selectedEvent]);
|
||||||
|
|
||||||
// 滚动到左侧
|
|
||||||
const scrollLeft = () => {
|
|
||||||
if (scrollContainerRef.current) {
|
|
||||||
scrollContainerRef.current.scrollBy({
|
|
||||||
left: -400,
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 滚动到右侧
|
|
||||||
const scrollRight = () => {
|
|
||||||
if (scrollContainerRef.current) {
|
|
||||||
scrollContainerRef.current.scrollBy({
|
|
||||||
left: 400,
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 监听滚动位置,更新箭头显示状态
|
|
||||||
const handleScroll = (e) => {
|
|
||||||
const container = e.target;
|
|
||||||
const scrollLeft = container.scrollLeft;
|
|
||||||
const scrollWidth = container.scrollWidth;
|
|
||||||
const clientWidth = container.clientWidth;
|
|
||||||
|
|
||||||
setShowLeftArrow(scrollLeft > 0);
|
|
||||||
setShowRightArrow(scrollLeft < scrollWidth - clientWidth - 10);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 时间轴样式配置
|
|
||||||
const getTimelineBoxStyle = () => {
|
|
||||||
return {
|
|
||||||
bg: useColorModeValue('gray.50', 'gray.700'),
|
|
||||||
borderColor: useColorModeValue('gray.400', 'gray.500'),
|
|
||||||
borderWidth: '2px',
|
|
||||||
textColor: useColorModeValue('blue.600', 'blue.400'),
|
|
||||||
boxShadow: 'sm',
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card ref={ref} {...rest} bg={cardBg} borderColor={borderColor} mb={4}>
|
<Card ref={ref} {...rest} bg={cardBg} borderColor={borderColor} mb={4}>
|
||||||
{/* 标题部分 */}
|
{/* 标题部分 */}
|
||||||
@@ -142,106 +96,12 @@ const DynamicNewsCard = forwardRef(({
|
|||||||
|
|
||||||
{/* 横向滚动事件列表 */}
|
{/* 横向滚动事件列表 */}
|
||||||
{!loading && events && events.length > 0 && (
|
{!loading && events && events.length > 0 && (
|
||||||
<Box position="relative">
|
<EventScrollList
|
||||||
{/* 左侧滚动按钮 */}
|
events={events}
|
||||||
{showLeftArrow && (
|
selectedEvent={selectedEvent}
|
||||||
<IconButton
|
onEventSelect={setSelectedEvent}
|
||||||
icon={<ChevronLeftIcon boxSize={6} />}
|
|
||||||
position="absolute"
|
|
||||||
left="-4"
|
|
||||||
top="50%"
|
|
||||||
transform="translateY(-50%)"
|
|
||||||
zIndex={2}
|
|
||||||
onClick={scrollLeft}
|
|
||||||
colorScheme="blue"
|
|
||||||
variant="solid"
|
|
||||||
size="md"
|
|
||||||
borderRadius="full"
|
|
||||||
shadow="md"
|
|
||||||
aria-label="向左滚动"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 右侧滚动按钮 */}
|
|
||||||
{showRightArrow && (
|
|
||||||
<IconButton
|
|
||||||
icon={<ChevronRightIcon boxSize={6} />}
|
|
||||||
position="absolute"
|
|
||||||
right="-4"
|
|
||||||
top="50%"
|
|
||||||
transform="translateY(-50%)"
|
|
||||||
zIndex={2}
|
|
||||||
onClick={scrollRight}
|
|
||||||
colorScheme="blue"
|
|
||||||
variant="solid"
|
|
||||||
size="md"
|
|
||||||
borderRadius="full"
|
|
||||||
shadow="md"
|
|
||||||
aria-label="向右滚动"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 横向滚动容器 */}
|
|
||||||
<Flex
|
|
||||||
ref={scrollContainerRef}
|
|
||||||
overflowX="auto"
|
|
||||||
overflowY="hidden"
|
|
||||||
gap={4}
|
|
||||||
py={4}
|
|
||||||
px={2}
|
|
||||||
onScroll={handleScroll}
|
|
||||||
css={{
|
|
||||||
'&::-webkit-scrollbar': {
|
|
||||||
height: '8px',
|
|
||||||
},
|
|
||||||
'&::-webkit-scrollbar-track': {
|
|
||||||
background: useColorModeValue('#f1f1f1', '#2D3748'),
|
|
||||||
borderRadius: '10px',
|
|
||||||
},
|
|
||||||
'&::-webkit-scrollbar-thumb': {
|
|
||||||
background: useColorModeValue('#888', '#4A5568'),
|
|
||||||
borderRadius: '10px',
|
|
||||||
},
|
|
||||||
'&::-webkit-scrollbar-thumb:hover': {
|
|
||||||
background: useColorModeValue('#555', '#718096'),
|
|
||||||
},
|
|
||||||
// 平滑滚动
|
|
||||||
scrollBehavior: 'smooth',
|
|
||||||
// 触摸设备优化
|
|
||||||
WebkitOverflowScrolling: 'touch',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{events.map((event, index) => (
|
|
||||||
<Box
|
|
||||||
key={event.id}
|
|
||||||
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) => {
|
|
||||||
setSelectedEvent(clickedEvent);
|
|
||||||
// 只更新详情面板,不触发父组件回调
|
|
||||||
}}
|
|
||||||
onTitleClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
setSelectedEvent(event);
|
|
||||||
// 只更新详情面板,不触发父组件回调
|
|
||||||
}}
|
|
||||||
onToggleFollow={() => {}}
|
|
||||||
timelineStyle={getTimelineBoxStyle()}
|
|
||||||
borderColor={borderColor}
|
borderColor={borderColor}
|
||||||
/>
|
/>
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 详情面板 */}
|
{/* 详情面板 */}
|
||||||
|
|||||||
@@ -0,0 +1,175 @@
|
|||||||
|
// src/views/Community/components/DynamicNewsCard/EventScrollList.js
|
||||||
|
// 横向滚动事件列表组件
|
||||||
|
|
||||||
|
import React, { useRef, useState } from 'react';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
IconButton,
|
||||||
|
useColorModeValue
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons';
|
||||||
|
import DynamicNewsEventCard from '../EventCard/DynamicNewsEventCard';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 横向滚动事件列表组件
|
||||||
|
* @param {Array} events - 事件列表
|
||||||
|
* @param {Object} selectedEvent - 当前选中的事件
|
||||||
|
* @param {Function} onEventSelect - 事件选择回调
|
||||||
|
* @param {string} borderColor - 边框颜色
|
||||||
|
*/
|
||||||
|
const EventScrollList = ({
|
||||||
|
events,
|
||||||
|
selectedEvent,
|
||||||
|
onEventSelect,
|
||||||
|
borderColor
|
||||||
|
}) => {
|
||||||
|
const scrollContainerRef = useRef(null);
|
||||||
|
const [showLeftArrow, setShowLeftArrow] = useState(false);
|
||||||
|
const [showRightArrow, setShowRightArrow] = useState(true);
|
||||||
|
|
||||||
|
// 滚动到左侧
|
||||||
|
const scrollLeft = () => {
|
||||||
|
if (scrollContainerRef.current) {
|
||||||
|
scrollContainerRef.current.scrollBy({
|
||||||
|
left: -400,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 滚动到右侧
|
||||||
|
const scrollRight = () => {
|
||||||
|
if (scrollContainerRef.current) {
|
||||||
|
scrollContainerRef.current.scrollBy({
|
||||||
|
left: 400,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听滚动位置,更新箭头显示状态
|
||||||
|
const handleScroll = (e) => {
|
||||||
|
const container = e.target;
|
||||||
|
const scrollLeft = container.scrollLeft;
|
||||||
|
const scrollWidth = container.scrollWidth;
|
||||||
|
const clientWidth = container.clientWidth;
|
||||||
|
|
||||||
|
setShowLeftArrow(scrollLeft > 0);
|
||||||
|
setShowRightArrow(scrollLeft < scrollWidth - clientWidth - 10);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 时间轴样式配置
|
||||||
|
const getTimelineBoxStyle = () => {
|
||||||
|
return {
|
||||||
|
bg: useColorModeValue('gray.50', 'gray.700'),
|
||||||
|
borderColor: useColorModeValue('gray.400', 'gray.500'),
|
||||||
|
borderWidth: '2px',
|
||||||
|
textColor: useColorModeValue('blue.600', 'blue.400'),
|
||||||
|
boxShadow: 'sm',
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box position="relative">
|
||||||
|
{/* 左侧滚动按钮 */}
|
||||||
|
{showLeftArrow && (
|
||||||
|
<IconButton
|
||||||
|
icon={<ChevronLeftIcon boxSize={6} />}
|
||||||
|
position="absolute"
|
||||||
|
left="-4"
|
||||||
|
top="50%"
|
||||||
|
transform="translateY(-50%)"
|
||||||
|
zIndex={2}
|
||||||
|
onClick={scrollLeft}
|
||||||
|
colorScheme="blue"
|
||||||
|
variant="solid"
|
||||||
|
size="md"
|
||||||
|
borderRadius="full"
|
||||||
|
shadow="md"
|
||||||
|
aria-label="向左滚动"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 右侧滚动按钮 */}
|
||||||
|
{showRightArrow && (
|
||||||
|
<IconButton
|
||||||
|
icon={<ChevronRightIcon boxSize={6} />}
|
||||||
|
position="absolute"
|
||||||
|
right="-4"
|
||||||
|
top="50%"
|
||||||
|
transform="translateY(-50%)"
|
||||||
|
zIndex={2}
|
||||||
|
onClick={scrollRight}
|
||||||
|
colorScheme="blue"
|
||||||
|
variant="solid"
|
||||||
|
size="md"
|
||||||
|
borderRadius="full"
|
||||||
|
shadow="md"
|
||||||
|
aria-label="向右滚动"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 横向滚动容器 */}
|
||||||
|
<Flex
|
||||||
|
ref={scrollContainerRef}
|
||||||
|
overflowX="auto"
|
||||||
|
overflowY="hidden"
|
||||||
|
gap={4}
|
||||||
|
py={4}
|
||||||
|
px={2}
|
||||||
|
onScroll={handleScroll}
|
||||||
|
css={{
|
||||||
|
'&::-webkit-scrollbar': {
|
||||||
|
height: '8px',
|
||||||
|
},
|
||||||
|
'&::-webkit-scrollbar-track': {
|
||||||
|
background: useColorModeValue('#f1f1f1', '#2D3748'),
|
||||||
|
borderRadius: '10px',
|
||||||
|
},
|
||||||
|
'&::-webkit-scrollbar-thumb': {
|
||||||
|
background: useColorModeValue('#888', '#4A5568'),
|
||||||
|
borderRadius: '10px',
|
||||||
|
},
|
||||||
|
'&::-webkit-scrollbar-thumb:hover': {
|
||||||
|
background: useColorModeValue('#555', '#718096'),
|
||||||
|
},
|
||||||
|
// 平滑滚动
|
||||||
|
scrollBehavior: 'smooth',
|
||||||
|
// 触摸设备优化
|
||||||
|
WebkitOverflowScrolling: 'touch',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{events.map((event, index) => (
|
||||||
|
<Box
|
||||||
|
key={event.id}
|
||||||
|
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>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EventScrollList;
|
||||||
Reference in New Issue
Block a user