白话Vue.extend(自定义弹窗)
在使用vue开发中,大部分情况下我们都使用组件的方式进行渲染,但是在某些情况下,组件渲染确实带来很多问题(主要是麻烦),比如说,我需要一个确认弹窗,那么,一般情况下,大概会有下面一个流程:引入弹窗组件 =》声明部分弹窗需要的变量 =》 点击弹窗触发按钮 =》临时存储弹窗可能需要的部分变量参数 =》 弹窗回调方法;
如果一个文件中包含多个弹窗信息,那么其中的重复工作量可以想象,确实比较麻烦,那么问题来,可不可以把这个问题简单化呢,嗯,,,我想到了一个偷懒的方法,如果我可以通过api式的接口来渲染弹窗来解决问题呢,类似一下方式:
this.$yioks_confirm()
.then(res => {
console.log('用户确认了操作', res)
})
.catch(err => {
console.log('用户取消了该操作', err)
})
如果使用上面的方法,我不用引入组件,不用为组件声明变量数据,不用额外写用户操作的回调方法。完美!所以,为了实现上面的功能,我想到了*vue.extend()*来建立一个单独的渲染组件;
官方文档中提到,Vue.extend,使用基础vue构造器,创建一个子类,参数是一个包含组建选项的对象;data选项是特例,在这里必须是一个函数;
<div id="mount-point"></div>
// 创建构造器
var Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('#mount-point')
渲染结果就是:
<p>Walter White aka Heisenberg</p>
emmm,乍一看之下,这个文档似乎想告诉我点什么,但是又好像没有。好吧,就上面实例来看,Vue.textend可以创建一个Vue子类,然后大概可以使用js的方式,渲染html;这好像跟没说一样,我们可以这么理解,通过Vue.extend我们可以把一个html模板插入到现有dom中;想想我们的需求,把我们需要的弹窗模板,插入到现有的html中,很好,可以满足我们的需求!
弹窗实例
直接上代码,在根目录下新建extend文件夹,并下该文件夹下建立我们需要的弹窗文件夹;在confirm文件夹下建立src文件夹存放模板源码,新建index.js文件,方便将模板挂载到Vue上,供我们调用(以上是我个人喜好,各位看官大佬随意哈!);
模板文件:src/extend/confirm/src/confirm.vue
<template>
<transition name="fade">
<div class="confirm-box" v-if="isShow" ref="confirmDom">
<div class="modal-bg" >
<div class="confirm-content">
<!-- 标题区域 -->
<div class="title-content">
<p>{{ confirmTitle || "提示" }}</p>
<span
v-show="isShowClose"
class="close-btn"
@click="handleConfirm('close')"
>X</span
>
</div>
<!-- 提示区域 -->
<div class="msg-content">
<img
v-if="type === 'success'"
src="https://img-blog.csdnimg.cn/2022010704413484175.png"
alt="error"
/>
<img
v-if="type === 'warning'"
src="https://img-blog.csdnimg.cn/2022010704413495696.png"
alt="error"
/>
<img
v-if="type === 'error'"
src="https://img-blog.csdnimg.cn/2022010704413530613.png"
alt="error"
/>
<p class="tips-text">{{ confirmMsg }}</p>
</div>
<!-- 自定义配置按钮项 -->
<div v-if="btnList.length" class="action-content">
<div
v-for="(btnItem, index) in btnList"
:key="index"
:class="btnItem.classType"
class="btn"
@click="handleConfirm(btnItem.funFlag)"
>
{{ btnItem.benText }}
</div>
</div>
<!-- 默认按钮项目 -->
<div v-else class="action-content">
<div
class="btn confirm"
v-if="confirmBtnText"
@click="handleConfirm('confirm')"
>
{{ confirmBtnText }}
</div>
<div
class="btn other"
v-if="otherBtnText"
@click="handleConfirm('other')"
>
{{ otherBtnText }}
</div>
<div
class="btn cancel"
v-if="cancelBtnText"
@click="handleConfirm('cancel')"
>
{{ cancelBtnText }}
</div>
</div>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
data() {
return {
isShow: true,
confirmTitle: "",
confirmMsg: "",
type: "success", // success, error, warning
confirmBtnText: "确定",
cancelBtnText: "取消",
otherBtnText: "", // 冗余
btnList: [], // { classType: 'success', funFlag: 'suc', benText: 'confirm' }
isShowClose: true,
};
},
methods: {
handleConfirm(status) {
},
closeConfirm() {
this.isShow = false;
if (this.$refs.confirmDom) this.$refs.confirmDom.remove();
},
},
};
</script>
<style lang="stylus" scoped>
@import '~./css/confirm'
.fade-enter-active, .fade-leave-active
transition opacity 0.4s
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */
opacity 0
</style>
样式文件src/extend/confirm/src/css/confirm.styl
.confirm-box
position: fixed
top 0px
left 0px
display: flex
justify-content: center
align-items: center
background-color: rgba(14,14,14,.3)
width: 100vw
height: 100vh
user-select: none
.modal-bg
.confirm-content
width: 460px
// height: 300px
background: #fff
border-radius: 4px
margin-bottom: 100px
.title-content
height: 40px
width: 100%
padding 25px 15px 15px
box-sizing: border-box
display: flex
align-items: center
justify-content: space-between
user-select: none
border-radius: 4px 4px 0 0
position: relative
p
font-size: 1.4em
.close-btn
box-sizing: border-box
background-color: #4cd964
color: #fff
padding: 5px 10px
cursor pointer
border-radius: 50%
position: absolute
top: -13px
right: -13px
.msg-content
min-height: 200px
padding: 5px 15px
box-sizing: border-box
display: flex
justify-content: center
align-items: center
flex-direction: column
img
width: 64px
height: 64px
margin-bottom: 35px
.tips-text
font-size: 1.2em
font-weight: 500
display: flex
align-items: center
justify-content: center
.action-content
display: flex
height: 40px
align-items: center
justify-content: space-around
width: 80%
margin: 0 auto
padding-bottom: 20px
margin-top: 20px
.btn
padding: 8px 25px
cursor pointer
border-radius 4px
color #fff
margin: 0px 10px
&.confirm
background-color: #7bed9f
&.cancel
background-color: #ff4757
&.other
background-color: #ffa502
js配置文件extend/confirm/src/confirm.js
import Vue from "vue";
import confirm from './confirm.vue';
let ConfirmConstructor = Vue.extend(confirm);
let instance;
const Confirm = function(options = {}) {
return new Promise((resolve,reject) => {
instance = new ConfirmConstructor({
el: document.createElement('div'),
data: options,
}).$mount();
document.body.appendChild(instance.$el);
instance.content = options;
instance.handleConfirm = function(status) {
const { btnList } = instance;
if('close' === status) {
reject(status);
instance.isShow = false;
// instance.closeConfirm();
}
if(btnList && btnList.length) {
btnList.forEach(element => {
if(['err','error','cancel'].includes(element.funFlag)) {
reject(element.funFlag)
instance.isShow = false;
instance.closeConfirm();
} else {
resolve(element.funFlag)
instance.isShow = false;
instance.closeConfirm();
}
});
} else {
if(status === 'cancel') {
reject(status);
instance.isShow = false;
// instance.closeConfirm();
} else {
resolve(status)
instance.isShow = false;
// instance.closeConfirm();
}
}
}
})
}
export default Confirm;
js配置文件src/extend/confirm/index.js
import Vue from "vue";
import Confirm from "./src/confirm";
Vue.prototype.$yioks_confirm = Confirm; //在vue实例上挂载我们的组件
最后,需要在main.js文件中使用本文件即可
import './extend/confirm'; // 执行关联
之后,就可以在需要的地方放心使用就可以了;
// 点击按钮,提示信息
handleBtnClick() {
this.$yioks_confirm({
confirmMsg: '这是一条警示信息',
type: 'error',
btnList: [{ classType: 'confirm', funFlag: 'suc', benText: 'confirm' },
{ classType: 'cancel', funFlag: 'err', benText: 'cancel' }
]
}).then(res => {
console.log('用户确认了操作', res)
}).catch(err => {
console.log('用户取消了该操作', err)
})
}
通过btnList自定义配置的弹窗按钮:
默认配置弹窗:
以上就是我提供的方法;这里解释下该方法的使用方便道友们自己扩展编辑:
在confirm.vue文件中的data方法所定义的变量,均可以通过配置参数重新定义到渲染的插件中;例如修改按钮的文字,
自定义配置项
this.$yioks_confirm({
confirmTitle: "", // 弹窗标题
confirmMsg: "", // 弹窗提示信息
type: "success", // 默认提供三种样式;success, error, warning;默认success
confirmBtnText: '确定',// 确认按钮文字
cancelBtnText: '取消', // 取消按钮文字
otherBtnText: '', // 提供额外的第三个按钮,注意按钮文字如果配置为空,则弹窗不显示按钮
btnList: [], // 如果需要多个按钮,则可以通过btnList配置。注意配置按钮的参数
isShowClose: true, // 是否显示右上角关闭按钮
})
自定义按钮列表
注意,按钮标记funFlag字段如果配置成err
,error
,cancel
,则会被一部reject处理,即用户只能通过catch方法处理该按钮的结果。按钮样式,在我的样式文件中提供了三种不同的风格:success
,error
,warning
;
btnList: [
{
classType: 'success', // 按钮样式,默认提供三种样式:success, error, warning;默认success
funFlag: 'suc', // 按钮标记,用户可以在then中活到到按钮标记区分用户的操作,用户可以自己定义
benText: 'confirm' // 按钮文字
}
]
以上,就完成一个自定义的弹窗,以后就可以在代码中愉快的使用了;详细代码丢码云了,有需要的道友自己去爬;这个东西只是方便我自己使用,参考了elementUI代码的处理方式,有什么问题欢迎大家指正。大家也可以自己修改使用。