贴合业务只有两级 可以放开末尾的按钮控制
import React, {Key, useState} from "react";
import {Input, Modal, Space, Tree} from "antd";
import {FieldDataNode} from "rc-tree/lib/interface";
import type {TreeProps} from 'antd/es/tree';
import {
EditTwoTone,
PlusCircleTwoTone,
MinusCircleTwoTone,
CloseCircleTwoTone,
CheckCircleTwoTone,
} from "@ant-design/icons";
import {nanoid} from "nanoid";
type DataNode = FieldDataNode<{
key: string;
title: string;
level: number;
editValue?: string;
isEditable?: boolean;
}>
const treeData: DataNode[] = [
{
title: "0",
key: nanoid(),
level: 1,
children: [
{
title: "0-1",
level: 2,
key: nanoid(),
},
{
title: "0-2",
level: 2,
key: nanoid(),
},
{
title: "0-3",
level: 2,
key: nanoid(),
},
{
title: "0-4",
level: 2,
key: nanoid(),
},
],
},
];
export const CategoryModal = (props) => {
const [gData, setGData] = useState<DataNode[]>([{
title: "root",
key: 'root',
level: 0,
children: treeData
}]
);
const [expandedKeys, setExpandedKeys] = useState<Key[]>(['root'].concat(gData.map(item => item.key)));
const onAdd = (key) => {
const data = [...gData];
loop(data, key, (item) => {
const children = Array.isArray(item.children) ? [...item.children] : []
children.push({
key: nanoid(), // 这个 key 应该是唯一的
title: "default",
level: item.level + 1
})
item.children = children
});
setExpandedKeys([...expandedKeys, key])
setGData(data);
};
const onEdit = (key) => {
const data = [...gData];
closeNode(data);
loop(data, key, (item) => {
item.editValue = item.title
item.isEditable = true
});
setGData(data);
};
// input 改变
const onChange = (e, key) => {
const data = [...gData];
loop(data, key, (item) => {
item.editValue = e.target.value
});
setGData(data)
};
// 保存
const onSave = (key) => {
const data = [...gData];
loop(data, key, (item) => {
item.isEditable = false
item.title = item.editValue || ''
});
console.log(data)
setGData(data);
};
// 关闭输入
const onClose = (key) => {
const data = [...gData];
loop(data, key, (item) => {
item.editValue = item.title
item.isEditable = false
});
setGData(data);
};
const closeNode = (data) =>
data.forEach((item) => {
item.isEditable = false;
if (item.children) {
closeNode(item.children);
}
});
const onDelete = (key) => {
const data = [...gData];
loop(data, key, (_, index, arr) => {
arr.splice(index, 1);
});
setGData(data);
};
// 找到节点数据并回调处理节点方法 callback回调: node-当前节点 i-当前节点序号 data-包含当前节点的节点组 parentNode 当前节点父节点
const loop = (
data: DataNode[],
key: React.Key,
callback: (node: DataNode, i: number, data: DataNode[], parent?: DataNode) => void,
parentNode?: DataNode,
) => {
for (let i = 0; i < data.length; i++) {
if (data[i].key === key) {
return callback(data[i], i, data, parentNode);
}
if (data[i].children) {
loop(data[i].children!, key, callback, data[i]);
}
}
};
const onDrop: TreeProps['onDrop'] = info => {
const dropKey = info.node.key; // 终止键
const dragKey = info.dragNode.key;// 拖动键
const dropPos = info.node.pos.split('-'); // 放置位置
// -1是移动到和他平级在他上面 1是移动到和他平级在他下面 0是移动到他下面作为他子集
const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
const data = [...gData];
// 被拖动的节点-dragKey
let dragObj: DataNode;
let dragIndex: number;
let dragArr: DataNode[];
let dragParent: DataNode | undefined;
loop(data, dragKey, (item, index, arr, parent) => {
dragObj = item;
dragIndex = index
dragArr = arr
dragParent = parent
});
// 放置的节点-dropKey
loop(data, dropKey, (item, index, arr, parent) => {
console.log(dragParent?.key, parent?.key, item.key, dropPosition)
// 同级
if (dragParent?.key == parent?.key) {
dragArr.splice(dragIndex, 1) // 卸载
if (dropPosition === -1) {
arr.splice(index!, 0, dragObj!); // 移动到和他平级在他上面
} else {
arr.splice(index! + 1, 0, dragObj!); // 移动到和他平级在他下面
}
}
// 同父级
if (dragParent?.key == item.key) {
dragArr.splice(dragIndex, 1) // 卸载
item.children?.splice(0, 0, dragObj!) // 放到子节点第一个
}
});
setGData(data);
};
const handleOk = () => {}
const handleCancel = () => {
props.closeModel()
}
return (
<Modal
open={props.open}
onOk={handleOk}
title='Hello World'
onCancel={handleCancel}
>
<Tree
draggable
blockNode
onDrop={onDrop}
expandedKeys={expandedKeys}
onExpand={setExpandedKeys}
treeData={gData}
titleRender={(nodeData: DataNode) => {
if (nodeData.isEditable) {
return (
<Space>
<Input maxLength={10} value={nodeData.editValue || ''} onChange={(e) => onChange(e, nodeData.key)}/>
<CloseCircleTwoTone twoToneColor="#f81d22" onClick={() => onClose(nodeData.key)}/>
<CheckCircleTwoTone onClick={() => onSave(nodeData.key)}/>
</Space>
);
} else {
return (
<Space>
<span>{nodeData.title}</span>
{nodeData.level > 0 && (
<EditTwoTone onClick={() => onEdit(nodeData.key)}/>
)}
{nodeData.level < 2 && (
<PlusCircleTwoTone twoToneColor="#95de64" onClick={() => onAdd(nodeData.key)}/>
)}
{nodeData.level > 0 && (
<MinusCircleTwoTone twoToneColor="#f81d22" onClick={() => onDelete(nodeData.key)}/>
)}
</Space>
);
}
}}
>
</Tree>
</Modal>
);
}
参考
https://juejin.cn/post/6844903815586512910
https://blog.csdn.net/weixin_44147791/article/details/124084064
https://ant.design/components/tree-cn/