update pay function
This commit is contained in:
@@ -535,177 +535,103 @@ 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">
|
||||||
|
<Badge
|
||||||
|
colorScheme="purple"
|
||||||
|
variant="subtle"
|
||||||
|
px={3}
|
||||||
|
py={1}
|
||||||
|
borderRadius="full"
|
||||||
|
fontSize="xs"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
color="gray.700"
|
|
||||||
>
|
>
|
||||||
{formatDateDisplay(item.date)}
|
{formatDateDisplay(item.date)}
|
||||||
</Text>
|
</Badge>
|
||||||
|
|
||||||
{item.price && (
|
{item.price && (
|
||||||
<Box
|
<Badge
|
||||||
bg="white"
|
colorScheme={priceInfo.color}
|
||||||
px={4}
|
variant="solid"
|
||||||
py={2}
|
px={3}
|
||||||
|
py={1}
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
boxShadow="sm"
|
fontSize="sm"
|
||||||
border="1px solid"
|
|
||||||
borderColor={`${priceInfo.color}.200`}
|
|
||||||
>
|
>
|
||||||
<HStack spacing={2}>
|
<HStack spacing={1}>
|
||||||
{priceInfo.icon && (
|
{priceInfo.icon && (
|
||||||
<Icon
|
<Icon as={priceInfo.icon} boxSize={3} />
|
||||||
as={priceInfo.icon}
|
)}
|
||||||
color={`${priceInfo.color}.500`}
|
<Text>{priceInfo.text}</Text>
|
||||||
boxSize={4}
|
</HStack>
|
||||||
/>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
<Text
|
|
||||||
fontWeight="bold"
|
|
||||||
fontSize="md"
|
|
||||||
color={`${priceInfo.color}.600`}
|
|
||||||
>
|
|
||||||
{priceInfo.text}
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
||||||
{item.price.stock_count && (
|
{/* 股票数量 */}
|
||||||
<Text
|
{item.price && item.price.stock_count && (
|
||||||
fontSize="xs"
|
<Text fontSize="xs" color="gray.600">
|
||||||
color="gray.500"
|
📊 统计股票: {item.price.stock_count} 只
|
||||||
mt={1}
|
|
||||||
>
|
|
||||||
统计股票: {item.price.stock_count} 只
|
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!item.price && !hasEvents && (
|
{/* 事件信息 */}
|
||||||
<Text fontSize="xs" color="gray.400" fontStyle="italic">
|
|
||||||
当日无数据
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</VStack>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* 中间 - 时间轴节点 */}
|
|
||||||
<Box
|
|
||||||
position="relative"
|
|
||||||
zIndex={2}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
w={hasEvents ? 14 : (item.price ? 10 : 6)}
|
|
||||||
h={hasEvents ? 14 : (item.price ? 10 : 6)}
|
|
||||||
borderRadius="full"
|
|
||||||
bg={
|
|
||||||
hasEvents ? 'purple.500' :
|
|
||||||
item.price ? (
|
|
||||||
priceInfo.color === 'red' ? 'red.400' :
|
|
||||||
priceInfo.color === 'green' ? 'green.400' :
|
|
||||||
'gray.400'
|
|
||||||
) : 'gray.300'
|
|
||||||
}
|
|
||||||
border="4px solid"
|
|
||||||
borderColor="white"
|
|
||||||
boxShadow="lg"
|
|
||||||
position="relative"
|
|
||||||
cursor={hasEvents ? 'pointer' : 'default'}
|
|
||||||
onClick={() => hasEvents && toggleDateExpand(item.date)}
|
|
||||||
_hover={hasEvents ? {
|
|
||||||
transform: 'scale(1.2)',
|
|
||||||
bg: 'pink.500'
|
|
||||||
} : {}}
|
|
||||||
transition="all 0.2s"
|
|
||||||
>
|
|
||||||
{hasEvents && (
|
|
||||||
<>
|
|
||||||
<Badge
|
|
||||||
position="absolute"
|
|
||||||
top="-2"
|
|
||||||
right="-2"
|
|
||||||
colorScheme="red"
|
|
||||||
borderRadius="full"
|
|
||||||
minW="24px"
|
|
||||||
h="24px"
|
|
||||||
display="flex"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="bold"
|
|
||||||
boxShadow="md"
|
|
||||||
border="2px solid white"
|
|
||||||
>
|
|
||||||
{item.events.length}
|
|
||||||
</Badge>
|
|
||||||
{/* 动画点击提示 */}
|
|
||||||
<Box
|
|
||||||
position="absolute"
|
|
||||||
top="50%"
|
|
||||||
left="50%"
|
|
||||||
transform="translate(-50%, -50%)"
|
|
||||||
w="100%"
|
|
||||||
h="100%"
|
|
||||||
borderRadius="full"
|
|
||||||
border="2px solid"
|
|
||||||
borderColor="purple.300"
|
|
||||||
opacity={0.5}
|
|
||||||
animation={`${pulseAnimation} 2s infinite`}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* 右侧 - 事件信息 */}
|
|
||||||
<Box
|
|
||||||
flex={1}
|
|
||||||
pl={8}
|
|
||||||
>
|
|
||||||
{hasEvents ? (
|
{hasEvents ? (
|
||||||
<Box>
|
<Box>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
colorScheme="purple"
|
colorScheme="purple"
|
||||||
|
width="full"
|
||||||
|
justifyContent="space-between"
|
||||||
rightIcon={
|
rightIcon={
|
||||||
<Icon
|
<Icon
|
||||||
as={isExpanded ? ChevronDownIcon : ChevronRightIcon}
|
as={isExpanded ? ChevronDownIcon : ChevronRightIcon}
|
||||||
@@ -713,21 +639,18 @@ const ConceptTimelineModal = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onClick={() => toggleDateExpand(item.date)}
|
onClick={() => toggleDateExpand(item.date)}
|
||||||
mb={2}
|
|
||||||
>
|
>
|
||||||
<HStack spacing={2}>
|
<HStack spacing={2} fontSize="sm">
|
||||||
<Text>
|
{item.events.filter(e => e.type === 'news').length > 0 && (
|
||||||
{item.events.filter(e => e.type === 'news').length > 0 &&
|
<Badge colorScheme="blue" variant="solid">
|
||||||
`${item.events.filter(e => e.type === 'news').length} 条新闻`}
|
📰 {item.events.filter(e => e.type === 'news').length}
|
||||||
</Text>
|
</Badge>
|
||||||
{item.events.filter(e => e.type === 'news').length > 0 &&
|
)}
|
||||||
item.events.filter(e => e.type === 'report').length > 0 &&
|
{item.events.filter(e => e.type === 'report').length > 0 && (
|
||||||
<Text>·</Text>
|
<Badge colorScheme="green" variant="solid">
|
||||||
}
|
📊 {item.events.filter(e => e.type === 'report').length}
|
||||||
<Text>
|
</Badge>
|
||||||
{item.events.filter(e => e.type === 'report').length > 0 &&
|
)}
|
||||||
`${item.events.filter(e => e.type === 'report').length} 份研报`}
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
</HStack>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
@@ -735,29 +658,14 @@ const ConceptTimelineModal = ({
|
|||||||
<VStack
|
<VStack
|
||||||
align="stretch"
|
align="stretch"
|
||||||
spacing={3}
|
spacing={3}
|
||||||
maxH="400px"
|
mt={3}
|
||||||
|
maxH="300px"
|
||||||
overflowY="auto"
|
overflowY="auto"
|
||||||
bg="white"
|
|
||||||
p={4}
|
|
||||||
borderRadius="lg"
|
|
||||||
boxShadow="sm"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="gray.200"
|
|
||||||
css={{
|
css={{
|
||||||
'&::-webkit-scrollbar': {
|
'&::-webkit-scrollbar': { width: '6px' },
|
||||||
width: '6px',
|
'&::-webkit-scrollbar-track': { background: '#f1f1f1', borderRadius: '10px' },
|
||||||
},
|
'&::-webkit-scrollbar-thumb': { background: '#c1c1c1', borderRadius: '10px' },
|
||||||
'&::-webkit-scrollbar-track': {
|
'&::-webkit-scrollbar-thumb:hover': { background: '#a8a8a8' }
|
||||||
background: '#f1f1f1',
|
|
||||||
borderRadius: '10px',
|
|
||||||
},
|
|
||||||
'&::-webkit-scrollbar-thumb': {
|
|
||||||
background: '#c1c1c1',
|
|
||||||
borderRadius: '10px',
|
|
||||||
},
|
|
||||||
'&::-webkit-scrollbar-thumb:hover': {
|
|
||||||
background: '#a8a8a8',
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.events.map((event, eventIdx) => (
|
{item.events.map((event, eventIdx) => (
|
||||||
@@ -780,9 +688,7 @@ const ConceptTimelineModal = ({
|
|||||||
{event.type === 'news' ? '新闻' : '研报'}
|
{event.type === 'news' ? '新闻' : '研报'}
|
||||||
</Badge>
|
</Badge>
|
||||||
{event.source && (
|
{event.source && (
|
||||||
<Badge variant="subtle">
|
<Badge variant="subtle">{event.source}</Badge>
|
||||||
{event.source}
|
|
||||||
</Badge>
|
|
||||||
)}
|
)}
|
||||||
{event.publisher && (
|
{event.publisher && (
|
||||||
<Badge colorScheme="purple" variant="subtle">
|
<Badge colorScheme="purple" variant="subtle">
|
||||||
@@ -795,23 +701,12 @@ const ConceptTimelineModal = ({
|
|||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</HStack>
|
</HStack>
|
||||||
|
<Text fontWeight="bold" fontSize="sm" color="gray.800">
|
||||||
<Text
|
|
||||||
fontWeight="bold"
|
|
||||||
fontSize="sm"
|
|
||||||
color="gray.800"
|
|
||||||
>
|
|
||||||
{event.title}
|
{event.title}
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text fontSize="xs" color="gray.600" noOfLines={3}>
|
||||||
<Text
|
|
||||||
fontSize="xs"
|
|
||||||
color="gray.600"
|
|
||||||
noOfLines={3}
|
|
||||||
>
|
|
||||||
{event.content || '暂无内容'}
|
{event.content || '暂无内容'}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
variant="link"
|
variant="link"
|
||||||
@@ -819,10 +714,8 @@ const ConceptTimelineModal = ({
|
|||||||
leftIcon={<ViewIcon />}
|
leftIcon={<ViewIcon />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (event.type === 'news') {
|
if (event.type === 'news') {
|
||||||
// 🎯 追踪新闻点击和详情打开
|
|
||||||
trackNewsClicked(event, item.date);
|
trackNewsClicked(event, item.date);
|
||||||
trackNewsDetailOpened(event);
|
trackNewsDetailOpened(event);
|
||||||
|
|
||||||
setSelectedNews({
|
setSelectedNews({
|
||||||
title: event.title,
|
title: event.title,
|
||||||
content: event.content,
|
content: event.content,
|
||||||
@@ -832,10 +725,8 @@ const ConceptTimelineModal = ({
|
|||||||
});
|
});
|
||||||
setIsNewsModalOpen(true);
|
setIsNewsModalOpen(true);
|
||||||
} else if (event.type === 'report') {
|
} else if (event.type === 'report') {
|
||||||
// 🎯 追踪研报点击和详情打开
|
|
||||||
trackReportClicked(event, item.date);
|
trackReportClicked(event, item.date);
|
||||||
trackReportDetailOpened(event);
|
trackReportDetailOpened(event);
|
||||||
|
|
||||||
setSelectedReport({
|
setSelectedReport({
|
||||||
title: event.title,
|
title: event.title,
|
||||||
content: event.content,
|
content: event.content,
|
||||||
@@ -859,17 +750,99 @@ const ConceptTimelineModal = ({
|
|||||||
</Collapse>
|
</Collapse>
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Text fontSize="sm" color="gray.400" fontStyle="italic">
|
<Text fontSize="sm" color="gray.400" fontStyle="italic" textAlign="center" py={2}>
|
||||||
当日无相关资讯
|
📭 当日无相关资讯
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
</VStack>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* 时间轴节点 */}
|
||||||
|
<Box
|
||||||
|
position="relative"
|
||||||
|
zIndex={2}
|
||||||
|
flexShrink={0}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
w={hasEvents ? 16 : 12}
|
||||||
|
h={hasEvents ? 16 : 12}
|
||||||
|
borderRadius="full"
|
||||||
|
bgGradient={
|
||||||
|
hasEvents
|
||||||
|
? 'linear(135deg, purple.400, pink.400)'
|
||||||
|
: item.price
|
||||||
|
? (priceInfo.color === 'red'
|
||||||
|
? 'linear(135deg, red.400, red.500)'
|
||||||
|
: priceInfo.color === 'green'
|
||||||
|
? 'linear(135deg, green.400, green.500)'
|
||||||
|
: 'linear(135deg, gray.300, gray.400)')
|
||||||
|
: 'linear(135deg, gray.200, gray.300)'
|
||||||
|
}
|
||||||
|
border="4px solid white"
|
||||||
|
boxShadow="0 4px 12px rgba(0,0,0,0.15)"
|
||||||
|
position="relative"
|
||||||
|
cursor={hasEvents ? 'pointer' : 'default'}
|
||||||
|
onClick={() => hasEvents && toggleDateExpand(item.date)}
|
||||||
|
_hover={hasEvents ? {
|
||||||
|
transform: 'scale(1.15)',
|
||||||
|
boxShadow: '0 6px 20px rgba(139, 92, 246, 0.4)'
|
||||||
|
} : {}}
|
||||||
|
transition="all 0.3s"
|
||||||
|
display="flex"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
>
|
||||||
|
{hasEvents && (
|
||||||
|
<>
|
||||||
|
<Badge
|
||||||
|
position="absolute"
|
||||||
|
top="-1"
|
||||||
|
right="-1"
|
||||||
|
colorScheme="red"
|
||||||
|
borderRadius="full"
|
||||||
|
minW="22px"
|
||||||
|
h="22px"
|
||||||
|
display="flex"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
fontSize="xs"
|
||||||
|
fontWeight="bold"
|
||||||
|
boxShadow="md"
|
||||||
|
border="2px solid white"
|
||||||
|
>
|
||||||
|
{item.events.length}
|
||||||
|
</Badge>
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
top="50%"
|
||||||
|
left="50%"
|
||||||
|
transform="translate(-50%, -50%)"
|
||||||
|
w="100%"
|
||||||
|
h="100%"
|
||||||
|
borderRadius="full"
|
||||||
|
border="2px solid"
|
||||||
|
borderColor="purple.300"
|
||||||
|
opacity={0.5}
|
||||||
|
animation={`${pulseAnimation} 2s infinite`}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* 连接线到下一个节点 */}
|
||||||
|
{index < timelineData.length - 1 && (
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
top="100%"
|
||||||
|
left="50%"
|
||||||
|
transform="translateX(-50%)"
|
||||||
|
width="2px"
|
||||||
|
height="40px"
|
||||||
|
bgGradient="linear(to-b, purple.300, pink.300)"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{/* 分隔线 */}
|
|
||||||
{index < timelineData.length - 1 && (
|
|
||||||
<Divider opacity={0.3} />
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in New Issue
Block a user