先看实现功能:
首先npm i vue-cropper下载插件
组件index.vue
<template>
<div>
<el-upload ref="multiImageUpload"
:action="uploadImgUrl"
:headers="headers"
multiple
accept="image/png, image/jpeg"
list-type="picture-card"
:on-change="handleChange"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
:file-list="imageList"
:auto-upload="false"
:http-request="upload"
:show-file-list="true"
v-loading="loading"
element-loading-text="拼命上传中"
>
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="previewImage" title="预览" width="800" append-to-body>
<img :src="previewImageUrl" style="display: block; max-width: 100%; margin: 0 auto;">
</el-dialog>
<!-- 裁剪弹窗 -->
<el-dialog :append-to-body="true" :visible.sync="cropperModel" v-if="cropperModel" :close-on-press-escape=false width="800px" @close="beforeClose(cropperFile)">
<cropper-photo :img-file="cropperFile" ref="vueCropper" @upload="upload"></cropper-photo>
</el-dialog>
</div>
</template>
<script>
// import {getToken} from "@/utils/auth";
import CropperPhoto from './croppers'; //图片裁剪组件
import {uploadIocn} from '@/api/application/appAdd' //图片上传接口
import {generatefileUrl} from '@/api/file'
export default {
components:{
CropperPhoto
},
data() {
return {
//图片列表
imageList: [
],
imageLists: [],
//已上传文件字符串
uploadedFileStr: "",
//图片预览
previewImage: false,
//预览图地址
previewImageUrl: "",
//上传是否完成
uploadComplete: true,
dialogVisible: false,
uploadImgUrl: '', // 上传的图片服务器地址
headers: {
// Authorization: "Bearer " + getToken(),
},
cropperModel: false, // 剪裁组件弹窗开关
cropperFile: '',
cropperBlobFile:[],
uploadReponse:[],
loading: false,
imageIds:[]
};
},
props: {
value: {
type: String,
default: "",
},
//父组件传递过来的 需要处理 图片列表
parentImageList: {
type: String,
default: ""
},
list: {
type: Array,
default: ""
}
// fixedNumber: {
// type: Array,
// default: [1,1.5],
// // default: function(){
// // return[1,1.5]
// // }
// }
},
watch: {
parentImageList: function (newValue, oldValue) {
// console.log("newValue", newValue, "--oldValue", oldValue);
// console.log(newValue.length);
//转为数组
if (newValue.length === 0) {
console.log("点击了新增");
this.imageList.length = 0;
this.$refs["multiImageUpload"].clearFiles();
}
if (newValue.length > 0) {
if (newValue === "odd" || newValue === "even") {
console.log("点击了新增");
this.imageList.length = 0;
this.$refs["multiImageUpload"].clearFiles();
} else {
let split = newValue.split(",");
//先清空原有数据
this.imageList.length = 0;
if (split != null) {
for (let i = 0; i < split.length; i++) {
this.imageList.push({name: i, url: split[i]})
}
}
}
}
}
},
created() {
this.initImageList(this.parentImageList);
if(this.list.length != 0) {
this.imageList = this.list.map(e => {
this.imageIds.push(e.id)
e.url = generatefileUrl(e.url)
return e
})
}
},
methods: {
beforeUploadPicture(file) {
console.log("上传前" + file);
},
// 上传图片时调用 file上传后的文件结果
uploadProgress(event, file, fileList) {
// console.log(event, file, fileList);
// this.uploadComplete = false;
},
// 上传图片成功 res = file.response
uploadSuccess(res, file, fileList) {
console.log("上传图片成功");
console.log(res, file, fileList);
console.log("上传图片成功");
// this.uploadComplete = true;
// this.fileChange(fileList);
},
// 上传图片出错
uploadError(err, file, fileList) {
this.$message.error("上传出错");
},
// 移除图片
handleRemove(file, fileList,) {
console.log(fileList,"移除图片");
console.log(file, fileList)
this.imageIds = this.imageIds.filter(e => e !== file.id)
// fileList.forEach(imgId => {
// this.imageIds.push(imgId.id)
// })
console.log(this.imageIds, '移除图片传递的值')
this.$emit('uploud', this.imageIds)
this.fileChange(fileList);
this.imageList = fileList
},
handleChange(file,fileList){
this.cropperFile = ''
let types = ['image/jpeg','image/jpg','image/png'];
for (let i = 0; i < fileList.length; i++) {
// console.log('fileList[i]',fileList[i]);
const isImage = types.includes(fileList[fileList.length-1].raw.type);
const isLtSize = fileList[fileList.length-1].raw.size / 1024 / 1024 < 1
if (!isImage) {
this.$message.error({
message: '上传图片只能是 JPG、JPEG、PNG 格式!',
type: 'error'
});
return false;
this.cropperModel = false
}
if (!isLtSize) {
this.$message.error({
message:'上传图片大小不能超过 1MB!',
type: "error"
});
return false;
this.cropperModel = false
}
this.cropperModel = true
this.cropperFile = fileList[fileList.length-1]
// this.imageList.pop()
this.imageLists = fileList
console.log(this.imageLists,'上传类1别')
}
},
// 上传图片
upload (data,data2) {
console.log('uploaddata',data2);
this.loading = true
let files = new window.File([data], "ps.jpg", {type: 'image/jpeg'}) // myBlob.type 自定义文件名
const formData = new FormData()
formData.append('bucketName', 'images')
formData.append('fileName', data2)
formData.append('files', files)
const imageArrayList = []
uploadIocn(formData).then(res => {
console.log('uploadImagess',res);
this.imageList.push({
url: generatefileUrl(res.data[0].pathUrl),
id: res.data[0].id
})
this.imageIds.push(res.data[0].id)
this.$emit('uploud', this.imageIds)
this.cropperModel = false
const response = {response:{code: res.code, data: res.msg}}
// console.log('response',response);
this.uploadReponse.push(response)
// console.log('this.uploadReponse',this.uploadReponse);
this.fileChange(this.uploadReponse);
this.$message({
type: "success",
message: "上传成功",
})
this.loading = false
}, err => {
console.log(err);
this.$message({
type: "error",
message: "上传失败",
});
this.loading = false
})
},
// 设置photo值
fileChange(fileList) {
// console.log("fileChange", fileList);
let temp_str = '';
if (fileList.length > 0) {
//列表有数据
for (let i = 0; i < fileList.length; i++) {
// console.log(fileList[i]);
//判断原有数据还是上传返回的数据
//原有数据
// console.log(typeof (fileList[i].response) == "undefined");
if (typeof (fileList[i].response) == "undefined") {
if (i === 0) {
temp_str += fileList[i].url;
} else {
// 最终photo的格式是所有已上传的图片的url拼接的字符串(逗号隔开)
temp_str += ',' + fileList[i].url;
}
} else {
//上传返回数据
if (fileList[i].response.code === 200) {
if (i === 0) {
temp_str += fileList[i].response.data;
} else {
// 最终photo的格式是所有已上传的图片的url拼接的字符串(逗号隔开)
temp_str += ',' + fileList[i].response.data;
}
}
}
}
this.uploadedFileStr = temp_str;
}
// console.log("temp_str" + temp_str);
this.sendMsgToParent();
},
// 图片预览
handlePictureCardPreview(file) {
// console.log('handlePictureCardPreview',file);
this.previewImageUrl = file.url;
this.previewImage = true;
},
//初始化图片列表 data为字符串 第一次 created, 其他走监听器
//数组转换为 [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}]格式
initImageList(data) {
// console.log('initImageList',data);
if (data === "odd" || data === "even") {
this.imageList.length = 0;
} else {
//转为数组
if (data.length > 0) {
let split = data.split(",");
if (split != null) {
for (let i = 0; i < split.length; i++) {
this.imageList.push({name: i, url: split[i]})
}
}
} else {
this.imageList.length = 0;
}
}
},
//向父组件传值
sendMsgToParent() {
this.$emit("getFileListStr", this.uploadedFileStr);
},
beforeClose (done) {
console.log(done,'done')
// this.imageList.remove(done)
this.imageList = this.imageLists.filter(e=> e !== done )
this.cropperModel = false
},
aaa(done){
console.log(done,'done')
console.log('出发了')
this.imageList =[]
}
}
};
</script>
<style>
</style>
组件cropper.vue文件
<template>
<div>
<div class="cropper-content">
<!-- 剪裁框 -->
<div class="cropper">
<vueCropper
ref="cropper"
:img="option.img"
:outputSize="option.outputSize"
:outputType="option.outputType"
:info="option.info"
:canScale="option.canScale"
:autoCrop="option.autoCrop"
:autoCropWidth="option.autoCropWidth"
:autoCropHeight="option.autoCropHeight"
:fixed="option.fixed"
:fixedNumber="option.fixedNumber"
:full="option.full"
:fixedBox="option.fixedBox"
:canMove="option.canMove"
:canMoveBox="option.canMoveBox"
:original="option.original"
:centerBox="option.centerBox"
:height="option.height"
:infoTrue="option.infoTrue"
:maxImgSize="option.maxImgSize"
:enlarge="option.enlarge"
:mode="option.mode"
@realTime="realTime"
>
</vueCropper>
</div>
<!-- 预览框 -->
<div class="show-preview" :style="{'width': '300px', 'height': '300px', 'overflow': 'hidden', 'margin': '0 25px', 'display':'flex', 'align-items' : 'center'}">
<div :style="previews.div" class="preview">
<img :src="previews.url" :style="previews.img">
</div>
</div>
</div>
<div class="footer-btn">
<!-- 缩放旋转按钮 -->
<div class="scope-btn">
<el-button type="primary" icon="el-icon-zoom-in" @click="changeScale(1)">放大</el-button>
<el-button type="primary" icon="el-icon-zoom-out" @click="changeScale(-1)">缩小</el-button>
<!-- <el-button type="primary" @click="rotateLeft">逆时针旋转</el-button>
<el-button type="primary" @click="rotateRight">顺时针旋转</el-button> -->
</div>
<!-- 确认上传按钮 -->
<div class="upload-btn">
<el-button type="primary" @click="uploadImg('blob')">上传</el-button>
</div>
</div>
</div>
</template>
<script>
import { VueCropper } from 'vue-cropper'
export default {
components: { VueCropper },
data () {
return {
previews: {}, // 预览数据
option: {
img: '', // 裁剪图片的地址 (默认:空)
outputSize: 1, //裁剪生成图片的质量(可选0.1 - 1)
outputType: 'jpeg', //裁剪生成图片的格式(jpeg || png || webp)
info: true, //图片大小信息
canScale: true, //图片是否允许滚轮缩放
autoCrop: true, //是否默认生成截图框
autoCropWidth: 300, //默认生成截图框宽度
autoCropHeight: 150, //默认生成截图框高度
fixed: true, //是否开启截图框宽高固定比例
fixedNumber: [1.77, 1], //截图框的宽高比例
full: false, //false按原比例裁切图片,不失真
fixedBox: true, //固定截图框大小,不允许改变
canMove: false, //上传图片是否可以移动
canMoveBox: true, //截图框能否拖动
original: false, //上传图片按照原始比例渲染
centerBox: false, //截图框是否被限制在图片里面
height: true, //是否按照设备的dpr 输出等比例图片
infoTrue: false, //true为展示真实输出图片宽高,false展示看到的截图框宽高
maxImgSize: 3000, //限制图片最大宽度和高度
enlarge: 1, //图片根据截图框输出比例倍数
mode: '400px 150px' //图片默认渲染方式
},
downImg: '#',
loading: false
}
},
props:['imgFile','fixedNumber'],
created(){
this.loading = true
this.Update()
},
methods: {
changeScale (num) {
// console.log("点击了图片缩放");
// 图片缩放
num = num || 1
this.$refs.cropper.changeScale(num)
},
rotateLeft () {
console.log("点击了向左旋转");
// 向左旋转
this.$refs.cropper.rotateLeft()
},
rotateRight () {
console.log("点击了向右旋转");
// 向右旋转
this.$refs.cropper.rotateRight()
},
Update () {
this.option.img = ''
console.log('this.imgFile',this.imgFile);
this.option.img = this.imgFile.url
this.loading = false
},
realTime (data) {
// 实时预览
this.previews = data
},
uploadImg (type) {
this.loading = this.$loading({
lock: true,
text: "上传中",
background: "rgba(0, 0, 0, 0.7)",
});
// console.log("点击了上传");
const emitArr = []
// 将剪裁好的图片回传给父组件
event.preventDefault()
let that = this
if (type === 'blob') {
this.$refs.cropper.getCropBlob(data => {
console.log('getCropBlob',data);
that.$emit('upload', data, this.imgFile.name)
this.loading.close();
})
} else {
this.$refs.cropper.getCropData(data => {
// console.log('getCropData',data);
that.$emit('upload', data, this.imgFile.name)
this.loading.close();
})
}
}
}
}
</script>
<style>
.cropper-content {
display: flex;
display: -webkit-flex;
justify-content: flex-end;
-webkit-justify-content: flex-end;
}
.cropper-content .cropper {
width: 350px;
height: 300px;
}
.cropper-content .show-preview {
flex: 1;
-webkit-flex: 1;
display: flex;
display: -webkit-flex;
justify-content: center;
-webkit-justify-content: center;
overflow: hidden;
border: 1px solid #cccccc;
background: #cccccc;
margin-left: 40px;
}
.preview {
overflow: hidden;
border: 1px solid #cccccc;
background: #cccccc;
}
.footer-btn {
margin-top: 30px;
display: flex;
display: -webkit-flex;
justify-content: flex-end;
-webkit-justify-content: flex-end;
}
.footer-btn .scope-btn {
width: 250px;
display: flex;
display: -webkit-flex;
justify-content: space-between;
-webkit-justify-content: space-between;
}
.footer-btn .upload-btn {
flex: 1;
-webkit-flex: 1;
display: flex;
display: -webkit-flex;
justify-content: center;
-webkit-justify-content: center;
}
.footer-btn .btn {
outline: none;
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
-webkit-appearance: none;
text-align: center;
-webkit-box-sizing: border-box;
box-sizing: border-box;
outline: 0;
margin: 0;
-webkit-transition: 0.1s;
transition: 0.1s;
font-weight: 500;
padding: 8px 15px;
font-size: 12px;
border-radius: 3px;
color: #fff;
background-color: #67c23a;
border-color: #67c23a;
}
</style>