基于Handsontable.js + Excel.js实现表格预览和导出功能(公式渲染)

本文记录在html中基于Handsontable.js + Excel.js实现表格预览、导出、带公式单元格渲染功能,在这里我们在html中实现,当然也可以在vue、react等框架中使用npm下载导入依赖文件。

Handsontable官方文档

一、开发前的准备引入相关依赖库
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>基于Handsontable.js + Excel.js实现表格预览功能</title><!-- handsontable的css文件 https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.css --><link rel="stylesheet" href="./lib/handsontable.full.min.css">
</head>
<body><!-- https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.js --><script src="./lib/handsontable.full.min.js"></script><!-- https://cdn.jsdelivr.net/npm/handsontable/dist/languages/zh-CN.js --><script src="./lib/zh-CN.js"></script><!-- https://cdn.jsdelivr.net/npm/color-js --><script src="./lib/color-js.js"></script><!-- https://cdnjs.cloudflare.com/ajax/libs/hyperformula/1.4.0/hyperformula.min.js --><script src="./lib/hyperformula.full.min.js"></script><!-- https://cdn.jsdelivr.net/npm/exceljs/dist/exceljs.min.js --><script src="./lib/exceljs.min.js"></script><!-- https://cdn.jsdelivr.net/npm/xlsx/dist/xlsx.full.min.js --><script src="./lib/xlsx.full.min.js"></script><!-- https://cdn.jsdelivr.net/npm/fxparser@1.0.0/dist/fxparser.min.js --><script src="./lib/fxparser.min.js"></script>
</body>
</html>
二、编写页面布局
<body><input type="file" id="file"><button id="btn">预览</button><button id="export">导出</button>
</body>
三、编写预览核心代码
<script>var hot; //handsontable实例var themeJson; //主题jsonvar sheet; //当前sheetvar Color = net.brehaut.Color; //引入color-js库// 自定义渲染器函数function customRenderer(hotInstance, td, row, column, prop, value, cellProperties) {Handsontable.renderers.TextRenderer(hotInstance, td, row, column, prop, value, cellProperties);// 填充样式if ("fill" in cellProperties) {// 背景颜色if ("fgColor" in cellProperties.fill && cellProperties.fill.fgColor) {td.style.background = getColor(cellProperties.fill.fgColor,  themeJson);}}// 字体样式if ("font" in cellProperties) {// 加粗if ("bold" in cellProperties.font && cellProperties.font.bold) {td.style.fontWeight = "700";}// 字体颜色if ("color" in cellProperties.font && cellProperties.font.color) {td.style.color = getColor(cellProperties.font.color, themeJson);}// 字体大小if ("size" in cellProperties.font && cellProperties.font.size) {td.style.fontSize = cellProperties.font.size + "px";}// 字体类型if ("name" in cellProperties.font && cellProperties.font.name) {td.style.fontFamily = cellProperties.font.name;}// 字体倾斜if ("italic" in cellProperties.font && cellProperties.font.italic) {td.style.fontStyle = "italic";}// 下划线if ("underline" in cellProperties.font &&cellProperties.font.underline) {// 其实还有双下划线,但是双下划綫css中没有提供直接的设置方式,需要使用额外的css设置,所以我也就先懒得弄了td.style.textDecoration = "underline";// 删除线if ("strike" in cellProperties.font && cellProperties.font.strike) {td.style.textDecoration = "underline line-through";}} else {// 删除线if ("strike" in cellProperties.font && cellProperties.font.strike) {td.style.textDecoration = "line-through";}}}// 对齐if ("alignment" in cellProperties) {if ("horizontal" in cellProperties.alignment) {// 水平// 这里我直接用handsontable内置类做了,设置成类似htLeft的样子。//(handsontable)其实至支持htLeft, htCenter, htRight, htJustify四种,但是其是它还有centerContinuous、distributed、fill,遇到这几种就会没有效果,也可以自己设置,但是我还是懒的弄了,用到的时候再说吧const name =cellProperties.alignment.horizontal.charAt(0).toUpperCase() +cellProperties.alignment.horizontal.slice(1);td.classList.add(`ht${name}`);}if ("vertical" in cellProperties.alignment) {// 垂直// 这里我直接用handsontable内置类做了,设置成类似htTop的样子。const name =cellProperties.alignment.vertical.charAt(0).toUpperCase() +cellProperties.alignment.vertical.slice(1);td.classList.add(`ht${name}`);}}// 边框if ("border" in cellProperties) {if ("left" in cellProperties.border && cellProperties.border.left) {// 左边框const [borderWidth, borderStyle] = setBorder(cellProperties.border.left.style);let color = "";// console.log(row, column, borderWidth, borderStyle);if (cellProperties.border.left.color) {color = getColor(cellProperties.border.left.color, themeJson);}td.style.borderLeft = `${borderStyle} ${borderWidth} ${color}`;}if ("right" in cellProperties.border && cellProperties.border.right) {// 左边框const [borderWidth, borderStyle] = setBorder(cellProperties.border.right.style);// console.log(row, column, borderWidth, borderStyle);let color = "";if (cellProperties.border.right.color) {color = getColor(cellProperties.border.right.color, themeJson);}td.style.borderRight = `${borderStyle} ${borderWidth} ${color}`;}if ("top" in cellProperties.border && cellProperties.border.top) {// 左边框const [borderWidth, borderStyle] = setBorder(cellProperties.border.top.style);let color = "";// console.log(row, column, borderWidth, borderStyle);if (cellProperties.border.top.color) {color = getColor(cellProperties.border.top.color, themeJson);}td.style.borderTop = `${borderStyle} ${borderWidth} ${color}`;}if ("bottom" in cellProperties.border && cellProperties.border.bottom) {// 左边框const [borderWidth, borderStyle] = setBorder(cellProperties.border.bottom.style);let color = "";// console.log(row, column, borderWidth, borderStyle);if (cellProperties.border.bottom.color) {color = getColor(cellProperties.border.bottom.color, themeJson);}td.style.borderBottom = `${borderStyle} ${borderWidth} ${color}`;}}}// 在 Handsontable 初始化之前注册渲染器Handsontable.renderers.registerRenderer('customStylesRenderer', customRenderer);// 点击预览按钮document.getElementById('btn').addEventListener('click', async function () {var hotData = null; // Handsontable 数据var file = document.getElementById('file').files[0]; // 获取文件对象const workbook = new ExcelJS.Workbook(); // 创建一个工作簿对象await workbook.xlsx.load(file); // 加载Excel文件const worksheet = workbook.getWorksheet(1); // 获取第一个工作表sheet = worksheet; // 将工作表赋值给全局变量// 遍历工作表中的所有行(包括空行)const sheetData = [];worksheet.eachRow({ includeEmpty: true }, function (row, rowNumber) {const row_values = row.values.slice(1); // 获取行数据,并排除第一列为null的数据const newRowValue = [...row_values];// 将行数据添加到sheetData数组中sheetData.push(newRowValue);});// 将数据赋值给HandsontablehotData = sheetData; // 将主题xml转换成jsonconst themeXml = workbook._themes.theme1;const options = {ignoreAttributes: false,attributeNamePrefix: "_",};const parser = new XMLParser(options);const json = parser.parse(themeXml);themeJson = json// 获取合并的单元格const mergeCells = [];for (let i in worksheet._merges) {const { top, left, bottom, right } = worksheet._merges[i].model;mergeCells.push({row: top - 1,col: left - 1,rowspan: bottom - top + 1,colspan: right - left + 1});};// 将数据加载到HyperFormulahot = new Handsontable(document.getElementById('hot'), {// 数据data: hotData,colHeaders: true,rowHeaders: true,language: 'zh-CN',readOnly: true,width: '100%',height: 'calc(100% - 25px)',//handsontable的许可证licenseKey: 'non-commercial-and-evaluation',// 行高rowHeights: function (index) {if (sheet.getRow(index + 1).height) {// exceljs获取的行高不是像素值,事实上,它是23px - 13.8 的一个映射。所以需要将它转化为像素值return sheet.getRow(index + 1).height * (23 / 13.8);}return 23; // 默认},// 列宽colWidths: function (index) {if (sheet.getColumn(index + 1).width) {// exceljs获取的列宽不是像素值,事实上,它是81px - 8.22 的一个映射。所以需要将它转化为像素值return sheet.getColumn(index + 1).width * (81 / 8.22);}return 81; // 默认},// 自定义单元格样式cells: function (row, col, prop) {const cellProperties = {};const cellStyle = sheet.getCell(row + 1, col + 1).style;if (JSON.stringify(cellStyle) !== "{}") {// console.log(row+1, col+1, cellStyle);for (let key in cellStyle) {cellProperties[key] = cellStyle[key];}}return { ...cellProperties, renderer: "customStylesRenderer" };},// 合并单元格mergeCells: mergeCells});//8.将handsontable实例渲染到页面上hot.render();});// 根据主题和明暗度themeId获取颜色function getThemeColor(themeJson, themeId, tint) {let color = "";const themeColorScheme =themeJson["a:theme"]["a:themeElements"]["a:clrScheme"];switch (themeId) {case 0:color = themeColorScheme["a:lt1"]["a:sysClr"]["_lastClr"];break;case 1:color = themeColorScheme["a:dk1"]["a:sysClr"]["_lastClr"];break;case 2:color = themeColorScheme["a:lt2"]["a:srgbClr"]["_val"];break;case 3:color = themeColorScheme["a:dk2"]["a:srgbClr"]["_val"];break;default:color = themeColorScheme[`a:accent${themeId - 3}`]["a:srgbClr"]["_val"];break;}// 根据tint修改颜色深浅color = "#" + color;const colorObj = Color(color);if (tint) {if (tint > 0) {// 淡色color = colorObj.lighten(tint).hex();} else {// 深色color = colorObj.darken(Math.abs(tint)).hex();}}return color;}// 获取颜色function getColor(obj, themeJson) {if ("argb" in obj) {// 标准色// rgba格式去掉前两位: FFFF0000 -> FF0000return "#" + obj.argb.substring(2);} else if ("theme" in obj) {// 主题颜色if ("tint" in obj) {return getThemeColor(themeJson, obj.theme, obj.tint);} else {return getThemeColor(themeJson, obj.theme, null);}}}// 设置边框function setBorder(style) {let borderStyle = "solid";let borderWidth = "1px";switch (style) {case "thin":borderWidth = "thin";break;case "dotted":borderStyle = "dotted";break;case "dashDot":borderStyle = "dashed";break;case "hair":borderStyle = "solid";break;case "dashDotDot":borderStyle = "dashed";break;case "slantDashDot":borderStyle = "dashed";break;case "medium":borderWidth = "2px";break;case "mediumDashed":borderStyle = "dashed";borderWidth = "2px";break;case "mediumDashDotDot":borderStyle = "dashed";borderWidth = "2px";break;case "mdeiumDashDot":borderStyle = "dashed";borderWidth = "2px";break;case "double":borderStyle = "double";break;case "thick":borderWidth = "3px";break;default:break;}// console.log(borderStyle, borderWidth);return [borderStyle, borderWidth];}
</script>

如下图所示:

在这里插入图片描述

通过以上代码,我们成功将Excel文件中的数据、样式、合并单元格等信息加载到了Handsontable中,并渲染到了页面上。

对于单元格中带有公式的,按照以上代码会出现问题,就是带有公式的单元格会渲染成"[object Object]",如下图所示:

请添加图片描述

所以我们需要对公式进行处理,将公式替换为对应的值。请看以下处理公式的方法。

在遍历单元格时我们可以打印看下读取到单元格的数据具体是什么样的,然后根据具体情况进行处理,如下图所示:

在这里插入图片描述
由图可以看到带有公式的单元格是一个对象,这就是带有公式的单元格渲染后是"[object Object]"的原因。解决方法就是将带有公式的单元格替换为对应的值,请看以下代码:

worksheet.eachRow({ includeEmpty: true }, function (row, rowNumber) {const row_values = row.values.slice(1); // 获取行数据,并排除第一列为null的数据const newRowValue = [...row_values];newRowValue.forEach(function(item, index) {if(Object.prototype.toString.call(item) === "[object Object]") {if(item.formula) {// 如果是公式,则保留公式,否则将结果作为值newRowValue[index] = item.formula.includes("=") ? item.formula : "=" + item.result;}};})sheetData.push(newRowValue);
});
通过处理后,带有公示的单元格不在渲染为"[object Object]“,但是会渲染出具体使用了什么公式,如:”=SUM(B1:B2)“会渲染为”=2",如下图所示:

在这里插入图片描述
解决方法请看以下代码:

// 定义HyperFormula 公式配置配置
const hyperformulaInstance = HyperFormula.buildEmpty({licenseKey: 'internal-use-in-handsontable'
});
hot = new Handsontable(document.getElementById('hot'), {// 数据data: hotData,colHeaders: true,rowHeaders: true,language: 'zh-CN',readOnly: true,// 开启公式formulas: {engine: hyperformulaInstance,sheetName: "Sheet1",},width: '100%',height: 'calc(100% - 25px)',//handsontable的许可证licenseKey: 'non-commercial-and-evaluation',
})
我们开启公式后,带有公示的单元格会渲染为对应的值,如下图所示:

在这里插入图片描述
至此,我们成功将Excel文件中的数据、样式、合并单元格等信息加载到了Handsontable中,并渲染到了页面上。

使用Handsontable导出Excel文件,请看以下代码:
// 将handsontable实例导出为excel文件
document.getElementById('export').addEventListener('click', function () {var exportData = Handsontable.helper.createEmptySpreadsheetData(100, 100);hot.getData().forEach(function (row, rowIndex) {row.forEach(function (cell, colIndex) {exportData[rowIndex][colIndex] = cell;});});var workbook = XLSX.utils.book_new();var worksheet = XLSX.utils.aoa_to_sheet(exportData);XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');XLSX.writeFile(workbook, 'export.xlsx');
});

此处贴出整体代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>基于Handsontable.js + Excel.js实现表格预览功能</title><!-- handsontable的css文件 https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.css --><link rel="stylesheet" href="./lib/handsontable.full.min.css"><style>* {margin: 0;padding: 0;}html,body {width: 100%;height: 100%;}</style>
</head><body><input type="file" id="file"><button id="btn">预览</button><button id="export">导出</button><!-- handsontable的容器 --><div id="hot"></div><!-- https://cdn.jsdelivr.net/npm/color-js --><script src="./lib/color-js.js"></script><!-- https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.js --><script src="./lib/handsontable.full.min.js"></script><!-- https://cdn.jsdelivr.net/npm/handsontable/dist/languages/zh-CN.js --><script src="./lib/zh-CN.js"></script><!-- https://cdnjs.cloudflare.com/ajax/libs/hyperformula/1.4.0/hyperformula.min.js --><script src="./lib/hyperformula.full.min.js"></script><!-- https://cdn.jsdelivr.net/npm/exceljs/dist/exceljs.min.js --><script src="./lib/exceljs.min.js"></script><!-- https://cdn.jsdelivr.net/npm/xlsx/dist/xlsx.full.min.js --><script src="./lib/xlsx.full.min.js"></script><!-- https://cdn.jsdelivr.net/npm/fxparser@1.0.0/dist/fxparser.min.js --><script src="./lib/fxparser.min.js"></script><script>//在html中使用excel+handsontable实现excel预览功能window.onload = function () {//1.引入handsontable的css和js文件//3.获取excel文件var hot; //handsontable实例var themeJson; //主题jsonvar sheet; //当前sheetvar Color = net.brehaut.Color; //引入color-js库// 自定义渲染器函数function customRenderer(hotInstance, td, row, column, prop, value, cellProperties) {Handsontable.renderers.TextRenderer(hotInstance, td, row, column, prop, value, cellProperties);// 填充样式if ("fill" in cellProperties) {// 背景颜色if ("fgColor" in cellProperties.fill && cellProperties.fill.fgColor) {td.style.background = getColor(cellProperties.fill.fgColor,themeJson);}}// 字体样式if ("font" in cellProperties) {// 加粗if ("bold" in cellProperties.font && cellProperties.font.bold) {td.style.fontWeight = "700";}// 字体颜色if ("color" in cellProperties.font && cellProperties.font.color) {td.style.color = getColor(cellProperties.font.color, themeJson);}// 字体大小if ("size" in cellProperties.font && cellProperties.font.size) {td.style.fontSize = cellProperties.font.size + "px";}// 字体类型if ("name" in cellProperties.font && cellProperties.font.name) {td.style.fontFamily = cellProperties.font.name;}// 字体倾斜if ("italic" in cellProperties.font && cellProperties.font.italic) {td.style.fontStyle = "italic";}// 下划线if ("underline" in cellProperties.font &&cellProperties.font.underline) {// 其实还有双下划线,但是双下划綫css中没有提供直接的设置方式,需要使用额外的css设置,所以我也就先懒得弄了td.style.textDecoration = "underline";// 删除线if ("strike" in cellProperties.font && cellProperties.font.strike) {td.style.textDecoration = "underline line-through";}} else {// 删除线if ("strike" in cellProperties.font && cellProperties.font.strike) {td.style.textDecoration = "line-through";}}}// 对齐if ("alignment" in cellProperties) {if ("horizontal" in cellProperties.alignment) {// 水平// 这里我直接用handsontable内置类做了,设置成类似htLeft的样子。//(handsontable)其实至支持htLeft, htCenter, htRight, htJustify四种,但是其是它还有centerContinuous、distributed、fill,遇到这几种就会没有效果,也可以自己设置,但是我还是懒的弄了,用到的时候再说吧const name = cellProperties.alignment.horizontal.charAt(0).toUpperCase() +cellProperties.alignment.horizontal.slice(1);td.classList.add(`ht${name}`);}if ("vertical" in cellProperties.alignment) {// 垂直// 这里我直接用handsontable内置类做了,设置成类似htTop的样子。const name =cellProperties.alignment.vertical.charAt(0).toUpperCase() +cellProperties.alignment.vertical.slice(1);td.classList.add(`ht${name}`);}}// 边框if ("border" in cellProperties) {if ("left" in cellProperties.border && cellProperties.border.left) {// 左边框const [borderWidth, borderStyle] = setBorder(cellProperties.border.left.style);let color = "";// console.log(row, column, borderWidth, borderStyle);if (cellProperties.border.left.color) {color = getColor(cellProperties.border.left.color, themeJson);}td.style.borderLeft = `${borderStyle} ${borderWidth} ${color}`;}if ("right" in cellProperties.border && cellProperties.border.right) {// 左边框const [borderWidth, borderStyle] = setBorder(cellProperties.border.right.style);// console.log(row, column, borderWidth, borderStyle);let color = "";if (cellProperties.border.right.color) {color = getColor(cellProperties.border.right.color, themeJson);}td.style.borderRight = `${borderStyle} ${borderWidth} ${color}`;}if ("top" in cellProperties.border && cellProperties.border.top) {// 左边框const [borderWidth, borderStyle] = setBorder(cellProperties.border.top.style);let color = "";// console.log(row, column, borderWidth, borderStyle);if (cellProperties.border.top.color) {color = getColor(cellProperties.border.top.color, themeJson);}td.style.borderTop = `${borderStyle} ${borderWidth} ${color}`;}if ("bottom" in cellProperties.border && cellProperties.border.bottom) {// 左边框const [borderWidth, borderStyle] = setBorder(cellProperties.border.bottom.style);let color = "";// console.log(row, column, borderWidth, borderStyle);if (cellProperties.border.bottom.color) {color = getColor(cellProperties.border.bottom.color, themeJson);}td.style.borderBottom = `${borderStyle} ${borderWidth} ${color}`;}}}// 在 Handsontable 初始化之前注册渲染器Handsontable.renderers.registerRenderer('customStylesRenderer', customRenderer);// 点击预览document.getElementById('btn').addEventListener('click', async function () {var hotData = null; // Handsontable 数据var file = document.getElementById('file').files[0]; // 获取文件对象const workbook = new ExcelJS.Workbook(); // 创建一个工作簿对象await workbook.xlsx.load(file); // 加载Excel文件const worksheet = workbook.getWorksheet(1); // 获取第一个工作表sheet = worksheet; // 将工作表赋值给全局变量// 遍历工作表中的所有行(包括空行)const sheetData = [];worksheet.eachRow({ includeEmpty: true }, function (row, rowNumber) {const row_values = row.values.slice(1); // 获取行数据,并排除第一列为null的数据const newRowValue = [...row_values];newRowValue.forEach(function(item, index) {if(Object.prototype.toString.call(item) === "[object Object]") {if(item.formula) {// 如果是公式,则保留公式,否则将结果作为值newRowValue[index] = item.formula.includes("=") ? item.formula : "=" + item.result;}};})sheetData.push(newRowValue);});// 将数据赋值给HandsontablehotData = sheetData; // 将主题xml转换成jsonconst themeXml = workbook._themes.theme1;const options = {ignoreAttributes: false,attributeNamePrefix: "_",};const parser = new XMLParser(options);const json = parser.parse(themeXml);themeJson = json// 获取合并的单元格const mergeCells = [];for (let i in worksheet._merges) {const { top, left, bottom, right } = worksheet._merges[i].model;mergeCells.push({row: top - 1,col: left - 1,rowspan: bottom - top + 1,colspan: right - left + 1,});};// 定义HyperFormula配置const hyperformulaInstance = HyperFormula.buildEmpty({licenseKey: 'internal-use-in-handsontable'});// 将数据加载到HyperFormulahot = new Handsontable(document.getElementById('hot'), {// 数据data: hotData,colHeaders: true,rowHeaders: true,language: 'zh-CN',readOnly: true,// 公式formulas: {engine: hyperformulaInstance,sheetName: "Sheet1",},width: '100%',height: 'calc(100% - 25px)',//handsontable的许可证licenseKey: 'non-commercial-and-evaluation',// 行高rowHeights: function (index) {if (sheet.getRow(index + 1).height) {// exceljs获取的行高不是像素值,事实上,它是23px - 13.8 的一个映射。所以需要将它转化为像素值return sheet.getRow(index + 1).height * (23 / 13.8);}return 23; // 默认},// 列宽colWidths: function (index) {if (sheet.getColumn(index + 1).width) {// exceljs获取的列宽不是像素值,事实上,它是81px - 8.22 的一个映射。所以需要将它转化为像素值return sheet.getColumn(index + 1).width * (81 / 8.22);}return 81; // 默认},// 自定义单元格样式cells: function (row, col, prop) {const cellProperties = {};const cellStyle = sheet.getCell(row + 1, col + 1).style;if (JSON.stringify(cellStyle) !== "{}") {// console.log(row+1, col+1, cellStyle);for (let key in cellStyle) {cellProperties[key] = cellStyle[key];}}return { ...cellProperties, renderer: "customStylesRenderer" };},// 合并单元格mergeCells: mergeCells});//将handsontable实例渲染到页面上hot.render();});// 将handsontable实例导出为excel文件document.getElementById('export').addEventListener('click', function () {var exportData = Handsontable.helper.createEmptySpreadsheetData(100, 100);hot.getData().forEach(function (row, rowIndex) {row.forEach(function (cell, colIndex) {exportData[rowIndex][colIndex] = cell;});});var workbook = XLSX.utils.book_new();var worksheet = XLSX.utils.aoa_to_sheet(exportData);XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');XLSX.writeFile(workbook, 'export.xlsx');});// 根据主题和明暗度themeId获取颜色function getThemeColor(themeJson, themeId, tint) {let color = "";const themeColorScheme = themeJson["a:theme"]["a:themeElements"]["a:clrScheme"];switch (themeId) {case 0:color = themeColorScheme["a:lt1"]["a:sysClr"]["_lastClr"];break;case 1:color = themeColorScheme["a:dk1"]["a:sysClr"]["_lastClr"];break;case 2:color = themeColorScheme["a:lt2"]["a:srgbClr"]["_val"];break;case 3:color = themeColorScheme["a:dk2"]["a:srgbClr"]["_val"];break;default:color = themeColorScheme[`a:accent${themeId - 3}`]["a:srgbClr"]["_val"];break;}// 根据tint修改颜色深浅color = "#" + color;const colorObj = Color(color);if (tint) {if (tint > 0) {// 淡色color = colorObj.lighten(tint).hex();} else {// 深色color = colorObj.darken(Math.abs(tint)).hex();}}return color;}// 获取颜色function getColor(obj, themeJson) {if ("argb" in obj) {// 标准色// rgba格式去掉前两位: FFFF0000 -> FF0000return "#" + obj.argb.substring(2);} else if ("theme" in obj) {// 主题颜色if ("tint" in obj) {return getThemeColor(themeJson, obj.theme, obj.tint);} else {return getThemeColor(themeJson, obj.theme, null);}}}// 设置边框function setBorder(style) {let borderStyle = "solid";let borderWidth = "1px";switch (style) {case "thin":borderWidth = "thin";break;case "dotted":borderStyle = "dotted";break;case "dashDot":borderStyle = "dashed";break;case "hair":borderStyle = "solid";break;case "dashDotDot":borderStyle = "dashed";break;case "slantDashDot":borderStyle = "dashed";break;case "medium":borderWidth = "2px";break;case "mediumDashed":borderStyle = "dashed";borderWidth = "2px";break;case "mediumDashDotDot":borderStyle = "dashed";borderWidth = "2px";break;case "mdeiumDashDot":borderStyle = "dashed";borderWidth = "2px";break;case "double":borderStyle = "double";break;case "thick":borderWidth = "3px";break;default:break;}// console.log(borderStyle, borderWidth);return [borderStyle, borderWidth];}}</script>
</body></html>

参考文献:
前端实现(excel)xlsx文件预览

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/56605.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务经典应用架构图

从网上找了一个经典的微服务架构图&#xff0c;资料来源于若依开源系统的ruoyi-cloud&#xff0c;仅供参考&#xff01;

面向城市运行“一网统管”的实景三维示范应用

在新型智慧城市建设的浪潮中&#xff0c;实景三维技术正成为推动城市治理现代化的重要力量。“一网统管”作为城市运行管理的新理念&#xff0c;强调了跨部门协作和数据共享&#xff0c;而实景三维技术为此提供了强有力的支撑。本文将探讨实景三维技术如何赋能“一网统管”&…

Linux笔记---vim的使用

1. vim的基本概念 Vim是一款功能强大的文本编辑器&#xff0c;它起源于Unix系统的vi编辑器&#xff0c;并在其基础上进行了许多改进和增强。 Vim以其高效的键盘操作、高度的可定制性和强大的文本处理能力而闻名&#xff0c;尤其受程序员和系统管理员的欢迎。 Vim支持多种模式…

cmake 编译 01

CMakeLists.txt cmake_minimum_required(VERSION 3.10)project(MyProject)set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True)# 如果顶层 CMakeLists.txt 文件中使用了 add_subdirectory() 命令&#xff0c;CMake 会进入指定的子目录&#xff0c;并处理该目录…

2024年超好用的防泄密软件分享|10款加密防泄密软件推荐

在当今数字化时代&#xff0c;企业数据安全已成为不可忽视的重要议题。随着数据泄露事件频发&#xff0c;选择一款高效可靠的防泄密软件变得尤为重要。本文将为您推荐10款在2024年备受推崇的防泄密软件&#xff0c;并重点介绍Ping32防泄密软件的功能与优势。 1. Ping32防泄密软…

Zico 2 靶机 - 详细流程

✨ 准备工作 靶机 && kali 环境要求 机器名网络配置靶机Zico 2NAT 模式攻击机kaliNAT 模式 靶机下载链接&#xff1a;zico2: 1 ~ VulnHub 打开 VMware&#xff0c;将 zico2.ova 拖拽到 VMware 中 设置 虚拟机名称(A) - 存储路径(P)- 导入 若是&#xff0c;…

3. 单例模式唯一性问题—构造函数

1. 构造函数带来的唯一性问题指什么&#xff1f; 对于不继承MonoBehaviour的单例模式基类 我们要避免在外部 new 单例模式类对象 例如 &#xff08;完整单例模式定义在上一节&#xff09; public class Main : MonoBehaviour {void Start(){// 破坏单例模式的唯一性&#xf…

【Python】AI Navigator对话流式输出

前言 在上一章节,我们讲解了如何使用Anaconda AI Navigator软件结合python搭建本机的大模型环境 【python】AI Navigator的使用及搭建本机大模型_anaconda ai navigator-CSDN博客 但是在上一章节搭建的大模型环境无法流式输出,导致输出需要等待很久,所以在这一章节,解决…

使用Three.js和Force-Directed Graph实现3D知识图谱可视化

先看样式&#xff1a; 在当今信息爆炸的时代&#xff0c;如何有效地组织和展示复杂的知识结构成为一个重要的挑战。3D知识图谱可视化是一种直观、交互性强的方式来呈现知识之间的关系。本文将详细介绍如何使用HTML、JavaScript、Three.js和Force-Directed Graph库来实现一个交互…

【深度学习】阿里云GPU服务器免费试用3月

【深度学习】阿里云GPU服务器免费试用3月 1.活动页面2.选择交互式建模PAI-DSW3.开通 PAI 并创建默认工作空间4.前往默认工作空间5.创建交互式建模&#xff08;DSW&#xff09;实例 1.活动页面 阿里云免费使用活动页面 2.选择交互式建模PAI-DSW 支持抵扣PAI-DSW入门机型计算用量…

【Unity新闻】Unity 6 正式版发布

Unity CEO Matt Bromberg 在今天自豪地宣布&#xff0c;Unity 6 正式发布&#xff01;作为迄今为止最强大和稳定的版本&#xff0c;Unity 6 为游戏和应用开发者提供了大量的新功能和工具&#xff0c;帮助他们加速开发并提升性能。 本次正式版是6.0000.0.23f1&#xff08;LTS&a…

spring-boot学习(2)

上次学习截止到拦截器 1.构建RESfun服务 PathVariable通过url路径获取url传递过来的信息 2.MyBatisPlus 第三行的mydb要改为自己的数据库名 第四&#xff0c;五行的账号密码改成自己的 MaooerScan告诉项目自己的这个MyBatisPlus是使用在哪里的&#xff0c;包名 实体类的定义…

AGI 之 【Dify】 之 使用 Docker 在 Windows 端本地部署 Dify 大语言模型(LLM)应用开发平台

AGI 之 【Dify】 之 使用 Docker 在 Windows 端本地部署 Dify 大语言模型&#xff08;LLM&#xff09;应用开发平台 目录 AGI 之 【Dify】 之 使用 Docker 在 Windows 端本地部署 Dify 大语言模型&#xff08;LLM&#xff09;应用开发平台 一、简单介绍 二、Docker 下载安…

设置 Notepad++ 制表符(Tab 缩进)宽度为2个空格大小

Notepad 默认的制表符宽度是 4 个空格的大小&#xff0c;一个规模比较大的代码段或者 xml 等文件&#xff0c;小屏幕打开时看到的情景真的和让人着急&#xff0c;拖来拖去&#xff01;有两种方案可以解决这种情况。 修改缩进为空格 这种我们不太推荐&#xff0c;但是有些公司…

小白必看web专题!配置环境还在用phpstudy?该用docker了!(php+nginx+mysql+phpmyadmin。)

大家好&#xff0c;我是Dest1ny。 大家用mac或者是windows都是使用phpstudy。 今天docker来搭建一整个phpnginxmysqlphpmyadmin。 而且mac用docker比较方便&#xff01; docker才是众望所归。 大家多多点赞&#xff0c;多多支持&#xff0c;谢谢&#xff01;&#xff01;&…

Qml-Item的构造和显示顺序

Qml-Item的构造和显示顺序 qml文件中组件构造顺序 在同一个qml文件中&#xff0c;同层级的Item, 文件尾的Item优先构造&#xff0c;文件首的Item后构造。这就能解释默认情况下同一个qml文件中&#xff0c;几个同层级的item都设置了focus:true&#xff0c;为啥最上面item最终有…

echarts设置x轴中文垂直显示,x轴滚动条

echarts官网配置&#xff0c;主要配置dataZoom option {xAxis: {type: category,data: [张三,李四,王五,赵六,孙七,周八,吴九,郑十,钱十一,陈十二,刘十三,杨十四,黄十五,何十六,宋十七],axisLabel: {formatter: function (value) {return value.split().join(\n); // 使用换行…

超全!一文详解大型语言模型的11种微调方法

导读&#xff1a;大型预训练模型是一种在大规模语料库上预先训练的深度学习模型&#xff0c;它们可以通过在大量无标注数据上进行训练来学习通用语言表示&#xff0c;并在各种下游任务中进行微调和迁移。随着模型参数规模的扩大&#xff0c;微调和推理阶段的资源消耗也在增加。…

爬虫实战(黑马论坛)

1.定位爬取位置内容&#xff1a; # -*- coding: utf-8 -*- import requests import time import re# 请求的 URL 和头信息 url https://bbs.itheima.com/forum-425-1.html headers {user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like…