针对el-upload的二次封装

针对el-upload的二次封装

前几天在使用element-ui中的el-upload组件的时候,发现默认的上传方法想要跨域还有携带请求头,都是挺麻烦的,所以针对el-upload组件进行了二次封装,下面代码中的systemApi是我们项目中的文件上传api,
<!--
 * @Author: yxc
 * @Date: 2020-08-20 11:00:36
 * @Descripttion: 文件上传
 * @LastEditors: yxc
 * @LastEditTime: 2020-09-15 15:44:12
-->
<template>
  <div v-if="uploadType === 'img'" class="img-upload">
    <el-upload
      class="upload-demo"
      action="#"
      :multiple="multiple"
      :show-file-list="showFileList"
      :accept="fileType.toString()"
      :before-upload="beforeUpload"
      :on-preview="handlePictureCardPreview"
      :on-remove="handleRemove"
      :file-list="fileList"
      :list-type="listType"
      :http-request="uploadImg"
    >
      <template v-if="listType === 'picture-card' || listType === '' || listType === null">
        <i class="el-icon-plus"></i>
      </template>
      <template v-else>
        <el-button size="small" type="primary">{
     {
      buttenText }}</el-button>
      </template>
      <div v-if="isShowPromptText" slot="tip" class="el-upload__tip">{
     {
      promptText }}</div>
    </el-upload>
    <el-dialog :visible.sync="imgDialog">
      <img width="100%" :src="dialogImageUrl" alt="" />
    </el-dialog>
  </div>
  <div v-else-if="uploadType === 'onePicture'" class="img-upload">
    <el-upload
      class="avatar-uploader"
      action="#"
      :show-file-list="showFileList"
      :accept="fileType.toString()"
      :before-upload="beforeUpload"
      :on-preview="handlePictureCardPreview"
      :on-remove="handleRemove"
      :http-request="uploadImg"
    >
      <img v-if="fileList.length > 0" :src="fileList && fileList[0].url" class="avatar">
      <i v-else-if="value ===''" class="el-icon-plus avatar-uploader-icon" />
      <i v-else class="el-icon-plus avatar-uploader-icon"></i>
    </el-upload>
  </div>
  <div v-else class="file-upload">
    <el-upload
      class="upload-demo"
      action="#"
      :multiple="multiple"
      :show-file-list="showFileList"
      :accept="fileType.toString()"
      :before-upload="beforeUpload"
      :on-remove="handleRemove"
      :file-list="fileList"
      :list-type="listType"
      :http-request="uploadImg"
    >
      <template v-if="listType === 'picture-card'">
        <i class="el-icon-plus"></i>
      </template>
      <template v-else>
        <el-button size="small" type="primary">{
     {
      buttenText }}</el-button>
      </template>
      <div v-if="isShowPromptText" slot="tip" class="el-upload__tip">{
     {
      promptText }}</div>
    </el-upload>
  </div>
</template>
<script>
import systemFileApi from '@/api/file/system-file'
export default {
     
  name: 'FileUpload',
  components: {
     },
  props: {
     
    listType: {
     
      // 对应elementui的upload组件中的listtype
      type: String,
      default: null
    },
    fileTag: {
     
      // 文件标签  必填
      type: String,
      required: true
    },
    value: {
     
      // 上传文件的列表
      type: [Array, String],
      required: true
    },
    uploadType: {
     
      // 上传类型  文件 or 图片 or 单张图片
      type: String,
      default: 'img',
      validator: function(value) {
     
        return ['img', 'other', 'onePicture'].indexOf(value) !== -1
      }
    },
    buttenText: {
     
      // 上传按钮的文字
      type: String,
      default: '点击上传'
    },
    fileType: {
     
      // 允许上传文件类型,字符串数组格式  例如['.jpg', '.png', '.jpeg', '.gif']
      type: Array,
      required: true
    },
    fileSize: {
     
      // 允许上传文件大小
      type: Number,
      default: 5
    },
    multiple: {
     
      // 是否允许多选
      type: Boolean,
      default: true
    },
    isShowPromptText: {
     
      // 是否显示下面提示文字
      type: Boolean,
      default: true
    },
    showFileList: {
     
      // 是否显示文件列表
      type: Boolean,
      default: true
    }
  },
  data() {
     
    return {
     
      fileList: [], // 已经上传文件的列表  拼接了文件服务器地址的
      imgDialog: false, // 图片查看弹窗是否显示
      dialogImageUrl: '', // 图片弹窗中图片链接
      promptText: '', // 提示信息
      fileServer: ''  // 文件服务器地址
    }
  },
  watch: {
     
    value: {
     
      handler(newValue, oldValue) {
     
        // 监听v-model绑定值value,value值改变时,随时改变自身fileList中值

        if (newValue !== null && newValue !== '') {
     
          if (this.multiple) {
     
            // 图片多选,value为数组
            this.fileList = newValue.map(item => {
     
              return {
     
                id: item.fileId,
                name: item.fileName,
                url: this.fileServer + item.fileUrl
              }
            })
          } else {
     
          // 单选,value为字符串
            if (newValue.indexOf('http') >= 0) {
     
              this.fileList = [{
      id: '', name: '', url: newValue }]
            } else {
     
              this.fileList = [{
      id: '', name: '', url: this.fileServer + newValue }]
            }
          }
        } else {
     
          // 如果value为空,设置fileList为空
          this.fileList = []
        }
      },
      deep: true
    }
    // value: function(newValue, oldValue) {
     
    //

    // }
  },
  created() {
     },
  async mounted() {
     
    // 生成提示信息
    this.promptText = '只能上传' + this.fileType.toString() + '文件,且大小不能超过' + this.fileSize + 'M'
    // 获取文件服务器地址
    this.fileServer = await this.$store.dispatch('app/getFileServer')
    // 根据v-model绑定值,给fileList赋值
    if (this.value !== null && this.value !== '') {
     
      if (this.multiple) {
     
        // 图片多选,value为数组
        this.fileList = this.value.map(item => {
     
          return {
     
            id: item.fileId,
            name: item.fileName,
            url: this.fileServer + item.fileUrl
          }
        })
      } else {
     
        // 单选,value为字符串
        if (this.value.indexOf('http') === 0) {
     
          this.fileList = [{
      id: '', name: '', url: this.value }]
        } else {
     
          this.fileList = [{
      id: '', name: '', url: this.fileServer + this.value }]
        }
      }
    } else {
     
      // 如果value为空,设置fileList为空
      this.fileList = []
    }
  },
  methods: {
     
    // 上传之前,检查文件类型和文件大小  如果是头像单文件上传,上传前检查是否已经上传头像,已上传,要删除
    beforeUpload(file) {
     
      /**
       * 检查文件大小
       */
      if (file.size / 1024 / 1024 > this.fileSize) {
     
        this.$message({
     
          type: 'error',
          message: `文件大小超过${
       this.fileSize}M`
        })
        return false
      }
      /**
       * 检查文件类型  利用文件名的后缀名来判断文件类型
       */
      var type = file.name.split('.')[file.name.split('.').length - 1]
      if (this.fileType.indexOf('.' + type) < 0) {
     
        this.$message({
     
          type: 'error',
          message: `文件类型错误`
        })
        return false
      }
      /**
       * 头像单文件上传
       */
      if (this.uploadType === 'onePicture') {
     
        if (this.fileList.length > 0) {
     
          this.handleRemove(this.fileList[0])
        }
        return true
      }
    },
    // 上传图片到系统文件
    uploadImg(file) {
     
      systemFileApi.uploadInfo(file.file, this.fileTag, file.file.name).then(res => {
     
        if (res.msg === 'success') {
     
          /**
           * 多选时,绑定值为对象数组格式[{fileId: 'xxx', fileName: 'xxx', fileUrl: 'xxx'}]
           */
          if (this.multiple) {
     
            var data = {
     
              // 给highlinhtSData赋值
              fileId: res.data.fileIdHexString,
              fileName: res.data.fileOriginalName,
              fileUrl: res.data.fileUrl
            }
            this.value.push(data)
            this.$emit('afterFileup')
          } else {
     
            /**
             * 单选时,绑定值为String格式,直接是fileUrl
             */
            this.$emit('input', res.data.fileUrl)
          }
          // 给filelist添加已经上传成功的文件
          var fileListItem = {
     
            id: res.data.fileIdHexString,
            name: res.data.fileOriginalName,
            url: res.data.fullFileUrl,
            status: 'success'
          }
          this.fileList.push(fileListItem)
        }
      })
    },
    // 删除图片  服务器中删除   fileList中删除   value中删除
    handleRemove(file) {
     
      if (file.status === 'success') {
     
        systemFileApi.deleteByUrl(file.url.slice(this.fileServer.length)).then(res => {
     
          if (res.msg === 'success') {
     
            // 删除fileList中的文件
            this.fileList = this.fileList.filter(item => item.url !== file.url)
            /**
             * 文件选择为多选文件时,删除v-model绑定值中的对应对象
             */
            if (this.multiple) {
     
              // 删除value中的文件  使用$emit('input'),因为这里是改变了原来数组
              this.$emit(
                'input',
                this.value.filter(item => item.fileId !== file.id)
              )
            } else {
     
              /**
               * 文件选择为单选时,给v-model绑定值赋值为null
               */
              this.$emit('inpur', null)
            }
          }
        })
      } else {
     
        this.fileList = this.fileList.filter(item => item.id !== file.id)
      }
    },
    handlePictureCardPreview(file) {
     
      this.dialogImageUrl = file.url
      this.imgDialog = true
    }
  }
}
</script>
<style lang="scss">
.avatar-uploader .el-upload {
     
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
     
  border-color: #409eff;
}
.avatar-uploader-icon {
     
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}
.avatar {
     
  width: 178px;
  height: 178px;
  display: block;
}
</style>

文件上传的api,一定要使用FormData格式

/**
   * @Author: yxc
   * @Date: 2020-08-19 15:29:15
   * @LastEditors: yxc
   * @Descripttion: 上传文件,并返回信息
   * @param data FormData 二进制文件
   * @param fileTag String 文件标签
   * @param fileOriginalName String
   * @return axios
   */
  uploadInfo(file, fileTag, fileOriginalName) {
     
    const data = new FormData()
    data.append('file', file)
    const params = new URLSearchParams()
    params.append('fileTag', fileTag)
    params.append('fileOriginalName', fileOriginalName)
    return request({
     
      url: '/file/system-files/upload/info',
      method: 'post',
      headers: {
     
        'Content-Type': 'multipart/form-data'
      },
      data,
      params: params
    })
  }

有不对的地方,欢迎大家做出指正

你可能感兴趣的:(js,vue)