# cruda **Repository Path**: holyhigh2/cruda ## Basic Information - **Project Name**: cruda - **Description**: GitHub同步仓库 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2023-03-28 - **Last Updated**: 2025-12-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ![Cruda](./logo-32.png) Cruda ![npm](https://img.shields.io/npm/v/cruda) ![NPM](https://img.shields.io/npm/l/cruda) 一个CRUD视图模型,提供数据、状态、操作的托管如 `submit()`/`form.name`/`loading.table`,可以让开发者使用更少的代码,更快的构建CRUD视图。 Cruda通过适配器提供跨UI框架的统一接口。 ## 概念图 ![Cruda](./conception.png) ## Demos - [element-ui](https://stackblitz.com/edit/cruda-element-ui?file=src%2FApp.vue) - [element-plus](https://stackblitz.com/edit/cruda-element-plus?file=src%2FApp.vue) ## 特性 - 数据托管 ```html ``` - 操作托管 ```js $crud.submit(formEl) //submit form $crud.reload() //reload table $crud.cancel() //cancel form ``` - 状态托管 ```html ``` - 逻辑封装 ```js /* 当调用toQuery方法时, Cruda 会 1. 设置 loading.query 为 True 2. 打包 query, pagination, order 等数据 3. 触发钩子 4. 捕获异常 5. ... */ $crud.toQuery() ``` - RESTful HTTP 方法 ```js $crud.toQuery() //GET $crud.toDelete() //DELETE $crud.doUpdate() //PUT ``` - 多UI框架支持 - 多实例支持 - ... ## 使用 ### 1. 安装 - [cruda-adapter-element-ui](https://gitee.com/holyhigh2/cruda-element-ui) - [cruda-adapter-element-plus](https://gitee.com/holyhigh2/cruda-element-plus) ### 2. 激活 - [cruda-adapter-element-ui](https://gitee.com/holyhigh2/cruda-element-ui) - [cruda-adapter-element-plus](https://gitee.com/holyhigh2/cruda-element-plus) ### 3. 多实例 - [cruda-adapter-element-ui](https://gitee.com/holyhigh2/cruda-element-ui) - [cruda-adapter-element-plus](https://gitee.com/holyhigh2/cruda-element-plus) ### 4. 钩子 - [cruda-adapter-element-ui](https://gitee.com/holyhigh2/cruda-element-ui) - [cruda-adapter-element-plus](https://gitee.com/holyhigh2/cruda-element-plus) ### 5. 自定义组件 - [cruda-adapter-element-ui](https://gitee.com/holyhigh2/cruda-element-ui) - [cruda-adapter-element-plus](https://gitee.com/holyhigh2/cruda-element-plus) ### 6. URL 参数 - [cruda-adapter-element-ui](https://gitee.com/holyhigh2/cruda-element-ui) - [cruda-adapter-element-plus](https://gitee.com/holyhigh2/cruda-element-plus) ### 7. 全局默认值 当项目中存在通用的CRUD场景时,可以设置全局默认值/钩子/... ```ts //rs具有哪些属性取决于后端返回值 CRUD.defaults[CRUD.HOOK.AFTER_QUERY] = function (crud, rs) { crud.pagination.total = rs.data.total crud.table.data = rs.data.records || [] } CRUD.defaults.pagination.pageSize = 10 CRUD.defaults.view.opQuery = true CRUD.defaults.table.rowKey = 'id' ``` ### 8. RESTAPI 可以修改API地址/请求方法来适配后端服务 ```js CRUD.RESTAPI = { QUERY: { url: "", method: "GET" }, QUERY_DETAILS: { url: "/{id}", method: "GET" },//(v1.20+) ADD: { url: "", method: "POST" }, UPDATE: { url: "", method: "PUT" }, DELETE: { url: "", method: "DELETE" }, EXPORT: { url: "/export", method: "GET" }, IMPORT: { url: "/import", method: "POST" }, SORT: { url: "/sort", method: "PUT" }, COPY: { url: "/copy", method: "POST" }, ADD_OR_UPDATE: { url: "/addorupdate", method: "POST" },//(v1.20+) } ``` #### 实例API (v1.9+) 也可以在激活时设置实例专用的RESTAPI ```js const $crud = useCrud({ url:'/api/single', restApi:{ //支持对象格式或字符串格式(仅能设置url) ADD:{url:'/add-batch',method:'POST'}, COPY:'/copy2' } }) ``` >**\*** api的key必须是大写格式 ### 9. xApi (v1.5+) `CRUD.xApi(apiName,apiUrl,config)` 可用于扩展 CRUD RESTAPI 以对业务接口进行统一管理。下面的例子演示了如何扩展一个CRUD API ```js //1. 注册新接口(通常在系统初始化阶段调用)。注册API后,会 /** * a. 在crud实例中注册 toClone 方法 * b. 在CRUD.HOOK中注册 BEFORE_CLONE、AFTER_CLONE 钩子 */ CRUD.xApi('clone','/clone',{method:'POST'}) //2. 监听注册钩子 onHook(this,CRUD.HOOK.AFTER_CLONE,(crud,rs)=>{ console.log('xApi-->',rs.data) }) //3. 调用 toClone 方法 this.$crud.toClone({x:1,y:2}); ``` ### 10. 自动响应 (v1.8+) 通过配置`autoResponse`参数可以实现add/update/delete/copy操作的自动视图刷新,避免全部通过`reload()`操作而导致的状态丢失。一个典型的场景就是树表操作 —— 可避免因刷新而导致的层级关闭或页面闪烁。具体配置如下 ```ts //1. 设置 response 校验器,用于验证返回结果是否成功同时也表示开启自动更新 $crud.autoResponse.validator = (response:{status:number})=>{ return response.status === 200 } //2. autoResponse参数支持默认设置。对于 add/copy 这样的新增操作必须设置getter函数以便从后台获取具有主键的新增记录 CRUD.defaults.autoResponse.getter = (response:any)=>{ return [response.data] } //2. 如果新增记录在前端获取主键,可以在getter中直接返回提交数据。对于更新操作,如果未设置getter则使用提交数据进行更新 CRUD.defaults.autoResponse.getter = (response:any,submitRows?:any[])=>{ return submitRows } //3. 对于树表,新增操作还可以设置parentKeyField属性以便查找上级。默认pid CRUD.defaults.autoResponse.parentKeyField = 'pid' //4. 插入操作时可以指定插入位置 head 或 tail。默认head CRUD.defaults.autoResponse.position = 'head' //5. 对于树表,删除操作还可以设置childrenKeyField属性以便查找下级。默认children CRUD.defaults.autoResponse.childrenKeyField = 'children' ``` 之后CRUDA就会替你自动更新视图。如果想手动控制可以在 add/update/delete/copy 操作的 AFTER_XXX 钩子中调用更新方法或自行实现更新 ```ts // 其他可用钩子见下方文档 onHook(CRUD.HOOK.AFTER_DELETE,(crud,rs,rows,autoProcess)=>{ autoProcess() }) ``` ### 11. 前端分页 (v1.11+) 通过配置`pagination.frontend`参数可以开启前端分页,cruda会自动处理列表切片同时非查询的钩子处理逻辑保持不变 ```ts useCrud({ ... pagination:{ //在实例页面开启前端分页 frontend:true } }) //设置全局默认钩子【可选】。如果没有钩子,默认缓存整个rs CRUD.defaults[CRUD.HOOK.BEFORE_CACHE] = function (crud, rs, cache) { //缓存结果集 cache({ data:{ rows:rs.data.rows, total:rs.data.total } }) } CRUD.defaults[CRUD.HOOK.AFTER_QUERY] = function (crud, rs/*开启缓存/前端分页后,rs表示缓存数据 */, params, slice) { //过滤缓存结果 const keyword = params.keyword let list = filter(defaultTo(rs.data.rows, []),(item:any)=>{ return test(item.email, keyword) || test(item.uname, keyword) || test(item.ip, keyword) || test(item.domain, keyword) }) crud.pagination.total = defaultTo(list.length, 0); //使用slice自动切片,如果未开启前端分页,slice函数直接返回list crud.table.data = slice(list); } ``` 开启前端分页后`toQuery/reload`方法都不会请求后台接口,如需要刷新数据可使用`reset`方法 ## Cruda API ### Props - view ✅⚡ > 业务组件通过 view 来控制 UI > > - opQuery 查询(框/按钮/..)开关 > - opAdd 新增按钮显示开关 > - opUpdate 编辑按钮显示开关 > - opDelete 删除按钮显示开关 > - opExport 导出按钮显示开关 > - opImport 导入按钮显示开关 > - opSort 排序按钮显示开关 > - opCopy 复制按钮显示开关 - loading > 通过 loading 控制锁定状态 > > - query 查询按钮锁定开关 > - table 表格锁定开关 > - del 删除按钮锁定开关 > - submit 提交按钮锁定开关 > - form 表单加载锁定开关 > - export 导出按钮锁定开关 > - import 导入按钮锁定开关 > - sort 排序按钮锁定开关 > - copy 复制按钮锁定开关 - query > 托管查询条件的容器 - sortation > 托管排序结果的容器 - table > 表格容器托管当前 crud 实例的列表/tree 数据及显示状态 > > - data 表格数据 > - selection 当前选中行 > - allColumns 表格所有列,用于动态展示 > - orders 排序列表,会传递给 GET 请求 > - rowKey✅ 表格行的 id key,默认为'id'。通常由适配器自动设置 - pagination⚡ > 分页容器托管当前 crud 实例的列表分页状态 > > - pageSize✅ 每页记录数 > - currentPage 当前页号 > - total 总记录数 > - frontend 是否前端分页 - form > 表单容器托管当前 crud 实例的表单数据 - formStatus > 表单当前状态 0:默认;1:新增;2:更新;3:查看;4:新增或更新 - params > crud 激活参数,通过对象方式构造 crud 时可以注入。可用于自定义组件中进行附加操作,比如附加 CRUD 权限控制 - error > crud 错误信息{name,message,status}。可以用于监控并作出合适的反馈,比如网络超时提示 - editingId > 当前正在编辑器的表单行id,根据`table.rowKey`属性确定 - key > 多实例下每个crud实例的标识 - recoverable✅⚡ > 是否开启编辑快照,开启后会在新增/编辑/新增编辑时保存form快照 - snapshots > 保存快照内容的对象,key是table.row的id。可用于在视图中显示快照状态,详见examples - invalidBreak✅⚡ > 表单校验检测到首个无效信息时中断校验 - autoResponse✅⚡ > 在表单提交 add/update/delete/copy操作后,自动更新`crud.table.data`视图 - defaultQuery⚡ > 默认查询参数,GET请求时自动合并到请求参数中 - restApi⚡ > 实例api,可覆盖 `8. RESTAPI` - cache⚡ > 开启查询缓存,开启后可通过`BEFORE_CACHE`钩子定制缓存或使用默认的后台返回结果。当`pagination.frontend`开启时该属性自动开启 ✅ **_表示支持全局默认值_** ⚡ **_表示支持对象激活参数_** ### Methods - toQuery(query?: Record) : Promise > (未开启前端分页时) 启动 crud 实例的查询。向指定 REST 地址发送 GET 请求。query参数会与$crud.query进行[merge](https://holyhigh2.github.io/func.js/api/modules/object#merge)但不会修改$crud.query - toDelete(rows: Record | Record[]) : Promise > 启动 crud 实例的删除。向指定 REST 地址发送 DELETE _**(默认)**_ 请求 - toExport() : Promise > 启动 crud 实例的导出。向指定 REST 地址发送 GET _**(默认)**_ 请求 - toImport(file: File | File[],fieldName?: string) : Promise > 启动 crud 实例的导入。fieldName可指定服务端接收名称,如果为空,当file为单个对象时默认为`file`,当file为数组时默认为`files`。向指定 REST 地址发送 POST _**(默认)**_ 请求 - toAdd(...args) > 设置 form 状态为新增。 - toUpdate(row) : Promise > 设置 form 状态为更新。向指定 REST 地址发送 GET _**(默认)**_ 请求 - toAddOrUpdate(...args) : Promise > 设置 form 状态为新增或更新 - toView(row?: Record) : Promise > 设置 form 状态为查看。向指定 REST 地址发送 GET _**(默认)**_ 请求 - toSort() : Promise > 启动 crud 实例的排序。向指定 REST 地址发送 PUT _**(默认)**_ 请求 - toCopy() : Promise > 启动 crud 实例的复制。向指定 REST 地址发送 POST _**(默认)**_ 请求 - cancel() > 设置 form 状态为取消。 - submit(...args) : Promise > args参数会传递给 `BEFORE_SUBMIT` 并可自行实现校验逻辑 - submitAdd(...args) : Promise > 同`submit()`,但不会校验`formStatus` - submitUpdate(...args) : Promise > 同`submit()`,但不会校验`formStatus` - submitAddOrUpdate(...args) : Promise > 同`submit()`,但不会校验`formStatus` - submitForm(form, ...args) > **_*依赖适配器_**。可以对1或多个表单(或具有validate()方法的组件)进行校验,并在通过后调用`submit()` - reload(query?: Record) : Promise > 重置分页信息并执行一次 toQuery() - getRestURL() : string > 获取 crud 实例的服务地址。通常用于 crud 内部 - setURLParams(paramObj) > 设置服务地址中的参数表 - getDetails(rowId) : Promise > 获取行信息。通常用于 crud 内部 - changeSelection(selection: Record[]) > 用在 table 的 selection-change 事件中,记录 table 当前已选记录 - changeOrder(sortData: { column: Record prop: string order: string | null }) > 用在 table 的 sort-change 事件,会自动管理排序信息并触发查询 - getContext() > 获取crud实例所在的上下文 - reset(query?: Record) : Promise > 清除缓存并执行一次 reload() ### Hooks - BEFORE_QUERY(crud,params,orders,cancel) _**async**_ > 查询前回调,可以修改请求参数(params),比如分页名称等,可取消。取消后不会触发 AFTER_QUERY > **注意** ,params 为提交接口的实际对象(包含 query、pagination),此处修改 crud.query/pagination 的内容不会提交到接口 - AFTER_QUERY(crud,rs,params,slice) _**async**_ > 查询后回调,可以获取查询结果,设置表格。如果开启前端分页则可使用params过滤列表并通过`slice(rows)`函数进行自动切片 - BEFORE_DELETE(crud,rows,cancel) _**async**_ > 删除前调用,可取消。取消后不会触发 AFTER_DELETE - AFTER_DELETE(crud,rs,rows,autoProcess) _**async**_ > 删除后调用. `autoProcess()` 用来自动更新视图 - BEFORE_ADD(crud,cancel,...args) _**async**_ > 新增前调用,可以用来清空表单或产生 uuid 等。可取消,取消后表单状态不变。*...args* 来自`toAdd()` - AFTER_ADD(crud,rs,autoProcess) _**async**_ > 新增提交后 `AFTER_SUBMIT`前调用. `autoProcess()` 用来自动更新视图 - BEFORE_UPDATE(crud,row,cancel,skip) _**async**_ > 编辑前调用,可以用来锁定某些字段。可取消,取消后表单状态不变; `skip()`用来跳过记录详情查询,跳过后不会触发 AFTER_DETAILS - AFTER_UPDATE(crud,rs,autoProcess) _**async**_ > 编辑提交后 `AFTER_SUBMIT`前调用. `autoProcess()` 用来自动更新视图 - BEFORE_ADD_OR_UPDATE(crud,cancel,doView,...args) _**async**_ > 新增/编辑前调用,可以用来锁定某些字段。可取消,取消后表单状态不变; `doView()`用来请求记录详情查询,默认不会查询 - AFTER_ADD_OR_UPDATE(crud,rs,autoProcess) _**async**_ > 新增/编辑提交后 `AFTER_SUBMIT`前调用. `autoProcess()` 用来自动更新视图 - BEFORE_VIEW(crud,row,cancel,skip) _**async**_ > 查看前调用,可以用来对显示内容进行格式化等。可取消,取消后表单状态不变; `skip()`用来跳过记录详情查询,跳过后不会触发 AFTER_DETAILS - AFTER_DETAILS(crud,rs) _**async**_ > 编辑/查看(默认)开启详情查询后触发 - AFTER_DETAILS_UPDATE(crud,rs) _**async**_ > 编辑开启详情查询后触发,在`AFTER_DETAILS`之后 - AFTER_DETAILS_ADD_OR_UPDATE(crud,rs) _**async**_ > 新增/编辑开启详情查询后触发,在`AFTER_DETAILS`之后 - AFTER_DETAILS_VIEW(crud,rs) _**async**_ > 查看(默认)开启详情查询后触发,在`AFTER_DETAILS`之后 - BEFORE_SUBMIT(crud,cancel,setForm,...args) _**async**_ > 提交前调用,可对 form 进行最后加工,可取消。取消后不会触发 `AFTER_SUBMIT`。`setForm(formObject)`用于设置提交到后台的form数据,默认`crud.form` - AFTER_SUBMIT(crud,rs,autoProcess) _**async**_ > 提交后调用,可以用来刷新页面、发送通知或其他操作. `autoProcess()` 用来自动更新视图 - BEFORE_EXPORT(crud,params,orders,cancel) _**async**_ > 导出前调用,同 BEFORE_QUERY,可取消。取消后不会触发 AFTER_EXPORT - AFTER_EXPORT(crud,rs) _**async**_ > 获取导出数据后调用 > 表单取消编辑时触发(调用 cancel 后) - BEFORE_IMPORT(crud,params,file,cancel) _**async**_ > 导入文件上传前调用,可在 params 中添加额外参数,当file是数组时可修改上传数量;可取消。取消后不会触发 AFTER_IMPORT; - AFTER_IMPORT(crud,rs) _**async**_ > 导入上传完成后调用 - BEFORE_SORT(crud,sortation,cancel) _**async**_ > 排序前调用,可用来打开排序框或启动自动排序处理,可取消。取消后不会触发 AFTER_SORT - AFTER_SORT(crud,rs) _**async**_ > 提交排序后调用,可以用来刷新页面、发送通知或其他操作 - BEFORE_COPY(crud,rows,cancel) _**async**_ > 复制记录前调用,可取消。取消后不会触发 AFTER_COPY - AFTER_COPY(crud,rs,rows,autoProcess) _**async**_ > 复制完成后调用. `autoProcess()` 用来自动更新视图 - ON_ERROR(crud,error) > 操作发生错误时调用 - ON_CANCEL(crud) > 操作发生错误时调用 - ON_VALIDATE(crud,isValid,invalidFields) > 调用submitForm时触发 - BEFORE_RECOVER(crud,cancel,snapshot) _**async**_ > 恢复快照前调用 - BEFORE_CACHE(crud,rs,cache) _**async**_ > 开启cache并完成首次后台请求后调用。`cache(data)` 用来定制缓存数据 ## 错误信息 - Must specify 'crudName' when multiple instances detected > 多实例时调用 lookUpCrud 方法未指定 crud 标识。解决方法见【6. 自定义组件】 - form validation Xxx > 表单提交时验证错误信息 - Cannot find [request] in the installation options > 安装时未指定请求器。解决方法见【1. 安装】 - table.rowKey is a blank value 'Xxx', it may cause an error - toDelete/Update/View() > 进行删除/编辑/查看操作时未指定 table.rowKey。可以设置默认/$crud 实例的对应属性 ## 工作流图 ![Cruda Workflow](./workflow.jpg)