egg 生成Excel

项目中因为某些特定环境导致 不能前端去生成Excel只能通过后端接口去做 网上找了很大示例 总算是写出来了 趁着空闲来记录下

egg项目搭建我这里就不说了,再提一下,我这里使用的是ts版本的

我这边使用了某个npm插件
npm install exceljs
安装好后就可以用了 下面我来写下具体示例

公共方法

app/server/common.ts

import { Service } from 'egg';
const Excel = require('exceljs');

export default class Test extends Service {

  /**
   * 数据并生成excel
   * @param {Array} headers excel标题栏
   * @param {Array} param 数据参数
   * @param {string} name 文件名称
   */
  public async excelCommon(headers:any, data:any, name: string,) {
    let columns:any = [];// exceljs要求的columns
    let hjRow = {};// 合计行
    let titleRows = headers.length;// 标题栏行数

    // 处理表头
    for (let i = 0; i < titleRows; i++) {
        let row = headers[i];
        for (let j = 0, rlen = row.length; j < rlen; j++) {
            let col = row[j];
            let { f, t, w = 15 } = col;
            if (!f) continue;// 不存在f则跳过

            if (col.totalRow) hjRow[f] = true;
            if (col.totalRowText) hjRow[f] = col.totalRowText;
            col.style = { alignment: { vertical: 'middle', horizontal: 'center' } };
            col.header = t;
            col.key = f;
            col.width = w;
            columns.push(col);
        }
    }


    let workbook = new Excel.Workbook();
    let sheet = workbook.addWorksheet('My Sheet', { views: [{ xSplit: 1, ySplit: 1 }] });
    sheet.columns = columns;
    sheet.addRows(data);

    // 处理复杂表头
    if (titleRows > 1) {
        for (let i = 1; i < titleRows; i++)  sheet.spliceRows(1, 0, []);// 头部插入空行

        for (let i = 0; i < titleRows; i++) {
            let row = headers[i];
            for (let j = 0, rlen = row.length; j < rlen; j++) {
                let col = row[j];
                if (!col.m1) continue;

                sheet.getCell(col.m1).value = col.t;
                sheet.mergeCells(col.m1 + ":" + col.m2);
            }
        }
    }

    // 处理样式、日期、字典项
    sheet.eachRow(function (row, rowNumber) {
        // 设置行高
        row.height = 25;

        row.eachCell({ includeEmpty: true },  (cell)=> {
              // 设置边框 黑色 细实线
              const top:any = { style: 'thin', color: { argb: '000000' } };
              const left:any = { style: 'thin', color: { argb: '000000' } };
              const bottom:any = { style: 'thin', color: { argb: '000000' } };
              const right:any = { style: 'thin', color: { argb: '000000' } };
              cell.border = { top, left, bottom, right };

              // 设置标题部分为粗体
              if (rowNumber <= titleRows) { cell.font = { bold: true }; return; }
          });
      });


      this.ctx.set('Content-Type', 'application/vnd.openxmlformats');
      // 这个地方的空格不要更改
      this.ctx.set('Content-Disposition', "attachment;filename*=UTF-8' '" + encodeURIComponent(name) + '.xlsx');
      this.ctx.body = await workbook.xlsx.writeBuffer();
    }
}

控制器方法调用

app/controller/home.ts

import { Controller } from 'egg';


export default class HomeController extends Controller {
  public async index() {
      const { ctx } = this;
        let headers = [
          [ 
              { t: '下单时间', f: 'trade_time', totalRow: true },
              { t: '订单类型', f: 'order_type', totalRow: true },
              { t: '手机号码', f: 'phone_number', totalRow: true },
              { t: '扫描状态', f: 'scan_status', totalRow: true },
              { t: '交易状态', f: 'ctf_order_status', totalRow: true },
              { t: '订单份额(克)', f: 'trade_share', totalRow: true },
              { t: '当时账号总份额(克)', f: 'account_share', totalRow: true },
              { t: '订单号', f: 'order_no', totalRow: true },
          ]
        ];
        let data = [
            { trade_time: '2020-12-10', order_type:'线上', phone_number:'18374009921', scan_status:'1', ctf_order_status:'1', trade_share:'2', account_share:'2', order_no:'164656456546' },
            { trade_time: '2020-12-10', order_type:'线下', phone_number:'18374009921', scan_status:'1', ctf_order_status:'1', trade_share:'2', account_share:'2', order_no:'164656456546' }
        ] // 需要在这边自己适配数据,这边为空

    await ctx.service.common.excelCommon(headers, data, '订单信息');
  }  
}

路由

import { Application } from 'egg';

export default (app: Application) => {
  const { controller, router } = app;

  router.get('/Execl', controller.home.index);
};

这样就可以生成了 直接拿着接口去浏览器输出就好了


image.png

这里借鉴了这个大佬的 https://github.com/Wuwei9536/egg-exceljs

https://github.com/exceljs/exceljs/blob/HEAD/README_zh.md#add-rows 中文

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355

推荐阅读更多精彩内容