| // src/pages/LoginPage/LoginPage.jsx |
| |
| import React, { useState, useEffect } from 'react' |
| import { useNavigate, 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, |
| clearAuthInfo // ← 新增 |
| } from '../../utils/auth' |
| import { hashPassword } from '../../utils/crypto' |
| import './LoginPage.css' |
| |
| const baseURL = 'http://10.126.59.25:8082' |
| |
| export default function LoginPage() { |
| const navigate = useNavigate() |
| |
| // —— 登录页加载时先清除旧的认证信息 —— |
| useEffect(() => { |
| clearAuthInfo(/* clearRemembered= */ false) |
| }, []) |
| |
| 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, content }) |
| } |
| // 关闭错误弹窗 |
| const closeErrorModal = () => { |
| setErrorModal({ visible: false, title: '', content: '' }) |
| } |
| // 显示成功提示 |
| const showSuccessAlert = (message) => { |
| setSuccessAlert({ visible: true, message }) |
| setTimeout(() => { |
| setSuccessAlert({ visible: false, message: '' }) |
| }, 3000) |
| } |
| |
| // 初始化:检查登录 & 填充“记住我” |
| useEffect(() => { |
| if (isLoggedIn()) { |
| console.log('用户已登录') |
| // 如果想自动跳转: navigate('/home', { replace: true }) |
| } |
| const { email, password, rememberMe } = getRememberedLoginInfo() |
| if (rememberMe && email) { |
| setFormData({ email, password }) |
| setRememberMe(true) |
| } |
| }, []) |
| |
| const handleEmailChange = (e) => { |
| setFormData(f => ({ ...f, email: e.target.value })) |
| if (errors.email) setErrors(e => ({ ...e, email: '' })) |
| } |
| const handlePasswordChange = (e) => { |
| setFormData(f => ({ ...f, password: e.target.value })) |
| if (errors.password) setErrors(e => ({ ...e, password: '' })) |
| } |
| const handleRememberMeChange = (e) => { |
| const checked = e.target.checked |
| setRememberMe(checked) |
| if (!checked) { |
| saveRememberedLoginInfo('', '', false) |
| } |
| } |
| |
| const validateForm = () => { |
| const newErr = { email: '', password: '' } |
| let hasError = false |
| if (!formData.email.trim() || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { |
| newErr.email = '请输入有效的邮箱地址' |
| hasError = true |
| } |
| if (!formData.password.trim() || formData.password.length < 6) { |
| newErr.password = '密码长度至少6位' |
| hasError = true |
| } |
| setErrors(newErr) |
| return !hasError |
| } |
| |
| const handleSubmit = async (e) => { |
| e.preventDefault() |
| if (!validateForm()) return |
| |
| setIsLoading(true) |
| try { |
| const res = await fetch(baseURL + '/login', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| email: formData.email, |
| password: hashPassword(formData.password) |
| }) |
| }) |
| const result = await res.json() |
| if (result.success) { |
| showSuccessAlert('登录成功!正在跳转...') |
| saveAuthInfo(result.token, result.user, rememberMe) |
| saveRememberedLoginInfo(formData.email, formData.password, rememberMe) |
| setTimeout(() => { |
| const uid = result.user.id |
| switch (result.user.role) { |
| case 'admin': |
| navigate(`/admin/${uid}`, { replace: true }) |
| break |
| case 'superadmin': |
| navigate(`/superadmin/${uid}/users`, { replace: true }) |
| break |
| default: |
| navigate('/home', { replace: true }) |
| } |
| }, 1500) |
| } else { |
| let title = '登录失败' |
| let content = result.message || '登录失败,请检查您的邮箱和密码' |
| if (result.message) { |
| if (/邮箱|email/.test(result.message)) { |
| title = '邮箱验证失败' |
| content = '请输入正确的邮箱地址' |
| } else if (/密码|password/.test(result.message)) { |
| title = '密码验证失败' |
| content = '密码不正确,请重试' |
| } |
| } |
| showErrorModal(title, content) |
| } |
| } catch (err) { |
| console.error(err) |
| showErrorModal('网络异常', '无法连接到服务器,请稍后重试') |
| } finally { |
| setIsLoading(false) |
| } |
| } |
| |
| return ( |
| <div className="login-container"> |
| <div className="login-background" /> |
| {isLoading && ( |
| <div className="loading-overlay"> |
| <div className="loading-content"> |
| <div className="loading-spinner-large" /> |
| <p className="loading-text">正在登录...</p> |
| </div> |
| </div> |
| )} |
| <div className="login-content"> |
| <div className="login-card"> |
| {successAlert.visible && ( |
| <Alert |
| message={successAlert.message} |
| type="success" |
| icon={<CheckCircleOutlined />} |
| closable |
| style={{ marginBottom: 16, borderRadius: 8 }} |
| /> |
| )} |
| <div className="login-header"> |
| <h1>欢迎来到小红书</h1> |
| <p>标记我的生活</p> |
| </div> |
| <form className="login-form" onSubmit={handleSubmit}> |
| <div className="form-group"> |
| <Input |
| type="email" |
| placeholder="邮箱" |
| value={formData.email} |
| onChange={handleEmailChange} |
| prefix={<MailOutlined />} |
| status={errors.email ? 'error' : ''} |
| /> |
| {errors.email && <div className="error-message">{errors.email}</div>} |
| </div> |
| <div className="form-group"> |
| <Input.Password |
| placeholder="密码" |
| value={formData.password} |
| onChange={handlePasswordChange} |
| prefix={<LockOutlined />} |
| 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">忘记密码?</Link> |
| </div> |
| <button |
| type="submit" |
| className={`login-button ${isLoading ? 'loading' : ''}`} |
| disabled={isLoading} |
| > |
| {isLoading ? '登录中...' : '登录'} |
| </button> |
| </form> |
| <div className="signup-link"> |
| <p>还没有账户?<Link to="/register">立即注册</Link></p> |
| </div> |
| </div> |
| </div> |
| <Modal |
| title={<><ExclamationCircleOutlined style={{ color: '#ff4d4f' }} /> {errorModal.title}</>} |
| open={errorModal.visible} |
| onOk={closeErrorModal} |
| cancelButtonProps={{ style: { display: 'none' } }} |
| > |
| <p>{errorModal.content}</p> |
| </Modal> |
| </div> |
| ) |
| } |