useAuth.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import { createContext, useContext, PropsWithChildren } from 'react'
  2. import Taro from '@tarojs/taro'
  3. import { InferResponseType, InferRequestType } from 'hono'
  4. import { QueryClient, useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
  5. import { enterpriseAuthClient } from '../api'
  6. // 用户类型定义 - 使用企业用户认证
  7. export type User = InferResponseType<typeof enterpriseAuthClient.me.$get, 200>
  8. type LoginRequest = InferRequestType<typeof enterpriseAuthClient.login.$post>['json']
  9. // 企业用户注册可能由管理员创建,前端不提供注册接口
  10. type RegisterRequest = { username: string; password: string }
  11. export interface AuthContextType {
  12. user: User | null
  13. login: (data: LoginRequest) => Promise<User>
  14. logout: () => Promise<void>
  15. register: (data: RegisterRequest) => Promise<User>
  16. updateUser: (userData: Partial<User>) => void
  17. isLoading: boolean
  18. isLoggedIn: boolean
  19. }
  20. const AuthContext = createContext<AuthContextType | undefined>(undefined)
  21. // 导出queryClient以供外部使用(如果需要)
  22. export const queryClient = new QueryClient()
  23. export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
  24. const queryClient = useQueryClient()
  25. const { data: user, isLoading } = useQuery<User | null, Error>({
  26. queryKey: ['currentUser'],
  27. queryFn: async () => {
  28. const token = Taro.getStorageSync('enterprise_token')
  29. if (!token) {
  30. return null
  31. }
  32. try {
  33. const response = await enterpriseAuthClient.me.$get({})
  34. if (response.status !== 200) {
  35. throw new Error('获取用户信息失败')
  36. }
  37. const user = await response.json()
  38. Taro.setStorageSync('enterpriseUserInfo', JSON.stringify(user))
  39. return user
  40. } catch (error) {
  41. Taro.removeStorageSync('enterprise_token')
  42. Taro.removeStorageSync('enterpriseUserInfo')
  43. return null
  44. }
  45. },
  46. staleTime: Infinity, // 用户信息不常变动,设为无限期
  47. refetchOnWindowFocus: false, // 失去焦点不重新获取
  48. refetchOnReconnect: false, // 网络重连不重新获取
  49. })
  50. const loginMutation = useMutation<User, Error, LoginRequest>({
  51. mutationFn: async (data) => {
  52. const response = await enterpriseAuthClient.login.$post({ json: data })
  53. if (response.status !== 200) {
  54. throw new Error('登录失败')
  55. }
  56. const { token, user } = await response.json()
  57. Taro.setStorageSync('enterprise_token', token)
  58. Taro.setStorageSync('enterpriseUserInfo', JSON.stringify(user))
  59. // if (refresh_token) {
  60. // Taro.setStorageSync('enterprise_refresh_token', refresh_token)
  61. // }
  62. return user
  63. },
  64. onSuccess: (newUser) => {
  65. queryClient.setQueryData(['currentUser'], newUser)
  66. },
  67. onError: (error) => {
  68. Taro.showToast({
  69. title: error.message || '登录失败,请检查用户名和密码',
  70. icon: 'none',
  71. duration: 2000,
  72. })
  73. },
  74. })
  75. const registerMutation = useMutation<User, Error, RegisterRequest>({
  76. mutationFn: async () => {
  77. // 企业用户注册由管理员创建,前端不提供注册接口
  78. throw new Error('企业用户注册请联系管理员创建账户')
  79. },
  80. onSuccess: (newUser) => {
  81. queryClient.setQueryData(['currentUser'], newUser)
  82. },
  83. onError: (error) => {
  84. Taro.showToast({
  85. title: error.message || '企业用户注册请联系管理员',
  86. icon: 'none',
  87. duration: 3000,
  88. })
  89. },
  90. })
  91. const logoutMutation = useMutation<void, Error>({
  92. mutationFn: async () => {
  93. try {
  94. const response = await enterpriseAuthClient.logout.$post({})
  95. if (response.status !== 200) {
  96. throw new Error('登出失败')
  97. }
  98. } catch (error) {
  99. console.error('Logout error:', error)
  100. } finally {
  101. Taro.removeStorageSync('enterprise_token')
  102. Taro.removeStorageSync('enterprise_refresh_token')
  103. Taro.removeStorageSync('enterpriseUserInfo')
  104. }
  105. },
  106. onSuccess: () => {
  107. queryClient.setQueryData(['currentUser'], null)
  108. Taro.redirectTo({ url: '/pages/login/index' })
  109. },
  110. onError: (error) => {
  111. Taro.showToast({
  112. title: error.message || '登出失败',
  113. icon: 'none',
  114. duration: 2000,
  115. })
  116. },
  117. })
  118. const updateUserMutation = useMutation<User, Error, Partial<User>>({
  119. mutationFn: async () => {
  120. // 企业用户信息更新可能由管理员管理,前端不提供更新接口
  121. throw new Error('企业用户信息更新请联系管理员')
  122. },
  123. onSuccess: (updatedUser) => {
  124. queryClient.setQueryData(['currentUser'], updatedUser)
  125. Taro.showToast({
  126. title: '更新成功',
  127. icon: 'success',
  128. duration: 2000,
  129. })
  130. },
  131. onError: (error) => {
  132. Taro.showToast({
  133. title: error.message || '更新失败,请重试',
  134. icon: 'none',
  135. duration: 2000,
  136. })
  137. },
  138. })
  139. const updateUser = updateUserMutation.mutateAsync
  140. const value = {
  141. user: user || null,
  142. login: loginMutation.mutateAsync,
  143. logout: logoutMutation.mutateAsync,
  144. register: registerMutation.mutateAsync,
  145. updateUser,
  146. isLoading: isLoading || loginMutation.isPending || registerMutation.isPending || logoutMutation.isPending,
  147. isLoggedIn: !!user,
  148. }
  149. return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
  150. }
  151. export const useAuth = () => {
  152. const context = useContext(AuthContext)
  153. if (context === undefined) {
  154. throw new Error('useAuth must be used within an AuthProvider')
  155. }
  156. return context
  157. }