需求
如下圖的一個(gè)帶搜索功能的樹形選擇器,搜索的時(shí)候隱藏掉沒有關(guān)聯(lián)的節(jié)點(diǎn),同時(shí),如果子節(jié)點(diǎn)中有命中的,則不隱藏。

示例圖,來自Antd
思路
- 首先,這個(gè)樹形選擇器的層級(jí)并不能確定,這里考慮用一個(gè)方法遞歸調(diào)用來實(shí)現(xiàn)。
- 遞歸實(shí)現(xiàn)的思路應(yīng)該從最后一個(gè)子節(jié)點(diǎn)思考,并且父節(jié)點(diǎn)在子節(jié)點(diǎn)命中了搜索詞的時(shí)候也要顯示。
代碼實(shí)現(xiàn)
最后實(shí)現(xiàn)的代碼就如下,覺得還是比較有趣的,也算是寫了很久無聊的業(yè)務(wù)代碼寫點(diǎn)邏輯換個(gè)心情吧。
鋪墊一下,data的數(shù)據(jù)結(jié)構(gòu):
treeData: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
name: PropTypes.string,
subTrees: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
name: PropTypes.string,
iconClassName: PropTypes.string,
})
),
})
),
遞歸function代碼如下:
renderTreeNodes = data => {
const { searchValue, isSearchFilter } = this.props;
const returnData = [];
for (const item of data) {
const { name } = item;
let title = this.highlightSearchValue(name, searchValue);
if (item.iconClassName) {
title = (
<span>
<i className={`${ICON_FONT} ${item.iconClassName}`} />
{title}
</span>
);
}
if (item.subTrees) {
const subTreeNodes = this.renderTreeNodes(item.subTrees);
if (
(isSearchFilter && subTreeNodes.length > 0) ||
(!isSearchFilter || name.indexOf(searchValue) > -1)
) {
// if on searchFilter mode, push the parent node when child nodes exist
returnData.push(
<TreeNode key={item.id} title={title}>
{subTreeNodes}
</TreeNode>
);
}
} else if (!isSearchFilter || name.indexOf(searchValue) > -1) {
// if on searchFilter mode, push the last child node only when the node is match the search value
// otherwise, push the node anyway
returnData.push(<TreeNode key={item.id} title={title} />);
}
}
return returnData;
};
文本怎么高亮?實(shí)現(xiàn)如下:
highlightSearchValue = (name, searchValue) => {
if (!searchValue) {
return <span>{name}</span>;
}
const index = name.indexOf(searchValue);
const beforeStr = name.substr(0, index);
const afterStr = name.substr(index + searchValue.length);
return index > -1 ? (
<span>
{beforeStr}
<span className={cx('highlight-search-value')}>{searchValue}</span>
{afterStr}
</span>
) : (
<span>{name}</span>
);
};