LoginPage.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import React, { useState } from 'react';
  2. import { useForm } from 'react-hook-form';
  3. import { EyeIcon, EyeSlashIcon, UserIcon, LockClosedIcon } from '@heroicons/react/24/outline';
  4. import { useNavigate } from 'react-router-dom';
  5. import { useAuth } from '@/client/home/hooks/AuthProvider';
  6. const LoginPage: React.FC = () => {
  7. const { register, handleSubmit, formState: { errors } } = useForm();
  8. const [showPassword, setShowPassword] = useState(false);
  9. const [loading, setLoading] = useState(false);
  10. const { login } = useAuth();
  11. const navigate = useNavigate();
  12. const onSubmit = async (data: any) => {
  13. try {
  14. setLoading(true);
  15. await login(data.username, data.password);
  16. navigate('/');
  17. } catch (error) {
  18. console.error('Login error:', error);
  19. alert((error as Error).message || '登录失败,请检查用户名和密码');
  20. } finally {
  21. setLoading(false);
  22. }
  23. };
  24. return (
  25. <div className="flex justify-center items-center min-h-screen bg-gray-100">
  26. <div className="w-full max-w-md bg-white rounded-lg shadow-md overflow-hidden">
  27. <div className="p-6 sm:p-8">
  28. <div className="text-center mb-8">
  29. <h2 className="text-2xl font-bold text-gray-900">网站登录</h2>
  30. <p className="mt-2 text-sm text-gray-600">登录您的账号以继续</p>
  31. </div>
  32. <form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
  33. <div>
  34. <label htmlFor="username" className="block text-sm font-medium text-gray-700 mb-1">
  35. 用户名
  36. </label>
  37. <div className="relative">
  38. <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
  39. <UserIcon className="h-5 w-5 text-gray-400" />
  40. </div>
  41. <input
  42. id="username"
  43. type="text"
  44. 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`}
  45. placeholder="请输入用户名"
  46. {...register('username', {
  47. required: '用户名不能为空',
  48. minLength: { value: 3, message: '用户名至少3个字符' }
  49. })}
  50. />
  51. </div>
  52. {errors.username && (
  53. <p className="mt-1 text-sm text-red-600">{errors.username.message?.toString()}</p>
  54. )}
  55. </div>
  56. <div>
  57. <label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1">
  58. 密码
  59. </label>
  60. <div className="relative">
  61. <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
  62. <LockClosedIcon className="h-5 w-5 text-gray-400" />
  63. </div>
  64. <input
  65. id="password"
  66. type={showPassword ? 'text' : 'password'}
  67. 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`}
  68. placeholder="请输入密码"
  69. {...register('password', {
  70. required: '密码不能为空',
  71. minLength: { value: 6, message: '密码至少6个字符' }
  72. })}
  73. />
  74. <button
  75. type="button"
  76. className="absolute inset-y-0 right-0 pr-3 flex items-center"
  77. onClick={() => setShowPassword(!showPassword)}
  78. >
  79. {showPassword ? (
  80. <EyeSlashIcon className="h-5 w-5 text-gray-400" />
  81. ) : (
  82. <EyeIcon className="h-5 w-5 text-gray-400" />
  83. )}
  84. </button>
  85. </div>
  86. {errors.password && (
  87. <p className="mt-1 text-sm text-red-600">{errors.password.message?.toString()}</p>
  88. )}
  89. </div>
  90. <div>
  91. <button
  92. type="submit"
  93. disabled={loading}
  94. 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"
  95. >
  96. {loading ? '登录中...' : '登录'}
  97. </button>
  98. </div>
  99. </form>
  100. <div className="mt-6">
  101. <div className="relative">
  102. <div className="absolute inset-0 flex items-center">
  103. <div className="w-full border-t border-gray-300"></div>
  104. </div>
  105. <div className="relative flex justify-center text-sm">
  106. <span className="px-2 bg-white text-gray-500">还没有账号?</span>
  107. </div>
  108. </div>
  109. <div className="mt-4">
  110. <button
  111. type="button"
  112. onClick={() => navigate('/register')}
  113. 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"
  114. >
  115. 注册账号
  116. </button>
  117. </div>
  118. </div>
  119. </div>
  120. </div>
  121. </div>
  122. );
  123. };
  124. export default LoginPage;