纯前端 —— 200行JS代码、实现导出Excel、支持DIY样式,纵横合并_生成excel格式的js-程序员宅基地

技术标签: 前端  vue.js  javascript  

9a69fede8b2044a79dd834e3e48f20b4.png前期回顾f8e3cc1a0f694ac2b665ca2ad14c49d7.png

Vue3 + TS + Element-Plus 封装Tree组件 《亲测可用》_vue3+ts 组件封装-程序员宅基地icon-default.png?t=N7T8https://blog.csdn.net/m0_57904695/article/details/131664157?spm=1001.2014.3001.5501

目录

具体思路:

1. 准备HTML结构

2. 定义CSS样式

3. 初始化表格数据

4. 创建表格函数createTable

5. 将表格添加到页面中

6. 导出表格为Excel的函数exportTableToExcel

7. 绑定导出按钮事件

8. 实时更新表格

9. 错误处理

10. 完成

一、 使用JS 创建Table

二、️ 保留样式导出

三、完整源码、拷贝即可运行

四、 仓库地址、演示地址

五、  发布NPM

六、 结语 


纯前端实现导出Exlel

  1. 支持实时预览、根据静态数据或接口返回,实现导出带有样式的Excel
  2. 支持定义单元格、行、合并单元样式自定义 
  3. 可以随意自由的纵横合并
  4. 纯Js和Css实现 约~200行
  5. 代码极简、附有源码及详细注释
  • 先说思路:使用前端HTNL5 Table标签,实现表格,最后将其装换成 创建完整的HTML页面内容导出,动态识别样式
  • 再贴详细的每一步步骤
  • 最终放置所有源码

具体思路:

通过JavaScript操作DOM来创建一个表格,并且可以将这个表格导出为Excel文件,同时保留了表格的样式和结构(包括纵向和横向的单元格合并)。下面是实现这个功能的详细步骤:

1. 准备HTML结构

HTML文档定义了基本的页面结构,包括一个用于显示表格的<div>容器、一个导出按钮和一个用于输入JSON数据的<textarea>用于在页面填写改写JSON数据,实时预览效果。

2. 定义CSS样式

在<head>标签内,定义了表格的样式,包括表格宽度、边框、文本对齐、背景颜色等。还定义了一些特殊样式类,如.highlight、.row-bg-blue等,用于在JavaScript中动态应用。

3. 初始化表格数据

在<script>标签内,定义了一个名为tableData的对象,它包含了表格的标题和行数据。行数据中包含了文本内容、样式类名以及合并单元格的信息(colspan和rowspan)。

4. 创建表格函数createTable

这个函数接收tableData和标题文本作为参数,然后动态创建一个HTML表格。它遍历tableData中的每一行,为每个单元格创建<td>或<th>元素,并根据数据中的colspan和rowspan属性来合并单元格。

5. 将表格添加到页面中

通过调用createTable函数,并将返回的表格元素添加到#table-container中,实现了表格的显示。

6. 导出表格为Excel的函数exportTableToExcel

这个函数首先获取表格的HTML代码,然后遍历所有的样式表,将CSS规则作为字符串拼接起来。接着,创建一个包含Excel命名空间和样式的完整HTML文档。最后,使用Blob对象和URL.createObjectURL方法创建一个可下载的链接,并触发下载。

7. 绑定导出按钮事件

为导出按钮添加点击事件监听器,当按钮被点击时,调用exportTableToExcel函数,实现表格的导出。

8. 实时更新表格

为<textarea>添加input事件监听器,当用户修改JSON数据时,尝试解析新的JSON数据,并用它来更新页面上的表格。

9. 错误处理

在解析JSON时,如果发生错误,会在控制台输出错误信息,帮助用户定位问题。

10. 完成

至此,用户可以在文本域中输入JSON格式的表格数据,页面会实时显示表格,并且可以通过点击按钮将其导出为Excel文件,同时保留了所有的样式和结构设置。

一、 使用JS 创建Table

 // 创建表格
        function createTable(tableData, captionText) {
            const table = document.createElement('table');
            // 表头
            const caption = document.createElement('caption');
            caption.textContent = captionText;
            table.appendChild(caption);

            tableData.forEach((rowData, rowIndex) => {
                const tr = document.createElement('tr');
                // 应用行样式
                if (rowData.className) {
                    tr.classList.add(rowData.className);
                }
                // 处理行数据
                const cells = rowData.cells || rowData;
                cells.forEach((cellData, cellIndex) => {
                    if (cellData) {
                        // 创建td或th元素
                        const cell = rowIndex === 0 ? document.createElement('th') : document.createElement('td');
                        // 设置单元格内容
                        cell.textContent = cellData.text;
                        // 合并单元格-列
                        if (cellData.colspan) cell.rowSpan = cellData.colspan;
                        // 合并单元格-行
                        if (cellData.rowspan) cell.colSpan = cellData.rowspan;
                        // 应用单元格样式
                        if (cellData.className) cell.classList.add(cellData.className);

                        tr.appendChild(cell);
                    }
                });
                table.appendChild(tr);
            });
            return table;
        }

二、️ 保留样式导出

     // 保留样式导出 
        function exportTableToExcel(tableId, filename = '') {
            // 获取表格的HTML代码
            var tableHTML = document.querySelector('#table-container table').outerHTML;
            // 为了确保样式能够被导出,我们需要将样式标签内的CSS规则也一并获取
            var css = '';
            var styleSheets = document.styleSheets;
            for (var i = 0; i < styleSheets.length; i++) {
                try {
                    var rules = styleSheets[i].cssRules || styleSheets[i].rules;
                    for (var r = 0; r < rules.length; r++) {
                        css += rules[r].cssText + '\n';
                    }
                } catch (e) {
                    console.error(e);
                }
            }

            // 创建完整的HTML页面内容
            var fullHTML = `
        <html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">
        <head>
            <!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>
            <x:Name>{sheetname}</x:Name>
            <x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>
            </x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->
            <style>
                ${css}
            </style>
        </head>
        <body>
            ${tableHTML}
        </body>
        </html>
    `;

            // 创建Blob对象
            var blob = new Blob([fullHTML], {
                type: 'application/vnd.ms-excel'
            });

            // 创建下载链接
            var downloadLink = document.createElement("a");

            // 文件名
            filename = filename ? filename + '.xls' : 'export.xls';

            // 创建下载链接
            downloadLink.href = URL.createObjectURL(blob);
            downloadLink.download = filename;

            // 触发下载
            document.body.appendChild(downloadLink);
            downloadLink.click();
            document.bodyNaNpxoveChild(downloadLink);
        }

三、完整源码、拷贝即可运行

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <link rel="icon" href="./favicon.ico">
    <style>
        /* 设置表格的基本样式 */
        table {
            width: 80%;
            /* 表格宽度 */
            border-collapse: collapse;
            /* 边框合并 */
            margin: auto;
            /* 居中显示 */
            /* background-color: #f8f8f8; */
            /* 背景颜色 */
        }

        /* 设置表格单元格的样式 */
        th,
        td {
            border: 1px solid #090909;
            /* 单元格边框 */
            padding: 10px;
            /* 内边距 */
            text-align: center;
            /* 文本居中 */
            font-family: Arial, sans-serif;
            /* 字体 */
        }

        /* 设置表头的样式 */
        th {
            /* background-color: #4CAF50; */
            /* 背景颜色 */
            color: rgb(10, 0, 0);
            /* 文本颜色 */
        }

        /* 鼠标悬停时单元格的样式 */
        td:hover {
            background-color: #ddd;
            /* 背景颜色 */
        }

        /* 合并单元格的特殊样式 */
        .merged-cell {
            background-color: #ffcccb;
            /* 背景颜色 */
        }

        /* 自定义的单元格样式 */
        .highlight {
            font-weight: bold;
        }

        .text-red {
            color: #ff0000;
            /* 文本颜色为红色 */
        }

        /* 自定义的行样式 */
        .row-bg-blue {
            background-color: #C6E0B4;

        }

        .row-bg-yellow {
            background-color: #FFFF00;
        }

        .textStyle {
            font-size: 34px;
            font-weight: bold;
            color: red;
            font-style: italic;
            text-decoration: underline;
        }

        #export-btn {
            margin-bottom: 25px;
        }

        #json-input {
            width: 100%;
            height: 500px;
            margin: auto;
            overflow-y: auto;

        }
    </style>
</head>

<body>

    <div id="table-container"></div>
    <button id="export-btn">导出为Excel</button>

    <textarea id="json-input"></textarea>
    <script>
        // 表格数据和合并规则 colspan 和 rowspan 合并规则 设置后就需要删除对应规则的单元格
        const tableData = {
            title: '这是表格标题',
            rows: [
                // 表头
                [
                    { text: '初始时间', },
                    { text: '2024/1/10 9:12', }
                ],
                // 数据行
                [
                    { text: '类别/级别', className: 'highlight' },
                    { text: '类别/级别名称', className: 'highlight' },
                    { text: '类别/类别子名称', className: 'highlight' },
                    { text: '上季度销售总金额', className: 'highlight' },
                    { text: '当季度销售总金额', className: 'highlight' },
                    { text: '同比季度增长率', className: 'highlight' }
                ],
                // 合并单元格-纵列
                [
                    { text: '级别', colspan: 5 },
                    { text: '一级' },
                    { text: '特制重大事件' },
                    { text: '100' },
                    { text: '110' },
                    { text: '10.00%' }
                ],
                [
                    { text: '二级' },
                    { text: '重大事件' },
                    { text: '200' },
                    { text: '120' },
                    { text: '-40.00%' },
                ],
                [
                    { text: '三级' },
                    { text: '较大事件' },
                    { text: '300' },
                    { text: '130' },
                    { text: '-56.67%' }
                ],
                [
                    { text: '四级' },
                    { text: '一般事件' },
                    { text: '400' },
                    { text: '140' },
                    { text: '-65.00%' }
                ],
                // 添加行样式
                { className: 'row-bg-blue', cells: [{ text: '合计', }, { text: null }, { text: '1000' }, { text: '500' }, { text: '-50.00%' }] },
                //   合并单元格-横列
                { className: 'row-bg-yellow', cells: [{ text: '总计', }, { text: '100%', rowspan: 2, className: 'textStyle' }, { text: '1000' }, { text: '500' }, { text: '-50.00%' }] }
            ]
        }

        // 创建表格
        function createTable(tableData, captionText) {
            const table = document.createElement('table');
            // 表头
            const caption = document.createElement('caption');
            caption.textContent = captionText;
            table.appendChild(caption);

            tableData.forEach((rowData, rowIndex) => {
                const tr = document.createElement('tr');
                // 应用行样式
                if (rowData.className) {
                    tr.classList.add(rowData.className);
                }
                // 处理行数据
                const cells = rowData.cells || rowData;
                cells.forEach((cellData, cellIndex) => {
                    if (cellData) {
                        // 创建td或th元素
                        const cell = rowIndex === 0 ? document.createElement('th') : document.createElement('td');
                        // 设置单元格内容
                        cell.textContent = cellData.text;
                        // 合并单元格-列
                        if (cellData.colspan) cell.rowSpan = cellData.colspan;
                        // 合并单元格-行
                        if (cellData.rowspan) cell.colSpan = cellData.rowspan;
                        // 应用单元格样式
                        if (cellData.className) cell.classList.add(cellData.className);

                        tr.appendChild(cell);
                    }
                });
                table.appendChild(tr);
            });
            return table;
        }

        // 将表格添加到页面中
        document.getElementById('table-container').appendChild(createTable(tableData.rows, tableData.title));

        // 保留样式导出 
        function exportTableToExcel(tableId, filename = '') {
            // 获取表格的HTML代码
            var tableHTML = document.querySelector('#table-container table').outerHTML;
            // 为了确保样式能够被导出,我们需要将样式标签内的CSS规则也一并获取
            var css = '';
            var styleSheets = document.styleSheets;
            for (var i = 0; i < styleSheets.length; i++) {
                try {
                    var rules = styleSheets[i].cssRules || styleSheets[i].rules;
                    for (var r = 0; r < rules.length; r++) {
                        css += rules[r].cssText + '\n';
                    }
                } catch (e) {
                    console.error(e);
                }
            }

            // 创建完整的HTML页面内容
            var fullHTML = `
        <html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">
        <head>
            <!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>
            <x:Name>{sheetname}</x:Name>
            <x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>
            </x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->
            <style>
                ${css}
            </style>
        </head>
        <body>
            ${tableHTML}
        </body>
        </html>
    `;

            // 创建Blob对象
            var blob = new Blob([fullHTML], {
                type: 'application/vnd.ms-excel'
            });

            // 创建下载链接
            var downloadLink = document.createElement("a");

            // 文件名
            filename = filename ? filename + '.xls' : 'export.xls';

            // 创建下载链接
            downloadLink.href = URL.createObjectURL(blob);
            downloadLink.download = filename;

            // 触发下载
            document.body.appendChild(downloadLink);
            downloadLink.click();
            document.bodyNaNpxoveChild(downloadLink);
        }

        // 绑定按钮点击事件
        document.getElementById('export-btn').addEventListener('click', function () {
            exportTableToExcel('table-container', 'zk-table-export');
        });

        // 获取文本域和表格容器的引用
        const jsonInput = document.getElementById('json-input');
        const tableContainer = document.getElementById('table-container');

        // 初始化文本域的内容为当前的 tableData
        jsonInput.value = JSON.stringify(tableData, null, 2);

        // 为文本域添加事件监听器
        jsonInput.addEventListener('input', function () {
            try {
                // 尝试解析文本域中的 JSON
                const newTableData = JSON.parse(jsonInput.value);
                // 清空表格容器并添加新的表格
                tableContainer.innerHTML = '';
                tableContainer.appendChild(createTable(newTableData.rows, newTableData.title));
            } catch (e) {
                // 如果 JSON 解析出错,可以在这里处理错误,例如显示错误信息
                console.error('JSON 解析错误: ', e.message);
                // alert('JSON 解析错误:' + e.message);
            }
        });
    </script>

</body>

</html>

四、 仓库地址、演示地址

仓库:

Export_Excel_JS: ️ 纯前端JavaScript实现Excel导出:支持多样式、合并与样式控制 200行纯Js (gitee.com)

在线演示:

zhang-kun8888.gitee.io/export_-excel_-js/

五、  发布NPM

  • 最后可以将其工程化,发布到NPM上,御用于JS项目和Vue项目,这个我已经在做了,过后就会发行!
  • 其他 纯前端的功能  则在《JavaScript 《公司开发功能》 99+ 大合集》这篇博文中!

六、 结语 

7730e2bd39d64179909767e1967da702.jpeg

 _______________________________  期待再见  _______________________________

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_57904695/article/details/135537511

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法