20260923165454
This commit is contained in:
1 parent
0abe3706c8
commit
e6935b8fa4
173 files changed
+14975
-2295
No files matched your search
@@ -0,0 +1,124 @@
|
||||
import axios from 'axios'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { Message } from '@/components/ui'
|
||||
|
||||
const AUTH_ERROR_CODE = 401
|
||||
const SESSION_REPLACED_CODE = 40101
|
||||
|
||||
const http = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
|
||||
timeout: 15000,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
let redirectingToLogin = false
|
||||
|
||||
function redirectToLogin(reason) {
|
||||
const currentPath = window.location.pathname.replace(/\/+$/, '') || '/'
|
||||
if (currentPath === '/login' || redirectingToLogin) return
|
||||
|
||||
redirectingToLogin = true
|
||||
const redirect = `${window.location.pathname}${window.location.search}${window.location.hash}`
|
||||
const query = new URLSearchParams({ redirect })
|
||||
if (reason) query.set('reason', reason)
|
||||
window.location.assign(`/login?${query.toString()}`)
|
||||
}
|
||||
|
||||
function isAuthenticationError(code) {
|
||||
return code === AUTH_ERROR_CODE || code === SESSION_REPLACED_CODE
|
||||
}
|
||||
|
||||
function handleUnauthorized(code) {
|
||||
const authStore = useAuthStore()
|
||||
authStore.clearSession()
|
||||
redirectToLogin(code === SESSION_REPLACED_CODE ? 'session-replaced' : 'expired')
|
||||
}
|
||||
|
||||
// 请求拦截器:注入 Bearer token(调用时读取 store,保证 pinia 已激活)
|
||||
http.interceptors.request.use((config) => {
|
||||
const authStore = useAuthStore()
|
||||
if (authStore.loginInfo.token) {
|
||||
config.headers.Authorization = `Bearer ${authStore.loginInfo.token}`
|
||||
}
|
||||
return config
|
||||
})
|
||||
|
||||
// 响应拦截器:统一按 ApiResult 结构处理
|
||||
http.interceptors.response.use(
|
||||
(response) => {
|
||||
const payload = response.data
|
||||
|
||||
// 文件下载(导出等):blob 响应没有 code 字段,原样透传响应本身,
|
||||
// 调用方才能读到 headers(Content-Disposition 里的文件名、content-type 用于区分错误 JSON)
|
||||
if (typeof Blob !== 'undefined' && payload instanceof Blob) {
|
||||
return response
|
||||
}
|
||||
|
||||
// 无 code 字段时按成功透传
|
||||
if (payload?.code === undefined) {
|
||||
return {
|
||||
success: true,
|
||||
code: 0,
|
||||
message: '',
|
||||
data: payload,
|
||||
}
|
||||
}
|
||||
|
||||
payload.success = payload.code === 0
|
||||
|
||||
if (payload.success) return response
|
||||
|
||||
if (isAuthenticationError(payload.code)) handleUnauthorized(payload.code)
|
||||
|
||||
const err = new Error(payload.message || '请求失败')
|
||||
err.code = payload.code
|
||||
err.data = payload.data
|
||||
err.success = payload.success
|
||||
throw err
|
||||
},
|
||||
(error) => {
|
||||
const status = error.response?.status
|
||||
const payload = error.response?.data
|
||||
|
||||
// 仅鉴权类错误(401 / 40101)才清会话并跳登录页;
|
||||
// 网络异常(无 status)、服务器 5xx 等普通错误不应把用户登出
|
||||
const errorCode = payload?.code ?? status ?? 0
|
||||
if (isAuthenticationError(errorCode)) handleUnauthorized(errorCode)
|
||||
|
||||
const message =
|
||||
payload?.message ||
|
||||
(error.code === 'ECONNABORTED' ? '请求超时,请稍后重试' : '网络异常,请检查服务是否正常')
|
||||
|
||||
const requestError = new Error(message)
|
||||
requestError.code = payload?.code ?? status ?? -1
|
||||
requestError.data = payload?.data
|
||||
requestError.success = false
|
||||
|
||||
// 服务端不可达(无 status)/ 5xx 属全局性问题,统一弹 Message 提示;
|
||||
// 4xx 等业务错误由调用方自行决定提示方式。
|
||||
// 打 toasted 标记:登录、上传/删除等已有自身 Message 的调用方据此去重,避免重复弹窗
|
||||
if (!status || status >= 500) {
|
||||
Message.error(message)
|
||||
requestError.toasted = true
|
||||
}
|
||||
|
||||
throw requestError
|
||||
},
|
||||
)
|
||||
|
||||
export function post(path, data, config) {
|
||||
return http.post(path, data, config).then((r) => r.data)
|
||||
}
|
||||
|
||||
export function put(path, data, config) {
|
||||
return http.put(path, data, config).then((r) => r.data)
|
||||
}
|
||||
|
||||
export function get(path, params, config) {
|
||||
return http.get(path, { params, ...config }).then((r) => r.data)
|
||||
}
|
||||
|
||||
export default http
|
||||
Reference in new issue
Block a user