(1). 穿梭框左右侧均为省市树,支持搜索,支持全选
(2). 某城市从左侧移到右侧后,左侧为不可勾选状态。
(3). 移动省下部分城市,左右侧这个省都要作为父节点展示。
*提示:只有左侧一级下的二级为全选状态,一级才作为一项移到右侧,否则一级节点只作为父节点展示并不是输出项,如广西省二级只移了梧州市一项,所以广西省不算一项,右侧总共为5项。rightData = [广东省,广州市,深圳市,佛山市,梧州市]
*根据官方组件结合自己的数据业务场景修改,附上我的代码,供参考:
import React, { useState } from 'react'
import { Transfer, Tree } from 'antd'
const AuthorizationModal = () => {
const [targetKeys, setTargetKeys] = useState([])
const [rightTreeData, setRightTreeData] = useState([])
const treeData = [
{ key: 0, title: '福建省' },
{
key: 1,
title: '广东省',
children: [
{ key: 10, title: '广州市' },
{ key: 11, title: '深圳市' },
{ key: 12, title: '佛山市' }
],
},
{
key: 2,
title: '广西省',
children: [
{ key: 20, title: '梧州市' },
{ key: 21, title: '北海市' },
{ key: 22, title: '玉林市' }
],
}
]
const generateTree = (treeNodes = [], checkedKeys = []) =>
treeNodes.map(({ children, ...props }) => ({
...props,
disabled: checkedKeys.includes(props.key),
children: generateTree(children, checkedKeys),
}))
const dealCheckboxSeleted = ({ node, onItemSelect, onItemSelectAll }, direction) => {
let {
checked,
halfCheckedKeys,
node: { key, children },
} = node
// 勾选的是父节点
if (children?.length > 0) {
let keys = []
let temp = []
if (direction === 'left') {
let state = false
if (rightTreeData.length > 0) {
rightTreeData?.map(item => {
if (item.childCompanies?.length > 0 && (item.key == key)) {
temp = childCompanies.filter(v => !item.childCompanies.some((t => t.key === v.key)))
temp?.forEach(child => {
keys.push(child.key)
})
} else {
state = true
}
})
} else {
state = true
}
if (state) {
children?.forEach(child => {
keys.push(child.key)
})
}
onItemSelectAll([...keys, key], checked)
}
if (direction === 'right') {
children?.forEach(child => {
keys.push(child.key)
})
onItemSelectAll([...keys], checked)
}
} else {
// 勾选的是子节点
if (!checked) {
// 查找该元素的父元素
let parentKeys = []
parentKeys = [halfCheckedKeys?.[0]] || []
if (parentKeys[0] == undefined) {
// 当一级下的二级全部取消勾选,一级也取消勾选
treeData.forEach(tree => {
if (tree.children) {
tree.children?.forEach(child => {
if (child?.key === key) {
parentKeys.push(tree?.key)
}
})
}
})
}
onItemSelectAll([...parentKeys, key], checked)
} else {
let parentKey = ''
treeData.forEach(tree => {
if (tree?.children) {
tree.children?.forEach(child => {
if (child?.key === key) {
parentKey = tree?.key
}
})
}
})
if (!halfCheckedKeys?.includes(parentKey) && parentKey != '') {
onItemSelectAll([key, parentKey], checked)
} else {
onItemSelect(key, checked)
}
}
}
}
const TreeTransfer = ({ dataSource, targetKeys, ...restProps }) => {
const transferDataSource = []
const dataSourceData = dataSource
let test = [...rightTreeData]
function flatten(list = []) {
list.forEach(item => {
transferDataSource.push(item)
flatten(item.children)
})
}
flatten(dataSource)
return (
<Transfer
{...restProps}
targetKeys={targetKeys}
dataSource={transferDataSource}
className="tree-transfer"
showSearch
showSelectAll={true}
render={item => item.title}
rowKey={record => record.key}
// 搜索功能逻辑
onSearch={(dir, val) => {
let data = (dir === 'left' ? dataSourceData : rightTreeData)
// 1.先遍历二级,过滤出搜索对应的数据;
// 2.如果二级有数据,过滤出二级的companyName和一级的companyName
// 3.最后把一级不符合搜索对应的值过滤
const newDeptList = data?.map(item => {
item = Object.assign({}, item)
if (item.children) {
item.children = item.children?.filter(res => (res.title.indexOf(val) > -1))
}
return item
}).filter(item => {
if (item.children?.length > 0 || val.length == 0) {
item = Object.assign({}, item)
item.children?.filter(e => (
e.title.indexOf(val) > -1 ? '' : item.title.indexOf(val) > -1
))
} else {
item = item.title.indexOf(val) > -1
}
return item
})
console.log(newDeptList, 165);
if (dir === 'left') {
dataSource = newDeptList
}
if (dir === 'right') {
test = newDeptList
}
}}
>
{({ direction, onItemSelect, onItemSelectAll, selectedKeys }) => {
if (direction === 'left') {
const checkedKeys = [...selectedKeys, ...targetKeys]
return (
<Tree
blockNode
checkable
defaultExpandAll
checkedKeys={checkedKeys}
treeData={generateTree(dataSource, targetKeys)}
fieldNames={{ title: 'title', key: 'key', children: 'children' }}
onCheck={(_, node) => {
dealCheckboxSeleted({ node, onItemSelect, onItemSelectAll }, direction)
}}
onSelect={(_, node) => {
dealCheckboxSeleted({ node, onItemSelect, onItemSelectAll }, direction)
}}
/>
)
}
if (direction === 'right') {
const checkedKeys = [...selectedKeys]
return (
<Tree
blockNode
checkable
defaultExpandAll
checkedKeys={checkedKeys}
treeData={test}
fieldNames={{ title: 'title', key: 'key', children: 'children' }}
onCheck={(_, node) => {
dealCheckboxSeleted({ node, onItemSelect, onItemSelectAll }, direction)
}}
onSelect={(_, node) => {
dealCheckboxSeleted({ node, onItemSelect, onItemSelectAll }, direction)
}}
/>
)
}
}}
</Transfer>
)
}
/**
* 改变右边tree数据
* @param {*右边tree需要处理的keys集合} keys
* @param {*0-删除以上的keys 1-新增以上的keys} type
*/
const getRightTreeData = (keys, type) => {
let arr = [...rightTreeData]
if (keys?.length > 0) {
keys.forEach(key => {
treeData.forEach(data => {
if (key === data.key) {
// 勾选的是父节点,查看右侧是否有勾选对象
let index = arr.findIndex(i => {
return i.key === key
})
if (type === 1) {
if (index === -1) {
arr.push(data)
} else if (index > -1 && arr?.[index]?.children?.length < data?.children?.length) {
// 先选择子项再勾选该父级时,传过来的keys是 ['0-1-0','0-1'],此时第一次循环已经将该父级放到arr中,
// 再遍历0-1时,需要先删除再将全部的children复制
arr.splice(index, 1)
arr.push(data)
}
} else if (type === 0) {
if (index > -1) {
arr.splice(index, 1)
}
}
} else {
// 勾选的是子节点
// 左侧数据处理
let selectedParentKey = '' //选定的父项id
let selectedObj = {} //选定对象
if (data?.children?.length > 0) {
data.children.forEach(child => {
if (key === child.key) {
selectedParentKey = data.key
selectedObj = child
}
})
}
// 右侧数据处理
if (Object.keys(selectedObj)?.length > 0) {
let newData = {}
// 查看右侧是否有选中子项的父项
let index = arr.findIndex(item => {
return item.key === selectedParentKey
})
if (index > -1) {
// 右侧已有选中子项的父项,selectedIndex查看右侧子项是否有勾选对象
let oldChildArr = [...arr[index].children]
let selectedIndex = oldChildArr?.findIndex(o => {
return o.key === selectedObj.key
})
if (selectedIndex === -1 && type === 1) {
arr[index].children.push(selectedObj)
}
if (selectedIndex > -1 && type === 0) {
arr[index].children.splice(selectedIndex, 1)
if (arr[index].children?.length === 0) {
arr.splice(index, 1)
}
}
} else {
// 右侧没有选中子项的父项
if (type === 1) {
newData = { ...data }
newData.children = []
newData.children.push(selectedObj)
arr.push(newData)
} else if (type === 0) {
arr = []
}
}
}
}
})
})
setRightTreeData(arr)
}
}
// 左右移动按钮
const onChange = (keys, direction, moveKeys) => {
let changeArrType = 1 // 0-删除 1-新增
if (direction == 'left') {
changeArrType = 0
if (keys.length > 0) {
treeData.forEach(item => {
let index = keys.indexOf(item.key)
if (index > -1 && item.children?.length > 0) {
item.children?.forEach(v => {
if (moveKeys.includes(v.key)) {
keys.splice(index, 1)
}
})
}
})
}
}
setTargetKeys(keys)
let keysList = changeArrType === 1 ? keys : moveKeys
getRightTreeData(keysList, changeArrType)
};
return <TreeTransfer dataSource={treeData} targetKeys={targetKeys} onChange={onChange} />
}
export default AuthorizationModal