Antd的Transfer树穿梭框(支持搜索和全选)

(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
  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值