update pay ui
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* 热点概览组件 - 科技感设计
|
||||
* 热点概览组件 - Modern Spatial & Glassmorphism 设计
|
||||
* 展示大盘分时走势 + 概念异动标注
|
||||
*
|
||||
* 布局设计:
|
||||
@@ -20,12 +20,11 @@ import {
|
||||
Flex,
|
||||
Spacer,
|
||||
Tooltip,
|
||||
useColorModeValue,
|
||||
IconButton,
|
||||
Collapse,
|
||||
SimpleGrid,
|
||||
} from '@chakra-ui/react';
|
||||
import { keyframes } from '@emotion/react';
|
||||
import { keyframes, css } from '@emotion/react';
|
||||
import {
|
||||
Flame,
|
||||
List,
|
||||
@@ -37,11 +36,19 @@ import {
|
||||
AlertCircle,
|
||||
TrendingUp,
|
||||
TrendingDown,
|
||||
Sparkles,
|
||||
} from 'lucide-react';
|
||||
|
||||
import { useHotspotData } from './hooks';
|
||||
import { IndexMinuteChart, ConceptAlertList, AlertSummary } from './components';
|
||||
import { ALERT_TYPE_CONFIG, getAlertTypeLabel } from './utils/chartHelpers';
|
||||
import {
|
||||
glassEffect,
|
||||
colors,
|
||||
glowEffects,
|
||||
getMarketColor,
|
||||
getMarketGlow,
|
||||
} from '../../theme/glassTheme';
|
||||
|
||||
// 动画效果
|
||||
const gradientShift = keyframes`
|
||||
@@ -51,68 +58,94 @@ const gradientShift = keyframes`
|
||||
`;
|
||||
|
||||
const pulseGlow = keyframes`
|
||||
0%, 100% { opacity: 0.5; }
|
||||
50% { opacity: 1; }
|
||||
0%, 100% { opacity: 0.6; transform: scale(1); }
|
||||
50% { opacity: 1; transform: scale(1.02); }
|
||||
`;
|
||||
|
||||
const floatAnimation = keyframes`
|
||||
0%, 100% { transform: translateY(0px); }
|
||||
50% { transform: translateY(-3px); }
|
||||
`;
|
||||
|
||||
const shimmer = keyframes`
|
||||
0% { background-position: -200% 0; }
|
||||
100% { background-position: 200% 0; }
|
||||
`;
|
||||
|
||||
/**
|
||||
* 紧凑型异动卡片(用于横向滚动)
|
||||
* 紧凑型异动卡片(用于横向滚动)- Glassmorphism 风格
|
||||
*/
|
||||
const CompactAlertCard = ({ alert, onClick, isSelected }) => {
|
||||
const cardBg = useColorModeValue('white', '#0d0d0d');
|
||||
const borderColor = useColorModeValue('gray.200', '#2d2d2d');
|
||||
const textColor = useColorModeValue('gray.800', 'white');
|
||||
const subTextColor = useColorModeValue('gray.500', 'gray.400');
|
||||
|
||||
const config = ALERT_TYPE_CONFIG[alert.alert_type] || ALERT_TYPE_CONFIG.surge;
|
||||
const isUp = alert.alert_type !== 'surge_down';
|
||||
|
||||
return (
|
||||
<Box
|
||||
bg={cardBg}
|
||||
borderRadius="xl"
|
||||
borderWidth="2px"
|
||||
borderColor={isSelected ? config.color : borderColor}
|
||||
bg={glassEffect.light.bg}
|
||||
backdropFilter={glassEffect.light.backdropFilter}
|
||||
borderRadius="16px"
|
||||
border={isSelected ? `1px solid ${config.color}60` : glassEffect.light.border}
|
||||
p={3}
|
||||
minW="180px"
|
||||
maxW="200px"
|
||||
cursor="pointer"
|
||||
onClick={() => onClick?.(alert)}
|
||||
transition="all 0.2s"
|
||||
_hover={{
|
||||
borderColor: config.color,
|
||||
transform: 'translateY(-2px)',
|
||||
boxShadow: `0 4px 15px ${config.color}25`,
|
||||
}}
|
||||
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
_hover={{
|
||||
bg: 'rgba(255, 255, 255, 0.05)',
|
||||
border: `1px solid ${config.color}50`,
|
||||
transform: 'translateY(-4px)',
|
||||
boxShadow: `0 8px 25px ${config.color}20, inset 0 1px 0 rgba(255,255,255,0.1)`,
|
||||
}}
|
||||
css={isSelected ? css`animation: ${floatAnimation} 3s ease-in-out infinite;` : undefined}
|
||||
>
|
||||
{/* 顶部渐变条 */}
|
||||
{/* 顶部渐变发光条 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
h="3px"
|
||||
h="2px"
|
||||
bgGradient={`linear(to-r, ${config.gradient[0]}, ${config.gradient[1]})`}
|
||||
opacity={isSelected ? 1 : 0.7}
|
||||
boxShadow={isSelected ? `0 0 15px ${config.color}60` : 'none'}
|
||||
/>
|
||||
|
||||
{/* 背景光晕 */}
|
||||
{isSelected && (
|
||||
<Box
|
||||
position="absolute"
|
||||
top="-30px"
|
||||
right="-30px"
|
||||
w="80px"
|
||||
h="80px"
|
||||
borderRadius="full"
|
||||
bg={`${config.color}15`}
|
||||
filter="blur(25px)"
|
||||
pointerEvents="none"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 时间 + 类型 */}
|
||||
<HStack justify="space-between" mb={1}>
|
||||
<Text fontSize="xs" color={subTextColor} fontFamily="mono">
|
||||
<HStack justify="space-between" mb={1.5}>
|
||||
<Text fontSize="xs" color={colors.text.muted} fontFamily="mono">
|
||||
{alert.time}
|
||||
</Text>
|
||||
<HStack
|
||||
spacing={1}
|
||||
px={1.5}
|
||||
px={2}
|
||||
py={0.5}
|
||||
borderRadius="md"
|
||||
bg={`${config.color}15`}
|
||||
borderRadius="full"
|
||||
bg={`${config.color}20`}
|
||||
border={`1px solid ${config.color}30`}
|
||||
>
|
||||
<Icon
|
||||
as={isUp ? TrendingUp : TrendingDown}
|
||||
boxSize={3}
|
||||
color={config.color}
|
||||
css={css`filter: drop-shadow(0 0 4px ${config.color}80);`}
|
||||
/>
|
||||
<Text fontSize="10px" fontWeight="bold" color={config.color}>
|
||||
{getAlertTypeLabel(alert.alert_type)}
|
||||
@@ -124,9 +157,10 @@ const CompactAlertCard = ({ alert, onClick, isSelected }) => {
|
||||
<Text
|
||||
fontWeight="bold"
|
||||
fontSize="sm"
|
||||
color={textColor}
|
||||
color={colors.text.primary}
|
||||
noOfLines={1}
|
||||
mb={1}
|
||||
mb={1.5}
|
||||
css={isSelected ? css`text-shadow: 0 0 20px ${config.color}40;` : undefined}
|
||||
>
|
||||
{alert.concept_name}
|
||||
</Text>
|
||||
@@ -134,15 +168,20 @@ const CompactAlertCard = ({ alert, onClick, isSelected }) => {
|
||||
{/* 分数 + Alpha */}
|
||||
<HStack justify="space-between" fontSize="xs">
|
||||
<HStack spacing={1}>
|
||||
<Text color={subTextColor}>评分</Text>
|
||||
<Text fontWeight="bold" color={config.color}>
|
||||
<Text color={colors.text.tertiary}>评分</Text>
|
||||
<Text
|
||||
fontWeight="bold"
|
||||
color={config.color}
|
||||
css={css`text-shadow: 0 0 10px ${config.color}50;`}
|
||||
>
|
||||
{Math.round(alert.final_score || 0)}
|
||||
</Text>
|
||||
</HStack>
|
||||
{alert.alpha != null && (
|
||||
<Text
|
||||
fontWeight="bold"
|
||||
color={alert.alpha >= 0 ? '#ff4d4f' : '#52c41a'}
|
||||
color={getMarketColor(alert.alpha)}
|
||||
css={css`text-shadow: 0 0 10px ${getMarketColor(alert.alpha)}50;`}
|
||||
>
|
||||
α {alert.alpha >= 0 ? '+' : ''}{alert.alpha.toFixed(1)}%
|
||||
</Text>
|
||||
@@ -160,53 +199,76 @@ const CompactAlertCard = ({ alert, onClick, isSelected }) => {
|
||||
const HotspotOverview = ({ selectedDate }) => {
|
||||
const [selectedAlert, setSelectedAlert] = useState(null);
|
||||
const [showDetailList, setShowDetailList] = useState(false);
|
||||
const [autoExpandAlertKey, setAutoExpandAlertKey] = useState(null);
|
||||
|
||||
// 获取数据
|
||||
const { loading, error, data } = useHotspotData(selectedDate);
|
||||
|
||||
// 颜色主题
|
||||
const cardBg = useColorModeValue('white', '#0a0a0a');
|
||||
const borderColor = useColorModeValue('gray.200', '#1f1f1f');
|
||||
const textColor = useColorModeValue('gray.800', 'white');
|
||||
const subTextColor = useColorModeValue('gray.600', 'gray.400');
|
||||
const sectionBg = useColorModeValue('gray.50', '#0d0d0d');
|
||||
const scrollbarColor = useColorModeValue('#ddd', '#333');
|
||||
// Glassmorphism 颜色主题
|
||||
const cardBg = glassEffect.card.bg;
|
||||
const borderColor = colors.border.primary;
|
||||
const textColor = colors.text.primary;
|
||||
const subTextColor = colors.text.secondary;
|
||||
const sectionBg = glassEffect.light.bg;
|
||||
const scrollbarColor = 'rgba(139, 92, 246, 0.3)';
|
||||
|
||||
// 点击异动标注
|
||||
// 点击异动标注 - 自动展开详细列表并选中
|
||||
const handleAlertClick = useCallback((alert) => {
|
||||
setSelectedAlert(alert);
|
||||
// 自动展开详细列表并设置需要展开的项
|
||||
setShowDetailList(true);
|
||||
const alertKey = `${alert.concept_id}-${alert.time}`;
|
||||
setAutoExpandAlertKey(alertKey);
|
||||
}, []);
|
||||
|
||||
// 渲染加载状态
|
||||
// 渲染加载状态 - Glassmorphism 风格
|
||||
if (loading) {
|
||||
return (
|
||||
<Box
|
||||
bg={cardBg}
|
||||
borderRadius="2xl"
|
||||
borderWidth="1px"
|
||||
borderColor={borderColor}
|
||||
backdropFilter={glassEffect.card.backdropFilter}
|
||||
borderRadius="24px"
|
||||
border={glassEffect.card.border}
|
||||
boxShadow={glassEffect.card.boxShadow}
|
||||
overflow="hidden"
|
||||
position="relative"
|
||||
>
|
||||
{/* 极光背景 */}
|
||||
<Box
|
||||
h="4px"
|
||||
bgGradient="linear(to-r, orange.400, red.500, pink.500)"
|
||||
position="absolute"
|
||||
inset={0}
|
||||
bgGradient="radial(ellipse at 30% 20%, rgba(139, 92, 246, 0.15) 0%, transparent 50%)"
|
||||
pointerEvents="none"
|
||||
/>
|
||||
{/* 顶部发光条 */}
|
||||
<Box
|
||||
h="3px"
|
||||
bgGradient="linear(to-r, #8b5cf6, #ec4899, #f97316)"
|
||||
backgroundSize="200% 200%"
|
||||
css={{ animation: `${gradientShift} 3s ease infinite` }}
|
||||
css={css`animation: ${gradientShift} 3s ease infinite;`}
|
||||
boxShadow="0 0 20px rgba(139, 92, 246, 0.5)"
|
||||
/>
|
||||
<Center h="500px" p={6}>
|
||||
<VStack spacing={4}>
|
||||
<VStack spacing={6}>
|
||||
<Box position="relative">
|
||||
<Spinner size="xl" color="orange.400" thickness="3px" speed="0.8s" />
|
||||
<Spinner size="xl" color="#8b5cf6" thickness="3px" speed="0.8s" />
|
||||
<Box
|
||||
position="absolute"
|
||||
inset={0}
|
||||
inset={-4}
|
||||
borderRadius="full"
|
||||
css={{ animation: `${pulseGlow} 2s ease-in-out infinite` }}
|
||||
boxShadow="0 0 30px rgba(251, 146, 60, 0.3)"
|
||||
css={css`animation: ${pulseGlow} 2s ease-in-out infinite;`}
|
||||
boxShadow="0 0 40px rgba(139, 92, 246, 0.4)"
|
||||
/>
|
||||
</Box>
|
||||
<VStack spacing={1}>
|
||||
<Text color={textColor} fontWeight="medium">加载热点概览数据</Text>
|
||||
<VStack spacing={2}>
|
||||
<Text
|
||||
color={textColor}
|
||||
fontWeight="bold"
|
||||
fontSize="lg"
|
||||
css={css`text-shadow: 0 0 20px rgba(139, 92, 246, 0.5);`}
|
||||
>
|
||||
加载热点概览数据
|
||||
</Text>
|
||||
<Text color={subTextColor} fontSize="sm">正在获取市场异动信息...</Text>
|
||||
</VStack>
|
||||
</VStack>
|
||||
@@ -215,24 +277,42 @@ const HotspotOverview = ({ selectedDate }) => {
|
||||
);
|
||||
}
|
||||
|
||||
// 渲染错误状态
|
||||
// 渲染错误状态 - Glassmorphism 风格
|
||||
if (error) {
|
||||
return (
|
||||
<Box
|
||||
bg={cardBg}
|
||||
borderRadius="2xl"
|
||||
borderWidth="1px"
|
||||
borderColor={borderColor}
|
||||
backdropFilter={glassEffect.card.backdropFilter}
|
||||
borderRadius="24px"
|
||||
border="1px solid rgba(239, 68, 68, 0.3)"
|
||||
boxShadow="0 8px 32px rgba(239, 68, 68, 0.1)"
|
||||
overflow="hidden"
|
||||
position="relative"
|
||||
>
|
||||
<Box h="4px" bg="red.500" />
|
||||
<Box h="3px" bg="#ef4444" boxShadow="0 0 15px rgba(239, 68, 68, 0.5)" />
|
||||
<Center h="400px" p={6}>
|
||||
<VStack spacing={4}>
|
||||
<Box p={4} borderRadius="full" bg="red.50">
|
||||
<Icon as={AlertCircle} boxSize={10} color="red.400" />
|
||||
<Box
|
||||
p={4}
|
||||
borderRadius="full"
|
||||
bg="rgba(239, 68, 68, 0.1)"
|
||||
border="1px solid rgba(239, 68, 68, 0.2)"
|
||||
>
|
||||
<Icon
|
||||
as={AlertCircle}
|
||||
boxSize={10}
|
||||
color="#ef4444"
|
||||
css={css`filter: drop-shadow(0 0 10px rgba(239, 68, 68, 0.5));`}
|
||||
/>
|
||||
</Box>
|
||||
<VStack spacing={1}>
|
||||
<Text color="red.400" fontWeight="medium">数据加载失败</Text>
|
||||
<Text
|
||||
color="#ef4444"
|
||||
fontWeight="bold"
|
||||
css={css`text-shadow: 0 0 15px rgba(239, 68, 68, 0.5);`}
|
||||
>
|
||||
数据加载失败
|
||||
</Text>
|
||||
<Text color={subTextColor} fontSize="sm" textAlign="center">{error}</Text>
|
||||
</VStack>
|
||||
</VStack>
|
||||
@@ -245,124 +325,258 @@ const HotspotOverview = ({ selectedDate }) => {
|
||||
|
||||
const { index, alerts, alert_summary } = data;
|
||||
|
||||
// 计算市场颜色
|
||||
const marketColor = getMarketColor(index?.change_pct || 0);
|
||||
const marketGlow = getMarketGlow(index?.change_pct || 0);
|
||||
|
||||
return (
|
||||
<Box
|
||||
bg={cardBg}
|
||||
borderRadius="2xl"
|
||||
borderWidth="1px"
|
||||
borderColor={borderColor}
|
||||
backdropFilter={glassEffect.card.backdropFilter}
|
||||
borderRadius="24px"
|
||||
border={glassEffect.card.border}
|
||||
boxShadow={glassEffect.card.boxShadow}
|
||||
overflow="hidden"
|
||||
transition="all 0.3s"
|
||||
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
||||
position="relative"
|
||||
>
|
||||
{/* 顶部装饰条 */}
|
||||
{/* 极光背景装饰 */}
|
||||
<Box
|
||||
h="4px"
|
||||
bgGradient="linear(to-r, orange.400, red.500, pink.500)"
|
||||
backgroundSize="200% 200%"
|
||||
css={{ animation: `${gradientShift} 3s ease infinite` }}
|
||||
position="absolute"
|
||||
inset={0}
|
||||
bgGradient="radial(ellipse at 20% 10%, rgba(139, 92, 246, 0.12) 0%, transparent 50%)"
|
||||
pointerEvents="none"
|
||||
/>
|
||||
<Box
|
||||
position="absolute"
|
||||
inset={0}
|
||||
bgGradient="radial(ellipse at 80% 90%, rgba(236, 72, 153, 0.08) 0%, transparent 50%)"
|
||||
pointerEvents="none"
|
||||
/>
|
||||
|
||||
<Box p={5}>
|
||||
{/* 头部 */}
|
||||
<Flex align="center" mb={4}>
|
||||
<HStack spacing={3}>
|
||||
{/* 顶部发光装饰条 */}
|
||||
<Box
|
||||
h="3px"
|
||||
bgGradient="linear(to-r, #8b5cf6, #ec4899, #f97316)"
|
||||
backgroundSize="200% 200%"
|
||||
css={css`animation: ${gradientShift} 3s ease infinite;`}
|
||||
boxShadow="0 0 20px rgba(139, 92, 246, 0.5)"
|
||||
/>
|
||||
|
||||
<Box p={6} position="relative">
|
||||
{/* 头部 - Glassmorphism */}
|
||||
<Flex align="center" mb={5}>
|
||||
<HStack spacing={4}>
|
||||
<Box
|
||||
p={2}
|
||||
borderRadius="xl"
|
||||
bgGradient="linear(to-br, orange.400, red.500)"
|
||||
boxShadow="0 4px 15px rgba(251, 146, 60, 0.4)"
|
||||
p={3}
|
||||
borderRadius="16px"
|
||||
bgGradient="linear(to-br, #8b5cf6, #ec4899)"
|
||||
boxShadow="0 8px 25px rgba(139, 92, 246, 0.4)"
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
>
|
||||
<Icon as={Flame} boxSize={5} color="white" />
|
||||
{/* 图标发光效果 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
inset={0}
|
||||
bgGradient="linear(to-br, rgba(255,255,255,0.2), transparent)"
|
||||
/>
|
||||
<Icon
|
||||
as={Flame}
|
||||
boxSize={6}
|
||||
color="white"
|
||||
css={css`filter: drop-shadow(0 0 8px rgba(255,255,255,0.5));`}
|
||||
/>
|
||||
</Box>
|
||||
<VStack align="flex-start" spacing={0}>
|
||||
<Heading size="md" color={textColor} fontWeight="bold">热点概览</Heading>
|
||||
<Text fontSize="xs" color={subTextColor}>实时概念异动监控</Text>
|
||||
<Heading
|
||||
size="md"
|
||||
color={textColor}
|
||||
fontWeight="bold"
|
||||
css={css`text-shadow: 0 0 30px rgba(139, 92, 246, 0.3);`}
|
||||
>
|
||||
热点概览
|
||||
</Heading>
|
||||
<HStack spacing={1}>
|
||||
<Icon as={Sparkles} boxSize={3} color={colors.accent.purple} />
|
||||
<Text fontSize="xs" color={subTextColor}>实时概念异动监控</Text>
|
||||
</HStack>
|
||||
</VStack>
|
||||
</HStack>
|
||||
<Spacer />
|
||||
<HStack spacing={2}>
|
||||
<HStack spacing={3}>
|
||||
{alerts.length > 0 && (
|
||||
<HStack spacing={1} px={3} py={1.5} borderRadius="full" bg="orange.50">
|
||||
<Icon as={Zap} boxSize={3.5} color="orange.500" />
|
||||
<Text fontSize="sm" fontWeight="bold" color="orange.500">{alerts.length}</Text>
|
||||
<HStack
|
||||
spacing={2}
|
||||
px={4}
|
||||
py={2}
|
||||
borderRadius="full"
|
||||
bg="rgba(139, 92, 246, 0.15)"
|
||||
border="1px solid rgba(139, 92, 246, 0.3)"
|
||||
boxShadow="0 0 15px rgba(139, 92, 246, 0.2)"
|
||||
>
|
||||
<Icon
|
||||
as={Zap}
|
||||
boxSize={4}
|
||||
color={colors.accent.purple}
|
||||
css={css`filter: drop-shadow(0 0 6px #8b5cf6);`}
|
||||
/>
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight="bold"
|
||||
color={colors.accent.purple}
|
||||
css={css`text-shadow: 0 0 10px rgba(139, 92, 246, 0.5);`}
|
||||
>
|
||||
{alerts.length}
|
||||
</Text>
|
||||
</HStack>
|
||||
)}
|
||||
<Tooltip label="展示大盘走势与概念异动的关联" hasArrow maxW="200px">
|
||||
<Box cursor="help">
|
||||
<Box cursor="help" p={2} borderRadius="full" _hover={{ bg: 'rgba(255,255,255,0.05)' }}>
|
||||
<Icon as={Info} color={subTextColor} boxSize={4} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</HStack>
|
||||
</Flex>
|
||||
|
||||
{/* 统计摘要 - 简化版 */}
|
||||
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={4} mb={4}>
|
||||
{/* 指数信息 */}
|
||||
<Box bg={sectionBg} borderRadius="xl" p={4} borderWidth="1px" borderColor={borderColor}>
|
||||
{/* 统计摘要 - Glassmorphism Bento Grid */}
|
||||
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={4} mb={5}>
|
||||
{/* 指数信息卡片 */}
|
||||
<Box
|
||||
bg={sectionBg}
|
||||
backdropFilter={glassEffect.light.backdropFilter}
|
||||
borderRadius="20px"
|
||||
border={glassEffect.light.border}
|
||||
p={5}
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
transition="all 0.3s"
|
||||
_hover={{
|
||||
border: `1px solid ${marketColor}30`,
|
||||
boxShadow: `0 8px 30px ${marketColor}15`,
|
||||
}}
|
||||
>
|
||||
{/* 背景光晕 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top="-20px"
|
||||
right="-20px"
|
||||
w="100px"
|
||||
h="100px"
|
||||
borderRadius="full"
|
||||
bg={`${marketColor}10`}
|
||||
filter="blur(30px)"
|
||||
pointerEvents="none"
|
||||
/>
|
||||
<HStack justify="space-between" align="flex-start">
|
||||
<VStack align="flex-start" spacing={0}>
|
||||
<Text fontSize="xs" color={subTextColor}>{index?.name || '上证指数'}</Text>
|
||||
<VStack align="flex-start" spacing={1}>
|
||||
<Text fontSize="xs" color={colors.text.muted} letterSpacing="1px" textTransform="uppercase">
|
||||
{index?.name || '上证指数'}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize="2xl"
|
||||
fontSize="3xl"
|
||||
fontWeight="bold"
|
||||
color={(index?.change_pct || 0) >= 0 ? '#ff4d4f' : '#52c41a'}
|
||||
color={marketColor}
|
||||
css={css`text-shadow: 0 0 30px ${marketColor}60;`}
|
||||
>
|
||||
{index?.latest_price?.toFixed(2) || '-'}
|
||||
</Text>
|
||||
</VStack>
|
||||
<VStack align="flex-end" spacing={0}>
|
||||
<VStack align="flex-end" spacing={2}>
|
||||
<HStack
|
||||
spacing={1}
|
||||
px={2}
|
||||
py={1}
|
||||
spacing={2}
|
||||
px={3}
|
||||
py={1.5}
|
||||
borderRadius="full"
|
||||
bg={(index?.change_pct || 0) >= 0 ? 'red.50' : 'green.50'}
|
||||
bg={`${marketColor}15`}
|
||||
border={`1px solid ${marketColor}25`}
|
||||
>
|
||||
<Icon
|
||||
as={(index?.change_pct || 0) >= 0 ? TrendingUp : TrendingDown}
|
||||
boxSize={3}
|
||||
color={(index?.change_pct || 0) >= 0 ? '#ff4d4f' : '#52c41a'}
|
||||
boxSize={4}
|
||||
color={marketColor}
|
||||
css={css`filter: drop-shadow(0 0 4px ${marketColor});`}
|
||||
/>
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight="bold"
|
||||
color={(index?.change_pct || 0) >= 0 ? '#ff4d4f' : '#52c41a'}
|
||||
color={marketColor}
|
||||
css={css`text-shadow: 0 0 10px ${marketColor}50;`}
|
||||
>
|
||||
{(index?.change_pct || 0) >= 0 ? '+' : ''}{(index?.change_pct || 0).toFixed(2)}%
|
||||
</Text>
|
||||
</HStack>
|
||||
<HStack spacing={3} mt={1} fontSize="xs" color={subTextColor}>
|
||||
<Text>高 <Text as="span" color="#ff4d4f" fontWeight="bold">{index?.high?.toFixed(2)}</Text></Text>
|
||||
<Text>低 <Text as="span" color="#52c41a" fontWeight="bold">{index?.low?.toFixed(2)}</Text></Text>
|
||||
<HStack spacing={4} fontSize="xs" color={colors.text.tertiary}>
|
||||
<HStack spacing={1}>
|
||||
<Text>高</Text>
|
||||
<Text color={colors.market.up} fontWeight="bold">{index?.high?.toFixed(2)}</Text>
|
||||
</HStack>
|
||||
<HStack spacing={1}>
|
||||
<Text>低</Text>
|
||||
<Text color={colors.market.down} fontWeight="bold">{index?.low?.toFixed(2)}</Text>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</VStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
{/* 异动统计 */}
|
||||
<Box bg={sectionBg} borderRadius="xl" p={4} borderWidth="1px" borderColor={borderColor}>
|
||||
<HStack justify="space-between" mb={2}>
|
||||
<Text fontSize="sm" fontWeight="medium" color={textColor}>今日异动</Text>
|
||||
<Text fontSize="xs" color="orange.500" fontWeight="bold">{alerts.length} 次</Text>
|
||||
{/* 异动统计卡片 */}
|
||||
<Box
|
||||
bg={sectionBg}
|
||||
backdropFilter={glassEffect.light.backdropFilter}
|
||||
borderRadius="20px"
|
||||
border={glassEffect.light.border}
|
||||
p={5}
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
transition="all 0.3s"
|
||||
_hover={{
|
||||
border: '1px solid rgba(139, 92, 246, 0.3)',
|
||||
boxShadow: '0 8px 30px rgba(139, 92, 246, 0.1)',
|
||||
}}
|
||||
>
|
||||
<HStack justify="space-between" mb={3}>
|
||||
<Text fontSize="sm" fontWeight="bold" color={textColor}>今日异动</Text>
|
||||
<Text
|
||||
fontSize="sm"
|
||||
color={colors.accent.purple}
|
||||
fontWeight="bold"
|
||||
css={css`text-shadow: 0 0 10px rgba(139, 92, 246, 0.5);`}
|
||||
>
|
||||
{alerts.length} 次
|
||||
</Text>
|
||||
</HStack>
|
||||
<Flex gap={2} flexWrap="wrap">
|
||||
{Object.entries(alert_summary || {})
|
||||
.filter(([_, count]) => count > 0)
|
||||
.slice(0, 4)
|
||||
.slice(0, 5)
|
||||
.map(([type, count]) => {
|
||||
const config = ALERT_TYPE_CONFIG[type];
|
||||
if (!config) return null;
|
||||
return (
|
||||
<HStack
|
||||
key={type}
|
||||
spacing={1}
|
||||
px={2}
|
||||
py={1}
|
||||
borderRadius="md"
|
||||
bg={`${config.color}10`}
|
||||
spacing={1.5}
|
||||
px={3}
|
||||
py={1.5}
|
||||
borderRadius="full"
|
||||
bg={`${config.color}15`}
|
||||
border={`1px solid ${config.color}25`}
|
||||
transition="all 0.2s"
|
||||
_hover={{
|
||||
bg: `${config.color}25`,
|
||||
boxShadow: `0 0 15px ${config.color}30`,
|
||||
}}
|
||||
>
|
||||
<Text fontSize="xs" color={config.color}>{config.label}</Text>
|
||||
<Text fontSize="xs" fontWeight="bold" color={config.color}>{count}</Text>
|
||||
<Text
|
||||
fontSize="xs"
|
||||
fontWeight="bold"
|
||||
color={config.color}
|
||||
css={css`text-shadow: 0 0 8px ${config.color}50;`}
|
||||
>
|
||||
{count}
|
||||
</Text>
|
||||
</HStack>
|
||||
);
|
||||
})}
|
||||
@@ -370,22 +584,54 @@ const HotspotOverview = ({ selectedDate }) => {
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
|
||||
{/* 大尺寸分时图 */}
|
||||
{/* 大尺寸分时图 - Glassmorphism */}
|
||||
<Box
|
||||
bg={sectionBg}
|
||||
borderRadius="xl"
|
||||
borderWidth="1px"
|
||||
borderColor={borderColor}
|
||||
p={4}
|
||||
mb={4}
|
||||
backdropFilter={glassEffect.light.backdropFilter}
|
||||
borderRadius="20px"
|
||||
border={glassEffect.light.border}
|
||||
p={5}
|
||||
mb={5}
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
>
|
||||
<HStack spacing={2} mb={3}>
|
||||
<Box p={1.5} borderRadius="lg" bg="purple.50">
|
||||
<Icon as={LineChart} boxSize={4} color="purple.500" />
|
||||
{/* 图表区域背景光晕 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
bottom="-50px"
|
||||
left="50%"
|
||||
transform="translateX(-50%)"
|
||||
w="60%"
|
||||
h="100px"
|
||||
borderRadius="full"
|
||||
bg="rgba(139, 92, 246, 0.08)"
|
||||
filter="blur(40px)"
|
||||
pointerEvents="none"
|
||||
/>
|
||||
<HStack spacing={3} mb={4}>
|
||||
<Box
|
||||
p={2}
|
||||
borderRadius="12px"
|
||||
bg="rgba(139, 92, 246, 0.15)"
|
||||
border="1px solid rgba(139, 92, 246, 0.25)"
|
||||
>
|
||||
<Icon
|
||||
as={LineChart}
|
||||
boxSize={5}
|
||||
color={colors.accent.purple}
|
||||
css={css`filter: drop-shadow(0 0 6px #8b5cf6);`}
|
||||
/>
|
||||
</Box>
|
||||
<Text fontSize="sm" fontWeight="bold" color={textColor}>大盘分时走势</Text>
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight="bold"
|
||||
color={textColor}
|
||||
css={css`text-shadow: 0 0 20px rgba(139, 92, 246, 0.3);`}
|
||||
>
|
||||
大盘分时走势
|
||||
</Text>
|
||||
<Tooltip label="图表上的标记点表示概念异动时刻,点击可查看详情" hasArrow>
|
||||
<Icon as={Info} boxSize={3} color={subTextColor} cursor="help" />
|
||||
<Icon as={Info} boxSize={3.5} color={colors.text.muted} cursor="help" />
|
||||
</Tooltip>
|
||||
</HStack>
|
||||
<IndexMinuteChart
|
||||
@@ -396,23 +642,38 @@ const HotspotOverview = ({ selectedDate }) => {
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 异动列表 - 横向滚动 */}
|
||||
{/* 异动列表 - Glassmorphism 横向滚动 */}
|
||||
{alerts.length > 0 && (
|
||||
<Box>
|
||||
<Flex justify="space-between" align="center" mb={3}>
|
||||
<HStack spacing={2}>
|
||||
<Box p={1.5} borderRadius="lg" bg="orange.50">
|
||||
<Icon as={List} boxSize={4} color="orange.500" />
|
||||
<Flex justify="space-between" align="center" mb={4}>
|
||||
<HStack spacing={3}>
|
||||
<Box
|
||||
p={2}
|
||||
borderRadius="12px"
|
||||
bg="rgba(249, 115, 22, 0.15)"
|
||||
border="1px solid rgba(249, 115, 22, 0.25)"
|
||||
>
|
||||
<Icon
|
||||
as={List}
|
||||
boxSize={5}
|
||||
color={colors.accent.orange}
|
||||
css={css`filter: drop-shadow(0 0 6px #f97316);`}
|
||||
/>
|
||||
</Box>
|
||||
<Text fontSize="sm" fontWeight="bold" color={textColor}>异动记录</Text>
|
||||
<Text fontSize="xs" color={subTextColor}>(横向滚动查看更多)</Text>
|
||||
<Text fontSize="xs" color={colors.text.muted}>(点击卡片查看个股详情)</Text>
|
||||
</HStack>
|
||||
<Tooltip label={showDetailList ? '收起详细列表' : '展开详细列表'} hasArrow>
|
||||
<IconButton
|
||||
icon={<Icon as={showDetailList ? ChevronUp : ChevronDown} boxSize={4} />}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
borderRadius="lg"
|
||||
borderRadius="12px"
|
||||
color={colors.text.secondary}
|
||||
_hover={{
|
||||
bg: 'rgba(255,255,255,0.05)',
|
||||
color: textColor,
|
||||
}}
|
||||
onClick={() => setShowDetailList(!showDetailList)}
|
||||
aria-label="切换详细列表"
|
||||
/>
|
||||
@@ -422,13 +683,14 @@ const HotspotOverview = ({ selectedDate }) => {
|
||||
{/* 横向滚动卡片 */}
|
||||
<Box
|
||||
overflowX="auto"
|
||||
pb={2}
|
||||
pb={3}
|
||||
sx={{
|
||||
'&::-webkit-scrollbar': { height: '6px' },
|
||||
'&::-webkit-scrollbar-track': { background: 'transparent' },
|
||||
'&::-webkit-scrollbar-track': { background: 'rgba(255,255,255,0.02)', borderRadius: '3px' },
|
||||
'&::-webkit-scrollbar-thumb': {
|
||||
background: scrollbarColor,
|
||||
borderRadius: '3px',
|
||||
'&:hover': { background: 'rgba(139, 92, 246, 0.5)' },
|
||||
},
|
||||
}}
|
||||
>
|
||||
@@ -446,33 +708,84 @@ const HotspotOverview = ({ selectedDate }) => {
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
{/* 详细列表(可展开) */}
|
||||
{/* 详细列表(可展开) - Glassmorphism */}
|
||||
<Collapse in={showDetailList} animateOpacity>
|
||||
<Box
|
||||
mt={4}
|
||||
bg={sectionBg}
|
||||
borderRadius="xl"
|
||||
borderWidth="1px"
|
||||
borderColor={borderColor}
|
||||
p={4}
|
||||
backdropFilter={glassEffect.light.backdropFilter}
|
||||
borderRadius="20px"
|
||||
border={glassEffect.light.border}
|
||||
p={5}
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
>
|
||||
{/* 背景光晕 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top="50%"
|
||||
left="50%"
|
||||
transform="translate(-50%, -50%)"
|
||||
w="80%"
|
||||
h="200px"
|
||||
borderRadius="full"
|
||||
bg="rgba(139, 92, 246, 0.05)"
|
||||
filter="blur(60px)"
|
||||
pointerEvents="none"
|
||||
/>
|
||||
<ConceptAlertList
|
||||
alerts={alerts}
|
||||
onAlertClick={handleAlertClick}
|
||||
selectedAlert={selectedAlert}
|
||||
maxHeight="400px"
|
||||
autoExpandAlertKey={autoExpandAlertKey}
|
||||
onAutoExpandComplete={() => setAutoExpandAlertKey(null)}
|
||||
/>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* 无异动提示 */}
|
||||
{/* 无异动提示 - Glassmorphism */}
|
||||
{alerts.length === 0 && (
|
||||
<Center py={8} bg={sectionBg} borderRadius="xl">
|
||||
<VStack spacing={2}>
|
||||
<Icon as={Zap} boxSize={8} color={subTextColor} opacity={0.5} />
|
||||
<Text color={subTextColor} fontSize="sm">当日暂无概念异动数据</Text>
|
||||
<Center
|
||||
py={12}
|
||||
bg={sectionBg}
|
||||
backdropFilter={glassEffect.light.backdropFilter}
|
||||
borderRadius="20px"
|
||||
border={glassEffect.light.border}
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
>
|
||||
{/* 背景光晕 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top="50%"
|
||||
left="50%"
|
||||
transform="translate(-50%, -50%)"
|
||||
w="150px"
|
||||
h="150px"
|
||||
borderRadius="full"
|
||||
bg="rgba(139, 92, 246, 0.1)"
|
||||
filter="blur(40px)"
|
||||
pointerEvents="none"
|
||||
/>
|
||||
<VStack spacing={3}>
|
||||
<Box
|
||||
p={4}
|
||||
borderRadius="full"
|
||||
bg="rgba(139, 92, 246, 0.1)"
|
||||
border="1px solid rgba(139, 92, 246, 0.2)"
|
||||
>
|
||||
<Icon
|
||||
as={Zap}
|
||||
boxSize={8}
|
||||
color={colors.accent.purple}
|
||||
opacity={0.6}
|
||||
css={css`filter: drop-shadow(0 0 10px rgba(139, 92, 246, 0.3));`}
|
||||
/>
|
||||
</Box>
|
||||
<Text color={colors.text.tertiary} fontSize="sm">当日暂无概念异动数据</Text>
|
||||
</VStack>
|
||||
</Center>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user