vue 基于 el-upload 封装图片上传组件

组件封装

新建image-upload.vue 文件:

<template>
  <div class="img-upload-container">
    <div class="img-upload" :class="{'limit-num': fileList.length>=limit, 'mini': size === 'small'}">
      <el-upload ref="upload" :action="url" :file-list="fileList" list-type="picture-card" :on-success="handleSuccess" :on-remove="handleRemove" :on-preview="handlePictureCardPreview" :before-upload="beforeAvatarUpload">
        <i class="el-icon-plus"></i>
        <p class="el-upload__tip" slot="tip" v-if="tips">{{tips}}</p>
        <div slot="file" slot-scope="{file}" class="img-con">
          <img class="el-upload-list__item-thumbnail" :src="file.url" alt="">
          <span class="el-upload-list__item-actions">
            <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
              <i class="el-icon-zoom-in"></i>
            </span>
            <span class="el-upload-list__item-delete" @click="handleRemove(file)">
              <i class="el-icon-delete"></i>
            </span>
            <span v-if="size === 'small'" style="display:block;marginLeft:0" class="el-upload-list__item-delete" @click="onChangeHandle(file)">
              <i class="el-icon-edit"></i>
            </span>
            <span v-else class="el-upload-list__item-delete" @click="onChangeHandle(file)">
              <i class="el-icon-edit"></i>
            </span>
          </span>
        </div>
      </el-upload>
      <el-dialog :visible.sync="dialogVisible">
        <img width="100%" append-to-body :src="dialogImageUrl" alt />
      </el-dialog>
    </div>
  </div>
</template>

<script>
export default {
  name: 'ImgUpload',
  componentName: 'ImgUpload',
  data () {
    return {
      imgWidth: 0,
      imgHeight: 0,
      url: `xxx`, // 图片上传接口地址
      fileList: [],
      dialogImageUrl: '',
      dialogVisible: false,
      picIndex: -1,
      vmodelType: ''
    }
  },
  props: {
    value: {
      type: [String, Array]
    },
    tips: {
      type: String,
      default: ''
    },
    size: {
      type: String,
      default: 'medium' // small
    },
    limit: {
      type: Number,
      default: 2
    },
    limitSize: {
      type: Number,
      default: 10
    },
    valueType: {
      type: String,
      default: 'String' // Object
    },
    // 是否校验图片尺寸,默认不校验
    isCheckPicSize: {
      type: Boolean,
      default: false
    },
    checkWidth: {
      type: Number,
      default: 0 // 图片限制宽度
    },
    checkHeight: {
      type: Number,
      default: 0 // 图片限制高度
    },
    topLimitWidth: {
      type: Number,
      default: 0 // 图片限制宽度上限(有时需要校验上传图片宽度在一个范围内)
    },
    topLimitHeight: {
      type: Number,
      default: 0 // 图片限制高度上限(有时需要校验上传图片高度在一个范围内)
    },
    busiType: {
      type: Number,
      default: 2
    },
    index: {
      type: Number,
      default: -1 // 当前图片index,限制可以上传多张时,针对某一张进行操作,需要知道当前的index
    },
    limitType: {
      type: String,
      default: '' // gif,webp/gif/webp (限制上传格式)
    }
  },
  watch: {
    value: {
      deep: true,
      handler: function (val, oldVal) {
        if (val) {
          if (this.valueType === 'Object') {
            this.fileList = this.value.map(item => ({ id: item.id, url: item.url, name: item.name }))
          } else {
            if (this.vmodelType === 'array') {
              this.fileList = this.value.map(item => ({ url: item }))
            } else {
              this.fileList = [{ url: val }]
            }
          }
        } else {
          this.fileList = []
        }
      }
    }
  },
  created () {
    if (this.valueType === 'Object') {
      this.vmodelType = 'array'
    } else {
      const res = this.isString(this.value)
      if (res === true) {
        this.vmodelType = 'string'
      } else {
        this.vmodelType = 'array'
      }
    }
    // console.log('created vmodelType', this.vmodelType)
    if (this.value) {
      if (this.valueType === 'Object') {
        this.fileList = this.value.map(item => ({
          id: item.id ? item.id : '',
          url: item.url,
          name: item.name
        }))
      } else {
        if (this.vmodelType === 'array') {
          this.fileList = this.value.map(item => ({ url: item }))
        } else {
          this.fileList = [{ url: this.value }]
        }
      }
    }
  },
  mounted () {
  },
  methods: {
    findItem (uid) {
      this.fileList.forEach((ele, i) => {
        if (uid === ele.uid) {
          this.picIndex = i
        }
      })
    },
    onChangeHandle (file, fileList) {
      // console.log('onChangeHandle', file, this.fileList)
      this.findItem(file.uid)
      this.$refs.upload.$refs['upload-inner'].handleClick()
    },
    beforeAvatarUpload (file) {
      const imgType = file.type
      const isLtSize = file.size / 1024 / 1024 < this.limitSize
      const TYPE_NOGIFWEBP = ['image/png', 'image/jpeg', 'image/jpg']
      const TYPE_NOGIF = ['image/png', 'image/jpeg', 'image/jpg', 'image/webp']
      const TYPE_NOWEBP = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif']
      const TYPE_ALL = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/webp']
      let isType = true
      if (this.limitType && this.limitType.indexOf('gif') !== -1 && this.limitType.indexOf('webp') !== -1) {
        if (TYPE_NOGIFWEBP.indexOf(imgType) === -1) {
          isType = false
          this.$message.error('仅支持上传 jpg、png、jpeg 格式的图片!')
        }
      } else if (this.limitType && this.limitType.indexOf('gif') !== -1) {
        if (TYPE_NOGIF.indexOf(imgType) === -1) {
          isType = false
          this.$message.error('仅支持上传 jpg、png、jpeg、webp 格式的图片!')
        }
      } else if (this.limitType && this.limitType.indexOf('webp') !== -1) {
        if (TYPE_NOWEBP.indexOf(imgType) === -1) {
          isType = false
          this.$message.error('仅支持上传 jpg、png、jpeg、gif 格式的图片!')
        }
      } else {
        if (TYPE_ALL.indexOf(imgType) === -1) {
          isType = false
          this.$message.error('仅支持上传 jpg、png、jpeg、webp、gif 格式的图片!')
        }
      }

      if (!isLtSize) {
        this.$message.error(`上传图片大小不能超过${this.limitSize}MB!`)
      }
      if (this.isCheckPicSize === true) {
        const width = this.checkWidth
        const height = this.checkHeight
        const topWidth = this.topLimitWidth
        const topHeight = this.topLimitHeight
        const that = this
        const isSize = new Promise((resolve, reject) => {
          // window对象,将blob或file读取成一个url
          const _URL = window.URL || window.webkitURL
          const img = new Image()
          // image对象的onload事件,当图片加载完成后执行的函数
          img.onload = () => {
            that.imgWidth = img.width
            that.imgHeight = img.height
            if (width && height) { // 校验图片的宽度和高度
              let valid = false
              if (topWidth && topHeight) {
                // 校验图片宽度和高度范围
                valid = ((width <= img.width) && (img.width <= topWidth)) && ((height <= img.height) && (img.height <= topHeight))
              } else if (topHeight) {
                // 校验图片高度范围
                valid = img.width === width && ((height <= img.height) && (img.height <= topHeight))
              } else if (topWidth) {
                // 校验图片宽度范围
                valid = ((width <= img.width) && (img.width <= topWidth)) && img.height === height
              } else {
                // 校验图片宽度、高度固定值
                valid = img.width === width && height === img.height
              }
              valid ? resolve() : reject(new Error('error'))
            } else if (width) { // 只校验图片的宽度
              let valid = false
              if (topWidth) {
                // 校验图片宽度范围
                valid = (width <= img.width) && (img.width <= topWidth)
              } else {
                // 校验图片宽度固定值
                valid = img.width === width
              }
              valid ? resolve() : reject(new Error('error'))
            } if (height) { // 只校验图片的高度
              let valid = false
              if (topHeight) {
                // 校验图片高度范围
                valid = (height <= img.height) && (img.height <= topHeight)
              } else {
                // 校验图片高度固定值
                valid = img.height === height
              }
              valid ? resolve() : reject(new Error('error'))
            }
          }
          img.src = _URL.createObjectURL(file)
        }).then(() => {
          return file
        }, () => {
          let text = ''
          if (width && height) {
            if (topWidth && topHeight) {
              text = `图片尺寸限制为:宽度${width}~${topWidth}px,高度${height}~${topHeight}px!`
            } else if (topHeight) {
              text = `图片尺寸限制为:宽度${width}px,高度${height}~${topHeight}px!`
            } else if (topWidth) {
              text = `图片尺寸限制为:宽度${width}~${topWidth}px,高度${height}px!`
            } else {
              text = `图片尺寸限制为:宽度${width}px,高度${height}px!`
            }
          } else if (width) {
            if (topWidth) {
              text = `图片尺寸限制为:宽度${width}~${topWidth}px!`
            } else {
              text = `图片尺寸限制为:宽度${width}px!`
            }
          } else if (height) {
            if (topHeight) {
              text = `图片尺寸限制为:高度${height}~${topHeight}px!`
            } else {
              text = `图片尺寸限制为:高度${height}px!`
            }
          }
          this.$message.error(text)
          return Promise.reject(new Error('error'))
        })
        return isType && isLtSize && isSize
      } else {
        // window对象,将blob或file读取成一个url
        const _URL = window.URL || window.webkitURL
        const img = new Image()
        img.onload = () => { // image对象的onload事件,当图片加载完成后执行的函数
          this.imgWidth = img.width
          this.imgHeight = img.height
          // console.log('getWidthAndHeight', this.imgWidth, this.imgHeight)
        }
        img.src = _URL.createObjectURL(file)
        return isType && isLtSize
      }
    },
    // 判断是否是String
    isString (str) {
      return ((str instanceof String) || (typeof str).toLowerCase() === 'string')
    },
    handleRemove (file, fileList) {
      this.findItem(file.uid)
      this.fileList.splice(this.picIndex, 1)
      fileList = JSON.parse(JSON.stringify(this.fileList))
      this.exportImg(fileList)
    },
    handleSuccess (res, file, fileList) {
      // console.log('handleSuccess fileList', res)
      // console.log('handleSuccess fileList', file)
      if (this.picIndex !== -1) {
        fileList.splice(this.picIndex, 1)
      }
      this.exportImg(fileList)
    },
    handleError (err) {
      this.$message.error(err)
    },
    handlePictureCardPreview (file) {
      this.dialogImageUrl = file.url
      this.dialogVisible = true
    },
    exportImg (fileList = []) {
      // console.log('exportImg fileList', fileList)
      this.fileList = fileList
      if (fileList.length !== 0) {
        if (this.valueType === 'Object') {
          const imgs = fileList.map(item => {
            if (item.response && item.response.result) {
              item.id = item.response.result[0].id
              item.url = item.response.result[0].url + '&width=' + this.imgWidth + '&height=' + this.imgHeight
              item.name = item.response.result[0].fileName
            }
            return {
              id: item.id,
              url: item.url,
              name: item.name
            }
          })
          this.$emit('input', imgs)
          // console.log('exportImg imgs', imgs)
        } else {
          if (this.vmodelType === 'array') {
            const imgs = fileList.map(item => {
              if (item.response && item.response.result) {
                item.url = item.response.result[0].url + '&width=' + this.imgWidth + '&height=' + this.imgHeight
              }
              return item.url
            })
            this.$emit('input', imgs)
            // console.log('exportImg imgs', imgs)
          } else {
            const resUrl = fileList[0].response.result[0].url + '&width=' + this.imgWidth + '&height=' + this.imgHeight
            this.$emit('input', resUrl)
            // console.log('exportImg resUrl', resUrl)
          }
        }
      } else {
        this.$emit('input', '')
      }
      this.picIndex = -1
    }
  }
}
</script>

<style lang='less'>
@small-size: 80px;
.img-upload&&.limit-num {
  .el-upload--picture-card {
    display: none !important;
  }
}
.img-upload&&.mini {
  .el-upload {
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
  }
  .el-upload-list__item {
    width: @small-size;
    height: @small-size;
    text-align: center;
    /*去除upload组件过渡效果*/
    transition: none !important;
  }
  .el-upload--picture-card {
    width: @small-size;
    height: @small-size;
    line-height: @small-size;
    text-align: center;
  }
}
.el-upload-list__item&&.is-success {
  .img-con {
    width: 100%;
    height: 100%;
  }
}
</style>

使用

然后,在需要上传图片的页面,直接引入组件使用即可:

<template>
  <div>    
    <img-upload v-model="img" :size="'small'" :tips="'单图字符串'" :limit="1" :limitSize="5"></img-upload>
    <img-upload v-model="imgs" :size="'small'" :tips="'单图数组'" :limit="1" :limitSize="1" :isCheckPicSize="true" :checkWidth="200" :checkHeight="200"></img-upload>
    <img-upload v-model="imgList" :size="'small'" :tips="'多图数组'" :limit="6" :limitSize="1" valueType="Object"></img-upload>  
  </div>
</template>
<script>
import ImgUpload from '@/components/image-upload'

export default {
  name: 'add',
  components: {
    ImgUpload
  },
  data () {
    return {
      img: '',
      imgs: [
        'https://xxx'
      ],
      imgList: [
        {
          name: 'flower.jpg',
          url: 'xxx'
        }
      ]
     }    
  },
  props: {
  },
  watch: {
  },
  created () {    
  },
  mounted () {
  },
  methods: {    
  }
}
</script>

页面效果:
vue 基于 el-upload 封装图片上传组件

组件不设置尺寸时,默认为大尺寸:

<img-upload v-model="img" :tips="'单图字符串'" :limit="1" :limitSize="5"></img-upload>
<img-upload v-model="imgs" :tips="'单图数组'" :limit="1" :limitSize="1" :isCheckPicSize="true" :checkWidth="200" :checkHeight="200"></img-upload>
<img-upload v-model="imgList" :tips="'多图数组'" :limit="6" :limitSize="1" valueType="Object"></img-upload>  

如下图:
vue 基于 el-upload 封装图片上传组件

上一篇:shell基础


下一篇:el-select 多选框使用 以及回显默认选中说明