update ui

This commit is contained in:
2025-11-13 16:07:14 +08:00
parent 1d9b50a94e
commit 5aa0507a65
4 changed files with 1086 additions and 9 deletions

View File

@@ -0,0 +1,463 @@
// src/views/Community/components/HeroPanel.js
// 顶部说明面板组件:产品功能介绍 + 沪深指数折线图 + 热门概念词云图
import React, { useEffect, useState, useMemo } from 'react';
import {
Box,
Card,
CardBody,
Flex,
VStack,
HStack,
Text,
Heading,
useColorModeValue,
SimpleGrid,
Icon,
Stat,
StatLabel,
StatNumber,
StatHelpText,
StatArrow,
Spinner,
Center,
} from '@chakra-ui/react';
import { TrendingUp, Activity, Globe, Zap } from 'lucide-react';
import ReactECharts from 'echarts-for-react';
import { logger } from '../../../utils/logger';
/**
* 获取指数行情数据
*/
const fetchIndexKline = async (indexCode) => {
try {
const response = await fetch(`/api/index/${indexCode}/kline`);
const data = await response.json();
return data;
} catch (error) {
logger.error('HeroPanel', 'fetchIndexKline', error);
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: 50, // 获取前50个概念用于词云
page: 1,
sort_by: 'change_pct'
})
});
const data = await response.json();
if (data.results) {
return data.results.map(item => ({
name: item.concept,
value: Math.abs(item.price_info?.avg_change_pct || 1), // 使用涨跌幅绝对值作为权重
change_pct: item.price_info?.avg_change_pct || 0,
}));
}
return [];
} catch (error) {
logger.error('HeroPanel', 'fetchPopularConcepts', error);
return [];
}
};
/**
* 迷你折线图组件
*/
const MiniIndexChart = ({ indexCode, indexName }) => {
const [chartData, setChartData] = useState(null);
const [loading, setLoading] = useState(true);
const [latestData, setLatestData] = useState(null);
const chartBg = useColorModeValue('transparent', 'transparent');
const lineColor = useColorModeValue('#FFD700', '#FFD700'); // 金色
const areaColor = useColorModeValue('rgba(255, 215, 0, 0.15)', 'rgba(255, 215, 0, 0.1)');
useEffect(() => {
const loadData = async () => {
setLoading(true);
const data = await fetchIndexKline(indexCode);
if (data && data.data && data.data.length > 0) {
// 取最近一个交易日的数据
const latest = data.data[data.data.length - 1];
setLatestData({
close: latest[2],
change: ((latest[2] - latest[1]) / latest[1] * 100).toFixed(2),
isPositive: latest[2] >= latest[1]
});
// 准备图表数据最近60个交易日
const recentData = data.data.slice(-60);
setChartData({
dates: recentData.map(item => item[0]),
values: recentData.map(item => item[2])
});
}
setLoading(false);
};
loadData();
}, [indexCode]);
const chartOption = useMemo(() => {
if (!chartData) return {};
return {
backgroundColor: chartBg,
grid: {
left: 5,
right: 5,
top: 5,
bottom: 20,
containLabel: false
},
xAxis: {
type: 'category',
data: chartData.dates,
show: false
},
yAxis: {
type: 'value',
show: false,
scale: true
},
series: [{
type: 'line',
data: chartData.values,
smooth: true,
symbol: 'none',
lineStyle: {
color: lineColor,
width: 2,
shadowColor: lineColor,
shadowBlur: 8,
shadowOffsetY: 2
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0,
color: areaColor
}, {
offset: 1,
color: 'rgba(255, 215, 0, 0)'
}]
}
}
}]
};
}, [chartData, chartBg, lineColor, areaColor]);
if (loading) {
return (
<Center h="120px">
<Spinner size="sm" color="gold" />
</Center>
);
}
return (
<VStack spacing={2} align="stretch" h="120px">
<HStack justify="space-between">
<VStack align="start" spacing={0}>
<Text fontSize="xs" color="whiteAlpha.700">{indexName}</Text>
<Text fontSize="lg" fontWeight="bold" color="white">
{latestData?.close.toFixed(2)}
</Text>
</VStack>
<HStack spacing={1}>
<StatArrow type={latestData?.isPositive ? 'increase' : 'decrease'} />
<Text
fontSize="sm"
fontWeight="bold"
color={latestData?.isPositive ? 'green.300' : 'red.300'}
>
{latestData?.isPositive ? '+' : ''}{latestData?.change}%
</Text>
</HStack>
</HStack>
<Box flex="1">
<ReactECharts
option={chartOption}
style={{ height: '80px', width: '100%' }}
opts={{ renderer: 'canvas' }}
/>
</Box>
</VStack>
);
};
/**
* 概念词云图组件
*/
const ConceptWordCloud = () => {
const [concepts, setConcepts] = useState([]);
const [loading, setLoading] = useState(true);
const chartBg = useColorModeValue('transparent', 'transparent');
useEffect(() => {
const loadConcepts = async () => {
setLoading(true);
const data = await fetchPopularConcepts();
setConcepts(data);
setLoading(false);
};
loadConcepts();
}, []);
const chartOption = useMemo(() => {
if (concepts.length === 0) return {};
return {
backgroundColor: chartBg,
tooltip: {
show: true,
formatter: (params) => {
const changePct = params.data.change_pct;
const sign = changePct > 0 ? '+' : '';
return `${params.name}<br/>涨跌: ${sign}${changePct.toFixed(2)}%`;
}
},
series: [{
type: 'wordCloud',
shape: 'circle',
left: 'center',
top: 'center',
width: '100%',
height: '100%',
right: null,
bottom: null,
sizeRange: [12, 40],
rotationRange: [-45, 45],
rotationStep: 45,
gridSize: 8,
drawOutOfBound: false,
layoutAnimation: true,
textStyle: {
fontFamily: 'sans-serif',
fontWeight: 'bold',
color: function (params) {
// 根据涨跌幅设置颜色
const changePct = params.data.change_pct;
if (changePct > 5) return '#ff4d4f'; // 大涨:红色
if (changePct > 3) return '#ff7875'; // 中涨:浅红
if (changePct > 0) return '#ffa940'; // 小涨:橙色
if (changePct === 0) return '#FFD700'; // 平盘:金色
if (changePct > -3) return '#95de64'; // 小跌:浅绿
if (changePct > -5) return '#52c41a'; // 中跌:绿色
return '#13c2c2'; // 大跌:青色
}
},
emphasis: {
focus: 'self',
textStyle: {
shadowBlur: 10,
shadowColor: '#FFD700',
fontSize: 20
}
},
data: concepts
}]
};
}, [concepts, chartBg]);
if (loading) {
return (
<Center h="200px">
<Spinner size="md" color="gold" />
</Center>
);
}
return (
<ReactECharts
option={chartOption}
style={{ height: '200px', width: '100%' }}
opts={{ renderer: 'canvas' }}
/>
);
};
/**
* 产品特性图标组件
*/
const FeatureIcon = ({ icon, title, description }) => {
return (
<HStack spacing={3} align="start">
<Box
p={2}
borderRadius="lg"
bg="whiteAlpha.200"
color="gold"
>
<Icon as={icon} boxSize={5} />
</Box>
<VStack align="start" spacing={0}>
<Text fontSize="sm" fontWeight="bold" color="white">
{title}
</Text>
<Text fontSize="xs" color="whiteAlpha.700">
{description}
</Text>
</VStack>
</HStack>
);
};
/**
* 顶部说明面板主组件
*/
const HeroPanel = () => {
const gradientBg = 'linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 50%, #1a1a1a 100%)';
const cardBorder = '1px solid';
const borderColor = useColorModeValue('rgba(255, 215, 0, 0.3)', 'rgba(255, 215, 0, 0.2)');
return (
<Card
bg={gradientBg}
borderColor={borderColor}
borderWidth={cardBorder}
boxShadow="0 8px 32px rgba(0, 0, 0, 0.4)"
mb={6}
overflow="hidden"
position="relative"
>
{/* 装饰性背景图案 */}
<Box
position="absolute"
top="-20%"
right="-10%"
width="500px"
height="500px"
borderRadius="full"
bg="radial-gradient(circle, rgba(255, 215, 0, 0.1) 0%, transparent 70%)"
pointerEvents="none"
/>
<CardBody p={6}>
<SimpleGrid columns={{ base: 1, lg: 3 }} spacing={6}>
{/* 左侧:产品介绍 */}
<Box>
<VStack align="start" spacing={4}>
<Heading size="lg" color="white" fontWeight="extrabold">
<Text
bgGradient="linear(to-r, #FFD700, #FFA500)"
bgClip="text"
>
价值前沿
</Text>
</Heading>
<Text fontSize="sm" color="whiteAlpha.800" lineHeight="1.8">
实时捕捉市场动态智能分析投资机会
<br />
整合多维数据源为您提供专业的投资决策支持
</Text>
<VStack spacing={3} align="stretch" w="100%">
<FeatureIcon
icon={Activity}
title="实时监控"
description="7×24小时追踪市场动态"
/>
<FeatureIcon
icon={TrendingUp}
title="智能分析"
description="AI驱动的概念板块分析"
/>
<FeatureIcon
icon={Globe}
title="全面覆盖"
description="A股全市场深度数据"
/>
<FeatureIcon
icon={Zap}
title="极速响应"
description="毫秒级数据更新推送"
/>
</VStack>
</VStack>
</Box>
{/* 中间:沪深指数折线图 */}
<Box>
<VStack spacing={4} h="100%">
<Box
w="100%"
p={4}
bg="whiteAlpha.100"
borderRadius="xl"
borderWidth="1px"
borderColor="whiteAlpha.200"
backdropFilter="blur(10px)"
>
<MiniIndexChart indexCode="000001" indexName="上证指数" />
</Box>
<Box
w="100%"
p={4}
bg="whiteAlpha.100"
borderRadius="xl"
borderWidth="1px"
borderColor="whiteAlpha.200"
backdropFilter="blur(10px)"
>
<MiniIndexChart indexCode="399001" indexName="深证成指" />
</Box>
</VStack>
</Box>
{/* 右侧:热门概念词云图 */}
<Box>
<VStack align="start" spacing={3} h="100%">
<HStack>
<Text fontSize="md" fontWeight="bold" color="white">
🔥 热门概念
</Text>
<Text fontSize="xs" color="whiteAlpha.600">
实时更新
</Text>
</HStack>
<Box
w="100%"
flex="1"
bg="whiteAlpha.100"
borderRadius="xl"
borderWidth="1px"
borderColor="whiteAlpha.200"
backdropFilter="blur(10px)"
p={3}
>
<ConceptWordCloud />
</Box>
<Text fontSize="xs" color="whiteAlpha.600" textAlign="center" w="100%">
字体大小表示热度 · 颜色表示涨跌幅
</Text>
</VStack>
</Box>
</SimpleGrid>
</CardBody>
</Card>
);
};
export default HeroPanel;