From 9a6230e51ed50fdcbca98fe249d982f933965624 Mon Sep 17 00:00:00 2001 From: zzlgreat Date: Wed, 3 Dec 2025 13:06:23 +0800 Subject: [PATCH] update pay ui --- .../DynamicNewsDetail/MiniKLineChart.js | 31 ++++- .../DynamicNewsDetail/RelatedStocksSection.js | 120 +++++++++++++----- .../DynamicNewsDetail/StockListItem.js | 18 ++- 3 files changed, 123 insertions(+), 46 deletions(-) diff --git a/src/views/Community/components/DynamicNewsDetail/MiniKLineChart.js b/src/views/Community/components/DynamicNewsDetail/MiniKLineChart.js index a13c39ea..92d0074c 100644 --- a/src/views/Community/components/DynamicNewsDetail/MiniKLineChart.js +++ b/src/views/Community/components/DynamicNewsDetail/MiniKLineChart.js @@ -15,9 +15,11 @@ import { * @param {string} stockCode - 股票代码 * @param {string} eventTime - 事件时间(可选) * @param {Function} onClick - 点击回调(可选) + * @param {Array} preloadedData - 预加载的K线数据(可选,由父组件批量加载后传入) + * @param {boolean} loading - 外部加载状态(可选) * @returns {JSX.Element} */ -const MiniKLineChart = React.memo(function MiniKLineChart({ stockCode, eventTime, onClick }) { +const MiniKLineChart = React.memo(function MiniKLineChart({ stockCode, eventTime, onClick, preloadedData, loading: externalLoading }) { const [data, setData] = useState([]); const [loading, setLoading] = useState(false); const mountedRef = useRef(true); @@ -44,6 +46,21 @@ const MiniKLineChart = React.memo(function MiniKLineChart({ stockCode, eventTime return; } + // 优先使用预加载的数据(由父组件批量请求后传入) + if (preloadedData !== undefined) { + setData(preloadedData || []); + setLoading(false); + loadedRef.current = true; + dataFetchedRef.current = true; + return; + } + + // 如果外部正在加载,显示loading状态,不发起单独请求 + if (externalLoading) { + setLoading(true); + return; + } + if (dataFetchedRef.current) { return; } @@ -52,8 +69,8 @@ const MiniKLineChart = React.memo(function MiniKLineChart({ stockCode, eventTime const cacheKey = getCacheKey(stockCode, stableEventTime, 'daily'); const cachedData = klineDataCache.get(cacheKey); - if (cachedData && cachedData.length > 0) { - setData(cachedData); + if (cachedData !== undefined) { + setData(cachedData || []); loadedRef.current = true; dataFetchedRef.current = true; return; @@ -62,7 +79,7 @@ const MiniKLineChart = React.memo(function MiniKLineChart({ stockCode, eventTime dataFetchedRef.current = true; setLoading(true); - // 获取日K线数据 + // 获取日K线数据(备用方案) fetchKlineData(stockCode, stableEventTime, 'daily') .then((result) => { if (mountedRef.current) { @@ -78,7 +95,7 @@ const MiniKLineChart = React.memo(function MiniKLineChart({ stockCode, eventTime loadedRef.current = true; } }); - }, [stockCode, stableEventTime]); + }, [stockCode, stableEventTime, preloadedData, externalLoading]); const chartOption = useMemo(() => { // 提取K线数据 [open, close, low, high] @@ -179,7 +196,9 @@ const MiniKLineChart = React.memo(function MiniKLineChart({ stockCode, eventTime }, (prevProps, nextProps) => { return prevProps.stockCode === nextProps.stockCode && prevProps.eventTime === nextProps.eventTime && - prevProps.onClick === nextProps.onClick; + prevProps.onClick === nextProps.onClick && + prevProps.preloadedData === nextProps.preloadedData && + prevProps.loading === nextProps.loading; }); export default MiniKLineChart; diff --git a/src/views/Community/components/DynamicNewsDetail/RelatedStocksSection.js b/src/views/Community/components/DynamicNewsDetail/RelatedStocksSection.js index ebcacaa2..49e1c25f 100644 --- a/src/views/Community/components/DynamicNewsDetail/RelatedStocksSection.js +++ b/src/views/Community/components/DynamicNewsDetail/RelatedStocksSection.js @@ -25,9 +25,13 @@ const RelatedStocksSection = ({ watchlistSet = new Set(), onWatchlistToggle }) => { - // K线数据状态:{ [stockCode]: data[] } - const [klineDataMap, setKlineDataMap] = useState({}); - const [klineLoading, setKlineLoading] = useState(false); + // 分时图数据状态:{ [stockCode]: data[] } + const [timelineDataMap, setTimelineDataMap] = useState({}); + const [timelineLoading, setTimelineLoading] = useState(false); + + // 日K线数据状态:{ [stockCode]: data[] } + const [dailyDataMap, setDailyDataMap] = useState({}); + const [dailyLoading, setDailyLoading] = useState(false); // 稳定的事件时间 const stableEventTime = useMemo(() => { @@ -40,67 +44,113 @@ const RelatedStocksSection = ({ return stocks.map(s => s.stock_code).sort().join(','); }, [stocks]); - // 计算是否应该显示 loading - const shouldShowLoading = useMemo(() => { + // 计算分时图是否应该显示 loading + const shouldShowTimelineLoading = useMemo(() => { if (!stocks || stocks.length === 0) return false; - const currentDataKeys = Object.keys(klineDataMap).sort().join(','); + const currentDataKeys = Object.keys(timelineDataMap).sort().join(','); if (stocksKey !== currentDataKeys) { return true; } - return klineLoading; - }, [stocks, stocksKey, klineDataMap, klineLoading]); + return timelineLoading; + }, [stocks, stocksKey, timelineDataMap, timelineLoading]); - // 批量加载K线数据 + // 计算日K线是否应该显示 loading + const shouldShowDailyLoading = useMemo(() => { + if (!stocks || stocks.length === 0) return false; + const currentDataKeys = Object.keys(dailyDataMap).sort().join(','); + if (stocksKey !== currentDataKeys) { + return true; + } + return dailyLoading; + }, [stocks, stocksKey, dailyDataMap, dailyLoading]); + + // 批量加载分时图数据 useEffect(() => { if (!stocks || stocks.length === 0) { - setKlineDataMap({}); - setKlineLoading(false); + setTimelineDataMap({}); + setTimelineLoading(false); return; } - // 立即设置 loading 状态 - setKlineLoading(true); - + setTimelineLoading(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 }); + if (Object.keys(cachedData).length === stockCodes.length) { + setTimelineDataMap(cachedData); + setTimelineLoading(false); + logger.debug('RelatedStocksSection', '分时图数据全部来自缓存', { stockCount: stockCodes.length }); return; } - logger.debug('RelatedStocksSection', '批量加载K线数据', { + logger.debug('RelatedStocksSection', '批量加载分时图数据', { totalCount: stockCodes.length, - cachedCount: Object.keys(cachedData).length, - uncachedCount: uncachedCodes.length, eventTime: stableEventTime }); - // 批量请求 fetchBatchKlineData(stockCodes, stableEventTime, 'timeline') .then((batchData) => { - setKlineDataMap({ ...cachedData, ...batchData }); - setKlineLoading(false); + setTimelineDataMap({ ...cachedData, ...batchData }); + setTimelineLoading(false); }) .catch((error) => { - logger.error('RelatedStocksSection', '批量加载K线数据失败', error); - setKlineDataMap(cachedData); - setKlineLoading(false); + logger.error('RelatedStocksSection', '批量加载分时图数据失败', error); + setTimelineDataMap(cachedData); + setTimelineLoading(false); + }); + }, [stocksKey, stableEventTime]); + + // 批量加载日K线数据 + useEffect(() => { + if (!stocks || stocks.length === 0) { + setDailyDataMap({}); + setDailyLoading(false); + return; + } + + setDailyLoading(true); + const stockCodes = stocks.map(s => s.stock_code); + + // 检查缓存 + const cachedData = {}; + stockCodes.forEach(code => { + const cacheKey = getCacheKey(code, stableEventTime, 'daily'); + const cached = klineDataCache.get(cacheKey); + if (cached !== undefined) { + cachedData[code] = cached; + } + }); + + if (Object.keys(cachedData).length === stockCodes.length) { + setDailyDataMap(cachedData); + setDailyLoading(false); + logger.debug('RelatedStocksSection', '日K线数据全部来自缓存', { stockCount: stockCodes.length }); + return; + } + + logger.debug('RelatedStocksSection', '批量加载日K线数据', { + totalCount: stockCodes.length, + eventTime: stableEventTime + }); + + fetchBatchKlineData(stockCodes, stableEventTime, 'daily') + .then((batchData) => { + setDailyDataMap({ ...cachedData, ...batchData }); + setDailyLoading(false); + }) + .catch((error) => { + logger.error('RelatedStocksSection', '批量加载日K线数据失败', error); + setDailyDataMap(cachedData); + setDailyLoading(false); }); }, [stocksKey, stableEventTime]); @@ -119,8 +169,10 @@ const RelatedStocksSection = ({ eventTime={eventTime} isInWatchlist={watchlistSet.has(stock.stock_code)} onWatchlistToggle={onWatchlistToggle} - klineData={klineDataMap[stock.stock_code]} - klineLoading={shouldShowLoading && !klineDataMap[stock.stock_code]} + timelineData={timelineDataMap[stock.stock_code]} + timelineLoading={shouldShowTimelineLoading && !timelineDataMap[stock.stock_code]} + dailyData={dailyDataMap[stock.stock_code]} + dailyLoading={shouldShowDailyLoading && !dailyDataMap[stock.stock_code]} /> ))} diff --git a/src/views/Community/components/DynamicNewsDetail/StockListItem.js b/src/views/Community/components/DynamicNewsDetail/StockListItem.js index f20e54fd..35fe821e 100644 --- a/src/views/Community/components/DynamicNewsDetail/StockListItem.js +++ b/src/views/Community/components/DynamicNewsDetail/StockListItem.js @@ -39,8 +39,10 @@ import { PROFESSIONAL_COLORS } from '@constants/professionalTheme'; * @param {string} props.eventTime - 事件时间(可选) * @param {boolean} props.isInWatchlist - 是否在自选股中 * @param {Function} props.onWatchlistToggle - 切换自选股回调 - * @param {Array} props.klineData - 预加载的K线数据(可选,由父组件批量加载后传入) - * @param {boolean} props.klineLoading - K线数据加载状态 + * @param {Array} props.timelineData - 预加载的分时图数据(可选,由父组件批量加载后传入) + * @param {boolean} props.timelineLoading - 分时图数据加载状态 + * @param {Array} props.dailyData - 预加载的日K线数据(可选,由父组件批量加载后传入) + * @param {boolean} props.dailyLoading - 日K线数据加载状态 */ const StockListItem = ({ stock, @@ -48,8 +50,10 @@ const StockListItem = ({ eventTime = null, isInWatchlist = false, onWatchlistToggle, - klineData, - klineLoading = false + timelineData, + timelineLoading = false, + dailyData, + dailyLoading = false }) => { const isMobile = useSelector(selectIsMobile); const cardBg = PROFESSIONAL_COLORS.background.card; @@ -241,8 +245,8 @@ const StockListItem = ({ @@ -285,6 +289,8 @@ const StockListItem = ({