如何封装组合条件表单

787 阅读4分钟

实现效果

自带必填校验:

image.png

表单保存的数据结构:

{
    "id": "rdzPu6KiSavZ2kXNDbxb7",
    "conjunction": "or",
    "conditionList": [
        {
            "deep": 1,
            "type": "single",
            "conjunction": "and",
            "id": "r80t9ddghnQxp_Dx_BWek",
            "left": {
                "value": "id_XXXtest_text",
                "label": "测试字符串类型",
                "type": "text",
                "textFormProps": {
                    "placeholder": "请输入文本",
                    "maxLength": 12,
                    "showCount": true
                }
            },
            "op": "like",
            "right": "Test",
            "desc": "",
            "not": true
        },
        {
            "deep": 1,
            "type": "single",
            "conjunction": "and",
            "id": "DCBjQNeE28NnU263v2hBS",
            "left": {
                "value": "id_XXXtest_number",
                "label": "测试数字",
                "type": "number",
                "numberFormProps": {
                    "placeholder": "请输入数字",
                    "min": 0,
                    "max": 100
                }
            },
            "op": "equal",
            "right": 100,
            "desc": "",
            "not": false
        },
        {
            "deep": 1,
            "type": "group",
            "conjunction": "and",
            "id": "hzPF6LPbTLFwh2C9xsNfA",
            "desc": "",
            "not": false,
            "conditionList": [
                {
                    "deep": 2,
                    "type": "single",
                    "conjunction": "and",
                    "id": "76Zd0UkRguMRuXcqtmUe7",
                    "left": {
                        "value": "id_XXXtest_text",
                        "label": "测试字符串类型",
                        "type": "text",
                        "textFormProps": {
                            "placeholder": "请输入文本",
                            "maxLength": 12,
                            "showCount": true
                        }
                    },
                    "op": "ends_with",
                    "right": "end",
                    "desc": "",
                    "not": false
                },
                {
                    "deep": 2,
                    "type": "single",
                    "conjunction": "and",
                    "id": "GvguhKYpQuHE44cYmsCO9",
                    "left": {
                        "value": "id_XXXtest_date",
                        "label": "测试日期",
                        "type": "date",
                        "dateFormProps": {
                            "format": "YYYY-MM-DD",
                            "showNow": true
                        }
                    },
                    "op": "between",
                    "right": [
                        "2024-06-13",
                        "2024-06-14"
                    ],
                    "desc": "",
                    "not": false
                }
            ]
        },
        {
            "deep": 1,
            "type": "group",
            "conjunction": "or",
            "id": "eEc5NX2EeQkFesUzCbrKy",
            "desc": "",
            "not": false,
            "conditionList": [
                {
                    "deep": 2,
                    "type": "single",
                    "conjunction": "and",
                    "id": "2fgxXVjuc50QUtSAElss0",
                    "left": {
                        "value": "id_XXXtest_select",
                        "label": "测试多选",
                        "type": "select",
                        "selectFormProps": {
                            "options": [
                                {
                                    "label": "选项一",
                                    "value": "1"
                                },
                                {
                                    "label": "选项二",
                                    "value": "2"
                                },
                                {
                                    "label": "选项三",
                                    "value": "3"
                                }
                            ],
                            "maxCount": 2
                        }
                    },
                    "op": "select_any_in",
                    "right": [
                        {
                            "label": "选项二",
                            "value": "2",
                            "key": "2"
                        },
                        {
                            "label": "选项一",
                            "value": "1",
                            "key": "1"
                        }
                    ],
                    "desc": "",
                    "not": false
                },
                {
                    "deep": 2,
                    "type": "single",
                    "conjunction": "and",
                    "id": "7elHOzbniz0umXVZ4SkaW",
                    "left": {
                        "value": "id_XXXtest_text",
                        "label": "测试字符串类型",
                        "type": "text",
                        "textFormProps": {
                            "placeholder": "请输入文本",
                            "maxLength": 12,
                            "showCount": true
                        }
                    },
                    "op": "is_empty",
                    "desc": "",
                    "not": false
                }
            ]
        }
    ]
}

背景

在我们业务开发中(经常在OA审批流、工作流、规则引擎、告警监控等业务),经常会遇到设置条件的场景,一些复杂的条件是通过多个条件组的组合而成的。这个时候需要一个组件来实现复杂的条件动态组合;

百度出品的amis中ConditionBuilder组件的交互上大致可以满足这种业务场景。

image.png

但是这个组件又没有完全满足我的需求,比如这个组件没有校验必填提示,如果是用户输入,需要做额外的校验,还有样式上我也有些定制化的想法,所以自己动手封装了一个组件。

实现过程

主要功能梳理

表单项分类(type)

可以分为单个条件和条件组两类,其中单个条件可以删除当前项目以及逻辑非(not)操作;条件组可以添加条件,添加条件组,删除条件组操作。

组合逻辑关系分类(conjunction)

同层级别下通过且/或 逻辑关系组合在一起,希望不同的逻辑关系显示不同的颜色区分开来;

层级限制(deep)

虽然可以动态嵌套多层条件组,但是不希望无限嵌套,嵌套太多层很有可能是条件设计本身有问题,一般几层条件组就可以满足大多数场景,所以可以预留一个参数,通过参数限制嵌套层数,当达到层级后就不允许继续向内层添加条件组了。

操作符类型(op)

根据表单类型的不同,操作符可能也不太相同,比如:等于、不等于、小于、小于或等于、大于、大于或等于、属于范围、不属于范围、包含、不包含、模糊匹配、不匹配、匹配开头、匹配结尾、为空、不为空 等等。

条件值的表单类型(type)

根据最左侧定义的表单类型和中间操作符就可以枚举实现最右边表单类型,比如左侧定义的是日期类型,中间操作符选择大于或者属于那么右边就分别对应了不同的表单类型;

目前左侧定义表单类型我实现了以下类型:text、number、date、time、select

核心思路

理论上是可以无限嵌套的,只需要封装好一个递归,执行递归即可,当深度达到设定上限就不许允许继续添加;

结果

image.png

备注

这个组件还有很多可以优化的地方,比如属性透传、更多的表单类型、校验自定义配置、每条记录支持描述配置(预留desc字段)等等,后续会慢慢优化完善。