最终效果如下: vue3项目,使用了element-plus组件库,和vue-cropper头像上传插件,实现了一个头像组件 插件文档:www.npmjs.com/package/vue... 同时参考了文章:blog.csdn.net/m0_62317155...
vue-cropper一个奇怪的bug是,使用此插件就不能导入vue的h函数,否则无法正常工作。另外,vue-cropper没有ts类型声明,所以本头像组件的lang是js
本头像组件代码如下,可根据需要复制使用:
html
<!-- 修改头像组件 -->
<script setup lang="js">
import Avatar from '@/assets/images/defaultAvatar.png'
import { useUserStore } from '@/stores'
import { ref, reactive, computed } from 'vue'
import { postUploadAvatar } from '@/api/user/index.ts' // 上传头像的api
// 头像图片截取合适尺寸后上传
import 'vue-cropper/dist/index.css'
import { VueCropper } from "vue-cropper"
const root = ref() // 本组件根元素,ElMessage渲染用
const userStore = useUserStore()
// 原本的头像
const originalAvatar = computed(() => {
if (userStore.userInfo && userStore.userInfo.avatar) return userStore.userInfo.avatar
else return Avatar
})
// 裁剪上传插件ref
const cropper = ref()
// 裁剪上传头像尺寸插件参数
const options = reactive({
avatar: Avatar,
outputType: 'jpg,jpeg,png',
autoCrop: true,
fixed: true,
fixedNumber: [1, 1],
canMove: false,
centerBox: true
})
// 当上传图片多于1张时进行处理
const upload = ref()
const onExceed = (files) => {
upload.value.clearFiles()
const file = files[0]
upload.value.handleStart(file)
}
// 是否在进行上传操作
const isUploading = ref(false)
// 当选中了图片要上传时
const onAvatarChange = (uploadObj) => {
if (uploadObj.raw.size <= (1024 ** 2) * 10) {
const fileReader = new FileReader()
fileReader.onload = function (e) {
options.avatar = e.target.result
}
fileReader.readAsDataURL(uploadObj.raw)
isUploading.value = true
} else {
upload.value.clearFiles()
ElMessage.error({ message: '上传图片大小不能超过10MB', appendTo: root.value })
}
}
// 点击上传按钮,上传头像
const uploadAvatar = () => {
// 调用插件的获取截图函数
cropper.value.getCropBlob(async (data) => {
try {
const imgType = data.type.substring(6)
const tempFile = new File([data], 'avatar.' + imgType, { type: data.type, lastModified: Date.now() })
const responseUrl = await postUploadAvatar(tempFile)
userStore.userInfo.avatar = responseUrl
isUploading.value = false
ElMessage.success({ message: '修改成功', appendTo: root.value })
} catch (e) {
ElMessage.error({ message: '上传失败', appendTo: root.value })
}
})
}
// 取消上传头像
const cancelUpload = () => {
options.avatar = ''
isUploading.value = false
}
</script>
<template>
<div style="position: relative;" ref="root">
<div v-if="!isUploading">
<el-tooltip effect="light" content="点击上传头像" placement="top">
<el-upload ref="upload" class="avatar-upload" accept=".jpeg,.jpg,.png" :limit="1" :auto-upload="false"
:on-exceed="onExceed" :show-file-list="false" :on-change="onAvatarChange">
<img class="avatar-img" :src="originalAvatar" alt="" @click="changeAvatar">
</el-upload>
</el-tooltip>
</div>
<div v-else>
<VueCropper class="avatar-editing" ref="cropper" :img="options.avatar" :outputType="options.outputType"
:autoCrop="options.autoCrop" :fixed="options.fixed" :fixedNumber="options.fixedNumber"
:canMove="options.canMove" :centerBox="options.centerBox" />
<span class="avatar-editing-span" @click="uploadAvatar">上传</span>
<span class="avatar-editing-span" @click="cancelUpload">取消</span>
</div>
</div>
</template>
<style scoped lang="scss">
.avatar-img {
display: inline-block;
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
margin-bottom: 5px;
width: 130px;
height: 130px;
border-radius: 50%;
cursor: pointer;
}
.avatar-editing {
display: inline-block;
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
margin-bottom: 5px;
width: 130px;
height: 130px;
}
.avatar-editing-span {
display: inline-block;
position: absolute;
left: 50%;
top: 55px;
transform: translateX(85px);
color: #1764FF;
cursor: pointer;
&:last-child {
transform: translateX(130px);
}
}
:deep(.el-message) {
transform: translateX(16px);
}
</style>