You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

234 lines
6.6 KiB

import { createContext, useContext, useState, useEffect, type ReactNode } from 'react'
import type { User, MenuItem, UserOrgInfo } from '@/types'
import { apiClient } from '@/services/api'
interface AuthContextType {
user: User | null
token: string | null
isAuthenticated: boolean
isLoading: boolean
login: (account: string, password: string) => Promise<void>
loginWithToken: (token: string) => void
logout: () => void
currentOrg: { id: number; name: string } | null
userOrgs: UserOrgInfo[]
userMenus: MenuItem[]
switchOrg: (orgId: number) => Promise<void>
refreshMenus: () => Promise<void>
}
const AuthContext = createContext<AuthContextType | undefined>(undefined)
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [token, setToken] = useState<string | null>(null)
const [isLoading, setIsLoading] = useState(true)
const [currentOrg, setCurrentOrg] = useState<{ id: number; name: string } | null>(null)
const [userOrgs, setUserOrgs] = useState<UserOrgInfo[]>([])
const [userMenus, setUserMenus] = useState<MenuItem[]>([])
useEffect(() => {
const storedToken = localStorage.getItem('token')
const storedUser = localStorage.getItem('user')
if (storedToken) {
setToken(storedToken)
}
if (storedUser) {
try {
setUser(JSON.parse(storedUser))
} catch {
localStorage.removeItem('user')
}
}
const savedOrg = localStorage.getItem('currentOrg')
if (savedOrg) {
try { setCurrentOrg(JSON.parse(savedOrg)) } catch {}
}
setIsLoading(false)
}, [])
const refreshMenus = async () => {
try {
const data = await apiClient.getCurrentMenus()
setUserMenus(data.list || [])
} catch (e) {
console.error('Failed to fetch menus:', e)
}
}
const loadUserContext = async () => {
try {
const orgsData = await apiClient.getUserOrgs()
const orgs = orgsData.list || []
setUserOrgs(orgs)
if (orgs.length > 0) {
const storedOrg = localStorage.getItem('currentOrg')
let selectedOrg = orgs[0]
if (storedOrg) {
try {
const parsed = JSON.parse(storedOrg)
const found = orgs.find((o: UserOrgInfo) => o.orgId === parsed.id)
if (found) selectedOrg = found
} catch {}
}
setCurrentOrg({ id: selectedOrg.orgId, name: selectedOrg.orgName })
localStorage.setItem('currentOrg', JSON.stringify({ id: selectedOrg.orgId, name: selectedOrg.orgName }))
}
await refreshMenus()
} catch (e) {
console.error('Failed to load user context:', e)
await refreshMenus()
}
}
const switchOrgFn = async (orgId: number) => {
try {
const data = await apiClient.switchOrg(orgId)
if (data.token) {
localStorage.setItem('token', data.token)
setToken(data.token)
try {
const payload = JSON.parse(atob(data.token.split('.')[1]))
const userData: User = {
id: payload.userId || 0,
username: payload.username || '',
email: '',
role: payload.role || 'user',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
}
setUser(userData)
localStorage.setItem('user', JSON.stringify(userData))
} catch {}
const org = userOrgs.find((o: UserOrgInfo) => o.orgId === orgId)
if (org) {
setCurrentOrg({ id: org.orgId, name: org.orgName })
localStorage.setItem('currentOrg', JSON.stringify({ id: org.orgId, name: org.orgName }))
}
await refreshMenus()
}
} catch (e) {
console.error('Failed to switch org:', e)
throw e
}
}
useEffect(() => {
if (token && !isLoading) {
loadUserContext()
}
}, [token, isLoading])
const login = async (account: string, password: string) => {
try {
const response = await apiClient.login({ account, password })
if (response.success && response.token) {
setToken(response.token)
// 解析 JWT 获取用户信息
try {
const payload = JSON.parse(atob(response.token.split('.')[1]))
const userData: User = {
id: payload.userId || 0,
username: payload.username || account,
email: '',
role: payload.role || 'user',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
}
setUser(userData)
localStorage.setItem('user', JSON.stringify(userData))
} catch {
const userData: User = {
id: 0,
username: account,
email: '',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
}
setUser(userData)
localStorage.setItem('user', JSON.stringify(userData))
}
}
} catch (error) {
throw error
}
}
const loginWithToken = (ssoToken: string) => {
localStorage.setItem('token', ssoToken)
setToken(ssoToken)
// 解析 JWT 获取用户信息
try {
const payload = JSON.parse(atob(ssoToken.split('.')[1]))
const userData: User = {
id: payload.userId || 0,
username: payload.username || '',
email: payload.email || '',
role: payload.role || 'user',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
}
setUser(userData)
localStorage.setItem('user', JSON.stringify(userData))
} catch {
// JWT 解析失败,使用占位数据
const userData: User = {
id: 0,
username: 'SSO User',
email: '',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
}
setUser(userData)
localStorage.setItem('user', JSON.stringify(userData))
}
}
const logout = () => {
apiClient.logout()
setToken(null)
setUser(null)
setCurrentOrg(null)
setUserOrgs([])
setUserMenus([])
localStorage.removeItem('user')
localStorage.removeItem('currentOrg')
}
const value: AuthContextType = {
user,
token,
isAuthenticated: !!token,
isLoading,
login,
loginWithToken,
logout,
currentOrg,
userOrgs,
userMenus,
switchOrg: switchOrgFn,
refreshMenus,
}
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
export function useAuth() {
const context = useContext(AuthContext)
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider')
}
return context
}