如图
import React, { Component } from 'react';
import {Tree, Icon, TreeSelect, Col, Row, Button, Popconfirm, message} from 'antd';
import styles from '../style/tree.less';
const { TreeNode } = Tree;
class EditableTree extends Component {
constructor(props){
super(props)
this.state = {
expandedKeys: [],
data: [
// {
// modelGroupName: 'Root',
// defaultValue: 'Root',
// first: true,
// value: '0',
// key: '0',
// parentKey: '0',
// isEditable: false
// }
],
selectTreeVal: '',
showTitle: ''
}
this.changeTree = this.changeTree.bind(this)
}
componentDidMount(){
}
isNullUndefined(val){
if (typeof val === 'undefined' || val === '' || val === null) {
return true;
} else {
return false;
}
}
renderTreeNodes = data => data.map((item) => {
if (item.isEditable) {
item.title = (
<div onClick={(e) => {e.stopPropagation()}} id="treeDiv">
<input
className={styles.inputField}
value={item.modelGroupName}
onChange={(e) => this.onChange(e, item, data)}
/>
<Icon type='close' className='iconSty' onClick={(e) => this.onClose(item.key, item, data)} />
<Popconfirm
getPopupContainer={() => document.getElementById('treeDiv')}
title="确定要保存吗?"
onConfirm={(e) => this.onSave(item, data)}
okText="确定"
placement="bottomLeft"
cancelText="取消"
>
<Icon type='check' className='iconSty' />
</Popconfirm>
</div>
);
} else {
item.title = (
<div id='treeDivdel' className={styles.titleContainer} onClick={() => this.onChangeTree(item)}>
<div>
{item.modelGroupName}
</div>
<div className={styles.operationField} >
{item.first ? null : (
<Icon className='iconSty' type='edit' onClick={(e) => {e.stopPropagation(); this.onEdit(data, item.key)}} />
)}
<Icon className='iconSty' type='plus' onClick={(e) => {e.stopPropagation(); this.onAdd(data, item.key)}} />
{item.first ? null : (
<Popconfirm
getPopupContainer={(triggerNode) => triggerNode.parentElement}
title="确定要删除吗?"
placement="bottomLeft"
onConfirm={(e) => {e.stopPropagation(); this.onDelete(data, item)}}
onCancel={(e) => e.stopPropagation()}
okText="确定"
cancelText="取消"
>
<Icon className='iconSty' type='minus' onClick={(e) => e.stopPropagation()} />
</Popconfirm>
)}
</div>
</div>
)
}
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} value={item.value}>
{this.renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode {...item} />;
})
onAdd = (data, e) => {
console.log('add', data);
this.addNode(e, data);
this.setState({
expandedKeys: this.state.expandedKeys,
data: data
});
}
addNode = (key, data) => data.map((item, index) => {
if (item.key === key) {
if (item.children) {
item
.children
.push({
modelGroupName: '',
defaultValue: '',
first: false,
value: Math.random().toString(36).substring(2, 6),
key: Math.random().toString(36).substring(2, 6),
parentKey: key,
parentId: item.id ? item.id : '',
parentPId: item.pid ? item.pid : '',
isEditable: true
});
} else {
item.children = [];
item
.children
.push({
modelGroupName: '',
first: false,
defaultValue: '',
value: Math.random().toString(36).substring(2, 6),
key: Math.random().toString(36).substring(2, 6),
parentKey: key,
parentId: item.id ? item.id : '',
parentPId: item.pid ? item.pid : '',
isEditable: true
});
}
return;
}
if (item.children) {
this.addNode(key, item.children)
}
})
onDelete = (data, item) => {
// console.log('delete');
if (item.id) {
this.props.sendDelModelList(item)
} else {
this.deleteNode(item.key, data);
this.setState({
data: data
});
}
}
deleteNode = (key, data) => data.map((item, index) => {
if (item.key === key) {
data.splice(index, 1);
} else {
if (item.children) {
this.deleteNode(key, item.children)
}
}
})
onEdit = (data, key) => {
// console.log('edit');
this.editNode(key, data);
this.setState({
data: data
});
}
editNode = (key, data) => data.map((item) => {
if (item.key === key) {
item.isEditable = true;
} else {
item.isEditable = false;
}
item.modelGroupName = item.defaultValue; // 当某节点处于编辑状态,并改变数据,点击编辑其他节点时,此节点变成不可编辑状态,value 需要回退到 defaultvalue
if (item.children) {
this.editNode(key, item.children)
}
})
onClose = (key, item, data) => {
// console.log('close');
this.closeNode(key, item.defaultValue, data);
this.setState({
data: data
});
}
closeNode = (key, defaultValue, data) => data.map((item) => {
item.isEditable = false;
if (item.key === key) {
item.modelGroupName = defaultValue;
}
if (item.children) {
this.closeNode(key, defaultValue, item.children)
}
if (this.isNullUndefined(item.modelGroupName) && this.isNullUndefined(item.defaultValue)){
this.deleteNode(item.key, data);
}
})
onSave = (item, data) => {
// console.log('save')
// console.log(item)
if (this.isNullUndefined(item.modelGroupName)){
return message.warning('请填写模型名称');
}
this.saveNode(item.key, data);
this.setState({
data: data
});
this.props.sendAddModelList(item) // 调用接口的方法
}
saveNode = (key, data) => data.map((item) => {
if (item.key === key) {
item.modelGroupName = item.modelGroupName;
item.defaultValue = item.modelGroupName;
}
if (item.children) {
this.saveNode(key, item.children)
}
item.isEditable = false;
})
onChange = (e, item, data) => {
this.changeNode(item.key, e.target.value, data);
this.setState({
data: data
});
}
changeNode = (key, value, data) => data.map((item) => {
if (item.key === key) {
item.modelGroupName = value;
}
if (item.children) {
this.changeNode(key, value, item.children)
}
})
onChangeTree = (item) => {
console.log(item, "====");
// this.setState({
// selectTreeVal: value
// })
};
changeTree(e){
if (this.isNullUndefined(e)) {
this.props.selectModel({ modelGroupName: '' })
}
}
render() {
const { modelData } = this.props
return (
<div >
<TreeSelect onChange={this.changeTree} treeDefaultExpandAll allowClear style={{ width: '100%' }} >
{this.renderTreeNodes(modelData)}
</TreeSelect>
</div>
)
}
}
export default EditableTree;
css 样式 新建一个样式 tree.less
.inputField {
border: none;
border-bottom: 1px solid;
background: none;
line-height: normal;
min-width: 50px;
outline: none;
&:focus {
outline: none;
}
}
#treeDivdel{
display: flex;
justify-content: space-between;
}
.titleContainer {
.operationField {
visibility: hidden;
}
&:hover {
.operationField {
visibility: visible;
}
}
}
:global {
.ant-tree li .ant-tree-node-content-wrapper:hover {
background-color: unset;
}
}
.iconSty{
margin-left: 10px;
font-size: 12px;
border: 2px solid #e8e8e8;
color: rgba(0, 0, 0, 0.65);
background: #e8e8e8;
border-radius: 10px;
padding: 5px;
}
:global {
.ant-select-selection-selected-value{
.iconSty {
display: none;
}
}
}
参考 https://github.com/JerryMissTom/react-editable-tree/blob/master/EditableTree.js