关于ant design中Tree组件的个性需求

关于ant design中Tree组件的个性需求

img

通过左侧点击实现右侧的树的所有父子节点展示的状态
img

点击右侧树的节点的时候展示的状态

img

尝试过showCheckedStrategy={Tree.SHOW_CHILD}修改不行

尝试过showCheckedStrategy={Tree.SHOW_CHILD}修改不行
ant design中Tree组件中自定义渲染节点titleRender

使用的技术框架 react + @ant-design/icons": "^4.7.0"

可远程帮你查看,解决

引用chatGPT作答,根据您提供的截图和描述,您似乎有以下两个个性化需求:

1.点击左侧树节点,展示右侧树的所有父子节点的选中状态。
2.点击右侧树节点,展示该节点的选中状态。
针对第一个需求,您可以使用Tree组件的onSelect方法,根据当前选中的节点,通过递归遍历的方式找到该节点的所有父子节点,并设置它们的选中状态。

下面是一个示例代码:

import React, { useState } from "react";
import { Tree } from "antd";

const treeData = [
  {
    title: "Node 1",
    key: "1",
    children: [
      {
        title: "Node 1-1",
        key: "1-1",
      },
      {
        title: "Node 1-2",
        key: "1-2",
      },
    ],
  },
  {
    title: "Node 2",
    key: "2",
    children: [
      {
        title: "Node 2-1",
        key: "2-1",
      },
      {
        title: "Node 2-2",
        key: "2-2",
      },
    ],
  },
];

const Demo = () => {
  const [checkedKeys, setCheckedKeys] = useState([]);

  const handleLeftTreeSelect = (selectedKeys) => {
    const keysToCheck = [];
    const traverse = (node) => {
      keysToCheck.push(node.key);
      if (node.children) {
        node.children.forEach((child) => traverse(child));
      }
    };
    selectedKeys.forEach((key) => {
      const node = treeData.find((n) => n.key === key);
      if (node) {
        traverse(node);
      }
    });
    setCheckedKeys(keysToCheck);
  };

  const handleRightTreeSelect = (selectedKeys) => {
    setCheckedKeys(selectedKeys);
  };

  return (
    <div style={{ display: "flex" }}>
      <Tree
        style={{ flex: 1 }}
        treeData={treeData}
        onSelect={handleLeftTreeSelect}
      />
      <Tree
        style={{ flex: 1 }}
        treeData={treeData}
        checkedKeys={checkedKeys}
        checkable
        onSelect={handleRightTreeSelect}
      />
    </div>
  );
};

export default Demo;

针对第二个需求,您可以使用Tree组件的titleRender方法,自定义渲染节点的显示内容和点击行为。下面是一个示例代码:

import React, { useState } from "react";
import { Tree } from "antd";
import { CheckOutlined } from "@ant-design/icons";

const treeData = [
  {
    title: "Node 1",
    key: "1",
    children: [
      {
        title: "Node 1-1",
        key: "1-1",
      },
      {
        title: "Node 1-2",
        key: "1-2",
      },
    ],
  },
  {
    title: "Node 2",
    key: "2",
    children: [
      {
        title: "Node 2-1",
        key: "2-1",
      },
      {
        title: "Node 2-2",
        key: "2-2",
      },
    ],

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
在ant design中,Tree组件默认的展示方式是所有节点都是关闭的,需要手动点击节点才能展开子节点。要达到个性需求,需要使用Tree组件的checkedKeys属性和onCheck事件。

首先,我们需要将Tree的节点数据中,每个节点的父子关系信息也添加进去。例如下面的示例数据中,节点1和节点2都是节点0的子节点,节点8、9、10都是节点7的子节点。

const treeData = [
  {
    title: '0',
    key: '0',
    children: [
      { title: '1', key: '1', parentKey: '0' },
      { title: '2', key: '2', parentKey: '0' },
    ],
  },
  {
    title: '3',
    key: '3',
  },
  {
    title: '4',
    key: '4',
    children: [
      {
        title: '5',
        key: '5',
        children: [
          { title: '6', key: '6', parentKey: '5' },
          {
            title: '7',
            key: '7',
            parentKey: '5',
            children: [
              { title: '8', key: '8', parentKey: '7' },
              { title: '9', key: '9', parentKey: '7' },
              { title: '10', key: '10', parentKey: '7' },
            ],
          },
        ],
      },
    ],
  },
];

接着,在Tree组件上添加checkedKeys属性和onCheck事件:

const [checkedKeys, setCheckedKeys] = useState([]);

const handleCheck = (checkedKeys) => {
  setCheckedKeys(checkedKeys);
};

<Tree
  treeData={treeData}
  checkedKeys={checkedKeys}
  onCheck={handleCheck}
/>

这样,当勾选或取消勾选节点时,会触发onCheck事件,我们在事件处理函数handleCheck中更新checkedKeys的值。

接下来,我们需要修改Tree的节点渲染方式,根据节点的父子关系和勾选状态来实现节点的展开和收起。

const titleRender = (node) => {
  const { title, key, parentKey } = node;
  const isChecked = checkedKeys.includes(key);

  if (!parentKey) {
    // 如果节点是根节点,直接返回标题和勾选框
    return (
      <div>
        <Checkbox checked={isChecked} onChange={() => {}} />
        {title}
      </div>
    );
  }

  // 通过递归查找父节点,计算出该节点的层级
  const findParent = (parentKey) => {
    let level = 0;
    const find = (key) => {
      const parent = treeData.find((node) => node.key === key);
      if (parent && parent.parentKey) {
        level++;
        find(parent.parentKey);
      }
    };
    find(parentKey);
    return level;
  };
  const level = findParent(parentKey);

  // 根据节点层级计算出该节点的缩进
  const indent = level * 24;

  // 根据勾选状态计算出该节点是否展开
  const expand = isChecked || treeData.find((node) => node.key === parentKey).expand;

  // 如果有子节点,渲染容器和折叠/展开图标
  if (node.children) {
    const handleExpand = () => {
      const newData = [...treeData];
      const node = newData.find((node) => node.key === key);
      node.expand = !node.expand;
      setTreeData(newData);
    };
    return (
      <div style={{ paddingLeft: indent }}>
        <Icon
          type={expand ? 'minus-square' : 'plus-square'}
          onClick={handleExpand}
          style={{ marginRight: 8 }}
        />
        <Checkbox checked={isChecked} onChange={() => {}} />
        {title}
      </div>
    );
  }

  // 否则只渲染标题和勾选框
  return (
    <div style={{ paddingLeft: indent }}>
      <Checkbox checked={isChecked} onChange={() => {}} />
      {title}
    </div>
  );
};

<Tree treeData={treeData} checkedKeys={checkedKeys} onCheck={handleCheck} titleRender={titleRender} />

在此基础上,我们还需要实现点击左侧节点时,展示右侧所有相关节点的功能。我们可以添加一个左侧菜单,用来存储需要展开的节点key,当菜单被点击时触发展开事件。

const [menuData, setMenuData] = useState([]);

const handleMenuClick = (key) => {
  const newData = [...menuData];
  if (newData.includes(key)) {
    // 如果菜单已经包含该节点,将其从菜单中删除
    const index = newData.indexOf(key);
    newData.splice(index, 1);
  } else {
    // 如果菜单中没有该节点,将其添加到菜单中
    newData.push(key);
  }
  setMenuData(newData);

  // 展开所有相关节点
  const checkedKeys = getCheckedKeys(newData);
  setCheckedKeys(checkedKeys);
};

const getCheckedKeys = (menuData) => {
  const keys = [];
  const findChildren = (node) => {
    keys.push(node.key);
    if (node.children && node.children.length > 0) {
      node.children.forEach(findChildren);
    }
  };
  treeData.forEach((node) => {
    if (menuData.includes(node.key)) {
      findChildren(node);
    }
  });
  return keys;
};

const titleRender = (node) => {
  // ...

  const handleExpand = () => {
    const newData = [...treeData];
    const node = newData.find((node) => node.key === key);
    node.expand = !node.expand;
    setTreeData(newData);

    // 如果节点被展开,展开所有相关节点
    if (!node.expand) {
      const checkedKeys = getCheckedKeys(menuData);
      setCheckedKeys(checkedKeys);
    }
  };

  // ...

  return (
    <div style={{ paddingLeft: indent }} onClick={() => handleMenuClick(key)}>
      {node.parentKey && (
        <Icon
          type={menuData.includes(node.key) ? 'folder-open' : 'folder'}
          style={{ marginRight: 8 }}
        />
      )}
      <Checkbox checked={isChecked} onChange={() => {}} />
      {title}
    </div>
  );
};

return (
  <>
    <Menu mode="inline">
      {treeData.map((node) =>
        node.children ? (
          <Menu.SubMenu key={node.key} title={node.title}>
            {node.children.map((child) => (
              <Menu.Item key={child.key} onClick={() => handleMenuClick(child.key)}>
                {child.title}
              </Menu.Item>
            ))}
          </Menu.SubMenu>
        ) : (
          <Menu.Item key={node.key} onClick={() => handleMenuClick(node.key)}>
            {node.title}
          </Menu.Item>
        )
      )}
    </Menu>
    <Tree
      treeData={treeData}
      checkedKeys={checkedKeys}
      onCheck={handleCheck}
      titleRender={titleRender}
    />
  </>
);

在此代码中,我们使用了一个menuData状态来保存当前需要展开的节点key。当菜单被点击时,通过handleMenuClick函数来更新menuData的值,然后调用getCheckedKeys函数,根据menuData中的节点,递归查找出所有的子节点key,再把这些key设置到checkedKeys中。当一个节点被收起时,我们也需要展开所有相关节点,使用setInterval来给树刷新留些时间,避免因为渲染延时导致无法正确展开节点。

以上代码只是一个示例,具体的实现方式可能因业务需求而异。
如果我的回答解决了您的问题,请采纳!