import { defineStore } from 'pinia' import { onScopeDispose, ref } from 'vue' const TOKEN_KEY = 'oci-portal.token' const EXPIRES_KEY = 'oci-portal.expiresAt' export const useAuthStore = defineStore('auth', () => { const token = ref(localStorage.getItem(TOKEN_KEY) ?? '') const expiresAt = ref(localStorage.getItem(EXPIRES_KEY) ?? '') /** localStorage 是多标签页会话真值;401 判定前也主动同步,兜住 storage 事件尚未送达的窗口 */ function syncFromStorage() { token.value = localStorage.getItem(TOKEN_KEY) ?? '' expiresAt.value = localStorage.getItem(EXPIRES_KEY) ?? '' } function onStorage(event: StorageEvent) { if (event.storageArea && event.storageArea !== localStorage) return if (event.key !== null && event.key !== TOKEN_KEY && event.key !== EXPIRES_KEY) return syncFromStorage() } window.addEventListener('storage', onStorage) onScopeDispose(() => window.removeEventListener('storage', onStorage)) // 普通函数而非 computed:computed 只随 token/expiresAt 失效,时间流逝不会 // 触发重算,令牌自然过期后路由守卫会长期误判仍已登录 function isAuthed(): boolean { if (!token.value) return false if (!expiresAt.value) return true return new Date(expiresAt.value).getTime() > Date.now() } function setSession(newToken: string, newExpiresAt: string) { token.value = newToken expiresAt.value = newExpiresAt localStorage.setItem(TOKEN_KEY, newToken) localStorage.setItem(EXPIRES_KEY, newExpiresAt) } function logout() { token.value = '' expiresAt.value = '' localStorage.removeItem(TOKEN_KEY) localStorage.removeItem(EXPIRES_KEY) } return { token, expiresAt, isAuthed, setSession, syncFromStorage, logout } })