表单联动筛选有效数据(选择iphone颜色、内存、型号等)

371 阅读3分钟

需求说明

表单项的联动、筛选有效数据。

想象在淘宝买 iphone 手机时,当你选颜色,内存,套餐类型,是否有货等 其中一项时, 会在全量数据中 根据选中条件 筛选出有效数据,分别渲染到其他 FormItem 表单项, 所以每个表单项都是一个监听的 action,每次触发选择,都要进行筛选-匹配的流程。

为了方便管理

  1. 设计一个类,类中实现整个流程的子能力
  2. 方法间的调用,采用函数式编程思想
    • 拆分 + 组合( 组合优于继承 )
    • 拆分的价值: 可复用的个体、问题简单化
    • 一个入口: 保持纯度
  3. 实现函数式和面向对象的合体

FormItemDataLinkage-Class的结构

必要的 State 状态

  1. data - 全量数据
  2. formItemNames - 参与联动的表单项
  3. allActions - 要有可写的,外界(调用层)数据源
  4. defaultActionValue - 初始化的默认选中项
  5. form - 表单实例

linkage.png

对外暴露的能力

1. initial(): 根据初始值渲染初始状态

initial() {
    let that = this;	
    that.data && this.initialByGrade(	
        that.data,		
        that.defaultActionValue,		
        this.differentiate(		
            that.formItemNames,			
            that.allActions,			
            that.dismantling		
        )
    );
};

2. run(p1, [...actions]): 当日常切换时,监听并调用run方法更新数据状态,更新视图

run(defaultActionValue, [...states]) {
    let _monitorChange: MonitorChange = {
        envGrade: defaultActionValue,
        others: [...states]
    };
    let that = this;	
    this.data && this.unInitial(
        that.data,
        _monitorChange,
        that.deleteInvalidProperty,
        that.differentiate(
            that.formItemNames,
            that.allActions,
            that.dismantling,		
        ),		
        that.formItemNames	
    );
};

内部细分能力

1. initialByDefaultActionValue ()

initial()内部调用的方法,该方法是第一层检查,查找初始值在全量数据中是否有匹配数据

initialByDefaultActionValue(cbs: Data[], facv: string, difn: Function) {
    const fld = cbs?.filter((cb: Data) => cb?.envGrade === facv);	
    if (!fld?.length) {	
        return message?.error('没有该匹配项');	
    } else {	
        difn(fld);	
    }	
    return null;
};

2. differentiate()

分化设置,对其他被动联动项,逐个筛选是否有匹配项目

/**
*           ---> 3项都为空: 重新 initialByGrade (获取全量数据 - combines)
*          |
* 分化设置 --
*          |
*          |                     ---> 当前为空: 清除显示项,清空下拉列表
*          |                    |
*           ---> 3项不都为空 ->map-                              ---> 当前存在上次选中的项: 显示默认值(不做任何操作)
*                                |                            |
*                                 ---> 当前不为空: 重设下拉数据 --
*                                                              |
*                                                               ---> 当前不存在上次选中的项: 清空显示项,
*/

differentiate(ns: string[], las: Function[], disFn: Function, cbs: Data[], facv: string) {
    let that = this;
    return function(fld: Data[]) {
        const disd = disFn(fld, ns) as SelectType[][];
        // 先判断 如果下面3项都为空,则重新 initialByGrade
        if (disd?.every(dis => !dis?.length)) {
            cbs && that.initialByGrade(cbs, facv, that.differentiate(ns, las, disFn, cbs, facv));
        } else {
            // 否则才细化	
            that.haveExisted(
                disd,
                that.currentEmpty(ns, las),
                that.currentNotEmpty(ns, las)
            );
        };
    };
};

3. haveExisted()

细化分化,对单独一个表单项做判断,用到currentEmpty(), currentNotEmpty()

haveExisted(diss: SelectType[][], ef: Function, nef: Function) {
  diss?.forEach((dis: SelectType[], index) => {
    // 当前项为空
    if (!dis?.length) ef(index);
    else nef(index, dis);
  });
};

4. deleteInvalidProperty()

去掉无效属性, 值被清空 或 没有值,该属性不参与匹配

deleteInvalidProperty(mc: Monitor) {
    let obj: Partial<Monitor> = {};	
    for (let i in mc) {	
        if (mc[i]) {	
            obj[i] = mc[i];		
        }
    }	
    return obj;
};

5. currentEmpty(), currentNotEmpty()

如果当前筛选出数据为空:清空当前表单项展示与Select.option数据

如果当前筛选出数据不为空:需要判断当前list中,是否有当前项显示的那一项,有则留着,没有则清楚显示项

currentEmpty(ns: string[], las: Function[]) {
    return (i: number) => {	
        this.form.setFieldsValue({ [ns[i]]: '' });		
        las[i]([]);	
    };
};

currentNotEmpty(ns: string[], las: Function[]) {
    return (i: number, dis: SelectType[]) => {	
        // 重新赋值下拉项		
        las[i](dis);	
        // 当前项不为空,需要判断当前list中,是否有当前项显示的那一项		
        // 有则留着,没有则清楚显示项		
        const exised = dis?.some(item => item?.value === this.form?.getFieldValue(ns[i]));	
        // 如果不存在清空显示项,存在则默认显示		
        if (!exised) {	
            this.form.setFieldsValue({ [ns[i]]: '' });		
        };	
    };
};

6. dismantling()

拆解,全量数据的类型可能是Data类型,Select.option需要的数据可能是SelectType,中间需要一个format的过程

dismantling(fld: Data[], ns: string[]): SelectType[][] {
    let lks: SelectType[][] = [[], [], []];	
    fld?.forEach((fil: Data) => {	
        lks?.forEach((lk: SelectType[], i) => {		
            lk?.push({ value: fil[ns[i]], text: fil[ns[i]] });		
        });	
    });	
    return lks;
};

Usage

useEffect(() => {
    // 实例化
    const linkage = new FormItemDataLinkage<D, S, M, C>(
        combines!,
        formItemNames,
        allActions,
        currentGrade,
        form
    );
    // 启动初始状态
    linkage.initial();
    setLinker(linkage);
}, [combines]);

  
// 监听日常操作
useEffect(() => {
    // maybe actions is [currentUnit、currentRegion、currentCluster]
    linker?.run(defaultActionValue, [...actions]);
}, [defaultActionValue, [...actions]]);