update ui
This commit is contained in:
@@ -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(() => {
|
||||||
|
|||||||
@@ -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);
|
||||||
}, []); // 空依赖数组,只在组件挂载时执行一次
|
}, []); // 空依赖数组,只在组件挂载时执行一次
|
||||||
|
|||||||
Reference in New Issue
Block a user