需求:打开弹窗,点击按钮选择上传一个Excel文件,只能上传.xlsx格式文件,且不超过20M。
首先大致画出了界面,因为不需要显示已上传文件列表,所以就el-upload这个组件来说,省了不少麻烦事。dom部分直接上代码:
<el-dialog title="导入" :visible.sync="uploadView" @close="uploadView = false">
<div class="dialog-wrap">
<!-- 上传 -->
<el-upload
ref="upload"
accept=".xlsx"
:limit="1"
:action="actionUrl"
:data="{'empCode':empCode}"
:show-file-list="false"
:before-upload="beforeUpload"
:on-success="upSuccess"
:on-error="upError"
:on-progress="upProgress"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传.xlsx格式文件,且不超过20M</div>
</el-upload>
<!-- 下载 -->
<el-button ref="exportBtn" size="small" type="primary" @click="downloadBtn">
下载模板
</el-button>
</div>
<template slot="footer">
<el-button @click="uploadView = false">关闭</el-button>
</template>
</el-dialog>
大致分析一下属性:
ref="upload" 后面给清除上传历史记录提供定位(此处可见后面的踩坑一);
accept=".xlsx" 和 :limit="1" 自然是用来控制一次只能上传一个文件且格式只能是.xlsx;
:action="actionUrl" 是上传的路径(或者说是后台接受上传文件的接口),此处用在vue定义的data中属性actionUrl更灵活,方便我取当前域和端口再拼上后台给的接口名;
:data="{'empCode':empCode}" 是后台接口约定的需要传的额外参数,以Object形式传参;
:show-file-list="false" 隐藏掉文件上传列表;
:before-upload="beforeUpload", :on-success="upSuccess", :on-error="upError", :on-progress="upProgress",简单的说,就是在不同时间点触发的四个事件,具体解释可以看下官方的API:
接下来看下四个事件的分工:
before-upload 上传前触发:限制文件的格式和大小
on-success 上传成功后触发:成功事件+提示信息
on-error 上传失败后触发:提示信息
on-progress 上传过程中、上传成功或失败前触发:这里我用到它主要是想加一个loading效果
// 上传前校验文件格式(Excel)和文件大小(限制20M)
beforeUpload(file) {
const fileSuffix = file.name.substring(file.name.lastIndexOf('.') + 1)
const whiteList = ['xlsx']
if (whiteList.indexOf(fileSuffix) === -1) {
this.$message({ message: '只能上传.xlsx格式文件', type: 'warning' })
return false
}
const isLt2M = file.size / 1024 / 1024 > 20
if (isLt2M) {
this.$message({ message: '上传文件大小不能超过 20MB', type: 'warning' })
return false
}
},
// 上传成功
upSuccess(response) {
this.upCount = 0 // 控制过程事件的次数,每次成功都将计数清零
this.$loading(false) // 关闭遮幕loading效果
this.$refs.upload.clearFiles() // 上传成功之后清除历史记录
if (response.success) {
...
} else {
...
}
},
// 上传失败
upError(e) {
this.upCount = 0 // 控制过程事件的次数,每次失败都将计数清零
this.$refs.upload.clearFiles() // 上传成功之后清除历史记录
this.$loading(false) // 关闭遮幕loading效果
this.$notify({ title: '失败', message: '导入失败', type: 'error', duration: 3000 }) // 提示失败
},
// 上传过程
upProgress(e) {
this.upCount++ // 过程调用计数
if (this.upCount === 1) { // 保证上传过程中只会打开一个遮幕
this.$loading(true) // 显示遮幕loading效果
}
},
踩坑一:上传成功或失败后,再点击上传按钮没反应?
原因是没有清空上传的历史记录,故需要加一句 this.$refs.upload.clearFiles() 清空已上传的文件列表(该方法不支持在 before-upload 中调用)
踩坑二:弹窗里有上传和下载两个功能,一开始俩el-button都是写在el-upload里的,点击下载的时候,也会弹出文件选择框。
原因el-upload标签中只设计上传相关,故需要将下载按钮放到同el-upload外面
踩坑三:on-progress偶发性多次触发?
我调试了很多遍,原因还不清楚,在上传数据量较大的WPS表格文件时会偶发性地出现过程事件被调用了多次,同一个文件有时正常有时要跑两次,有时甚至双击选择文件和单机选中文件再点确定 也会影响过程事件的调用次数,这样就会出现先执行一次beforeUpload,然后upProgress执行两次,最后执行了一次upSuccess,很奇怪。。。因为我的过程事件是用来显示loading遮幕的,如果on-progress多次执行,页面上就会重叠多个遮幕,所以被逼无奈我只好采用计数的方法,只允许在第一次执行过程函数的时候打开一个loading,然后再在成功或失败后关闭它。