使用vue-cropper作头像编辑工具

This commit is contained in:
bob
2024-11-13 17:36:54 +08:00
parent 9bf44bbefd
commit 07d5a69f53
3 changed files with 75 additions and 46 deletions

View File

@@ -24,6 +24,7 @@
"protobufjs": "^7.4.0",
"uuid": "^10.0.0",
"vue": "^3.4.29",
"vue-cropper": "^1.1.1",
"vue-router": "^4.3.3"
},
"devDependencies": {

8
pnpm-lock.yaml generated
View File

@@ -38,6 +38,9 @@ importers:
vue:
specifier: ^3.4.29
version: 3.4.38
vue-cropper:
specifier: ^1.1.1
version: 1.1.1
vue-router:
specifier: ^4.3.3
version: 4.4.3(vue@3.4.38)
@@ -1603,6 +1606,9 @@ packages:
terser:
optional: true
vue-cropper@1.1.1:
resolution: {integrity: sha512-WsqKMpaBf9Osi1LQlE/5AKdD0nHWOy1asLXocaG8NomOWO07jiZi968+/PbMmnD0QbPJOumDQaGuGa13qys85A==}
vue-demi@0.14.10:
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
engines: {node: '>=12'}
@@ -3145,6 +3151,8 @@ snapshots:
fsevents: 2.3.3
sass: 1.77.8
vue-cropper@1.1.1: {}
vue-demi@0.14.10(vue@3.4.38):
dependencies:
vue: 3.4.38

View File

@@ -2,57 +2,74 @@
import { ref } from 'vue'
import { userStore } from '@/stores'
import { Plus, Upload } from '@element-plus/icons-vue'
import selectAvatar from '@/assets/select_avatar.jpg'
import { userUploadAvatarService } from '@/api/user'
import { ElMessage } from 'element-plus'
import 'vue-cropper/dist/index.css'
import { VueCropper } from 'vue-cropper'
defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue', 'update:newAvatar'])
const userData = userStore()
const uploadRef = ref()
const imgUrl = ref(userData.user.avatar)
const cropper = ref()
const img = ref('')
const previewImg = ref('')
const isLoading = ref(false)
let selectedFile
const onSelected = (file) => {
imgUrl.value = URL.createObjectURL(file.raw)
selectedFile = file.raw
}
const onSuccess = () => {}
const beforeUpload = () => {}
const onUpload = async () => {
if (!selectedFile) {
ElMessage.warning('您还未选择新头像!')
return
}
isLoading.value = true
try {
const res = await userUploadAvatarService({ file: selectedFile })
emit('update:newAvatar', res.data.data)
emit('update:modelValue', false)
selectedFile = null
ElMessage.success('头像上传成功')
} catch (error) {
/* empty */
} finally {
isLoading.value = false
}
}
const fileName = ref('')
// 打开的时候触发
const onOpen = () => {
imgUrl.value = userData.user.avatar
fileName.value = userData.user.avatar?.split('/').pop()
img.value = userData.user.avatar
previewImg.value = img.value
}
// 关闭的时候触发
const onClose = () => {
isLoading.value = false
}
// 选择了文件触发
const onSelected = (file) => {
fileName.value = file.name
img.value = URL.createObjectURL(file.raw)
previewImg.value = img.value
}
const onUpload = async () => {
cropper.value.getCropBlob(async (blob) => {
const lastDotIndex = fileName.value.lastIndexOf('.')
const prefix = fileName.value.substring(0, lastDotIndex)
const suffix = fileName.value.substring(lastDotIndex)
let file = new File(
[blob],
`${prefix}_${cropper.value.cropW}x${cropper.value.cropH}${suffix}`,
{
type: blob.type,
lastModified: Date.now()
}
)
isLoading.value = true
try {
const res = await userUploadAvatarService({ file: file })
emit('update:newAvatar', res.data.data)
emit('update:modelValue', false)
ElMessage.success('头像上传成功')
} catch (error) {
/* empty */
} finally {
isLoading.value = false
}
})
}
const stopCrop = () => {
cropper.value.getCropData((data) => {
previewImg.value = data
})
}
</script>
<template>
@@ -72,10 +89,21 @@ const onClose = () => {
:auto-upload="false"
:show-file-list="false"
:on-change="onSelected"
:on-success="onSuccess"
:before-upload="beforeUpload"
>
<img :src="imgUrl || selectAvatar" class="avatar" />
<div class="canvas" @click.stop style="width: 400px; height: 400px">
<vueCropper
ref="cropper"
class="avatar"
:img="img"
:full="true"
:autoCrop="true"
:autoCropWidth="250"
:autoCropHeight="250"
:canScale="true"
:centerBox="true"
@mouseup="stopCrop"
></vueCropper>
</div>
</el-upload>
</div>
@@ -83,9 +111,9 @@ const onClose = () => {
<div class="preview-area">
<span style="font-size: 16px">预览</span>
<el-avatar class="preview-100" :src="imgUrl || selectAvatar" />
<el-avatar class="preview-100" :src="previewImg" />
<span>100×100</span>
<el-avatar class="preview-40" :src="imgUrl || selectAvatar" />
<el-avatar class="preview-40" :src="previewImg" />
<span>40×40</span>
</div>
<div class="button-area">
@@ -151,12 +179,4 @@ const onClose = () => {
height: 40px;
}
}
img {
width: 400px;
height: 400px;
object-fit: cover;
text-align: center;
border-radius: 10px;
}
</style>