Step1:封装download方法

// 通用下载方法
export function download(url, params, filename) {
  let downProgress = {}
  let uniSign = new Date().getTime() + '' // 可能会连续点击下载多个文件,这里用时间戳来区分每一次下载的文件
  return axios.post(url, params, {
    transformRequest: [(params) => {
      return tansParams(params)
    }],
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    responseType: 'blob',
    onDownloadProgress(progress) {
      // console.log(progress)
      // progress对象中的loaded表示已经下载的数量,total表示总数量,这里计算出百分比
      downProgress = Math.round(100 * progress.loaded / progress.total)
      // 将此次下载的文件名和下载进度组成对象再用vuex状态管理
      store.commit('downLoadProgress/SET_PROGRESS', { path: uniSign, 'progress': downProgress })
    }
  }).then((data) => {// 文件流传输完成后,开启文件下载
    const content = data
    const blob = new Blob([content])
    if ('download' in document.createElement('a')) {
      const elink = document.createElement('a')
      elink.download = filename || url.split('/')[url.split('/').length - 1]
      elink.style.display = 'none'
      elink.href = URL.createObjectURL(blob)
      document.body.appendChild(elink)
      elink.click()
      URL.revokeObjectURL(elink.href)
      document.body.removeChild(elink)
    } else {
      navigator.msSaveBlob(blob, filename)
    }
  }).catch((r) => {
    console.error(r)
    this.$message.error('该文件无法下载')
  })
}

/**
 * 参数处理
 * @param {*} params  参数
 */
export function tansParams(params) {
  let result = ''
  for (const propName of Object.keys(params)) {
    const value = params[propName];
    var part = encodeURIComponent(propName) + "=";
    if (value !== null && typeof (value) !== "undefined") {
      if (typeof value === 'object') {
        for (const key of Object.keys(value)) {
          if (value[key] !== null && typeof (value[key]) !== 'undefined') {
            let params = propName + '[' + key + ']';
            var subPart = encodeURIComponent(params) + "=";
            result += subPart + encodeURIComponent(value[key]) + "&";
          }
        }
      } else {
        result += part + encodeURIComponent(value) + "&";
      }
    }
  }
  return result
}

Step2:main.js中全局引入

这一步是为了方便日后调用,可以省略

import {download} from '@/utils/download'
Vue.prototype.download = download

Step3:加入Vuex,在store中添加downLoadProgress模块

1. 在store/modules下增加downLoadProgress.js文件

在这里插入图片描述

//downLoadProgress.js
const state = {
  progressList: [] // 文件下载进度列表
}

const mutations = {
  SET_PROGRESS: (state, progressObj) => { // 修改进度列表
    if (state.progressList.length) { // 如果进度列表存在
      if (state.progressList.find(item => item.path == progressObj.path)) { // 前面说的path时间戳是唯一存在的,所以如果在进度列表中找到当前的进度对象
        state.progressList.find(item => item.path == progressObj.path).progress = progressObj.progress // 改变当前进度对象的progress
      }
    } else {
      state.progressList.push(progressObj) // 当前进度列表为空,没有下载任务,直接将该进度对象添加到进度数组内
    }
  },
  DEL_PROGRESS: (state, props) => {
    state.progressList.splice(state.progressList.findIndex(item => item.path == props), 1) // 删除进度列表中的进度对象
  }
}
const actions = {}
export default {
  namespaced: true,
  state,
  mutations,
  actions
}

2.在store的index.js中添加downLoadProgress模块

import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import user from './modules/user'
import tagsView from './modules/tagsView'
import permission from './modules/permission'
import settings from './modules/settings'
import getters from './getters'
import downLoadProgress from './modules/downLoadProgress'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    app,
    user,
    tagsView,
    permission,
    settings,
    downLoadProgress
  },
  getters
})

export default store

Step4:在mixins文件夹下增加downloadProgress.js混入

src/mixins/downloadProgress.js

import { mapState } from 'vuex'

export const downloadProgress = {
  name: 'downLoadProgress',
  computed: {
    ...mapState({
      'progressList': state => state.downLoadProgress.progressList
    })
  },
  data() {
    return {
      notify: {} // 用来维护下载文件进度弹框对象
    }
  },
  watch: { // 监听进度列表
    progressList: {
      handler(n) {
        console.log(n)
        let data = JSON.parse(JSON.stringify(n))
        data.forEach(item => {
          const domList = [...document.getElementsByClassName(item.path)]
          if (domList.find(i => i.className == item.path)) { // 如果页面已经有该进度对象的弹框,则更新它的进度progress
            domList.find(i => i.className == item.path).innerHTML = item.progress + '%'
          } else {
            if (item.progress === null) { // 此处容错处理,如果后端传输文件流报错,删除当前进度对象
              this.$store.commit('downLoadProgress/DEL_PROGRESS', item.path)
              return
            }// 如果页面中没有该进度对象所对应的弹框,页面新建弹框,并在notify中加入该弹框对象,属性名为该进度对象的path(上文可知path是唯一的),属性值为$notify(element ui中的通知组件)弹框对象
            this.notify[item.path] = this.$notify.success({
              // title: 'Info',
              dangerouslyUseHTMLString: true,
              message: `<p style="width: 100px;">正在下载<span class="${item.path}" style="float: right">${item.progress}%</span></p>`, // 显示下载百分比,类名为进度对象的path(便于后面更新进度百分比)
              showClose: false,
              duration: 0
            })
          }
          console.log(item.progress + '%', '-------------------------->')

          if (item.progress == 100) { // 如果下载进度到了100%,关闭该弹框,并删除notify中维护的弹框对象
            this.notify[item.path].close()
            // delete this.notify[item.path] 上面的close()事件是异步的,这里直接删除会报错,利用setTimeout,将该操作加入异步队列
            setTimeout(() => {
              delete this.notify[item.path]
            }, 1000)
            this.$store.commit('downLoadProgress/DEL_PROGRESS', item.path)// 删除downLoadProgress中state的progressList中的进度对象
          }
        })
      },
      deep: true
    }
  }
}


Step5: 页面调用

1. 在需要下载的页面引入downloadProgress.js

import {downloadProgress} from '@/mixins/downloadProgress.js'
export default {
  name: 'mcu',
  mixins:[downloadProgress],
  data() {
  return {}
  }
  ...
}

2. 调用方法

downloadFile() {
      let url = '***/***';
      this.download(url, '', '下载进度测试');
}

页面效果

在这里插入图片描述

Logo

前往低代码交流专区

更多推荐