Skip to content

Dialog 对话框 ​

用于承载需要用户聚焦处理的确认、编辑或详情内容。

基础用法 ​

vue
<template>
  <x-button type="primary" @click="visible = true">打开对话框</x-button>
  <x-dialog v-model="visible" title="标题" width="520px">
    内容
    <template #footer="{ cancel, ok, loadingObj }">
      <x-space>
        <x-button :loading="loadingObj.cancel" @click="cancel">取消</x-button>
        <x-button type="primary" :loading="loadingObj.ok" @click="ok">确认</x-button>
      </x-space>
    </template>
  </x-dialog>
</template>

<script setup lang="ts">
import { ref } from 'vue';

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

异步关闭 ​

vue
<template>
  <x-button type="primary" @click="visible = true">异步确认</x-button>
  <x-dialog v-model="visible" title="提交确认" :on-before-cancel="beforeClose">
    确认按钮会等待异步任务完成;取消按钮会被拦截并保持打开。
    <template #footer="{ cancel, ok, loadingObj }">
      <x-space>
        <x-button :loading="loadingObj.cancel" @click="cancel">取消</x-button>
        <x-button type="primary" :loading="loadingObj.ok" @click="ok">提交</x-button>
      </x-space>
    </template>
  </x-dialog>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const visible = ref(false);

const beforeClose = async (action: 'cancel' | 'ok') => {
  if (action === 'cancel') return false;
  await new Promise((resolve) => window.setTimeout(resolve, 1200));
  return true;
};
</script>

显示方式 ​

vue
<template>
  <div class="demo-inline-group">
    <x-button @click="centerVisible = true">居中</x-button>
    <x-button @click="topVisible = true">顶部距离</x-button>
    <x-button @click="fullscreenVisible = true">全屏</x-button>
  </div>
  <x-dialog v-model="centerVisible" center title="居中对话框" />
  <x-dialog v-model="topVisible" title="自定义顶部距离" top="80px" />
  <x-dialog v-model="fullscreenVisible" title="全屏对话框" fullscreen />
</template>

<script setup lang="ts">
import { ref } from 'vue';

const centerVisible = ref(false);
const topVisible = ref(false);
const fullscreenVisible = ref(false);
</script>

保留内容状态 ​

vue
<template>
  <x-button @click="visible = true">打开并保留内容</x-button>
  <x-dialog v-model="visible" title="关闭后保留内容" :destroy-on-closed="false">
    <x-input v-model="text" placeholder="关闭后再次打开,内容仍会保留" />
  </x-dialog>
</template>

<script setup lang="ts">
import { ref } from 'vue';

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

命令式调用 ​

vue
<template>
  <div class="demo-inline-group">
    <x-button type="primary" @click="openCommandDialog">命令式打开</x-button>
    <x-button @click="openNoFooterDialog">无底部按钮</x-button>
    <x-button @click="openManualDialog">手动更新关闭</x-button>
    <x-button @click="Dialog.destroyAll()">关闭全部</x-button>
  </div>
</template>

<script setup lang="ts">
import { Dialog } from 'x-next';

const openCommandDialog = () => {
  Dialog({
    title: '提交确认',
    content: '确认提交当前表单吗?',
    onOk: () => console.log('ok'),
  });
};

const openNoFooterDialog = () => {
  Dialog({
    title: '说明',
    content: () => 'footer=false 时不会显示底部按钮',
    footer: false,
  });
};

const openManualDialog = () => {
  const instance = Dialog({
    title: '处理中',
    content: '3 秒后关闭',
  });
  window.setTimeout(() => instance.update({ content: '即将关闭' }), 1500);
  window.setTimeout(() => instance.close(), 3000);
};
</script>

局部容器 ​

renderTo 接受选择器或 HTMLElement。宿主应在打开前已经挂载,找不到目标时会回退到 body;为局部弹层提供明确的宽高。局部宿主使用定位上下文,静态定位的宿主会在打开期间临时设置为 position: relative,弹层根使用绝对定位。关闭动画结束或组件卸载后恢复原始内联样式;同一宿主上的多个 Dialog 会保持锁定,直到最后一个关闭。

局部遮罩只覆盖宿主区域,overflow: hidden 会裁剪超出区域的弹层内容。全屏及页面级模态交互建议使用默认 body 宿主;局部容器的尺寸、祖先裁剪和层叠上下文仍由业务布局控制。disabled 仅关闭 Teleport,适合内联渲染,不代表禁用 Dialog 交互,也不取消 renderTo 对应的滚动锁。

vue
<template>
  <x-button @click="openLocalDialog">挂载到局部容器</x-button>
  <div id="dialog-local-container" class="dialog-local-container"></div>
</template>

<script setup lang="ts">
import { Dialog } from 'x-next';

const openLocalDialog = () => {
  Dialog({
    title: '局部容器',
    content: 'renderTo 找不到目标时会回退到 body',
    renderTo: '#dialog-local-container',
  });
};
</script>

切换宿主与嵌套对话框 ​

打开后可修改 renderTo,滚动锁会从旧宿主转移到新宿主。下面的两个 Dialog 使用同一个目标,关闭其中一层仍保留另一层的锁;关闭后可滚动宿主查看恢复效果。

宿主 A:关闭后可滚动
宿主 B:关闭后可滚动
vue
<template>
  <button @click="visible = true">打开局部对话框</button>
  <div class="dialog-host-row">
    <div id="dialog-host-a" class="dialog-host"><div class="host-content">宿主 A</div></div>
    <div id="dialog-host-b" class="dialog-host"><div class="host-content">宿主 B</div></div>
  </div>
  <DialogComponent v-model="visible" :render-to="target" title="局部编辑" width="280px" top="24px" :mask-to-close="false">
    <p>当前宿主:{{ target === '#dialog-host-a' ? 'A' : 'B' }}</p>
    <button @click="target = target === '#dialog-host-a' ? '#dialog-host-b' : '#dialog-host-a'">切换宿主</button>
    <button @click="nestedVisible = true">打开第二层</button>
  </DialogComponent>
  <DialogComponent v-model="nestedVisible" :render-to="target" title="第二层" width="260px" top="48px">
    关闭这一层后,下层对话框仍保持宿主滚动锁。
  </DialogComponent>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { DialogComponent } from 'x-next';
import 'x-next/style/base.css';
import 'x-next/style/dialog.css';

const visible = ref(false);
const nestedVisible = ref(false);
const target = ref('#dialog-host-a');
</script>

<style scoped>
.dialog-host-row { display: flex; flex-wrap: wrap; gap: 12px; width: 100%; }
.dialog-host { position: relative; flex: 1 1 300px; height: 320px; overflow: auto; }
.host-content { min-height: 640px; }
</style>

按需导入 ​

包根导出的 DialogComponent 是声明式 Vue 组件,用于模板、v-model 和插槽;Dialog 是命令式函数,用于 Dialog(options)、返回的 close/update 和 Dialog.destroyAll()。全局安装组件库后仍可在模板中使用 <x-dialog>。

ts
import { DialogComponent, Dialog } from 'x-next';
vue
<template>
  <button @click="visible = true">声明式打开</button>
  <DialogComponent v-model="visible" title="创建知识库" width="480px">
    <label for="knowledge-name">名称</label>
    <input id="knowledge-name" v-model="name" />
    <template #footer="{ cancel, ok }">
      <button @click="cancel">取消</button>
      <button @click="ok">确认</button>
    </template>
  </DialogComponent>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { DialogComponent } from 'x-next';
import 'x-next/style/base.css';
import 'x-next/style/dialog.css';

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

样式按需引入(base.css 为共享基础层,多个组件只需引入一次):

ts
import 'x-next/style/base.css';
import 'x-next/style/dialog.css';

Component Props ​

参数说明类型默认值
modelValue是否显示对话框booleanfalse
renderTo挂载容器,找不到时回退到 bodystring | HTMLElement'body'
disabled是否禁用 Teleportbooleanfalse
top距离顶部位置,center=false 时生效string | number'3vh'
center是否居中显示booleanfalse
width宽度string | number0
height高度string | number0
minWidth最小宽度string | number0
minHeight最小高度string | number0
title标题string''
mask是否显示遮罩booleantrue
maskToClose点击遮罩是否关闭booleantrue
escToClose按下 Esc 是否关闭booleantrue
fullscreen是否全屏booleanfalse
destroyOnClosed关闭后是否销毁内容booleantrue
onBeforeCancel关闭前拦截,返回 false 阻止关闭(action?: 'cancel' | 'ok') => boolean | Promise<boolean>-
popupClass弹层自定义类名string-
showClose是否显示关闭按钮booleantrue
showHeaderBottomLine是否显示标题底部分割线booleanfalse
hideFooter是否隐藏底部区域booleanfalse

Component Events ​

事件名说明回调参数
update:modelValue显隐状态变化时触发value: boolean
open打开时触发-
opened打开动画结束后触发-
close请求关闭时触发action: 'cancel' | 'ok', event?: Event
closed关闭动画结束后触发-

Component Slots ​

插槽名说明参数
title自定义标题-
default对话框主体内容-
footer自定义底部操作区{ cancel, ok, loadingObj, loading-obj }

Dialog Methods ​

方法说明参数返回值
Dialog(options)打开命令式对话框DialogOptionsDialogReturn
Dialog.destroyAll()关闭全部命令式对话框-void

Dialog Options ​

参数说明类型默认值
title标题DialogContent-
content正文内容DialogContent-
footer自定义底部;false 时隐藏底部boolean | VNode | (scoped) => DialogRenderResult默认按钮
okText确认按钮文案string'确认'
cancelText取消按钮文案string'取消'
mask是否显示遮罩booleantrue
maskToClose点击遮罩是否关闭booleantrue
beforeOnOk确认前拦截() => boolean | Promise<boolean>-
beforeOnCancel取消前拦截() => boolean | Promise<boolean>-
onOk点击确认回调(event: Event) => void-
onCancel点击取消回调(event: Event) => void-
width宽度string | number368
height高度string | number'auto'
top距离顶部位置string | number0
center是否居中booleantrue
renderTo挂载容器,找不到时回退到 bodystring | HTMLElement'body'
hideCancel是否隐藏取消按钮booleanfalse
hideOk是否隐藏确认按钮booleanfalse
popupClass弹层自定义类名string-

返回值 ​

方法说明参数
close()关闭当前对话框-
update(options)更新当前对话框配置Partial<DialogOptions>

无障碍说明 ​

Dialog 打开后会聚焦到首个可通过 Tab 到达的元素,Tab / Shift+Tab 在对话框内部循环;tabindex 为负数的元素仍可通过业务代码聚焦,但不会作为顺序循环的边界。隐藏、inert 和禁用控件会被跳过,透明或零宽的输入仍可接受键盘焦点。没有可用的顺序焦点时聚焦对话框容器。

关闭或条件卸载会把焦点恢复到打开前仍在文档中的元素;若焦点已移到其他交互区域或后来打开的 Dialog,则保留当前焦点。对话框容器带有 role="dialog" 和标题关联,右上角关闭控件是可聚焦按钮。无标题时使用 aria-label 或 aria-labelledby 提供名称,可通过 aria-describedby 关联说明。