z-upload.vue 7.49 KB
<template>
  <view class="z-upload" :class="{ 'z-upload--disabled': disabled, block }">
    <template v-for="(item, index) in list">
      <template v-if="index < limitCount">
        <view :key="index" class="z-upload__image">
          <image class="image" :src="item" @click="handlePreview(index)" />
          <view v-if="!disabled && !_readonly" class="z-upload__clear" @click="handleDelete(index)">
            <view class="icon-clear" />
          </view>
        </view>
      </template>
    </template>
    <template v-if="!disabled && !_readonly && list.length < limitCount">
      <view class="z-upload__uploader" @click="openActionSheet">
        <view class="icon"><render-svg name="icon-camera" :size="block ? '40rpx' : '50rpx'" /></view>
        <view v-if="title" class="title">{{ title }}</view>
      </view>
    </template>
    <z-bottom-popup v-model="visible" background-color="#F2F2F6">
      <view class="z-upload__item" @click="handleUpload('camera')">
        <text class="z-upload__item-text">拍照</text>
      </view>
      <template v-if="!cameraOnly">
        <view class="z-upload__item" @click="handleUpload('album')">
          <text class="z-upload__item-text">相册</text>
        </view>
      </template>
      <view class="z-upload__item cancel" @click="visible = false">
        <text class="z-upload__item-text">取消</text>
      </view>
    </z-bottom-popup>
    <view style="position: absolute;top: -999999px;">    //把canvas移出视窗
        <canvas :style="{'width':w,'height': h}" id="firstCanvas"></canvas>
    </view>
  </view>
</template>

<script>
import cache from '@/utils/cache';
import config from '@/config';
import { clearURL } from '@/utils/param.js';
import dayjs from 'dayjs';

export default {
  name: 'ZUpload',
  props: {
    value: String,
    limit: {
      type: Number,
      default: 5,
    },
    disabled: Boolean,
    readonly: Boolean,
    title: String,
    block: Boolean,
    watermark: String,
    cameraOnly: Boolean,
    compress: Boolean,
  },
  data() {
    return {
      visible: false,
      w: '',
      h: '',
    };
  },
  computed: {
    limitCount({ limit, block }) {
      if (block) return 1;
      return limit;
    },
    list({ value = '' }) {
      if (value) {
        return value.split(',');
      }
      return [];
    },
    _readonly({ form, readonly }) {
      if (form && form.readonly) {
        return true;
      }
      return readonly;
    },
  },
  methods: {
    // 预览图片
    handlePreview(index) {
      uni.previewImage({ urls: this.list, current: index });
    },
    // 删除图片
    handleDelete(index) {
      uni.showModal({
        title: '提示',
        content: '确定删除这张图片吗?',
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        success: (res) => {
          if (res.confirm) {
            const list = [...this.list];
            list.splice(index, 1);
            this.$emit('input', list.join(','));
          }
        }
      });
    },
    // 打开上传菜单
    openActionSheet() {
      if (!this.disabled && !this.readonly) {
        this.visible = true;
      }
    },
    // 上传
    handleUpload(sourceType) {
      if (this.list.length >= this.limit) {
        return uni.showToast({ icon: 'none', title: `最多允许上传${this.limit}张图片` });
      }
      this.visible = false;
      uni.chooseImage({
        count: this.limit - this.list.length,
        sourceType: [sourceType],
        success: (res) => {
          const tempFilePaths = res.tempFilePaths || [];
          if (tempFilePaths.length > 0) {
            this.uploadImageList(tempFilePaths);
          }
        }
      });
    },
    // 批量上传图片
    uploadImageList(tempFilePaths) {
      uni.showLoading({ title: '上传中...' });
      Promise.all(
        tempFilePaths.map((tempFilePath) => {
          return this.uploadImage(tempFilePath);
        }),
      ).then(function (errors) {
        uni.hideLoading();
        setTimeout(() => {
          uni.showToast({ icon: 'none', title: '上传成功' });
        }, 1000);
      }).catch(function() {
        uni.hideLoading();
      });
    },
    // 执行上传
    uploadImage(filePath) {
      if (!filePath) {
        return;
      }
      const url = clearURL(config.newUploadHost);
      const header = {
        'Authorization': `Bearer ${this.$store.state?.userInfo?.accessToken}`,
      };
      return new Promise((resolve, reject) => {
        uni.uploadFile({
          url,
          name: 'file',
          header,
          formData: {
            module: 'dispatch-ma',
            category: 'opr',
            needCompress: this.compress,
          },
          filePath,
          success: (response) => {
            const { statusCode } = response;
            const data = JSON.parse(response.data.replace(/\ufeff/g, '') || '{}');
            const result = data.result || [];
            const url = result[0];
            if (url) {
              let list = [...this.list];
              list.push(url);
              this.$emit('input', list.join(','));
              resolve(url);
            }
          },
          fail: (error) => {
            reject(error);
          }
        });
      });
    },
  },
};
</script>

<style lang="less">
.z-upload {
  box-sizing: border-box;
  display: inline-flex;
  flex-wrap: wrap;
  margin-bottom: -20px;
  &__item {
    height: 100rpx;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    border-bottom: 1px solid rgba(151, 151, 151, 0.1);
    background-color: #ffffff;
    &.cancel {
      margin-top: 40rpx;
      border-top: 1px solid rgba(151, 151, 151, 0.1);
    }
  }
  &.block {
    .z-upload__uploader,
    .z-upload__image {
      width: 100%;
    }
    .z-upload__uploader,
    .z-upload__image {
      height: 65px;
      margin-right: 0;
    }
  }
  &__uploader {
    margin-right: 30rpx;
    margin-bottom: 30rpx;
    box-sizing: border-box;
    background: #f6f8fb;
    border-radius: 10px;
    padding: 15px;
    width: 145rpx;
    height: 145rpx;
    background: #f6f8fb;
    border-radius: 10px;
    border: 1px solid #dddddd;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    .title {
      font-size: 14px;
      font-weight: 400;
      color: #999999;
    }
  }
  &__image {
    margin-right: 30rpx;
    margin-bottom: 30rpx;
    box-sizing: border-box;
    background: #f6f8fb;
    border-radius: 10px;
    width: 145rpx;
    height: 145rpx;
    .image {
      border-radius: 10px;
      height: 100%;
      width: 100%;
      object-fit: cover;
    }
    position: relative;
  }
  &__clear {
    position: absolute;
    top: -8px;
    right: -8px;
    .icon-clear {
      height: 20px;
      width: 20px;
      background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsLXJ1bGU9Im5vbnplcm8iIGZpbGw9Im5vbmUiPjxjaXJjbGUgZmlsbC1vcGFjaXR5PSIuOSIgZmlsbD0iIzJCMkEyNyIgY3g9IjIwIiBjeT0iMjAiIHI9IjIwIi8+PHBhdGggZD0iTTI4LjM2IDEyLjEwNWExLjUgMS41IDAgMCAxIC4wOTcgMi4wMTRsLS4wOTcuMTA3LTYuMDExIDYuMDEgNi4wMSA2LjAxMWExLjUgMS41IDAgMCAxLTIuMDE0IDIuMjE5bC0uMTA3LS4wOTgtNi4wMS02LjAxLTYuMDEgNi4wMWExLjUgMS41IDAgMCAxLTIuMjE5LTIuMDE0bC4wOTctLjEwNyA2LjAxLTYuMDExLTYuMDEtNi4wMWExLjUgMS41IDAgMCAxIDIuMDE1LTIuMjE4bC4xMDcuMDk3IDYuMDEgNi4wMSA2LjAxLTYuMDFhMS41IDEuNSAwIDAgMSAyLjEyMiAweiIgZmlsbD0iI0ZGRiIvPjwvZz48L3N2Zz4=');
      background-repeat: no-repeat;
      background-size: contain;
    }
  }
}
</style>