feat: 优化 token 过期提示

This commit is contained in:
chaoszhu
2026-08-29 22:42:27 +08:00
parent 1982d1d0f6
commit af38e3ea7c
2 changed files with 46 additions and 18 deletions
+8 -5
View File
@@ -89,6 +89,13 @@ const useStore = defineStore('global', {
localStorage.setItem('deviceId', deviceId)
this.$patch({ user: username, deviceId })
},
clearLoginInfo() {
localStorage.removeItem('token')
localStorage.removeItem('uid')
localStorage.removeItem('user')
localStorage.removeItem('deviceId')
this.$patch({ token: null, uid: null, user: null, deviceId: null })
},
async removeLoginInfo(removeSession = false) {
try {
if (removeSession && this.deviceId) {
@@ -97,11 +104,7 @@ const useStore = defineStore('global', {
} catch (err) {
console.error('注销登录凭证失败: ', err.message)
} finally {
localStorage.removeItem('token')
localStorage.removeItem('uid')
localStorage.removeItem('user')
localStorage.removeItem('deviceId')
this.$patch({ token: null, uid: null, user: null, deviceId: null })
this.clearLoginInfo()
}
},
async getMainData() {
+38 -13
View File
@@ -8,8 +8,35 @@ axios.defaults.withCredentials = true
axios.defaults.baseURL = process.env.serviceApiPrefix || '/api/v1'
const instance = axios.create()
let authGeneration = 0
function handleAuthFailure(error, skipErrorMessage) {
const store = useStore()
const requestToken = error.config?.headers?.token || null
// 重新登录后仍可能收到旧请求的失败响应,不能让旧响应清除新的登录态。
if (store.token && requestToken !== store.token) return
// 首个失败响应推进版本号,同一批并发请求及更早的响应会自动失效。
if (error.config?.authGeneration !== authGeneration) return
authGeneration += 1
store.clearLoginInfo()
if (router.currentRoute.value.path === '/login') return
if (!skipErrorMessage) {
ElMessage.closeAll()
ElMessage({
message: error.response?.data?.msg || '登录状态已失效,请重新登录',
type: 'error',
center: true
})
}
router.replace('/login')
}
instance.interceptors.request.use((config) => {
config.authGeneration = authGeneration
config.headers.token = useStore().token
return config
}, (error) => {
@@ -20,25 +47,23 @@ instance.interceptors.request.use((config) => {
instance.interceptors.response.use((response) => {
if (response.status === 200) return response.data
}, (error) => {
let { response } = error
const { response } = error
const skipErrorMessage = error.config?.skipErrorMessage
if (error?.message?.includes('timeout')) {
if (!skipErrorMessage) ElMessage({ message: '请求超时', type: 'error', center: true })
return Promise.reject(error)
}
switch (response?.data?.status) {
case 401: // token过期
router.push('login')
return Promise.reject(error)
case 403:
if (response?.data?.data?.code === 'IP_ACCESS_DENIED') {
window.location.reload()
return Promise.reject(error)
}
if (!skipErrorMessage) ElMessage({ message: `${ response?.data?.msg || '登录错误' }`, type: 'error', center: true })
router.push('login')
return Promise.reject(error)
if (response?.data?.data?.code === 'IP_ACCESS_DENIED') {
window.location.reload()
return Promise.reject(error)
}
if ([401, 403,].includes(response?.data?.status)) {
handleAuthFailure(error, skipErrorMessage)
return Promise.reject(error)
}
switch (response?.status) {
case 404:
if (!skipErrorMessage) ElMessage({ message: '404 Not Found', type: 'error', center: true })