vue+elementui+vueCropper裁剪上传图片背景颜色为黑色解决方案

复制代码
组件
<template>
  <div class="avatar-uploader">
    <!-- 头像展示 -->
    <div class="avatar-show">
      <img :src="avatarUrl" v-if="avatarUrl" class="avatar">
      <i class="el-icon-plus avatar-uploader-icon" v-else></i>
    </div>

    <!-- 上传按钮 -->
    <el-upload
        class="avatar-upload"
        action="#"
        :show-file-list="false"
        :before-upload="beforeAvatarUpload"
        :http-request="handleUpload"
    >
      <el-button size="small" type="primary">点击上传</el-button>
      <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过2MB</div>
    </el-upload>

    <!-- 裁剪对话框 -->
    <el-dialog title="裁剪头像" :visible.sync="dialogVisible" width="800px">
      <div class="cropper-content">
        <div class="cropper">
          <vue-cropper
              ref="cropper"
              :img="cropperImg"
              :output-size="1"
              :output-type="'png'"
              :info="true"
              :full="true"
              :can-move="true"
              :can-move-box="true"
              :fixed="true"
              :fixed-number="[1, 1]"
              :center-box="true"
              :auto-crop="true"
              :auto-crop-width="200"
              :auto-crop-height="200"
          ></vue-cropper>
        </div>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="handleCrop">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import { VueCropper } from 'vue-cropper'

export default {
  components: {
    VueCropper
  },
  props: {
    value: String // 用于v-model绑定
  },
  data() {
    return {
      dialogVisible: false,
      cropperImg: '', // 裁剪图片的base64
      // avatarUrl: this.value || require('@/assets/logo.png') // 默认头像
      avatarUrl: null // 默认头像
    }
  },
  methods: {
    // 上传前校验
    beforeAvatarUpload(file) {
      const isJPG = file.type === 'image/jpeg' || file.type === 'image/png'
      const isLt2M = file.size / 1024 / 1024 < 2

      if (!isJPG) {
        this.$message.error('上传头像图片只能是 JPG/PNG 格式!')
      }
      if (!isLt2M) {
        this.$message.error('上传头像图片大小不能超过 2MB!')
      }
      return isJPG && isLt2M
    },

    // 处理上传
    handleUpload(file) {
      const reader = new FileReader()
      reader.onload = (event) => {
        this.cropperImg = event.target.result
        this.dialogVisible = true
        this.$nextTick(() => {
          this.$refs.cropper.replace(event.target.result)
        })
      }
      reader.readAsDataURL(file.file)
    },

    // 处理裁剪
    handleCrop() {
      this.$refs.cropper.getCropBlob((blob) => {
        // 创建FormData对象上传
        const formData = new FormData()
        formData.append('file', blob, 'avatar.png')
        // 这里替换为你的上传API
        this.$axios.post('/api/upload/avatar', formData, {
          headers: {
            'Content-Type': 'multipart/form-data'
          }
        }).then(response => {
          this.avatarUrl = response.data.url
          this.$emit('input', this.avatarUrl) // 更新v-model
          this.dialogVisible = false
          this.$message.success('头像上传成功!')
        }).catch(error => {
          this.$message.error('头像上传失败!')
          console.error(error)
        })
      })
    },

    // 也可以使用base64格式
    handleCropBase64() {
      this.$refs.cropper.getCropData((data) => {
        this.avatarUrl = data
        this.$emit('input', data) // 更新v-model
        this.dialogVisible = false
        this.$message.success('头像裁剪成功!')
      })
    }
  },
  watch: {
    value(newVal) {
      this.avatarUrl = newVal
    }
  }
}
</script>

<style scoped>
.avatar-uploader {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.avatar-show {
  width: 150px;
  height: 150px;
  border: 1px dashed #d9d9d9;
  border-radius: 50%;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-bottom: 20px;
}

.avatar {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
}

.cropper-content {
  display: flex;
  justify-content: center;
}

.cropper {
  width: 500px;
  height: 500px;
}

.el-upload__tip {
  margin-top: 10px;
  text-align: center;
}
</style>

组件调用

<template>

<div>

<h2>个人头像设置</h2>

<avatar-uploader v-model="avatar"></avatar-uploader>

</div>

</template>

<script>

import AvatarUploader from '@/components/AvatarUploader'

export default {

components: {

AvatarUploader

},

data() {

return {

avatar: '' // 初始头像地址

}

}

}

</script>

注意一定要配置:output-type="'png'"

相关推荐
慧一居士1 小时前
Element Plus 按需引入的配置使用说明和完整示例
前端·vue.js
gis开发之家4 小时前
《Vue3 从入门到大神35篇》Vue3 源码详解(五):effect 依赖收集原理——track 与 trigger 是如何工作的?
javascript·typescript·前端框架·vue3·vue3源码
橘子星5 小时前
我一个前端切图仔,凭什么能在浏览器里跑大模型?
前端·javascript·前端框架
半个落月5 小时前
用 LangChain JS 做可控写作实验:理解温度参数、提示词与异步调用
javascript·人工智能·后端
观远数据5 小时前
Excel到数据资产池:文件数据入湖的治理规范怎么建
前端·javascript·excel
CoderWeen5 小时前
我写了个能一步步点着看的 Dijkstra 可视化项目(Vue3 + Leaflet + Generator)
前端·javascript·vue.js
无人生还6 小时前
第 1 篇:从 Vue3 到 React 的思维转变
前端·vue.js·react.js
就业发动机6 小时前
我用 Tauri 做了一个约 1.5 MB 的开源屏幕标注工具:MarkerOn
vue.js·rust
爱分享的程序猿-Clark6 小时前
【前端分享】vue开发项目,如何实现 从A页面跳转到B页面,并传值!
前端·javascript·vue.js
冰暮流星7 小时前
javascript之String方法介绍
开发语言·javascript·ecmascript