Files
vf_react/src/views/Community/components/DynamicNewsCard/VerticalModeLayout.js
2025-11-27 15:08:14 +08:00

173 lines
5.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/views/Community/components/DynamicNewsCard/VerticalModeLayout.js
// 纵向分栏模式布局组件
import React, { useState } from 'react';
import {
Box,
VStack,
Flex,
Center,
Text,
useBreakpointValue,
useDisclosure
} from '@chakra-ui/react';
import { InfoIcon } from '@chakra-ui/icons';
import HorizontalDynamicNewsEventCard from '../EventCard/HorizontalDynamicNewsEventCard';
import EventDetailScrollPanel from './EventDetailScrollPanel';
import EventDetailModal from '../EventDetailModal';
/**
* 纵向分栏模式布局
* 固定布局:左侧事件列表 3fr | 右侧详情 7fr
*
* @param {string} display - CSS display 属性(用于显示/隐藏组件)
* @param {Array} events - 当前页的事件列表(分页数据)
* @param {Object} selectedEvent - 当前选中的事件
* @param {Function} onEventSelect - 事件选择回调
* @param {Object} eventFollowStatus - 事件关注状态
* @param {Function} onToggleFollow - 关注按钮回调
* @param {Function} getTimelineBoxStyle - 时间线样式获取函数
* @param {string} borderColor - 边框颜色
*/
const VerticalModeLayout = React.memo(({
display = 'flex',
events,
selectedEvent,
onEventSelect,
eventFollowStatus,
onToggleFollow,
getTimelineBoxStyle,
borderColor,
}) => {
// 详情面板重置 key预留用于未来功能
const [detailPanelKey] = useState(0);
// 响应式布局
const isMobile = useBreakpointValue({ base: true, lg: false });
const flexDirection = useBreakpointValue({ base: 'column', lg: 'row' });
const gap = useBreakpointValue({ base: 3, lg: 6 });
// 移动端模态框控制
const { isOpen: isMobileModalOpen, onOpen: onMobileModalOpen, onClose: onMobileModalClose } = useDisclosure();
const [mobileSelectedEvent, setMobileSelectedEvent] = useState(null);
// 处理移动端事件点击
const handleMobileEventClick = (event) => {
if (isMobile) {
setMobileSelectedEvent(event);
onMobileModalOpen();
} else {
onEventSelect(event);
}
};
// 固定布局比例左侧4右侧6- 平衡布局,确保左侧有足够空间显示内容
const leftFlex = '4';
const rightFlex = '6';
return (
<Flex
display={display}
direction={flexDirection}
gap={gap}
position="relative"
transition="all 0.3s ease-in-out"
h="100%"
overflow="hidden"
>
{/* 左侧:事件列表 - 独立滚动 */}
<Box
flex={isMobile ? '1' : leftFlex}
minWidth={0}
w={isMobile ? '100%' : 'auto'}
overflowY="auto"
h="100%"
data-event-list-container="true"
css={{
overscrollBehavior: 'contain',
'&::-webkit-scrollbar': {
width: '6px',
},
'&::-webkit-scrollbar-track': {
background: '#f1f1f1',
},
'&::-webkit-scrollbar-thumb': {
background: '#888',
borderRadius: '3px',
},
'&::-webkit-scrollbar-thumb:hover': {
background: '#555',
},
}}
>
{/* 事件列表 */}
{events && events.length > 0 ? (
<VStack
spacing={2}
align="stretch"
p={2}
>
{events.map((event) => (
<HorizontalDynamicNewsEventCard
key={event.id}
event={event}
isSelected={selectedEvent?.id === event.id}
onEventClick={() => handleMobileEventClick(event)}
isFollowing={eventFollowStatus[event.id]?.isFollowing}
followerCount={eventFollowStatus[event.id]?.followerCount}
onToggleFollow={onToggleFollow}
timelineStyle={getTimelineBoxStyle()}
borderColor={borderColor}
indicatorSize="default"
layout="vertical"
/>
))}
</VStack>
) : (
/* 空状态 */
<Center h="100%" minH="400px">
<VStack spacing={4}>
<InfoIcon w={12} h={12} color="gray.400" />
<Text fontSize="lg" color="gray.500" textAlign="center">
当前筛选条件下暂无数据
</Text>
<Text fontSize="sm" color="gray.400" textAlign="center">
请尝试调整筛选条件
</Text>
</VStack>
</Center>
)}
</Box>
{/* 右侧:事件详情 - 独立滚动 - 移动端隐藏 */}
{!isMobile && (
<Box
flex={rightFlex}
minHeight={0}
position="relative"
overflow="hidden"
h="100%"
>
{/* 详情面板 */}
<EventDetailScrollPanel
key={detailPanelKey}
detailMode="no-header"
selectedEvent={selectedEvent}
/>
</Box>
)}
{/* 移动端详情弹窗 */}
{isMobile && (
<EventDetailModal
open={isMobileModalOpen}
onClose={onMobileModalClose}
event={mobileSelectedEvent}
/>
)}
</Flex>
);
});
export default VerticalModeLayout;