ExcelJS实现导入转换HTML展示(附源码可直接使用)
目录
简介
开始实践
难点
文件示例
效果预览
具体实现
安装
完整代码
总结
简介
在日常工作中,我们可能会遇到需要上传并展示 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功能很强大推荐大家自己尝试一下。