update pay ui

This commit is contained in:
2025-12-05 15:55:32 +08:00
parent b60c196f9e
commit c54318c3c9
3 changed files with 308 additions and 284 deletions

View File

@@ -26,15 +26,22 @@ import { FaTable } from 'react-icons/fa';
import marketService from '@services/marketService';
import { logger } from '@utils/logger';
// 股票信息类型
// 股票信息类型 - 兼容新旧API格式
interface StockInfo {
stock_code: string;
stock_name: string;
stock_code?: string;
stock_name?: string;
code?: string; // 新API格式
name?: string; // 新API格式
reason?: string;
change_pct?: number;
[key: string]: unknown;
}
// 获取股票代码兼容新旧API
const getStockCode = (stock: StockInfo): string => stock.code || stock.stock_code || '';
// 获取股票名称兼容新旧API
const getStockName = (stock: StockInfo): string => stock.name || stock.stock_name || '未知';
// 概念信息类型
export interface ConceptInfo {
concept_id?: string;
@@ -69,9 +76,12 @@ const ConceptStocksModal: React.FC<ConceptStocksModalProps> = ({
const [stockMarketData, setStockMarketData] = useState<Record<string, MarketData>>({});
const [loadingStockData, setLoadingStockData] = useState(false);
// 色主题
const cardBg = useColorModeValue('white', '#1a1a1a');
const hoverBg = useColorModeValue('gray.50', '#2a2a2a');
// 色主题颜色
const cardBg = 'rgba(15, 23, 42, 0.95)';
const hoverBg = 'whiteAlpha.100';
const textColor = 'white';
const subTextColor = 'whiteAlpha.700';
const borderColor = 'whiteAlpha.100';
// 响应式配置 - 添加 fallback 避免首次渲染时返回 undefined 导致弹窗异常
const isMobile = useBreakpointValue({ base: true, md: false }, { fallback: 'md' });
@@ -91,13 +101,14 @@ const ConceptStocksModal: React.FC<ConceptStocksModalProps> = ({
for (let i = 0; i < stocks.length; i += batchSize) {
const batch = stocks.slice(i, i + batchSize);
const promises = batch.map(async (stock) => {
if (!stock.stock_code) return null;
const seccode = stock.stock_code.substring(0, 6);
const stockCode = getStockCode(stock);
if (!stockCode) return null;
const seccode = stockCode.substring(0, 6);
try {
const response = await marketService.getTradeData(seccode, 1);
if (response.success && response.data?.length > 0) {
const latestData = response.data[response.data.length - 1];
return { stock_code: stock.stock_code, ...latestData };
return { stock_code: stockCode, ...latestData };
}
} catch (error) {
logger.warn('ConceptStocksModal', '获取股票行情失败', { stockCode: seccode });
@@ -168,16 +179,18 @@ const ConceptStocksModal: React.FC<ConceptStocksModalProps> = ({
<Table variant="simple" size="sm" minW={isMobile ? '600px' : undefined}>
<Thead position="sticky" top={0} bg={cardBg} zIndex={1}>
<Tr>
<Th whiteSpace="nowrap"></Th>
<Th whiteSpace="nowrap"></Th>
<Th isNumeric whiteSpace="nowrap"></Th>
<Th isNumeric whiteSpace="nowrap"></Th>
<Th whiteSpace="nowrap" minW="200px"></Th>
<Th whiteSpace="nowrap" color={subTextColor} borderColor={borderColor}></Th>
<Th whiteSpace="nowrap" color={subTextColor} borderColor={borderColor}></Th>
<Th isNumeric whiteSpace="nowrap" color={subTextColor} borderColor={borderColor}></Th>
<Th isNumeric whiteSpace="nowrap" color={subTextColor} borderColor={borderColor}></Th>
<Th whiteSpace="nowrap" minW="200px" color={subTextColor} borderColor={borderColor}></Th>
</Tr>
</Thead>
<Tbody>
{stocks.map((stock, idx) => {
const marketData = stockMarketData[stock.stock_code];
const stockCode = getStockCode(stock);
const stockName = getStockName(stock);
const marketData = stockMarketData[stockCode];
const changePercent = marketData?.change_percent;
return (
@@ -185,15 +198,15 @@ const ConceptStocksModal: React.FC<ConceptStocksModalProps> = ({
key={idx}
_hover={{ bg: hoverBg }}
cursor="pointer"
onClick={() => handleStockClick(stock.stock_code)}
onClick={() => handleStockClick(stockCode)}
>
<Td color="blue.500" fontWeight="medium">
{stock.stock_name}
<Td color="cyan.400" fontWeight="medium" borderColor={borderColor}>
{stockName}
</Td>
<Td>{stock.stock_code}</Td>
<Td isNumeric>
<Td color={subTextColor} borderColor={borderColor}>{stockCode}</Td>
<Td isNumeric color={textColor} borderColor={borderColor}>
{loadingStockData ? (
<Spinner size="xs" />
<Spinner size="xs" color="purple.400" />
) : marketData?.close ? (
`¥${marketData.close.toFixed(2)}`
) : (
@@ -203,23 +216,24 @@ const ConceptStocksModal: React.FC<ConceptStocksModalProps> = ({
<Td
isNumeric
fontWeight="bold"
borderColor={borderColor}
color={
changePercent && changePercent > 0
? 'red.500'
? 'red.400'
: changePercent && changePercent < 0
? 'green.500'
: 'gray.500'
? 'green.400'
: 'whiteAlpha.500'
}
>
{loadingStockData ? (
<Spinner size="xs" />
<Spinner size="xs" color="purple.400" />
) : changePercent !== undefined ? (
`${changePercent > 0 ? '+' : ''}${changePercent.toFixed(2)}%`
) : (
'-'
)}
</Td>
<Td fontSize="xs" color="gray.600" maxW="300px">
<Td fontSize="xs" color={subTextColor} maxW="300px" borderColor={borderColor}>
<Text noOfLines={2}>{stock.reason || '-'}</Text>
</Td>
</Tr>

View File

@@ -608,10 +608,20 @@ const ConceptTimelineModal = ({
scrollBehavior="inside"
isCentered
>
<ModalOverlay />
<ModalContent maxW="1400px" m={{ base: 0, md: 'auto' }} mx="auto">
<ModalOverlay bg="blackAlpha.800" backdropFilter="blur(8px)" />
<ModalContent
maxW="1400px"
m={{ base: 0, md: 'auto' }}
mx="auto"
bg="rgba(15, 23, 42, 0.95)"
backdropFilter="blur(20px)"
border="1px solid"
borderColor="whiteAlpha.100"
>
<ModalHeader
bgGradient="linear(135deg, purple.600 0%, purple.500 50%, pink.500 100%)"
bg="rgba(15, 23, 42, 0.98)"
borderBottom="1px solid"
borderColor="whiteAlpha.100"
color="white"
position="sticky"
top={0}
@@ -624,35 +634,34 @@ const ConceptTimelineModal = ({
<Icon
as={FaChartLine}
boxSize={{ base: 4, md: 6 }}
filter="drop-shadow(0 2px 4px rgba(0,0,0,0.2))"
color="cyan.400"
/>
<Text
fontSize={{ base: 'md', md: 'xl' }}
fontWeight="bold"
textShadow="0 2px 4px rgba(0,0,0,0.2)"
color="white"
noOfLines={1}
maxW={{ base: '120px', md: 'none' }}
>
{conceptName} - 历史时间轴
</Text>
<Badge
colorScheme="yellow"
px={{ base: 2, md: 3 }}
py={1}
borderRadius="full"
fontSize={{ base: 'xs', md: 'sm' }}
boxShadow="md"
>
最近100天
</Badge>
<Badge
bg="whiteAlpha.300"
bg="purple.500"
color="white"
px={{ base: 2, md: 3 }}
py={1}
borderRadius="full"
fontSize={{ base: 'xs', md: 'sm' }}
>
最近100天
</Badge>
<Badge
bg="whiteAlpha.100"
color="whiteAlpha.800"
px={{ base: 2, md: 3 }}
py={1}
borderRadius="full"
fontSize="xs"
backdropFilter="blur(10px)"
display={{ base: 'none', sm: 'flex' }}
>
🔥 Max版功能
@@ -671,29 +680,29 @@ const ConceptTimelineModal = ({
<ModalBody
py={{ base: 2, md: 6 }}
px={{ base: 0, md: 6 }}
bg="gray.50"
bg="transparent"
css={{
'&::-webkit-scrollbar': {
width: '8px',
},
'&::-webkit-scrollbar-track': {
background: '#f1f1f1',
background: 'rgba(255,255,255,0.05)',
borderRadius: '10px',
},
'&::-webkit-scrollbar-thumb': {
background: '#c1c1c1',
background: 'rgba(255,255,255,0.2)',
borderRadius: '10px',
},
'&::-webkit-scrollbar-thumb:hover': {
background: '#a8a8a8',
background: 'rgba(255,255,255,0.3)',
},
}}
>
{loading ? (
<Center py={20}>
<VStack spacing={4}>
<Spinner size="xl" color="purple.500" thickness="4px" />
<Text color="gray.600">正在加载时间轴数据...</Text>
<Spinner size="xl" color="cyan.400" thickness="4px" />
<Text color="whiteAlpha.700">正在加载时间轴数据...</Text>
</VStack>
</Center>
) : timelineData.length > 0 ? (
@@ -715,83 +724,79 @@ const ConceptTimelineModal = ({
spacing={{ base: 1, md: 2 }}
px={{ base: 2, md: 4 }}
py={{ base: 1, md: 2 }}
bg="purple.50"
bg="whiteAlpha.100"
borderRadius="lg"
border="1px solid"
borderColor="purple.200"
boxShadow="sm"
borderColor="purple.500"
flexShrink={0}
>
<Box w={{ base: 2, md: 3 }} h={{ base: 2, md: 3 }} bg="#9F7AEA" borderRadius="full" />
<Text fontSize={{ base: 'xs', md: 'sm' }} fontWeight="medium" color="gray.700" whiteSpace="nowrap">📰 新闻</Text>
<Text fontSize={{ base: 'xs', md: 'sm' }} fontWeight="medium" color="whiteAlpha.900" whiteSpace="nowrap">📰 新闻</Text>
</HStack>
<HStack
spacing={{ base: 1, md: 2 }}
px={{ base: 2, md: 4 }}
py={{ base: 1, md: 2 }}
bg="purple.50"
bg="whiteAlpha.100"
borderRadius="lg"
border="1px solid"
borderColor="purple.300"
boxShadow="sm"
borderColor="purple.600"
flexShrink={0}
>
<Box w={{ base: 2, md: 3 }} h={{ base: 2, md: 3 }} bg="#805AD5" borderRadius="full" />
<Text fontSize={{ base: 'xs', md: 'sm' }} fontWeight="medium" color="gray.700" whiteSpace="nowrap">📊 研报</Text>
<Text fontSize={{ base: 'xs', md: 'sm' }} fontWeight="medium" color="whiteAlpha.900" whiteSpace="nowrap">📊 研报</Text>
</HStack>
<HStack
spacing={{ base: 1, md: 2 }}
px={{ base: 2, md: 4 }}
py={{ base: 1, md: 2 }}
bg="red.50"
bg="whiteAlpha.100"
borderRadius="lg"
border="1px solid"
borderColor="red.200"
boxShadow="sm"
borderColor="red.400"
flexShrink={0}
>
<Icon as={FaArrowUp} color="red.500" boxSize={{ base: 2, md: 3 }} />
<Text fontSize={{ base: 'xs', md: 'sm' }} fontWeight="medium" color="gray.700" whiteSpace="nowrap">上涨</Text>
<Icon as={FaArrowUp} color="red.400" boxSize={{ base: 2, md: 3 }} />
<Text fontSize={{ base: 'xs', md: 'sm' }} fontWeight="medium" color="whiteAlpha.900" whiteSpace="nowrap">上涨</Text>
</HStack>
<HStack
spacing={{ base: 1, md: 2 }}
px={{ base: 2, md: 4 }}
py={{ base: 1, md: 2 }}
bg="green.50"
bg="whiteAlpha.100"
borderRadius="lg"
border="1px solid"
borderColor="green.200"
boxShadow="sm"
borderColor="green.400"
flexShrink={0}
>
<Icon as={FaArrowDown} color="green.500" boxSize={{ base: 2, md: 3 }} />
<Text fontSize={{ base: 'xs', md: 'sm' }} fontWeight="medium" color="gray.700" whiteSpace="nowrap">下跌</Text>
<Icon as={FaArrowDown} color="green.400" boxSize={{ base: 2, md: 3 }} />
<Text fontSize={{ base: 'xs', md: 'sm' }} fontWeight="medium" color="whiteAlpha.900" whiteSpace="nowrap">下跌</Text>
</HStack>
<HStack
spacing={{ base: 1, md: 2 }}
px={{ base: 2, md: 4 }}
py={{ base: 1, md: 2 }}
bg="orange.50"
bg="whiteAlpha.100"
borderRadius="lg"
border="1px solid"
borderColor="orange.200"
boxShadow="sm"
borderColor="orange.400"
flexShrink={0}
>
<Text fontSize={{ base: 'xs', md: 'sm' }} fontWeight="bold">🔥</Text>
<Text fontSize={{ base: 'xs', md: 'sm' }} fontWeight="medium" color="gray.700" whiteSpace="nowrap">涨3%+</Text>
<Text fontSize={{ base: 'xs', md: 'sm' }} fontWeight="medium" color="whiteAlpha.900" whiteSpace="nowrap">涨3%+</Text>
</HStack>
</Flex>
{/* FullCalendar 日历组件 */}
<Box
height={{ base: '500px', md: '700px' }}
bg="white"
bg="rgba(15, 23, 42, 0.6)"
borderRadius={{ base: 'none', md: 'xl' }}
boxShadow={{ base: 'none', md: 'lg' }}
border="1px solid"
borderColor="whiteAlpha.100"
p={{ base: 1, md: 4 }}
sx={{
// FullCalendar 样式定制
// FullCalendar 深色主题样式定制
'.fc': {
height: '100%',
},
@@ -808,42 +813,61 @@ const ConceptTimelineModal = ({
'.fc-toolbar-title': {
fontSize: { base: '1rem', md: '1.5rem' },
fontWeight: 'bold',
color: 'purple.600',
color: 'white',
},
'.fc-button': {
backgroundColor: '#9F7AEA',
borderColor: '#9F7AEA',
backgroundColor: 'rgba(139, 92, 246, 0.6)',
borderColor: 'rgba(139, 92, 246, 0.8)',
color: 'white',
padding: { base: '4px 8px', md: '6px 12px' },
fontSize: { base: '12px', md: '14px' },
'&:hover': {
backgroundColor: '#805AD5',
borderColor: '#805AD5',
backgroundColor: 'rgba(139, 92, 246, 0.8)',
borderColor: 'rgba(139, 92, 246, 1)',
},
'&:active, &:focus': {
backgroundColor: '#6B46C1',
borderColor: '#6B46C1',
backgroundColor: 'rgba(139, 92, 246, 1)',
borderColor: 'rgba(139, 92, 246, 1)',
boxShadow: 'none',
},
},
'.fc-button-active': {
backgroundColor: '#6B46C1',
borderColor: '#6B46C1',
backgroundColor: 'rgba(139, 92, 246, 1)',
borderColor: 'rgba(139, 92, 246, 1)',
},
// 深色主题 - 表格边框和背景
'.fc-theme-standard td, .fc-theme-standard th': {
borderColor: 'rgba(255, 255, 255, 0.1)',
},
'.fc-theme-standard .fc-scrollgrid': {
borderColor: 'rgba(255, 255, 255, 0.1)',
},
'.fc-col-header-cell': {
backgroundColor: 'rgba(15, 23, 42, 0.8)',
},
'.fc-col-header-cell-cushion': {
color: 'rgba(255, 255, 255, 0.8)',
fontSize: { base: '0.75rem', md: '0.875rem' },
padding: { base: '4px 2px', md: '8px' },
},
'.fc-daygrid-day': {
cursor: 'pointer',
transition: 'all 0.2s',
backgroundColor: 'transparent',
'&:hover': {
backgroundColor: 'purple.50',
backgroundColor: 'rgba(139, 92, 246, 0.2)',
},
},
'.fc-daygrid-day-number': {
color: 'rgba(255, 255, 255, 0.9)',
padding: { base: '2px', md: '4px' },
fontSize: { base: '0.75rem', md: '0.875rem' },
},
'.fc-col-header-cell-cushion': {
fontSize: { base: '0.75rem', md: '0.875rem' },
padding: { base: '4px 2px', md: '8px' },
'.fc-day-today': {
backgroundColor: 'rgba(139, 92, 246, 0.15) !important',
},
'.fc-day-other .fc-daygrid-day-number': {
color: 'rgba(255, 255, 255, 0.4)',
},
'.fc-event': {
cursor: 'pointer',
@@ -855,12 +879,15 @@ const ConceptTimelineModal = ({
transition: 'all 0.2s',
'&:hover': {
transform: 'scale(1.05)',
boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
boxShadow: '0 2px 8px rgba(0,0,0,0.4)',
},
},
'.fc-daygrid-event-harness': {
marginBottom: { base: '1px', md: '2px' },
},
'.fc-more-link': {
color: 'rgba(255, 255, 255, 0.8)',
},
// H5 端隐藏事件文字,只显示色块
'@media (max-width: 768px)': {
'.fc-event-title': {
@@ -898,24 +925,23 @@ const ConceptTimelineModal = ({
<Center py={24}>
<VStack
spacing={6}
bg="white"
bg="rgba(15, 23, 42, 0.6)"
p={12}
borderRadius="2xl"
boxShadow="xl"
border="2px dashed"
borderColor="purple.200"
borderColor="whiteAlpha.200"
>
<Icon
as={FaHistory}
boxSize={24}
color="purple.300"
opacity={0.5}
color="purple.400"
opacity={0.6}
/>
<VStack spacing={2}>
<Text fontSize="2xl" fontWeight="bold" color="gray.700">
<Text fontSize="2xl" fontWeight="bold" color="white">
暂无历史数据
</Text>
<Text fontSize="md" color="gray.500">
<Text fontSize="md" color="whiteAlpha.600">
该概念在最近100天内没有相关事件记录
</Text>
</VStack>
@@ -941,45 +967,43 @@ const ConceptTimelineModal = ({
size="4xl"
scrollBehavior="inside"
>
<ModalOverlay />
<ModalContent borderRadius="2xl" overflow="hidden">
<ModalOverlay bg="blackAlpha.800" backdropFilter="blur(8px)" />
<ModalContent
borderRadius="2xl"
overflow="hidden"
bg="rgba(15, 23, 42, 0.95)"
border="1px solid"
borderColor="whiteAlpha.100"
>
<ModalHeader
bgGradient="linear(135deg, purple.600 0%, purple.500 50%, pink.500 100%)"
bg="rgba(15, 23, 42, 0.98)"
borderBottom="1px solid"
borderColor="whiteAlpha.100"
color="white"
py={6}
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))"
color="cyan.400"
/>
<Text fontSize="xl" fontWeight="bold" textShadow="0 2px 4px rgba(0,0,0,0.2)">
<Text fontSize="xl" fontWeight="bold" color="white">
{formatDateDisplay(selectedDate)}
</Text>
</HStack>
{selectedDateData.price && (
<HStack spacing={4} fontSize="sm" flexWrap="wrap">
<Badge
colorScheme={getPriceInfo(selectedDateData.price).color}
variant="solid"
bg={getPriceInfo(selectedDateData.price).color === 'red' ? 'red.500' :
getPriceInfo(selectedDateData.price).color === 'green' ? 'green.500' : 'whiteAlpha.300'}
color="white"
px={4}
py={2}
borderRadius="full"
fontSize="md"
boxShadow="md"
>
<HStack spacing={2}>
{getPriceInfo(selectedDateData.price).icon && (
@@ -996,14 +1020,13 @@ const ConceptTimelineModal = ({
{selectedDateData.price.stock_count && (
<HStack
spacing={1}
bg="whiteAlpha.300"
bg="whiteAlpha.100"
px={3}
py={1}
borderRadius="full"
backdropFilter="blur(10px)"
>
<Text fontWeight="medium">📊 统计股票:</Text>
<Text fontWeight="bold">{selectedDateData.price.stock_count} </Text>
<Text fontWeight="medium" color="whiteAlpha.800">📊 统计股票:</Text>
<Text fontWeight="bold" color="white">{selectedDateData.price.stock_count} </Text>
</HStack>
)}
</HStack>
@@ -1012,7 +1035,7 @@ const ConceptTimelineModal = ({
</ModalHeader>
<ModalCloseButton color="white" />
<ModalBody py={6}>
<ModalBody py={6} bg="transparent">
{selectedDateData.events && selectedDateData.events.length > 0 ? (
<VStack align="stretch" spacing={6}>
{/* 统计卡片 */}
@@ -1020,35 +1043,33 @@ const ConceptTimelineModal = ({
<Box
px={6}
py={3}
bg="blue.50"
bg="whiteAlpha.100"
borderRadius="lg"
boxShadow="sm"
border="1px solid"
borderColor="blue.200"
borderColor="blue.500"
>
<HStack spacing={2}>
<Icon as={FaNewspaper} color="blue.500" boxSize={5} />
<Text fontWeight="bold" fontSize="lg" color="blue.700">
<Icon as={FaNewspaper} color="blue.400" boxSize={5} />
<Text fontWeight="bold" fontSize="lg" color="blue.300">
{selectedDateData.events.filter(e => e.type === 'news').length}
</Text>
<Text fontSize="sm" color="gray.600">条新闻</Text>
<Text fontSize="sm" color="whiteAlpha.700">条新闻</Text>
</HStack>
</Box>
<Box
px={6}
py={3}
bg="green.50"
bg="whiteAlpha.100"
borderRadius="lg"
boxShadow="sm"
border="1px solid"
borderColor="green.200"
borderColor="green.500"
>
<HStack spacing={2}>
<Icon as={FaFileAlt} color="green.500" boxSize={5} />
<Text fontWeight="bold" fontSize="lg" color="green.700">
<Icon as={FaFileAlt} color="green.400" boxSize={5} />
<Text fontWeight="bold" fontSize="lg" color="green.300">
{selectedDateData.events.filter(e => e.type === 'report').length}
</Text>
<Text fontSize="sm" color="gray.600">篇研报</Text>
<Text fontSize="sm" color="whiteAlpha.700">篇研报</Text>
</HStack>
</Box>
</HStack>
@@ -1059,58 +1080,38 @@ const ConceptTimelineModal = ({
<Box
key={eventIdx}
p={5}
bgGradient={
event.type === 'news'
? 'linear(to-br, blue.50, blue.100)'
: 'linear(to-br, green.50, green.100)'
}
bg="whiteAlpha.50"
borderRadius="xl"
borderLeft="5px solid"
borderLeftColor={event.type === 'news' ? 'blue.500' : 'green.500'}
boxShadow="sm"
borderLeftColor={event.type === 'news' ? 'blue.400' : 'green.400'}
border="1px solid"
borderColor="whiteAlpha.100"
_hover={{
transform: 'translateX(6px) translateY(-2px)',
boxShadow: 'lg',
borderLeftColor: event.type === 'news' ? 'blue.600' : 'green.600',
bg: 'whiteAlpha.100',
borderLeftColor: event.type === 'news' ? 'blue.300' : 'green.300',
}}
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,
}}
>
<VStack align="start" spacing={3}>
<HStack spacing={2} flexWrap="wrap">
<Badge
colorScheme={event.type === 'news' ? 'blue' : 'green'}
variant="solid"
bg={event.type === 'news' ? 'blue.500' : 'green.500'}
color="white"
fontSize="sm"
px={3}
py={1}
borderRadius="full"
boxShadow="sm"
>
{event.type === 'news' ? '📰 新闻' : '📊 研报'}
</Badge>
{event.publisher && (
<Badge
colorScheme="purple"
variant="subtle"
bg="purple.500"
color="white"
fontSize="xs"
px={2}
py={1}
@@ -1121,8 +1122,8 @@ const ConceptTimelineModal = ({
)}
{event.rating && (
<Badge
colorScheme="orange"
variant="solid"
bg="orange.500"
color="white"
fontSize="xs"
px={2}
py={1}
@@ -1133,8 +1134,8 @@ const ConceptTimelineModal = ({
)}
{event.security_name && (
<Badge
colorScheme="cyan"
variant="subtle"
bg="cyan.600"
color="white"
fontSize="xs"
px={2}
py={1}
@@ -1148,7 +1149,7 @@ const ConceptTimelineModal = ({
<Text
fontWeight="bold"
fontSize="lg"
color="gray.800"
color="white"
lineHeight="1.4"
>
{event.title}
@@ -1156,7 +1157,7 @@ const ConceptTimelineModal = ({
<Text
fontSize="sm"
color="gray.600"
color="whiteAlpha.700"
noOfLines={3}
lineHeight="1.6"
>
@@ -1166,8 +1167,8 @@ const ConceptTimelineModal = ({
<HStack spacing={4} w="100%" justify="space-between" align="center" mt={1}>
{event.time && (
<HStack spacing={1}>
<Icon as={FaClock} color="gray.500" boxSize={3} />
<Text fontSize="xs" color="gray.500" fontWeight="medium">
<Icon as={FaClock} color="whiteAlpha.500" boxSize={3} />
<Text fontSize="xs" color="whiteAlpha.500" fontWeight="medium">
{formatDateTime(event.time)}
</Text>
</HStack>
@@ -1225,24 +1226,24 @@ const ConceptTimelineModal = ({
<Center py={16}>
<VStack
spacing={5}
bg="gray.50"
bg="whiteAlpha.50"
p={10}
borderRadius="2xl"
border="2px dashed"
borderColor="gray.300"
borderColor="whiteAlpha.200"
>
<Icon
as={FaHistory}
boxSize={20}
color="gray.400"
color="purple.400"
opacity={0.6}
/>
<VStack spacing={2}>
<Text fontSize="xl" fontWeight="bold" color="gray.600">
<Text fontSize="xl" fontWeight="bold" color="white">
当日无新闻或研报
</Text>
{selectedDateData.price && (
<Text fontSize="md" color="gray.500">
<Text fontSize="md" color="whiteAlpha.600">
仅有涨跌幅数据
</Text>
)}
@@ -1253,20 +1254,20 @@ const ConceptTimelineModal = ({
</ModalBody>
<ModalFooter
borderTop="2px solid"
borderColor="gray.100"
bg="gray.50"
borderTop="1px solid"
borderColor="whiteAlpha.100"
bg="rgba(15, 23, 42, 0.98)"
py={4}
>
<Button
colorScheme="purple"
bg="purple.500"
color="white"
size="lg"
px={8}
onClick={onDateDetailClose}
boxShadow="md"
_hover={{
bg: 'purple.400',
transform: 'translateY(-2px)',
boxShadow: 'lg',
}}
transition="all 0.2s"
>
@@ -1285,30 +1286,30 @@ const ConceptTimelineModal = ({
size="4xl"
scrollBehavior="inside"
>
<ModalOverlay />
<ModalContent>
<ModalHeader bg="green.500" color="white">
<ModalOverlay bg="blackAlpha.800" backdropFilter="blur(8px)" />
<ModalContent bg="rgba(15, 23, 42, 0.95)" border="1px solid" borderColor="whiteAlpha.100">
<ModalHeader bg="rgba(15, 23, 42, 0.98)" borderBottom="1px solid" borderColor="whiteAlpha.100" color="white">
<VStack align="start" spacing={1}>
<Text fontSize="lg">{selectedReport?.title}</Text>
<HStack spacing={3} fontSize="sm" opacity={0.9}>
<Text fontSize="lg" color="white">{selectedReport?.title}</Text>
<HStack spacing={3} fontSize="sm">
{selectedReport?.publisher && (
<Badge colorScheme="whiteAlpha" variant="solid">
<Badge bg="green.500" color="white">
{selectedReport.publisher}
</Badge>
)}
{selectedReport?.author && (
<Text>{selectedReport.author}</Text>
<Text color="whiteAlpha.700">{selectedReport.author}</Text>
)}
{selectedReport?.time && (
<Text>{formatDateTime(selectedReport.time)}</Text>
<Text color="whiteAlpha.700">{formatDateTime(selectedReport.time)}</Text>
)}
{selectedReport?.rating && (
<Badge colorScheme="orange" variant="solid">
<Badge bg="orange.500" color="white">
{selectedReport.rating}
</Badge>
)}
{selectedReport?.security_name && (
<Badge colorScheme="cyan" variant="solid">
<Badge bg="cyan.600" color="white">
{selectedReport.security_name}
</Badge>
)}
@@ -1319,37 +1320,38 @@ const ConceptTimelineModal = ({
<ModalBody py={6}>
<Box
bg="gray.50"
bg="whiteAlpha.50"
p={6}
borderRadius="md"
border="1px solid"
borderColor="gray.200"
borderColor="whiteAlpha.100"
>
<Text
whiteSpace="pre-wrap"
fontSize="sm"
lineHeight="tall"
color="gray.700"
color="whiteAlpha.800"
>
{selectedReport?.content || '暂无内容'}
</Text>
</Box>
</ModalBody>
<ModalFooter borderTop="1px solid" borderColor="gray.200">
<ModalFooter borderTop="1px solid" borderColor="whiteAlpha.100" bg="rgba(15, 23, 42, 0.98)">
<HStack spacing={3}>
{selectedReport?.content_url && (
<Button
size="sm"
colorScheme="blue"
variant="outline"
bg="whiteAlpha.100"
color="white"
leftIcon={<ExternalLinkIcon />}
onClick={() => window.open(selectedReport.content_url, '_blank')}
_hover={{ bg: 'whiteAlpha.200' }}
>
查看原文
</Button>
)}
<Button size="sm" colorScheme="green" onClick={() => setIsReportModalOpen(false)}>
<Button size="sm" bg="green.500" color="white" onClick={() => setIsReportModalOpen(false)} _hover={{ bg: 'green.400' }}>
关闭
</Button>
</HStack>
@@ -1366,22 +1368,22 @@ const ConceptTimelineModal = ({
size="4xl"
scrollBehavior="inside"
>
<ModalOverlay />
<ModalContent>
<ModalHeader bg="blue.500" color="white">
<ModalOverlay bg="blackAlpha.800" backdropFilter="blur(8px)" />
<ModalContent bg="rgba(15, 23, 42, 0.95)" border="1px solid" borderColor="whiteAlpha.100">
<ModalHeader bg="rgba(15, 23, 42, 0.98)" borderBottom="1px solid" borderColor="whiteAlpha.100" color="white">
<VStack align="start" spacing={1}>
<Text fontSize="lg">{selectedNews?.title}</Text>
<HStack spacing={3} fontSize="sm" opacity={0.9}>
<Text fontSize="lg" color="white">{selectedNews?.title}</Text>
<HStack spacing={3} fontSize="sm">
{selectedNews?.source && (
<Badge
colorScheme={selectedNews.source === 'zsxq' ? 'purple' : 'whiteAlpha'}
variant="solid"
bg={selectedNews.source === 'zsxq' ? 'purple.500' : 'blue.500'}
color="white"
>
{selectedNews.source === 'zsxq' ? '知识星球' : selectedNews.source}
</Badge>
)}
{selectedNews?.time && (
<Text>{formatDateTime(selectedNews.time)}</Text>
<Text color="whiteAlpha.700">{formatDateTime(selectedNews.time)}</Text>
)}
</HStack>
</VStack>
@@ -1390,38 +1392,39 @@ const ConceptTimelineModal = ({
<ModalBody py={6}>
<Box
bg="gray.50"
bg="whiteAlpha.50"
p={6}
borderRadius="md"
border="1px solid"
borderColor="gray.200"
borderColor="whiteAlpha.100"
>
<Text
whiteSpace="pre-wrap"
fontSize="sm"
lineHeight="tall"
color="gray.700"
color="whiteAlpha.800"
>
{selectedNews?.content || '暂无内容'}
</Text>
</Box>
</ModalBody>
<ModalFooter borderTop="1px solid" borderColor="gray.200">
<ModalFooter borderTop="1px solid" borderColor="whiteAlpha.100" bg="rgba(15, 23, 42, 0.98)">
<HStack spacing={3}>
{/* zsxq来源不显示查看原文按钮 */}
{selectedNews?.url && selectedNews?.source !== 'zsxq' && (
<Button
size="sm"
colorScheme="blue"
variant="outline"
bg="whiteAlpha.100"
color="white"
leftIcon={<ExternalLinkIcon />}
onClick={() => window.open(selectedNews.url, '_blank')}
_hover={{ bg: 'whiteAlpha.200' }}
>
查看原文
</Button>
)}
<Button size="sm" colorScheme="blue" onClick={() => setIsNewsModalOpen(false)}>
<Button size="sm" bg="blue.500" color="white" onClick={() => setIsNewsModalOpen(false)} _hover={{ bg: 'blue.400' }}>
关闭
</Button>
</HStack>

View File

@@ -73,9 +73,10 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
const [useCustomRange, setUseCustomRange] = useState(false);
const toast = useToast();
const bg = useColorModeValue('white', 'gray.800');
const cardBg = useColorModeValue('gray.50', 'gray.700');
const borderColor = useColorModeValue('gray.200', 'gray.600');
// 深色主题颜色(固定使用深色模式)
const bg = 'rgba(15, 23, 42, 0.8)';
const cardBg = 'rgba(15, 23, 42, 0.6)';
const borderColor = 'whiteAlpha.100';
// 获取统计数据
const fetchStatsData = async (days = timeRange, startDate = null, endDate = null) => {
@@ -250,21 +251,21 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
return 'gray.500';
};
// 统计卡片组件 - 美化
// 统计卡片组件 - 深色主题
const StatsCard = ({ title, icon, color, data, renderItem, isLoading }) => (
<Box p={4}>
{isLoading ? (
<VStack spacing={3} align="stretch">
{[1, 2, 3, 4, 5].map((i) => (
<HStack key={i} justify="space-between" p={3} bg="gray.50" borderRadius="lg">
<HStack key={i} justify="space-between" p={3} bg="whiteAlpha.50" borderRadius="lg">
<HStack spacing={2} flex={1}>
<Skeleton height="20px" width="20px" borderRadius="full" />
<Skeleton height="20px" width="20px" borderRadius="full" startColor="whiteAlpha.100" endColor="whiteAlpha.200" />
<VStack align="start" spacing={1} flex={1}>
<Skeleton height="14px" width="80%" />
<Skeleton height="12px" width="60%" />
<Skeleton height="14px" width="80%" startColor="whiteAlpha.100" endColor="whiteAlpha.200" />
<Skeleton height="12px" width="60%" startColor="whiteAlpha.100" endColor="whiteAlpha.200" />
</VStack>
</HStack>
<Skeleton height="20px" width="50px" borderRadius="md" />
<Skeleton height="20px" width="50px" borderRadius="md" startColor="whiteAlpha.100" endColor="whiteAlpha.200" />
</HStack>
))}
</VStack>
@@ -275,15 +276,15 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
width: '4px',
},
'&::-webkit-scrollbar-track': {
background: '#f1f1f1',
background: 'rgba(255,255,255,0.05)',
borderRadius: '10px',
},
'&::-webkit-scrollbar-thumb': {
background: '#c1c1c1',
background: 'rgba(255,255,255,0.2)',
borderRadius: '10px',
},
'&::-webkit-scrollbar-thumb:hover': {
background: '#a8a8a8',
background: 'rgba(255,255,255,0.3)',
},
}}
>
@@ -309,14 +310,14 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
align="center"
p={3}
borderRadius="xl"
bg={index < 3 ? 'red.50' : 'gray.50'}
bg={index < 3 ? 'rgba(239, 68, 68, 0.15)' : 'whiteAlpha.50'}
border="1px solid"
borderColor={index < 3 ? 'red.100' : 'gray.200'}
borderColor={index < 3 ? 'red.500' : 'whiteAlpha.100'}
_hover={{
transform: 'translateY(-1px)',
shadow: 'md',
cursor: 'pointer',
bg: index < 3 ? 'red.100' : 'gray.100'
bg: index < 3 ? 'rgba(239, 68, 68, 0.25)' : 'whiteAlpha.100'
}}
transition="all 0.2s"
onClick={() => onConceptClick?.(null, item.name)}
@@ -324,8 +325,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
<HStack spacing={3} flex={1}>
<Box position="relative">
<Badge
colorScheme={index === 0 ? 'yellow' : index === 1 ? 'orange' : index === 2 ? 'red' : 'gray'}
variant="solid"
bg={index === 0 ? 'yellow.500' : index === 1 ? 'orange.500' : index === 2 ? 'red.500' : 'whiteAlpha.200'}
color="white"
borderRadius="full"
minW="24px"
h="24px"
@@ -344,16 +345,16 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
position="absolute"
top="-8px"
right="-8px"
color="yellow.500"
color="yellow.400"
boxSize={3}
/>
)}
</Box>
<VStack align="start" spacing={0} flex={1}>
<Text fontSize="sm" fontWeight="bold" noOfLines={1} color="gray.800">
<Text fontSize="sm" fontWeight="bold" noOfLines={1} color="white">
{item.name}
</Text>
<HStack spacing={2} fontSize="xs" color="gray.600">
<HStack spacing={2} fontSize="xs" color="whiteAlpha.600">
<HStack spacing={1}>
<Icon as={FaChartLine} boxSize={2.5} />
<Text>{item.stock_count}</Text>
@@ -367,8 +368,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
</VStack>
</HStack>
<Badge
colorScheme="red"
variant="solid"
bg="red.500"
color="white"
borderRadius="lg"
fontSize="xs"
fontWeight="bold"
@@ -392,22 +393,22 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
align="center"
p={3}
borderRadius="xl"
bg={index < 3 ? 'green.50' : 'gray.50'}
bg={index < 3 ? 'rgba(34, 197, 94, 0.15)' : 'whiteAlpha.50'}
border="1px solid"
borderColor={index < 3 ? 'green.100' : 'gray.200'}
borderColor={index < 3 ? 'green.500' : 'whiteAlpha.100'}
_hover={{
transform: 'translateY(-1px)',
shadow: 'md',
cursor: 'pointer',
bg: index < 3 ? 'green.100' : 'gray.100'
bg: index < 3 ? 'rgba(34, 197, 94, 0.25)' : 'whiteAlpha.100'
}}
transition="all 0.2s"
onClick={() => onConceptClick?.(null, item.name)}
>
<HStack spacing={3} flex={1}>
<Badge
colorScheme={index < 3 ? 'green' : 'gray'}
variant="solid"
bg={index < 3 ? 'green.500' : 'whiteAlpha.200'}
color="white"
borderRadius="full"
minW="24px"
h="24px"
@@ -421,10 +422,10 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
{index + 1}
</Badge>
<VStack align="start" spacing={0} flex={1}>
<Text fontSize="sm" fontWeight="bold" noOfLines={1} color="gray.800">
<Text fontSize="sm" fontWeight="bold" noOfLines={1} color="white">
{item.name}
</Text>
<HStack spacing={2} fontSize="xs" color="gray.600">
<HStack spacing={2} fontSize="xs" color="whiteAlpha.600">
<HStack spacing={1}>
<Icon as={FaChartLine} boxSize={2.5} />
<Text>{item.stock_count}</Text>
@@ -438,8 +439,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
</VStack>
</HStack>
<Badge
colorScheme="green"
variant="solid"
bg="green.500"
color="white"
borderRadius="lg"
fontSize="xs"
fontWeight="bold"
@@ -463,14 +464,14 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
align="center"
p={3}
borderRadius="xl"
bg={index < 3 ? 'orange.50' : 'gray.50'}
bg={index < 3 ? 'rgba(251, 146, 60, 0.15)' : 'whiteAlpha.50'}
border="1px solid"
borderColor={index < 3 ? 'orange.100' : 'gray.200'}
borderColor={index < 3 ? 'orange.500' : 'whiteAlpha.100'}
_hover={{
transform: 'translateY(-1px)',
shadow: 'md',
cursor: 'pointer',
bg: index < 3 ? 'orange.100' : 'gray.100'
bg: index < 3 ? 'rgba(251, 146, 60, 0.25)' : 'whiteAlpha.100'
}}
transition="all 0.2s"
onClick={() => onConceptClick?.(null, item.name)}
@@ -478,8 +479,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
<HStack spacing={3} flex={1}>
<Box position="relative">
<Badge
colorScheme={index === 0 ? 'orange' : index < 3 ? 'yellow' : 'gray'}
variant="solid"
bg={index === 0 ? 'orange.500' : index < 3 ? 'yellow.500' : 'whiteAlpha.200'}
color="white"
borderRadius="full"
minW="24px"
h="24px"
@@ -498,16 +499,16 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
position="absolute"
top="-8px"
right="-8px"
color="orange.500"
color="orange.400"
boxSize={3}
/>
)}
</Box>
<VStack align="start" spacing={0} flex={1}>
<Text fontSize="sm" fontWeight="bold" noOfLines={1} color="gray.800">
<Text fontSize="sm" fontWeight="bold" noOfLines={1} color="white">
{item.name}
</Text>
<HStack spacing={2} fontSize="xs" color="gray.600">
<HStack spacing={2} fontSize="xs" color="whiteAlpha.600">
<HStack spacing={1}>
<Icon as={FaNewspaper} boxSize={2.5} />
<Text>{item.news_count}</Text>
@@ -521,8 +522,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
</VStack>
</HStack>
<Badge
colorScheme="orange"
variant="solid"
bg="orange.500"
color="white"
borderRadius="lg"
fontSize="xs"
fontWeight="bold"
@@ -546,14 +547,14 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
align="center"
p={3}
borderRadius="xl"
bg={index < 3 ? 'purple.50' : 'gray.50'}
bg={index < 3 ? 'rgba(168, 85, 247, 0.15)' : 'whiteAlpha.50'}
border="1px solid"
borderColor={index < 3 ? 'purple.100' : 'gray.200'}
borderColor={index < 3 ? 'purple.500' : 'whiteAlpha.100'}
_hover={{
transform: 'translateY(-1px)',
shadow: 'md',
cursor: 'pointer',
bg: index < 3 ? 'purple.100' : 'gray.100'
bg: index < 3 ? 'rgba(168, 85, 247, 0.25)' : 'whiteAlpha.100'
}}
transition="all 0.2s"
onClick={() => onConceptClick?.(null, item.name)}
@@ -561,8 +562,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
<HStack spacing={3} flex={1}>
<Box position="relative">
<Badge
colorScheme={index < 3 ? 'purple' : 'gray'}
variant="solid"
bg={index < 3 ? 'purple.500' : 'whiteAlpha.200'}
color="white"
borderRadius="full"
minW="24px"
h="24px"
@@ -581,23 +582,23 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
position="absolute"
top="-8px"
right="-8px"
color="purple.500"
color="purple.400"
boxSize={3}
/>
)}
</Box>
<VStack align="start" spacing={0} flex={1}>
<Text fontSize="sm" fontWeight="bold" noOfLines={1} color="gray.800">
<Text fontSize="sm" fontWeight="bold" noOfLines={1} color="white">
{item.name}
</Text>
<Text fontSize="xs" color="gray.600">
<Text fontSize="xs" color="whiteAlpha.600">
均幅 {formatChange(item.avg_change)}
</Text>
</VStack>
</HStack>
<Badge
colorScheme="purple"
variant="solid"
bg="purple.500"
color="white"
borderRadius="lg"
fontSize="xs"
fontWeight="bold"
@@ -621,14 +622,14 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
align="center"
p={3}
borderRadius="xl"
bg={index < 3 ? 'cyan.50' : 'gray.50'}
bg={index < 3 ? 'rgba(34, 211, 238, 0.15)' : 'whiteAlpha.50'}
border="1px solid"
borderColor={index < 3 ? 'cyan.100' : 'gray.200'}
borderColor={index < 3 ? 'cyan.500' : 'whiteAlpha.100'}
_hover={{
transform: 'translateY(-1px)',
shadow: 'md',
cursor: 'pointer',
bg: index < 3 ? 'cyan.100' : 'gray.100'
bg: index < 3 ? 'rgba(34, 211, 238, 0.25)' : 'whiteAlpha.100'
}}
transition="all 0.2s"
onClick={() => onConceptClick?.(null, item.name)}
@@ -636,8 +637,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
<HStack spacing={3} flex={1}>
<Box position="relative">
<Badge
colorScheme={index === 0 ? 'cyan' : index < 3 ? 'blue' : 'gray'}
variant="solid"
bg={index === 0 ? 'cyan.500' : index < 3 ? 'blue.500' : 'whiteAlpha.200'}
color="white"
borderRadius="full"
minW="24px"
h="24px"
@@ -656,23 +657,23 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
position="absolute"
top="-8px"
right="-8px"
color="cyan.500"
color="cyan.400"
boxSize={3}
/>
)}
</Box>
<VStack align="start" spacing={0} flex={1}>
<Text fontSize="sm" fontWeight="bold" noOfLines={1} color="gray.800">
<Text fontSize="sm" fontWeight="bold" noOfLines={1} color="white">
{item.name}
</Text>
<Text fontSize="xs" color="gray.600">
<Text fontSize="xs" color="whiteAlpha.600">
累计 {formatChange(item.total_change)}
</Text>
</VStack>
</HStack>
<Badge
colorScheme="cyan"
variant="solid"
bg="cyan.500"
color="white"
borderRadius="lg"
fontSize="xs"
fontWeight="bold"
@@ -689,14 +690,17 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
return (
<Box>
{/* 顶部标题卡片 */}
{/* 顶部标题卡片 - 深色玻璃态 */}
<Box
bg="linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
bg="rgba(15, 23, 42, 0.8)"
backdropFilter="blur(20px)"
p={4}
borderRadius="xl"
mb={4}
position="relative"
overflow="hidden"
border="1px solid"
borderColor="whiteAlpha.100"
>
{/* 背景装饰 */}
<Box
@@ -706,8 +710,9 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
width="80px"
height="80px"
borderRadius="full"
bg="whiteAlpha.200"
filter="blur(10px)"
bg="purple.500"
opacity={0.2}
filter="blur(20px)"
/>
<Box
position="absolute"
@@ -716,15 +721,16 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
width="60px"
height="60px"
borderRadius="full"
bg="whiteAlpha.100"
filter="blur(8px)"
bg="cyan.500"
opacity={0.15}
filter="blur(15px)"
/>
<VStack align="start" spacing={3} position="relative" w="full">
<Flex justify="space-between" align="center" w="full">
<HStack spacing={2}>
<Box p={2} bg="whiteAlpha.200" borderRadius="lg">
<Icon as={FaChartLine} color="white" boxSize={4} />
<Box p={2} bg="whiteAlpha.100" borderRadius="lg" border="1px solid" borderColor="cyan.500">
<Icon as={FaChartLine} color="cyan.400" boxSize={4} />
</Box>
<VStack align="start" spacing={0}>
<Heading size="sm" color="white" fontWeight="bold">
@@ -868,8 +874,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
</VStack>
</Box>
{/* 主内容卡片 */}
<Box bg={bg} borderRadius="xl" border="1px" borderColor={borderColor} shadow="sm" overflow="hidden">
{/* 主内容卡片 - 深色玻璃态 */}
<Box bg={bg} backdropFilter="blur(20px)" borderRadius="xl" border="1px" borderColor={borderColor} overflow="hidden">
<Tabs
index={activeTab}
onChange={(index) => {
@@ -882,7 +888,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
size="sm"
>
<TabList
bg="gray.50"
bg="rgba(15, 23, 42, 0.6)"
borderBottom="1px"
borderColor={borderColor}
overflowX="auto"
@@ -902,6 +908,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
px={3}
py={3}
whiteSpace="nowrap"
color="whiteAlpha.700"
_selected={{
bg: `${tab.color}.500`,
color: 'white',
@@ -917,7 +924,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
bg: `${tab.color}.500`,
}
}}
_hover={{ bg: `${tab.color}.50` }}
_hover={{ bg: 'whiteAlpha.100', color: 'white' }}
transition="all 0.2s"
>
<HStack spacing={1}>