Vant2 图片上传组件封装

<template>
  <div class="photo-uploader">
    <div class="photo-list">
      <van-uploader
        multiple
        v-if="isEditMode"
        :capture="capture"
        :max-count="maxCount"
        :before-read="beforeRead"
        :after-read="afterRead"
        v-model="fileList"
        preview-size="86"
        @delete="deleteImg"
        @click-upload="onClickUpload"
        accept="image/*"
      >
        <div class="photo-button">
          <span><van-icon name="plus" size="1.6rem" color="#ADB1B3" /></span>
          <span class="photo-text">点击上传</span>
        </div>
      </van-uploader>

      <div class="photo-preview" v-else>
        <van-row gutter="16">
          <div v-if="fileList.length === 0" class="empty-block">
            暂无照片
          </div>
          <van-col v-else span="8" v-for="(item, index) in fileList" :key="index" style="padding-bottom: 1.6rem">
            <div class="photo-item">
              <van-image width="86" height="86" :src="item.url" @click="showOneImg(fileList, index)"></van-image>
            </div>
          </van-col>
        </van-row>
      </div>
    </div>
  </div>
</template>

<script>
import { ImagePreview } from 'vant'
import { cloneDeep, pick } from 'lodash'
import axios from 'axios'
import Sso from '@/sso' // 改成自己项目中获取 Token 的方法

export default {
  props: {
    isEditMode: {
      type: Boolean,
      defaut: true,
    },
    value: {
      type: Array,
      default: () => [],
    },
    maxCount: {
      type: Number,
      default: 15,
    },
    capture: {
      type: String,
      default: null, // 默认不选择
    },
    fileType: {
      type: Array,
      default: () => ['png', 'jpg', 'jpeg', 'heic', 'bmp'],
    },
  },
  data() {
    return {
      uploadURL: process.env.VUE_APP_BASE_API + '/common/upload',
      baseUrl: process.env.VUE_APP_BASE_API,
      fileList: [],
    }
  },
  computed: {
    isUploading() {
      let f = false
      for (let i = 0, list = this.fileList; i < list.length; i++) {
        if (list[i].status === 'uploading') {
          f = true
          break
        }
      }

      return f
    },
  },
  watch: {
    value: {
      handler(val) {
        const list = cloneDeep(val)
        if (this.fileList.length !== list.length) {
          this.fileList = list
        }
      },
      deep: true,
      immediate: true,
    },
    fileList: {
      handler(val, oldVal) {
        this.$emit(
          'input',
          val.map((item) => pick(item, ['name', 'url', 'uid']))
        )
      },

      deep: true,
    },
  },
  methods: {
    onClickUpload() {
      const fun = () => {
        // 客户端的方法
        setTimeout(() => {
          GoCom.maxWindow()
        }, 300)
        window.removeEventListener('focus', fun)
      }
      window.addEventListener('focus', fun)
    },
    beforeRead(file) {
      if (!Array.isArray(file)) {
        file = [file]
      }
      let error = 0
      for (let i = 0; i < file.length; i++) {
        const name = file[i].name.split('.')[file[i].name.split('.').length - 1]
        if (this.fileType.indexOf(name.toLowerCase()) === -1) {
          this.$toast(`格式错误,支持上传 ${this.fileType.join('、')} 格式的图片`)
          error = 1
          break
        } else {
          error = 0
        }
      }
      if (error === 1) {
        return false
      } else {
        if (this.fileList.length + file.length > 15) {
          this.$toast('最多允许上传 15 张图片')
          return false
        } else {
          return true
        }
      }
    },
    afterRead(res) {
      if (!Array.isArray(res)) {
        res = [res]
      }
      for (let i = 0; i < res.length; i++) {
        let item = res[i]
        item.status = 'uploading'
        let formData = new FormData()
        formData.append('file', item.file)
        axios
          .post(this.uploadURL, formData, {
            headers: {
              Authorization: 'Bearer ' + Sso.getToken(),
              'Content-Type': 'multipart/form-data',
            },
          })
          .then((response) => {
            if (response.data.code === 200) {
              Object.assign(item, {
                status: 'done',
                name: this.baseUrl + response.data.fileName,
                url: this.baseUrl + response.data.fileName,
                uid: Date.now(),
              })
            } else {
              this.uploadFailed(item, response.data.msg)
            }
          })
          .catch(() => {
            this.uploadFailed(item, '网络错误,上传失败!')
          })
      }
    },
    uploadFailed(item, msg) {
      item.status = 'failed'
      this.$toast(msg)
    },
    deleteImg(code) {
      const list = this.fileList
      let item
      let i
      let found = false
      for (i = 0; i < list.length; i++) {
        item = list[i]
        if (item.name === code.name) {
          found = true
          break
        }
      }
      if (found) this.fileList = [...list.slice(0, i), ...list.slice(i + 1)]
    },
    showOneImg(fileList, index) {
      let list = fileList.map((e) => {
        return e.url
      })

      ImagePreview({
        images: list,
        startPosition: index,
      })
    },
  },
}
</script>

<style scoped lang="scss">
.photo-uploader .photo-list {
  padding-top: 0.8rem;
}

.photo-uploader .photo-list .photo-preview .photo-item {
  position: relative;
  padding: 0.8rem;
  border: 1px solid rgba(0, 28, 77, 0.08);
  border-radius: 0.4rem;
  display: flex;
  justify-content: center;
  align-items: center;
}

.photo-uploader .photo-list .photo-button {
  width: 9.6rem;
  height: 9.6rem;
  margin-bottom: 1.6rem;
  background: #f7f8fb;
  box-sizing: border-box;
  border-radius: 0.4rem;
  text-align: center;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.photo-uploader .photo-list .photo-button .photo-text {
  font-style: normal;
  font-weight: normal;
  font-size: 14px;
  line-height: 22px;
  color: #adb1b3;
}

.photo-uploader ::v-deep .van-uploader__preview {
  width: 9.6rem;
  height: 9.6rem;
  border: 1px solid rgba(0, 28, 77, 0.08);
  margin: 0 1rem 1rem 0;
  border-radius: 0.4rem;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
}

.photo-uploader ::v-deep .van-uploader__preview img {
  width: 8.6rem;
  height: 8.6rem;
}

.photo-uploader ::v-deep .van-uploader__preview .van-uploader__preview-delete {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background-color: #adb1b3;
  display: flex;
  align-items: center;
  position: absolute;
  top: -1rem;
  right: -1rem;
}

.photo-uploader ::v-deep .van-uploader__preview .van-uploader__preview-delete .van-uploader__preview-delete-icon {
  font-size: 16px;
  color: #fff;
  font-weight: 600;
  position: absolute;
  top: 0.2rem;
  right: 0.2rem;
}
.empty-block {
  padding-bottom: 1.6rem;
}
</style>

猜你喜欢

发表评论

最新发布