在使用:antd tree-demo-directory的时候,
我代码如下:
import React, { useMemo, useState } from 'react';
import { Input, Tree } from 'antd';
import type { TreeDataNode, TreeProps } from 'antd';
import './Left.css';
import { DownOutlined } from '@ant-design/icons';
const { Search } = Input;
// 预定义的静态树形数据
const treeData: TreeDataNode[] = [
{
title: 'parent 0',
key: '0-0',
children: [
{ title: 'leaf 0-0', key: '0-0-0', isLeaf: true },
{ title: 'leaf 0-1', key: '0-0-1', isLeaf: true },
],
},
{
title: 'parent 1',
key: '0-1',
children: [
{ title: 'leaf 1-0', key: '0-1-0', isLeaf: true },
{ title: 'leaf 1-1', key: '0-1-1', isLeaf: true },
],
},
];
// 获取父节点 Key
const getParentKey = (key: React.Key, tree: TreeDataNode[]): React.Key | null => {
for (const node of tree) {
if (node.children) {
if (node.children.some((item) => item.key === key)) {
return node.key;
}
const parentKey = getParentKey(key, node.children);
if (parentKey !== null) {
return parentKey;
}
}
}
return null;
};
export const Left: React.FC = () => {
const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
const [checkedKeys, setCheckedKeys] = useState<React.Key[]>([]);
const [searchValue, setSearchValue] = useState('');
const [autoExpandParent, setAutoExpandParent] = useState(true);
// 搜索框变化事件
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target;
const newExpandedKeys = treeData
.flatMap((item) => findMatchingKeys(item, value))
.filter((key, i, self): key is React.Key => !!(key && self.indexOf(key) === i));
setExpandedKeys(newExpandedKeys);
setSearchValue(value);
setAutoExpandParent(true);
};
const findMatchingKeys = (node: TreeDataNode, value: string): React.Key[] => {
const matchingKeys: React.Key[] = [];
const strTitle = String(node.title);
// 如果当前节点匹配,添加当前节点的 key
if (strTitle.toLowerCase().includes(value.toLowerCase())) {
matchingKeys.push(node.key);
// 添加父节点 key 以确保节点可见
const parentKey = getParentKey(node.key, treeData);
if (parentKey) {
matchingKeys.push(parentKey);
}
}
// 递归搜索子节点
if (node.children) {
node.children.forEach((child) => {
matchingKeys.push(...findMatchingKeys(child, value));
});
}
return matchingKeys;
};
// 动态生成过滤后的树数据
const filteredTreeData = useMemo(() => {
const loop = (data: TreeDataNode[]): TreeDataNode[] =>
data
.map((item) => {
const strTitle = item.title as string;
const index = strTitle.indexOf(searchValue);
const beforeStr = strTitle.substring(0, index);
const afterStr = strTitle.slice(index + searchValue.length);
const displayTitle =
index > -1 ? (
<span key={item.key}>
{beforeStr}
<span className="site-tree-search-value">{searchValue}</span>
{afterStr}
</span>
) : (
<span key={item.key}>{strTitle}</span>
);
if (item.children) {
return { title: strTitle, key: item.key, displayTitle, children: loop(item.children) };
}
return { title: strTitle, key: item.key, displayTitle };
})
.filter((item) => {
const strTitle = item.title as string;
return strTitle.toLowerCase().includes(searchValue.toLowerCase());
});
return loop(treeData);
}, [searchValue, treeData]);
// 勾选事件
const onCheck: TreeProps['onCheck'] = (checkedKeysValue) => {
console.log('onCheck', checkedKeysValue);
setCheckedKeys(checkedKeysValue as React.Key[]);
};
// 展开事件
const onExpand: TreeProps['onExpand'] = (newExpandedKeys) => {
console.log('onExpand', newExpandedKeys);
setExpandedKeys(newExpandedKeys);
setAutoExpandParent(false);
};
return (
<div>
<Search style={{ marginBottom: 8 }} placeholder="Search" onChange={onChange} />
<Tree
showLine
checkable
switcherIcon={<DownOutlined />}
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onCheck}
checkedKeys={checkedKeys}
treeData={filteredTreeData}
titleRender={(nodeData) => nodeData.displayTitle}
/>
</div>
);
};
但是搜索是否有问题:数据搜索不到:
请问是什么原因导致呢?
修改 findMatchingKeys 函数
const findMatchingKeys = (node: TreeDataNode, value: string): React.Key[] => {
const matchingKeys: React.Key[] = [];
const strTitle = String(node.title);
// 如果当前节点匹配,添加当前节点的 key
if (strTitle.toLowerCase().includes(value.toLowerCase())) {
matchingKeys.push(node.key);
// 添加父节点 key 以确保节点可见
const parentKey = getParentKey(node.key, treeData);
if (parentKey) {
matchingKeys.push(parentKey);
}
}
// 递归搜索子节点
if (node.children) {
node.children.forEach((child) => {
matchingKeys.push(...findMatchingKeys(child, value));
});
}
return matchingKeys;
};
问题在于搜索逻辑有几个关键错误:
const filteredTreeData = useMemo(() => {
const loop = (data: TreeDataNode[]): TreeDataNode[] => {
return data.map((item) => {
const strTitle = String(item.title);
const index = strTitle.toLowerCase().indexOf(searchValue.toLowerCase());
const beforeStr = strTitle.substring(0, index);
const afterStr = strTitle.slice(index + searchValue.length);
const newItem: TreeDataNode = { ...item };
if (index > -1) {
// 匹配节点,创建高亮显示
newItem.title = (
<span>
{beforeStr}
<span className="site-tree-search-value">{strTitle.substr(index, searchValue.length)}</span>
{afterStr}
</span>
);
}
// 处理子节点
if (item.children) {
newItem.children = loop(item.children);
// 如果有匹配的子节点,即使父节点不匹配也要保留
if (newItem.children.length > 0) {
return newItem;
}
}
// 返回匹配的节点
return index > -1 ? newItem : (item.children ? newItem : null);
}).filter(Boolean) as TreeDataNode[];
};
return searchValue ? loop(treeData) : treeData;
}, [searchValue]);
原因是:
过滤逻辑问题:在 filteredTreeData 中,过滤时只考虑了当前节点的 title,没有考虑子节点是否匹配。如果父节点不匹配但子节点匹配,父节点会被过滤掉,导致子节点也无法显示。
按照此可解决:
import React, { useMemo, useState } from 'react';
import { Input, Tree } from 'antd';
import type { TreeDataNode, TreeProps } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import './Left.css'
const { Search } = Input;
// 预定义的静态树形数据
const treeData: TreeDataNode[] = [
{
title: 'parent 0',
key: '0-0',
children: [
{ title: 'leaf 0-0', key: '0-0-0', isLeaf: true },
{ title: 'leaf 0-1', key: '0-0-1', isLeaf: true },
],
},
{
title: 'parent 1',
key: '0-1',
children: [
{ title: 'leaf 1-0', key: '0-1-0', isLeaf: true },
{ title: 'leaf 1-1', key: '0-1-1', isLeaf: true },
],
},
];
// 获取父节点 Key
const getParentKey = (key: React.Key, tree: TreeDataNode[]): React.Key | null => {
for (const node of tree) {
if (node.children) {
if (node.children.some((item) => item.key === key)) {
return node.key;
}
const parentKey = getParentKey(key, node.children);
if (parentKey !== null) {
return parentKey;
}
}
}
return null;
};
export const Left: React.FC = () => {
const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
const [checkedKeys, setCheckedKeys] = useState<React.Key[]>([]);
const [searchValue, setSearchValue] = useState('');
const [autoExpandParent, setAutoExpandParent] = useState(true);
// 搜索框变化事件
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target;
const newExpandedKeys = treeData
.flatMap((item) => findMatchingKeys(item, value))
.filter((key, i, self): key is React.Key => !!(key && self.indexOf(key) === i));
setExpandedKeys(newExpandedKeys);
setSearchValue(value);
setAutoExpandParent(true);
};
const findMatchingKeys = (node: TreeDataNode, value: string): React.Key[] => {
const matchingKeys: React.Key[] = [];
const strTitle = String(node.title);
// 如果当前节点匹配,添加当前节点的 key
if (strTitle.toLowerCase().includes(value.toLowerCase())) {
matchingKeys.push(node.key);
// 添加父节点 key 以确保节点可见
let parentKey = getParentKey(node.key, treeData);
while (parentKey) {
matchingKeys.push(parentKey);
parentKey = getParentKey(parentKey, treeData);
}
}
// 递归搜索子节点
if (node.children) {
node.children.forEach((child) => {
matchingKeys.push(...findMatchingKeys(child, value));
});
}
return matchingKeys;
};
// 动态生成过滤后的树数据
const filteredTreeData = useMemo(() => {
const loop = (data: TreeDataNode[]): TreeDataNode[] => {
return data.filter((item) => {
const strTitle = String(item.title);
const isMatch = strTitle.toLowerCase().includes(searchValue.toLowerCase());
const hasMatchingChildren = item.children && loop(item.children).length > 0;
return isMatch || hasMatchingChildren;
}).map((item) => {
const strTitle = item.title as string;
const index = strTitle.indexOf(searchValue);
const beforeStr = strTitle.substring(0, index);
const afterStr = strTitle.slice(index + searchValue.length);
const displayTitle =
index > -1 ? (
<span key={item.key}>
{beforeStr}
<span className="site-tree-search-value">{searchValue}</span>
{afterStr}
</span>
) : (
<span key={item.key}>{strTitle}</span>
);
if (item.children) {
const children = loop(item.children);
return { title: strTitle, key: item.key, displayTitle, children };
}
return { title: strTitle, key: item.key, displayTitle };
});
};
return loop(treeData);
}, [searchValue, treeData]);
// 勾选事件
const onCheck: TreeProps['onCheck'] = (checkedKeysValue) => {
console.log('onCheck', checkedKeysValue);
setCheckedKeys(checkedKeysValue as React.Key[]);
};
// 展开事件
const onExpand: TreeProps['onExpand'] = (newExpandedKeys) => {
console.log('onExpand', newExpandedKeys);
setExpandedKeys(newExpandedKeys);
setAutoExpandParent(false);
};
return (
<div>
<Search style={{ marginBottom: 8 }} placeholder="Search" onChange={onChange} />
<Tree
showLine
checkable
switcherIcon={<DownOutlined />}
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onCheck}
checkedKeys={checkedKeys}
treeData={filteredTreeData}
titleRender={(nodeData) => nodeData.displayTitle}
/>
</div>
);
};
我在看最新的开发者IDE: Zed的时候, 发现它有一个标签:gpui 请问gpui是什么? 和 GPU 有关系吗?
请问,下面的图到底是什么类型的图?拓扑图?分支图?流程图?还是别的什么图? 有没有对应的插件可以即插即用,最好是组件可以自定义的......
我见测试框架vitest使用中,有如下的示例: 有2个问题想要咨询: 1)请问这里配置context 的目的是什么呢? 2)有报错:
现在就是我定义了一个接口Igoods,但是我不知道怎么用它来约束下面返回的goods这个数组,老师写的方法是在数组后面加上了as Igoods[],请问还有其他的方法吗?感谢各位大佬! 代码部分:
,文字前面跟着已到期或者即将到期,但是这段文字是右对齐,且以最长文本的长度作为整个盒子的宽度,并设置背景色。
我使用了next.js 项目,但是在组件中,GetServerSideProps并未执行,请问这个是什么原因呢? 1.是否是写的代码有误? 2.是否是GetServerSideProps 只能在页面中使用,而不能在组件中使用? 组件代码: