首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue自定义组件封装_vue组件的双向绑定实现

vue自定义组件封装_vue组件的双向绑定实现

作者头像
全栈程序员站长
发布2022-11-09 13:14:45
1.3K0
发布2022-11-09 13:14:45
举报

大家好,又见面了,我是你们的朋友全栈君。

封装组件的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双向了

这就是最终的呈现效果啦!!!
这就是最终的呈现效果啦!!!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/190462.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022年9月23日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 封装组件的v-model
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档