接入前端登录权限底座

This commit is contained in:
andy
2026-07-09 20:42:16 +08:00
parent c190d5e3a8
commit e49b2ccc1d
27 changed files with 1745 additions and 68 deletions

View File

@@ -0,0 +1,146 @@
import { defineStore } from 'pinia'
import { fetchCurrentAuth, loginAuth, logoutAuth } from '@/services/authService'
import { clearStoredAccessToken, getStoredAccessToken, setStoredAccessToken } from '@/services/authSession'
import type {
AuthHotelResult,
AuthLoginRequest,
AuthLoginResult,
AuthMeResult,
AuthMenuResult,
AuthPermissionCode,
AuthUserResult,
} from '@/types/auth'
interface AuthState {
accessToken: string | null
user: AuthUserResult | null
defaultHotelId: string | null
hotels: AuthHotelResult[]
permissions: AuthPermissionCode[]
menus: AuthMenuResult[]
selectedHotelId: string | null
restoreAttempted: boolean
restoring: boolean
}
export const useAuthStore = defineStore('auth', {
state: (): AuthState => ({
accessToken: getStoredAccessToken(),
user: null,
defaultHotelId: null,
hotels: [],
permissions: [],
menus: [],
selectedHotelId: null,
restoreAttempted: false,
restoring: false,
}),
getters: {
isAuthenticated: (state) => Boolean(state.accessToken && state.user),
selectedHotel: (state) =>
state.hotels.find((hotel) => hotel.hotel_id === state.selectedHotelId) ?? state.hotels[0] ?? null,
visibleMenus: (state) =>
[...state.menus]
.filter((menu) => Boolean(menu.route_path))
.sort((left, right) => (left.sort_order ?? 0) - (right.sort_order ?? 0)),
firstMenuPath(): string | null {
return this.visibleMenus[0]?.route_path ?? null
},
},
actions: {
hasPermission(permission: AuthPermissionCode): boolean {
return this.permissions.includes(permission)
},
hasStoredToken(): boolean {
return Boolean(getStoredAccessToken())
},
async login(request: AuthLoginRequest): Promise<AuthLoginResult> {
const result = await loginAuth(request)
this.applyLoginResult(result)
return result
},
async restoreFromSession(): Promise<boolean> {
if (this.restoreAttempted && this.isAuthenticated) {
return true
}
const token = getStoredAccessToken()
if (!token) {
this.clearAuth()
this.restoreAttempted = true
return false
}
this.accessToken = token
this.restoring = true
try {
const result = await fetchCurrentAuth()
this.applyAuthContext(result)
this.restoreAttempted = true
return true
} catch {
this.clearAuth()
this.restoreAttempted = true
return false
} finally {
this.restoring = false
}
},
async logout(): Promise<void> {
try {
if (this.accessToken) {
await logoutAuth()
}
} catch {
// 本地退出必须完成;后端登出失败时不保留过期 token 或用户上下文。
} finally {
this.clearAuth()
this.restoreAttempted = true
}
},
applyLoginResult(result: AuthLoginResult): void {
this.accessToken = result.access_token
setStoredAccessToken(result.access_token)
this.applyAuthContext(result)
this.restoreAttempted = true
},
applyAuthContext(result: AuthMeResult): void {
this.user = result.user
this.defaultHotelId = result.default_hotel_id
this.hotels = result.hotels
this.permissions = result.permissions
this.menus = result.menus
this.selectedHotelId = resolveSelectedHotelId(result.hotels, result.default_hotel_id, this.selectedHotelId)
},
setSelectedHotelId(hotelId: string): void {
if (this.hotels.some((hotel) => hotel.hotel_id === hotelId)) {
this.selectedHotelId = hotelId
}
},
clearAuth(): void {
clearStoredAccessToken()
this.accessToken = null
this.user = null
this.defaultHotelId = null
this.hotels = []
this.permissions = []
this.menus = []
this.selectedHotelId = null
this.restoring = false
},
},
})
function resolveSelectedHotelId(
hotels: AuthHotelResult[],
defaultHotelId: string | null,
currentHotelId: string | null,
): string | null {
if (currentHotelId && hotels.some((hotel) => hotel.hotel_id === currentHotelId)) {
return currentHotelId
}
if (defaultHotelId && hotels.some((hotel) => hotel.hotel_id === defaultHotelId)) {
return defaultHotelId
}
return hotels.find((hotel) => hotel.default_hotel)?.hotel_id ?? hotels[0]?.hotel_id ?? null
}