首先说下我们的需求:
根据materialUI组件的treeView 来进行前端的模糊搜索
展开所选节点所在的父节点,
同时所匹配到的节点高亮显示
思路:需要先把全部的树节点平铺到一层,
然后根据所选择的子节点(这里场景是搜索,可以是多个子节点),
循环遍历多个所选择的子节点,
然后写一个递归函数,依次传递所选择节点的parsentid,
去跟已经平铺到一层的全部节点进行对比,parsentid === id 则添加到父节点的数组中,
然后再传递 已经匹配上的 全部节点中的 那一个节点 (因为父节点还可能拥有父节点),进行递归。
具体的代码:
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import TreeView from '@material-ui/lab/TreeView';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import TreeItem from '@material-ui/lab/TreeItem';
const data = [
{
id: 93,
label: 'USA',
labelCn: 'USA',
value: 93,
children: [
{
id: 94,
label: 'US Citizen',
labelCn: 'US Citizen',
value: 94,
parentId: 93,
checked: false,
},
{
id: 95,
label: 'Green Card',
labelCn: 'Green Card',
value: 95,
parentId: 93,
checked: false,
},
{
id: 96,
label: 'Working Visa',
labelCn: 'Working Visa',
value: 96,
children: [
{
id: 97,
label: 'H',
labelCn: 'H',
value: 97,
parentId: 96,
checked: false,
},
{
id: 98,
label: 'L',
labelCn: 'L',
value: 98,
parentId: 96,
checked: false,
},
{
id: 99,
label: 'O',
labelCn: 'O',
value: 99,
parentId: 96,
checked: false,
children: null,
},
{
id: 9999,
label: 'WW',
labelCn: 'WW',
value: 9999,
parentId: 96,
checked: false,
children: null,
},
],
parentId: 93,
checked: false,
},
{
id: 100,
label: 'EAD Card',
labelCn: 'EAD Card',
value: 100,
children: [
{
id: 101,
label: 'OPT/STEM OPT',
labelCn: 'OPT/STEM OPT',
value: 101,
parentId: 100,
checked: false,
},
{
id: 102,
label: 'Asylum',
labelCn: 'Asylum',
value: 102,
parentId: 100,
checked: false,
},
{
id: 103,
label: 'H4-EAD',
labelCn: 'H4-EAD',
value: 103,
parentId: 100,
checked: false,
},
{
id: 104,
label: 'J2-EAD',
labelCn: 'J2-EAD',
value: 104,
parentId: 100,
checked: false,
},
{
id: 105,
label: 'L2-EAD',
labelCn: 'L2-EAD',
value: 105,
parentId: 100,
checked: false,
},
{
id: 106,
label: '485 EAD',
labelCn: '485 EAD',
value: 106,
parentId: 100,
checked: false,
},
],
parentId: 93,
checked: false,
},
],
parentId: 0,
checked: false,
},
{
id: 107,
label: 'Canada',
labelCn: 'Canada',
value: 107,
children: [
{
id: 108,
label: 'Canadian Citizen',
labelCn: 'Canadian Citizen',
value: 108,
parentId: 107,
checked: false,
},
{
id: 109,
label: 'Permanent Resident',
labelCn: 'Permanent Resident',
value: 109,
parentId: 107,
checked: false,
},
{
id: 110,
label: 'Open Work Permit',
labelCn: 'Open Work Permit',
value: 110,
parentId: 107,
checked: false,
},
{
id: 111,
label: 'Student Visa',
labelCn: 'Student Visa',
value: 111,
parentId: 107,
checked: false,
},
],
parentId: 0,
checked: false,
},
{
id: 112,
label: 'Europe',
labelCn: 'Europe',
value: 112,
children: [
{
id: 113,
label: 'Citizen',
labelCn: 'Citizen',
value: 113,
parentId: 112,
checked: false,
},
{
id: 114,
label: 'Permanent Resident',
labelCn: 'Permanent Resident',
value: 114,
parentId: 112,
checked: false,
},
{
id: 115,
label: 'Work permit with restrictions',
labelCn: 'Work permit with restrictions',
value: 115,
parentId: 112,
checked: false,
},
{
id: 116,
label: 'Student Visa',
labelCn: 'Student Visa',
value: 116,
parentId: 112,
checked: false,
},
],
parentId: 0,
checked: false,
},
];
const useStyles = makeStyles({
root: {
height: 110,
flexGrow: 1,
maxWidth: 400,
},
});
export default function RecursiveTreeView() {
const classes = useStyles();
const [expanded, setExpanded] = React.useState([]);
const [selected, setSelected] = React.useState([]);
const setTreeLabel = (node) => {
return (
<div>
<span>
{node.label} - {node.id}
</span>
</div>
);
};
const renderTree = (nodes) =>
nodes.map((item, index) => {
return (
<TreeItem
key={item.id}
setChecked={(item) => {
setChecked(item);
}}
nodeId={`${item.id}`}
item={item}
label={setTreeLabel(item)}
>
{item.children ? renderTree(item.children) : null}
</TreeItem>
);
});
const handleToggle = (event, nodeIds) => {
setExpanded(nodeIds);
};
const handleSelect = (event, nodeIds) => {
setSelected(nodeIds);
};
let timer = null;
const search = (e) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
let value = e.target.value;
let expanded = [];
let selected = [];
if (value) {
selected = findNodes(data, value, []);
expanded = findParsent(selected);
}
setSelected(selected.map((n) => String(n.id)));
setExpanded(expanded.map((n) => String(n.id)));
}, 100);
};
const setSpreadTreeData = (tree, data = []) => {
for (let i = 0; i < tree.length; i++) {
let item = tree[i];
data.push(item);
item.children && setSpreadTreeData(item.children, data);
}
return data;
};
const findNodes = (data, value, arr) => {
for (let node of data) {
if (node.label.toUpperCase().includes(value.toUpperCase())) {
arr.push(node);
}
if (node.children) {
findNodes(node.children, value, arr);
}
}
return arr;
};
const findParsent = (selected) => {
let spreadTreeData = setSpreadTreeData(data);
let parsentNodes = [];
let dist = (parentId, arr) => {
for (let s of spreadTreeData) {
if (s.id == parentId) {
arr.push(s);
if (s.parentId) {
dist(s.parentId, arr);
}
}
}
return arr;
};
for (let s of selected) {
parsentNodes = dist(s.parentId, parsentNodes);
}
return parsentNodes;
};
return (
<div>
<input type="text" onChange={search} />
<TreeView
className={classes.root}
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpanded={['root']}
defaultExpandIcon={<ChevronRightIcon />}
expanded={expanded}
selected={selected}
onNodeToggle={handleToggle}
multiSelect
onNodeSelect={handleSelect}
>
{renderTree(data)}
</TreeView>
</div>
);
}
效果图: