vue3中将一个简单的弹窗提示组件转变为函数调用

4044次阅读 201人点赞 作者: WuBin 发布时间: 2024-08-23 14:00:03
扫码到手机查看

简单的弹窗组件

<template>
    <transition name="fadeInUp">
        <div class="modal-alert"
             id="modal-alert"
             v-show="showModal"
        >
            <div class="modal-alert-inner">
                <span class="modal-icon"></span>
                <p class="modal-p">{{ textTips }}</p>
                <div class="modal-btns-box">
                    <button type="button"
                            class="modal-btn"
                            @click="clickBtn"
                    >
                        {{ textButton }}
                    </button>
                    <div class="modal-close" @click="close"></div>
                </div>
            </div>
        </div>
    </transition>
</template>

<script type="text/ecmascript-6">
    import { hasParentWithClassInArray } from "@/common/js/fns";

    export default {
        name: "modal-alert",
        emits: ['clickBtn', 'close'],
        props: {
            textTips: {
                type: String,
                default() {
                    return '无';
                }
            },
            textButton: {
                type: String,
                default() {
                    return '无';
                }
            }
        },
        mounted() {
            this.showModal = true;
            this.clickOtherCloseModal();
        },
        data() {
            return {
                showModal: false
            }
        },
        methods: {
            clickBtn() {
                this.$emit('clickBtn');
                this.close();
            },
            open() {
                this.showModal = true;
            },
            close() {
                this.showModal = false;
                // 关闭之后就卸载点击事件
                document.body.removeEventListener('click', this.handleModal);
                this.$emit('close');
            },
            handleModal(event) {
                const classes = ['talking-roles-select-list', 'history-list-wrapper', 'm-header-menu-btn'];
                const clickedElement = event.target;
                const parentInArr = hasParentWithClassInArray(clickedElement, classes);
                if (parentInArr) {
                    this.close();
                }
            },
            // 点击其他地方关闭此弹窗
            clickOtherCloseModal() {

                document.body.addEventListener('click', this.handleModal);
            },

        },
    }
</script>

<style lang="less" rel="stylesheet/less" scoped>
    @import "@/common/less/variable";

    /* 响应式PC */
    @media screen and (min-width: 641px) {
        .modal-alert {
            position: fixed;
            z-index: @zindex-VI;
            width: calc((100% - @aside-left-width) * 0.6);
            left: @aside-left-width;
            right: 0;
            bottom: 190px;
            margin: 0 auto;
            background-color: #fff;
            box-shadow: 0 0px 8px @color-bg-l;
            border-radius: 50px;

            .modal-alert-inner {
                padding: 15px 20px;
                display: flex;
                align-items: center;
                justify-content: space-between;
            }

            .modal-p {
                display: inline-block;
                padding-left: 15px;
                flex: 1;
                line-height: 20px;
                padding-right: 10px;
            }

            .modal-icon {
                display: inline-block;
                width: 24px;
                height: 24px;
                background: url("icon-tips.svg") center center no-repeat;
                background-size: auto 100%;
            }

            .modal-close {
                width: 24px;
                height: 24px;
                background: url("icon-close.svg") center center no-repeat;
                background-size: auto 80%;
                cursor: pointer;
            }

            .modal-btns-box {
                flex: 0 0 150px;
                display: flex;
                align-items: center;
                justify-content: space-between;
            }

            .modal-btn {
                background: #fed547;
                height: 36px;
                padding: 0 20px;
                border-radius: 5px;
                cursor: pointer;

                &:hover {
                    background: #e4bf3e;
                }
            }
        }
    }

    /* 响应式PC end */
    /* 响应式 M */
    @media screen and (max-width: 640px) {
        .modal-alert {
            position: fixed;
            z-index: @zindex-VI;
            width: calc(100% - 40px);
            left: 0;
            right: 0;
            bottom: 120px;
            margin: 0 auto;
            background-color: #fff;
            box-shadow: 0 0px 8px darken(@color-bg-l, 10%);
            border-radius: 10px;

            .modal-alert-inner {
                padding: 15px 20px;
            }

            .modal-p {
                display: inline-block;
                padding: 0 32px 0 32px;
                line-height: 20px;
                background: url("icon-tips.svg") left center no-repeat;
                background-size: auto 24px;
            }

            .modal-icon {
                display: inline-block;
                width: 24px;
                height: 24px;
                background: url("icon-tips.svg") center center no-repeat;
                background-size: auto 100%;
                display: none;
            }

            .modal-close {
                width: 24px;
                height: 24px;
                background: url("icon-close.svg") center center no-repeat;
                background-size: auto 80%;
                cursor: pointer;
                position: absolute;
                top: 13px;
                right: 10px;
            }

            .modal-btns-box {
                display: flex;
                align-items: center;
                justify-content: center;
                padding-top: 15px;
            }

            .modal-btn {
                background: #fed547;
                height: 40px;
                width: 70%;
                border-radius: 5px;
                cursor: pointer;

                &:hover {
                    background: #e4bf3e;
                }
            }
        }
    }

    /* 响应式 M end */
</style>

注意,关闭状态一定要在创建一次之后,在组件内部进行维护!

将组件封装成函数

import { createApp, h } from 'vue';
import ModalAlert from '@/components/modal-alert/modal-alert';

export function useModalAlert() {
    return function (options = {}) {
        const { textTips, textButton, onClickBtn, onClose } = options;
        const modalWrapperID = 'modal-alert-wrapper';
        // 动画执行时间
        const animateDuration = 300;

        // 如果存在外层元素 那么就组织继续创建
        if (document.getElementById(modalWrapperID)) {
            return;
        }

        // 创建一个 div 作为组件的挂载点
        const div = document.createElement('div');
        div.id = modalWrapperID;
        document.body.appendChild(div);

        const app = createApp({
            render() {
                return h(ModalAlert, {
                    textTips,
                    textButton,
                    onClickBtn: () => {
                        onClickBtn && onClickBtn();
                        setTimeout(() => {
                            app.unmount(div);
                            // document.body.removeChild(div);
                        }, animateDuration);
                    },
                    onClose: () => {
                        onClose && onClose();
                        setTimeout(() => {
                            app.unmount(div);
                            document.body.removeChild(div);
                        }, animateDuration);
                    }
                });
            }
        });
        app.mount(div);
    };
}

解释:

1、这里使用setTimeOut是为了保证弹窗动画效果的执行

  • useModalAlert函数使用 Vue 3 的createApp创建了一个新的应用实例,并将你的modal-alert组件挂载到一个新创建的div上。

  • showModal函数中,可以传递textTipstextButton和事件处理函数onClickonClose,这些将传递给modal-alert组件的 props 和事件。

  • 当用户点击确认按钮或关闭按钮时,组件会被卸载,并从 DOM 中移除。

调用示例

setup

<template>
    <div>
        <button @click="showMyModal">Show Modal</button>
    </div>
</template>

<script>
export default {
    setup() {
        const showMyModal = () => {
            showModal();
        };

        return { showMyModal };
    }
};
</script>

或者

import { useModalAlert } from '@/utils/useModalAlert'; // 假设你将函数放在 utils 文件夹中

export default {
    setup() {
        const showModal = useModalAlert();

        const showMyModal = () => {
            showModal({
                textTips: 'This is a tip message',
                textButton: 'Confirm',
                onClick: () => {
                    console.log('Button clicked');
                },
                onClose: () => {
                    console.log('Modal closed');
                }
            });
        };

        return { showMyModal };
    }
};

非setup中调用

<template>
  <div>
    <button @click="showModalAlert">Show Modal</button>
  </div>
</template>

<script>
import { useModalAlert } from '@/utils/useModalAlert'; // 假设你将函数放在 utils 文件夹中

export default {
  name: 'MyComponent',
  methods: {
    showModalAlert() {
      const showModal = useModalAlert();

      showModal({
        textTips: 'This is a tip message',
        textButton: 'Confirm',
        onClick: () => {
          console.log('Button clicked');          // this.somehandle() 执行组件中的其他方法
        },
        onClose: () => {
          console.log('Modal closed');
        }
      });
    }
  }
};
</script>

点赞 支持一下 觉得不错?客官您就稍微鼓励一下吧!
关键词:组件函数化,createApp
推荐阅读
  • uniapp实现被浏览器唤起的功能

    当用户打开h5链接时候,点击打开app若用户在已经安装过app的情况下直接打开app,若未安装过跳到应用市场下载安装这个功能在实现上主要分为两种场景,从普通浏览器唤醒以及从微信唤醒。

    9119次阅读 589人点赞 发布时间: 2022-12-14 16:34:53 立即查看
  • Vue

    盘点Vue2和Vue3的10种组件通信方式

    Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异;本文将通过选项式API组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式。

    3884次阅读 289人点赞 发布时间: 2022-08-19 09:40:16 立即查看
  • JS

    几个高级前端常用的API

    推荐4个前端开发中常用的高端API,分别是MutationObserver、IntersectionObserver、getComputedstyle、getBoundingClientRect、requ...

    14139次阅读 919人点赞 发布时间: 2021-11-11 09:39:54 立即查看
  • PHP

    【正则】一些常用的正则表达式总结

    在日常开发中,正则表达式是非常有用的,正则表达式在每个语言中都是可以使用的,他就跟JSON一样,是通用的。了解一些常用的正则表达式,能大大提高你的工作效率。

    12947次阅读 445人点赞 发布时间: 2021-10-09 15:58:58 立即查看
  • 【中文】免费可商用字体下载与考证

    65款免费、可商用、无任何限制中文字体打包下载,这些字体都是经过长期验证,经得住市场考验的,让您规避被无良厂商起诉的风险。

    11539次阅读 926人点赞 发布时间: 2021-07-05 15:28:45 立即查看
  • Vue

    Vue3开发一个v-loading的自定义指令

    在vue3中实现一个自定义的指令,有助于我们简化开发,简化复用,通过一个指令的调用即可实现一些可高度复用的交互。

    15646次阅读 1248人点赞 发布时间: 2021-07-02 15:58:35 立即查看
  • JS

    关于手机上滚动穿透问题的解决

    当页面出现浮层的时候,滑动浮层的内容,正常情况下预期应该是浮层下边的内容不会滚动;然而事实并非如此。在PC上使用css即可解决,但是在手机端,情况就变的比较复杂,就需要禁止触摸事件才可以。

    14843次阅读 1209人点赞 发布时间: 2021-05-31 09:25:50 立即查看
  • Vue

    Vue+html2canvas截图空白的问题

    在使用vue做信网单页专题时,有海报生成的功能,这里推荐2个插件:一个是html2canvas,构造好DOM然后转canvas进行截图;另外使用vue-canvas-poster(这个截止到2021年3月...

    29040次阅读 2281人点赞 发布时间: 2021-03-02 09:04:51 立即查看
  • Vue

    vue-router4过度动画无效解决方案

    在初次使用vue3+vue-router4时候,先后遇到了过度动画transition进入和退出分别无效的情况,搜遍百度没没找到合适解决方法,包括vue-route4有一些API都进行了变化,以前的一些操...

    25064次阅读 1931人点赞 发布时间: 2021-02-23 13:37:20 立即查看
交流 收藏 目录