Bootstrap

el-upload上传组件在vue中的应用和踩坑

需求:打开弹窗,点击按钮选择上传一个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,然后再在成功或失败后关闭它。

 

;