封装裁剪图片的组件
<template>
<!-- 裁剪 -->
<el-dialog
v-model="imgUpload.dialogCropping"
custom-class="dialog_custom"
title="裁剪图片"
append-to-body
:close-on-press-escape="false"
:show-close="false"
:close-on-click-modal="false"
>
<div>
<div class="h-96 w-full" style="height: 500px">
<vueCropper
ref="cropperRef"
class="vue_cropper_custom"
:img="imgCropping.imageUrl"
:outputType="imgCropping.outputType"
:autoCrop="imgCropping.autoCrop"
:autoCropWidth="imgCropping.autoCropWidth"
:autoCropHeight="imgCropping.autoCropHeight"
:fixed="imgCropping.fixed"
:fixedNumber="imgCropping.fixedNumber"
:centerBox="imgCropping.centerBox"
:full="imgCropping.full"
></vueCropper>
</div>
</div>
<template #footer>
<el-button v-focus @click="handleCropping(false)"> 取消 </el-button>
<el-button type="primary" @click="handleCropping(true)"> 确定 </el-button>
</template>
</el-dialog>
</template>
<script setup>
import { reactive, defineEmits, ref } from "vue";
import { ElMessage } from "element-plus";
const cropperRef = ref(null);
const emit = defineEmits(["imgUpload"]);
const imgCropping = reactive({
imageUrl: "",
// 裁剪生成图片的格式
outputType: "png",
// 是否默认生成截图框
autoCrop: true,
// 上传图片按照原始比例渲染
// original: true,
// 是否输出原图比例的截图
full: false,
// 默认生成截图框宽度
autoCropWidth: 160,
// 默认生成截图框高度
autoCropHeight: 90,
// 是否开启截图框宽高固定比例
fixed: true,
// 截图框的宽高比例
fixedNumber: [16, 9],
// 截图框是否被限制在图片里面
centerBox: true,
});
/** 图片上传 */
const imgUpload = reactive({
// 是否展示裁剪
dialogCropping: false,
isCropping: false, // 判断是否已经截图
// 图片
imageUrl: "",
rawFile: {},
// 图片格式
imgBmp: "image/*",
// 图片名称
imgName: "",
});
const openDialog = (bool, rawFile) => {
console.log(rawFile, "rawFilerawFile");
// 进入裁剪
// 图片名称
imgUpload.imgName = rawFile.name;
imgCropping.imageUrl = URL.createObjectURL(rawFile);
imgUpload.dialogCropping = bool;
imgUpload.rawFile = rawFile;
};
// base64转file
const base64ToFile = (base64, filename = "") => {
const arr = base64.split(",");
let mime = arr[0].match(/:(.*?);/)[1]; // 匹配出图片类型
mime = mime.replace("data:", ""); // 去掉data:image/png;base64 // 去掉url中的base64,并转化为Uint8Array类型
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
};
// 生成裁剪图片
//还有一种getCropBlob转换成blob格式的
const handleCropping = (type) => {
if (type) {
cropperRef.value.getCropData((data) => {
//此时data就是base64格式
// base64转换成二进制格式File
let rawFileAvatar = base64ToFile(data, "avatar.png")
//imgUpload.imageUrl【blob:http://localhost/c653d7c0-cdbe-42e2-9748-48c97350b871】让图片复显的
imgUpload.imageUrl = URL.createObjectURL(rawFileAvatar);
imgUpload.dialogCropping = false;
// 图片信息
emit(
"img-upload",
rawFileAvatar,
imgUpload.imageUrl,
imgUpload.imgName
);
});
} else {
imgUpload.imageUrl = "";
imgUpload.dialogCropping = false;
}
};
// 清除图片
const clearImg = () => {
imgUpload.imageUrl = "";
// emit("img-upload");
};
defineExpose({ openDialog });
</script>
//父组件
template
<el-upload
:class="{ isShowImg:PictureList.value.length > 0 }"
action="#"
list-type="picture-card"
:auto-upload="false"
:limit="1"
:on-exceed="handleExceed"
v-model:file-list="PictureList.valuet"
:on-change="handleChange"
>
<el-icon>
<Plus />
</el-icon>
<template #file="{ file }">
<div>
<img
class="el-upload-list__item-thumbnail"
:src="file.url"
alt=""
/>
<span class="el-upload-list__item-actions">
<span
v-if="!disabled"
class="el-upload-list__item-delete"
@click="handleRemovePicture(file)"
>
<el-icon>
<Delete />
</el-icon>
</span>
</span>
</div>
</template>
</el-upload>
<Cropper ref="cropperRef" @imgUpload="imgUpload" />
<script setup>
const cropperRef = ref(null)
图片上传的change方法:
const handleChange = (data, fileList) => {
let file = data.raw;
const isJpgPng =
file.type === "image/jpeg" ||
file.type === "image/png" ||
file.type === "image/gif";
if (!isJpgPng) {
ElMessage({ message: "上传文件格式只能是jpg/png/gif", type: "warning" });
const currIdx = fileList.indexOf(file);
fileList.splice(currIdx, 1);
return false;
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
ElMessage({ message: "上传头像图片大小不能超过 2MB!", type: "warning" });
const currIdx = fileList.indexOf(file);
fileList.splice(currIdx, 1);
return false;
}
const isSize = new Promise(function (resolve, reject) {
const img = new Image();
img.onload = function () {
const valid = img.width / img.height === 16 / 9;
valid ? resolve() : reject();
};
img.src = URL.createObjectURL(file);
// selectedImageUrl.value = URL.createObjectURL(file);
console.log(selectedImageUrl.value, "selectedImageUrl");
}).then(
() => {
return file;
},
() => {
ElMessage({
message: "上传图片比例只能为16:9, 请进行裁剪!",
type: "warning",
});
const currIdx = PictureList.value.indexOf(file);
PictureList.value.splice(currIdx, 1);
cropperRef.value.openDialog(true, file);
// return Promise.reject();
return false;
}
);
if (fileList.length > 0) {
isImgUploadRef.value.clearValidate();
}
return isJpgPng && isLt2M && isSize;
};
const imgUpload = (data, blob, name) => {
const formData = new FormData();
formData.append("file", data);
console.log(formData);
PictureList.value.push({
name: name,
url: blob,
data: data,
type: "封面",
// response: {
// data: res.data,
// },
});
};