and 树结构穿梭框
import React, { Component, useEffect, useState } from 'react';
import { Tree, Input, Button, Card } from 'antd';
// import requset from '@/utils/request';
import { cloneDeep } from 'lodash';
import { useSetState, useAsyncEffect } from 'ahooks'
const { Search } = Input;
//扁平后数据
let arr: any[] = [];
type propsType = {
data: any,
type: any,
dataType: any,
treedatas: (res: any) => void
}
const TransferTree: React.FC<propsType> = ({
data,type,dataType, treedatas,
}) => {
const [state, setState] = useSetState<any>({
treeData: [],
expandedKeys: [],
searchValue: '',
autoExpandParent: true,
loading: false,
rightTree: [],
leftTree: [],
checkedKeysLeft: [],
leftBtnDisabled: true,
rightBtnDisabled: true,
arrRightKey: [],
})
const a = (arrRightKey: any) => {
let b = arr.filter((item: any) => {
return !arrRightKey.includes(item.key)
})
let f = b.map((item: any) => {
return item.key
})
return f;
}
const componentDidMount = (data:any) => {
arr = [];
let chosenIds = data?.chosenIds || [];
let allTree = data?.allTree || [];
//请求树结构
//如果没有给父节点key,那就给每个子节点设置父节点key
const newDATA = cloneDeep(allTree);
setParent(newDATA);
//扁平数据
setnode(newDATA);
//扁平数据
setState({
rightTree: ListToTree(buquanParentKey(chosenIds, allTree)),
leftTree: ListToTree(buquanParentKey(a(chosenIds), allTree)),
checkedKeysLeft: {
checked: [],
halfChecked: []
},
leftBtnDisabled: true,
rightBtnDisabled: true,
arrRightKey: chosenIds,
treeData:allTree,
});
treedatas(chosenIds)
}
// 补全 右侧数据
const findParent = (data: any, target: any, result: any) => {
for (let i in data) {
let item = data[i]
if (item.key == target) {
//将查找到的目标数据加入结果数组中
//可根据需求unshift(item.id)或unshift(item)
result.unshift(item.key)
return true
}
if (item.children && item.children.length > 0) {
let ok = findParent(item.children, target, result)
if (ok) {
result.unshift(item.key)
return true
}
}
}
//走到这说明没找到目标
return false
}
const buquanParentKey = (childkey: any, allTree = state.treeData) => {
let objs: any = []
childkey.forEach((item: any) => {
let a: any[] = []
findParent(allTree, item, a)
objs.push(a);
})
return [...new Set([...objs.flat(Infinity)])]
}
//1设置父节点id
const setParent = (treeData: any) => {
treeData && treeData.map((t: any) => {
if (t.children && t.children.length) {
t.children.map((c: any) => {
c.parentKey = t.key;
});
setParent(t.children);
} else {
return;
}
});
};
//2扁平数据
const setnode = (c: any) => {
c && c.map((item: any) => {
arr.push(Object.assign(item, {title: item.title+(item.status ? '-启用':'-禁用')}));
item.children && setnode(item.children);
});
};
//左边选中的key追加到右边的key
const checked = (checkedkeys: any, node: any) => {
const { halfCheckedKeys } = node || {};
//设置左边复选框选中的key
setState({
checkedKeysLeft: {
checked: checkedkeys,
halfChecked: halfCheckedKeys
}
});
//
let checkedAllLeftKey = checkedkeys;
//右边key
setState({
checkedAllLeftKey,
leftBtnDisabled: !checkedkeys.length
});
};
const checkedRight = (checkedkeys: any, node: any) => {
const { halfCheckedKeys } = node || {};
//左边删除右边选中后的key
let checkedAllRightKey = checkedkeys.concat(halfCheckedKeys);;
setState({
checkedAllRightKey,
rightBtnDisabled: !checkedkeys.length,
checkedKeysRight: {
checked: checkedkeys,
halfChecked: halfCheckedKeys
}
});
};
//5.把选中的扁平的数据变成树结构
const ListToTree = (checkedAllKey: any) => {
//3,防止循环插入重复数据,把children设置null
arr.map(item => {
if (item.children) {
item.children = '';
}
return item;
});
//4.在扁平后的数据中筛选出选中的数据
let selectValue = checkedAllKey.map((item: any) => {
return arr.find(a => {
return item == a.key;
});
});
let copyList = cloneDeep(selectValue);
// console.log(copyList);
copyList.forEach((ele: any) => {
// 第一次forEach遍历到所有的数组对象,不做去重,数组依旧拥有6个元素
let parentId = ele.parentKey;
if (!parentId) {
//是根元素的话 ,不做任何操作.
} else {
//如果ele是子元素的话 ,把ele扔到他的父亲的child数组中.它的父元素也可能是别人的子元素
copyList.forEach((d: any) => {
if (d.key === parentId) {
let childArray = d.children;
// 一则避免了去给d.child声明定义空数组
// childArray为undefined时,用push方法去报undefined.push的错
if (!childArray) {
childArray = [];
}
// 用push保证同级别的对象有序追加
childArray.push(ele);
d.children = childArray;
} //else的地方不做处理,保证了最深层的对象没有child属性
});
}
});
//去除重复元素
copyList = copyList.filter((ele: any) => !ele.parentKey);
return copyList;
}
//把选中的数据设置到右边
const toRight = () => {
const { checkedAllLeftKey, checkedKeysLeft }: any = state;
let cak = [...new Set([...state.arrRightKey, ...checkedAllLeftKey])]
setState({
rightTree: ListToTree(buquanParentKey(cak)),
leftTree: ListToTree(buquanParentKey(a(cak))),
checkedKeysLeft: {
checked: [],
halfChecked: []
},
checkedKeysRight: {
checked: [],
halfChecked: []
},
leftBtnDisabled: true,
rightBtnDisabled: true,
arrRightKey: cak,
});
if ( dataType == 3){
const filterParent = arr.filter((item:any)=>item.parentKey)
const filterParentKey = filterParent.map((item:any)=>{return item.key;})
let cak_new = cak.filter((item:any)=>{ return filterParentKey.includes(item)})
treedatas(cak_new)
} else {
treedatas(buquanParentKey(cak))
}
};
//取消右边的数据
const toLeft = () => {
const { checkedAllRightKey, checkedKeysRight }: any = state;
let arrRightKey = buquanParentKey(state.arrRightKey);
checkedKeysRight.checked.forEach((item: any) => {
let index = arrRightKey.findIndex((c: any) => {
return item === c;
});
arrRightKey.splice(index, 1);
});
let cak = state.arrRightKey.filter((item: any) => arrRightKey.indexOf(item) > -1);
setState({
rightTree: ListToTree(buquanParentKey(cak)),
leftTree: ListToTree(buquanParentKey(a(cak))),
// checkedAllLeftKey: arrRightKey,
checkedKeysLeft: {
checked: [],
halfChecked: []
},
checkedKeysRight: {
checked: [],
halfChecked: []
},
leftBtnDisabled: true,
rightBtnDisabled: true,
arrRightKey: cak,
});
if ( dataType == 3){
const filterParent = arr.filter((item:any)=>item.parentKey)
const filterParentKey = filterParent.map((item:any)=>{return item.key;})
let cak_new = cak.filter((item:any)=>{ return filterParentKey.includes(item)})
treedatas(cak_new)
} else {
treedatas(buquanParentKey(cak))
}
};
const onExpand = (expandedKeys: any) => {
setState({
expandedKeys,
autoExpandParent: false
});
};
const getParentKey = (key: any, tree: any): 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 onChange = (e: any) => {
const { value } = e.target;
const newarr = cloneDeep(arr);
const newDATA = cloneDeep(state.treeData);
const expandedKeys = newarr
.map(item => {
if (item.title.indexOf(value) > -1) {
return getParentKey(item.key, newDATA);
}
return null;
})
.filter((item, i, self) => item && self.indexOf(item) === i);
setState({
expandedKeys,
searchValue: value,
autoExpandParent: true
});
};
const loop = (data: any) =>
data.map((item: any) => {
const index = item.title.indexOf(state?.searchValue);
const beforeStr = item.title.substr(0, index);
const afterStr = item.title.substr(
index + state?.searchValue.length
);
const title =
index > -1 ? (
<span>
{beforeStr}
<span style={{ color: 'red' }}>
{state?.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
};
});
useEffect(() => {
setState({
treeData: [],
arrRightKey: [],
});
componentDidMount(data)
}, [data, type])
return (
<div style={{ display: 'flex' }}>
{
type !='look' && (
<>
<div style={{ width: '50%' }}>
<Search
style={{ marginBottom: 8 }}
placeholder="Search"
onChange={onChange}
/>
<Card loading={state.loading} style={{ width: '100%', height: '500px', overflow: 'auto' }}>
<Tree
showLine
checkable
checkStrictly={false}
onCheck={checked}
onExpand={onExpand}
checkedKeys={state.checkedKeysLeft}
expandedKeys={state.expandedKeys}
autoExpandParent={state.autoExpandParent}
treeData={loop(state?.leftTree)}
>
{/* {renderTreeNode()} */}
</Tree>
</Card>
</div>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', margin: '0 10px' }}>
<Button
onClick={toRight}
disabled={state?.leftBtnDisabled}
>
添加 >
</Button>
<Button
style={{ marginTop: '10px' }}
onClick={toLeft}
disabled={state?.rightBtnDisabled}
>
< 移除
</Button>
</div>
</>
)
}
<div style={{ width: '50%', marginLeft: (type == 'look' ? '166px' : '') }}>
<Card loading={state.loading} style={{ width: '100%', height: '500px', overflow: 'auto', marginTop: (type == 'look' ? '' : '40px') }}>
<Tree
showLine
checkable
disabled={type == 'look'}
checkStrictly={false}
onCheck={checkedRight}
checkedKeys={state.checkedKeysRight}
treeData={state?.rightTree}
>
{/* {renderTreeNodeCopy()} */}
</Tree>
</Card>
</div>
</div>
);
}
export default TransferTree