Как динамически добавить второй ряд заголовков на мой компонент таблицы - PullRequest
0 голосов
/ 29 апреля 2019

Я пытаюсь создать стандартизированную систему таблиц для нескольких представлений в моем приложении. Дизайнер, не обращая внимания на процесс, решил, что для некоторых из них на столе должны быть два заголовка. Проблема тогда становится:

Каков наилучший способ добавить второй заголовок в таблицу, размер которого будет соответствовать размеру столбцов?

Таблица содержит ряд строк и столбцов. Но в таблице есть возможность скрывать и показывать столбцы. Мне нужно, чтобы новый заголовок изменял размеры всякий раз, когда я прячу другой столбец, и скрывал себя, если все столбцы под ним скрыты.

Вот содержимое компонента:

<template>
    <!-- Basic table. Adapt it's content and size based on the props received -->
    <div class="table-content">
        <a>
            <!-- export table for Excel -->
            <JsonExcel :data="sortedContent" style="width: 50px;">
                <i class="fa fa-file-excel"></i>
            </JsonExcel>
        </a>

        <!-- bouton pour afficher le panneau des settings -->
        <button @click="togglePanel()">settings</button>

        <table class="c-base-table" :class="tableName">
            <!-- Opening table -->
            <thead>
                <!-- Header table -->
                <tr>
                    <!-- Show columns titles -->
                    <th
                        v-for="key in columns"
                        @click="sortBy(key)"
                        :class="[
                            columnsShown.includes(key) ? '' : 'hidden-col',
                            key,
                            {active: sortKey == key}
                        ]"
                        :key="key"
                    >
                        {{ columnsNames[key] | capitalize }}
                        <span
                            class="arrow"
                            :class="sortOrders[key] > 0 ? 'asc' : 'dsc'"
                        ></span>
                    </th>
                </tr>
            </thead>
            <tbody>
                <!-- table content -->
                <tr v-for="entry in sortedContent" :key="entry.id">
                    <!-- content sorted depending on the chosen column -->
                    <td
                        v-for="key in columns"
                        :class="[columnsShown.includes(key) ? '' : 'hidden-col', key]"
                        :key="entry.id + key"
                    >
                        {{ entry[key] | limitDecimal }}
                    </td>
                </tr>
                <tr v-if="hasTotal">
                    <!-- if an array called "columnToTotal" is given, it will calculate the total of the chosen columns -->
                    <td
                        v-for="key in columns"
                        :class="[columnsShown.includes(key) ? '' : 'hidden-col', key]"
                        :key="key"
                    >
                        <div v-if="columnsToTotal.includes(key)">
                            {{ sumColumn[key] }}
                        </div>
                    </td>
                </tr>
            </tbody>
        </table>
        <!-- If there are more than [rowPerPage] row in the table, start the pagination -->
        <div v-if="listLength > rowPerPage">
            <Pagination
                @changePage="changePage"
                :totalNumPages="totalNumPages"
                :numPage="numPage"
            ></Pagination>
        </div>
        <!-- settings -->
        <div class="panel-config" v-show="panelIsShown">
            <ul>
                <!-- list of columns with a checkbox to show/hide that column -->
                <li v-for="key in columns" :class="key" :key="key">
                    <input
                        type="checkbox"
                        :id="key"
                        :value="key"
                        v-model="columnsShown"
                    />
                    {{ columnsNames[key] | capitalize }}
                </li>
            </ul>
            <!-- If asked, it will show a set of datepickers to use update the infos -->
            <div v-if="toggleDate" id="dateSettings">
                <Datepicker v-model="beginDate"></Datepicker>
                <Datepicker v-model="endDate"></Datepicker>
                <button @click="updateContent">update</button>
            </div>
            <hr />
            <slot></slot>
        </div>
    </div>
</template>

<script>
// Imports
import {isNumber} from 'util';
import Datepicker from 'gso-datepicker';
import JsonExcel from 'vue-json-excel';
import Pagination from './pagination.vue';

export default {
    name: 'baseTable',
    components: {
        JsonExcel,
        Pagination,
        Datepicker
    },
    filters: {
        // capitalize the 1st letter
        capitalize: function(str) {
            return str.charAt(0).toUpperCase() + str.slice(1);
        },
        // limit value to 2 decimal IF it is a decimal number
        limitDecimal: function(value) {
            if (isNumber(value) && value !== Math.floor(value)) {
                return parseFloat(value).toFixed(2);
            }

            return value;
        }
    },
    // parameters received by the component
    props: [
        'columns',
        'tableContent',
        'tableName',
        'columnsToTotal',
        'columnsNames',
        'columnsShown',
        'toggleDate'
    ],
    data: function() {
        var sortOrders = {};
        this.columns.forEach(function(key) {
            sortOrders[key] = 1;
        });
        return {
            // initialization
            sortKey: '',
            sortOrders: sortOrders,
            panelIsShown: false,
            beginDate: new Date(),
            endDate: new Date(),
            numPage: 1, // current page for pagination
            rowPerPage: 20 // max number of row per page
        };
    },
    computed: {
        // Sorted content
        sortedContent: function() {
            var sortKey = this.sortKey;
            var order = this.sortOrders[sortKey] || 1;
            var tableContent = this.tableContent;
            if (sortKey) {
                tableContent = tableContent.slice().sort(function(a, b) {
                    a = a[sortKey];
                    b = b[sortKey];
                    return (a === b ? 0 : a > b ? 1 : -1) * order;
                });
            }

            tableContent = tableContent.slice(
                (this.numPage - 1) * this.rowPerPage,
                this.numPage * this.rowPerPage
            );
            return tableContent;
        },
        // calculate totals of selected columns
        sumColumn: function() {
            const sumTot = [];
            const sortedContent = this.sortedContent;

            this.columnsToTotal.forEach(function(column) {
                sumTot[column] = sortedContent.reduce(function(sum, item) {
                    return sum + item[column];
                }, 0);
            });

            return sumTot;
        },
        // check if making total is necessary
        hasTotal: function() {
            return this.columnsToTotal !== undefined;
        },
        // check if columns have a name
        hasNames: function() {
            return this.columnsNames !== undefined;
        },
        // calculate the number of rows
        listLength: function() {
            return this.tableContent.length;
        },
        // calculate the number of page required for these rows
        totalNumPages: function() {
            return Math.ceil(this.listLength / this.rowPerPage);
        }
    },
    watch: {
        columns: function() {
            var sortOrders = {};
            this.columns.forEach(function(key) {
                sortOrders[key] = 1;
            });
            this.sortOrders = sortOrders;
        }
    },

    methods: {
        // Sorting of the table
        sortBy: function(key) {
            this.sortKey = key;
            if (this.sortOrders[key] === undefined) {
                this.sortOrders[key] = 1;
            }

            this.sortOrders[key] = this.sortOrders[key] * -1;
        },
        // hide/show settings
        togglePanel: function() {
            this.panelIsShown = !this.panelIsShown;
        },
        // send beginDate and endDate to parent to update the content
        updateContent: function() {
            var beginDate = this.beginDate.toISOString().split('T')[0];
            var endDate = this.endDate.toISOString().split('T')[0];
            this.$emit('updateContent', beginDate, endDate);
        },
        // change page number
        changePage: function(numPage) {
            this.numPage = numPage;
        }
    }
};
</script>

Например, если я отправлю следующий контент для заголовка:

columns: [
            'agentName',
            'employersAmount',
            'contractsAmount',
            'contractsAmountPrestaWeb',
            'contractsAmountPresta',
            'contractsAmountPrestaLight',
            'contractsAmountPaper',
            'employersAmountPrestaWeb',
            'employersAmountPresta',
            'employersAmountPrestaLight',
            'employersAmountPaper',
            'contractsPercentPrestaWeb',
            'contractsPercentPresta',
            'contractsPercentPrestaLight',
            'contractsPercentPaper',
            'employerPercentPrestaWeb',
            'employerPercentPresta',
            'employerPercentPrestaLight',
            'employerPercentPapier',
            'officeName'
        ]

columnsNames: {
            agentName: 'Gestionnaire',
            employersAmount: '# Dossiers',
            contractsAmount: '# Contrats',
            contractsAmountPrestaWeb: '# Contrats PW',
            contractsAmountPresta: '# Contrats PW',
            contractsAmountPrestaLight: '# Contrats PL',
            contractsAmountPaper: '# Contrats pa',
            employersAmountPrestaWeb: '# Dossiers PW',
            employersAmountPresta: '# Dossiers Pr',
            employersAmountPrestaLight: '# Dossiers PL',
            employersAmountPaper: '# Dossiers pa',
            contractsPercentPrestaWeb: '% Contrats PW',
            contractsPercentPresta: '% Contrats Pr',
            contractsPercentPrestaLight: '% Contrats PL',
            contractsPercentPaper: '% Contrats pa',
            employerPercentPrestaWeb: '% Dossiers PW',
            employerPercentPresta: '% Dossiers Pr',
            employerPercentPrestaLight: '% Dossiers PL',
            employerPercentPapier: '% Dossiers pa',
            officeName: 'Bureau'
        }

Я получаю следующий результат:

<table>
  <tr>

    <th>Gestionnaire</th>

    <th># Dossiers</th>

    <th># Contrats</th>

    <th># Contrats PW</th>
    <th># Contrats PL</th>
    <th># Contrats Pr</th>
    <th># Contrats Pa</th>

    <th># Dossiers PW</th>
    <th># Dossiers PL</th>
    <th># Dossiers Pr</th>
    <th># Dossiers Pa</th>

    <th>% Contrats PW</th>
    <th>% Contrats PL</th>
    <th>% Contrats Pr</th>
    <th>% Contrats Pa</th>

    <th>% Dossiers PW</th>
    <th>% Dossiers PL</th>
    <th>% Dossiers Pr</th>
    <th>% Dossiers Pa</th>

    <th>Bureau</th>
  </tr>

</table>

Хотя я бы хотел что-то вроде этого:

<table>

  <tr>

    <th rowspan="2">Gestionnaire</th>

    <th colspan="9">Dossiers</th>

    <th colspan="9">Contrats</th>

    <th rowspan="2">Bureau</th>

  </tr>
  <tr>

    <th>total</th>

    <th># PW</th>
    <th># PL</th>
    <th># Pr</th>
    <th># Pa</th>

    <th>% PW</th>
    <th>% PL</th>
    <th>% Pr</th>
    <th>% Pa</th>

    <th>total</th>

    <th># PW</th>
    <th># PL</th>
    <th># Pr</th>
    <th># Pa</th>

    <th>% PW</th>
    <th>% PL</th>
    <th>% Pr</th>
    <th>% Pa</th>

  </tr>

</table>

Зная, что я могу (через массив columnsShown) скрыть некоторые столбцы, и что мне потребуется обновление colspan для соответствия количеству соответствующих столбцов.

Что было бы лучшим способом изменить мой код для этого? Мне все еще нужно, чтобы таблица выглядела как первая, но, возможно, изменив некоторые реквизиты или фактически отправив несколько реквизитов, измените поведение, чтобы иметь двойной заголовок. Я думал о добавлении новой опоры, как

columnsContainer: [
    Files:{
        'employersAmount',
        'employersAmountPrestaWeb',
        'employersAmountPresta',
        'employersAmountPrestaLight',
        'employersAmountPaper','employerPercentPrestaWeb',
        'employerPercentPresta',
        'employerPercentPrestaLight',
        'employerPercentPapier'
    },
    Contracts:{
        'contractsAmount',
        'contractsAmountPrestaWeb',
        'contractsAmountPresta',
        'contractsAmountPrestaLight',
        'contractsAmountPaper',
        'contractsPercentPrestaWeb',
        'contractsPercentPresta',
        'contractsPercentPrestaLight',
        'contractsPercentPaper',
    }
]

и, если он пустой, создайте простой заголовок, а если он что-то заполнен, посмотрите на ключ и организуйте заголовок на основе этого ключа.

...