| import React, { useState } from 'react'; |
| import { Link, useNavigate } from 'react-router-dom'; |
| import { Input, Button, message, Modal, Alert } from 'antd'; |
| import { MailOutlined, LockOutlined, UserOutlined, SafetyOutlined, ExclamationCircleOutlined, CheckCircleOutlined } from '@ant-design/icons'; |
| import { hashPassword } from '../../utils/crypto'; |
| import './RegisterPage.css'; |
| |
| const baseURL = 'http://10.126.59.25:8082'; |
| |
| const RegisterPage = () => { |
| const [formData, setFormData] = useState({ |
| username: '', |
| email: '', |
| emailCode: '', |
| password: '', |
| confirmPassword: '' |
| }); |
| |
| const [errors, setErrors] = useState({ |
| username: '', |
| email: '', |
| emailCode: '', |
| password: '', |
| confirmPassword: '' |
| }); |
| |
| const [emailCodeSent, setEmailCodeSent] = useState(false); |
| const [countdown, setCountdown] = useState(0); |
| const [sendingCode, setSendingCode] = useState(false); |
| const [isLoading, setIsLoading] = useState(false); |
| const [errorModal, setErrorModal] = useState({ |
| visible: false, |
| title: '', |
| content: '' |
| }); |
| const [successAlert, setSuccessAlert] = useState({ |
| visible: false, |
| message: '' |
| }); |
| const [emailCodeSuccessAlert, setEmailCodeSuccessAlert] = useState({ |
| visible: false, |
| message: '' |
| }); |
| |
| const navigate = useNavigate(); |
| |
| // 显示错误弹窗 |
| const showErrorModal = (title, content) => { |
| setErrorModal({ |
| visible: true, |
| title: title, |
| content: content |
| }); |
| }; |
| |
| // 关闭错误弹窗 |
| const closeErrorModal = () => { |
| setErrorModal({ |
| visible: false, |
| title: '', |
| content: '' |
| }); |
| }; |
| |
| // 显示成功提示 |
| const showSuccessAlert = (message) => { |
| setSuccessAlert({ |
| visible: true, |
| message: message |
| }); |
| |
| // 3秒后自动隐藏 |
| setTimeout(() => { |
| setSuccessAlert({ |
| visible: false, |
| message: '' |
| }); |
| }, 3000); |
| }; |
| |
| // 显示邮件验证码发送成功提示 |
| const showEmailCodeSuccessAlert = (message) => { |
| setEmailCodeSuccessAlert({ |
| visible: true, |
| message: message |
| }); |
| |
| // 5秒后自动隐藏 |
| setTimeout(() => { |
| setEmailCodeSuccessAlert({ |
| visible: false, |
| message: '' |
| }); |
| }, 5000); |
| }; |
| |
| // 倒计时效果 |
| React.useEffect(() => { |
| let timer; |
| if (countdown > 0) { |
| timer = setTimeout(() => { |
| setCountdown(countdown - 1); |
| }, 1000); |
| } |
| return () => clearTimeout(timer); |
| }, [countdown]); |
| |
| // 发送邮箱验证码 |
| const sendEmailCode = async () => { |
| // 验证邮箱格式 |
| if (!formData.email || typeof formData.email !== 'string' || !formData.email.trim()) { |
| setErrors(prev => ({ |
| ...prev, |
| email: '请先输入邮箱地址' |
| })); |
| return; |
| } |
| |
| if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { |
| setErrors(prev => ({ |
| ...prev, |
| email: '请输入有效的邮箱地址' |
| })); |
| return; |
| } |
| |
| setSendingCode(true); |
| |
| try { |
| // 调用后端API发送验证码 |
| const response = await fetch(baseURL + '/send-verification-code', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify({ |
| email: formData.email, |
| verification_type: 'register' |
| }) |
| }); |
| |
| if (!response.ok) { |
| throw new Error(`HTTP ${response.status}: ${response.statusText}`); |
| } |
| |
| const result = await response.json(); |
| |
| if (result.success) { |
| showEmailCodeSuccessAlert('验证码已发送到您的邮箱'); |
| setEmailCodeSent(true); |
| setCountdown(60); // 60秒倒计时 |
| |
| // 清除邮箱错误提示 |
| setErrors(prev => ({ |
| ...prev, |
| email: '' |
| })); |
| } else { |
| // 根据具体错误信息进行处理 |
| const errorMessage = result.message || '发送验证码失败,请稍后再试'; |
| |
| if (errorMessage.includes('邮箱') && (errorMessage.includes('已注册') || errorMessage.includes('已存在'))) { |
| setErrors(prev => ({ |
| ...prev, |
| email: errorMessage |
| })); |
| } else { |
| showErrorModal('发送验证码失败', errorMessage); |
| } |
| } |
| |
| } catch (error) { |
| console.error('发送验证码失败:', error); |
| |
| // 根据错误类型显示不同的错误信息 |
| if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) { |
| showErrorModal('网络连接失败', '无法连接到服务器,请检查您的网络连接后重试。如果问题持续存在,请联系客服。'); |
| } else if (error.message.includes('HTTP 500')) { |
| showErrorModal('服务器错误', '服务器出现了内部错误,请稍后重试。如果问题持续存在,请联系客服。'); |
| } else if (error.message.includes('HTTP 429')) { |
| showErrorModal('发送频率限制', '验证码发送过于频繁,请稍后再试。为了防止垃圾邮件,系统限制了发送频率。'); |
| } else if (error.message.includes('HTTP 400')) { |
| showErrorModal('请求错误', '邮箱格式错误,请检查邮箱地址是否正确。'); |
| } else { |
| showErrorModal('发送失败', '发送验证码失败,请稍后重试。如果问题持续存在,请联系客服。'); |
| } |
| } finally { |
| setSendingCode(false); |
| } |
| }; |
| |
| const handleInputChange = (field) => (e) => { |
| const value = e.target.value; |
| setFormData(prev => ({ |
| ...prev, |
| [field]: value |
| })); |
| |
| // 清除对应字段的错误提示 |
| if (errors[field]) { |
| setErrors(prev => ({ |
| ...prev, |
| [field]: '' |
| })); |
| } |
| }; |
| |
| const handlePasswordChange = (e) => { |
| const value = e.target.value; |
| setFormData(prev => ({ |
| ...prev, |
| password: value |
| })); |
| |
| // 清除密码错误提示 |
| if (errors.password) { |
| setErrors(prev => ({ |
| ...prev, |
| password: '' |
| })); |
| } |
| }; |
| |
| const handleConfirmPasswordChange = (e) => { |
| const value = e.target.value; |
| setFormData(prev => ({ |
| ...prev, |
| confirmPassword: value |
| })); |
| |
| // 清除确认密码错误提示 |
| if (errors.confirmPassword) { |
| setErrors(prev => ({ |
| ...prev, |
| confirmPassword: '' |
| })); |
| } |
| }; |
| |
| const validateForm = () => { |
| const newErrors = { |
| username: '', |
| email: '', |
| emailCode: '', |
| password: '', |
| confirmPassword: '' |
| }; |
| |
| let hasError = false; |
| |
| // 验证用户名 |
| if (!formData.username || typeof formData.username !== 'string' || !formData.username.trim()) { |
| newErrors.username = '请输入用户名'; |
| hasError = true; |
| } else if (formData.username.length < 2) { |
| newErrors.username = '用户名至少2个字符'; |
| hasError = true; |
| } else if (formData.username.length > 20) { |
| newErrors.username = '用户名不能超过20个字符'; |
| hasError = true; |
| } |
| |
| // 验证邮箱 |
| if (!formData.email || typeof formData.email !== 'string' || !formData.email.trim()) { |
| newErrors.email = '请输入邮箱地址'; |
| hasError = true; |
| } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { |
| newErrors.email = '请输入有效的邮箱地址'; |
| hasError = true; |
| } |
| |
| // 验证邮箱验证码 |
| if (!formData.emailCode || typeof formData.emailCode !== 'string' || !formData.emailCode.trim()) { |
| newErrors.emailCode = '请输入邮箱验证码'; |
| hasError = true; |
| } else if (formData.emailCode.length !== 6 || !/^\d{6}$/.test(formData.emailCode)) { |
| newErrors.emailCode = '请输入6位数字验证码'; |
| hasError = true; |
| } |
| |
| // 验证密码 |
| if (!formData.password || typeof formData.password !== 'string' || !formData.password.trim()) { |
| newErrors.password = '请输入密码'; |
| hasError = true; |
| } else if (formData.password.length < 6) { |
| newErrors.password = '密码长度至少6位'; |
| hasError = true; |
| } else if (formData.password.length > 20) { |
| newErrors.password = '密码长度不能超过20位'; |
| hasError = true; |
| } |
| |
| // 验证确认密码 |
| if (!formData.confirmPassword || typeof formData.confirmPassword !== 'string' || !formData.confirmPassword.trim()) { |
| newErrors.confirmPassword = '请确认密码'; |
| hasError = true; |
| } else if (formData.password !== formData.confirmPassword) { |
| newErrors.confirmPassword = '两次输入的密码不一致'; |
| hasError = true; |
| } |
| |
| setErrors(newErrors); |
| return !hasError; |
| }; |
| |
| const handleSubmit = async (e) => { |
| e.preventDefault(); |
| |
| // 验证表单 |
| if (!validateForm()) { |
| return; |
| } |
| |
| setIsLoading(true); |
| |
| try { |
| // 调用后端API进行注册 |
| const registerResponse = await fetch(baseURL + '/register', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify({ |
| username: formData.username, |
| email: formData.email, |
| password: hashPassword(formData.password), // 前端加密密码 |
| verification_code: hashPassword(formData.emailCode) // 前端加密验证码 |
| }) |
| }); |
| |
| if (!registerResponse.ok) { |
| throw new Error(`HTTP ${registerResponse.status}: ${registerResponse.statusText}`); |
| } |
| |
| const registerResult = await registerResponse.json(); |
| |
| if (registerResult.success) { |
| showSuccessAlert('注册成功!欢迎加入小红书,正在跳转到登录页面...'); |
| // 清空表单数据 |
| setFormData({ |
| username: '', |
| email: '', |
| emailCode: '', |
| password: '', |
| confirmPassword: '' |
| }); |
| setErrors({ |
| username: '', |
| email: '', |
| emailCode: '', |
| password: '', |
| confirmPassword: '' |
| }); |
| // 延迟跳转到登录页面,让用户看到成功提示 |
| setTimeout(() => { |
| navigate('/login'); |
| }, 2000); |
| } else { |
| // 处理不同的注册失败情况 |
| const errorMessage = registerResult.message || '注册失败,请稍后再试'; |
| |
| // 如果是邮箱已存在的错误,将错误显示在邮箱字段下方 |
| if (errorMessage.includes('邮箱') && (errorMessage.includes('已存在') || errorMessage.includes('已注册'))) { |
| setErrors(prev => ({ |
| ...prev, |
| email: errorMessage |
| })); |
| } |
| // 如果是用户名已存在的错误,将错误显示在用户名字段下方 |
| else if (errorMessage.includes('用户名') && (errorMessage.includes('已存在') || errorMessage.includes('已被使用'))) { |
| setErrors(prev => ({ |
| ...prev, |
| username: errorMessage |
| })); |
| } |
| else { |
| // 其他错误显示在消息框中 |
| message.error(errorMessage); |
| } |
| } |
| |
| } catch (error) { |
| console.error('注册失败:', error); |
| |
| // 根据错误类型显示不同的错误信息 |
| if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) { |
| showErrorModal('网络连接失败', '无法连接到服务器,请检查您的网络连接后重试。如果问题持续存在,请联系客服。'); |
| } else if (error.message.includes('HTTP 500')) { |
| showErrorModal('服务器内部错误', '服务器出现了内部错误,请稍后重试或联系客服。技术团队已收到通知。'); |
| } else if (error.message.includes('HTTP 400')) { |
| showErrorModal('请求参数错误', '请求参数有误,请检查您输入的信息是否正确。'); |
| } else if (error.message.includes('HTTP 409')) { |
| showErrorModal('用户信息冲突', '您输入的邮箱或用户名可能已被其他用户使用,请尝试使用其他邮箱或用户名。'); |
| } else if (error.message.includes('HTTP')) { |
| showErrorModal('请求失败', `请求失败 (${error.message}),请稍后重试。如果问题持续存在,请联系客服。`); |
| } else { |
| showErrorModal('注册失败', '注册过程中发生未知错误,请稍后重试。如果问题持续存在,请联系客服。'); |
| } |
| } finally { |
| setIsLoading(false); |
| } |
| }; |
| |
| return ( |
| <div className="register-container"> |
| <div className="register-background"></div> |
| |
| {isLoading && ( |
| <div className="loading-overlay"> |
| <div className="loading-content"> |
| <div className="loading-spinner-large"></div> |
| <p className="loading-text">正在注册...</p> |
| </div> |
| </div> |
| )} |
| |
| <div className="register-content"> |
| <div className="register-card"> |
| {/* 成功提示 */} |
| {successAlert.visible && ( |
| <div style={{ marginBottom: '16px' }}> |
| <Alert |
| message={successAlert.message} |
| type="success" |
| icon={<CheckCircleOutlined />} |
| showIcon |
| closable |
| onClose={() => setSuccessAlert({ visible: false, message: '' })} |
| style={{ |
| borderRadius: '8px', |
| border: '1px solid #b7eb8f', |
| backgroundColor: '#f6ffed' |
| }} |
| /> |
| </div> |
| )} |
| |
| {/* 邮件验证码发送成功提示 */} |
| {emailCodeSuccessAlert.visible && ( |
| <div style={{ marginBottom: '16px' }}> |
| <Alert |
| message={emailCodeSuccessAlert.message} |
| type="success" |
| icon={<CheckCircleOutlined />} |
| showIcon |
| closable |
| onClose={() => setEmailCodeSuccessAlert({ visible: false, message: '' })} |
| style={{ |
| borderRadius: '8px', |
| border: '1px solid #b7eb8f', |
| backgroundColor: '#f6ffed' |
| }} |
| /> |
| </div> |
| )} |
| |
| <div className="register-header"> |
| <h1 className="register-title">加入小红书</h1> |
| <p className="register-subtitle">发现美好生活,分享精彩瞬间</p> |
| </div> |
| |
| <form className="register-form" onSubmit={handleSubmit}> |
| <div className="form-group"> |
| <Input |
| type="text" |
| id="username" |
| name="username" |
| className={`form-input ${errors.username ? 'input-error' : ''}`} |
| placeholder="请输入用户名" |
| value={formData.username} |
| onChange={handleInputChange('username')} |
| prefix={<UserOutlined />} |
| size="large" |
| title="" |
| status={errors.username ? 'error' : ''} |
| /> |
| {errors.username && ( |
| <div className="error-message"> |
| {errors.username} |
| </div> |
| )} |
| </div> |
| |
| <div className="form-group"> |
| <Input |
| type="email" |
| id="email" |
| name="email" |
| className={`form-input ${errors.email ? 'input-error' : ''}`} |
| placeholder="请输入邮箱地址" |
| value={formData.email} |
| onChange={handleInputChange('email')} |
| prefix={<MailOutlined />} |
| size="large" |
| title="" |
| status={errors.email ? 'error' : ''} |
| /> |
| {errors.email && ( |
| <div className="error-message"> |
| {errors.email} |
| </div> |
| )} |
| </div> |
| |
| <div className="form-group"> |
| <div className="email-code-wrapper"> |
| <Input |
| type="text" |
| id="emailCode" |
| name="emailCode" |
| className={`form-input email-code-input ${errors.emailCode ? 'input-error' : ''}`} |
| placeholder="请输入6位验证码" |
| value={formData.emailCode} |
| onChange={handleInputChange('emailCode')} |
| prefix={<SafetyOutlined />} |
| maxLength={6} |
| size="large" |
| title="" |
| status={errors.emailCode ? 'error' : ''} |
| /> |
| <Button |
| type="primary" |
| className="send-code-button" |
| onClick={sendEmailCode} |
| loading={sendingCode} |
| disabled={countdown > 0 || !formData.email || sendingCode} |
| size="large" |
| > |
| {countdown > 0 ? `${countdown}s后重发` : (emailCodeSent ? '重新发送' : '发送验证码')} |
| </Button> |
| </div> |
| {errors.emailCode && ( |
| <div className="error-message"> |
| {errors.emailCode} |
| </div> |
| )} |
| </div> |
| |
| <div className="form-group"> |
| <Input.Password |
| id="password" |
| name="password" |
| className={`form-input ${errors.password ? 'input-error' : ''}`} |
| placeholder="请输入密码" |
| value={formData.password} |
| onChange={handlePasswordChange} |
| prefix={<LockOutlined />} |
| size="large" |
| title="" |
| status={errors.password ? 'error' : ''} |
| /> |
| {errors.password && ( |
| <div className="error-message"> |
| {errors.password} |
| </div> |
| )} |
| </div> |
| |
| <div className="form-group"> |
| <Input.Password |
| id="confirmPassword" |
| name="confirmPassword" |
| className={`form-input ${errors.confirmPassword ? 'input-error' : ''}`} |
| placeholder="请确认密码" |
| value={formData.confirmPassword} |
| onChange={handleConfirmPasswordChange} |
| prefix={<LockOutlined />} |
| size="large" |
| title="" |
| status={errors.confirmPassword ? 'error' : ''} |
| /> |
| {errors.confirmPassword && ( |
| <div className="error-message"> |
| {errors.confirmPassword} |
| </div> |
| )} |
| </div> |
| |
| <button |
| type="submit" |
| className={`register-button ${isLoading ? 'loading' : ''}`} |
| disabled={isLoading} |
| > |
| {isLoading ? ( |
| <> |
| <div className="loading-spinner"></div> |
| 注册中... |
| </> |
| ) : ( |
| '立即注册' |
| )} |
| </button> |
| </form> |
| |
| <div className="login-link"> |
| <p>已有账户? <Link to="/login">立即登录</Link></p> |
| </div> |
| </div> |
| </div> |
| |
| {/* 错误弹窗 */} |
| <Modal |
| title={ |
| <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}> |
| <ExclamationCircleOutlined style={{ color: '#ff4d4f', fontSize: '18px' }} /> |
| {errorModal.title} |
| </div> |
| } |
| open={errorModal.visible} |
| onOk={closeErrorModal} |
| onCancel={closeErrorModal} |
| okText="我知道了" |
| cancelButtonProps={{ style: { display: 'none' } }} |
| centered |
| className="error-modal" |
| > |
| <div style={{ padding: '16px 0', fontSize: '14px', lineHeight: '1.6' }}> |
| {errorModal.content} |
| </div> |
| </Modal> |
| </div> |
| ); |
| }; |
| |
| export default RegisterPage; |