引入 vue-cropper 第三方插件
npm install vue-cropper
在 main.js 全局进入
import VueCropper from 'vue-cropper'
Vue.use(VueCropper)
封装图片裁剪模态框
components/Upload/CropperModal.vue
<template>
<div id="create-cropper-modal">
<a-modal
:visible="visible"
:title="options.title"
:maskClosable="false"
:confirmLoading="confirmLoading"
:width="600"
@cancel="handleCancel"
>
<a-row>
<a-col :xs="24" :md="24" :style="{ height: '400px' }">
<vue-cropper
ref="cropper"
:img="options.img"
:info="true"
:autoCrop="options.autoCrop"
:autoCropWidth="options.autoCropWidth"
:autoCropHeight="options.autoCropHeight"
:fixedBox="options.fixedBox"
:mode="cropperMode"
@realTime="realTime"
>
</vue-cropper>
</a-col>
<!-- <a-col :xs="24" :md="12" :style="{ height: '250px' }">
<div
:class="
options.previewsCircle
? 'avatar-upload-preview'
: 'avatar-upload-preview_range'
"
>
<img :src="previews.url" :style="previews.img" />
</div>
</a-col> -->
</a-row>
<template slot="footer">
<a-button key="back" @click="handleCancel">取消</a-button>
<a-button
key="submit"
type="primary"
:loading="confirmLoading"
@click="handleSumbit"
>
确定
</a-button>
</template>
</a-modal>
</div>
</template>
<script>
// import { Upgrad } from "corporatetrainingbundle/js/service";
export default {
props: {
//图片存储在oss上的上级目录名
imgType: {
type: String,
default: "",
},
fileName: {
type: String, // 文件名
default: "",
},
cropperMode: {
// 图片渲染方式
type: String,
default: "contain",
},
},
mounted() {},
data() {
return {
visible: false,
img: null,
confirmLoading: false,
options: {
img: "", //裁剪图片的地址
autoCrop: true, //是否默认生成截图框
autoCropWidth: 200, //默认生成截图框宽度
autoCropHeight: 200, //默认生成截图框高度
fixedBox: true, //是否固定截图框大小 不允许改变
previewsCircle: false, //预览图是否是原圆形
title: "修改图片",
},
previews: {},
url: {
upload: "/sys/common/saveToImgByStr",
},
};
},
methods: {
edit(record) {
this.visible = true;
this.options = Object.assign({}, this.options, record);
},
// 取消截图
handleCancel() {
this.confirmLoading = false;
this.visible = false;
this.$emit("cropper-no");
},
// 确认截图
handleSumbit() {
this.confirmLoading = true;
// 获取截图的base64 数据
this.$refs.cropper.getCropData(async (data) => {
const file = this.dataURLtoFile(data, this.fileName);
let formData = new window.FormData();
formData.append("file", file);
// 调用后端接口上传裁剪后图片,获取图片url传递给组件表单和回显
// formData.append("token", $("#upload_token").val());
// try {
// const { id, url } = await Upgrad.uploadImageApi(formData);
// this.$emit("cropper-ok", id, url);
// } catch (error) {
// this.$message.error("网络异常,请稍后再试!");
// } finally {
// this.visible = false;
// this.confirmLoading = false;
// }
});
},
//移动框的事件
realTime(data) {
this.previews = data;
},
// base 64 转成二进制文件流
dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
},
},
};
</script>
<style lang="less" scoped>
#create-cropper-modal {
.avatar-upload-preview_range,
.avatar-upload-preview {
position: absolute;
top: 50%;
transform: translate(50%, -50%);
width: 100%;
height: 100%;
border-radius: 50%;
box-shadow: 0 0 4px #ccc;
overflow: hidden;
img {
background-color: red;
height: 100%;
}
}
.avatar-upload-preview_range {
border-radius: 0;
}
}
</style>
封装上传组件
components/Upload/CropperUpload.vue
<template>
<div id="create-ant-upload-preview">
<div class="ant-upload-preview">
<div class="avatatImg">
<a-upload
name="avatar"
listType="picture-card"
:showUploadList="false"
:beforeUpload="beforeUpload"
:customRequest="function () {}"
@change="handleChange"
:accept="accept"
>
<img
class="upload_img"
v-if="imageUrl"
:src="imageUrl"
alt="avatar"
/>
<div v-else>
<a-icon :type="loading ? 'loading' : 'plus'" />
<div class="ant-upload-text">上传</div>
</div>
</a-upload>
<div class="text">
<div>{{ avatarText }}</div>
</div>
</div>
<!-- 引入裁剪组件 -->
<CropperModal
ref="CropperModal"
:imgType="imgType"
:fileName="fileName"
:cropperMode="cropperMode"
@cropper-no="handleCropperClose"
@cropper-ok="handleCropperSuccess"
/>
</div>
</div>
</template>
<script>
import CropperModal from "./CropperModal";
export default {
components: { CropperModal },
props: {
//图片裁切配置
options: {
type: Object,
default: function () {
return {
autoCrop: true, //是否默认生成截图框
autoCropWidth: 1029, //默认生成截图框宽度
autoCropHeight: 480, //默认生成截图框高度
fixedBox: true, //是否固定截图框大小 不允许改变
previewsCircle: false, //预览图是否是原圆形
title: "修改图片",
};
},
},
avatarText: {
type: String,
default:
"支持jpg, gif, png格式的图片, 建议最小尺寸480x270,大小不能超过2M",
},
// 上传图片的大小,单位M
imgSize: {
type: Number,
default: 2 * 1024 * 1024,
},
//图片存储在oss上的上级目录名
imgType: {
type: String,
default: "",
},
// 图片地址
imageUrl: {
type: String,
default: "",
},
},
data() {
return {
isError: false,
accept: "image/png,image/jpg,image/jpeg,image/gif",
fileName: "", // 文件名
loading: false,
isStopRun: false,
cropperMode: "contain", // 图片渲染方式
};
},
methods: {
//从本地选择文件
handleChange(info) {
if (this.isError || this.isStopRun) {
return;
}
this.loading = info.file.status;
this.fileName = info.file.name;
this.getBase64(info.file.originFileObj, (imageUrl) => {
const target = Object.assign({}, this.options, {
img: imageUrl,
});
this.$refs.CropperModal.edit(target);
});
},
// 上传之前 格式与大小校验
beforeUpload(file) {
// this.$emit("avatarLoadingFn", true);
this.isError = false;
this.isStopRun = false;
const imgTypeArr = ["image/jpeg", "image/png", "image/jpg", "image/gif"];
const isJpgOrPng = imgTypeArr.includes(file.type);
if (!isJpgOrPng) {
this.isError = true;
this.$message.error("不能上传其他类型的图片");
return;
}
const isSizeLegal = file.size <= this.imgSize;
if (!isSizeLegal) {
this.$message.error("图片大小超过限制");
this.isStopRun = true;
this.isError = true;
return;
}
// 获取上传图片尺寸
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
var img = new Image();
img.src = reader.result;
img.onload = () => {
// 1、先根据图片裁剪的尺寸
if (this.options.autoCropWidth > this.options.autoCropHeight) {
// 2、再根据上传图片尺寸
this.cropperMode = this.options.autoCropWidth + "px auto";
} else {
if (img.width >= img.height) {
this.cropperMode = "auto " + this.options.autoCropHeight + "px";
} else {
this.cropperMode = this.options.autoCropWidth + "px auto";
}
}
};
};
return isJpgOrPng;
},
//获取服务器返回的地址
handleCropperSuccess(id, url) {
//将返回的数据回显
this.loading = false;
this.$emit("avatarfn", id, url);
},
// 取消上传
handleCropperClose() {
this.loading = false;
},
getBase64(img, callback) {
if (img) {
const reader = new FileReader();
reader.addEventListener("load", () => callback(reader.result));
reader.readAsDataURL(img);
}
},
},
};
</script>
<style lang="less" scoped>
#create-ant-upload-preview {
.avatar-upload-wrapper {
height: 180px;
width: 100%;
}
//更改上传组件宽高
/deep/ .ant-upload-select {
width: 198px;
height: 108px;
}
.ant-upload-preview {
background-color: #fff;
.ant-upload-select-picture-card i {
font-size: 14px;
color: #5e6166;
}
.upload_img {
width: 100%;
}
.ant-upload-select-picture-card .ant-upload-text {
font-size: 14px;
margin-top: 8px;
color: #5e6166;
}
}
.ant-upload-picture-card-wrapper {
width: auto;
}
.avatar-uploader > .ant-upload {
width: 128px;
height: 128px;
}
.avatatImg {
display: flex;
flex-direction: column;
.text {
div {
line-height: 22px !important;
font-weight: 600;
color: #919399;
font-size: 14px;
font-style: normal;
font-weight: 400;
}
}
}
}
</style>
页面引入使用
App.vue
<template>
<div>
<CropperUpload
:imageUrl="coverUrl"
:options="coverOptions"
@avatarfn="coverUrlFn"
avatarText="支持jpg, gif, png格式的图片, 建议最小尺寸480x270,大小不能超过2M"
/>
</div>
</template>
<script>
import CropperUpload from "./components/Upload/CropperUpload.vue";
export default {
data() {
return {
form: {},
coverUrl: "",
coverOptions: {
autoCrop: true, //是否默认生成截图框
autoCropWidth: 480, //默认生成截图框宽度
autoCropHeight: 270, //默认生成截图框高度
fixedBox: true, //是否固定截图框大小 不允许改变
previewsCircle: false, //预览图是否是原圆形
title: "修改图片",
},
};
},
created() {},
mounted() {},
methods: {
coverUrlFn(id, url) {
this.coverUrl = url;
this.form.coverId = id;
},
},
components: {
CropperUpload,
},
};
</script>