LoginPage.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import React from 'react';
  2. import { useForm } from 'react-hook-form';
  3. import { zodResolver } from '@hookform/resolvers/zod';
  4. import { z } from 'zod';
  5. import { Link, useNavigate } from 'react-router-dom';
  6. import { useAuth } from '@/client/home/hooks/AuthProvider';
  7. import { toast } from 'sonner';
  8. import { Button } from '@/client/components/ui/button';
  9. import { Input } from '@/client/components/ui/input';
  10. import { Label } from '@/client/components/ui/label';
  11. import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/client/components/ui/card';
  12. import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/client/components/ui/form';
  13. import { Eye, EyeOff, User, Lock } from 'lucide-react';
  14. const loginSchema = z.object({
  15. username: z.string().min(3, '用户名至少3个字符'),
  16. password: z.string().min(6, '密码至少6个字符'),
  17. });
  18. type LoginFormData = z.infer<typeof loginSchema>;
  19. const LoginPage: React.FC = () => {
  20. const { login } = useAuth();
  21. const navigate = useNavigate();
  22. const form = useForm<LoginFormData>({
  23. resolver: zodResolver(loginSchema),
  24. defaultValues: {
  25. username: '',
  26. password: '',
  27. },
  28. });
  29. const onSubmit = async (data: LoginFormData) => {
  30. try {
  31. await login(data.username, data.password);
  32. toast.success('登录成功!');
  33. navigate('/');
  34. } catch (error) {
  35. toast.error(error instanceof Error ? error.message : '登录失败,请检查用户名和密码');
  36. }
  37. };
  38. return (
  39. <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-50 to-slate-100 px-4 py-12">
  40. <Card className="w-full max-w-md border-0 shadow-xl">
  41. <CardHeader className="space-y-1">
  42. <CardTitle className="text-2xl font-bold text-center">欢迎回来</CardTitle>
  43. <CardDescription className="text-center">
  44. 登录您的账号以继续
  45. </CardDescription>
  46. </CardHeader>
  47. <CardContent>
  48. <Form {...form}>
  49. <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
  50. <FormField
  51. control={form.control}
  52. name="username"
  53. render={({ field }) => (
  54. <FormItem>
  55. <FormLabel>用户名</FormLabel>
  56. <FormControl>
  57. <div className="relative">
  58. <User className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
  59. <Input
  60. placeholder="请输入用户名"
  61. className="pl-10"
  62. {...field}
  63. />
  64. </div>
  65. </FormControl>
  66. <FormMessage />
  67. </FormItem>
  68. )}
  69. />
  70. <FormField
  71. control={form.control}
  72. name="password"
  73. render={({ field }) => (
  74. <FormItem>
  75. <FormLabel>密码</FormLabel>
  76. <FormControl>
  77. <div className="relative">
  78. <Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
  79. <Input
  80. type="password"
  81. placeholder="请输入密码"
  82. className="pl-10"
  83. {...field}
  84. />
  85. </div>
  86. </FormControl>
  87. <FormMessage />
  88. </FormItem>
  89. )}
  90. />
  91. <Button
  92. type="submit"
  93. className="w-full"
  94. disabled={form.formState.isSubmitting}
  95. >
  96. {form.formState.isSubmitting ? '登录中...' : '登录'}
  97. </Button>
  98. </form>
  99. </Form>
  100. </CardContent>
  101. <CardFooter className="flex flex-col space-y-4">
  102. <div className="text-sm text-center">
  103. <span className="text-muted-foreground">还没有账号?</span>
  104. <Button
  105. variant="link"
  106. className="px-1"
  107. asChild
  108. >
  109. <Link to="/register">立即注册</Link>
  110. </Button>
  111. </div>
  112. <div className="text-xs text-center text-muted-foreground">
  113. <p>测试账号:admin / admin123</p>
  114. </div>
  115. </CardFooter>
  116. </Card>
  117. </div>
  118. );
  119. };
  120. export default LoginPage;