Files
it/frontend/src/utils/request.js

297 lines
6.7 KiB
JavaScript
Raw Normal View History

2025-11-03 19:47:36 +08:00
/**
* Axios请求工具
* 统一处理HTTP请求和响应
*/
import axios from 'axios'
import { ElMessage, ElLoading } from 'element-plus'
import { getToken, removeToken } from '@/utils/auth'
import router from '@/router'
import globalErrorHandler, { ErrorTypes } from './errorHandler'
// 创建axios实例
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
timeout: 15000, // 请求超时时间
headers: {
'Content-Type': 'application/json'
}
})
// 存储当前请求的loading实例
let loadingInstance = null
// 存储当前请求数量
let requestCount = 0
/**
* 显示loading
*/
const showLoading = () => {
if (requestCount === 0) {
loadingInstance = ElLoading.service({
lock: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)'
})
}
requestCount++
}
/**
* 隐藏loading
*/
const hideLoading = () => {
requestCount--
if (requestCount <= 0) {
requestCount = 0
if (loadingInstance) {
loadingInstance.close()
loadingInstance = null
}
}
}
/**
* 请求拦截器
*/
service.interceptors.request.use(
config => {
// 显示loading可选
if (config.showLoading !== false) {
showLoading()
}
// 添加token到请求头
const token = getToken()
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
// 添加请求ID用于追踪
config.headers['X-Request-ID'] = generateRequestId()
// 添加时间戳防止缓存
if (config.method === 'get') {
config.params = {
...config.params,
_t: Date.now()
}
}
return config
},
error => {
// 请求错误处理
hideLoading()
globalErrorHandler.handleError(error, {
type: ErrorTypes.NETWORK,
context: 'request_interceptor',
showMessage: true
})
return Promise.reject(error)
}
)
/**
* 响应拦截器
*/
service.interceptors.response.use(
response => {
// 隐藏loading
hideLoading()
// 获取响应数据
const res = response.data
// 根据后端约定的响应码处理
if (response.status === 200) {
// 如果响应中包含code字段根据code判断
if (res.code !== undefined) {
// 成功响应
if (res.code === 200 || res.code === 0) {
return res.data || res
}
// token过期或无效
else if (res.code === 401) {
ElMessage.error('登录已过期,请重新登录')
removeToken()
router.push('/login')
return Promise.reject(new Error('登录已过期'))
}
// 权限不足
else if (res.code === 403) {
ElMessage.error('权限不足')
return Promise.reject(new Error('权限不足'))
}
// 其他业务错误
else {
const message = res.message || '服务器响应错误'
ElMessage.error(message)
return Promise.reject(new Error(message))
}
}
// 如果没有code字段直接返回数据
else {
return res
}
} else {
// 处理非200状态码
return handleErrorResponse(response)
}
},
error => {
// 隐藏loading
hideLoading()
// 处理响应错误
if (error.response) {
// 服务器返回了响应但状态码不在2xx范围内
return handleErrorResponse(error.response)
} else if (error.request) {
// 请求已发出,但没有收到响应
globalErrorHandler.handleNetworkError(error, {
context: 'no_response',
showMessage: true
})
return Promise.reject(error)
} else {
// 请求配置出错
globalErrorHandler.handleError(error, {
type: ErrorTypes.NETWORK,
context: 'request_config',
showMessage: true
})
return Promise.reject(error)
}
}
)
/**
* 处理错误响应
* @param {Object} response - 响应对象
*/
function handleErrorResponse(response) {
const { status, data } = response
// 使用全局错误处理器处理API错误
globalErrorHandler.handleApiError(response, {
showMessage: true
})
return Promise.reject(new Error(data?.message || `请求失败 (${status})`))
}
/**
* 生成请求ID
*/
function generateRequestId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2)
}
/**
* 封装GET请求
* @param {string} url - 请求地址
* @param {Object} params - 请求参数
* @param {Object} config - 请求配置
*/
export function get(url, params = {}, config = {}) {
return service.get(url, {
params,
...config
})
}
/**
* 封装POST请求
* @param {string} url - 请求地址
* @param {Object} data - 请求数据
* @param {Object} config - 请求配置
*/
export function post(url, data = {}, config = {}) {
return service.post(url, data, config)
}
/**
* 封装PUT请求
* @param {string} url - 请求地址
* @param {Object} data - 请求数据
* @param {Object} config - 请求配置
*/
export function put(url, data = {}, config = {}) {
return service.put(url, data, config)
}
/**
* 封装DELETE请求
* @param {string} url - 请求地址
* @param {Object} config - 请求配置
*/
export function del(url, config = {}) {
return service.delete(url, config)
}
/**
* 封装上传文件请求
* @param {string} url - 请求地址
* @param {FormData} formData - 表单数据
* @param {Object} config - 请求配置
*/
export function upload(url, formData, config = {}) {
return service.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
...config
})
}
/**
* 封装下载文件请求
* @param {string} url - 请求地址
* @param {Object} params - 请求参数
* @param {string} filename - 下载文件名
*/
export function download(url, params = {}, filename = '') {
return service.get(url, {
params,
responseType: 'blob'
}).then(response => {
// 创建下载链接
const blob = new Blob([response])
const downloadUrl = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = downloadUrl
// 设置下载文件名
link.download = filename || `download_${Date.now()}`
// 触发下载
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
// 释放URL对象
window.URL.revokeObjectURL(downloadUrl)
return response
})
}
/**
* 取消请求
* @param {string} url - 请求地址
*/
export function cancelRequest(url) {
// 这里可以实现取消特定请求的逻辑
// 例如使用CancelToken或AbortController
}
/**
* 取消所有请求
*/
export function cancelAllRequests() {
// 这里可以实现取消所有请求的逻辑
// 例如存储所有请求的CancelToken或AbortController
}
export default service