2024.4.4今天我学习了如何使用若依的vue-cropper上传图片组件,在工作中遇到一个需求,需要对上传的图片进行旋转的操作,然后我先找到el-upload组件,使用之后发现它有一个自动上传和非自动上传的功能,是不是就可以通过非自动上传的功能然后跳一个弹窗,在弹窗里面进行操作再上传。
一、用el-upload
在使用非自动上传之后用on-change事件来触发一个弹窗,把上传的图片先放大显示,然后下面放一个左右旋转的按钮,通过ref this.$ref.xxx.style.transform修改样式进行旋转,但是这样只能单纯的旋转图片,不能把旋转之后的图片保存下来。
官方地址:
Element - The world's most popular Vue UI framework
二、用vue-cropper
后来百度找到若依有自带上传图片旋转的方法。
官方地址:若依管理系统
官方代码地址:ruoyi-ui/src/views/system/user/profile/userAvatar.vue · 若依/RuoYi-Vue - Gitee.com
相关属性:
ref="cropper":为裁剪器组件指定一个引用名称,以便在其他地方引用和操作该组件。
:img="option.img":指定要裁剪的图片的路径或数据。option.img 是一个变量,用于存储图片的信息。
:outputSize="option.size":指定裁剪后的输出尺寸大小。option.size 是一个变量,用于存储输出尺寸的信息。
:outputType="option.outputType":指定裁剪后的输出类型。option.outputType 是一个变量,用于存储输出类型的信息。
:info="true":启用/禁用裁剪器的信息面板,用于显示裁剪框的位置和尺寸等信息。
:full="option.full":指定是否允许裁剪器占满整个父容器的大小。
:canMove="option.canMove":指定是否允许移动裁剪框。
:canMoveBox="option.canMoveBox":指定是否允许移动整个裁剪区域。
:original="option.original":指定是否使用原始图片进行裁剪,而不是经过缩放的图片。
:autoCrop="option.autoCrop":指定是否自动进行裁剪。
:autoCropWidth="option.autoCropWidth":指定自动裁剪的宽度。
:autoCropHeight="option.autoCropHeight":指定自动裁剪的高度。
:fixedBox="option.fixedBox":指定是否使用固定的裁剪框大小。
@realTime="realTime":监听实时裁剪事件,当裁剪框位置或尺寸发生变化时触发 realTime 方法。
@imgLoad="imgLoad":监听图片加载事件,当图片加载完成时触发 imgLoad 方法。
这些属性和事件可以根据实际需求来配置和处理裁剪器的行为和交互。
效果如下:
完整代码如下:
<template>
<div>
<div class="user-info-head" @click="editCropper()"><img v-bind:src="options.img" title="点击上传头像" class="img-circle img-lg" /></div>
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
<el-row>
<el-col :xs="24" :md="12" :style="{height: '350px'}">
<vue-cropper
ref="cropper"
:img="options.img"
:info="true"
:autoCrop="options.autoCrop"
:autoCropWidth="options.autoCropWidth"
:autoCropHeight="options.autoCropHeight"
:fixedBox="options.fixedBox"
:outputType="options.outputType"
@realTime="realTime"
v-if="visible"
/>
</el-col>
<el-col :xs="24" :md="12" :style="{height: '350px'}">
<div class="avatar-upload-preview">
<img :src="previews.url" :style="previews.img" />
</div>
</el-col>
</el-row>
<br />
<el-row>
<el-col :lg="2" :sm="3" :xs="3">
<el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
<el-button size="small">
选择
<i class="el-icon-upload el-icon--right"></i>
</el-button>
</el-upload>
</el-col>
<el-col :lg="{span: 1, offset: 2}" :sm="2" :xs="2">
<el-button icon="el-icon-plus" size="small" @click="changeScale(1)"></el-button>
</el-col>
<el-col :lg="{span: 1, offset: 1}" :sm="2" :xs="2">
<el-button icon="el-icon-minus" size="small" @click="changeScale(-1)"></el-button>
</el-col>
<el-col :lg="{span: 1, offset: 1}" :sm="2" :xs="2">
<el-button icon="el-icon-refresh-left" size="small" @click="rotateLeft()"></el-button>
</el-col>
<el-col :lg="{span: 1, offset: 1}" :sm="2" :xs="2">
<el-button icon="el-icon-refresh-right" size="small" @click="rotateRight()"></el-button>
</el-col>
<el-col :lg="{span: 2, offset: 6}" :sm="2" :xs="2">
<el-button type="primary" size="small" @click="uploadImg()">提 交</el-button>
</el-col>
</el-row>
</el-dialog>
</div>
</template>
<script>
import store from "@/store";
import { VueCropper } from "vue-cropper";
import { uploadAvatar } from "@/api/system/user";
import { debounce } from '@/utils'
export default {
components: { VueCropper },
data() {
return {
// 是否显示弹出层
open: false,
// 是否显示cropper
visible: false,
// 弹出层标题
title: "修改头像",
options: {
img: store.getters.avatar, //裁剪图片的地址
autoCrop: true, // 是否默认生成截图框
autoCropWidth: 200, // 默认生成截图框宽度
autoCropHeight: 200, // 默认生成截图框高度
fixedBox: true, // 固定截图框大小 不允许改变
outputType:"png", // 默认生成截图为PNG格式
filename: 'avatar' // 文件名称
},
previews: {},
resizeHandler: null
};
},
methods: {
// 编辑头像
editCropper() {
this.open = true;
},
// 打开弹出层结束时的回调
modalOpened() {
this.visible = true;
if (!this.resizeHandler) {
this.resizeHandler = debounce(() => {
this.refresh()
}, 100)
}
window.addEventListener("resize", this.resizeHandler)
},
// 刷新组件
refresh() {
this.$refs.cropper.refresh();
},
// 覆盖默认的上传行为
requestUpload() {
},
// 向左旋转
rotateLeft() {
this.$refs.cropper.rotateLeft();
},
// 向右旋转
rotateRight() {
this.$refs.cropper.rotateRight();
},
// 图片缩放
changeScale(num) {
num = num || 1;
this.$refs.cropper.changeScale(num);
},
// 上传预处理
beforeUpload(file) {
if (file.type.indexOf("image/") == -1) {
this.$modal.msgError("文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。");
} else {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
this.options.img = reader.result;
this.options.filename = file.name;
};
}
},
// 上传图片
uploadImg() {
this.$refs.cropper.getCropBlob(data => {
let formData = new FormData();
formData.append("avatarfile", data, this.options.filename);
uploadAvatar(formData).then(response => {
this.open = false;
this.options.img = process.env.VUE_APP_BASE_API + response.imgUrl;
store.commit('SET_AVATAR', this.options.img);
this.$modal.msgSuccess("修改成功");
this.visible = false;
});
});
},
// 实时预览
realTime(data) {
this.previews = data;
},
// 关闭窗口
closeDialog() {
this.options.img = store.getters.avatar
this.visible = false;
window.removeEventListener("resize", this.resizeHandler)
}
}
};
</script>
<style scoped lang="scss">
.user-info-head {
position: relative;
display: inline-block;
height: 120px;
}
.user-info-head:hover:after {
content: '+';
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
color: #eee;
background: rgba(0, 0, 0, 0.5);
font-size: 24px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
cursor: pointer;
line-height: 110px;
border-radius: 50%;
}
</style>