封装组件的v-model
再看组件的时候,不如elementui等等,我们时常看到它们的组件都是直接通过v-model双向绑定的,而不是通过我们常用的属性(prop)传递过去,子组件修改通过$emit,或者通过vuex等等来返回父组件,这样的方法也不是说不行,但是总感觉没有elementui那样的写法高级。所以我们也来简单的封装一个看起来高级一点点的组件。
简单的借用elementui的组件做一个二开
<template>
<div class="image-video-upload">
<div v-if="imageUrl" class="upload-success">
<custom-image :src="imageUrl" class="result-image" :style="imageStyle" />
<i v-if="!disabled" class="el-icon-circle-close" @click="handleRemove" />
</div>
<el-upload
v-show="!imageUrl"
ref="uploader"
class="uploader"
:action="action"
:disabled="disabled"
:headers="headers"
:accept="accept"
:show-file-list="false"
:on-success="handlerSuccess"
:on-error="handlerError"
:before-upload="beforeUploadHandler"
:style="imageStyle"
:on-progress="handleProcess"
:data="uploadData"
>
<el-progress v-if="uploading" type="circle" :percentage="percentage" :width="80" />
<i v-else class="el-icon-plus" />
</el-upload>
<div class="el-upload__tip" v-html="tip" />
</div>
</template>
export default {
name: 'UploadImage',
components: {
CustomImage
},
props: {
//v-model直接使用value
value: {
type: String,
default: ''
},
bucket: {
type: String,
default: 'knight-dev'
},
tip: {
type: String,
default: ''
},
// 控制图片样式(长宽等)
imageStyle: {
type: String,
default: 'width: 200px;height: 100px;'
},
accept: {
type: String,
default: '.jpg,.jpeg,.png,.gif,.bmp,.JPG,.JPEG,.PBG,.GIF,.BMP'
},
width: {
type: Number,
default: 0
},
height: {
type: Number,
default: 0
},
// 图片尺寸验证类型
validType: {
type: Number,
default: 0 // 0:比列验证,1:实际大小验证
},
// 图片尺寸验证
valid: {
type: Number,
default: 0 // 0: 不验证,不提示 1:验证提示,不停止上传 2: 验证提示,停止上传
},
// 图片大小验证,单位M
imageSize: {
type: Number,
default: 3
},
disabled: {
type: Boolean,
default: false
}
},
data() {
return {
uploading: false,
percentage: 0,
imageUrl: this.value ? downloadUrl + '/' + this.value : '',
action: uploadUrl
}
},
computed: {
uploadData() {
return { bucket: this.bucket }
},
headers() {
const header = {}
const { access_token, login_name } = this.$store.getters.authInfo
header['Authorization'] = 'Bearer ' + access_token
return header
}
},
watch: {
value(value) {
this.imageUrl = value ? downloadUrl + '/' + value : ''
}
},
methods: {
handleProcess(event, file, fileList) {
this.percentage = +file.percentage.toFixed(0)
},
beforeUploadHandler(file) {
this.uploading = true
this.percentage = 0
const fileSize = file.size / 1024 / 1024
if (fileSize > this.imageSize) {
this.uploading = false
this.$message.error(`图片文件大小不能超过 ${this.imageSize}MB!`)
return false
} else if (this.valid == 0 || this.width == 0 || this.height == 0) {
return true
}
return new Promise((resolve, reject) => {
this.filetoDataURL(file, url => {
this.dataURLtoImage(url, img => {
const { width, height } = this
let validTemp = true
let str = ''
if (this.validType == 0) {
validTemp =
(width / height).toFixed(2) ==
(img.width / img.height).toFixed(2)
str = '比例'
} else {
validTemp = width == img.width && height == img.height
str = '宽高'
}
if (!validTemp) {
this.$message.error(`图片${str}不符合建议要求!`)
if (this.valid == 2) {
this.uploading = false
return reject()
}
}
resolve()
})
})
})
},
handlerSuccess(res) {
if (res.code == 200) {
const imageUrl = res.data.fileName[0]
this.uploading = false
this.setValue(imageUrl)
}
},
handlerError() {
this.uploading = false
},
setValue(imageUrl) {
this.imageUrl = imageUrl
//返回值直接使用$emit('input','value')即可
this.$emit('input', imageUrl)
},
handleRemove() {
this.setValue('')
}
}
}
.image-video-upload {
display: flex;
.uploader,
.result-image {
display: flex;
font-size: 28px;
color: #8c939d;
text-align: center;
background-color: #fbfdff;
border: 1px dashed #c0ccda;
border-radius: 6px;
cursor: pointer;
}
.result-image {
border: 1px solid #c0ccda;
}
.uploader{
&:hover {
border-color: #409eff;
color: #409eff;
}
::v-deep {
.el-upload{
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
&:focus{
border-color: #fbfdff;
color: #8c939d;
}
&:hover{
border-color: #409eff;
color: #409eff;
}
}
}
}
.el-upload__tip {
flex: 1;
line-height: 21px;
display: flex;
align-items: center;
margin-left: 20px;
margin-top: 0;
}
.upload-success {
position: relative;
border-radius: 6px;
overflow: hidden;
&:hover {
.icon-success {
display: none;
}
.remove-wrapper {
display: flex;
}
}
.el-icon-circle-close{
position: absolute;
top: 0;
right: 0;
font-size: 20px;
color: #F56C6C;
}
}
.remove-wrapper {
display: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.5);
}
.icon-success {
position: absolute;
right: -15px;
top: -6px;
width: 40px;
height: 24px;
// line-height: 24px;
text-align: center;
background: #13ce66;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
-webkit-box-shadow: 0 0 1pc 1px rgba(0, 0, 0, 0.2);
box-shadow: 0 0 1pc 1px rgba(0, 0, 0, 0.2);
.el-icon-check {
color: #ffffff;
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
}
}
.el-icon-delete {
font-size: 20px;
color: #fff;
cursor: pointer;
}
}
这样使用的时候就可以直接引入组件使用了,绑定的值也是可以通过v-midel双向了