|
|
@@ -0,0 +1,207 @@
|
|
|
+import { createContext, useContext, PropsWithChildren } from 'react'
|
|
|
+import Taro from '@tarojs/taro'
|
|
|
+import { QueryClient, useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|
|
+import { enterpriseAuthClient } from '../api'
|
|
|
+
|
|
|
+// 登录请求类型
|
|
|
+interface LoginRequest {
|
|
|
+ phone: string
|
|
|
+ password: string
|
|
|
+}
|
|
|
+
|
|
|
+// 用户类型定义 - 企业用户
|
|
|
+export interface User {
|
|
|
+ id: number
|
|
|
+ username: string
|
|
|
+ phone: string | null
|
|
|
+ email: string | null
|
|
|
+ nickname: string | null
|
|
|
+ name: string | null
|
|
|
+ avatarFileId: number | null
|
|
|
+ avatarFile?: {
|
|
|
+ id: number
|
|
|
+ name: string
|
|
|
+ fullUrl: string
|
|
|
+ type: string | null
|
|
|
+ size: number | null
|
|
|
+ } | null
|
|
|
+ companyId: number | null
|
|
|
+ company?: {
|
|
|
+ id: number
|
|
|
+ companyName: string
|
|
|
+ contactPerson: string | null
|
|
|
+ contactPhone: string | null
|
|
|
+ } | null
|
|
|
+ createdAt: string
|
|
|
+ updatedAt: string
|
|
|
+}
|
|
|
+
|
|
|
+// 企业用户注册可能由管理员创建,前端不提供注册接口
|
|
|
+interface RegisterRequest {
|
|
|
+ username: string
|
|
|
+ password: string
|
|
|
+}
|
|
|
+
|
|
|
+export interface AuthContextType {
|
|
|
+ user: User | null
|
|
|
+ login: (data: LoginRequest) => Promise<User>
|
|
|
+ logout: () => Promise<void>
|
|
|
+ register: (data: RegisterRequest) => Promise<User>
|
|
|
+ updateUser: (userData: Partial<User>) => void
|
|
|
+ isLoading: boolean
|
|
|
+ isLoggedIn: boolean
|
|
|
+}
|
|
|
+
|
|
|
+const AuthContext = createContext<AuthContextType | undefined>(undefined)
|
|
|
+
|
|
|
+// 导出queryClient以供外部使用(如果需要)
|
|
|
+export const queryClient = new QueryClient()
|
|
|
+
|
|
|
+export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
+ const queryClient = useQueryClient()
|
|
|
+
|
|
|
+ const { data: user, isLoading } = useQuery<User | null, Error>({
|
|
|
+ queryKey: ['currentUser'],
|
|
|
+ queryFn: async () => {
|
|
|
+ const token = Taro.getStorageSync('enterprise_token')
|
|
|
+ if (!token) {
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ const response = await enterpriseAuthClient.me.$get({})
|
|
|
+ if (response.status !== 200) {
|
|
|
+ throw new Error('获取用户信息失败')
|
|
|
+ }
|
|
|
+ const user = await response.json()
|
|
|
+ Taro.setStorageSync('enterpriseUserInfo', JSON.stringify(user))
|
|
|
+ return user
|
|
|
+ } catch (_error) {
|
|
|
+ Taro.removeStorageSync('enterprise_token')
|
|
|
+ Taro.removeStorageSync('enterpriseUserInfo')
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ },
|
|
|
+ staleTime: Infinity, // 用户信息不常变动,设为无限期
|
|
|
+ refetchOnWindowFocus: false, // 失去焦点不重新获取
|
|
|
+ refetchOnReconnect: false, // 网络重连不重新获取
|
|
|
+ })
|
|
|
+
|
|
|
+ const loginMutation = useMutation<User, Error, LoginRequest>({
|
|
|
+ mutationFn: async (data) => {
|
|
|
+ const response = await enterpriseAuthClient.login.$post({ json: data })
|
|
|
+ if (response.status !== 200) {
|
|
|
+ throw new Error('登录失败')
|
|
|
+ }
|
|
|
+ const { token, user } = await response.json()
|
|
|
+ Taro.setStorageSync('enterprise_token', token)
|
|
|
+ Taro.setStorageSync('enterpriseUserInfo', JSON.stringify(user))
|
|
|
+ // if (refresh_token) {
|
|
|
+ // Taro.setStorageSync('enterprise_refresh_token', refresh_token)
|
|
|
+ // }
|
|
|
+ return user
|
|
|
+ },
|
|
|
+ onSuccess: (newUser) => {
|
|
|
+ queryClient.setQueryData(['currentUser'], newUser)
|
|
|
+ },
|
|
|
+ onError: (error) => {
|
|
|
+ Taro.showToast({
|
|
|
+ title: error.message || '登录失败,请检查用户名和密码',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000,
|
|
|
+ })
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ const registerMutation = useMutation<User, Error, RegisterRequest>({
|
|
|
+ mutationFn: async () => {
|
|
|
+ // 企业用户注册由管理员创建,前端不提供注册接口
|
|
|
+ throw new Error('企业用户注册请联系管理员创建账户')
|
|
|
+ },
|
|
|
+ onSuccess: (newUser) => {
|
|
|
+ queryClient.setQueryData(['currentUser'], newUser)
|
|
|
+ },
|
|
|
+ onError: (error) => {
|
|
|
+ Taro.showToast({
|
|
|
+ title: error.message || '企业用户注册请联系管理员',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 3000,
|
|
|
+ })
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ const logoutMutation = useMutation<void, Error>({
|
|
|
+ mutationFn: async () => {
|
|
|
+ try {
|
|
|
+ const response = await enterpriseAuthClient.logout.$post({})
|
|
|
+ if (response.status !== 200) {
|
|
|
+ throw new Error('登出失败')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Logout error:', error)
|
|
|
+ } finally {
|
|
|
+ Taro.removeStorageSync('enterprise_token')
|
|
|
+ Taro.removeStorageSync('enterprise_refresh_token')
|
|
|
+ Taro.removeStorageSync('enterpriseUserInfo')
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onSuccess: async () => {
|
|
|
+ // 先清除用户状态
|
|
|
+ queryClient.setQueryData(['currentUser'], null)
|
|
|
+ // 使用 reLaunch 关闭所有页面并跳转到登录页
|
|
|
+ // 添加延迟确保状态更新完成
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 100))
|
|
|
+ Taro.reLaunch({ url: '/pages/login/index' })
|
|
|
+ },
|
|
|
+ onError: (error) => {
|
|
|
+ Taro.showToast({
|
|
|
+ title: error.message || '登出失败',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000,
|
|
|
+ })
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ const updateUserMutation = useMutation<User, Error, Partial<User>>({
|
|
|
+ mutationFn: async () => {
|
|
|
+ // 企业用户信息更新可能由管理员管理,前端不提供更新接口
|
|
|
+ throw new Error('企业用户信息更新请联系管理员')
|
|
|
+ },
|
|
|
+ onSuccess: (updatedUser) => {
|
|
|
+ queryClient.setQueryData(['currentUser'], updatedUser)
|
|
|
+ Taro.showToast({
|
|
|
+ title: '更新成功',
|
|
|
+ icon: 'success',
|
|
|
+ duration: 2000,
|
|
|
+ })
|
|
|
+ },
|
|
|
+ onError: (error) => {
|
|
|
+ Taro.showToast({
|
|
|
+ title: error.message || '更新失败,请重试',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000,
|
|
|
+ })
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ const updateUser = updateUserMutation.mutateAsync
|
|
|
+
|
|
|
+ const value = {
|
|
|
+ user: user || null,
|
|
|
+ login: loginMutation.mutateAsync,
|
|
|
+ logout: logoutMutation.mutateAsync,
|
|
|
+ register: registerMutation.mutateAsync,
|
|
|
+ updateUser,
|
|
|
+ isLoading: isLoading || loginMutation.isPending || registerMutation.isPending || logoutMutation.isPending,
|
|
|
+ isLoggedIn: !!user,
|
|
|
+ }
|
|
|
+
|
|
|
+ return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
|
|
|
+}
|
|
|
+
|
|
|
+export const useAuth = () => {
|
|
|
+ const context = useContext(AuthContext)
|
|
|
+ if (context === undefined) {
|
|
|
+ throw new Error('useAuth must be used within an AuthProvider')
|
|
|
+ }
|
|
|
+ return context
|
|
|
+}
|