低码:标准列表页应该是什么样子?

点击↑上方↑蓝色“编了个程”关注我~

7aea0c89fb8c37cd07bf6b2fd790e7de.png

这是Yasin的第 76 篇原创文章

9b910e7327e79b5fec171d9c5bdd3f43.png

最近在研究B端产品在低码平台上的落地实践,打算先从列表页入手。因为绝大多数B端产品都会有一个列表页作为操作动线的起点,它们具有非常大的共性,比较适合低码搭建。本文试图总结绝大多数列表页产品的共性,用于搭建一个比较通用的列表页低码模板。

列表页的产品功能

8e8e5b1cace3cbd0f4574a916dc34dd5.png

筛选与搜索

用户只关心自己想关心的数据,过滤掉自己不关心的数据。为了满足这个实际的用户需求,列表页通常会有筛选和搜索的功能。

筛选

筛选指的是通过一系列的筛选条件组合,最终形成一个多维度的筛选条件。

筛选有多种产品形态。最常见的就是「多个输入框、下拉菜单、时间选择器的组合」。还有一种是「使用Tab来控制显示不同的筛选条件」

9b4542d1e67f6396c7b66be44bda2d02.png

最后一种是“标签”的形式,比如“最近3个月创建”,“审批中”等等,做得高级一点的产品,用户可以根据所有的筛选条件,自己创建属于自己的常用标签。标签其实是快捷操作的概念,可能不止是筛选的组合,还有可能加上排序的能力。

3dc8ebce86a76433330b44ea44d063ef.png

搜索

而搜索分为两类,一类是简单搜索,适用于绝大多数场景。它的产品形态就是一个简单的输入框,用户可以输入关键字,没有任何反馈,和筛选项一起,组成数据过滤的最终条件。简单搜索有可能会分字段搜索,比如按名称搜索、按code搜索等。

982854fef1923b2ef96295c3a2cefde1.png b9423ea89fe54f7f3c41ae51ac897ff1.png

另一类是复杂搜索,它有实时反馈,会根据关键字返回数据的列表和个性化预览,甚至是智能推荐,复杂搜索通常底层是搜索引擎实现。

扫描二维码关注公众号,回复: 15277156 查看本文章
7311f3e04180accfa4331465b315950d.png

分页和排序

列表页一定是要支持分页的。分页有几个核心的参数:每页的数量,当前页码,总页码(或总数)。其中,每页的数量和当前页码是用户选择的,而总页码是后端返回的。

分页和排序一般是在一起的。就算没有排序条件,那一定会有一个默认的排序方式(id、创建时间等),这样底层才能实现分页。

分页会有「页码过大导致性能低下」的问题。普遍的有两种解决方式:

  • 用户不能直接选比较大的页码,只能小幅度往后挪,具体可以参考谷歌搜索的实现

  • 前端传入后端一个lastId,代表上一页的最后id,初始值为0,用这个id去作为一种过滤条件

如果是后端关系型数据库,数据量比较大,后端想要性能好,一般会给能排序的字段建立索引。但如果是在分库分表的设计下,更适合把数据同步到搜索引擎,走搜索引擎。

批量操作

表格是数据的聚合。批量操作可以提升用户的使用效率。比如:批量修改状态、批量删除、批量导出等。批量操作一般是表格每行左边有一个多选框,让用户可以勾选多条数据,然后做某个操作。比较通用的操作是删除和导出。但也有很多特定的业务操作,通常是用表格上方的按钮来触发。

a9c4a4d0c3643674d09fc6770d845f2b.png

导出

导出一般指的是把数据导出到一个excel表格中。导出一般有多种范围:

  • 导出库内所有数据

  • 导出当前搜索条件的所有数据

  • 导出自己勾选的数据

导出一般比较耗时,所以异步设计的比较多。比如发送导出的文件链接到聊天框,或者去一个导出任务中心查看导出进度和下载。

数据权限体系

一个复杂的系统往往会有多人的数据。其中就涉及到数据权限体系了。数据权限体系可能是复杂的,可能与角色、数据维度或状态等有关。数据权限影响数据的展示和数据的操作按钮。

数据权限体系有两种设计。一种是“所见即所得”,用户看见的数据一定是他有权限的,能看到的操作按钮一定是他有权限操作的。这种用户体验好,但后端实现会比较复杂,需要后端过滤数据、组装操作按钮的显隐规则,权限复杂一点的甚至有可能对性能有影响。

另一种是数据和操作按钮的显示与特定的用户无关,但在列表页不显示重要的数据或脱敏展示,在实际操作的时候再校验权限,无权限的话,再提醒用户。这种设计后端实现简单,在查询的时候不需要考虑用户的权限,操作的权限校验也只收敛到写接口,但用户体验会差一点。

我自己之前做的一个项目管理系统,就是用的第一种方式,用户看到的数据是自己作为创建人或协作人的数据,且有父子项目的权限联动,还有管理员角色的考虑,实现起来有点复杂,逻辑维护成本都比较大。如果条件允许的话,希望产品同学还是可以多考虑第二种方式~

通用的写操作:增删改

列表页一定会有很多按钮。其中创建按钮应该是在表格上方的,而编辑按钮和删除按钮是在表格行中的操作区。

增删改查是基本每个业务都会遇到的通用接口。其中添加和编辑应该是几乎一模一样的表单和字段,只是编辑有唯一主键,而添加没有。

在添加和编辑的表单中,可能也会有一些枚举选项,或者搜索别的领域的数据出来选择,作为一个字段关联到自己要创建的对象中去。

如果表单的字段过多,可以添加分组进行引导,比如分步骤填写,横向的或者纵向的多步骤填写表单都有,具体看团队的设计规范。表单的字段之间也可能有交互的联动逻辑,可能前面填写的东西,会影响后面的表单内容渲染。

删除一般要支持批量删除,且删除后不可从产品正常的操作中恢复。如果要恢复,那就不要叫删除了,改为失效等状态更合适。

列表页的标准接口定义

通过定义一系列列表页的标准接口。具体的业务来实现这个标准接口。这样在根据模板创建具体的列表页实例时,选择一套实现接口,就可以快速关联和生成具体的列表页。

7f2329dd232e0b861c863cf7672613e8.png

枚举接口

列表页会有一些筛选项是枚举的形式。在传统的低码平台,这些枚举可以用拖拉拽的可视化方式搭建。但如果想要更自动化一些,可以定义一个获取枚举列表的标准接口,用于自动渲染筛选项,也可以用于创建表单。

struct GenerateRequest {  
    1: string tenant // 租户  
    2: string groupCode // 套件code  
}  
  
struct EnumFieldWithChildren {  
    1: string          key // 唯一键  
    2: string          label // 展示标签  
    3: string          remark // 注释  
    4: list<EnumFieldWithChildren> children // 子枚举  
}  
  
struct QueryEnumResponse {  
    1: string key // 枚举的key  
    2: string label // 枚举的展示标签  
    3: bool filter // 是否是筛选项  
    4: bool tab // 是否是tab标签  
    5: string filter_key // 筛选传给后端的key  
    6: bool multi_select // 是否可以多选  
    7: list<EnumFieldWithChildren> items // 枚举值列表  
}  
  

list<QueryEnumResponse> QueryEnum(1: GenerateRequest req)(api.post="/standard/query_enum")

查询与排序接口

struct QueryListRequest {  
    1: list<QueryListParam> params // 过滤条件  
    2: string order // 排序  
    3: i32 page_size // 每页大小  
    4: i32 page // 当前页码  
    5: i64 last_id // 上页最大id  
    100: BaseReuest base  
}  
  
struct QueryListParam {  
    1: string key // 筛选key  
    2: string value // 筛选值  
    3: QueryParamType type // 筛选类型  
}  
  
enum QueryParamType  {  
    EQ // 相等  
    IN // 符合条件中的一个  
    GT // 大于  
    LT // 小于  
    SEARCH // 搜索  
}  
  
struct QueryListResponse {  
    1: i32 code  
    2: string msg  
    3: QueryListRes data  
}  
  
struct QueryListRes {  
    1: i64 total // 总数  
    2: list<Object> items // 数据,json格式  
}


QueryListResponse QueryList(1: QueryListRequest req)(api.post="/standard/query_list")
->

注:这里查询结果用的是Object格式,代表它应该是一个通用的抽象模型描述。在标准接口的实现中,它应该被替换成对应的实现的结构体。

<-

增删改接口

创建和编辑都是传入一个模型对象。而删除只需要传入唯一键就可以了。创建可以返回唯一键,用于跳转到详情页等场景。

struct CreateDataRequest {
    1: Object data // 数据,无数据唯一键
    100: BaseReuest base
}

struct CreateDataResponse {
    1: string data // 唯一键
    99: string code // 错误码
    100: string msg // 错误信息
}


struct EditDataRequest {
    1: Object data // 数据,有数据唯一键
    100: BaseReuest base
}

struct DeleteDataRequest {
    1: string code // 数据唯一键,可以是id,也可以是业务code,根据模型的定义来
    100: BaseReuest base
}

CreateDataResponse CreateData(1: CreateDataRequest req)(api.post="/standard/create_data")  
BaseResponse EditData(1: EditDataRequest req)(api.post="/standard/edit_data")  
BaseResponse DeleteData(1: DeleteDataRequest req)(api.post="/standard/delete_data")

批量操作接口

struct BatchOperationRequest {  
    1: list<string> codes // 批量操作的唯一键  
    2: string operation // 具体的操作指令  
    3: string params // 操作参数,如:批量修改数据到具体的某个状态  
}

BaseResponse BatchOperation(1: BatchOperationRequest req)(api.post="/standard/batch_operation")

批量操作接口可用于批量删除、批量改状态的业务操作。也可以用于批量导出,但如果导出需要按照当前的搜索条件来的话,可能使用查询列表那种参数格式会跟好一点,这里不赘述。

一些问题的补充思考&讨论

数据权限如何做更好?

前面讨论了数据权限体系。具体如何实现「取决于后端的接口实现」,对于接口定义来说是没有区别的。脱敏展示也是后端脱敏后返回比较安全。

搜索要分维度吗?

有时候搜索可能不止一个维度,筛选区可能会有多个搜索框。比如:按订单名称搜索,按商家名称搜索,按达人名称搜索等,返回同时满足多个维度的搜索条件的数据。

所以如果考虑通用的话,标准接口的定义需要支持多维度搜索,本文中的接口定义也是这么做的。

标签筛选如何实现?

标签筛选可以前端拖拽配置,也可以后端通过接口返回。通过接口返回可能是搭建更高效的方式,具体的实现思路是,新开一个筛选项配置的接口,返回的标签的tag和对应的filter params,查询列表的时候传入就可以了,这里不做单独定义。

除了接口定义,还需要模型定义吗

列表页的字段类型、label,创建和编辑表单的字段类型、label都需要能够从一个地方获取到这些元信息。如果只有接口定义,可能需要解析接口的字段类型、字段名、字段注释等信息,可能需要自己写或者复用一套接口解析引擎。

如果使用单独的模型定义,实现成本会更低,但维护成本会变大,尤其是列表页模型、创建和编辑模型不完全一样的时候,可能需要维护多套模型的元数据,增加维护成本。

64ce1a2caad72bbab9ea9b16df60d0e1.png

关于作者

我是Yasin,一个爱写博客的技术人

微信公众号:编了个程(blgcheng)

个人网站:https://yasinshaw.com

欢迎关注这个公众号96c97d1a5ca630ed21b48fa153773e99.png

83ede033865769066d537fe92fb65149.png

猜你喜欢

转载自blog.csdn.net/yasinshaw/article/details/127761836