滑动续期与多标签同步、列表报错提示、分页排序体验改进

This commit is contained in:
2026-08-11 11:44:57 +08:00
parent ec8a50c4c0
commit 343563bb79
18 changed files with 358 additions and 24 deletions
+21 -4
View File
@@ -92,6 +92,7 @@ async function handleUnauthorized(sentToken: string) {
const waited = await waitForSessionRefresh(sentToken)
if (waited) await new Promise<void>((resolve) => setTimeout(resolve, 0))
const auth = useAuthStore()
auth.syncFromStorage()
if (sentToken !== auth.token) return
auth.logout()
if (location.pathname !== '/login') {
@@ -140,11 +141,25 @@ function trackSessionRefresh(token: string): () => void {
}
}
function applySessionRefresh(value: unknown) {
if (!value || typeof value !== 'object') return
/** 滑动续期:后端对临过期令牌在响应头中换发同会话新令牌;仅当发送时令牌仍是
* 当前会话令牌才应用(与 401 处理同一快照原则,防旧会话迟到响应覆盖新会话)。 */
function applyRenewedSession(resp: Response, sentToken: string) {
// 测试环境的最小 Response stub 可能无 headers
const token = resp.headers?.get('X-Renewed-Token')
const expiresAt = resp.headers?.get('X-Renewed-Expires-At')
if (!token || !expiresAt || !sentToken) return
const auth = useAuthStore()
auth.syncFromStorage()
if (sentToken !== auth.token) return
auth.setSession(token, expiresAt)
}
function applySessionRefresh(value: unknown): boolean {
if (!value || typeof value !== 'object') return false
const refresh = value as { token?: unknown; expiresAt?: unknown }
if (typeof refresh.token !== 'string' || typeof refresh.expiresAt !== 'string') return
if (typeof refresh.token !== 'string' || typeof refresh.expiresAt !== 'string') return false
useAuthStore().setSession(refresh.token, refresh.expiresAt)
return true
}
/** 统一请求封装:注入 JWT、401 登出、错误转 ApiError;
@@ -190,7 +205,8 @@ async function doRequest<T>(
// 202/204 等成功响应可能无 body,直接 resp.json() 会抛 Unexpected end of JSON input
const text = await resp.text()
const result = (text ? JSON.parse(text) : undefined) as T
if (opts.refreshesSession && sentToken) applySessionRefresh(result)
const bodyRefreshed = !!sentToken && !!opts.refreshesSession && applySessionRefresh(result)
if (!bodyRefreshed) applyRenewedSession(resp, sentToken ?? '')
return result
}
@@ -214,6 +230,7 @@ export async function rawFetch(
body: opts.body,
})
if (!resp.ok) await parseError(resp, sentToken)
applyRenewedSession(resp, sentToken ?? '')
return resp
}