Dialog 对话框
用于打断当前操作、展示需要确认的信息。Dialog 有两种变体:
basic(默认):视口居中的模态卡片,用于确认、提示和简短表单。fullscreen:铺满视口的全屏对话框,用于移动端的多步骤表单等需要完整空间的任务。
v-model:open 控制显示;打开时组件 Teleport 到 body、锁定页面滚动、把焦点移入面板并在内部循环。 关闭方式由操作按钮、关闭按钮(全屏变体)、遮罩点击与 Escape 组成,后两者受 dismissible 控制。
基础对话框
标题用 title,正文用默认插槽,操作按钮放到 actions 插槽。操作按钮用 variant="text" 的 Button,与正文形成层级。
vue
<script setup lang="ts">
import { ref } from "vue";
import { Button, Dialog } from "@sharpdotnut/dotsharp";
const open = ref(false);
</script>
<template>
<Button @click="open = true">打开基础对话框</Button>
<Dialog v-model:open="open" title="Basic dialog title">
A dialog is a modal window that appears in front of app content to provide critical information
or ask for a decision.
<template #actions>
<Button variant="text" @click="open = false">Text button</Button>
<Button variant="text" @click="open = false">Text button</Button>
</template>
</Dialog>
</template>actions 是右对齐的弹性行;需要左对齐的次要操作(例如「了解更多」)时,给它加 class="mr-auto"。
图标与居中标题
icon 与 Icon 的 icon 同源,渲染在标题上方并让标题居中;正文仍然左对齐。 icon 插槽优先于 icon 属性,用来放加载指示器等自定义内容。
vue
<script setup lang="ts">
import { ref } from "vue";
import { Button, Dialog } from "@sharpdotnut/dotsharp";
import progressActivity from "@iconify-icons/material-symbols/progress-activity";
const open = ref(false);
</script>
<template>
<Dialog v-model:open="open" title="Reset settings?" :icon="progressActivity">
正文……
<template #actions>
<Button variant="text">Cancel</Button>
<Button variant="text" @click="open = false">Accept</Button>
</template>
</Dialog>
</template>左对齐操作
三个操作按钮时,把次要操作推到行首,主操作留在行尾:
vue
<Dialog v-model:open="open" title="Use location service?">
正文……
<template #actions>
<Button variant="text" class="mr-auto">Learn more</Button>
<Button variant="text" @click="open = false">Disagree</Button>
<Button variant="text" @click="open = false">Agree</Button>
</template>
</Dialog>全屏对话框
variant="fullscreen" 铺满视口:顶部栏固定,包含关闭按钮、title 与行尾的 actions 插槽; 正文在剩余空间内独立滚动,适合需要边输入边确认的表单。进入与退出都从底部滑入滑出。
vue
<script setup lang="ts">
import { ref } from "vue";
import { Button, Dialog, Input } from "@sharpdotnut/dotsharp";
const open = ref(false);
const eventName = ref("");
</script>
<template>
<Dialog v-model:open="open" variant="fullscreen" title="Full-screen dialog title">
<div class="mx-auto flex max-w-xl flex-col gap-4">
<Input v-model="eventName" placeholder="Event name" />
</div>
<template #actions>
<Button variant="text" @click="open = false">Save</Button>
</template>
</Dialog>
</template>全屏变体的 actions 渲染在顶部栏行尾;icon 与 icon 插槽只对 basic 生效。关闭按钮的图标固定为 close,无障碍名称由 close-label 提供(默认 Close)。
关闭行为
默认 dismissible 为 true:点击遮罩、按 Escape 都会关闭对话框。置为 false 后这两种方式都失效, 对话框只能由操作按钮关闭——用于必须做出决定或未保存的编辑。
vue
<Dialog v-model:open="open" title="Save changes?" :dismissible="false">
You have unsaved changes. Choose an action to continue.
<template #actions>
<Button variant="text" @click="open = false">Discard</Button>
<Button variant="text" @click="open = false">Save</Button>
</template>
</Dialog>长内容
正文区域有最大高度,超出时在卡片内滚动;标题与操作按钮保持固定。 basic 卡片的最大高度是 100dvh - 3rem,宽度上限 560px、左右各留 24px。
vue
<Dialog v-model:open="open" title="服务条款">
<p v-for="index in 20" :key="index" class="mb-3">第 {{ index }} 条……</p>
<template #actions>
<Button variant="text" @click="open = false">关闭</Button>
</template>
</Dialog>API
Dialog
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| v-model:open | boolean | false | 是否显示 |
| title | string | — | 标题,同时作为对话框的无障碍名称 |
| variant | "basic" | "fullscreen" | "basic" | basic 居中卡片;fullscreen 铺满视口 |
| icon | IconifyIcon | string | — | 标题上方的图标,仅 basic;存在时标题居中 |
| dismissible | boolean | true | 是否允许点击遮罩 / Escape 关闭 |
| close-label | string | "Close" | 全屏对话框关闭按钮的 aria-label |
插槽
| 名称 | 变体 | 说明 |
|---|---|---|
| default | 全部 | 正文内容 |
| actions | 全部 | 操作按钮;basic 在卡片底部右对齐,fullscreen 在顶部栏行尾 |
| icon | basic | 标题上方的自定义图标,优先于 icon 属性 |
可访问性
- 面板是
role="dialog"+aria-modal="true",title存在时通过aria-labelledby关联标题; 没有标题时请在组件上自行传aria-label。 - 打开时焦点进入面板,Tab / Shift+Tab 在面板内循环,关闭后焦点回到打开前的元素;Escape 关闭受
dismissible控制。 - 遮罩带
aria-hidden,不会进入无障碍树;遮罩只承担关闭,不需要可聚焦。 - 打开期间锁定
body滚动,多个对话框叠加时只在最后一个关闭后恢复。
结构
- 组件 Teleport 到
body,遮罩fixed inset-0 z-50(--ds-scrim),面板absolute inset-0 z-10。 basic面板:m-auto居中,最大宽度 560px,圆角 28px(rounded-ds-xl),内边距 24px; 背景bg-ds-surface,阴影shadow-xl。进入 200ms 淡入并从 95% 放大,退出 150ms。fullscreen面板:h-full w-full,顶部栏高 64px;进入 250ms 从底部滑入,退出 200ms 滑出。- 两种变体都可被
class或其它属性覆盖(透传到面板),但不要改position与inset。