目錄
簡介
開始實踐
難點
文件示例
效果預覽
具體實現
安裝
完整代碼
總結
簡介
在日常工作中,我們可能會遇到需要上傳并展示 Excel 文件的需求,實現文件內容的在線預覽。 這里給大家接收一個組件庫exceljs,這個組件庫進過實踐發現可以實現我們需要的功能。在這里我為了方便使用了(技術棧?Vue 3 + Element Ui 筆者自己使用的項目模板)來實現該功能。
開始實踐
NPM地址?內部有中文文檔
難點
基礎單元格和合并單元格和空白單元格的混合處理
文字樣式和背景樣式的讀取映射
文件示例
這里直接造了一個兩個sheet的excel做測試
效果預覽
具體實現
安裝
npm install exceljs
完整代碼
<template><div><el-uploadaction="":auto-upload="false":show-file-list="true":on-change="handleFileUpload"accept=".xlsx,.xls"><el-button type="primary"> 上傳 Excel </el-button></el-upload><!-- 渲染 Excel 生成的 HTML 表格 --><div v-html="tableHtml" /></div>
</template><script setup>
import { ref } from "vue";
import * as ExcelJS from "exceljs";const tableHtml = ref(""); // 存儲 HTML 表格內容
const themeColors = {0: "#FFFFFF", // 白色 √1: "#000000", // 黑色 √2: "#C9CDD1", // 灰色 √3: "#4874CB", // 藍色 √4: "#D9E1F4", // 淺藍 √5: "#F9CBAA", // 橙色 √6: "#F2BA02", // 淺橙 √7: "#00FF00", // 淺綠 √8: "#30C0B4", // 青色 √9: "#E54C5E", // 紅色 √10: "#FFC7CE", // 淺紅11: "#7030A0", // 紫色
};// 獲取單元格顏色
const getCellColor = (cell) => {if (cell.fill && cell.fill.fgColor) {if (cell.fill.fgColor.argb) {return `#${cell.fill.fgColor.argb.substring(2)}`; // ARGB 轉 RGB}if (cell.fill.fgColor.theme !== undefined) {return themeColors[cell.fill.fgColor.theme] || "#FFFFFF"; // 主題色轉換}}return ""; // 無顏色
};
// 獲取單元格字體顏色
const getCellFontColor = (cell) => {if (cell.font && cell.font.color && cell.font.color.argb) {return `#${cell.font.color.argb.substring(2)}`; // ARGB 轉 RGB}if (cell.font && cell.font.color && cell.font.color.theme) {return themeColors[cell.font.color.theme] || "#000"; // 主題色轉換}return "#000"; // 默認黑色
};
const handleStyles = (cell) => {let styles = [];// 讀取字體顏色styles.push(`color: ${getCellFontColor(cell)}`);// 讀取背景色styles.push(`background-color: ${getCellColor(cell)}`);// 加粗if (cell.font && cell.font.bold) {styles.push("font-weight: bold");}// 文字對齊if (cell.alignment) {if (cell.alignment.horizontal) {styles.push(`text-align: ${cell.alignment.horizontal}`);}if (cell.alignment.vertical) {styles.push(`vertical-align: ${cell.alignment.vertical}`);}}return styles.join("; ");
};// 獲取工作表維度信息
const getWorksheetDimensions = (worksheet) => {let maxRow = 0;let maxCol = 0;worksheet.eachRow((row, rowIndex) => {maxRow = Math.max(maxRow, rowIndex);row.eachCell((cell, colIndex) => {maxCol = Math.max(maxCol, colIndex);});});return { maxRow, maxCol };
};// 處理上傳的 Excel 文件
const handleFileUpload = async (file) => {const excelData = await readExcel(file.raw);tableHtml.value = excelData; // 更新 HTML 表格內容
};
// 處理常規單元格內容
const handleValueSimple = (value) => {if (!value) return " ";if (typeof value === "object" && value.richText) {const valueStr = value.richText.reduce((acc, curr) => {let colorValue = "";if (curr.font && curr.font.color && curr.font.color.theme) {colorValue = getCellFontColor(curr) || `#000`;}if (curr.font && curr.font.color && curr.font.color.argb) {colorValue = `#${curr.font.color.argb.substring(2)}`;} else {colorValue = `#000`;}return acc + `<span style="color:${colorValue}">${curr.text}</span>`;}, "");return valueStr;}return value.toString();
};
// 處理合并單元格內容
const handleValue = (value) => {if (!value) return " ";if (typeof value === "object" && value.richText) {const valueArr = value.richText.reduce((acc, curr) => {let colorValue = "";if (curr.font && curr.font.color && curr.font.color.argb) {colorValue = `#${curr.font.color.argb.substring(2)}`;} else {colorValue = `#000`;}const newData = curr.text.split(/\r/).map((item) => `<p style="color:${colorValue}">${item}</p>`);return acc.concat(newData);}, []);return valueArr.join("").replace(/\n/g, "<br />");}return value.toString();
};let worksheetIds = [];
// 讀取 Excel 并轉換成 HTML
const readExcel = async (file) => {const workbook = new ExcelJS.Workbook();const arrayBuffer = await file.arrayBuffer();const { worksheets } = await workbook.xlsx.load(arrayBuffer);worksheetIds = worksheets.map((v) => v.id); // 獲取工作表 ID集合let allHtml = "";workbook.eachSheet(function (worksheet, sheetId) {// 處理合并單元格const merges = worksheet?.model?.merges || [];const currentSheetIndex = worksheetIds.indexOf(sheetId); // 獲取當前工作表的索引// 獲取工作表維度const { maxRow, maxCol } = getWorksheetDimensions(worksheet);allHtml +='<table border="1" style="border-collapse: collapse;width:100%;margin-bottom: 20px;">';// 使用雙重循環確保每個單元格位置都被處理for (let rowIndex = 1; rowIndex <= maxRow; rowIndex++) {allHtml += "<tr>";for (let colIndex = 1; colIndex <= maxCol; colIndex++) {const cell = worksheet.getCell(rowIndex, colIndex);let cellValue = cell.value || "";// 檢查當前單元格是否在合并范圍內let isInMerge = false;let isMergeStart = false;let rowspan = 1;let colspan = 1;for (let merge of merges) {const [start, end] = merge.split(":");const startCell = worksheet.getCell(start);const endCell = worksheet.getCell(end);const startRow = startCell.row;const startCol = startCell.col;const endRow = endCell.row;const endCol = endCell.col;if (rowIndex >= startRow && rowIndex <= endRow && colIndex >= startCol && colIndex <= endCol) {isInMerge = true;if (rowIndex === startRow && colIndex === startCol) {isMergeStart = true;rowspan = endRow - startRow + 1;colspan = endCol - startCol + 1;}break;}}// 如果是合并單元格的起始位置,創建合并單元格if (isMergeStart) {let styles = handleStyles(cell);const mergeValue = cellValue || " ";allHtml += `<td rowspan="${rowspan}" colspan="${colspan}" style="${styles}">${handleValue(mergeValue)}</td>`;}// 如果不在合并范圍內,創建普通單元格else if (!isInMerge) {let styles = handleStyles(cell);const displayValue = cellValue ? handleValueSimple(cellValue) : " ";allHtml += `<td style="${styles}">${displayValue}</td>`;}// 如果單元格在合并范圍內但不是起始位置,跳過(由合并單元格處理)}allHtml += "</tr>";}allHtml += "</table>";});return allHtml;
};
</script>
總結
exceljs功能很多,這里給大家介紹了execljs的一種用法,實現導入轉換html頁面顯示,便于瀏覽。大家感興趣可以去翻翻文檔??NPM地址?內部有中文文檔,exceljs功能很強大推薦大家自己嘗試一下。