前端开发-如何生成并导出xlsx或者csv文件

原创文章
声明:作者声明此文章为原创,未经作者同意,请勿转载,若转载,务必注明本站出处,本平台保留追究侵权法律责任的权利。
全栈老韩
全栈工程师,擅长iOS App开发、前端(vue、react、nuxt、小程序&Taro)开发、Flutter、React Native、后端(midwayjs、golang、express、koa)开发、docker容器、seo优化等。

在前端开发中,尤其是一些管理后台的开发项目中,经常有需要生成并导出表格的场景。最常见的表格格式莫过于xlsx和csv这两种格式的excel文件。

这种场景有两种实践:

  1. 服务端生成了excel表格,将文件uri网络地址通过接口传给前端,前端使用window.location.href或者window.open等方法使用跳转下载这个uri对应的表格文件。
  2. 前端根据已有的数据,自行生成excel表格,实现自动下载文件。

第1个实践对于前端来说是比较方便的,今天我们介绍第2个实践。介绍的包含2种格式(xlsx、csv)的文件生成和下载,推荐xlsx格式的excel。

一、xlsx格式excel文件生成和下载

这种方式通过使用前端库xlsx来实现:

package.json 复制代码
...
"dependencies": {
  ...
  "xlsx": "^0.18.5",
  ...
}
...

封装代码:

xlsxFileHandle.ts 复制代码
import * as XLSX from 'xlsx';

export interface excelType {
  json: object;
  name: string;
  titleArr: string[];
  sheetName: string;
}

/**
 * 导出成xlsx文件
 * @param params 传入的数据格式
 */
export const exportExcel = (params: excelType) => {
    const keyArray = [];
    let data = [];
    const getLength = function (obj: object) {
        let count = 0;
        for (const i in obj) {
          if (Object.prototype.hasOwnProperty.call(obj, i)) {
            count++;
          }
        }
        return count;
    };
	
    for (const key1 in params.json) {
      if (Object.prototype.hasOwnProperty.call(params.json, key1)) {
        const element = (params.json as { [key: string]: object })[key1];
        const rowDataArray = [];
        for (const key2 in element) {
          if (Object.prototype.hasOwnProperty.call(element, key2)) {
            const element2 = (element as { [key: string]: object })[key2];
            rowDataArray.push(element2);
            if (keyArray.length < getLength(element)) {
              keyArray.push(key2);
            }
          }
        }
        data.push(rowDataArray);
      }
    }
    data.splice(0, 0, params.titleArr as any);
    data = data.filter(e => e.length > 0)

    console.log(data);
    let ws = XLSX.utils.aoa_to_sheet(data);
    let wb = XLSX.utils.book_new();
    // 隐藏英文字段表头
    const wsrows = [{ hidden: false }];
      /* 设置worksheet每列的最大宽度 */
      const colWidth = data.filter(e => e.length > 0).map((row) =>
        row.map((val) => {
          /* 先判断是否为null/undefined */
          if (val == null) {
            return {
              wch: 20,
            };
          } else if (val.toString().charCodeAt(0) > 255) {
            /* 再判断是否为中文 */
            return {
              wch: val.toString().length * 2,
            };
          } else {
            return {
              wch: val.toString().length*2,
            };
          }
        })
      );
      /* 以第一行为初始值 */
      const result = colWidth[1];
      for (let i = 1; i < colWidth.length; i++) {
        for (let j = 0; j < colWidth[i].length; j++) {
          if (result[j].wch < colWidth[i][j].wch) {
            result[j].wch = colWidth[i][j].wch;
          }
        }
      }
      ws['!cols'] = result;
      ws['!rows'] = wsrows; // ws - worksheet
      XLSX.utils.book_append_sheet(wb, ws, params.sheetName);
      /* generate file and send to client */
      XLSX.writeFile(wb, `${params.name}.xlsx`);
};

使用实例代码:

payExport.ts 复制代码
import { exportExcel } from "../xlsxFileHandle"

/**
 * 导出成xlsx文件
 */
export const ExportForXlsx = (batch_id: number, valueList: Array<xxxListItem>) => {
    const titleList = [
        "ID",
        "订单ID",
        "姓",
        "名",
        "收款人名字",
        "电话",
        "账号号码",
        "银行名字",
        "账号名字",
        "收款金额",
        "选填,附言",
        "扣款金额",
        "汇率",
        "手续费$",
        "创建时间",
        "完成时间",
        "订单状态",
    ]
    const getStatusDesc = (status: number) => {
        if (status == 1) {
            return '待付款'
        }
        if (status == 2) {
            return '失败'
        }
        if (status == 3) {
            return '成功'
        }
        if (status == 4) {
            return '处理中'
        }
        if (status == 5) {
            return '失败-系统'
        }
        return '未知'
    }
    const newValueList = valueList.map(e => {
        return {
            "ID" : e.payee_id + '',
            "订单ID": e.order_id + '',
            "姓": e.last_name + '',
            "名": e.first_name + '',
            "收款人名字": e.payee_name + '',
            "电话": e.phone_number + '',
            "账号号码": e.account_id + '',
            "银行名字": e.back_name + '',
            "账号名字": e.bank_account_number + '',
            "收款金额": e.receive_amount + '',
            "选填,附言": e.remark + '',
            "扣款金额": e.buckle_amount + '',
            "汇率": e.exchange_rate + '',
            "手续费$": e.handling_fee + '',
            "创建时间": e.create_time + '',
            "完成时间": (e.complete_time > 0 ? moment(e.complete_time).format('YYYY-MM-DD HH:mm:ss') : e.complete_time) + '',
            "订单状态": getStatusDesc(e.status) + ''
        }
    })
    const tableName = '支付-' + batch_id //表名
    exportExcel({
        json: newValueList,//数据
        name: tableName, // 表名
        titleArr: titleList,//表头
        sheetName: batch_id + '',//页签
    })
}

以上方法得到的效果:
导出excel

二、csv格式excel文件生成和下载

不使用任何库,而是将数据合成字符串。
封装代码:

csvFile.ts 复制代码
/**
 * 导出成.csv格式的表格
 * @param titleList 比如:['ID', '姓名', '年龄']
 * @param valueList 比如:[ [1, '张三', 28], [2, '李四', 22] ]
 */
export const extractCsvFileFromData = (titleList: Array<string>, valueList: Array<any>) => {
    const totalList = [titleList, ...valueList]
    // 将数据转换为CSV格式的字符串  
    let csvContent = "data:text/csv;charset=utf-8,";  
    totalList.forEach((row, index) => {  
      if (index > 0) {  
        csvContent += "\n";  
      }  
      csvContent += row.join(",");  
    });  

    // 创建一个可下载的链接  
    const encodedUri = encodeURI(csvContent);  
    const link = document.createElement("a");  
    link.setAttribute("href", encodedUri);  
    link.setAttribute("download", "数据.csv");  
    document.body.appendChild(link); // 需要添加到body  
    link.click();
}

使用示例代码:

pay.ts 复制代码
import moment from "moment"

/**
 * 将明细列表,生成csv文件
 * @param valueList 明细列表数据
 */
export const GenerateCSVFileFrom = (valueList: Array<xxxListItem>) => {
    const titleList = [
        "ID",
        "订单ID",
        "姓",
        "名",
        "收款人名字",
        "电话",
        "账号号码",
        "银行名字",
        "账号名字",
        "收款金额",
        "选填,附言",
        "扣款金额",
        "汇率",
        "手续费$",
        "创建时间",
        "完成时间",
        "订单状态",
    ]

    const getStatusDesc = (status: number) => {
        if (status == 1) {
            return '待付款'
        }
        if (status == 2) {
            return '失败'
        }
        if (status == 3) {
            return '成功'
        }
        if (status == 4) {
            return '处理中'
        }
        if (status == 5) {
            return '失败-系统'
        }
        return '未知'
    }
    
    const newValueList = valueList.map(e => {
        return [
            e.payee_id + '', e.order_id + '', e.last_name + '', e.first_name + '',
            e.payee_name + '', e.phone_number + '', e.account_id + '', e.back_name + '',
            e.bank_account_number + '', e.receive_amount + '', e.remark + '', e.buckle_amount + '',
            e.exchange_rate + '', e.handling_fee + '', e.create_time + '', (e.complete_time > 0 ? moment(e.complete_time).utcOffset(-5).format('YYYY-MM-DD HH:mm:ss') : e.complete_time) + '',
            getStatusDesc(e.status) + ''
        ]
    })
   
    extractCsvFileFromData(titleList, newValueList)
}

以上就是在前端开发中,生成和导出xlsx和csv格式的excel文件的简单实践,希望对大家有帮助。

暂无评论,快来发表第一条评论吧