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

Nuxt UI中useModal程序化打开多模态框被覆盖问题求助

解决Nuxt UI程序化打开多模态框实例被覆盖的问题

问题描述

我有两个Nuxt UI Modal组件ComponentA和ComponentB,组件代码结构如下:

<script setup lang="ts">
const model = defineModel({type: Boolean, default: false})
</script>
<u-modal v-model="model">
    <u-card class="flex flex-col gap-6 py-6">
      ...
    </u-card>
</u-modal>

使用useModal()程序化打开模态框时,出现以下问题:

  1. 同一组件多次打开时,第二个实例会替换第一个:
const modalA1 = useModal();
modalA1.open(ComponentA, options);

const modalA2 = useModal();
modalA2.open(ComponentA, options);
  1. 打开不同组件时,后打开的模态框会覆盖之前的:
const modalA = useModal();
modalA.open(ComponentA, options);

const modalB = useModal();
modalB.open(ComponentB, options);

模板中直接嵌套多个模态框可正常工作,但程序化方式无法实现多实例共存,需要解决该问题。

问题原因

Nuxt UI的useModal()默认是单例模式,内部仅维护一个模态框实例,每次调用open方法都会替换当前的组件与配置,导致新模态框覆盖旧的。

解决方案

方案1:自定义多模态框管理器

创建一个支持多实例的composable,手动管理模态框的创建与销毁:

// composables/useMultiModal.ts
import { ref, h, render } from 'vue'

interface ModalInstance {
  id: string
  component: any
  props: Record<string, any>
  close: () => void
}

export const useMultiModal = () => {
  const modals = ref<ModalInstance[]>([])
  const container = document.createElement('div')
  document.body.appendChild(container)

  const open = (component: any, props: Record<string, any> = {}) => {
    // 生成唯一ID区分实例
    const id = `modal-${Date.now()}-${Math.random().toString(36).slice(2)}`
    const vModel = ref(true)
    
    const close = () => {
      vModel.value = false
      // 等待关闭动画结束后移除实例
      setTimeout(() => {
        modals.value = modals.value.filter(m => m.id !== id)
        // 重新渲染剩余模态框
        render(h('div', {}, modals.value.map(m => h(m.component, {
          ...m.props,
          modelValue: true,
          'onUpdate:modelValue': (val: boolean) => !val && m.close()
        })))), container)
      }, 300) // 匹配Nuxt UI模态框默认关闭动画时长
    }

    const instance: ModalInstance = {
      id,
      component,
      props: {
        ...props,
        modelValue: vModel.value,
        'onUpdate:modelValue': (val: boolean) => {
          vModel.value = val
          !val && close()
        }
      },
      close
    }

    modals.value.push(instance)
    // 渲染所有当前存在的模态框
    render(h('div', {}, modals.value.map(m => h(m.component, m.props))), container)

    return close
  }

  return { open, modals }
}

使用方式:

const multiModal = useMultiModal()

// 打开多个实例,互不干扰
const closeA1 = multiModal.open(ComponentA, { /* 自定义props */ })
const closeA2 = multiModal.open(ComponentA, { /* 自定义props */ })
const closeB = multiModal.open(ComponentB, { /* 自定义props */ })

// 单独关闭某个模态框
// closeA1()

方案2:模板动态渲染多模态框

在页面模板中用数组管理模态框状态,动态渲染多个独立实例:

<template>
  <div>
    <u-modal 
      v-for="modal in modalList" 
      :key="modal.id"
      v-model="modal.show"
    >
      <component :is="modal.component" v-bind="modal.props" />
    </u-modal>
  </div>
</template>

<script setup lang="ts">
interface ModalItem {
  id: string
  component: any
  props: Record<string, any>
  show: boolean
}

const modalList = ref<ModalItem[]>([])

const openModal = (component: any, props: Record<string, any> = {}) => {
  const id = `modal-${Date.now()}-${Math.random().toString(36).slice(2)}`
  modalList.value.push({
    id,
    component,
    props,
    show: true
  })
}

const closeModal = (id: string) => {
  const modal = modalList.value.find(m => m.id === id)
  if (modal) {
    modal.show = false
    setTimeout(() => {
      modalList.value = modalList.value.filter(m => m.id !== id)
    }, 300)
  }
}

// 调用示例
openModal(ComponentA, { /* 自定义props */ })
openModal(ComponentA, { /* 自定义props */ })
openModal(ComponentB, { /* 自定义props */ })
</script>

内容的提问来源于stack exchange,提问作者Sukanta Paul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 03:35:03