900 lines
29 KiB
JavaScript
900 lines
29 KiB
JavaScript
// 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;
|