axios+electron-log封装

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辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

友情链接更多精彩内容