// src/components/Auth/AuthFormContent.js // 统一的认证表单组件 import React, { useState, useEffect, useRef } from "react"; import { useNavigate } from "react-router-dom"; import { Box, Button, FormControl, Input, Heading, VStack, HStack, Stack, useToast, Icon, FormErrorMessage, Center, AlertDialog, AlertDialogBody, AlertDialogFooter, AlertDialogHeader, AlertDialogContent, AlertDialogOverlay, Text, Link as ChakraLink, useBreakpointValue, Divider, IconButton, } from "@chakra-ui/react"; import { FaLock, FaWeixin } from "react-icons/fa"; import { useAuth } from "../../contexts/AuthContext"; import { useAuthModal } from "../../contexts/AuthModalContext"; import { authService } from "../../services/authService"; import AuthHeader from './AuthHeader'; import VerificationCodeInput from './VerificationCodeInput'; import WechatRegister from './WechatRegister'; // API配置 const isProduction = process.env.NODE_ENV === 'production'; const API_BASE_URL = isProduction ? "" : "http://49.232.185.254:5000"; // 统一配置对象 const AUTH_CONFIG = { // UI文本 title: "价值前沿", subtitle: "开启您的投资之旅", formTitle: "登陆/注册", buttonText: "登录/注册", loadingText: "验证中...", successTitle: "验证成功", successDescription: "欢迎!", errorTitle: "验证失败", // API配置 api: { endpoint: '/api/auth/register/phone', purpose: 'register', }, // 功能开关 features: { successDelay: 1000, // 延迟1秒显示成功提示 } }; export default function AuthFormContent() { const toast = useToast(); const navigate = useNavigate(); const { checkSession } = useAuth(); const { handleLoginSuccess } = useAuthModal(); // 使用统一配置 const config = AUTH_CONFIG; // 追踪组件挂载状态,防止内存泄漏 const isMountedRef = useRef(true); const cancelRef = useRef(); // AlertDialog 需要的 ref // 页面状态 const [isLoading, setIsLoading] = useState(false); const [errors, setErrors] = useState({}); // 昵称设置引导对话框 const [showNicknamePrompt, setShowNicknamePrompt] = useState(false); const [currentPhone, setCurrentPhone] = useState(""); // 响应式布局配置 const isMobile = useBreakpointValue({ base: true, md: false }); const stackDirection = useBreakpointValue({ base: "column", md: "row" }); const stackSpacing = useBreakpointValue({ base: 4, md: 8 }); // 表单数据 const [formData, setFormData] = useState({ phone: "", verificationCode: "", }); // 验证码状态 const [verificationCodeSent, setVerificationCodeSent] = useState(false); const [sendingCode, setSendingCode] = useState(false); const [countdown, setCountdown] = useState(0); // 输入框变化处理 const handleInputChange = (e) => { const { name, value } = e.target; setFormData(prev => ({ ...prev, [name]: value })); }; // 倒计时逻辑 useEffect(() => { let timer; let isMounted = true; if (countdown > 0) { timer = setInterval(() => { if (isMounted) { setCountdown(prev => prev - 1); } }, 1000); } else if (countdown === 0 && isMounted) { setVerificationCodeSent(false); } return () => { isMounted = false; if (timer) clearInterval(timer); }; }, [countdown]); // 发送验证码 const sendVerificationCode = async () => { const credential = formData.phone; if (!credential) { toast({ title: "请先输入手机号", status: "warning", duration: 3000, }); return; } if (!/^1[3-9]\d{9}$/.test(credential)) { toast({ title: "请输入有效的手机号", status: "warning", duration: 3000, }); return; } try { setSendingCode(true); const response = await fetch(`${API_BASE_URL}/api/auth/send-verification-code`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ credential, type: 'phone', purpose: config.api.purpose // 根据模式使用不同的purpose }), }); if (!response) { throw new Error('网络请求失败,请检查网络连接'); } const data = await response.json(); if (!isMountedRef.current) return; if (!data) { throw new Error('服务器响应为空'); } if (response.ok && data.success) { toast({ title: "验证码已发送", description: "验证码已发送到您的手机号", status: "success", duration: 3000, }); setVerificationCodeSent(true); setCountdown(60); } else { throw new Error(data.error || '发送验证码失败'); } } catch (error) { if (isMountedRef.current) { toast({ title: "发送验证码失败", description: error.message || "请稍后重试", status: "error", duration: 3000, }); } } finally { if (isMountedRef.current) { setSendingCode(false); } } }; // 提交处理(登录或注册) const handleSubmit = async (e) => { e.preventDefault(); setIsLoading(true); try { const { phone, verificationCode, nickname } = formData; // 表单验证 if (!phone || !verificationCode) { toast({ title: "请填写完整信息", description: "手机号和验证码不能为空", status: "warning", duration: 3000, }); return; } if (!/^1[3-9]\d{9}$/.test(phone)) { toast({ title: "请输入有效的手机号", status: "warning", duration: 3000, }); return; } // 构建请求体 const requestBody = { credential: phone, verification_code: verificationCode, login_type: 'phone', }; // 调用API(根据模式选择不同的endpoint const response = await fetch(`${API_BASE_URL}/api/auth/login-with-code`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, credentials: 'include', body: JSON.stringify(requestBody), }); if (!response) { throw new Error('网络请求失败,请检查网络连接'); } const data = await response.json(); if (!isMountedRef.current) return; if (!data) { throw new Error('服务器响应为空'); } if (response.ok && data.success) { // 更新session await checkSession(); toast({ title: config.successTitle, description: config.successDescription, status: "success", duration: 2000, }); // 检查是否为新注册用户 if (data.isNewUser) { // 新注册用户,延迟后显示昵称设置引导 setTimeout(() => { setCurrentPhone(phone); setShowNicknamePrompt(true); }, config.features.successDelay); } else { // 已有用户,直接登录成功 setTimeout(() => { handleLoginSuccess({ phone }); }, config.features.successDelay); } } else { throw new Error(data.error || `${config.errorTitle}`); } } catch (error) { console.error('Auth error:', error); if (isMountedRef.current) { toast({ title: config.errorTitle, description: error.message || "请稍后重试", status: "error", duration: 3000, }); } } finally { if (isMountedRef.current) { setIsLoading(false); } } }; // 微信H5登录处理 const handleWechatH5Login = async () => { try { // 1. 构建回调URL const redirectUrl = `${window.location.origin}/home/wechat-callback`; // 2. 显示提示 toast({ title: "即将跳转", description: "正在跳转到微信授权页面...", status: "info", duration: 2000, isClosable: true, }); // 3. 获取微信H5授权URL const response = await authService.getWechatH5AuthUrl(redirectUrl); if (!response || !response.auth_url) { throw new Error('获取授权链接失败'); } // 4. 延迟跳转,让用户看到提示 setTimeout(() => { window.location.href = response.auth_url; }, 500); } catch (error) { console.error('微信H5登录失败:', error); toast({ title: "跳转失败", description: error.message || "请稍后重试", status: "error", duration: 3000, isClosable: true, }); } }; // 组件卸载时清理 useEffect(() => { isMountedRef.current = true; return () => { isMountedRef.current = false; }; }, []); return ( <>
{config.formTitle} {errors.phone} {/* 验证码输入框 + 移动端微信图标 */} {/* 移动端:验证码下方的微信登录图标 */} {isMobile && ( 其他登录方式: } size="sm" variant="ghost" color="#07C160" borderRadius="md" minW="24px" minH="24px" _hover={{ bg: "green.50", color: "#06AD56" }} _active={{ bg: "green.100" }} onClick={handleWechatH5Login} isDisabled={isLoading} /> )} {/* 隐私声明 */} 登录即表示您同意价值前沿{" "} 《用户协议》 {" "}和{" "} 《隐私政策》
{/* 桌面端:右侧二维码扫描 */} {!isMobile && (
)}
{/* 只在需要时才渲染 AlertDialog,避免创建不必要的 Portal */} {showNicknamePrompt && ( { setShowNicknamePrompt(false); handleLoginSuccess({ phone: currentPhone }); }} isCentered closeOnEsc={true} closeOnOverlayClick={false}> 完善个人信息 您已成功注册!是否前往个人中心设置昵称和其他信息? )} ); }