update pay ui
This commit is contained in:
@@ -81,7 +81,7 @@ const DynamicNewsDetailPanel = ({ event, showHeader = true }) => {
|
|||||||
|
|
||||||
// 使用 Hook 获取实时数据
|
// 使用 Hook 获取实时数据
|
||||||
// - autoLoad: false - 禁用自动加载所有数据,改为手动触发
|
// - autoLoad: false - 禁用自动加载所有数据,改为手动触发
|
||||||
// - autoLoadQuotes: false - 禁用自动加载行情,延迟到展开时加载(减少请求)
|
// - autoLoadQuotes: true - 股票数据加载后自动加载行情(相关股票默认展开)
|
||||||
const {
|
const {
|
||||||
stocks,
|
stocks,
|
||||||
quotes,
|
quotes,
|
||||||
@@ -93,7 +93,7 @@ const DynamicNewsDetailPanel = ({ event, showHeader = true }) => {
|
|||||||
loadHistoricalData,
|
loadHistoricalData,
|
||||||
loadChainAnalysis,
|
loadChainAnalysis,
|
||||||
refreshQuotes
|
refreshQuotes
|
||||||
} = useEventStocks(event?.id, event?.created_at, { autoLoad: false, autoLoadQuotes: false });
|
} = useEventStocks(event?.id, event?.created_at, { autoLoad: false, autoLoadQuotes: true });
|
||||||
|
|
||||||
// 🎯 加载事件详情(增加浏览量)- 与 EventDetailModal 保持一致
|
// 🎯 加载事件详情(增加浏览量)- 与 EventDetailModal 保持一致
|
||||||
const loadEventDetail = useCallback(async () => {
|
const loadEventDetail = useCallback(async () => {
|
||||||
@@ -229,12 +229,14 @@ const DynamicNewsDetailPanel = ({ event, showHeader = true }) => {
|
|||||||
setHasLoadedHistorical(false);
|
setHasLoadedHistorical(false);
|
||||||
setHasLoadedTransmission(false);
|
setHasLoadedTransmission(false);
|
||||||
|
|
||||||
// 相关股票默认展开,预加载股票列表
|
// 相关股票默认展开,预加载股票列表和行情数据
|
||||||
setIsStocksOpen(true);
|
setIsStocksOpen(true);
|
||||||
if (canAccessStocks) {
|
if (canAccessStocks) {
|
||||||
console.log('%c📊 [相关股票] 事件切换,预加载股票列表(获取数量)', 'color: #10B981; font-weight: bold;', { eventId: event?.id });
|
console.log('%c📊 [相关股票] 事件切换,预加载股票列表和行情数据', 'color: #10B981; font-weight: bold;', { eventId: event?.id });
|
||||||
loadStocksData();
|
loadStocksData();
|
||||||
setHasLoadedStocks(true);
|
setHasLoadedStocks(true);
|
||||||
|
// 由于默认展开,直接加载行情数据
|
||||||
|
setHasLoadedQuotes(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 历史事件默认折叠,但预加载数据(显示数量吸引点击)
|
// 历史事件默认折叠,但预加载数据(显示数量吸引点击)
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
// src/views/Community/components/DynamicNewsDetail/RelatedStocksSection.js
|
// src/views/Community/components/DynamicNewsDetail/RelatedStocksSection.js
|
||||||
// 相关股票列表区组件(纯内容,不含标题)
|
// 相关股票列表区组件(纯内容,不含标题)
|
||||||
|
|
||||||
import React from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
import { VStack } from '@chakra-ui/react';
|
import { VStack } from '@chakra-ui/react';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
import StockListItem from './StockListItem';
|
import StockListItem from './StockListItem';
|
||||||
|
import { fetchBatchKlineData, klineDataCache, getCacheKey } from '../StockDetailPanel/utils/klineDataCache';
|
||||||
|
import { logger } from '../../../../utils/logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 相关股票列表区组件(纯内容部分)
|
* 相关股票列表区组件(纯内容部分)
|
||||||
@@ -22,6 +25,85 @@ const RelatedStocksSection = ({
|
|||||||
watchlistSet = new Set(),
|
watchlistSet = new Set(),
|
||||||
onWatchlistToggle
|
onWatchlistToggle
|
||||||
}) => {
|
}) => {
|
||||||
|
// K线数据状态:{ [stockCode]: data[] }
|
||||||
|
const [klineDataMap, setKlineDataMap] = useState({});
|
||||||
|
const [klineLoading, setKlineLoading] = useState(false);
|
||||||
|
|
||||||
|
// 稳定的事件时间
|
||||||
|
const stableEventTime = useMemo(() => {
|
||||||
|
return eventTime ? dayjs(eventTime).format('YYYY-MM-DD HH:mm') : '';
|
||||||
|
}, [eventTime]);
|
||||||
|
|
||||||
|
// 稳定的股票列表 key
|
||||||
|
const stocksKey = useMemo(() => {
|
||||||
|
if (!stocks || stocks.length === 0) return '';
|
||||||
|
return stocks.map(s => s.stock_code).sort().join(',');
|
||||||
|
}, [stocks]);
|
||||||
|
|
||||||
|
// 计算是否应该显示 loading
|
||||||
|
const shouldShowLoading = useMemo(() => {
|
||||||
|
if (!stocks || stocks.length === 0) return false;
|
||||||
|
const currentDataKeys = Object.keys(klineDataMap).sort().join(',');
|
||||||
|
if (stocksKey !== currentDataKeys) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return klineLoading;
|
||||||
|
}, [stocks, stocksKey, klineDataMap, klineLoading]);
|
||||||
|
|
||||||
|
// 批量加载K线数据
|
||||||
|
useEffect(() => {
|
||||||
|
if (!stocks || stocks.length === 0) {
|
||||||
|
setKlineDataMap({});
|
||||||
|
setKlineLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 立即设置 loading 状态
|
||||||
|
setKlineLoading(true);
|
||||||
|
|
||||||
|
const stockCodes = stocks.map(s => s.stock_code);
|
||||||
|
|
||||||
|
// 先检查缓存
|
||||||
|
const cachedData = {};
|
||||||
|
const uncachedCodes = [];
|
||||||
|
stockCodes.forEach(code => {
|
||||||
|
const cacheKey = getCacheKey(code, stableEventTime, 'timeline');
|
||||||
|
const cached = klineDataCache.get(cacheKey);
|
||||||
|
if (cached !== undefined) {
|
||||||
|
cachedData[code] = cached;
|
||||||
|
} else {
|
||||||
|
uncachedCodes.push(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果全部缓存命中,直接使用
|
||||||
|
if (uncachedCodes.length === 0) {
|
||||||
|
setKlineDataMap(cachedData);
|
||||||
|
setKlineLoading(false);
|
||||||
|
logger.debug('RelatedStocksSection', 'K线数据全部来自缓存', { stockCount: stockCodes.length });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug('RelatedStocksSection', '批量加载K线数据', {
|
||||||
|
totalCount: stockCodes.length,
|
||||||
|
cachedCount: Object.keys(cachedData).length,
|
||||||
|
uncachedCount: uncachedCodes.length,
|
||||||
|
eventTime: stableEventTime
|
||||||
|
});
|
||||||
|
|
||||||
|
// 批量请求
|
||||||
|
fetchBatchKlineData(stockCodes, stableEventTime, 'timeline')
|
||||||
|
.then((batchData) => {
|
||||||
|
setKlineDataMap({ ...cachedData, ...batchData });
|
||||||
|
setKlineLoading(false);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logger.error('RelatedStocksSection', '批量加载K线数据失败', error);
|
||||||
|
setKlineDataMap(cachedData);
|
||||||
|
setKlineLoading(false);
|
||||||
|
});
|
||||||
|
}, [stocksKey, stableEventTime]);
|
||||||
|
|
||||||
// 如果没有股票数据,不渲染
|
// 如果没有股票数据,不渲染
|
||||||
if (!stocks || stocks.length === 0) {
|
if (!stocks || stocks.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
@@ -37,6 +119,8 @@ const RelatedStocksSection = ({
|
|||||||
eventTime={eventTime}
|
eventTime={eventTime}
|
||||||
isInWatchlist={watchlistSet.has(stock.stock_code)}
|
isInWatchlist={watchlistSet.has(stock.stock_code)}
|
||||||
onWatchlistToggle={onWatchlistToggle}
|
onWatchlistToggle={onWatchlistToggle}
|
||||||
|
klineData={klineDataMap[stock.stock_code]}
|
||||||
|
klineLoading={shouldShowLoading && !klineDataMap[stock.stock_code]}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|||||||
@@ -39,13 +39,17 @@ import { PROFESSIONAL_COLORS } from '@constants/professionalTheme';
|
|||||||
* @param {string} props.eventTime - 事件时间(可选)
|
* @param {string} props.eventTime - 事件时间(可选)
|
||||||
* @param {boolean} props.isInWatchlist - 是否在自选股中
|
* @param {boolean} props.isInWatchlist - 是否在自选股中
|
||||||
* @param {Function} props.onWatchlistToggle - 切换自选股回调
|
* @param {Function} props.onWatchlistToggle - 切换自选股回调
|
||||||
|
* @param {Array} props.klineData - 预加载的K线数据(可选,由父组件批量加载后传入)
|
||||||
|
* @param {boolean} props.klineLoading - K线数据加载状态
|
||||||
*/
|
*/
|
||||||
const StockListItem = ({
|
const StockListItem = ({
|
||||||
stock,
|
stock,
|
||||||
quote = null,
|
quote = null,
|
||||||
eventTime = null,
|
eventTime = null,
|
||||||
isInWatchlist = false,
|
isInWatchlist = false,
|
||||||
onWatchlistToggle
|
onWatchlistToggle,
|
||||||
|
klineData,
|
||||||
|
klineLoading = false
|
||||||
}) => {
|
}) => {
|
||||||
const isMobile = useSelector(selectIsMobile);
|
const isMobile = useSelector(selectIsMobile);
|
||||||
const cardBg = PROFESSIONAL_COLORS.background.card;
|
const cardBg = PROFESSIONAL_COLORS.background.card;
|
||||||
@@ -237,6 +241,8 @@ const StockListItem = ({
|
|||||||
<MiniTimelineChart
|
<MiniTimelineChart
|
||||||
stockCode={stock.stock_code}
|
stockCode={stock.stock_code}
|
||||||
eventTime={eventTime}
|
eventTime={eventTime}
|
||||||
|
preloadedData={klineData}
|
||||||
|
loading={klineLoading}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|||||||
Reference in New Issue
Block a user