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 = ({