update ui

This commit is contained in:
2025-11-13 16:17:32 +08:00
parent 5aa0507a65
commit 5ddf8d3c09
2 changed files with 45 additions and 20 deletions

View File

@@ -14,11 +14,6 @@ import {
useColorModeValue, useColorModeValue,
SimpleGrid, SimpleGrid,
Icon, Icon,
Stat,
StatLabel,
StatNumber,
StatHelpText,
StatArrow,
Spinner, Spinner,
Center, Center,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
@@ -27,15 +22,20 @@ import ReactECharts from 'echarts-for-react';
import { logger } from '../../../utils/logger'; import { logger } from '../../../utils/logger';
/** /**
* 获取指数行情数据 * 获取指数行情数据(日线数据)
*/ */
const fetchIndexKline = async (indexCode) => { const fetchIndexKline = async (indexCode) => {
try { try {
const response = await fetch(`/api/index/${indexCode}/kline`); // 使用日线数据获取最近60个交易日
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(); const data = await response.json();
logger.debug('HeroPanel', 'fetchIndexKline success', { indexCode, dataLength: data?.data?.length });
return data; return data;
} catch (error) { } catch (error) {
logger.error('HeroPanel', 'fetchIndexKline', error); logger.error('HeroPanel', 'fetchIndexKline error', { indexCode, error: error.message });
return null; return null;
} }
}; };
@@ -86,30 +86,41 @@ const MiniIndexChart = ({ indexCode, indexName }) => {
const areaColor = useColorModeValue('rgba(255, 215, 0, 0.15)', 'rgba(255, 215, 0, 0.1)'); const areaColor = useColorModeValue('rgba(255, 215, 0, 0.15)', 'rgba(255, 215, 0, 0.1)');
useEffect(() => { useEffect(() => {
let isMounted = true;
const loadData = async () => { const loadData = async () => {
setLoading(true); setLoading(true);
const data = await fetchIndexKline(indexCode); const data = await fetchIndexKline(indexCode);
if (data && data.data && data.data.length > 0) { if (isMounted && data && data.data && data.data.length > 0) {
// 取最近一个交易日的数据 // 取最近一个交易日的数据
const latest = data.data[data.data.length - 1]; const latest = data.data[data.data.length - 1];
const prevClose = latest.prev_close || latest.close;
setLatestData({ setLatestData({
close: latest[2], close: latest.close,
change: ((latest[2] - latest[1]) / latest[1] * 100).toFixed(2), change: prevClose ? (((latest.close - prevClose) / prevClose) * 100).toFixed(2) : '0.00',
isPositive: latest[2] >= latest[1] isPositive: latest.close >= prevClose
}); });
// 准备图表数据最近60个交易日 // 准备图表数据最近60个交易日
const recentData = data.data.slice(-60); const recentData = data.data.slice(-60);
setChartData({ setChartData({
dates: recentData.map(item => item[0]), dates: recentData.map(item => item.time),
values: recentData.map(item => item[2]) values: recentData.map(item => item.close)
}); });
} }
setLoading(false);
if (isMounted) {
setLoading(false);
}
}; };
loadData(); loadData();
return () => {
isMounted = false;
};
}, [indexCode]); }, [indexCode]);
const chartOption = useMemo(() => { const chartOption = useMemo(() => {
@@ -184,13 +195,12 @@ const MiniIndexChart = ({ indexCode, indexName }) => {
</Text> </Text>
</VStack> </VStack>
<HStack spacing={1}> <HStack spacing={1}>
<StatArrow type={latestData?.isPositive ? 'increase' : 'decrease'} />
<Text <Text
fontSize="sm" fontSize="sm"
fontWeight="bold" fontWeight="bold"
color={latestData?.isPositive ? 'green.300' : 'red.300'} color={latestData?.isPositive ? 'green.300' : 'red.300'}
> >
{latestData?.isPositive ? '+' : ''}{latestData?.change}% {latestData?.isPositive ? '↑' : '↓'} {latestData?.isPositive ? '+' : ''}{latestData?.change}%
</Text> </Text>
</HStack> </HStack>
</HStack> </HStack>
@@ -215,14 +225,24 @@ const ConceptWordCloud = () => {
const chartBg = useColorModeValue('transparent', 'transparent'); const chartBg = useColorModeValue('transparent', 'transparent');
useEffect(() => { useEffect(() => {
let isMounted = true;
const loadConcepts = async () => { const loadConcepts = async () => {
setLoading(true); setLoading(true);
const data = await fetchPopularConcepts(); const data = await fetchPopularConcepts();
setConcepts(data); if (isMounted && data && data.length > 0) {
setLoading(false); setConcepts(data);
}
if (isMounted) {
setLoading(false);
}
}; };
loadConcepts(); loadConcepts();
return () => {
isMounted = false;
};
}, []); }, []);
const chartOption = useMemo(() => { const chartOption = useMemo(() => {

View File

@@ -120,10 +120,15 @@ const Community = () => {
}; };
// ⚡ 首次进入页面时滚动到内容区域(考虑导航栏高度) // ⚡ 首次进入页面时滚动到内容区域(考虑导航栏高度)
const hasScrolled = useRef(false);
useEffect(() => { useEffect(() => {
// 只在第一次挂载时执行滚动
if (hasScrolled.current) return;
// 延迟执行确保DOM已完全渲染 // 延迟执行确保DOM已完全渲染
const timer = setTimeout(() => { const timer = setTimeout(() => {
if (containerRef.current) { if (containerRef.current) {
hasScrolled.current = true;
// 滚动到容器顶部,自动考虑导航栏的高度 // 滚动到容器顶部,自动考虑导航栏的高度
containerRef.current.scrollIntoView({ containerRef.current.scrollIntoView({
behavior: 'auto', behavior: 'auto',
@@ -131,7 +136,7 @@ const Community = () => {
inline: 'nearest' inline: 'nearest'
}); });
} }
}, 0); }, 100);
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, []); // 空依赖数组,只在组件挂载时执行一次 }, []); // 空依赖数组,只在组件挂载时执行一次