- 新增 AlertFilterSection 组件,支持内联显示 - 筛选标签(类型+数量)、异动总数徽章、日期选择器整合到标题行 - 移除与灵活屏重复的三指数卡片组件 - 简化热点概览整体布局结构 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
652 lines
20 KiB
JavaScript
652 lines
20 KiB
JavaScript
/**
|
||
* 热点概览组件 - Modern Spatial & Glassmorphism 设计
|
||
* 展示大盘分时走势 + 概念异动标注
|
||
*
|
||
* 布局设计:
|
||
* - 顶部:标题 + 日期选择器 + 异动数量
|
||
* - 中部:大尺寸分时图(主要展示区域)
|
||
* - 下方:统计卡片(指数信息 + 异动统计)
|
||
* - 底部:异动列表(横向滚动卡片)
|
||
*/
|
||
import React, { useState, useCallback } from 'react';
|
||
import {
|
||
Box,
|
||
Heading,
|
||
Text,
|
||
HStack,
|
||
VStack,
|
||
Spinner,
|
||
Center,
|
||
Spacer,
|
||
Icon,
|
||
Flex,
|
||
Tooltip,
|
||
useDisclosure,
|
||
} from '@chakra-ui/react';
|
||
import { keyframes, css } from '@emotion/react';
|
||
import {
|
||
Flame,
|
||
List,
|
||
LineChart,
|
||
Info,
|
||
Zap,
|
||
AlertCircle,
|
||
TrendingUp,
|
||
TrendingDown,
|
||
Sparkles,
|
||
} from 'lucide-react';
|
||
|
||
import { useHotspotData } from './hooks';
|
||
import { IndexMinuteChart, AlertDetailDrawer, AlertFilterSection } from './components';
|
||
import { ALERT_TYPE_CONFIG, getAlertTypeLabel } from './utils/chartHelpers';
|
||
import {
|
||
glassEffect,
|
||
colors,
|
||
getMarketColor,
|
||
} from '../../theme/glassTheme';
|
||
|
||
// 动画效果
|
||
const gradientShift = keyframes`
|
||
0% { background-position: 0% 50%; }
|
||
50% { background-position: 100% 50%; }
|
||
100% { background-position: 0% 50%; }
|
||
`;
|
||
|
||
const pulseGlow = keyframes`
|
||
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 config = ALERT_TYPE_CONFIG[alert.alert_type] || ALERT_TYPE_CONFIG.surge;
|
||
const isUp = alert.alert_type !== 'surge_down';
|
||
|
||
return (
|
||
<Box
|
||
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.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="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.5}>
|
||
<Text fontSize="xs" color={colors.text.muted} fontFamily="mono">
|
||
{alert.time}
|
||
</Text>
|
||
<HStack
|
||
spacing={1}
|
||
px={2}
|
||
py={0.5}
|
||
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)}
|
||
</Text>
|
||
</HStack>
|
||
</HStack>
|
||
|
||
{/* 概念名称 */}
|
||
<Text
|
||
fontWeight="bold"
|
||
fontSize="sm"
|
||
color={colors.text.primary}
|
||
noOfLines={1}
|
||
mb={1.5}
|
||
css={isSelected ? css`text-shadow: 0 0 20px ${config.color}40;` : undefined}
|
||
>
|
||
{alert.concept_name}
|
||
</Text>
|
||
|
||
{/* 分数 + Alpha */}
|
||
<HStack justify="space-between" fontSize="xs">
|
||
<HStack spacing={1}>
|
||
<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={getMarketColor(alert.alpha)}
|
||
css={css`text-shadow: 0 0 10px ${getMarketColor(alert.alpha)}50;`}
|
||
>
|
||
α {alert.alpha >= 0 ? '+' : ''}{alert.alpha.toFixed(1)}%
|
||
</Text>
|
||
)}
|
||
</HStack>
|
||
</Box>
|
||
);
|
||
};
|
||
|
||
/**
|
||
* 热点概览主组件
|
||
* @param {Object} props
|
||
* @param {Date|null} props.selectedDate - 选中的交易日期
|
||
* @param {Function} props.onDateChange - 日期变更回调
|
||
* @param {Date} props.minDate - 最小可选日期
|
||
* @param {Date} props.maxDate - 最大可选日期
|
||
*/
|
||
const HotspotOverview = ({ selectedDate, onDateChange, minDate, maxDate, onDataLoaded }) => {
|
||
const [selectedAlert, setSelectedAlert] = useState(null);
|
||
const [drawerAlertData, setDrawerAlertData] = useState(null);
|
||
// 选中的异动类型过滤器(null 表示全部)
|
||
const [selectedAlertType, setSelectedAlertType] = useState(null);
|
||
|
||
// 右边栏抽屉控制
|
||
const { isOpen: isDrawerOpen, onOpen: onDrawerOpen, onClose: onDrawerClose } = useDisclosure();
|
||
|
||
// 获取数据
|
||
const { loading, refreshing, error, data } = useHotspotData(selectedDate);
|
||
|
||
// 当数据加载完成时,通知父组件
|
||
React.useEffect(() => {
|
||
if (data && onDataLoaded) {
|
||
onDataLoaded(data);
|
||
}
|
||
}, [data, onDataLoaded]);
|
||
|
||
// Glassmorphism 颜色主题
|
||
const cardBg = glassEffect.card.bg;
|
||
const textColor = colors.text.primary;
|
||
const subTextColor = colors.text.secondary;
|
||
const sectionBg = glassEffect.light.bg;
|
||
const scrollbarColor = 'rgba(139, 92, 246, 0.3)';
|
||
|
||
// 点击分时图上的异动标注 - 打开右边栏抽屉显示详情
|
||
const handleChartAlertClick = useCallback((alertGroupData) => {
|
||
// alertGroupData 包含 { alerts, timeRange, alertCount, time }
|
||
setDrawerAlertData(alertGroupData);
|
||
onDrawerOpen();
|
||
}, [onDrawerOpen]);
|
||
|
||
// 点击底部异动卡片 - 打开右边栏抽屉显示单个异动详情
|
||
const handleCardAlertClick = useCallback((alert) => {
|
||
setSelectedAlert(alert);
|
||
// 构造单个异动的数据格式
|
||
setDrawerAlertData({
|
||
alerts: [alert],
|
||
timeRange: alert.time,
|
||
alertCount: 1,
|
||
time: alert.time,
|
||
});
|
||
onDrawerOpen();
|
||
}, [onDrawerOpen]);
|
||
|
||
// 点击异动类型标签 - 切换过滤器
|
||
const handleAlertTypeClick = useCallback((type) => {
|
||
setSelectedAlertType(prevType => prevType === type ? null : type);
|
||
}, []);
|
||
|
||
// 渲染加载状态 - Glassmorphism 风格
|
||
if (loading) {
|
||
return (
|
||
<Box
|
||
bg={cardBg}
|
||
backdropFilter={glassEffect.card.backdropFilter}
|
||
borderRadius="24px"
|
||
border={glassEffect.card.border}
|
||
boxShadow={glassEffect.card.boxShadow}
|
||
overflow="hidden"
|
||
position="relative"
|
||
>
|
||
{/* 极光背景 */}
|
||
<Box
|
||
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={css`animation: ${gradientShift} 3s ease infinite;`}
|
||
boxShadow="0 0 20px rgba(139, 92, 246, 0.5)"
|
||
/>
|
||
<Center h="500px" p={6}>
|
||
<VStack spacing={6}>
|
||
<Box position="relative">
|
||
<Spinner size="xl" color="#8b5cf6" thickness="3px" speed="0.8s" />
|
||
<Box
|
||
position="absolute"
|
||
inset={-4}
|
||
borderRadius="full"
|
||
css={css`animation: ${pulseGlow} 2s ease-in-out infinite;`}
|
||
boxShadow="0 0 40px rgba(139, 92, 246, 0.4)"
|
||
/>
|
||
</Box>
|
||
<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>
|
||
</Center>
|
||
</Box>
|
||
);
|
||
}
|
||
|
||
// 渲染错误状态 - Glassmorphism 风格
|
||
if (error) {
|
||
return (
|
||
<Box
|
||
bg={cardBg}
|
||
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="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="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="#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>
|
||
</Center>
|
||
</Box>
|
||
);
|
||
}
|
||
|
||
if (!data) return null;
|
||
|
||
const { index, alerts, alert_summary } = data;
|
||
|
||
// 根据选中的类型过滤异动列表
|
||
const filteredAlerts = selectedAlertType
|
||
? alerts.filter(alert => alert.alert_type === selectedAlertType)
|
||
: alerts;
|
||
|
||
|
||
return (
|
||
<Box
|
||
bg={cardBg}
|
||
backdropFilter={glassEffect.card.backdropFilter}
|
||
borderRadius="24px"
|
||
border={glassEffect.card.border}
|
||
boxShadow={glassEffect.card.boxShadow}
|
||
overflow="hidden"
|
||
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
||
position="relative"
|
||
>
|
||
{/* 极光背景装饰 */}
|
||
<Box
|
||
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
|
||
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={3}
|
||
borderRadius="16px"
|
||
bgGradient="linear(to-br, #8b5cf6, #ec4899)"
|
||
boxShadow="0 8px 25px rgba(139, 92, 246, 0.4)"
|
||
position="relative"
|
||
overflow="hidden"
|
||
>
|
||
{/* 图标发光效果 */}
|
||
<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"
|
||
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>
|
||
</Flex>
|
||
|
||
{/* 大尺寸分时图 - Glassmorphism */}
|
||
<Box
|
||
bg={sectionBg}
|
||
backdropFilter={glassEffect.light.backdropFilter}
|
||
borderRadius="20px"
|
||
border={glassEffect.light.border}
|
||
p={5}
|
||
mb={5}
|
||
position="relative"
|
||
overflow="hidden"
|
||
>
|
||
{/* 图表区域背景光晕 */}
|
||
<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"
|
||
/>
|
||
{/* 标题行:大盘分时走势 + 筛选区 */}
|
||
<Flex align="center" mb={4} flexWrap="wrap" gap={3}>
|
||
<HStack spacing={3} flexShrink={0}>
|
||
<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}
|
||
css={css`text-shadow: 0 0 20px rgba(139, 92, 246, 0.3);`}
|
||
>
|
||
大盘分时走势
|
||
</Text>
|
||
<Tooltip label="图表上的标记点表示概念异动时刻,点击可查看详情" hasArrow>
|
||
<Icon as={Info} boxSize={3.5} color={colors.text.muted} cursor="help" />
|
||
</Tooltip>
|
||
</HStack>
|
||
<Spacer />
|
||
{/* 筛选区内联 */}
|
||
<AlertFilterSection
|
||
alertSummary={alert_summary}
|
||
selectedAlertType={selectedAlertType}
|
||
onAlertTypeClick={handleAlertTypeClick}
|
||
onClearFilter={() => setSelectedAlertType(null)}
|
||
totalCount={alerts.length}
|
||
filteredCount={filteredAlerts.length}
|
||
selectedDate={selectedDate}
|
||
onDateChange={onDateChange}
|
||
minDate={minDate}
|
||
maxDate={maxDate}
|
||
/>
|
||
</Flex>
|
||
<Box position="relative">
|
||
<IndexMinuteChart
|
||
indexData={index}
|
||
alerts={alerts}
|
||
onAlertClick={handleChartAlertClick}
|
||
height="420px"
|
||
/>
|
||
{/* 刷新时的轻量级加载指示器 */}
|
||
{refreshing && (
|
||
<Center
|
||
position="absolute"
|
||
top={0}
|
||
left={0}
|
||
right={0}
|
||
bottom={0}
|
||
bg="rgba(0, 0, 0, 0.3)"
|
||
borderRadius="12px"
|
||
zIndex={10}
|
||
>
|
||
<Spinner size="lg" color="#8b5cf6" thickness="3px" />
|
||
</Center>
|
||
)}
|
||
</Box>
|
||
</Box>
|
||
|
||
{/* 异动列表 - Glassmorphism 横向滚动 */}
|
||
{alerts.length > 0 && (
|
||
<Box>
|
||
<HStack spacing={3} mb={4}>
|
||
<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={colors.text.muted}>
|
||
{selectedAlertType
|
||
? `(已筛选 ${ALERT_TYPE_CONFIG[selectedAlertType]?.label || selectedAlertType},共 ${filteredAlerts.length} 条)`
|
||
: '(点击卡片查看详情)'}
|
||
</Text>
|
||
</HStack>
|
||
|
||
{/* 横向滚动卡片 */}
|
||
<Box
|
||
overflowX="auto"
|
||
pb={3}
|
||
sx={{
|
||
'&::-webkit-scrollbar': { height: '6px' },
|
||
'&::-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)' },
|
||
},
|
||
}}
|
||
>
|
||
<HStack spacing={3} pb={1}>
|
||
{[...filteredAlerts]
|
||
.sort((a, b) => (b.time || '').localeCompare(a.time || ''))
|
||
.map((alert, idx) => (
|
||
<CompactAlertCard
|
||
key={`${alert.concept_id}-${alert.time}-${idx}`}
|
||
alert={alert}
|
||
onClick={handleCardAlertClick}
|
||
isSelected={selectedAlert?.concept_id === alert.concept_id && selectedAlert?.time === alert.time}
|
||
/>
|
||
))}
|
||
</HStack>
|
||
</Box>
|
||
</Box>
|
||
)}
|
||
|
||
{/* 无异动提示 - Glassmorphism */}
|
||
{filteredAlerts.length === 0 && (
|
||
<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">
|
||
{selectedAlertType
|
||
? `未找到「${ALERT_TYPE_CONFIG[selectedAlertType]?.label || selectedAlertType}」类型的异动`
|
||
: '当日暂无概念异动数据'}
|
||
</Text>
|
||
{selectedAlertType && (
|
||
<Text
|
||
fontSize="xs"
|
||
color={colors.accent.purple}
|
||
cursor="pointer"
|
||
onClick={() => setSelectedAlertType(null)}
|
||
_hover={{ textDecoration: 'underline' }}
|
||
>
|
||
查看全部异动
|
||
</Text>
|
||
)}
|
||
</VStack>
|
||
</Center>
|
||
)}
|
||
</Box>
|
||
|
||
{/* 异动详情右边栏抽屉 */}
|
||
<AlertDetailDrawer
|
||
isOpen={isDrawerOpen}
|
||
onClose={onDrawerClose}
|
||
alertData={drawerAlertData}
|
||
/>
|
||
</Box>
|
||
);
|
||
};
|
||
|
||
export default HotspotOverview;
|