事件中心UI优化

This commit is contained in:
2025-11-07 09:45:42 +08:00
parent a3810499cc
commit 67c7fa49e8
5 changed files with 234 additions and 140 deletions

View File

@@ -456,7 +456,7 @@ const [currentMode, setCurrentMode] = useState('vertical');
top={isFixedMode ? `${TOTAL_NAV_HEIGHT}px` : 'auto'} top={isFixedMode ? `${TOTAL_NAV_HEIGHT}px` : 'auto'}
left={isFixedMode ? 0 : 'auto'} left={isFixedMode ? 0 : 'auto'}
right={isFixedMode ? 0 : 'auto'} right={isFixedMode ? 0 : 'auto'}
maxW={isFixedMode ? 'container.xl' : '100%'} maxW={isFixedMode ? '1600px' : '100%'}
mx={isFixedMode ? 'auto' : 0} mx={isFixedMode ? 'auto' : 0}
px={isFixedMode ? { base: 3, md: 4 } : undefined} px={isFixedMode ? { base: 3, md: 4 } : undefined}
zIndex={isFixedMode ? 999 : 1} zIndex={isFixedMode ? 999 : 1}
@@ -502,7 +502,7 @@ const [currentMode, setCurrentMode] = useState('vertical');
left={isFixedMode ? 0 : 'auto'} left={isFixedMode ? 0 : 'auto'}
right={isFixedMode ? 0 : 'auto'} right={isFixedMode ? 0 : 'auto'}
bottom={isFixedMode ? `${FOOTER_HEIGHT}px` : 'auto'} bottom={isFixedMode ? `${FOOTER_HEIGHT}px` : 'auto'}
maxW={isFixedMode ? 'container.xl' : '100%'} maxW={isFixedMode ? '1600px' : '100%'}
mx={isFixedMode ? 'auto' : 0} mx={isFixedMode ? 'auto' : 0}
h={isFixedMode ? `calc(100vh - ${TOTAL_NAV_HEIGHT + headerHeight + FOOTER_HEIGHT}px)` : 'auto'} h={isFixedMode ? `calc(100vh - ${TOTAL_NAV_HEIGHT + headerHeight + FOOTER_HEIGHT}px)` : 'auto'}
px={isFixedMode ? { base: 3, md: 4 } : undefined} px={isFixedMode ? { base: 3, md: 4 } : undefined}
@@ -579,9 +579,9 @@ const [currentMode, setCurrentMode] = useState('vertical');
{/* 四排模式详情弹窗 - 未打开时不渲染 */} {/* 四排模式详情弹窗 - 未打开时不渲染 */}
{isModalOpen && ( {isModalOpen && (
<Modal isOpen={isModalOpen} onClose={onModalClose} size="6xl" scrollBehavior="inside"> <Modal isOpen={isModalOpen} onClose={onModalClose} size="full" scrollBehavior="inside">
<ModalOverlay /> <ModalOverlay />
<ModalContent> <ModalContent maxW="1600px" mx="auto" my={8}>
<ModalHeader> <ModalHeader>
{modalEvent?.title || '事件详情'} {modalEvent?.title || '事件详情'}
</ModalHeader> </ModalHeader>

View File

@@ -8,6 +8,8 @@ import {
HStack, HStack,
Heading, Heading,
Text, Text,
Badge,
Icon,
useColorModeValue, useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { ViewIcon } from '@chakra-ui/icons'; import { ViewIcon } from '@chakra-ui/icons';
@@ -100,7 +102,7 @@ const EventHeaderInfo = ({ event, importance, isFollowing, followerCount, onTogg
</Text> </Text>
</Flex> </Flex>
{/* 第三行:涨跌幅指标 + 重要性文本 */} {/* 第三行:涨跌幅指标 + 重要性徽章 */}
<HStack spacing={3} align="center"> <HStack spacing={3} align="center">
<Box maxW="500px"> <Box maxW="500px">
<StockChangeIndicators <StockChangeIndicators
@@ -110,19 +112,28 @@ const EventHeaderInfo = ({ event, importance, isFollowing, followerCount, onTogg
/> />
</Box> </Box>
{/* 重要性文本 */} {/* 重要性徽章 - 使用渐变色和图标 */}
<Box <Badge
bg={importance.bgColor} px={4}
borderWidth="2px" py={2}
borderColor={importance.badgeBg} borderRadius="full"
px={2} fontSize="md"
py={1} fontWeight="bold"
borderRadius="md" bgGradient={
importance.level === 'S' ? 'linear(to-r, red.500, red.700)' :
importance.level === 'A' ? 'linear(to-r, orange.500, orange.700)' :
importance.level === 'B' ? 'linear(to-r, blue.500, blue.700)' :
'linear(to-r, gray.500, gray.700)'
}
color="white"
boxShadow="lg"
display="flex"
alignItems="center"
gap={2}
> >
<Text fontSize="sm" color={importance.badgeBg} whiteSpace="nowrap" fontWeight="medium"> <Icon as={importance.icon} boxSize={5} />
重要性{getImportanceText()} <Text>重要性{getImportanceText()}</Text>
</Text> </Badge>
</Box>
</HStack> </HStack>
</Box> </Box>
); );

View File

@@ -3,7 +3,7 @@
import React from 'react'; import React from 'react';
import { import {
SimpleGrid, VStack,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import StockListItem from './StockListItem'; import StockListItem from './StockListItem';
@@ -29,7 +29,7 @@ const RelatedStocksSection = ({
} }
return ( return (
<SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} spacing={4}> <VStack align="stretch" spacing={3}>
{stocks.map((stock, index) => ( {stocks.map((stock, index) => (
<StockListItem <StockListItem
key={index} key={index}
@@ -40,7 +40,7 @@ const RelatedStocksSection = ({
onWatchlistToggle={onWatchlistToggle} onWatchlistToggle={onWatchlistToggle}
/> />
))} ))}
</SimpleGrid> </VStack>
); );
}; };

View File

@@ -10,6 +10,11 @@ import {
Button, Button,
IconButton, IconButton,
Collapse, Collapse,
Stat,
StatLabel,
StatNumber,
StatHelpText,
StatArrow,
useColorModeValue, useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { StarIcon } from '@chakra-ui/icons'; import { StarIcon } from '@chakra-ui/icons';
@@ -101,46 +106,56 @@ const StockListItem = ({
<> <>
<Box <Box
bg={cardBg} bg={cardBg}
borderWidth="1px" borderWidth="2px"
borderColor={borderColor} borderColor={borderColor}
borderRadius="md" borderRadius="xl"
p={4} p={5}
onClick={handleViewDetail} onClick={handleViewDetail}
cursor="pointer" cursor="pointer"
_hover={{ position="relative"
boxShadow: 'md', overflow="hidden"
borderColor: 'blue.300', _before={{
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '4px',
bgGradient: 'linear(to-r, blue.400, purple.500, pink.500)',
}} }}
transition="all 0.2s" _hover={{
boxShadow: '2xl',
borderColor: 'blue.400',
transform: 'translateY(-4px)',
}}
transition="all 0.3s ease-in-out"
> >
<VStack align="stretch" spacing={3}> {/* 横向布局:左侧基础信息 + 中间图表 + 右侧操作按钮 */}
{/* 顶部:股票代码 + 名称 + 操作按钮(上下两行布局) */} <Flex direction={{ base: 'column', lg: 'row' }} gap={4} align="stretch">
<VStack align="stretch" spacing={2}> {/* 左侧:基础信息区(股票代码、名称、涨跌幅) */}
{/* 第一行:股票代码 + 涨跌幅 + 操作按钮 */} <Flex direction="column" minW={{ base: 'auto', lg: '200px' }} maxW={{ base: 'auto', lg: '240px' }} gap={2}>
<Flex justify="space-between" align="center"> {/* 股票代码和名称 */}
{/* 左侧:代码 + 涨跌幅 */} <VStack align="stretch" spacing={2}>
<Flex align="baseline" gap={2}> <Flex align="center" gap={2}>
<Text <Box
fontSize="md" bgGradient="linear(to-r, blue.400, blue.600)"
fontWeight="bold" px={3}
color={codeColor} py={1}
cursor="pointer" borderRadius="md"
onClick={handleViewDetail} boxShadow="sm"
_hover={{ textDecoration: 'underline' }}
> >
{stock.stock_code} <Text
</Text> fontSize="md"
<Text fontWeight="bold"
fontSize="sm" color="white"
fontWeight="semibold" cursor="pointer"
color={getChangeColor(change)} onClick={handleViewDetail}
> _hover={{ opacity: 0.8 }}
{formatChange(change)} transition="opacity 0.2s"
</Text> >
</Flex> {stock.stock_code}
</Text>
{/* 右侧:操作按钮 */} </Box>
<Flex gap={2}>
{onWatchlistToggle && ( {onWatchlistToggle && (
<IconButton <IconButton
size="sm" size="sm"
@@ -150,115 +165,183 @@ const StockListItem = ({
onClick={handleWatchlistClick} onClick={handleWatchlistClick}
aria-label={isInWatchlist ? '已关注' : '加自选'} aria-label={isInWatchlist ? '已关注' : '加自选'}
title={isInWatchlist ? '已关注' : '加自选'} title={isInWatchlist ? '已关注' : '加自选'}
borderRadius="full"
/> />
)} )}
<Button
size="sm"
colorScheme="blue"
onClick={(e) => {
e.stopPropagation();
handleViewDetail();
}}
display={{ base: 'inline-flex', lg: 'none' }}
>
查看
</Button>
</Flex> </Flex>
</Flex>
{/* 第二行:公司名称 + 分时图 + K线图 */}
<Flex align="center" gap={3}>
{/* 左侧:公司名称 */}
<Text <Text
fontSize="sm" fontSize="md"
fontWeight="medium" fontWeight="semibold"
color={codeColor} color={nameColor}
bg={useColorModeValue('blue.50', 'blue.900')} noOfLines={1}
px={2}
py={0.5}
borderRadius="sm"
whiteSpace="nowrap"
flexShrink={0}
> >
{stock.stock_name} {stock.stock_name}
</Text> </Text>
</VStack>
{/* 右侧:分时图 + K线图 */} {/* 涨跌幅 - 使用 Stat 组件 */}
<Flex gap={2} flex={1} onClick={(e) => e.stopPropagation()}> <Stat
{/* 分时图 */} bg={useColorModeValue('gray.50', 'gray.700')}
<Box flex={1} minW={0}> px={3}
<MiniTimelineChart py={2}
stockCode={stock.stock_code} borderRadius="lg"
eventTime={eventTime} boxShadow="sm"
onClick={() => setIsModalOpen(true)} >
/> <StatLabel fontSize="xs" color={descColor}>
</Box> 涨跌幅
</StatLabel>
<StatNumber
fontSize="2xl"
fontWeight="bold"
color={getChangeColor(change)}
>
{formatChange(change)}
</StatNumber>
{change !== null && change !== undefined && !isNaN(change) && (
<StatHelpText mb={0}>
<StatArrow type={change >= 0 ? 'increase' : 'decrease'} />
{Math.abs(change).toFixed(2)}%
</StatHelpText>
)}
</Stat>
</Flex>
{/* K线图 */} {/* 中间:图表区(分时图 + K线图 */}
<Box flex={1} minW={0}> <Flex flex={1} gap={3} onClick={(e) => e.stopPropagation()} minW={0}>
<MiniKLineChart {/* 分时图 */}
stockCode={stock.stock_code} <Box
eventTime={eventTime} flex={1}
onClick={() => setIsModalOpen(true)} minW={0}
/> borderWidth="1px"
</Box> borderColor={useColorModeValue('blue.100', 'blue.700')}
</Flex> borderRadius="lg"
</Flex> p={3}
</VStack> bg={useColorModeValue('blue.50', 'blue.900')}
transition="all 0.2s"
{/* 分隔线 */} _hover={{
<Box borderTop="1px solid" borderColor={dividerColor} /> borderColor: useColorModeValue('blue.300', 'blue.500'),
boxShadow: 'md'
{/* 关联描述 */} }}
{relationText && relationText !== '--' && ( >
<Box> <Text
<Text fontSize="xs" color={descColor} mb={1}> fontSize="xs"
关联描述 color={useColorModeValue('blue.700', 'blue.200')}
mb={2}
fontWeight="bold"
textTransform="uppercase"
letterSpacing="wide"
>
📈 分时走势
</Text>
<MiniTimelineChart
stockCode={stock.stock_code}
eventTime={eventTime}
onClick={() => setIsModalOpen(true)}
/>
</Box>
{/* K线图 */}
<Box
flex={1}
minW={0}
borderWidth="1px"
borderColor={useColorModeValue('purple.100', 'purple.700')}
borderRadius="lg"
p={3}
bg={useColorModeValue('purple.50', 'purple.900')}
transition="all 0.2s"
_hover={{
borderColor: useColorModeValue('purple.300', 'purple.500'),
boxShadow: 'md'
}}
>
<Text
fontSize="xs"
color={useColorModeValue('purple.700', 'purple.200')}
mb={2}
fontWeight="bold"
textTransform="uppercase"
letterSpacing="wide"
>
📊 K线走势
</Text>
<MiniKLineChart
stockCode={stock.stock_code}
eventTime={eventTime}
onClick={() => setIsModalOpen(true)}
/>
</Box>
</Flex>
{/* 右侧:操作按钮 */}
<Flex direction="column" gap={2} minW={{ base: 'auto', lg: '120px' }} justify="center">
<Button
size="md"
bgGradient="linear(to-r, blue.400, blue.600)"
color="white"
_hover={{
bgGradient: "linear(to-r, blue.500, blue.700)",
transform: "scale(1.05)",
}}
_active={{
bgGradient: "linear(to-r, blue.600, blue.800)",
}}
boxShadow="md"
borderRadius="lg"
fontWeight="bold"
onClick={(e) => {
e.stopPropagation();
handleViewDetail();
}}
transition="all 0.2s"
>
查看详情
</Button>
</Flex>
</Flex>
{/* 关联描述(折叠区域) */}
{relationText && relationText !== '--' && (
<Box mt={4} pt={4} borderTop="1px solid" borderColor={dividerColor}>
<Flex justify="space-between" align="center" mb={2}>
<Text fontSize="sm" fontWeight="semibold" color={descColor}>
关联描述
</Text> </Text>
<Collapse in={isDescExpanded} startingHeight={40}>
<Text
fontSize="sm"
color={nameColor}
lineHeight="1.6"
cursor={needTruncate ? "pointer" : "default"}
onClick={(e) => {
if (needTruncate) {
e.stopPropagation();
setIsDescExpanded(!isDescExpanded);
}
}}
_hover={needTruncate ? { opacity: 0.8 } : {}}
>
{relationText}
</Text>
</Collapse>
{needTruncate && ( {needTruncate && (
<Button <Button
size="xs" size="xs"
variant="link" variant="ghost"
colorScheme="blue" colorScheme="blue"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setIsDescExpanded(!isDescExpanded); setIsDescExpanded(!isDescExpanded);
}} }}
mt={1}
> >
{isDescExpanded ? '收起' : '展开'} {isDescExpanded ? '收起' : '展开'}
</Button> </Button>
)} )}
</Flex>
{/* 合规提示 */} <Collapse in={isDescExpanded} startingHeight={needTruncate ? 40 : undefined}>
<Text <Text
fontSize="xs" fontSize="sm"
color="gray.500" color={nameColor}
mt={2} lineHeight="1.8"
fontStyle="italic"
> >
以上关联描述由AI生成仅供参考不构成投资建议 {relationText}
</Text> </Text>
</Box> </Collapse>
)}
</VStack> {/* 合规提示 */}
<Text
fontSize="xs"
color="gray.500"
mt={3}
fontStyle="italic"
>
以上关联描述由AI生成仅供参考不构成投资建议
</Text>
</Box>
)}
</Box> </Box>
{/* 股票详情弹窗 - 未打开时不渲染 */} {/* 股票详情弹窗 - 未打开时不渲染 */}

View File

@@ -152,7 +152,7 @@ const Community = () => {
return ( return (
<Box minH="100vh" bg={bgColor}> <Box minH="100vh" bg={bgColor}>
{/* 主内容区域 */} {/* 主内容区域 */}
<Container ref={containerRef} maxW="container.xl" pt={6} pb={8}> <Container ref={containerRef} maxW="1600px" pt={6} pb={8}>
{/* 热点事件区域 */} {/* 热点事件区域 */}
<HotEventsSection events={hotEvents} /> <HotEventsSection events={hotEvents} />