如何导出Excel文件 -- excel_hm介绍 ##三方SDK##

上海张律师
• 阅读 1

前阵子在和朋友讨论,记账类的app已经那么普遍了,如果想要做点不一样的还能加什么功能。从我自己的角度出发,我觉得完全可以加一个导出Excel嘛。但是目前原生鸿蒙并没有导出Excel的方法,常规的做法是通过服务器端生成Excel文件后,再下载到本地……服务器端的方案肯定是不成的,因为大家都知道,记账属于独立开发三件套止一,独立开发意味着……哪里来的钱租服务器啊。

所以,我们还是自己做一个Excel导出的三方库吧

首先要思考一下原理:excel并不像html网页或者txt文件一样,可以通过直接编辑内容指定一个文件格式来生成文件,如果我们对一个excel文件右键通过记事本打开的话,你会发现一堆乱码,不过这个思路还是可以用的,比如说csv格式的数据然后保存成xls格式,但是csv只能上数据不能做样式,还好,还有一个xml可以用,因为从Excel 2003开始,就引入了xml格式,而且还可以支持样式。

接下来思路就简单了,大致来说就是:定义一个数据格式,要包含文件名、每行每列的内容还可以加入样式,然后根据xml的文件格式去生成一个完整的文件内容,最后通过@ohos.file.fs (文件管理)接口,来创建一个文件,写入内容,再保存。保存到本地用的是@ohos.file.picker (选择器)中的DocumentViewPicker,即让用户选择一个文件保存的地址。

已经上传到了OpenHarmony的三方库中,具体使用办法如下:

安装使用

基础安装

ohpm install excel_hm

1. 导入库文件

import {
   ExcelGenerator,
   ExcelTableData,
   ExcelCellData,
   ExcelGenerateOptions,
   ExcelGenerateResult
} from 'excel_hm';

2. 创建生成器实例

const context = getContext(this) as common.UIAbilityContext;
const excelGenerator = new ExcelGenerator(context);

数据格式规范

ExcelTableData 接口

interface ExcelTableData {
title: string;                    // 表格标题(必填)
headers?: string[];               // 表头数组(可选)
data: ExcelCellData[][];         // 二维数据数组(必填)
titleStyle?: ExcelCellStyle;     // 标题样式(可选)
headerStyle?: ExcelCellStyle;    // 表头样式(可选)
dataStyle?: ExcelCellStyle;      // 数据样式(可选)
}

ExcelCellData 接口

interface ExcelCellData {
value: string;                   // 单元格内容(必填)
style?: ExcelCellStyle;         // 单元格样式(可选)
}

ExcelCellStyle 接口

interface ExcelCellStyle {
fontWeight?: 'normal' | 'bold'; // 字体粗细
fontSize?: number;               // 字体大小
fontColor?: string;             // 字体颜色
backgroundColor?: string;        // 背景颜色
alignment?: 'left' | 'center' | 'right'; // 对齐方式
}

使用示例

基础用法

// 1. 创建简单表格数据
const tableData = ExcelGenerator.createSimpleTableData(
   '员工信息表',
   ['姓名', '年龄', '部门'],
   [
      ['张三', '25', '技术部'],
      ['李四', '30', '销售部'],
      ['王五', '28', '人事部']
   ]
);

// 2. 生成Excel文件
const result = await excelGenerator.generateExcel(tableData);

if (result.success) {
   console.log('生成成功:'+ result.message);
   // 3. 保存到本地
   const saveResult = await excelGenerator.saveToLocal(result.filePath!, result.fileName!);
   console.log('保存结果:'+ saveResult.message);
} else {
   console.error('生成失败:'+ result.message);
}

完整Demo代码

import { common } from '@kit.AbilityKit';
import router from '@ohos.router';
import {
   ExcelGenerator,
   ExcelTableData,
   ExcelCellData,
   ExcelGenerateOptions,
   ExcelGenerateResult
} from 'excel_hm';

@Entry
@Component
struct Excel {
   @State tableTitle: string = '员工信息表';
   @State rows: number = 5;
   @State cols: number = 3;
   @State tableData: ExcelCellData[][] = [];
   @State isGenerating: boolean = false;
   @State message: string = '';
   @State messageType: 'success' | 'error' | '' = '';
   @State showFileActions: boolean = false;

   private excelGenerator?: ExcelGenerator;

   aboutToAppear() {
      // 初始化Excel生成器
      const context = getContext(this) as common.UIAbilityContext;
      this.excelGenerator = new ExcelGenerator(context);

      this.initializeTable();
   }

   // 初始化表格数据
   initializeTable() {
      this.tableData = [];

      // 默认测试数据
      const testData = [
         ['姓名', '年龄', '部门'],
         ['张三', '25', '技术部'],
         ['李四', '30', '销售部'],
         ['王五', '28', '人事部'],
         ['赵六', '32', '财务部']
      ];

      for (let i = 0; i < this.rows; i++) {
         const row: ExcelCellData[] = [];
         for (let j = 0; j < this.cols; j++) {
            // 如果有测试数据且在范围内,使用测试数据,否则为空
            const testValue = (i < testData.length && j < testData[i].length) ? testData[i][j] : '';
            row.push({ value: testValue });
         }
         this.tableData.push(row);
      }
   }

   build() {
      Column() {
         // 标题栏
         Row() {

            Text('Excel生成器')
               .fontSize(20)
               .fontWeight(FontWeight.Bold)
               .fontColor('#8B4513')

            Blank()
         }
         .width('100%')
            .padding({ left: 20, right: 20, top: 15, bottom: 15 })
            .backgroundColor('#F5F5F5')

         // 主要内容区域
         Scroll() {
            Column({ space: 20 }) {
               // 表格配置卡片
               this.buildConfigCard()

               // 表格标题卡片
               this.buildTitleCard()

               // 表格编辑卡片
               this.buildTableCard()

               // 生成按钮卡片
               this.buildGenerateCard()

               // 消息提示
               if (this.message) {
                  this.buildMessageCard()
               }

               // 文件操作卡片
               if (this.showFileActions) {
                  this.buildFileActionsCard()
               }
            }
            .padding({ left: 20, right: 20, top: 20, bottom: 30 })
               .width('100%')
         }
         .layoutWeight(1)
            .backgroundColor('#F8F8F8')
            .scrollable(ScrollDirection.Vertical)
            .scrollBar(BarState.Auto)
      }
      .width('100%')
         .height('100%')
         .backgroundColor('#F8F8F8')
   }

   @Builder
   buildConfigCard() {
      Column({ space: 15 }) {
         Row({ space: 10 }) {
            Text('⚙️')
               .fontSize(24)
            Text('表格配置')
               .fontSize(18)
               .fontWeight(FontWeight.Medium)
               .fontColor('#8B4513')
         }
         .alignItems(VerticalAlign.Center)

         Row({ space: 20 }) {
            Column({ space: 8 }) {
               Text('行数')
                  .fontSize(14)
                  .fontColor('#666666')

               Row({ space: 10 }) {
                  Button('-')
                     .width(40)
                     .height(40)
                     .fontSize(18)
                     .backgroundColor('#F0F0F0')
                     .fontColor('#666666')
                     .borderRadius(8)
                     .enabled(this.rows > 1)
                     .onClick(() => {
                        if (this.rows > 1) {
                           this.rows--;
                           this.initializeTable();
                        }
                     })

                  Text(this.rows.toString())
                     .fontSize(16)
                     .fontWeight(FontWeight.Medium)
                     .width(40)
                     .textAlign(TextAlign.Center)

                  Button('+')
                     .width(40)
                     .height(40)
                     .fontSize(18)
                     .backgroundColor('#8B4513')
                     .fontColor('#FFFFFF')
                     .borderRadius(8)
                     .enabled(this.rows < 10)
                     .onClick(() => {
                        if (this.rows < 10) {
                           this.rows++;
                           this.initializeTable();
                        }
                     })
               }
            }
            .layoutWeight(1)

            Column({ space: 8 }) {
               Text('列数')
                  .fontSize(14)
                  .fontColor('#666666')

               Row({ space: 10 }) {
                  Button('-')
                     .width(40)
                     .height(40)
                     .fontSize(18)
                     .backgroundColor('#F0F0F0')
                     .fontColor('#666666')
                     .borderRadius(8)
                     .enabled(this.cols > 1)
                     .onClick(() => {
                        if (this.cols > 1) {
                           this.cols--;
                           this.initializeTable();
                        }
                     })

                  Text(this.cols.toString())
                     .fontSize(16)
                     .fontWeight(FontWeight.Medium)
                     .width(40)
                     .textAlign(TextAlign.Center)

                  Button('+')
                     .width(40)
                     .height(40)
                     .fontSize(18)
                     .backgroundColor('#8B4513')
                     .fontColor('#FFFFFF')
                     .borderRadius(8)
                     .enabled(this.cols < 10)
                     .onClick(() => {
                        if (this.cols < 10) {
                           this.cols++;
                           this.initializeTable();
                        }
                     })
               }
            }
            .layoutWeight(1)
         }
         .width('100%')
      }
      .width('100%')
         .padding(20)
         .backgroundColor('#FFFFFF')
         .borderRadius(12)
         .shadow({
            radius: 8,
            color: '#10000000',
            offsetX: 0,
            offsetY: 2
         })
   }

   @Builder
   buildTitleCard() {
      Column({ space: 15 }) {
         Row({ space: 10 }) {
            Text('📝')
               .fontSize(24)
            Text('表格标题')
               .fontSize(18)
               .fontWeight(FontWeight.Medium)
               .fontColor('#8B4513')
         }
         .alignItems(VerticalAlign.Center)

         TextInput({
            text: this.tableTitle,
            placeholder: '请输入表格标题...'
         })
            .fontSize(16)
            .backgroundColor('#F8F8F8')
            .borderRadius(8)
            .padding({ left: 15, right: 15 })
            .border({
               width: 1,
               color: '#E0E0E0'
            })
            .onChange((value: string) => {
               this.tableTitle = value;
            })
      }
      .width('100%')
         .padding(20)
         .backgroundColor('#FFFFFF')
         .borderRadius(12)
         .shadow({
            radius: 8,
            color: '#10000000',
            offsetX: 0,
            offsetY: 2
         })
   }

   @Builder
   buildTableCard() {
      Column({ space: 15 }) {
         Row({ space: 10 }) {
            Text('📊')
               .fontSize(24)
            Text('表格内容')
               .fontSize(18)
               .fontWeight(FontWeight.Medium)
               .fontColor('#8B4513')
         }
         .alignItems(VerticalAlign.Center)

         // 表格编辑区域
         Scroll() {
            Column({ space: 2 }) {
               ForEach(this.tableData, (row: ExcelCellData[], rowIndex: number) => {
                  Row({ space: 2 }) {
                     ForEach(row, (cell: ExcelCellData, colIndex: number) => {
                        TextInput({
                           text: cell.value,
                           placeholder: `R${rowIndex + 1}C${colIndex + 1}`
                        })
                           .fontSize(12)
                           .backgroundColor('#F8F8F8')
                           .borderRadius(4)
                           .padding({ left: 8, right: 8 })
                           .border({
                              width: 1,
                              color: '#E0E0E0'
                           })
                           .layoutWeight(1)
                           .height(40)
                           .onChange((value: string) => {
                              this.tableData[rowIndex][colIndex].value = value;
                           })
                     })
                  }
                  .width('100%')
               })
            }
         }
         .width('100%')
            .backgroundColor('#FFFFFF')
            .borderRadius(8)
            .padding(10)
            .border({
               width: 1,
               color: '#E0E0E0'
            })

         Text(`当前表格大小:${this.rows} 行 × ${this.cols} 列`)
            .fontSize(12)
            .fontColor('#999999')
      }
      .width('100%')
         .padding(20)
         .backgroundColor('#FFFFFF')
         .borderRadius(12)
         .shadow({
            radius: 8,
            color: '#10000000',
            offsetX: 0,
            offsetY: 2
         })
   }

   @Builder
   buildGenerateCard() {
      Column({ space: 15 }) {
         Row({ space: 10 }) {
            Text('💾')
               .fontSize(24)
            Text('生成Excel')
               .fontSize(18)
               .fontWeight(FontWeight.Medium)
               .fontColor('#8B4513')
         }
         .alignItems(VerticalAlign.Center)

         Text('点击下方按钮生成Excel文件,将自动弹出保存对话框')
            .fontSize(14)
            .fontColor('#666666')

         Button() {
            Row({ space: 10 }) {
               if (this.isGenerating) {
                  LoadingProgress()
                     .width(20)
                     .height(20)
                     .color('#FFFFFF')
               } else {
                  Text('📄')
                     .fontSize(20)
               }
               Text(this.isGenerating ? '生成中...' : '生成Excel文件')
                  .fontSize(16)
                  .fontColor('#FFFFFF')
                  .fontWeight(FontWeight.Medium)
            }
         }
         .width('100%')
            .height(50)
            .backgroundColor('#8B4513')
            .borderRadius(12)
            .enabled(!this.isGenerating && this.tableTitle.trim() !== '')
            .onClick(() => {
               this.generateExcel();
            })

         if (this.tableTitle.trim() === '') {
            Text('请先输入表格标题')
               .fontSize(12)
               .fontColor('#FF6B6B')
         }
      }
      .width('100%')
         .padding(20)
         .backgroundColor('#FFFFFF')
         .borderRadius(12)
         .shadow({
            radius: 8,
            color: '#10000000',
            offsetX: 0,
            offsetY: 2
         })
   }

   @Builder
   buildMessageCard() {
      Row({ space: 10 }) {
         Text(this.messageType === 'success' ? '✅' : '❌')
            .fontSize(20)
         Text(this.message)
            .fontSize(14)
            .fontColor(this.messageType === 'success' ? '#27ae60' : '#FF6B6B')
            .layoutWeight(1)
      }
      .width('100%')
         .padding(15)
         .backgroundColor(this.messageType === 'success' ? '#F0FFF4' : '#FFF5F5')
         .borderRadius(8)
         .border({
            width: 1,
            color: this.messageType === 'success' ? '#90EE90' : '#FFE0E0'
         })
   }

   @Builder
   buildFileActionsCard() {
      Column({ space: 15 }) {
         Row({ space: 10 }) {
            Text('✅')
               .fontSize(24)
            Text('生成成功')
               .fontSize(18)
               .fontWeight(FontWeight.Medium)
               .fontColor('#27ae60')
         }
         .alignItems(VerticalAlign.Center)

         Text('Excel文件已成功生成并保存!您可以在保存的位置找到文件,支持在WPS Office、Microsoft Excel等应用中打开。')
            .fontSize(14)
            .fontColor('#666666')
            .textAlign(TextAlign.Center)

         Text('💡 提示:如需再次保存到其他位置,请重新点击生成按钮')
            .fontSize(12)
            .fontColor('#999999')
            .textAlign(TextAlign.Center)
      }
      .width('100%')
         .padding(20)
         .backgroundColor('#F0FFF4')
         .borderRadius(12)
         .border({
            width: 1,
            color: '#90EE90'
         })
   }

   // 生成Excel文件
   async generateExcel() {
      if (this.tableTitle.trim() === '') {
         this.showMessage('请输入表格标题', 'error');
         return;
      }

      if (!this.excelGenerator) {
         this.showMessage('Excel生成器未初始化', 'error');
         return;
      }

      try {
         this.isGenerating = true;
         this.message = '';
         this.showFileActions = false;

         // 构建表格数据
         const tableData: ExcelTableData = {
            title: this.tableTitle,
            data: this.tableData,
            // titleStyle: {
            //   fontWeight: 'bold',
            //   fontSize: 18,
            //   backgroundColor: '#4CAF50'
            // },
            // headerStyle: {
            //   fontWeight: 'bold',
            //   fontSize: 14,
            //   backgroundColor: '#E8F5E8'
            // },
            // dataStyle: {
            //   fontSize: 12
            // }
         }

         // 生成Excel文件
         const result = await this.excelGenerator?.generateExcel(tableData);

         if (result?.success) {
            this.showMessage(`Excel文件生成成功:${result.fileName}`, 'success');
            console.log(`Excel文件已保存到:${result.filePath}`);

            // 自动保存到本地
            if (result.filePath && result.fileName) {
               const saveResult = await this.excelGenerator?.saveToLocal(result.filePath, result.fileName);
               if (saveResult?.success) {
                  this.showFileActions = true;
                  this.showMessage(saveResult.message, 'success');
               } else {
                  this.showMessage(saveResult.message, 'error');
               }
            }
         } else {
            this.showMessage(result?.message, 'error');
         }

      } catch (error) {
         console.error('生成Excel文件失败:', error);
         this.showMessage(`生成失败:${error.message}`, 'error');
      } finally {
         this.isGenerating = false;
      }
   }

   // 显示消息
   showMessage(msg: string, type: 'success' | 'error') {
      this.message = msg;
      this.messageType = type;

      // 3秒后自动清除消息
      setTimeout(() => {
         this.message = '';
         this.messageType = '';
      }, 3000);
   }
}
点赞
收藏
评论区
推荐文章
Easter79 Easter79
3年前
vue+iview中的table表格导出excel表格
一、iveiw框架中table中有exportCsv()方法可以导出.csv后缀文件,类似于excel文件,但是并不是excel文件。二、实现table表格导出excel文件利用Blob.js和Export2Excel.js两个文件实现  1.Blob.js和Export2Excel.js文件    链接:https://pan.baid
Wesley13 Wesley13
3年前
java简单的导出Excel文件
特点列出List中要导入到Excel的列名中文名,然后就完事了。具体的看看main()你就知道有多简单了。利用POI,本工具类可以实现Excel导出和下载,话不多说,放码过来。 packagecom.fantong.ois.wms.utils;importorg.apache.poi.hssf.usermodel.
Wesley13 Wesley13
3年前
java实现 Excel 导入导出
日常工作中,Excel是我们经常需要处理的文件,报表的生成,数据的导出,几乎每个项目都需要写对应的处理.作者也是编写这块代码大军的一员,能否有方法让我们不用重复编写代码呢,能否只要简单配置就可以完成我们的Excel生成呢,作者分析了Excel和对象的关系,发现Row就是我们的一个对象cell是我们的一个属性,从而开发了Easypoi,下面讲解下用
Stella981 Stella981
3年前
HuTool工具类使用之Excel文档的导入导出
HuTool工具类使用之Excel文档的导入导出前言在日常的工作开发中,Excel的导入和导出是必不可少的,如果自己写相应的导入导出方法,会显得十分繁琐,本文采用Hutool工具类实现的Excel导入导出功能,可以大幅度减少今后开发中Excel的导入导出的相关操作。
Stella981 Stella981
3年前
PHP导入导出EXCELl,CSV
PHP导入导出Excel,CSVHTML<formaction"{:U('Admin/Unit/importcsv')}"method"post"name"myform"id"myform"enctype"multipart/formdata"<input
Wesley13 Wesley13
3年前
JAVA导出EXCEL表格
hi~好久不见小伙伴们,最近比较忙,没什么时间写,今天给大家分享的是JAVA如何导出EXCEL表格,因为最近有做这样一个功能,所以分享出来,如有不对之处,敬请指正。在许多企业办公系统中,经常会有用户要求,需要对数据进行统计并且可以直接下载Excel文件,这样子的话,既然客户提出了要求,我们就应该去满足吖,毕竟客户是上帝嘛,那么我们如何去实现呢?且看我
Stella981 Stella981
3年前
EasyExcel写入百万级数据到多sheet
EasyExcel是什么?快速、简单避免OOM的java处理Excel工具一、项目需求    从mongo库中查询数据,导出到excel文件中。但是动态导出的excel有多少列、列名是什么、有多少sheet页都需要动态获取。所以生成的excel也必须是动态生成,不能通过注解配置对象映射。而且写入的数据量,有可能达到100W级,使用传统的PO
Stella981 Stella981
3年前
Excel导出(适合初学者)
这里有两个版本\,第一个是我自己写的一个导入导出,另一个是不知道复制哪位大佬的,这已经不重要了,重点是自己看懂会用就可以Excel导出步骤:生成Excel文档新建Sheet新建Row新建Cell单元格写单元格数据版本一:这个适合刚开始学习Excel导出,代码简洁易懂package
LeeFJ LeeFJ
2年前
Foxnic-SQL (15) —— 使用记录集导入或导出Excel
很多时候,我们需要将外部Excel表中的数据导入到数据库,或是需要将某个查询结果导出到Excel文件中,对于这种简单的操作,FoxnicSQL已经内置了ExcelReader和ExcelWriter用于处理Excel数据。本文中的示例代码均可在https://gitee.com/LeeFJ/foxnicsamples项目中找到。读取Excel到RcdSetFoxnicSQL使用ExcelReader类读取Excel中某个sheet的数据,这些数据将被读取到RcdSet,通过RcdSet可以完成数据库保存等操作。在读取Excel前需要定义Excel结构,将Excel列映射到数据库字段,如下代码所示。一旦Excel数据转换成RcdSet,开发人员就可以去做其它更多额外的数据处理。
京东云开发者 京东云开发者
12个月前
记录一次使用easypoi时与源码博弈的过程
一、背景介绍最近刚刚接手了保险一线之声平台的开发和维护工作,第一个需要修复的问题是:平台的事件导出成excel功能在经过一次上线之后突然不可用了,于是就开始了几轮痛苦的排查以及与源码博弈的过程。二、问题描述一线之声在事件查询菜单下支持将结果导出为Excel