实现自动上传
// 模板
<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 = true
给VueCropper截图的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;
}