update pay ui
This commit is contained in:
@@ -26,15 +26,22 @@ import { FaTable } from 'react-icons/fa';
|
|||||||
import marketService from '@services/marketService';
|
import marketService from '@services/marketService';
|
||||||
import { logger } from '@utils/logger';
|
import { logger } from '@utils/logger';
|
||||||
|
|
||||||
// 股票信息类型
|
// 股票信息类型 - 兼容新旧API格式
|
||||||
interface StockInfo {
|
interface StockInfo {
|
||||||
stock_code: string;
|
stock_code?: string;
|
||||||
stock_name: string;
|
stock_name?: string;
|
||||||
|
code?: string; // 新API格式
|
||||||
|
name?: string; // 新API格式
|
||||||
reason?: string;
|
reason?: string;
|
||||||
change_pct?: number;
|
change_pct?: number;
|
||||||
[key: string]: unknown;
|
[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 {
|
export interface ConceptInfo {
|
||||||
concept_id?: string;
|
concept_id?: string;
|
||||||
@@ -69,9 +76,12 @@ const ConceptStocksModal: React.FC<ConceptStocksModalProps> = ({
|
|||||||
const [stockMarketData, setStockMarketData] = useState<Record<string, MarketData>>({});
|
const [stockMarketData, setStockMarketData] = useState<Record<string, MarketData>>({});
|
||||||
const [loadingStockData, setLoadingStockData] = useState(false);
|
const [loadingStockData, setLoadingStockData] = useState(false);
|
||||||
|
|
||||||
// 颜色主题
|
// 深色主题颜色
|
||||||
const cardBg = useColorModeValue('white', '#1a1a1a');
|
const cardBg = 'rgba(15, 23, 42, 0.95)';
|
||||||
const hoverBg = useColorModeValue('gray.50', '#2a2a2a');
|
const hoverBg = 'whiteAlpha.100';
|
||||||
|
const textColor = 'white';
|
||||||
|
const subTextColor = 'whiteAlpha.700';
|
||||||
|
const borderColor = 'whiteAlpha.100';
|
||||||
|
|
||||||
// 响应式配置 - 添加 fallback 避免首次渲染时返回 undefined 导致弹窗异常
|
// 响应式配置 - 添加 fallback 避免首次渲染时返回 undefined 导致弹窗异常
|
||||||
const isMobile = useBreakpointValue({ base: true, md: false }, { fallback: 'md' });
|
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) {
|
for (let i = 0; i < stocks.length; i += batchSize) {
|
||||||
const batch = stocks.slice(i, i + batchSize);
|
const batch = stocks.slice(i, i + batchSize);
|
||||||
const promises = batch.map(async (stock) => {
|
const promises = batch.map(async (stock) => {
|
||||||
if (!stock.stock_code) return null;
|
const stockCode = getStockCode(stock);
|
||||||
const seccode = stock.stock_code.substring(0, 6);
|
if (!stockCode) return null;
|
||||||
|
const seccode = stockCode.substring(0, 6);
|
||||||
try {
|
try {
|
||||||
const response = await marketService.getTradeData(seccode, 1);
|
const response = await marketService.getTradeData(seccode, 1);
|
||||||
if (response.success && response.data?.length > 0) {
|
if (response.success && response.data?.length > 0) {
|
||||||
const latestData = response.data[response.data.length - 1];
|
const latestData = response.data[response.data.length - 1];
|
||||||
return { stock_code: stock.stock_code, ...latestData };
|
return { stock_code: stockCode, ...latestData };
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn('ConceptStocksModal', '获取股票行情失败', { stockCode: seccode });
|
logger.warn('ConceptStocksModal', '获取股票行情失败', { stockCode: seccode });
|
||||||
@@ -168,16 +179,18 @@ const ConceptStocksModal: React.FC<ConceptStocksModalProps> = ({
|
|||||||
<Table variant="simple" size="sm" minW={isMobile ? '600px' : undefined}>
|
<Table variant="simple" size="sm" minW={isMobile ? '600px' : undefined}>
|
||||||
<Thead position="sticky" top={0} bg={cardBg} zIndex={1}>
|
<Thead position="sticky" top={0} bg={cardBg} zIndex={1}>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th whiteSpace="nowrap">股票名称</Th>
|
<Th whiteSpace="nowrap" color={subTextColor} borderColor={borderColor}>股票名称</Th>
|
||||||
<Th whiteSpace="nowrap">股票代码</Th>
|
<Th whiteSpace="nowrap" color={subTextColor} borderColor={borderColor}>股票代码</Th>
|
||||||
<Th isNumeric whiteSpace="nowrap">现价</Th>
|
<Th isNumeric whiteSpace="nowrap" color={subTextColor} borderColor={borderColor}>现价</Th>
|
||||||
<Th isNumeric whiteSpace="nowrap">当日涨跌幅</Th>
|
<Th isNumeric whiteSpace="nowrap" color={subTextColor} borderColor={borderColor}>当日涨跌幅</Th>
|
||||||
<Th whiteSpace="nowrap" minW="200px">板块原因</Th>
|
<Th whiteSpace="nowrap" minW="200px" color={subTextColor} borderColor={borderColor}>板块原因</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</Thead>
|
</Thead>
|
||||||
<Tbody>
|
<Tbody>
|
||||||
{stocks.map((stock, idx) => {
|
{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;
|
const changePercent = marketData?.change_percent;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -185,15 +198,15 @@ const ConceptStocksModal: React.FC<ConceptStocksModalProps> = ({
|
|||||||
key={idx}
|
key={idx}
|
||||||
_hover={{ bg: hoverBg }}
|
_hover={{ bg: hoverBg }}
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
onClick={() => handleStockClick(stock.stock_code)}
|
onClick={() => handleStockClick(stockCode)}
|
||||||
>
|
>
|
||||||
<Td color="blue.500" fontWeight="medium">
|
<Td color="cyan.400" fontWeight="medium" borderColor={borderColor}>
|
||||||
{stock.stock_name}
|
{stockName}
|
||||||
</Td>
|
</Td>
|
||||||
<Td>{stock.stock_code}</Td>
|
<Td color={subTextColor} borderColor={borderColor}>{stockCode}</Td>
|
||||||
<Td isNumeric>
|
<Td isNumeric color={textColor} borderColor={borderColor}>
|
||||||
{loadingStockData ? (
|
{loadingStockData ? (
|
||||||
<Spinner size="xs" />
|
<Spinner size="xs" color="purple.400" />
|
||||||
) : marketData?.close ? (
|
) : marketData?.close ? (
|
||||||
`¥${marketData.close.toFixed(2)}`
|
`¥${marketData.close.toFixed(2)}`
|
||||||
) : (
|
) : (
|
||||||
@@ -203,23 +216,24 @@ const ConceptStocksModal: React.FC<ConceptStocksModalProps> = ({
|
|||||||
<Td
|
<Td
|
||||||
isNumeric
|
isNumeric
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
|
borderColor={borderColor}
|
||||||
color={
|
color={
|
||||||
changePercent && changePercent > 0
|
changePercent && changePercent > 0
|
||||||
? 'red.500'
|
? 'red.400'
|
||||||
: changePercent && changePercent < 0
|
: changePercent && changePercent < 0
|
||||||
? 'green.500'
|
? 'green.400'
|
||||||
: 'gray.500'
|
: 'whiteAlpha.500'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{loadingStockData ? (
|
{loadingStockData ? (
|
||||||
<Spinner size="xs" />
|
<Spinner size="xs" color="purple.400" />
|
||||||
) : changePercent !== undefined ? (
|
) : changePercent !== undefined ? (
|
||||||
`${changePercent > 0 ? '+' : ''}${changePercent.toFixed(2)}%`
|
`${changePercent > 0 ? '+' : ''}${changePercent.toFixed(2)}%`
|
||||||
) : (
|
) : (
|
||||||
'-'
|
'-'
|
||||||
)}
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
<Td fontSize="xs" color="gray.600" maxW="300px">
|
<Td fontSize="xs" color={subTextColor} maxW="300px" borderColor={borderColor}>
|
||||||
<Text noOfLines={2}>{stock.reason || '-'}</Text>
|
<Text noOfLines={2}>{stock.reason || '-'}</Text>
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
|
|||||||
@@ -608,10 +608,20 @@ const ConceptTimelineModal = ({
|
|||||||
scrollBehavior="inside"
|
scrollBehavior="inside"
|
||||||
isCentered
|
isCentered
|
||||||
>
|
>
|
||||||
<ModalOverlay />
|
<ModalOverlay bg="blackAlpha.800" backdropFilter="blur(8px)" />
|
||||||
<ModalContent maxW="1400px" m={{ base: 0, md: 'auto' }} mx="auto">
|
<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
|
<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"
|
color="white"
|
||||||
position="sticky"
|
position="sticky"
|
||||||
top={0}
|
top={0}
|
||||||
@@ -624,35 +634,34 @@ const ConceptTimelineModal = ({
|
|||||||
<Icon
|
<Icon
|
||||||
as={FaChartLine}
|
as={FaChartLine}
|
||||||
boxSize={{ base: 4, md: 6 }}
|
boxSize={{ base: 4, md: 6 }}
|
||||||
filter="drop-shadow(0 2px 4px rgba(0,0,0,0.2))"
|
color="cyan.400"
|
||||||
/>
|
/>
|
||||||
<Text
|
<Text
|
||||||
fontSize={{ base: 'md', md: 'xl' }}
|
fontSize={{ base: 'md', md: 'xl' }}
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
textShadow="0 2px 4px rgba(0,0,0,0.2)"
|
color="white"
|
||||||
noOfLines={1}
|
noOfLines={1}
|
||||||
maxW={{ base: '120px', md: 'none' }}
|
maxW={{ base: '120px', md: 'none' }}
|
||||||
>
|
>
|
||||||
{conceptName} - 历史时间轴
|
{conceptName} - 历史时间轴
|
||||||
</Text>
|
</Text>
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme="yellow"
|
bg="purple.500"
|
||||||
px={{ base: 2, md: 3 }}
|
|
||||||
py={1}
|
|
||||||
borderRadius="full"
|
|
||||||
fontSize={{ base: 'xs', md: 'sm' }}
|
|
||||||
boxShadow="md"
|
|
||||||
>
|
|
||||||
最近100天
|
|
||||||
</Badge>
|
|
||||||
<Badge
|
|
||||||
bg="whiteAlpha.300"
|
|
||||||
color="white"
|
color="white"
|
||||||
px={{ base: 2, md: 3 }}
|
px={{ base: 2, md: 3 }}
|
||||||
py={1}
|
py={1}
|
||||||
borderRadius="full"
|
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"
|
fontSize="xs"
|
||||||
backdropFilter="blur(10px)"
|
|
||||||
display={{ base: 'none', sm: 'flex' }}
|
display={{ base: 'none', sm: 'flex' }}
|
||||||
>
|
>
|
||||||
🔥 Max版功能
|
🔥 Max版功能
|
||||||
@@ -671,29 +680,29 @@ const ConceptTimelineModal = ({
|
|||||||
<ModalBody
|
<ModalBody
|
||||||
py={{ base: 2, md: 6 }}
|
py={{ base: 2, md: 6 }}
|
||||||
px={{ base: 0, md: 6 }}
|
px={{ base: 0, md: 6 }}
|
||||||
bg="gray.50"
|
bg="transparent"
|
||||||
css={{
|
css={{
|
||||||
'&::-webkit-scrollbar': {
|
'&::-webkit-scrollbar': {
|
||||||
width: '8px',
|
width: '8px',
|
||||||
},
|
},
|
||||||
'&::-webkit-scrollbar-track': {
|
'&::-webkit-scrollbar-track': {
|
||||||
background: '#f1f1f1',
|
background: 'rgba(255,255,255,0.05)',
|
||||||
borderRadius: '10px',
|
borderRadius: '10px',
|
||||||
},
|
},
|
||||||
'&::-webkit-scrollbar-thumb': {
|
'&::-webkit-scrollbar-thumb': {
|
||||||
background: '#c1c1c1',
|
background: 'rgba(255,255,255,0.2)',
|
||||||
borderRadius: '10px',
|
borderRadius: '10px',
|
||||||
},
|
},
|
||||||
'&::-webkit-scrollbar-thumb:hover': {
|
'&::-webkit-scrollbar-thumb:hover': {
|
||||||
background: '#a8a8a8',
|
background: 'rgba(255,255,255,0.3)',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Center py={20}>
|
<Center py={20}>
|
||||||
<VStack spacing={4}>
|
<VStack spacing={4}>
|
||||||
<Spinner size="xl" color="purple.500" thickness="4px" />
|
<Spinner size="xl" color="cyan.400" thickness="4px" />
|
||||||
<Text color="gray.600">正在加载时间轴数据...</Text>
|
<Text color="whiteAlpha.700">正在加载时间轴数据...</Text>
|
||||||
</VStack>
|
</VStack>
|
||||||
</Center>
|
</Center>
|
||||||
) : timelineData.length > 0 ? (
|
) : timelineData.length > 0 ? (
|
||||||
@@ -715,83 +724,79 @@ const ConceptTimelineModal = ({
|
|||||||
spacing={{ base: 1, md: 2 }}
|
spacing={{ base: 1, md: 2 }}
|
||||||
px={{ base: 2, md: 4 }}
|
px={{ base: 2, md: 4 }}
|
||||||
py={{ base: 1, md: 2 }}
|
py={{ base: 1, md: 2 }}
|
||||||
bg="purple.50"
|
bg="whiteAlpha.100"
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor="purple.200"
|
borderColor="purple.500"
|
||||||
boxShadow="sm"
|
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
>
|
>
|
||||||
<Box w={{ base: 2, md: 3 }} h={{ base: 2, md: 3 }} bg="#9F7AEA" borderRadius="full" />
|
<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>
|
||||||
<HStack
|
<HStack
|
||||||
spacing={{ base: 1, md: 2 }}
|
spacing={{ base: 1, md: 2 }}
|
||||||
px={{ base: 2, md: 4 }}
|
px={{ base: 2, md: 4 }}
|
||||||
py={{ base: 1, md: 2 }}
|
py={{ base: 1, md: 2 }}
|
||||||
bg="purple.50"
|
bg="whiteAlpha.100"
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor="purple.300"
|
borderColor="purple.600"
|
||||||
boxShadow="sm"
|
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
>
|
>
|
||||||
<Box w={{ base: 2, md: 3 }} h={{ base: 2, md: 3 }} bg="#805AD5" borderRadius="full" />
|
<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>
|
||||||
<HStack
|
<HStack
|
||||||
spacing={{ base: 1, md: 2 }}
|
spacing={{ base: 1, md: 2 }}
|
||||||
px={{ base: 2, md: 4 }}
|
px={{ base: 2, md: 4 }}
|
||||||
py={{ base: 1, md: 2 }}
|
py={{ base: 1, md: 2 }}
|
||||||
bg="red.50"
|
bg="whiteAlpha.100"
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor="red.200"
|
borderColor="red.400"
|
||||||
boxShadow="sm"
|
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
>
|
>
|
||||||
<Icon as={FaArrowUp} color="red.500" boxSize={{ base: 2, md: 3 }} />
|
<Icon as={FaArrowUp} color="red.400" boxSize={{ base: 2, md: 3 }} />
|
||||||
<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>
|
||||||
<HStack
|
<HStack
|
||||||
spacing={{ base: 1, md: 2 }}
|
spacing={{ base: 1, md: 2 }}
|
||||||
px={{ base: 2, md: 4 }}
|
px={{ base: 2, md: 4 }}
|
||||||
py={{ base: 1, md: 2 }}
|
py={{ base: 1, md: 2 }}
|
||||||
bg="green.50"
|
bg="whiteAlpha.100"
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor="green.200"
|
borderColor="green.400"
|
||||||
boxShadow="sm"
|
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
>
|
>
|
||||||
<Icon as={FaArrowDown} color="green.500" boxSize={{ base: 2, md: 3 }} />
|
<Icon as={FaArrowDown} color="green.400" boxSize={{ base: 2, md: 3 }} />
|
||||||
<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>
|
||||||
<HStack
|
<HStack
|
||||||
spacing={{ base: 1, md: 2 }}
|
spacing={{ base: 1, md: 2 }}
|
||||||
px={{ base: 2, md: 4 }}
|
px={{ base: 2, md: 4 }}
|
||||||
py={{ base: 1, md: 2 }}
|
py={{ base: 1, md: 2 }}
|
||||||
bg="orange.50"
|
bg="whiteAlpha.100"
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor="orange.200"
|
borderColor="orange.400"
|
||||||
boxShadow="sm"
|
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
>
|
>
|
||||||
<Text fontSize={{ base: 'xs', md: 'sm' }} fontWeight="bold">🔥</Text>
|
<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>
|
</HStack>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{/* FullCalendar 日历组件 */}
|
{/* FullCalendar 日历组件 */}
|
||||||
<Box
|
<Box
|
||||||
height={{ base: '500px', md: '700px' }}
|
height={{ base: '500px', md: '700px' }}
|
||||||
bg="white"
|
bg="rgba(15, 23, 42, 0.6)"
|
||||||
borderRadius={{ base: 'none', md: 'xl' }}
|
borderRadius={{ base: 'none', md: 'xl' }}
|
||||||
boxShadow={{ base: 'none', md: 'lg' }}
|
border="1px solid"
|
||||||
|
borderColor="whiteAlpha.100"
|
||||||
p={{ base: 1, md: 4 }}
|
p={{ base: 1, md: 4 }}
|
||||||
sx={{
|
sx={{
|
||||||
// FullCalendar 样式定制
|
// FullCalendar 深色主题样式定制
|
||||||
'.fc': {
|
'.fc': {
|
||||||
height: '100%',
|
height: '100%',
|
||||||
},
|
},
|
||||||
@@ -808,42 +813,61 @@ const ConceptTimelineModal = ({
|
|||||||
'.fc-toolbar-title': {
|
'.fc-toolbar-title': {
|
||||||
fontSize: { base: '1rem', md: '1.5rem' },
|
fontSize: { base: '1rem', md: '1.5rem' },
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: 'purple.600',
|
color: 'white',
|
||||||
},
|
},
|
||||||
'.fc-button': {
|
'.fc-button': {
|
||||||
backgroundColor: '#9F7AEA',
|
backgroundColor: 'rgba(139, 92, 246, 0.6)',
|
||||||
borderColor: '#9F7AEA',
|
borderColor: 'rgba(139, 92, 246, 0.8)',
|
||||||
color: 'white',
|
color: 'white',
|
||||||
padding: { base: '4px 8px', md: '6px 12px' },
|
padding: { base: '4px 8px', md: '6px 12px' },
|
||||||
fontSize: { base: '12px', md: '14px' },
|
fontSize: { base: '12px', md: '14px' },
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: '#805AD5',
|
backgroundColor: 'rgba(139, 92, 246, 0.8)',
|
||||||
borderColor: '#805AD5',
|
borderColor: 'rgba(139, 92, 246, 1)',
|
||||||
},
|
},
|
||||||
'&:active, &:focus': {
|
'&:active, &:focus': {
|
||||||
backgroundColor: '#6B46C1',
|
backgroundColor: 'rgba(139, 92, 246, 1)',
|
||||||
borderColor: '#6B46C1',
|
borderColor: 'rgba(139, 92, 246, 1)',
|
||||||
boxShadow: 'none',
|
boxShadow: 'none',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'.fc-button-active': {
|
'.fc-button-active': {
|
||||||
backgroundColor: '#6B46C1',
|
backgroundColor: 'rgba(139, 92, 246, 1)',
|
||||||
borderColor: '#6B46C1',
|
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': {
|
'.fc-daygrid-day': {
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
transition: 'all 0.2s',
|
transition: 'all 0.2s',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'purple.50',
|
backgroundColor: 'rgba(139, 92, 246, 0.2)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'.fc-daygrid-day-number': {
|
'.fc-daygrid-day-number': {
|
||||||
|
color: 'rgba(255, 255, 255, 0.9)',
|
||||||
padding: { base: '2px', md: '4px' },
|
padding: { base: '2px', md: '4px' },
|
||||||
fontSize: { base: '0.75rem', md: '0.875rem' },
|
fontSize: { base: '0.75rem', md: '0.875rem' },
|
||||||
},
|
},
|
||||||
'.fc-col-header-cell-cushion': {
|
'.fc-day-today': {
|
||||||
fontSize: { base: '0.75rem', md: '0.875rem' },
|
backgroundColor: 'rgba(139, 92, 246, 0.15) !important',
|
||||||
padding: { base: '4px 2px', md: '8px' },
|
},
|
||||||
|
'.fc-day-other .fc-daygrid-day-number': {
|
||||||
|
color: 'rgba(255, 255, 255, 0.4)',
|
||||||
},
|
},
|
||||||
'.fc-event': {
|
'.fc-event': {
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
@@ -855,12 +879,15 @@ const ConceptTimelineModal = ({
|
|||||||
transition: 'all 0.2s',
|
transition: 'all 0.2s',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
transform: 'scale(1.05)',
|
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': {
|
'.fc-daygrid-event-harness': {
|
||||||
marginBottom: { base: '1px', md: '2px' },
|
marginBottom: { base: '1px', md: '2px' },
|
||||||
},
|
},
|
||||||
|
'.fc-more-link': {
|
||||||
|
color: 'rgba(255, 255, 255, 0.8)',
|
||||||
|
},
|
||||||
// H5 端隐藏事件文字,只显示色块
|
// H5 端隐藏事件文字,只显示色块
|
||||||
'@media (max-width: 768px)': {
|
'@media (max-width: 768px)': {
|
||||||
'.fc-event-title': {
|
'.fc-event-title': {
|
||||||
@@ -898,24 +925,23 @@ const ConceptTimelineModal = ({
|
|||||||
<Center py={24}>
|
<Center py={24}>
|
||||||
<VStack
|
<VStack
|
||||||
spacing={6}
|
spacing={6}
|
||||||
bg="white"
|
bg="rgba(15, 23, 42, 0.6)"
|
||||||
p={12}
|
p={12}
|
||||||
borderRadius="2xl"
|
borderRadius="2xl"
|
||||||
boxShadow="xl"
|
|
||||||
border="2px dashed"
|
border="2px dashed"
|
||||||
borderColor="purple.200"
|
borderColor="whiteAlpha.200"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
as={FaHistory}
|
as={FaHistory}
|
||||||
boxSize={24}
|
boxSize={24}
|
||||||
color="purple.300"
|
color="purple.400"
|
||||||
opacity={0.5}
|
opacity={0.6}
|
||||||
/>
|
/>
|
||||||
<VStack spacing={2}>
|
<VStack spacing={2}>
|
||||||
<Text fontSize="2xl" fontWeight="bold" color="gray.700">
|
<Text fontSize="2xl" fontWeight="bold" color="white">
|
||||||
暂无历史数据
|
暂无历史数据
|
||||||
</Text>
|
</Text>
|
||||||
<Text fontSize="md" color="gray.500">
|
<Text fontSize="md" color="whiteAlpha.600">
|
||||||
该概念在最近100天内没有相关事件记录
|
该概念在最近100天内没有相关事件记录
|
||||||
</Text>
|
</Text>
|
||||||
</VStack>
|
</VStack>
|
||||||
@@ -941,45 +967,43 @@ const ConceptTimelineModal = ({
|
|||||||
size="4xl"
|
size="4xl"
|
||||||
scrollBehavior="inside"
|
scrollBehavior="inside"
|
||||||
>
|
>
|
||||||
<ModalOverlay />
|
<ModalOverlay bg="blackAlpha.800" backdropFilter="blur(8px)" />
|
||||||
<ModalContent borderRadius="2xl" overflow="hidden">
|
<ModalContent
|
||||||
|
borderRadius="2xl"
|
||||||
|
overflow="hidden"
|
||||||
|
bg="rgba(15, 23, 42, 0.95)"
|
||||||
|
border="1px solid"
|
||||||
|
borderColor="whiteAlpha.100"
|
||||||
|
>
|
||||||
<ModalHeader
|
<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"
|
color="white"
|
||||||
py={6}
|
py={6}
|
||||||
position="relative"
|
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}>
|
<VStack align="start" spacing={3} position="relative" zIndex={1}>
|
||||||
<HStack spacing={3}>
|
<HStack spacing={3}>
|
||||||
<Icon
|
<Icon
|
||||||
as={CalendarIcon}
|
as={CalendarIcon}
|
||||||
boxSize={6}
|
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)}
|
{formatDateDisplay(selectedDate)}
|
||||||
</Text>
|
</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
{selectedDateData.price && (
|
{selectedDateData.price && (
|
||||||
<HStack spacing={4} fontSize="sm" flexWrap="wrap">
|
<HStack spacing={4} fontSize="sm" flexWrap="wrap">
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme={getPriceInfo(selectedDateData.price).color}
|
bg={getPriceInfo(selectedDateData.price).color === 'red' ? 'red.500' :
|
||||||
variant="solid"
|
getPriceInfo(selectedDateData.price).color === 'green' ? 'green.500' : 'whiteAlpha.300'}
|
||||||
|
color="white"
|
||||||
px={4}
|
px={4}
|
||||||
py={2}
|
py={2}
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
fontSize="md"
|
fontSize="md"
|
||||||
boxShadow="md"
|
|
||||||
>
|
>
|
||||||
<HStack spacing={2}>
|
<HStack spacing={2}>
|
||||||
{getPriceInfo(selectedDateData.price).icon && (
|
{getPriceInfo(selectedDateData.price).icon && (
|
||||||
@@ -996,14 +1020,13 @@ const ConceptTimelineModal = ({
|
|||||||
{selectedDateData.price.stock_count && (
|
{selectedDateData.price.stock_count && (
|
||||||
<HStack
|
<HStack
|
||||||
spacing={1}
|
spacing={1}
|
||||||
bg="whiteAlpha.300"
|
bg="whiteAlpha.100"
|
||||||
px={3}
|
px={3}
|
||||||
py={1}
|
py={1}
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
backdropFilter="blur(10px)"
|
|
||||||
>
|
>
|
||||||
<Text fontWeight="medium">📊 统计股票:</Text>
|
<Text fontWeight="medium" color="whiteAlpha.800">📊 统计股票:</Text>
|
||||||
<Text fontWeight="bold">{selectedDateData.price.stock_count} 只</Text>
|
<Text fontWeight="bold" color="white">{selectedDateData.price.stock_count} 只</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
)}
|
)}
|
||||||
</HStack>
|
</HStack>
|
||||||
@@ -1012,7 +1035,7 @@ const ConceptTimelineModal = ({
|
|||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalCloseButton color="white" />
|
<ModalCloseButton color="white" />
|
||||||
|
|
||||||
<ModalBody py={6}>
|
<ModalBody py={6} bg="transparent">
|
||||||
{selectedDateData.events && selectedDateData.events.length > 0 ? (
|
{selectedDateData.events && selectedDateData.events.length > 0 ? (
|
||||||
<VStack align="stretch" spacing={6}>
|
<VStack align="stretch" spacing={6}>
|
||||||
{/* 统计卡片 */}
|
{/* 统计卡片 */}
|
||||||
@@ -1020,35 +1043,33 @@ const ConceptTimelineModal = ({
|
|||||||
<Box
|
<Box
|
||||||
px={6}
|
px={6}
|
||||||
py={3}
|
py={3}
|
||||||
bg="blue.50"
|
bg="whiteAlpha.100"
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
boxShadow="sm"
|
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor="blue.200"
|
borderColor="blue.500"
|
||||||
>
|
>
|
||||||
<HStack spacing={2}>
|
<HStack spacing={2}>
|
||||||
<Icon as={FaNewspaper} color="blue.500" boxSize={5} />
|
<Icon as={FaNewspaper} color="blue.400" boxSize={5} />
|
||||||
<Text fontWeight="bold" fontSize="lg" color="blue.700">
|
<Text fontWeight="bold" fontSize="lg" color="blue.300">
|
||||||
{selectedDateData.events.filter(e => e.type === 'news').length}
|
{selectedDateData.events.filter(e => e.type === 'news').length}
|
||||||
</Text>
|
</Text>
|
||||||
<Text fontSize="sm" color="gray.600">条新闻</Text>
|
<Text fontSize="sm" color="whiteAlpha.700">条新闻</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
px={6}
|
px={6}
|
||||||
py={3}
|
py={3}
|
||||||
bg="green.50"
|
bg="whiteAlpha.100"
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
boxShadow="sm"
|
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor="green.200"
|
borderColor="green.500"
|
||||||
>
|
>
|
||||||
<HStack spacing={2}>
|
<HStack spacing={2}>
|
||||||
<Icon as={FaFileAlt} color="green.500" boxSize={5} />
|
<Icon as={FaFileAlt} color="green.400" boxSize={5} />
|
||||||
<Text fontWeight="bold" fontSize="lg" color="green.700">
|
<Text fontWeight="bold" fontSize="lg" color="green.300">
|
||||||
{selectedDateData.events.filter(e => e.type === 'report').length}
|
{selectedDateData.events.filter(e => e.type === 'report').length}
|
||||||
</Text>
|
</Text>
|
||||||
<Text fontSize="sm" color="gray.600">篇研报</Text>
|
<Text fontSize="sm" color="whiteAlpha.700">篇研报</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
</Box>
|
</Box>
|
||||||
</HStack>
|
</HStack>
|
||||||
@@ -1059,58 +1080,38 @@ const ConceptTimelineModal = ({
|
|||||||
<Box
|
<Box
|
||||||
key={eventIdx}
|
key={eventIdx}
|
||||||
p={5}
|
p={5}
|
||||||
bgGradient={
|
bg="whiteAlpha.50"
|
||||||
event.type === 'news'
|
|
||||||
? 'linear(to-br, blue.50, blue.100)'
|
|
||||||
: 'linear(to-br, green.50, green.100)'
|
|
||||||
}
|
|
||||||
borderRadius="xl"
|
borderRadius="xl"
|
||||||
borderLeft="5px solid"
|
borderLeft="5px solid"
|
||||||
borderLeftColor={event.type === 'news' ? 'blue.500' : 'green.500'}
|
borderLeftColor={event.type === 'news' ? 'blue.400' : 'green.400'}
|
||||||
boxShadow="sm"
|
border="1px solid"
|
||||||
|
borderColor="whiteAlpha.100"
|
||||||
_hover={{
|
_hover={{
|
||||||
transform: 'translateX(6px) translateY(-2px)',
|
transform: 'translateX(6px) translateY(-2px)',
|
||||||
boxShadow: 'lg',
|
bg: 'whiteAlpha.100',
|
||||||
borderLeftColor: event.type === 'news' ? 'blue.600' : 'green.600',
|
borderLeftColor: event.type === 'news' ? 'blue.300' : 'green.300',
|
||||||
}}
|
}}
|
||||||
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
position="relative"
|
position="relative"
|
||||||
overflow="hidden"
|
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}>
|
<VStack align="start" spacing={3}>
|
||||||
<HStack spacing={2} flexWrap="wrap">
|
<HStack spacing={2} flexWrap="wrap">
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme={event.type === 'news' ? 'blue' : 'green'}
|
bg={event.type === 'news' ? 'blue.500' : 'green.500'}
|
||||||
variant="solid"
|
color="white"
|
||||||
fontSize="sm"
|
fontSize="sm"
|
||||||
px={3}
|
px={3}
|
||||||
py={1}
|
py={1}
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
boxShadow="sm"
|
|
||||||
>
|
>
|
||||||
{event.type === 'news' ? '📰 新闻' : '📊 研报'}
|
{event.type === 'news' ? '📰 新闻' : '📊 研报'}
|
||||||
</Badge>
|
</Badge>
|
||||||
{event.publisher && (
|
{event.publisher && (
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme="purple"
|
bg="purple.500"
|
||||||
variant="subtle"
|
color="white"
|
||||||
fontSize="xs"
|
fontSize="xs"
|
||||||
px={2}
|
px={2}
|
||||||
py={1}
|
py={1}
|
||||||
@@ -1121,8 +1122,8 @@ const ConceptTimelineModal = ({
|
|||||||
)}
|
)}
|
||||||
{event.rating && (
|
{event.rating && (
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme="orange"
|
bg="orange.500"
|
||||||
variant="solid"
|
color="white"
|
||||||
fontSize="xs"
|
fontSize="xs"
|
||||||
px={2}
|
px={2}
|
||||||
py={1}
|
py={1}
|
||||||
@@ -1133,8 +1134,8 @@ const ConceptTimelineModal = ({
|
|||||||
)}
|
)}
|
||||||
{event.security_name && (
|
{event.security_name && (
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme="cyan"
|
bg="cyan.600"
|
||||||
variant="subtle"
|
color="white"
|
||||||
fontSize="xs"
|
fontSize="xs"
|
||||||
px={2}
|
px={2}
|
||||||
py={1}
|
py={1}
|
||||||
@@ -1148,7 +1149,7 @@ const ConceptTimelineModal = ({
|
|||||||
<Text
|
<Text
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
fontSize="lg"
|
fontSize="lg"
|
||||||
color="gray.800"
|
color="white"
|
||||||
lineHeight="1.4"
|
lineHeight="1.4"
|
||||||
>
|
>
|
||||||
{event.title}
|
{event.title}
|
||||||
@@ -1156,7 +1157,7 @@ const ConceptTimelineModal = ({
|
|||||||
|
|
||||||
<Text
|
<Text
|
||||||
fontSize="sm"
|
fontSize="sm"
|
||||||
color="gray.600"
|
color="whiteAlpha.700"
|
||||||
noOfLines={3}
|
noOfLines={3}
|
||||||
lineHeight="1.6"
|
lineHeight="1.6"
|
||||||
>
|
>
|
||||||
@@ -1166,8 +1167,8 @@ const ConceptTimelineModal = ({
|
|||||||
<HStack spacing={4} w="100%" justify="space-between" align="center" mt={1}>
|
<HStack spacing={4} w="100%" justify="space-between" align="center" mt={1}>
|
||||||
{event.time && (
|
{event.time && (
|
||||||
<HStack spacing={1}>
|
<HStack spacing={1}>
|
||||||
<Icon as={FaClock} color="gray.500" boxSize={3} />
|
<Icon as={FaClock} color="whiteAlpha.500" boxSize={3} />
|
||||||
<Text fontSize="xs" color="gray.500" fontWeight="medium">
|
<Text fontSize="xs" color="whiteAlpha.500" fontWeight="medium">
|
||||||
{formatDateTime(event.time)}
|
{formatDateTime(event.time)}
|
||||||
</Text>
|
</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
@@ -1225,24 +1226,24 @@ const ConceptTimelineModal = ({
|
|||||||
<Center py={16}>
|
<Center py={16}>
|
||||||
<VStack
|
<VStack
|
||||||
spacing={5}
|
spacing={5}
|
||||||
bg="gray.50"
|
bg="whiteAlpha.50"
|
||||||
p={10}
|
p={10}
|
||||||
borderRadius="2xl"
|
borderRadius="2xl"
|
||||||
border="2px dashed"
|
border="2px dashed"
|
||||||
borderColor="gray.300"
|
borderColor="whiteAlpha.200"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
as={FaHistory}
|
as={FaHistory}
|
||||||
boxSize={20}
|
boxSize={20}
|
||||||
color="gray.400"
|
color="purple.400"
|
||||||
opacity={0.6}
|
opacity={0.6}
|
||||||
/>
|
/>
|
||||||
<VStack spacing={2}>
|
<VStack spacing={2}>
|
||||||
<Text fontSize="xl" fontWeight="bold" color="gray.600">
|
<Text fontSize="xl" fontWeight="bold" color="white">
|
||||||
当日无新闻或研报
|
当日无新闻或研报
|
||||||
</Text>
|
</Text>
|
||||||
{selectedDateData.price && (
|
{selectedDateData.price && (
|
||||||
<Text fontSize="md" color="gray.500">
|
<Text fontSize="md" color="whiteAlpha.600">
|
||||||
仅有涨跌幅数据
|
仅有涨跌幅数据
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
@@ -1253,20 +1254,20 @@ const ConceptTimelineModal = ({
|
|||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
|
||||||
<ModalFooter
|
<ModalFooter
|
||||||
borderTop="2px solid"
|
borderTop="1px solid"
|
||||||
borderColor="gray.100"
|
borderColor="whiteAlpha.100"
|
||||||
bg="gray.50"
|
bg="rgba(15, 23, 42, 0.98)"
|
||||||
py={4}
|
py={4}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
colorScheme="purple"
|
bg="purple.500"
|
||||||
|
color="white"
|
||||||
size="lg"
|
size="lg"
|
||||||
px={8}
|
px={8}
|
||||||
onClick={onDateDetailClose}
|
onClick={onDateDetailClose}
|
||||||
boxShadow="md"
|
|
||||||
_hover={{
|
_hover={{
|
||||||
|
bg: 'purple.400',
|
||||||
transform: 'translateY(-2px)',
|
transform: 'translateY(-2px)',
|
||||||
boxShadow: 'lg',
|
|
||||||
}}
|
}}
|
||||||
transition="all 0.2s"
|
transition="all 0.2s"
|
||||||
>
|
>
|
||||||
@@ -1285,30 +1286,30 @@ const ConceptTimelineModal = ({
|
|||||||
size="4xl"
|
size="4xl"
|
||||||
scrollBehavior="inside"
|
scrollBehavior="inside"
|
||||||
>
|
>
|
||||||
<ModalOverlay />
|
<ModalOverlay bg="blackAlpha.800" backdropFilter="blur(8px)" />
|
||||||
<ModalContent>
|
<ModalContent bg="rgba(15, 23, 42, 0.95)" border="1px solid" borderColor="whiteAlpha.100">
|
||||||
<ModalHeader bg="green.500" color="white">
|
<ModalHeader bg="rgba(15, 23, 42, 0.98)" borderBottom="1px solid" borderColor="whiteAlpha.100" color="white">
|
||||||
<VStack align="start" spacing={1}>
|
<VStack align="start" spacing={1}>
|
||||||
<Text fontSize="lg">{selectedReport?.title}</Text>
|
<Text fontSize="lg" color="white">{selectedReport?.title}</Text>
|
||||||
<HStack spacing={3} fontSize="sm" opacity={0.9}>
|
<HStack spacing={3} fontSize="sm">
|
||||||
{selectedReport?.publisher && (
|
{selectedReport?.publisher && (
|
||||||
<Badge colorScheme="whiteAlpha" variant="solid">
|
<Badge bg="green.500" color="white">
|
||||||
{selectedReport.publisher}
|
{selectedReport.publisher}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{selectedReport?.author && (
|
{selectedReport?.author && (
|
||||||
<Text>{selectedReport.author}</Text>
|
<Text color="whiteAlpha.700">{selectedReport.author}</Text>
|
||||||
)}
|
)}
|
||||||
{selectedReport?.time && (
|
{selectedReport?.time && (
|
||||||
<Text>{formatDateTime(selectedReport.time)}</Text>
|
<Text color="whiteAlpha.700">{formatDateTime(selectedReport.time)}</Text>
|
||||||
)}
|
)}
|
||||||
{selectedReport?.rating && (
|
{selectedReport?.rating && (
|
||||||
<Badge colorScheme="orange" variant="solid">
|
<Badge bg="orange.500" color="white">
|
||||||
{selectedReport.rating}
|
{selectedReport.rating}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{selectedReport?.security_name && (
|
{selectedReport?.security_name && (
|
||||||
<Badge colorScheme="cyan" variant="solid">
|
<Badge bg="cyan.600" color="white">
|
||||||
{selectedReport.security_name}
|
{selectedReport.security_name}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
@@ -1319,37 +1320,38 @@ const ConceptTimelineModal = ({
|
|||||||
|
|
||||||
<ModalBody py={6}>
|
<ModalBody py={6}>
|
||||||
<Box
|
<Box
|
||||||
bg="gray.50"
|
bg="whiteAlpha.50"
|
||||||
p={6}
|
p={6}
|
||||||
borderRadius="md"
|
borderRadius="md"
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor="gray.200"
|
borderColor="whiteAlpha.100"
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
whiteSpace="pre-wrap"
|
whiteSpace="pre-wrap"
|
||||||
fontSize="sm"
|
fontSize="sm"
|
||||||
lineHeight="tall"
|
lineHeight="tall"
|
||||||
color="gray.700"
|
color="whiteAlpha.800"
|
||||||
>
|
>
|
||||||
{selectedReport?.content || '暂无内容'}
|
{selectedReport?.content || '暂无内容'}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</ModalBody>
|
</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}>
|
<HStack spacing={3}>
|
||||||
{selectedReport?.content_url && (
|
{selectedReport?.content_url && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
colorScheme="blue"
|
bg="whiteAlpha.100"
|
||||||
variant="outline"
|
color="white"
|
||||||
leftIcon={<ExternalLinkIcon />}
|
leftIcon={<ExternalLinkIcon />}
|
||||||
onClick={() => window.open(selectedReport.content_url, '_blank')}
|
onClick={() => window.open(selectedReport.content_url, '_blank')}
|
||||||
|
_hover={{ bg: 'whiteAlpha.200' }}
|
||||||
>
|
>
|
||||||
查看原文
|
查看原文
|
||||||
</Button>
|
</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>
|
</Button>
|
||||||
</HStack>
|
</HStack>
|
||||||
@@ -1366,22 +1368,22 @@ const ConceptTimelineModal = ({
|
|||||||
size="4xl"
|
size="4xl"
|
||||||
scrollBehavior="inside"
|
scrollBehavior="inside"
|
||||||
>
|
>
|
||||||
<ModalOverlay />
|
<ModalOverlay bg="blackAlpha.800" backdropFilter="blur(8px)" />
|
||||||
<ModalContent>
|
<ModalContent bg="rgba(15, 23, 42, 0.95)" border="1px solid" borderColor="whiteAlpha.100">
|
||||||
<ModalHeader bg="blue.500" color="white">
|
<ModalHeader bg="rgba(15, 23, 42, 0.98)" borderBottom="1px solid" borderColor="whiteAlpha.100" color="white">
|
||||||
<VStack align="start" spacing={1}>
|
<VStack align="start" spacing={1}>
|
||||||
<Text fontSize="lg">{selectedNews?.title}</Text>
|
<Text fontSize="lg" color="white">{selectedNews?.title}</Text>
|
||||||
<HStack spacing={3} fontSize="sm" opacity={0.9}>
|
<HStack spacing={3} fontSize="sm">
|
||||||
{selectedNews?.source && (
|
{selectedNews?.source && (
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme={selectedNews.source === 'zsxq' ? 'purple' : 'whiteAlpha'}
|
bg={selectedNews.source === 'zsxq' ? 'purple.500' : 'blue.500'}
|
||||||
variant="solid"
|
color="white"
|
||||||
>
|
>
|
||||||
{selectedNews.source === 'zsxq' ? '知识星球' : selectedNews.source}
|
{selectedNews.source === 'zsxq' ? '知识星球' : selectedNews.source}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{selectedNews?.time && (
|
{selectedNews?.time && (
|
||||||
<Text>{formatDateTime(selectedNews.time)}</Text>
|
<Text color="whiteAlpha.700">{formatDateTime(selectedNews.time)}</Text>
|
||||||
)}
|
)}
|
||||||
</HStack>
|
</HStack>
|
||||||
</VStack>
|
</VStack>
|
||||||
@@ -1390,38 +1392,39 @@ const ConceptTimelineModal = ({
|
|||||||
|
|
||||||
<ModalBody py={6}>
|
<ModalBody py={6}>
|
||||||
<Box
|
<Box
|
||||||
bg="gray.50"
|
bg="whiteAlpha.50"
|
||||||
p={6}
|
p={6}
|
||||||
borderRadius="md"
|
borderRadius="md"
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor="gray.200"
|
borderColor="whiteAlpha.100"
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
whiteSpace="pre-wrap"
|
whiteSpace="pre-wrap"
|
||||||
fontSize="sm"
|
fontSize="sm"
|
||||||
lineHeight="tall"
|
lineHeight="tall"
|
||||||
color="gray.700"
|
color="whiteAlpha.800"
|
||||||
>
|
>
|
||||||
{selectedNews?.content || '暂无内容'}
|
{selectedNews?.content || '暂无内容'}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</ModalBody>
|
</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}>
|
<HStack spacing={3}>
|
||||||
{/* zsxq来源不显示查看原文按钮 */}
|
{/* zsxq来源不显示查看原文按钮 */}
|
||||||
{selectedNews?.url && selectedNews?.source !== 'zsxq' && (
|
{selectedNews?.url && selectedNews?.source !== 'zsxq' && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
colorScheme="blue"
|
bg="whiteAlpha.100"
|
||||||
variant="outline"
|
color="white"
|
||||||
leftIcon={<ExternalLinkIcon />}
|
leftIcon={<ExternalLinkIcon />}
|
||||||
onClick={() => window.open(selectedNews.url, '_blank')}
|
onClick={() => window.open(selectedNews.url, '_blank')}
|
||||||
|
_hover={{ bg: 'whiteAlpha.200' }}
|
||||||
>
|
>
|
||||||
查看原文
|
查看原文
|
||||||
</Button>
|
</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>
|
</Button>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|||||||
@@ -73,9 +73,10 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
const [useCustomRange, setUseCustomRange] = useState(false);
|
const [useCustomRange, setUseCustomRange] = useState(false);
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
const bg = useColorModeValue('white', 'gray.800');
|
// 深色主题颜色(固定使用深色模式)
|
||||||
const cardBg = useColorModeValue('gray.50', 'gray.700');
|
const bg = 'rgba(15, 23, 42, 0.8)';
|
||||||
const borderColor = useColorModeValue('gray.200', 'gray.600');
|
const cardBg = 'rgba(15, 23, 42, 0.6)';
|
||||||
|
const borderColor = 'whiteAlpha.100';
|
||||||
|
|
||||||
// 获取统计数据
|
// 获取统计数据
|
||||||
const fetchStatsData = async (days = timeRange, startDate = null, endDate = null) => {
|
const fetchStatsData = async (days = timeRange, startDate = null, endDate = null) => {
|
||||||
@@ -250,21 +251,21 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
return 'gray.500';
|
return 'gray.500';
|
||||||
};
|
};
|
||||||
|
|
||||||
// 统计卡片组件 - 美化版
|
// 统计卡片组件 - 深色主题版
|
||||||
const StatsCard = ({ title, icon, color, data, renderItem, isLoading }) => (
|
const StatsCard = ({ title, icon, color, data, renderItem, isLoading }) => (
|
||||||
<Box p={4}>
|
<Box p={4}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<VStack spacing={3} align="stretch">
|
<VStack spacing={3} align="stretch">
|
||||||
{[1, 2, 3, 4, 5].map((i) => (
|
{[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}>
|
<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}>
|
<VStack align="start" spacing={1} flex={1}>
|
||||||
<Skeleton height="14px" width="80%" />
|
<Skeleton height="14px" width="80%" startColor="whiteAlpha.100" endColor="whiteAlpha.200" />
|
||||||
<Skeleton height="12px" width="60%" />
|
<Skeleton height="12px" width="60%" startColor="whiteAlpha.100" endColor="whiteAlpha.200" />
|
||||||
</VStack>
|
</VStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
<Skeleton height="20px" width="50px" borderRadius="md" />
|
<Skeleton height="20px" width="50px" borderRadius="md" startColor="whiteAlpha.100" endColor="whiteAlpha.200" />
|
||||||
</HStack>
|
</HStack>
|
||||||
))}
|
))}
|
||||||
</VStack>
|
</VStack>
|
||||||
@@ -275,15 +276,15 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
width: '4px',
|
width: '4px',
|
||||||
},
|
},
|
||||||
'&::-webkit-scrollbar-track': {
|
'&::-webkit-scrollbar-track': {
|
||||||
background: '#f1f1f1',
|
background: 'rgba(255,255,255,0.05)',
|
||||||
borderRadius: '10px',
|
borderRadius: '10px',
|
||||||
},
|
},
|
||||||
'&::-webkit-scrollbar-thumb': {
|
'&::-webkit-scrollbar-thumb': {
|
||||||
background: '#c1c1c1',
|
background: 'rgba(255,255,255,0.2)',
|
||||||
borderRadius: '10px',
|
borderRadius: '10px',
|
||||||
},
|
},
|
||||||
'&::-webkit-scrollbar-thumb:hover': {
|
'&::-webkit-scrollbar-thumb:hover': {
|
||||||
background: '#a8a8a8',
|
background: 'rgba(255,255,255,0.3)',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -309,14 +310,14 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
align="center"
|
align="center"
|
||||||
p={3}
|
p={3}
|
||||||
borderRadius="xl"
|
borderRadius="xl"
|
||||||
bg={index < 3 ? 'red.50' : 'gray.50'}
|
bg={index < 3 ? 'rgba(239, 68, 68, 0.15)' : 'whiteAlpha.50'}
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor={index < 3 ? 'red.100' : 'gray.200'}
|
borderColor={index < 3 ? 'red.500' : 'whiteAlpha.100'}
|
||||||
_hover={{
|
_hover={{
|
||||||
transform: 'translateY(-1px)',
|
transform: 'translateY(-1px)',
|
||||||
shadow: 'md',
|
shadow: 'md',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
bg: index < 3 ? 'red.100' : 'gray.100'
|
bg: index < 3 ? 'rgba(239, 68, 68, 0.25)' : 'whiteAlpha.100'
|
||||||
}}
|
}}
|
||||||
transition="all 0.2s"
|
transition="all 0.2s"
|
||||||
onClick={() => onConceptClick?.(null, item.name)}
|
onClick={() => onConceptClick?.(null, item.name)}
|
||||||
@@ -324,8 +325,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
<HStack spacing={3} flex={1}>
|
<HStack spacing={3} flex={1}>
|
||||||
<Box position="relative">
|
<Box position="relative">
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme={index === 0 ? 'yellow' : index === 1 ? 'orange' : index === 2 ? 'red' : 'gray'}
|
bg={index === 0 ? 'yellow.500' : index === 1 ? 'orange.500' : index === 2 ? 'red.500' : 'whiteAlpha.200'}
|
||||||
variant="solid"
|
color="white"
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
minW="24px"
|
minW="24px"
|
||||||
h="24px"
|
h="24px"
|
||||||
@@ -344,16 +345,16 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
position="absolute"
|
position="absolute"
|
||||||
top="-8px"
|
top="-8px"
|
||||||
right="-8px"
|
right="-8px"
|
||||||
color="yellow.500"
|
color="yellow.400"
|
||||||
boxSize={3}
|
boxSize={3}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<VStack align="start" spacing={0} flex={1}>
|
<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}
|
{item.name}
|
||||||
</Text>
|
</Text>
|
||||||
<HStack spacing={2} fontSize="xs" color="gray.600">
|
<HStack spacing={2} fontSize="xs" color="whiteAlpha.600">
|
||||||
<HStack spacing={1}>
|
<HStack spacing={1}>
|
||||||
<Icon as={FaChartLine} boxSize={2.5} />
|
<Icon as={FaChartLine} boxSize={2.5} />
|
||||||
<Text>{item.stock_count}股</Text>
|
<Text>{item.stock_count}股</Text>
|
||||||
@@ -367,8 +368,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
</VStack>
|
</VStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme="red"
|
bg="red.500"
|
||||||
variant="solid"
|
color="white"
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
fontSize="xs"
|
fontSize="xs"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
@@ -392,22 +393,22 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
align="center"
|
align="center"
|
||||||
p={3}
|
p={3}
|
||||||
borderRadius="xl"
|
borderRadius="xl"
|
||||||
bg={index < 3 ? 'green.50' : 'gray.50'}
|
bg={index < 3 ? 'rgba(34, 197, 94, 0.15)' : 'whiteAlpha.50'}
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor={index < 3 ? 'green.100' : 'gray.200'}
|
borderColor={index < 3 ? 'green.500' : 'whiteAlpha.100'}
|
||||||
_hover={{
|
_hover={{
|
||||||
transform: 'translateY(-1px)',
|
transform: 'translateY(-1px)',
|
||||||
shadow: 'md',
|
shadow: 'md',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
bg: index < 3 ? 'green.100' : 'gray.100'
|
bg: index < 3 ? 'rgba(34, 197, 94, 0.25)' : 'whiteAlpha.100'
|
||||||
}}
|
}}
|
||||||
transition="all 0.2s"
|
transition="all 0.2s"
|
||||||
onClick={() => onConceptClick?.(null, item.name)}
|
onClick={() => onConceptClick?.(null, item.name)}
|
||||||
>
|
>
|
||||||
<HStack spacing={3} flex={1}>
|
<HStack spacing={3} flex={1}>
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme={index < 3 ? 'green' : 'gray'}
|
bg={index < 3 ? 'green.500' : 'whiteAlpha.200'}
|
||||||
variant="solid"
|
color="white"
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
minW="24px"
|
minW="24px"
|
||||||
h="24px"
|
h="24px"
|
||||||
@@ -421,10 +422,10 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
{index + 1}
|
{index + 1}
|
||||||
</Badge>
|
</Badge>
|
||||||
<VStack align="start" spacing={0} flex={1}>
|
<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}
|
{item.name}
|
||||||
</Text>
|
</Text>
|
||||||
<HStack spacing={2} fontSize="xs" color="gray.600">
|
<HStack spacing={2} fontSize="xs" color="whiteAlpha.600">
|
||||||
<HStack spacing={1}>
|
<HStack spacing={1}>
|
||||||
<Icon as={FaChartLine} boxSize={2.5} />
|
<Icon as={FaChartLine} boxSize={2.5} />
|
||||||
<Text>{item.stock_count}股</Text>
|
<Text>{item.stock_count}股</Text>
|
||||||
@@ -438,8 +439,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
</VStack>
|
</VStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme="green"
|
bg="green.500"
|
||||||
variant="solid"
|
color="white"
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
fontSize="xs"
|
fontSize="xs"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
@@ -463,14 +464,14 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
align="center"
|
align="center"
|
||||||
p={3}
|
p={3}
|
||||||
borderRadius="xl"
|
borderRadius="xl"
|
||||||
bg={index < 3 ? 'orange.50' : 'gray.50'}
|
bg={index < 3 ? 'rgba(251, 146, 60, 0.15)' : 'whiteAlpha.50'}
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor={index < 3 ? 'orange.100' : 'gray.200'}
|
borderColor={index < 3 ? 'orange.500' : 'whiteAlpha.100'}
|
||||||
_hover={{
|
_hover={{
|
||||||
transform: 'translateY(-1px)',
|
transform: 'translateY(-1px)',
|
||||||
shadow: 'md',
|
shadow: 'md',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
bg: index < 3 ? 'orange.100' : 'gray.100'
|
bg: index < 3 ? 'rgba(251, 146, 60, 0.25)' : 'whiteAlpha.100'
|
||||||
}}
|
}}
|
||||||
transition="all 0.2s"
|
transition="all 0.2s"
|
||||||
onClick={() => onConceptClick?.(null, item.name)}
|
onClick={() => onConceptClick?.(null, item.name)}
|
||||||
@@ -478,8 +479,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
<HStack spacing={3} flex={1}>
|
<HStack spacing={3} flex={1}>
|
||||||
<Box position="relative">
|
<Box position="relative">
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme={index === 0 ? 'orange' : index < 3 ? 'yellow' : 'gray'}
|
bg={index === 0 ? 'orange.500' : index < 3 ? 'yellow.500' : 'whiteAlpha.200'}
|
||||||
variant="solid"
|
color="white"
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
minW="24px"
|
minW="24px"
|
||||||
h="24px"
|
h="24px"
|
||||||
@@ -498,16 +499,16 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
position="absolute"
|
position="absolute"
|
||||||
top="-8px"
|
top="-8px"
|
||||||
right="-8px"
|
right="-8px"
|
||||||
color="orange.500"
|
color="orange.400"
|
||||||
boxSize={3}
|
boxSize={3}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<VStack align="start" spacing={0} flex={1}>
|
<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}
|
{item.name}
|
||||||
</Text>
|
</Text>
|
||||||
<HStack spacing={2} fontSize="xs" color="gray.600">
|
<HStack spacing={2} fontSize="xs" color="whiteAlpha.600">
|
||||||
<HStack spacing={1}>
|
<HStack spacing={1}>
|
||||||
<Icon as={FaNewspaper} boxSize={2.5} />
|
<Icon as={FaNewspaper} boxSize={2.5} />
|
||||||
<Text>{item.news_count}</Text>
|
<Text>{item.news_count}</Text>
|
||||||
@@ -521,8 +522,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
</VStack>
|
</VStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme="orange"
|
bg="orange.500"
|
||||||
variant="solid"
|
color="white"
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
fontSize="xs"
|
fontSize="xs"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
@@ -546,14 +547,14 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
align="center"
|
align="center"
|
||||||
p={3}
|
p={3}
|
||||||
borderRadius="xl"
|
borderRadius="xl"
|
||||||
bg={index < 3 ? 'purple.50' : 'gray.50'}
|
bg={index < 3 ? 'rgba(168, 85, 247, 0.15)' : 'whiteAlpha.50'}
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor={index < 3 ? 'purple.100' : 'gray.200'}
|
borderColor={index < 3 ? 'purple.500' : 'whiteAlpha.100'}
|
||||||
_hover={{
|
_hover={{
|
||||||
transform: 'translateY(-1px)',
|
transform: 'translateY(-1px)',
|
||||||
shadow: 'md',
|
shadow: 'md',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
bg: index < 3 ? 'purple.100' : 'gray.100'
|
bg: index < 3 ? 'rgba(168, 85, 247, 0.25)' : 'whiteAlpha.100'
|
||||||
}}
|
}}
|
||||||
transition="all 0.2s"
|
transition="all 0.2s"
|
||||||
onClick={() => onConceptClick?.(null, item.name)}
|
onClick={() => onConceptClick?.(null, item.name)}
|
||||||
@@ -561,8 +562,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
<HStack spacing={3} flex={1}>
|
<HStack spacing={3} flex={1}>
|
||||||
<Box position="relative">
|
<Box position="relative">
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme={index < 3 ? 'purple' : 'gray'}
|
bg={index < 3 ? 'purple.500' : 'whiteAlpha.200'}
|
||||||
variant="solid"
|
color="white"
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
minW="24px"
|
minW="24px"
|
||||||
h="24px"
|
h="24px"
|
||||||
@@ -581,23 +582,23 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
position="absolute"
|
position="absolute"
|
||||||
top="-8px"
|
top="-8px"
|
||||||
right="-8px"
|
right="-8px"
|
||||||
color="purple.500"
|
color="purple.400"
|
||||||
boxSize={3}
|
boxSize={3}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<VStack align="start" spacing={0} flex={1}>
|
<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}
|
{item.name}
|
||||||
</Text>
|
</Text>
|
||||||
<Text fontSize="xs" color="gray.600">
|
<Text fontSize="xs" color="whiteAlpha.600">
|
||||||
均幅 {formatChange(item.avg_change)}
|
均幅 {formatChange(item.avg_change)}
|
||||||
</Text>
|
</Text>
|
||||||
</VStack>
|
</VStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme="purple"
|
bg="purple.500"
|
||||||
variant="solid"
|
color="white"
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
fontSize="xs"
|
fontSize="xs"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
@@ -621,14 +622,14 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
align="center"
|
align="center"
|
||||||
p={3}
|
p={3}
|
||||||
borderRadius="xl"
|
borderRadius="xl"
|
||||||
bg={index < 3 ? 'cyan.50' : 'gray.50'}
|
bg={index < 3 ? 'rgba(34, 211, 238, 0.15)' : 'whiteAlpha.50'}
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor={index < 3 ? 'cyan.100' : 'gray.200'}
|
borderColor={index < 3 ? 'cyan.500' : 'whiteAlpha.100'}
|
||||||
_hover={{
|
_hover={{
|
||||||
transform: 'translateY(-1px)',
|
transform: 'translateY(-1px)',
|
||||||
shadow: 'md',
|
shadow: 'md',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
bg: index < 3 ? 'cyan.100' : 'gray.100'
|
bg: index < 3 ? 'rgba(34, 211, 238, 0.25)' : 'whiteAlpha.100'
|
||||||
}}
|
}}
|
||||||
transition="all 0.2s"
|
transition="all 0.2s"
|
||||||
onClick={() => onConceptClick?.(null, item.name)}
|
onClick={() => onConceptClick?.(null, item.name)}
|
||||||
@@ -636,8 +637,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
<HStack spacing={3} flex={1}>
|
<HStack spacing={3} flex={1}>
|
||||||
<Box position="relative">
|
<Box position="relative">
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme={index === 0 ? 'cyan' : index < 3 ? 'blue' : 'gray'}
|
bg={index === 0 ? 'cyan.500' : index < 3 ? 'blue.500' : 'whiteAlpha.200'}
|
||||||
variant="solid"
|
color="white"
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
minW="24px"
|
minW="24px"
|
||||||
h="24px"
|
h="24px"
|
||||||
@@ -656,23 +657,23 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
position="absolute"
|
position="absolute"
|
||||||
top="-8px"
|
top="-8px"
|
||||||
right="-8px"
|
right="-8px"
|
||||||
color="cyan.500"
|
color="cyan.400"
|
||||||
boxSize={3}
|
boxSize={3}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<VStack align="start" spacing={0} flex={1}>
|
<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}
|
{item.name}
|
||||||
</Text>
|
</Text>
|
||||||
<Text fontSize="xs" color="gray.600">
|
<Text fontSize="xs" color="whiteAlpha.600">
|
||||||
累计 {formatChange(item.total_change)}
|
累计 {formatChange(item.total_change)}
|
||||||
</Text>
|
</Text>
|
||||||
</VStack>
|
</VStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme="cyan"
|
bg="cyan.500"
|
||||||
variant="solid"
|
color="white"
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
fontSize="xs"
|
fontSize="xs"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
@@ -689,14 +690,17 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{/* 顶部标题卡片 */}
|
{/* 顶部标题卡片 - 深色玻璃态 */}
|
||||||
<Box
|
<Box
|
||||||
bg="linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
|
bg="rgba(15, 23, 42, 0.8)"
|
||||||
|
backdropFilter="blur(20px)"
|
||||||
p={4}
|
p={4}
|
||||||
borderRadius="xl"
|
borderRadius="xl"
|
||||||
mb={4}
|
mb={4}
|
||||||
position="relative"
|
position="relative"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
|
border="1px solid"
|
||||||
|
borderColor="whiteAlpha.100"
|
||||||
>
|
>
|
||||||
{/* 背景装饰 */}
|
{/* 背景装饰 */}
|
||||||
<Box
|
<Box
|
||||||
@@ -706,8 +710,9 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
width="80px"
|
width="80px"
|
||||||
height="80px"
|
height="80px"
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
bg="whiteAlpha.200"
|
bg="purple.500"
|
||||||
filter="blur(10px)"
|
opacity={0.2}
|
||||||
|
filter="blur(20px)"
|
||||||
/>
|
/>
|
||||||
<Box
|
<Box
|
||||||
position="absolute"
|
position="absolute"
|
||||||
@@ -716,15 +721,16 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
width="60px"
|
width="60px"
|
||||||
height="60px"
|
height="60px"
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
bg="whiteAlpha.100"
|
bg="cyan.500"
|
||||||
filter="blur(8px)"
|
opacity={0.15}
|
||||||
|
filter="blur(15px)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<VStack align="start" spacing={3} position="relative" w="full">
|
<VStack align="start" spacing={3} position="relative" w="full">
|
||||||
<Flex justify="space-between" align="center" w="full">
|
<Flex justify="space-between" align="center" w="full">
|
||||||
<HStack spacing={2}>
|
<HStack spacing={2}>
|
||||||
<Box p={2} bg="whiteAlpha.200" borderRadius="lg">
|
<Box p={2} bg="whiteAlpha.100" borderRadius="lg" border="1px solid" borderColor="cyan.500">
|
||||||
<Icon as={FaChartLine} color="white" boxSize={4} />
|
<Icon as={FaChartLine} color="cyan.400" boxSize={4} />
|
||||||
</Box>
|
</Box>
|
||||||
<VStack align="start" spacing={0}>
|
<VStack align="start" spacing={0}>
|
||||||
<Heading size="sm" color="white" fontWeight="bold">
|
<Heading size="sm" color="white" fontWeight="bold">
|
||||||
@@ -868,8 +874,8 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
</VStack>
|
</VStack>
|
||||||
</Box>
|
</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
|
<Tabs
|
||||||
index={activeTab}
|
index={activeTab}
|
||||||
onChange={(index) => {
|
onChange={(index) => {
|
||||||
@@ -882,7 +888,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
<TabList
|
<TabList
|
||||||
bg="gray.50"
|
bg="rgba(15, 23, 42, 0.6)"
|
||||||
borderBottom="1px"
|
borderBottom="1px"
|
||||||
borderColor={borderColor}
|
borderColor={borderColor}
|
||||||
overflowX="auto"
|
overflowX="auto"
|
||||||
@@ -902,6 +908,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
px={3}
|
px={3}
|
||||||
py={3}
|
py={3}
|
||||||
whiteSpace="nowrap"
|
whiteSpace="nowrap"
|
||||||
|
color="whiteAlpha.700"
|
||||||
_selected={{
|
_selected={{
|
||||||
bg: `${tab.color}.500`,
|
bg: `${tab.color}.500`,
|
||||||
color: 'white',
|
color: 'white',
|
||||||
@@ -917,7 +924,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
bg: `${tab.color}.500`,
|
bg: `${tab.color}.500`,
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
_hover={{ bg: `${tab.color}.50` }}
|
_hover={{ bg: 'whiteAlpha.100', color: 'white' }}
|
||||||
transition="all 0.2s"
|
transition="all 0.2s"
|
||||||
>
|
>
|
||||||
<HStack spacing={1}>
|
<HStack spacing={1}>
|
||||||
|
|||||||
Reference in New Issue
Block a user