nestjs 导出excel

1、安装

npm install exceljs

2、封装导出函数

import * as ExcelJS from 'exceljs';
import { Response } from 'express';
import { DelFlagEnum, SexEnum, StatusEnum } from './base-enum';

/**
 * 
 * @param options 
 * data : 数据
 * header : 表头。
 * dictMap : 字典映射 主要用与将数据转为中文展示
 * sheetName : sheet名称
 * 
 * 参数示例:
·*  {
        sheetName: '调度日志',
        data: list.data.list,
        header: [
            { title: '日志编号', dataIndex: 'jobLogId' },
            { title: '任务名称', dataIndex: 'jobName' },
            { title: '任务组名', dataIndex: 'jobGroup' },
            { title: '调用目标字符串', dataIndex: 'invokeTarget' },
            { title: '日志信息', dataIndex: 'jobMessage' },
            { title: '执行时间', dataIndex: 'createTime',formateStr:(value)=>value.format('YYYY-MM-DD HH:mm:ss') },
        ],
        dictMap: {
            status: {
                '0': '成功',
                '1': '失败',
            },
            jobGroup: {
                SYSTEM: '系统',
                DEFAULT: '默认',
            },
        },
    }
 * @param res 
 */

/**
 * 通用枚举映射配置
 */
export const commonExportMap = {
    status: {
        [StatusEnum.NORMAL]: '正常',
        [StatusEnum.STOP]: '停用',
    },
    sex: {
        [SexEnum.MAN]: '男',
        [SexEnum.WOMAN]: '女',
    },
    delFlag: {
        [DelFlagEnum.NORMAL]: '正常',
        [DelFlagEnum.DELETE]: '已删除',
    }
};

//导出excel
export async function ExportExcel(
    options: {
        data: any[];
        header: any[];
        dictMap?: any;
        sheetName?: string;
    }, res: Response
) {

    let data = options.data;
    // 创建excel实例
    const workBook = new ExcelJS.Workbook();
    // 创建sheet
    const sheetName = options.sheetName || 'Sheet1';
    // 将sheet添加到工作簿中
    const worksheet = workBook.addWorksheet(sheetName);
    // 创建sheetName的列. 格式化表头数据
    worksheet.columns = options.header.map(item => ({
        header: item.title,
        key: item.key,
        width: item.width || 20,
    }));

    const dictMap = { ...options.dictMap, ...commonExportMap };

    data = data.map(item => {
        // 用来保存每一行的数据。对数据进行格式化、转换后返回
        const newItem = {}
        options.header.forEach(field => {
            // 通过头部的key 获取字段名称 用来下面去取数据上的值 同时也判断是否需要哦映射为中文
            const fieldName = field.key;
            // 获取字段值
            const dataValue = item[fieldName];
            if (dictMap[fieldName]) {
                newItem[fieldName] = dictMap[fieldName][dataValue] || dataValue;
            } else {
                newItem[fieldName] = dataValue;
            }
            if (field.formateStr && typeof field.formateStr == 'function') {
                newItem[fieldName] = field.formateStr(newItem[fieldName]);
            }
        });
        return newItem;
    });

    // 定义表头样式
    const headerStyle: any = {
        font: {
            size: 10,
            bold: true,
            color: { argb: 'ffffff' },
        },
        alignment: { vertical: 'middle', horizontal: 'center' },
        fill: {
            type: 'pattern',
            pattern: 'solid',
            fgColor: { argb: '808080' },
        },
        border: {
            top: { style: 'thin', color: { argb: '9e9e9e' } },
            left: { style: 'thin', color: { argb: '9e9e9e' } },
            bottom: { style: 'thin', color: { argb: '9e9e9e' } },
            right: { style: 'thin', color: { argb: '9e9e9e' } },
        },
    };

    const headerRow = worksheet.getRow(1);
    headerRow.eachCell((cell) => {
        cell.style = headerStyle;
    });

    worksheet.columns.forEach((column) => {
        column.alignment = { vertical: 'middle', horizontal: 'center' };
    });

    worksheet.addRows(data);

    const buffer = await workBook.xlsx.writeBuffer();
    res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    res.setHeader('Content-Disposition', 'attachment;filename=sheet.xlsx');
    res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
    res.setHeader('X-Content-Type-Options', 'nosniff');
    // 将 Excel 文件的二进制流数据返回给客户端
    res.end(buffer, 'binary');

}

3、调用

// 控制器。记得必须要有 @Res()
@Post("/exportExcel")
exportExcel(@Body() body: ListUserDto, @Res() res: Response) {
    return this.userService.exportExcel(body, res)
}


//service
async exportExcel(body: ListUserDto, res: Response) {
    const options = {
        data: await this.userRepository.find({
            where: {
                ...whereFilter(body, {
                    excludeFields: ['userName']
                }),
                userName: Like(`%${body.userName || ''}%`)
            }
        }),
        header: [
            {
                key: 'userName',
                title: '用户名'
            },
            {
                key: 'nickName',
                title: '昵称'
            },
            {
                key: 'email',
                title: '邮箱'
            },
            {
                key: 'phoneNumber',
                title: '手机号'
            },
        ]
    }
    return await ExportExcel(options, res)
}

4、前端处理

// 对返回的流进行处理 然后下载
exportUserExcel().then(res => {
    const time = dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss")
    let url = window.URL.createObjectURL(new Blob([res as any]))
    let link = document.createElement('a')
    link.style.display = 'none'
    link.href = url
    link.setAttribute('download', `用户列表${time}.xls`)
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link); //下载完成移除元素
    window.URL.revokeObjectURL(url); //释放掉blob对象
    message.success("下载完成")
})



// 要记得添加 responseType 为 'blob'
export const exportUserExcel = () => {
    return request<any>({
        url: api.exportUserExcel,
        method: "POST",
        responseType: "blob"
    })
}


// 响应拦截器过滤一下  response.config.responseType != 'blob'
instance.interceptors.response.use((response) => {
    console.log(response)
    if (response.data && response.data.code != 200 && response.config.responseType != 'blob') {
        message.error(response.data.message)
    }
    return response.data;
}, error => {
    message.error(error.message)
    return Promise.reject(error);
});

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值