前几天想了解vue如何写弹窗组件,参考了知乎上的回答:
https://www.zhihu.com/questio...
有以下两种可取的写法:
1.状态管理 如果弹窗组件放在根组件,使用vuex来管理组件的show和hide。放在组件内,通过增加v-show或v-if来控制,可结合slot,定义不同需求的弹窗
2.事件管理 注册一个全局事件来打开弹窗,传入需展示的文字和相关的逻辑控制,可结合promise,实现异步
觉得对用像confirme和propmt这类弹窗,还是事件驱动的好。最好就是能使用promise回调。
于是手痒就写了一个。下面是代码。
propmt.js
import Vue from ‘vue‘
import promptComponent from ‘./prompt.vue‘ // 引入弹窗的vue文件
const promptConstructor = Vue.extend(promptComponent); // 注册组件
let instance = new promptConstructor().$mount(‘‘); // 获得组件的实例
document.body.appendChild(instance.$el); // 将组件的element插入到body中
const Alert = (text,okText)=>{
if(instance.show === true) { //防止多次触发
return;
}
// 为弹窗数据赋值
instance.show = true;
instance.isAlert = true;
instance.okText = okText||‘确定‘;
instance.message = text;
//返回一个promise对象,并为按钮添加事件监听
return new Promise(function(resolve,reject) {
instance.$refs.okBtn.addEventListener(‘click‘,function() {
instance.show = false;
resolve(true);
})
})
};
const Confirm = (text,okText,cancelText)=>{
if(instance.show === true) {
return;
}
instance.show = true;
instance.okText = okText||‘确定‘;
instance.cancelText = cancelText||‘取消‘;
instance.message = text;
return new Promise(function(resolve,reject) {
instance.$refs.cancelBtn.addEventListener(‘click‘,function() {
instance.show = false;
resolve(false);
});
instance.$refs.okBtn.addEventListener(‘click‘,function() {
instance.show = false;
resolve(true);
})
})
};
const Prompt = (text,okText,inputType, defaultValue)=>{
if(instance.show === true) {
return;
}
instance.show = true;
instance.isPrompt = true;
instance.okText = okText||‘确定‘;
instance.message = text;
instance.inputType = inputType || ‘text‘;
instance.inputValue = defaultValue || ‘‘;
return new Promise(function(resolve,reject) {
instance.$refs.okBtn.addEventListener(‘click‘,function() {
instance.show = false;
resolve(instance.inputValue);
})
})
};
export {Alert,Confirm,Prompt}
prompt.vue
<style lang="less" scoped>
.confirm-enter-active {
transition: all .2s;
}
.confirm-leave-active {
transition: opacity .2s;
}
.confirm-leave-to {
opacity: 0;
}
.confirm-enter {
opacity: 0;
}
.confirm {
position: relative;
font-family: PingFangSC-Regular;
font-size: 17px;
-webkit-user-select: none;
user-select: none;
// 遮罩层样式
.masker {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .4);
-webkit-transition: opacity .1s linear;
transition: opacity .1s linear;
z-index: 100;
}
// 入库数据错误样式
.box {
position: absolute;
top: 50%;
left: 50%;
width: 72%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
text-align: center;
border-radius: 12px;
background-color: #fff;
.message {
height: 97px;
line-height: 24px;
font-family: PingFangSC-Regular;
font-size: 17px;
vertical-align: middle;
color: #999;
letter-spacing: -0.41px;
p {
margin: 20px auto 0 auto;
vertical-align: middle;
}
&::after {
content: ‘‘;
height: 100%;
}
}
.prompt {
margin: 20px 0;
width: 100%;
p {
margin: 5px auto;
font-size: 17px;
line-height: 24px;
}
input {
margin: 5px auto;
border: 1px solid #333;
border-radius: 6px;
width: 100px;
height: 30px;
font-size: 14px;
line-height: 20px;
text-align: center;
}
}
.button-group {
a {
width: calc(50% - 0.5px);
text-align: center;
font-size: 17px;
line-height: 43px;
color: blue;
}
.max-width {
width: 100% !important;;
}
}
}
}
</style>
<template>
<transition name="confirm">
<div class="confirm" v-show="show">
<div class="masker" @touchmove.prevent>
<div class="box">
<div class="message" v-if="!isPrompt">
<p>{{message}}</p>
</div>
<div class="prompt" v-if="isPrompt">
<p>{{message}}</p>
<input type="text" v-model="inputValue" v-if="inputType === ‘text‘" ref="inputEl">
<input type="tel" v-model.number="inputValue" @keydown="enterCheck" v-if="inputType === ‘tel‘" ref="inputEl">
</div>
<div class="button-group clearfix bd-top">
<a class="bd-right fl" ref="cancelBtn" v-show="!isAlert && !isPrompt">{{cancelText}}</a>
<a class="fr" ref="okBtn" :class="{‘max-width‘: isAlert || isPrompt}">{{okText}}</a>
</div>
</div>
</div>
</div>
</transition>
</template>
<script type="text/ecmascript-6">
import {mapState} from ‘vuex‘
export default{
data() {
return {
show: false,
message: ‘请输入提示语‘,
okText: ‘确定‘,
cancelText: ‘取消‘,
isAlert: false,
isPrompt: false,
inputValue: ‘‘,
inputType: ‘‘
}
},
methods: {
// 金额输入框校验
enterCheck(event) {
// 只允许输入数字,删除键,11位数字
if (event.keyCode === 46 || event.keyCode === 8) {
return;
}
if (event.keyCode < 47 || event.keyCode > 58 || event.keyCode === 190) {
event.returnValue = false;
}
},
},
watch: {
show(){
if (this.show) {
this.$nextTick(() => {
console.log(this.$refs.inputEl);
console.log(this.inputType);
this.$refs.inputEl.focus();
});
}
}
}
}
</script>
main.js
import {Alert,Prompt,Confirm} from ‘../lib/components/prompt/prompt.js‘
Vue.prototype.Alert = function(text,okText) {
return Alert(text,okText)
};
Vue.prototype.Confirm = function(text,okText,cancelText) {
return Confirm(text,okText,cancelText)
};
Vue.prototype.Prompt = function(text,okText,inputType, defaultValue) {
return Prompt(text,okText,inputType, defaultValue)
};
component.vue:
inputName() {
this.Prompt(‘请输入名称‘,‘确认‘,‘text‘).then(res =>{
// do something use res
});
},