Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | import axios from 'axios'
import { useAuthStore } from '@/stores/auth'
import { Message } from '@/components/ui/message/message-manager'
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
// 无 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 default http
|