实现效果:
鼠标悬浮到头像上,下方出现修改头像
点击修改头像出现弹框,弹框中可上传头像,并支持头像的裁剪及预览
实现方式:
1.tempalte中
<div class="img-box"><img v-if="avatarImgUrl" :src="avatarImgUrl" class="avatar" /><div class="text" @click="toFixImg()">修改头像</div></div>
注意:用服务端做渲染的同学v-if="avatarImgUrl"必须添加,不然会因水合作用图片出现问题。
<el-dialogv-model="dialogVisible"title="修改图片"width="700"@close="cancelFixAvatar":close-on-click-modal="false"><inputtype="file"accept="image/*"@change="onFileChange"id="myFileInput"style="display: none"ref="fileInput"/><!-- 触发文件选择的按钮 --><button type="button" class="change-img" @click="triggerFileInput">选择图片</button><!-- 图片裁剪区域 --><div v-if="imageUrl" class="cropper-img"><vue-cropper:key="imageKey"ref="cropper":src="imageUrl"style="width: 300px; height: 300px":options="cropperOptions"/><div @click="cropImage" class="confirm-btn">点击预览</div><!-- 显示裁剪后的图片(可选) --><div v-if="croppedImageUrl" class="cropped"><img:src="croppedImageUrl"alt="Cropped Image"style="width: 200px; height: 200px; border-radius: 50%"/></div></div><template #footer><div class="dialog-footer"><el-button @click="cancelFixAvatar">取消</el-button><el-button type="primary" @click="fixAvatar"> 确定 </el-button></div></template></el-dialog>
以上为点击修改头像出现的弹框,实现裁剪图片主要使用的是cropperjs插件,需先安装此插件。
npm install cropperjs --save
2.script中
引入相关文件及定义变量
import 'cropperjs/dist/cropper.css';
import VueCropper from 'vue-cropperjs';
const dialogVisible = ref(false);
const avatarImgUrl = ref('');
const imageUrl = ref('');
const croppedImageUrl = ref('');
const cropperOptions = ref({aspectRatio: 1, // 设置裁剪框的比例viewMode: 1, // 限制图片的拖动范围// ...其他选项
});
const token = ref('');
const croppedImg = ref('');
const imageKey = ref(0); // 使用 key 来强制重新渲染
const cropper = ref(null);
定义使用的相关方法
// 点击“修改头像”
const toFixImg = () => {dialogVisible.value = true;nextTick(() => {document.getElementById('myFileInput').addEventListener('change', onFileChange);});
};// 监听上传的头像变化
const onFileChange = (e) => {const file = e.target.files[0];if (!file) return;const reader = new FileReader();reader.onload = (e) => {imageUrl.value = e.target.result;imageKey.value += 1;};reader.readAsDataURL(file);
};
// 再次点击“选择图片”替换
const triggerFileInput = () => {fileInput.value.click();
};
// 裁剪图片
const cropImage = () => {cropper.value.getCroppedCanvas().toBlob((blob) => {croppedImageUrl.value = URL.createObjectURL(blob);let file = new File([blob], 'test.png', { type: blob.type });// 这里添加将blob发送到服务器的逻辑,根据个人情况,此处调用的后端接口const formData = new FormData();formData.append('files', file);formData.append('folder', 'avatarArr');let uploadFileRequest = new Request(config.public.baseUrl +'xxxx',{method: 'post',headers: {Authorization: token.value,},body: formData,},);fetch(uploadFileRequest).then((response) => {let result = response.text();result.then((res) => {const resdata = JSON.parse(res);if (resdata.code == 200) {croppedImg.value = resdata.data[0].fileAddr;}});});}, 'image/jpeg');
};
// 修改头像弹框的确定按钮
const fixAvatar = () => {if (!croppedImg.value || croppedImg.value == '') {ElMessage({message: '请预览效果后点击保存',type: 'warning',customClass: 'mzindex',});return;}
// 此处调用后端提供的保存接口};
// 修改弹框的取消按钮
const cancelFixAvatar = () => {imageUrl.value = '';croppedImageUrl.value = '';imageKey.value = 0;dialogVisible.value = false;
};// 监听头像的改变
watch(() => store.fetchImage(),(newVal) => {if (newVal) {avatarImgUrl.value = `${config.public.baseUrl}/digit-trade-platform-system/file/${newVal}`;}},{ immediate: true },{ deep: true },
);
3.style中
.img-box {position: relative;display: inline-block;width: 100px; /* 或者你需要的大小 */height: 100px; /* 和宽度相同,形成圆形 */.avatar {width: 100%;height: 100%;object-fit: cover;border-radius: 50%;cursor: pointer;}.icon{position: absolute;bottom: 0;right: 0;}.text {position: absolute;bottom: 0; /* 文本定位在底部 */left: 50%; /* 水平居中 */width: 60%;padding: 5px 0;transform: translateX(-50%); /* 文本水平居中 */background-color: rgba(0, 0, 0, 0.5); /* 半透明背景 */color: white;text-align: center;border-radius: 0 0 50% 50% / 50%; /* 底部平直的圆角 */opacity: 0;transition: opacity 0.3s ease; /* 添加过渡效果 */font-size: 11px;cursor: pointer;}/* 鼠标悬浮在头像上时显示文本 */.avatar:hover + .text,.text:hover {opacity: 1; /* 鼠标悬浮时显示文本 */}}.change-img {background-color: $mainColor; /* Green */border: none;color: white;text-align: center;text-decoration: none;display: inline-block;font-size: 16px;margin: 4px 2px;cursor: pointer;border-radius: 4px;width: 100px;height: 40px;line-height: 40px;}.cropper-img {width: 100%;margin: 20px 0;display: flex;.confirm-btn {margin: 140px 20px 0 20px;width: 100px;height: 36px;background-color: $mainColor;color: #fff;text-align: center;line-height: 36px;border-radius: 4px;cursor: pointer;}.cropped {margin-top: 60px;}}