update pay function

This commit is contained in:
2025-11-23 12:21:19 +08:00
parent 5857144180
commit 0bcf6a93f7

View File

@@ -535,148 +535,283 @@ const ConceptTimelineModal = ({
</VStack> </VStack>
</Center> </Center>
) : timelineData.length > 0 ? ( ) : timelineData.length > 0 ? (
<Box position="relative" maxW="1200px" mx="auto"> <Box position="relative" maxW="1000px" mx="auto" px={4}>
{/* 时间轴主线 */} <VStack spacing={6} align="stretch" position="relative">
<Box
position="absolute"
left="50%"
transform="translateX(-50%)"
top={0}
bottom={0}
width="2px"
bgGradient="linear(to-b, purple.200, pink.200)"
zIndex={0}
/>
<VStack spacing={0} align="stretch" position="relative">
{timelineData.map((item, index) => { {timelineData.map((item, index) => {
const priceInfo = getPriceInfo(item.price); const priceInfo = getPriceInfo(item.price);
const hasEvents = item.events.length > 0; const hasEvents = item.events.length > 0;
const isExpanded = expandedDates[item.date]; const isExpanded = expandedDates[item.date];
const isLeft = index % 2 === 0; // 偶数项在左,奇数项在右
return ( return (
<Box key={`${item.date}-${index}`} position="relative"> <Box key={`${item.date}-${index}`} position="relative">
<Flex <Flex
align="center" direction={isLeft ? 'row' : 'row-reverse'}
py={4} align="flex-start"
position="relative" position="relative"
_hover={{ bg: 'white' }} gap={4}
transition="all 0.2s"
borderRadius="lg"
px={4}
> >
{/* 左侧 - 涨跌幅信息 */} {/* 内容区域 */}
<Box <Box
flex={1} flex={1}
pr={8} maxW="450px"
textAlign="right" bg="white"
p={5}
borderRadius="xl"
boxShadow="lg"
border="2px solid"
borderColor={hasEvents ? 'purple.200' : 'gray.200'}
position="relative"
_hover={{
transform: 'translateY(-4px)',
boxShadow: '2xl',
borderColor: hasEvents ? 'purple.400' : 'gray.300'
}}
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
_before={{
content: '""',
position: 'absolute',
top: '20px',
[isLeft ? 'right' : 'left']: '-12px',
width: 0,
height: 0,
borderStyle: 'solid',
borderWidth: isLeft ? '12px 12px 12px 0' : '12px 0 12px 12px',
borderColor: isLeft
? `transparent ${hasEvents ? 'var(--chakra-colors-purple-200)' : 'var(--chakra-colors-gray-200)'} transparent transparent`
: `transparent transparent transparent ${hasEvents ? 'var(--chakra-colors-purple-200)' : 'var(--chakra-colors-gray-200)'}`
}}
> >
<VStack align="end" spacing={2}> <VStack align="stretch" spacing={3}>
<Text {/* 日期标签 */}
fontSize="sm" <HStack justify="space-between" flexWrap="wrap">
fontWeight="bold" <Badge
color="gray.700" colorScheme="purple"
> variant="subtle"
{formatDateDisplay(item.date)} px={3}
</Text> py={1}
{item.price && (
<Box
bg="white"
px={4}
py={2}
borderRadius="full" borderRadius="full"
boxShadow="sm" fontSize="xs"
border="1px solid" fontWeight="bold"
borderColor={`${priceInfo.color}.200`}
> >
<HStack spacing={2}> {formatDateDisplay(item.date)}
{priceInfo.icon && ( </Badge>
<Icon
as={priceInfo.icon}
color={`${priceInfo.color}.500`}
boxSize={4}
/>
)}
<Text
fontWeight="bold"
fontSize="md"
color={`${priceInfo.color}.600`}
>
{priceInfo.text}
</Text>
</HStack>
{item.price.stock_count && ( {item.price && (
<Text <Badge
fontSize="xs" colorScheme={priceInfo.color}
color="gray.500" variant="solid"
mt={1} px={3}
> py={1}
统计股票: {item.price.stock_count} borderRadius="full"
</Text> fontSize="sm"
)} >
</Box> <HStack spacing={1}>
{priceInfo.icon && (
<Icon as={priceInfo.icon} boxSize={3} />
)}
<Text>{priceInfo.text}</Text>
</HStack>
</Badge>
)}
</HStack>
{/* 股票数量 */}
{item.price && item.price.stock_count && (
<Text fontSize="xs" color="gray.600">
📊 统计股票: {item.price.stock_count}
</Text>
)} )}
{!item.price && !hasEvents && ( {/* 事件信息 */}
<Text fontSize="xs" color="gray.400" fontStyle="italic"> {hasEvents ? (
当日无数据 <Box>
<Button
size="sm"
variant="ghost"
colorScheme="purple"
width="full"
justifyContent="space-between"
rightIcon={
<Icon
as={isExpanded ? ChevronDownIcon : ChevronRightIcon}
transition="transform 0.2s"
/>
}
onClick={() => toggleDateExpand(item.date)}
>
<HStack spacing={2} fontSize="sm">
{item.events.filter(e => e.type === 'news').length > 0 && (
<Badge colorScheme="blue" variant="solid">
📰 {item.events.filter(e => e.type === 'news').length}
</Badge>
)}
{item.events.filter(e => e.type === 'report').length > 0 && (
<Badge colorScheme="green" variant="solid">
📊 {item.events.filter(e => e.type === 'report').length}
</Badge>
)}
</HStack>
</Button>
<Collapse in={isExpanded} animateOpacity>
<VStack
align="stretch"
spacing={3}
mt={3}
maxH="300px"
overflowY="auto"
css={{
'&::-webkit-scrollbar': { width: '6px' },
'&::-webkit-scrollbar-track': { background: '#f1f1f1', borderRadius: '10px' },
'&::-webkit-scrollbar-thumb': { background: '#c1c1c1', borderRadius: '10px' },
'&::-webkit-scrollbar-thumb:hover': { background: '#a8a8a8' }
}}
>
{item.events.map((event, eventIdx) => (
<Box
key={eventIdx}
p={3}
bg={event.type === 'news' ? 'blue.50' : 'green.50'}
borderRadius="md"
borderLeft="3px solid"
borderLeftColor={event.type === 'news' ? 'blue.400' : 'green.400'}
_hover={{ transform: 'translateX(4px)' }}
transition="all 0.2s"
>
<VStack align="start" spacing={2}>
<HStack spacing={2} flexWrap="wrap">
<Badge
colorScheme={event.type === 'news' ? 'blue' : 'green'}
variant="solid"
>
{event.type === 'news' ? '新闻' : '研报'}
</Badge>
{event.source && (
<Badge variant="subtle">{event.source}</Badge>
)}
{event.publisher && (
<Badge colorScheme="purple" variant="subtle">
{event.publisher}
</Badge>
)}
{event.rating && (
<Badge colorScheme="orange" variant="solid">
{event.rating}
</Badge>
)}
</HStack>
<Text fontWeight="bold" fontSize="sm" color="gray.800">
{event.title}
</Text>
<Text fontSize="xs" color="gray.600" noOfLines={3}>
{event.content || '暂无内容'}
</Text>
<Button
size="xs"
variant="link"
colorScheme="blue"
leftIcon={<ViewIcon />}
onClick={() => {
if (event.type === 'news') {
trackNewsClicked(event, item.date);
trackNewsDetailOpened(event);
setSelectedNews({
title: event.title,
content: event.content,
source: event.source,
time: event.time,
url: event.url
});
setIsNewsModalOpen(true);
} else if (event.type === 'report') {
trackReportClicked(event, item.date);
trackReportDetailOpened(event);
setSelectedReport({
title: event.title,
content: event.content,
publisher: event.publisher,
author: event.author,
time: event.time,
rating: event.rating,
security_name: event.security_name,
content_url: event.content_url
});
setIsReportModalOpen(true);
}
}}
>
查看详情
</Button>
</VStack>
</Box>
))}
</VStack>
</Collapse>
</Box>
) : (
<Text fontSize="sm" color="gray.400" fontStyle="italic" textAlign="center" py={2}>
📭 当日无相关资讯
</Text> </Text>
)} )}
</VStack> </VStack>
</Box> </Box>
{/* 中间 - 时间轴节点 */} {/* 时间轴节点 */}
<Box <Box
position="relative" position="relative"
zIndex={2} zIndex={2}
flexShrink={0}
> >
<Box <Box
w={hasEvents ? 14 : (item.price ? 10 : 6)} w={hasEvents ? 16 : 12}
h={hasEvents ? 14 : (item.price ? 10 : 6)} h={hasEvents ? 16 : 12}
borderRadius="full" borderRadius="full"
bg={ bgGradient={
hasEvents ? 'purple.500' : hasEvents
item.price ? ( ? 'linear(135deg, purple.400, pink.400)'
priceInfo.color === 'red' ? 'red.400' : : item.price
priceInfo.color === 'green' ? 'green.400' : ? (priceInfo.color === 'red'
'gray.400' ? 'linear(135deg, red.400, red.500)'
) : 'gray.300' : priceInfo.color === 'green'
? 'linear(135deg, green.400, green.500)'
: 'linear(135deg, gray.300, gray.400)')
: 'linear(135deg, gray.200, gray.300)'
} }
border="4px solid" border="4px solid white"
borderColor="white" boxShadow="0 4px 12px rgba(0,0,0,0.15)"
boxShadow="lg"
position="relative" position="relative"
cursor={hasEvents ? 'pointer' : 'default'} cursor={hasEvents ? 'pointer' : 'default'}
onClick={() => hasEvents && toggleDateExpand(item.date)} onClick={() => hasEvents && toggleDateExpand(item.date)}
_hover={hasEvents ? { _hover={hasEvents ? {
transform: 'scale(1.2)', transform: 'scale(1.15)',
bg: 'pink.500' boxShadow: '0 6px 20px rgba(139, 92, 246, 0.4)'
} : {}} } : {}}
transition="all 0.2s" transition="all 0.3s"
display="flex"
alignItems="center"
justifyContent="center"
> >
{hasEvents && ( {hasEvents && (
<> <>
<Badge <Badge
position="absolute" position="absolute"
top="-2" top="-1"
right="-2" right="-1"
colorScheme="red" colorScheme="red"
borderRadius="full" borderRadius="full"
minW="24px" minW="22px"
h="24px" h="22px"
display="flex" display="flex"
alignItems="center" alignItems="center"
justifyContent="center" justifyContent="center"
fontSize="sm" fontSize="xs"
fontWeight="bold" fontWeight="bold"
boxShadow="md" boxShadow="md"
border="2px solid white" border="2px solid white"
> >
{item.events.length} {item.events.length}
</Badge> </Badge>
{/* 动画点击提示 */}
<Box <Box
position="absolute" position="absolute"
top="50%" top="50%"
@@ -693,183 +828,21 @@ const ConceptTimelineModal = ({
</> </>
)} )}
</Box> </Box>
</Box>
{/* 右侧 - 事件信息 */} {/* 连接线到下一个节点 */}
<Box {index < timelineData.length - 1 && (
flex={1} <Box
pl={8} position="absolute"
> top="100%"
{hasEvents ? ( left="50%"
<Box> transform="translateX(-50%)"
<Button width="2px"
size="sm" height="40px"
variant="ghost" bgGradient="linear(to-b, purple.300, pink.300)"
colorScheme="purple" />
rightIcon={
<Icon
as={isExpanded ? ChevronDownIcon : ChevronRightIcon}
transition="transform 0.2s"
/>
}
onClick={() => toggleDateExpand(item.date)}
mb={2}
>
<HStack spacing={2}>
<Text>
{item.events.filter(e => e.type === 'news').length > 0 &&
`${item.events.filter(e => e.type === 'news').length} 条新闻`}
</Text>
{item.events.filter(e => e.type === 'news').length > 0 &&
item.events.filter(e => e.type === 'report').length > 0 &&
<Text>·</Text>
}
<Text>
{item.events.filter(e => e.type === 'report').length > 0 &&
`${item.events.filter(e => e.type === 'report').length} 份研报`}
</Text>
</HStack>
</Button>
<Collapse in={isExpanded} animateOpacity>
<VStack
align="stretch"
spacing={3}
maxH="400px"
overflowY="auto"
bg="white"
p={4}
borderRadius="lg"
boxShadow="sm"
border="1px solid"
borderColor="gray.200"
css={{
'&::-webkit-scrollbar': {
width: '6px',
},
'&::-webkit-scrollbar-track': {
background: '#f1f1f1',
borderRadius: '10px',
},
'&::-webkit-scrollbar-thumb': {
background: '#c1c1c1',
borderRadius: '10px',
},
'&::-webkit-scrollbar-thumb:hover': {
background: '#a8a8a8',
},
}}
>
{item.events.map((event, eventIdx) => (
<Box
key={eventIdx}
p={3}
bg={event.type === 'news' ? 'blue.50' : 'green.50'}
borderRadius="md"
borderLeft="3px solid"
borderLeftColor={event.type === 'news' ? 'blue.400' : 'green.400'}
_hover={{ transform: 'translateX(4px)' }}
transition="all 0.2s"
>
<VStack align="start" spacing={2}>
<HStack spacing={2} flexWrap="wrap">
<Badge
colorScheme={event.type === 'news' ? 'blue' : 'green'}
variant="solid"
>
{event.type === 'news' ? '新闻' : '研报'}
</Badge>
{event.source && (
<Badge variant="subtle">
{event.source}
</Badge>
)}
{event.publisher && (
<Badge colorScheme="purple" variant="subtle">
{event.publisher}
</Badge>
)}
{event.rating && (
<Badge colorScheme="orange" variant="solid">
{event.rating}
</Badge>
)}
</HStack>
<Text
fontWeight="bold"
fontSize="sm"
color="gray.800"
>
{event.title}
</Text>
<Text
fontSize="xs"
color="gray.600"
noOfLines={3}
>
{event.content || '暂无内容'}
</Text>
<Button
size="xs"
variant="link"
colorScheme="blue"
leftIcon={<ViewIcon />}
onClick={() => {
if (event.type === 'news') {
// 🎯 追踪新闻点击和详情打开
trackNewsClicked(event, item.date);
trackNewsDetailOpened(event);
setSelectedNews({
title: event.title,
content: event.content,
source: event.source,
time: event.time,
url: event.url
});
setIsNewsModalOpen(true);
} else if (event.type === 'report') {
// 🎯 追踪研报点击和详情打开
trackReportClicked(event, item.date);
trackReportDetailOpened(event);
setSelectedReport({
title: event.title,
content: event.content,
publisher: event.publisher,
author: event.author,
time: event.time,
rating: event.rating,
security_name: event.security_name,
content_url: event.content_url
});
setIsReportModalOpen(true);
}
}}
>
查看详情
</Button>
</VStack>
</Box>
))}
</VStack>
</Collapse>
</Box>
) : (
<Text fontSize="sm" color="gray.400" fontStyle="italic">
当日无相关资讯
</Text>
)} )}
</Box> </Box>
</Flex> </Flex>
{/* 分隔线 */}
{index < timelineData.length - 1 && (
<Divider opacity={0.3} />
)}
</Box> </Box>
); );
})} })}