update pay function

This commit is contained in:
2025-11-23 14:25:38 +08:00
parent 4da1d580fc
commit f578969ee6

View File

@@ -46,6 +46,7 @@ import {
FaHistory, FaHistory,
FaNewspaper, FaNewspaper,
FaFileAlt, FaFileAlt,
FaClock,
} from 'react-icons/fa'; } from 'react-icons/fa';
import { keyframes } from '@emotion/react'; import { keyframes } from '@emotion/react';
@@ -58,6 +59,11 @@ const pulseAnimation = keyframes`
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.5; } 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.5; }
`; `;
const shimmerAnimation = keyframes`
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
`;
// API配置 - 与主文件保持一致 // API配置 - 与主文件保持一致
const API_BASE_URL = process.env.NODE_ENV === 'production' const API_BASE_URL = process.env.NODE_ENV === 'production'
? '/concept-api' ? '/concept-api'
@@ -213,24 +219,35 @@ const ConceptTimelineModal = ({
// 如果有价格数据,添加价格事件 // 如果有价格数据,添加价格事件
if (hasPriceData) { if (hasPriceData) {
const changePercent = item.price.avg_change_pct;
const isSignificantRise = changePercent >= 3; // 涨幅 >= 3% 为重大利好
let bgColor = '#e2e8f0'; let bgColor = '#e2e8f0';
let title = priceInfo.text;
if (priceInfo.color === 'red') { if (priceInfo.color === 'red') {
bgColor = '#FC8181'; // 红色(上涨) if (isSignificantRise) {
// 涨幅 >= 3%,使用醒目的橙红色 + 火焰图标
bgColor = '#F56565'; // 更深的红色
title = `🔥 ${priceInfo.text}`;
} else {
bgColor = '#FC8181'; // 普通红色(上涨)
}
} else if (priceInfo.color === 'green') { } else if (priceInfo.color === 'green') {
bgColor = '#68D391'; // 绿色(下跌) bgColor = '#68D391'; // 绿色(下跌)
} }
events.push({ events.push({
id: `${item.date}-price`, id: `${item.date}-price`,
title: priceInfo.text, title: title,
date: item.date, date: item.date,
start: item.date, start: item.date,
backgroundColor: bgColor, backgroundColor: bgColor,
borderColor: bgColor, borderColor: isSignificantRise ? '#C53030' : bgColor, // 深红色边框强调
extendedProps: { extendedProps: {
eventType: 'price', eventType: 'price',
priceInfo, priceInfo,
originalData: item, originalData: item,
isSignificantRise, // 标记重大涨幅
} }
}); });
} }
@@ -589,19 +606,46 @@ const ConceptTimelineModal = ({
<ModalOverlay /> <ModalOverlay />
<ModalContent maxW="1400px" m={4}> <ModalContent maxW="1400px" m={4}>
<ModalHeader <ModalHeader
bgGradient="linear(to-r, purple.500, pink.500)" bgGradient="linear(135deg, purple.600 0%, purple.500 50%, pink.500 100%)"
color="white" color="white"
position="sticky" position="sticky"
top={0} top={0}
zIndex={10} zIndex={10}
py={6}
boxShadow="lg"
> >
<HStack spacing={3}> <HStack spacing={4} flexWrap="wrap">
<Icon as={FaChartLine} /> <Icon
<Text>{conceptName} - 历史时间轴</Text> as={FaChartLine}
<Badge colorScheme="yellow" ml={2}> boxSize={6}
filter="drop-shadow(0 2px 4px rgba(0,0,0,0.2))"
/>
<Text
fontSize="xl"
fontWeight="bold"
textShadow="0 2px 4px rgba(0,0,0,0.2)"
>
{conceptName} - 历史时间轴
</Text>
<Badge
colorScheme="yellow"
px={3}
py={1}
borderRadius="full"
fontSize="sm"
boxShadow="md"
>
最近100天 最近100天
</Badge> </Badge>
<Badge colorScheme="purple" ml={2} fontSize="xs"> <Badge
bg="whiteAlpha.300"
color="white"
px={3}
py={1}
borderRadius="full"
fontSize="xs"
backdropFilter="blur(10px)"
>
🔥 Max版功能 🔥 Max版功能
</Badge> </Badge>
</HStack> </HStack>
@@ -638,22 +682,81 @@ const ConceptTimelineModal = ({
) : timelineData.length > 0 ? ( ) : timelineData.length > 0 ? (
<Box position="relative" maxW="1200px" mx="auto" px={4}> <Box position="relative" maxW="1200px" mx="auto" px={4}>
{/* 图例说明 */} {/* 图例说明 */}
<Flex justify="center" mb={4} flexWrap="wrap" gap={3}> <Flex justify="center" mb={6} flexWrap="wrap" gap={4}>
<HStack spacing={2}> <HStack
<Box w={4} h={4} bg="#9F7AEA" borderRadius="sm" /> spacing={2}
<Text fontSize="sm">有新闻/研报</Text> px={4}
py={2}
bg="purple.50"
borderRadius="lg"
border="1px solid"
borderColor="purple.200"
boxShadow="sm"
transition="all 0.2s"
_hover={{ transform: 'translateY(-2px)', boxShadow: 'md' }}
>
<Box w={3} h={3} bg="#9F7AEA" borderRadius="full" boxShadow="sm" />
<Text fontSize="sm" fontWeight="medium" color="gray.700">📰 新闻</Text>
</HStack> </HStack>
<HStack spacing={2}> <HStack
<Box w={4} h={4} bg="#FC8181" borderRadius="sm" /> spacing={2}
<Text fontSize="sm">上涨</Text> px={4}
py={2}
bg="purple.50"
borderRadius="lg"
border="1px solid"
borderColor="purple.300"
boxShadow="sm"
transition="all 0.2s"
_hover={{ transform: 'translateY(-2px)', boxShadow: 'md' }}
>
<Box w={3} h={3} bg="#805AD5" borderRadius="full" boxShadow="sm" />
<Text fontSize="sm" fontWeight="medium" color="gray.700">📊 研报</Text>
</HStack> </HStack>
<HStack spacing={2}> <HStack
<Box w={4} h={4} bg="#68D391" borderRadius="sm" /> spacing={2}
<Text fontSize="sm">下跌</Text> px={4}
py={2}
bg="red.50"
borderRadius="lg"
border="1px solid"
borderColor="red.200"
boxShadow="sm"
transition="all 0.2s"
_hover={{ transform: 'translateY(-2px)', boxShadow: 'md' }}
>
<Icon as={FaArrowUp} color="red.500" boxSize={3} />
<Text fontSize="sm" fontWeight="medium" color="gray.700">上涨</Text>
</HStack> </HStack>
<HStack spacing={2}> <HStack
<Box w={4} h={4} bg="#e2e8f0" borderRadius="sm" /> spacing={2}
<Text fontSize="sm">无数据</Text> px={4}
py={2}
bg="green.50"
borderRadius="lg"
border="1px solid"
borderColor="green.200"
boxShadow="sm"
transition="all 0.2s"
_hover={{ transform: 'translateY(-2px)', boxShadow: 'md' }}
>
<Icon as={FaArrowDown} color="green.500" boxSize={3} />
<Text fontSize="sm" fontWeight="medium" color="gray.700">下跌</Text>
</HStack>
<HStack
spacing={2}
px={4}
py={2}
bg="orange.50"
borderRadius="lg"
border="1px solid"
borderColor="orange.200"
boxShadow="sm"
transition="all 0.2s"
_hover={{ transform: 'translateY(-2px)', boxShadow: 'md' }}
>
<Text fontSize="sm" fontWeight="bold">🔥</Text>
<Text fontSize="sm" fontWeight="medium" color="gray.700">涨3%+</Text>
</HStack> </HStack>
</Flex> </Flex>
@@ -748,28 +851,32 @@ const ConceptTimelineModal = ({
displayEventTime={false} displayEventTime={false}
/> />
</Box> </Box>
{/* 底部说明 */}
<Box textAlign="center" mt={6}>
<Badge
colorScheme="purple"
variant="subtle"
px={4}
py={2}
borderRadius="full"
fontSize="sm"
>
时间轴起始点
</Badge>
</Box>
</Box> </Box>
) : ( ) : (
<Center py={20}> <Center py={24}>
<VStack spacing={4}> <VStack
<Icon as={FaHistory} boxSize={16} color="gray.300" /> spacing={6}
<Text fontSize="lg" color="gray.500"> bg="white"
暂无历史数据 p={12}
</Text> borderRadius="2xl"
boxShadow="xl"
border="2px dashed"
borderColor="purple.200"
>
<Icon
as={FaHistory}
boxSize={24}
color="purple.300"
opacity={0.5}
/>
<VStack spacing={2}>
<Text fontSize="2xl" fontWeight="bold" color="gray.700">
暂无历史数据
</Text>
<Text fontSize="md" color="gray.500">
该概念在最近100天内没有相关事件记录
</Text>
</VStack>
</VStack> </VStack>
</Center> </Center>
)} )}
@@ -780,8 +887,24 @@ const ConceptTimelineModal = ({
</Box> </Box>
</ModalBody> </ModalBody>
<ModalFooter borderTop="1px solid" borderColor="gray.200"> <ModalFooter
<Button colorScheme="purple" onClick={onClose}> borderTop="2px solid"
borderColor="purple.100"
bg="gray.50"
py={4}
>
<Button
colorScheme="purple"
size="lg"
px={8}
onClick={onClose}
boxShadow="md"
_hover={{
transform: 'translateY(-2px)',
boxShadow: 'lg',
}}
transition="all 0.2s"
>
关闭 关闭
</Button> </Button>
</ModalFooter> </ModalFooter>
@@ -798,28 +921,69 @@ const ConceptTimelineModal = ({
scrollBehavior="inside" scrollBehavior="inside"
> >
<ModalOverlay /> <ModalOverlay />
<ModalContent> <ModalContent borderRadius="2xl" overflow="hidden">
<ModalHeader bgGradient="linear(to-r, purple.500, pink.500)" color="white"> <ModalHeader
<VStack align="start" spacing={2}> bgGradient="linear(135deg, purple.600 0%, purple.500 50%, pink.500 100%)"
<HStack> color="white"
<Icon as={CalendarIcon} /> py={6}
<Text>{formatDateDisplay(selectedDate)}</Text> position="relative"
_before={{
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
bgGradient: 'linear(to-r, transparent, whiteAlpha.200, transparent)',
animation: `${shimmerAnimation} 3s infinite`,
}}
>
<VStack align="start" spacing={3} position="relative" zIndex={1}>
<HStack spacing={3}>
<Icon
as={CalendarIcon}
boxSize={6}
filter="drop-shadow(0 2px 4px rgba(0,0,0,0.2))"
/>
<Text fontSize="xl" fontWeight="bold" textShadow="0 2px 4px rgba(0,0,0,0.2)">
{formatDateDisplay(selectedDate)}
</Text>
</HStack> </HStack>
{selectedDateData.price && ( {selectedDateData.price && (
<HStack spacing={3} fontSize="sm"> <HStack spacing={4} fontSize="sm" flexWrap="wrap">
<Badge colorScheme={getPriceInfo(selectedDateData.price).color} variant="solid" px={3} py={1}> <Badge
<HStack spacing={1}> colorScheme={getPriceInfo(selectedDateData.price).color}
<Icon variant="solid"
as={getPriceInfo(selectedDateData.price).icon} px={4}
boxSize={3} py={2}
/> borderRadius="full"
<Text>{getPriceInfo(selectedDateData.price).text}</Text> fontSize="md"
boxShadow="md"
>
<HStack spacing={2}>
{getPriceInfo(selectedDateData.price).icon && (
<Icon
as={getPriceInfo(selectedDateData.price).icon}
boxSize={4}
/>
)}
<Text fontWeight="bold">
{getPriceInfo(selectedDateData.price).text}
</Text>
</HStack> </HStack>
</Badge> </Badge>
{selectedDateData.price.stock_count && ( {selectedDateData.price.stock_count && (
<Text opacity={0.9}> <HStack
📊 统计股票: {selectedDateData.price.stock_count} spacing={1}
</Text> bg="whiteAlpha.300"
px={3}
py={1}
borderRadius="full"
backdropFilter="blur(10px)"
>
<Text fontWeight="medium">📊 统计股票:</Text>
<Text fontWeight="bold">{selectedDateData.price.stock_count} </Text>
</HStack>
)} )}
</HStack> </HStack>
)} )}
@@ -829,20 +993,85 @@ const ConceptTimelineModal = ({
<ModalBody py={6}> <ModalBody py={6}>
{selectedDateData.events && selectedDateData.events.length > 0 ? ( {selectedDateData.events && selectedDateData.events.length > 0 ? (
<VStack align="stretch" spacing={4}> <VStack align="stretch" spacing={6}>
{/* 统计卡片 */}
<HStack spacing={4} justifyContent="center">
<Box
px={6}
py={3}
bg="blue.50"
borderRadius="lg"
boxShadow="sm"
border="1px solid"
borderColor="blue.200"
>
<HStack spacing={2}>
<Icon as={FaNewspaper} color="blue.500" boxSize={5} />
<Text fontWeight="bold" fontSize="lg" color="blue.700">
{selectedDateData.events.filter(e => e.type === 'news').length}
</Text>
<Text fontSize="sm" color="gray.600">条新闻</Text>
</HStack>
</Box>
<Box
px={6}
py={3}
bg="green.50"
borderRadius="lg"
boxShadow="sm"
border="1px solid"
borderColor="green.200"
>
<HStack spacing={2}>
<Icon as={FaFileAlt} color="green.500" boxSize={5} />
<Text fontWeight="bold" fontSize="lg" color="green.700">
{selectedDateData.events.filter(e => e.type === 'report').length}
</Text>
<Text fontSize="sm" color="gray.600">篇研报</Text>
</HStack>
</Box>
</HStack>
{/* 事件列表 */}
<VStack align="stretch" spacing={5}>
{selectedDateData.events.map((event, eventIdx) => ( {selectedDateData.events.map((event, eventIdx) => (
<Box <Box
key={eventIdx} key={eventIdx}
p={4} p={5}
bg={event.type === 'news' ? 'blue.50' : 'green.50'} bgGradient={
borderRadius="lg" event.type === 'news'
borderLeft="4px solid" ? 'linear(to-br, blue.50, blue.100)'
borderLeftColor={event.type === 'news' ? 'blue.400' : 'green.400'} : 'linear(to-br, green.50, green.100)'
}
borderRadius="xl"
borderLeft="5px solid"
borderLeftColor={event.type === 'news' ? 'blue.500' : 'green.500'}
boxShadow="sm"
_hover={{ _hover={{
transform: 'translateX(4px)', transform: 'translateX(6px) translateY(-2px)',
boxShadow: 'md', boxShadow: 'lg',
borderLeftColor: event.type === 'news' ? 'blue.600' : 'green.600',
}}
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
cursor="pointer"
position="relative"
overflow="hidden"
_before={{
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '3px',
bgGradient: event.type === 'news'
? 'linear(to-r, blue.400, blue.600, blue.400)'
: 'linear(to-r, green.400, green.600, green.400)',
opacity: 0,
transition: 'opacity 0.3s',
}}
_hover_before={{
opacity: 1,
}} }}
transition="all 0.2s"
> >
<VStack align="start" spacing={3}> <VStack align="start" spacing={3}>
<HStack spacing={2} flexWrap="wrap"> <HStack spacing={2} flexWrap="wrap">
@@ -850,104 +1079,175 @@ const ConceptTimelineModal = ({
colorScheme={event.type === 'news' ? 'blue' : 'green'} colorScheme={event.type === 'news' ? 'blue' : 'green'}
variant="solid" variant="solid"
fontSize="sm" fontSize="sm"
px={3}
py={1}
borderRadius="full"
boxShadow="sm"
> >
{event.type === 'news' ? '📰 新闻' : '📊 研报'} {event.type === 'news' ? '📰 新闻' : '📊 研报'}
</Badge> </Badge>
{event.source && (
<Badge variant="subtle" fontSize="xs">
{event.source}
</Badge>
)}
{event.publisher && ( {event.publisher && (
<Badge colorScheme="purple" variant="subtle" fontSize="xs"> <Badge
colorScheme="purple"
variant="subtle"
fontSize="xs"
px={2}
py={1}
borderRadius="md"
>
{event.publisher} {event.publisher}
</Badge> </Badge>
)} )}
{event.rating && ( {event.rating && (
<Badge colorScheme="orange" variant="solid" fontSize="xs"> <Badge
{event.rating} colorScheme="orange"
variant="solid"
fontSize="xs"
px={2}
py={1}
borderRadius="md"
>
{event.rating}
</Badge> </Badge>
)} )}
{event.security_name && ( {event.security_name && (
<Badge colorScheme="cyan" variant="subtle" fontSize="xs"> <Badge
{event.security_name} colorScheme="cyan"
variant="subtle"
fontSize="xs"
px={2}
py={1}
borderRadius="md"
>
🏢 {event.security_name}
</Badge> </Badge>
)} )}
</HStack> </HStack>
<Text fontWeight="bold" fontSize="md" color="gray.800"> <Text
fontWeight="bold"
fontSize="lg"
color="gray.800"
lineHeight="1.4"
>
{event.title} {event.title}
</Text> </Text>
<Text fontSize="sm" color="gray.600" noOfLines={4}> <Text
fontSize="sm"
color="gray.600"
noOfLines={3}
lineHeight="1.6"
>
{event.content || '暂无内容'} {event.content || '暂无内容'}
</Text> </Text>
{event.time && ( <HStack spacing={4} w="100%" justify="space-between" align="center" mt={1}>
<Text fontSize="xs" color="gray.500"> {event.time && (
🕐 {formatDateTime(event.time)} <HStack spacing={1}>
</Text> <Icon as={FaClock} color="gray.500" boxSize={3} />
)} <Text fontSize="xs" color="gray.500" fontWeight="medium">
{formatDateTime(event.time)}
</Text>
</HStack>
)}
<Button <Button
size="sm" size="sm"
colorScheme="blue" colorScheme={event.type === 'news' ? 'blue' : 'green'}
variant="outline" variant="solid"
leftIcon={<ViewIcon />} leftIcon={<ViewIcon />}
onClick={() => { boxShadow="sm"
if (event.type === 'news') { _hover={{
trackNewsClicked(event, selectedDate); transform: 'scale(1.05)',
trackNewsDetailOpened(event); boxShadow: 'md',
setSelectedNews({ }}
title: event.title, transition="all 0.2s"
content: event.content, onClick={() => {
source: event.source, if (event.type === 'news') {
time: event.time, trackNewsClicked(event, selectedDate);
url: event.url, trackNewsDetailOpened(event);
}); setSelectedNews({
setIsNewsModalOpen(true); title: event.title,
} else if (event.type === 'report') { content: event.content,
trackReportClicked(event, selectedDate); source: event.source,
trackReportDetailOpened(event); time: event.time,
setSelectedReport({ url: event.url,
title: event.title, });
content: event.content, setIsNewsModalOpen(true);
publisher: event.publisher, } else if (event.type === 'report') {
author: event.author, trackReportClicked(event, selectedDate);
time: event.time, trackReportDetailOpened(event);
rating: event.rating, setSelectedReport({
security_name: event.security_name, title: event.title,
content_url: event.content_url, content: event.content,
}); publisher: event.publisher,
setIsReportModalOpen(true); author: event.author,
} time: event.time,
}} rating: event.rating,
> security_name: event.security_name,
查看全文 content_url: event.content_url,
</Button> });
setIsReportModalOpen(true);
}
}}
>
查看详情
</Button>
</HStack>
</VStack> </VStack>
</Box> </Box>
))} ))}
</VStack> </VStack>
) : ( ) : (
<Center py={12}> <Center py={16}>
<VStack spacing={4}> <VStack
<Icon as={FaHistory} boxSize={16} color="gray.300" /> spacing={5}
<Text fontSize="lg" color="gray.500"> bg="gray.50"
当日无新闻或研报 p={10}
</Text> borderRadius="2xl"
{selectedDateData.price && ( border="2px dashed"
<Text fontSize="sm" color="gray.400"> borderColor="gray.300"
仅有涨跌幅数据 >
<Icon
as={FaHistory}
boxSize={20}
color="gray.400"
opacity={0.6}
/>
<VStack spacing={2}>
<Text fontSize="xl" fontWeight="bold" color="gray.600">
当日无新闻或研报
</Text> </Text>
)} {selectedDateData.price && (
<Text fontSize="md" color="gray.500">
仅有涨跌幅数据
</Text>
)}
</VStack>
</VStack> </VStack>
</Center> </Center>
)} )}
</ModalBody> </ModalBody>
<ModalFooter borderTop="1px solid" borderColor="gray.200"> <ModalFooter
<Button colorScheme="purple" onClick={onDateDetailClose}> borderTop="2px solid"
borderColor="gray.100"
bg="gray.50"
py={4}
>
<Button
colorScheme="purple"
size="lg"
px={8}
onClick={onDateDetailClose}
boxShadow="md"
_hover={{
transform: 'translateY(-2px)',
boxShadow: 'lg',
}}
transition="all 0.2s"
>
关闭 关闭
</Button> </Button>
</ModalFooter> </ModalFooter>