优化:没有权限的进修默认回到登录页面

This commit is contained in:
ivan 2026-03-31 23:45:08 +08:00
parent 1a63d2d6e1
commit 8456ba90c8
4 changed files with 55 additions and 23 deletions

View File

@ -1,5 +1,36 @@
import { i18n } from '@/plugins/i18n' import { i18n } from '@/plugins/i18n'
export type HttpUnauthorizedHandler = () => void
let httpUnauthorizedHandler: HttpUnauthorizedHandler | null = null
/**
* HTTP 401
* main.ts app.use(pinia)app.use(router)
*/
export function setHttpUnauthorizedHandler(handler: HttpUnauthorizedHandler | null) {
httpUnauthorizedHandler = handler
}
function notifyHttpUnauthorized() {
try {
httpUnauthorizedHandler?.()
} catch (e) {
console.error('[request] httpUnauthorizedHandler failed', e)
}
}
async function parseJsonBody<T>(res: Response): Promise<T> {
if (res.status === 401) {
notifyHttpUnauthorized()
throw new Error(`HTTP 401: ${res.statusText}`)
}
if (!res.ok) {
throw new Error(`HTTP ${res.status}: ${res.statusText}`)
}
return res.json() as Promise<T>
}
/** 请求基础 URL默认 https://api.xtrader.vip可通过环境变量 VITE_API_BASE_URL 覆盖 */ /** 请求基础 URL默认 https://api.xtrader.vip可通过环境变量 VITE_API_BASE_URL 覆盖 */
export const BASE_URL = export const BASE_URL =
(import.meta as { env?: { VITE_API_BASE_URL?: string } }).env?.VITE_API_BASE_URL ?? (import.meta as { env?: { VITE_API_BASE_URL?: string } }).env?.VITE_API_BASE_URL ??
@ -77,10 +108,7 @@ export async function get<T = unknown>(
...config?.headers, ...config?.headers,
} }
const res = await fetch(url.toString(), { method: 'GET', headers }) const res = await fetch(url.toString(), { method: 'GET', headers })
if (!res.ok) { return parseJsonBody<T>(res)
throw new Error(`HTTP ${res.status}: ${res.statusText}`)
}
return res.json() as Promise<T>
} }
/** /**
@ -102,10 +130,7 @@ export async function post<T = unknown>(
headers, headers,
body: body !== undefined ? JSON.stringify(body) : undefined, body: body !== undefined ? JSON.stringify(body) : undefined,
}) })
if (!res.ok) { return parseJsonBody<T>(res)
throw new Error(`HTTP ${res.status}: ${res.statusText}`)
}
return res.json() as Promise<T>
} }
/** /**
@ -129,10 +154,7 @@ export async function uploadFile<T = unknown>(
headers, headers,
body: form, body: form,
}) })
if (!res.ok) { return parseJsonBody<T>(res)
throw new Error(`HTTP ${res.status}: ${res.statusText}`)
}
return res.json() as Promise<T>
} }
/** /**
@ -154,10 +176,7 @@ export async function put<T = unknown>(
headers, headers,
body: body !== undefined ? JSON.stringify(body) : undefined, body: body !== undefined ? JSON.stringify(body) : undefined,
}) })
if (!res.ok) { return parseJsonBody<T>(res)
throw new Error(`HTTP ${res.status}: ${res.statusText}`)
}
return res.json() as Promise<T>
} }
/** /**
@ -179,8 +198,5 @@ export async function httpDelete<T = unknown>(
headers, headers,
body: body !== undefined ? JSON.stringify(body) : undefined, body: body !== undefined ? JSON.stringify(body) : undefined,
}) })
if (!res.ok) { return parseJsonBody<T>(res)
throw new Error(`HTTP ${res.status}: ${res.statusText}`)
}
return res.json() as Promise<T>
} }

View File

@ -5,11 +5,23 @@ import App from './App.vue'
import router from './router' import router from './router'
import vuetify from './plugins/vuetify' import vuetify from './plugins/vuetify'
import { i18n } from './plugins/i18n' import { i18n } from './plugins/i18n'
import { setHttpUnauthorizedHandler } from './api/request'
import { useUserStore } from './stores/user'
const app = createApp(App) const app = createApp(App)
app.use(createPinia()) const pinia = createPinia()
app.use(pinia)
app.use(router) app.use(router)
setHttpUnauthorizedHandler(() => {
const userStore = useUserStore(pinia)
userStore.clearLocalSession()
if (router.currentRoute.value.name !== 'login') {
router.replace({ name: 'login' })
}
})
app.use(i18n) app.use(i18n)
app.use(vuetify) app.use(vuetify)

View File

@ -165,6 +165,11 @@ export const useUserStore = defineStore('user', () => {
// 忽略失败,仍执行本地登出 // 忽略失败,仍执行本地登出
} }
} }
clearLocalSession()
}
/** 仅清除本地登录态(如 HTTP 401不请求服务端黑名单 */
function clearLocalSession() {
disconnectUserSocket() disconnectUserSocket()
token.value = '' token.value = ''
user.value = null user.value = null
@ -235,6 +240,7 @@ export const useUserStore = defineStore('user', () => {
balance, balance,
setUser, setUser,
logout, logout,
clearLocalSession,
getAuthHeaders, getAuthHeaders,
fetchUsdcBalance, fetchUsdcBalance,
fetchUserInfo, fetchUserInfo,

View File

@ -1454,8 +1454,6 @@ async function updateChartData() {
ensureChartSeries() ensureChartSeries()
setChartData(data.value) setChartData(data.value)
nextTick(() => scheduleCryptoScrollToRealtime()) nextTick(() => scheduleCryptoScrollToRealtime())
cryptoWsUnsubscribe?.()
cryptoWsUnsubscribe = null
cryptoWsUnsubscribe = subscribeCryptoRealtime(sym, applyCryptoRealtimePoint) cryptoWsUnsubscribe = subscribeCryptoRealtime(sym, applyCryptoRealtimePoint)
} finally { } finally {
if (gen === cryptoLoadGeneration) { if (gen === cryptoLoadGeneration) {