wisetc / practice

Practice conclusion
5 stars 0 forks source link

导出 excel 的前端模块实现 #36

Open wisetc opened 4 years ago

wisetc commented 4 years ago

概述

使用 axios 和 file-saver 并利用后台返回 stream 的下载接口来实现导出 excel 的功能。

前提

假设:

  1. 后台接口提供的返回数据正常为 stream,异常为 json
  2. 前端接口基于 axios 定义,且设定 responseType: 'blob'

依赖:

  1. element-ui
  2. @wisetc/utils-adapter
  3. file-saver

接口

// 定义的 axois 请求
type AxiosHttpRequest = any;

type AnyData = {
    [k: string]: any;
};

function ExportFunc(params: AnyData): Promise<{ filename: string, blob: Blob }>

function createExportFunc(api: AxiosHttpRequest): ExportFunc;

实现

假定有使用 axios 实现的后台 ExcelApi,其返回符合 ResultPromise,实现 createExportFunc 如下:

import assert from 'assert'
import { blob } from '@wisetc/utils-adapter'
import { ExcelApi } from '@orgname/services-http-apis'

function createExportFunc(api) {
  return async function(params) {
    try {
      const res = await api(params)
      assert(res, '服务未响应')
      assert(res.status === 200, '服务未正常响应')
      const contentDisposition = res.request.getResponseHeader(
        'Content-Disposition'
      )
      if (!contentDisposition) {
        const data = await blob.blob2Json(res.data)
        console.log(data)
        assert(data.code === 200, data.message || String(data.code))
      }

      let filename = 'output.xls'
      if (contentDisposition && typeof contentDisposition === 'string') {
        filename = contentDisposition.replace(
          /attachment;filename=(.+)$/i,
          (s0, s1) => s1
        )
      }

      return {
        filename,
        blob: res.data,
      }
    } catch (error) {
      return Promise.reject(error)
    }
  }
}

export const exportSignExcel = createExportFunc(ExcelApi.exportSignExcel)

ResultPromise 的定义:

import { AxiosResponse } from 'axios';

type Result<T> = {
  code: number;
  data: T;
  message: string;
};

export type ResultPromise<T> = Promise<AxiosResponse<Result<T>>>;

实现下载功能:

import FileSaver from 'file-saver'
import { Message } from 'element-ui'

function createDownloadFunc(func) {
  return async function(params) {
    try {
      const { blob, filename } = await func(params)
      FileSaver.saveAs(blob, filename)
      Message.info('正在下载... 请不要重复操作。')
    } catch (err) {
      Message.error(err.message)
    }
  }
}

export const dl = createDownloadFunc(exportSignExcel)

总结

实现了下载 excel 的前端接口和下载功能。

全文完。