React antd 4.x 异步加载treeSelect 返显
使用antd4.x treeSelect 异步加载要做到显示保存其实很简单,返显需要一些处理才能实现
首先需要使用到
treeExpandedKeys
展开属性, 在异步加载的treeSelect 使用treeExpandedKeys
是能够触发onLoad
事件,会自动调用接口去请求数据完成返显, 下面是基于我的业务部分实现, 有些参数是基于我自己的业务,自己甄别就好
import React, {FC, useEffect, useState} from "react";
import _ from "lodash";
import {TreeSelect} from "antd";
import {history} from "@@/core/history";
import {getXiangYuTree} from "@/components/Editor/components/RightMenu/BasicInfo/CustomField/service";
//翔宇加载异步treeSelect
interface IProps {
nowValue: any;
item: any;
changeValue: (value: string, code: string, type: string) => void
extend: any[];
replaceValue: any
cid: string
}
const XYLoadTree: FC<IProps> = (props) => {
const {nowValue, item, changeValue, extend, replaceValue, cid} = props
const [treeData, setData] = useState([])
const [parentId, setParentId] = useState(0)
const [label, setLabel] = useState('')
const [initState, setState] = useState(false)
const [TreeExpand, setTreeExpand] = useState<string[]>(['0'])
const [treeLoadedKeys, seTtreeLoadedKeys] = useState<string[]>([])
const siteId = extend.find( item => item.code === "siteId" ).value
// 判断是否叶子节点, 各有实现方式随意就好
const isLeafs = (list) => {
let treeList = [...list]
for (let listElement of list) {
listElement.isLeaf = !listElement.isParent
}
return treeList
}
// 初始化设置反显
const initSetView = (treeList) => {
//设置返显,找了一下文档可以使用antd的官网文档labelInValue 这个属性更好用,我这里有因为各种问题没去使用
if (extend.find(item => item.code === 'columnId')?.label) {
const label = extend.find(item => item.code === 'columnId')?.label[0]
const value = extend.find(item => item.code === 'columnId')?.value
const findValue = treeList.find( item => item.id === value[value.length -1] )
// 是否需要反显
setState(label?.length > 0 )
// 是否点击在第一层
if (findValue?.id) {
setLabel(value)
} else {
setLabel(label)
}
}
}
// 请求接口后挂载节点antd Tree 组件提供的方法,很好用,tree结构异步加载的方式都可以用这个拼接数据
const updateTreeData = (list: any, key: React.Key, children: any) => {
return list.map((node: any) => {
if (node.id === key) {
return {
...node,
children,
};
}
if (node.children) {
return {
...node,
children: updateTreeData(node.children, key, children),
};
}
return node;
});
};
const onLoadData = ({id}) => {
return new Promise(resolve => {
getXiangYuTree(siteId, cid, id).then((res) => {
if (res.success) {
const treeList = isLeafs(JSON.parse(replaceValue(res?.data)).Column)
setData(updateTreeData(treeData,id, treeList))
seTtreeLoadedKeys(treeList.filter( item => item.id ))
// setTreeExpand((old) => [...old, id])
//initState用户没有onChange && 当前加载的有没有要做反显的
const value = extend.find(item => item.code === 'columnId')?.value
const findValue = treeList.find( item => item.id === value[value.length -1] )
if (findValue?.id) {
setLabel(value[value.length -1])
}
resolve(undefined);
}
})
})
}
// 由于treeSelect 因为性能问题没有提供父节点访问路径,因此百度找了个方法,官网提供的方法在异步状态时并不好用,因此暴力算就好了此时不考虑性能
function findParentIds(dataSource, nodeId) {
const parentIds = []; // 用于存储所有父节点ID的数组
// 定义一个递归函数,用于遍历整棵树并查找子节点的所有父节点
function traverse(node, nodeId) {
if (node.id === nodeId) { // 如果当前节点的ID等于子节点的ID,则表示已经找到了子节点,可以开始向上查找父节点
return true; // 返回true表示已经找到了子节点
}
if (node?.children) { // 如果当前节点有子节点,则继续遍历子节点
for (const childNode of node.children) {
if (traverse(childNode, nodeId)) { // 如果在子节点中找到了子节点的父节点,则将当前节点的ID添加到父节点ID数组中,并返回true表示已经找到了子节点
parentIds.push(node.id);
return true;
}
}
}
return false; // 如果当前节点不是子节点的父节点,则返回false
}
// 从根节点开始遍历整棵树,并调用递归函数查找子节点的所有父节点
for (const node of dataSource) {
if (traverse(node, nodeId)) { // 如果在当前节点的子树中找到了子节点的父节点,则直接退出循环
break;
}
}
return parentIds; // 返回所有父节点ID的数组
}
const onChage = (value: string, label: string) => {
//在onChange中获取父节点路径的value 或 id 用来给 treeExpandedKeys 做返显, 我这里是后端比较忙没有新加字段保存路径,因此自己做了一些处理
const values = [...findParentIds(treeData, value)].reverse()
values.push(value)
setState(false);
// 返给父组件数据,根据业务自己写就可以了
changeValue(values, item.code, item.type, label)
setTreeExpand([])
}
// 请求接口获取treeSelect数据
useEffect(() => {
getXiangYuTree(siteId, cid, parentId).then((res) => {
const treeList = isLeafs(JSON.parse(replaceValue(res?.data))?.Column)
seTtreeLoadedKeys(treeList.filter( item => item.id ))
initSetView(treeList)
setData(treeList || [])
})
}, [extend.find( item => item.code === "siteId" ).value])
const onDropdownVisibleChange = (open) => {
if (open) {
// 这里会有一个时差关系,如果不写个定时器就不会展开到目标节点,有懂原理的大佬请告知一下
setTimeout(() => {
const value = extend.find(item => item.code === 'columnId')?.value
const expand = value.slice(0, value.length-1)
setTreeExpand(expand)
}, 500)
}
}
return (
<>
<TreeSelect
style={{ width: '100%' }}
fieldNames={{value: 'id' , label: 'title'}}
treeExpandedKeys={TreeExpand}
value={ initState ? label : nowValue }
onTreeExpand={(keys) => {setTreeExpand(keys)}}
dropdownStyle={{ maxHeight: 300, overflow: 'auto' }}
loadData={onLoadData}
treeData={treeData}
placeholder={item.extend?.tips || ''}
treeCheckable={item.extend.ifMulty === 'true'}
onChange={(value, label) => onChage(value, label) }
onDropdownVisibleChange={onDropdownVisibleChange}
allowClear
/>
</>
)
}
export default XYLoadTree