Dialog 对话框
在保留当前页面状态的情况下,告知用户并承载相关操作。
基础用法
Dialog 弹出一个对话框,适合需要定制性更大的场景。
vue
<template>
<ExButton type="primary" @click="dialogVisible = true">打开对话框</ExButton>
<ExDialog v-model="dialogVisible" title="提示">
<p>这是一段信息</p>
<p>这是另一段信息</p>
</ExDialog>
</template>
<script setup>
import { ref } from 'vue';
const dialogVisible = ref(false);
</script>自定义标题
通过 title 插槽可以自定义标题内容。
vue
<template>
<ExButton type="primary" @click="dialogVisible = true">自定义标题</ExButton>
<ExDialog v-model="dialogVisible">
<template #title>
<span style="color: var(--ex-color-neon-blue-500);"> 🎮 游戏设置 </span>
</template>
<template #icon>
<img
src="https://api.iconify.design/ri/settings-3-line.svg"
alt="设置"
width="24"
height="24"
/>
</template>
<div>画质:超高</div>
<div>帧率:144 FPS</div>
</ExDialog>
</template>不同类型
通过 type 属性可以设置不同类型的对话框。
vue
<template>
<ExButton type="primary" @click="dialogVisible = true">主要对话框</ExButton>
<ExDialog v-model="dialogVisible" title="主要对话框" type="primary">
<template #icon>
<img
src="https://api.iconify.design/ri/information-line.svg"
alt="信息"
width="24"
height="24"
/>
</template>
<p>这是一个主要类型的对话框。</p>
</ExDialog>
</template>不同尺寸
通过 size 属性可以设置不同尺寸的对话框。
vue
<template>
<ExButton @click="dialogVisible = true">小尺寸</ExButton>
<ExDialog v-model="dialogVisible" title="小尺寸对话框" size="small">
<p>这是一个小尺寸的对话框。</p>
</ExDialog>
</template>自定义底部
通过 footer 插槽可以自定义底部内容。
vue
<template>
<ExButton type="primary" @click="dialogVisible = true">自定义底部</ExButton>
<ExDialog
v-model="dialogVisible"
title="确认操作"
:confirm-loading="confirmLoading"
@confirm="handleConfirm"
>
<p>您确定要执行此操作吗?</p>
</ExDialog>
</template>
<script setup>
import { ref } from 'vue';
const dialogVisible = ref(false);
const confirmLoading = ref(false);
const handleConfirm = () => {
confirmLoading.value = true;
setTimeout(() => {
confirmLoading.value = false;
dialogVisible.value = false;
}, 2000);
};
</script>居中显示
设置 center 属性可以让对话框内容居中显示。
vue
<template>
<ExButton type="primary" @click="dialogVisible = true">居中对话框</ExButton>
<ExDialog v-model="dialogVisible" title="居中显示" center>
<div style="text-align: center;">
<div style="font-size: 48px;">🏆</div>
<div>恭喜获胜!</div>
</div>
</ExDialog>
</template>全屏对话框
设置 fullscreen 属性可以让对话框全屏显示。
vue
<template>
<ExButton type="primary" @click="dialogVisible = true">全屏对话框</ExButton>
<ExDialog v-model="dialogVisible" title="全屏对话框" fullscreen>
<div>全屏内容</div>
</ExDialog>
</template>嵌套对话框
对话框可以嵌套使用。
vue
<template>
<ExButton type="primary" @click="outerVisible = true">打开外层对话框</ExButton>
<ExDialog v-model="outerVisible" title="外层对话框">
<p>这是外层对话框的内容。</p>
<ExButton type="primary" @click="innerVisible = true">打开内层对话框</ExButton>
<ExDialog v-model="innerVisible" title="内层对话框" :z-index="1060">
<p>这是内层对话框的内容。</p>
</ExDialog>
</ExDialog>
</template>
<script setup>
import { ref } from 'vue';
const outerVisible = ref(false);
const innerVisible = ref(false);
</script>自定义关闭按钮
通过 close 插槽可以自定义关闭按钮。
vue
<template>
<ExButton type="primary" @click="dialogVisible = true">自定义关闭按钮</ExButton>
<ExDialog v-model="dialogVisible" title="自定义关闭">
<template #close>
<img
src="https://api.iconify.design/ri/close-circle-line.svg"
alt="关闭"
width="20"
height="20"
/>
</template>
<p>自定义关闭按钮</p>
</ExDialog>
</template>不显示底部
设置 :show-footer="false" 可以隐藏底部操作栏。
vue
<template>
<ExButton type="primary" @click="dialogVisible = true">无底部对话框</ExButton>
<ExDialog v-model="dialogVisible" title="纯内容展示" :show-footer="false">
<p>这是一个纯内容展示的对话框。</p>
</ExDialog>
</template>关闭前回调
通过 before-close 属性可以在关闭前执行回调,返回 false 可以阻止关闭。
当前状态: 有未保存内容(阻止关闭)
vue
<template>
<ExButton type="primary" @click="dialogVisible = true">打开对话框</ExButton>
<ExDialog v-model="dialogVisible" title="编辑内容" :before-close="handleBeforeClose">
<p>尝试关闭对话框,如果有未保存内容会被阻止。</p>
</ExDialog>
</template>
<script setup>
import { ref } from 'vue';
const dialogVisible = ref(false);
const hasUnsavedChanges = ref(true);
const handleBeforeClose = async () => {
// 如果有未保存的更改,阻止关闭
if (hasUnsavedChanges.value) {
console.log('有未保存的内容,阻止关闭');
return false;
}
return true;
};
</script>API
Dialog Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
model-value / v-model | 是否显示对话框 | boolean | false |
title | 对话框标题 | string | '' |
size | 对话框尺寸 | 'small' | 'medium' | 'large' | 'full' | 'medium' |
type | 对话框类型 | 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'default' |
show-close | 是否显示关闭按钮 | boolean | true |
modal | 是否显示遮罩层 | boolean | true |
close-on-click-modal | 是否可以通过点击遮罩层关闭 | boolean | true |
close-on-press-escape | 是否可以通过按下 ESC 键关闭 | boolean | true |
show-footer | 是否显示底部操作栏 | boolean | true |
confirm-text | 确认按钮文本 | string | '确认' |
cancel-text | 取消按钮文本 | string | '取消' |
show-confirm | 是否显示确认按钮 | boolean | true |
show-cancel | 是否显示取消按钮 | boolean | true |
confirm-loading | 确认按钮加载状态 | boolean | false |
confirm-disabled | 确认按钮禁用状态 | boolean | false |
center | 是否居中显示 | boolean | false |
fullscreen | 是否全屏显示 | boolean | false |
lock-scroll | 是否锁定滚动 | boolean | true |
custom-class | 自定义类名 | string | '' |
modal-class | 遮罩层自定义类名 | string | '' |
before-open | 对话框打开前的回调 | () => boolean | Promise<boolean> | undefined |
before-close | 对话框关闭前的回调 | () => boolean | Promise<boolean> | undefined |
z-index | 层级 | number | 1050 |
append-to-body | 是否追加到 body | boolean | true |
destroy-on-close | 是否在关闭时销毁内容 | boolean | false |
aria-label | 无障碍标签 | string | undefined |
Dialog Events
| 事件名 | 说明 | 类型 |
|---|---|---|
update:model-value | 更新 modelValue | (value: boolean) => void |
open | 对话框打开时触发 | () => void |
opened | 对话框打开动画结束时触发 | () => void |
close | 对话框关闭时触发 | () => void |
closed | 对话框关闭动画结束时触发 | () => void |
confirm | 点击确认按钮时触发 | () => void |
cancel | 点击取消按钮时触发 | () => void |
Dialog Slots
| 插槽名 | 说明 |
|---|---|
default | 对话框内容 |
title | 自定义标题 |
icon | 自定义图标 |
close | 自定义关闭按钮 |
footer | 自定义底部内容 |
Dialog Methods
| 方法名 | 说明 | 类型 |
|---|---|---|
open | 打开对话框 | () => void |
close | 关闭对话框 | () => void |
getElement | 获取对话框 DOM 元素 | () => HTMLDivElement | null |
无障碍支持
- 使用
role="dialog"和aria-modal属性 - 支持 ESC 键关闭
- 完整的 ARIA 标签支持
- 支持屏幕阅读器
- 自动锁定焦点在对话框内
受控 / 非受控
Dialog 通过 v-model(modelValue)受控,父级是唯一事实来源:
vue
<template>
<ExButton @click="visible = true">打开</ExButton>
<ExDialog v-model="visible" title="标题">内容</ExDialog>
</template>
<script setup>
import { ref } from 'vue'
const visible = ref(false)
</script>- 确认不自动关闭:
confirm事件触发后对话框保持打开,便于做异步校验或显示confirm-loading;校验通过后由你设置v-model = false关闭。 - 拦截关闭:
before-close返回false可阻止关闭(点击遮罩、ESC、关闭按钮都会经过它)。
vue
<ExDialog
v-model="visible"
:confirm-loading="loading"
:before-close="onBeforeClose"
@confirm="submit"
/>常见问题
Q:点了确认按钮对话框没关闭? 这是预期行为——confirm 不会自动关闭,方便异步提交。在 @confirm 回调里 visible.value = false,或在提交成功后关闭。
Q:如何禁止点击遮罩 / ESC 关闭? 设置 :close-on-click-modal="false" 和 :close-on-press-escape="false",或用 before-close 统一拦截。
Q:多个对话框叠加时滚动条乱跳 / 关不干净? 已内置引用计数的滚动锁与浮层栈:叠加时滚动只锁一次,ESC 只关闭最顶层,全部关闭后才恢复滚动。
Q:对话框里的表单每次打开都被重置? 对话框内容随关闭从 DOM 卸载(每次打开重新渲染)。需要跨开关保留状态时,把状态提升到父组件维护。
最佳实践
- 危险/不可逆操作配合
type="danger"+before-close做二次确认,或直接使用 Popconfirm。 - 表单提交用
confirm-loading表达加载态,提交成功后再关闭,失败则保持打开并提示。 - 内容较多时用
size="large"或fullscreen,避免内部出现双滚动条。 - 始终提供
title或aria-label,保证屏幕阅读器可识别对话框用途。
主题定制
Dialog 不定义独立的组件级变量,而是消费 ExUI 全局设计令牌。定制方式:
- 单个实例:用
custom-class/modal-class添加类名后覆盖样式。 - 全局:覆盖对话框所消费的全局令牌:
css
:root {
--ex-color-bg-surface: #11151c; /* 对话框背景 */
--ex-color-border-primary: #1f2a37; /* 边框 / 分割线 */
}