Files
workspace/code/fms-workflow-refactor-backup-20260923/http.js
T
2026-09-23 16:54:54 +08:00

125 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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