前端文件导出

140 阅读13分钟

分享一下 xlsx 这个库的常见使用场景,仅代表个人使用习惯,不喜勿喷

一、useExcel Hook封装

import { getTypeOf, isAvailableArr } from '@/utils'
import { message } from 'antd'
import type {
  BookType,
  ColInfo,
  Range,
  RowInfo,
  Sheet2JSONOpts,
  WorkBook,
} from 'xlsx'
import * as XLSX from 'xlsx'

// 导出文件配置
export type IExportConfig = {
  name?: string // 导出文件名称
  bookType?: BookType // 导出文件类型
  sheetName?: string // sheet名称
  errorMsg?: string // 错误提示
  headers?: Record<string, string> // 自定义表头,导出的文件里面只有在定义中的字段,并且如果数据为空的话,只生成一个表头。示例:{id: 'ID', name: '链接名称', site_type: '官网类型'}
  merges?: Range[] // 单元格合并
  colInfo?: ColInfo[] // 列属性
  rowInfo?: RowInfo[] // 行属性
}

// 多sheet导出
export type IExtraSheetConfig = {
  name?: string // 导出文件名称
  bookType?: BookType // 导出文件类型
  sheets: ({ json: any[] } & Omit<IExportConfig, 'name' | 'bookType'>)[]
}

/**
 * @desc 自定义导出文件hook
 */
const useExcel = () => {
  /**
   * @desc 一维数组导出Excel文件
   *
   * 此函数将一个一维数组转换为Excel文件并下载
   * 支持自定义表头、单元格合并、列属性和行属性
   *
   * @param {any[]} json - 要导出的数据数组
   * @param {IExportConfig} config - 导出配置项
   * @param {string} [config.name='导出'] - 导出文件名称
   * @param {BookType} [config.bookType='xlsx'] - 导出文件类型
   * @param {string} [config.sheetName='Sheet1'] - 工作表名称
   * @param {Record<string, string>} [config.headers] - 自定义表头映射
   * @param {Range[]} [config.merges] - 单元格合并范围
   * @param {ColInfo[]} [config.colInfo] - 列属性配置
   * @param {RowInfo[]} [config.rowInfo] - 行属性配置
   * @returns {void}
   */
  function exportJson2Excel<T = any>(json: T[], config?: IExportConfig) {
    const {
      name = '导出',
      sheetName = 'Sheet1',
      bookType = 'xlsx',
      headers,
      merges,
      colInfo,
      rowInfo,
    } = config || {}
    let lists = [...json]
    if (
      headers &&
      getTypeOf(headers) === 'Object' &&
      isAvailableArr(Object.keys(headers))
    ) {
      // 没有数据的时候用header去生成一个空表头
      if (!isAvailableArr(lists)) {
        const headersField = Object.values(headers)
        const headerObj: Record<string, any> = {}
        headersField.forEach((f) => {
          headerObj[f] = null
        })
        lists = [headerObj as T]
      } else {
        // 有数据的时候根据header去生成
        const headerFields = Object.entries(headers)
        lists = json.map((j) => {
          const obj: Record<string, any> = {}
          for (const [key, value] of headerFields) {
            obj[value] = j[key as keyof T] ?? null
          }
          return obj
        }) as T[]
      }
    }

    const wb = XLSX.utils.book_new()
    const ws = XLSX.utils.json_to_sheet(lists)
    if (merges) {
      ws['!merges'] = merges
    }
    if (colInfo) {
      ws['!cols'] = colInfo
    }
    if (rowInfo) {
      ws['!rows'] = rowInfo
    }
    XLSX.utils.book_append_sheet(wb, ws, sheetName)
    XLSX.writeFile(wb, `${name}.${bookType}`, { bookType })
  }

  /**
   * @desc 一维数组多sheet导出Excel文件
   *
   * 此函数将多个一维数组分别放在不同的工作表中导出为一个Excel文件
   * 支持自定义表头、单元格合并、列属性和行属性
   *
   * @param {IExtraSheetConfig} params - 多sheet导出配置项
   * @param {string} [params.name='导出'] - 导出文件名称
   * @param {BookType} [params.bookType='xlsx'] - 导出文件类型
   * @param {Array} params.sheets - 工作表配置数组
   * @param {any[]} params.sheets[].json - 要导出的数据数组
   * @param {string} [params.sheets[].sheetName] - 工作表名称
   * @param {Record<string, string>} [params.sheets[].headers] - 自定义表头映射
   * @param {Range[]} [params.sheets[].merges] - 单元格合并范围
   * @param {ColInfo[]} [params.sheets[].colInfo] - 列属性配置
   * @param {RowInfo[]} [params.sheets[].rowInfo] - 行属性配置
   * @returns {void}
   */
  function exportJson2ExcelSheets<T = any>(params: IExtraSheetConfig) {
    const { name = '导出', bookType = 'xlsx', sheets } = params || {}
    const wb = XLSX.utils.book_new()
    if (isAvailableArr(sheets)) {
      sheets?.forEach((s) => {
        const { json, headers, merges, colInfo, rowInfo, sheetName } = s || {}
        let lists = [...json]
        if (
          headers &&
          getTypeOf(headers) === 'Object' &&
          isAvailableArr(Object.keys(headers))
        ) {
          // 没有数据的时候用header去生成一个空表头
          if (!isAvailableArr(lists)) {
            const headersField = Object.values(headers)
            const headerObj: Record<string, any> = {}
            headersField.forEach((f) => {
              headerObj[f] = null
            })
            lists = [headerObj as T]
          } else {
            // 有数据的时候根据header去生成
            const headerFields = Object.entries(headers)
            lists = json.map((j) => {
              const obj: Record<string, any> = {}
              for (const [key, value] of headerFields) {
                obj[value] = j[key] ?? null
              }
              return obj
            }) as T[]
          }
        }

        const ws = XLSX.utils.json_to_sheet(lists)
        if (merges) {
          ws['!merges'] = merges
        }
        if (colInfo) {
          ws['!cols'] = colInfo
        }
        if (rowInfo) {
          ws['!rows'] = rowInfo
        }
        XLSX.utils.book_append_sheet(wb, ws, sheetName)
      })
    } else {
      const ws = XLSX.utils.json_to_sheet([])
      XLSX.utils.book_append_sheet(wb, ws)
    }

    XLSX.writeFile(wb, `${name}.${bookType}`, { bookType })
  }

  /**
   * @desc 二维数组导出Excel文件
   *
   * 此函数将一个二维数组转换为Excel文件并下载
   * 支持单元格合并、列属性和行属性
   *
   * @param {any[][]} aoas - 要导出的二维数组
   * @param {IExportConfig} config - 导出配置项
   * @param {string} [config.name='导出'] - 导出文件名称
   * @param {BookType} [config.bookType='xlsx'] - 导出文件类型
   * @param {string} [config.sheetName='Sheet1'] - 工作表名称
   * @param {Range[]} [config.merges] - 单元格合并范围
   * @param {ColInfo[]} [config.colInfo] - 列属性配置
   * @param {RowInfo[]} [config.rowInfo] - 行属性配置
   * @returns {void}
   */
  function exportAoa2Excel<T = any>(aoas: T[][], config?: IExportConfig) {
    const {
      name = '导出',
      sheetName = 'Sheet1',
      bookType = 'xlsx',
      merges,
      colInfo,
      rowInfo,
    } = config || {}

    const wb = XLSX.utils.book_new()
    const ws = XLSX.utils.aoa_to_sheet(aoas)
    if (merges) {
      ws['!merges'] = merges
    }
    if (colInfo) {
      ws['!cols'] = colInfo
    }
    if (rowInfo) {
      ws['!rows'] = rowInfo
    }

    XLSX.utils.book_append_sheet(wb, ws, sheetName)
    XLSX.writeFile(wb, `${name}.${bookType}`, { bookType })
  }

  /**
   * @desc 从本地文件读取Excel工作簿
   *
   * 此函数从File或Blob对象读取Excel文件并返回工作簿对象
   *
   * @param {File | Blob} file - 要读取的Excel文件
   * @returns {Promise<WorkBook | false>} 返回工作簿对象或false(读取失败时)
   */
  function readWorkbookFromLocalFile(
    file: File | Blob,
  ): Promise<WorkBook | false> {
    return new Promise((resolve) => {
      const reader = new FileReader()
      reader.readAsBinaryString(file)
      reader.onload = function (e) {
        const data = (e.target as any)?.result
        const workbook = XLSX.read(data, {
          type: 'binary',
          raw: true,
          cellNF: true,
        })
        resolve(workbook)
      }
      reader.onerror = function () {
        resolve(false)
      }
    })
  }

  /**
   * @desc 从本地Excel文件读取数据为JSON格式
   *
   * 此函数从File或Blob对象读取Excel文件并返回第一个工作表的数据为JSON数组
   *
   * @param {File | Blob} file - 要读取的Excel文件
   * @param {Sheet2JSONOpts} [options] - 工作表转JSON的选项
   * @returns {Promise<any[] | false>} 返回JSON数组或false(读取失败时)
   */
  function readFileToJson<T = any>(
    file: File | Blob,
    options?: Sheet2JSONOpts,
  ): Promise<T[] | false> {
    return new Promise((resolve) => {
      const reader = new FileReader()
      reader.readAsBinaryString(file)
      reader.onload = function (e) {
        const data = (e.target as any)?.result
        const workbook = XLSX.read(data, {
          type: 'binary',
          raw: true,
          cellNF: true,
        })
        const json = XLSX.utils.sheet_to_json<T>(
          workbook.Sheets[workbook.SheetNames[0]],
          options,
        )
        resolve(json)
      }
      reader.onerror = function () {
        resolve(false)
      }
    })
  }

  /**
   * @desc 从本地Excel文件读取多个工作表的数据为JSON格式
   *
   * 此函数从File或Blob对象读取Excel文件并返回所有工作表的数据为JSON对象
   *
   * @param {File | Blob} file - 要读取的Excel文件
   * @param {Sheet2JSONOpts} [options] - 工作表转JSON的选项
   * @returns {Promise<Record<string, any[]> | false>} 返回包含所有工作表数据的对象或false(读取失败时)
   */
  function readFileToJsons(
    file: File | Blob,
    options?: Sheet2JSONOpts,
  ): Promise<Record<string, any[]> | false> {
    return new Promise((resolve) => {
      const reader = new FileReader()
      reader.readAsBinaryString(file)
      reader.onload = function (e) {
        const data = e.target?.result
        const workbook = XLSX.read(data, {
          type: 'binary',
          raw: true,
          cellNF: true,
        })

        const sheetNames = workbook.SheetNames
        if (!sheetNames?.length) {
          resolve(false)
        }

        const jsons = sheetNames.reduce(
          (pre: Record<string, any[]>, cur: string) => {
            const json = XLSX.utils.sheet_to_json(workbook.Sheets[cur], options)
            pre[cur] = json
            return pre
          },
          {},
        )

        resolve(jsons)
      }
      reader.onerror = function () {
        resolve(false)
      }
    })
  }

  /**
   * @desc 文件流导出Excel文件
   *
   * 此函数从File或Blob对象读取Excel文件流并导出为Excel文件
   *
   * @param {File | Blob} file - 要导出的Excel文件流
   * @param {IExportConfig} config - 导出配置项
   * @param {string} [config.name='导出'] - 导出文件名称
   * @param {BookType} [config.bookType='xlsx'] - 导出文件类型
   * @param {string} [config.errorMsg='下载失败'] - 错误提示信息
   * @returns {Promise<void>}
   */
  async function exportBuffer2Excel(file: File | Blob, config?: IExportConfig) {
    const {
      name = '导出',
      bookType = 'xlsx',
      errorMsg = '下载失败',
    } = config || {}
    const wb: WorkBook | false = await readWorkbookFromLocalFile(file)
    if (wb) {
      XLSX.writeFile(wb, `${name}.${bookType}`, { bookType })
    } else {
      message.error(errorMsg)
    }
  }

  /**
   * @desc 从URL导出Excel文件
   *
   * 此函数从URL获取Excel文件并导出
   *
   * @param {string} url - Excel文件的URL地址
   * @param {IExportConfig} config - 导出配置项
   * @param {string} [config.name='导出'] - 导出文件名称
   * @param {BookType} [config.bookType='xlsx'] - 导出文件类型
   * @param {string} [config.errorMsg='下载失败'] - 错误提示信息
   * @returns {Promise<void>}
   */
  async function exportUrl2Excel(url: string, config?: IExportConfig) {
    fetch(url)
      .then((response) => response.blob())
      .then((blob) => {
        exportBuffer2Excel(new Blob([blob]), config)
      })
      .catch((error) => {
        throw error
      })
  }

  /**
   * @desc 从工作表中读取指定列的数据
   *
   * 此函数从Excel文件的指定工作表中读取指定列的数据
   *
   * @param {File | Blob} file - Excel文件
   * @param {number} [sheetIndex=0] - 工作表索引
   * @param {string[]} [columns=['A']] - 要读取的列数组(如['A', 'B'])
   * @param {Record<string, string>} [fieldsMap={}] - 列名到字段名的映射
   * @returns {Promise<any[] | false>} 返回包含指定列数据的数组或false(读取失败时)
   */
  function readColumnFromSheet(
    file: File | Blob,
    sheetIndex: number = 0,
    columns: string[] = ['A'],
    fieldsMap: Record<string, string> = {},
  ): Promise<any[] | false> {
    return new Promise((resolve) => {
      const reader = new FileReader()
      reader.readAsBinaryString(file)
      reader.onload = function (e) {
        const data = (e.target as any)?.result
        const workbook = XLSX.read(data, {
          type: 'binary',
          raw: true,
          cellNF: true,
        })

        const sheetName = workbook.SheetNames[sheetIndex]
        const worksheet = workbook.Sheets[sheetName]

        // 获取 A 列的所有数据,跳过第一行(标题行)
        const columnData = []
        const range = XLSX.utils.decode_range(worksheet['!ref'] as string)

        // 从第2行开始(索引为1,跳过标题行)
        for (let row = range.s.r + 1; row <= range.e.r; row++) {
          const res: Record<string, any> = {}
          columns.forEach((col: string) => {
            const columnIndex = XLSX.utils.decode_col(col) // 将列字母转换为索引
            const cellAddress = XLSX.utils.encode_cell({
              r: row,
              c: columnIndex,
            }) // A列是第0列
            const cell = worksheet[cellAddress]
            const field = fieldsMap[col] ?? col
            res[field] = cell ? cell.v : ''
          })
          columnData.push(res)
        }

        resolve(columnData)
      }
      reader.onerror = function () {
        resolve(false)
      }
    })
  }

  /**
   * @desc 从工作表中获取指定列的数据
   *
   * 此函数从Excel工作表中获取指定列的所有数据(跳过标题行)
   *
   * @param {XLSX.WorkSheet} worksheet - Excel工作表对象
   * @param {string} column - 要获取数据的列(如'A'、'B')
   * @returns {any[]} 返回指定列的数据数组
   */
  function getColumnData(worksheet: XLSX.WorkSheet, column: string) {
    const columnData = []
    const range = XLSX.utils.decode_range(worksheet['!ref'] as string)
    const columnIndex = XLSX.utils.decode_col(column) // 将列字母转换为索引

    for (let row = range.s.r + 1; row <= range.e.r; row++) {
      const cellAddress = XLSX.utils.encode_cell({ r: row, c: columnIndex })
      const cell = worksheet[cellAddress]
      columnData.push(cell ? cell.v : null)
    }

    return columnData
  }

  /**
   * @desc 从本地文件读取Excel工作簿
   *
   * 此函数从File或Blob对象读取Excel文件并返回工作簿对象
   *
   * @param {File | Blob} file - 要读取的Excel文件
   * @param {XLSX.ParsingOptions} [options] - 解析选项
   * @returns {Promise<WorkBook | false>} 返回工作簿对象或false(读取失败时)
   */
  function readWorkbookFromFile(
    file: File | Blob,
    options: XLSX.ParsingOptions = {
      type: 'binary',
      raw: true,
      cellNF: true,
    },
  ): Promise<WorkBook | false> {
    return new Promise((resolve) => {
      const reader = new FileReader()
      reader.readAsBinaryString(file)
      reader.onload = function (e) {
        const data = (e.target as any)?.result
        const workbook = XLSX.read(data, options)
        resolve(workbook)
      }
    })
  }

  /**
   * @desc 从URL读取Excel工作簿
   *
   * 此函数从URL读取Excel文件并返回工作簿对象
   *
   * @param {string} url - Excel文件的URL地址
   * @param {XLSX.ParsingOptions} [options] - 解析选项
   * @returns {WorkBook} 返回工作簿对象
   */
  function readWorkbookFromUrl(
    url: string,
    options: XLSX.ParsingOptions = {
      type: 'string',
      raw: true,
      cellNF: true,
    },
  ): WorkBook {
    const workbook = XLSX.readFile(url, options)
    return workbook
  }

  /**
   * @desc 从URL读取Excel工作簿
   *
   * 此函数从URL读取Excel文件并返回工作簿对象
   *
   * @param {string} url - Excel文件的URL地址
   * @param {XLSX.ParsingOptions} [options] - 解析选项
   * @returns {WorkBook} 返回工作簿对象
   */
  async function readWorkbookFromUrl1(url: string): Promise<WorkBook | false> {
    try {
      const res = await fetch(url)
      const blob = await res.blob()
      const wb = readWorkbookFromFile(new Blob([blob]))
      return Promise.resolve(wb)
    } catch  {
      return Promise.resolve(false)
    }
  }

  /**
   * @desc 保存工作簿为Excel文件
   *
   * 此函数将工作簿对象保存为Excel文件
   *
   * @param {WorkBook} workbook - 要保存的工作簿对象
   * @param {string} fileName - 文件名
   * @param {XLSX.WritingOptions} [options] - 写入选项
   * @returns {void}
   */
  function saveWorkbook(
    workbook: WorkBook,
    fileName: string,
    options?: XLSX.WritingOptions,
  ) {
    XLSX.writeFile(workbook, fileName, options)
  }

  return {
    exportJson2Excel,
    exportAoa2Excel,
    exportBuffer2Excel,
    exportJson2ExcelSheets,
    exportUrl2Excel,
    readWorkbookFromLocalFile,
    readFileToJson,
    readFileToJsons,
    readColumnFromSheet,
    getColumnData,
    readWorkbookFromFile,
    readWorkbookFromUrl,
    readWorkbookFromUrl1,
    saveWorkbook,
  }
}

export default useExcel

二、使用场景

以下使用示例都是基于上面的 useExcel

模拟的一条导出数据

const exportDatas = [
  {
    id: 6,
    name: '假期/周末',
    uniq_key: 2,
    status: 1,
    admin_name: '王光环',
    last_update_time: 1733900693,
  },
  {
    id: 52,
    name: '12345678901234567890',
    uniq_key: 32141,
    status: 1,
    admin_name: '王光环',
    last_update_time: 1723458626,
  },
  {
    id: 31,
    name: '王林1',
    uniq_key: 43,
    status: 0,
    admin_name: '王光环',
    last_update_time: 1723458305,
  },
  {
    id: 24,
    name: '王林',
    uniq_key: 44,
    status: 0,
    admin_name: '王光环',
    last_update_time: 1723458246,
  },
  {
    id: 50,
    name: 'dsfgj',
    uniq_key: 23,
    status: 0,
    admin_name: '朱宇航',
    last_update_time: 1723457090,
  },
  {
    id: 45,
    name: '泥瓦工个',
    uniq_key: 32,
    status: 0,
    admin_name: '王光环',
    last_update_time: 1723451897,
  },
  {
    id: 44,
    name: '生产制造隔热',
    uniq_key: 123421,
    status: 0,
    admin_name: '王光环',
    last_update_time: 1723451890,
  },
  {
    id: 42,
    name: '哥人',
    uniq_key: 121,
    status: 0,
    admin_name: '王光环',
    last_update_time: 1723451876,
  },
  {
    id: 40,
    name: '亮反而',
    uniq_key: 22,
    status: 0,
    admin_name: '王光环',
    last_update_time: 1723451857,
  },
  {
    id: 39,
    name: '生产制造热',
    uniq_key: 156,
    status: 0,
    admin_name: '王光环',
    last_update_time: 1723451849,
  },
  {
    id: 38,
    name: 'v个人头',
    uniq_key: 21,
    status: 0,
    admin_name: '王光环',
    last_update_time: 1723451840,
  },
  {
    id: 37,
    name: '而非',
    uniq_key: 1232,
    status: 0,
    admin_name: '王光环',
    last_update_time: 1723451832,
  },
  {
    id: 36,
    name: '道路工/公路工/铁路工反而',
    uniq_key: 12343,
    status: 0,
    admin_name: '王光环',
    last_update_time: 1723451826,
  },
  {
    id: 35,
    name: '泥瓦工非',
    uniq_key: 15,
    status: 0,
    admin_name: '王光环',
    last_update_time: 1723451818,
  },
  {
    id: 34,
    name: '道路工/公路工/铁路工',
    uniq_key: 14,
    status: 0,
    admin_name: '王光环',
    last_update_time: 1723451811,
  },
  {
    id: 33,
    name: '淡而无味',
    uniq_key: 11,
    status: 0,
    admin_name: '王光环',
    last_update_time: 1723451802,
  },
  {
    id: 4,
    name: '一日一结',
    uniq_key: 1,
    status: 1,
    admin_name: '王光环',
    last_update_time: 1723449520,
  },
  {
    id: 12,
    name: '在线兼职',
    uniq_key: 8,
    status: 1,
    admin_name: '朱宇航',
    last_update_time: 1723445457,
  },
  {
    id: 7,
    name: '手艺活',
    uniq_key: 3,
    status: 1,
    admin_name: '朱宇航',
    last_update_time: 1723432946,
  },
  {
    id: 10,
    name: '在家兼职',
    uniq_key: 6,
    status: 1,
    admin_name: '朱宇航',
    last_update_time: 1723172619,
  },
]

const headers = {
  id: 'ID',
  name: '链接名称',
  uniq_key: '唯一标识',
  status: '状态',
  admin_name: '编辑人',
  last_update_time: '最新编辑时间',
}

1、一维数组导出

/**
   * @name: exportJson2ExcelHandler
   * @description: 一维数组导出
   */
  const exportJson2ExcelHandler = () => {
    exportJson2Excel(exportDatas, {
      headers,
      name: '一维数组导出数据',
      sheetName: '一维数组导出数据',
    })
  }

2、一维数组多sheet导出

/**
* @name: exportJson2ExcelSheetsHandler
* @description: 一维数组多Sheet导出
*/
const exportJson2ExcelSheetsHandler = () => {
const tip = `模板说明:\n
1、“企业id”填写说明:必填,需填写正确\n
2、“企业名称”填写说明:选填\n
3、“第三方平台”&“该企业的第三方id”填写说明:选填,目前第三方平台限填纷享销客`
const template = [
  {
    企业名称: null,
    企业id: null,
    第三方平台: null,
    该企业的第三方id: null,
    该企业的第三方名称: null,
    模板说明: tip,
  },
]
const merges = [{ s: { r: 1, c: 5 }, e: { r: 9, c: 5 } }]
const colInfo = [
  { width: 20 },
  { width: 40 },
  { width: 20 },
  { width: 20 },
  { width: 20 },
  { width: 40 },
]

exportJson2ExcelSheets({
  name: '一维数组多Sheet导出数据',
  sheets: [
    { json: template, sheetName: '模板', merges, colInfo },
    {
      headers,
      json: exportDatas,
      sheetName: '数据',
    },
  ],
})
}

3、简单的模板下载

/**
* @name: downloadTemplate1
* @description: 简单的模板下载
*/
const downloadTemplate1 = async () => {
const name = '切词导入模板'
const sheetName = '导入模板'
const bookType = 'xlsx'
const merges = [{ s: { r: 1, c: 3 }, e: { r: 7, c: 3 } }]
const colInfo = [{ width: 20 }, { width: 20 }, { width: 20 }, { width: 50 }]
const tip = `1.“所属词包”填写说明:词包名称需与“词表词包管理”模块中的词包名称一致;\n
2. “状态”中,选项有待启用,已启用 \n(1)“待启用”状态的切词,将不被用户端取用 \n(2)“已启用”状态的切词,将被用户端取用`
const template = [
  {
    '切词(必填)': null,
    '所属词包(选填)': null,
    '状态(必填)': null,
    模板使用说明: tip,
  },
]
exportJson2Excel(template, { name, sheetName, merges, colInfo, bookType })
}

4、复杂的模板下载

// 工种没获取到的时候
  const withoutWorksTemplate = () => {
    const name = '搜索热词导入模板'
    const sheetName = '导入模板'
    const bookType = 'xlsx'
    const merges = [{ s: { r: 1, c: 2 }, e: { r: 7, c: 2 } }]
    const colInfo = [{ width: 20 }, { width: 20 }, { width: 40 }, { width: 30 }]
    const tip = `1.“适用工种”填写说明:\n
(1)最右侧为最新的二级工种名称,请以此在“适用工种”中,录入正确的二级工种名称。\n
(2)录入多个工种时,需使用英文逗号隔开`
    const template = [
      {
        '工种定向热词(必填)': '测试',
        '适用工种(必填)': '二级工种1,二级工种2',
        模板使用说明: tip,
      },
    ]
    exportJson2Excel(template, { name, sheetName, merges, colInfo, bookType })
  }

  /**
   * @name: downloadTemplate
   * @description: 复杂的模板下载
   */
  const downloadTemplate = () => {
    const name = '搜索热词导入模板'
    const sheetName = '导入模板'
    const bookType = 'xlsx'
    const merges = [{ s: { r: 1, c: 2 }, e: { r: 7, c: 2 } }]
    const colInfo = [{ width: 20 }, { width: 20 }, { width: 40 }, { width: 30 }]
    const tip = `1.“适用工种”填写说明:\n
(1)最右侧为最新的二级工种名称,请以此在“适用工种”中,录入正确的二级工种名称。\n
(2)录入多个工种时,需使用英文逗号隔开`

    toggleLoading()

    getAllWorks({ level: 2, online: 1 })
      .then((res) => {
        const { code, result } = res || {}
        if (code !== 0) return

        if (isAvailableArr(result.list)) {
          const header = [
            '工种定向热词(必填)',
            '适用工种(必填)',
            '模板使用说明',
            '最新二级工种列表',
          ]
          const workNames = result.list.map((l: any) => l.name)
          const contents = workNames.map((w: string, index: number) => {
            if (index === 0) {
              return ['测试', '二级工种1,二级工种2', tip, w]
            }
            return [null, null, null, w]
          })
          const template = [header, ...contents]
          exportAoa2Excel(template, {
            name,
            sheetName,
            merges,
            colInfo,
            bookType,
          })
        } else {
          withoutWorksTemplate()
        }
      })
      .catch(() => {
        withoutWorksTemplate()
      })
      .finally(toggleLoading)
  }
  
  // getAllWorks为一个接口,不用在意,我们最终只是想取到里面的值而已,没有该接口我们也可以模拟一个对象数组 ,只需要里面有name属性就行

5、文件流导出

/**
   * @name: exportCardData
   * @description: 导出统计明细(文件流导出)
   */
  const exportCardData = async () => {
    toggleLoading()
    try {
      const res = await exportCardDatas({ pg: 1, pagesize: 100, export: 1 })
      if (!res.code) {
        exportBuffer2Excel(new Blob(['\ufeff', res as any]), {
          name: '卡片统计明细',
          bookType: 'xlsx',
        })
      } else {
        message.error('导出统计明细失败')
      }
    } catch {
      //
    } finally {
      toggleLoading()
    }
  }
  
  // exportCardDatas是一个返回值为文件流的接口,可以自己模拟

6、文件地址下载

/**
   * @name: downloadTempUrl
   * @description: 文件地址下载(网络请求导出)
   */
  const downloadTempUrl = () => {
    const temp_url =
      'https://jgj-store.oss-cn-beijing.aliyuncs.com/operation/manual_rule/demo.xlsx'
    exportUrl2Excel(temp_url, { name: '手动触达推送人群导入模板' })
  }

7、读取本地文件为json

function readFileToJson(
    file: File | Blob,
  ): Promise<any | false> {
    return new Promise((resolve) => {
      const reader = new FileReader()
      reader.readAsBinaryString(file)
      reader.onload = function (e) {
        const data = (e.target as any)?.result
        const workbook = XLSX.read(data, {
          type: 'binary',
          raw: true,
          cellNF: true,
        })
        const json = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]])
        resolve(json)
      }
      reader.onerror = function () {
        resolve(false)
      }
    })
  }