项目背景
基于vue3和ts,使用VueCropper完成对图片裁剪组件封装
<template>
<el-dialog
:title="title"
:width="width"
:append-to-body="true"
:close-on-click-modal="false"
v-model:visible="showDialog"
v-model="showDialog"
@close="hiddenView"
>
<div class="content">
<div class="cropper-box">
<vue-cropper
ref="cropper"
:can-move="true"
:auto-crop="true"
:fixed="true"
:fixed-number="fixedNumber"
:img="cropperImg"
output-type="png"
@realTime="realTime"
/>
</div>
<div class="preview">
<div class="preview-name">预览</div>
<img :style="{ width: previewWidth, height: previewHeight, 'border-radius': previewRadius }" :src="previewImg" class="preview-img" />
</div>
</div>
<div class="dialog-footer">
<el-button size="small" type="primary" @click="submiteImage()">{{ saveButtonTitle }}</el-button>
</div>
</el-dialog>
</template>
<script lang="ts" setup>
import 'vue-cropper/dist/index.css'
import { VueCropper } from 'vue-cropper'
import { ref, watch, onMounted } from 'vue'
const props = defineProps({
width: {
type: String,
default: '450px'
},
title: {
type: String,
default: '编辑头像'
},
saveButtonTitle: {
type: String,
default: '开始上传'
},
show: {
type: Boolean,
default: false
},
fixedNumber: {
type: Array,
default: () => {
return [1, 1]
}
},
previewWidth: {
type: String,
default: '70px'
},
previewHeight: {
type: String,
default: '70px'
},
previewRadius: {
type: String,
default: '35px'
},
file: [File],
image: String
})
const emit = defineEmits(['close', 'save'])
const showDialog = ref<Boolean>(false)
const cropperImg = ref<String | undefined>('')
const previewImg = ref<any>('')
const cropper = ref<any>(null)
watch(
() => props.show,
(newValue) => {
showDialog.value = newValue
},
{
deep: true,
immediate: true
}
)
watch(
() => props.image,
(newValue) => {
cropperImg.value = newValue
}
)
onMounted(() => {
cropperImg.value = props.image
})
function realTime() {
cropper.value.getCropData((cropperData: any) => {
previewImg.value = cropperData
})
}
function submiteImage() {
// 获取截图的blob数据
cropper.value.getCropBlob((data: Blob) => {
emit('save', {
blob: data,
file: props.file,
image: previewImg.value
})
hiddenView()
})
}
function hiddenView() {
emit('close')
}
</script>
<style lang="scss" scoped>
.cropper-box {
width: 300px;
height: 300px;
margin-right: 15px;
}
.preview {
position: absolute;
bottom: 0;
right: 0;
.preview-name {
margin-bottom: 8px;
font-size: 13px;
color: #666;
}
.preview-img {
display: block;
}
}
.content {
position: relative;
padding: 0 30px;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
padding-top: 20px;
}
</style>
调用
<input id="uploadAvatarBtn" type="file" accept="image/png, image/jpeg, image/gif, image/jpg" style="display: none" @change="uploadFile" />
<Editimg :show="showEditImage" :file="editFile" :image="editImage" @save="submitImage" @close="showEditImage = false" />
<script>
//修改头像
function setAvatar() {
document.getElementById('uploadAvatarBtn')?.click()
}
/**
* 图片操作
* @param event
*/
function uploadFile(event: Event) {
const files: any = (event.target as HTMLInputElement).files
const file = files[0]
const reader = new FileReader()
reader.onload = (e: any) => {
let result
if (typeof e.target?.result === 'object') {
// 把Array Buffer转化为blob 如果是base64不需要
result = window.URL.createObjectURL(new Blob([e.target.result!]))
} else {
result = e.target?.result
}
editImage.value = result
editFile.value = file
showEditImage.value = true
e.target!.value = ''
}
reader.readAsDataURL(file)
}
/**
* 上传提交头像修改
* @param data
*/
async function submitImage(data: any) {
const param = {
id: 1,
file: data.blob
}
const response = await adminUsersUpdateImgAPI(param)
AccountInfoFormData.img = response.host + response.file_path
userStore.setAvatar(AccountInfoFormData.img)
ElMessage.success('上传成功!')
}
</script>