使用方式

<upload-img :maxLength="6"
            limit-type=".png,.jpg,.jpeg"
                    :filesList="checkFileList"
                    @addFileList="checkFileUpdate" @deleteFileList="checkFileDelete"></upload-img>

可选属性

属性名称 属性说明
show-loading 是否展示上传时的loading
max-length 可上传的最大文件数量
files-list 回显的图片集合
type 上传按钮样式类型 0:图片 1:按钮
disabled 是否禁用上传
can-delete 是否允许删除文件
folder 文件在服务器上的文件夹,如minio中基础文件夹的下一级文件夹
max-size 允许上传的最大文件大小,单位M
limit-type 所允许上传的文件类型,比如设置limit-type = '.png,.jpg,.jpeg’则限值上传图片类型,为空时允许上传所有类型文件

回调事件

事件名称 事件说明
deleteFileList 删除文件时触发,将删除文件后获取到的文件信息返回
addFileList 新增文件时触发,将上传文件后获取到的文件信息返回

组件代码

<template>
  <div class="imgList">
    <div>
      <div
        class="img"
        v-show="item.url"
        v-for="(item, index) in upFileList"
        :key="index"
        draggable="true"
        @dragstart="dragstartEventImg($event, index)"
        @dragenter="dragenterEventImg($event, index)"
        @dragend="dragendEventImg($event, index)"
        @dragover="dragoverEventImg">
        <el-tooltip class="item" effect="dark" :content="item.name" placement="top-start">
          <img v-if="['png', 'jpg', 'jpeg'].find(function(ii,index,arr){return ii === getFileType(item.url)})"
               :src="fileOnlineShow + item.url" alt="" @click="previewFile(fileOnlineShow + item.url)"/>
          <svg-icon
            v-else-if="['doc', 'docx', 'xls', 'xlsx', 'pdf'].find(function(ii,index,arr){return ii === getFileType(item.url)})"
            :icon-class="getFileType(item.url)"></svg-icon>
          <svg-icon v-else icon-class="other"></svg-icon>
        </el-tooltip>
        <i
          v-if="canDelete"
          class="el-icon-error"
          @click="deleteFile(item)"
        ></i>
      </div>
    </div>
    <div
      class="uploadFile"
      :class="{ disabledStyle: disabled }"
      v-show="maxLength > filesList.length"
    >
      <el-upload
        :disabled="disabled"
        :multiple="true"
        name="file"
        ref="imageUpload"
        :headers="headers"
        :before-upload="beforeUploadFile"
        :data="{ folder: folder }"
        :show-file-list="false"
        :action="fileUploadApi"
        :on-success="uploadFile"
        :limit="maxLength"
        :file-list="filesList"
        :on-progress="progress"
        :on-exceed="onExceed"
        :on-error="onError"
        :accept="limitType"
        lazy
      >
        <div :disabled="disabled" class="uploadBtn" v-if="type === 0">
          <i class="el-icon-plus"></i>
        </div>
        <el-button
          :disabled="disabled"
          size="small"
          class="normal"
          v-else-if="type === 1"
        >上传文件
        </el-button
        >
      </el-upload>
    </div>
    <div class="uploading" v-if="showLoading" v-show="progressShow">
      <div class="left">
        <i class="el-icon-paperclip"></i>
      </div>
      <div class="right">
        <div class="right-top">
          <span>{{ fileName }}</span>
          <i class="el-icon-close" @click="cancelUpload = true"></i>
        </div>
        <div class="right-bottom">
          <el-progress
            :percentage="percentage"
            :format="format"
            :stroke-width="2"
            :status="status"
          ></el-progress>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import store from "@/store";
import {mapGetters} from "vuex";
import Base64 from "@/utils/base64";

export default {
  props: {
    // 是否展示上传时的loading
    showLoading: {
      type: Boolean,
      default: true,
    },
    maxLength: {
      // 可上传的最大数量
      type: Number,
      default: 1,
    },
    // 图片集合
    filesList: {
      type: Array,
      default: () => [],
    },
    // 上传按钮样式类型
    type: {
      type: Number,
      default: 0,
    },
    // 是否禁用
    disabled: {
      type: Boolean,
      default: false,
    },
    // 是否可删除文件
    canDelete: {
      type: Boolean,
      default: true,
    },
    // 基础子文件夹
    folder: {
      type: String,
      default: 'imsp'
    },
    // 最大允许上传的文件大小,默认单位M
    maxSize: {
      type: Number,
      default: 100
    },
    // 限制的文件类型
    limitType: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      headers: {
        'Authorization': 'Bearer ' + store.getters.access_token
      },
      upFileList: [],
      percentage: 0,
      fileName: '',
      status: null,
      progressShow: false,
      showBigImg: '',
      dialogVisible: false,
      tempArr: [],
      startImgIndex: '', //拖动前图片index
      endImgIndex: '', // 结束拖动的index
      cancelUpload: false,
    }
  },
  computed: {
    // fileOnlineShow是根据文件路径获取文件流的后端controller接口地址,如果是直接用nginx代理,则为nginx配置的路径
    // fileUploadApi是上传文件的接口地址
    ...mapGetters(['fileUploadApi', 'fileOnlineShow'])
  },
  components: {},
  mounted() {
    this.upFileList = this.filesList
  },
  watch: {
    filesList(val) {
      this.upFileList = val
    },
  },
  methods: {
    // 拖动开始
    dragstartEventImg($event, index) {
      this.startImgIndex = index
    },
    // 拖动时
    dragenterEventImg($event, index) {
      this.endImgIndex = index
    },
    // 拖动结束
    dragendEventImg($event, index) {
      const currRow = this.upFileList.splice(this.startImgIndex, 1)[0]
      this.upFileList.splice(this.endImgIndex, 0, currRow)
      // this.$emit('getImgList', this.upFileList)
    },
    // 放置
    dragoverEventImg(e) {
      e.preventDefault()
    },
    // 预览文件
    previewFile(file) {
      window.open("https://preview.allbs.cn/onlinePreview?url=" + encodeURIComponent(new Base64().encode(location.origin + file)));
    },
    format(percentage) {
      return percentage === 100 ? '99%' : `${percentage}%` //防止到100时后端处理要时间,会卡在100一小段时间,优化体验
    },
    // 超出限制
    onExceed() {
      this.$message.error('选择的文件超出个数限制')
    },
    // 删除图片
    deleteFile(item) {
      this.upFileList.splice(this.upFileList.findIndex(item => item.fileId === item.fileId), 1)
      console.log("删除文件" + item.fileId);
      this.$emit('deleteFileList', item.fileId)
    },
    // 上传时的钩子
    progress(event, file, fileList) {
      this.progressShow = true
      this.percentage = Math.ceil(event.percent)
      if (this.cancelUpload) {
        this.$refs.imageUpload.abort()
        this.$message.error('文件已取消上传')
        this.upFileList = this.upFileList.filter((item) => {
          return item.url
        })
        this.progressShow = false
      }
    },
    // 上传失败
    onError(err, file, fileList) {
      this.$message.error('文件上传失败')
      this.progressShow = false
      this.status = null
      this.percentage = 0
      this.fileName = ''
    },
    // 文件上传前
    beforeUploadFile(file) {
      this.cancelUpload = false
      if (file.size / 1024 / 1024 >= this.maxSize) {
        this.$message.error('只能上传小于' + this.maxSize + 'M的图片!')
        return false
      }
    },
    // 文件上传成功
    uploadFile(response, file, fileList) {
      if (response.code === 200) {
        this.upFileList.push(response.data)
        this.$emit('addFileList', response.data.fileId)
        this.$message.success('文件上传成功')
      } else {
        this.$message.error('文件上传失败')
      }
      // 进度条消失
      this.progressShow = false
      this.status = null
      this.percentage = 0
      this.fileName = ''
    },
    getFileType(url) {
      return url.substring(url.lastIndexOf(".") + 1, url.length);
    },
  },
}
</script>

<style lang="scss" scoped>
.imgList {
  display: flex;
  flex-wrap: wrap;

  div {
    .img {
      position: relative;

      img {
        cursor: pointer;
        width: 64px;
        height: 64px;
        display: inline-block;
      }

      width: 64px;
      height: 64px;
      display: inline-block;
      margin-right: 8px;

      .el-icon-error {
        font-size: 16px;
        position: absolute;
        right: -8px;
        top: -8px;
        color: red;

        &::before {
          cursor: pointer;
        }
      }
    }
  }
}

.normal {
  &:hover {
    color: #5d8eff !important;
    background-color: #fff !important;
    border-color: #5d8eff !important;
  }

  &::before {
    content: '';
    cursor: pointer;
    background-image: url('/assets/images/upload.png');
    background-size: 14px 14px;
    display: inline-block;
    width: 14px;
    height: 14px;
    transform: translateY(2px);
    margin-right: 6px;
  }
}

.uploadBtn {
  width: 64px;
  height: 64px;
  background: #f7f8fa;
  border: 1px solid #ebebe8;
  position: relative;

  i {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: #bebec5;
    font-size: 24px;
  }
}

.uploading {
  display: flex;
  width: 162px;
  margin-left: 8px;
  margin-top: 28px;

  .left {
    margin-right: 8px;

    i {
      display: inline-block;
      width: 14px;
      height: 14px;
      color: #000;
    }
  }

  .right {
    width: 140px;

    .right-top {
      display: flex;
      justify-content: space-between;

      span {
        height: 22px;
        font-size: 14px;
        font-weight: 400;
        color: rgba(0, 0, 0, 0.45);
        line-height: 22px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        width: 130px;
      }
    }
  }

  .el-icon-close {
    line-height: 22px;
  }
}

.disabledStyle {
  cursor: not-allowed;
}

/deep/ .svg-icon {
  width: 64px;
  height: 64px;
  cursor: pointer;
}
</style>

涉及到的svg图片