20260923165454

This commit is contained in:
oneao committed 2026-09-23 16:54:54 +08:00
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