117 lines
3.9 KiB
TypeScript
117 lines
3.9 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { Form, Input, Button, Card, Space, Typography, Alert, Checkbox, Divider } from 'antd';
|
|
import { UserOutlined, LockOutlined, CheckSquareOutlined } from '@ant-design/icons';
|
|
import { api } from '../api/axios';
|
|
|
|
const { Title, Text, Link } = Typography;
|
|
|
|
function Login() {
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const navigate = useNavigate();
|
|
|
|
const onFinish = async (values: any) => {
|
|
setLoading(true);
|
|
setError(null);
|
|
try {
|
|
const response = await api.post('/auth/login', {
|
|
username: values.username,
|
|
password: values.password,
|
|
});
|
|
localStorage.setItem('token', response.data.token);
|
|
localStorage.setItem('username', values.username);
|
|
navigate('/');
|
|
} catch (err: any) {
|
|
const errorMessage = err.response?.data?.message || '登录失败,请检查用户名或密码。';
|
|
setError(errorMessage);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="flex items-center justify-center min-h-screen bg-gray-100" style={{ background: '#f0f2f5',height: '100vh',display: 'flex',alignItems: 'center',justifyContent: 'center' }}>
|
|
<Card
|
|
className="w-full max-w-sm"
|
|
variant="borderless"
|
|
style={{
|
|
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
|
|
padding: '40px 24px 24px',
|
|
textAlign: 'center',
|
|
width: '500px',
|
|
margin: '0 auto 200px',
|
|
}}
|
|
>
|
|
<Space direction="vertical" size="large" style={{ width: '100%' }}>
|
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: 24 }}>
|
|
<CheckSquareOutlined style={{ fontSize: '48px', color: '#1890ff' }} />
|
|
<Title level={2} style={{ margin: '8px 0 0' }}>Todo List</Title>
|
|
<Text type="secondary">轻松管理您的任务</Text>
|
|
</div>
|
|
|
|
{error && (
|
|
<Alert
|
|
message="账户或密码错误"
|
|
description={error}
|
|
type="error"
|
|
showIcon
|
|
closable
|
|
onClose={() => setError(null)}
|
|
style={{ marginBottom: 24 }}
|
|
/>
|
|
)}
|
|
|
|
<Form
|
|
name="login"
|
|
initialValues={{ remember: true }}
|
|
onFinish={onFinish}
|
|
autoComplete="off"
|
|
layout="vertical"
|
|
style={{ width: '100%' }}
|
|
>
|
|
<Form.Item
|
|
name="username"
|
|
rules={[{ required: true, message: '请输入用户名!' }]}
|
|
>
|
|
<Input
|
|
prefix={<UserOutlined className="site-form-item-icon" />}
|
|
placeholder="账户"
|
|
size="large"
|
|
/>
|
|
</Form.Item>
|
|
|
|
<Form.Item
|
|
name="password"
|
|
rules={[{ required: true, message: '请输入密码!' }]}
|
|
>
|
|
<Input.Password
|
|
prefix={<LockOutlined className="site-form-item-icon" />}
|
|
placeholder="密码"
|
|
size="large"
|
|
/>
|
|
</Form.Item>
|
|
|
|
<Form.Item>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
<Checkbox>自动登录</Checkbox>
|
|
<Space>
|
|
<Link href="/forgot-password">忘记密码</Link>
|
|
<Link href="/register">注册新账号</Link>
|
|
</Space>
|
|
</div>
|
|
</Form.Item>
|
|
|
|
<Form.Item>
|
|
<Button type="primary" htmlType="submit" className="w-full" size="large" loading={loading} style={{width: '100%',}}>
|
|
登录
|
|
</Button>
|
|
</Form.Item>
|
|
</Form>
|
|
</Space>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default Login;
|