--- description: nl-admin-view Vben 5.7 官方组件用法规范(Page/Form/Modal/Drawer/VxeTable 等) globs: apps/web-antd/**/*.{vue,ts} alwaysApply: false --- # Vben 组件规则(对齐 5.7.0 官方文档 + 本仓库约定) 当前版本:**Vben Admin 5.7.0**。文档来源:https://doc.vben.pro/components/ 业务页统一在 `apps/web-antd`;表单/表格必须走本仓库适配器,不要直接依赖底层实现细节。 `lock` / `unlock`、Promise 版 `onBeforeClose`、`animationType`、`center-footer` 等在 5.7 已是基线能力,规则里不再标注「>5.5.x」版本门槛。 ## 选型速查 | 场景 | 用什么 | 不要用 | |---|---|---| | 页面外壳 | `Page`(`@vben/common-ui`) | 自写页面头/底容器 | | 列表+搜索 | `useVbenVxeGrid`(`#/adapter/vxe-table`) | 直接 `new VxeGrid` / antd `Table` | | 表单 | `useVbenForm`(`#/adapter/form`) | antd `Form` / 手写校验 | | 复杂弹窗(表单/多步) | `useVbenModal` | antd `Modal` | | 侧滑面板 | `useVbenDrawer` | antd `Drawer` | | 轻量确认/提示/单输入 | `alert` / `confirm` / `prompt` | 为简单确认再开 Modal | | 表格行/工具栏按钮 | `#/components/table-action` 的 `TableAction` | 散落一排 `Button` | | 远程下拉/树选 | 适配器里的 `ApiSelect` / `ApiTreeSelect`(底层 `ApiComponent`) | 页面里手写 options 拉取逻辑 | | 超长文本 | `EllipsisText` | CSS 省略后无 Tooltip | | 只读详情字段组 | `VbenDescriptions` | 手写 label-value 网格 | | 头像/固定比例裁剪 | `VCropper` | 无裁剪直接上传原图(需要裁剪时) | | 富文本 | `VbenTiptap`(`@vben/plugins/tiptap`) | 再引一套编辑器 | --- ## Page - 业务页根节点用 ``;列表页常见:`` - `title` / `description` / `#extra` 都没有内容时,头部不渲染 - `autoContentHeight`:内容区按可视高度自动扣减头/底;额外偏移用 `heightOffset`(px) - `appendToMain` 的 Modal/Drawer 挂到内容区时,**Page 必须开 `auto-content-height`**,否则高度计算不对 - 插槽:`default` / `title` / `description` / `extra` / `footer` --- ## Modal(`useVbenModal`) ```ts const [Modal, modalApi] = useVbenModal({ connectedComponent: ExtraModal, // 复杂内容抽离子组件 draggable: true, // overflow: true, // 需要拖出可视区时再开 }); ``` 强制约定: - 复杂弹窗用 `connectedComponent`;内外共享 `modalApi` - **严禁覆盖 `#footer`**(会丢掉默认取消/确定与 loading) - 扩展底部只用:`prepend-footer` / `center-footer` / `append-footer` - 提交防重复:优先 `modalApi.lock()` / `unlock()`(`unlock` = `lock(false)`;锁定时确认按钮 loading、禁关弹窗);或 `setState({ confirmLoading: true })` - 回填:`modalApi.setData(data).open()`;内部 `onOpenChange` 里 `modalApi.getData()` - 参数优先级:`slot` > `props` > `state`;`connectedComponent` 时同名事件**以内侧为准**(`onOpenChange` 内外都会触发) - 默认属性可在 `apps/web-antd/src/bootstrap.ts` 的 `setDefaultModalProps` 统一改 - 动画:`animationType: 'slide' | 'scale'`(默认 slide) - `destroyOnClose`:关闭后销毁内部 Modal 及子组件(配合 `connectedComponent` 时同样生效) 常用 API:`open` / `close` / `setState` / `setData` / `getData` / `lock` / `unlock` / `useStore` --- ## Drawer(`useVbenDrawer`) - API 形态与 Modal 基本一致:`connectedComponent`、`setData/getData`、`lock/unlock`、footer 三插槽 - **同样严禁覆盖 `#footer`** - `placement`: `left | right | top | bottom`(默认 `right`) - 关闭前校验用 `onBeforeClose`(支持 Promise / 返回 `false`);需要重置内部状态可开 `destroyOnClose` - 挂内容区同样要配合 Page 的 `auto-content-height` - 默认属性:`setDefaultDrawerProps` --- ## Alert(轻量提示) 来自 `@vben/common-ui`:`alert` / `confirm` / `prompt` / `useAlertContext` - **简单确认、提示、单字段输入**用这套;复杂表单/多控件仍用 Modal - `confirm().then/catch`;异步关闭用 `beforeClose`,返回 `false` 可阻止关闭 - `prompt` 可传 `component` + `componentProps`(antd Select 需 `popupClassName: 'pointer-events-auto'`) - 自定义内容里需要主动确认/取消:`useAlertContext().doConfirm/doCancel`(仅 setup/函数组件) - 动态创建的 Alert **不支持 HMR**,改代码后需关掉重开 --- ## Form(`useVbenForm` from `#/adapter/form`) ```ts const [Form, formApi] = useVbenForm({ schema: [ { fieldName: 'nick_name', // 与后端 snake_case 对齐 label: '昵称', component: 'Input', rules: 'required', // 选择类用 selectRequired componentProps: { placeholder: '请输入' }, }, ], }); ``` 强制约定: - 只从 `#/adapter/form` 引入,不直接玩 TanStack Form 实例 - 动态改字段:`formApi.updateSchema([...])`;**没有** `setComponentProps` - 重置:`formApi.resetForm()`;**禁止** `resetFields()`(antd API,会报错) - 取值/赋值:`getValues()` / `setValues()`;需要提交编解码时用 `codec` / `getRawValues()` - 校验:`formApi.validate().then(e => { if (e.valid) ... })` 或 `validateAndSubmitForm()` - 字段自定义 slot:控件绑定用 `v-bind="slotProps.componentProps"`(不要再 `v-bind="slotProps"`) - 联动优先 `dependencies.resolve(context)` 一次返回 `show/disabled/rules/componentProps...` - 远程 options:打开时拉取后 `updateSchema` 注入;或用 `ApiSelect`/`ApiTreeSelect` - 密码字段用 `InputPassword`(适配器组件名),不要 `Input` + `type="password"` --- ## Vxe Table(`useVbenVxeGrid` from `#/adapter/vxe-table`) ```ts const [Grid, gridApi] = useVbenVxeGrid({ formOptions, // 搜索表单(Vben Form) gridOptions: { proxyConfig: { ajax: { query: async ({ page }, formValues) => { // 返回适配器约定结构 return { items: [], total: 0 }; }, }, }, toolbarConfig: { search: true }, }, gridEvents, }); ``` 强制约定: - 远程列表走 `proxyConfig.ajax.query`,禁止自己 fetch 再赋 `data` - 响应结构对齐适配器:`{ items, total }`(见 `apps/web-antd/src/adapter/vxe-table.ts`) - 分页从 `{ page }` 取 `currentPage` / `pageSize` - 刷新:`gridApi.query()` 保留页;`gridApi.reload()` 回第一页 - loading:`gridApi.setLoading(bool)` - 工具栏插槽:`toolbar-actions`(左)/ `toolbar-tools`(右) - 操作列:列上 `slots: { default: 'action' }`,页面 `