# Adapter > 文档路径:`apps/wisdom-legacy/docs/adapter.md`(勿放在 `src/` 下,避免 Vite 解析 `.md` 中的 import / SFC 语法) `adapter` 是 wisdom-legacy 对 Vben 表单、VXE 表格、弹窗/抽屉的统一封装层。业务页面通常按 **Grid 列表 + EditShell 编辑** 组合开发。 ``` views/xxx/ ├── xxx.data.ts # defineGrid + defineEditShell(声明式配置) ├── XxxList.vue # useGridPage(列表页) └── modules/XxxEdit.vue # useEditShell(编辑表单) ``` ## 目录 | 目录 | 职责 | | --- | --- | | `form/` | Vben 表单封装:`useVbenForm`、校验规则、通用 schema 字段 | | `vxe-table/` | 表格页封装:`defineGrid`、`useGridPage`、列助手、单元格渲染器、数据代理 | | `shell/` | Modal / Drawer 壳层:`useShell`、`defineEditShell`、`useEditShell` | | `antd/` | Ant Design Vue 组件类型映射 | | `setup.ts` | 应用启动时注册 form、vxe-table、antd 适配 | --- ## 初始化 在应用入口调用 `adapter/setup.ts`,依次完成 antd 组件注册、表单规则注册、VXE 表格与渲染器注册。 ```ts import initAdapter from '#/adapter/setup'; await initAdapter(app); ``` --- ## Form 表单 ### 导出 API ```ts import { useVbenForm, // 创建表单组件与 api defineStatusField, // 启用/禁用状态字段 defineFormSchema, // 按 model 回填 defaultValue 的动态 schema z, // Zod 校验(re-export) } from '#/adapter/form'; import type { VbenFormProps, VbenFormSchema } from '#/adapter/form'; ``` ### useVbenForm 底层基于 `@vben/common-ui` 的 `useVbenForm`,并绑定项目内 Ant Design 组件类型。 ```ts const [Form, formApi] = useVbenForm({ layout: 'vertical', showDefaultActions: false, schema: [ { component: 'Input', fieldName: 'name', label: '名称', rules: 'required', // 内置规则 }, { component: 'Input', fieldName: 'phone', label: '手机号', rules: z.string().regex(/^\d{11}$/), // Zod schema }, ], async handleSubmit(values) { // 提交逻辑 }, }); ``` 模板中渲染 `
`,通过 `formApi` 操作表单: | 方法 | 说明 | | ---------------------------------------- | ----------------- | | `setValues(values, merge?, validate?)` | 回填表单 | | `getValues()` | 获取当前值 | | `validate()` / `validateAndSubmitForm()` | 校验 / 校验并提交 | | `resetForm()` | 重置 | | `setState({ schema })` | 动态更新 schema | ### Schema 字段 每个 schema 项对应一个表单项,常用属性: | 属性 | 说明 | | --- | --- | | `component` | 组件名,如 `Input`、`Select`、`ApiTreeSelect` | | `fieldName` | 字段路径,支持 `department.id` 点语法 | | `label` | 标签 | | `rules` | `'required'`、`'selectRequired'`、Zod schema、或规则数组 | | `defaultValue` | 默认值 | | `hide` / `disabled` | 隐藏 / 禁用 | | `dependencies` | 联动:`if`、`rules`、`triggerFields` | | `componentProps` | 传给底层组件的 props | | `formItemClass` / `wrapperClass` | 布局 class | | `renderComponentContent` | 自定义组件插槽内容(如 Checkbox 文案) | **条件显示示例:** ```ts { component: 'InputPassword', fieldName: 'password', label: '密码', dependencies: { if: (values) => !values.id, // 仅新增时显示 triggerFields: ['id'], }, } ``` **自定义插槽:** 在 `` 内用 `#fieldName` 覆盖默认渲染(见下方 EditShell 示例)。 ### 内置规则 `form/setup.ts` 注册了项目级规则: - `required` — 非空字符串 - `selectRequired` — 选择类非空 配合 `z`(Zod)可直接使用 API schema 字段: ```ts import { UserVOSchema } from '#/api/system'; rules: UserVOSchema.shape.name, ``` ### defineStatusField 生成统一的启用/禁用单选字段: ```ts defineStatusField(); // 默认 true defineStatusField(false); // 默认 false ``` ### defineFormSchema 将静态 schema 包装为函数,按打开时的 `model` 为已有字段补充 `defaultValue`: ```ts const schema = defineFormSchema([ { component: 'Input', fieldName: 'name', label: '姓名' }, ]); // useEditShell / useVbenForm 中:schema(model) 按行数据回填默认值 ``` ### 独立表单(非列表编辑) 不经过 `useEditShell` 时,可直接 `useVbenForm` 自建页面或弹窗: ```ts const [Form, formApi] = useVbenForm({ schema: [...], handleSubmit: async (values) => { /* ... */ }, }); ``` --- ## Grid 表格页 ### 导出 API ```ts import { defineGrid, useGridPage, editModal, editDrawer, editShell, defineTagRender, } from '#/adapter/vxe-table'; ``` ### defineGrid — 声明列表配置 在 `*.data.ts` 中集中定义查询字段、列、数据源: ```ts import { defineGrid } from '#/adapter/vxe-table'; import { listDepartmentMethod } from '#/api/system'; export const departmentGrid = defineGrid({ scope: 'system.department', // 解析 tableTitle、createText query: listDepartmentMethod, // alova 分页查询 Method 工厂 tree: true, // 树表(或传入 TreePreset 对象) fields: [ // 搜索区 schema(复用 form schema 结构) { component: 'Input', fieldName: 'name', label: '名称' }, ], columns: (col) => [ // 列定义,col 为列助手 col.seq({ width: 140, treeNode: true }), { field: 'name', title: '名称', minWidth: 120 }, ...col.audit(), // 审计字段列组 col.status({ confirm: true }), // 状态开关列 col.actions(['edit', 'delete'], 180), ], }); ``` #### GridDefinition 主要字段 | 字段 | 说明 | | --- | --- | | `scope` | i18n 前缀,生成「xxx列表」「新增 xxx」文案 | | `query` | `(page, size, query?) => Method`,返回 `{ items, total }` | | `fields` | 搜索表单 schema,静态数组或 `(model) => schema[]` | | `columns` | 列配置,静态数组或 `(col) => columns[]` | | `tree` | `true` 或 `{ parentField, indent, trigger }` | | `checkbox` | 多选列配置 | | `pager` | 分页,`false` 关闭 | | `toolbar` | 工具栏配置 | | `form` | 搜索区 VbenForm 额外选项 | | `features` | `{ selection, batch, treeExpand }` | | `events` | VXE grid 事件监听 | | `options.overrides` | 透传 `gridOptions` 覆盖项 | #### 列助手 `col` 在 `columns: (col) => [...]` 回调中使用: | 方法 | 说明 | | --- | --- | | `col.seq(options?)` | 序号列 | | `col.checkbox(options?)` | 多选列 | | `col.status({ confirm? })` | 状态 Switch 列,触发 `status` handler | | `col.audit(visible?)` | 创建/更新审计列组 | | `col.action(code, options?)` | 单列操作按钮 | | `col.actions(codes, width?)` | 操作列,`'edit'` / `'delete'` 或 `{ code, text }` | 自定义单元格渲染: ```ts import { defineTagRender } from '#/adapter/vxe-table'; cellRender: defineTagRender([ { label: '目录', value: 'catalog', color: 'blue' }, { label: '菜单', value: 'menu', color: 'green' }, ]), ``` 列 `slots: { default: 'title' }` 时,在 `` 内用同名插槽渲染。 ### useGridPage — 组装列表页 ```ts // XxxList.vue — script setup import { editModal, useGridPage } from '#/adapter/vxe-table'; import { deleteDepartmentMethod, editDepartmentStatusMethod, } from '#/api/system'; import { departmentGrid } from './department.data'; import DepartmentEdit from './modules/DepartmentEdit.vue'; const { Grid, Edit, scope, actions, tree } = useGridPage(departmentGrid, { edit: editModal(DepartmentEdit), append: true, delete: deleteDepartmentMethod, status: editDepartmentStatusMethod, }); ``` ```html ``` #### 返回值 | 属性 | 说明 | | --------------------- | -------------------------------------- | | `Grid` | 表格组件(含搜索区) | | `Edit` | 编辑壳层组件(`edit` handler 存在时) | | `scope` | `{ key, tableTitle, createText }` | | `actions` | 页面动作 API | | `grid` | VXE 底层 api(`query`、`setState` 等) | | `tree` | 树表展开/收起 `{ toggle, loading }` | | `selection` / `batch` | 多选与批量操作(开启 checkbox 时) | #### actions API | 方法 | 说明 | | ------------------------ | ---------------------------------------------- | | `actions.create()` | 打开新增编辑(`edit` handler,`status: true`) | | `actions.edit(row?)` | 打开编辑 | | `actions.append(row)` | 新增下级(`append: true` 时) | | `actions.run(code, row)` | 触发行操作(与列 `code` 对应) | | `actions.has(code)` | 是否注册了该 handler | #### PageHandlers — 第二参数 handler 的 key 与列操作 `code`、内置能力对应: | Handler | 类型 | 说明 | | --- | --- | --- | | `edit` | `Component` / `editModal(comp)` / `editDrawer(comp)` / `RowHandler` | 编辑弹窗/抽屉 | | `delete` | `MethodInput` | 删除,`(row) => deleteMethod(row)` | | `status` | `MethodInput` | 状态切换 | | `append` | `true` / `RowHandler` | 树表新增下级 | | `batch` | `BatchHandlers` | 批量操作 | | `events` | `GridEvents` | 表格事件 | | 自定义 code | `RowHandler` / `MethodInput` | 与 `col.action('code')` 对应 | **MethodInput:** 直接传 alova Method 工厂,框架自动调用并处理刷新: ```ts delete: deleteUserMethod, // (row) => Method ``` **RowHandler:** 完全自定义,返回刷新指令: ```ts rest: ({ row }) => { passwordApi.setData(row).open(); return undefined; // 不刷新 }, ``` **HandlerResult — 控制列表刷新:** | 返回值 | 行为 | | --------------------------- | ----------------------- | | `true` | `grid.query()` 全量刷新 | | `false` / `undefined` | 不刷新 | | `{ reload: true }` | 同 `true` | | `{ reload: { id: 'xxx' } }` | 带参数局部刷新 | 编辑壳层关闭时,若 close 载荷含 `id != null`,`edit` handler 自动返回 `true` 触发刷新。 #### 编辑壳层配置 ```ts // 简写:默认 modal edit: DepartmentEdit, // 推荐:显式壳层类型 edit: editModal(DepartmentEdit), edit: editDrawer(RoleEdit), // 完整配置 edit: editShell({ type: 'drawer', connectedComponent: MenuEdit, defaultRow: { status: true }, class: 'w-full max-w-200', }), ``` 标题由编辑组件内 `defineEditShell` 的 `scope` / `title` 在打开时自动设置,列表侧无需重复传 `title`。 --- ## Shell 编辑(Form + Grid 联动) 编辑流程将 **Form schema** 与 **Modal/Drawer 壳层**、**alova 提交** 串联为 `defineEditShell` + `useEditShell`。 ### defineEditShell — 声明编辑配置 与 `defineGrid` 对称,放在 `*.data.ts`: ```ts import { defineEditShell } from '#/adapter/shell/edit'; import { defineStatusField } from '#/adapter/form'; export const departmentForm = defineEditShell({ scope: 'system.department', submit: editDepartmentMethod, shell: 'modal', prepare(data) { if (data.pid === '0') delete data.pid; return data; }, load: getRoleMenuMethod, shouldLoad: (data) => !!data.id, form: { wrapperClass: 'grid-cols-2' }, schema: [ { component: 'Input', fieldName: 'name', label: '名称', rules: 'required' }, defineStatusField(), ], }); ``` #### 常用配置 | 字段 | 说明 | | ------------- | --------------------------------------------- | | `submit` | alova 提交 Method 工厂 | | `shell` | 壳层类型与选项(`load`/`confirm` 由框架托管) | | `schema` | 表单字段,静态数组或 `(model) => schema[]` | | `prepare` | 打开时整理行数据 | | `load` | 详情加载 Method | | `shouldLoad` | 是否执行 load,默认有 `id` 时加载 | | `shouldTitle` | 是否更新标题,默认 `true` | | `transform` | 提交成功后 close 载荷变换,默认补齐刷新 `id` | | `config` | alova `useForm` 额外配置 | **transform 与列表刷新:** 后端编辑接口常返回 `null`。默认 `transform` 会补充占位 `id`,使 grid 在关闭后触发 `query()`。自定义: ```ts transform: (data) => ({ id: data }), transform: false, ``` ### useEditShell — 编辑组件 ```ts // modules/DepartmentEdit.vue — script setup import { useEditShell } from '#/adapter/shell'; import { departmentForm } from '../department.data'; const { Form, Shell, api } = useEditShell(departmentForm); ``` ```html ``` #### 与 Grid 的双层壳层 列表页 `useGridPage` 外层:`useShell(type, { connectedComponent: XxxEdit })` 编辑组件内层:`useEditShell` → `useShell`(无 `connectedComponent`) Vben 通过 `provide/inject` 将内外层合并为**同一个** Modal/Drawer。子组件的 `Shell` 只负责挂载表单与 footer 插槽,**不要在模板传 `title` / `class`**(由 `defineEditShell` 与 `load` 时 `setState` 管理)。 #### api 方法 | 方法 | 说明 | | --------------------- | --------------------------- | | `api.open(data?)` | 独立使用时打开并注入数据 | | `api.submit()` | 校验并提交 | | `api.reset()` | 重置表单并同步 alova 上下文 | | `api.close(payload?)` | 关闭壳层 | | `api.form` | Vben 表单 api | | `api.shell` | 壳层 api | #### 生命周期 handlers ```ts useEditShell(definition, { onOpened(model) {}, handleLoad(model) {}, onLoaded(model) {}, handleSubmit(value) {}, onSuccess(data) {}, onClosed() {}, }); ``` #### 自定义 schema 插槽 ```html ``` ### 独立 Shell(非 Grid 编辑) 参考用户列表的「重置密码」: ```ts import { useShell } from '#/adapter/shell'; const [Password, passwordApi] = useShell('modal', { title: '重置密码', connectedComponent: UserPassword, }); ``` --- ## 标准 CRUD 页面清单 1. **API 层** — alova Method 工厂:`listXxxMethod`、`editXxxMethod`、`deleteXxxMethod` 2. **`xxx.data.ts`** — `defineGrid` + `defineEditShell`,共用类型与 schema 规则 3. **`modules/XxxEdit.vue`** — `useEditShell(xxxForm)`,按需加插槽与 footer 4. **`XxxList.vue`** — `useGridPage(xxxGrid, { edit, delete, status, ... })`,渲染 `` + `` ### 文件对照(系统模块) | 列表定义 | 编辑定义 | 列表页 | 编辑组件 | | --- | --- | --- | --- | | `departmentGrid` | `departmentForm` | `DepartmentList.vue` | `DepartmentEdit.vue` | | `userGrid` | `userForm` | `UserList.vue` | `UserEdit.vue` | | `roleGrid` | `roleForm` | `RoleList.vue` | `RoleEdit.vue` | | `menuGrid` | `menuForm` | `MenuList.vue` | `MenuEdit.vue` | --- ## 数据流 ``` 用户点击「编辑」 → useGridPage.openEditor(row) → 外层 Shell.open(row) → 内层 useEditShell.load → prepare(row) → [可选] load API 合并详情 → setValues + 更新标题 → 用户确认 → validateAndSubmitForm → alova send → transform(响应) → close(载荷) → grid 检测 id → query() 刷新 ``` --- ## 进阶 ### 树表 `defineGrid({ tree: true })` + `col.seq({ treeNode: true })` + handler `append: true`。 ### 自定义列插槽 `columns` 中声明 `slots: { default: 'title' }`,`` 渲染。 ### 渲染器扩展 `vxe-table/renderer/` 提供 `defineCellRenderer`、`defineOperationRender` 等,在 `vxe-table/setup.ts` 注册。业务列通过 `defineTagRender`、`defineSwitchRender` 使用。 ### 代理层 底层查询走 `defineGridProxy`(`useGrid` 内部使用),将 alova Method 绑定到 VXE `proxyConfig.ajax`。一般业务只需在 `defineGrid` 上传入 `query` Method 工厂。 --- ## 导入路径速查 ```ts // 表单 import { useVbenForm, defineStatusField, z } from '#/adapter/form'; // 表格页 import { defineGrid, useGridPage, editModal, editDrawer, } from '#/adapter/vxe-table'; // 编辑壳层 import { useEditShell } from '#/adapter/shell'; import { defineEditShell } from '#/adapter/shell/edit'; ```