前端使用xlsx-js-style实现Excel导出

210 阅读3分钟

哈喽,大家好,不知不觉从24届毕业大半年了,好久没更新(水文)了,今天趁着周五摸鱼的时间,再水一篇,啊呸,是写一篇水文。。好文。。

作为一名能摸鱼就摸鱼的切图仔,最近对接了一个比我还会摸鱼的后端,实属让我受教了,他说他的理念是:能不干的事,少干;能前端干的事,就让前端干。

  • 那天,我满怀希望的去找他,我说:“哥,我的好大哥,能麻烦你帮我做一件事吗?”。(星星眼🤩)
  • 他表情一凝,目不转睛的盯着数据库表,故作深沉的说:“什么事?”。
  • 我心中一沉,心想:完了,看来这件事悬了。
  • 不过我还是,把脸凑过去,期待地说到:“这个反馈历史接口的能帮我存个字段吗?一个反馈人的name,这样我处理方便一点”。(星星眼🤩)
  • “不是已经有userId了吗?你去请求用户体系接口拿到列表后一一配对不就行了吗?不需要存”
  • 好吧,我的脸已经垮了🤡,现在列表数据不多,到时候数据量大了起来,看吧,呵呵,再见

日常相爱相杀线 -----------------------------------------------------------------日常相爱相杀线

进入正题,今天我来找他了,“哥,能帮我做个导出Excel的接口吗?” “不能”,“好的,再见,睡好哦您” 话说后端不给我们做导出Excel功能,那前端仔怎么做呢?很简单的😎,使用xlsx-js-style就行了(这里得说下,感谢开源库)xlsx-js-style相较于xlsx,它很好地处理样式问题,只需npm i xlsx-js-style就行,代码如下:

import { message, TableProps } from 'antd';
import { ColumnTitle } from 'antd/es/table/interface';
import * as XLSX from 'xlsx-js-style';
export type DataLine = Array<Array<any>> // 表格内容可以包含任意类型内容
export type HeaderLine = Array<string>
type WorksheetData = [HeaderLine, ...DataLine];

/**
 * 
 * @param headerLine excel标题行
 * @param dataLine excel数据行
 * @param filename excel文件名
 */
export const handleExport = (headerLine: HeaderLine, dataLine: DataLine, filename: string) => {
    if (headerLine.length > 0 && dataLine.length > 0) {
        // 将标题行与数据拼接
        const worksheetData: WorksheetData = [headerLine, ...dataLine];
        // 创建工作表
        const ws = XLSX.utils.aoa_to_sheet(worksheetData);
        // 设置单元格样式 - 居中对齐
        const centerStyle = {
            alignment: {
                horizontal: 'center',
                vertical: 'center',
            },
        };
        // 设置标题行样式 - 加粗,居中对齐
        const headerStyle = {
            font: {
                bold: true,
            },
            alignment: {
                horizontal: 'center',
                vertical: 'center',
            },
        };
        // 应用样式到所有单元格
        const range = XLSX.utils.decode_range(ws['!ref']);
        for (let R = range.s.r; R <= range.e.r; R++) {
            for (let C = range.s.c; C <= range.e.c; C++) {
                const cellAddress = XLSX.utils.encode_cell({ r: R, c: C });
                if (!ws[cellAddress]) continue;
                ws[cellAddress].s = R === 0 ? headerStyle : centerStyle; // 标题行应用 headerStyle,其余行应用 centerStyle
            }
        }
        // 创建工作簿并添加工作表
        const wb = XLSX.utils.book_new();
        XLSX.utils.book_append_sheet(wb, ws, filename);
        // 导出 Excel 文件
        XLSX.writeFile(wb, `${filename}.xlsx`);
    } else {
        message.error('没有数据,无法导出')
    }
};

/**
 * 
 * @param columns 表格的columns
 * @returns 生成导出标题行
 */
export const generateHeader = <T>(columns: TableProps<T>['columns'] = []): ColumnTitle<T>[] => {
    return columns.map(c => c.title);
};

/**
 * 
 * @param data 传入表格数据
 * @param keys 表格数据的字段key集合
 * @returns 返回excel的数据行
 */
export const DeriveData = <T extends object>(data: T[], keys: (keyof T)[]) => {
    const dataSource = data.map((item, idx) => {
        return [
            idx + 1, // 序号
            ...keys.map(key => item[key])
        ];
    });
    return dataSource;
};

ps:以上内容纯属虚构,目的为了吸引人增加阅读量,遭遇如有雷同,那为你默哀一分钟,阿门😇🤡