React 树形组件数据追加至节点中,点击节点弹出对话框

我编写了一个demo实现点击标签弹出窗体,在窗体中输入信息然后把数据保存到树形组件中,最后再点击添加上去的组件弹出抽屉的窗体。

部分代码:
//接收项目组件传递的数据 (这段功能已经实现)
    const handleSave=(formData)=>{
        debugger
        // setData([...treeData, formData]); // 将表单数据添加到数组中
        
        const newNode = {
        title: `${formData.newNodeNo}-${formData.newNodeName}`,
        value: randomId,
        children: [
            { title: `${formData.newNodeNo}-${formData.newNodePName} `, value: `${randomId}-1`},
            { title: <PlusCircleOutlined onClick={showModalForm} /> , value: `${randomId}-2`}
            ],  
    };

    const newData = [...treeData];
    if (!selectedNode) {
      newData.push(newNode);
    } else {
      selectedNode.children.push(newNode);
    }
    setTreeData(newData);
    }

 const showModalBaisc=()=>{
      setIsModalOpenBasic(true);
    };

这段代码就是实现上面那段代码保存后的数据,点击里面的+号标签弹出一个窗体输入信息

![img](https://img-mid.csdnimg.cn/release/static/image/mid/ask/648177942386189.png "#left")

![img](https://img-mid.csdnimg.cn/release/static/image/mid/ask/739687942386148.png "#left")
我想要以下代码实现图片中所描述的功能,把输入的数据保存到这个+号标签的下面注意这个下面是子节点不是平级的,然后点击这个子节点弹出抽屉的对话窗体,主要就是这段代码调整一下就可以了。

const FormSave = (data,value,node) => {
        // 找到目标节点并添加新的子节点
        let newData = [...treeData];
        const addDataToNode = (data, nodeId, newData) => {
            for (let i = 0; i < data.length; i++) {
            const item = data[i];
            nodeId=item.value;
            if (item.value === nodeId) {
                debugger
                if(item.title==<PlusCircleOutlined onClick={showModalForm} />)
                {
                    item.children.push(newData);
                }
                debugger
                console.log('标签',item.title);
                break;
            } else if (item.children && item.children.length > 0) {
                addDataToNode(item.children, nodeId, newData);
            }
            
            }
        };
        const newNodeData = {
            title: data.FormName,
            value: Math.random().toString(36).substr(2, 9),
            onClick: () => showModalBaisc(),
            children: [],
            
        };
        
        addDataToNode(newData, node, newNodeData);
        setTreeData(newData);
        setIsModalOpenForm(false);
        // 将要传递给BasicDrawer子组件的数据赋值给basicData状态变量
        setBasicData({ ...data, nodeId: randomId });
      };
return(

        <div>
            <ProjectComponets onSave={handleSave} />
            <TreeSelect  {...tProps} onSelect={onSelect}   treeData={treeData} />
             <FormComponets
                visible={isModalOpenForm}
                onClose={handleCloseModal}
                onSave={(data, value, node) => FormSave(data, value, node, setTreeData)}
                treeData={treeData}
                setTreeData={setTreeData}  // 确保将setTreeData函数作为属性传递给FormComponets
            />
            <BasicDrawer visible={isModalOpenBaic} basicData={basicData} 
            onClose={handleCloseModalBasic}
            />
        </div>
    );

子组件
import React, { useState } from 'react';
import { Button, Drawer } from 'antd';

function BasicDrawer({visible,basicData}){
  
  const [open, setOpen] = useState(false);
  const onClose = () => {
        setOpen(false);
  };
 
    return (
      <>
       
        <Drawer title="Basic Drawer" visible={visible} placement="right"  onClose={onClose}>
          <p> {basicData ? basicData.ObjectName : ''}.</p>
          <p> {basicData ? basicData.FormName : ''}.</p>
          <p> {basicData ? basicData.EnglishName : ''}</p>
        </Drawer>
      </>
    );
}export default BasicDrawer

可能需要以下几步:

  1. 在React中创建一个树形组件。可以使用第三方库如react-treebeardreact-sortable-tree等来简化开发。
  2. 加载树形数据,把数据渲染成树型结构,并把数据储存在状态中。
  3. 创建一个弹出框组件,用于显示节点详细信息。可以使用第三方库如react-modalreact-bootstrap-modal等来简化开发。
  4. 在树形组件中添加事件处理函数,当用户点击某个节点时调用该函数并传入节点数据。函数根据节点数据动态生成弹出框内容,并打开弹出框显示详细信息。

简单的示例代码:

import React, { useState } from 'react';
import Treebeard from 'react-treebeard';
import Modal from 'react-modal';

const data = {
  name: 'Root',
  toggled: true,
  children: [
    {
      name: 'Node 1',
    },
    {
      name: 'Node 2',
      children: [
        {
          name: 'Node 2.1',
        },
      ],
    },
  ],
};

const App = () => {
  const [modalOpen, setModalOpen] = useState(false);
  const [selectedNode, setSelectedNode] = useState(null);

  const onToggle = (node, toggled) => {
    if (selectedNode) {
      selectedNode.active = false;
    }
    node.active = true;
    if (node.children) {
      node.toggled = toggled;
    }
    setSelectedNode(node);
    setModalOpen(true);
  };

  const closeModal = () => {
    if (selectedNode) {
      selectedNode.active = false;
    }
    setSelectedNode(null);
    setModalOpen(false);
  };

  const renderTree = (data) => (
    <Treebeard
      data={data}
      onToggle={onToggle}
    />
  );

  const renderModal = () => (
    <Modal isOpen={modalOpen} onRequestClose={closeModal}>
      {selectedNode && (
        <div>
          <h2>{selectedNode.name}</h2>
          {selectedNode.children && renderTree(selectedNode.children)}
        </div>
      )}
    </Modal>
  );

  return (
    <>
      {renderTree(data)}
      {renderModal()}
    </>
  );
};

export default App;

上图

img

img

img

img

img

img

  • 你可以参考下这个问题的回答, 看看是否对你有帮助, 链接: https://ask.csdn.net/questions/963550
  • 我还给你找了一篇非常好的博客,你可以看看是否有帮助,链接:React实战开发-----一个有关兰州疫情分析的软件,本人负责前端开发,本博客记录整个开发的流程,供大家参考
  • 除此之外, 这篇博客: react 手把手写一个登录界面,还能从中能学到什么?中的 组件化后 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 看到sin-in.component.tsx文件过于冗余,并且部分控件不能复用,样式也不能复用…下面就是抽离组件:
    新建components文件夹目录结构如下:

    • components
      • custom-button
        • custom-button.component.tsx
        • custom-button.style.scss
      • form-input
        • form-input.component.tsx
        • form-input.style.scss

    form-input.component.tsx

    import React from 'react'
    import './form-input.style.scss'
    
    type IProps=Readonly<{
        type:string;
        name:string;
        value:string;
        labelName:string;
        required:boolean;
        onChange:(e:any)=>void;
    }>;
    
    const FormInput = ({labelName,...otherProps}:IProps) => {
        return (
            <div className='group'>
                <input className='form-input' {...otherProps}/>
                {
                    labelName? 
                    (<label className={`${otherProps.value.length?'shrink':''} form-input-label`}>{labelName}</label>)
                    :null
                }
               
            </div>
        )
    }
    export default FormInput;
    

    form-input.style.scss

    $sub-color:grey;
    $main-color:black;
    @mixin shrinkLabel {
        top:-14px;
        font-size: 12px;
        color:$main-color
    }
    
    .group{
        position: relative;
        margin:45px 0;
        
        .form-input{
            background: none;
            background-color: white;
            color:$sub-color;
            font-size: 18px;
            padding: 10px 10px 10px 5px;
            display: block;
            width: 100%;
            border:none;
            border-radius: 0;
            border-bottom: 1px solid $sub-color;
            margin:25px 0;
    
            &:focus{
                outline: none;
            }
            &:focus ~ .form-input-label{
                @include shrinkLabel()
            }
        }
        
        input[type='password']{
            letter-spacing: 0.3em;
        }
    
        .form-input-label{
            color:$sub-color;
            font-size: 16px;
            font-weight: normal;
            position: absolute;
            pointer-events: none;
            left: 5px;
            top: 10px;
            transition: 300ms ease all;
    
            &.shrink{
                @include shrinkLabel()
            }
        }
       
    }
    

    custom-button.component.tsx

    import React from 'react'
    import './custom-button.style.scss'
    
    const CustomButton = ({children,...otherProps}:any) => {
        return (
            <button
            {...otherProps}
             className='custom-button'>
                {children}
            </button>
        )
    }
    export default CustomButton
    

    custom-button.syle.scss

    .custom-button{
        min-width: 165px;
        width: auto;
        height: 50px;
        letter-spacing:0.5px;
        line-height: 50px;
        padding: 0 35px 0 35px;
        font-size: 15px;
        background-color: black;
        color:white;
        text-transform: uppercase;
        font-weight:bolder;
        border: none;
        cursor: pointer;
    
        &:hover{
            background-color: white;
            color:black;
            border:1px solid black;
        }
    }
    

    下面我我们回到pages文件夹下的 sign-in目录中
    sign-in.component.tsx

    import React from 'react'
    import FormInput from '../form-input/form-input'
    import CustomButton from  '../custom-button/custom-button.component'
    import './sign-in.style.scss'
    
    export default class SignIn extends React.Component<any, any>{
        constructor(props: any) {
            super(props);
            this.state = {
                email: '',
                password: ''
            }
            this.handleInput = this.handleInput.bind(this);
            this.handleSubmit = this.handleSubmit.bind(this)
    
        }
        handleInput(e: React.FormEvent) {
            const { value = '', name = '' } = e.target as any;
            this.setState({
                [name]: value
            })
        }
        handleSubmit(e: React.FormEvent) {
            e.preventDefault();
    
            console.log(this.state);
        }
        render() {
            const { email, password } = this.state;
            return (
                <div className="sign-in">
                    <h2>I already have an account</h2>
                    <span>Sign in with your email and passowrd</span>
    
                    <form onSubmit={this.handleSubmit}>
                        <FormInput
                            type="email"
                            name='email'
                            value={email}
                            labelName='Email'
                            required
                            onChange={this.handleInput}
                        />
                        <FormInput
                            type="password"
                            name='password'
                            labelName='Password'
                            value={password}
                            onChange={this.handleInput}
                            required
                        />
                       <CustomButton type='submit'>Sign in</CustomButton>
                    </form>
                </div>
            )
        }
    }
    
    

    sign-in.style.scss

    .sign-in{
        width: 30vw;
        display: flex;
        flex-direction: column;
       justify-content: center;
    }