You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 20:50:25