前言

最近接触了一个使用uniapp写的app,其中有个导出Excel的功能,在此做一下总结。

最终结果

app端生成excel,修改部分单元格参数(文字居中,单元格类型为文本),最后导出.xlsx文件到该app的documents目录下。

一些问题

0.Blob or html

在uniapp中导出excel,铺天盖地的都是sheetjs + Blob,然后一顿下载,那是在H5!app里面压根就没有blob对象。(这里引发另一个思考,就是在app端使用webview,这样就能有Blob对象了,没试过,感觉应该是可行的。)

app中,需要使用系统IO流来创建.xlsx文件,然后写入数据。如果写入的是下面的HTML模板,就可以用excel打开了:

 let worksheet = "sheet1";
 let tableHtml= '<tr><td>姓名</td><td>电话</td><td>邮箱</td></tr>'
      // 循环遍历,每行加入tr标签,每个单元格加td标签
      for (let i = 0; i < jsonData.length; i++) {
        tableHtml+= '<tr>'
        for (let item in jsonData[i]) {
          // 增加\t为了不让表格显示科学计数法或者其他格式
          tableHtml += `<td>${jsonData[i][item] + '\t'}</td>`
        }
        tableHtml+= '</tr>'
      }
 //下载的表格模板数据
 let template = `<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 encoding="UTF-8"><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>
                     <x:Name>${worksheet}</x:Name>
                     <x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>
                     </x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->
                     </head><body>${tableHtml}</body></html>`;

先不用急着复制, tableHtml 是可以通过sheetJS转换的,下面会提到,当然你自己循环生成也能用,可以直接看保存的实现。

1.SheetJS or xlsx-js-style

我们页面中的数据一般都是数组对象类型的,sheetJS可以转换json为html,省去一部分思考。
SheetJS: https://github.com/SheetJS/sheetjs
SheetJS中文文档: https://github.com/rockboom/SheetJS-docs-zh-CN/
SheetJS这个库确实挺牛的,但是它的社区版本不支持修改单元格样式,需要升级到Pro版本,然后就是要你发邮件给作者申请,巴拉巴拉。但是!简单的单元格样式修改是可以通过修改源码来达成的。笔者这里主要是居中对齐和单元格数据类型必须是文本类型,完全够用了。

再来说下xlsx-js-style。
xlsx-js-style : https://github.com/gitbrent/xlsx-js-style
官方的描述是 SheetJS Community Edition + Basic Cell Styles ,听起来不错,但是因为app中保存文件的原因,没有采用。

实现

思路是这样的:

  1. 修改sheetJS部分源码,满足单元格样式需求
  2. 使用sheetJS转换JSON为html文本(需要取出table标签,注入到准备好的html模板中)
  3. 使用H5+ api保存文件

先从github上将 xlsx.core.min.js 下载到项目中。

1.单元格居中 & 设置文本类型

源码中搜索 s[“data-v”] 可以找到需要编辑的位置:
此处新增一行代码:

s["style"] = "text-align: center; mso-number-format:'\@'";

在这里插入图片描述

笔者通过观察,发现这里的s就是单元格对象,转化为html时候,s上的属性会被写为单元格的行内式,text-align: center不用解释了,mso-number-format:'\@'"意思是将此单元格数据类型设置为自定义类型,值为@

有人说,你不是设置成文本类型吗,怎么又弄成自定义类型了。笔者打开excel验证过,当设置一个单元格格式为自定义类型,值为@的时候,再查看该单元格类型,就会自动变为文本类型。

原因我也查过,在excel中@是引用单元格的原文,所以你只写一个@,其实就是原文显示,最终经历了一些不为人知的类型处理,就摇身一变,成了文本类型。感兴趣可以看下这篇文章:Excel自定义格式之“@”符号

2.使用sheetJS转换JSON为html文本

数据格式如下:

excelData: [
    {
        no: "1",
        tag: "weqwrqwrqwrqwrq",
        remark: "22",
    },
    {
        no: "2",
        tag: "342343434343",
        remark: "33",
    },
    {
        no: "3",
        tag: "25125152512",
        remark: "44",
    },
],

转换为html:

const sheet = XLSX.utils.json_to_sheet(this.excelData);
const htmlSheet = XLSX.utils.sheet_to_html(sheet);

这里导出的html是一个完整的包含html标签的字符串,而我们只需要table标签的内容,使用正则提取下:

const pattern = /<table(?:(?!<\/table>).|\n)*?<\/table>/;
const tableHtml = htmlSheet.match(pattern)[0];

最终获取到完整的模板:

const writeData = this.tableToExcel(tableHtml);

function tableToExcel(tableHtml) {
   //列标题
   let worksheet = "sheet1";
   //下载的表格模板数据
   let template = `<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 encoding="UTF-8"><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>
                       <x:Name>${worksheet}</x:Name>
                       <x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>
                       </x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->
                       </head><body>${tableHtml}</body></html>`;
   return template;
},

使用H5+ api保存文件

这里主要使用的是io接口,文档在此:html5plus io文档
保存文件代码如下:

 // #ifdef APP-PLUS
plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, function(fs) {
    let root = fs.root;
    // 直接在 documents 中创建文件
    root.getFile(
        `${new Date().getTime()}.xlsx`,
        {
            create: true,
        },
        (fileEntry) => {
            fileEntry.createWriter(
                (writer) => {
                    // 写入数据
                    writer.write(writeData);
                    // 写入文件成功完成的回调函数
                    writer.onwrite = (e) => {
                        uni.showToast({
                            title: `导出成功`,
                            icon: "none",
                        });
                    };
                },
                (err) => {
                    console.log(err, "创建文件写入器错误");
                }
            );
        },
        (err) => {
            console.log(err);
        }
    );
});
// #endif

关于app存储目录的问题,可以看这篇文章: plus.io
io的api一层套一层,需要自己耐心体会,一定可以达到目的!
至此,导出excel功能就收官了!

以上!

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐