Skip to content

Modal 模态框

模态对话框,在当前页面打开一个浮层,承载相关操作。

基础用法

最简单的用法。默认为中等尺寸(520px)。

vue
<template>
  <ExButton type="primary" @click="visible = true">打开模态框</ExButton>

  <ExModal v-model="visible" title="基础模态框">
    <p>这是模态框的内容</p>
  </ExModal>
</template>

<script setup>
import { ref } from 'vue';

const visible = ref(false);
</script>

不同类型

通过 type 属性可以设置不同类型的模态框,不同类型会有不同的边框颜色和发光效果。

vue
<template>
  <ExButton type="primary" @click="visible = true">主要模态框</ExButton>

  <ExModal v-model="visible" title="主要模态框" type="primary" size="medium">
    <template #icon>
      <img
        src="https://api.iconify.design/ri/information-line.svg"
        alt="信息"
        width="24"
        height="24"
      />
    </template>
    <p>这是一个主要类型的模态框,带有霓虹蓝色边框和发光效果。</p>
  </ExModal>
</template>

不同尺寸

通过 size 属性可以设置不同尺寸的模态框。

vue
<template>
  <ExButton @click="visible = true">小尺寸</ExButton>

  <ExModal v-model="visible" title="小尺寸模态框" size="small">
    <p>这是一个小尺寸的模态框。</p>
  </ExModal>
</template>

自定义宽度

通过 width 属性可以自定义模态框宽度。

vue
<template>
  <ExButton type="primary" @click="visible = true">自定义宽度</ExButton>

  <ExModal v-model="visibleWidth" title="自定义宽度" :width="800">
    <p>这个模态框的宽度是 800px。</p>
  </ExModal>
</template>

居中显示

设置 centered 属性可以让模态框垂直居中显示。

vue
<template>
  <ExButton type="primary" @click="visible = true">居中模态框</ExButton>

  <ExModal v-model="visibleCenter" title="居中显示" size="medium" centered>
    <div style="text-align: center;">
      <div style="font-size: 48px;">🏆</div>
      <div>恭喜获胜!</div>
    </div>
  </ExModal>
</template>

自定义按钮文字

通过 okTextcancelText 属性可以自定义按钮文字。

vue
<template>
  <ExButton type="primary" @click="visible = true">自定义按钮</ExButton>

  <ExModal
    v-model="visible"
    title="删除确认"
    ok-text="删除"
    cancel-text="我再想想"
    ok-type="danger"
  >
    <p>确定要删除这条记录吗?</p>
  </ExModal>
</template>

自定义底部

通过 footer 插槽可以自定义底部内容。

vue
<template>
  <ExButton type="primary" @click="visible = true">自定义底部</ExButton>

  <ExModal v-model="visibleCustomFooter" title="自定义底部" size="medium">
    <p>这是一个自定义底部的模态框。</p>
    <template #footer>
      <ExButton type="default" @click="visibleCustomFooter = false">知道了</ExButton>
    </template>
  </ExModal>
</template>

确认加载状态

通过 confirmLoading 属性可以让确认按钮处于加载状态。需要配合 before-close 属性来阻止模态框自动关闭。

vue
<template>
  <ExButton type="primary" @click="visible = true">加载状态</ExButton>

  <ExModal 
    v-model="visible" 
    title="提交表单" 
    :confirm-loading="confirmLoading"
    :before-close="() => !confirmLoading"
    @ok="handleOk"
  >
    <p>点击确定按钮后,按钮会显示加载状态。</p>
    <p>2秒后自动关闭。</p>
  </ExModal>
</template>

<script setup>
import { ref } from 'vue';

const visible = ref(false);
const confirmLoading = ref(false);

const handleOk = () => {
  confirmLoading.value = true;
  // 模拟异步操作(如API请求)
  setTimeout(() => {
    confirmLoading.value = false;
    visible.value = false;  // 操作完成后关闭模态框
  }, 2000);
};
</script>

不可点击遮罩关闭

设置 :mask-closable="false" 可以禁止点击遮罩层关闭。

vue
<template>
  <ExButton type="primary" @click="visible = true">不可点击遮罩关闭</ExButton>

  <ExModal v-model="visible" title="重要提示" :mask-closable="false">
    <p>这个模态框不能通过点击遮罩层关闭。</p>
  </ExModal>
</template>

自定义样式

通过 bodyStyle 属性可以自定义内容区域样式。

vue
<template>
  <ExButton type="primary" @click="visible = true">自定义样式</ExButton>

  <ExModal
    v-model="visible"
    title="自定义样式"
    :body-style="{
      background: 'linear-gradient(135deg, rgba(0, 240, 255, 0.1), rgba(255, 0, 127, 0.1))',
      minHeight: '200px',
    }"
  >
    <div>自定义内容样式</div>
  </ExModal>
</template>

嵌套模态框

模态框可以嵌套使用。

vue
<template>
  <ExButton type="primary" @click="outerVisible = true">打开外层模态框</ExButton>

  <ExModal v-model="outerVisible" title="外层模态框">
    <p>这是外层模态框的内容。</p>
    <ExButton type="primary" @click="innerVisible = true">打开内层模态框</ExButton>

    <ExModal v-model="innerVisible" title="内层模态框" :z-index="1010">
      <p>这是内层模态框的内容。</p>
    </ExModal>
  </ExModal>
</template>

<script setup>
import { ref } from 'vue';

const outerVisible = ref(false);
const innerVisible = ref(false);
</script>

API

属性说明类型默认值
model-value / v-model是否显示模态框booleanfalse
title模态框标题string''
size模态框尺寸'small' | 'medium' | 'large''medium'
type模态框类型'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info''default'
show-close是否显示关闭按钮booleantrue
mask是否显示遮罩层booleantrue
mask-closable是否可以通过点击遮罩层关闭booleantrue
keyboard是否可以通过按下 ESC 键关闭booleantrue
ok-text确认按钮文本string'确定'
cancel-text取消按钮文本string'取消'
ok-type确认按钮类型'default' | 'primary' | 'success' | 'warning' | 'danger''primary'
confirm-loading确认按钮加载状态booleanfalse
centered是否居中显示booleanfalse
width宽度string | numberundefined
z-index层级number1000
append-to-body是否追加到 bodybooleantrue
destroy-on-close是否在关闭时销毁内容booleanfalse
wrap-class-name包装器自定义类名string''
body-style内容区域自定义样式Record<string, string | number>undefined
before-close模态框关闭前的回调() => boolean | Promise<boolean>undefined
aria-label无障碍标签stringundefined
事件名说明类型
update:model-value更新 modelValue(value: boolean) => void
ok点击确定按钮时触发() => void
cancel点击取消按钮时触发() => void
after-close模态框关闭动画结束后触发() => void
插槽名说明
default模态框内容
title自定义标题
icon自定义图标
footer自定义底部内容
closeIcon自定义关闭按钮图标
方法名说明类型
getElement获取模态框 DOM 元素() => HTMLDivElement | null

无障碍支持

  • 使用 role="dialog"aria-modal 属性
  • 支持 ESC 键关闭
  • 完整的 ARIA 标签支持
  • 支持屏幕阅读器
  • 自动锁定焦点在模态框内

受控 / 非受控

Modal 通过 v-modelmodelValue)受控:

vue
<template>
  <ExModal v-model="visible" title="标题" @ok="onOk" @cancel="onCancel">内容</ExModal>
</template>

<script setup>
import { ref } from 'vue'
const visible = ref(false)
</script>
  • 点击确定先触发 ok,再经 before-close 校验,通过后自动关闭;点击取消/遮罩/ESC 触发 cancel
  • 异步提交时用 :confirm-loading="true" 锁定确定按钮,并用 before-close 在校验未过时返回 false 阻止关闭。

Modal 与 Dialog 功能相近:Modal 偏 Ant Design 风格(ok/cancelmaskkeyboard),Dialog 偏 Element 风格(confirm/cancelmodalclose-on-press-escape)。同一项目建议只选其一以保持一致。

常见问题

Q:okcancelbefore-close 的执行顺序? 先 emit ok/cancel,再执行 before-closebefore-close 返回 false 时不更新 modelValue,模态框保持打开。

Q:如何禁止 ESC / 点击遮罩关闭? 设置 :keyboard="false":mask-closable="false"

Q:多个模态框叠加? 支持叠加:滚动锁引用计数、ESC 只关最顶层、关闭后焦点归还触发元素。

最佳实践

  • 二次确认/表单提交confirm-loading + before-close,提交成功后关闭。
  • 危险操作ok-type="danger" 强化视觉警示。
  • 始终设置 titlearia-label 以满足无障碍。

主题定制

Modal 不定义独立的组件级变量,而是消费 ExUI 全局设计令牌。定制方式:

  • 单个实例:用 wrap-class-name / body-style 定制。
  • 全局:覆盖所消费的全局令牌:
css
:root {
  --ex-color-bg-surface: #11151c;     /* 模态框背景 */
  --ex-color-border-primary: #1f2a37; /* 边框 / 分割线 */
}