update pay ui

This commit is contained in:
2025-12-03 12:39:59 +08:00
parent ae9904cd03
commit dd975a65b2
3 changed files with 166 additions and 65 deletions

View File

@@ -17,9 +17,11 @@ import {
* @param {string} stockCode - 股票代码
* @param {string} eventTime - 事件时间(可选)
* @param {Function} onClick - 点击回调(可选)
* @param {Array} preloadedData - 预加载的K线数据可选由父组件批量加载后传入
* @param {boolean} loading - 外部加载状态(可选)
* @returns {JSX.Element}
*/
const MiniTimelineChart = React.memo(function MiniTimelineChart({ stockCode, eventTime, onClick }) {
const MiniTimelineChart = React.memo(function MiniTimelineChart({ stockCode, eventTime, onClick, preloadedData, loading: externalLoading }) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const mountedRef = useRef(true);
@@ -65,6 +67,15 @@ const MiniTimelineChart = React.memo(function MiniTimelineChart({ stockCode, eve
return;
}
// 优先使用预加载的数据(由父组件批量请求后传入)
if (preloadedData !== undefined) {
setData(preloadedData || []);
setLoading(false);
loadedRef.current = true;
dataFetchedRef.current = true;
return;
}
// 如果已经请求过数据,不再重复请求
if (dataFetchedRef.current) {
return;
@@ -75,6 +86,12 @@ const MiniTimelineChart = React.memo(function MiniTimelineChart({ stockCode, eve
return;
}
// 如果外部正在加载,等待外部加载完成
if (externalLoading) {
setLoading(true);
return;
}
// 检查批量请求的函数
const checkBatchAndLoad = () => {
// 再次检查缓存(批量请求可能已完成)
@@ -119,7 +136,6 @@ const MiniTimelineChart = React.memo(function MiniTimelineChart({ stockCode, eve
}
// 延迟一小段时间再检查(等待批量请求启动)
// 因为 StockTable 的 useEffect 可能还没执行
setLoading(true);
const timeoutId = setTimeout(() => {
if (!mountedRef.current || dataFetchedRef.current) return;
@@ -129,7 +145,7 @@ const MiniTimelineChart = React.memo(function MiniTimelineChart({ stockCode, eve
return;
}
// 仍然没有批量请求,发起单独请求
// 仍然没有批量请求,发起单独请求(备用方案)
dataFetchedRef.current = true;
fetchKlineData(stockCode, stableEventTime)
@@ -147,10 +163,10 @@ const MiniTimelineChart = React.memo(function MiniTimelineChart({ stockCode, eve
loadedRef.current = true;
}
});
}, 50); // 延迟 50ms 等待批量请求启动
}, 100); // 延迟 100ms 等待批量请求
return () => clearTimeout(timeoutId);
}, [stockCode, stableEventTime, loadData]); // 注意这里使用 stableEventTime
}, [stockCode, stableEventTime, loadData, preloadedData, externalLoading]); // 添加 preloadedData 和 externalLoading 依赖
const chartOption = useMemo(() => {
const prices = data.map(item => item.close ?? item.price).filter(v => typeof v === 'number');
@@ -249,10 +265,12 @@ const MiniTimelineChart = React.memo(function MiniTimelineChart({ stockCode, eve
</div>
);
}, (prevProps, nextProps) => {
// 自定义比较函数只有当stockCode、eventTime或onClick变化时才重新渲染
// 自定义比较函数
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 MiniTimelineChart;

View File

@@ -4,7 +4,7 @@ import { Table, Button } from 'antd';
import { StarFilled, StarOutlined } from '@ant-design/icons';
import dayjs from 'dayjs';
import MiniTimelineChart from './MiniTimelineChart';
import { preloadBatchKlineData } from '../utils/klineDataCache';
import { fetchBatchKlineData, klineDataCache, getCacheKey } from '../utils/klineDataCache';
import { logger } from '../../../../../utils/logger';
/**
@@ -29,27 +29,71 @@ const StockTable = ({
}) => {
// 展开/收缩的行
const [expandedRows, setExpandedRows] = useState(new Set());
// 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]);
// 批量加载K线数据
// 使用 stocks 的 JSON 字符串作为依赖项的 key避免引用变化导致重复加载
// 批量加载K线数据
// 使用 stocks 的 JSON 字符串作为依赖项的 key避免引用变化导致重复加载
const stocksKey = useMemo(() => {
return stocks.map(s => s.stock_code).sort().join(',');
}, [stocks]);
useEffect(() => {
if (stocks.length > 0) {
const stockCodes = stocks.map(s => s.stock_code);
logger.debug('StockTable', '批量预加载K线数据', {
stockCount: stockCodes.length,
eventTime: stableEventTime
});
preloadBatchKlineData(stockCodes, stableEventTime, 'timeline');
if (stocks.length === 0) {
setKlineDataMap({});
return;
}
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);
logger.debug('StockTable', 'K线数据全部来自缓存', { stockCount: stockCodes.length });
return;
}
logger.debug('StockTable', '批量加载K线数据', {
totalCount: stockCodes.length,
cachedCount: Object.keys(cachedData).length,
uncachedCount: uncachedCodes.length,
eventTime: stableEventTime
});
setKlineLoading(true);
// 批量请求未缓存的数据
fetchBatchKlineData(stockCodes, stableEventTime, 'timeline')
.then((batchData) => {
// 合并缓存数据和新数据
setKlineDataMap({ ...cachedData, ...batchData });
setKlineLoading(false);
})
.catch((error) => {
logger.error('StockTable', '批量加载K线数据失败', error);
// 失败时使用已有的缓存数据
setKlineDataMap(cachedData);
setKlineLoading(false);
});
}, [stocksKey, stableEventTime]); // 使用 stocksKey 而非 stocks 对象引用
// 切换行展开状态
@@ -175,6 +219,8 @@ const StockTable = ({
<MiniTimelineChart
stockCode={record.stock_code}
eventTime={stableEventTime}
preloadedData={klineDataMap[record.stock_code]}
loading={klineLoading && !klineDataMap[record.stock_code]}
/>
),
},
@@ -225,7 +271,7 @@ const StockTable = ({
);
},
},
], [quotes, stableEventTime, expandedRows, toggleRowExpand, watchlistSet, onWatchlistToggle]);
], [quotes, stableEventTime, expandedRows, toggleRowExpand, watchlistSet, onWatchlistToggle, klineDataMap, klineLoading]);
return (
<div style={{ position: 'relative' }}>