需求:
- 完成excel表格內容轉json,excel表格內可能存在多個表格,要求全部解析出來。
- 完成表格內合服功能,即:提取表格內老服務器與新服務器數據,多臺老服務器對應合并到一臺新服務器上
3.最終輸出結果為:[{‘10086-l’:[‘10086-1’,‘10086-2’,…]}] …
一、前置準備
安裝依賴
首先確保安裝 xlsx 庫:
npm install xlsx
核心思路
嵌套表格通常通過空行、固定標題或特定標識分隔,且子表格結構一致(如列數、表頭相同)。需通過遍歷行數據,識別子表格的邊界(起始行、結束行),再提取每個子表格的內容并轉換為 JSON。
二、實現步驟及代碼
假設 Excel 結構如下(示例):
包含 3 個子表格,以空行分隔,每個子表格有表頭(name、id、老服務器,新服務器)和數據行。
1. 讀取 Excel 文件并解析工作表
return new Promise((resolve, reject) => {const reader = new FileReader();// 開始讀取文件內容reader.readAsArrayBuffer(raw);reader.onload = (e) => {// 將文件內容讀取為ArrayBuffer并解析為workbook對象const data = e.target?.result;// 第一行開始 第五行結束const workbook = XLSX.read(data, { type: "array" });// 獲取第一個工作表數據并轉換為JSON格式const sheetName = workbook.SheetNames[0];const worksheet = workbook.Sheets[sheetName];const rows = XLSX.utils.sheet_to_json(worksheet, {header: 1,raw: false, // 保留原始值(字符串)});};// 處理文件讀取錯誤reader.onerror = (error) => {reject(error);};});
2. 識別子表格邊界
通過空行(元素全為 undefined 或空字符串)分隔子表格,記錄每個子表格的起始行和結束行:
// 存儲所有子表格的范圍:{ start: 行索引, end: 行索引 }
const tableRanges = [];
let currentStart = null;// 遍歷所有行,識別子表格邊界
for (let i = 0; i < rows.length; i++) {const row = rows[i];// 判斷是否為空行(過濾全空或僅含空字符串的行)const isEmptyRow = row.every(cell => cell === undefined || cell === null || cell === '');if (!isEmptyRow) {// 非空行:若未記錄起始行,則標記為子表格開始if (currentStart === null) {currentStart = i;}} else {// 空行:若已記錄起始行,則標記為子表格結束if (currentStart !== null) {tableRanges.push({ start: currentStart, end: i - 1 });currentStart = null;}}
}// 處理最后一個子表格(若文件末尾無空行)
if (currentStart !== null) {tableRanges.push({ start: currentStart, end: rows.length - 1 });
}
3. 提取子表格數據并轉換為 JSON
假設每個子表格的第一行為表頭,后續為數據行:
// 存儲最終結果:鍵為子表格索引,值為數據數組// 存儲最終結果:鍵為子表格索引,值為數據數組const result = [];tableRanges.forEach((range, tableIndex) => {const { start, end } = range;// 表頭行(子表格起始行)const headers = rows[start].filter(cell => cell !== undefined && cell !== ''); // 過濾空單元格// 數據行(從表頭下一行到結束行)const tableData = [];for (let i = start + 1; i <= end; i++) {const row = rows[i];const rowData = {};// 映射表頭與數據(忽略超出表頭列數的單元格)headers.forEach((header, colIndex) => {rowData[header] = row[colIndex] || ''; // 空單元格用空字符串代替});tableData.push(rowData);}tableData.forEach(rowData => {if (rowData.newServer) {}});let data = transformServers(tableData)if (Object.keys(data).length) {result.push(data)}});
三、關鍵處理說明
1. 邊界識別優化
若子表格通過特定標題(如 “表格 1 開始”)分隔,可修改空行判斷邏輯,例如:
// 假設子表格以 "表格x" 開頭
const isTableHeader = row[0]?.startsWith('表格');
if (isTableHeader) { /* 標記子表格開始 */ }
- 合并單元格處理
若存在合并單元格,xlsx 會僅在合并區域的首個單元格保留值,其他為 undefined。可通過以下方式填充合并單元格數據:
// 先獲取合并單元格信息
const merges = worksheet['!merges'] || [];
// 遍歷合并區域,復制首個單元格的值到其他位置
merges.forEach(merge => {const { s, e } = merge; // s: 起始行/列, e: 結束行/列const value = rows[s.r][s.c];for (let r = s.r; r <= e.r; r++) {for (let c = s.c; c <= e.c; c++) {rows[r][c] = value;}}
});
數據類型轉換
默認輸出為字符串,可根據需求自行格式化
function transformServers(servers) {const result = {};let currentNewServer = null;let keys = Object.keys(servers[0]);for (const serverInfo of servers) {// 如果當前項有newServer值,更新當前newServerif (serverInfo[keys[3]]) {currentNewServer = serverInfo[keys[3]];// 初始化數組并添加當前serverresult[currentNewServer] = [serverInfo[keys[2]]];}// 如果當前有活躍的newServer且當前項沒有newServer,添加到數組else if (currentNewServer) {result[currentNewServer].push(serverInfo[keys[2]]);}}return result;
}
四、頁面代碼部分
<template>
<el-dialogtitle="提示":visible.sync="dialogVisible"width="500":before-close="handleClose"><el-uploadclass="upload-demo"dragaction="''":multiple="false":auto-upload="false":file-list="fileList":on-change="handleChange":on-remove="beforeUpload"ref="upload":limit="1"accept=".xlsx,.xls"><i class="el-icon-upload"></i><div class="el-upload__text"><div>將文件拖到此處,或<em>點擊上傳</em></div><div class="el-upload__tip">上傳excel文件,請勿超過5M,每次最多只能上傳1個文件</div></div></el-upload><span slot="footer" class="dialog-footer"><el-button type="primary" @click="toJson">tojson</el-button><el-button @click="dialogVisible = false">取 消</el-button><el-button type="primary" @click="dialogVisible = false">確 定</el-button></span>
</el-dialog>
</template>
<script>import {excelToJson} from '../../../utils/excel2json'export default {data() {return {dialogVisible: false,fileList: [],};},methods: { handleChange(file, fileList){console.log(fileList,file,'---');this.fileList = fileList; },beforeUpload(file,fileList){console.log(fileList,'--');this.fileList = fileList; },handleClose(done) {this.$confirm('確認關閉?').then(_ => {done();}).catch(_ => {});},toJson(){console.log(this.fileList);excelToJson(this.fileList[0].raw)}},};
</script>
<style lang="scss">
.upload-demo{
.el-upload {width:100%;
}
.el-upload-dragger{width:100%;
}width:100%;
}
</style>
五、總結
通過上述步驟,可實現對嵌套規律表格的解析:
讀取 Excel 并轉換為行數組;
根據空行或特定標識識別子表格范圍;
提取每個子表格的表頭和數據,映射為 JSON 對象;
按需處理合并單元格和數據類型。
根據實際 Excel 結構,只需調整邊界識別邏輯和表頭映射規則即可適配不同場景。