文档路径:
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 表格与渲染器注册。
import initAdapter from '#/adapter/setup';
await initAdapter(app);
import {
useVbenForm, // 创建表单组件与 api
defineStatusField, // 启用/禁用状态字段
defineFormSchema, // 按 model 回填 defaultValue 的动态 schema
z, // Zod 校验(re-export)
} from '#/adapter/form';
import type { VbenFormProps, VbenFormSchema } from '#/adapter/form';
底层基于 @vben/common-ui 的 useVbenForm,并绑定项目内 Ant Design 组件类型。
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) {
// 提交逻辑
},
});
模板中渲染 <Form />,通过 formApi 操作表单:
| 方法 | 说明 |
|---|---|
setValues(values, merge?, validate?) |
回填表单 |
getValues() |
获取当前值 |
validate() / validateAndSubmitForm() |
校验 / 校验并提交 |
resetForm() |
重置 |
setState({ 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 文案) |
条件显示示例:
{
component: 'InputPassword',
fieldName: 'password',
label: '密码',
dependencies: {
if: (values) => !values.id, // 仅新增时显示
triggerFields: ['id'],
},
}
自定义插槽: 在 <Form> 内用 #fieldName 覆盖默认渲染(见下方 EditShell 示例)。
form/setup.ts 注册了项目级规则:
required — 非空字符串selectRequired — 选择类非空配合 z(Zod)可直接使用 API schema 字段:
import { UserVOSchema } from '#/api/system';
rules: UserVOSchema.shape.name,
生成统一的启用/禁用单选字段:
defineStatusField(); // 默认 true
defineStatusField(false); // 默认 false
将静态 schema 包装为函数,按打开时的 model 为已有字段补充 defaultValue:
const schema = defineFormSchema<UserVO>([
{ component: 'Input', fieldName: 'name', label: '姓名' },
]);
// useEditShell / useVbenForm 中:schema(model) 按行数据回填默认值
不经过 useEditShell 时,可直接 useVbenForm 自建页面或弹窗:
const [Form, formApi] = useVbenForm({
schema: [...],
handleSubmit: async (values) => { /* ... */ },
});
import {
defineGrid,
useGridPage,
editModal,
editDrawer,
editShell,
defineTagRender,
} from '#/adapter/vxe-table';
在 *.data.ts 中集中定义查询字段、列、数据源:
import { defineGrid } from '#/adapter/vxe-table';
import { listDepartmentMethod } from '#/api/system';
export const departmentGrid = defineGrid<DepartmentVO>({
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),
],
});
| 字段 | 说明 |
|---|---|
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 } |
自定义单元格渲染:
import { defineTagRender } from '#/adapter/vxe-table';
cellRender: defineTagRender([
{ label: '目录', value: 'catalog', color: 'blue' },
{ label: '菜单', value: 'menu', color: 'green' },
]),
列 slots: { default: 'title' } 时,在 <Grid> 内用同名插槽渲染。
// 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,
});
<!-- XxxList.vue — template -->
<Edit />
<Grid>
<template #toolbar-tools>
<a-button type="primary" @click="actions.create()">
{{ scope.createText }}
</a-button>
</template>
</Grid>
| 属性 | 说明 |
|---|---|
Grid |
表格组件(含搜索区) |
Edit |
编辑壳层组件(edit handler 存在时) |
scope |
{ key, tableTitle, createText } |
actions |
页面动作 API |
grid |
VXE 底层 api(query、setState 等) |
tree |
树表展开/收起 { toggle, loading } |
selection / batch |
多选与批量操作(开启 checkbox 时) |
| 方法 | 说明 |
|---|---|
actions.create() |
打开新增编辑(edit handler,status: true) |
actions.edit(row?) |
打开编辑 |
actions.append(row) |
新增下级(append: true 时) |
actions.run(code, row) |
触发行操作(与列 code 对应) |
actions.has(code) |
是否注册了该 handler |
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 工厂,框架自动调用并处理刷新:
delete: deleteUserMethod, // (row) => Method
RowHandler: 完全自定义,返回刷新指令:
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 触发刷新。
// 简写:默认 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。
编辑流程将 Form schema 与 Modal/Drawer 壳层、alova 提交 串联为 defineEditShell + useEditShell。
与 defineGrid 对称,放在 *.data.ts:
import { defineEditShell } from '#/adapter/shell/edit';
import { defineStatusField } from '#/adapter/form';
export const departmentForm = defineEditShell<DepartmentVO>({
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()。自定义:
transform: (data) => ({ id: data }),
transform: false,
// modules/DepartmentEdit.vue — script setup
import { useEditShell } from '#/adapter/shell';
import { departmentForm } from '../department.data';
const { Form, Shell, api } = useEditShell(departmentForm);
<!-- modules/DepartmentEdit.vue — template -->
<Shell>
<form class="mx-4" />
<template #prepend-footer>
<a-button type="primary" danger @click="api.reset()">重置</a-button>
</template>
</Shell>
列表页 useGridPage 外层:useShell(type, { connectedComponent: XxxEdit })
编辑组件内层:useEditShell → useShell(无 connectedComponent)
Vben 通过 provide/inject 将内外层合并为同一个 Modal/Drawer。子组件的 Shell 只负责挂载表单与 footer 插槽,不要在模板传 title / class(由 defineEditShell 与 load 时 setState 管理)。
| 方法 | 说明 |
|---|---|
api.open(data?) |
独立使用时打开并注入数据 |
api.submit() |
校验并提交 |
api.reset() |
重置表单并同步 alova 上下文 |
api.close(payload?) |
关闭壳层 |
api.form |
Vben 表单 api |
api.shell |
壳层 api |
useEditShell(definition, {
onOpened(model) {},
handleLoad(model) {},
onLoaded(model) {},
handleSubmit(value) {},
onSuccess(data) {},
onClosed() {},
});
<form>
<template #menuIds="slotProps">
<Tree v-bind="slotProps" :tree-data="tree" multiple />
</template>
</form>
参考用户列表的「重置密码」:
import { useShell } from '#/adapter/shell';
const [Password, passwordApi] = useShell('modal', {
title: '重置密码',
connectedComponent: UserPassword,
});
listXxxMethod、editXxxMethod、deleteXxxMethodxxx.data.ts — defineGrid + defineEditShell,共用类型与 schema 规则modules/XxxEdit.vue — useEditShell(xxxForm),按需加插槽与 footerXxxList.vue — useGridPage(xxxGrid, { edit, delete, status, ... }),渲染 <Edit /> + <Grid />| 列表定义 | 编辑定义 | 列表页 | 编辑组件 |
|---|---|---|---|
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' },<Grid #title="{ row }"> 渲染。
vxe-table/renderer/ 提供 defineCellRenderer、defineOperationRender 等,在 vxe-table/setup.ts 注册。业务列通过 defineTagRender、defineSwitchRender 使用。
底层查询走 defineGridProxy(useGrid 内部使用),将 alova Method 绑定到 VXE proxyConfig.ajax。一般业务只需在 defineGrid 上传入 query Method 工厂。
// 表单
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';