|
@@ -1,91 +1,131 @@
|
|
|
import React, { useState } from 'react';
|
|
import React, { useState } from 'react';
|
|
|
-import { Form, Input, Button, Card, Typography, message, Divider } from 'antd';
|
|
|
|
|
-import { UserOutlined, LockOutlined } from '@ant-design/icons';
|
|
|
|
|
-import { Link, useNavigate } from 'react-router-dom';
|
|
|
|
|
-import { authClient } from '@/client/api';
|
|
|
|
|
|
|
+import { useForm } from 'react-hook-form';
|
|
|
|
|
+import { EyeIcon, EyeSlashIcon, UserIcon, LockClosedIcon } from '@heroicons/react/24/outline';
|
|
|
|
|
+import { useNavigate } from 'react-router-dom';
|
|
|
import { useAuth } from '@/client/home/hooks/AuthProvider';
|
|
import { useAuth } from '@/client/home/hooks/AuthProvider';
|
|
|
|
|
|
|
|
-const { Title } = Typography;
|
|
|
|
|
-
|
|
|
|
|
const LoginPage: React.FC = () => {
|
|
const LoginPage: React.FC = () => {
|
|
|
|
|
+ const { register, handleSubmit, formState: { errors } } = useForm();
|
|
|
|
|
+ const [showPassword, setShowPassword] = useState(false);
|
|
|
const [loading, setLoading] = useState(false);
|
|
const [loading, setLoading] = useState(false);
|
|
|
- const [form] = Form.useForm();
|
|
|
|
|
const { login } = useAuth();
|
|
const { login } = useAuth();
|
|
|
const navigate = useNavigate();
|
|
const navigate = useNavigate();
|
|
|
|
|
|
|
|
- const handleSubmit = async (values: any) => {
|
|
|
|
|
|
|
+ const onSubmit = async (data: any) => {
|
|
|
try {
|
|
try {
|
|
|
setLoading(true);
|
|
setLoading(true);
|
|
|
- await login(values.username, values.password);
|
|
|
|
|
-
|
|
|
|
|
- message.success('登录成功');
|
|
|
|
|
|
|
+ await login(data.username, data.password);
|
|
|
navigate('/');
|
|
navigate('/');
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('Login error:', error);
|
|
console.error('Login error:', error);
|
|
|
- message.error((error as Error).message || '登录失败,请检查用户名和密码');
|
|
|
|
|
|
|
+ alert((error as Error).message || '登录失败,请检查用户名和密码');
|
|
|
} finally {
|
|
} finally {
|
|
|
setLoading(false);
|
|
setLoading(false);
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
- <div className="flex justify-center items-center min-h-screen bg-gray-50">
|
|
|
|
|
- <Card className="w-full max-w-md shadow-lg">
|
|
|
|
|
- <div className="text-center mb-6">
|
|
|
|
|
- <Title level={2}>社交媒体平台</Title>
|
|
|
|
|
- <p className="text-gray-500">登录您的账号</p>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <Form
|
|
|
|
|
- form={form}
|
|
|
|
|
- name="login_form"
|
|
|
|
|
- layout="vertical"
|
|
|
|
|
- onFinish={handleSubmit}
|
|
|
|
|
- >
|
|
|
|
|
- <Form.Item
|
|
|
|
|
- name="username"
|
|
|
|
|
- label="用户名"
|
|
|
|
|
- rules={[{ required: true, message: '请输入用户名' }]}
|
|
|
|
|
- >
|
|
|
|
|
- <Input
|
|
|
|
|
- prefix={<UserOutlined className="text-primary" />}
|
|
|
|
|
- placeholder="请输入用户名"
|
|
|
|
|
- />
|
|
|
|
|
- </Form.Item>
|
|
|
|
|
|
|
+ <div className="flex justify-center items-center min-h-screen bg-gray-100">
|
|
|
|
|
+ <div className="w-full max-w-md bg-white rounded-lg shadow-md overflow-hidden">
|
|
|
|
|
+ <div className="p-6 sm:p-8">
|
|
|
|
|
+ <div className="text-center mb-8">
|
|
|
|
|
+ <h2 className="text-2xl font-bold text-gray-900">网站登录</h2>
|
|
|
|
|
+ <p className="mt-2 text-sm text-gray-600">登录您的账号以继续</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
- <Form.Item
|
|
|
|
|
- name="password"
|
|
|
|
|
- label="密码"
|
|
|
|
|
- rules={[{ required: true, message: '请输入密码' }]}
|
|
|
|
|
- >
|
|
|
|
|
- <Input.Password
|
|
|
|
|
- prefix={<LockOutlined className="text-primary" />}
|
|
|
|
|
- placeholder="请输入密码"
|
|
|
|
|
- />
|
|
|
|
|
- </Form.Item>
|
|
|
|
|
|
|
+ <form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <label htmlFor="username" className="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
+ 用户名
|
|
|
|
|
+ </label>
|
|
|
|
|
+ <div className="relative">
|
|
|
|
|
+ <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
|
|
|
+ <UserIcon className="h-5 w-5 text-gray-400" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <input
|
|
|
|
|
+ id="username"
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ className={`w-full pl-10 pr-3 py-2 border ${errors.username ? 'border-red-300' : 'border-gray-300'} rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent`}
|
|
|
|
|
+ placeholder="请输入用户名"
|
|
|
|
|
+ {...register('username', {
|
|
|
|
|
+ required: '用户名不能为空',
|
|
|
|
|
+ minLength: { value: 3, message: '用户名至少3个字符' }
|
|
|
|
|
+ })}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {errors.username && (
|
|
|
|
|
+ <p className="mt-1 text-sm text-red-600">{errors.username.message?.toString()}</p>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
+ 密码
|
|
|
|
|
+ </label>
|
|
|
|
|
+ <div className="relative">
|
|
|
|
|
+ <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
|
|
|
+ <LockClosedIcon className="h-5 w-5 text-gray-400" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <input
|
|
|
|
|
+ id="password"
|
|
|
|
|
+ type={showPassword ? 'text' : 'password'}
|
|
|
|
|
+ className={`w-full pl-10 pr-10 py-2 border ${errors.password ? 'border-red-300' : 'border-gray-300'} rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent`}
|
|
|
|
|
+ placeholder="请输入密码"
|
|
|
|
|
+ {...register('password', {
|
|
|
|
|
+ required: '密码不能为空',
|
|
|
|
|
+ minLength: { value: 6, message: '密码至少6个字符' }
|
|
|
|
|
+ })}
|
|
|
|
|
+ />
|
|
|
|
|
+ <button
|
|
|
|
|
+ type="button"
|
|
|
|
|
+ className="absolute inset-y-0 right-0 pr-3 flex items-center"
|
|
|
|
|
+ onClick={() => setShowPassword(!showPassword)}
|
|
|
|
|
+ >
|
|
|
|
|
+ {showPassword ? (
|
|
|
|
|
+ <EyeSlashIcon className="h-5 w-5 text-gray-400" />
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <EyeIcon className="h-5 w-5 text-gray-400" />
|
|
|
|
|
+ )}
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {errors.password && (
|
|
|
|
|
+ <p className="mt-1 text-sm text-red-600">{errors.password.message?.toString()}</p>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <button
|
|
|
|
|
+ type="submit"
|
|
|
|
|
+ disabled={loading}
|
|
|
|
|
+ className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50"
|
|
|
|
|
+ >
|
|
|
|
|
+ {loading ? '登录中...' : '登录'}
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </form>
|
|
|
|
|
|
|
|
- <Form.Item>
|
|
|
|
|
- <Button
|
|
|
|
|
- type="primary"
|
|
|
|
|
- htmlType="submit"
|
|
|
|
|
- className="w-full h-10 text-base"
|
|
|
|
|
- loading={loading}
|
|
|
|
|
- >
|
|
|
|
|
- 登录
|
|
|
|
|
- </Button>
|
|
|
|
|
- </Form.Item>
|
|
|
|
|
- </Form>
|
|
|
|
|
-
|
|
|
|
|
- <Divider>还没有账号?</Divider>
|
|
|
|
|
-
|
|
|
|
|
- <Button
|
|
|
|
|
- type="default"
|
|
|
|
|
- className="w-full"
|
|
|
|
|
- onClick={() => navigate('/register')}
|
|
|
|
|
- >
|
|
|
|
|
- 注册账号
|
|
|
|
|
- </Button>
|
|
|
|
|
- </Card>
|
|
|
|
|
|
|
+ <div className="mt-6">
|
|
|
|
|
+ <div className="relative">
|
|
|
|
|
+ <div className="absolute inset-0 flex items-center">
|
|
|
|
|
+ <div className="w-full border-t border-gray-300"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="relative flex justify-center text-sm">
|
|
|
|
|
+ <span className="px-2 bg-white text-gray-500">还没有账号?</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div className="mt-4">
|
|
|
|
|
+ <button
|
|
|
|
|
+ type="button"
|
|
|
|
|
+ onClick={() => navigate('/register')}
|
|
|
|
|
+ className="w-full flex justify-center py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
|
|
|
+ >
|
|
|
|
|
+ 注册账号
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
);
|
|
);
|
|
|
};
|
|
};
|