| 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; |