diff --git a/src/components/Auth/AuthFormContent.js b/src/components/Auth/AuthFormContent.js index 684b302f..02e25ad0 100644 --- a/src/components/Auth/AuthFormContent.js +++ b/src/components/Auth/AuthFormContent.js @@ -182,6 +182,7 @@ export default function AuthFormContent() { const config = AUTH_CONFIG; const isMountedRef = useRef(true); + const wechatRef = useRef(null); // Tab 状态: 'wechat' | 'phone' const [activeTab, setActiveTab] = useState('wechat'); @@ -203,6 +204,14 @@ export default function AuthFormContent() { // 切换 Tab const handleTabChange = (tab) => { + // 切换到手机登录时,停止微信轮询 + if (tab === 'phone' && wechatRef.current) { + wechatRef.current.stopPolling(); + } + // 切换到微信登录时,自动获取二维码 + if (tab === 'wechat' && wechatRef.current) { + wechatRef.current.fetchQRCode(); + } setActiveTab(tab); authEvents.trackLoginPageViewed(); // 追踪切换 }; @@ -381,7 +390,19 @@ export default function AuthFormContent() { useEffect(() => { isMountedRef.current = true; authEvents.trackLoginPageViewed(); - return () => { isMountedRef.current = false; }; + + // 组件挂载时,如果默认 Tab 是微信登录,自动获取二维码 + // 使用 setTimeout 确保 ref 已经绑定 + const timer = setTimeout(() => { + if (activeTab === 'wechat' && wechatRef.current) { + wechatRef.current.fetchQRCode(); + } + }, 100); + + return () => { + isMountedRef.current = false; + clearTimeout(timer); + }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -459,7 +480,7 @@ export default function AuthFormContent() { // 渲染微信登录区域 const renderWechatLogin = () => (
- +
扫码即表示同意{" "} { @@ -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 ? ( -