• Element-ui上传图片按顺序展示


    背景

    不知道你上传图片的时候有没有过这样的情况,批量上传多张图片,可能因为图片大小或者网络问题,导致图片返回的顺序和上传时的顺序不一样。因为我们公司是做电商的,即使我们的支持拖动排序,运营还是希望图片能够严格的按照他们上传的顺序展示。

    解决问题

    在上传组件的on-success的方法中,有3个参数 response, file, fileList 其中fileList就是之前上传成功图片的集合,且upload组件提供了clearFiles方法,用来清空fileList,每次上传成功,我们调用clearFiles方法就行了

    上代码

    <template>
      <!-- 上传单张图片 -->
      <div v-if="!multiple" class="image-item">
        <div class="image-wrap" v-if="imgUrl">
          <img :src="imgUrl" :style="imgStyle" />
          <div class="icon-wrap" @click.stop="removeFile">
            <i class="el-icon-delete"></i>
          </div>
        </div>
        <el-upload
          v-else
          ref="imageUpload"
          action="//up.qbox.me"
          :before-upload="beforeUpload"
          :on-success="handleSuccess"
          class="image-uploader"
          :on-error="onError"
          :data="form"
          :show-file-list="false"
          :disabled="loading"
          accept="image/*">
          <i :class="loading ? 'el-icon-loading' : 'el-icon-plus'" :style="imgStyle"></i>
        </el-upload>
      </div>
      <!-- 上传多张图片 -->
      <div class="image-list" v-else>
        <draggable v-model="showImgList" :options="{group:'image'}" @change="dragChange">
          <div v-for="(image, index) in showImgList" :key="index" class="image-wrap">
            <img :src="imgUrl" :style="imgStyle" />
            <div class="icon-wrap" @click.stop="removeFile(index)">
              <i class="el-icon-delete"></i>
            </div>
          </div>
          <el-upload
            ref="imageListUpload"
            action="//up.qbox.me"
            :before-upload="beforeUpload"
            :on-success="handleSuccess"
            class="image-uploader"
            :on-error="onError"
            :data="form"
            multiple
            :disabled="loading"
            :show-file-list="false"
            accept="image/*">
            <i :class="loading ? 'el-icon-loading' : 'el-icon-plus'" :style="imgStyle"></i>
          </el-upload>
        </draggable>
      </div>
    </template>
    <script type="text/babel">
    /**
     * 上传图片或文件
     */
    import md5 from 'blueimp-md5'
    import draggable from 'vuedraggable'
    
    export default {
      props: {
        // 接收和返回的数据
        data: {
          type: [Array, String, Object],
          default: () => {
            return ''
          }
        },
        // 上传多个文件时,文件限制的个数
        limit: {
          type: Number,
          default: () => {
            return 100
          }
        },
        // 一次上传多个
        multiple: {
          type: Boolean,
          default: false,
        },
        //图片展示的宽度
        imgWidth: {
          type: Number,
          default: 150,
        },
        imgHeight: {
          type: Number,
          default: 150,
        },
        //期望上传图片的宽度
        rule: [ Object, Function ]
      },
      data() {
        return {
          imgUrl: '',
          imageCdn: '', //图片的cdn
          form: {
            token: '', //七牛上传的token
          },
          showImgList: [],
          fileList: [],
          clipboard: false,
          isDrag: false,
          handleSuccess: null,
          loading: false,
        }
      },
      components: { draggable },
      watch: {
        data: {
          handler(value) {
            if (!this.multiple) {
              this.imgUrl = value
            } else if (this.multiple) {
              this.showImgList = value
            }
          },
          immediate: true
        }
      },
      computed: {
        imgStyle() {
          return {
             this.imgWidth + 'px', 
            height: this.imgHeight + 'px',
            lineHeight: this.imgHeight + 'px',
          }
        }
      },
      mounted() {
        //防抖
        this.handleSuccess = _.debounce(this.uploadSuccess, 500)
      },
      methods: {
        beforeUpload(file) {
          if (file.type.split('/')[0] === 'image') {
            let tempSize = file.size / 5242880
            if (tempSize > 1) {
              this.$message.error('图片尺寸不得大于5M!')
              return false
            }
          }
          this.loading = true
          let tempNames = file.name.split('.')
          let fileType = tempNames[tempNames.length - 1]
          let curr = (+new Date()).toString()
          let random = Math.random() * 10000
          let md5Str = md5(`${curr}${random}${file.name}`)
          this.form.key = `ai-admin/${md5Str}.${fileType}`
        },
        async uploadSuccess(response, file, fileList) {
          try {
            for (let fileInfo of fileList) {
              let imageInfo = await this.getImageInfo(fileInfo.response.key)
              if (this.rule) {
                this.rule(imageInfo, (error) => {
                  if (error) {
                    throw(error)
                  }
                })
              }
              if (imageInfo.width > 2048 || imageInfo.height > 2048) {
                throw(new Error('图片长或者宽不能超过2048'))
              } else {
                if (this.type === 'image') {
                  this.imgUrl = response.key
                  this.$emit('update:data', response.key)
                } else {
                  if (this.showImgList.length >= this.limit) { // 限制图片张数
                    this.showImgList.length = this.limit
                    throw(new Error(`最多上传 ${this.limit} 张图片`))
                  }
                  this.showImgList.push(imageInfo)
                  this.$emit('update:data', this.showImgList)
                }
              }
            }
          } catch (error) {
            this.$message.error(error.message)
          } finally {
            this.loading = false
            this.$refs.imageListUpload && this.$refs.imageListUpload.clearFiles()
            this.$refs.imageUpload && this.$refs.imageUpload.clearFiles()
          }
        },
        removeFile(index) {
          this.$confirm('确定删除该图片吗?', '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          }).then(() => {
            if (this.type === 'image') {
              this.$emit('update:data', typeof this.data === 'object' ? {} : '')
            } else {
              this.showImgList.splice(index, 1)
              this.$emit('update:data', this.showImgList)
            }
          })
        },
        onError() {
          this.$message.error('上传文件失败')
        },
        getImageInfo(url){
          return new Promise((resolve, reject)=>{
            let image = new Image()
            image.src = `${this.imageCdn}${url}`
            image.onload = () => {
              resolve({
                image: url,
                 image.width,
                height: image.height
              })
            }
            image.onerror = () => {
              reject(new Error('Could not load image at ' + url));
            };
          })
        },
        dragChange() {
          this.$emit('update:data', this.showImgList)
        },
        handleRemove(file, fileList) {
          let imgList = fileList.map(item => {
            return item.response.key
          })
          this.$emit('update:data', imgList)
        },
        handlerClipboard(event) {
          if (this.clipboard) {
            const rawFile = getImageFromClipboard(event)
            if (rawFile) {
              this.$refs.elUpload.handleStart(rawFile)
              this.$refs.elUpload.$refs['upload-inner'].upload(rawFile)
            }
          }
        },
      }
    }
    </script>
    <style lang="less" scoped>
    .image-list, .image-item {
      display: flex;
      .image-wrap {
        position: relative;
        display: inline-block;
        box-sizing: content-box;
        margin: 0 8px 8px 0;
        vertical-align: top;
        &:hover {
          .icon-wrap {
            opacity: 1;
          }
        }
        .icon-wrap {
          position: absolute;
          left: 0;
          bottom: 0;
          width: 100%;
          height: 30px;
          cursor: default;
          text-align: center;
          color: #fff;
          opacity: 0;
          font-size: 20px;
          background-color: rgba(0, 0, 0, .7);
          transition: opacity .3s;
          .el-icon-zoom-in {
            cursor: pointer;
            margin-right: 8px;
          }
          .el-icon-delete {
            cursor: pointer;
          }
        }
      }
    }
    .image-item {
      display: inline-flex;
    }
    /deep/.image-uploader {
      display: inline-block;
      .el-upload {
        border: 1px dashed #d9d9d9;
        border-radius: 6px;
        cursor: pointer;
        position: relative;
        overflow: hidden;
        [class^="el-icon"] {
          font-size: 28px;
          color: #8c939d;
          text-align: center;
        }
        &:hover {
          border-color: #409EFF;
        }
      }
    }
    </style>

    注意

    这是我封装的上传图片的组件,支持 类v-model的传参方式,上传多张图时支持拖动,写图片规则(例如宽高是多少),删除图片

    特别注意:因为我们还封装了图片组件,在这里被我替换了,所以图片展示有上面的代码可能有点问题,稍微改下就行

    用法(只允许上传正方形的图)

    <template>
      <image-upload :data.sync="image" :rule="rule"></image-upload>
    </template>
    <script>
    export default {
      data(){
        let validate = ({ width, height }, callback) => {
          if (width === height) {
            callback()
          } else {
            callback(new Error('请上传正方形的图'))
          }
        }
        return {
          rule: validate,
          image: ''
        }
      },
    }
    </script>

    效果

     

  • 相关阅读:
    [转]MySql 5.7关键字和保留字-附表
    [转]超链接标签简单的几个样式属性
    layui table 前台数字格式保留两位小数,不足补0(mysql 数据库)
    [转]Object.keys()和for in的排序问题
    [转]对form:input标签中的数字进行格式化
    [转]bootstrap table 动态列数
    [转]bootstrap的table插件动态加载表头
    [转]【MyBatis】Decimal映射到实体类出现科学计数法问题
    [转]MySQL函数大全 及用法示例
    [转]mysql update case when和where之间的注意事项
  • 原文地址:https://www.cnblogs.com/mianbaodaxia/p/11421092.html
Copyright © 2020-2023  润新知