LoginPage.tsx 5.7 KB

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