diff --git a/src/views/Community/components/HeroPanel.js b/src/views/Community/components/HeroPanel.js index 619e1c7e..4f87893d 100644 --- a/src/views/Community/components/HeroPanel.js +++ b/src/views/Community/components/HeroPanel.js @@ -12,13 +12,11 @@ import { Text, Heading, useColorModeValue, - SimpleGrid, Icon, Spinner, Center, - Tooltip, } from '@chakra-ui/react'; -import { AlertCircle, Clock, TrendingUp } from 'lucide-react'; +import { AlertCircle, Clock, TrendingUp, Info } from 'lucide-react'; import ReactECharts from 'echarts-for-react'; import { logger } from '../../../utils/logger'; @@ -28,24 +26,14 @@ const animations = ` 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.6; transform: scale(1.1); } } - @keyframes float3d { - 0%, 100% { transform: translateY(0) translateZ(0) rotateX(0deg); } - 25% { transform: translateY(-8px) translateZ(20px) rotateX(5deg); } - 50% { transform: translateY(-4px) translateZ(10px) rotateX(0deg); } - 75% { transform: translateY(-12px) translateZ(30px) rotateX(-5deg); } - } - @keyframes glow { - 0%, 100% { box-shadow: 0 0 5px currentColor, 0 0 10px currentColor; } - 50% { box-shadow: 0 0 20px currentColor, 0 0 30px currentColor, 0 0 40px currentColor; } - } - @keyframes orbit { - 0% { transform: rotate(0deg) translateX(120px) rotate(0deg); } - 100% { transform: rotate(360deg) translateX(120px) rotate(-360deg); } - } @keyframes shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } + @keyframes floatSlow { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-10px); } + } `; // 注入样式 @@ -67,7 +55,6 @@ const fetchIndexKline = async (indexCode) => { 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(); - logger.debug('HeroPanel', 'fetchIndexKline success', { indexCode, dataLength: data?.data?.length }); return data; } catch (error) { logger.error('HeroPanel', 'fetchIndexKline error', { indexCode, error: error.message }); @@ -89,7 +76,6 @@ const fetchPopularConcepts = async () => { if (data.results?.length > 0) { return data.results.map(item => ({ name: item.concept, - value: Math.abs(item.price_info?.avg_change_pct || 1) + 5, change_pct: item.price_info?.avg_change_pct || 0, })); } @@ -110,18 +96,17 @@ const isInTradingTime = () => { }; /** - * 迷你K线图组件(保持原有样式) + * 紧凑型K线指数卡片 */ -const MiniIndexChart = ({ indexCode, indexName }) => { +const CompactIndexCard = ({ indexCode, indexName }) => { const [chartData, setChartData] = useState(null); const [loading, setLoading] = useState(true); const [latestData, setLatestData] = useState(null); - const [currentDate, setCurrentDate] = useState(''); const upColor = '#ec0000'; const downColor = '#00da3c'; - const loadDailyData = useCallback(async () => { + const loadData = useCallback(async () => { const data = await fetchIndexKline(indexCode); if (data?.data?.length > 0) { const latest = data.data[data.data.length - 1]; @@ -131,8 +116,7 @@ const MiniIndexChart = ({ indexCode, indexName }) => { change: prevClose ? (((latest.close - prevClose) / prevClose) * 100).toFixed(2) : '0.00', isPositive: latest.close >= prevClose }); - setCurrentDate(latest.time); - const recentData = data.data.slice(-60); + const recentData = data.data.slice(-40); setChartData({ dates: recentData.map(item => item.time), klineData: recentData.map(item => [item.open, item.close, item.low, item.high]), @@ -142,75 +126,32 @@ const MiniIndexChart = ({ indexCode, indexName }) => { setLoading(false); }, [indexCode]); - const loadMinuteData = useCallback(async () => { - try { - const response = await fetch(`/api/index/${indexCode}/kline?type=minute`); - if (!response.ok) return; - const data = await response.json(); - if (data?.data?.length > 0) { - const latest = data.data[data.data.length - 1]; - const dayOpen = data.data[0].open; - setLatestData({ - close: latest.close, - change: dayOpen ? (((latest.close - dayOpen) / dayOpen) * 100).toFixed(2) : '0.00', - isPositive: latest.close >= dayOpen - }); - } - } catch (error) { - logger.error('HeroPanel', 'loadMinuteData error', error); - } - }, [indexCode]); - useEffect(() => { - let isMounted = true; - let intervalId = null; - const init = async () => { - setLoading(true); - await loadDailyData(); - if (isMounted && isInTradingTime()) await loadMinuteData(); - if (isMounted) setLoading(false); - }; - init(); - if (isInTradingTime()) { - intervalId = setInterval(() => { - if (isInTradingTime()) loadMinuteData(); - else if (intervalId) clearInterval(intervalId); - }, 60000); - } - return () => { isMounted = false; if (intervalId) clearInterval(intervalId); }; - }, [indexCode, loadDailyData, loadMinuteData]); + loadData(); + }, [loadData]); const chartOption = useMemo(() => { if (!chartData) return {}; return { backgroundColor: 'transparent', - grid: { left: 10, right: 10, top: 5, bottom: 20, containLabel: false }, + grid: { left: 5, right: 5, top: 5, bottom: 5, containLabel: false }, tooltip: { trigger: 'axis', axisPointer: { type: 'cross', lineStyle: { color: 'rgba(255, 215, 0, 0.5)', width: 1, type: 'dashed' } }, backgroundColor: 'rgba(20, 20, 20, 0.95)', borderColor: '#FFD700', borderWidth: 1, - textStyle: { color: '#fff', fontSize: 11, fontFamily: 'monospace' }, - padding: [8, 12], + textStyle: { color: '#fff', fontSize: 10, fontFamily: 'monospace' }, + padding: [6, 10], formatter: (params) => { const idx = params[0].dataIndex; const raw = chartData.rawData[idx]; if (!raw) return ''; const prevClose = raw.prev_close || raw.open; - const change = raw.close - prevClose; - const changePct = prevClose ? ((change / prevClose) * 100).toFixed(2) : '0.00'; + const changePct = prevClose ? (((raw.close - prevClose) / prevClose) * 100).toFixed(2) : '0.00'; const isUp = raw.close >= prevClose; const color = isUp ? '#ec0000' : '#00da3c'; - const sign = isUp ? '+' : ''; - return `