# Modal 弹窗
:::tip 说明
项目使用 `basicModal` 组件(`src/components/Modal/src/BasicModal.vue`)配合 `useModal` hook 实现弹窗。编辑弹窗通过 `v-if` 懒加载,减少首屏渲染负担。
:::
## 组件架构
```
src/components/Modal/
├── src/
│ ├── BasicModal.vue # 主组件(基于 NaiveUI n-modal)
│ ├── props.ts # 属性定义
│ ├── type.ts # 类型定义(ModalProps、ModalMethods)
│ └── hooks/
│ └── useModalContext.ts # 弹窗上下文
└── index.ts # 导出 useModal 等
```
## basicModal 组件
`basicModal` 基于 NaiveUI `n-modal` 封装,通过 `useModal` hook 管理弹窗状态,提供全屏切换、自定义头部、自动 OK/Cancel 按钮等能力:
```vue
...
```
```typescript
const [modalRegister, { openModal, setSubLoading }] = useModal({
title: '编辑',
subBtuText: '确定',
width: 600,
});
```
核心功能:
- `useModal` hook 返回 `modalRegister`(绑定到组件 `@register`)和操作方法
- `openModal` 打开弹窗、`setSubLoading` 控制提交按钮 loading
- 自动渲染 OK/Cancel 底部按钮,无需手动编写 footer 模板
- `@on-ok` 监听确定按钮点击,`@on-close` 监听关闭事件
## 实际用法(position 模块)
以岗位管理为例,使用 `basicModal` + `useModal` hook 管理弹窗状态。
### 父组件(index.vue)
```vue
```
父组件通过 `ref` 获取子组件实例,调用 `createModalRef.value.openModal()` 打开弹窗。子组件通过 `defineExpose({ openModal })` 暴露该方法。
### 编辑弹窗(edit.vue)
```vue
正常
停用
```
## 弹窗数据流
```
父组件点击编辑 → positionId = record.id → editVisible = true → nextTick → createModalRef.openModal()
↓
basicModal 显示 → useModal 配置标题和按钮
↓
edit.vue onMounted → positionId != 0 → setFormData() → 获取详情填充表单
↓
用户点击确定 → @on-ok → handleSubmit → formRef.validate() → positionUpdate(formData) → setSubLoading(false)
↓
emit('success') → 父组件 reloadTable() → 表格刷新
```
## 确认弹窗
删除等危险操作使用 NaiveUI 的 `useDialog` 组合式函数:
```typescript
import { useMessage, useDialog } from 'naive-ui';
const message = useMessage();
const dialog = useDialog();
async function handleDelete(record: Recordable) {
let ids = [];
if (!record) {
ids = selectionData.value.map(({ id }) => id);
}
dialog.warning({
title: '提示',
content: '确定要删除?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
record ? await positionDelete(record.id) : await positionBatchDelete(ids);
message.success('删除成功');
reloadTable();
},
});
}
```
`dialog.warning` 返回一个对话框实例,用户点击"确定"后执行 `onPositiveClick` 回调。与旧版 `confirm` 不同,不需要 `await` 和 `try/catch`,逻辑全部写在回调中。
:::tip 温馨提示
弹窗组件通过 `v-if` 控制懒加载,首屏不加载弹窗代码。`useModal` 的 `setSubLoading` 控制确定按钮 loading 状态,防止重复提交。`message.success` 和 `dialog.warning` 来自 NaiveUI 的 `useMessage()` 和 `useDialog()` 组合式函数,需要在 `setup` 中调用获取实例。
:::
## 总结
编辑弹窗使用 `basicModal` + `useModal` hook 管理弹窗状态,通过 `defineExpose({ openModal })` 暴露给父组件调用。`useModal` 返回 `modalRegister` 绑定到 `@register`,以及 `openModal`、`setSubLoading` 等操作方法。表单使用 NaiveUI 的 `n-form` 系列组件,绑定方式为 `v-model:value`,校验字段用 `path` 属性。删除确认使用 `useDialog` 的 `dialog.warning` 回调模式。操作成功后通过 `emit('success')` 通知父组件刷新表格。