我编写了一个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);
};
这段代码就是实现上面那段代码保存后的数据,点击里面的+号标签弹出一个窗体输入信息


我想要以下代码实现图片中所描述的功能,把输入的数据保存到这个+号标签的下面注意这个下面是子节点不是平级的,然后点击这个子节点弹出抽屉的对话窗体,主要就是这段代码调整一下就可以了。
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
可能需要以下几步:
react-treebeard
、react-sortable-tree
等来简化开发。react-modal
、react-bootstrap-modal
等来简化开发。简单的示例代码:
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;
上图
看到sin-in.component.tsx
文件过于冗余,并且部分控件不能复用,样式也不能复用…下面就是抽离组件:
新建components文件夹
目录结构如下:
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;
}