react生成树结构表格

问题描述

现在有需求要生成一个权限的表格

clipboard.png
大概是这样, 后端传回来的数据是一棵树结构的, 最深层有五层, 最浅层有一层,
现在我使用递归生成的表格, 结构可以但是每一项之间的border会有重叠, 调起来比较麻烦

问题出现的环境背景及自己尝试过哪些方法

相关代码

// 请把代码文本粘贴到下方(请勿用图片代替代码)

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>;
    })

}

你期待的结果是什么?实际看到的错误信息又是什么?

现在时间比较紧, 不能在这上面浪费太多时间, 有没有别的办法或者库来生成这种表格

阅读 4.3k
1 个回答

我基于antd的table和checkbox自己写了一个, 你可以试试

图片描述

/**
 * 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
}

//subArray中有元素在array中返回true
const hasInArray = (subArray, array) => {
    for(let i in subArray) {
        if (array.indexOf(subArray[i]) >= 0)
            return true
    }
}

//subArray中有元素不在array中返回true
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; i++) {
            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]; j++) {
                            rowSpan++
                        }
                        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}
            />
        )
    }
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题