使用Vue3撸一个弹窗组件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Vue3撸一个弹窗组件相关的知识,希望对你有一定的参考价值。

参考技术A 这里text,confirmBtnText,cancelBtnText三个变量由父组件传入。

text :弹窗的文本信息;

confirmBtnText :确认按钮的文本信息;

cancelBtnText :取消按钮的文本信息;

这里主要是定义confirm的过渡动画。

定义个props用来接收父组件传来的值。

定义data里面的变量visible来控制弹窗的出现和消失。

定义几个事件来完成这个组件。

confirm () :点击确认按钮,隐藏组件,并向父组件派发confirm事件;

cancel () :点击取消按钮,隐藏组件,并向父组件派发cancel事件;

hide () :隐藏组件;

show () :显示组件;

使用 emits 将这两个事件派发出去。

引用组件,拿到组件的ref,并定义一个confirm事件,来控制点击确定后面要干的事情。text,confirmBtnText控制组件显示的文本信息。

在使用组件的按钮处定义@click事件,来唤起组件让其显示。

业务逻辑这里使用setup函数组合API来编写。

先用ref拿到组件的dom

VUE2——事件驱动弹窗

前几天想了解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
    });
},






以上是关于使用Vue3撸一个弹窗组件的主要内容,如果未能解决你的问题,请参考以下文章

封装弹窗/抽屉,使用hook函数形式减少template内部代码

记录--Vue3+TS(uniapp)手撸一个聊天页面

微信小程序-自己封装一个弹窗组件

3、Vue3.0 实现一个简单的气泡弹窗

利用渲染函数,实现动态创建弹窗-Vue3.X

利用渲染函数,实现动态创建弹窗-Vue3.X