update ui

This commit is contained in:
2025-11-14 07:25:12 +08:00
parent 6ddcbf80d7
commit c412aeceee
4 changed files with 76 additions and 15 deletions

View File

@@ -2,10 +2,25 @@
// 纵向分栏模式布局组件 // 纵向分栏模式布局组件
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Box, VStack, Flex, Center, Text, useBreakpointValue } from '@chakra-ui/react'; import {
Box,
VStack,
Flex,
Center,
Text,
useBreakpointValue,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalCloseButton,
useDisclosure
} from '@chakra-ui/react';
import { InfoIcon } from '@chakra-ui/icons'; import { InfoIcon } from '@chakra-ui/icons';
import HorizontalDynamicNewsEventCard from '../EventCard/HorizontalDynamicNewsEventCard'; import HorizontalDynamicNewsEventCard from '../EventCard/HorizontalDynamicNewsEventCard';
import EventDetailScrollPanel from './EventDetailScrollPanel'; import EventDetailScrollPanel from './EventDetailScrollPanel';
import DynamicNewsDetailPanel from '../../DynamicNewsDetail/DynamicNewsDetailPanel';
/** /**
* 纵向分栏模式布局 * 纵向分栏模式布局
@@ -38,6 +53,20 @@ const VerticalModeLayout = ({
const flexDirection = useBreakpointValue({ base: 'column', lg: 'row' }); const flexDirection = useBreakpointValue({ base: 'column', lg: 'row' });
const gap = useBreakpointValue({ base: 3, lg: 6 }); 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- 平衡布局,确保左侧有足够空间显示内容 // 固定布局比例左侧4右侧6- 平衡布局,确保左侧有足够空间显示内容
const leftFlex = '4'; const leftFlex = '4';
const rightFlex = '6'; const rightFlex = '6';
@@ -89,7 +118,7 @@ const VerticalModeLayout = ({
key={event.id} key={event.id}
event={event} event={event}
isSelected={selectedEvent?.id === event.id} isSelected={selectedEvent?.id === event.id}
onEventClick={() => onEventSelect(event)} onEventClick={() => handleMobileEventClick(event)}
isFollowing={eventFollowStatus[event.id]?.isFollowing} isFollowing={eventFollowStatus[event.id]?.isFollowing}
followerCount={eventFollowStatus[event.id]?.followerCount} followerCount={eventFollowStatus[event.id]?.followerCount}
onToggleFollow={onToggleFollow} onToggleFollow={onToggleFollow}
@@ -133,6 +162,24 @@ const VerticalModeLayout = ({
/> />
</Box> </Box>
)} )}
{/* 移动端详情弹窗 */}
{isMobile && (
<Modal isOpen={isMobileModalOpen} onClose={onMobileModalClose} size="full" scrollBehavior="inside">
<ModalOverlay bg="blackAlpha.800" backdropFilter="blur(10px)" />
<ModalContent maxW="100vw" m={0} borderRadius={0}>
<ModalHeader bg="gray.900" color="white" borderBottom="1px solid" borderColor="gray.700">
{mobileSelectedEvent?.title || '事件详情'}
</ModalHeader>
<ModalCloseButton color="white" />
<ModalBody p={0} bg="gray.900">
{mobileSelectedEvent && (
<DynamicNewsDetailPanel event={mobileSelectedEvent} showHeader={false} />
)}
</ModalBody>
</ModalContent>
</Modal>
)}
</Flex> </Flex>
); );
}; };

View File

@@ -3,7 +3,7 @@
import React, { useRef, useMemo, useEffect } from 'react'; import React, { useRef, useMemo, useEffect } from 'react';
import { useVirtualizer } from '@tanstack/react-virtual'; import { useVirtualizer } from '@tanstack/react-virtual';
import { Box, Grid, Spinner, Text, VStack, Center, HStack, IconButton } from '@chakra-ui/react'; import { Box, Grid, Spinner, Text, VStack, Center, HStack, IconButton, useBreakpointValue } from '@chakra-ui/react';
import { RepeatIcon } from '@chakra-ui/icons'; import { RepeatIcon } from '@chakra-ui/icons';
import { useColorModeValue } from '@chakra-ui/react'; import { useColorModeValue } from '@chakra-ui/react';
import DynamicNewsEventCard from '../EventCard/DynamicNewsEventCard'; import DynamicNewsEventCard from '../EventCard/DynamicNewsEventCard';
@@ -52,14 +52,26 @@ const VirtualizedFourRowGrid = ({
const scrollbarThumbBg = useColorModeValue('#888', '#4A5568'); const scrollbarThumbBg = useColorModeValue('#888', '#4A5568');
const scrollbarThumbHoverBg = useColorModeValue('#555', '#718096'); const scrollbarThumbHoverBg = useColorModeValue('#555', '#718096');
// 将事件按 columnsPerRow 个一组分成行 // 响应式列数
const responsiveColumns = useBreakpointValue({
base: 1, // 移动端:单列
sm: 2, // 小屏2列
md: 2, // 中屏2列
lg: 3, // 大屏3列
xl: 4, // 超大屏4列
});
// 使用响应式列数或传入的列数
const actualColumnsPerRow = responsiveColumns || columnsPerRow;
// 将事件按 actualColumnsPerRow 个一组分成行
const rows = useMemo(() => { const rows = useMemo(() => {
const r = []; const r = [];
for (let i = 0; i < events.length; i += columnsPerRow) { for (let i = 0; i < events.length; i += actualColumnsPerRow) {
r.push(events.slice(i, i + columnsPerRow)); r.push(events.slice(i, i + actualColumnsPerRow));
} }
return r; return r;
}, [events, columnsPerRow]); }, [events, actualColumnsPerRow]);
// 配置虚拟滚动器(纵向滚动 + 动态高度测量) // 配置虚拟滚动器(纵向滚动 + 动态高度测量)
const rowVirtualizer = useVirtualizer({ const rowVirtualizer = useVirtualizer({
@@ -301,17 +313,17 @@ const VirtualizedFourRowGrid = ({
w="100%" w="100%"
transform={`translateY(${virtualRow.start}px)`} transform={`translateY(${virtualRow.start}px)`}
> >
{/* 使用 Grid 横向排列卡片(列数由 columnsPerRow 决定) */} {/* 使用 Grid 横向排列卡片(列数由 actualColumnsPerRow 决定) */}
<Grid <Grid
templateColumns={`repeat(${columnsPerRow}, 1fr)`} templateColumns={`repeat(${actualColumnsPerRow}, 1fr)`}
gap={columnsPerRow === 1 ? 3 : 4} gap={actualColumnsPerRow === 1 ? 3 : 4}
w="100%" w="100%"
> >
{rowEvents.map((event, colIndex) => ( {rowEvents.map((event, colIndex) => (
<Box key={event.id} w="100%" minW={0}> <Box key={event.id} w="100%" minW={0}>
<CardComponent <CardComponent
event={event} event={event}
index={virtualRow.index * columnsPerRow + colIndex} index={virtualRow.index * actualColumnsPerRow + colIndex}
isFollowing={eventFollowStatus[event.id]?.isFollowing || false} isFollowing={eventFollowStatus[event.id]?.isFollowing || false}
followerCount={eventFollowStatus[event.id]?.followerCount || event.follower_count || 0} followerCount={eventFollowStatus[event.id]?.followerCount || event.follower_count || 0}
isSelected={selectedEvent?.id === event.id} isSelected={selectedEvent?.id === event.id}

View File

@@ -8,6 +8,7 @@ import {
useColorModeValue, useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import CollapsibleHeader from './CollapsibleHeader'; import CollapsibleHeader from './CollapsibleHeader';
import { PROFESSIONAL_COLORS } from '../../../../constants/professionalTheme';
/** /**
* 通用可折叠区块组件 * 通用可折叠区块组件
@@ -37,7 +38,7 @@ const CollapsibleSection = ({
showModeToggle = false, showModeToggle = false,
defaultMode = 'detailed' defaultMode = 'detailed'
}) => { }) => {
const sectionBg = useColorModeValue('gray.50', 'gray.750'); const sectionBg = PROFESSIONAL_COLORS.background.secondary;
// 模式状态:'detailed' | 'simple' // 模式状态:'detailed' | 'simple'
const [displayMode, setDisplayMode] = useState(defaultMode); const [displayMode, setDisplayMode] = useState(defaultMode);

View File

@@ -8,6 +8,7 @@ import {
Text, Text,
useColorModeValue, useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { PROFESSIONAL_COLORS } from '../../../../constants/professionalTheme';
/** /**
* 事件描述区组件 * 事件描述区组件
@@ -15,9 +16,9 @@ import {
* @param {string} props.description - 事件描述文本 * @param {string} props.description - 事件描述文本
*/ */
const EventDescriptionSection = ({ description }) => { const EventDescriptionSection = ({ description }) => {
const sectionBg = useColorModeValue('gray.50', 'gray.750'); const sectionBg = PROFESSIONAL_COLORS.background.secondary;
const headingColor = useColorModeValue('gray.700', 'gray.200'); const headingColor = PROFESSIONAL_COLORS.text.primary;
const textColor = useColorModeValue('gray.600', 'gray.400'); const textColor = PROFESSIONAL_COLORS.text.secondary;
// 如果没有描述,不渲染 // 如果没有描述,不渲染
if (!description) { if (!description) {