基于antd实现动态修改节点的Tree组件

文章讲述了如何在基于React和AntDesign的项目中,利用Tree组件实现动态添加和删除节点,以及自定义节点时的输入验证和下拉菜单功能。作者详细介绍了DropdownInput组件的创建和使用,以及编辑节点和删除节点的处理逻辑。

前言

之前遇到一个需求,可对于任意节点添加或删除子节点。首先技术栈是基于react+ant design,ant提供了Tree组件,但都是根据固定的数据渲染出树结构,如果需要新增或删除节点,官网并未提供。

实现过程

新增节点

首先,要记录选中节点,在有选中的情况下点击全局的新增按钮,就相当于在选中的节点下新增子节点,否则直接在最外层节点添加新的节点(此时的情况就是有多个并列的根节点)。当然也可以直接点击节点出现下拉菜单,选择操作
在这里插入图片描述

然后,实现新增功能,在点击新增按钮之后,相应的节点位置出现输入框,按回车或者输入框失去焦点代表输入完成。找到插入位置,将新增的节点插入。

输入状态:
在这里插入图片描述
输入完成后:
在这里插入图片描述
需要自定义节点,点击节点(ant Dropdown组件也支持右键)显示下拉弹窗。
这里的DropdownInput是自定义的组件,因为需要校验输入内容

// DropdownInput组件
import {
   
    Dropdown, Input } from "antd";
import React, {
   
   
  forwardRef,
  useEffect,
  useImperativeHandle,
  useRef,
  useState,
} from "react";
import type {
   
    InputProps } from "antd";
import _ from "lodash";

interface DropdownInputType extends InputProps {
   
   
  errorInfo?: string;
  initValue?: string;
}

const DropdownInputFun: React.ForwardRefRenderFunction<
  unknown,
  DropdownInputType
> = (props, ref) => {
   
   
  const {
   
    errorInfo, initValue, onChange, onBlur, onPressEnter } = props;
  const [open, setOpen] = useState<boolean>(false);
  const [errorText, setErrorText] = useState<string>("请输入中英文数字及下划线");
  const [value, setValue] = useState<string>(""); // 值

  const inputRef = useRef<any>(null);

  useImperativeHandle(ref, () => inputRef?.current);

  useEffect(() => {
   
   
    if (initValue) setValue(initValue);
  }, [initValue]);

  useEffect(() => {
   
   
    if (errorInfo) setErrorText(errorInfo);
  }, [errorInfo]);

  /** 监听输入报错 */
  const handleChange = _.debounce((e: any, isSure = false) => {
   
   
    const {
   
    value } = e?.target;
    const reg = /^[a-zA-Z0-9_\u4e00-\u9fa5]+$/;
    if (!reg.test(value)) {
   
   
      setOpen(true);
    } else {
   
   
      setOpen(false);
      onChange?.(value);
    }
  }, 300);

  return (
    <Dropdown
      overlay={
   
   
        <div
          style={
   
   {
   
   
            background: "#fff",
            padding: "8px 12px",
            height: 20,
            boxShadow: "0px 2px 12px 0px rgba(0,0,0,0.06)",
          }}
        >
          {
   
   errorText}
        </div>
      }
      open={
   
   open}
    >
      <Input
        ref={
   
   inputRef}
        value={
   
   value}
        onChange={
   
   (e) => {
   
   
          e?.persist();
          setValue(e?.target?.value);
          handleChange(e);
        }}
        onBlur={
   
   (e) => {
   
   
          !open && onBlur?.(e);
        }}
        onPressEnter={
   
   (e: any) => {
   
   
          !open && onPressEnter?.(e);
        }}
        style={
   
   {
   
    width: 272, borderColor: open ? "red" : "" }}
      />
    </Dropdown>
  );
};
const DropdownInput = forwardRef(DropdownInputFun);
export 
可以通过在Tree组件中设置onSelect函数来实现点击节点文字展开收起。 具体实现步骤如下: 1.Tree组件中设置onSelect函数,该函数会在节点被选中时触发。 ```javascript <Tree onSelect={onSelect} // 其他属性 > // 树节点 </Tree> ``` 2. 在onSelect函数中判断被选中的节点是否有子节点,如果有则展开节点,否则不做任何操作。 ```javascript const onSelect = (selectedKeys, { node }) => { const { children } = node.props; if (children) { setExpandedKeys(expandedKeys => { const index = expandedKeys.indexOf(selectedKeys[0]); if (index > -1) { // 如果节点已展开,则收起节点 return [...expandedKeys.slice(0, index), ...expandedKeys.slice(index + 1)]; } else { // 如果节点未展开,则展开节点 return [...expandedKeys, selectedKeys[0]]; } }); } } ``` 3.Tree组件中设置expandedKeys属性,该属性为展开的节点的key的数组。 ```javascript <Tree onSelect={onSelect} expandedKeys={expandedKeys} // 其他属性 > // 树节点 </Tree> ``` 完整代码如下: ```javascript import React, { useState } from 'react'; import { Tree } from 'antd'; const Demo = () => { const [expandedKeys, setExpandedKeys] = useState([]); const onSelect = (selectedKeys, { node }) => { const { children } = node.props; if (children) { setExpandedKeys(expandedKeys => { const index = expandedKeys.indexOf(selectedKeys[0]); if (index > -1) { // 如果节点已展开,则收起节点 return [...expandedKeys.slice(0, index), ...expandedKeys.slice(index + 1)]; } else { // 如果节点未展开,则展开节点 return [...expandedKeys, selectedKeys[0]]; } }); } } return ( <Tree onSelect={onSelect} expandedKeys={expandedKeys} > // 树节点 </Tree> ); } export default Demo; ```
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值