2025-01-16 07:40:06 +00:00
|
|
|
import { useRuntimeConfig } from '#app'
|
2025-01-22 03:24:41 +00:00
|
|
|
import { ofetch} from 'ofetch'
|
|
|
|
import { message } from '@/components/x-message/useMessage.js'
|
|
|
|
import { authStore } from "@/stores/auth/index.js"
|
2025-01-16 07:40:06 +00:00
|
|
|
|
2025-01-22 03:24:41 +00:00
|
|
|
let httpStatusErrorHandler
|
2025-01-16 08:18:38 +00:00
|
|
|
let http
|
2025-01-16 07:40:06 +00:00
|
|
|
|
2025-01-22 03:24:41 +00:00
|
|
|
// HTTP 状态码映射
|
|
|
|
const HTTP_STATUS_MAP = {
|
|
|
|
400: '请求参数错误',
|
|
|
|
401: '未授权或登录过期',
|
|
|
|
403: '访问被禁止',
|
|
|
|
404: '请求的资源不存在',
|
|
|
|
500: '服务器内部错误',
|
|
|
|
502: '网关错误',
|
|
|
|
503: '服务暂时不可用',
|
|
|
|
504: '网关超时'
|
|
|
|
}
|
|
|
|
|
2025-01-16 07:40:06 +00:00
|
|
|
export function setupHttp() {
|
2025-01-22 03:24:41 +00:00
|
|
|
if (http) return http
|
2025-01-16 07:40:06 +00:00
|
|
|
|
|
|
|
const config = useRuntimeConfig()
|
2025-01-16 08:18:38 +00:00
|
|
|
const baseURL = config.public.NUXT_PUBLIC_API_BASE
|
2025-01-22 03:24:41 +00:00
|
|
|
const { token } = authStore()
|
2025-01-21 03:43:27 +00:00
|
|
|
const router = useRouter()
|
2025-01-22 03:24:41 +00:00
|
|
|
|
|
|
|
const defaultOptions = {
|
2025-01-16 07:40:06 +00:00
|
|
|
baseURL,
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
2025-01-22 03:24:41 +00:00
|
|
|
timeout: 15000, // 15秒超时
|
|
|
|
retry: 3,
|
|
|
|
retryDelay: 1000,
|
|
|
|
}
|
|
|
|
|
|
|
|
http = ofetch.create({
|
|
|
|
...defaultOptions,
|
|
|
|
|
|
|
|
// 请求拦截
|
|
|
|
async onRequest({ options, request }) {
|
|
|
|
// 添加 token
|
2025-01-16 07:40:06 +00:00
|
|
|
options.headers = {
|
|
|
|
...options.headers,
|
2025-01-22 03:24:41 +00:00
|
|
|
Authorization: token.value
|
|
|
|
}
|
|
|
|
|
|
|
|
// GET 请求添加时间戳防止缓存
|
|
|
|
if (request.toLowerCase().includes('get')) {
|
|
|
|
options.params = {
|
|
|
|
...options.params,
|
|
|
|
_t: Date.now()
|
|
|
|
}
|
2025-01-16 07:40:06 +00:00
|
|
|
}
|
|
|
|
},
|
2025-01-22 03:24:41 +00:00
|
|
|
|
|
|
|
// 响应拦截
|
2025-01-16 08:18:38 +00:00
|
|
|
async onResponse({ response }) {
|
2025-01-22 03:24:41 +00:00
|
|
|
const data = response._data
|
|
|
|
|
|
|
|
// 处理业务错误
|
|
|
|
if (data.status === 1) {
|
|
|
|
message.error(data.msg || '操作失败')
|
|
|
|
return Promise.reject(new Error(data.msg))
|
|
|
|
}
|
|
|
|
|
|
|
|
// 处理登录失效
|
|
|
|
if (data.status === 401) {
|
|
|
|
message.error('登录已过期,请重新登录')
|
|
|
|
token.value = '' // 清除 token
|
|
|
|
router.replace('/login')
|
|
|
|
return Promise.reject(new Error('未登录或登录已过期'))
|
|
|
|
}
|
|
|
|
|
|
|
|
return response
|
2025-01-16 08:18:38 +00:00
|
|
|
},
|
2025-01-22 03:24:41 +00:00
|
|
|
|
|
|
|
// 响应错误处理
|
|
|
|
async onResponseError({ response, request }) {
|
|
|
|
// 网络错误
|
|
|
|
if (!response) {
|
|
|
|
message.error('网络连接失败,请检查网络设置')
|
|
|
|
return Promise.reject(new Error('网络错误'))
|
2025-01-16 07:40:06 +00:00
|
|
|
}
|
2025-01-22 03:24:41 +00:00
|
|
|
const status = response.status
|
|
|
|
const data = response._data
|
|
|
|
|
|
|
|
// 处理 HTTP 状态错误
|
|
|
|
const errorMessage = data.msg || HTTP_STATUS_MAP[status] || '请求失败'
|
|
|
|
|
|
|
|
if (Array.isArray(data.msg)) {
|
|
|
|
data.msg.forEach(item => {
|
|
|
|
httpStatusErrorHandler?.(item, status)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
httpStatusErrorHandler?.(errorMessage, status)
|
2025-01-16 07:40:06 +00:00
|
|
|
}
|
2025-01-22 03:24:41 +00:00
|
|
|
|
|
|
|
message.error(errorMessage)
|
|
|
|
return Promise.reject(data)
|
2025-01-16 07:40:06 +00:00
|
|
|
},
|
|
|
|
})
|
2025-01-22 03:24:41 +00:00
|
|
|
|
|
|
|
return http
|
|
|
|
}
|
|
|
|
|
|
|
|
export function createAbortController() {
|
|
|
|
return new AbortController()
|
2025-01-16 07:40:06 +00:00
|
|
|
}
|
|
|
|
|
2025-01-16 08:18:38 +00:00
|
|
|
export function injectHttpStatusErrorHandler(handler) {
|
2025-01-16 07:40:06 +00:00
|
|
|
httpStatusErrorHandler = handler
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getHttp() {
|
|
|
|
if (!http) {
|
|
|
|
throw new Error('HTTP client not initialized. Call setupHttp first.')
|
|
|
|
}
|
|
|
|
return http
|
|
|
|
}
|
2025-01-22 03:24:41 +00:00
|
|
|
|
|
|
|
// 导出请求工具函数
|
|
|
|
export async function request(url, options) {
|
|
|
|
const http = getHttp()
|
|
|
|
try {
|
|
|
|
const response = await http(url, options)
|
|
|
|
return response.data
|
|
|
|
} catch (error) {
|
|
|
|
throw error
|
|
|
|
}
|
|
|
|
}
|