扫码即表示同意{" "}
{
@@ -139,7 +156,7 @@ const getStatusColor = (status) => {
const getStatusText = (status) => STATUS_MESSAGES[status] || "点击按钮获取二维码";
-export default function WechatRegister({ subtitle }) {
+const WechatRegister = forwardRef(function WechatRegister({ subtitle }, ref) {
const { closeModal } = useAuthModal();
const { refreshSession } = useAuth();
const authEvents = useAuthEvents({ component: 'WechatRegister', isMobile: false });
@@ -148,13 +165,16 @@ export default function WechatRegister({ subtitle }) {
const [wechatSessionId, setWechatSessionId] = useState("");
const [wechatStatus, setWechatStatus] = useState(WECHAT_STATUS.NONE);
const [isLoading, setIsLoading] = useState(false);
+ const [iframeLoading, setIframeLoading] = useState(true);
const pollIntervalRef = useRef(null);
const timeoutRef = useRef(null);
+ const warningTimeoutRef = useRef(null); // 过期预警定时器
const isMountedRef = useRef(true);
const containerRef = useRef(null);
const sessionIdRef = useRef(null);
const wechatStatusRef = useRef(WECHAT_STATUS.NONE);
+ const isLoadingRef = useRef(false); // 用于防止重复请求(避免闭包陷阱)
const clearTimers = useCallback(() => {
if (pollIntervalRef.current) {
@@ -165,8 +185,21 @@ export default function WechatRegister({ subtitle }) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
+ if (warningTimeoutRef.current) {
+ clearTimeout(warningTimeoutRef.current);
+ warningTimeoutRef.current = null;
+ }
}, []);
+ // 重置所有状态(切换 Tab 时调用)
+ const resetState = useCallback(() => {
+ clearTimers();
+ setWechatAuthUrl("");
+ setWechatSessionId("");
+ setWechatStatus(WECHAT_STATUS.NONE);
+ sessionIdRef.current = null;
+ }, [clearTimers]);
+
const handleLoginSuccess = useCallback(async (sessionId, status) => {
try {
const response = await authService.loginWithWechat(sessionId);
@@ -237,6 +270,15 @@ export default function WechatRegister({ subtitle }) {
const startPolling = useCallback(() => {
clearTimers();
pollIntervalRef.current = setInterval(checkWechatStatus, POLL_INTERVAL);
+
+ // 2 分钟后显示即将过期提示
+ warningTimeoutRef.current = setTimeout(() => {
+ if (isMountedRef.current) {
+ message.warning("二维码即将过期,请尽快扫码", 5);
+ }
+ }, QR_CODE_WARNING_TIME);
+
+ // 5 分钟后过期
timeoutRef.current = setTimeout(() => {
clearTimers();
sessionIdRef.current = null;
@@ -245,9 +287,13 @@ export default function WechatRegister({ subtitle }) {
}, [checkWechatStatus, clearTimers]);
const getWechatQRCode = useCallback(async () => {
+ // 防止重复请求(使用 ref 避免闭包陷阱)
+ if (isLoadingRef.current) return;
+
try {
+ isLoadingRef.current = true;
setIsLoading(true);
- const isRefresh = Boolean(wechatSessionId);
+ const isRefresh = Boolean(sessionIdRef.current); // 使用 ref 判断是否刷新
authEvents.trackWechatLoginInitiated(isRefresh ? 'qr_refresh' : 'qr_area');
const response = await authService.getWechatQRCode();
@@ -257,8 +303,9 @@ export default function WechatRegister({ subtitle }) {
throw new Error(response?.message || '获取二维码失败');
}
- if (isRefresh) {
- authEvents.trackWechatQRRefreshed(wechatSessionId, response.data.session_id);
+ const oldSessionId = sessionIdRef.current;
+ if (isRefresh && oldSessionId) {
+ authEvents.trackWechatQRRefreshed(oldSessionId, response.data.session_id);
} else {
authEvents.trackWechatQRDisplayed(response.data.session_id, response.data.auth_url);
}
@@ -267,15 +314,17 @@ export default function WechatRegister({ subtitle }) {
setWechatAuthUrl(response.data.auth_url);
setWechatSessionId(response.data.session_id);
setWechatStatus(WECHAT_STATUS.WAITING);
+ setIframeLoading(true); // 重置 iframe 加载状态
startPolling();
} catch (error) {
authEvents.trackError('api', error.message || '获取二维码失败');
logger.error('WechatRegister', 'getWechatQRCode', error);
message.error(error.message || "获取微信授权失败,请稍后重试");
} finally {
+ isLoadingRef.current = false;
if (isMountedRef.current) setIsLoading(false);
}
- }, [startPolling, wechatSessionId, authEvents]);
+ }, [startPolling, authEvents]); // 移除 wechatSessionId 依赖,使用 ref
const handleGetQRCodeClick = useCallback(async () => {
try {
@@ -296,6 +345,12 @@ export default function WechatRegister({ subtitle }) {
};
}, [clearTimers]);
+ // 暴露方法给父组件
+ useImperativeHandle(ref, () => ({
+ fetchQRCode: getWechatQRCode,
+ stopPolling: resetState, // 停止轮询并重置状态
+ }), [getWechatQRCode, resetState]);
+
return (
微信登陆
@@ -305,22 +360,30 @@ export default function WechatRegister({ subtitle }) {
{wechatStatus === WECHAT_STATUS.WAITING ? (
-
+ <>
+ {iframeLoading && (
+
+ 二维码加载中...
+
+ )}
+