使用exceljs导出luckysheet表格 纯前端 支持离线使用

一.技术

exceljs,luckysheet

二.实现

参考网上博文exceljs对导出lucksheet表格的实现,发现存在一些问题并给予修复:

1.字体颜色、字号,加粗等适配的问题.

2.单元格对齐方式不生效;

3.单元格边框无法绘制;

4.单元格边框颜色及线型错乱;

5.单元格列宽处理;

6.合并单元格导出错乱;

7.其他的一些BUG

三.注意事项

1、由于luckysheet在网页端和excel分辨率无法保持完全一致,所以导出到excel中的时候,可能存在单元格大小与原表格不一致的情况,需要在setStyleAndValue中对单元格大小进行手动调整,具体可查看代码注释。后续也会逐渐进行自动适配。

2、目前仅支持表格,数据透视表的导出;不支持图片,图表的导出,后续有时间慢慢完善。

四.使用教程

1、安装 exceljs、file-saver

使用以下命令通过 npm 安装 exceljsfile-saver

npm install exceljs file-saver

2、引用导出Excel文件

安装完成后把找到 exceljs.min.js 和 FileSaver.min.js 文件拷贝到自己的项目中,并添加引用

D:\project\ExcelJS-demo\node_modules\exceljs\dist\exceljs.min.js
D:\project\ExcelJS-demo\node_modules\file-saver\dist\FileSaver.min.js
<script type="text/javascript" src="luckysheet/exceljs/exceljs.min.js"></script>
<script type="text/javascript" src="luckysheet/exceljs/FileSaver.min.js"></script>

3、调用导出Excel函数

这个函数是我自己封装的版本

在项目里新建一个js文件,名为:exportExcel.js (可自定义),把下面这段导出的代码粘贴进去

// 导出 Luckysheet 内容为 Excel(ExcelJS)
async function exportLuckysheetToExcelByExcelJS() {//创建工作簿const workbook = new ExcelJS.Workbook();// 启用样式支持(关键配置)workbook.useStyles = true;// 拿到当前激活页的配置对象const activeSheet = luckysheet.getSheet();const originalSheetIndex = activeSheet.order ?? 0;// 激活每个 sheet,确保数据初始化(特别是数据透视表)const sheets = luckysheet.getAllSheets();for (let i = 0; i < sheets.length; i++) {luckysheet.setSheetActive(i);//每切换一次表格,延迟1ms,为了让表格数据能够正常加载和渲染。await new Promise(resolve => setTimeout(resolve, 1));if (i == sheets.length - 1) {// 恢复原始激活的 sheetluckysheet.setSheetActive(originalSheetIndex);}}// 重新获取激活后的所有工作表const initializedSheets = luckysheet.getAllSheets();initializedSheets.forEach(sheet => {const worksheet = workbook.addWorksheet(sheet.name);// 1. 填充数据与样式sheet.data.forEach((row, rowIndex) => {row.forEach((cell, colIndex) => {if (!cell) return;const excelCell = worksheet.getCell(rowIndex + 1, colIndex + 1);// 值或公式excelCell.value = cell.f ? { formula: cell.f, result: cell.v } : cell.v;// 字体样式(字号、颜色、加粗、斜体、下划线、字体名)const fontSizePx = cell.fs !== undefined ? cell.fs : 10;const font = { size: fontSizePx };if (cell.fc) font.color = { argb: hexToARGB(cell.fc) };if (cell.bl === 1) font.bold = true;if (cell.cl === 1) font.italic = true;if (cell.ul === 1) font.underline = true;if (cell.ff) font.name = cell.ff;excelCell.font = font;// 背景色if (cell.bg) {excelCell.fill = {type: 'pattern',pattern: 'solid',fgColor: { argb: hexToARGB(cell.bg) }};}// 对齐方式const hAlignMap = { 0: 'center', 1: 'left', 2: 'right' };const vAlignMap = { 0: 'middle', 1: 'top', 2: 'bottom' };const alignment = {};if (cell.ht !== undefined) alignment.horizontal = hAlignMap[cell.ht];if (cell.vt !== undefined) alignment.vertical = vAlignMap[cell.vt];if (Object.keys(alignment).length > 0) excelCell.alignment = alignment;});});// 2. 合并单元格const mergedMap = new Set();Object.values(sheet.config?.merge || {}).forEach(merge => {const r1 = merge.r + 1, c1 = merge.c + 1;const r2 = merge.r + merge.rs, c2 = merge.c + merge.cs;const mergeKey = `${r1},${c1},${r2},${c2}`;if (mergedMap.has(mergeKey)) return;mergedMap.add(mergeKey);try {worksheet.mergeCells(r1, c1, r2, c2);} catch (e) {console.warn(`跳过已合并区域:${mergeKey}`, e);}});// 3. 边框处理(透视表默认细边框)if (!sheet.config?.borderInfo && sheet.isPivotTable) {const { maxRow, maxCol } = getUsedRange(sheet);sheet.config = sheet.config || {};sheet.config.borderInfo = [{rangeType: "range",borderType: "border-all",style: "1",color: "#000000",range: [{ row: [0, maxRow - 1], column: [0, maxCol - 1] }]}];}(sheet.config?.borderInfo || []).forEach(border => {const rawColor = border.color === '#000' ? '#000000' : border.color;const color = hexToARGB(rawColor || '#000000');const borderStyleMap = {"1": "thin", // 细线"2": "dashed",// 虚线"3": "dotted", // 点线"4": "thick",// 粗线"5": "thick",// 粗线"6": "dashed",// 虚线"7": "dotted", // 点线"8": "medium",// 中等"9": "dashed",// 虚线"10": "thick"// 粗线};const styleName = borderStyleMap[border.style] || 'thin';const style = { style: styleName, color: { argb: color } };(border.range || []).forEach(range => {const r1 = range.row[0], r2 = range.row[1];const c1 = range.column[0], c2 = range.column[1];for (let r = r1; r <= r2; r++) {for (let c = c1; c <= c2; c++) {const cell = worksheet.getCell(r + 1, c + 1);const oldBorder = cell.border || {};let newBorder = { ...oldBorder };switch (border.borderType) {case 'left': newBorder.left = style; break;case 'right': newBorder.right = style; break;case 'top': newBorder.top = style; break;case 'bottom': newBorder.bottom = style; break;case 'border-all':case 'all':newBorder = {top: style, bottom: style,left: style, right: style};break;}cell.border = newBorder;}}});});// 4. 列宽设置const colConfig = sheet.config?.columnlen || {};const colCount = sheet.data?.[0]?.length || 0;for (let c = 0; c < colCount; c++) {const excelCol = worksheet.getColumn(c + 1);const luckysheetWidth = colConfig[c];if (luckysheetWidth !== undefined) {excelCol.width = Math.round(luckysheetWidth / 7 * 100) / 100;} else {excelCol.width = 10;}}});// 5. 生成文件并下载const buffer = await workbook.xlsx.writeBuffer();saveAs(new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }),'onlieExcel.xlsx');
}// 转换颜色为 ExcelJS ARGB 格式
function hexToARGB(hex) {if (!hex || !hex.startsWith('#')) return undefined;const rgb = hex.slice(1).toUpperCase();return 'FF' + rgb;
}// 获取使用区域的最大行列
function getUsedRange(sheet) {let maxRow = 0;let maxCol = 0;sheet.data.forEach((row, rowIndex) => {if (row) {row.forEach((cell, colIndex) => {if (cell && cell.v !== undefined && cell.v !== null && cell.v !== '') {maxRow = Math.max(maxRow, rowIndex);maxCol = Math.max(maxCol, colIndex);}});}});return { maxRow: maxRow + 1, maxCol: maxCol + 1 };
}

在页面里引用 exportExcel.js 文件

<script type="text/javascript" src="luckysheet/exceljs/exportExcel.js"></script>

调用 exportLuckysheetToExcelByExcelJS() 方法实现导出

<a href="javascript:exportLuckysheetToExcelByExcelJS()" id="btnExport">导出Xlsx</a>

历时3天的劳动成果终于结束,收官,撒花 ✿✿ヽ(°▽°)ノ✿

五.源码下载

luckysheet demo 完整代码,包括以下功能:

1、Luckysheet 本地引入方式,已解决断网报错,字体图标不显示的问题

2、使用SheetJS实现导入到luckysheet中,纯前端,支持离线使用

3、使用ExcelJS实现导出luckysheet表格,纯前端,支持离线使用

点击 下载demo


 

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

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

相关文章

从0到1学习c++ 命名空间

也是好久没写博客了&#xff0c;主播这半年一直在忙别的领域&#xff0c;在磁力驱动领域干了一年&#xff0c;最好发现自己对这个领域并不是很感兴趣&#xff0c;做这个领域多半都是为了发文章&#xff0c;现在闲下来了&#xff0c;主播终于也是过上好日子了&#xff0c;主播又…

大模型提示词漏洞攻防测试:技术分析与实践指南

引言 随着ChatGPT、Claude、Gemini等大型语言模型(LLMs)的广泛应用&#xff0c;它们已经成为现代AI系统的核心组件&#xff0c;被整合到各种产品和服务中。这些模型通过提示。Prompts)与用户进行交互&#xff0c;而提示词作为人类与AI沟通的桥梁&#xff0c;其安全性变得尤为重…

Golang实现 - 实现只有表头的 Excel 模板,并在指定列添加了下拉框功能。生成的 Excel 文件在打开时,指定列的单元格会显示下拉选项

该版本完全兼容最新版 excelize 库 (v2.7)&#xff0c;实现了只有表头的 Excel 模板&#xff0c;并在指定列添加了下拉框功能。生成的 Excel 文件在打开时&#xff0c;指定列的单元格会显示下拉选择箭头。代码如下&#xff1a;package mainimport ("fmt""log&qu…

全连接队列

监听套接字使用socket接口创建一个套接字&#xff0c;然后bind给套接字绑定地址&#xff0c;最后listen将套接字设置为监听套接字。监听套接字以前理解是三元组标识&#xff0c;后面看了netstat&#xff0c;觉得应该是五元组&#xff0c;只不过它这个五元组是{协议&#xff0c;…

JavaWeb-JSP

JSP JSP就是模板引擎 Template&#xff0c;因为看到的jsp是模板不变的&#xff0c;如果想让页面发生改变&#xff0c;就是自己添加java代码改变页面。有Java代码&#xff0c;Tomcat服务器就会对jsp模板进行解析&#xff0c;解析完之后就是Servlet&#xff08;java类&#xff09…

大模型中常说的Token到底是什么?和Cookie和Session有什么区别?一文讲清

什么是Token&#xff08;令牌&#xff09;Acesss Token是访问资源接口&#xff08;API&#xff09;时所需要的资源凭证。简单token的组成&#xff1a;uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign&#xff08;签名&#xff0c;token的前几位以哈希算法压缩成的一定长…

RAGFlow:检索增强生成技术的高效实现与深度探索

在当今信息爆炸的时代&#xff0c;如何从海量的数据中快速、准确地获取并利用有价值的信息&#xff0c;成为了众多领域面临的关键挑战。检索增强生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;技术应运而生&#xff0c;它将信息检索与大型语言模型&#xff…

【轨物洞见】光伏逆变器数据:分布式电站价值回归的“第一块多米诺骨牌”

1. 逆变器&#xff1a;光伏电站的核心“数据心脏” 逆变器是将光伏组件产生的直流电转换为交流电的关键设备&#xff0c;其性能直接影响着整个电站的效率与稳定性。对其电压、电流、功率参数以及故障告警信息进行远程数据采集&#xff0c;是实现精细化运维和预测性维护的起点。…

如何在 npm 上发布 Element Plus 二次封装组件

在一次开发中&#xff0c;小李接到一个重要的任务&#xff1a;将 Element Plus 中的时间组件根据团队的独特需求进行二次封装。他灵机一动&#xff0c;决定将这个自定义组件打包成一个 npm 包&#xff0c;以便团队的其他小伙伴们可以快速、方便地使用。接下来&#xff0c;让我们…

vue2使用v-viewer图片预览:打开页面自动预览,禁止关闭预览,解决在微信浏览器的页面点击事件老是触发预览初始化的问题

1、安装&#xff1a; npm install v-viewer viewerjs2、在 main.js 中全局注册&#xff1a; import Viewer from v-viewer; import viewerjs/dist/viewer.css; Vue.use(Viewer ); //配置项&#xff08;可选&#xff0c;根据需求调整&#xff09; // Vue.use(Viewer, { // d…

开源 Arkts 鸿蒙应用 开发(八)多媒体--相册和相机

文章的目的为了记录使用Arkts 进行Harmony app 开发学习的经历。本职为嵌入式软件开发&#xff0c;公司安排开发app&#xff0c;临时学习&#xff0c;完成app的开发。开发流程和要点有些记忆模糊&#xff0c;赶紧记录&#xff0c;防止忘记。 相关链接&#xff1a; 开源 Arkts …

无线通信资源分配相关算法

1.Maximum Clique First (MCF)是一种启发式图着色算法&#xff08;heuristic graph coloring algorithm&#xff09;&#xff0c;它的核心思想是&#xff1a;优先为图中最大团&#xff08;maximum clique&#xff09;中的顶点分配不同的颜色&#xff0c;然后再依次为其他顶点上…

Kafka监控体系搭建:基于Prometheus+JMX+Grafana的全方位性能观测方案

为什么需要Kafka监控监控架构概述步骤一&#xff1a;部署JMX Exporter 1.1 下载JMX Agent1.2 创建指标暴露配置 步骤二&#xff1a;配置Kafka集成JMX 2.1 启动参数配置2.2 验证指标暴露 步骤三&#xff1a;配置Prometheus采集 3.1 修改Prometheus配置3.2 验证数据采集 步骤四&a…

stack 和 queue

目录 一、stack 1.1 stack 的介绍 1.2 stack的使用 1&#xff09;最小栈 2&#xff09;栈的弹出压入序列 3&#xff09;逆波兰表达式求值 1.3 stack 的模拟使用 二、queue 2.1 queue的介绍 2.2 queue的使用 2.3 queue的模拟使用 三、容器适配器 3.1 什么是容器适配…

sqlsuger 子表获取主表中的一个字段的写法

在使用 SQL 语言进行数据库操作时&#xff0c;如果你想要从子表获取数据&#xff0c;同时关联到主表中的一个字段&#xff0c;通常我们会使用 SQL 的 JOIN 语句。JOIN 语句允许你通过一个或多个共同的字段将两个或多个表连接起来。这里我将展示几种常见的 JOIN 类型&#xff08…

Docker配置Gitlab-runner实现自动化容器化部署前端项目

叠甲前言 本文仅作为个人学习GitLab的CI/CD功能记录&#xff0c;不适合作为专业性指导&#xff0c;如有纰漏&#xff0c;烦请君指正。 云主机注册Gitlab Runner 自动化构建部署的弊端 在前一文中&#xff0c;我们在Linux云主机上注册了Gitlab-runner, 每次在gitlab流水线上发…

MySQL介绍和MySQL包安装

文章目录MySQL介绍和安装1.MySQL介绍1.1 MySQL 的定义1.2 MySQL 的特点1.3 MySQL 的应用领域1.4 MySQL 的存储引擎1.5 MySQL 的架构1.6 MySQL 的优势和局限性1.7 MySQL 的未来发展趋势2.MySQL安装2.1 主机初始化2.1.1 设置网卡名2.1.2 设置ip地址2.1.3 配置镜像源2.1.4 关闭防火…

J2EE模式---视图助手模式

视图助手模式基础概念视图助手模式&#xff08;View Helper Pattern&#xff09;是一种结构型设计模式&#xff0c;其核心思想是将视图层中复杂的逻辑提取到独立的助手类中&#xff0c;使视图代码更加简洁、易于维护。视图助手通常提供一系列工具方法&#xff0c;用于处理格式化…

开源的语音合成大模型-Cosyvoice使用介绍

1 模型概览 CosyVoice 是由阿里巴巴达摩院通义实验室开发的新一代生成式语音合成大模型系列&#xff0c;其核心目标是通过大模型技术深度融合文本理解与语音生成&#xff0c;实现高度拟人化的语音合成体验。该系列包含初代 CosyVoice 及其升级版 CosyVoice 2.0&#xff0c;两者…

深度学习·CLIP

CLIP 数据大小 4亿个文本-图像对&#xff0c;而且是高质量的 预训练方法 Text encoder“The text sequence is bracketed with [SOS] and [EOS] tokens and the activations of the highest layer of the transformer at the [EOS] token are used as the feature representati…