Files
vf_react/src/views/Community/components/HeroPanel.js
2025-12-01 14:01:14 +08:00

900 lines
29 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/views/Community/components/HeroPanel.js
// 顶部说明面板组件:事件中心 + 沪深指数K线图 + 热门概念3D动画
import React, { useEffect, useState, useMemo, useCallback, useRef } from 'react';
import {
Box,
Card,
CardBody,
Flex,
VStack,
HStack,
Text,
Heading,
useColorModeValue,
Icon,
Spinner,
Center,
} from '@chakra-ui/react';
import { AlertCircle, Clock, TrendingUp, Info } from 'lucide-react';
import ReactECharts from 'echarts-for-react';
import { logger } from '../../../utils/logger';
// 定义动画
const animations = `
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.6; transform: scale(1.1); }
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
@keyframes floatSlow {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
`;
// 注入样式
if (typeof document !== 'undefined') {
const styleId = 'hero-panel-animations';
if (!document.getElementById(styleId)) {
const styleSheet = document.createElement('style');
styleSheet.id = styleId;
styleSheet.innerText = animations;
document.head.appendChild(styleSheet);
}
}
/**
* 获取指数行情数据(日线数据)
*/
const fetchIndexKline = async (indexCode) => {
try {
const response = await fetch(`/api/index/${indexCode}/kline?type=daily`);
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const data = await response.json();
return data;
} catch (error) {
logger.error('HeroPanel', 'fetchIndexKline error', { indexCode, error: error.message });
return null;
}
};
/**
* 获取热门概念数据
*/
const fetchPopularConcepts = async () => {
try {
const response = await fetch('/concept-api/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: '', size: 60, page: 1, sort_by: 'change_pct' })
});
const data = await response.json();
if (data.results?.length > 0) {
return data.results.map(item => ({
name: item.concept,
change_pct: item.price_info?.avg_change_pct || 0,
}));
}
return [];
} catch (error) {
logger.error('HeroPanel', 'fetchPopularConcepts error', error);
return [];
}
};
/**
* 判断当前是否在交易时间内
*/
const isInTradingTime = () => {
const now = new Date();
const timeInMinutes = now.getHours() * 60 + now.getMinutes();
return timeInMinutes >= 570 && timeInMinutes <= 900;
};
/**
* 精美K线指数卡片 - 类似 KLineChartModal 风格
*/
const CompactIndexCard = ({ indexCode, indexName }) => {
const [chartData, setChartData] = useState(null);
const [loading, setLoading] = useState(true);
const [latestData, setLatestData] = useState(null);
const upColor = '#ef5350'; // 涨 - 红色
const downColor = '#26a69a'; // 跌 - 绿色
const loadData = useCallback(async () => {
const data = await fetchIndexKline(indexCode);
if (data?.data?.length > 0) {
const latest = data.data[data.data.length - 1];
const prevClose = latest.prev_close || data.data[data.data.length - 2]?.close || latest.open;
const changeAmount = latest.close - prevClose;
const changePct = prevClose ? ((changeAmount / prevClose) * 100) : 0;
setLatestData({
close: latest.close,
open: latest.open,
high: latest.high,
low: latest.low,
changeAmount: changeAmount,
changePct: changePct,
isPositive: changeAmount >= 0
});
const recentData = data.data.slice(-60); // 增加到60天
setChartData({
dates: recentData.map(item => item.time),
klineData: recentData.map(item => [item.open, item.close, item.low, item.high]),
volumes: recentData.map(item => item.volume || 0),
rawData: recentData
});
}
setLoading(false);
}, [indexCode]);
useEffect(() => {
loadData();
}, [loadData]);
const chartOption = useMemo(() => {
if (!chartData) return {};
return {
backgroundColor: 'transparent',
grid: [
{ left: 0, right: 0, top: 8, bottom: 28, containLabel: false },
{ left: 0, right: 0, top: '75%', bottom: 4, containLabel: false }
],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: { color: 'rgba(255, 215, 0, 0.6)', width: 1 },
lineStyle: { color: 'rgba(255, 215, 0, 0.4)', width: 1, type: 'dashed' }
},
backgroundColor: 'rgba(15, 15, 25, 0.98)',
borderColor: 'rgba(255, 215, 0, 0.5)',
borderWidth: 1,
borderRadius: 8,
padding: [12, 16],
textStyle: { color: '#e0e0e0', fontSize: 12 },
extraCssText: 'box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);',
formatter: (params) => {
const idx = params[0]?.dataIndex;
if (idx === undefined) return '';
const raw = chartData.rawData[idx];
if (!raw) return '';
// 计算涨跌
const prevClose = raw.prev_close || (idx > 0 ? chartData.rawData[idx - 1]?.close : raw.open) || raw.open;
const changeAmount = raw.close - prevClose;
const changePct = prevClose ? ((changeAmount / prevClose) * 100) : 0;
const isUp = changeAmount >= 0;
const color = isUp ? '#ef5350' : '#26a69a';
const sign = isUp ? '+' : '';
return `
<div style="min-width: 180px;">
<div style="font-weight: bold; color: #FFD700; margin-bottom: 10px; font-size: 13px; border-bottom: 1px solid rgba(255,215,0,0.2); padding-bottom: 8px;">
📅 ${raw.time}
</div>
<div style="display: grid; grid-template-columns: auto 1fr; gap: 6px 16px; font-size: 12px;">
<span style="color: #999;">开盘</span>
<span style="text-align: right; font-family: monospace;">${raw.open.toFixed(2)}</span>
<span style="color: #999;">收盘</span>
<span style="text-align: right; font-weight: bold; color: ${color}; font-family: monospace;">${raw.close.toFixed(2)}</span>
<span style="color: #999;">最高</span>
<span style="text-align: right; color: #ef5350; font-family: monospace;">${raw.high.toFixed(2)}</span>
<span style="color: #999;">最低</span>
<span style="text-align: right; color: #26a69a; font-family: monospace;">${raw.low.toFixed(2)}</span>
</div>
<div style="margin-top: 10px; padding-top: 8px; border-top: 1px solid rgba(255,255,255,0.1); display: flex; justify-content: space-between; align-items: center;">
<span style="color: #999; font-size: 11px;">涨跌幅</span>
<span style="color: ${color}; font-weight: bold; font-size: 14px; font-family: monospace;">
${sign}${changeAmount.toFixed(2)} (${sign}${changePct.toFixed(2)}%)
</span>
</div>
</div>
`;
}
},
xAxis: [
{
type: 'category',
data: chartData.dates,
gridIndex: 0,
show: false,
boundaryGap: true
},
{
type: 'category',
data: chartData.dates,
gridIndex: 1,
show: false,
boundaryGap: true
}
],
yAxis: [
{
type: 'value',
gridIndex: 0,
show: false,
scale: true
},
{
type: 'value',
gridIndex: 1,
show: false,
scale: true
}
],
dataZoom: [{
type: 'inside',
xAxisIndex: [0, 1],
start: 50,
end: 100,
zoomOnMouseWheel: true,
moveOnMouseMove: true
}],
series: [
{
name: 'K线',
type: 'candlestick',
data: chartData.klineData,
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
color: upColor,
color0: downColor,
borderColor: upColor,
borderColor0: downColor,
borderWidth: 1
},
barWidth: '65%'
},
{
name: '成交量',
type: 'bar',
data: chartData.volumes,
xAxisIndex: 1,
yAxisIndex: 1,
itemStyle: {
color: (params) => {
const idx = params.dataIndex;
const raw = chartData.rawData[idx];
return raw && raw.close >= raw.open ? 'rgba(239,83,80,0.5)' : 'rgba(38,166,154,0.5)';
}
},
barWidth: '65%'
}
]
};
}, [chartData, upColor, downColor]);
if (loading) {
return (
<Center h="140px">
<VStack spacing={2}>
<Spinner size="sm" color="gold" thickness="2px" />
<Text fontSize="10px" color="whiteAlpha.500">加载{indexName}...</Text>
</VStack>
</Center>
);
}
return (
<Flex direction="column" h="140px">
{/* 顶部:指数名称和数据 */}
<Flex justify="space-between" align="center" mb={1}>
<HStack spacing={2}>
<Box
w="3px"
h="14px"
borderRadius="full"
bg={latestData?.isPositive ? '#ef5350' : '#26a69a'}
/>
<Text fontSize="sm" color="whiteAlpha.800" fontWeight="semibold">
{indexName}
</Text>
</HStack>
<HStack spacing={3}>
<Text fontSize="lg" fontWeight="bold" color="white" fontFamily="monospace">
{latestData?.close?.toFixed(2)}
</Text>
<Box
px={2}
py={0.5}
borderRadius="md"
bg={latestData?.isPositive ? 'rgba(239,83,80,0.15)' : 'rgba(38,166,154,0.15)'}
border="1px solid"
borderColor={latestData?.isPositive ? 'rgba(239,83,80,0.3)' : 'rgba(38,166,154,0.3)'}
>
<Text
fontSize="sm"
fontWeight="bold"
fontFamily="monospace"
color={latestData?.isPositive ? '#ef5350' : '#26a69a'}
>
{latestData?.isPositive ? '▲' : '▼'} {latestData?.isPositive ? '+' : ''}{latestData?.changePct?.toFixed(2)}%
</Text>
</Box>
</HStack>
</Flex>
{/* K线图区域 */}
<Box flex="1" position="relative">
<ReactECharts
option={chartOption}
style={{ height: '100%', width: '100%' }}
opts={{ renderer: 'canvas' }}
/>
{/* 底部提示 */}
<Text
position="absolute"
bottom={0}
right={1}
fontSize="9px"
color="whiteAlpha.300"
>
滚轮缩放 · 拖动查看
</Text>
</Box>
</Flex>
);
};
/**
* 流动式热门概念组件 - HeroUI 风格
* 特点:
* 1. 三行横向滚动,每行方向不同
* 2. 卡片式设计,带渐变边框
* 3. 悬停时暂停滚动,放大效果
* 4. 流光动画效果
*/
const FlowingConcepts = () => {
const [concepts, setConcepts] = useState([]);
const [loading, setLoading] = useState(true);
const [hoveredIdx, setHoveredIdx] = useState(null);
const [isPaused, setIsPaused] = useState(false);
useEffect(() => {
const load = async () => {
const data = await fetchPopularConcepts();
setConcepts(data.slice(0, 30)); // 取30个概念
setLoading(false);
};
load();
}, []);
const getColor = (pct) => {
if (pct > 5) return { bg: 'rgba(255,23,68,0.15)', border: '#ff1744', text: '#ff1744', glow: 'rgba(255,23,68,0.4)' };
if (pct > 2) return { bg: 'rgba(255,82,82,0.12)', border: '#ff5252', text: '#ff5252', glow: 'rgba(255,82,82,0.3)' };
if (pct > 0) return { bg: 'rgba(255,138,128,0.1)', border: '#ff8a80', text: '#ff8a80', glow: 'rgba(255,138,128,0.25)' };
if (pct === 0) return { bg: 'rgba(255,215,0,0.1)', border: '#FFD700', text: '#FFD700', glow: 'rgba(255,215,0,0.25)' };
if (pct > -2) return { bg: 'rgba(105,240,174,0.1)', border: '#69f0ae', text: '#69f0ae', glow: 'rgba(105,240,174,0.25)' };
if (pct > -5) return { bg: 'rgba(0,230,118,0.12)', border: '#00e676', text: '#00e676', glow: 'rgba(0,230,118,0.3)' };
return { bg: 'rgba(0,200,83,0.15)', border: '#00c853', text: '#00c853', glow: 'rgba(0,200,83,0.4)' };
};
const handleClick = (name) => {
window.open(`https://valuefrontier.cn/htmls/${name}.html`, '_blank');
};
if (loading) {
return (
<Center h="100%">
<VStack spacing={2}>
<Spinner size="md" color="gold" thickness="3px" />
<Text fontSize="xs" color="whiteAlpha.500">加载热门概念...</Text>
</VStack>
</Center>
);
}
// 将概念分成三行
const row1 = concepts.slice(0, 10);
const row2 = concepts.slice(10, 20);
const row3 = concepts.slice(20, 30);
// 渲染单个概念卡片
const renderConceptCard = (concept, globalIdx) => {
const colors = getColor(concept.change_pct);
const isActive = hoveredIdx === globalIdx;
return (
<Box
key={globalIdx}
flexShrink={0}
px={3}
py={2}
mx={2}
bg={isActive ? colors.bg : 'rgba(255,255,255,0.03)'}
border="1px solid"
borderColor={isActive ? colors.border : 'rgba(255,255,255,0.08)'}
borderRadius="xl"
cursor="pointer"
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
transform={isActive ? 'scale(1.1) translateY(-4px)' : 'scale(1)'}
boxShadow={isActive
? `0 8px 32px ${colors.glow}, 0 0 0 1px ${colors.border}40, inset 0 1px 0 rgba(255,255,255,0.1)`
: '0 2px 8px rgba(0,0,0,0.2)'
}
onClick={() => handleClick(concept.name)}
onMouseEnter={() => {
setHoveredIdx(globalIdx);
setIsPaused(true);
}}
onMouseLeave={() => {
setHoveredIdx(null);
setIsPaused(false);
}}
position="relative"
overflow="hidden"
_before={isActive ? {
content: '""',
position: 'absolute',
top: 0,
left: '-100%',
width: '200%',
height: '100%',
background: `linear-gradient(90deg, transparent, ${colors.glow}, transparent)`,
animation: 'shimmer 1.5s infinite',
} : {}}
>
<HStack spacing={2}>
<Text
fontSize="sm"
fontWeight={isActive ? 'bold' : 'semibold'}
color={isActive ? colors.text : 'whiteAlpha.900'}
whiteSpace="nowrap"
textShadow={isActive ? `0 0 10px ${colors.glow}` : 'none'}
>
{concept.name}
</Text>
<Text
fontSize="xs"
fontWeight="bold"
color={colors.text}
whiteSpace="nowrap"
>
{concept.change_pct > 0 ? '+' : ''}{concept.change_pct.toFixed(2)}%
</Text>
</HStack>
</Box>
);
};
// 渲染滚动行
const renderScrollRow = (items, direction, startIdx, duration) => {
const animationName = direction === 'left' ? 'scrollLeft' : 'scrollRight';
return (
<Box
overflow="hidden"
position="relative"
py={1}
_before={{
content: '""',
position: 'absolute',
left: 0,
top: 0,
bottom: 0,
width: '60px',
background: 'linear-gradient(90deg, rgba(10,10,20,1) 0%, transparent 100%)',
zIndex: 2,
pointerEvents: 'none',
}}
_after={{
content: '""',
position: 'absolute',
right: 0,
top: 0,
bottom: 0,
width: '60px',
background: 'linear-gradient(90deg, transparent 0%, rgba(10,10,20,1) 100%)',
zIndex: 2,
pointerEvents: 'none',
}}
>
<Flex
animation={isPaused ? 'none' : `${animationName} ${duration}s linear infinite`}
sx={{
[`@keyframes ${animationName}`]: direction === 'left' ? {
'0%': { transform: 'translateX(0)' },
'100%': { transform: 'translateX(-50%)' },
} : {
'0%': { transform: 'translateX(-50%)' },
'100%': { transform: 'translateX(0)' },
},
}}
>
{/* 复制两份实现无缝滚动 */}
{[...items, ...items].map((concept, idx) =>
renderConceptCard(concept, startIdx + (idx % items.length))
)}
</Flex>
</Box>
);
};
return (
<Box h="100%" w="100%" py={2}>
<VStack spacing={2} h="100%" justify="center">
{renderScrollRow(row1, 'left', 0, 35)}
{renderScrollRow(row2, 'right', 10, 40)}
{renderScrollRow(row3, 'left', 20, 32)}
</VStack>
</Box>
);
};
/**
* 详细使用说明提示框
*/
const InfoTooltip = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<Box position="relative" display="inline-block">
{/* 触发器:小标签 */}
<HStack
spacing={1.5}
px={2.5}
py={1}
bg="rgba(255,215,0,0.08)"
border="1px solid rgba(255,215,0,0.2)"
borderRadius="full"
cursor="pointer"
transition="all 0.2s"
_hover={{
bg: 'rgba(255,215,0,0.15)',
borderColor: 'rgba(255,215,0,0.4)',
}}
onMouseEnter={() => setIsOpen(true)}
onMouseLeave={() => setIsOpen(false)}
onClick={() => setIsOpen(!isOpen)}
>
<Icon as={Info} color="gold" boxSize={3} />
<Text fontSize="xs" color="gold" fontWeight="medium">
使用说明
</Text>
</HStack>
{/* 悬浮提示框 */}
<Box
position="absolute"
top="calc(100% + 8px)"
left="0"
zIndex={1000}
w="420px"
p={5}
bg="rgba(10,10,20,0.98)"
borderRadius="xl"
border="1px solid rgba(255,215,0,0.3)"
boxShadow="0 15px 50px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,215,0,0.1), inset 0 1px 0 rgba(255,255,255,0.05)"
opacity={isOpen ? 1 : 0}
visibility={isOpen ? 'visible' : 'hidden'}
transform={isOpen ? 'translateY(0)' : 'translateY(-10px)'}
transition="all 0.25s ease"
onMouseEnter={() => setIsOpen(true)}
onMouseLeave={() => setIsOpen(false)}
>
{/* 小箭头 */}
<Box
position="absolute"
top="-6px"
left="20px"
w="12px"
h="12px"
bg="rgba(10,10,20,0.98)"
borderTop="1px solid rgba(255,215,0,0.3)"
borderLeft="1px solid rgba(255,215,0,0.3)"
transform="rotate(45deg)"
/>
<VStack align="stretch" spacing={4}>
{/* 标题 */}
<Text fontSize="sm" fontWeight="bold" color="gold" borderBottom="1px solid rgba(255,215,0,0.2)" pb={2}>
📖 事件中心使用指南
</Text>
{/* 1. SABC重要度说明 */}
<Box>
<HStack mb={2}>
<Text fontSize="xs" fontWeight="bold" color="whiteAlpha.900">
1 重要度等级 (SABC)
</Text>
</HStack>
<Box pl={3} borderLeft="2px solid rgba(255,215,0,0.3)" py={1}>
<Text fontSize="11px" color="whiteAlpha.800" lineHeight="1.6">
重要度由<Text as="span" color="cyan.300" fontWeight="bold"> AI大模型 </Text>
基于<Text as="span" color="gold" fontWeight="bold">事件本身的影响范围和重大程度</Text>
<Text as="span" color="orange.300" fontWeight="bold">并非收益率预测策略</Text>
S级表示影响范围广关注度高的重大事件C级表示影响较小的普通事件
</Text>
</Box>
</Box>
{/* 2. 利好利空并存说明 */}
<Box>
<HStack mb={2}>
<Text fontSize="xs" fontWeight="bold" color="whiteAlpha.900">
2 事件筛选机制
</Text>
</HStack>
<Box pl={3} borderLeft="2px solid rgba(255,215,0,0.3)" py={1}>
<Text fontSize="11px" color="whiteAlpha.800" lineHeight="1.6">
事件列表中<Text as="span" color="#ef5350" fontWeight="bold">利好</Text>
<Text as="span" color="#26a69a" fontWeight="bold">利空</Text>
建议在<Text as="span" color="purple.300" fontWeight="bold">历史相关事件</Text>
</Text>
<VStack align="stretch" spacing={0.5} mt={1.5} pl={2}>
<Text fontSize="10px" color="whiteAlpha.600"> 历史上类似事件的市场反应</Text>
<Text fontSize="10px" color="whiteAlpha.600"> 事件的超预期程度</Text>
<Text fontSize="10px" color="whiteAlpha.600"> 综合判断事件的投资价值</Text>
</VStack>
</Box>
</Box>
{/* 3. 数据延迟提醒 */}
<Box
p={3}
bg="rgba(255,100,0,0.1)"
border="1px solid rgba(255,100,0,0.25)"
borderRadius="lg"
>
<HStack mb={2}>
<Icon as={Clock} color="orange.400" boxSize={4} />
<Text fontSize="xs" fontWeight="bold" color="orange.300">
3 数据延迟提醒
</Text>
</HStack>
<Text fontSize="11px" color="whiteAlpha.800" lineHeight="1.6">
由于模型需要通过算法检索和分析历史数据事件结果和发生时间会有
<Text as="span" color="orange.300" fontWeight="bold"> 2-3分钟 </Text>
<Text as="span" color="#ef5350" fontWeight="bold">切勿追高</Text>
</Text>
</Box>
{/* 4. 盘前新闻经验 */}
<Box
p={3}
bg="rgba(59,130,246,0.1)"
border="1px solid rgba(59,130,246,0.25)"
borderRadius="lg"
>
<HStack mb={2}>
<Icon as={AlertCircle} color="blue.400" boxSize={4} />
<Text fontSize="xs" fontWeight="bold" color="blue.300">
4 实用经验分享
</Text>
</HStack>
<Text fontSize="11px" color="whiteAlpha.800" lineHeight="1.6">
一个比较有效的经验是在
<Text as="span" color="blue.300" fontWeight="bold"> 9:20左右 </Text>
研究<Text as="span" color="gold" fontWeight="bold">上一交易日收盘后至盘前</Text>
往往能发现一些<Text as="span" color="cyan.300" fontWeight="bold">当日主线题材</Text>
</Text>
</Box>
{/* 操作提示 */}
<Box pt={1}>
<Text fontSize="10px" color="whiteAlpha.400" textAlign="center">
💡 点击事件卡片查看详情 · K线图支持滚轮缩放
</Text>
</Box>
</VStack>
</Box>
</Box>
);
};
/**
* 顶部说明面板主组件
*/
const HeroPanel = () => {
const gradientBg = 'linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 25%, #16213e 50%, #1a1a2e 75%, #0a0a0a 100%)';
const borderColor = useColorModeValue('rgba(255, 215, 0, 0.3)', 'rgba(255, 215, 0, 0.25)');
return (
<Card
bg={gradientBg}
borderColor={borderColor}
borderWidth="1px"
boxShadow="0 20px 60px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,215,0,0.1) inset"
mb={6}
overflow="hidden"
position="relative"
>
{/* 装饰性光晕 */}
<Box
position="absolute"
top="-50%"
right="-30%"
width="600px"
height="600px"
borderRadius="full"
bg="radial-gradient(circle, rgba(255,215,0,0.08) 0%, transparent 70%)"
pointerEvents="none"
filter="blur(40px)"
/>
<Box
position="absolute"
bottom="-40%"
left="-20%"
width="400px"
height="400px"
borderRadius="full"
bg="radial-gradient(circle, rgba(100,150,255,0.05) 0%, transparent 70%)"
pointerEvents="none"
filter="blur(50px)"
/>
<CardBody p={{ base: 4, md: 5 }}>
{/* 标题行:标题 + 使用说明 + 交易状态 */}
<Flex align="center" justify="space-between" mb={4} wrap="wrap" gap={2}>
<HStack spacing={3}>
<Heading size="lg">
<Text
bgGradient="linear(to-r, #FFD700, #FFA500, #FFD700)"
bgClip="text"
backgroundSize="200% 100%"
animation="shimmer 3s linear infinite"
fontWeight="extrabold"
>
事件中心
</Text>
</Heading>
{/* 使用说明 - 悬浮提示 */}
<InfoTooltip />
</HStack>
{/* 右侧:交易状态 */}
{isInTradingTime() && (
<HStack
spacing={1.5}
px={2.5}
py={1}
borderRadius="full"
bg="rgba(0,218,60,0.1)"
border="1px solid rgba(0,218,60,0.3)"
>
<Box
w="6px"
h="6px"
borderRadius="full"
bg="#00da3c"
animation="pulse 1.5s infinite"
boxShadow="0 0 8px #00da3c"
/>
<Text fontSize="xs" color="#00da3c" fontWeight="bold">
交易中
</Text>
</HStack>
)}
</Flex>
{/* 内容区:指数 + 概念 */}
<Flex
direction={{ base: 'column', lg: 'row' }}
gap={4}
>
{/* 左侧:双指数横向排列 */}
<Flex
direction={{ base: 'column', md: 'row' }}
gap={4}
flex={{ lg: '0 0 58%' }}
>
{/* 上证指数 */}
<Box
flex="1"
p={4}
minH="160px"
bg="rgba(255,255,255,0.03)"
borderRadius="xl"
border="1px solid rgba(255,255,255,0.08)"
transition="all 0.3s"
_hover={{
bg: 'rgba(255,255,255,0.05)',
borderColor: 'rgba(239,83,80,0.4)',
boxShadow: '0 4px 20px rgba(239,83,80,0.15)',
}}
>
<CompactIndexCard indexCode="000001" indexName="上证指数" />
</Box>
{/* 深证成指 */}
<Box
flex="1"
p={4}
minH="160px"
bg="rgba(255,255,255,0.03)"
borderRadius="xl"
border="1px solid rgba(255,255,255,0.08)"
transition="all 0.3s"
_hover={{
bg: 'rgba(255,255,255,0.05)',
borderColor: 'rgba(38,166,154,0.4)',
boxShadow: '0 4px 20px rgba(38,166,154,0.15)',
}}
>
<CompactIndexCard indexCode="399001" indexName="深证成指" />
</Box>
</Flex>
{/* 右侧:热门概念 - 流动式设计 */}
<Box
flex={{ lg: '1' }}
minH={{ base: '180px', lg: '160px' }}
bg="linear-gradient(135deg, rgba(0,0,0,0.4) 0%, rgba(20,20,40,0.3) 50%, rgba(0,0,0,0.4) 100%)"
borderRadius="2xl"
border="1px solid"
borderColor="rgba(255,215,0,0.15)"
overflow="hidden"
position="relative"
boxShadow="inset 0 2px 20px rgba(0,0,0,0.3), 0 4px 30px rgba(0,0,0,0.2)"
_before={{
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '1px',
background: 'linear-gradient(90deg, transparent, rgba(255,215,0,0.3), transparent)',
}}
>
{/* 标题栏 - 更精致的设计 */}
<Flex
position="absolute"
top={0}
left={0}
right={0}
px={4}
py={2}
bg="rgba(0,0,0,0.3)"
backdropFilter="blur(10px)"
borderBottom="1px solid rgba(255,255,255,0.05)"
justify="space-between"
align="center"
zIndex={10}
>
<HStack spacing={2}>
<Box
p={1.5}
bg="rgba(255,215,0,0.15)"
borderRadius="lg"
border="1px solid rgba(255,215,0,0.2)"
>
<Icon as={TrendingUp} color="gold" boxSize={4} />
</Box>
<VStack align="start" spacing={0}>
<Text fontSize="sm" fontWeight="bold" color="gold">
热门概念
</Text>
<Text fontSize="10px" color="whiteAlpha.500">
实时涨跌排行
</Text>
</VStack>
</HStack>
<HStack spacing={1} px={2} py={1} bg="rgba(255,255,255,0.05)" borderRadius="full">
<Box w="6px" h="6px" borderRadius="full" bg="gold" animation="pulse 2s infinite" />
<Text fontSize="10px" color="whiteAlpha.600">点击查看详情</Text>
</HStack>
</Flex>
{/* 流动式概念展示 */}
<Box pt="52px" h="100%">
<FlowingConcepts />
</Box>
</Box>
</Flex>
</CardBody>
</Card>
);
};
export default HeroPanel;