blob: bdd75d066d53828212e129dbc777ae5d5047130b [file] [log] [blame]
// 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>
)
}