关于element中自定义上传文件,el-upload的使用及避坑

17,623 阅读1分钟

引言

在我们的日常开发中,上传文件是一个很普遍的需求。这时候大家使用Vue的都会想到el-upload这个组件,但我们的需求中,经常都是需要上传多个文件,传参等。此时就需要我们来使用他之中的http-request来做一个自定义上传。

上传文件较简单,可直接观看完整代码部分,一看即懂。上传请求见下图:

image.png

传统Form表单提交

文件上传分很多种方式,el-upload这一组件的上传呢,实质上使用的是form表单提交,一般上传都是给后端传二进制文件即可。

image.png

<input type="file" id="upload">
console.log($("#upload").files);  =>  即可拿到一个包含二进制文件的数组。

但是这样的方式并不能满足我们的开发需求,他只能拿到该次上传的文件,且如果用户有增删文件或上传不符合规定的文件,都还需要我们进一步判断。

upload4.gif 所以呢,我们可以直接使用el-upload组件,对其进行自定义上传,并使用其已经封装好的钩子函数。

el-upload的自定义上传

在组件中使用http-request这一属性,赋值即为我们的上传函数。再配合组件中的on-change,on-remove,来拿到我们需要的File。

image.png

// 采用form表单的提交方式
let formData = new FormData();
this.upload_List.forEach((item, index) => {
  formData.append("file", FileXX); // 添加文件
});

因为append会添加一个新值到实例对象的一个已存在的键中,如果键不存在则会添加该键。
可以类似理解为=> file : [].push(xxx),所以无论是多个文件一次上传,或单个文件上传都可以使用该方式。

FormData.append()

1.避坑

在开发中,我发现: (1)当删除文件时,该文件的raw为object,不是file。 (2)用户二次上传文件时,on-change中的形参filelist第二次上传的文件的raw为object,也不是file。 所以,在on-change,on-remove中,我们不能拿filelist直接赋值,而是要进行一个新数组的增删子项处理。

upload2.gif

2.完整代码如下(包含了一些判断)

<el-upload
    id="upload"
    ref="upload"
    drag
    multiple
    :limit="100"
    :file-list="fileList"
    action="#"
    :http-request="Execute_File"
    :auto-upload="false"
    :on-change="upload_change"
    :on-remove="upload_remove"
    :on-exceed="upload_exceed"
    >
    <i class="el-icon-upload"></i>
    <div class="el-upload__text">
      将文件拖到此处,或
      <em>点击选取</em>
    </div>
</el-upload>

data(){
    return {
      fileList: [], //深拷贝,判断重名及第一步时的文件信息展示
      upload_List: [], //上传的file,
      notifyPromise: Promise.resolve() // 解决Notification组件的高度塌陷问题
    };
}

methods:{
    upload_change: function(file, fileList) {
      // 判断 > 1M
      if (file.size > 1048576) {
        fileList.pop();
        let msg_size = `您上传的${file.name},该文件大于1M,请您重新上传。`;
        this.notify_self(msg_size, "size");
        return false;
      }
      // 判断重名文件
      let repeat_judge = this.fileList.find(item => {
        return item.name == file.name;
      });
      if (repeat_judge) {
        fileList.pop();
        let msg_repeat = `您上传的${file.name},该文件有重名文件,请您重新上传。`;
        this.notify_self(msg_repeat, "repeat");
        return false;
      }
      this.fileList = JSON.parse(JSON.stringify(fileList));
      this.upload_List.push(file);
    },
    upload_remove(file, fileList) {
      this.fileList = JSON.parse(JSON.stringify(fileList));
      // 不直接赋值是因为打印出来的数据中,如果多个文件删至只剩一个时,该文件的raw为object,不是file
      this.upload_List.forEach((item, index) => {
        if (item.name == file.name) {
          this.upload_List.splice(index, 1);
        }
      });
    },
    upload_exceed(files, fileList) {
      this.$alert("您最多只能上传100个文件!", "上传文件", {
        confirmButtonText: "确定",
        type: "warning"
      });
    },
    notify_self(msg, type) {
      this.notifyPromise = this.notifyPromise.then(this.$nextTick).then(() => {
        this.$notify({
          title: `${type == "size" ? "文件大于1M" : "文件重名"}`,
          message: msg,
          iconClass: `${
            type == "size" ? "el-icon-s-opportunity" : "el-icon-message-solid"
          }`,
          customClass: `${type == "size" ? "notify_size" : "notify_repeat"}`,
          duration: 6000
        });
      });
    },
    Execute_File() {
        // 传输文件
        let formData = new FormData();
        this.upload_List.forEach((item, index) => {
            formData.append("file", item.raw);
        });
        $.axios({
            url:"XXX",
            method:"post",
            data: formData,
            params:{
                test1:"1",
                test2:"2"
            } //传参
        }).then(res = > { })
          .catch(res = > { })
    },
}

3.Notification组件。组件的高度塌陷的问题

插播一个题外话~ ,在上传文件判断时,如果不合规定我们是通过element的Notification组件来提示,但该组件是存在一个高度塌陷的问题的。

NG.gif

data(){
    notifyPromise: Promise.resolve()
},

this.notifyPromise = this.notifyPromise.then(this.$nextTick).then(() => {
    this.$notify({
      title: "111111",
      message: "22222",
    });
});

这个解决方法是百度得来的,嘿嘿, 原博客链接:element-ui Notification重叠问题,原因及解决办法 我的理解是通过Promise将其变为同步操作流程,通过this.$nextTick来获取更新后的dom状态,dom更新完之后,再执行Notification组件。

emmm 说实话,我没懂他这里这种语法糖的写法,在我的理解中,上面这种写法实际上是下方这个流程,但是我这样改写了之后,没起作用。有大哥理解的话,麻烦评论区留言,指导一下。

this.notifyPromise = new Promise((resolve, reject) => {
    resolve();
  })
    .then(() => {
      return new Promise((resolve, reject) => {
        this.$nextTick(() => {
          resolve();
        });
      });
    })
    .then(() => {
      this.$notify({
        title: "111111",
        message: "22222"
      });
    });

尾言

好人一生平安,给弟弟点个赞吧 ~