Vue3图片裁剪(VueCropper)+Elementplus->upload(手动上传)+compressorjs图片压缩

1,057 阅读1分钟
image.png image.png image.png

实现自动上传

image.png

// 模板

<template>
    <div class="flex photo">
          <span><span style="margin-right: 4px;color:#f56c6c;">*</span>照片</span>
          <el-upload 
             accept="image/jpeg,image/png,image/jpg" 
             class="avatar-uploader" 
             :class="(isClickSubmit && !imageUrl) ? 'el-upload' : 'avatar-uploader'" 
             action="#" 
             :auto-upload="false" 
             :http-request="customLoad" 
             :disabled="disabledUpload"  // 是否禁用上传可选择:show-file-list="false" 
             :on-change="avatarChange"
           >
            <img v-if="imageUrl" :src="imageUrl" class="avatar"/>
            <el-icon v-else class="avatar-uploader-icon">
              <Plus />
            </el-icon>
          </el-upload>
          // 这一行可以忽略
          <div v-if="isClickSubmit && !imageUrl" class="tip-word">请填充该项表单</div>
    </div>
    
    <el-dialog 
       v-model="cropperDialog" 
       @close="close" 
       :close-on-click-modal="false" 
       title="图片裁剪" 
       width="500px"
    >
        <div style="margin-bottom:20px;height:300px;">
          <VueCropper
            ref="cropper"
            :img="option.img"
            :outputSize="option.size"
            :outputType="option.outputType"
            :info="true"
            :full="option.full"
            :fixed="option.fixed"
            :fixedNumber="option.fixedNumber"
            :can-move="option.canMove"
            :can-move-box="option.canMoveBox"
            :fixed-box="option.fixedBox"
            :original="option.original"
            :auto-crop="option.autoCrop"
            :auto-crop-width="option.autoCropWidth"
            :auto-crop-height="option.autoCropHeight"
            :center-box="option.centerBox"
            :high="option.high"
            mode="cover"
            :max-img-size="option.max"
          ></VueCropper>
        </div>
        <div style="text-align: right;">
          <el-button type="primary" @click="screenshot">截取</el-button>
        </div>
  </el-dialog>
</template>

<script setup>
import Compressor from 'compressorjs'
import { ref, reactive } from 'vue'
import api from '@/services/api'
import request from '@/services/request'

const cropper = ref(null)
const cropperDialog = ref(false)
const picName = ref('') // 图片名称
const option = reactive({
   img: '',
   size: 1,
   full: true,
   outputType: 'png',
   canMove: true,
   fixedBox: false,
   original: false,
   canMoveBox: true,
   autoCrop: true,
   // 只有自动截图开启 宽度高度才生效
   autoCropWidth: 150,
   autoCropHeight: 150,
   centerBox: true,
   high: true,
   max: 99999,
   fixed: true,
   fixedNumber: [5, 4]
})

// Blob转base64格式
function blobToBase64 (blob) {
  return new Promise((resolve, reject) => {
    const fileReader = new FileReader()
    fileReader.onload = (e) => {
      resolve(e.target.result)
    }
    // readAsDataURL
    fileReader.readAsDataURL(blob)
    fileReader.onerror = () => {
      reject(new Error('blobToBase64 error'))
    }
  })
}

// 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
const avatarChange = response => {
  picName.value = response.name
  // 打开弹窗
  cropperDialog.value = trueVueCropper截图的img赋值,让弹窗内的图片显示
  option.img = URL.createObjectURL(response.raw)
}

// 点击截取后触发
const screenshot = () => {
  cropper.value.getCropBlob(async (data) => {
    console.log('裁剪前图片大小:', data.size)
    const base64 = await blobToBase64(data)
    imageUrl.value = base64

    // eslint-disable-next-line no-new
    /*
      * 1.图片压缩,接收类型:File或Blob
        2.要使quality生效,被压缩图片的类型需要是`image/jpeg`或`image/webp`
        3.convertSize生效,被压缩图片类型:`image/webp`
    */
    new Compressor(new window.File([data], picName.value, { type: 'image/webp' }), {
      quality: 0.7,
      convertSize: 4883,
      success (result) {
        console.log('裁剪图片压缩后大小:', result.size)
        customLoad(new window.File([result], picName.value, { type: data.type }))
      },
      error (err) {
        console.log(err.message)
      }
    })
    cropperDialog.value = false
  })
}

// 自动上传
const customLoad = async (file) => {
  // 自个调用接口(文件流上传,后台返回图片的相对路径)
  const { data: { data: { avatar } } } = await request(api.YG_UPLOAD, { file }, { headers: { 'Content-Type': 'multipart/form-data' } })
  // ruleForm用于后续的表单提交
  ruleForm.avatar = avatar
}
</script>
样式

.photo {
  position: relative;
  margin-bottom: 20px;
  align-items: center;
  text-align: right;
  & > span {
    margin-right: 12px;
    width: 109px;
    font-size: 14px;
    color: #606266;
  }
  & > .tip-word {
    position: absolute;
    bottom: -15px;
    left: 120px;
    color:#f56c6c;
    font-size: 12px;
  }
}

.avatar-uploader .avatar {
  width: 178px;
  height: 178px;
  display: block;
}

.el-upload {
  border: 1px solid red;
  border-radius: 5px;
}

.avatar-uploader .el-upload {
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: var(--el-transition-duration-fast);
}

.el-icon.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  text-align: center;
}

详情点击这里