解决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>
相关推荐
JIngJaneIL24 分钟前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
敲敲了个代码31 分钟前
隐式类型转换:哈基米 == 猫 ? true :false
开发语言·前端·javascript·学习·面试·web
澄江静如练_41 分钟前
列表渲染(v-for)
前端·javascript·vue.js
JustHappy1 小时前
「chrome extensions🛠️」我写了一个超级简单的浏览器插件Vue开发模板
前端·javascript·github
Loo国昌1 小时前
Vue 3 前端工程化:架构、核心原理与生产实践
前端·vue.js·架构
sg_knight1 小时前
拥抱未来:ECMAScript Modules (ESM) 深度解析
开发语言·前端·javascript·vue·ecmascript·web·esm
LYFlied1 小时前
【每日算法】LeetCode 17. 电话号码的字母组合
前端·算法·leetcode·面试·职场和发展
开发者小天1 小时前
react中useEffect的用法,以及订阅模式的原理
前端·react.js·前端框架
前端白袍2 小时前
Vue:如何实现一个具有复制功能的文字按钮?
前端·javascript·vue.js