效果图:
1、安装vue-cropper
npm install vue-cropper
2、在需要的页面引入vue-cropper
import { VueCropper } from 'vue-cropper'
components: {
VueCropper,
},
3、HTML
<el-form-item class="artImg" label="文章封面">
<div style="display: flex">
<div>
<el-button v-if="openDialogs" type="primary" @click="openDialog">
点击上传长方形封面
</el-button>
<div v-if="openDialogshide">
<el-upload
ref="upload"
:action="action"
:before-upload="bfUpload"
:class="{
hide: hideUpload,
}"
:file-list="fileList"
:headers="headers"
:limit="limitCount"
list-type="picture-card"
:on-change="handleChange"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
:on-success="handleSuccess"
>
<i class="el-icon-plus"></i>
</el-upload>
//图片预览
<el-dialog append-to-body :visible.sync="dialogVisible">
<img alt="" :src="dialogImageUrl" width="100%" />
</el-dialog>
</div>
</div>
</div>
</el-form-item>
// 弹框
<el-dialog
class="cropperDialog"
:close-on-click-modal="false"
title="文章封面裁剪"
:visible.sync="dialogVisibleImgs"
width="40%"
@close="close"
>
<el-row>
<el-col
v-if="option.img == ''"
:span="24"
style="
text-align: center;
font-size: 16px;
color: red;
padding: 30px 0;
"
>
请先选择图片
</el-col>
<el-col v-else :span="24">
<div class="cropper-content">
<div class="cropper" style="text-align: center">
<vueCropper
ref="cropper"
:auto-crop="option.autoCrop"
:can-move="option.canMove"
:can-move-box="option.canMoveBox"
:center-box="option.centerBox"
:fixed="option.fixed"
:fixed-box="option.fixedBox"
:fixed-number="option.fixedNumber"
:full="option.full"
:img="option.img"
:info="true"
:info-true="option.infoTrue"
:original="option.original"
:output-size="option.size"
:output-type="option.outputType"
/>
</div>
</div>
</el-col>
</el-row>
<template #footer>
<div>
<el-button :loading="loading" type="primary" @click="upload">
上传并保存
</el-button>
<el-upload
accept="image/jpeg,image/gif,image/png,image/jpg"
:action="action"
:auto-upload="false"
:headers="headers"
list-type="picture-card"
:on-change="onChange"
>
<template #default>
<el-button type="primary">选择图片</el-button>
</template>
</el-upload>
</div>
</template>
</el-dialog>
4、js
/config文件夹中 uploadGoodsImage (按自己的项目需求配置)
/api/article文件夹中 uploadURL (按自己项目需求配置)
<script>
import { VueCropper } from 'vue-cropper' //vue-cropper插件
import { uploadURL } from '@/config' // //引入封装好的上传接口
import {uploadGoodsImage } from '@/api/article' //引入封装好的上传接口
import { mapGetters } from 'vuex' //因项目需求上传headers中必须带有token,储存在vuex中
export default {
components: {
VueCropper,
},
data(){
option: {
img: '', // 裁剪图片的地址
info: true, // 裁剪框的大小信息
outputSize: 0.8, // 裁剪生成图片的质量
outputType: 'png', // 裁剪生成图片的格式
canScale: false, // 图片是否允许滚轮缩放
autoCrop: true, // 是否默认生成截图框
autoCropWidth: 460, // 默认生成截图框宽度
autoCropHeight: 240, // 默认生成截图框高度
fixedBox: false, // 固定截图框大小 不允许改变
fixed: true, // 是否开启截图框宽高固定比例
fixedNumber: [23, 12], // 截图框的宽高比例
full: true, // 是否输出原图比例的截图
canMoveBox: true, // 截图框能否拖动
original: true, // 上传图片按照原始比例渲染
centerBox: true, // 截图框是否被限制在图片里面
infoTrue: true, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
canMove: true,
enlarge: 1,
},
openDialogshide: false,
openDialogs: true,
loading: false,
dialogVisibleImgs: false, //弹框
hideUpload: false,
limitCount: 1,
dialogImageUrl: '', //图片预览
dialogVisible: false, //图片预览
fileList: [],
headers: {},
action: uploadURL,
},
computed: {
...mapGetters({
token: 'user/token',
}),
},
created() {
this.headers['Authorization'] = `Bearer ${this.token}`
},
methods:{
//将base64转换为file文件流
base64toFile(dataurl, filename = 'file') {
let arr = dataurl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let suffix = mime.split('/')[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime,
})
},
// 选择图片
onChange(file, fileList) {
console.log(file, fileList)
this.$nextTick(() => {
this.option.img = URL.createObjectURL(file.raw)
})
},
// 关闭弹框
close() {
this.dialogVisibleImgs = false
this.option.img = ''
},
// 上传点击保存
upload() {
if (!this.option.img && this.option.img == '')
return this.ME('请先选择图片')
this.loading = true
this.$refs.cropper.getCropData((data) => {
const optionImg = this.base64toFile(data)
// this.option.img = this.base64toFile(data)
const formData = new FormData()
formData.append('file', optionImg)
//请求
uploadGoodsImage(formData)
.then((res) => {
this.MS('上传成功')
this.articleForm.artImg = res.data.filePath
this.fileList.push({
url: res.data.filePath,
})
this.hideUpload = this.fileList.length >= this.limitCount
this.openDialogs = false
this.openDialogshide = true
this.loading = false
this.close()
})
.catch(() => {
this.loading = false
})
})
},
// 点击上传封面
openDialog() {
this.dialogVisibleImgs = true
},
//上传图片判断格式
bfUpload(file) {
if (
'image/png' == file.type ||
'image/jpeg' == file.type ||
'image/jpg' == file.type
) {
console.log()
} else {
this.ME('图片上传失败,请上传png,jpeg,jpg格式')
return false
}
},
// 上传
handleChange(file, fileList) {
console.log(file, fileList)
this.hideUpload = fileList.length >= this.limitCount
},
// 上传成功
handleSuccess(response, file) {
console.log(response, file)
this.articleForm.artImg = response.data.filePath
this.fileList.push({
url: response.data.filePath,
})
},
// 删除图片
handleRemove(fileList) {
this.articleForm.artImg = ''
this.fileList = []
this.option.img = ''
this.hideUpload = fileList.length >= this.limitCount
this.openDialogs = true
this.openDialogshide = false
},
// 图片预览
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
}
}
</script>
5、css
<style lang="scss" scoped>
.artImg {
::v-deep.hide .el-upload--picture-card {
display: none;
}
}
::v-deep.cropper-content {
.cropper {
width: auto;
height: 400px;
}
}
</style>