美文网首页
二次封装ele-ui上传公共组件(支持多选,放大,删除)

二次封装ele-ui上传公共组件(支持多选,放大,删除)

作者: 邹小小白 | 来源:发表于2021-05-11 17:46 被阅读0次

在compnents新建一个uploader.vue文件
<template>

  <dir class="element-ui-uploader-extension">

    <div class="file-list row">

      <div class="file" v-for="obj in fileList" :key="obj.uid" :style="previewStyle">

        <img class="img-cover" v-if="obj.status === 'success' && type === 'image'" :src="obj.path">

        <video class="img-cover" v-else-if="obj.status === 'success' && type === 'video'" :src=" obj.path"></video>

        <!-- 默认图 -->

        <img class="img-cover" v-else-if="obj.status === 'uploading'" src="/img/image_default.png">

        <!-- 进度条 -->

        <el-progress v-if="obj.status === 'uploading'" class="file-progress" type="circle" :width="50" :stroke-width="2" :percentage="Number(obj.percentage.toFixed(2))"></el-progress>

        <div v-if="obj.status === 'success'" class="control-mask">

          <i class="el-icon-zoom-in" @click="view(obj)"></i>

          <i v-if="!disabled" class="el-icon-delete" @click="del(obj)"></i>

        </div>

      </div>

      <el-upload v-if="!disabled" ref="upload" v-show="fileList.length < max"  accept=".png, .jpg, .jpeg" :action="uploadUrl" :show-file-list="false" :limit="max" :multiple="multiple" :disabled="disabled" :headers="headers" :on-change="change" :on-exceed="exceed" :on-progress="progress" :before-upload="beforeUpload" :on-success="success" :on-error="error" :on-remove="remove" :before-remove="beforeRemove">

        <div class="placeholder" :style="previewStyle">

          <i class="el-icon-plus" title="上传"></i>

          <span class='placeholder-text'>{{ placeholderText }}</span>

        </div>

      </el-upload>

    </div>

    <el-dialog v-if="dialogVisible" width="800px" :visible.sync="dialogVisible" :close-on-click-modal="false" append-to-body :lock-scroll="false">

      <img style="width: 100%;height:100%;object-fit:cover" v-if="type === 'image'" class="img-cover" :src="dialogImageUrl" alt="">

      <video v-if="type === 'video'" class="img-cover" :src="dialogImageUrl" controls="controls"></video>

    </el-dialog>

  </dir>

</template>

<script>

import {getToken} from '@/util/auth'; // 获取上传token

export default {

  props: {

    type: {

      // 上传文件类型 [image, video, file]

      type: String,

      default: function () {

        return 'image'

      }

    },

    value: {

      // 表单值 用逗号分隔的资源相对地址

      // type: String,

      required: true

    },

    max: {

      // 上传文件数量

      type: Number,

      default: function () {

        return 1

      }

    },

    maxMB: {

      // 上传文件大小 单位 MB

      type: Number,

      default: 2

    },

    accept: {

      // 上传文件格式

      type: Array,

      default: function () {

        const accept = {

          //  image: [

          //  'jpg',

          //  'jpeg',

          //  'png',

          //  'gif',

          //  'bmp',

          //  'JPG',

          //  'JPEG',

          //  'PNG',

          //  'GIF',

          //  'BMP'

          // ],

          image: [

            'jpg',

            'jpeg',

            'png',

          ],

          // video : ['flv', 'mpg', 'mpeg', 'avi', 'wmv', 'mov', 'asf', 'rm', 'rmvb', 'mkv', 'm4v', 'mp4'],

          // 考虑视频播放兼容性

          video: ['mp4', 'mpg', 'mpeg'],

          file: ['ppt', 'pptx', 'doc', 'docx', 'xls', 'xlsx', 'pdf']

        }

        return accept[this.type]

      }

    },

    disabled: {

      // 禁用

      type: Boolean,

      default() {

        return false

      }

    },

    fileSavePath: String, // 要传给服务端的保存文件夹

    width: Number, // 图片宽度限制 需要配合高度使用,单一设置无效

    height: Number, // 图片高度限制 需要配合宽度使用,单一设置无效

    previewStyle: {

      // 展示样式

      type: Object,

      default() {

        return {

          width: '138px',

          height: '138px'

        }

      }

    },

      /* 提示文字内容 */

      placeholderText: {

          type: String,

          default() {

              return '大小 < 2M;长宽=3:2'

          }

      }

  },

  created() {

    this.getValue()

  },

  data() {

    return {

      dialogImageUrl: '',

      dialogVisible: false,

      fileList: [],

      uploadUrl: '/api/blade-resource/oss/endpoint/put-file', // 上传地址

    }

  },

  computed: {

    multiple() {

      return this.max > 1

    },

    headers() {

      return {

        'Blade-Auth': 'bearer '+getToken()

      }

    }

  },

  watch: {

    value() {

      this.getValue()

    }

  },

  methods: {

    getUUID() {

      var s = []

      var hexDigits = '0123456789abcdef'

      for (var i = 0; i < 36; i++) {

        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)

      }

      s[14] = '4'

      s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1)

      s[8] = s[13] = s[18] = s[23] = '-'

      return s.join('')

    },

    getValue() {

      this.fileList = []

      if (!this.value) {

          if (this.$refs.upload) {

              this.$refs.upload.clearFiles()

          }

          return

      }

      const arr = this.value.split(',')

      if (arr.length === 1) {

          this.fileList = [{ name: '图片-1', path: arr[0], status: 'success' }]

      } else if (arr.length > 1) {

          arr.forEach((el, i) => {

              const obj = this.fileList.find((item) => {

                  return item.path === el

              })

              if (!obj && el) {

                  this.fileList.push({

                      name: '图片-' + (i + 1),

                      path: el,

                      uid: this.getUUID(),

                      status: 'success'

                  })

              }

          })

      } else {

          return

      }

    },

    // 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。

    beforeUpload(file) {

      const maxMB = this.maxMB

      const accept = this.accept

      const format = file.type.split('/')[1]

      const isFormat = accept.indexOf(format) != -1

      const isSize = file.size < maxMB * 1024 * 1024

      // console.log(file, fileList)

      // console.log(format, isFormat, isSize)

      // 检验文件格式

      if (!isFormat) {

        this.$message.error(

          `文件格式错误,请上传${accept.join(',')}格式的文件`

        )

        return false

      }

      // 校验文件大小

      if (!isSize) {

        this.$message.error(`文件太大,请上传${maxMB}MB内的文件`)

        return false

      }

      // 校验图片尺寸

      return this.imgScale(file)

    },

    // 删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除。

    beforeRemove(file) {

      // console.log(file, fileList)

      if (file.status === 'success') {

        return this.$confirm(`确定移除 ${file.name}?`)

      }

    },

    // 文件超出个数限制时的钩子

    exceed(files, fileList) {

      console.log(files, fileList)

      this.$message.error(`超出数量限制,请选择${this.max}个文件`)

    },

    // 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用

    change(file, fileList) {

      console.log(file, fileList)

    },

    // 文件上传时的钩子

    progress(event, file) {

      // console.log(event, file, fileList, file.status)

      this.fileList = this.fileList.filter((el) => {

        return el.uid !== file.uid

      })

      this.fileList.push(file)

    },

    // 文件上传成功时的钩子

    success(response, file, fileList) {

      console.log(response, file, fileList, file.status)

      this.fileList.forEach((el) => {

        if (el.uid === file.uid) {

          el.path = file.response.data.link

        }

      })

      this.setValue()

    },

    // 文件上传失败时的钩子

    error(err, file, fileList) {

      console.log(err, file, fileList, file.status)

    },

    // 文件列表移除文件时的钩子

    remove(file, fileList) {

      console.log(file, fileList, file.status)

      this.fileList = this.fileList.filter((el) => {

        return el.uid !== file.uid

      })

    },

    // 校验图片尺寸

    imgScale(file) {

      // 没有限制宽高,直接上传

      const width = this.width

      const height = this.height

      if (!width || !height) return true

      return new Promise((resolve, reject) => {

        let reader = new FileReader()

        reader.file = file

        reader.readAsDataURL(file)

        reader.onload = (res) => {

          const result = res.target.result

          const img = new Image()

          img.onload = () => {

            if (

              img.height !== parseFloat(height) ||

              img.width !== parseFloat(width)

            ) {

              this.$message.error(

                `图片尺寸错误,请上传${this.width}px*${this.height}px的图片`

              )

              reject(false)

            } else {

              resolve(true)

            }

          }

          img.src = result

        }

      })

    },

    // 查看已上传图片

    view(file) {

      const obj = this.fileList.find((el) => {

        return el.uid === file.uid

      })

      this.dialogImageUrl =  obj.path

      this.dialogVisible = true

    },

    // 删除已上传图片

    del(file) {

      this.$confirm(`确定移除?`, { type: 'warning' })

        .then(() => {

          this.fileList = this.fileList.filter((el) => {

            return el.uid !== file.uid

          })

          this.setValue()

          this.$refs.upload.clearFiles()

          this.$message({

            type: 'success',

            message: '删除成功!'

          })

        })

        .catch(() => {

          this.$message({

            type: 'info',

            message: '已取消删除'

          })

        })

      // this.fileList = this.fileList.filter((el) => {

      //  return el.uid !== file.uid

      // })

      // this.setValue()

      // this.$refs.upload.clearFiles()

      // this.$message({

      //  type: 'success',

      //  message: '删除成功!'

      // })

    },

    setValue() {

      const value = this.fileList

        .map((el) => {

          return el.path

        })

        .join(',')

      this.$emit('input', value) // 传入父组件接受处理数据

      this.$emit('checkValidate') // 需要效验就触发对应多validateField(‘name’)

    }

  }

}

</script>

<style lang="scss" scoped>

.element-ui-uploader-extension {

  padding: 0;

  margin: 0px;

  .placeholder {

    border: 1px dashed #dddddd;

    border-radius: 5px;

    display: flex;

    flex-direction: column;

    align-items: center;

    justify-content: center;

    font-size: 20px;

    color: #aaaaaa;

    margin-right: 10px;

    &:hover {

      border: 1px dashed #409eff;

    }

    .placeholder-text{

      display: inline-block;

      width: 100%;

      font-size: 10px;

      transform: scale(0.8);

      line-height: 14px;

      color: #C0C4D6;

    }

  }

  .file-list {

    display: flex;

    flex-wrap: wrap;

    align-items: center;

  }

  .file {

    position: relative;

    margin-right: 10px;

    border: 1px dashed #dddddd;

    border-radius: 4px;

    overflow: hidden;

    .img-cover{

      width: 100%;

      height: 100%;

      object-fit: cover;

    }

    .file-progress {

      position: absolute;

      left: 0;

      right: 0;

      bottom: 0;

      top: 0;

      margin: auto;

      width: 50px;

      height: 50px;

      .el-progress-bar__outer,

      .el-progress-bar__inner {

        border-radius: 0;

      }

    }

    .control-mask {

      display:none;

      position: absolute;

      left: 0;

      right: 0;

      bottom: 0;

      top: 0;

      width: 100%;

      height: 100%;

      background: rgba(0,0,0, 0.5);

      align-items: center;

      justify-content: space-evenly;

      i {

        font-size: 20px;

        color: #fff;

        // background: rgba(255, 255, 255, 0.8);

        width: 30px;

        height: 30px;

        border-radius: 50%;

        display: flex;

        align-items: center;

        justify-content: center;

        cursor: pointer;

        &:hover {

          // background: rgba(255, 255, 255, 1);

          color: #fff;

        }

      }

    }

    &:hover {

      .control-mask {

        display: flex;

      }

    }

  }

}

</style>

父组件引用

import uploader from '@/components/form/uploader.vue'


好了这样一个公共组件就封装完了,工作中用起来就很方便,统一样式管理.

相关文章