egg export excel function

example:

Use egg+vue3 to process the data export function of the bill list.
Don't talk nonsense, let's see the results first

result

The results downloaded by the front-end browser are placed here.
insert image description here

egg-backend

add-in exceljs

yarn add exceljs

Encapsulate the service of exporting excel

service/exportExcel.js

const Service = require('egg').Service;
const Excel = require('exceljs');
 
class exportExcel extends Service {
    
    
  /**
   * 数据并生成excel
   * @param {Array} headers excel标题栏
   * @param {Array} param 数据参数
   * @param {string} name 文件名称
   */
   async excelCommon(headers, data, name) {
    
    
      let columns = [];// exceljs要求的columns
      let hjRow = {
    
    };// 合计行
      let titleRows = headers.length;// 标题栏行数
 
      // 处理表头
      headers.forEach(row=>{
    
    
        row.forEach(col=>{
    
    
          let {
    
     f, t ,w} = col;
          if (f){
    
    
            if (col.totalRow) hjRow[f] = true;
            if (col.totalRowText) hjRow[f] = col.totalRowText;
            col.style = {
    
     alignment: {
    
     vertical: 'middle', horizontal: 'center' } };
            col.header = t; //文字
            col.key = f; //对应的字段
            col.width = w ? w : 15;//宽度
            columns.push(col);
          }
        })
      })
 
      let workbook = new Excel.Workbook();
      let sheet = workbook.addWorksheet('My Sheet', {
    
     views: [{
    
     xSplit: 1, ySplit: 1 }] });
      sheet.columns = columns;
      sheet.addRows(data);
 
      // 处理复杂表头
      if (titleRows > 1) {
    
    
        // 头部插入空行
        for (let i = 1; i < titleRows; i++)  sheet.spliceRows(1, 0, []);
 
        headers.forEach(row=>{
    
    
          row.forEach(col=>{
    
    
            if (col.m1){
    
    
              sheet.getCell(col.m1).value = col.t;
              sheet.mergeCells(col.m1 + ":" + col.m2);
            }
 
          })
        })
 
      }
 
      // 处理样式、日期、字典项
      sheet.eachRow(function (row, rowNumber) {
    
    
        // 设置行高
        row.height = 25;
        row.eachCell({
    
     includeEmpty: true },  (cell)=> {
    
    
          // 设置边框 黑色 细实线
          const top = {
    
     style: 'thin', color: {
    
     argb: '000000' } };
          const left = {
    
     style: 'thin', color: {
    
     argb: '000000' } };
          const bottom = {
    
     style: 'thin', color: {
    
     argb: '000000' } };
          const right = {
    
     style: 'thin', color: {
    
     argb: '000000' } };
          cell.border = {
    
     top, left, bottom, right };
          // 设置标题部分为粗体
          if (rowNumber <= titleRows) {
    
     cell.font = {
    
     bold: true }; return; }
        });
      });
      this.ctx.set('Content-Type', 'application/vnd.openxmlformats');
      // 这个地方的空格不要更改
      this.ctx.set('Content-Disposition', "attachment;filename*=UTF-8' '" + encodeURIComponent(name) + '.xlsx');
      return await workbook.xlsx.writeBuffer()
    }
}
module.exports = exportExcel;

controller

controller/bill.js

'use strict';
const Controller = require('egg').Controller;

class BillController extends Controller {
    
    
  async list() {
    
    
    const {
    
     ctx } = this;
    const params = ctx.query;
    ctx.body = await ctx.service.bill.list(params);
  }


  async downExcel() {
    
    
    console.log('downExcel111111111111111---');
    const {
    
     ctx } = this;
    const params = ctx.query;
    //两层数组,一层是行,一层是列,允许多行
    let headers = [
      [
        {
    
     t: 'id', f: 'id', totalRow: true },
        {
    
     t: '流水号', f: 'water_num', totalRow: true },
        {
    
     t: '时间', f: 'create_time', totalRow: true },
      ]
    ];
    // 注:f的字段名需要跟数据库的字段一致
    let data = await this.app.model.Bill.findAll({
    
    
      where: {
    
    },
      raw: true
    });
    let res = await ctx.service.exportExcel.excelCommon(headers, data, '订单信息')
    console.log('res--------: ', res);
    // ctx.body = ctx.helper.resSuc(res); // 包一层有问题
    ctx.body = res;
  }
}
module.exports = BillController;

router

  router.get('/bill/downExcel', controller.bill.downExcel);

to be optimized

Data cannot be wrapped in one layer, such as {code, data, msg}, otherwise an error will occur

front-end-vue3

There are two options to download

  1. a tag direct download: Disadvantage 1 can only use get 2jwt authentication problem (token problem)
  2. Normal axios download

axios package

Focus on downFile, pay attention to config configuration: responseType: 'blob'
path: service/request.ts

import axios from 'axios'
import type {
    
     AxiosInstance } from 'axios'
import cache from '@/utils/cache'
import {
    
     ElMessage } from 'element-plus'
import router from '@/router'
const BASE_URL = process.env.VUE_APP_BASE_URL || ''

class ZGLRequest {
    
    
  instance: AxiosInstance
  constructor(baseURL: string, timeout = 10000) {
    
    
    this.instance = axios.create({
    
    
      baseURL,
      timeout
    })
    this.instance.interceptors.request.use((config: any) => {
    
    
      const token = cache.getCache('token')
      if (token) {
    
    
        config.headers['token'] = token
      }
      return config
    })
    this.instance.interceptors.response.use((response: any) => {
    
    
      const res = response.data
      if (res.code !== 10000) {
    
    
        ElMessage.error(res.msg)
        if (res.code === 10001) {
    
    
          // token失效就跳到登录页
          router.push('/login')
        }
      }
      return response
    })
  }
  request(config: any) {
    
    
    return new Promise((resolve, reject) => {
    
    
      this.instance
        .request(config)
        .then((res) => {
    
    
          console.log('request-then-res-------: ', res);
          resolve(res.data)
        })
        .catch((err) => {
    
    
          console.log('request-err: ', err);
          reject(err)
        })
    })
  }
  post(config: any) {
    
    
    return this.request({
    
     ...config, method: 'post' })
  }
  get(config: any) {
    
    
    return this.request({
    
     ...config, method: 'get' })
  }
  patch(config: any) {
    
    
    return this.request({
    
     ...config, method: 'patch' })
  }
  delete(config: any) {
    
    
    return this.request({
    
     ...config, method: 'delete' })
  }
  // 注意responseType: 'blob'
  getfile(config: any) {
    
    
    return this.request({
    
     ...config, method: 'get', responseType: 'blob'})
  }
}

export default new ZGLRequest(BASE_URL)

Package exportExcel.ts

service/exportExcel.ts

import zglRequest from './request'

export default {
    
    
  // 方案1
  getUrl: (url: any) => {
    
    
    let host = process.env.VUE_APP_BASE_URL == '/api' ? 'http://192.168.0.67:7003' : process.env.VUE_APP_BASE_URL;
    console.log('host: ', host);
    return host + url;
  },
  // 方案2
  exportfn: () => zglRequest.downFile({
    
     url: '/bill/downExcel', }),
}

page use

// 方案1 - 类似a标签
<el-button type="primary" @click="exportfn">导出-简单版</el-button>
// 方案2 - axios
<el-button type="primary" @click="exportfnjinjie">导出-进阶版</el-button>


// js------------------
// 方案1 - 类似a标签
const exportfn = () => {
    
    
  let url = exportExcel.getUrl('/bill/downExcel')
  window.location.href = url
}

// 方案2 - axios
const exportfnjinjie = () => {
    
    
  exportExcel.exportfn().then((res: any) => {
    
    
    downFile(res, 'file.xlsx')
  })

  // 可以将此方法封装
  function downFile(content: any, filename: any) {
    
    
    // 创建隐藏的可下载链接
    var eleLink = document.createElement('a')
    eleLink.download = filename
    eleLink.style.display = 'none'
    // 字符内容转变成blob地址
    var blob = new Blob([content])
    eleLink.href = URL.createObjectURL(blob)
    document.body.appendChild(eleLink)
    // 触发点击
    eleLink.click()
    // 然后移除
    document.body.removeChild(eleLink)
  }
}

insert image description here

Guess you like

Origin blog.csdn.net/u012570307/article/details/127518449