feat(HotspotOverview): 重构筛选区布局,与分时图标题同行显示
- 新增 AlertFilterSection 组件,支持内联显示 - 筛选标签(类型+数量)、异动总数徽章、日期选择器整合到标题行 - 移除与灵活屏重复的三指数卡片组件 - 简化热点概览整体布局结构 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -17,11 +17,10 @@ import {
|
||||
VStack,
|
||||
Spinner,
|
||||
Center,
|
||||
Spacer,
|
||||
Icon,
|
||||
Flex,
|
||||
Spacer,
|
||||
Tooltip,
|
||||
SimpleGrid,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { keyframes, css } from '@emotion/react';
|
||||
@@ -38,15 +37,12 @@ import {
|
||||
} from 'lucide-react';
|
||||
|
||||
import { useHotspotData } from './hooks';
|
||||
import { IndexMinuteChart, AlertDetailDrawer } from './components';
|
||||
import { IndexMinuteChart, AlertDetailDrawer, AlertFilterSection } from './components';
|
||||
import { ALERT_TYPE_CONFIG, getAlertTypeLabel } from './utils/chartHelpers';
|
||||
import TradeDatePicker from '@components/TradeDatePicker';
|
||||
import {
|
||||
glassEffect,
|
||||
colors,
|
||||
glowEffects,
|
||||
getMarketColor,
|
||||
getMarketGlow,
|
||||
} from '../../theme/glassTheme';
|
||||
|
||||
// 动画效果
|
||||
@@ -198,7 +194,7 @@ const CompactAlertCard = ({ alert, onClick, isSelected }) => {
|
||||
* @param {Date} props.minDate - 最小可选日期
|
||||
* @param {Date} props.maxDate - 最大可选日期
|
||||
*/
|
||||
const HotspotOverview = ({ selectedDate, onDateChange, minDate, maxDate }) => {
|
||||
const HotspotOverview = ({ selectedDate, onDateChange, minDate, maxDate, onDataLoaded }) => {
|
||||
const [selectedAlert, setSelectedAlert] = useState(null);
|
||||
const [drawerAlertData, setDrawerAlertData] = useState(null);
|
||||
// 选中的异动类型过滤器(null 表示全部)
|
||||
@@ -208,11 +204,17 @@ const HotspotOverview = ({ selectedDate, onDateChange, minDate, maxDate }) => {
|
||||
const { isOpen: isDrawerOpen, onOpen: onDrawerOpen, onClose: onDrawerClose } = useDisclosure();
|
||||
|
||||
// 获取数据
|
||||
const { loading, error, data } = useHotspotData(selectedDate);
|
||||
const { loading, refreshing, error, data } = useHotspotData(selectedDate);
|
||||
|
||||
// 当数据加载完成时,通知父组件
|
||||
React.useEffect(() => {
|
||||
if (data && onDataLoaded) {
|
||||
onDataLoaded(data);
|
||||
}
|
||||
}, [data, onDataLoaded]);
|
||||
|
||||
// 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;
|
||||
@@ -352,9 +354,6 @@ const HotspotOverview = ({ selectedDate, onDateChange, minDate, maxDate }) => {
|
||||
? alerts.filter(alert => alert.alert_type === selectedAlertType)
|
||||
: alerts;
|
||||
|
||||
// 计算市场颜色
|
||||
const marketColor = getMarketColor(index?.change_pct || 0);
|
||||
const marketGlow = getMarketGlow(index?.change_pct || 0);
|
||||
|
||||
return (
|
||||
<Box
|
||||
@@ -430,54 +429,9 @@ const HotspotOverview = ({ selectedDate, onDateChange, minDate, maxDate }) => {
|
||||
</HStack>
|
||||
</VStack>
|
||||
</HStack>
|
||||
<Spacer />
|
||||
<HStack spacing={3}>
|
||||
{/* 日期选择器 */}
|
||||
{onDateChange && (
|
||||
<TradeDatePicker
|
||||
value={selectedDate}
|
||||
onChange={onDateChange}
|
||||
latestTradeDate={null}
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
{alerts.length > 0 && (
|
||||
<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" p={2} borderRadius="full" _hover={{ bg: 'rgba(255,255,255,0.05)' }}>
|
||||
<Icon as={Info} color={subTextColor} boxSize={4} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</HStack>
|
||||
</Flex>
|
||||
|
||||
{/* 大尺寸分时图 - Glassmorphism(移到统计卡片前面) */}
|
||||
{/* 大尺寸分时图 - Glassmorphism */}
|
||||
<Box
|
||||
bg={sectionBg}
|
||||
backdropFilter={glassEffect.light.backdropFilter}
|
||||
@@ -501,206 +455,73 @@ const HotspotOverview = ({ selectedDate, onDateChange, minDate, maxDate }) => {
|
||||
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}
|
||||
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>
|
||||
<IndexMinuteChart
|
||||
indexData={index}
|
||||
alerts={alerts}
|
||||
onAlertClick={handleChartAlertClick}
|
||||
height="420px"
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 统计摘要 - 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={1}>
|
||||
<Text fontSize="xs" color={colors.text.muted} letterSpacing="1px" textTransform="uppercase">
|
||||
{index?.name || '上证指数'}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize="3xl"
|
||||
fontWeight="bold"
|
||||
color={marketColor}
|
||||
css={css`text-shadow: 0 0 30px ${marketColor}60;`}
|
||||
>
|
||||
{index?.latest_price?.toFixed(2) || '-'}
|
||||
</Text>
|
||||
</VStack>
|
||||
<VStack align="flex-end" spacing={2}>
|
||||
<HStack
|
||||
spacing={2}
|
||||
px={3}
|
||||
py={1.5}
|
||||
borderRadius="full"
|
||||
bg={`${marketColor}15`}
|
||||
border={`1px solid ${marketColor}25`}
|
||||
>
|
||||
<Icon
|
||||
as={(index?.change_pct || 0) >= 0 ? TrendingUp : TrendingDown}
|
||||
boxSize={4}
|
||||
color={marketColor}
|
||||
css={css`filter: drop-shadow(0 0 4px ${marketColor});`}
|
||||
/>
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight="bold"
|
||||
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={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}
|
||||
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}>
|
||||
<HStack spacing={2}>
|
||||
<Text fontSize="sm" fontWeight="bold" color={textColor}>今日异动</Text>
|
||||
<Text fontSize="xs" color={colors.text.muted}>(点击筛选)</Text>
|
||||
</HStack>
|
||||
<HStack spacing={2}>
|
||||
{selectedAlertType && (
|
||||
<Text
|
||||
fontSize="xs"
|
||||
color={colors.accent.purple}
|
||||
cursor="pointer"
|
||||
onClick={() => setSelectedAlertType(null)}
|
||||
_hover={{ textDecoration: 'underline' }}
|
||||
>
|
||||
清除筛选
|
||||
</Text>
|
||||
)}
|
||||
<Text
|
||||
fontSize="sm"
|
||||
{/* 标题行:大盘分时走势 + 筛选区 */}
|
||||
<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}
|
||||
fontWeight="bold"
|
||||
css={css`text-shadow: 0 0 10px rgba(139, 92, 246, 0.5);`}
|
||||
>
|
||||
{selectedAlertType ? `${filteredAlerts.length}/${alerts.length}` : alerts.length} 次
|
||||
</Text>
|
||||
</HStack>
|
||||
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>
|
||||
<Flex gap={2} flexWrap="wrap">
|
||||
{Object.entries(alert_summary || {})
|
||||
.filter(([_, count]) => count > 0)
|
||||
.slice(0, 5)
|
||||
.map(([type, count]) => {
|
||||
const config = ALERT_TYPE_CONFIG[type];
|
||||
if (!config) return null;
|
||||
const isSelected = selectedAlertType === type;
|
||||
return (
|
||||
<HStack
|
||||
key={type}
|
||||
spacing={1.5}
|
||||
px={3}
|
||||
py={1.5}
|
||||
borderRadius="full"
|
||||
bg={isSelected ? `${config.color}35` : `${config.color}15`}
|
||||
border={isSelected ? `2px solid ${config.color}` : `1px solid ${config.color}25`}
|
||||
cursor="pointer"
|
||||
transition="all 0.2s"
|
||||
transform={isSelected ? 'scale(1.05)' : 'scale(1)'}
|
||||
boxShadow={isSelected ? `0 0 20px ${config.color}40` : 'none'}
|
||||
onClick={() => handleAlertTypeClick(type)}
|
||||
_hover={{
|
||||
bg: `${config.color}25`,
|
||||
boxShadow: `0 0 15px ${config.color}30`,
|
||||
transform: 'scale(1.02)',
|
||||
}}
|
||||
>
|
||||
<Text fontSize="xs" color={config.color} fontWeight={isSelected ? 'bold' : 'medium'}>{config.label}</Text>
|
||||
<Text
|
||||
fontSize="xs"
|
||||
fontWeight="bold"
|
||||
color={config.color}
|
||||
css={css`text-shadow: 0 0 8px ${config.color}50;`}
|
||||
>
|
||||
{count}
|
||||
</Text>
|
||||
</HStack>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
<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>
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
|
||||
{/* 异动列表 - Glassmorphism 横向滚动 */}
|
||||
{alerts.length > 0 && (
|
||||
|
||||
Reference in New Issue
Block a user