// 通用下载方法
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
}
这一步是为了方便日后调用,可以省略
import {download} from '@/utils/download'
Vue.prototype.download = download
//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
}
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
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: `正在下载${item.path}" style="float: right">${item.progress}%
`, // 显示下载百分比,类名为进度对象的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
}
}
}
import {downloadProgress} from '@/mixins/downloadProgress.js'
export default {
name: 'mcu',
mixins:[downloadProgress],
data() {
return {}
}
...
}
downloadFile() {
let url = '***/***';
this.download(url, '', '下载进度测试');
}