Login.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import React, { useState } from 'react';
  2. import {
  3. Form,
  4. Input,
  5. Button,
  6. Card,
  7. App,
  8. } from 'antd';
  9. import {
  10. UserOutlined,
  11. LockOutlined,
  12. EyeOutlined,
  13. EyeInvisibleOutlined
  14. } from '@ant-design/icons';
  15. import { useNavigate } from 'react-router';
  16. import {
  17. useAuth,
  18. } from '../hooks/AuthProvider';
  19. // 登录页面
  20. export const LoginPage = () => {
  21. const { message } = App.useApp();
  22. const { login } = useAuth();
  23. const [form] = Form.useForm();
  24. const [loading, setLoading] = useState(false);
  25. const navigate = useNavigate();
  26. const handleSubmit = async (values: { username: string; password: string }) => {
  27. try {
  28. setLoading(true);
  29. // 获取地理位置
  30. let latitude: number | undefined;
  31. let longitude: number | undefined;
  32. try {
  33. if (navigator.geolocation) {
  34. const position = await new Promise<GeolocationPosition>((resolve, reject) => {
  35. navigator.geolocation.getCurrentPosition(resolve, reject);
  36. });
  37. latitude = position.coords.latitude;
  38. longitude = position.coords.longitude;
  39. }
  40. } catch (geoError) {
  41. console.warn('获取地理位置失败:', geoError);
  42. }
  43. await login(values.username, values.password, latitude, longitude);
  44. // 登录成功后跳转到管理后台首页
  45. navigate('/admin/dashboard');
  46. } catch (error: any) {
  47. message.error(error instanceof Error ? error.message : '登录失败');
  48. } finally {
  49. setLoading(false);
  50. }
  51. };
  52. return (
  53. <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100 py-12 px-4 sm:px-6 lg:px-8">
  54. <div className="max-w-md w-full space-y-8">
  55. <div className="text-center">
  56. <div className="w-16 h-16 bg-primary/10 rounded-full flex items-center justify-center mx-auto mb-4">
  57. <UserOutlined style={{ fontSize: 32, color: '#1890ff' }} />
  58. </div>
  59. <h2 className="mt-2 text-center text-3xl font-extrabold text-gray-900">
  60. 管理后台登录
  61. </h2>
  62. <p className="mt-2 text-gray-500">请输入您的账号和密码</p>
  63. </div>
  64. <Card className="shadow-lg border-none transition-all duration-300 hover:shadow-xl">
  65. <Form
  66. form={form}
  67. name="login"
  68. onFinish={handleSubmit}
  69. autoComplete="off"
  70. layout="vertical"
  71. >
  72. <Form.Item
  73. name="username"
  74. rules={[{ required: true, message: '请输入用户名' }]}
  75. label="用户名"
  76. >
  77. <Input
  78. prefix={<UserOutlined className="text-primary" />}
  79. placeholder="请输入用户名"
  80. size="large"
  81. className="transition-all duration-200 focus:border-primary focus:ring-1 focus:ring-primary"
  82. />
  83. </Form.Item>
  84. <Form.Item
  85. name="password"
  86. rules={[{ required: true, message: '请输入密码' }]}
  87. label="密码"
  88. >
  89. <Input.Password
  90. prefix={<LockOutlined className="text-primary" />}
  91. placeholder="请输入密码"
  92. size="large"
  93. iconRender={(visible) => (visible ? <EyeOutlined /> : <EyeInvisibleOutlined />)}
  94. className="transition-all duration-200 focus:border-primary focus:ring-1 focus:ring-primary"
  95. />
  96. </Form.Item>
  97. <Form.Item>
  98. <Button
  99. type="primary"
  100. htmlType="submit"
  101. size="large"
  102. block
  103. loading={loading}
  104. className="h-12 text-lg transition-all duration-200 hover:shadow-lg"
  105. >
  106. 登录
  107. </Button>
  108. </Form.Item>
  109. </Form>
  110. <div className="mt-6 text-center text-gray-500 text-sm">
  111. <p>测试账号: <span className="font-medium">admin</span> / <span className="font-medium">admin123</span></p>
  112. <p className="mt-1">© {new Date().getFullYear()} 管理系统. 保留所有权利.</p>
  113. </div>
  114. </Card>
  115. </div>
  116. </div>
  117. );
  118. };