React spanning tree structure table

problem description

now there is a need to generate a permission table

clipboard.png
something like this. The data sent back from the backend is of a tree structure, with five layers in the deepest layer and one layer in the shallowest layer.
now I use the recursive generated table, and the structure is OK, but the border between each item will overlap, which is troublesome to adjust.

the environmental background of the problems and what methods you have tried

related codes

/ / Please paste the code text below (do not replace the code with pictures)

createChildBlock (list) {

    let permissionIds = this.props.permissionIds;

    let toggleCheckPermission = this.props.toggleCheckPermission;

    return list.map(row => {
        const checked = this.isCheck(row);

        return <div className={"tree-content"} key={row.treeId || row.id}>
            <div className={"parent-block"}>
                <span>

                    <label htmlFor={row.treeId || row.id}>
                        <Checkbox id={row.treeId || row.id} indeterminate={this.isGroup(row) && !checked} checked={checked} onChange={function ({target}) {
                            toggleCheckPermission(row, target.checked)
                        }}/>
                        {row.treeName || row.perName}
                    </label>
                </span>
            </div>
            <div className={"child-block"}>
                {this.createChildBlock(row.listChild || row.listPerm ||  [])}
            </div>
        </div>;
    })

}

what result do you expect? What is the error message actually seen?

time is tight now, so we can"t waste too much time on it. Is there any other way or library to generate this kind of table


I wrote one myself based on antd's table and checkbox. You can try

.

/**
 * Created by siver on 2018/11/23 16:37.
 */
import React from 'react'
import {Checkbox, Table} from 'antd'
import PropTypes from 'prop-types'

const TestData = [
    {
        title: "",
        children: [
            {
                title: ""
            },
            {
                title: ""
            }
        ]
    },
    {
        title: ""
    },
    {
        title: "",
        children: [
            {
                title: "",
                children: [
                    {
                        title: ""
                    },
                    {
                        title: ""
                    }
                ]
            }
        ]
    },
    {
        title: ""
    },
    {
        title: ""
    },
    {
        title: "",
        children: [
            {
                title: "",
                children: [
                    {
                        title: ""
                    },
                    {
                        title: ""
                    },
                    {
                        title: ""
                    }
                ]
            },
            {
                title: ""
            }
        ]
    }
]

//Data, 
const encodeData = (data, i = 0, addData = {}) => {
    let ret = []
    data.map(item => {
        let next = Object.assign({[i]: item.title}, addData)
        if (item.children) {
            ret.push(...encodeData(item.children, i + 1, next))
        } else {
            ret.push(next)
        }
    })
    return ret
}

//
const getMaxDepth = data => {
    let max = 1
    data.map(item => {
        if (item.children) {
            let childDepth = getMaxDepth(item.children)
            if (max < 1 + childDepth)
                max = 1 + childDepth
        }
    })
    return max
}

//map, 
const getChildrenMap = data => {
    let ret = {}
    data.map(item => {
        if(item.children){
            ret[item.title] = []
            let childrenMap = getChildrenMap(item.children)
            item.children.map(subItem => {
                if(childrenMap[subItem.title]){
                    ret[item.title].push(...childrenMap[subItem.title])
                } else {
                    ret[item.title].push(subItem.title)
                }
            })
            ret = Object.assign(ret, childrenMap)
        }
    })
    return ret
}

//subArrayarraytrue
const hasInArray = (subArray, array) => {
    for(let i in subArray) {
        if (array.indexOf(subArray[i]) >= 0)
            return true
    }
}

//subArrayarraytrue
const hasNotInArray = (subArray, array) => {
    for(let i in subArray) {
        if (array.indexOf(subArray[i]) < 0)
            return true
    }
}

export default class TreeTable extends React.Component {
    static props = {
        data: PropTypes.arrayOf(PropTypes.object),  //
        onChange: PropTypes.func,   // Function(checkedKeys: Array)
        columnWidthArray: PropTypes.arrayOf(PropTypes.string),  //, 100%
        checkedKeys: PropTypes.array    //key
    }

    static defaultProps = {
        data: TestData,
        onChange: () => {},
        columnWidthArray: ["10%","10%","30%","25%","25%"],
        checkedKeys: []
    }

    generateData = () => {
        let {data, columnWidthArray} = this.props
        //
        const dataSource = encodeData(data)
        //, 
        const max = getMaxDepth(data)
        //childrenMap, 
        this.childrenKeyMap = getChildrenMap(data)
        let columns = []
        for (let i = 0; i < max; iPP) {
            columns.push({
                key: i,
                dataIndex: i,
                title: i,
                width: columnWidthArray[i],
                render: (t, r, rowIndex) => {
                    const obj = {
                        children: t ? this.getCheckBox(t) : "",
                        props: {}
                    }
                    //
                    if (r[i] === undefined) {
                        obj.props.colSpan = 0
                    } else if (r[i + 1] === undefined && i < max - 1) {
                        obj.props.colSpan = max - i
                    }
                    //
                    if (dataSource[rowIndex - 1] && dataSource[rowIndex - 1][i] === dataSource[rowIndex][i]) {
                        obj.props.rowSpan = 0
                    } else {
                        let rowSpan = 1
                        for (let j = 1; dataSource[rowIndex + j] && dataSource[rowIndex + j][i] === dataSource[rowIndex][i]; jPP) {
                            rowSpanPP
                        }
                        obj.props.rowSpan = rowSpan
                    }
                    return obj
                },
            })
        }
        this.setState({dataSource, columns})
    }

    getCheckBox = t => {
        let {checkedKeys} = this.state
        const hasSeleted = hasInArray(this.childrenKeyMap[t], checkedKeys)
        const hasUnSeleted = hasNotInArray(this.childrenKeyMap[t], checkedKeys)
        return (
            <Checkbox
                checked={this.childrenKeyMap[t] ? hasSeleted && !hasUnSeleted : checkedKeys.indexOf(t) >= 0}
                indeterminate={this.childrenKeyMap[t] ? hasSeleted && hasUnSeleted : false}
                onChange={e => {
                    if (e.target.checked) {
                        //, 
                        if(this.childrenKeyMap[t]){
                            this.childrenKeyMap[t].map(item => {
                                if(checkedKeys.indexOf(item) < 0){
                                    checkedKeys.push(item)
                                }
                            })
                        } else {
                            checkedKeys.push(t)
                        }
                    } else {
                        //, 
                        if(this.childrenKeyMap[t]){
                            this.childrenKeyMap[t].map(item => {
                                if(checkedKeys.indexOf(item) >= 0){
                                    checkedKeys.splice(checkedKeys.indexOf(item), 1)
                                }
                            })
                        } else {
                            checkedKeys.splice(checkedKeys.indexOf(t), 1)
                        }
                    }
                    this.setState({checkedKeys})
                    this.props.onChange(checkedKeys)
                }}
            >{t}</Checkbox>
        )
    }

    constructor(props) {
        super(props)

        this.state = {
            dataSource: [],
            columns: [],
            checkedKeys: []
        }
    }

    componentWillMount() {
        this.generateData()
    }

    componentDidUpdate(preProps) {
        if(preProps.data !== this.props.data){
            this.generateData()
        }
        if(preProps.checkedKeys !== this.props.checkedKeys){
            this.setState({
                checkedKeys: this.props.checkedKeys
            })
        }
    }

    render() {
        let {dataSource, columns} = this.state
        return (
            <Table
                bordered
                pagination={false}
                scroll={{y: true}}
                showHeader={false}
                dataSource={dataSource}
                columns={columns}
            />
        )
    }
}
Menu