vue上传组件封装

分享个工作中用到的vue上传组件,用的是iview框架,但是iview的上传不支持取消上传,索性就自己重新写一个。

该组件支持上传,停止,重新上传的方法,并且支持上传百分比,计算上传剩余时间这些,话不多说,先上图一张了解一下:


下面这里就是源码和使用的demo了:

1.upload.vue

<style scoped>
.ivu-upload {
    display: inline-block;
}
</style>
<template>
  <div :class="uploadClass" @click="handleClick" @drop.prevent="onDrop" @dragover.prevent="dragOver = true" @dragleave.prevent="dragOver = false">
    <input ref="fileInput" type="file" style="display:none;" :class="[prefixClass + '-input']" @change="handleChange" :name="name" :multiple="multiple" :accept="accept">
    <slot></slot>
  </div>
</template>
<script>
const prefixClass = "ivu-upload";
export default {
  data() {
    return {
      prefixClass: prefixClass,
      dragOver: false,
      fileList: [], // 上传的文件队列
      uploadPool: [], // 上传文件对象池,做取消和暂停用
      fileIndex: 1 // 多个文件选择,有先后顺序

    };
  },
  props: {
    // 名称
    name: {
      type: String,
      default: "file"
    },
    // 文件上传的请求头部
    headers: {
      type: Object
    },
    // 文件上传附带参数
    data: {
      type: Object
    },
    // 上传控件的类型,可选值为 select(点击选择),drag(支持拖拽)
    type: {
      type: String,
      default: "select"
    },
    // 文件上传的地址
    action: {
      type: String
    },
    // 是否可以多文件选择
    multiple: {
      type: Boolean,
      default: false
    },
    // 可以接受的文件格式
    accept: {
      type: String
    },
    // 上传超时时间,默认0,不限时
    timeout: {
      type: Number,
      default: 0
    },
    // 最大文件大小,字节KB
    maxSize: {
      type: Number
    },
    // 上传文件个数,默认10个
    uploadSize: {
      type: Number,
      default: 10
    },
    // 可接受的文件后缀名
    format: {
      type: Array,
      default() {
        return [];
      }
    },
    // 上传之前钩子
    onBefore: {
      type: Function,
      default() {
        return true;
      }
    },
    // 上传进度钩子
    onProgress: {
      type: Function,
      default() {
        return {};
      }
    },
    // 上传成功钩子
    onSuccess: {
      type: Function,
      default() {
        return {};
      }
    },
    // 上传失败钩子
    onError: {
      type: Function,
      default() {
        return {};
      }
    },
    // 上传文件格式错误
    onFormatError: {
      type: Function,
      default() {
        return {};
      }
    },
    // 上传文件大小超过限制
    onExceedSize: {
      type: Function,
      default() {
        return {};
      }
    },
    // 上传请求超时
    onTimeout: {
      type: Function,
      default() {
        return {};
      }
    }
  },
  components: {},
  created() { },
  mounted() { },
  computed: {
    uploadClass() {
      return prefixClass;
    }
  },
  methods: {
    /**
     * 点击按钮选择文件
     */
    handleClick() {
      this.$refs.fileInput.click();
    },
    /**
     * 选择文件
     */
    handleChange(e) {
      let files = e.target.files;
      if (!files) return;

      // 文件列表
      let uploadList = Array.prototype.slice.call(files);
      // 判断上传文件的个数
      if (uploadList.length > this.uploadSize) {
        this.$Message.warning("最多上传" + this.uploadSize + "个文件!");
        return;
      }
      if (!this.multiple) uploadList = uploadList.slice(0, 1);
      if (uploadList.length === 0) return;
      uploadList.forEach(file => {
        this.handleBeforeUpload(file);
      });
      this.$refs.fileInput.value = null;
    },
    /**
     * 上传文件之前钩子
     */
    handleBeforeUpload(file) {

      // 选择文件上传之前设置文件的基本信息
      file.uid = Date.now() + this.fileIndex++;
      file.status = "waiting";
      file.percent = 0;

      // 将上传的文件存入到文件列表
      this.fileList.push(file);

      // onBefore钩子返回false,则停止上传
      if (!this.onBefore) {
        return this.handleValidate(file);
      }
      // 赋值一份file返回到before
      let _file = {
        uid: file.uid,
        name: file.name,
        percent: 0,
        status: file.status,
        size: file.size,
        lastModified: file.lastModified,
        lastModifiedDate: file.lastModifiedDate
      };

      const before = this.onBefore(_file);
      if (before && before.then) {
        before.then(processedFile => {
          if (
            Object.prototype.toString.call(processedFile) === "[object File]"
          ) {
            this.handleValidate(processedFile);
          } else {
            this.handleValidate(file);
          }
        });
      } else if (before !== false) {
        this.handleValidate(file);
      }
    },
    /**
     * 上传的文件校验
     */
    handleValidate(file) {
      var flag = true;
      // 上传验证大小

      if (this.maxSize) {

        if (file.size > this.maxSize * 1024) {
          flag = false;
          this.onExceedSize(file, this.fileList);
          return false;
        }
      }
      // 上传验证格式
      if (this.format.length) {
        const fileFormat = file.name
          .split(".")
          .pop()
          .toLocaleLowerCase();
        const checked = this.format.some(
          item => item.toLocaleLowerCase() == fileFormat
        );
        if (!checked) {
          flag = false;
          this.onFormatError(file, this.fileList);
          return false;
        }
      }
      // 验证文件通过,开始上传文件
      if (flag) {
        this.handlePost(file);
      }
    },
    /**
     * 上传文件请求
     */
    handlePost(file) {
      let that = this;
      let formData = new FormData();
      let xmlHttp = new XMLHttpRequest();
      var ot; //上传开始时间
      var oloaded; // 已经上传的文件大小

      // 将上传对象放到上传队列池里
      this.uploadPool.push({
        uid: file.uid,
        formData: formData,
        xmlHttp: xmlHttp
      });

      // 验证通过,可以上传
      formData.append(this.name, file);

      //判断附带参数不为空
      if (this.data) {
        Object.keys(this.data).map(key => {
          formData.append(key, this.data[key]);
        });
      }
      if (typeof XMLHttpRequest == "undefined") return;
      if (xmlHttp) {
        // 设置请求头
        if (this.headers) {
          Object.keys(this.headers).map(key => {
            xmlHttp.setRequestHeader(key, this.headers[key]);
          });
        }
        // 设置请求超时时间
        xmlHttp.timeout = this.timeout;
        // 初始化上传请求
        xmlHttp.open("post", this.action, true);

        // 获得上传信息
        if (xmlHttp) {
          // 上传开始
          xmlHttp.loadstart = function (e) {
            file.status = "starting";
            file.percent = 0;
            // 计算上传剩余时间
            ot = new Date().getTime(); //设置上传开始时间
            oloaded = 0; //设置上传开始时,以上传的文件大小为0
          };
          // 请求超时处理
          xmlHttp.ontimeout = function (e) {
            that.onTimeout(e, file);
          };
          // 上传进度
          xmlHttp.upload.onprogress = function (e) {
            console.log(e);
            file.status = "uploading";
            if (e.total > 0) {
              file.percent = e.loaded / e.total * 100;
            }

            // 计算上传剩余时间
            let nt = new Date().getTime(); //获取当前时间
            let pertime = (nt - ot) / 1000; //计算出上次调用该方法时到现在的时间差,单位为s
            ot = new Date().getTime(); //重新赋值时间,用于下次计算
            let perload = e.loaded - oloaded; //计算该分段上传的文件大小,单位b
            oloaded = e.loaded; //重新赋值已上传文件大小,用以下次计算
            //上传速度计算
            let speed = perload / pertime; //单位b/s
            let bspeed = speed;
            let units = "b/s"; //单位名称
            if (speed / 1024 > 1) {
              speed = speed / 1024;
              units = "k/s";
            }
            if (speed / 1024 > 1) {
              speed = speed / 1024;
              units = "m/s";
            }
            speed = speed.toFixed(1);
            //剩余时间
            let resttime = ((e.total - e.loaded) / bspeed).toFixed(1);
            if (isNaN(speed)) {
              speed = 0;
            }
            if (isNaN(resttime)) {
              resttime = 0;
            }
            // 调用进度方法
            that.onProgress(
              file,
              that.fileList,
              speed + units,
              resttime + "秒"
            );
          };
          // 上传成功
          xmlHttp.onload = function (e) {
            console.log(xmlHttp);
            if (this.status === 200 && this.readyState === 4) {
              // 上传成功,设置成功的信息
              file.status = "finished";
              file.percent = 100;
              that.onSuccess(
                JSON.parse(this.responseText),
                file,
                that.fileList
              );
            }
          };
          // 上传失败
          xmlHttp.onerror = function (e) {
            console.log("上传失败", this.responseText);
            file.percent = 100;
            file.status = "error";
            that.onError(JSON.parse(xmlHttp.responseText), file);
          };
        }
        // 发送数据
        xmlHttp.send(formData);
      }
    },
    /**
     * 重新开始上传
     */
    onRestart(file) {
      var that = this;
      this.uploadPool.forEach(function (v, i) {
        if (v.uid == file.uid) {
          if (v.xmlHttp) {
            v.xmlHttp.open("post", that.action, true);
            v.xmlHttp.send(v.formData);
          }
        }
      });
    },
    /**
     * 暂停上传
     */
    onPause(file) { },
    /**
     * 继续上传
     */
    onContinue(file) {
      var that = this;
      this.uploadPool.forEach(function (v, i) {
        if (v.uid == file.uid) {
          if (v.xmlHttp) {
            v.xmlHttp.open("post", that.action, true);
            v.xmlHttp.send(v.formData);
          }
        }
      });
    },
    /**
     * 取消上传
     */
    onCancel(file) {
      console.log("onCancel", file);
      this.uploadPool.forEach(function (v, i) {
        if (v.uid == file.uid) {
          v.xmlHttp && v.xmlHttp.abort();
        }
      });
    }
  }
};
</script>

2.uploadDemo.vue,使用的方法附上

<style scoped>
.table {
    width: 60%;
    border-collapse: collapse;
    margin: 0 auto;
    border: solid 1px #ddd;
    border-radius: 4px;
}

.table th {
    background: #eee;
    padding: 8px 12px;
    text-align: left;
}

.table tr td {
    padding: 5px;
}
</style>
<template>
  <div style="">
    <div style="text-align:center;padding:20px;">
      <Upload ref="upload" :action="action" :max-size="100000" :data="data" multiple :format="fileType" :on-timeout="uploadTimeout" :on-before="uploadBefore" :on-progress="uploadProgress" :on-success="uploadSuccess" :on-error="uploadError" :on-format-error="uploadFormatError" :on-exceed-size="onExceedSize">
        <Button type="primary" icon="ios-cloud-upload-outline">Upload files</Button>
      </Upload>
    </div>
    <table class="table">
      <thead>
        <th style="width:30%;">文件名</th>
        <th style="width:10%;">文件大小</th>
        <th style="width:10%;">状态</th>
        <th style="width:30%;text-align:center;">操作</th>
      </thead>
      <tbody v-for="(file,index) in uploadFileList">
        <tr>
          <td>{{file.name}}</td>
          <td>{{file.size | formatSize}}</td>
          <td>{{file.percent}}-{{file.status}}</td>
          <td align="center">
            <Button type="error" @click.stop="uploadCancel(file);">停止上传</Button>
            <Button type="warning" @click.stop="uploadRestart(file);">重新上传</Button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>
<script>
import Upload from "@/components/common/upload/upload.vue";
import requestApi from "@/assets/js/apiEntry.js";

export default {
  data() {
    return {
      action: requestApi.saveCompanyPagerFiles,
      // action: "http://zz-tf.com/dbcenter/pagerFile/saveCompanyPagerFiles",
      data: {
        token: "555",
        uname: "郜仕伟",
        pagerFileId: "e37e4c37-4289-4bb9-aa42-903e48aef39e"
      },
      fileType: ["rar", "zip", "txt"], // 可以接受的文件类型
      uploadFileList: [] // 文件上传的列表
    };
  },
  components: {
    Upload
  },
  filters: {
    /**
     * 转换文件的大小,并返回字符串
     */
    formatSize: function (bytes) {
      if (bytes === 0) return "0B";
      var k = 1024;
      var sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
      var i = Math.floor(Math.log(bytes) / Math.log(k));
      return (bytes / Math.pow(k, i)).toPrecision(3) + sizes[i];
    }
  },
  created() { },
  mounted() { },
  methods: {
    /**
     * 上传之前钩子
     */
    uploadBefore(file) {
      console.log("uploadBefore", file);
      this.uploadFileList.push(file);
    },
    /**
     * 上传进度钩子
     */
    uploadProgress(file, fileList, speed, restime) {
      console.log("uploadProgress", file, fileList, "上传速度:" + speed + ", 预计剩余:" + restime);
      this.uploadFileList.forEach(function (v, i) {
        if (v.uid == file.uid) {
          v.percent = file.percent;
          v.status = file.status;
          return false;
        }
      });
    },
    /**
     * 上传成功钩子
     */
    uploadSuccess(response, file, fileList) {
      console.log("uploadSuccess", response, file, fileList);
      this.uploadFileList.forEach(function (v, i) {
        if (v.uid == file.uid) {
          v.percent = file.percent;
          v.status = file.status;
          return false;
        }
      });
    },
    /**
     * 上传请求超时
     */
    uploadTimeout(event, file) {
      console.log("uploadTimeout", event, file);
    },
    /**
     * 上传失败钩子
     */
    uploadError(response, file, fileList) {
      console.log("uploadError", file, fileList);
    },
    /**
     * 上传文件格式错误
     */
    uploadFormatError(file, fileList) {
      console.log("uploadFormatError", file, fileList);
    },
    /**
     * 上传文件大小不允许
     */
    onExceedSize(file, fileList) {
      console.log("onExceedSize", file, fileList);
    },
    /**
     * 上传取消
     */
    uploadCancel(file) {
      this.$refs.upload.onCancel(file);
    },
    /**
     * 重新上传
     */
    uploadRestart(file) {
      this.$refs.upload.onRestart(file);
    }
  }
};
</script>

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值