写人人都看得懂的筛选条件表达式

560 阅读2分钟

什么是筛选条件表达式?

筛选条件表达式是一个逻辑表达式,用于判断数据项是否匹配特定条件。

image.png

为什么要人人看得懂?(用户能理解 )

  • 我们的产品需要数据的流转;
  • 数据流转中对数据的二次加工就需要用到筛选条件表达式;
  • 面向所有用户。

所以产品的一环 就是让用户可以在需要的时候自己写表达式

如何可视化一个通用的筛选条件表达式?

假定我们现在有一个简单的场景:  我想找到一个数据包里(负责人=xx)的所有数据

那么我们需要的筛选条件表达式大概是: {type: 'str', operator: 'ge', value: '姜杉'}

那么在渲染该数据的时候,格式大概是: 

Type -- Operator -- Value

负责人 -- 等于 -- xx

假定我们有一套约定的规则 后端枚举定义了一些数据的类型,按照此约定我们可以做出一个基类组件,即:

Column 列类型操作Component 展示的组件
str等于,不等于,为空,以xxx开始....输入框Input
array等于,包含...选择框Select
date_time在范围内,在范围外...日期选择器DatePicker
int大于,小于....数字输入框NumberInput

根据数据类型展示的字段也同样如此:

image.png

基类组件的能力就是内置了后端定义的类型枚举该如何渲染,即拿即用,几乎不会出错。

与直观数据不同,将这样一份数据渲染出来并且容易理解,在组件层需要关注下面事情:

首先看下数据支持全场景的数据结构:

{
  "operator_filter": {
    "$and": [
      {
        "$or": [
          {
            "column": "价格",
            "operator": "between",
            "param": [
              1,
              1000
            ]
          },
          {
            "column": "类型",
            "operator": "in",
            "param": [
              "零食",
              "服饰"
            ]
          }
        ]
      },
      {
        "$not": {
          "column": "名称",
          "operator": "contain",
          "param": "过期"
        }
      }
    ]
  }
}

想要完全支持复杂的数据结构,在渲染层还需要额外做:

  1. 可以正确表示复杂的嵌套关系

image.png

  1. 需要根据值类型使用不同的组件去处理

image.png

  1. 需要提供更友好的编写环境(提供数据源,字段名称等)

image.png

  1. 同一个数据类型的不同展现形式

image.png

这样下来我们就得到了可以正确创造筛选条件表达式的通用产品。

业务同学A: 写的真好,我想拿过来用一下。诶?那我想要找负责人等于业务同学B的,还要输入他的名字啊,我想选啊?

开发同学J: 你这个就是str类型啊? str类型就是Input输入框啊,怎么让你选?

业务同学A:不能选我怎么用,真是一坨XX。

业务同学A:另外我还有状态栏想要选的,虽然是12345,数字,但1就是新增啊,你不能让我输入1啊。还有个custom_array_str这个类型是我们业务私有化的数据格式,你怎么展示不出来啊?

开发同学J: 嗯。好。

如何可视化一个较为业务的筛选条件表达式?

组件需要服务业务,就需要在保留既定功能的基础上,继续允许定制业务场景,

允许自定义类型 -- 操作 -- 值组件;

image.png

组件的入参和出参支持二次定制transform函数

原本: {operator_filter} -> 组件 ->{operator_filter}

现在: 

image.png

所以这种场景也得到了满足:

由于历史原因表达式的格式后端保留了两种,在前端的处理上最终只认一种表达式,所以在表达式的接收上提供了转换函数,实现兼容

{operator_filter} = f({condition})

目前支持的所有扩展方式如下:

image.png

image.png

通用组件继续逐层解耦

针对业务的二次开发可以使得筛选器满足不同的业务场景,这是宏观的处理,那么微观上同样也可以拆分。

场景:市民云想要使用该组件,但是想要和antd design 强绑定,该如何使用不同的组件库进行组件的渲染?

拆分:

  1. 通用组件自己传入想要使用的组件,渲染由开发者来决定(组件需要支持受控属性value和onChange)
components={

Input: <AntdInput />

Select: <AntdSelect />

}

2. 自由定制所有逻辑的渲染:

逻辑支持具体内容支持方式
操作复制,删除..执行函数onCopy, onDelete
关系且,或,非允许传入渲染组件来展示
添加添加条件,添加条件组允许传入渲染组件来展示

即在所有需要渲染的地方都留有口子可以定制,只保留纯逻辑处理,所以逐层解耦的最终目的为:

实现逻辑固化,以最小的代价侵入各个产品,保证逻辑统一。

最终引出一个课题:前端需要合理设计自己的黑盒。

目前的筛选条件表达式的应用:

  • 数据包针对数据的筛选(后端计算)
  • 显隐关系控制(前端计算)

还需要进一步处理的地方 

进一步处理的地方一般为约定的通用逻辑和现有的组件逻辑设计冲突所导致的

  1. 可视化操作的优化空间

    单条表达式的拖拽/排序

    条件组展开/收起

    非($not)操作的可视化处理

  2. 数据结构上的优化空间?

    2.1 一条表达式需要额外补充表达式才能正确表达:

    {operator_filter} -> 数据二次处理 -> 还原(可以还原?) -> 多了一个表达式?
    -> 去掉添加的表达式(还原过程中很容易出bug)
    

    2.2 需要多个列来确定一个字段(状态值):

    多列数据源 -> 单列表达式数据源 -> {operator_filter} -> 根据值将列名修改成对应的多列(出问题的点)