auth.tsx 5.5 KB

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