The front-end Vue combines with the xlxs library to parse excel files and dynamically assemble table headers!

Directory

    • 1 Introduction
    • 2.Data definition
    • 3. Page layout
    • 4. Upload previous events
    • 5. Parse the excel file and assemble the corresponding relationship between the system header and the excel header
    • 6. Drop-down box change event

1. Preface

Recently, there is a demand that users can import a custom excel file at will, so that users can choose and assemble the corresponding relationships of table headers. The purpose of this is to solve the limitations of the template and make the import more flexible.

2. Data definition

data() {<!-- -->
        return {<!-- -->
            file: null,
            excelHeaders: [],
            tableData: [],
            page: {<!-- -->},
            scanPage: {<!-- -->},
            list: [],
            total: 0,
            scanList: [],
            scanTotal: 0,
            excelLimit: 0,
            dataLoading: false,
            visible: false,
            importDescVisible: false,
            importVisible: false,
            uploadLoading: false,
            fileName: '',
            users: [],
            selectedHeaders: [],
            headerOptions: [],
            excelData: [],
            tempExcelData: [],
            headerMappings: [],
            systemHeaders: [
                 {<!-- -->
                    title: 'number',
                    isRequire: true
                },
                {<!-- -->
                    title: 'Transaction Date',
                    isRequire: true
                },
                {<!-- -->
                    title: 'Payer',
                    isRequire: true
                },
                {<!-- -->
                    title: 'Payer Account',
                    isRequire: true
                },
                // {<!-- -->
                // title: 'Payee',
                // isRequire: true
                // },
                // {<!-- -->
                // title: 'Payee account number',
                // isRequire: true
                // },
                {<!-- -->
                    title: 'Amount',
                    isRequire: true
                },
                {<!-- -->
                    title: 'Abstract',
                    isRequire: false
                },
                {<!-- -->
                    title: 'Remarks',
                    isRequire: false
                },
            ],
            initSystemHeaders: [
                {<!-- -->
                    title: 'number',
                    isRequire: true
                },
                {<!-- -->
                    title: 'Transaction Date',
                    isRequire: true
                },
                {<!-- -->
                    title: 'Payer',
                    isRequire: true
                },
                {<!-- -->
                    title: 'Payer Account',
                    isRequire: true
                },
                // {<!-- -->
                // title: 'Payee',
                // isRequire: true
                // },
                // {<!-- -->
                // title: 'Payee account number',
                // isRequire: true
                // },
                {<!-- -->
                    title: 'Amount',
                    isRequire: true
                },
                {<!-- -->
                    title: 'Abstract',
                    isRequire: false
                },
                {<!-- -->
                    title: 'Remarks',
                    isRequire: false
                },
            ],
            fileFormat: ['xlsx', 'xlc', 'xlm', 'xls', 'xlt', 'xlw', 'csv'],
            batchStatus: '',
            headerMap: new Map,
            scanLoading: false,
            batchNumber: '',
            tableSize: 0,
            showTableSize: 0,
        }
    }

3. Page layout

<div>
    <el-upload
            action=""
            accept=".xlsx"
            :limit="1"
            :before-upload="beforeUpload"
            :on-exceed="handleExceed"
            :on-success="handleUploadSuccess"
            :on-error="handleUploadError">
        <el-button type="primary" plain>Select file</el-button>
    </el-upload>
    <div slot="tip" class="el-upload__tip">Only xlsx/xls files can be uploaded, and no more than 5MB</div>
    <div v-if="this.fileName !==''" style="margin-top: 10px;font-size: 15px;">
        This import file name:
        <span style="margin-right: 15px;color: #dd1100">
            {<!-- -->{ this.fileName }}
        </span>
        Number of imported data items:
        <span style="margin-right: 15px;color: #dd1100">
            {<!-- -->{this.tableSize -1}}
        </span>
        Display number of data items:
        <span style="margin-right: 15px;color: #dd1100">
            {<!-- -->{this.tableData.length}}
        </span>
    </div>
    <div style="overflow-x: auto;">
        <table class="text-center" cellspacing="0" cellpadding="0" border="1"
                               style="margin-top: 10px;width: 3000px">
            <thead style="height: 35px;background-color: #f2dae2">
                <tr>
                    <th colspan="1" rowspan="1" style="width: 1500px"
                                    v-for="(header, index) in systemHeaders"
                                    :key="index"
                                    :style="header.isRequire ? { color: 'red' } : {}">
                                    {<!-- -->{ header.title }}
                    </th>
                </tr>
            </thead>
            <tbody v-if="tableData.length >0" class="text-center">
                <tr style="height: 35px">
                    <td v-for="(header, index) in excelHeaders" :key="index">
                        <select v-model="selectedHeaders[index]"
                                            @change="handleHeaderChange(index)"
                                            :disabled="systemHeaders[index].title === '-'"
                                            style="height: 35px;width: 100%">
                            <option v-for="option in headerOptions" :key="option" :value="option">
                                            {<!-- -->{ option }}
                                        </option>
                        </select>
                    </td>
                </tr>
                <tr v-for="(row, rowIndex) in tableData" :key="rowIndex" style="min-height: 35px">
                    <td v-for="(cell, cellIndex) in row"
                                    :key="cellIndex"
                                    :style="cellIndex >= systemHeaders.length ? { 'width': '1500px' }: '' ">
                                    {<!-- -->{ cell }}
                   </td>
                </tr>
            </tbody>
        </table>
    </div>
    <el-button type="warning" plain @click="handleRemove" :disabled="this.fileName === ''">
                        Remove files
                    </el-button>
    <el-button type="success" plain @click="confirmImport" style="margin-top: 15px"
                               :disabled="this.excelLimit === 0" :loading="this.uploadLoading">
                        Confirm import
    </el-button>
</div>

4. Upload previous events

 beforeUpload(file) {<!-- -->
            this.file = file;
            this.fileName = file.name
            let fileSize = file.size
            const FIVE_M = 5 * 1024 * 1024
            //Do not allow uploads larger than 5M
            if (fileSize > FIVE_M) {<!-- -->
                this.$message.error("Maximum upload 5MB")
                return false
            }
            const suffix = file.name.split('.').reverse()[0];
            if (!this.fileFormat.includes(suffix)) {<!-- -->
                this.$message.error('Only .xlsx or .xls files can be uploaded')
                return false
            }
            this.handleFileUploaded(file)
            this.excelLimit = 1
            return false; // Prevent default upload behavior
        }

5. Parse the excel file and assemble the corresponding relationship between the system header and the excel header

 handleFileUploaded(file) {<!-- -->
            console.log("==============>>Start parsing excel file<<==============")
            let reader = new FileReader()
            let _this = this;
            reader.onload = (e) => {<!-- -->
                const data = new Uint8Array(e.target.result)
                const workbook = XLSX.read(data, {<!-- -->
                    type: 'array',
                    cellDates: true
                })
                const worksheet = workbook.Sheets[workbook.SheetNames[0]]
                const jsonData = XLSX.utils.sheet_to_json(worksheet, {<!-- -->header: 1})
                _this.tableSize = jsonData.length
                const regex = /^[a-zA-Z]{3} [a-zA-Z]{3} \d{2} \d{4} \d{2}:\d{2} :\d{2} GMT\ + \d{4} \(China Standard Time\)$/;
                const tableData = jsonData.map(row => {<!-- -->
                    return row.map(cell => {<!-- -->
                        if (cell !== '' || cell !== null) {<!-- -->
                            if (regex.test(cell.toString())) {<!-- -->
                                let date = new Date(cell);
                                date.setDate(date.getDate() + 1);
                                const isoDateString = date.toISOString();
                                return isoDateString.slice(0, 10).replace('T', '-');
                            }
                        }
                        return cell;
                    });
                });
                // Get the header of Excel
                _this.excelHeaders = tableData[0]
                // Generate drop-down box options based on system header and Excel header
                for (let index = 0; index < _this.excelHeaders.length; index + + ) {<!-- -->
                    const excelHeader = _this.excelHeaders[index]
                    _this.headerOptions.push(excelHeader)
                }

                if (!_this.objectsAreEqual(_this.systemHeaders, _this.excelHeaders)) {<!-- -->
                    _this.headerOptions.unshift('missing')
                }

                //Initialize the selected header
                _this.initSelectHeader();
                // Get the data of the corresponding column (only the first 5 items are displayed here)
                _this.tableData = JSON.parse(JSON.stringify(tableData.slice(1, 6)))
                _this.tempExcelData = JSON.parse(JSON.stringify(tableData.slice(1, 6)))
                //Initialize table data
                _this.initExcelData()
                //Assemble header initial relationship
                _this.handHeaderMapping()
            }
            reader.readAsArrayBuffer(file)
        },
        initSelectHeader() {<!-- -->
            this.systemHeaders.forEach((systemHeader) => {<!-- -->
                let selectedHeader = 'Missing';
                let _excelHeader = '';
                for (let index = 0; index <this.excelHeaders.length; index + + ) {<!-- -->
                    const excelHeader = this.excelHeaders[index]
                    if (excelHeader === systemHeader.title) {<!-- -->
                        _excelHeader = excelHeader
                        break;
                    }
                }
                if (_excelHeader !== '') {<!-- -->
                    this.selectedHeaders.push(_excelHeader)
                } else {<!-- -->
                    this.selectedHeaders.push(selectedHeader)
                }
            });
            if (this.excelHeaders.length > this.systemHeaders.length) {<!-- -->
                this.selectedHeaders = this.selectedHeaders.concat(this.excelHeaders.slice(this.selectedHeaders.length));
                const moreLength = this.excelHeaders.length - this.systemHeaders.length
                for (let index = 0; index < moreLength; index + + ) {<!-- -->
                    this.systemHeaders.push({<!-- -->title: '-', isRequire: false})
                }
            }
        },
        initExcelData() {<!-- -->
            for (let index = 0; index <this.selectedHeaders.length; index + + ) {<!-- -->
                this.handleHeaderChange(index)
            }
        },
        objectsAreEqual(obj1, obj2) {<!-- -->
            return JSON.stringify(obj1) === JSON.stringify(obj2);
        },
        handHeaderMapping() {<!-- -->
            const headerMap = new Map();
            let filteredSystemHeaders = this.systemHeaders.filter(header => header.title !== '-')
            filteredSystemHeaders.forEach((item, index) => {<!-- -->
                let key = this.selectedHeaders[index]
                headerMap.set(item.title, key)
            })
            this.headerMap = headerMap;
        }

6. Drop-down box change event

handleHeaderChange(index) {<!-- -->
            const selectedHeader = this.selectedHeaders[index]
            if (selectedHeader === 'missing') {<!-- -->
                // If missing is selected, clear the data in the corresponding column
                this.tableData.forEach(row => {<!-- -->
                    row[index] = ''
                })
            } else {<!-- -->
                // If the Excel header is selected, the data of the corresponding column will be displayed under the system header.
                this.tableData.forEach((row, _index) => {<!-- -->
                    const rowIndex = this.excelHeaders.findIndex(item => item === selectedHeader);
                    if (rowIndex >= 0) {<!-- -->
                        row[index] = this.tempExcelData[_index][rowIndex]
                    }
                })
            }
            //Update header mapping
            if (this.systemHeaders[index] !== undefined & amp; & amp; this.systemHeaders[index] !== null) {<!-- -->
                let _systemHeader = this.systemHeaders[index].title
                this.headerMap.set(_systemHeader, selectedHeader)
            }
        }