項目開發的過程中經常會遇到樹狀的數據,比如:選城市,選分類,選公司部門等等等等。。。基本上都是保存的樹狀結構。
下面的圖就是一個典型的樹型結構,
- id,唯一值
- name,名字
- isLeaf,是否是最末級的葉子節點
- idPath,當前節點與所有父級節點的id以“,”拼接
- parentId, 父節點id(如果沒有父節點則此字段返回值爲null)
- children,孩子節點(如果沒有孩子節點則此字段返回值爲null)
關聯關係就是如果isLeaf爲true那就沒有children,如果爲false那就有children。
如果你項目的樹結構的深度不是很深、數據不是很多,完全可以忽略本博客。
但是筆者的項目這個分類有七層左右的深度,一共五千多條數據,所以就必須進行優化了,否則每次都要加載八秒左右纔會生成這個TreeSelect。
接下來開始進行優化,我們先優化sql:
SELECT
id as key,
name as title,
isLeaf,
parentId,
FROM AssetClassification
WHERE parentId = 前端傳過來的parentId;
初始化的第一層數據sql請後端朋友自行寫吧,我就不在寫了。
之前sql是把所有層級的分類都獲取了,而我們這次只獲取你點擊節點的子節點。
如果後端朋友還有什麼疑惑請在下方評論,筆者會十二小時之內解答。節日除外哈,週六週日正常解答。
接下來開始前端優化
筆者這邊是React項目,用的antd。
做此功能用了Tree組件。
接下來是代碼,註釋和說明我都寫在了代碼裏,
前端朋友們對於不懂的地方也請在下方留言哈!
import React, {Component} from 'react';
import {
Input,
Popover,
Select,
Tree,
Tooltip,
} from 'antd';
import {
getThePrimaryClassificationList, // 請求初始化最上級分類的方法
getAssetClassificationChildrenList, // 請求當前節點子節點的方法
getIsLeafClassificationData, // 獲取當前選中節點的所有葉子級節點的方法(如果你不需要此功能就忽視)
} from '../../services/asset/getClassification';
const { Option } = Select;
const { Search } = Input;
const { TreeNode } = Tree;
export default class Index extends Component {
constructor(props) {
super(props);
this.state = {
assetClassificationList: [], // 初始最上級的數據
expandedKeys: [],
autoExpandParent: true,
checkedKeys: [], // 選中的key
treeData: [], // 分類的所有數據
thePrimaryClassificationId: '',
quertClassificationStr: '',
};
}
componentDidMount() {
this.getThePrimaryClassificationData();
this.getAssetClassificationChildrenData();
}
onExpand = (expandedKeys) => {
// console.log('onExpand', expandedKeys);
// if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
this.setState({
expandedKeys,
autoExpandParent: false,
});
};
onCheck = async (checkedKeys) => {
this.setState({
checkedKeys,
})
let childKeys = [];
if (checkedKeys.length > 0) {
childKeys = await getIsLeafClassificationData(checkedKeys);
}
// 在這裏去觸發父組件,每次把結果返回給父組件
this.props.onChangeValue(childKeys)
// 這個是父組件要顯示的漢字
const quertClassificationStr = childKeys.map(item => item.title).join(',');
this.setState({
quertClassificationStr,
});
};
onSelect = (selectedKeys) => {
this.onExpand(selectedKeys);
};
onLoadData = (treeNode = {}) => {
return new Promise((resolve) => {
if (treeNode.props.children) {
resolve();
return;
}
setTimeout(async () => {
const childrenNodes = await getAssetClassificationChildrenList(treeNode.props.dataRef.key)
treeNode.props.dataRef.children = childrenNodes;
this.setState({
treeData: [...this.state.treeData],
});
resolve();
}, 500);
});
}
getThePrimaryClassificationData = async () => {
const res = await getThePrimaryClassificationList();
this.setState({
assetClassificationList: res,
});
}
// 得到初始化的第一層數據
getAssetClassificationChildrenData = async () => {
const { thePrimaryClassificationId } = this.state;
const classificationChildrenList = await getAssetClassificationChildrenList(thePrimaryClassificationId);
this.setState({
treeData: classificationChildrenList,
});
}
// 渲染樹節點
renderTreeNodes = (data) => {
return data.map((item) => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{this.renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode key={item.key} {...item} dataRef={item}/>;
}) || [];
}
render() {
const { treeData } = this.state;
const component = (
<div>
<Tree
checkable
loadData={this.onLoadData} // 每次加載數據的時候觸發,我們這次優化的關鍵!!!
onExpand={this.onExpand} // 展開的組件的時候觸發
expandedKeys={this.state.expandedKeys}
autoExpandParent={this.state.autoExpandParent}
onCheck={this.onCheck} // 選中的時候觸發
checkedKeys={this.state.checkedKeys}
onSelect={this.onSelect} // 點擊行文字的時候觸發()
style={{
height: '300px',
overflow: 'auto',
}}
>
{this.renderTreeNodes(treeData)}
</Tree>
</div>
);
return (
<div>
<Popover
content={component}
trigger="click"
placement="bottom"
>
<div>
點此顯示分類選擇框
</div>
</Popover>
</div>
);
}
}