vue3+element-plus+vue-cropper实现裁剪图片上传

1.vue3+element-plus+vue-cropper实现裁剪图片

  • element-UI官网
  • element-plus官网
  • vue-cropper
  • vue3使用vue-cropper安装:npm install vue-cropper@next

2.vue-cropper插件:

 <vue-cropper :img="option.img" /><script setup>import {reactive} from "vue";// 裁剪的配置const option = reactive({img:'https://img1.baidu.com/it/u=4049022245,514596079&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500'})</script>

3.效果图:

在这里插入图片描述

4.实现cropperUpload组件:

<template><div class="uploadMian"><div class="img-item" v-for="(item, index) in fileList" :key="index"><img :src="item.src" /><el-icon class="uploader-close" @click="delFn(index)"><Close /></el-icon><div v-if="item.isSuccess" class="uploader-Check"><el-icon ><Check /></el-icon></div><div class="button-div" v-if="item.file && isCropper"><el-button type="success" @click="uploadFileFn(item, index)">上传</el-button><el-button type="primary" @click="cropperFn(item, index)">裁剪</el-button></div></div><el-uploadv-if="multiple || (!multiple && fileList.length == 0)"class="avatar-uploader"action="#":accept="acceptArray.length > 0? acceptArray.map((n) => acceptType[n]).join(','): '*'":http-request="!isCropper ? uploadFileFn : () => {}":multiple="multiple":show-file-list="false":before-upload="beforeAvatarUpload"><el-icon class="avatar-uploader-icon"><Plus /></el-icon></el-upload></div><el-dialog title="裁切图片" v-model="showCropper" width="550px"><div class="cropper-content"><div class="cropper-box"><div class="cropper"><vue-cropperref="cropperRefs":img="option.img":output-size="option.outputSize":info="option.info":can-scale="option.canScale":auto-crop="option.autoCrop":auto-crop-width="option.autoCropWidth":auto-crop-height="option.autoCropHeight":fixed="option.fixed":fixed-number="option.fixedNumber":full="option.full":fixed-box="option.fixedBox":can-move="option.canMove":can-move-box="option.canMoveBox":original="option.original":center-box="option.centerBox":height="option.height":info-true="option.infoTrue":max-img-size="option.maxImgSize":enlarge="option.enlarge":mode="option.mode":limit-min-size="option.limitMinSize"/></div></div></div><span slot="footer"><div class="dialog-footer"><el-button @click="showCropper = false">取 消</el-button><el-button type="primary" @click="onSubmit">确 定</el-button></div></span></el-dialog>
</template><script setup>
import { ref, reactive, watch } from "vue";
import { Plus, Close,Check } from "@element-plus/icons-vue";
import "vue-cropper/dist/index.css";
import { VueCropper } from "vue-cropper";
const props = defineProps({// 额外值otherData: {type: Object,default: () => {},},// 请求头headers: {type: Object,default: () => {},},//  参数值modelValue: {type: Array,default: () => {return [];},},// 多选multiple: {type: Boolean,default: false,},//   大小限制:10 * 1024 * 1024 = 10MBsize: {type: Number,default: 10 * 1024 * 1024,},// 是否需要裁剪isCropper: {type: Boolean,default: true,},// 请求的urlsendUrl: {type: String,default: "",},
});
const emits = defineEmits(["update:modelValue"]);
const cropperRefs = ref();
const cropperCb = ref(null);
const showCropper = ref(false);
let fileList = reactive([]);
const acceptArray = reactive(["png", "jpg", "jpeg"]); //选择类型
const acceptType = reactive({doc: "application/msword",docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",ppt: "application/vnd.ms-powerpoint",pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",xls: "application/vnd.ms-excel",xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",pdf: "application/pdf",csv: ".csv",txt: "text/plain",image: "image/*",png: "image/png",gif: "image/gif",jpg: "image/jpg",jpeg: "image/jpeg",
});// 监听传入的值
watch(props.modelValue,(value) => {const valueList = value || [];let newFileList = [];valueList.forEach((item) => {const indexThis=fileList.findIndex(n=>n.src==item)if(indexThis==-1){newFileList.push({src: item,isSuccess: true,});}});fileList.unshift(...newFileList);},{ immediate: true, deep: true }
);// 监听当前页面的fileList
watch(fileList,(value) => {const valueList = value.map((n) => {if (n.isSuccess) {return n.src;}return null;}).filter((n) => n != null);emits("update:modelValue", valueList);},{ deep: true }
);// 裁剪的配置
const option = reactive({img: "", // 裁剪图片的地址outputSize: 1, // 裁剪生成图片的质量(可选0.1 - 1)outputType: "jpeg", // 裁剪生成图片的格式(jpeg || png || webp)info: false, // 图片大小信息canScale: true, // 图片是否允许滚轮缩放autoCrop: true, // 是否默认生成截图框autoCropWidth: 230, //默认生成截图框宽度autoCropHeight: 150, //默认生成截图框高度fixed: false, // 是否开启截图框宽高固定比例fixedNumber: [1.53, 1], //截图框的宽高比例full: false, // false按原比例裁切图片,不失真fixedBox: false, // 固定截图框大小,不允许改变canMove: true, // 上传图片是否可以移动canMoveBox: true, // 截图框能否拖动original: true, // 上传图片按照原始比例渲染centerBox: true, // 截图框是否被限制在图片里面high: false, // 是否按照设备的dpr 输出等比例图片infoTrue: false, // true为展示真实输出图片宽高,false展示看到的截图框宽高maxImgSize: 3000, // 限制图片最大宽度和高度enlarge: 1, // 图片根据截图框输出比例倍数mode: "550px 400px", // 图片默认渲染方式limitMinSize: [108, 108], // 裁剪框限制最小区域minCropBoxWidth: 108, // 设置最小裁切框宽度minCropBoxHeight: 108, // 设置最小裁切框高度
});// 类型,大小判断
const judegFileSize = (file) => {const filterSize = (size) => {const pow1024 = (num) => {return Math.pow(1024, num);};if (!size) return "";if (size < pow1024(1)) return size + " B";if (size < pow1024(2)) return (size / pow1024(1)).toFixed(0) + " KB";if (size < pow1024(3)) return (size / pow1024(2)).toFixed(0) + " MB";if (size < pow1024(4)) return (size / pow1024(3)).toFixed(0) + " GB";return (size / pow1024(4)).toFixed(2) + " TB";};let retunBoolean = true;let fileSize = file.size;//判断文件类型const fileExtArray = file.name.split(".");const judegFn = () => {if (acceptArray.indexOf(fileExtArray.at(-1)) == -1) {alert(`${file.name}上传失败,只能上传${acceptArray.join("、")}`);retunBoolean = false;}};if (acceptArray.length > 0) {if (acceptArray.indexOf("image") != -1) {var pattern = /(\.jpg|\.jpeg|\.png|\.gif)$/i;// 判断文件名是否匹配图片格式的正则表达式if (!pattern.test(`.${fileExtArray.at(-1)}`)) {judegFn();}} else {judegFn();}}if (retunBoolean) {if (props.size > 0 && fileSize > props.size) {alert(`最大上传${filterSize(props.size)}`);retunBoolean = false;}}return retunBoolean;
};
const beforeAvatarUpload = (rawFile) => {let retunBoolean = judegFileSize(rawFile);if (retunBoolean) {fileList.push({src: URL.createObjectURL(rawFile),file: rawFile,});}return retunBoolean;
};// 裁剪
const cropperFn = (item, index) => {showCropper.value = true;option.img = URL.createObjectURL(item.file);const reader = new FileReader();reader.readAsDataURL(item.file);cropperCb.value = (res) => {if (res) {cropperRefs.value.getCropBlob((data) => {const result = new File([data], item.file.name, {type: item.file.type,lastModified: Date.now(),});result["uid"] = item.file.uid;fileList.splice(index, 1, {src: URL.createObjectURL(result),file: result,});showCropper.value = false;});}};
};// 删除
const delFn = (index) => {fileList.splice(index, 1);
};
// 弹窗确定裁剪
const onSubmit = () => {if (cropperCb.value) cropperCb.value(true);
};// 真实上传
const uploadFileFn = (item) => {if (props.sendUrl == "") return false;const successFn = (url) => {const index = fileList.findIndex((n) => {if (n.file && n.file.uid == item.file.uid) {return true;}return false;});if (index != -1) {fileList.splice(index, 1, {src: url,file: item.file,isSuccess: true,});}};// successFn(item.src);const formData = new FormData();formData.append("file", item.file);if (props.otherData) {Object.keys(props.otherData).forEach((key) => {formData.append(key, props.otherData[key]);});}fetch(props.sendUrl, {method: "POST",body: formData,headers: props.headers,"Content-type": "multipart/form-data",}).then((respone) => respone.json()).then((res) => {// 接口成功后替换urlsuccessFn("成功的url");}).catch((error) => {// 接口失败的});
};
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;border: 1px solid #ccc;width: 178px;height: 178px;text-align: center;
}.uploadMian {vertical-align: top;display: flex;flex-wrap: wrap;
}
.avatar-uploader {
}.img-item {display: inline-block;width: 178px;height: 178px;margin-right: 10px;border: 1px solid #ccc;position: relative;img {width: 100%;height: 100%;object-fit: contain;position: relative;z-index: 9;}&:hover{.el-icon.uploader-close {display: flex !important;}}.uploader-Check{width: 40px;height: 40px;position: absolute;z-index: 18;top: 0;left: 0;display: flex;background-color: #67c23a;clip-path: polygon(0 0 ,100% 0, 0 100%);-webkit-clip-path:polygon(0 0 ,100% 0,0 100% );.el-icon{position: absolute;top: 4px;left: 4px;color: #fff;}}.el-icon.uploader-close {display: none;position: absolute;z-index: 20;top: -5px;right: -5px;width: 20px;height: 20px;background-color: red;justify-content: center;align-items: center;border-radius: 50%;color: #fff;font-size: 12px;cursor: pointer;}.button-div {position: absolute;height: 45px;z-index: 20;bottom: 0;left: 0;width: 100%;background-color: rgba(0, 0, 0, 0.2);display: flex;justify-content: space-around;align-items: center;}
}
.cropper-content {display: flex;display: -webkit-flex;justify-content: flex-end;.cropper-box {width: 550px;.cropper {width: auto;height: 400px;}}.show-preview {flex: 1;-webkit-flex: 1;display: flex;display: -webkit-flex;justify-content: center;.preview {overflow: hidden;border: 1px solid #67c23a;background: #cccccc;}}
}
.dialog-footer {display: flex;justify-content: center;margin-top: 10px;
}
</style>

5.使用:

 <cropperUpload :otherData="{a:100}" :headers="{}" v-model="urlList" :multiple="true" sendUrl="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15" /><script setup>import cropperUpload from "./cropperUpload.vue";// 裁剪的配置const urlList = reactive(['https://img1.baidu.com/it/u=4049022245,514596079&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500'])</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/186973.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32F407-14.3.7-01PWM输入模式

PWM 输入模式 此模式是输入捕获模式的一个特例。其实现步骤与输入捕获模式基本相同&#xff0c;仅存在以下不同之处&#xff1a; 例如&#xff0c;可通过以下步骤对应用于 TI1① 的 PWM 的周期&#xff08;位于 TIMx_CCR1⑨ 寄存器中&#xff09;和占空 比&#xff08;位于 …

认识JVM 一个Java文件的JVM之旅

准备 我是一个java文件&#xff0c;如何实现我的功能呢&#xff1f;需要去JVM(Java Virtual Machine)这个地方旅行。 变身 我高高兴兴的来到JVM&#xff0c;想要开始JVM之旅&#xff0c;它确说&#xff1a;“现在的我还不能进去&#xff0c;需要做一次转换&#xff0c;生成c…

VUE2+THREE.JS 模型上方显示信息框/标签(CSS3DSprite精灵模型)

THREE.JS 模型上方显示信息框/标签---CSS3DSprite精灵模型 1.CSS2DRenderer/CSS3DRenderer/Sprite的优劣2.实现模型上方显示信息框2.1 引入2.2 初始化加载的时候就执行此方法2.3 animate循环执行2.4 获取设备状态并在每个设备上显示设备状态2.5 样式 CSS3DSprite精灵模型面向摄…

python中的函数定义

默认参数 注&#xff1a; 在Python中&#xff0c;print(x, and y both correct)是一条打印语句&#xff08;print statement&#xff09;&#xff0c;用于将一条消息输出到控制台或终端。它的作用是将变量x的值和字符串and y both correct同时输出到屏幕上。 在这个语句中&…

Windows11如何让桌面图标的箭头消失(去掉快捷键箭头)

在Windows 11中&#xff0c;桌面图标的箭头是快捷方式图标的一个标志&#xff0c;用来表示该图标是一个指向文件、文件夹或程序的快捷方式。如果要隐藏这些箭头&#xff0c;你需要修改Windows注册表或使用第三方软件。 在此之前&#xff0c;我需要提醒你&#xff0c;修改注册表…

Android 滑动按钮(开关) SwitchCompat 自定义风格

原生的SwitchCompat控件如下图&#xff0c;不说不堪入目&#xff0c;也算是不敢恭维了。开个玩笑... 所以我们就需要对SwitchCompat进行自定义风格&#xff0c;效果如下图 代码如下 <androidx.appcompat.widget.SwitchCompatandroid:id"id/switch_compat"android:…

【Linux】第二十二站:文件(二)深入理解重定向

文章目录 一、重定向1.文件描述符对应的分配规则2.重定向的接口 二、再次实现myshell1.实现细节2.盘点文件与进程替换的一个细节3.代码 三、1号文件和2号文件的区别四、如何理解“一切皆文件&#xff1f;” 一、重定向 1.文件描述符对应的分配规则 我们先看如下代码 #includ…

《C++PrimerPlus》第9章 内存模型和名称空间

9.1 单独编译 Visual Studio中新建头文件和源代码 通过解决方案资源管理器&#xff0c;如图所示&#xff1a; 分成三部分的程序&#xff08;直角坐标转换为极坐标&#xff09; 头文件coordin.h #ifndef __COORDIN_H__ // 如果没有被定义过 #define __COORDIN_H__struct pola…

智能优化算法应用:基于平衡优化器算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于平衡优化器算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于平衡优化器算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.平衡优化器算法4.实验参数设定5.算法结果…

全志XR806基于FreeRTOS下部署竞技机器人先进模糊控制器

前言 很荣幸参与到由“极术社区和全志在线联合组织”举办的XR806开发板试用活动。本人热衷于各种的开发板的开发&#xff0c;同时更愿意将其实现到具体项目中。秉承以上原则&#xff0c;发现大家的重心都放在开发中的环境构建过程&#xff0c;缺少了不少实际应用场景的运用&am…

【WebSocket】通信协议基于 node 的简单实践和心跳机制和断线重连的实现

前后端 WebSocket 连接 阮一峰大佬 WebSocket 技术博客 H5 中提供的 WebSocket 协议是基于 TCP 的全双工传输协议。它属于应用层协议&#xff0c;并复用 HTTP 的握手通道。它只需要一次握手就可以创建持久性的连接。 那么什么是全双工呢&#xff1f; 全双工是计算机网络中的…

如何计算数据泄露的成本

现在&#xff0c;几乎所有类型的组织每天都在发生企业 IT 网络遭到破坏的情况。它们是任何合规官员最担心的问题&#xff0c;并且找出更好的方法来防止它们或从中恢复是合规官员永远不会远离的想法。 但数据泄露的实际成本是多少&#xff1f;该数字从何而来&#xff1f;当您获…

考试复习

选择20道 填空10道 判断10道 简答4-5道 编程题2道 一、选择题 1.js中更改一个input框的值&#xff1a; <input ida type"text" value"123456"> 通过a.value改变他的值 方法&#xff1a; 在script标签中通过id获得该输入框对象&#xff0c;然…

Flutter应用程序的加固原理

在移动应用开发中&#xff0c;Flutter已经成为一种非常流行的技术选项&#xff0c;可以同时在Android和iOS平台上构建高性能、高质量的移动应用程序。但是&#xff0c;由于其跨平台特性&#xff0c;Flutter应用程序也面临着一些安全风险&#xff0c;例如反编译、代码泄露、数据…

numpy知识库:深入理解numpy.resize函数和数组的resize方法

前言 numpy中的resize函数顾名思义&#xff0c;可以用于调整数组的大小。但具体如何调整&#xff1f;数组形状变了&#xff0c;意味着数组中的元素个数发生了变化(增加或减少)&#xff0c;如何确定resize后的新数组中每个元素的数值呢&#xff1f;本次博文就来探讨并试图回答这…

electron调用dll问题总汇

通过一天的调试安装&#xff0c;electron调用dll成功&#xff0c;先列出当前的环境&#xff1a;node版本: 18.12.0&#xff0c;32位的&#xff08;因为dll为32位的&#xff09; VS2019 python node-gyp 1、首先要查看报错原因&#xff0c;通常在某一行会有提示&#xff0c;常…

elk+filebeat+kafka集群部署

EFK实验架构图&#xff1a; 实现高并发&#xff0c;无需指定logstash 3台esfile&#xff0c;3台kafka 20.0.0.10 esfile 20.0.0.20 esfile 20.0.0.30 esfile 20.0.0.11 kafka 20.0.0.12 kafka 20.0.0.13 kafka在es1主机上解压filebeat cd filebeat 安装nginx服务 vim /usr/loc…

无人机助力电力设备螺母缺销智能检测识别,python基于YOLOv7开发构建电力设备螺母缺销高分辨率图像小目标检测系统

传统作业场景下电力设备的运维和维护都是人工来完成的&#xff0c;随着现代技术科技手段的不断发展&#xff0c;基于无人机航拍飞行的自动智能化电力设备问题检测成为了一种可行的手段&#xff0c;本文的核心内容就是基于YOLOv7来开发构建电力设备螺母缺销检测识别系统&#xf…

软件设计师——计算机网络(一)

&#x1f4d1;前言 本文主要是【计算机网络】——软件设计师计算机网络的题目&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304…

Arduino、ESP8266、HTML相关知识点记录

C代码 const char *ssid "********"; // 这里定义将要建立的WiFi名称。 const char *password "********"; // 这里定义将要建立的WiFi密码。 多WiFi连接&#xff1a; wifiMulti.addAP("**…