update pay ui
This commit is contained in:
@@ -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
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user