封装el-Upload

  • 目前在编项目中,页面有用到el-Upload,所以对el-Upload做了二次封装, 组件在个人开发使用不错,但不确定能满足各种业务需求,所以这里主要和大家分享一下设计思路。用一次爽一次,越用越爽。
分析问题:
  • el-Upload是element-ui库的表单组件,如果我们要将其进行二次封装,那么需要考虑几个问题:
  • 上传的样式类型如(如:头像,照片墙 ,缩略图,列表缩略图,文件列表,拖拽上传,自定义等),
  • 手动上传,自动上传
  • 上传大小及类型校验
  • 等等
组件内部template:
  • 详情及组件使用方法:( 请查看《upload上传API》)。
  • 这里特别注意(使用icon模式的时候,提前把icon图标封装进去,否则显示不出来)
<template>
  <div>
    <el-upload
      ref="upload"
      :drag="drag"
      class="avatar-uploader"
      :action="action"
      :auto-upload="autoUpload"
      :list-type="listType"
      :show-file-list="showFileList"
      :file-list="fileList"
      :limit="limit"
      :data="data"
      :before-upload="beforeUploadFn"
      :on-success="successFn"
      :on-error="errorFn"
      :on-remove="removeFn"
      :on-preview="handlePictureCardPreview"
      :on-exceed="exceedFn"
      :on-change="handleAvatarSuccess">
      <div v-if="listType === 'head' && !drag" v-loading="loading"
           element-loading-text="上传中...">
        <template >
          <div slot="trigger">
            <slot name="triggers">
              <img v-if="imageUrl" :src="imageUrl" class="avatar" :style="styleOptions">
              <i v-else class="el-icon-plus avatar-uploader-icon" :style="styleOptions"></i>
            </slot>
          </div>
        </template>
      </div>
      <div v-if="listType === 'picture-card' && !drag">
        <template >
          <div slot="trigger">
            <slot name="triggers">
              <i class="el-icon-plus"></i>
            </slot>
          </div>
        </template>
      </div>
      <div v-if="listType === 'text' && !drag || listType === 'picture' && !drag">
        <template >
          <div slot="trigger">
            <slot name="triggers">
              <el-button size="small" type="primary">点击上传</el-button>
            </slot>
          </div>
        </template>
      </div>
      <div v-if="drag" style="width: 150px;height: 150px;line-height: 150px;overflow: hidden">
        <template >
          <div slot="trigger">
            <slot name="triggers">
              <i class="el-icon-upload"></i>
              <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
            </slot>
          </div>
        </template>
      </div>
      <p v-if="tip" slot="tip" class="el-upload__tip">{{tip}}</p>
    </el-upload>
    <el-dialog :visible.sync="dialogVisible">
      <img width="100%" :src="dialogImageUrl" alt="">
    </el-dialog>
  </div>
</template>

<script>
  export default {
    name: 'wb-upload',
    props: {
      // 上传框样式控制
      styleOptions: {
        type: Object,
        default: {
          width: '100px',
          height: '100px',
          lineHeight: '100px',
        }
      },
      // 是否开启icon图标模式(仅限picture模式使用)
      isIcon: {
        type:Boolean,
        default:false
      },
      // 上传时附带的额外参数
      data: {
        type:Object
      },
      // 是否使用拖拽
      drag: {
        type: Boolean,
        default: false
      },
      // 上传的类型
      fileType: {
        type: Array
      },
      // 要展示的列表数据
      fileList: {
        type: Array
      },
      // 提示语
      tip: {
        type: String
      },
      // 上传的大小
      fileSize: {
        type: Number
      },
      // 文件列表的类型
      listType: {
        type: String
      },
      // 是否显示已上传文件列表
      showFileList: {
        type:Boolean,
        default: false
      },
      // 最大允许上传个数
      limit: {
        type: Number
      },
      // 上传地址
      action: {
        type: String
      },
      //  是否自动上传
      autoUpload: {
        type: Boolean,
        default: false
      }
    },
    data() {
      return {
        // 要显示的图片地址
        imageUrl: '',
        // 加载
        loading: false,
        // 查看图片
        dialogVisible: false,
        dialogImageUrl:''
      };
    },
    methods: {
      /**
       * 统一处理上传文件类型,大小校验
       * */
      typeFn (file,fileList) {


        //上传文件的后缀名称
        const extension = file.name.split('.')[1];
        // console.log(extension);

        // 判断是否开启icon模式
        if(this.isIcon){
          // 仅限picture 模式使用
          if(this.listType === 'picture'){
            if(extension === 'txt'){
              file.url = './src/static/img/txt.png'
            }else if(extension === 'png' || extension === 'jpg' ){
              file.url = './src/static/img/jpg.png'
            }else if(extension === 'xls' || extension === 'xlsx'){
              file.url = './src/static/img/xls.png'
            }else if(extension === 'pdf'){
              file.url = './src/static/img/pdf.png'
            }else if(extension === 'docx' || extension === 'doc'){
              file.url = './src/static/img/docx.png'
            }
          }
        }

        // 要上传的类型
        let TypeArr = this.fileType;
        let isType = TypeArr.some(item=>{
          return item === extension
        });
        const isSize = file.size / 1024  < this.fileSize;
        if (!isType) {
          this.loading = false;
          let str = TypeArr.join();
          this.$message.error(`上传类型只能是${str}格式!`);
          // 清除当前上传数据
          if(fileList){
            fileList.splice(fileList.length - 1,1);
          }
          return false
        }
        if (!isSize) {
          this.loading = false;
          this.$message.error(`上传图片大小不能超过 ${this.fileSize}KB!`);
          // 清除当前上传数据
          if(fileList){
            fileList.splice(fileList.length - 1,1);
          }
          return false
        }
        return true
      },
      // 点击上传列表回调(点击查看图片)
      handlePictureCardPreview(file) {
        // console.log(file)
        if(this.listType === 'picture' || this.listType === 'picture-card'){
          this.dialogImageUrl = file.url;
          this.dialogVisible = true;
        }
      },

      /**
       * 手动上传发生改变的时候触发事件
       * */
      handleAvatarSuccess(file, fileList) {
        // console.log(file)
        // 自动上传的时候不走这里
        if(this.autoUpload){
          return false
        }
        // console.log('手动')
        this.loading = true;
        // 上传校验
        if(this.typeFn(file,fileList)){
          // 反给父组件数据
          let res = {
            file: file.raw,
            fileList
          };
          this.$emit('success',res);
          // 根据不同的类型,赋值
          if(this.listType === 'head'){
            // 把文件流转换为blob展示出来
            this.imageUrl = URL.createObjectURL(file.raw);
            // console.log(this.imageUrl)
            // 清除上传数据
            this.$refs['upload'].clearFiles();
          }
          this.loading = false;
        }else {
          return  false
        }



      },



      /**
       * 自动上传上传文件之前的钩子
       * */
      beforeUploadFn (file, fileList) {
        // 开始加载
        this.loading = true;

        // 判断显示列表是否为true
        if(this.listType === 'head'){
          return this.typeFn(file,fileList)
        }else if(this.showFileList){
          // 上传校验
          // console.log('自动');
          return this.typeFn(file,fileList)
        }else {
          console.error('showFileList必须为:true')
          return  false
        }

      },


      /**
       * 文件上传失败时的钩子
       * */
      errorFn (error, file, fileList) {
        this.loading = false;
        // 返给父组件数据
        let err = {
          error,
          file,
          fileList
        };
        this.$emit('error',err);
      },


      /**
       * 文件上传成功时的钩子
       * */
      successFn (response,file, fileList) {
        this.loading = false;
        // 根据不同的类型,赋值
        if(this.listType === 'head'){
          // 把文件流转换为blob展示出来
          this.imageUrl = URL.createObjectURL(file.raw);
        }
        // 返给父组件数据
        let res = {
            response,
            file,
            fileList
          };
        if(this.listType === 'head'){
          this.$emit('success',res);
          // 清除上传数据
          this.$refs['upload'].clearFiles();
        }else {
          this.$emit('success',res);
        }
      },

      /**
       * 文件列表移除文件时的钩子
       * */
      removeFn (file, fileList) {
        // 返给父组件数据
        let res = {
          file,
          fileList
        };
        this.$emit('remove',res);
      },

      /**
       * 文件超出个数限制时的钩子
       * */
      exceedFn (files, fileList) {
        this.$message.error(`上传文件不能超出${this.limit}个`)
      }
    },
    mounted() {
    }
  }
</script>

<style scoped>
  .avatar-uploader >>>.el-upload {
    cursor: pointer;
    position: relative;
  }
  .avatar-uploader >>>.el-upload:hover {
    border-color: #409EFF;
  }
  .avatar-uploader ::v-deep.el-upload {
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
  }
  .avatar-uploader ::v-deep.el-upload:hover {
    border-color: #409EFF;
  }
  .avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 120px;
    height: 120px;
    line-height: 120px;
    text-align: center;
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
  }
  .avatar {
    width: 120px;
    height: 120px;
    display: block;
  }
  >>>.el-upload-list--picture-card .el-upload-list__item-actions span{
    font-size:inherit;
  }

</style>