277 lines
9.9 KiB
JavaScript
277 lines
9.9 KiB
JavaScript
import { useState, useEffect } from 'react'
|
||
import { useNavigate } from 'react-router-dom'
|
||
import { Form, Input, Button, Tabs } from 'antd'
|
||
import { UserOutlined, LockOutlined, MailOutlined, ArrowRightOutlined } 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 [loginForm] = Form.useForm()
|
||
const navigate = useNavigate()
|
||
const { setUser, setToken } = useUserStore()
|
||
|
||
const handleLogin = async (values) => {
|
||
setLoading(true)
|
||
try {
|
||
const res = await login(values)
|
||
Toast.success('登录成功')
|
||
|
||
// 保存 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-page">
|
||
{/* 左侧介绍区域 */}
|
||
<div className="login-left">
|
||
<div className="login-left-content">
|
||
<div className="logo-section">
|
||
<div className="logo-icon">
|
||
<img src="/favicon.svg" alt="NexDocus" width="42" height="42" />
|
||
</div>
|
||
<span className="logo-text">NexDocus</span>
|
||
</div>
|
||
|
||
<div className="intro-section">
|
||
<h1 className="intro-title">
|
||
团队协作
|
||
<br />
|
||
<span className="highlight">文档管理平台</span>
|
||
</h1>
|
||
<p className="intro-desc">
|
||
全流程文档共享,提升团队协作效率的新一代解决方案。
|
||
</p>
|
||
</div>
|
||
|
||
<div className="footer-info">
|
||
<div className="footer-links">
|
||
<span>团队知识共享</span>
|
||
<span>大模型能力加持</span>
|
||
</div>
|
||
<p className="copyright">© 2024 NexDocus. All rights reserved.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 右侧登录区域 */}
|
||
<div className="login-right">
|
||
<div className="login-form-container">
|
||
<Tabs
|
||
activeKey={activeTab}
|
||
onChange={setActiveTab}
|
||
items={[
|
||
{
|
||
key: 'login',
|
||
label: '账号登录',
|
||
children: (
|
||
<div className="form-wrapper">
|
||
<p className="form-subtitle">欢迎回来,请输入您的登录凭证。</p>
|
||
|
||
<Form
|
||
form={loginForm}
|
||
name="login"
|
||
onFinish={handleLogin}
|
||
autoComplete="off"
|
||
layout="vertical"
|
||
>
|
||
<Form.Item
|
||
label="用户名"
|
||
name="username"
|
||
rules={[{ required: true, message: '请输入用户名' }]}
|
||
>
|
||
<Input
|
||
prefix={<UserOutlined style={{ color: '#bfbfbf' }} />}
|
||
placeholder="请输入用户名"
|
||
size="large"
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item
|
||
label="密码"
|
||
name="password"
|
||
rules={[{ required: true, message: '请输入密码' }]}
|
||
>
|
||
<Input.Password
|
||
prefix={<LockOutlined style={{ color: '#bfbfbf' }} />}
|
||
placeholder="请输入密码"
|
||
size="large"
|
||
visibilityToggle
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item style={{ marginBottom: '12px' }}>
|
||
<Button
|
||
type="primary"
|
||
htmlType="submit"
|
||
loading={loading}
|
||
block
|
||
size="large"
|
||
icon={<ArrowRightOutlined />}
|
||
iconPosition="end"
|
||
>
|
||
立即登录
|
||
</Button>
|
||
</Form.Item>
|
||
</Form>
|
||
</div>
|
||
)
|
||
},
|
||
{
|
||
key: 'register',
|
||
label: '注册账号',
|
||
children: (
|
||
<div className="form-wrapper">
|
||
<p className="form-subtitle">创建您的账号,开始使用平台。</p>
|
||
|
||
<Form
|
||
name="register"
|
||
onFinish={handleRegister}
|
||
autoComplete="off"
|
||
layout="vertical"
|
||
>
|
||
<Form.Item
|
||
label="用户名"
|
||
name="username"
|
||
rules={[
|
||
{ required: true, message: '请输入用户名' },
|
||
{ min: 3, message: '用户名至少3个字符' },
|
||
]}
|
||
>
|
||
<Input
|
||
prefix={<UserOutlined style={{ color: '#bfbfbf' }} />}
|
||
placeholder="用户名"
|
||
size="large"
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item
|
||
label="邮箱"
|
||
name="email"
|
||
rules={[
|
||
{ type: 'email', message: '请输入有效的邮箱地址' },
|
||
]}
|
||
>
|
||
<Input
|
||
prefix={<MailOutlined style={{ color: '#bfbfbf' }} />}
|
||
placeholder="邮箱(选填)"
|
||
size="large"
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item
|
||
label="密码"
|
||
name="password"
|
||
rules={[
|
||
{ required: true, message: '请输入密码' },
|
||
{ min: 6, message: '密码至少6个字符' },
|
||
]}
|
||
>
|
||
<Input.Password
|
||
prefix={<LockOutlined style={{ color: '#bfbfbf' }} />}
|
||
placeholder="密码"
|
||
size="large"
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item
|
||
label="确认密码"
|
||
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 style={{ color: '#bfbfbf' }} />}
|
||
placeholder="确认密码"
|
||
size="large"
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item>
|
||
<Button
|
||
type="primary"
|
||
htmlType="submit"
|
||
loading={loading}
|
||
block
|
||
size="large"
|
||
>
|
||
注册
|
||
</Button>
|
||
</Form.Item>
|
||
</Form>
|
||
</div>
|
||
)
|
||
}
|
||
]}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default Login
|