上传组件及展示,(可简历建立vue文件直接赋值)
<template>
<div>
<div class="uploadWrap">
<el-upload
class="avatar-uploader"
action="#"
:http-request="uploadFileFunc"
:before-upload="beforeAvatarUpload"
:disabled="uploadImgTchDisabled"
:show-file-list="false"
accept=".jpg, .jpeg, .png, .PNG"
>
<div class="avatar-uploader-icon">
<i class="el-icon-plus" />
</div>
</el-upload>
<div
v-for="(item, index) in imgUrlList"
v-show="imgUrlList.length > 0"
:key="index"
@click="selectImg(item)"
>
<div v-if="index===0" class="coverimg" :class="{ selectedItem: item === bindVal }">
<img class="img_size" :src="item" :style="{ 'object-fit': mode }">
<span v-if="imgUrlList.length - index > imgList.length" class="delWrap">
<div class="delBox" @click.stop="handleRemove(index)">
<i class="el-icon-delete" />
</div>
</span>
</div>
</div>
</div>
<ImgCropper
:img-url="originalImg"
:ratio="ratio"
:width="ratio[0]"
:height="ratio[1]"
:show.sync="showCropper"
@success="success"
/>
</div>
</template>
<script>
import mixin from '@/mixin/two-ways'
import { uploadImg } from '@/api/common'
export default {
name: 'ImgTemplate',
mixins: [mixin()],
props: {
imgList: {
type: Array,
default: () => []
},
mode: {
type: String,
default: 'cover'
},
ratio: {
type: Array,
default: () => [750, 1334]
},
// 是否需要裁剪
isCut: {
type: Boolean,
default: true
}
},
data() {
return {
imgUrlList: [],
originalImg: '',
showCropper: false,
uploadImgTchDisabled: false
}
},
watch: {
imgList() {
this.imgUrlList = Object.assign([], this.imgList)
}
},
methods: {
handleRemove(index) {
this.imgUrlList.splice(index, 1)
this.bindVal = this.imgUrlList[0]
},
getObj() {
return { backgroundImg: this.planTotalObj.m1 }
},
selectImg(item) {
this.bindVal = item
},
success({ blob }) {
this.handleTchBeforeUpload(blob)
},
handleLimit(file, fileList) {
if (fileList.length >= 1) {
this.disabled = true
} else {
this.disabled = false
}
this.$forceUpdate()
},
uploadFileFunc(file) {
if (!this.isCut) {
this.handleTchBeforeUpload(file.file)
}
},
beforeAvatarUpload(file) {
var hase = file.name.lastIndexOf('.')
var hasetype = file.name.substr(hase).toLowerCase()
const isJPEG = hasetype === '.jpeg'
const isJPG = hasetype === '.jpg'
const isGIF = hasetype === '.gif'
const isPNG = hasetype === '.png'
if (!isJPG && !isJPEG && !isGIF && !isPNG) {
this.$message({
message: '上传图片格式不正确!',
type: 'error'
})
}
if (this.isCut) {
this.originalImg = window.URL.createObjectURL(new Blob([file]))
this.showCropper = true
}
const result = isJPG || isJPEG || isGIF || isPNG
return result
},
handleTchBeforeUpload(file) {
const formData = new FormData() // formdata格式
// 追加文件的名字
formData.append('imageFile', file)
this.uploadImgTchDisabled = true
const loading = this.$loading({
lock: true,
text: '图片上传中...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
uploadImg(formData)
.then(res => {
loading.close()
this.uploadImgTchDisabled = false
if (res.successful && res.rt) {
this.imgUrlList.unshift(res.rt.oSSFileDto.path)
this.bindVal = res.rt.oSSFileDto.path
}
})
.catch(() => {
loading.close()
this.uploadImgTchDisabled = false
})
}
}
}
</script>
<style scoped lang="scss">
::v-deep .avatar-uploader {
height: 110px;
width: 110px;
border-radius: 4px;
margin: 0 12px 12px 0;
border-radius: 4px;
.el-upload {
background: rgba(255, 255, 255, 0.2);
border-radius: 4px;
}
.el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
color: #fff;
width: 110px;
height: 110px;
display: flex;
align-items: center;
justify-content: center;
.el-icon-plus {
font-size: 30px;
}
}
.avatar {
width: 100%;
height: 100%;
display: block;
}
}
.uploadWrap {
display: flex;
align-items: center;
flex-wrap: wrap;
.delWrap {
z-index: 101;
position: absolute;
width: 100%;
height: 100%;
right: 0;
top: 0;
cursor: pointer;
text-align: center;
color: #fff;
opacity: 0;
font-size: 20px;
transition: opacity 0.3s;
&:hover {
opacity: 10;
}
.delBox {
position: absolute;
right: 0;
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.4;
background: #07164d;
border-radius: 0 4px 0 4px;
-webkit-transition: all 0.3s ease-in;
-moz-transition: all 0.3s ease-in;
transition: all 0.3s ease-in;
.el-icon-delete {
margin-left: 2px;
margin-bottom: 2px;
cursor: pointer;
font-size: 16px;
}
}
}
.selectedItem {
position: relative;
border: 3px solid #11fefd !important;
transform: scale(1.02);
&::before {
content: '';
position: absolute;
left: -1px;
top: -1px;
width: 16px;
height: 16px;
background: url('//image.g2s.cn/zhs_yanfa_150820/ablecommons/demo/202207/d9a8a6b0241f478693e0133adf8245c1.png');
background-size: cover;
image-rendering: -moz-crisp-edges; /* Firefox */
image-rendering: -o-crisp-edges; /* Opera */
image-rendering: -webkit-optimize-contrast; /*Webkit (non-standard naming) */
image-rendering: crisp-edges;
-ms-interpolation-mode: nearest-neighbor; /* IE (non-standard property) */
}
}
.coverimg {
position: relative;
cursor: pointer;
width: 110px;
height: 110px;
background-repeat: no-repeat;
background-size: 100% 100%;
margin: 0 12px 12px 0;
border-radius: 4px;
border: 1px solid rgba(29, 95, 133, 1);
.img_size {
width: 100%;
height: 100%;
}
}
}
::v-deep .hiddenUpload .el-upload.el-upload--picture-card {
display: none !important;
}
::v-deep .hiddenUpload .el-button--success.is-plain {
display: none !important;
}
</style>
截图组件,前提是学会看之前的双向书库绑定
<template>
<el-dialog title="照片裁剪" :visible.sync="syncShow" width="650px" append-to-body>
<div style="width: 100%; height: 500px">
<vue-cropper
ref="cropper"
auto-crop
:fixed="fixed"
:img="imgUrl"
:auto-crop-width="width"
info-true
output-type="png"
:auto-crop-height="height"
:fixed-number="ratio"
:center-box="false"
/>
</div>
<span slot="footer" class="dialog-footer">
<div class="vue-cropper-operation fl">
<el-button
type="primary"
size="mini"
@click="$refs.cropper.changeScale(1)"
>放大</el-button>
<el-button
type="primary"
size="mini"
@click="$refs.cropper.changeScale(-1)"
>缩小</el-button>
<el-button
type="primary"
size="mini"
@click="$refs.cropper.rotateLeft()"
>左旋转</el-button>
<el-button
type="primary"
size="mini"
@click="$refs.cropper.rotateRight()"
>右旋转</el-button>
</div>
<el-button @click="syncShow = false">取 消</el-button>
<el-button type="primary" @click="confirm">确 定</el-button>
</span>
</el-dialog>
</template>
<script>
import twoWaysSyncMixin from '@/mixin/two-ways-sync'
import { VueCropper } from 'vue-cropper'
export default {
name: 'ImgCropper',
components: {
VueCropper
},
mixins: [
twoWaysSyncMixin({
show: {
type: Boolean,
default: false
}
})
],
props: {
// 裁剪图片地址
imgUrl: {
type: [String, Blob],
required: false,
default: ''
},
// 裁剪图片宽度
width: {
type: Number,
default: 280
},
// 裁剪图片高
height: {
type: Number,
default: 280
},
// 宽高比
ratio: {
type: Array,
default: () => [9, 16]
},
// 固定宽高比
fixed: {
type: Boolean,
default: true
},
// 自动上传
autoUpload: {
type: Boolean,
default: false
},
// 获取数据类型
fetchType: {
type: String,
default: 'blob',
validator(value) {
const valid = ['blob', 'base64'].includes(value)
return valid
}
}
},
methods: {
confirm() {
const obj = {
blob: 'getCropBlob',
base64: 'getCropData'
}
// 获取截图数据
this.$refs.cropper[obj[this.fetchType]]((data) => {
this.syncShow = false
this.handleSuccess(data)
})
},
// 处理成功回调
handleSuccess(blob) {
this.$emit('success', {
blob
})
}
}
}
</script>
<style lang="scss" scoped></style>
在哪个页面使用直接引入
<ImgTemplate
v-model="ruleForm.bannerFileUrl"
:ratio="[750, 750]"
/>