基于antd3 Tree实现可编辑菜单树,支持节点新增,编辑,删除
基于antd Tree,实现了可编辑菜单树,支持以下功能:
树节点 新增,编辑,删除
提示:以下代码,可参考
一、效果
二、完整代码
1.引入库
代码如下(示例):
import * as React from 'react';
import {
Modal, Button, Input, Popconfirm, Tree, Icon } from 'antd';
const {
TreeNode } = Tree;
var tempKey: any = '1000';
const App = (props) => {
const {
onHandleCancel = () => {
}, onHandleOk = () => {
}, data = {
} } = props;
var datavalue = [
{
value: "0",
defaultValue: "0",
key: "0",
parentKey: '0',
isEditable: false,
children: [
{
value: "0-1",
key: "0-1",
defaultValue: "0-1",
isEditable: false,
},
{
value: "0-2",
key: "0-2",
defaultValue: "0-2",
isEditable: false,
},
],
},
{
value: "1",
defaultValue: "1",
key: "1",
parentKey: '1',
isEditable: false,
children: [
{
value: "0-1-1",
key: "0-1-1",
defaultValue: "0-1-1",
isEditable: false,
},
{
value: "0-2-1",
key: "0-2-1",
defaultValue: "0-2-1",
isEditable: false,
},
],
},
]
const [treeData, setTreeData] = React.useState(datavalue);
const onDragEnter = (info) => {
console.log(info);
};
// 拖拽
const onDrop = (info) => {
console.log(info);
const dropKey = info.node.props.eventKey;
const dragKey = info.dragNode.props.eventKey;
const dropPos = info.node.props.pos.split('-');
const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
const loop = (data, key, callback) => {
for (let i = 0; i < data.length; i++) {
if (data[i].key === key) {
return callback(data[i], i, data);
}
if (data[i].children) {
loop(data[i].children, key, callback);
}
}
};
const data = [...treeData];
let dragObj;
loop(data, dragKey, (item, index, arr) => {
arr.splice(index, 1);
dragObj = item;
});
if (!info.dropToGap) {
// Drop on the content
loop(data, dropKey, (item) => {
item.children = item.children || [];
// where to insert 示例添加到尾部,可以是随意位置
item.children.push(dragObj);
});
} else if (
(info.node.props.children || []).length > 0 && // Has children
info.node.props.expanded && // Is expanded
dropPosition === 1 // On the bottom gap
) {
loop(data, dropKey, (item) => {
item.children = item.children || [];
// where to insert 示例添加到头部,可以是随意位置
item.children.unshift(dragObj);
});
} else {
let ar;
let i;
loop(data, dropKey, (item, index, arr) => {
ar = arr;
i = index;
});
if (dropPosition === -1) {
ar.splice(i, 0, dragObj);
} else {
ar.splice(i + 1, 0, dragObj);
}
}
setTreeData(data);
};
//新增
const onAdd = (key) => {
console.log('onAdd', key);
var treeDataOld = JSON.parse(JSON.stringify(treeData));
var treeDataNew = addNode(key, treeDataOld);
setTreeData(treeDataNew);
tempKey++;
function addNode(key, data) {
data.forEach((item) => {
if (item.key === key) {
item.children
? item.children.push({
value: `默认值${
tempKey}`,
key: `${
tempKey}`
})
: (item.children = [
{
value: `默认值${
tempKey}`,
key: `${
tempKey}`
}
]);
} else {
if (item.children) {
addNode(key, item.children);
}
}
});
return data;
}
};
//删除
const onDelete = (key) => {
console.log('onAdd', key);
var treeDataOld = JSON.parse(JSON.stringify(treeData));
var treeDataNew = deleteNode(key, treeDataOld);
setTreeData(treeDataNew);
function deleteNode(key, arr) {
arr.map((item, index) => {
if (item.key == key) {
arr.splice(index, 1);
}
if (item.children) {
deleteNode(key, item.children);
}
});
return arr;
}
};
// const onChange = (e, key) => {
// console.log('onChange', e, key);
// var treeDataOld = JSON.parse(JSON.stringify(treeData));
// var treeDataNew = editNode(key, treeDataOld, e.target.value);
// setTreeData(treeDataNew);
// function editNode(key, data, val) {
// data.forEach((item) => {
// if (item.key === key) {
// item.title = val;
// } else {
// if (item.children) {
// editNode(key, item.children, val);
// }
// }
// });
// return data;
// }
// };
const onChange = (e, key) => {
changeNode(key, e.target.value, treeData);
setTreeData(treeData.slice());
};
const changeNode = (key, value, data) =>
data.forEach((item) => {
if (item.key === key) {
item.value = value;
}
if (item.children) {
changeNode(key, value, item.children);
}
});
const onEdit = (key) => {
editNode(key, treeData);
setTreeData(treeData.slice());
};
const editNode = (key, data) =>
data.forEach((item) => {
if (item.key === key) {
item.isEditable = true;
} else {
item.isEditable = false;
}
// item.value = item.defaultValue; // 当某节点处于编辑状态,并改变数据,点击编辑其他节点时,此节点变成不可编辑状态,value 需要回退到 defaultvalue
if (item.children) {
editNode(key, item.children);
}
});
//编辑树节点时直接选中
const callbackRef = React.useCallback((node) => {
node && node.focus();
node && node.select();
}, []);
const handleBlur = (e, item) => {
// let arr = _.cloneDeep(dataSource)
// const value = e.target.value;
// if (value) {//更改右侧树对应节点的值
// setDataSource(treeLip(arr, item.associativeId, e.target.value))
// }
// setEditType({ edit: false, id: item.associativeId })
}
const onSave = (key) => {
saveNode(key, treeData);
setTreeData(treeData.slice());
};
const saveNode = (key, data) =>
data.forEach((item) => {
if (item.key === key) {
item.defaultValue = item.value;
}
if (item.children) {
saveNode(key, item.children);
}
item.isEditable = false;
});
const InputCopy = (item) => {
return <Input autoFocus={
true} onBlur={
(e) => handleBlur(e, item)} ref={
callbackRef} maxLength={
40} />
}
const renderTreeNodes = (data) => {
let nodeArr = data.map((item) => {
if (item.isEditable) {
item.title = (
<div>
<input value={
item.value || ''} onChange={
(e) => onChange(e, item.key)} />
<Icon type="close" style={
{
marginLeft: 10 }} onClick={
() => onDelete(item.key)} />
<Icon type="check-circle" style={
{
marginLeft: 10 }} onClick={
() => onSave(item.key)} />
</div>
);
} else {
item.title = (
<div>
<span>{
item.value}</span>
<span>
<Icon type="plus" style={
{
marginLeft: 10 }} onClick={
() => onAdd(item.key)} />
<Icon type="form" style={
{
marginLeft: 10 }} onClick={
() => onEdit(item.key)} />
<Icon type="close" style={
{
marginLeft: 10 }} onClick={
() => onDelete(item.key)} />
</span>
</div>
);
}
if (item.children) {
return (
<TreeNode title={
item.title} key={
item.key} dataRef={
item}>
{
renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode title={
item.title} key={
item.key} />;
});
return nodeArr;
};
const iconRender = (node: any) => {
let htmlNode = <i className="icon iconfont icon-yuandian" />;
if (!node.isLeaf) {
htmlNode = (
<i style={
{
color: '#F2B223' }} className="icon iconfont icon-tree-folder-close" />
);
if (node.expanded) {
htmlNode = (
<i style={
{
color: '#F2B223' }} className="icon iconfont icon-tree-folder-open" />
);
}
}
return <React.Fragment>{
htmlNode}</React.Fragment>;
}
return (
<>
<Modal
title={
data?.header}
visible={
true}
onOk={
() => onHandleOk()}
onCancel={
() => onHandleCancel(false)}
okText="确认"
cancelText="取消"
maskClosable={
false}
>
<Tree
className="draggable-tree"
defaultExpandAll={
true}
showLine
showIcon
// icon={iconRender}
draggable
onDragEnter={
onDragEnter}
onDrop={
onDrop}
>
{
treeData?.length && renderTreeNodes(treeData)}
</Tree>
</Modal>
</>
);
};
export default App;