zoukankan      html  css  js  c++  java
  • 自定义树形穿梭框组件

    因项目需求是库表结构,这里做的是动态加载子选项,默认不显示表名选项,点击库名再去动态加载并显示该库中的所有表,效果如下:

    .ts代码:

    // 自定义树形穿梭框
    import React, { useEffect, useState } from 'react';
    import { Tree, Checkbox, Button, Input, message } from 'antd';
    import { CaretDownOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons';
    import './TreeSelectTransfer.less';
    import { ApiRes } from '@/api/config';
    
    const { Search } = Input;
    const { TreeNode } = Tree;
    
    const getLeftAndRightKey = (data, rightDataKey = {}) => {
        let len = data.length;
        let leftData: any = new Array(len).fill('').map(() => {
            return { children: [] };
        });
        let rightData: any = new Array(len).fill('').map(() => {
            return { children: [] };
        });
    
        let leftTotalCount = 0;
        let rightTotalCount = 0;
        data.map((item, index) => {
            if (!item.children) {
                leftData[index].name = item.name;
                leftData[index].key = index + '';
            } else {
                item.children.map((childItem, childIndex) => {
                    let nowKey = `${childItem.id}`;
                    if (nowKey in rightDataKey) {
                        rightData[index].name = item.name;
                        rightData[index].key = index + '';
                        rightData[index].children.push(childItem);
                        rightTotalCount += 1;
                    } else {
                        leftTotalCount += 1;
                        leftData[index].name = item.name;
                        leftData[index].key = index + '';
                        leftData[index].children.push(childItem);
                    }
                });
            }
        });
        return {
            leftData,
            rightData,
            leftTotalCount,
            rightTotalCount
        };
    };
    
    // 搜索过滤
    const filterData = (data, filterValue) => {
        let filterTrimV = filterValue.trim();
        if (!filterTrimV) {
            return data;
        }
        data = data.filter((item) => {
            if (item.name && item.name.includes(filterTrimV)) {
                return true;
            } else if (item.children && item.children.length) {
                item.children = item.children.filter((childItem) => {
                    if (childItem.name.includes(filterTrimV)) {
                        return true;
                    } else {
                        return false;
                    }
                });
                return item.children.length > 0;
            }
            return true;
        });
        return data;
    };
    
    function TreeSelectTransfer(props) {
        const [leftSelectKey, setLeftSelectKey] = useState({});
        const [rightDataKey, setRightDataKey] = useState({});
        const [rightSelectKey, setRightSelectKey] = useState({});
        const [leftFValue, setLeftFValue] = useState('');
        const [rightFValue, setRightFValue] = useState('');
        const [selectKeys, setSelectKeys] = useState([]);
    
        const [dataSource, setDataSource] = useState([]);
        const [data, setData] = useState([]);
        const [childrenData, setChildrenData] = useState([]);
        const [fLeftData, setFLeftData] = useState([]);
        const [leftData, setLeftData] = useState([]);
        const [rightData, setRightData] = useState([]);
        const [fRightData, setFRightData] = useState([]);
        const [leftSelectAll, setLeftSelectAll] = useState(true);
        const [rightSelectAll, setRightSelectAll] = useState(true);
        const [leftTotalCount, setLeftTotalCount] = useState(null);
        const [rightTotalCount, setRightTotalCount] = useState(null);
        const { titles = [] } = props;
    
        // 全选
        const selectOrCancelAll = (type, selectKeys, data, isSelectAll) => () => {
            if (isSelectAll) {
                if (type === 'leftSelectKey') {
                    setLeftSelectKey({});
                } else {
                    setRightSelectKey({});
                }
            } else {
                let newSelectKeys = { ...selectKeys };
                data.map((item) => {
                    item.children.map((childItem) => {
                        if (!(childItem.key in newSelectKeys)) {
                            newSelectKeys[childItem.id] = childItem.name;
                        }
                    });
                });
                if (type === 'leftSelectKey') {
                    setLeftSelectKey(newSelectKeys);
                } else {
                    setRightSelectKey(newSelectKeys);
                }
            }
        };
    
        const selectGoToRight = () => {
            const { handleChangeTable } = props;
            const dataKey = { ...rightDataKey, ...leftSelectKey };
            let targetKeys = Object.keys(dataKey);
            setLeftSelectKey({});
            setRightDataKey(dataKey);
            if (typeof handleChangeTable == 'function') {
                handleChangeTable(targetKeys, 'right', selectKeys);
            }
        };
        const selectGoToLeft = () => {
            const { handleChangeTable } = props;
            for (let key in rightSelectKey) {
                delete rightDataKey[key];
            }
            let targetKeys = Object.keys(rightDataKey);
            setRightSelectKey({});
            setRightDataKey(rightDataKey);
    
            if (typeof handleChangeTable == 'function') {
                handleChangeTable(targetKeys, 'left', selectKeys);
            }
        };
    
        const bindCheckLeft = (checkedKeys, e) => {
            if (!checkedKeys.length) {
                setLeftSelectKey({});
            }
            if (e.checkedNodes.length) {
                const temp = {};
                const keys = [];
                e.checkedNodes.map((item) => {
                    if (!item.children) {
                        temp[item.key] = item.title;
                        keys.push(item.key);
                    }
                });
                setLeftSelectKey(temp);
                setSelectKeys(keys);
            }
        };
        const bindCheckRight = (checkedKeys, e) => {
            if (!checkedKeys.length) {
                setRightSelectKey({});
            }
            if (e.checkedNodes.length) {
                const temp = {};
                const keys = [];
                e.checkedNodes.map((item) => {
                    if (!item.children) {
                        temp[item.key] = item.title;
                        keys.push(item.key);
                    }
                });
                setRightSelectKey(temp);
                setSelectKeys(keys);
            }
        };
        const searchOnChange = (type, e) => {
            const { value } = e.target;
            if (type == 'left') {
                setLeftFValue(value);
            } else {
                setRightFValue(value);
            }
        };
    
        const onSelectNode = (selectedKeys, node) => {
            const { Api, dbId } = props.childrenParams;
            Api({ dbId, schemaName: node.node.title }).then((res: ApiRes) => {
                if (res.code === 200) {
                    const temp =
                        res.data.length &&
                        res.data.map((item, index) => {
                            return {
                                id: item.tableId,
                                key: `${selectedKeys}-${index}`,
                                name: item.tableName
                            };
                        });
                    dataSource.length &&
                        dataSource.map((item) => {
                            if (item.name === node.node.title) {
                                item.children = temp;
                            }
                        });
                    setChildrenData(temp);
                    setData(dataSource);
                } else {
                    message.error(res.message);
                }
            });
        };
        useEffect(() => {
            if (props.tableCacheMap.length) {
                setDataSource(props.tableCacheMap);
                setData(props.tableCacheMap);
            }
        }, [props.tableCacheMap]);
        useEffect(() => {
            let { leftData, rightData, leftTotalCount, rightTotalCount } = getLeftAndRightKey(data, rightDataKey);
            setLeftData(leftData);
            setRightData(rightData);
            setLeftTotalCount(leftTotalCount);
            setRightTotalCount(rightTotalCount);
            setLeftSelectAll(leftSelectCount == leftTotalCount);
            setRightSelectAll(rightSelectCount == rightTotalCount);
    
            const leftList = filterData(leftData, leftFValue);
            const rightList = filterData(rightData, rightFValue);
            setFLeftData(leftList);
            setFRightData(rightList);
            if (leftSelectCount === 0) {
                setLeftSelectAll(false);
            }
            if (rightSelectCount === 0) {
                setRightSelectAll(false);
            }
        }, [data, childrenData, Object.keys(rightDataKey).length, leftFValue, rightFValue, leftSelectKey, rightSelectKey]);
        useEffect(() => {}, [leftData, rightData, leftFValue]);
    
        let leftSelectCount = Object.keys(leftSelectKey).length;
        let rightSelectCount = Object.keys(rightSelectKey).length;
        let isHaveLeftChecked = leftSelectCount > 0;
        let isHaveRightChecked = rightSelectCount > 0;
    
        return (
            <div className="tree-select-transfer">
                <div className="tst-l">
                    <div className="tst-header">
                        <Checkbox
                            indeterminate={!leftSelectAll && isHaveLeftChecked}
                            onChange={selectOrCancelAll('leftSelectKey', leftSelectKey, leftData, leftSelectAll)}
                            checked={leftSelectAll}
                        >
                            {`${leftSelectCount ? leftSelectCount + '/' : ''}${leftTotalCount} 项`}
                        </Checkbox>
                        {titles[0] ? <span style={{ float: 'right' }}>{titles[0]}</span> : null}
                    </div>
                    <Search style={{ marginBottom: 8 }} placeholder="Search" onChange={(e) => searchOnChange('left', e)} />
                    <div style={{ overflow: 'auto', height: '300px' }}>
                        <Tree
                            checkable
                            showIcon
                            defaultExpandAll
                            onCheck={bindCheckLeft}
                            checkedKeys={Object.keys(leftSelectKey)}
                            switcherIcon={<CaretDownOutlined />}
                            onSelect={onSelectNode}
                        >
                            {fLeftData.map((item) => {
                                if (!!item.name) {
                                    return (
                                        <TreeNode title={item.name} key={item.key} checkable={!!item.children.length}>
                                            {item.children.map((childItem) => {
                                                return <TreeNode key={childItem.id} title={childItem.name} />;
                                            })}
                                        </TreeNode>
                                    );
                                }
                            })}
                        </Tree>
                    </div>
                </div>
                <div className="tst-m ">
                    <Button
                        type="primary"
                        style={{ marginBottom: 4 }}
                        disabled={!isHaveLeftChecked}
                        onClick={selectGoToRight}
                    >
                        <RightOutlined />
                    </Button>
                    <Button type="primary" disabled={!isHaveRightChecked} onClick={selectGoToLeft}>
                        <LeftOutlined />
                    </Button>
                </div>
                <div className="tst-r">
                    <div className="tst-header">
                        <Checkbox
                            indeterminate={!rightSelectAll && isHaveRightChecked}
                            checked={rightSelectAll}
                            onChange={selectOrCancelAll('rightSelectKey', rightSelectKey, rightData, rightSelectAll)}
                        >
                            {`${rightSelectCount ? rightSelectCount + '/' : ''}${rightTotalCount} 项`}
                        </Checkbox>
                        {titles[1] ? <span style={{ float: 'right' }}>{titles[1]}</span> : null}
                    </div>
                    <Search style={{ marginBottom: 8 }} placeholder="Search" onChange={(e) => searchOnChange('right', e)} />
                    <div style={{ overflow: 'auto', height: '300px' }}>
                        <Tree
                            checkable
                            showIcon
                            defaultExpandAll
                            onCheck={bindCheckRight}
                            checkedKeys={Object.keys(rightSelectKey)}
                            switcherIcon={<CaretDownOutlined />}
                        >
                            {fRightData.map((item) => {
                                if (!!item.name) {
                                    return (
                                        <TreeNode title={item.name} key={item.key}>
                                            {item.children.map((childItem) => {
                                                let isChecked = childItem.key in rightSelectKey;
                                                return (
                                                    <TreeNode
                                                        icon={<Checkbox checked={isChecked} />}
                                                        key={childItem.id}
                                                        title={childItem.name}
                                                    />
                                                );
                                            })}
                                        </TreeNode>
                                    );
                                }
                            })}
                        </Tree>
                    </div>
                </div>
            </div>
        );
    }
    
    export default TreeSelectTransfer;

    样式 .less:

    .tree-select-transfer {
      overflow: hidden;
      width: 832px;
      height: 400px;
      display: flex;
      align-items: center;
      vertical-align: middle;
        .tst-l, .tst-r {
          background-color: #fff;
          flex: 1;
          border: 1px solid #d9d9d9;
          display: inline-block;
          border-radius: 4px;
          position: relative;
          height: 100%;
          padding: 0 8px;
          padding-top: 40px;
          width: 0;
          .tst-header {
            height: 34px;
            padding: 6px 12px;
            border-radius: 4px 4px 0 0;
            color: #000;
            border-bottom: 1px solid #e8e8e8;
            overflow: hidden;
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
          }
          .ant-tree-node-content-wrapper .ant-tree-iconEle {
            display: none;
          }
          .ant-tree-child-tree .ant-tree-node-content-wrapper .ant-tree-iconEle {
            display: inline-block;
          }
          .ant-tree-child-tree .ant-tree-switcher {
            display: none;
          }
          .ant-input-suffix {
            color: rgba(0,0,0,0.45);
            cursor: pointer;
            &:hover {
              color: rgba(0,0,0,0.8);
            }
          }
        }
        .tst-m {
          display: grid;
          height: 60px;
          margin: 0 20px;
          bottom {
            width: 32px;
          }
        }
    }

    页面引入:

    import React, { useState, useEffect } from 'react';
    import { listDsSchemaName, listTablesByDsIdAndSchemaName } from '@/api/offlineTask/index';
    import TreeSelectTransfer from '@/components/treeSelectTransfer';
    
    function BigTask(props) {
        const [tableCacheMap, setTableCacheMap] = useState([]);
        
        // 获取所有库名
        function getListDsSchemaName(dbId) {
           ...
        }
    
        // 数据表API接口及参数
        const childrenParams = {
            Api: listTablesByDsIdAndSchemaName,
            dbId: props.syncForm.dbId
        };
    
        // 点击库名获取改库下所有数据表
        function handleChangeTable(targetKeys, direction, moveKeys) {
          ...
        }
      
        return (
          <TreeSelectTransfer
             tableCacheMap={tableCacheMap}
             childrenParams={childrenParams}
             handleChangeTable={handleChangeTable}
           />
      )
    }
    export default BigTask;
  • 相关阅读:
    Pwn-warmup_csaw_2016 writeup
    操作系统习题总结
    操作系统-存储器管理部分(待更新)
    树与二叉树之间的互相转换
    黑客攻防技术宝典-反病毒篇笔记(三)
    jaegeropentracing的Java-client完整分布式追踪链
    jaegeropentracing的Java-client
    IDEA2018.2版本注册
    Spring整合CXF webservice restful 实例
    带有WS-Security验证的webservice
  • 原文地址:https://www.cnblogs.com/minjh/p/15127585.html
Copyright © 2011-2022 走看看