组件
<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'"