Skip to content

Latest commit

 

History

History
336 lines (309 loc) · 7.36 KB

动态表头生成算法.md

File metadata and controls

336 lines (309 loc) · 7.36 KB
title date tags
动态表头生成算法
2017-09-18
Javascript
算法

前言

最近接了个需求,需要根据数据结构动态的生成表格。会出现嵌套的表头。

antd

在我的印象中,antdTable 实现了这样的功能。然后我就看了它的数据结构,类似这样的

const columns = [
    {
        title: 'Name',
        dataIndex: 'name',
        key: 'name',
    },
    {
        title: 'Other',
        children: [
            {
                title: 'Age',
                dataIndex: 'age',
                key: 'age',
            },
            {
                title: 'Address',
                children: [
                    {
                        title: 'Street',
                        dataIndex: 'street',
                        key: 'street',
                    },
                    {
                        title: 'Block',
                        children: [
                            {
                                title: 'Building',
                                dataIndex: 'building',
                                key: 'building',
                            },
                            {
                                title: 'Door No.',
                                dataIndex: 'number',
                                key: 'number',
                            },
                        ],
                    },
                ],
            },
        ],
    },
    {
        title: 'Company',
        children: [
            {
                title: 'Company Address',
                dataIndex: 'companyAddress',
                key: 'companyAddress',
            },
            {
                title: 'Company Name',
                dataIndex: 'companyName',
                key: 'companyName',
            },
        ],
    },
    {
        title: 'Gender',
        dataIndex: 'gender',
        key: 'gender',
    },
];

const dataSourse = [{
    key: i,
    name: 'John Brown',
    age: i + 1,
    street: 'Lake Park',
    building: 'C',
    number: 2035,
    companyAddress: 'Lake Street 42',
    companyName: 'SoftLake Co',
    gender: 'M',
}, {
    key: i,
    name: 'John Brown',
    age: i + 1,
    street: 'Lake Park',
    building: 'C',
    number: 2035,
    companyAddress: 'Lake Street 42',
    companyName: 'SoftLake Co',
    gender: 'M',
}, {
    key: i,
    name: 'John Brown',
    age: i + 1,
    street: 'Lake Park',
    building: 'C',
    number: 2035,
    companyAddress: 'Lake Street 42',
    companyName: 'SoftLake Co',
    gender: 'M',
}];

在antd的官网上,生成了这样的示例。

NameOther CompanyGender
AgeAddress Company Address Company Name
StreetBlock
Building Door No.
John Brown1 Lake ParkC 2035 Lake Street 42SoftLake Co M
John Brown 2 Lake Park C 2035 Lake Street 42 SoftLake Co M
John Brown 2 Lake Park C 2035 Lake Street 42 SoftLake Co M

经过研究,它的html是这样的

<table class="T-table" border="1px solid #dcdcdc">
	<thead>
		<tr>
			<th rowspan="4">Name</th><th colspan="4">Other</th>
			<th colspan="2">Company</th><th rowspan="4">Gender</th>
		</tr>
		<tr>
			<th rowspan="3">Age</th><th colspan="3">Address</th>
			<th rowspan="3">Company Address</th>
			<th rowspan="3">Company Name</th>
		</tr>
		<tr>
			<th rowspan="2">Street</th><th colspan="2">Block</th>
		</tr>
		<tr>
			<th rowspan="1">Building</th>
			<th rowspan="1">Door No.</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>John Brown</td><td>1</td>
			<td>Lake Park</td><td>C</td>
			<td>2035</td>
			<td>Lake Street 42</td><td>SoftLake Co</td>
			<td>M</td>
		</tr>
		<tr>
			<td>John Brown</td>
			<td>2</td>
			<td>Lake Park</td>
			<td>C</td>
			<td>2035</td>
			<td>Lake Street 42</td>
			<td>SoftLake Co</td>
			<td>M</td>
		</tr>
		<tr>
			<td>John Brown</td>
			<td>2</td>
			<td>Lake Park</td>
			<td>C</td>
			<td>2035</td>
			<td>Lake Street 42</td>
			<td>SoftLake Co</td>
			<td>M</td>
		</tr>
	</tbody>
</table>

实现

接下来,我就要用自己的算法来实现这个表头。保证表头的嵌套和数据的对号入座。

因为是React项目,如果要写jsx是一定要引入React的,不然就会报错。

// (求大佬帮忙改进)
import React from 'react';

// 表的最大深度
let max = 0;
// 节点的最大children数
let maxColSpan = 0;
// 放置格式化后的表头节点
export const stack = {};
// 只与dataIndex相关的key
export const key = [];
// 放置每个节点的层级
const level = {};
// 将节点归类,比如,第一层或者只有一层的为一类,第二层的为一类
const tr = {};

/**
 * 获取表的最大深度,顺便生成key和level
 * @param item
 * @param length
 */
function getMax(item, length) {
    if (item.children) {
        item.children.forEach((i) => {
            getMax(i, length + 1);
        });
    } else {
        max = max < length ? length : max;
        key.push(item.title);
    }
    level[item.title] = length;
}

/**
 * 获取节点下面最底部的节点个数
 * @param item
 * @param length
 */
function getChildrenMaxLength(item, length) {
    if (item.children) {
        item.children.forEach((i) => {
            getChildrenMaxLength(i, (length + item.children.length) - 1);
        });
    } else {
        maxColSpan = maxColSpan > length ? maxColSpan : length;
    }
}

/**
 * 生成节点的colSpan和rowSpan
 * @param item
 */
function getSpan(item) {
    if (item.children) {
        maxColSpan = 0;
        getChildrenMaxLength(item, 1);
        stack[item.title] = {
            ...item,
            colSpan: maxColSpan,
        };
        item.children.forEach((i) => {
            getSpan(i);
        });
    } else {
        stack[item.title] = {
            ...item,
            rowSpan: (max + 1) - level[item.title],
        };
    }
}


export default (columns) => {
    columns.forEach((col) => {
        getMax(col, 1);
    });

    columns.forEach((col) => {
        getSpan(col);
    });

    // 根据根据节点的层级,进行分类
    Object.keys(level).forEach((item) => {
        if (!tr[level[item]]) {
            tr[level[item]] = [];
        }
        tr[level[item]].push(item);
    });

    // 生成表头
    return Object.values(tr).map((item, index) => <tr key={`T-tHead-tr-${index}`}>
        {
                item.map((it, ind) => {
                    const th = stack[it];
                    return (<th
                        key={`T-tHead-th-${ind}`}
                        colSpan={th.colSpan}
                        rowSpan={th.rowSpan}
                    >
                        {th.title}
                    </th>);
                })
            }
    </tr>);
};

在使用之时,就能生成对应的表头了。