# Vben3 CRUD 页面模板生成 Skill > 用于**成堪数智平台**快速生成标准 CRUD 页面。 > 核心产出:`index.vue` + `config.ts` + `drawer.vue` + `api/xxx.ts` 四件套。 --- ## 一、技术栈 | 层级 | 技术 | 层级 | 技术 | |------|------|------|------| | 框架 | Vue 3 Composition API + ` ``` ### 5.4 TreeWrapper模板(左树右表) > 左侧树节点点击 → 右侧表格按`menuId`过滤;新增时预设当前树节点。 **生成步骤**: | # | 文件 | 操作 | 必改处 | |---|------|------|--------| | ① | `comp/treeWrapper.vue` | 直接复制,零修改 | 0 | | ② | `comp/tree.vue` | 复制后替换树API | **1** | | ③ | `config.ts` | 复制后填充表格/搜索/表单 | **3** | | ④ | `drawer.vue` | 复制后替换CRUD API | **1** | | ⑤ | `index.vue` | 复制后替换CRUD API | **1** | > 生成时先将`{module}`替换为模块名(如`user`),`{模块名称}`替换为中文(如`用户`)。 #### 5.4.1 treeWrapper.vue(复制即用) ```vue ``` #### 5.4.2 tree.vue(改1处:树API) ```vue ``` #### 5.4.3 config.ts(改3处:表格列/搜索/表单) ```typescript import { cloneDeep, session } from '@vben/utils'; import { get{Module}Page } from '#/api/{module}'; // ⓐ 共用API let globParams: Record = {}; export const setGlobParams = (params: any) => { globParams = { ...globParams, ...params }; }; export const tableConfig = { options: { tableTitle: '{模块名称}列表', gridOptions: { checkboxConfig: { reserve: true }, columns: [ // ⓑ 替换表格列 // { field: 'name', minWidth: 200, title: '名称' }, // { field: 'isEnable', width: 90, title: '状态', slots: { default: "isEnable" } }, ], proxyConfig: { ajax: { query: async (params: any, formValues: any) => { const res: any = await get{Module}Page({ ...params.page, ...formValues, ...globParams }); if (res.code !== 1) return { items: [], total: 0 }; return { items: cloneDeep(res.result.records), total: +res.result.total || 0 }; }, }, }, }, formOptions: { commonConfig: { labelWidth: 60 }, showCollapseButton: false, collapsed: false, schema: [ // ⓒ 替换搜索字段 // { component: 'Input', fieldName: 'keyword', label: '关键字', componentProps: { clearable: true, placeholder: '请输入' } }, ], wrapperClass: 'grid-cols-1 md:grid-cols-4', submitButtonOptions: { auth: '@ums:{module}:search' }, resetButtonOptions: { auth: '@ums:{module}:reset' }, }, }, formObj: { operateOptions: [ { code: 'edit', text: '编辑', type: 'primary', directives: [{ name: 'auth', value: '@ums:{module}:edit' }] }, { code: 'delete', text: '删除', type: 'danger', directives: [{ name: 'auth', value: '@ums:{module}:delete' }] }, { code: 'menuAuth', text: '权限配置', type: 'primary', directives: [{ name: 'auth', value: '@ums:{module}:auth' }] }, ], }, isView: false, showOperate: true, showSeq: true, buttonAuth: { addBtn: '@ums:{module}:add' }, }; export const drawerConfig = { formConfig: { wrapperClass: 'grid-cols-1', layout: 'horizontal', schemaConfig: [{ schema: [ // ⓓ 替换表单字段 { formItemClass: 'form-item-col3 hidden', fieldName: 'id', label: '', dependencies: { show: false } }, // { component: 'Input', componentProps: { placeholder: '请输入名称', clearable: true }, fieldName: 'name', label: '名称', rules: 'required' }, // { component: 'ApiTreeSelect', componentProps: { labelField: 'name', valueField: 'id', childrenField: 'children', api: () => session.getItem("menuNode"), multiple: true }, fieldName: 'menus', label: '上级菜单', rules: 'required' }, ], }], }, tableConfig: { show: false }, mapConfig: { show: false }, layoutConfig: { column: 1 }, }; // 权限树配置(供drawerAuth使用) const treeConfig = { searchOptions: { show: true, placeholder: '搜索' }, treeOptions: { options: { expandOnClickNode: false, data: [] as any, nodeKey: 'id', checkStrictly: false } } }; export const authTreeOptions = { title: '未授权角色', ...treeConfig, treeOptions: { ...treeConfig.treeOptions, options: { ...treeConfig.treeOptions.options, showCheckbox: true, props: { children: 'children', label: 'name', value: 'id' } } } }; export const authSelectTreeOptions = { title: '已授权角色', ...treeConfig, treeOptions: { ...treeConfig.treeOptions, options: { ...treeConfig.treeOptions.options, showCheckbox: true, props: { children: 'children', label: 'name', value: 'id' } } } }; ``` #### 5.4.4 drawer.vue(改1处:CRUD API) ```vue ``` #### 5.4.5 drawerAuth.vue(可选,角色授权) > 不需要可跳过,同时删除index.vue中的``和config.ts中的`authTreeOptions`/`authSelectTreeOptions`。依赖`#/components/treeTransfer/fileOuth.vue`。 ```vue ``` #### 5.4.6 index.vue(改1处:CRUD API) ```vue ``` > 新增时预设 `menusArr` 为当前树节点;编辑时通过 `get{Module}ById` 回填菜单权限;提交时用 `commWay.getFileRes` 处理文件。**不需要文件上传或菜单关联可删除对应逻辑。** ```vue ``` ### 5.5 drawer.vue 基础版(仅表单) ```vue ``` ### 5.6 drawer.vue 内嵌表格版 ```vue ``` ## 六、表单组件速查 | component | 用途 | 关键componentProps | |-----------|------|--------------------| | `Input` | 文本输入 | `placeholder`, `clearable`, `type: 'textarea'`, `rows`, `maxLength` | | `Select` / `ApiSelect` | 下拉/远程下拉 | `options`/`api`, `labelField`, `valueField`, `multiple` | | `ApiTreeSelect` | 远程树选择 | `api`, `labelField`, `valueField`, `childrenField`, `multiple` | | `RadioGroup` / `ApiRadioGroup` | 单选组 | `options: [{ label, value }]`/`api` | | `DatePicker` | 日期选择 | `format: 'YYYY-MM-DD'`, `valueFormat: 'YYYY-MM-DD HH:mm:ss'` | | `Switch` | 开关 | `activeValue`, `inactiveValue` | ### 校验规则 ```typescript rules: 'required' // 必填 rules: 'selectRequired' // 必选 rules: z.string().email({ message: '请输入正确的电子邮箱' }).nullable().optional() // 邮箱 rules: z.string().refine((value) => regexp.phone.test(String(value)), { message: '请输入正确的联系电话' }).nullable().optional() // 正则 dependencies: { if(values: any) { return values.type === 'temporary' }, triggerFields: ['type'] } // 条件显示 ``` ## 七、配置对象核心字段 ### 7.1 tableConfig ```typescript { options: { tableTitle: '列表标题', gridOptions: { checkboxConfig: { reserve: true }, columns: [], proxyConfig: { ajax: { query: async (params, formValues) => ({ items, total }) } } }, formOptions: { schema: [], wrapperClass: 'grid-cols-1 md:grid-cols-4' } }, formObj: { operateOptions: [{ code, text, type, directives: [{ name: 'auth', value }] }] }, isView: false, showOperate: true, showSeq: true, buttonAuth: { addBtn: '@ums:{module}:add' }, } ``` ### 7.2 drawerConfig ```typescript { formConfig: { wrapperClass: 'grid-cols-1 md:grid-cols-3', layout: 'horizontal', schemaConfig: [{ schema: [] }] }, tableConfig: { show: false }, // show: true时配columns/editConfig mapConfig: { show: false }, layoutConfig: { column: 1 }, } ``` ## 八、常用样式类 | 类名 | 用途 | |------|------| | `form-item-col3` | 表单项占满整行(3列布局) | | `form-item-col3 hidden` | 隐藏表单字段(如id) | | `grid-cols-1 md:grid-cols-3` | 表单3列响应式 | | `grid-cols-1 md:grid-cols-4` | 搜索表单4列响应式 | | `w-[600px]` | 抽屉宽度(class-name) | ## 九、权限标识 ``` 格式: @ums:{module}:{action} 常见action: add/edit/delete/search/reset/detail/import/export/auth 使用: v-auth指令 / buttonAuth / directives / submitButtonOptions ``` ## 十、API响应 ```typescript { code: 1, result: { records: [...], total: 100 } } // 分页 { items: [...], total: 100 } // 前端代理返回 ``` ## 十一、导入速查 ```typescript // index.vue: EditTable, Page, getIconFont, ElButton/ElMessage/ElPopconfirm/ElSwitch // drawer.vue: Drawer, AddForm, simpleTitle, ElButton/ElMessage // config.ts: useVbenForm, cloneDeep, session, z (from @vben-core/form-ui) // 路径别名: #/* → ./src/* ``` ## 十二、生成步骤 1. **API文件**: `src/api/{module}.ts` (分页+列表+ID查询+新增+修改+删除) 2. **页面目录**: `src/views/{module}/` 3. **config.ts**: tableConfig(表格/搜索) + drawerConfig(表单) 4. **drawer.vue**: 根据是否有子表选模板 5. **index.vue**: 根据是否有左树选模板 6. **路由**: 添加路由配置 7. **验证**: 启动检查