50 lines
1.7 KiB
TypeScript
50 lines
1.7 KiB
TypeScript
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 }
|
|
})
|