| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'
- import Taro from '@tarojs/taro'
- import { QueryClient, useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
- import { talentAuthClient } from '../api'
- import type { InferResponseType } from 'hono'
- // 使用RPC类型推断,确保类型与后端API完全一致
- export type TalentUserInfo = InferResponseType<typeof talentAuthClient.me.$get, 200>
- export interface AuthContextType {
- isLoggedIn: boolean
- user: TalentUserInfo | null
- token: string | null
- login: (identifier: string, password: string) => Promise<void>
- logout: () => Promise<void>
- refreshUser: () => Promise<void>
- loading: boolean
- }
- const AuthContext = createContext<AuthContextType | undefined>(undefined)
- const TOKEN_KEY = 'talent_token'
- const USER_KEY = 'talent_user'
- // 导出queryClient以供外部使用(与mini-enterprise-auth-ui保持一致)
- export const queryClient = new QueryClient()
- export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
- const queryClient = useQueryClient()
- const [isLoggedIn, setIsLoggedIn] = useState(false)
- const [token, setToken] = useState<string | null>(null)
- const [loading, setLoading] = useState(true)
- // 使用React Query管理用户状态
- const { data: user, isLoading } = useQuery<TalentUserInfo | null, Error>({
- queryKey: ['talentCurrentUser'],
- queryFn: async () => {
- const storedToken = Taro.getStorageSync(TOKEN_KEY)
- if (!storedToken) {
- return null
- }
- try {
- const response = await talentAuthClient.me.$get()
- if (response.status !== 200) {
- throw new Error('获取用户信息失败')
- }
- // 使用 response.json() 解析数据
- const userInfo = await response.json()
- // 缓存到本地存储(序列化为JSON字符串)
- Taro.setStorageSync(USER_KEY, JSON.stringify(userInfo))
- return userInfo
- } catch (error) {
- console.error('获取用户信息失败:', error)
- Taro.removeStorageSync(TOKEN_KEY)
- Taro.removeStorageSync(USER_KEY)
- return null
- }
- },
- staleTime: Infinity,
- refetchOnWindowFocus: false,
- refetchOnReconnect: false,
- })
- // 同步isLoggedIn状态
- useEffect(() => {
- setIsLoggedIn(!!user)
- setToken(Taro.getStorageSync(TOKEN_KEY) || null)
- setLoading(false)
- }, [user])
- const loginMutation = useMutation<void, Error, { identifier: string; password: string }>({
- mutationFn: async ({ identifier, password }) => {
- const response = await talentAuthClient.login.$post({
- json: {
- identifier: identifier.trim(),
- password: password.trim()
- }
- })
- if (response.status !== 200) {
- throw new Error('登录失败')
- }
- // 使用 response.json() 解析数据
- const data = await response.json()
- const { token, user } = data
- // 保存到本地存储(user序列化为JSON字符串)
- Taro.setStorageSync(TOKEN_KEY, token)
- Taro.setStorageSync(USER_KEY, JSON.stringify(user))
- setToken(token)
- },
- onSuccess: () => {
- // 刷新用户信息
- queryClient.invalidateQueries({ queryKey: ['talentCurrentUser'] })
- },
- onError: (error) => {
- Taro.showToast({
- title: error.message || '登录失败,请检查账号和密码',
- icon: 'none',
- duration: 2000,
- })
- },
- })
- const logoutMutation = useMutation<void, Error>({
- mutationFn: async () => {
- try {
- // TODO: 调用退出API
- // await talentAuthClient.logout.$post()
- } catch (error) {
- console.error('Logout error:', error)
- } finally {
- Taro.removeStorageSync(TOKEN_KEY)
- Taro.removeStorageSync(USER_KEY)
- }
- },
- onSuccess: () => {
- queryClient.setQueryData(['talentCurrentUser'], null)
- Taro.reLaunch({ url: '/pages/login/index' })
- },
- onError: (error) => {
- Taro.showToast({
- title: error.message || '登出失败',
- icon: 'none',
- duration: 2000,
- })
- },
- })
- const login = async (identifier: string, password: string): Promise<void> => {
- await loginMutation.mutateAsync({ identifier, password })
- }
- const logout = async (): Promise<void> => {
- await logoutMutation.mutateAsync()
- }
- const refreshUser = async (): Promise<void> => {
- await queryClient.invalidateQueries({ queryKey: ['talentCurrentUser'] })
- }
- const value: AuthContextType = {
- isLoggedIn: !!user,
- user: user || null,
- token,
- login,
- logout,
- refreshUser,
- loading: loading || isLoading,
- }
- return (
- <AuthContext.Provider value={value}>
- {children}
- </AuthContext.Provider>
- )
- }
- export const useAuth = (): AuthContextType => {
- const context = useContext(AuthContext)
- if (context === undefined) {
- throw new Error('useAuth must be used within an AuthProvider')
- }
- return context
- }
- /**
- * useRequireAuth Hook
- * 检查登录状态,未登录则重定向到登录页
- */
- export const useRequireAuth = (): void => {
- const { isLoggedIn } = useAuth()
- useEffect(() => {
- if (!isLoggedIn) {
- Taro.reLaunch({ url: '/pages/login/index' })
- }
- }, [isLoggedIn])
- }
- export default AuthContext
|