图片裁剪组件

This commit is contained in:
陈红丽 2024-09-02 15:09:25 +08:00
parent c01a5a4fd0
commit f34a697cc9
5 changed files with 66 additions and 14 deletions

View File

@ -3,7 +3,12 @@
<slot name="default"></slot> <slot name="default"></slot>
<template v-if="!$slots.default"> <template v-if="!$slots.default">
<div class="img-cropper-img" @click="openCropper"> <div class="img-cropper-img" @click="openCropper">
<img :src="src" :class="{ circled: circled }" /> <img :src="src" :class="{ circled: circled }" v-if="src"/>
<div class="addImg" v-else>
<el-icon>
<plus />
</el-icon>
</div>
<div class="mask" :class="{ circled: circled }"> <div class="mask" :class="{ circled: circled }">
<el-icon class="el-input__icon"> <el-icon class="el-input__icon">
<UploadOutlined /> <UploadOutlined />
@ -17,6 +22,7 @@
confirmButText="确认上传" confirmButText="确认上传"
:uploadApi="uploadApi" :uploadApi="uploadApi"
:circled="circled" :circled="circled"
@uploadSuccess="handleSuccess"
/> />
</div> </div>
</template> </template>
@ -43,8 +49,10 @@
const iconSize = cssUnit(Math.ceil(parseInt(getWidth) / 4)); const iconSize = cssUnit(Math.ceil(parseInt(getWidth) / 4));
// const emit = defineEmits(['change']); const emit = defineEmits(['uploadSuccess']);
function handleSuccess(value){
emit('uploadSuccess',value)
}
function openCropper() { function openCropper() {
cropperRef.value.showModal(); cropperRef.value.showModal();
} }
@ -61,18 +69,30 @@
overflow: hidden; overflow: hidden;
text-align: center; text-align: center;
position: relative; position: relative;
&-img { &-img {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 50%; border-radius: 50%;
.addImg {
width: v-bind(getWidth);
height: v-bind(getWidth);
border:1px dashed #999999;
display: flex;
justify-content: center;
align-items: center;
.el-icon {
color: #999999;
font-size: v-bind(iconSize);
}
}
.mask { .mask {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(0, 0, 0, 0.6); background: rgba(0, 0, 0, 0.5);
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;

View File

@ -160,7 +160,7 @@
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { basicModal, useModal } from '@/components/Modal'; import { basicModal, useModal } from '@/components/Modal';
import CropperImage from './CropperImage.vue'; import CropperImage from './CropperImage.vue';
import { dataURLtoBlob } from '@/utils/file/base64Conver'; import { dataURLtoBlob ,base64ToFile} from '@/utils/file/base64Conver';
import { isFunction } from '@/utils/is'; import { isFunction } from '@/utils/is';
import { import {
UploadOutlined, UploadOutlined,
@ -251,11 +251,14 @@
} }
const uploadApi = props.uploadApi; const uploadApi = props.uploadApi;
if (uploadApi && isFunction(uploadApi)) { if (uploadApi && isFunction(uploadApi)) {
const blob = dataURLtoBlob(previewSource.value); const file = base64ToFile(previewSource.value,filename);
try { try {
setSubLoading(true); setSubLoading(true);
const result = await uploadApi({ name: 'file', file: blob, filename }); const formData = new FormData()
emit('uploadSuccess', { source: previewSource.value, data: result.data }); formData.append('file',file)
formData.append('name',filename)
const result = await uploadApi(formData);
emit('uploadSuccess', result);
closeModal(); closeModal();
} finally { } finally {
setSubLoading(false); setSubLoading(false);

View File

@ -39,3 +39,19 @@ export function urlToBase64(url: string, mineType?: string): Promise<string> {
img.src = url; img.src = url;
}); });
} }
/**
* base64 to file
* @param dataURL
*/
export function base64ToFile(dataurl:any, filename:any) {
let arr = dataurl.split(",");
let mime = arr[0].match(/:(.*?);/)[1];
let bstr = atob(arr[1]);
let len = bstr.length;
let u8arr = new Uint8Array(len);
while (len--) {
u8arr[len] = bstr.charCodeAt(len);
}
return new File([u8arr], filename, {type: mime});
}

View File

@ -243,7 +243,6 @@ const getMenu = async () => {
const data: any = await getMenuList(); const data: any = await getMenuList();
const menu: any = {id: 0, name: "顶级", children: []}; const menu: any = {id: 0, name: "顶级", children: []};
const lists = buildTree(data) const lists = buildTree(data)
console.log(lists)
menu.children = arrayToTree( menu.children = arrayToTree(
treeToArray(lists).filter((item) => item.type ==0) treeToArray(lists).filter((item) => item.type ==0)
); );

View File

@ -97,14 +97,19 @@
</el-form-item> </el-form-item>
</div> </div>
<el-form-item label="头像" prop="avatar" :rules="{ required: true, message: '请输上传头像', trigger: 'blur' }"> <el-form-item label="头像" prop="avatar" :rules="{ required: true, message: '请输上传头像', trigger: 'blur' }">
<UploadImg @changeFileName="(name)=>formData.avatarName=name" <!-- <UploadImg @changeFileName="(name)=>formData.avatarName=name"
:fileType=" ['image/jpeg', 'image/png', 'image/jpg', 'image/gif']" :fileType=" ['image/jpeg', 'image/png', 'image/jpg', 'image/gif']"
name="user" name="user"
:fileSize="200" :fileSize="200"
:cropper="true" :cropper="true"
v-model:image-url="formData.avatar"> v-model:image-url="formData.avatar">
<template v-slot:tip>支持扩展名: jpg png jpeg;文件大小不超过200M</template> <template v-slot:tip>支持扩展名: jpg png jpeg;文件大小不超过200M</template>
</UploadImg> </UploadImg> -->
<Cropper ref="cropperCircled" :src="formData.avatar" :uploadApi="upload" title="矩形头像上传" @uploadSuccess="uploadSuccess">
<template #avatar>
</template>
</Cropper>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
@ -121,8 +126,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { getUserDetail, userAdd, userUpdate } from '@/api/system/user'; import { getUserDetail, userAdd, userUpdate } from '@/api/system/user';
import UploadImg from '@/components/Upload/Image.vue'; import UploadImg from '@/components/Upload/Image.vue';
import {upload} from '@/api/common'
import { Cropper } from '@/components/Cropper';
import chinaArea from '@/components/ChinaArea/index.vue'; import chinaArea from '@/components/ChinaArea/index.vue';
import { onMounted, reactive, shallowRef } from "vue"; import { ref,onMounted, reactive, shallowRef } from "vue";
import { getRoleAllList } from '@/api/system/role'; import { getRoleAllList } from '@/api/system/role';
import { getDeptList } from '@/api/system/dept'; import { getDeptList } from '@/api/system/dept';
import { getLevelAllList } from '@/api/system/level'; import { getLevelAllList } from '@/api/system/level';
@ -148,7 +155,7 @@ const props = defineProps({
default: 0 default: 0
} }
}); });
const cropperCircled = ref();
const emit = defineEmits(["success", "update:visible"]); const emit = defineEmits(["success", "update:visible"]);
const formData = reactive({ const formData = reactive({
id: 0, id: 0,
@ -184,6 +191,9 @@ const passwordConfirmValidator = (
callback(); callback();
}; };
const uploadSuccess =(data)=>{
formData.avatar = data.fileUrl
}
const dialogClose = () => { const dialogClose = () => {
emit("update:visible", false); emit("update:visible", false);
}; };
@ -240,6 +250,10 @@ const getAllDict = async () => {
list = await getPositionAllList(); list = await getPositionAllList();
optionData.positionList = list ? list : []; optionData.positionList = list ? list : [];
}; };
function cropperCircledImg() {
cropperCircled.value.openCropper();
}
onMounted(() => { onMounted(() => {
getAllDict() getAllDict()
if (props.userId) { if (props.userId) {