合并login
Change-Id: Ie06ed019cbb00d52e0b9e1f3c7a56c947b57a42c
diff --git a/Merge/front/src/pages/RegisterPage/RegisterPage.js b/Merge/front/src/pages/RegisterPage/RegisterPage.js
new file mode 100644
index 0000000..836e1cf
--- /dev/null
+++ b/Merge/front/src/pages/RegisterPage/RegisterPage.js
@@ -0,0 +1,625 @@
+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;