如题。基于一个React组件改造的树型菜单(仅是加了一个<ItemPanel结构)。
目标:保存展开节点,打开来一个节点时,收起上一个打开节点。
如何实现 ?
改造如下:
... ...
interface ItemNode {
parentId: string;
color: string;
label: string;
id: string;
desDatabase: string;
desSchema: string;
desTableName: string;
}
interface TreeNode {
isQueryChildren: boolean;
id: string;
parentId: string;
topicName: string;
describeInfo: string;
//null表示有子元素,只是需要请求
children: (TreeNode | ItemNode)[] | null;
}
//API每次请求的时候需要将数据保存在data和TreeNode中
const needAPI = (id: string): (TreeNode | ItemNode)[] => {
if (id === "ROOT") {
return [
{
isQueryChildren: false,
id: "1",
topic: "topic1",
parentId: "ROOT",
describeInfo: "sad",
children: null,
topicName: "主题集标题1"
},
{
isQueryChildren: false,
id: "2",
topic: "topic2",
parentId: "ROOT",
describeInfo: "sad",
children: null,
topicName: "主题集标题2"
},
{
isQueryChildren: false,
id: "3",
topic: "topic3",
parentId: "ROOT",
describeInfo: "sad",
children: null,
topicName: "主题集标题3"
}
];
} else if (id === "1") {
return [
{
parentId: "1",
color: "#FA8C16",
label: "表名称1",
id: "31"
},
{
parentId: "1",
color: "#FA8C16",
label: "表名称2",
id: "32"
},
{
parentId: "1",
color: "#FA8C16",
label: "表名称3",
id: "31"
},
{
parentId: "1",
color: "#FA8C16",
label: "表名称4",
id: "32"
},
{
parentId: "1",
color: "#FA8C16",
label: "表名称5",
id: "31"
},
{
parentId: "1",
color: "#FA8C16",
label: "表名称6",
id: "32"
}
];
} else if (id === "2") {
return [
{
parentId: "1111",
color: "#00BFFF",
label: "表名称3",
id: "33"
},
{
isQueryChildren: false,
id: "33",
topic: "topic2",
parentId: "root",
describeInfo: "sad",
children: null,
topicName: "sadsa"
}
];
}
return [];
};
interface ResponseInterface {
isSuccess: boolean;
message: string;
result: {
topicElementList: [
{
dataSetId: string;
desDatabase: string;
desSchema: string;
desTableName: string;
id: string;
topicId: string;
}
];
topicElementPages: 0;
topicList: [
{
describeInfo: string;
groupId: string;
id: string;
parentId: string;
topicName: string;
userId: string;
}
];
};
}
//------------------------------------------
//添加参数,更改原needAPI(测试数据),标记
const queryData = async (id: "ROOT"): Promise<(TreeNode | ItemNode)[]> => {
const param = {
parent_id: id,
current_page: 1
};
const resData = await new Promise<(TreeNode | ItemNode)[]>(
(resolve, reject) => {
NetUitl.get(
"/topic/get/all",
param,
(res: ResponseInterface) => {
if (res.isSuccess === false) {
reject(res.message);
}
const mergeListAndElement: (TreeNode | ItemNode)[] = [];
const { topicElementList, topicList } = res.result;
topicList.forEach(item => {
mergeListAndElement.push({
isQueryChildren: false,
id: item.id,
topicName: item.topicName,
parentId: id,
describeInfo: item.describeInfo,
children: null
});
});
topicElementList.forEach(item => {
mergeListAndElement.push({
parentId: item.topicId,
color: "#00BFFF",
label: item.desTableName,
id: item.id,
desDatabase: item.desDatabase,
desSchema: item.desSchema,
desTableName: item.desTableName
});
});
resolve(mergeListAndElement);
},
(res: any) => {
reject();
}
);
}
);
return resData;
};
//------------------------------------------
const queryDataById = async (id: string, update: () => void) => {
let res = await needAPI(id);
let node = treeNodeMap[id];
if ("children" in node) {
//建立树关系
node.children = res;
//将节点添加到字典,保存其数据
res.forEach(item => {
treeNodeMap[item.id] = item;
});
update();
}
};
//用来保存node的数据的引用
//使用id和节点对应
//会在渲染树的时候加上去的
const treeNodeMap: {
[index: string]: TreeNode | ItemNode;
} = {
ROOT: {
isQueryChildren: false,
id: "ROOT",
topicName: "ROOT",
parentId: "",
describeInfo: "根元素",
//null表示有子元素,只是需要请求
children: []
}
};
class FlowItemPanel extends React.Component<
{},
{
count: number;
}
> {
// const FlowItemPanel = () => (
state = {
count: 0
};
root: TreeNode = treeNodeMap["ROOT"] as any;
handleUpdate = () => {
this.setState(v => {
return {
count: v.count + 1
};
});
};
renderTree = (data: (TreeNode | ItemNode)[]) => {
let eles: React.ReactNode[] = [];
data.map(item => {
if (!("children" in item)) {
eles.push(
<ItemPanel className={styles.itemPanel}>
<Item
type="node"
size="72*72"
shape="flow-circle"
model={{
label: item.label,
id: item.id,
}}
src={""}
>
<span
style={{
"user-select": "none"
}}
>
{item.label}
</span>
</Item>
</ItemPanel>
);
} else {
let children = item.children;
//随便搞一个符号表示这个展开后是需要加载的
let childelems: React.ReactNodeArray = [<div>....</div>];
if (children !== null) {
childelems = this.renderTree(children);
}
eles.push(
<Tree
content={item.topicName}
// type="ITEM"
// canHide
onChange={() => {
let node = treeNodeMap[item.id] as TreeNode;
if (node.children === null && node.isQueryChildren === false) {
//表示请求过了
node.isQueryChildren = true;
queryDataById(item.id, this.handleUpdate);
}
}}
>
{childelems.length === 0 ? null : childelems}
</Tree>
);
}
});
return eles;
};
componentDidMount() {
queryDataById("ROOT", this.handleUpdate);
}
render() {
const newNode = this.state.newNode;
return this.renderTree(this.root.children);
}
// );
}
export default FlowItemPanel;
原版:https://codesandbox.io/embed/...
###已经实现,详见如下地址:
https://codesandbox.io/s/vigorous-meitner-ob7g8