Vue中点击Popover下拉框内Modal时如何关闭下拉并挂载Modal到body
问题
用VueJS、Tailwind CSS和Headless UI开发带Modal的Popover下拉框,要求点击下拉框内的按钮打开Modal时,同时关闭Popover下拉框。目前Modal能正常显示,但Popover下拉框会留在后方显示。Popover代码在父组件,Modal是子组件<subscriptions-modal>。
解决方案
核心逻辑是子组件触发事件通知父组件关闭Popover,同时保持Modal正常展示,具体实现步骤如下:
1. 子组件添加事件触发
修改<subscriptions-modal>,在打开Modal的点击事件里,通过defineEmits触发自定义事件通知父组件关闭Popover:
<template> <!-- 修改按钮点击事件,同时触发自定义事件 --> <button @click="openModal" type="button"> <a v-for="item in subscriptions" :key="item.name" class="-m-3 flex items-start rounded-lg p-3 hover:bg-gray-50"> <component :is="item.icon" class="h-6 w-6 flex-shrink-0 text-blue-600" aria-hidden="true" /> <div class="ml-4"> <p class="text-left text-base font-medium text-gray-900">{{ item.name }}</p> <p class="mt-1 text-sm text-gray-500">{{ item.description }}</p> </div> </a> </button> <!-- Modal原有代码不变 --> <teleport to="body"> <div v-show="isOpen" class="absolute inset-0 flex items-center justify-center bg-gray-700 bg-opacity-50 z-10"> <div class="max-w-2xl p-6 mx-4 bg-white rounded-md shadow-xl"> <div class="w-full max-w-md space-y-8 flex flex-col"> <div class="self-end items-center justify-between"> <svg @click="isOpen = false" xmlns="http://www.w3.org/2000/svg" class="w-8 h-8 text-red-900 cursor-pointer self-end" fill="none" viewBox="0 0 24 24" stroke="currentColor" > <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /> </svg> </div> <img class="mx-auto h-12 w-auto" /> <h2 class="mt-6 text-center text-3xl font-bold tracking-tight text-gray-900">Active Subscriptions</h2> <form class="mt-8 space-y-6" action="#" method="POST"> <input type="hidden" name="remember" value="true" /> <div class="text-center"> <p class="formText">No current subscriptions</p> </div> </form> </div> </div> </div> </teleport> </template> <script setup> import { CheckBadgeIcon } from '@heroicons/vue/24/outline'; import { ref, defineEmits } from 'vue'; // 定义要触发的事件 const emit = defineEmits(['close-popover']); let isOpen = ref(false); const subscriptions = [ { name: 'Subscriptions', description: 'Please see who all has subscribed to our dashboards', icon: CheckBadgeIcon, } ]; // 打开Modal同时触发关闭Popover的事件 const openModal = () => { isOpen.value = true; emit('close-popover'); }; </script>
2. 父组件监听事件并关闭Popover
Headless UI的Popover组件通过插槽提供close方法,可手动关闭Popover。在父组件中:
- 从
Popover插槽解构出close方法 - 在
<subscriptions-modal>上监听子组件的close-popover事件,调用close方法关闭Popover
修改后的父组件代码:
<template> <Popover class="relative bg-white"> <div class="mx-auto max-w-7xl px-4 sm:px-6"> <div class="flex items-center justify-between border-b-2 border-gray-100 py-6 md:justify-start md:space-x-10"> <PopoverGroup as="nav" class="hidden space-x-10 md:flex"> <!-- 从Popover插槽中解构出close方法 --> <Popover class="relative" v-slot="{ open, close }"> <PopoverButton :class="[ open ? 'text-gray-900' : 'text-gray-500', 'group inline-flex items-center rounded-md bg-white text-base font-medium hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2' ]" > <span>Solutions</span> <ChevronDownIcon :class="[open ? 'text-gray-600' : 'text-gray-400', 'ml-2 h-5 w-5 group-hover:text-gray-500']" aria-hidden="true" /> </PopoverButton> <!-- 过渡动画代码不变 --> <transition enter-active-class="transition ease-out duration-200" enter-from-class="opacity-0 translate-y-1" enter-to-class="opacity-100 translate-y-0" leave-active-class="transition ease-in duration-150" leave-from-class="opacity-100 translate-y-0" leave-to-class="opacity-0 translate-y-1" > <PopoverPanel class="absolute z-10 -ml-4 mt-3 w-screen max-w-md transform px-2 sm:px-0 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2" > <div class="overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5"> <div class="relative grid gap-6 bg-white px-5 py-6 sm:gap-8 sm:p-8"></div> <div class="space-y-6 bg-gray-50 px-5 py-5 sm:flex sm:space-y-0 sm:space-x-10 sm:px-8"> <!-- 监听子组件的close-popover事件,调用close方法 --> <subscriptions-modal @close-popover="close"></subscriptions-modal> </div> </div> </PopoverPanel> </transition> </Popover> </PopoverGroup> </div> </div> </Popover> </template> <script setup> import { Popover, PopoverButton, PopoverGroup, PopoverPanel } from '@headlessui/vue' import { ChevronDownIcon } from '@heroicons/vue/20/solid' import SubscriptionsModal from './SubscriptionsModal' </script>
3. 可选优化
点击Modal遮罩层或关闭按钮时,Popover已经处于关闭状态,无需额外修改,当前逻辑已满足需求。
内容的提问来源于stack exchange,提问作者ironman33
相关产品推荐
相关产品推荐

