解决antd TreeSelect 返回值不包含父节点问题 -自定义组件(react)

在写一个表单时使用了antd的 TreeSelect,在对TreeSelect的值提交时发现,父节点的值在半选状态时未提交,在选中状态时(子节点全选),子节点不提交,只提交父节点,这与后端需求不符,后端要求提交全部的节点,所有手动改造了一下,以适应需求,组件代码如下:

TypeScript 复制代码
import { TreeSelect } from "antd";
import React, { useState, useEffect } from "react";

export type FromTreeItem = {
    key?: number | string;
    title?: string;
    parentKey?: number | string;
    value?: number | string;
    children?: FromTreeItem[];
};

export type FromTreeSelectProps = {
    value?: any;
    treeData?: FromTreeItem[];
    onChange?: (value: any) => void;
};

/**  
 * 解决antd TreeSelect 返回值不包含父节点问题  
 * @param props   
 * @returns   
 */
const FromTreeSelect: React.FC<FromTreeSelectProps> = (props) => {
    const [selectedKeys, setSelectedKeys] = useState<(string | number)[]>([]);
    const [treeDataMap, setTreeDataMap] = useState<Record<string | number, FromTreeItem>>({});


    const treeToMap = (tree: FromTreeItem[]): Record<string | number, FromTreeItem> => {
        const map: Record<string | number, FromTreeItem> = {};
        const traverse = (nodes: FromTreeItem[]) => {
            nodes.forEach(node => {
                node.key ? map[node.key] = node : null; // 将当前节点添加到映射中  
                if (node.children) {
                    traverse(node.children); // 递归遍历子节点  
                }
            });
        };
        traverse(tree); // 从根节点开始遍历  
        return map;
    };
    useEffect(() => {
        if (props.treeData)
            setTreeDataMap(treeToMap(props.treeData))
    }, [props.treeData]);

    useEffect(() => {
        // 初始化 selectedKeys  
        if (props.value && treeDataMap) {
            setSelectedKeys(getAllChildrenKey(props.value));
        }
    }, [treeDataMap, props.value]);

    /**  
     * 根据选中的key找到所有父节点key并一起返回  
     * @param selectKeys   
     */
    const getAllNodeKey = (selectKeys: (string | number)[]): (string | number)[] => {
        const allKeys = new Set<string | number>(selectKeys);;
        const traverse = (node: FromTreeItem) => {
            if (node.parentKey) {
                allKeys.add(node.parentKey)
                traverse(treeDataMap[node.parentKey])
            }
        };
        selectKeys.forEach(key => {
            traverse(treeDataMap[key])
        });
        return Array.from(allKeys);
    };

    /**  
     * 根据给定的值 ,找到给定节点是否选中了子节点,如选中了子节点,则删除当前节点
     * @param keys   
     * @param tree   
     */
    const getAllChildrenKey = (keys: (string | number)[]): (string | number)[] => {
        if (!keys)
            return []
        const allKeys = new Set<string | number>(keys);
        keys.forEach(key => {
            const node: FromTreeItem = treeDataMap[key];
            if (node.children) {
                node.children.forEach((child) => {
                    if (node.key && allKeys.has(node.key) && child.key && allKeys.has(child.key)) {
                        allKeys.delete(node.key)
                    }
                });
            }
        })
        return Array.from(allKeys);
    };

    const handleChange = (newKeys: (string | number)[]) => {
        setSelectedKeys(newKeys);
        if (props.onChange) {
            props.onChange(getAllNodeKey(newKeys));
        }
    };

    const { SHOW_ALL } = TreeSelect;
    return (
        <TreeSelect
            treeCheckable
            treeData={props.treeData}
            value={selectedKeys}
            onChange={handleChange}
            showCheckedStrategy={SHOW_ALL}
        />
    );
};

export default FromTreeSelect;

使用方式同antd的其他组件,如:

TypeScript 复制代码
 <Form.Item
            name={'menuIds'}
            label={'菜单'}
            rules={[{ required: true }]}
          >
            <FromTreeSelect treeData={menuTreeData} />
          </Form.Item>
相关推荐
Forever7_2 分钟前
Electron 淘汰!新的桌面端框架 更强大、更轻量化
前端·vue.js
不会敲代码111 分钟前
前端组件化样式隔离实战:React CSS Modules、styled-components 与 Vue scoped 对比
css·vue.js·react.js
Angelial12 分钟前
Vue3 嵌套路由 KeepAlive:动态缓存与反向配置方案
前端·vue.js
jiayu40 分钟前
Angular学习笔记24:Angular 响应式表单 FormArray 与 FormGroup 相互嵌套
前端
jiayu41 分钟前
Angular6学习笔记13:HTTP(3)
前端
小码哥_常44 分钟前
Kotlin抽象类与接口:相爱相杀的编程“CP”
前端
evelynlab1 小时前
Tapable学习
前端
进击的尘埃1 小时前
Vue3 响应式原理:从 Proxy 到依赖收集,手撸一个迷你 reactivity
javascript
willow1 小时前
JavaScript数据类型整理1
javascript
LeeYaMaster1 小时前
15个例子熟练异步框架 Zone.js
前端·angular.js