zhanghua
2022-11-03 d62876d53574eae2480c350a5251e0bb777e279e
http 请求重新封装
1个文件已修改
4个文件已添加
445 ■■■■■ 已修改文件
src/api/intelligentPatrol/statistics.js 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/http/index.js 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/http/interceptor-handler.js 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/http/url-recorder.js 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/loading.js 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/intelligentPatrol/statistics.js
@@ -1,21 +1,13 @@
import axios from "axios";
import {
    getToken
} from '@/utils/helper'
import http from '@/http'
const baseUrl = '/sccg/intelligentPatrol/statistics'
const token = {
    'Authorization': getToken()
}
export default {
    // 按违规类型统计
    searchByType: (data) => axios({
        method: 'get',
        url: baseUrl + '/unlawful/type',
        headers: {
            ...token
    searchByType: (params) => {
        return http.get('/sccg/intelligentPatrol/statistics/unlawful/type', params);
        },
        params: data
    }),
    // 按点位统计
    searchByPoint: (data) => axios({
        method: 'get',
src/http/index.js
New file
@@ -0,0 +1,218 @@
import axios from 'axios'
import {
  handleResponseSuccess,
  handleResponseFail,
  handleRequest
} from './interceptor-handler'
import {
  showFullScreenLoading
} from '../utils/loading'
// import { getStorage } from '@utils/storage'
// URL 记录器
import urlRecorder from './url-recorder'
const axiosInstance = axios.create({
  // baseURL: `${process.env.VUE_APP_API_BASE_URL}`,
  timeout: 1000 * 60 * 5
})
axiosInstance.interceptors.request.use(handleRequest)
axiosInstance.interceptors.response.use(handleResponseSuccess, handleResponseFail)
/**
 * 在 get 请求的 url 后面加个随机参数,以防浏览器缓存请求
 * @param {string} url
 * @return {string}
 */
function addVersion(url) {
  return url.includes('?') ? `${url}&v=${Date.now()}` : `${url}?v=${Date.now()}`
}
export default {
  // 默认的异常处理方法,会传入完整的data对象,可以在这里弹提示框
  defaultErrorHandler: null,
  /**
   * 通过get发送并接收json格式的数据(get发的本来就是json格式)。
   * 并统一处理常见的错误
   * @param {string} url
   * @param {object?} params={}
   * @param {boolean?} throwError 是否不使用默认的异常处理方法,而把异常抛出来
   * @param {int?} timeout 超时时间,默认10秒
   * @return {Promise} 返回一个promise对象。其中then方法传递回包中的data数据;catch事件则传递整个回包,其参数为{data:{},status{code:123,message:'xxx'}}
   */
  get(url, params = {}, throwError, timeout) {
    const config = {
      method: 'GET',
      url: addVersion(url),
      params,
      errorHandler: (!throwError && this.defaultErrorHandler) || null,
      // headers: { 'Content-Type': 'application/json', token: thisToken },
      headers: {
        'Content-Type': 'application/json'
      },
      withCredentials: this.withCredentials,
      timeout: timeout
    }
    if (params.showLoading === undefined) {
      showFullScreenLoading()
    }
    return axiosInstance(config)
  },
  /**
   * 通过post发送数据,使后端直接收到json格式的数据。并统一处理常见的错误
   * @param {string} url
   * @param {object?} data={}
   * @param {boolean?} throwError 是否不使用默认的异常处理方法,而把异常抛出来
   * @param {int?} timeout 超时时间,默认10秒
   * @return {Promise} 返回一个promise对象。其中then方法传递回包中的data数据;catch事件则传递整个回包,其参数为{data:{},status{code:123,message:'xxx'}}
   */
  post(url, data = {}, throwError) {
    const config = {
      method: 'POST',
      url,
      data: JSON.stringify(data),
      errorHandler: (!throwError && this.defaultErrorHandler) || null,
      headers: {
        'Content-Type': 'application/json'
      },
      withCredentials: this.withCredentials
    }
    urlRecorder.add(config)
    if (data.showLoading === undefined) {
      showFullScreenLoading()
    }
    return axiosInstance(config)
  },
  // PUT更新数据
  put(url, data = {}, throwError) {
    const config = {
      method: 'PUT',
      url,
      data: JSON.stringify(data),
      errorHandler: (!throwError && this.defaultErrorHandler) || null,
      headers: {
        'Content-Type': 'application/json'
      },
      withCredentials: this.withCredentials
    }
    urlRecorder.add(config)
    if (data.showLoading === undefined) {
      showFullScreenLoading()
    }
    return axiosInstance(config)
  },
  // DELETE更新数据
  delete(url, data = {}, throwError) {
    const config = {
      method: 'delete',
      url,
      data: JSON.stringify(data),
      errorHandler: (!throwError && this.defaultErrorHandler) || null,
      headers: {
        'Content-Type': 'application/json'
      },
      withCredentials: this.withCredentials
    }
    urlRecorder.add(config)
    if (data.showLoading === undefined) {
      showFullScreenLoading()
    }
    return axiosInstance(config)
  },
  /**
   * 通过表单get下载文件。并统一处理常见的错误
   * @param {string} url
   * @param {String} fileName 文件名
   * @param {Object?} data 上传进度回调,参数为event
   * @param {Function?} throwError 是否不使用默认的异常处理方法,而把异常抛出来
   * @return {Promise} 返回一个promise对象
   */
  downloadFile(url, params, data = {}, throwError) {
    const {
      fileName,
      type
    } = params
    return axiosInstance({
      method: 'GET',
      url,
      responseType: 'arraybuffer',
      data: JSON.stringify(data),
      errorHandler: (!throwError && this.defaultErrorHandler) || null,
      timeout: 50000,
      headers: {
        'Content-Type': 'application/json'
      }
    }).then(response => {
      const blob = new Blob([response], {
        type
      }) // 不兼容type
      // 利用a标签实现下载
      const link = document.createElement('a')
      link.style.display = 'none'
      link.setAttribute('type', MimeType) // 并不支持
      const downUrl = window.URL.createObjectURL(blob)
      link.href = downUrl
      // 添加到浏览器为了兼容 firefox
      document.body.appendChild(link)
      // 为了兼容qq浏览器,fileName中必须加上文件后缀
      link.download = fileName
      link.click()
      document.body.removeChild(link)
    })
  },
  /**
   * 通过a标签打开文件
   * @param {string} url
   */
  downloadFileByA(url) {
    // const {fileName} = params
    // 利用a标签实现下载
    const link = document.createElement('a')
    link.style.display = 'none'
    link.href = `${process.env.VUE_APP_DOWN_FILE}${url}`
    // 添加到浏览器为了兼容 firefox
    document.body.appendChild(link)
    // 为了兼容qq浏览器,fileName中必须加上文件后缀
    // link.download = fileName
    link.click()
    document.body.removeChild(link)
    return Promise.resolve()
  },
  /**
   * 通过a标签下载文件
   * @param {string} url
   */
  downloadFileByAtag(url) {
    // 利用a标签实现下载
    const link = document.createElement('a')
    link.download = url
    link.target = '_blank'
    link.style.display = 'none'
    link.href = url
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
    return Promise.resolve()
  },
  /**
   * 通过表单post上传文件并接收json格式的数据。并统一处理常见的错误
   * @param {string} url
   * @param {FormData|object} formElem FormData对象,或form Dom元素,其中需要含有一个name为files的选择文件的input元素
   * @param {Function?} onUploadProgress 上传进度回调,参数为event
   * @param {boolean?} throwError 是否不使用默认的异常处理方法,而把异常抛出来
   * @param {int?} timeout 超时时间,默认50秒
   * @return {Promise} 返回一个promise对象。其中then方法传递回包中的data数据;catch事件则传递整个回包,其参数为{data:{},status{code:123,message:'xxx'}}
   */
  uploadFile(url, formElem, onUploadProgress, throwError) {
    return axiosInstance({
      method: 'POST',
      url,
      data: formElem instanceof FormData ? formElem : new FormData(formElem),
      onUploadProgress,
      errorHandler: (!throwError && this.defaultErrorHandler) || null,
      timeout: 500000,
      enableRepeat: true
    })
  }
}
src/http/interceptor-handler.js
New file
@@ -0,0 +1,106 @@
// URL 记录器
import Vue from 'vue'
import urlRecorder from './url-recorder'
import {
  tryHideFullScreenLoading
} from '../utils/loading'
/**
 * 对成功返回的请求回包进行数据预处理
 * @param response
 * @returns {Promise}
 */
export function handleResponseSuccess(response) {
  debugger
  tryHideFullScreenLoading()
  urlRecorder.remove(response.config)
  const result = response.data
  // return result.content
  if (result.code === 200) {
    return result.data || result.content
  } else if (result.code && result.code !== 0) {
    return handleError(response.config, result)
  } else if (result === '') {
    Vue.prototype.$message({
      type: 'error',
      message: '服务内部错误'
    })
    return result
  } else {
    // 来自其它服务器的请求
    return result
  }
}
/**
 * 对发送失败的请求进行数据预处理,将error对象封装为统一的形式
 * @param error
 * @returns {Promise}
 */
export function handleResponseFail(error) {
  tryHideFullScreenLoading()
  urlRecorder.remove(error.config)
  let result
  if (error.response) {
    // 请求已发送,响应中返回了非2xx的错误码,包括304等
    result = {
      data: error.response.data,
      msg: error.response.statusText
    }
  } else if (error.request) {
    // 请求没有发送成功时的错误
    result = {
      data: error.request,
      msg: error.message
    }
  } else {
    // 设置请求时出错
    result = {
      msg: error.message
    }
  }
  return handleError(error.config, result)
}
/**
 * 统一的异常对象封装逻辑,在这里抛出异常
 * @param {object} requestConfig 发请求时传入axios的配置信息对象
 * @param {object|Error} result 请求回包对象,或异常信息
 * @param {object} result.content
 * @param {number} result.code
 * @param {string} result.msg
 * @returns {Promise}
 */
export function handleError(requestConfig, result) {
  // 必须是Error对象,否则throw时vuex要报warning
  // let err
  // if (result instanceof Error) {
  //   err = result
  // } else {
  //   err = new Error(result.msg)
  //   err.content = result.content
  //   err.code = result.code
  //   err.msg = result.msg
  // }
  if (requestConfig && requestConfig.errorHandler) {
    result.url = requestConfig.url
    requestConfig.errorHandler(result)
    result.processed = true
  }
  return Promise.reject(result)
}
/**
 * 请求拦截器,添加用户token
 */
export function handleRequest(config) {
  const token = sessionStorage.getItem('token');
  const tokenHead = sessionStorage.getItem('tokenHead');
  if (token && tokenHead) {
    // 这里经常搭配token使用,将token值配置到tokenkey中,将tokenkey放在请求头中
    config.headers['Authorization'] = tokenHead + token
  }
  return config
}
src/http/url-recorder.js
New file
@@ -0,0 +1,54 @@
// 记录正在发送中的请求,以判断是否重复发包
export default {
  // 正在进行中的请求列表
  urlList: [],
  /**
     * 添加一个新的请求记录,如果已经存在进行中的请求了,就抛出异常
     * @param {string} config
     * @param {string} config.url 请求地址
     * @param {string} config.enableRepeat 允许同时发送多个相同请求
     * @return {Promise}
     */
  add(config) {
    // 允许同时发送多个相同请求
    // 比如批量上传图片的时候
    if (config.enableRepeat) return Promise.resolve()
    const url = config.url
    // 如果请求还在队列里,说明还在处理,没有回包
    if (this.urlList.includes(url)) {
      const errorMessage = `请求 ${url} 正在处理,请稍后再试!`
      console.error(errorMessage)
      const error = {
        status: {
          code: 1007, // 临时写的
          debugMessage: url,
          // eslint-disable-next-line no-use-before-define
          message: error.message
        }
      }
      return Promise.reject(error)
    }
    // 这里没有前缀,但是remove中有
    this.urlList.push(`${process.env.VUE_APP_API_BASE_URL}${url}`)
    return Promise.resolve()
  },
  /**
     * 删除一个正在进行的请求记录
     * @param {string} config
     * @param {string} config.url 请求地址
     */
  remove(config) {
    // 允许同时发送多个相同请求
    if (config.enableRepeat) return
    const index = this.urlList.indexOf(config.url)
    if (index >= 0) {
      this.urlList.splice(index, 1)
    }
  }
}
src/utils/loading.js
New file
@@ -0,0 +1,47 @@
import { Loading } from 'element-ui'
/** 定义loading变量 */
let loading
/** 使用Element loading-start 方法 */
function startLoading() {
  loading = Loading.service({
    lock: true,
    text: '加载中……',
    background: 'rgba(0, 0, 0, 0.7)'
  })
}
/** 使用Element loading-close 方法 */
function endLoading() {
  loading.close()
}
/**
 * 那么 showFullScreenLoading() tryHideFullScreenLoading() 要干的事儿就是将同一时刻的请求合并。
 * 声明一个变量 needLoadingRequestCount,每次调用showFullScreenLoading方法 needLoadingRequestCount + 1。
 * 调用tryHideFullScreenLoading()方法,needLoadingRequestCount - 1。needLoadingRequestCount为 0 时,结束 loading。
 */
let needLoadingRequestCount = 0
/** 显示Loading */
export function showFullScreenLoading() {
  if (needLoadingRequestCount === 0) {
    startLoading()
  }
  needLoadingRequestCount++
}
/** 隐藏Loading */
export function tryHideFullScreenLoading() {
  if (needLoadingRequestCount <= 0) return
  needLoadingRequestCount--
  if (needLoadingRequestCount === 0) {
    endLoading()
  }
}
export default {
  showFullScreenLoading,
  tryHideFullScreenLoading
}