Upload 上传组件 Vue封装Element的Upload

561 阅读1分钟

几个点

  • isMax 控制上传图标是否隐藏
  • maxcount 控制最大上传数量
  • exceed 超出数量限制的提示
  • success remove 进行 fileList 的拼装 在外部表单中使用
  • beforeUpload 上传前压缩
  • data 拼入自定义参数
  • headers 加入token
  • :with-credentials="false" 去除session

其它说明

handlePictureCardPreviewdialog中会出现modal层级的问题,会遮照住放大显示的图片,使用了笨办法来解决

<template>
  <div :class="isMax ? 'g-upload-limit' : 'g-upload'">
    <!--
      https://element.eleme.cn/#/zh-CN/component/upload
      with-credentials="false" // 支持发送 cookie 凭证信息
      data	上传时附带的额外参数 object
      headers	设置上传的请求头部 object
      name 上传的文件字段名
      :on-change="handleChange"
    -->
    <el-upload
      ref="uploadComponents"
      class="g-upload-common"
      action="/file/upload_files"
      name="file"
      multiple
      :with-credentials="false"
      list-type="picture-card"
      :limit="maxcount"
      :headers="{ token: token }"
      :data="{ filePathType: fileType }"
      :on-preview="handlePictureCardPreview"
      :on-remove="handleRemove"
      :on-success="handleSuccess"
      :before-upload="beforeUpload"
      :on-exceed="handleExceed"
    >
      <i class="el-icon-plus" />
    </el-upload>
    <el-dialog :visible.sync="uploaderVisible">
      <img width="100%" :src="uploaderImageUrl" alt="">
    </el-dialog>
  </div>
</template>
<script>
import { getToken } from '@/utils/auth'
import { deleteFile } from '@/api/common'
export default {
  props: {
    // 传入最大上传数量
    maxcount: {
      type: Number,
      default: 9
    },
    // 额外参数
    fileType: {
      type: Number,
      default: 1
    }
  },
  data() {
    return {
      uploaderVisible: false, // 展示预览图
      uploaderImageUrl: '', // 预览图片链接
      isMax: false, // 隐藏上传按钮
      imgQuality: 0.5, // 压缩质量
      fileList: [], // 上传的文件列表, 例如: ['https://xxx.cdn.com/xxx.jpg']
      token: ''
    }
  },
  created() {
    this.token = getToken()
  },
  destroyed() {
    this.fileList = []
  },
  methods: {
    // 图片改变时修改filelist
    handleChange(file, fileList) {
      this.fileList = []
      fileList.forEach(el => {
        // 成功上传 才拼入数组
        if (el.response) {
          this.fileList.push(el.response.data[0].serviceFileName)
        }
      })
    },
    // 预览图片
    handlePictureCardPreview(file) {
      this.uploaderImageUrl = file.url
      this.uploaderVisible = true
      setTimeout(() => {
        document.querySelector('.v-modal').style.zIndex = '2000'
      }, 300)
    },
    // 上传文件成功
    handleSuccess(response, file, fileList) {
      this.handleChange(file, fileList)
      if (fileList.length >= this.maxcount) {
        this.isMax = true
      }
    },
    // 删除文件
    handleRemove(file, fileList) {
      deleteFile({ deleteFile: [file.response.data[0].serviceFileName] })
      if (fileList.length < this.maxcount) {
        this.isMax = false
      }
      this.handleChange(file, fileList)
    },
    // 文件超出个数限制时的钩子
    handleExceed(files, fileList) {
      this.$message.error('文件超出上传限制!')
    },
    dataURItoBlob(dataURI, type) {
      var binary = atob(dataURI.split(',')[1])
      var array = []
      for (var i = 0; i < binary.length; i++) {
        array.push(binary.charCodeAt(i))
      }
      return new Blob([new Uint8Array(array)], { type: 'jpg' })
    },
    beforeUpload(file) {
      const _this = this
      return new Promise(resolve => {
        const reader = new FileReader()
        const image = new Image()
        image.onload = (imageEvent) => {
          const canvas = document.createElement('canvas')
          const context = canvas.getContext('2d')
          const width = image.width * _this.imgQuality
          const height = image.height * _this.imgQuality
          canvas.width = width
          canvas.height = height
          context.clearRect(0, 0, width, height)
          context.drawImage(image, 0, 0, width, height)
          const dataUrl = canvas.toDataURL(file.type)
          const blobData = _this.dataURItoBlob(dataUrl, file.type)
          resolve(blobData)
        }
        reader.onload = e => { image.src = e.target.result }
        reader.readAsDataURL(file)
      })
    }
  }
}
</script>
<style lang='scss'>
.g-upload-limit {
  .g-upload-common {
    .el-upload--picture-card {
      display: none;
    }
  }
}
</style>



2020-11-11 更新日志

  • 服务端返回错误的过滤
  • 返回错误之后 删除错误图片预览
  • 增加视频 文件等上传功能
<template>
  <div :class="isMax ? 'g-upload-limit' : 'g-upload'">
    <!--
      https://element.eleme.cn/#/zh-CN/component/upload
      with-credentials="false" // 支持发送 cookie 凭证信息
      data	上传时附带的额外参数 object
      headers	设置上传的请求头部 object
      name 上传的文件字段名
      :on-change="handleChange"
    -->
    <!-- 图片 -->
    <div v-if="filetype === 1">
      <el-upload
        ref="uploadComponents"
        class="g-upload-common"
        action="/file/upload_files"
        name="file"
        multiple
        :with-credentials="false"
        list-type="picture-card"
        :limit="maxcount"
        accept="image/*"
        :headers="{ token: token }"
        :data="{ filePathType: filetype }"
        :on-preview="handlePictureCardPreview"
        :on-remove="handleRemove"
        :on-success="handleSuccess"
        :before-upload="beforeUpload"
        :on-exceed="handleExceed"
        @click="clearUploadedImage"
      >
        <i class="el-icon-plus" />
        <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,最好不超过500kb</div>
      </el-upload>
      <el-dialog :visible.sync="uploaderVisible">
        <img width="100%" :src="uploaderImageUrl" alt="">
      </el-dialog>
    </div>
    <!-- 附件 -->
    <div v-if="filetype === 2">
      <!-- accept=".xlsx,.xls" -->
      <el-upload
        ref="uploadComponents"
        class="upload-demo"
        action="/file/upload_files"
        multiple
        :with-credentials="false"
        name="file"
        :data="{ filePathType: filetype }"
        accept=".pdf"
        :limit="maxcount"
        :headers="{ token: token }"
        :on-remove="handleRemove"
        :on-success="handleSuccess"
        :on-exceed="handleExceed"
        @click="clearUploadedFile"
      >
        <el-button size="small" type="primary">点击上传</el-button>
      </el-upload>
    </div>
    <!-- 视频 -->
    <div v-if="filetype === 4">
      <el-upload
        ref="uploadComponents"
        class="upload-demo avatar-uploader el-upload--text"
        action="/file/upload_files"
        :with-credentials="false"
        name="file"
        :data="{ filePathType: filetype }"
        accept=".mp4,.avi"
        :limit="maxcount"
        :headers="{ token: token }"
        :on-success="handleVideoSuccess"
        :on-remove="handleRemove"
        :before-upload="beforeUploadVideo"
        :on-progress="uploadVideoProcess"
        :on-exceed="handleExceed"
        @click="clearUploadedFile"
      >
        <!-- 变量this.global.company.showVideoPath如果不存在,就不显示,存在就显示视频 -->
        <!-- <video v-if="showVideoPath !='' && !videoFlag" :src="showVideoPath" class="avatar video-avatar" controls="controls">您的浏览器不支持视频播放</video> -->
        <!-- <i v-else-if="showVideoPath =='' && !videoFlag" class="avatar-uploader-icon" /> -->
        <!-- <el-progress v-if="videoFlag == true" type="circle" :percentage="videoUploadPercent" style="margin-top:30px;" /> -->
        <el-button v-if="isShowUploadVideo" slot="trigger" class="video-btn" size="small" type="primary">选取文件</el-button>
        <P class="text" style="color: red;">请保证视频格式正确,且不超过100M</P>
      </el-upload>
    </div>
  </div>
</template>
<script>
import { getToken } from '@/utils/auth'
import { deleteFile } from '@/api/common'
export default {
  props: {
    // 传入最大上传数量
    maxcount: {
      type: Number,
      default: 9
    },
    // 额外参数
    filetype: {
      type: Number,
      default: 1
    }
  },
  data() {
    return {
      uploaderVisible: false, // 展示预览图
      uploaderImageUrl: '', // 预览图片链接
      isMax: false, // 隐藏上传按钮
      imgQuality: 0.5, // 压缩质量
      fileList: [], // 上传的文件列表, 例如: ['https://xxx.cdn.com/xxx.jpg']
      baseFileVOList: [], // 附件的列表
      videoList: [], // 上传的视频列表
      token: '',
      uploadUrl: '', // 上传视频到后台的地址
      videoFlag: false, // 是否显示进度条
      videoUploadPercent: '', // 进度条的进度,
      isShowUploadVideo: true, // 显示上传按钮
      uploaderVideoUrl: '', // 预览视频链接
      showVideoPath: ''
    }
  },
  created() {
    this.token = getToken()
  },
  destroyed() {
    this.fileList = []
    this.baseFileVOList = []
    this.videoList = []
  },
  methods: {
    // 清空图片
    clearUploadedImage() {
      this.$refs.uploadComponents.clearFiles()
    },
    // 清空文件
    clearUploadedFile() {
      this.$refs.uploadComponents.clearFiles()
    },
    // 删除没有上传成功的图片
    filterImgList(fileList) {
      // 深拷贝
      // const _fileList = fileList.slice()
      // const _fileList = Object.assign({}, fileList)
      fileList.forEach((el, i) => {
        if (el.response && (el.response.code !== 20000)) {
          fileList.splice(i, 1)
        }
      })
    },
    // 更新后端返回 20000 的fileList
    updateFileList(fileList) {
      this.fileList = []
      this.baseFileVOList = []
      fileList.forEach(el => {
        if (el.response && (el.response.code === 20000)) {
          if (this.filetype === 1) {
            this.fileList.push(el.response.data[0].serviceFileName)
          }
          if (this.filetype === 2) {
            this.baseFileVOList.push({
              filePath: el.response.data[0].serviceFileName,
              fileName: el.response.data[0].fileName
            })
          }
        }
      })
    },
    // 预览图片
    handlePictureCardPreview(file) {
      this.uploaderImageUrl = file.url
      this.uploaderVisible = true
      setTimeout(() => {
        document.querySelector('.v-modal').style.zIndex = '2000'
      }, 300)
    },
    // 上传文件成功
    handleSuccess(response, file, fileList) {
      if (response.code !== 20000) {
        this.filterImgList(fileList)
        this.$message.error(response.message)
      }
      this.updateFileList(fileList)
      if (fileList.length >= this.maxcount) {
        this.isMax = true
      }
    },
    // 删除文件
    handleRemove(file, fileList) {
      deleteFile({ deleteFile: [file.response.data[0].serviceFileName] })
      if (fileList.length < this.maxcount) {
        this.isMax = false
      }
      this.updateFileList(file, fileList)
    },
    // 文件超出个数限制时的钩子
    handleExceed(files, fileList) {
      this.$message.error('文件超出上传限制!')
    },
    dataURItoBlob(dataURI, type) {
      var binary = atob(dataURI.split(',')[1])
      var array = []
      for (var i = 0; i < binary.length; i++) {
        array.push(binary.charCodeAt(i))
      }
      return new Blob([new Uint8Array(array)], { type: 'jpg' })
    },
    beforeUpload(file) {
      const _this = this
      return new Promise(resolve => {
        const reader = new FileReader()
        const image = new Image()
        image.onload = (imageEvent) => {
          const canvas = document.createElement('canvas')
          const context = canvas.getContext('2d')
          const width = image.width * _this.imgQuality
          const height = image.height * _this.imgQuality
          canvas.width = width
          canvas.height = height
          context.clearRect(0, 0, width, height)
          context.drawImage(image, 0, 0, width, height)
          const dataUrl = canvas.toDataURL(file.type)
          const blobData = _this.dataURItoBlob(dataUrl, file.type)
          resolve(blobData)
        }
        reader.onload = e => { image.src = e.target.result }
        reader.readAsDataURL(file)
      })
    },
    // 上传前回调
    beforeUploadVideo(file) {
      const isLt100M = file.size / 1024 / 1024 < 100
      if (['video/mp4'].indexOf(file.type) === -1) { // 'video/ogg', 'video/flv', 'video/avi', 'video/wmv', 'video/rmvb'
        this.$message.error('请上传正确的视频格式')
        return false
      }
      if (!isLt100M) {
        this.$message.error('上传视频大小不能超过100MB哦!')
        return false
      }
      this.isShowUploadVideo = false
    },
    // 进度条
    uploadVideoProcess(event, file, fileList) {
      this.videoFlag = true
      this.videoUploadPercent = file.percentage.toFixed(0) * 1
    },
    // 上传成功回调
    handleVideoSuccess(response, file) {
      if (response.code === 20000) {
        this.isShowUploadVideo = true
        this.videoFlag = false
        this.videoUploadPercent = 0
        this.showVideoPath = response.data[0].serviceFileName
        var videoObj = {}
        videoObj.url = response.data[0].serviceFileName
        videoObj.name = response.data[0].fileName
        this.videoList.push(videoObj)
        this.$message.success(response.message)
      } else {
        this.$message.error(response.message)
      }
    }
  }
}
</script>
<style lang='scss'>
.g-upload-limit {
  .g-upload-common {
    .el-upload--picture-card {
      display: none;
    }
  }
}
</style>