242 lines
7.4 KiB
React
242 lines
7.4 KiB
React
|
|
import { useState, useEffect } from 'react'
|
||
|
|
import { useNavigate } from 'react-router-dom'
|
||
|
|
import { Form, Input, Button, Card, Tabs, Checkbox } from 'antd'
|
||
|
|
import { UserOutlined, LockOutlined, MailOutlined } from '@ant-design/icons'
|
||
|
|
import { login, register } from '@/api/auth'
|
||
|
|
import { getUserMenus } from '@/api/menu'
|
||
|
|
import useUserStore from '@/stores/userStore'
|
||
|
|
import Toast from '@/components/Toast/Toast'
|
||
|
|
import './Login.css'
|
||
|
|
|
||
|
|
function Login() {
|
||
|
|
const [loading, setLoading] = useState(false)
|
||
|
|
const [activeTab, setActiveTab] = useState('login')
|
||
|
|
const [rememberMe, setRememberMe] = useState(false)
|
||
|
|
const [loginForm] = Form.useForm()
|
||
|
|
const navigate = useNavigate()
|
||
|
|
const { setUser, setToken } = useUserStore()
|
||
|
|
|
||
|
|
// 组件加载时检查是否有保存的用户名
|
||
|
|
useEffect(() => {
|
||
|
|
const savedUsername = localStorage.getItem('remembered_username')
|
||
|
|
if (savedUsername) {
|
||
|
|
loginForm.setFieldsValue({ username: savedUsername })
|
||
|
|
setRememberMe(true)
|
||
|
|
}
|
||
|
|
}, [])
|
||
|
|
|
||
|
|
const handleLogin = async (values) => {
|
||
|
|
setLoading(true)
|
||
|
|
try {
|
||
|
|
const res = await login(values)
|
||
|
|
Toast.success('登录成功')
|
||
|
|
|
||
|
|
// 处理"记住用户"
|
||
|
|
if (rememberMe) {
|
||
|
|
localStorage.setItem('remembered_username', values.username)
|
||
|
|
} else {
|
||
|
|
localStorage.removeItem('remembered_username')
|
||
|
|
}
|
||
|
|
|
||
|
|
// 保存 token 和用户信息
|
||
|
|
localStorage.setItem('access_token', res.data.access_token)
|
||
|
|
localStorage.setItem('user_info', JSON.stringify(res.data.user))
|
||
|
|
setToken(res.data.access_token)
|
||
|
|
setUser(res.data.user)
|
||
|
|
|
||
|
|
// 获取用户菜单并跳转到第一个菜单
|
||
|
|
try {
|
||
|
|
const menuRes = await getUserMenus()
|
||
|
|
if (menuRes.data && menuRes.data.length > 0) {
|
||
|
|
const firstMenu = menuRes.data[0]
|
||
|
|
// 如果第一个菜单有子菜单,跳转到第一个子菜单
|
||
|
|
if (firstMenu.children && firstMenu.children.length > 0) {
|
||
|
|
navigate(firstMenu.children[0].path)
|
||
|
|
} else if (firstMenu.path) {
|
||
|
|
navigate(firstMenu.path)
|
||
|
|
} else {
|
||
|
|
// 如果都没有路径,默认跳转到项目列表
|
||
|
|
navigate('/projects')
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// 如果没有菜单,默认跳转到项目列表
|
||
|
|
navigate('/projects')
|
||
|
|
}
|
||
|
|
} catch (menuError) {
|
||
|
|
console.error('Load menus error:', menuError)
|
||
|
|
// 如果加载菜单失败,默认跳转到项目列表
|
||
|
|
navigate('/projects')
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Login error:', error)
|
||
|
|
const errorMsg = error.response?.data?.detail || error.message || '登录失败,请检查用户名和密码'
|
||
|
|
Toast.error('登录失败', errorMsg)
|
||
|
|
} finally {
|
||
|
|
setLoading(false)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const handleRegister = async (values) => {
|
||
|
|
setLoading(true)
|
||
|
|
try {
|
||
|
|
await register(values)
|
||
|
|
Toast.success('注册成功', '请使用您的账号登录')
|
||
|
|
setActiveTab('login')
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Register error:', error)
|
||
|
|
const errorMsg = error.response?.data?.detail || error.message || '注册失败'
|
||
|
|
Toast.error('注册失败', errorMsg)
|
||
|
|
} finally {
|
||
|
|
setLoading(false)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="login-container">
|
||
|
|
<Card className="login-card">
|
||
|
|
<div className="login-header">
|
||
|
|
<h1>NEX Docus</h1>
|
||
|
|
<p>团队协作文档管理平台</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Tabs activeKey={activeTab} onChange={setActiveTab} centered>
|
||
|
|
<Tabs.TabPane tab="登录" key="login">
|
||
|
|
<Form
|
||
|
|
form={loginForm}
|
||
|
|
name="login"
|
||
|
|
onFinish={handleLogin}
|
||
|
|
autoComplete="off"
|
||
|
|
size="large"
|
||
|
|
>
|
||
|
|
<Form.Item
|
||
|
|
name="username"
|
||
|
|
rules={[{ required: true, message: '请输入用户名' }]}
|
||
|
|
>
|
||
|
|
<Input
|
||
|
|
prefix={<UserOutlined />}
|
||
|
|
placeholder="用户名"
|
||
|
|
/>
|
||
|
|
</Form.Item>
|
||
|
|
|
||
|
|
<Form.Item
|
||
|
|
name="password"
|
||
|
|
rules={[{ required: true, message: '请输入密码' }]}
|
||
|
|
>
|
||
|
|
<Input.Password
|
||
|
|
prefix={<LockOutlined />}
|
||
|
|
placeholder="密码"
|
||
|
|
/>
|
||
|
|
</Form.Item>
|
||
|
|
|
||
|
|
<Form.Item>
|
||
|
|
<Checkbox
|
||
|
|
checked={rememberMe}
|
||
|
|
onChange={(e) => setRememberMe(e.target.checked)}
|
||
|
|
>
|
||
|
|
记住用户
|
||
|
|
</Checkbox>
|
||
|
|
</Form.Item>
|
||
|
|
|
||
|
|
<Form.Item>
|
||
|
|
<Button
|
||
|
|
type="primary"
|
||
|
|
htmlType="submit"
|
||
|
|
loading={loading}
|
||
|
|
block
|
||
|
|
>
|
||
|
|
登录
|
||
|
|
</Button>
|
||
|
|
</Form.Item>
|
||
|
|
</Form>
|
||
|
|
</Tabs.TabPane>
|
||
|
|
|
||
|
|
<Tabs.TabPane tab="注册" key="register">
|
||
|
|
<Form
|
||
|
|
name="register"
|
||
|
|
onFinish={handleRegister}
|
||
|
|
autoComplete="off"
|
||
|
|
size="large"
|
||
|
|
>
|
||
|
|
<Form.Item
|
||
|
|
name="username"
|
||
|
|
rules={[
|
||
|
|
{ required: true, message: '请输入用户名' },
|
||
|
|
{ min: 3, message: '用户名至少3个字符' },
|
||
|
|
]}
|
||
|
|
>
|
||
|
|
<Input
|
||
|
|
prefix={<UserOutlined />}
|
||
|
|
placeholder="用户名"
|
||
|
|
/>
|
||
|
|
</Form.Item>
|
||
|
|
|
||
|
|
<Form.Item
|
||
|
|
name="email"
|
||
|
|
rules={[
|
||
|
|
{ type: 'email', message: '请输入有效的邮箱地址' },
|
||
|
|
]}
|
||
|
|
>
|
||
|
|
<Input
|
||
|
|
prefix={<MailOutlined />}
|
||
|
|
placeholder="邮箱(选填)"
|
||
|
|
/>
|
||
|
|
</Form.Item>
|
||
|
|
|
||
|
|
<Form.Item
|
||
|
|
name="password"
|
||
|
|
rules={[
|
||
|
|
{ required: true, message: '请输入密码' },
|
||
|
|
{ min: 6, message: '密码至少6个字符' },
|
||
|
|
]}
|
||
|
|
>
|
||
|
|
<Input.Password
|
||
|
|
prefix={<LockOutlined />}
|
||
|
|
placeholder="密码"
|
||
|
|
/>
|
||
|
|
</Form.Item>
|
||
|
|
|
||
|
|
<Form.Item
|
||
|
|
name="confirm"
|
||
|
|
dependencies={['password']}
|
||
|
|
rules={[
|
||
|
|
{ required: true, message: '请确认密码' },
|
||
|
|
({ getFieldValue }) => ({
|
||
|
|
validator(_, value) {
|
||
|
|
if (!value || getFieldValue('password') === value) {
|
||
|
|
return Promise.resolve()
|
||
|
|
}
|
||
|
|
return Promise.reject(new Error('两次输入的密码不一致'))
|
||
|
|
},
|
||
|
|
}),
|
||
|
|
]}
|
||
|
|
>
|
||
|
|
<Input.Password
|
||
|
|
prefix={<LockOutlined />}
|
||
|
|
placeholder="确认密码"
|
||
|
|
/>
|
||
|
|
</Form.Item>
|
||
|
|
|
||
|
|
<Form.Item>
|
||
|
|
<Button
|
||
|
|
type="primary"
|
||
|
|
htmlType="submit"
|
||
|
|
loading={loading}
|
||
|
|
block
|
||
|
|
>
|
||
|
|
注册
|
||
|
|
</Button>
|
||
|
|
</Form.Item>
|
||
|
|
</Form>
|
||
|
|
</Tabs.TabPane>
|
||
|
|
</Tabs>
|
||
|
|
</Card>
|
||
|
|
|
||
|
|
<div className="login-footer">
|
||
|
|
<p>重置密码请联系管理员</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
export default Login
|