Skip to content

BizModal 业务弹层 ​

业务弹层统一壳:dialog 居中弹框 / drawer 侧向滑窗双模式,基于 el-dialog、el-drawer 二次封装。同时提供声明式组件(模板内 v-model)与函数式 API(openBizDialog / openBizDrawer,命令式打开、关闭即彻底销毁、promise 交付结果)两种用法,内置提交契约(loading 编排)、未保存脏检查、提交锁、单例防重复、级联滑窗(副滑窗在主滑窗左侧并排滑出、双栏联动)、尺寸预设(small / medium / large)、长内容滚动与全屏切换。

基础用法 ​

声明式:v-model 控制显隐,footer 按钮内置,confirm 事件中编排校验与提交,confirm-loading 控制确定按钮 loading(提交期间自动锁定 X / Esc / 遮罩 / 取消)。

width="small" 为尺寸预设(480px),也可传像素 / 百分比。

滑窗模式 ​

mode="drawer" 切换为侧向滑窗;纯展示场景可关闭内置按钮,用 #footer 插槽自定义。

标题由壳统一自绘(EP 原生 drawer 标题样式与 dialog 不一致,壳统一为 dialog 标题视觉);EP 原生属性(direction、with-header 等)自动透传。

增强能力:尺寸预设 / 长内容滚动 / 全屏切换 ​

width / size 支持 'small' | 'medium' | 'large' 预设(映射主题 token --biz-modal-width-small/medium/large,宽度规范统一可覆盖);内容超高时独立滚动、footer 常驻;fullscreenable 开启标题栏全屏切换按钮。

滚动阈值由 --biz-modal-body-max-height(默认 60vh)控制;全屏状态支持 v-model:fullscreen 受控。

函数式 API(推荐:表单/详情类弹框) ​

命令式打开:内容组件只写内容不写壳,useBizModalChild 注册 submit / isDirty 契约后,loading 编排、成功关框、失败保持、脏检查全部由引擎托管;关闭即彻底销毁(下次打开全新挂载,无需 reset),结果经 promise / onResult 交付。

包含:懒加载打开(loading 占位)、单例防重复(关旧开新)、弹框/滑窗换壳复用同一内容组件、纯展示型、onResult 快捷回调与 closeAllBizModals 全局关闭。

使用步骤 ​

ts
// 1. main.ts 安装插件(一次性,捕获应用 context,弹框内可用全局组件/store)
import { bizModalPlugin } from 'vue3-ep-biz-lib'
app.use(bizModalPlugin)

// 2. 内容组件 FooForm.vue:只写表单,注册提交契约
const submit = async () => {
  await formRef.value?.validate()          // 校验失败 → reject → 弹框保持打开
  await apiSave(form)                      // 接口报错 → throw → 同上
  return { id: form.id }                   // 返回值 → BizModalResult.data
}
const { close } = useBizModalChild({ submit, isDirty: () => isDirty() })

// 3. 页面中打开并消费结果
const { openBizDialog } = useBizModal()    // 弹框随本组件自动关闭
openBizDialog({
  component: () => import('./FooForm.vue'), // 懒加载(推荐,自动分包)
  props: { id: row.id },                    // → 传给 FooForm 的 props
  title: '编辑', width: 'small',
  singleton: 'FooForm',                     // 防重复打开
}).promise.then(({ action, data }) => {
  if (action === 'confirm') fetchList()     // 只有提交成功才刷新
})

方法 ​

方法参数返回说明
openBizDialogoptions: BizModalOpenOptionsBizModalHandle<T>函数式打开弹框(el-dialog 壳)
openBizDraweroptions: BizModalOpenOptionsBizModalHandle<T>函数式打开滑窗(el-drawer 壳)
useBizModaloptions?: { closeOnRouteChange?: boolean }{ openBizDialog, openBizDrawer, closeAll }组合式入口:返回的函数任意上下文可用,弹框随调用方组件 deactivated/unmounted 自动关闭;检测到路由时 path 变化自动关闭
useBizModalChildapi: { submit?, isDirty? }{ close }内容组件契约(壳托管模式):注册提交/脏检查,获得自关能力;不在壳内为无害空操作
closeAllBizModals——关闭当前全部弹框(退出登录等全局场景)
bizModalPluginapp.use(...)—插件:捕获应用 context(main.ts 安装一次)

OpenOptions 参数 ​

① 引擎行为参数

属性名类型默认值说明
componentComponent | (() => import(...))必填业务内容组件;传 () => import() 懒加载(加载期间自动展示 loading 占位,失败自动重试 1 次)
propsRecord<string, unknown>{}传给内容组件的 props;每次打开全新挂载,初始值即此处的值
onRecord<string, fn>{}透传给内容组件的事件监听器({ saved: fn } → emit('saved') 触发)
barebooleanfalse自含壳模式:组件自带 el-dialog/el-drawer,需遵守 modelValue / closed / confirm 约定
cascadebooleanfalse级联滑窗(仅 openBizDrawer 壳托管模式):紧贴已打开滑窗左缘并排滑出、无遮罩点击穿透,主/副可同时交互;多级依次左移(间距 16px);Esc 只关最顶层;无已开滑窗时退化为普通滑窗
singletonstring | { key, reopen? }—单例键:默认 replace(关旧开新,展示本次数据);{ key, reopen: 'ignore' } 为纯防双击复用
bindCallerbooleantrue随调用方组件 deactivate/unmount 自动关闭(事件回调等异步上下文取不到实例,请用 useBizModal)
onResult(result: BizModalResult) => void—结果快捷回调(与 promise.then 等价)

② 壳参数(同下方 Props 表的 title / width / size / fullscreenable / closeOnClickModal / closeOnPressEscape / beforeClose / confirmText / cancelText / showConfirm / showCancel / lockWhenLoading / lockedTip / scrollable,bare 模式下忽略)

③ EP 原生属性:未在上面列出的属性(append-to-body、align-center、top、direction、modal-class 等)直接写在 options 里,经 $attrs 落到底层 el-dialog / el-drawer。

BizModalHandle(返回句柄) ​

成员类型说明
keystring本次弹框键(传了 singleton 即其值,否则内部唯一键)
close(result?: BizModalResult) => void编程式关闭(优雅走关闭动画),可指定结果;已销毁句柄为安全空操作
promisePromise<BizModalResult<T>>销毁完成时 resolve 一次,永不 reject;action:'confirm' 提交成功(data 为 submit 返回值)/ 'cancel' 取消按钮 / 'close' X、Esc、遮罩、编程式、页面切走

函数式场景:删除确认 ​

替代 ElMessageBox 的业务化确认框:内容组件为纯展示型(不注册 submit),点确定即确认关闭,调用方在 onResult 里按 action 分支决定是否执行。

东门枪机摄像机东门岗亭在线
地库半球摄像机B2 车道入口离线
周界振动探测主机南围墙段 3在线

纯展示型内容组件不注册 submit,"删 除"即确认关闭;取消 / X / Esc 都不会执行删除(onResult 按 action 分支)。

取消 / X / Esc 都不会执行删除(action 为 'cancel' / 'close');只有点"删 除"才移除列表行。

函数式场景:新建 / 编辑表单 ​

submit 提交契约 + isDirty 脏检查全部引擎托管:确定按钮 loading(期间 X / Esc / 取 消 锁定)、校验失败保持打开、保存成功自动关框;有修改时点 X / Esc / 取 消 会先弹"放弃修改?"确认。

一号硬盘录像机NVR-8632N · 负责人:王强
A 区消防主机JTF-GOM/60ST · 负责人:李娜
周界振动探测主机AS-8300 · 负责人:赵敏

试试:清空名称点"确 定"(校验拦截保持打开);修改后点 X 或 Esc(脏检查先弹"放弃修改?"); 保存期间按钮 loading 且 X / 取 消 被锁定;保存成功后列表即时更新。

试试:清空名称点"确 定"(校验拦截)、修改后点 X(脏检查确认)、保存后列表即时更新;singleton 让编辑不同行时始终展示当前行数据。

函数式场景:多选回传 ​

泛型 openBizDialog<User[]>:submit 返回值即 result.data,props 回显已选——选择器 / 指派类弹窗的标准范式。

当前值班:王强 · 值班经理

泛型 `openBizDialog<User[]>`:submit 返回值即 `result.data`,指派结果直接回写页面;已选人员自动回显勾选态。

"至少一人"校验在 submit 内(为空提示并保持打开);确认指派后页面值班名单即时更新。

函数式场景:分步向导 ​

关闭内置 footer(showConfirm / showCancel 均为 false),"上一步 / 下一步 / 完成"由内容组件自管;完成时经 useBizModalChild 的 close({ action: 'confirm', data }) 自关并交付结果(自关不经提交锁 / 脏检查拦截)。

还没有告警规则,点下方按钮创建一条

drawer 滑窗 + ElSteps 两步向导;未填规则名称时"完成并启用"被拦下;完成后结果经 promise 回写页面列表。

drawer 滑窗 + ElSteps 两步向导;未填规则名称时"完成并启用"被拦下;完成后结果经 promise 回写页面。

函数式场景:自动关闭公告 ​

句柄编程式关闭:超时 handle.close() 与用户确认(action: 'confirm')经 promise 区分来源;句柄关闭幂等,用户先关后超时触发无副作用。

两条关闭路径经 promise 区分:点"知道了"为 confirm;8 秒无操作由 handle.close() 自动关(action='close')。 句柄关闭幂等,用户先关后超时触发无副作用。

8 秒无操作自动关闭;点"知道了"为确认已读,两条路径分别提示。

函数式场景:bare 自含壳 ​

复用自带 el-dialog 的存量组件:遵守 modelValue / update:modelValue / closed / confirm(数据) 约定即可纳入引擎(promise 交付、关闭即销毁、编程式关闭),无需改造组件。

当前设备名:东门枪机摄像机

内容组件自带 el-dialog 壳(左上方源码 legacy-device-dialog.vue):保存走组件自身 footer, emit('confirm', data) 的数据经 onResult 回写页面;关闭动画结束后引擎自动销毁,无需手动清理。

内容组件(legacy-device-dialog.vue)自带 el-dialog 壳,bare: true 声明后引擎注入关闭约定;保存结果经 onResult 回写页面。

工单详情弹窗(只读) ​

详情场景:show-confirm=false 仅留关闭按钮、#header 插槽挂状态标签、width=large 宽幅 + 长内容滚动。

长表单滑窗 ​

drawer 场景:42% 宽滑窗 + 分块表单(抬头 / 银行信息),scrollable 使 footer 保存按钮常驻不随内容滚走。

弹窗中开弹窗 ​

嵌套场景:外层声明式列表弹窗 → 行内「详情」按钮函数式打开子弹窗(appendToBody),onResult 回调感知关闭动作。

级联滑窗(二级滑窗) ​

函数式滑窗传 cascade: true:副滑窗不覆盖主滑窗,而是紧贴主滑窗左缘并排滑出,且无遮罩、点击穿透——主/副滑窗同时可见、同时可操作,适配「列表 + 详情」「表单 + 选择器」这类需要两栏对照联动的业务场景。

  • 副滑窗从主滑窗左缘滑出(滑入动画与右滑一致,方向感自然),主滑窗不被遮盖、不被锁交互
  • 多级级联:三级、四级滑窗依次继续向左排布(相邻间距 16px),同屏共存
  • Esc / 关闭按钮只作用于最顶层滑窗(EP 焦点陷阱栈保证);主滑窗关闭时,其内容组件经 useBizModal 打开的副滑窗随调用方作用域自动联动关闭
  • 当前没有已打开的滑窗时,cascade 自动退化为普通右滑窗;仅 openBizDrawer 壳托管模式支持(dialog / bare 传了会被忽略并 DEV 警告)

依次体验:行内「详情」→ 副滑窗紧贴主滑窗左缘滑出且互不遮盖;副滑窗内「物流跟踪」→ 三级滑窗继续左移; 副滑窗改备注后「确 定」→ 主滑窗列表实时更新;Esc 只关最顶层滑窗。

主滑窗工单列表 → 行内「详情」级联打开副滑窗(紧贴左缘、互不遮盖)→ 副滑窗内再开「物流跟踪」三级滑窗继续左移;副滑窗改备注「确 定」后经 promise 回传,主滑窗列表实时更新;singleton 让换行查看始终关旧开新。

点「选择设备」→ 选择器在主滑窗左侧并排滑出;两栏同屏,主滑窗表单仍可操作; 点选设备后回填主表单继续编辑;未选设备直接提交会被校验拦下(弹框保持打开)。

表单 + 选择器双栏:主滑窗填写调拨单,点「选择设备」在左侧级联打开选择器,同屏边看边选;点选设备经 close({ action: 'confirm', data }) 自关并回填主表单,主滑窗草稿不丢、继续编辑;未选设备直接提交会被 submit 校验拦下(保持打开)。

Props ​

属性名类型默认值说明
mode'dialog' | 'drawer''dialog'弹层模式:居中弹框 / 侧向滑窗
modelValue / v-modelbooleantrue弹层显隐
titlestring''标题
widthstring | number | 'small' | 'medium' | 'large''medium'弹框宽度(dialog):尺寸预设映射 --biz-modal-width-small/medium/large(480/560/720px)
sizestring | number | 'small' | 'medium' | 'large''40%'滑窗尺寸(drawer),预设同上
fullscreenbooleanfalse初始是否全屏(dialog);支持 v-model:fullscreen 受控
fullscreenablebooleanfalse标题栏显示"全屏切换"按钮(dialog)
closeOnClickModalbooleanfalse点击遮罩关闭(默认关,防表单误触)
closeOnPressEscapebooleantrue按 Esc 关闭
beforeClose(done: () => void) => void—关闭前拦截;需要关闭时必须调用 done()
confirmTextstring'确 定'确定按钮文案
cancelTextstring'取 消'取消按钮文案
showConfirmbooleantrue是否显示确定按钮
showCancelbooleantrue是否显示取消按钮(两者都 false 时隐藏整个 footer,除非提供 #footer)
confirmLoadingbooleanfalse确定按钮 loading(声明式受控;函数式由引擎自动管理)
lockWhenLoadingbooleantrueloading 期间锁定用户关闭路径(X/Esc/遮罩/取消),防"请求已发出但用户关框"
lockedTipstring'正在处理,请稍候…'锁定拦截时的提示文案(传空串关闭提示)
scrollablebooleantrue内容超长独立滚动(最大高度 --biz-modal-body-max-height,默认 60vh),footer 常驻

Events ​

事件名参数说明
update:modelValue(value: boolean)显隐更新(v-model)
update:fullscreen(value: boolean)全屏状态更新(v-model:fullscreen)
confirm—点击"确 定"(声明式用法在此编排校验/提交;函数式由引擎接管 submit 契约)
cancel—点击"取 消"(放行后自动关闭)
opened—打开动画结束(透传 el-dialog/el-drawer)
closed—关闭动画结束(透传;函数式引擎在此销毁子树)

Slots ​

插槽名说明
default内容区(函数式模式为业务内容组件)
header自定义标题区(透传 EP header 插槽作用域;启用后 title 由该插槽渲染,dialog 模式追加全屏按钮)
footer自定义底部区(覆盖内置按钮)

Expose ​

属性/方法说明
close编程式关闭(引擎 handle.close() 内部使用;声明式可直接调)
setConfirmLoading设置确定按钮 loading(函数式引擎在 submit 前后调用)
getChildSubmit取内容组件经 useBizModalChild 注册的 submit(未注册返回 undefined)
toggleFullscreen切换全屏状态(dialog)

与参考实现(ProModal)的增强对照 ​

能力ProModalBizModal
长内容滚动整体撑高,footer 随内容滚走body 独立滚动 + footer 常驻(scrollable)
尺寸规范仅字符串宽度small / medium / large 预设 token,全局统一覆盖
懒加载反馈body 空白loading 占位视图(120ms 防闪)
提交锁反馈静默拦截lockedTip warning 提示(可关)
全屏仅 EP 静态 propfullscreenable 标题栏切换 + v-model
结果消费仅 promise+ onResult 快捷回调
全局治理无closeAllBizModals()
TS 类型any 泛型擦除全量 unknown + 泛型 openBizDialog<TData>()
库化依赖静态依赖 vue-router / import.meta.env运行时探测 $route;env 安全访问