需求要求在每个节点的后面加上新增,编辑,删除按钮,并且能够点击编辑title的显示变成input输入框,antd的案例中没有这种情况,但是antd的api中给了titleRender可以自定义title样式。
<Tree
...
titleRender={onTitleRender}
...
>
</Tree>
const onTitleRender = (item: any) => { // title渲染函数
let content
let icon
let beforeIcon
if (item.key === pickedEdit) {
content = (<Input
// value={item.title}
defaultValue={item.title}
onChange={(e) => onChange(e, item.key)}
onPressEnter={(e) => onPressEnter(e, item.key)}
onBlur={(e) => onPressEnter(e, item.key)}
/>)
} else {
content = (<div onClick={(e) => chooseContent(e, item.title)}>{item.title}</div>)
}
if (!item.isLeaf) { // 该节点为父节点
beforeIcon = (<FolderOpenTwoTone twoToneColor="#cdb228" style={{ marginRight: '5px' }} />)
icon = (
<span style={{ display: 'flex' }}>
<PlusOutlined style={{ marginLeft: 10, color: '#409EFF' }} onClick={() => onAdd(item.key)} />
<EditOutlined style={{ marginLeft: 10, color: '#409EFF' }} onClick={() => onEdit(item.key)} />
<CloseOutlined style={{ marginLeft: 10, color: '#409EFF' }} onClick={() => onDelete(item.key)} />
</span>
)
} else { // 该节点为叶子节点
beforeIcon = (<FileTextOutlined style={{ marginRight: '5px' }} />)
icon = (
<span style={{ display: 'flex' }}>
<EditOutlined style={{ marginLeft: 10, color: '#409EFF' }} onClick={() => onEdit(item.key)} />
<CloseOutlined style={{ marginLeft: 10, color: '#409EFF' }} onClick={() => onDelete(item.key)} />
</span>
)
}
return (
<div style={{ display: 'flex', alignItems: 'center' }}>
{beforeIcon}
{content}
{icon}
</div>
);
};
这里我设置了一个全局变量pickedEdit来记录当前正在编辑的节点的key,当编辑的key有值后,key相等的title就会变成input框
我这里的判断是因为我们要求树只有三层,所以不给叶子节点add功能,也用不同的图标区分是否是叶子节点。
首先,返回的node肯定要加一个
其次,是搜索框change内容时触发的函数,如下
// 搜索框的change事件触发函数
const onChangeSearch = (e: any) => {
const { value } = e.target;
const expandedKeys = dataList
.map((item: any) => {
if (item.title.indexOf(value) > -1) {
return getParentKey(item.key, treeData);
}
return null;
})
.filter((item: any, i: any, self: any) => item && self.indexOf(item) === i);
setExpandedKeys(expandedKeys);
setSearchValue(value);
setAutoExpandParent(true);
};
// 生成key的数组
const generateList = (data: any) => {
for (let i = 0; i < data.length; i++) {
const node = data[i];
const { key, title } = node;
dataList.push({ key, title: title });
if (node.children) {
generateList(node.children);
}
}
};
generateList(treeData);
// 查询父节点的key
//@ts-ignore
const getParentKey = (key: any, tree: any) => {
let parentKey;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some((item: any) => item.key === key)) {
parentKey = node.key;
} else if (getParentKey(key, node.children)) {
parentKey = getParentKey(key, node.children);
}
}
}
return parentKey;
};
最后也是最最重要的:搜索算法
// 循环遍历搜索关键字
const loop = (data: any) =>
data.map((item: any) => {
// 将title分成三部分,中间的是搜索命中的内容
const index = item.title.indexOf(searchValue);
const beforeStr = item.title.substr(0, index);
const afterStr = item.title.substr(index + searchValue.length);
const title =
index > -1 ? (
<span>
{beforeStr}
<span style={{ color: '#f50' }}>{searchValue}</span>
{afterStr}
</span>
) : (
<span>{item.title}</span>
);
if (item.children) {
return { title, key: item.key, children: loop(item.children) };
}
return {
title,
key: item.key,
isLeaf: item.isLeaf
};
});
将title分为三部分,beforeStr,index和afterStr,其中index是搜索的内容;
如果index存在,就将index的内容样式改变,变成红色。
这里的搜索顺序是深度优先。
联调的时候突然发现,改完数据后上传,然后用setState更新treedata,这时界面上的数据没有变化,没更新?
想起来之前准备面试的时候学的深拷贝和浅拷贝,tree在ts中是以object类型保存的,那么setState会不会只是个浅拷贝,只存了这个变量的地址,所以它地址没变就没有重新渲染,那么就可以采用一些方式让他进行深拷贝,也可以改变一下它的地址,比如下面我采取的方法:
let temp = [...treeData]
setTreeData(temp)