初始化
Some checks failed
Some checks failed
This commit is contained in:
297
frontend/src/utils/request.js
Normal file
297
frontend/src/utils/request.js
Normal file
@@ -0,0 +1,297 @@
|
||||
/**
|
||||
* 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
|
||||
Reference in New Issue
Block a user