react19+Antd实现示例数据Tree+下拉菜单+搜索
【代码】react19+Antd实现示例数据Tree+下拉菜单+搜索。
·

import React, { useMemo, useState } from 'react'
import { Input, Tree, Dropdown } from 'antd'
import type { TreeDataNode } from 'antd'
import { MoreOutlined } from '@ant-design/icons'
import type { MenuProps } from 'antd'
import { MdOutlineAddTask } from 'react-icons/md'
const { Search } = Input
import { FaEdit } from 'react-icons/fa'
import { RiDeleteBinLine } from 'react-icons/ri'
// title: 节点标题 key:唯一标识 children: 子节点
const defaultData: TreeDataNode[] = [
{
title: 'NodeA',
key: 'nodea',
children: [
{ title: '123', key: 'nodea1' },
{ title: '456', key: 'nodea2' },
],
},
{
title: 'NodeB',
key: 'nodeb',
children: [
{ title: 'NodeB1', key: 'nodeb1' },
{ title: 'NodeB2', key: 'nodeb2' },
],
},
]
// 平铺处理,把所有节点的 key 和 title 存储到 dataList 中,让它成为一层的数据
const dataList: { key: React.Key; title: string }[] = []
function generateList(data: TreeDataNode[]): void {
data.forEach(node => {
dataList.push({ key: node.key, title: String(node.title) })
if (node.children) {
generateList(node.children)
}
})
}
generateList(defaultData)
// 递归获取一个节点的父节点的 key
function getParentKey(key: React.Key, tree: TreeDataNode[]): React.Key | null {
let parentKey: React.Key | null = null
for (const node of tree) {
if (node.children) {
if (node.children.some(item => item.key === key)) {
parentKey = node.key
} else {
const childParent = getParentKey(key, node.children)
if (childParent) {
parentKey = childParent
}
}
}
}
return parentKey
}
const SideOpts: React.FC = () => {
//存储当前展开节点的 keys 数组,用于控制树组件哪些节点处于展开状态。
const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([])
//存储搜索框内的关键字,用于匹配节点文本。
const [searchValue, setSearchValue] = useState('')
//控制树形组件自动展开父节点,通常在搜索时启用,方便显示匹配结果。
const [autoExpandParent, setAutoExpandParent] = useState(true)
// 搜索框 onChange 事件:当有输入时展开所有节点
function onChange(e: React.ChangeEvent<HTMLInputElement>) {
const { value } = e.target
setSearchValue(value)
if (value) {
// 筛选出匹配搜索词的节点 toLowerCase() 方法将所有字符串转换为小写 filter表示过滤出符合条件的数据
//indexOf 方法查找转换后的标题中是否包含转换后的搜索关键字 > -1 表示包含 -1 表示不包含
const matchedKeys = dataList
.filter(item => item.title.toLowerCase().indexOf(value.toLowerCase()) > -1)
.map(item => getParentKey(item.key, defaultData))
.filter((item, i, self) => item && self.indexOf(item) === i) as React.Key[]
// expandedKeys为所有展开节点的key的数组
setExpandedKeys(matchedKeys)
//当 expandedKeys 更新时,自动展开其父节点。
setAutoExpandParent(true)
} else {
setExpandedKeys([])
}
}
// 在 useMemo 中对树形数据做搜索高亮以及添加右侧菜单的处理
const treeData = useMemo(() => {
const loop = (data: TreeDataNode[]): TreeDataNode[] =>
data.map(item => {
const strTitle = String(item.title)
const index = strTitle.toLowerCase().indexOf(searchValue.toLowerCase())
let nodeTitle
if (index > -1 && searchValue) {
const beforeStr = strTitle.substring(0, index)
const matchStr = strTitle.substring(index, searchValue.length)
const afterStr = strTitle.substring(index + searchValue.length)
nodeTitle = (
<span>
{beforeStr}
<span style={{ color: '#f50' }}>{matchStr}</span>
{afterStr}
</span>
)
} else {
nodeTitle = <span>{strTitle}</span>
}
// 定义菜单,每个菜单项的点击事件可根据需要进行扩展
const items: MenuProps['items'] = [
{
key: 'edit',
label: (
<div>
<FaEdit />
<a onClick={() => console.log('编辑算子')}>编辑算子</a>
</div>
),
},
{
type: 'divider',
},
{
key: 'delete',
label: (
<div>
<RiDeleteBinLine />
<a onClick={() => console.log('删除算子')}>删除算子</a>
</div>
),
},
{
type: 'divider',
},
{
key: 'publish',
label: (
<div>
<MdOutlineAddTask />
<a onClick={() => console.log('发布算子')}>发布算子</a>
</div>
),
},
]
// 用 flex 布局包装标题和右侧菜单图标
const newTitle = (
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<div>{nodeTitle}</div>
{/* 只有当节点为叶子节点时,显示下拉菜单 */}
{!item.children && (
<Dropdown menu={{ items }} trigger={['click']}>
<span style={{ cursor: 'pointer', marginLeft: 8 }}>
<MoreOutlined />
</span>
</Dropdown>
)}
</div>
)
return {
title: newTitle,
key: item.key,
children: item.children ? loop(item.children) : undefined,
}
})
return loop(defaultData)
}, [searchValue])
return (
<div>
<Search style={{ marginBottom: 8 }} placeholder="搜索节点" onChange={onChange} />
<Tree
expandedKeys={expandedKeys}
onExpand={keys => {
setExpandedKeys(keys)
setAutoExpandParent(false)
}}
autoExpandParent={autoExpandParent}
treeData={treeData}
/>
</div>
)
}
export default SideOpts
更多推荐
所有评论(0)