vue3+vantui 图片压缩

<template>
  <div class="wrap">
    <div class="container">
      <div class="titleLine">住院费用</div>
      <div class="outBlock">
        <div class="timeLine blockTitle">
          <span>住院发票(必传)</span>
          <span>编辑</span>
        </div>
        <van-uploader class="uploader" multiple v-model="fileList" accept=".png, .jpg, .jpeg" preview-size="50px" :after-read="afterRead" @delete="deleteFile" />
      </div>
    </div>
    <div class="notice">
      注:
      <br />您最多可上传30张
    </div>

    <div class="n-uploadMsg" v-show="uploadConf.show">
      <div>
        <h3>上传中</h3>
        <p>请耐心等待</p>
        <p>当前进度{{ uploadOk }}/{{ fileList.length }}</p>
        <footer>
          <button @click="cancelUpload">取消上传</button>
          <button v-if="uploadConf.state !== 'pause'" @click="uploadConf.state = 'pause'">暂停</button>
          <button v-else @click="uploadAll">继续上传</button>
        </footer>
      </div>
    </div>

    <button id="btn" :disabled="!fileList.length" @click="uploadAll">保存并上传</button>
  </div>
</template>

<script>
import { Toast, Uploader } from 'vant'
import { reactive, computed } from 'vue'
import { uploadClaimImage } from '@/api/claimApi.js'
export default {
  components: {
    [Uploader.name]: Uploader
  },
  beforeMount() {
    this.formData = new FormData()
  },
  setup() {
    const fileList = reactive([])
    const uploadConf = reactive({
      show: false,
      state: 'ready'
    })
    const uploadOk = computed(() => fileList.filter(v => v.status).length)

    const afterRead = async item => {
      const arr = Object.prototype.toString.call(item) === '[object Array]' ? item : [item]

      for (const v of arr) {
        if (v.file.size / 1024 / 1024 > 1) {
          const q = (1 / v.file.size) * 1024 * 1024
          v.url = await compressImg({ src: v.content, q })
          if (v.url) {
            v.orgContent = v.content
            v.content = v.url
            v.q = q
            v.orgFile = v.file
            v.file = base64ToFile(v.url, v.file.name)
            console.log(`原始大小:${v.orgFile.size}, 压缩大小${v.file.size}`)
          } else v.url = v.content
        }
        fileList.push(v)
      }
      console.log(fileList)
    }

    const uploadAll = async () => {
      if (!fileList.length || uploadConf.state === 'uploading') return

      uploadConf.show = true
      uploadConf.state = 'uploading'

      const form = new FormData()
      form.set('orderId', 'dd20220622010213912312/BBY012/070008')
      for (const item of fileList.filter(v => !v.status)) {
        console.log(item)
        form.set('file', item.file) //file对象的
        form.set('file2', item.url) //base64的
        const res = await uploadClaimImage(form).catch(() => false)
        if (uploadConf.state !== 'uploading') return
        item.status = res ? 'done' : 'failed'
      }

      Toast('上传完毕')
      uploadConf.show = false
      uploadConf.state = 'ready'
    }
    const cancelUpload = () => {
      uploadConf.show = false
      uploadConf.state = 'ready'
      fileList.forEach(v => (v.status = ''))
    }

    const deleteFile = item => {
      const idx = fileList.findIndex(v => v.file === item.file)
      idx >= 0 && fileList.splice(idx)
      return true
    }

    const compressImg = async ({ src, q = 1, w, h, type = 'image/jpeg' }) => {
      if (q > 1 || q <= 0) q = 1
      const [img, c] = [new Image(), document.createElement('canvas')]

      img.src = src
      img.style.position = 'fixed'
      img.style.left = '-9999px'

      if (w) img.style.width = w
      if (h) img.style.height = h
      document.body.appendChild(img)

      let res = await new Promise(r => ((img.onload = () => r(1)), (img.onerror = () => r(0))))
      if (!res) return false

      w = c.width = img.clientWidth * q
      h = c.height = img.clientHeight * q
      const ctx = c.getContext('2d')

      ctx.drawImage(img, 0, 0, w, h)
      res = c.toDataURL(type, 1)
      document.body.appendChild(img)
      img.remove()
      c.remove()

      return res
    }
    const base64ToUnit8 = s => {
      const arr = s.split(','),
        binary = atob(arr[1]),
        u8 = new Uint8Array(binary.length)
      let i = binary.length
      while (i--) {
        u8[i] = binary.charCodeAt(i)
      }
      return u8
    }
    const base64ToFile = (s, fileName) => {
      const arr = s.split(',')
      return new File([base64ToUnit8(s)], fileName || `文件.${arr[0].match(/\/([^\\/;]+);/)[1]}`, { type: arr[0].match(/:([^]*);/)[1] })
    }

    return {
      fileList,
      picList,
      afterRead,
      uploadConf,
      uploadAll,
      uploadOk,
      cancelUpload,
      deleteFile
    }
  }
}
</script>

<style lang="less" scoped>
.wrap {
  .container {
    height: 100%;
    font-size: 28/100rem;
    color: #808080;
    background: #f0f0f0;

    .titleLine {
      color: #09b6f2;
      margin: 0.2rem;
    }

    .outBlock {
      margin: 0 10/100rem 20/100rem 10/100rem;
      background: #ffffff;

      .timeLine {
        padding: 10px 15px;
        line-height: 24px;
        font-size: 14px;
        color: #09b6f2;
        height: 24px;
        position: relative;
        display: flex;
        justify-content: space-between;

        &:after {
          content: ' ';
          position: absolute;
          pointer-events: none;
          box-sizing: border-box;
          left: 15px;
          right: 0;
          bottom: 0;
          -webkit-transform: scaleY(0.5);
          transform: scaleY(0.5);
          border-bottom: 1px solid #ebedf0;
        }
      }
    }

    .van-uploader {
      padding: 0.26667rem 0.4rem;
    }
  }

  .notice {
    padding: 10px 15px;
    color: #ff4444;
    margin: 0.2rem;
  }

  #btn {
    margin: 0.4rem 7%;
    width: 86%;
    line-height: 0.8rem;
    border-radius: 7px;
    color: #fff;
    text-align: center;
    outline: none;
    background: #09b6f2;
    border: none;
  }

  .n-uploadMsg {
    width: 100%;
    height: 100vh;
    background: rgba(0, 0, 0, 0.6);
    position: fixed;
    left: 0;
    top: 0;
    display: flex;
    align-items: center;
    justify-content: center;

    > div {
      width: 70vw;
      border-radius: 2vw;
      overflow: hidden;
      background: #fff;

      > h3 {
        padding: 7vw 0;
        color: #000;
        font-weight: bold;
        font-size: 5vw;
        margin: 0;
        text-align: center;
      }

      > p {
        font-size: 3.6vw;
        margin: 0;
        line-height: 1.6;
        color: #555;
        text-align: center;
      }

      > footer {
        display: flex;
        border-top: 1px solid #ddd;
        margin-top: 7vw;
        height: 10vw;
        font-size: 4vw;
        color: #09b6f2;
        align-items: stretch;

        > button {
          flex: 0 0 50%;
          border: 0;
          background: none;
          color: inherit;
          font-size: inherit;

          & + button {
            border-left: 1px solid #ddd;
          }
        }
      }
    }
  }
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

记忆怪 bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值