blob: c315b7dc46e17c8061033bf0a6fb82f4b6adaa72 [file] [log] [blame]
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { Input, Checkbox, Modal, Alert } from 'antd';
import { MailOutlined, LockOutlined, ExclamationCircleOutlined, CheckCircleOutlined } from '@ant-design/icons';
import {
getRememberedLoginInfo,
saveRememberedLoginInfo,
saveAuthInfo,
isLoggedIn
} from '../../utils/auth';
import { hashPassword } from '../../utils/crypto';
import './LoginPage.css';
const baseURL = 'http://10.126.59.25:8082';
const LoginPage = () => {
const [formData, setFormData] = useState({
email: '',
password: ''
});
const [rememberMe, setRememberMe] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [errors, setErrors] = useState({
email: '',
password: ''
});
const [errorModal, setErrorModal] = useState({
visible: false,
title: '',
content: ''
});
const [successAlert, setSuccessAlert] = useState({
visible: false,
message: ''
});
// 显示错误弹窗
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);
};
// 页面加载时检查是否有记住的登录信息
useEffect(() => {
// 检查是否已经登录
if (isLoggedIn()) {
// 如果已经有token,可以选择直接跳转到主页面
// window.location.href = '/test-dashboard';
console.log('用户已登录');
}
// 获取记住的登录信息
const rememberedInfo = getRememberedLoginInfo();
if (rememberedInfo.rememberMe && rememberedInfo.email) {
setFormData({
email: rememberedInfo.email,
password: rememberedInfo.password
});
setRememberMe(true);
}
}, []);
const handleEmailChange = (e) => {
const value = e.target.value;
setFormData(prev => ({
...prev,
email: value
}));
// 清除邮箱错误提示
if (errors.email) {
setErrors(prev => ({
...prev,
email: ''
}));
}
};
const handlePasswordChange = (e) => {
const value = e.target.value;
setFormData(prev => ({
...prev,
password: value
}));
// 清除密码错误提示
if (errors.password) {
setErrors(prev => ({
...prev,
password: ''
}));
}
};
const handleRememberMeChange = (e) => {
const checked = e.target.checked;
setRememberMe(checked);
// 如果取消记住我,清除已保存的登录信息
if (!checked) {
saveRememberedLoginInfo('', '', false);
}
};
const validateForm = () => {
const newErrors = {
email: '',
password: ''
};
let hasError = false;
// 验证邮箱
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.password || typeof formData.password !== 'string' || !formData.password.trim()) {
newErrors.password = '请输入密码';
hasError = true;
} else if (formData.password.length < 6) {
newErrors.password = '密码长度至少6位';
hasError = true;
}
setErrors(newErrors);
return !hasError;
};
const handleSubmit = async (e) => {
e.preventDefault();
// 验证表单
if (!validateForm()) {
return;
}
setIsLoading(true);
try {
// 发送登录请求到后端
const response = await fetch(baseURL + '/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: formData.email, // 后端支持邮箱登录
password: hashPassword(formData.password) // 前端加密密码
})
});
const result = await response.json();
if (result.success) {
// 显示成功提示
showSuccessAlert('登录成功!正在跳转...');
// 保存认证信息
saveAuthInfo(result.token, result.user, rememberMe);
// 保存或清除记住的登录信息
saveRememberedLoginInfo(formData.email, formData.password, rememberMe);
// 延迟跳转,让用户看到成功提示
setTimeout(() => {
window.location.href = '/test-dashboard';
}, 1500);
} else {
// 登录失败,显示错误信息
let errorTitle = '登录失败';
let errorContent = result.message || '登录失败,请检查您的邮箱和密码';
// 根据错误类型提供更详细的信息
if (result.message) {
if (result.message.includes('邮箱') || result.message.includes('email')) {
errorTitle = '邮箱验证失败';
errorContent = '您输入的邮箱地址不存在或格式不正确,请检查后重试。';
} else if (result.message.includes('密码') || result.message.includes('password')) {
errorTitle = '密码验证失败';
errorContent = '您输入的密码不正确,请检查后重试。如果忘记密码,请点击"忘记密码"进行重置。';
} else if (result.message.includes('用户不存在')) {
errorTitle = '用户不存在';
errorContent = '该邮箱尚未注册,请先注册账户或检查邮箱地址是否正确。';
} else if (result.message.includes('账户被锁定') || result.message.includes('locked')) {
errorTitle = '账户被锁定';
errorContent = '您的账户因安全原因被暂时锁定,请联系客服或稍后重试。';
}
}
showErrorModal(errorTitle, errorContent);
}
} catch (error) {
console.error('登录请求失败:', error);
// 根据错误类型显示不同的错误信息
if (error.name === 'TypeError' && error.message.includes('fetch')) {
showErrorModal('网络连接失败', '无法连接到服务器,请检查您的网络连接后重试。如果问题持续存在,请联系客服。');
} else if (error.name === 'AbortError') {
showErrorModal('请求超时', '请求超时,请检查网络连接后重试。');
} else {
showErrorModal('登录失败', '网络连接失败,请检查网络或稍后重试。如果问题持续存在,请联系客服。');
}
} finally {
setIsLoading(false);
}
};
return (
<div className="login-container">
<div className="login-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="login-content">
<div className="login-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>
)}
<div className="login-header">
<h1 className="login-title">欢迎来到小红书</h1>
<p className="login-subtitle">标记我的生活</p>
</div>
<form className="login-form" onSubmit={handleSubmit}>
<div className="form-group">
<Input
type="email"
id="email"
name="email"
className={`form-input ${errors.email ? 'input-error' : ''}`}
placeholder="请输入您的邮箱"
value={formData.email}
onChange={handleEmailChange}
prefix={<MailOutlined />}
size="large"
title=""
status={errors.email ? 'error' : ''}
/>
{errors.email && (
<div className="error-message">
{errors.email}
</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-options">
<Checkbox
checked={rememberMe}
onChange={handleRememberMeChange}
>
记住我
</Checkbox>
<Link to="/forgot-password" className="forgot-password">忘记密码?</Link>
</div>
<button
type="submit"
className={`login-button ${isLoading ? 'loading' : ''}`}
disabled={isLoading}
>
{isLoading ? (
<>
<div className="loading-spinner"></div>
登录中...
</>
) : (
'登录'
)}
</button>
</form>
<div className="signup-link">
<p>还没有账户? <Link to="/register">立即注册</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 LoginPage;