美文网首页
Ant+React树形穿梭框实现(支持搜索和全选)

Ant+React树形穿梭框实现(支持搜索和全选)

作者: 陶菇凉 | 来源:发表于2023-12-13 08:59 被阅读0次

    1.效果图

    image.png

    2.官方组件的示例不是很全面,而且右边不是树形的;直接上源码,我的是ts的语法,参考的原文章是js语法

    //组件代码
    import React, { useState,forwardRef,useImperativeHandle } from 'react'
    import { Transfer, Tree,Modal } from 'antd'
    import { DataNode } from 'antd/es/tree'
    import './chooseUserTransfer.less'
    const AuthorizationModal = (props: any, ref: any) => {
        const [targetKeys, setTargetKeys] = useState<any>([])
        const [rightTreeData, setRightTreeData] = useState([])
        const [treeData,setTreeData]=useState([])
        const generateTree = (treeNodes: DataNode[] = [], checkedKeys: string[] = []): DataNode[] =>
            treeNodes.map(({ children, ...props }:any) => ({
                ...props,
                disabled: checkedKeys.includes(props.key as string),
                children: generateTree(children, checkedKeys),
            }))
     
        const dealCheckboxSeleted = ({ node, onItemSelect, onItemSelectAll }:any, direction:any) => {
            let {
                checked,
                halfCheckedKeys,
                node: { key, children },
            } = node
            // 勾选的是父节点
            if (children?.length > 0) {
                let keys: any[] = []
                let temp = []
                if (direction === 'left') {
                    let state = false
                    if (rightTreeData.length > 0) {
                        rightTreeData?.map((item:any) => {
                            if (item?.childCompanies?.length > 0 && (item.key == key)) {
                                temp = item.childCompanies.filter((v:any) => !item.childCompanies.some(((t:any) => t.key === v.key)))
                                temp?.forEach((child:any) => {
                                    keys.push(child.key)
                                })
                            } else {
                                state = true
                            }
                        })
                    } else {
                        state = true
                    }
                    if (state) {
                        children?.forEach((child:any) => {
                            keys.push(child.key)
                        })
                    }
                    onItemSelectAll([...keys, key], checked)
                }
                if (direction === 'right') {
                    children?.forEach((child:any) => {
                        keys.push(child.key)
                    })
                    onItemSelectAll([...keys], checked)
                }
            } else {
                // 勾选的是子节点
                if (!checked) {
                    // 查找该元素的父元素
                    let parentKeys = []
                    parentKeys = [halfCheckedKeys?.[0]] || []
                    if (parentKeys[0] == undefined) {
                        // 当一级下的二级全部取消勾选,一级也取消勾选
                        treeData.forEach((tree:any) => {
                            if (tree.children) {
                                tree.children?.forEach((child:any) => {
                                    if (child?.key === key) {
                                        parentKeys.push(tree?.key)
                                    }
                                })
                            }
                        })
                    }
                    onItemSelectAll([...parentKeys, key], checked)
                } else {
                    let parentKey:any =''
                    treeData.forEach((tree:any) => {
                        if (tree?.children) {
                            tree.children?.forEach((child:any) => {
                                if (child?.key === key) {
                                    parentKey = tree?.key
                                }
                            })
                        }
                    })
     
                    if (!halfCheckedKeys?.includes(parentKey) && parentKey != '') {
                        onItemSelectAll([key, parentKey], checked)
                    } else {
                        onItemSelect(key, checked)
                    }
                }
            }
        }
        
        // modal组件
        const [type, setType] = useState(1);//1选择用户2选择用户组
        const [isModalOpen, setIsModalOpen] = useState(false);
        const handleOk = () => {
            let result:any = []
            rightTreeData.forEach((ele:any)=>{
              result.push({
                key:ele.key,
                title:ele.title,
              })
              ele.children.forEach((eles:any)=>{
                result.push({
                  key:eles.key,
                  title:eles.title,
                })
              })
            })
            result=result.filter((ele: any) =>targetKeys.includes(ele.key)&&ele.key.split('_').length>2)
            props.onChildData(type, result)
            setIsModalOpen(false);
          };
          const handleCancel = () => {
            setIsModalOpen(false);
          };
          useImperativeHandle(ref, () => ({
            // changeVal 就是暴露给父组件的方法
            // changeVal: () => {
            //     setIsModalOpen(true);
        
            // }
            showModal
        
          }));
          
        const showModal = (type: number, data: any, rightData: []) => {
            setIsModalOpen(true);
            setType(type)
            setTreeData(data)
            let keyArr:any=[]
            rightData.forEach((ele:any)=>{
                keyArr.push(ele.key)
                ele.children.forEach((eles:any)=>{
                    keyArr.push(eles.key)
                })
            })
            setTargetKeys(keyArr)
            setRightTreeData(rightData)
        };
        const TreeTransfer = ({ dataSource, targetKeys, ...restProps }:any) => {
            const transferDataSource:any = []
            const dataSourceData = dataSource
            let rightTreeDataResult = [...rightTreeData]
            function flatten(list = []) {
                list.forEach((item:any) => {
                    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:any) => {
                            item = Object.assign({}, item)
                            if (item.children) {
                                item.children = item.children?.filter((res:any) => (res.title.indexOf(val) > -1))
                            }
                            return item
                        }).filter((item:any) => {
                            if (item.children?.length > 0 || val.length == 0) {
                                item = Object.assign({}, item)
                                item.children?.filter((e:any) => (
                                    e.title.indexOf(val) > -1 ? '' : item.title.indexOf(val) > -1
                                ))
                            } else {
                                item = item.title.indexOf(val) > -1
                            }
                            return item
                        })
                        if (dir === 'left') {
                            dataSource = newDeptList
                        }
                        if (dir === 'right') {
                            rightTreeDataResult = newDeptList
                        }
                    }}
                >
                    {({ direction, onItemSelect, onItemSelectAll, selectedKeys }) => {
                        if (direction === 'left') {
                            const checkedKeys = [...selectedKeys, ...targetKeys]
                            return (
                                <Tree
                                    blockNode
                                    checkable
                                    checkedKeys={checkedKeys}
                                    defaultExpandedKeys={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') {
                            //defaultExpandAll 默认展开全部
                            const checkedKeys = [...selectedKeys]
                            return (
                                <Tree
                                    blockNode
                                    checkable
                                    checkedKeys={checkedKeys}
                                    treeData={rightTreeDataResult}
                                    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:any, type:any) => {
            let arr:any = [...rightTreeData]
            if (keys?.length > 0) {
                keys.forEach((key:any) => {
                    treeData.forEach((data:any) => {
                        if (key === data.key) {
                            // 勾选的是父节点,查看右侧是否有勾选对象
                            let index = arr.findIndex((i:any) => {
                                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:any = {}       //选定对象
                            if (data?.children?.length > 0) {
                                data.children.forEach((child:any) => {
                                    if (key === child.key) {
                                        selectedParentKey = data.key
                                        selectedObj = child
                                    }
                                })
                            }
                            // 右侧数据处理
                            if (Object.keys(selectedObj)?.length > 0) {
                                let newData:any = {}
                                // 查看右侧是否有选中子项的父项
                                let index = arr.findIndex((item:any) => {
                                    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:any, direction:any, moveKeys:any) => {
            let changeArrType = 1 // 0-删除  1-新增
            if (direction == 'left') {
                changeArrType = 0
                if (keys.length > 0) {
                    treeData.forEach((item:any) => {
                        let index = keys.indexOf(item.key)
                        if (index > -1 && item.children?.length > 0) {
                            item.children?.forEach((v:any) => {
                                if (moveKeys.includes(v.key)) {
                                    keys.splice(index, 1)
                                }
                            })
                        }
                    })
                }
            }
            setTargetKeys(keys)
            let keysList = changeArrType === 1 ? keys : moveKeys
            getRightTreeData(keysList, changeArrType)
        };
     
        return  <Modal width={600} title={type == 1 ? '选择用户' : '选择设备'} open={isModalOpen} onOk={handleOk} onCancel={handleCancel}>
           <TreeTransfer dataSource={treeData} targetKeys={targetKeys} onChange={onChange} /> </Modal>
    }
     
    export default forwardRef(AuthorizationModal)
    

    3.如何引入使用代码

    import ChooseUserTreeTransfer from "../../components/Common/Add/chooseUserTransfer"
    //方法
    const childRefUser = useRef<any>(null)
    const eventClick=()=>{
     childRefUser.current.showModal()
    }
    const getChildDataUser = (type: number, data: any) => {
    }
    //组件使用
     <ChooseUserTreeTransfer ref={childRefUser} onChildData={getChildDataUser}></ChooseUserTreeTransfer>
    

    备注
    参考地址:https://blog.csdn.net/weixin_49581008/article/details/128953065

    相关文章

      网友评论

          本文标题:Ant+React树形穿梭框实现(支持搜索和全选)

          本文链接:https://www.haomeiwen.com/subject/bffmgdtx.html