request.js
import axios from 'axios'
import log from 'electron-log'
import { ElMessage, ElLoading } from 'element-plus' // 引入ElLoading
const errorCode = {
401: '认证失败,无法访问系统资源',
403: '当前操作没有权限',
404: '访问资源不存在',
default: '系统未知错误,请反馈给管理员'
}
// 配置基础URL
const isProduction = process.env.NODE_ENV === 'production'
const BASE_URL = isProduction ? 'http://192.168.0.147:8080' : 'http://192.168.0.147:8080'
// 创建axios实例
const service = axios.create({
baseURL: BASE_URL,
timeout: 10000
})
// 请求队列管理
const pendingRequests = new Map()
// 全局loading控制
let loadingInstance = null
let loadingCount = 0 // 请求计数器
/**
* 显示全局loading
*/
const showLoading = () => {
if (loadingCount === 0) {
loadingInstance = ElLoading.service({
lock: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)'
})
}
loadingCount++
}
/**
* 隐藏全局loading
*/
const hideLoading = () => {
loadingCount--
if (loadingCount <= 0) {
loadingInstance?.close()
loadingInstance = null
loadingCount = 0 // 重置计数器
}
}
/**
* 生成请求的唯一标识符
* @param {Object} config - 请求配置
* @returns {string} 唯一标识
*/
const generateRequestId = (config) => {
return `${config.method?.toUpperCase() || 'GET'}->${config.url}`
}
/**
* 添加请求到队列
* @param {Object} config - 请求配置
*/
const addPendingRequest = (config) => {
const requestId = generateRequestId(config)
// 如果已有相同请求,取消前一个
if (pendingRequests.has(requestId)) {
const abortController = pendingRequests.get(requestId)
abortController.abort(`重复请求: ${requestId}`)
pendingRequests.delete(requestId)
}
// 创建新的AbortController
const abortController = new AbortController()
config.signal = abortController.signal
pendingRequests.set(requestId, abortController)
}
/**
* 从队列中移除请求
* @param {Object} config - 请求配置
*/
const removePendingRequest = (config) => {
const requestId = generateRequestId(config)
if (pendingRequests.has(requestId)) {
pendingRequests.delete(requestId)
}
}
// 请求拦截器
service.interceptors.request.use(
(config) => {
addPendingRequest(config) // 管理重复请求
// 显示loading(排除特定请求)
if (config.showLoading !== false) {
showLoading()
}
// 从 localStorage 获取 token
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
config.headers['Access-Control-Allow-Origin'] = '*'
return config
},
(error) => {
// 请求错误时也需要隐藏loading
if (error.config?.showLoading !== false) {
hideLoading()
}
log.error('请求拦截错误:', error)
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
(res) => {
// 隐藏loading(排除特定请求)
if (res.config?.showLoading !== false) {
hideLoading()
}
// 未设置状态码则默认成功状态
const code = res.data.code || 200
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
removePendingRequest(res.config) // 清除已完成请求
// 处理blob类型的错误响应
if (res.config.responseType === 'blob') {
const contentType = res.headers['content-type'] || ''
if (contentType.includes('application/json')) {
return parseBlobError(res.data)
}
}
if (code === 500) {
ElMessage({ message: msg, type: 'error' })
return Promise.reject(new Error(msg))
} else if (code === 601) {
ElMessage({ message: msg, type: 'warning' })
return Promise.reject('error')
} else if (code !== 200) {
ElMessage({ message: msg, type: 'error' }) // 使用ElMessage替代Notification
return Promise.reject('error')
} else {
return res.data
}
},
(error) => {
// 隐藏loading(排除特定请求)
if (error.config?.showLoading !== false) {
hideLoading()
}
// 如果是取消的请求,不进行错误提示
if (axios.isCancel(error)) {
log.warn('请求已取消:', error.message)
return Promise.reject(error)
}
// 统一错误处理
let errorMessage = '请求失败'
if (error.response) {
errorMessage = `服务器错误: ${error.response.status}`
log.error(`${errorMessage} - URL: ${error.config.url}`, error)
} else if (error.request) {
errorMessage = '网络连接失败'
log.error(`${errorMessage} - URL: ${error.config.url}`)
} else {
log.error('未知请求错误:', error)
}
// 统一错误提示
let { message } = error
if (message == 'Network Error') {
message = '后端接口连接异常'
} else if (message.includes('timeout')) {
message = '系统接口请求超时'
} else if (message.includes('Request failed with status code')) {
message = '系统接口' + message.substr(message.length - 3) + '异常'
}
ElMessage({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error)
}
)
/**
* 解析Blob类型的错误响应
* @param {Blob} blob - 错误数据
* @returns {Promise<never>} 包含错误信息的Promise
*/
const parseBlobError = (blob) => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => {
try {
const text = new TextDecoder('utf-8').decode(reader.result)
const errorData = JSON.parse(text)
log.error(`Blob响应错误: ${errorData.msg || '未知错误'}`)
ElMessage({ message: errorData.msg || 'Blob解析错误', type: 'error' })
reject(new Error(errorData.msg || 'Blob解析错误'))
} catch (e) {
log.error('Blob解析失败:', e)
ElMessage({ message: '响应数据解析失败', type: 'error' })
reject(new Error('响应数据解析失败'))
}
}
reader.onerror = () => {
log.error('Blob读取失败')
reject(new Error('Blob读取失败'))
}
reader.readAsArrayBuffer(blob)
})
}
// 封装GET请求
export const get = (url, params, config = {}) => service.get(url, { ...config, params })
// 封装POST请求
export const post = (url, data, config = {}) => service.post(url, data, config)
/**
* 取消所有进行中的请求
*/
export const cancelAllRequests = () => {
pendingRequests.forEach((controller, requestId) => {
controller.abort(`手动取消: ${requestId}`)
log.info(`请求已取消: ${requestId}`)
})
pendingRequests.clear()
// 取消所有请求后重置loading
if (loadingInstance) {
loadingInstance.close()
loadingInstance = null
loadingCount = 0
}
}
/**
*
* 请求示例
* get/post方法签名,支持传递config配置
* 可通过 { showLoading: false } 禁用单个请求的loading
*
* */
// // 普通请求(显示loading)
// get('/api/data', { page: 1 })
// // 静默请求(不显示loading)
// get('/api/silent', null, { showLoading: false })
// // 带特殊配置的POST请求
// post('/api/submit', formData, {
// headers: { 'X-Custom': 'value' },
// showLoading: false // 禁用该请求的loading
// })
api > login.js
import { post } from '../request'
// 登录
export function login(data) {
return post('/login', data, {
showLoading: false
})
}
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。