在vue3中使用vue-cropper完成头像裁剪上传图片功能

最终效果如下: 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>
相关推荐
Asort15 分钟前
JavaScript 从零开始(六):控制流语句详解——让代码拥有决策与重复能力
前端·javascript
无双_Joney33 分钟前
[更新迭代 - 1] Nestjs 在24年底更新了啥?(功能篇)
前端·后端·nestjs
在云端易逍遥35 分钟前
前端必学的 CSS Grid 布局体系
前端·css
ccnocare36 分钟前
选择文件夹路径
前端
艾小码37 分钟前
还在被超长列表卡到崩溃?3招搞定虚拟滚动,性能直接起飞!
前端·javascript·react.js
闰五月37 分钟前
JavaScript作用域与作用域链详解
前端·面试
泉城老铁41 分钟前
idea 优化卡顿
前端·后端·敏捷开发
前端康师傅41 分钟前
JavaScript 作用域常见问题及解决方案
前端·javascript
司宸43 分钟前
Prompt结构化输出:从入门到精通的系统指南
前端