multiple.vue 7.64 KB
<style lang="scss">
.flex {
  display: flex;
}
.flex-wrap {
  flex-wrap: wrap;
}
.pl-1 {
  padding-left: 0.25rem;
}
.pt-1 {
  padding-top: 0.25rem;
}
.pr-2 {
  padding-right: 0.5rem;
}
.pb-2 {
  padding-bottom: 0.5rem;
}
.ml-2 {
  margin-left: 0.5rem;
}
.text-xs {
  font-size: .75rem;
}
.text-grey {
  color: #d9d9d9;
}
.multiple-upload-file {
  border: 1px dashed #DCDFE6 !important;
  border-radius: 6px !important;
  cursor: pointer !important;
  position: relative !important;
  overflow: hidden !important;
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
  width: 120px !important;
  height: 120px !important;
  display: block !important;
  &:hover {
    .multiple-upload-file-mask {
      display: block;
    }
  }
  .multiple-upload-file-mask {
    position: relative;
    display: none;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    color: rgba(255, 255, 255, 0.9);
    background-color: rgba(0, 0, 0, 0.5);
    .multiple-upload-file-mask-btns {
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      i {
        font-size: 24px;
      }
    }
  }
}
</style>

<template>
  <div>
    <div class="flex flex-wrap">
      <draggable v-model="fileList" class="flex flex-wrap" @change="dragFile">
        <div class="pr-2 pb-2" v-for="(file, index) in fileList" :key="index">
          <div :style="{ 'background-image': `url(${file.url})` }" class="multiple-upload-file">
            <div class="multiple-upload-file-mask">
              <div class="multiple-upload-file-mask-btns">
                <i class="el-icon-search hover:text-blue" @click="handlePreview(file.url)"></i>
                <i class="el-icon-delete ml-2 hover:text-red" @click="handleRemove(file, index)"></i>
              </div>
            </div>
          </div>
        </div>
      </draggable>
      <div class="w-48">
        <el-upload
          v-if="fileList.length < limit"
          :action="url"
          :headers="headers"
          :on-success="handleSuccess"
          :before-upload="beforeUpload"
          :disabled="disabled"
          :show-file-list="false"
          multiple
          :file-list="fileList"
          :http-request="handleRequest"
        >
          <el-button size="small" plain icon="el-icon-plus" :disabled="fileList.length >= limit">
            点击上传
            <span class="text-grey pl-1">({{ fileList.length }} / {{ limit }})</span>
          </el-button>
          <div slot="tip" class="text-xs text-grey pt-1" v-if="fileList.length > 1">拖动图片可更换顺序</div>
        </el-upload>
        <div v-else>
          <el-button size="small" plain icon="el-icon-plus" disabled>
            点击上传
            <span class="text-grey pl-1">({{ fileList.length }} / {{ limit }})</span>
          </el-button>
          <div slot="tip" class="text-xs text-grey pt-1" v-if="fileList.length > 1">拖动图片可更换顺序</div>
        </div>
      </div>
    </div>
    <el-dialog class="photoPreviewer" :visible.sync="dialogVisible" append-to-body>
      <img width="100%" :src="dialogImageUrl" alt="">
    </el-dialog>
  </div>
</template>
<script>
export default {
  name: 'ImageUploadMultiple',
  props: {
    headers: {
      type: Object,
      default() {
        return {}
      }
    },
    url: {
      type: String,
      required: true
    },
    value: String,
    token: String,
    // 选择框禁用状态
    disabled: {
      type: Boolean,
      default: false
    },
    responseFormat: Function,
    errorFormat: Function,
    limit: {
      type: Number,
      default: 3
    }
  },
  data() {
    return {
      dialogImageUrl: '',
      dialogVisible: false,
      fileList: [],
      rowFileNameList: []
    };
  },
  created() {
    if (this.value) {
      this.fileList = this.value.split(',').map(url => {
        return { url };
      });
    } else {
      this.fileList = [];
      this.rowFileNameList = [];
    }
  },
  watch: {
    value(val) {
      if (val) {
        this.fileList = val.split(',').map(url => {
          return { url };
        });
      } else {
        this.fileList = [];
        this.rowFileNameList = [];
      }
    }
  },
  methods: {
    emitValue() {
      const result = [...this.fileList].map(data => {
        return data.url;
      });
      this.$emit('input', result.join(','));
    },
    handleSuccess(response = {}, filename) {
      const url = this.responseFormat(response);
      this.fileList.push({ url, name: filename });
      this.rowFileNameList.push({ url, filename });
      this.rowFileNameList = this.rowFileNameList.sort((a, b) => {
        return a.filename.localeCompare(b.filename, 'zh-Hans-CN', { numeric: true });
      });
      if (this.fileList.length === this.rowFileNameList.length) {
        this.rowFileNameList.forEach((data, index) => {
          this.$set(this.fileList, index, { ...this.fileList[index], url: data.url })
        });
      }
      this.emitValue();
    },
    beforeUpload(file) {
      const isLt2M = file.size / 1024 / 1024 < 2;
      if (!isLt2M) {
        this.$message.error('上传图片大小不能超过 2MB!');
      }
      return isLt2M;
    },
    handleRemove(file, index) {
      this.fileList.splice(index, 1);
      this.rowFileNameList.splice(index, 1);
      this.emitValue();
    },
    handlePreview(url) {
      this.dialogImageUrl = url;
      this.dialogVisible = true;
    },
    dragFile() {
      this.emitValue();
    },
    handleRequest(request = {}) {
      const { action, file, filename, headers } = request;
      const formData = new FormData();
      formData.append(filename, file);
      this.$axios.post(action, formData, { headers })
        .then((response = {}) => {
          if (response.status === 200) {
            this.handleSuccess(response.data, file.name);
          }
        })
        .catch(error => {
          if (this.errorFormat) {
            this.errorFormat(error);
          } else {
            const codeMessage = {
              200: '服务器成功返回请求的数据',
              201: '新建或修改数据成功。',
              202: '一个请求已经进入后台排队(异步任务)',
              204: '删除数据成功。',
              400: '发出的请求有错误,服务器没有进行新建或修改数据,的操作。',
              401: '用户没有权限(令牌、用户名、密码错误)。',
              403: '用户得到授权,但是访问是被禁止的。',
              404: '发出的请求针对的是不存在的记录,服务器没有进行操作',
              406: '请求的格式不可得。',
              410: '请求的资源被永久删除,且不会再得到的。',
              422: '当创建一个对象时,发生一个验证错误。',
              500: '服务器发生错误,请检查服务器',
              502: '网关错误',
              503: '服务不可用,服务器暂时过载或维护',
              504: '网关超时'
            };
            const { response = {} } = error;
            if (!(response.status >= 200 && response.status < 300)) {
              const errortext = codeMessage[response.status] || response.statusText;
              const messageContent = `${response.request ? response.request.responseURL : ''} ${errortext}`;
              this.$notify.error({ title: `请求错误 ${response.status}:`, message: messageContent, duration: 3000 });
            }
          }
        });
    }
  }
}
</script>