React.js 动态生成交叉表格

交叉表.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(
            {measure}
        )
    });
    // 根据放在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(
        {yCol}
    ));
    measureRow.unshift(yColLabelTds);
    result.push(
        
            {measureRow}
        
    );

    // row.push();

    // 整理表头中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(
                {data.name}
            );

        }
        result.unshift(
            
                {row}
            
        );
    }

    // 递归查找需要添加的父级数据
    const addParentData = (self: any, level: number, selfIndex: number, row: JSX.Element[]) => {
        row.unshift({self.name});
        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({value}));
        // @ts-ignore
        rowElements.push(measureThList);
        body.unshift(
            
                {rowElements}
            
        );
    }
    // @ts-ignore
    return result.concat(body);
};

const SummaryTable = (props: any) => {

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

    return (
        
            {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 动态生成交叉表格)