import { useState } from 'react';
|
import {
|
CheckCircleFilled,
|
LockOutlined,
|
SafetyCertificateOutlined,
|
UserOutlined,
|
} from '@ant-design/icons';
|
import { App as AntdApp, Button, Form, Input, Tag } from 'antd';
|
import { Navigate, useLocation, useNavigate } from 'react-router-dom';
|
import { useAuthStore } from '@/store/auth';
|
import type { LoginParams } from '@/types/auth';
|
import './Login.scss';
|
|
export default function Login() {
|
const [form] = Form.useForm<LoginParams>();
|
const [loading, setLoading] = useState(false);
|
|
const token = useAuthStore((state) => state.token);
|
const login = useAuthStore((state) => state.login);
|
const navigate = useNavigate();
|
const location = useLocation();
|
const { message } = AntdApp.useApp();
|
|
if (token) {
|
return <Navigate to="/dashboard" replace />;
|
}
|
|
const from = (location.state as { from?: string } | null)?.from;
|
const redirectTo = from && from.startsWith('/') && from !== '/login' ? from : '/dashboard';
|
|
const handleFinish = async (values: LoginParams) => {
|
setLoading(true);
|
try {
|
await login(values);
|
message.success('登录成功,欢迎回来');
|
navigate(redirectTo, { replace: true });
|
} catch (error) {
|
message.error(error instanceof Error ? error.message : '登录失败,请稍后重试');
|
} finally {
|
setLoading(false);
|
}
|
};
|
|
const fillAccount = (username: string, password: string) => {
|
form.setFieldsValue({ username, password });
|
};
|
|
return (
|
<div className="login-page">
|
<div className="login-shell">
|
<section className="login-brand">
|
<div className="login-brand__logo">
|
<span>C</span>
|
CRM Admin
|
</div>
|
|
<div className="login-brand__content">
|
<h1>
|
让团队协作
|
<br />
|
更简单、更高效
|
</h1>
|
<p>一套开箱即用的中后台管理框架,内置多级菜单、路由守卫与按钮级权限。</p>
|
<ul>
|
<li>
|
<CheckCircleFilled /> 2-3 级嵌套路由与动态菜单
|
</li>
|
<li>
|
<CheckCircleFilled /> 登录态校验与页面访问控制
|
</li>
|
<li>
|
<CheckCircleFilled /> 按钮级权限码精细控制
|
</li>
|
</ul>
|
</div>
|
|
<div className="login-brand__footer">Vite · React · TypeScript · Ant Design</div>
|
</section>
|
|
<section className="login-panel">
|
<div className="login-panel__inner">
|
<div className="login-panel__header">
|
<Tag color="purple" bordered={false}>
|
CRM 企业管理平台
|
</Tag>
|
<h2>欢迎回来 👋</h2>
|
<p>请登录你的账号以继续访问后台</p>
|
</div>
|
|
<Form
|
form={form}
|
layout="vertical"
|
size="large"
|
requiredMark={false}
|
initialValues={{ username: 'admin', password: 'admin123' }}
|
onFinish={handleFinish}
|
>
|
<Form.Item
|
name="username"
|
label="用户名"
|
rules={[{ required: true, message: '请输入用户名' }]}
|
>
|
<Input
|
prefix={<UserOutlined />}
|
placeholder="请输入用户名"
|
autoComplete="username"
|
/>
|
</Form.Item>
|
|
<Form.Item
|
name="password"
|
label="密码"
|
rules={[{ required: true, message: '请输入密码' }]}
|
>
|
<Input.Password
|
prefix={<LockOutlined />}
|
placeholder="请输入密码"
|
autoComplete="current-password"
|
/>
|
</Form.Item>
|
|
<div className="login-panel__helper">
|
<span>登录即表示同意平台使用规范</span>
|
<a onClick={() => message.info('请联系系统管理员重置密码')}>忘记密码?</a>
|
</div>
|
|
<Button type="primary" htmlType="submit" block loading={loading}>
|
登 录
|
</Button>
|
</Form>
|
|
<div className="login-demo">
|
<div className="login-demo__title">
|
<SafetyCertificateOutlined /> 演示账号
|
</div>
|
<div className="login-demo__accounts">
|
<button type="button" onClick={() => fillAccount('admin', 'admin123')}>
|
<span>管理员</span>
|
<code>admin / admin123</code>
|
</button>
|
<button type="button" onClick={() => fillAccount('user', 'user123')}>
|
<span>普通用户</span>
|
<code>user / user123</code>
|
</button>
|
</div>
|
</div>
|
</div>
|
</section>
|
</div>
|
</div>
|
);
|
}
|