update pay ui

This commit is contained in:
2025-12-03 16:50:39 +08:00
parent c136c2aed8
commit 63023adcf3

View File

@@ -1,16 +1,19 @@
// src/hooks/useWatchlist.js // src/hooks/useWatchlist.js
// 自选股管理自定义 Hook // 自选股管理自定义 Hook(导航栏专用,与 Redux 状态同步)
import { useState, useCallback } from 'react'; import { useState, useCallback, useEffect, useRef } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useToast } from '@chakra-ui/react'; import { useToast } from '@chakra-ui/react';
import { logger } from '../utils/logger'; import { logger } from '../utils/logger';
import { getApiBase } from '../utils/apiConfig'; import { getApiBase } from '../utils/apiConfig';
import { toggleWatchlist as toggleWatchlistAction } from '../store/slices/stockSlice';
const WATCHLIST_PAGE_SIZE = 10; const WATCHLIST_PAGE_SIZE = 10;
/** /**
* 自选股管理 Hook * 自选股管理 Hook(导航栏专用)
* 提供自选股加载、分页、移除等功能 * 提供自选股加载、分页、移除等功能
* 监听 Redux 中的 watchlist 变化,自动刷新行情数据
* *
* @returns {{ * @returns {{
* watchlistQuotes: Array, * watchlistQuotes: Array,
@@ -19,14 +22,24 @@ const WATCHLIST_PAGE_SIZE = 10;
* setWatchlistPage: Function, * setWatchlistPage: Function,
* WATCHLIST_PAGE_SIZE: number, * WATCHLIST_PAGE_SIZE: number,
* loadWatchlistQuotes: Function, * loadWatchlistQuotes: Function,
* handleRemoveFromWatchlist: Function * handleRemoveFromWatchlist: Function,
* followingEvents: Array
* }} * }}
*/ */
export const useWatchlist = () => { export const useWatchlist = () => {
const toast = useToast(); const toast = useToast();
const dispatch = useDispatch();
const [watchlistQuotes, setWatchlistQuotes] = useState([]); const [watchlistQuotes, setWatchlistQuotes] = useState([]);
const [watchlistLoading, setWatchlistLoading] = useState(false); const [watchlistLoading, setWatchlistLoading] = useState(false);
const [watchlistPage, setWatchlistPage] = useState(1); const [watchlistPage, setWatchlistPage] = useState(1);
const [followingEvents, setFollowingEvents] = useState([]);
// 从 Redux 获取自选股列表(用于监听变化)
const reduxWatchlist = useSelector(state => state.stock.watchlist);
// 用于跟踪上一次的 watchlist 长度,避免初始加载时重复请求
const prevWatchlistLengthRef = useRef(reduxWatchlist?.length || 0);
const isInitialMount = useRef(true);
// 加载自选股实时行情 // 加载自选股实时行情
const loadWatchlistQuotes = useCallback(async () => { const loadWatchlistQuotes = useCallback(async () => {
@@ -58,35 +71,74 @@ export const useWatchlist = () => {
} }
}, []); }, []);
// 从自选股移除 // 监听 Redux watchlist 变化,自动刷新行情数据
useEffect(() => {
// 跳过初始挂载(初始加载由 HomeNavbar 触发)
if (isInitialMount.current) {
isInitialMount.current = false;
prevWatchlistLengthRef.current = reduxWatchlist?.length || 0;
return;
}
const currentLength = reduxWatchlist?.length || 0;
const prevLength = prevWatchlistLengthRef.current;
// 只有当 watchlist 长度发生变化时才刷新
if (currentLength !== prevLength) {
logger.debug('useWatchlist', 'Redux watchlist 变化,刷新行情', {
prevLength,
currentLength
});
prevWatchlistLengthRef.current = currentLength;
// 延迟一小段时间再刷新,确保后端数据已更新
const timer = setTimeout(() => {
loadWatchlistQuotes();
}, 300);
return () => clearTimeout(timer);
}
}, [reduxWatchlist, loadWatchlistQuotes]);
// 从自选股移除(同时更新 Redux 和本地状态)
const handleRemoveFromWatchlist = useCallback(async (stockCode) => { const handleRemoveFromWatchlist = useCallback(async (stockCode) => {
try { try {
const base = getApiBase(); // 找到股票名称
const resp = await fetch(base + `/api/account/watchlist/${stockCode}`, { const stockItem = watchlistQuotes.find(item => {
method: 'DELETE', const normalize6 = (code) => {
credentials: 'include' const m = String(code || '').match(/(\d{6})/);
return m ? m[1] : String(code || '');
};
return normalize6(item.stock_code) === normalize6(stockCode);
}); });
const data = await resp.json().catch(() => ({})); const stockName = stockItem?.stock_name || '';
if (resp.ok && data && data.success !== false) {
setWatchlistQuotes((prev) => { // 通过 Redux action 移除(会同步更新 Redux 状态)
const normalize6 = (code) => { await dispatch(toggleWatchlistAction({
const m = String(code || '').match(/(\d{6})/); stockCode,
return m ? m[1] : String(code || ''); stockName,
}; isInWatchlist: true // 表示当前在自选股中,需要移除
const target = normalize6(stockCode); })).unwrap();
const updated = (prev || []).filter((x) => normalize6(x.stock_code) !== target);
const newMaxPage = Math.max(1, Math.ceil((updated.length || 0) / WATCHLIST_PAGE_SIZE)); // 更新本地状态(立即响应 UI
setWatchlistPage((p) => Math.min(p, newMaxPage)); setWatchlistQuotes((prev) => {
return updated; const normalize6 = (code) => {
}); const m = String(code || '').match(/(\d{6})/);
toast({ title: '已从自选股移除', status: 'info', duration: 1500 }); return m ? m[1] : String(code || '');
} else { };
toast({ title: '移除失败', status: 'error', duration: 2000 }); const target = normalize6(stockCode);
} const updated = (prev || []).filter((x) => normalize6(x.stock_code) !== target);
const newMaxPage = Math.max(1, Math.ceil((updated.length || 0) / WATCHLIST_PAGE_SIZE));
setWatchlistPage((p) => Math.min(p, newMaxPage));
return updated;
});
toast({ title: '已从自选股移除', status: 'info', duration: 1500 });
} catch (e) { } catch (e) {
toast({ title: '网络错误,移除失败', status: 'error', duration: 2000 }); logger.error('useWatchlist', '移除自选股失败', e);
toast({ title: e.message || '移除失败', status: 'error', duration: 2000 });
} }
}, [toast]); }, [dispatch, watchlistQuotes, toast]);
return { return {
watchlistQuotes, watchlistQuotes,
@@ -95,6 +147,7 @@ export const useWatchlist = () => {
setWatchlistPage, setWatchlistPage,
WATCHLIST_PAGE_SIZE, WATCHLIST_PAGE_SIZE,
loadWatchlistQuotes, loadWatchlistQuotes,
handleRemoveFromWatchlist handleRemoveFromWatchlist,
followingEvents
}; };
}; };