美文网首页Web
React.js 动态生成交叉表格

React.js 动态生成交叉表格

作者: 爱吃猫的老虎 | 来源:发表于2019-12-17 15:45 被阅读0次
交叉表.png

组件

import React from "react";
import { withStyles } from "@material-ui/core";
import { xAxisMockData, measureLabelMockData, mockYAxisColLabel, mockXAxisColLabel, yAxisMockData, mockTaskResult } from "../data";
import { cloneDeep, findIndex, filter, find, isEqual, forEach } from "lodash";

const style: object = (theme: any) => ({
    th: {
        border: '1px solid',
    },
    tr: {

    }
});

const generateHeader: any = (xAxisData: any[], classes: any) => {

    const xAxisDataCopy = cloneDeep(xAxisData);
    const xAxisDataLength: number = xAxisDataCopy.length;
    const maxXAxisColsCount: number = xAxisDataCopy[xAxisDataLength - 1].length;
    const result = [];
    const measureLabelLength: number = measureLabelMockData.length;
    let measureRow: any = [];
    const yAxisColLength: number = mockYAxisColLabel.length;
    const yAxisDataLength: number = yAxisMockData.length;
    // 生成x轴坐标
    for(let i = 0; i < xAxisDataLength; i++){
        xAxisDataCopy[i].unshift(
            {
                name: mockXAxisColLabel[i],
                colSpan: 1,
                rowSpan: 1,
            },
        )
    }

    // 生成表格左上角占位单元格
    xAxisDataCopy[0].unshift(
        {
            name: '',
            colSpan: yAxisColLength - 1,
            rowSpan: xAxisDataLength,
        },
    );

    // 整理度量标签
    measureLabelMockData.forEach(measure => {
        measureRow.push(
            <th className={classes.th}>{measure}</th>
        )
    });
    // 根据放在x轴最后一个维度数量重复生成度量标签
    const basicMeasureRow = cloneDeep(measureRow);
    for (let i = 1; i < maxXAxisColsCount; i++){
        measureRow = measureRow.concat(basicMeasureRow);
    }
    // 整理y轴刻度
    const yColLabelTds: JSX.Element[] = [];
    mockYAxisColLabel.forEach(yCol => yColLabelTds.push(
        <th className={classes.th}>{yCol}</th>
    ));
    measureRow.unshift(yColLabelTds);
    result.push(
        <tr>
            {measureRow}
        </tr>
    );

    // row.push(<th className={classes.th} colSpan={yAxisColLength} rowSpan={xAxisDataLength} />);

    // 整理表头中x轴的数据
    for(let i = xAxisDataLength - 1; i >= 0; i--){
        const row: JSX.Element[] = [];
        for(const data of xAxisDataCopy[i]){
            let colSpan = data.colSpan * measureLabelLength;
            const index = findIndex(xAxisDataCopy[i], d => d === data);
            // 第一行维度的前两个和每行维度的第一个
            if((index === 0) || (i === 0 && index === 1)){
                colSpan = data.colSpan;
            }
            row.push(
                <th className={classes.th} colSpan={colSpan} rowSpan={data.rowSpan}>{data.name}</th>
            );

        }
        result.unshift(
            <tr>
                {row}
            </tr>
        );
    }

    // 递归查找需要添加的父级数据
    const addParentData = (self: any, level: number, selfIndex: number, row: JSX.Element[]) => {
        row.unshift(<td className={classes.th} colSpan={self.colSpan} rowSpan={self.rowSpan}>{self.name}</td>);
        if(selfIndex === 0){
            // @ts-ignore
            const parent = find(yAxisMockData[level - 1], d => d.name === self.parent);
            // @ts-ignore
            const uncles = level === 1? yAxisMockData[0]: filter(yAxisMockData[level - 1], d => d.parent = parent.parent);
            const parentIndex = findIndex(uncles, d => isEqual(d, parent));
            if(level !== 0){
                addParentData(parent, level - 1, parentIndex, row);
            }
        }
    };

    const body = [];
    // 整理表格体中y轴数据
    const lastLevelIndex = yAxisDataLength - 1;
    const rowCopy = cloneDeep(yAxisMockData[lastLevelIndex]);
    rowCopy.reverse();

    // 生成一个二维数组 宽是x轴字段最后一个数据量 * 度量数量,高是y轴最后一个数据量
    const results = new Array(rowCopy.length);
    for(let i = 0; i < results.length; i++){
        results[i] = new Array(xAxisData[xAxisDataLength - 1].length * measureLabelLength).fill(null);
    }

    // 整理taskResult
    for(const row of mockTaskResult){
        let x = 0;
        let y = 0;
        const yAxisColCount = mockYAxisColLabel.length;
        const xAxisColCount = mockXAxisColLabel.length;
        const lastXColData = find(row, data => isEqual(data.colName, mockXAxisColLabel[xAxisColCount - 1]));
        const lastYColData = find(row, data => isEqual(data.colName, mockYAxisColLabel[yAxisColCount - 1]));
        // 找到纵坐标
        y = findIndex(yAxisMockData[yAxisColCount - 1], data => isEqual(data.name, lastYColData && lastYColData.value));
        // @ts-ignore
        x = findIndex(xAxisData[xAxisColCount - 1], data => isEqual(data.name, lastXColData && lastXColData.value)) * measureLabelLength;
        for(let i = 0; i < measureLabelLength; i++){
            const measure = find(row, data => isEqual(data.colName, measureLabelMockData[i]));
            // @ts-ignore
            results[y][x + i] = measure.value;
        }
    }

    results.reverse();
    // 整理y轴数据和度量数据
    for(let i = 0; i < rowCopy.length; i++){
        const data = rowCopy[i];
        const rowElements: JSX.Element[] = [];
        // @ts-ignore
        const brothers = filter(yAxisMockData[lastLevelIndex], d => d.parent === data.parent);
        const index = findIndex(brothers, d => isEqual(d, data));
        addParentData(data, lastLevelIndex, index, rowElements);
        const measureThList: JSX.Element[] = [];
        forEach(results[i], (value: string) => measureThList.push(<th className={classes.th}>{value}</th>));
        // @ts-ignore
        rowElements.push(measureThList);
        body.unshift(
            <tr>
                {rowElements}
            </tr>
        );
    }
    // @ts-ignore
    return result.concat(body);
};

const SummaryTable = (props: any) => {

    const { classes } = props;
    // 生成表格
    const Table: any = generateHeader(xAxisMockData, classes);

    return (
        <table>
            {Table}
        </table>
    )
};

export default withStyles(style)(SummaryTable);

data.js

const xAxisMockData = [
    [
        {
            name: '黑龙江',
            colSpan: 10,
            rowSpan: 1,
        },
        {
            name: '吉林',
            colSpan: 3,
            rowSpan: 1,
        },
        {
            name: '辽宁',
            colSpan: 1,
            rowSpan: 1,
        },
    ],
    [
        {
            name: '哈尔滨',
            parent: '黑龙江',
            colSpan: 4,
            rowSpan: 1,
        },
        {
            name: '大庆',
            parent: '黑龙江',
            colSpan: 4,
            rowSpan: 1,
        },
        {
            name: '鹤岗',
            parent: '黑龙江',
            colSpan: 2,
            rowSpan: 1,
        },
        {
            name: '长春',
            parent: '吉林',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '四平',
            parent: '吉林',
            colSpan: 2,
            rowSpan: 1,
        },
        {
            name: '大连',
            parent: '辽宁',
            colSpan: 1,
            rowSpan: 1,
        },
    ],
    [
        {
            name: '道里区',
            parent: '哈尔滨',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '南岗区',
            parent: '哈尔滨',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '道外区',
            parent: '哈尔滨',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '平房区',
            parent: '哈尔滨',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '萨尔图区',
            parent: '大庆',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '龙凤区',
            parent: '大庆',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '肇州县',
            parent: '大庆',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '林甸县',
            parent: '大庆',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '东山区',
            parent: '鹤岗',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '南山区',
            parent: '鹤岗',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '朝阳区',
            parent: '长春',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '铁西区',
            parent: '四平',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '铁东区',
            parent: '四平',
            colSpan: 1,
            rowSpan: 1,
        },
        {
            name: '中山区',
            parent: '大连',
            colSpan: 1,
            rowSpan: 1,
        },
    ]
];

const yAxisMockData = [
    [
        {
            name: '菜鸟驿站',
            colSpan: 1,
            rowSpan: 2,
        },
        {
            name: '京东',
            colSpan: 1,
            rowSpan: 2
        },
    ],
    [
        {
            name: '韵达',
            parent: '菜鸟驿站',
            colSpan: 1,
            rowSpan: 1
        },
        {
            name: '申通',
            parent: '菜鸟驿站',
            colSpan: 1,
            rowSpan: 1
        },
        {
            name: '京东物流',
            parent: '京东',
            colSpan: 1,
            rowSpan: 1
        },
        {
            name: '京东派',
            parent: '京东',
            colSpan: 1,
            rowSpan: 1
        },
    ]
];

const measureLabelMockData = [
    '好评度', '首重价格', '续重价格'
];

const mockYAxisColLabel = [
    '快递类别', '快递名称',
];

const mockXAxisColLabel = [
    '省', '市', '区'
];

const mockTaskResult = [
    [
        {
            colName: '省',
            value: '黑龙江'
        },
        {
            colName: '市',
            value: '大庆',
        },
        {
            colName: '区',
            value: '林甸县',
        },
        {
            colName: '快递类别',
            value: '京东'
        },
        {
            colName: '快递名称',
            value: '京东物流',
        },
        {
            colName: '好评度',
            value: 4.0,
        },
        {
            colName: '首重价格',
            value: 12,
        },
        {
            colName: '续重价格',
            value: 8,
        }
    ],
    [
        {
            colName: '省',
            value: '吉林'
        },
        {
            colName: '市',
            value: '长春',
        },
        {
            colName: '区',
            value: '朝阳区',
        },
        {
            colName: '快递类别',
            value: '菜鸟驿站'
        },
        {
            colName: '快递名称',
            value: '韵达',
        },
        {
            colName: '好评度',
            value: 4.5,
        },
        {
            colName: '首重价格',
            value: 10,
        },
        {
            colName: '续重价格',
            value: 10,
        }
    ],
];

export { measureLabelMockData, xAxisMockData, yAxisMockData, mockYAxisColLabel, mockXAxisColLabel, mockTaskResult };

相关文章

网友评论

    本文标题:React.js 动态生成交叉表格

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