blob: 836e1cfef367fa0118f7fb5293df23cd2ee2efa3 [file] [log] [blame]
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;