当前位置: 首页 > article >正文

vue2 头像上传+裁剪组件封装

背景:最近在进行公司业务开发时,遇到了头像上传限制尺寸的需求,即限制为一寸证件照(宽295像素,高413像素)。

用到的第三方库: "vue-cropper": "^0.5.5"

完整组件代码:

avatarUpload.vue

<!-- 上传图片并裁剪 -->
<template><div :style="{ marginTop: marginTop + 'px' }"><!-- 上传 --><template><div class="preview-img" v-show="previewImg" :style="{ width: uploadWidth + 'px', height: uploadHeight + 'px' }"><div class="preview-img-box" :style="{ lineHeight: uploadHeight + 'px' }"><i v-if="!disabled" class="el-icon-delete" @click="deleteFn"></i><i class="el-icon-view" @click="viewFn"></i><i v-if="!disabled" class="el-icon-refresh" @click="refreshFn"></i></div>        <img :src="previewImg" :style="{ width: '100%', height: '100%', objectFit: objectFit }" /></div><el-upload v-show="!previewImg" ref="upload" class="upload-demo":style="{ width: uploadWidth + 'px', height: uploadHeight + 'px' }" :action="actionUrl":on-change="handleChangeUpload" :auto-upload="false" :show-file-list="false" :disabled="disabled"><div class="upload-demo-icon" :style="{width: uploadWidth + 'px',height: uploadHeight + 'px',lineHeight: uploadHeight + 'px',}">+</div></el-upload></template><!-- 裁剪 --><el-dialog title="图片剪裁" :visible.sync="dialogVisible" class="crop-dialog" append-to-body><div style="padding: 0 20px"><div :style="{textAlign: 'center',width: cropperWidth != 0 ? cropperWidth + 'px' : 'auto',height: cropperHeight + 'px',}"><VueCropper ref="cropper" :img="cropperImg" :output-size="outputSize" :output-type="outputType" :info="info":full="full" :can-move="canMove" :can-move-box="canMoveBox" :original="original" :auto-crop="autoCrop":can-scale="canScale" :fixed="fixed" :fixed-number="fixedNumber" :fixed-box="fixedBox":center-box="centerBox" :info-true="infoTrue" :auto-crop-width="autoCropWidth":auto-crop-height="autoCropHeight" /></div></div><!-- 这里的按钮可以根据自己的需求进行增删--><div class="action-box" v-if="actionButtonFlag"><el-upload action="#" :auto-upload="false" :show-file-list="false" :on-change="handleChangeUpload"style="margin-right: 15px"><el-button title="更换图片" plain circle type="primary" icon="el-icon-refresh"></el-button></el-upload><el-button title="清除图片" plain circle type="primary" icon="el-icon-close" @click="clearImgHandle"></el-button><el-button title="向左旋转" plain circle type="primary" icon="el-icon-refresh-left"@click="rotateLeftHandle"></el-button><el-button title="向右旋转" plain circle type="primary" icon="el-icon-refresh-right" @click="rotateRightHandle"></el-button><el-button title="放大" plain circle type="primary" @click="changeScaleHandle(1)" icon="el-icon-zoom-in"></el-button><el-button title="缩小" plain circle type="primary" @click="changeScaleHandle(-1)" icon="el-icon-zoom-out"></el-button><!-- <el-button type="primary" @click="fixed = !fixed">{{ fixed ? "固定比例" : "自由比例" }}</el-button> --><el-button title="下载" plain circle type="primary" icon="el-icon-download"@click="downloadHandle('blob')"></el-button></div><div slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false">取 消</el-button><el-button type="primary" :loading="loading" @click="finish">确认</el-button></div></el-dialog></div>
</template><script>
import { VueCropper } from "vue-cropper";
import { insertImage } from '@/api/file';
export default {name: "Cropper",components: {VueCropper,},props: {marginTop: {type: Number,default: 0,},// 上传属性// 图片路径imgSrc: {type: String,default: "",},// 是否禁用disabled: {type: Boolean,default: false,},// 列表索引listIndex: {type: Number,default: null,},// 上传路径actionUrl: {type: String,default: "#",},// 上传宽度uploadWidth: {type: Number,default: 100,},// 上传高度uploadHeight: {type: Number,default: 100,},// 图片显示角度 传值详情参考mdn object-fitobjectFit: {type: String,default: "fill",},// 裁剪属性// 裁剪弹出框的宽度cropperWidth: {type: Number,default: 0,},// 裁剪弹出框的高度cropperHeight: {type: Number,default: 600,},// 裁剪生成图片的质量 0.1-1outputSize: {type: Number,default: 1,},// 裁剪生成图片的格式outputType: {type: String,default: "png",},// 裁剪框的大小信息info: {type: Boolean,default: true,},// 是否输出原图比例的截图full: {type: Boolean,default: false,},// 截图框能否拖动canMove: {type: Boolean,default: true,},// 截图框能否拖动canMoveBox: {type: Boolean,default: true,},// 上传图片按照原始比例渲染original: {type: Boolean,default: true,},// 是否默认生成截图框autoCrop: {type: Boolean,default: true,},// 图片是否允许滚轮缩放canScale: {type: Boolean,default: true,},// 是否开启截图框宽高固定比例fixed: {type: Boolean,default: true,},// 截图框的宽高比例 开启fixed生效fixedNumber: {type: Array,default: () => [5, 7],},// 固定截图框大小 不允许改变fixedBox: {type: Boolean,default: true,},// 截图框是否被限制在图片里面centerBox: {type: Boolean,default: true,},// true 为展示真实输出图片宽高 false 展示看到的截图框宽高infoTrue: {type: Boolean,default: true,},// 默认生成截图框宽度autoCropWidth: {type: Number,default: 295,},// 默认生成截图框高度autoCropHeight: {type: Number,default: 413,},// 是否出现操作按钮actionButtonFlag: {type: Boolean,default: false,},// 裁剪路径输出格式 base64:base64; blob:blob;cropFormat: {type: String,default: "blob",},// 图片最大宽度maxImgWidth: {type: Number,default: 648,},// 图片最大高度maxImgHeight: {type: Number,default: 1152,},// 头像扫描件idtxsmjid: {type: String,default: ""},// 是否禁用上传disabled:{type: Boolean,default: false}},data() {return {previewImg: "", // 预览图片地址dialogVisible: false, //图片裁剪弹框cropperImg: "", // 裁剪图片的地址loading: false, // 防止重复提交baseCsUrl: process.env.NODE_ENV === 'production' ? window.globalConfig.VUE_APP_BASE_API_CS : process.env.VUE_APP_BASE_API_CS, // 文件服务器地址fileName: "",};},watch: {imgSrc: {handler(newVal, oldVal) {this.previewImg = newVal;},deep: true, // 深度监听immediate: true, // 首次进入就监听},},methods: {// 上传按钮 限制图片大小和类型handleChangeUpload(file) {this.fileName = file.name;const isJPG =file.raw.type === "image/jpeg" || file.raw.type === "image/png";const isLt2M = file.size / 1024 / 1024 < 2;const min_isLt = file.size / 1024 > 20;if (!isJPG) {this.$message.error("上传头像图片只能是 JPG/PNG 格式!");return false;}if (!isLt2M) {this.$message.error("上传头像图片大小不能超过 2MB!");return false;}if (!min_isLt) {this.$modal.msgError("头像大小不能小于 20 K!");return false;}// 上传成功后将图片地址赋值给裁剪框显示图片this.$nextTick(async () => {// base64方式// this.option.img = await fileByBase64(file.raw)this.cropperImg = URL.createObjectURL(file.raw);this.loading = false;this.dialogVisible = true;});},// 放大/缩小changeScaleHandle(num) {num = num || 1;this.$refs.cropper.changeScale(num);},// 左旋转rotateLeftHandle() {this.$refs.cropper.rotateLeft();},// 右旋转rotateRightHandle() {this.$refs.cropper.rotateRight();},// 下载downloadHandle(type) {let aLink = document.createElement("a");aLink.download = "author-img";if (type === "blob") {this.$refs.cropper.getCropBlob((data) => {aLink.href = URL.createObjectURL(data);aLink.click();});} else {this.$refs.cropper.getCropData((data) => {aLink.href = data;aLink.click();});}},// 清理图片clearImgHandle() {this.cropperImg = "";},// 截图finish() {if (this.cropFormat == "base64") {// 获取截图的 base64 数据this.$refs.cropper.getCropData((data) => {this.loading = true;this.dialogVisible = false;this.getImgHeight(data, this.maxImgWidth, this.maxImgHeight).then((imgUrl) => {// console.log(imgUrl, "base64");this.previewImg = imgUrl;if (this.listIndex !== null) {this.$emit("successCheng", this.previewImg, this.listIndex);} else {this.$emit("successCheng", this.previewImg);}});});} else if (this.cropFormat == "blob") {// 获取截图的 blob 数据this.$refs.cropper.getCropBlob((blob) => {this.loading = true;// this.dialogVisible = false;this.getImgHeight(URL.createObjectURL(blob),this.maxImgWidth,this.maxImgHeight).then((imgUrl) => {// console.log(imgUrl, "blob");this.previewImg = imgUrl;if (this.listIndex !== null) {this.$emit("successCheng", this.previewImg, this.listIndex);} else {this.$emit("successCheng", this.previewImg);}});this.uploadImage(blob)});}},// 上传到后端uploadImage(blob) {let file = new File([blob], this.fileName, {type: "image/png",lastModified: Date.now(),});let fd = new FormData();fd.append("file", file);insertImage(fd, this.txsmjid).then((res) => {// console.log("图片上传", res);this.dialogVisible = false;if (res.code === 200) {this.$modal.msgSuccess("上传成功");}}).finally(() => {this.loading = false;});},// 预览图片viewFn() {let preIndex = 0;this.$viewerApi({images: [this.previewImg],options: {initialViewIndex: preIndex,},});},// 删除图片deleteFn() {this.previewImg = "";// this.$emit("deleteCheng");},// 更换图片refreshFn() {this.$refs["upload"].$refs["upload-inner"].handleClick();},// 获取图片高度并修改getImgHeight(imgSrc, scaleWidth = 648, scaleHeight = 1152) {return new Promise((resolve, reject) => {const img = new Image(); // 创建一个img对象img.src = imgSrc; // 设置图片地址let imgUrl = ""; // 接收图片地址img.onload = () => {if (img.width > scaleWidth || img.height > scaleHeight) {const canvas = document.createElement("canvas");const context = canvas.getContext("2d");canvas.width = scaleWidth;canvas.height = scaleHeight;context.drawImage(img, 0, 0, scaleWidth, scaleHeight);if (this.cropFormat == "blob") {imgUrl = this.base64toBlob(canvas.toDataURL("image/png", 1),"image/png");} else {imgUrl = canvas.toDataURL("image/png", 1);}resolve(imgUrl);} else {imgUrl = imgSrc;resolve(imgUrl);}};});},// base64转blobbase64toBlob(base64, type = "application/octet-stream") {// 去除base64头部const images = base64.replace(/^data:image\/\w+;base64,/, "");const bstr = atob(images);let n = bstr.length;const u8arr = new Uint8Array(n);while (n--) {u8arr[n] = bstr.charCodeAt(n);}return URL.createObjectURL(new Blob([u8arr], { type }));},},
};
</script><style lang="scss" scoped>
.preview-img {position: relative;cursor: pointer;&:hover {.preview-img-box {display: block;}}
}.preview-img-box {width: 100%;height: 100%;position: absolute;left: 0;top: 0;background-color: rgba(30, 28, 28, 0.5);display: none;color: #d9d9d9;font-size: 24px;text-align: center;
}.preview-img-number {width: 20px;height: 20px;overflow: hidden;position: absolute;right: 0;bottom: 0;background-color: rgba(8, 137, 53, 1);color: #d9d9d9;font-size: 20px;text-align: center;border-radius: 50%;
}.upload-demo {border: 1px dashed #d9d9d9;border-radius: 6px;cursor: pointer;
}.upload-demo-icon {font-size: 60px;color: #8c939d;text-align: center;
}.crop-dialog {.action-box {margin: 20px;display: flex;flex-wrap: wrap;justify-content: center;button {margin-top: 15px;//width: 80px;margin-right: 15px;}}.dialog-footer {text-align: center;button {width: 100px;}}
}
</style>

使用组件:

<template><div><!-- 图片裁剪 --><avatarUpload :key="new Date().getTime()" :imgSrc="imageUrl" :uploadWidth="148" :uploadHeight="207" :actionButtonFlag="true" :objectFit="'cover'" 
class="avatar-uploader" :txsmjid="txsmjid"></avatarUpload></div>
</template>
<script>
import avatarUpload from "./avatarUpload.vue";
export default {components: {avatarUpload,},data() {imageUrl:'',txsmjid:'',}      
}
</script>

相关文章:

vue2 头像上传+裁剪组件封装

背景&#xff1a;最近在进行公司业务开发时&#xff0c;遇到了头像上传限制尺寸的需求&#xff0c;即限制为一寸证件照&#xff08;宽295像素&#xff0c;高413像素&#xff09;。 用到的第三方库&#xff1a; "vue-cropper": "^0.5.5" 完整组件代码&…...

unity 鼠标更换指定图标

1.准备两张图 要求图片导入设置如下 将 Texture Type 改为 Cursor 确保 Read/Write Enabled 已勾选 取消勾选 Generate Mip Maps 将 Filter Mode 设为 Point (保持清晰边缘) 将 Compression 设为 None (无压缩) 2.创建脚本&#xff0c;把脚本挂到场景中 &#xff0c;该…...

AI-02a5a5.神经网络-与学习相关的技巧-权重初始值

权重的初始值 在神经网络的学习中&#xff0c;权重的初始值特别重要。实际上&#xff0c;设定什么样的权重初始值&#xff0c;经常关系到神经网络的学习能否成功。 不要将权重初始值设为 0 权值衰减&#xff08;weight decay&#xff09;&#xff1a;抑制过拟合、提高泛化能…...

【springcloud学习(dalston.sr1)】Eureka单个服务端的搭建(含源代码)(三)

该系列项目整体介绍及源代码请参照前面写的一篇文章【springcloud学习(dalston.sr1)】项目整体介绍&#xff08;含源代码&#xff09;&#xff08;一&#xff09; 这篇文章主要介绍单个eureka服务端的集群环境是如何搭建的。 通过前面的文章【springcloud学习(dalston.sr1)】…...

Node.js数据抓取技术实战示例

Node.js常用的库有哪些呢&#xff1f;比如axios或者node-fetch用来发送HTTP请求&#xff0c;cheerio用来解析HTML&#xff0c;如果是动态网页的话可能需要puppeteer这样的无头浏览器。这些工具的组合应该能满足大部分需求。 然后&#xff0c;可能遇到的难点在哪里&#xff1f;…...

[架构之美]Spring Boot集成MyBatis-Plus高效开发(十七)

[架构之美]Spring Boot集成MyBatis-Plus高效开发&#xff08;十七&#xff09; 摘要&#xff1a;本文通过图文代码实战&#xff0c;详细讲解Spring Boot整合MyBatis-Plus全流程&#xff0c;涵盖代码生成器、条件构造器、分页插件等核心功能&#xff0c;助你减少90%的SQL编写量…...

windows10 安装 QT

本地环境有个qt文件&#xff0c;这里是5.14.2 打开一个cmd窗口并指定到该文件根目录下 .\qt-opensource-windows-x86-5.14.2.exe --mirror https://mirrors.ustc.edu.cn/qtproject 执行上面命令 记住是文件名&#xff0c;记住不要傻 X的直接复制&#xff0c;是你的文件名 点击…...

WordPress 和 GPL – 您需要了解的一切

如果您使用 WordPress&#xff0c;GPL 对您来说应该很重要&#xff0c;您也应该了解它。查看有关 WordPress 和 GPL 的最全面指南。 您可能听说过 GPL&#xff08;通常被称为 WordPress 的权利法案&#xff09;&#xff0c;但很可能并不完全了解它。这是有道理的–这是一个复杂…...

计算机网络:什么是计算机网络?它的定义和组成是什么?

计算机网络是指通过通信设备和传输介质&#xff0c;将分布在不同地理位置的计算机、终端设备及其他网络设备连接起来&#xff0c;实现资源共享、数据传输和协同工作的系统。其核心目标是使设备之间能够高效、可靠地交换信息。 关键组成部分 硬件设备 终端设备&#xff1a;如计算…...

C++书本摆放 2024年信息素养大赛复赛 C++小学/初中组 算法创意实践挑战赛 真题详细解析

目录 C++书本摆放 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、运行结果 五、考点分析 六、 推荐资料 1、C++资料 2、Scratch资料 3、Python资料 C++书本摆放 2024年信息素养大赛 C++复赛真题 一、题目要求 1、编程实现 中科智慧科技…...

在scala中使用sparkSQL读入csv文件

以下是使用 Spark SQL&#xff08;Scala&#xff09;读取 CSV 文件的完整代码示例&#xff1a; scala import org.apache.spark.sql.SparkSession import org.apache.spark.sql.types._object CSVReadExample {def main(args: Array[String]): Unit {// 创建SparkSessionval…...

RabbitMQ 核心概念与消息模型深度解析(一)

一、RabbitMQ 是什么 在当今分布式系统盛行的时代&#xff0c;消息队列作为一种至关重要的中间件技术&#xff0c;扮演着实现系统之间异步通信、解耦和削峰填谷等关键角色 。RabbitMQ 便是消息队列领域中的佼佼者&#xff0c;是一个开源的消息代理和队列服务器&#xff0c;基于…...

论文阅读笔记——双流网络

双流网络论文 视频相比图像包含更多信息&#xff1a;运动信息、时序信息、背景信息等等。 原先处理视频的方法&#xff1a; CNN LSTM&#xff1a;CNN 抽取关键特征&#xff0c;LSTM 做时序逻辑&#xff1b;抽取视频中关键 K 帧输入 CNN 得到图片特征&#xff0c;再输入 LSTM&…...

思路解析:第一性原理解 SQL:连接(JOIN)

目录 题目描述 &#x1f3af; 应用第一性原理来思考这个 SQL 题目 ✅ 第一步&#xff1a;还原每个事件的本质单位 ✅ 第二步&#xff1a;如果一个表只有事件&#xff0c;如何构造事件对&#xff1f; ✅ 第三步&#xff1a;加过滤条件&#xff0c;只保留“同一机器、同一进…...

Java面向对象三大特性深度解析

Java面向对象三大特性封装继承多态深度解析 前言一、封装&#xff1a;数据隐藏与访问控制的艺术1.1 封装的本质与作用1.2 封装的实现方式1.2.1 属性私有化与方法公开化1.2.2 封装的访问修饰符 二、继承&#xff1a;代码复用与类型扩展的核心机制2.1 继承的定义与语法2.2 继承的…...

LabVIEW在电子电工教学中的应用

在电子电工教学领域&#xff0c;传统教学模式面临诸多挑战&#xff0c;如实验设备数量有限、实验过程存在安全隐患、教学内容更新滞后等。LabVIEW 作为一款功能强大的图形化编程软件&#xff0c;为解决这些问题提供了创新思路&#xff0c;在电子电工教学的多个关键环节发挥着重…...

Vue3 怎么在ElMessage消息提示组件中添加自定义icon图标

1、定义icon组件代码&#xff1a; <template><svg :class"svgClass" aria-hidden"true"><use :xlink:href"iconName" :fill"color"/></svg> </template><script> export default defineComponen…...

生活破破烂烂,AI 缝缝补补(附提示词)

写在前面&#xff1a;​【Fire 计算器】已上线&#xff0c;快算算财富自由要多少​ 现实不总温柔&#xff0c;愿你始终自渡。 请永远拯救自己于水火之中。 毛绒风格提示词&#xff08;供参考&#xff09;&#xff1a; 1. 逼真毛绒风 Transform this image into a hyperrealist…...

张 。。 通过Token实现Loss调优prompt

词编码模型和 API LLM不匹配,采用本地模型 理性中性案例(针对中性调整比较合理) 代码解释:Qwen2模型的文本编码与生成过程 这段代码展示了如何使用Qwen2模型进行文本的编码和解码操作。 模型加载与初始化 from transformers import AutoModelForCausalLM, AutoTokenizer...

Ubuntu 22.04.5 LTS上部署Docker及相关优化

以下是在Ubuntu 22.04.5 LTS上部署Docker及相关优化的步骤&#xff1a; 安装Docker 更新系统&#xff1a;在安装Docker之前&#xff0c;先确保系统是最新的&#xff0c;执行以下命令&#xff1a;sudo apt update sudo apt upgrade -y安装依赖包&#xff1a;安装一些必要的依赖…...

JVM学习专题(一)类加载器与双亲委派

目录 1、JVM加载运行全过程梳理 2、JVM Hotspot底层 3、war包、jar包如何加载 4、类加载器 我们来查看一下getLauncher&#xff1a; 1.我们先查看getExtClassLoader() 2、再来看看getAppClassLoader(extcl) 5、双亲委派机制 1.职责明确&#xff0c;路径隔离​&#xff…...

PyTorch API 9 - masked, nested, 稀疏, 存储

文章目录 torch.randomtorch.masked简介动机什么是 MaskedTensor&#xff1f; 支持的运算符一元运算符二元运算符归约操作查看与选择函数 torch.nested简介构造方法数据布局与形状支持的操作查看嵌套张量的组成元素填充张量的相互转换形状操作注意力机制 与 torch.compile 的配…...

进程相关面试题20道

一、基础概念与原理 1.进程的定义及其与程序的本质区别是什么&#xff1f; 答案&#xff1a;进程是操作系统分配资源的基本单位&#xff0c;是程序在数据集合上的一次动态执行过程。核心区别&#xff1a;​ 动态性&#xff1a;程序是静态文件&#xff0c;进程是动态执行实例…...

微信小程序学习之轮播图swiper

轮播图是小程序的重要组件&#xff0c;我们还是好好学滴。 1、上代码&#xff0c;直接布局一个轮播图组件(index.wxml)&#xff1a; <swiper class"swiper" indicator-active-color"#fa2c19" indicator-color"#fff" duration"{{durati…...

【万字逐行详解】深入解析ONNX Runtime图像分类程序main函数

本文将全面、详尽地解析一个使用ONNX Runtime进行图像分类的C++程序,不省略任何一行代码,逐行解释其语法和实现原理。这个程序展示了现代C++在计算机视觉领域的完整应用流程,从模型加载到结果可视化,涵盖了异常处理、性能分析等工程实践。 程序完整解析 1. 主函数框架 i…...

Linux复习笔记(五) 网络服务配置(dhcp)

二、网络服务配置 2.5 dhcp服务配置&#xff08;不涉及实际操作&#xff09; 要求&#xff1a;知道原理和常见的参数配置就行 2.5.1 概述DHCP&#xff08;Dynamic Host Configuration Protocol&#xff0c;动态主机配置协议&#xff09; DHCP&#xff08;Dynamic Host Conf…...

智慧工厂管理平台推荐?智慧工厂解决方案提供商有哪些?智慧工厂管理系统哪家好?

随着工业4.0和“双碳”目标的推进&#xff0c;智慧工厂管理平台成为制造企业数字化转型的核心工具。本文基于技术实力、应用场景、安全可靠三大维度&#xff0c;结合最新行业实践与用户需求&#xff0c;精选出十大智慧工厂解决方案提供商&#xff0c;助您快速匹配行业需求&…...

鸿蒙OSUniApp 实现的语音输入与语音识别功能#三方框架 #Uniapp

UniApp 实现的语音输入与语音识别功能 最近在开发跨平台应用时&#xff0c;客户要求添加语音输入功能以提升用户体验。经过一番调研和实践&#xff0c;我成功在UniApp项目中实现了语音输入与识别功能&#xff0c;现将过程和方法分享出来&#xff0c;希望对有类似需求的开发者有…...

windows版redis的使用

redis下载 Releases microsoftarchive/redishttps://github.com/microsoftarchive/redis/releases redis的启动和停止 进入路径的cmd 启动&#xff1a;redis-server.exe redis.windows.conf 停止&#xff1a;ctrlc 连接redis 指定要连接的IP和端口号 -h IP地址 -p 端口…...

Java版OA管理系统源码 手机版OA系统源码

Java版OA管理系统源码 手机版OA系统源码 一&#xff1a;OA系统的主要优势 1. 提升效率 减少纸质流程和重复性工作&#xff0c;自动化处理常规事务&#xff0c;缩短响应时间。 2. 降低成本 节省纸张、打印、通讯及人力成本&#xff0c;优化资源分配。 3. 规范管理 固化企…...