AuthProvider.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import React, { useState, useEffect, createContext, useContext } from 'react';
  2. import {
  3. useQuery,
  4. useQueryClient,
  5. } from '@tanstack/react-query';
  6. import axios from 'axios';
  7. import 'dayjs/locale/zh-cn';
  8. import type {
  9. AuthContextType
  10. } from '@/share/types';
  11. import { authClient } from '@/client/api';
  12. import type { InferResponseType, InferRequestType } from 'hono/client';
  13. export type User = InferResponseType<typeof authClient.me.$get, 200>;
  14. // 创建认证上下文
  15. const AuthContext = createContext<AuthContextType<User> | null>(null);
  16. // 认证提供器组件
  17. export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  18. const [user, setUser] = useState<User | null>(null);
  19. const [token, setToken] = useState<string | null>(localStorage.getItem('token'));
  20. const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
  21. const queryClient = useQueryClient();
  22. // 声明handleLogout函数
  23. const handleLogout = async () => {
  24. try {
  25. // 如果已登录,调用登出API
  26. if (token) {
  27. await authClient.logout.$post();
  28. }
  29. } catch (error) {
  30. console.error('登出请求失败:', error);
  31. } finally {
  32. // 清除本地状态
  33. setToken(null);
  34. setUser(null);
  35. setIsAuthenticated(false);
  36. localStorage.removeItem('token');
  37. // 清除Authorization头
  38. delete axios.defaults.headers.common['Authorization'];
  39. console.log('登出时已删除全局Authorization头');
  40. // 清除所有查询缓存
  41. queryClient.clear();
  42. }
  43. };
  44. // 使用useQuery检查登录状态
  45. const { isLoading } = useQuery({
  46. queryKey: ['auth', 'status', token],
  47. queryFn: async () => {
  48. if (!token) {
  49. setIsAuthenticated(false);
  50. setUser(null);
  51. return null;
  52. }
  53. try {
  54. // 设置全局默认请求头
  55. axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
  56. // 使用API验证当前用户
  57. const res = await authClient.me.$get();
  58. if (res.status !== 200) {
  59. const result = await res.json();
  60. throw new Error(result.message)
  61. }
  62. const currentUser = await res.json();
  63. setUser(currentUser);
  64. setIsAuthenticated(true);
  65. return { isValid: true, user: currentUser };
  66. } catch (error) {
  67. return { isValid: false };
  68. }
  69. },
  70. enabled: !!token,
  71. refetchOnWindowFocus: false,
  72. retry: false
  73. });
  74. const handleLogin = async (username: string, password: string, latitude?: number, longitude?: number): Promise<void> => {
  75. try {
  76. // 使用AuthAPI登录
  77. const response = await authClient.login.$post({
  78. json: {
  79. username,
  80. password
  81. }
  82. })
  83. if (response.status !== 200) {
  84. const result = await response.json()
  85. throw new Error(result.message);
  86. }
  87. const result = await response.json()
  88. // 保存token和用户信息
  89. const { token: newToken, user: newUser } = result;
  90. // 设置全局默认请求头
  91. axios.defaults.headers.common['Authorization'] = `Bearer ${newToken}`;
  92. // 保存状态
  93. setToken(newToken);
  94. setUser(newUser);
  95. setIsAuthenticated(true);
  96. localStorage.setItem('token', newToken);
  97. } catch (error) {
  98. console.error('登录失败:', error);
  99. throw error;
  100. }
  101. };
  102. return (
  103. <AuthContext.Provider
  104. value={{
  105. user,
  106. token,
  107. login: handleLogin,
  108. logout: handleLogout,
  109. isAuthenticated,
  110. isLoading
  111. }}
  112. >
  113. {children}
  114. </AuthContext.Provider>
  115. );
  116. };
  117. // 使用上下文的钩子
  118. export const useAuth = () => {
  119. const context = useContext(AuthContext);
  120. if (!context) {
  121. throw new Error('useAuth必须在AuthProvider内部使用');
  122. }
  123. return context;
  124. };