docs: document typed popup data contracts

This commit is contained in:
Dream
2026-08-05 09:54:01 +08:00
parent f16c780c2b
commit d0c8c887fd
6 changed files with 120 additions and 14 deletions

View File

@@ -50,6 +50,29 @@ Drawer 内的内容一般业务中,会比较复杂,所以我们可以将 dra
<DemoPreview dir="demos/vben-drawer/shared-data" />
### 数据类型约束
推荐在 connected 子组件中声明一次数据类型并暴露 `drawerApi`,外部会从 `connectedComponent` 自动推导 `setData``getData` 的类型:
```ts
// connected 子组件
const [Drawer, drawerApi] = useVbenDrawer<EditData>();
defineExpose({ drawerApi });
// 外部组件,无需重复声明 EditData
const [Drawer, drawerApi] = useVbenDrawer({
connectedComponent: EditDrawer,
});
```
无法从组件公开实例推导时,可以显式使用 `useVbenDrawer<EditData>()`。需要让多个文件共享同一契约时,可以在独立模块中预绑定:
```ts
export const useEditDrawer = createVbenDrawer<EditData>();
```
三种方式的优先级为显式泛型、connected component 自动推导、`unknown`。普通 SFC 通过 `defineExpose` 支持自动推导;泛型 SFC、函数式组件或被标注为宽 `Component` 的组件应使用显式泛型或契约工厂。`getData()` 在尚未调用 `setData()` 时返回 `undefined`,业务允许 `null`、部分对象等值时,需要在数据泛型中准确声明。
::: info 注意
- `VbenDrawer` 组件对于参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenDrawer参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
@@ -143,8 +166,8 @@ const [Drawer, drawerApi] = useVbenDrawer({
| setState | 动态设置抽屉状态属性 | `(((prev: DrawerState) => Partial<DrawerState>)\| Partial<DrawerState>)=>drawerApi` |
| open | 打开弹窗 | `()=>void` | --- |
| close | 关闭弹窗 | `()=>void` | --- |
| setData | 设置共享数据 | `<T>(data:T)=>drawerApi` | --- |
| getData | 获取共享数据 | `<T>()=>T` | --- |
| setData | 设置共享数据 | `(data:TData)=>drawerApi` | --- |
| getData | 获取共享数据 | `()=>TData\|undefined` | --- |
| useStore | 获取可响应式状态 | - | --- |
| lock | 将抽屉标记为提交中,锁定当前状态 | `(isLock:boolean)=>drawerApi` | >5.5.3 |
| unlock | lock方法的反操作解除抽屉的锁定状态也是lock(false)的别名 | `()=>drawerApi` | >5.5.3 |

View File

@@ -56,6 +56,29 @@ Modal 内的内容一般业务中,会比较复杂,所以我们可以将 moda
<DemoPreview dir="demos/vben-modal/shared-data" />
### 数据类型约束
推荐在 connected 子组件中声明一次数据类型并暴露 `modalApi`,外部会从 `connectedComponent` 自动推导 `setData``getData` 的类型:
```ts
// connected 子组件
const [Modal, modalApi] = useVbenModal<EditData>();
defineExpose({ modalApi });
// 外部组件,无需重复声明 EditData
const [Modal, modalApi] = useVbenModal({
connectedComponent: EditModal,
});
```
无法从组件公开实例推导时,可以显式使用 `useVbenModal<EditData>()`。需要让多个文件共享同一契约时,可以在独立模块中预绑定:
```ts
export const useEditModal = createVbenModal<EditData>();
```
三种方式的优先级为显式泛型、connected component 自动推导、`unknown`。普通 SFC 通过 `defineExpose` 支持自动推导;泛型 SFC、函数式组件或被标注为宽 `Component` 的组件应使用显式泛型或契约工厂。`getData()` 在尚未调用 `setData()` 时返回 `undefined`,业务允许 `null`、部分对象等值时,需要在数据泛型中准确声明。
## 动画类型
通过 `animationType` 属性可以控制弹窗的动画效果:
@@ -162,8 +185,8 @@ const [Modal, modalApi] = useVbenModal({
| setState | 动态设置弹窗状态属性 | `(((prev: ModalState) => Partial<ModalState>)\| Partial<ModalState>)=>modalApi` | - |
| open | 打开弹窗 | `()=>void` | - |
| close | 关闭弹窗 | `()=>void` | - |
| setData | 设置共享数据 | `<T>(data:T)=>modalApi` | - |
| getData | 获取共享数据 | `<T>()=>T` | - |
| setData | 设置共享数据 | `(data:TData)=>modalApi` | - |
| getData | 获取共享数据 | `()=>TData\|undefined` | - |
| useStore | 获取可响应式状态 | - | - |
| lock | 将弹窗标记为提交中,锁定当前状态 | `(isLock:boolean)=>modalApi` | >5.5.2 |
| unlock | lock方法的反操作解除弹窗的锁定状态也是lock(false)的别名 | `()=>modalApi` | >5.5.3 |

View File

@@ -3,9 +3,14 @@ import { ref } from 'vue';
import { useVbenDrawer } from '@vben/common-ui';
const data = ref();
interface SharedData {
content: string;
payload: string;
}
const [Drawer, drawerApi] = useVbenDrawer({
const data = ref<SharedData>();
const [Drawer, drawerApi] = useVbenDrawer<SharedData>({
onCancel() {
drawerApi.close();
},
@@ -14,10 +19,12 @@ const [Drawer, drawerApi] = useVbenDrawer({
},
onOpenChange(isOpen: boolean) {
if (isOpen) {
data.value = drawerApi.getData<Record<string, any>>();
data.value = drawerApi.getData();
}
},
});
defineExpose({ drawerApi });
</script>
<template>
<Drawer title="数据共享示例">

View File

@@ -3,9 +3,14 @@ import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
const data = ref();
interface SharedData {
content: string;
payload: string;
}
const [Modal, modalApi] = useVbenModal({
const data = ref<SharedData>();
const [Modal, modalApi] = useVbenModal<SharedData>({
onCancel() {
modalApi.close();
},
@@ -14,10 +19,12 @@ const [Modal, modalApi] = useVbenModal({
},
onOpenChange(isOpen: boolean) {
if (isOpen) {
data.value = modalApi.getData<Record<string, any>>();
data.value = modalApi.getData();
}
},
});
defineExpose({ modalApi });
</script>
<template>
<Modal title="数据共享示例">

View File

@@ -23,6 +23,29 @@ const [Drawer, drawerApi] = useVbenDrawer({
- Default drawer behavior can be adjusted in `apps/<app>/src/bootstrap.ts` through `setDefaultDrawerProps(...)`.
- `setState(...)` works on `DrawerState`, not `ModalState`.
## Shared Data Types
The recommended approach is to declare the data type once in the connected component and expose `drawerApi`. The outer call then infers the data contract from `connectedComponent`:
```ts
// Connected component
const [Drawer, drawerApi] = useVbenDrawer<EditData>();
defineExpose({ drawerApi });
// Outer component, EditData is inferred
const [Drawer, drawerApi] = useVbenDrawer({
connectedComponent: EditDrawer,
});
```
Use `useVbenDrawer<EditData>()` explicitly when the component type cannot expose the contract. For larger features, pre-bind one reusable contract in a separate module:
```ts
export const useEditDrawer = createVbenDrawer<EditData>();
```
The precedence is explicit generic, connected component inference, then `unknown`. Plain SFCs support inference through `defineExpose`; generic SFCs, functional components, and components widened to `Component` should use an explicit generic or contract factory. `getData()` returns `undefined` before `setData()` is called. Include `null` or partial payloads in the data type when they are valid business values.
## Key Props
| Prop | Description | Type |
@@ -50,7 +73,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
| `setState(...)` | updates drawer state |
| `open()` | opens the drawer |
| `close()` | closes the drawer |
| `setData(data)` | stores shared data |
| `getData<T>()` | reads shared data |
| `setData(data: TData)` | stores typed shared data |
| `getData()` | returns `TData \| undefined` |
| `lock(isLocked = true)` | locks the drawer into submitting state |
| `unlock()` | alias for `lock(false)` |

View File

@@ -23,6 +23,29 @@ const [Modal, modalApi] = useVbenModal({
- When `connectedComponent` is present, avoid pushing extra modal props through the connected side. Prefer `useVbenModal(...)` or `modalApi.setState(...)`.
- Default modal behavior can be adjusted in `apps/<app>/src/bootstrap.ts` through `setDefaultModalProps(...)`.
## Shared Data Types
The recommended approach is to declare the data type once in the connected component and expose `modalApi`. The outer call then infers the data contract from `connectedComponent`:
```ts
// Connected component
const [Modal, modalApi] = useVbenModal<EditData>();
defineExpose({ modalApi });
// Outer component, EditData is inferred
const [Modal, modalApi] = useVbenModal({
connectedComponent: EditModal,
});
```
Use `useVbenModal<EditData>()` explicitly when the component type cannot expose the contract. For larger features, pre-bind one reusable contract in a separate module:
```ts
export const useEditModal = createVbenModal<EditData>();
```
The precedence is explicit generic, connected component inference, then `unknown`. Plain SFCs support inference through `defineExpose`; generic SFCs, functional components, and components widened to `Component` should use an explicit generic or contract factory. `getData()` returns `undefined` before `setData()` is called. Include `null` or partial payloads in the data type when they are valid business values.
## Key Props
| Prop | Description | Type |
@@ -50,7 +73,7 @@ const [Modal, modalApi] = useVbenModal({
| `setState(...)` | updates modal state |
| `open()` | opens the modal |
| `close()` | closes the modal |
| `setData(data)` | stores shared data |
| `getData<T>()` | reads shared data |
| `setData(data: TData)` | stores typed shared data |
| `getData()` | returns `TData \| undefined` |
| `lock(isLocked = true)` | locks the modal into submitting state |
| `unlock()` | alias for `lock(false)` |