# Modal 弹窗 :::tip 说明 项目提供 `BasicModal` 组件(`src/components/Modal/src/BasicModal.vue`)和原生 `basicModal` 两种弹窗用法。编辑弹窗通过 `defineAsyncComponent` 懒加载,减少首屏渲染负担。 ::: ## 组件架构 ``` src/components/Modal/ ├── src/ │ ├── BasicModal.vue # 主组件(基于 n-modal) │ ├── props.ts # 属性定义 │ ├── type.ts # 类型定义(ModalProps、ModalMethods) │ └── hooks/ │ └── useModalContext.ts # 弹窗上下文 └── index.ts # 导出 useModal 等 ``` ## BasicModal 组件 `BasicModal` 基于 NaiveUI 的 `n-modal` 封装,提供全屏切换、自定义头部、提交按钮等能力: ```vue ``` 核心功能: - 自定义头部(标题 + 全屏/关闭按钮) - 提交按钮自动 loading(`setSubLoading`) - `setProps` 动态修改弹窗属性 - `openModal` / `closeModal` 控制显隐 ## 实际用法(position 模块) 以岗位管理为例,使用 `basicModal` + `useModal` hook: ### 父组件(index.vue) ```vue ``` ### 编辑弹窗(edit.vue) ```vue ``` ## 弹窗数据流 ``` 父组件点击编辑 → positionId = record.id → editVisible = true ↓ edit.vue onMounted → positionId != 0 → setFormData() → 获取详情填充表单 ↓ 用户修改 → submit → formRef.validate() → positionUpdate(formData) ↓ emit('success') → 父组件 reloadTable() → 表格刷新 ``` ## 确认弹窗 删除等危险操作使用 NaiveUI 的 `useDialog`: ```typescript import { useMessage, useDialog } from 'naive-ui'; const message = useMessage(); const dialog = useDialog(); async function handleDelete(record) { let ids = []; if (!record) { ids = rowKeys.value; } dialog.warning({ title: '提示', content: '确定要删除?', positiveText: '确定', negativeText: '取消', onPositiveClick: async () => { record?.id ? await positionDelete(record.id) : await positionBatchDelete(ids); message.success('删除成功'); reloadTable(); }, }); } ``` `dialog.warning()` 是 NaiveUI 的命令式对话框,返回 Promise,用户取消时不执行 `onPositiveClick` 回调。 ## v-model:visible 双向绑定 弹窗使用 `v-model:visible` 实现父子组件双向绑定: ```vue ``` :::tip 温馨提示 弹窗组件通过 `defineAsyncComponent(() => import('./edit.vue'))` 懒加载,首屏不加载弹窗代码。`useModal` hook 提供 `openModal`、`closeModal`、`setSubLoading` 等方法。关闭弹窗时通过 `v-model:visible` 自动同步父子状态。消息提示使用 `useMessage()`,确认弹窗使用 `useDialog()`。 ::: ## 总结 编辑弹窗使用 `basicModal`(基于 `n-modal`)+ `defineAsyncComponent` 懒加载,通过 `v-model:visible` 双向绑定显隐状态。父组件通过 `positionId` prop 传递编辑行 ID,子组件在 `onMounted` 中判断并加载详情数据。操作成功后 `emit('success')` 通知父组件刷新表格。消息提示使用 `useMessage()`,确认对话框使用 `useDialog()`。