关于vue3如何封装命令式modal(免写组件标签)

由于之前搜索其他modal框封装的文章时,发现大多数在使用时需要引入并注册组件,传入参数再写方法。每次引用时都要经过上述操作,觉得太麻烦了。后面通过一些资料摸索出vue3如何利用命令方式(即避免写组件标签)来弹出弹框,写下这篇文章,方便日后自己复习

调用及效果

先上使用方法及效果

代码

<template>
    <div class="container">
        <div class="title_box flex_box">
            <div class="out" @click="out">出来啦</div>
        </div>
    </div>
</template>

<script setup>
// 获取js文件
import globalCom from '@/components/globalCom/index'
let i = 0
function out(){
  globalCom.show({title:'55'+i++,leftText:'取消了'}).then(res=>{
    // 点击左侧res为left,右侧为right
    console.log(res);
  })
}
</script>

通过上图可以看到,该调用方式是第一次调用时将弹框挂载到页面上,然后是通过display属性及动态变更内容的方式来修改弹框内容
下面是该方法的封装以及实现

大致思路

组件设计

接下来看看组件

先像常规写弹框组件一样先将其写好,此处左右按钮分别设为left和right,

比较特殊的是需要准备leftClick和rightClick,这是外部需要能够控制的回调,点击左右按钮会分别调用

第二个比较特殊的是getShowModal,这是外部用于获取当前组件的showModal方法

<template>
    <div class="container" v-show="visible">
        <div class="pup_bg" @click="closeModal"></div>
        <div class="main">
            <div class="text">{{ title }}</div>
            <div class="btns">
                <div class="btn_left" @click="lClick">{{ leftText }}</div>
                <div class="btn_right" @click="rClick">{{ rightText }}</div>
            </div>
        </div>
    </div>
</template>

<script lang="ts">
import {  onMounted, ref } from "vue";
export default {
    props: {
        leftText: {
            type: String,
            default: '取消'
        },
        rightText:{
            type:String,
            default:'确认'
        },
        title: {
            type: String,
            default: '',
            require:true
        },
        getShowModal: {
            type: Function,
            default: () => { }
        },
        leftClick: {
            type: Function,
            default: () => { }
        },
        rightClick: {
            type: Function,
            default: () => { }
        }
    },
    setup(props) {
        let visible = ref(true)
        onMounted(() => {
            console.log('props');
        })
        // 点击左侧按钮 先调用传入的leftClick在关闭model
        function lClick() {
            props.leftClick()
            closeModal()
        }
        // 右侧同理
        function rClick() {
            props.rightClick()
            closeModal()
        }
        function showModal() {
            // 此处避免弹框展示时滚动
            document.body.style.overflow = 'hidden'
            visible.value = true
        }
        function closeModal(){
            document.body.style.overflow = 'visible'
            visible.value = false
        }
        // 使得外界可以获得并调用showModal
        props.getShowModal && props.getShowModal(showModal)
        return {
            visible, lClick, rClick,closeModal
        }
    }
}
</script>

<style lang="scss" scoped>
.btns {
    width: 100%;
    height: 1rem;
    font-size: 0.36rem;
    font-family: PingFangSC-Regular, PingFang SC;
    color: #999999;
    line-height: 1rem;
    display: flex;
    div {
        border-top: 1px solid #DDDDDD;
        flex: 1;
        text-align: center;
    }
    .btn_left{
        border-right: 1px solid #DDDDDD;
    }
    .btn_right{
        color: #00C8BE;
    }
}

.container {
    position: fixed;
    width: 100vw;
    height: 100vh;
    top: 0;
    left: 0;
    z-index: 999;
}

.main {
    position: absolute;
    display: flex;
    width: 5.4rem;
    left: 50%;
    height: 2.57rem;
    top: 50%;
    transform: translate(-50%, -50%);
    background: #FFFFFF;
    border-radius: 0.14rem;
    flex-direction: column;
    justify-content: space-between;
    align-items: center;
    z-index: 999;
}
.text {
    padding-top: 0.5rem;
    text-align: center;
    font-size: 0.32rem;
    color: #333333;
    font-weight: 500;
}
</style>

封装以便调用

接下来设置一个ts文件将其封装

目的:调用子组件的show方法,传入参数并做展示,

返回一个promise对象,用户点击之后再调用resolve并传入相关参数(例如让用户明确点了左边还是右边)

import { createVNode, render } from 'vue'
import ConfirmComponent from "./GlobalCom.vue";
import type {typeConfirm} from './index.d'
let showFn: () => void
// 获取到子组件的showFn,能够在外部控制组件的showModal功能
function getShowFn(showF: () => void) {
    showFn = showF
}
//也可以在此设置默认值
const config = {
    leftText:'这是左侧'
}
enum EPosition{
    left="left",
    right="right"
}
export default {
    /**
     * 
     * @param options 
     * @returns 
     * .then参数为 left 或right
     */
    show(options:typeConfirm) {
        return new Promise((resolve) => {
            options = Object.assign(config,options)
            function leftClick(){
                resolve(EPosition.left)
            }
            function rightClick(){
                resolve(EPosition.right)
            }
            showFn && showFn()
            const vnode = createVNode(ConfirmComponent, { ...options, getShowModal: getShowFn,leftClick,rightClick })
            // vue底层会利用diff算法来处理,避免重新渲染
            render(vnode, document.body)
        })

    }
}

typeConfirm如下

export interface typeConfirm {
    title: string,
    leftText?: string,
    rightText?: string,
}
posted @ 2023-04-20 00:46  Monday1997  阅读(841)  评论(0编辑  收藏  举报