优化登录状态错误时跳转页面到admin/login
This commit is contained in:
@@ -34,6 +34,7 @@ function resolveAdminThinkLang(): string {
|
|||||||
|
|
||||||
window.requests = []
|
window.requests = []
|
||||||
window.tokenRefreshing = false
|
window.tokenRefreshing = false
|
||||||
|
window.authRedirecting = false
|
||||||
const pendingMap = new Map()
|
const pendingMap = new Map()
|
||||||
const loadingInstance: LoadingInstance = {
|
const loadingInstance: LoadingInstance = {
|
||||||
target: null,
|
target: null,
|
||||||
@@ -245,20 +246,119 @@ export default createAxios
|
|||||||
* 处理异常
|
* 处理异常
|
||||||
* @param {*} error
|
* @param {*} error
|
||||||
*/
|
*/
|
||||||
|
/** 解析 axios 错误响应体(可能已是对象或 JSON 字符串) */
|
||||||
|
function normalizeAxiosErrorBody(data: unknown): anyObj | null {
|
||||||
|
if (data === undefined || data === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
try {
|
||||||
|
return JSON.parse(data) as anyObj
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof data === 'object') {
|
||||||
|
return data as anyObj
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后端未登录时通过 HTTP 303 + body.data.type === 'need login' 提示登录(见 Auth::LOGIN_RESPONSE_CODE)。
|
||||||
|
* Axios 将 303 视为失败,不会进入成功拦截器里对 code==303 的分支,需在此与之一致:清 token 并跳登录页。
|
||||||
|
*/
|
||||||
|
function redirectToLoginIfNeedLoginFromHttp303(error: any): boolean {
|
||||||
|
if (!error?.response || error.response.status !== 303) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const body = normalizeAxiosErrorBody(error.response.data)
|
||||||
|
if (!body || typeof body.data !== 'object' || body.data === null) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const needType = (body.data as anyObj).type
|
||||||
|
if (needType !== 'need login') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (window.authRedirecting) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const isAdminAppFlag = isAdminApp()
|
||||||
|
const loginRouteName = isAdminAppFlag ? 'adminLogin' : 'userLogin'
|
||||||
|
if (router.currentRoute.value.name === loginRouteName) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
window.authRedirecting = true
|
||||||
|
const adminInfo = useAdminInfo()
|
||||||
|
const userInfo = useUserInfo()
|
||||||
|
if (isAdminAppFlag) {
|
||||||
|
adminInfo.removeToken()
|
||||||
|
router.replace({ name: loginRouteName }).finally(() => {
|
||||||
|
window.authRedirecting = false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
userInfo.removeToken()
|
||||||
|
const to = router.currentRoute.value.fullPath
|
||||||
|
router.replace({ name: loginRouteName, query: to ? { to } : {} }).finally(() => {
|
||||||
|
window.authRedirecting = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
function httpErrorStatusHandle(error: any) {
|
function httpErrorStatusHandle(error: any) {
|
||||||
// 处理被取消的请求
|
// 处理被取消的请求
|
||||||
if (axios.isCancel(error)) return console.error(i18n.global.t('axios.Automatic cancellation due to duplicate request:') + error.message)
|
if (axios.isCancel(error)) return console.error(i18n.global.t('axios.Automatic cancellation due to duplicate request:') + error.message)
|
||||||
|
if (redirectToLoginIfNeedLoginFromHttp303(error)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
let message = ''
|
let message = ''
|
||||||
if (error && error.response) {
|
if (error && error.response) {
|
||||||
switch (error.response.status) {
|
switch (error.response.status) {
|
||||||
case 302:
|
case 302:
|
||||||
message = i18n.global.t('axios.Interface redirected!')
|
message = i18n.global.t('axios.Interface redirected!')
|
||||||
break
|
break
|
||||||
|
case 303: {
|
||||||
|
const body303 = normalizeAxiosErrorBody(error.response.data)
|
||||||
|
const serverMsg303 = body303 && typeof body303.msg === 'string' ? body303.msg : ''
|
||||||
|
message = serverMsg303 !== '' ? serverMsg303 : i18n.global.t('axios.Interface redirected!')
|
||||||
|
break
|
||||||
|
}
|
||||||
case 400:
|
case 400:
|
||||||
message = i18n.global.t('axios.Incorrect parameter!')
|
message = i18n.global.t('axios.Incorrect parameter!')
|
||||||
break
|
break
|
||||||
case 401:
|
case 401:
|
||||||
message = i18n.global.t('axios.You do not have permission to operate!')
|
message = i18n.global.t('axios.You do not have permission to operate!')
|
||||||
|
{
|
||||||
|
const data = error.response.data
|
||||||
|
const serverMsg = data && typeof data.msg === 'string' ? data.msg : ''
|
||||||
|
if (serverMsg) {
|
||||||
|
message = serverMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAdminAppFlag = isAdminApp()
|
||||||
|
const loginRouteName = isAdminAppFlag ? 'adminLogin' : 'userLogin'
|
||||||
|
if (!window.authRedirecting && router.currentRoute.value.name !== loginRouteName) {
|
||||||
|
window.authRedirecting = true
|
||||||
|
|
||||||
|
const adminInfo = useAdminInfo()
|
||||||
|
const userInfo = useUserInfo()
|
||||||
|
if (isAdminAppFlag) {
|
||||||
|
adminInfo.removeToken()
|
||||||
|
router.replace({ name: loginRouteName }).finally(() => {
|
||||||
|
window.authRedirecting = false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
userInfo.removeToken()
|
||||||
|
const to = router.currentRoute.value.fullPath
|
||||||
|
router
|
||||||
|
.replace({ name: loginRouteName, query: to ? { to } : {} })
|
||||||
|
.finally(() => {
|
||||||
|
window.authRedirecting = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
break
|
break
|
||||||
case 403:
|
case 403:
|
||||||
message = i18n.global.t('axios.You do not have permission to operate!')
|
message = i18n.global.t('axios.You do not have permission to operate!')
|
||||||
@@ -271,6 +371,49 @@ function httpErrorStatusHandle(error: any) {
|
|||||||
break
|
break
|
||||||
case 409:
|
case 409:
|
||||||
message = i18n.global.t('axios.The same data already exists in the system!')
|
message = i18n.global.t('axios.The same data already exists in the system!')
|
||||||
|
{
|
||||||
|
const data = error.response.data
|
||||||
|
const serverMsg = data && typeof data.msg === 'string' ? data.msg : ''
|
||||||
|
const authExpiredHints = [
|
||||||
|
'Token expiration',
|
||||||
|
'token expiration',
|
||||||
|
'登录态过期',
|
||||||
|
'请重新登录',
|
||||||
|
'Session expired',
|
||||||
|
'session expired',
|
||||||
|
'please login again',
|
||||||
|
'sila log masuk semula',
|
||||||
|
]
|
||||||
|
const looksLikeAuthExpired =
|
||||||
|
serverMsg !== '' && authExpiredHints.some((hint) => serverMsg.toLowerCase().includes(hint.toLowerCase()))
|
||||||
|
|
||||||
|
if (looksLikeAuthExpired) {
|
||||||
|
message = serverMsg
|
||||||
|
|
||||||
|
const isAdminAppFlag = isAdminApp()
|
||||||
|
const loginRouteName = isAdminAppFlag ? 'adminLogin' : 'userLogin'
|
||||||
|
if (!window.authRedirecting && router.currentRoute.value.name !== loginRouteName) {
|
||||||
|
window.authRedirecting = true
|
||||||
|
|
||||||
|
const adminInfo = useAdminInfo()
|
||||||
|
const userInfo = useUserInfo()
|
||||||
|
if (isAdminAppFlag) {
|
||||||
|
adminInfo.removeToken()
|
||||||
|
router.replace({ name: loginRouteName }).finally(() => {
|
||||||
|
window.authRedirecting = false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
userInfo.removeToken()
|
||||||
|
const to = router.currentRoute.value.fullPath
|
||||||
|
router
|
||||||
|
.replace({ name: loginRouteName, query: to ? { to } : {} })
|
||||||
|
.finally(() => {
|
||||||
|
window.authRedirecting = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
break
|
break
|
||||||
case 500:
|
case 500:
|
||||||
message = i18n.global.t('axios.Server internal error!')
|
message = i18n.global.t('axios.Server internal error!')
|
||||||
|
|||||||
1
web/types/global.d.ts
vendored
1
web/types/global.d.ts
vendored
@@ -3,6 +3,7 @@ interface Window {
|
|||||||
lazy: number
|
lazy: number
|
||||||
unique: number
|
unique: number
|
||||||
tokenRefreshing: boolean
|
tokenRefreshing: boolean
|
||||||
|
authRedirecting: boolean
|
||||||
requests: Function[]
|
requests: Function[]
|
||||||
eventSource: EventSource
|
eventSource: EventSource
|
||||||
loadLangHandle: Record<string, any>
|
loadLangHandle: Record<string, any>
|
||||||
|
|||||||
Reference in New Issue
Block a user