使用exceljs按照ant-table模版导入到excel表格(简略版-不带图片)

前言

平时可能会有导出到 excel 的需求,这里面直接使用 exceljs 仓库来实现导出功能,一般都是table表格导出,这里面主要介绍怎么通过数据 + antd-table column导出到 excel,图片就先不加入了,自己可以根据需要自己在了解一下即可

exceljs 地址

exceljs简介

第一步肯定是导入我们的库了

js 复制代码
const exceljs = require('exceljs');

声明一个 excel 对象,可以理解为 excel 文件本身,添加一个 worksheet,这个应该理解,每一个 excel 会有多个 sheet 表格,我们写入到其中一个就行,名字自己看着定义

创建表格、sheet

js 复制代码
const workbook = new exceljs.Workbook();
workbook.creator = '帅';
workbook.created = new Date();
// 添加工作表
const worksheet = workbook.addWorksheet('sheet1');

紧接着我们网其中一个 sheet 中添加数据就对了,多个 sheet 逻辑和单个类似,看自己需求了往后写哈,就是多几个 sheet 罢了

创建单元格标题栏(单页表头)

excel 表格样式应该都见过,是一个二维表格,长的跟 table 类似,只不过他们的表头也是单元格的一部分罢了(无论是横向还是纵向,这边以正常表格,也就是表头再上为模版开发,争取跟 table 结构一致)

我们的表头结构为 header、key、width 分别是 名称、键值、宽度,跟 table 可以说很像了

js 复制代码
const headerColumns: any[] = [];
columns.forEach((item, index) => {
    headerColumns.push({
        header: item.title,
        key: item.dataIndex,
        width: item.width ? item.width / 10 : 40, 这个像素个人尝试 / 10 差不多哈
    });
});
worksheet.columns = headerColumns;

创建单元格内容

每一行都是一个表格对象,遍历数据,将我们的数据,每组按照 key 分别放到一个对象中,每个对象就是一行,key就是列,就这样生成表格内容了

js 复制代码
const data: any[] = [];
datasource.forEach((item, index) => {
    const obj: any = {};
    columns.forEach((e, idx) => {
        const info = generateColumnKey(e, idx, item);
        obj[item.dataIndex] = item[item.dataIndex]
    });
    data.push(obj);
});
worksheet.addRows(data);

添加样式

就直说一个,我们的 第一行第一列是是索引行列,第二行是我们的标题,后面的才是我们的数据

这边以一个标题为例,获取其所在的row,然后遍历设置样式即可

js 复制代码
worksheet
    .getRow(1)
    .eachCell({ includeEmpty: true }, (cell: any, colNumber: number) => {
        cell.fill = {
            type: 'pattern',
            pattern: 'solid',
            fgColor: {
                argb: 'FFDEE6F0',
            },
        };
        cell.font = {
            size: 14,
            color: { argb: 'FF000000' }, // 黑色
            bold: true,
        };
        cell.border = {
            top: { style: 'thin' },
            left: { style: 'thin' },
            bottom: { style: 'thin' },
            right: { style: 'thin' },
        };
        cell.alignment = {
            horizontal: columns[colNumber - 1].align ?? 'left',
            vertical: 'middle',
        };
    })

实现导出功能

实际上的导出肯定跟我们想象的不一样,肯定要复杂不少,table里面又实用转化的,也就是不直接使用 dataIndex 的,那么我们可以声明一下 transform 方法接受他们的 render、renderText(前提是哪里不写 dom 节点哈,写了就没办法,转化方法只能自己再写了)

涉及到转化(render)的我们的 key 会发生变化,无法获取到内容,可以自己按照 规则声明一个唯一值即可(比如index,同一列所在索引是一样的),对于嵌套的,我们使用 reduce 取值即可,其他的直接获取内容即可,注意需要保证有值

当然最建议的是直接将 dto 转化成 vo,在一个对象,这样就不存在转化了,这里代码也会少一点,不过对于前端来说,一页就那么多数据,什么时候转化都一样,这就不强求了,如果条数非常多,那么就少点样式、提前加工好数据这样能提升一下效率

下面就是实现逻辑,自己尝试了一下,很不错

js 复制代码
export type ExcelTableColumns = {
    title: string;
    dataIndex: string | string[];
    width?: number; //实际算是文字长度了,英文1个多一点,中文两个多一点最佳,默认按照像素数量 / 10,并预留出部分空间,一般为 12-16号字体之间,这个略长一点
    align?: 'left' | 'right' | 'center';
    transform?: (value: any, record: any, index: number) => string | number; //render或者renderText,注意返回对象
};

function generateColumnKey(
    column: any,
    index: number,
    data?: any
): { key: string; value: any } {
    const info = {
        key: '',
        value: '',
    };
    if (column.transform) {
        info.key = `___transform__` + index;
        return info;
    }
    if (typeof column.dataIndex === 'string') {
        info.key = column.dataIndex;
        if (data) {
            info.value = data[column.dataIndex];
        }
    } else if (Array.isArray(column.dataIndex)) {
        info.key = column.dataIndex!.join('_');
        if (data) {
            info.value = column.dataIndex.reduce((pre: any, itm: any) => {
                return pre ? pre[itm] : '';
            }, data);
        }
    } else {
        info.key = column.dataIndex.toString();
    }
    return info;
}

//基本导出功能
export const exportExcelByTable = async (
    columns: ExcelTableColumns[],
    datasource: any[],
    filename?: string
) => {
    if (!datasource) {
        throw new Error('没有数据');
    }
    columns = columns.filter((e) => e.title && (e.transform || e.dataIndex));
    const exceljs = require('exceljs');
    const workbook = new exceljs.Workbook();
    workbook.creator = '卓朗天工';
    workbook.created = new Date();
    // 添加工作表
    const worksheet = workbook.addWorksheet('sheet1');
    const headerColumns: any[] = [];
    columns.forEach((item, index) => {
        const info = generateColumnKey(item, index);
        headerColumns.push({
            header: item.title,
            key: info.key,
            width: item.width ? item.width / 10 : 40,
        });
    });
    worksheet.columns = headerColumns;
    const data: any[] = [];
    datasource.forEach((item, index) => {
        const obj: any = {};
        columns.forEach((e, idx) => {
            const info = generateColumnKey(e, idx, item);
            obj[info.key] = e.transform
                ? e.transform(info.value, item, index)
                : info.value;
        });
        data.push(obj);
    });
    worksheet.addRows(data);
    worksheet
        .getRow(1)
        .eachCell({ includeEmpty: true }, (cell: any, colNumber: number) => {
            cell.fill = {
                type: 'pattern',
                pattern: 'solid',
                fgColor: {
                    argb: 'FFDEE6F0',
                },
            };
            cell.font = {
                size: 14,
                color: { argb: 'FF000000' }, // 黑色
                bold: true,
            };
            cell.border = {
                top: { style: 'thin' },
                left: { style: 'thin' },
                bottom: { style: 'thin' },
                right: { style: 'thin' },
            };
            cell.alignment = {
                horizontal: columns[colNumber - 1].align ?? 'left',
                vertical: 'middle',
            };
        });
    datasource.forEach((item, index) => {
        worksheet
            .getRow(index + 2)
            .eachCell(
                { includeEmpty: true },
                (cell: any, colNumber: number) => {
                    cell.fill = {
                        type: 'pattern',
                        pattern: 'solid',
                        fgColor: {
                            argb: 'FFFFFFFF',
                        },
                    };
                    cell.font = {
                        size: 12,
                        color: { argb: 'FF000000' }, // 黑色
                    };
                    cell.border = {
                        top: { style: 'thin' },
                        left: { style: 'thin' },
                        bottom: { style: 'thin' },
                        right: { style: 'thin' },
                    };
                    cell.alignment = {
                        horizontal: columns[colNumber - 1].align ?? 'left',
                        vertical: 'middle',
                    };
                }
            );
    });
    const buffer = await workbook.xlsx.writeBuffer();
    saveFile(buffer, filename);//文件导出
};

导出到文件功能,通过 a 标签实现下载

js 复制代码
const saveFile = (data: any, filename?: string) => {
    const blob = new Blob([data], {
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8',
    }) as any;
    const url = URL.createObjectURL(blob);
    const aLink = document.createElement('a');
    aLink.setAttribute(
        'download',
        filename ? filename : `${new Date().getTime()}.xlsx`
    );
    aLink.setAttribute('href', url);
    document.body.appendChild(aLink);
    aLink.click();
    document.body.removeChild(aLink);
    URL.revokeObjectURL(blob);
};

从我们的 table-column 到 excel 可能还需要一些操作,我们直接在转化一下吧,例如前面加个索引,还有类型不一样,加个类型强转

js 复制代码
export const exportExcelByTableCustom = async (
    columns: Record<string, any>[],
    datasource: any[],
    filename: string,
    no?: boolean | number
) => {
    const cols = columns.map((e: any) => {
        const transform = e.render
            ? e.render
            : e.renderText
              ? e.renderText
              : undefined;
        return {
            title: e.title,
            dataIndex: e.dataIndex,
            width: e.width ?? 300,
            align: e.align,
            transform,
        } as ExcelTableColumns;
    });
    no &&
        cols.unshift({
            title: 'No.',
            width: no === true ? 60 : no,
            align: 'center',
            transform: (_, __, index: number) => index + 1,
        } as ExcelTableColumns);
    await exportExcelByTable(cols, datasource, filename);
};

到上面一步基本可以直接用了

最后

如果有复用要求,可以写一个带接口的,如果接口分页处理,那么一页一页遍历处理的可以直接写好,如果是一次获取所有的,那就更简单了哈

相关推荐
图片转成excel表格9 小时前
Excel中快速计算服务天数的操作指导
excel
ruleslol9 小时前
VBA10-处理Excel的动态数据区域
excel·vba
颖火虫-IT赵云11 小时前
json转excel,读取json文件写入到excel中【rust语言】
rust·json·excel·jsontoexcel
hard_coding_wang14 小时前
AI生活之我用AI处理Excel表格
人工智能·excel
恋661 天前
关于Excel的操作,数据转换
java·excel
疯狂学习GIS1 天前
批量从Excel某一列中找到符合要求的值并提取其对应数据
excel·表格·office·表格文件·数据查询·vlookup·查询函数
小奥超人1 天前
密码忘记了,如何取消excel编辑限制?
windows·经验分享·microsoft·excel·办公技巧
觅远1 天前
python-读写Excel:openpyxl-(4)下拉选项设置
前端·python·excel
平头哥在等你2 天前
Excel快捷键大全
excel