Files
workspace/code/g3soft-libs/docs/content/ui/4.overlay/modal.md
T
2026-10-08 22:38:34 +08:00

7.3 KiB
Raw Blame History

title, description
title description
Modal 对话框 模态窗口,支持拖动、缩放、全屏与命令式确认框

Modal 对话框

Modal 在当前页面上方打开一个模态窗口,用户必须先处理它才能继续。

何时使用

  • 需要用户聚焦处理一件事:填写表单、确认危险操作、查看详情;
  • 需要打断当前流程、且不该被忽略时(用 maskClosable=false、keyboard=false、closable=false 收紧关闭入口);
  • 只是提示结果、不需要用户操作时,用 Message 更轻;
  • 内容很长、或需要保留页面上下文时,可以考虑 Drawer。

基础用法

v-model:open 控制显隐。三个事件区分关闭来源:confirm(底部确认按钮)、cancel(取消按钮 / 遮罩 / Esc / 右上角关闭)、closed(退场动画结束)。

:::demo{name="modal/basic"} :::

拖动、缩放与全屏

默认允许拖动标题栏移动面板、拖拽四边缩放、右上角切换全屏。三者互相协调:进入全屏会复位拖拽位置并禁用缩放手柄。

:::demo{name="modal/resize"} :::

居中显示

面板默认贴顶(align-items: flex-start)——这样长内容不会把标题栏顶出视口。确认类、短表单可以开 centered 垂直居中。

:::demo{name="modal/resize"} :::

控制关闭入口

maskClosable / keyboard / closable / footer 分别控制遮罩点击、Esc、右上角关闭按钮与底部按钮区。

:::demo{name="modal/mask"} :::

表单弹窗与异步确认

confirmLoading 期间阻止一切关闭(含遮罩、Esc、取消按钮),避免异步提交被打断;底部按钮自动进入加载态。

:::demo{name="modal/form"} :::

命令式确认框

const handle = G3Modal.confirm({
  title: '确定删除?',
  content: '删除后不可恢复',
  confirmText: '删除',
  confirmButtonProps: { danger: true },
  onConfirm: async () => {
    await api.remove() // 返回 Promise 时按钮 loading
  },
  onCancel: () => {},
})

handle.destroy() // 立即关闭并卸载(不等退场动画)

:::demo{name="modal/confirm"} :::

onConfirm 的 Promise 语义:resolve → 关闭;reject → 保持打开、解除 loading,错误由调用方提示。

onCancel 的触发范围:只有点击「取消」按钮才会调用 onCancel;点遮罩空白、Esc、右上角关闭按钮都是静默关闭(只关窗,不跑取消逻辑),避免用户随手点空白就触发取消副作用。

:::demo{name="modal/confirm-async"} :::

自定义底部与头部

#footer 完全接管底部;#header-extra 放标题右侧的额外动作。

:::demo{name="modal/footer-slot"} :::

API

Props(Modal)

名称 类型 默认值 说明
open boolean — 受控打开状态;不传即非受控
defaultOpen boolean false 非受控时的初始状态
title string '' 标题
width number | string 520 面板宽度。数字按 px,'60%' 之类的字符串原样使用
centered boolean false 垂直居中(默认贴顶,长内容不会把标题顶出视口)
mask boolean true 是否显示遮罩
maskClosable boolean true 点击遮罩空白关闭
keyboard boolean true Esc 关闭
closable boolean true 显示右上角关闭按钮
fullscreen boolean true 显示右上角全屏切换按钮
resizable boolean true 允许拖拽四边缩放宽高
footer boolean true 显示底部按钮区(取消 + 确认)
confirmText string 语言包 common.confirm 确认按钮文字
cancelText string 语言包 common.cancel 取消按钮文字
confirmLoading boolean false 确认中:按钮 loading,并阻止一切关闭操作
popupContainer PopupContainer ConfigProvider 配置 挂载容器,默认 body

Events(Modal)

名称 参数 说明
update:open (open: boolean) 打开状态变化
confirm () 点击底部确认按钮(不自动关闭,由你决定何时关)
cancel () 取消按钮 / 遮罩 / Esc / 右上角关闭被触发
closed () 退场动画结束、DOM 已销毁(此时滚动锁已释放、焦点已归还)

Slots(Modal)

名称 参数 说明
default — 正文
header-extra — 标题右侧的自定义动作(在全屏/关闭按钮之前)
footer — 自定义底部;不传时渲染「取消 + 确认」

G3Modal.confirm 参数

名称 类型 默认值 说明
title string '' 标题
content string '' 正文纯文本
confirmText string 语言包 确认按钮文字
cancelText string 语言包 取消按钮文字
confirmButtonProps Partial<ButtonProps> & { danger?: boolean } {} 透传给确认按钮;danger: true 等价于 type: 'danger'
onConfirm () => void | Promise<unknown> — 确认回调,返回 Promise 时按钮 loading
onCancel () => void — 取消回调(仅点击取消按钮触发)

返回值:{ destroy: () => void }。

类型定义

export interface ModalProps {
  open?: boolean
  defaultOpen?: boolean
  title?: string
  width?: number | string
  centered?: boolean
  mask?: boolean
  maskClosable?: boolean
  keyboard?: boolean
  closable?: boolean
  fullscreen?: boolean
  resizable?: boolean
  footer?: boolean
  confirmText?: string
  cancelText?: string
  confirmLoading?: boolean
  popupContainer?: PopupContainer
}

export interface ModalConfirmOptions {
  title?: string
  content?: string
  confirmText?: string
  cancelText?: string
  confirmButtonProps?: Partial<ButtonProps> & { danger?: boolean }
  onConfirm?: () => void | Promise<unknown>
  onCancel?: () => void
}

export interface ModalConfirmHandle {
  destroy: () => void
}

实现说明

  • 遮罩点击判定:只响应「点击事件的目标就是外层容器」且「mousedown 不在面板内」的情况。后者是为了防拖选文本:在输入框里按下鼠标、拖到遮罩上松开时,click.target 会回溯成共同的祖先容器,如果只看 click 就会误判成「点了遮罩要关闭」;
  • 焦点与滚动:打开时记录当前焦点、锁 body 滚动、把焦点移进面板,并用 Tab 循环把焦点约束在面板内;关闭时等退场动画结束才解锁滚动、复位尺寸、把焦点还给触发元素——否则动画还在播的时候背景就能滚动、焦点提前跳走,屏幕阅读器会跟丢;
  • 滚动锁是引用计数:Modal 里再开 Drawer、或弹窗里开下拉,不会互相提前解锁;
  • 命令式挂载:G3Modal.confirm() 自建容器挂到 body,open 初始即为 true,因此内部对 isOpen 用了 immediate: true 的 watch、并给 Transition 加了 appear,让「挂载」与「受控打开」走同一段初始化逻辑,退场动画结束后再卸载容器;
  • 层级:打开时从 z-index 注册表取 modal 档,闭包回收;浮层类(下拉、日期面板)取 popup 档,所以弹窗里的浮层一定盖在弹窗上。