图片裁剪

This commit is contained in:
陈红丽 2024-08-29 09:03:54 +08:00
parent 4470ca0774
commit efefd1b348
4 changed files with 238 additions and 9 deletions

View File

@ -58,6 +58,7 @@
"vue-router": "^4.3.2", "vue-router": "^4.3.2",
"vue-types": "^4.2.1", "vue-types": "^4.2.1",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"vue-cropper": "0.5.8",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {

View File

@ -38,7 +38,7 @@
<div class="upload-empty"> <div class="upload-empty">
<slot name="empty"> <slot name="empty">
<el-icon v-if="!loading"><Plus /></el-icon> <el-icon v-if="!loading"><Plus /></el-icon>
<span v-else>上传中{{progress}}%</span> <span v-else>上传中{{progress}}%</span>
<!-- <span>请上传图片</span> --> <!-- <span>请上传图片</span> -->
</slot> </slot>
</div> </div>
@ -51,9 +51,17 @@
:teleported="true" :teleported="true"
v-if="imgViewVisible" v-if="imgViewVisible"
@close="imgViewVisible = false" @close="imgViewVisible = false"
:url-list="[imageUrl.includes('http') ? imageUrl : baseURL + imageUrl]" :url-list="[imageUrl]"
/> />
</div> </div>
<Cropper
v-model:visible="cropperVisible"
v-if="cropperVisible"
:cropperSize="cropperSize"
:oImg="oImg"
@success="cropperSuccess"
:fileName="fileName"
></Cropper>
</template> </template>
<script setup lang="ts" name="UploadImg"> <script setup lang="ts" name="UploadImg">
@ -62,6 +70,7 @@ import { ElNotification, formContextKey, formItemContextKey } from 'element-plus
import type { UploadProps, UploadRequestOptions } from 'element-plus'; import type { UploadProps, UploadRequestOptions } from 'element-plus';
import { generateUUID } from '@/utils/auth'; import { generateUUID } from '@/utils/auth';
import {upload} from '@/api/common' import {upload} from '@/api/common'
import Cropper from '@/components/Upload/cropper.vue'
interface UploadFileProps { interface UploadFileProps {
imageUrl?: string; // ==> imageUrl?: string; // ==>
@ -75,9 +84,11 @@ interface UploadFileProps {
borderRadius?: string; // ==> 8px borderRadius?: string; // ==> 8px
iconSize?: number; iconSize?: number;
name?: string; name?: string;
cropper:Boolean,
cropperSize?:Object
} }
const cropperVisible=ref(false)
// //
const props = withDefaults(defineProps<UploadFileProps>(), { const props = withDefaults(defineProps<UploadFileProps>(), {
imageUrl: '', imageUrl: '',
@ -89,7 +100,9 @@ const props = withDefaults(defineProps<UploadFileProps>(), {
height: '150px', height: '150px',
width: '150px', width: '150px',
borderRadius: '8px', borderRadius: '8px',
name:'' name:'',
cropper:false,
cropperSize:null
}); });
// id // id
@ -122,10 +135,15 @@ interface UploadEmits {
(e: 'update:imageUrl', value: string): void, (e: 'update:imageUrl', value: string): void,
(e: 'changeFileName', value: string): void; (e: 'changeFileName', value: string): void;
} }
const fileName=ref('')
const oImg=ref('')
const emit = defineEmits<UploadEmits>(); const emit = defineEmits<UploadEmits>();
const loading=ref(false) const loading=ref(false)
const progress=ref(0) const progress=ref(0)
const cropperSuccess=(fileChile:any)=>{
actionFile(fileChile)
}
const actionFile=async (fileChild:any)=>{ const actionFile=async (fileChild:any)=>{
loading.value=true loading.value=true
try { try {
@ -133,7 +151,6 @@ const actionFile=async (fileChild:any)=>{
formData.append('file',fileChild) formData.append('file',fileChild)
formData.append('name',props.name) formData.append('name',props.name)
const res =await upload(formData) const res =await upload(formData)
console.log(res)
emit('update:imageUrl',res.fileUrl); emit('update:imageUrl',res.fileUrl);
emit('changeFileName',res.originalName) emit('changeFileName',res.originalName)
// el-form // el-form
@ -151,6 +168,22 @@ const handleHttpUploadOptions=ref({})
const handleHttpUpload = async (options: UploadRequestOptions) => { const handleHttpUpload = async (options: UploadRequestOptions) => {
handleHttpUploadOptions.value=options handleHttpUploadOptions.value=options
if(props.cropper){
fileName.value= options.file.name
let reader = new FileReader();
reader.readAsDataURL(options.file);
let img = new Image();
reader.onload = function (e) {
img.src = this.result;
img.onload = function () {
let base = e.target.result;
oImg.value= base;
event.target.value = "";
cropperVisible.value = true;
};
};
return
}
actionFile(options.file) actionFile(options.file)
}; };

View File

@ -0,0 +1,197 @@
<template>
<el-dialog
v-model="props.visible"
title="图片裁剪"
:append-to-body="true"
class="cropper_model_dlg"
width="500"
:close-on-click-modal="false"
:before-close="dialogClose"
>
<div class="cropper_content">
<div class="cropper" style="text-align: center;">
<vueCropper
ref="cropperRef"
:img="options.img"
:outputSize="options.outputSize"
:outputType="options.outputType"
:info="options.info"
:canScale="options.canScale"
:autoCrop="options.autoCrop"
:autoCropWidth="options.autoCropWidth"
:autoCropHeight="options.autoCropHeight"
:fixed="options.fixed"
:maximgSize="options.maximgSize"
:fixedBox="options.fixedBox"
:enlarge="options.enlarge"
:fixedNumber="options.fixedNumber"
></vueCropper>
</div>
<div class="cropper_btns">
<el-button @click="rotateLeft" icon="RefreshLeft" size="mini" title="左旋转"></el-button>
<el-button @click="rotateRight" icon="RefreshRight" size="mini" title="右旋转"></el-button>
<el-button @click="changeScale(1)" size="mini" title="放大">+</el-button>
<el-button @click="changeScale(-1)" size="mini" title="缩小">-</el-button>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogClose">取消</el-button>
<el-button type="primary" @click="submit">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import VueCropper from "vue-cropper/src/vue-cropper.vue";
import { ref, reactive} from 'vue';
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false
},
cropperSize: {
type: Object,
default() {
return null;
}
},
oImg: {
type: String,
default: ""
},
fileName: {
type: String,
default: ""
},
folder: {
type: String,
default: ""
}
});
const dialogClose = () => {
emit("update:visible", false);
};
const options = reactive({
img: props.oImg, //
outputSize: 1, //
outputType: "png", //
info: true, //
canScale: true, //
autoCrop: true, //
autoCropWidth: props.cropperSize ? props.cropperSize.width : 200, //
autoCropHeight: props.cropperSize ? props.cropperSize.height : 200, //
fixed: false, //
fixedNumber: [1, 1], //
full: true, //
fixedBox: true, //
canMove: true, //
canMoveBox: true, //
original: true, //
centerBox: false, //
high: false, // dpr
infoTrue: true, // truefalse
maximgSize: 100, //
enlarge: 1, //
mode: "contain" // (contain, cover, 100px, 100% auto)
});
const cropperRef = ref();
const addImgLoading = ref(false);
//
const rotateLeft = () => {
cropperRef.value.rotateLeft();
};
//
const rotateRight = () => {
cropperRef.value.rotateRight();
};
//
const changeScale = (num: any) => {
num = num || 1;
cropperRef.value.changeScale(num);
};
const emit = defineEmits(["success", "update:visible"]);
// base64
const dataURLtoFile=(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});
}
const submit=()=>{
cropperRef.value.getCropData(fileData => {
let file = dataURLtoFile(fileData, props.fileName);
emit("update:visible", false);
emit('success',file)
})
}
//
defineExpose({});
</script>
<style lang="scss">
.cropper_model_dlg {
.el-dialog__body {
padding: 0px 20px 60px !important
}
.cropper_content {
margin: 0 auto;
display: flex;
flex-direction: column;
align-items: center;
}
.add-img {
opacity: 0;
position: absolute;
width: 80px;
height: 28px;
top: 0px;
left: 0px;
}
.cropper {
width: 400px;
height: 400px;
background: yellow;
}
.cropper500 {
width: 600px;
height: 600px;
background: yellow;
border: 1px solid #e3e3e3;
}
.cropper_right {
width: 520px;
text-align: center;
}
.cropper_preview {
margin: 0 auto;
display: inline-block;
border: 1px solid #ddd;
}
.cropper_btns {
margin-top: 20px;
}
}
</style>

View File

@ -101,6 +101,7 @@
: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"
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>
@ -149,9 +150,6 @@ const props = defineProps({
}); });
const emit = defineEmits(["success", "update:visible"]); const emit = defineEmits(["success", "update:visible"]);
const uploadHeaders = reactive({
authorization: useUserStore().getToken
});
const formData = reactive({ const formData = reactive({
id: 0, id: 0,
avatarName:'', avatarName:'',