切换主题
YModal
适用范围:Legacy Admin Kit / Schema(
@yoyoboot/yoyo-admin-kit、旧admin-vue)。新admin-nuxtui使用 Nuxt UI;本页不是新宿主的必经开发路线。
定位 / Owner
YModal 拥有通用容器、焦点、关闭交互与尺寸,不拥有字段、校验和保存请求。
| 需求 | 选择 |
|---|---|
| 普通内容、详情、自定义操作 | YModal |
| 侧边详情或长任务 | YDrawer |
| 编辑、dirty 确认、保存继续、busy | YEditorPanel |
| 危险操作确认 | YConfirmDialog / YDangerConfirmDialog |
基础用法
ts
import { YModal, YButton } from '@yoyoboot/yoyo-admin-kit'
import '@yoyoboot/yoyo-admin-kit/styles.css'
import '@yoyoboot/yoyo-admin-kit/theme.css'在应用入口加载一次样式。Modal 不要求业务 provider;示例仅用浏览器内存,不访问 HTTP,也不是持久化验收。
可运行示例
基础内容与操作区
最小受控开关,actions 使用共享底部布局。
查看同源 Vue 代码
<script setup lang="ts">
import { ref } from 'vue'
import { YButton, YModal } from '@yoyoboot/yoyo-admin-kit'
const open = ref(false)
</script>
<template>
<YButton @click="open = true">查看详情</YButton>
<YModal v-model:open="open" title="记录详情" description="通用容器不拥有保存逻辑。">
<p>这里可以放置详情、说明或自定义内容。</p>
<template #actions><YButton @click="open = false">完成阅读</YButton></template>
</YModal>
</template>
尺寸、全屏与长内容
可运行示例
尺寸与滚动
选择尺寸,修改输入,切换全屏,再滚动到底部。
查看同源 Vue 代码
<script setup lang="ts">
import { ref } from 'vue'
import { YButton, YInput, YModal } from '@yoyoboot/yoyo-admin-kit'
const open = ref(false)
const size = ref<'sm' | 'md' | 'lg'>('md')
const note = ref('切换全屏后内容仍在')
</script>
<template>
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap">
<YButton v-for="value in (['sm', 'md', 'lg'] as const)" :key="value" variant="outline" @click="size = value; open = true">{{ value }} 尺寸</YButton>
</div>
<YModal v-model:open="open" title="尺寸与长内容" :size="size" allow-fullscreen>
<YInput v-model="note" aria-label="保留的输入" />
<p v-for="index in 32" :key="index" style="margin-block: 0.75rem">第 {{ index }} 段:正文独立滚动,底部操作仍可访问。</p>
<template #actions><YButton @click="open = false">完成</YButton></template>
</YModal>
</template>
关闭策略
可运行示例
来源与同步拦截
禁用来源不发请求;父级直接关闭不走业务拦截。
尚无关闭请求
查看同源 Vue 代码
<script setup lang="ts">
import { ref } from 'vue'
import { YButton, YCheckbox, YModal, type YSurfaceCloseRequest } from '@yoyoboot/yoyo-admin-kit'
const open = ref(false)
const allowEscape = ref(true)
const allowOutside = ref(false)
const intercept = ref(true)
const lastRequest = ref('尚无关闭请求')
const modal = ref<InstanceType<typeof YModal> | null>(null)
function onClose(request: YSurfaceCloseRequest) {
lastRequest.value = `${request.reason}:${intercept.value ? '已拦截' : '允许关闭'}`
if (intercept.value) request.preventDefault()
}
</script>
<template>
<YButton @click="open = true">体验关闭策略</YButton>
<p role="status">{{ lastRequest }}</p>
<YModal ref="modal" v-model:open="open" title="关闭策略" :close-on-escape="allowEscape" :close-on-outside="allowOutside" @close-request="onClose">
<div style="display: grid; gap: 1rem">
<YCheckbox v-model="allowEscape" label="允许 Escape" />
<YCheckbox v-model="allowOutside" label="允许点击遮罩" />
<YCheckbox v-model="intercept" label="同步拦截关闭请求" />
<p role="status">{{ lastRequest }}</p>
</div>
<template #actions>
<YButton variant="outline" @click="modal?.requestClose()">请求关闭</YButton>
<YButton @click="open = false">父级直接关闭</YButton>
</template>
</YModal>
</template>
焦点与完成事件
可运行示例
初始焦点和嵌套恢复
观察完成事件;关闭上层弹窗,检查焦点返回。
等待打开
查看同源 Vue 代码
<script setup lang="ts">
import { ref, useId } from 'vue'
import { YButton, YInput, YModal } from '@yoyoboot/yoyo-admin-kit'
const open = ref(false)
const nested = ref(false)
const value = ref('初始焦点进入这里')
const id = useId()
const events = ref<string[]>([])
const focusInput = () => document.getElementById(id)
function record(event: string) { events.value = [...events.value.slice(-5), event] }
</script>
<template>
<YButton @click="open = true">体验焦点与事件</YButton>
<p role="status">{{ events.join(' → ') || '等待打开' }}</p>
<YModal v-model:open="open" title="焦点与生命周期" :initial-focus="focusInput" @after-enter="record('after-enter')" @after-leave="record('after-leave')">
<YInput :id="id" v-model="value" aria-label="初始焦点字段" />
<YButton variant="outline" @click="nested = true">打开上层弹窗</YButton>
<p role="status">{{ events.join(' → ') }}</p>
</YModal>
<YModal v-model:open="nested" title="上层弹窗" size="sm">
<p>关闭后焦点回到下层触发按钮;关闭下层后回到示例入口。</p>
</YModal>
</template>
Public API
Props
| 名称 | 类型 | 默认值 | 职责 |
|---|---|---|---|
| title | string | 必填 | 可访问标题 |
| description | string | 未设置 | 可见说明;缺省有隐藏说明 |
| size | 'sm' / 'md' / 'lg' | 'md' | 桌面宽度,移动端受视口约束 |
| allowFullscreen | boolean | false | 全屏/恢复,不重建正文 |
| closeText | string | '关闭' | 关闭按钮可访问名称 |
| closeDisabled | boolean | false | 优先拒绝所有组件关闭请求 |
| closeOnEscape | boolean | true | 允许 Escape |
| closeOnOutside | boolean | true | 允许外部点击 |
| showClose | boolean | true | 仅控制关闭按钮可见性 |
| initialFocus | () => HTMLElement / null / undefined | 未设置 | 当前容器内可交互元素,否则回退 |
| restoreFocusTo | HTMLElement / null | 打开前焦点 | 失效则回退上层可交互目标 |
Models / Events / Slots / Expose
| 接口 | 类型/参数 | 时机与语义 |
|---|---|---|
| v-model:open | boolean,默认 false | 父级拥有最终显隐权 |
| close-request | YSurfaceCloseRequest | 合法关闭请求;同步 preventDefault() 阻止关闭 |
| after-enter | 无参数 | 当前打开过渡完成,无动画也通知一次 |
| after-leave | 无参数 | 当前关闭过渡完成,无动画也通知一次 |
| default | slot | 可滚动正文 |
| header-actions | slot | 标题区次级操作 |
| actions | slot | 固定操作区,启用 task 布局 |
| requestClose() | expose,void | programmatic 来源,遵循相同拦截链 |
YSurfaceCloseRequest.reason 为 close-button | escape | outside | programmatic。异步 dirty 确认应先同步阻止请求,再由 EditorPanel 或既有业务 owner 处理。父级 open=false 是权威覆盖,不发关闭请求,不能代替安全离开入口。隐藏关闭按钮时须提供其他可达退出操作。
能力与迁移
| 能力 | 当前边界 |
|---|---|
| 尺寸、全屏、嵌套 popup、任务操作区 | 已有能力,继续保留 |
| 来源、取消、initialFocus、完成事件 | 源码提供;采用制品前核对版本 |
| 非模态、拖动缩放、全局 openModal | 当前未提供 |
YDialog 是 deprecated 同对象兼容别名。新代码使用 YModal;旧 Dialog 页面 保留迁移入口。EditorPanel 新 surface 是 modal | drawer,旧 dialog 输入集中规范化为 modal。
Live Workbench
/frontend-module-demo/component-workbench/modal;旧 /dialog 入口仍可访问。
Verification / Source
packages/yoyo-admin/admin-kit/src/ui/components/modal/YModal.vuepackages/yoyo-admin/admin-kit/tests/unit/ui/dialog/dialog-behavior.spec.ts- 同源示例:
docs/对外文档/参考/既有Kit与Schema/示例/弹窗/。