第一次写博客有点慌
前段时间公司需要做个异步导出excel的功能
点击导出按钮后端返回数据前端通过node+exceljs把数据转为excel表格同时弹出弹框,支持翻页,弹框主要信息是一个列表,包括excel表名,如果导出完成了就有个下载表格,没有完成有个手动中断按钮,点击手动中断停止导出,添加导出失败,手动中断信息
刚开始用的是直接读取excel文件然后在里面写数据
核心伪代码如下
const filename = `xxx数据导出表${new Date().getTime()}.xlsx`;//表名
importPath = `${importPath}/kapi`;
const ws = await workbook.xlsx.readFile(path.join(importPath, fileName)).then(worksheet => {
return worksheet._worksheets[1];
}).catch(() => {
const worksheet = workbook.addWorksheet('订单列表');
worksheet.addRow(['表头','表头']);
return worksheet;
});
ws.addRow(['数据','数据'])
后来改成这样速度快了很多,基本上大部分代码如下
const path = require('path');
const fs = require('fs');
const axios = require('axios');
const Excel = require('exceljs');
let id=-1;//需要点击中断导出的id
// 设置生成excel文件所在的地址
importPath = `${importPath}/kapi`;
//往excel表里面添加数据
const getExcel = async ({list,getWs}) => {
getWs(list);
return {
errcode: 0
};
};
//翻页递归,直到最后一页
const getList = ({page,method,ctx,record,url,getWs}) => {
const pageSize = 10;
//调用数据列表接口返回相应数据
return axios({
method: method,
url,
timeout: 200 * 1000,
params: Object.assign({}, ctx.request.body, {
page: page,
page_size: pageSize
}),
headers: {
Cookie: ctx.headers.cookie,
'X-Requested-With': 'XMLHttpRequest'
}
}).then(async res => {
if (page == 1 && !res.data.total) {
return {
errcode: 1,
message: '没有可导出的列表'
};
}
//没调用一次就写一次表格
let _res = await getExcel({
list: res.data.data,
getWs,
});
if (_res.errcode) return _res;
//如果没有点击中断传给后端相应进度
if(id!=record.id){
const putRes = await axios({
method: 'POST',
url: `${ctx.headers.origin}/填写自己的后端接口`,
data: {
id:record.id,//导出表格的id
status: Math.floor((res.data.total <= page * pageSize ? res.data.total : page * pageSize)/res.data.total),//状态0未完成1已完成 字段怎么取根据自己的接口来
process: (res.data.total <= page * pageSize ? res.data.total : page * pageSize)/res.data.total*100,//进度0-100 字段怎么取根据自己的接口来
msg:''//错误信息
},
headers: {
Cookie: ctx.headers.cookie,
'X-Requested-With': 'XMLHttpRequest'
}
}).then(() => ({
errcode: 0
})).catch(e => ({
errcode: 1,
message: e.message
}));
if (putRes.errcode) return putRes;
}
//判断是否递归完成,或者是否中断
if (res.data.total <= page * pageSize||id==record.id) {
return {
errcode: 0
};
} else {//没完成而且没有中断的继续递归
return getList({
page: page + 1,
ctx,
method,
record,
url,
getWs,
});
}
}).catch(e => {
return {
errcode: 1,
message: e.message
};
});
};
const Api = {
//导出excel node接口
info: async (ctx, next) => {
const filename = `xxx数据导出表${new Date().getTime()}.xlsx`;//表名
const row=['姓名','id','地址']//表头
const ws = new Excel.stream.xlsx.WorkbookWriter({filename: path.join(importPath, filename)})//路径
const worksheet = ws.addWorksheet('sheet1');//sheet表名
worksheet.addRow(row).commit();//不管是表头或者数据每次添加都需要commit
//每点击导出添加一条记录
const postRes = await axios({
method: 'post',
url: `${ctx.headers.origin}/填写自己的后端接口(添加表格的接口)`,
data: {
type: 2,
name:filename,
},
headers: {
Cookie: ctx.headers.cookie,
'X-Requested-With': 'XMLHttpRequest'
}
}).then(res => ({
errcode: 0,
data: res.data
})).catch(e => ({
errcode: 1,
message: e.message
}));
if (postRes.errcode) {
ctx.body = postRes;
return;
}
getList({
page: 1,
method: 'POST',
ctx,
record: {fileName:filename,id:postRes.data.data.id},
url: `${ctx.headers.origin}/填写自己的后端接口(需要导出的列表接口)`,
getWs: (list) => {
list.forEach(order => {
worksheet.addRow([
//这里随便写的,根据实际接口返回的数据和excel表格所需的字段来
order.mp_name,
order.id,
order.name,
order.address,
//......
]).commit();//不管是表头或者数据每次添加都需要commit
});
}
}).then(res => {
ws.commit()//每次表格数据添加完成都需要commit这是必须的否则excel表格打不开
}).catch(e => {
throw new Error(e);
});
ctx.body = {
errcode: 0
};
},
//手动中断接口
stopExport: async (ctx, next) => {
id=ctx.request.body.id
ctx.body = await axios({
method: 'POST',
url: `${ctx.headers.origin}/填写自己的后端接口`,
data: {
id:id,
status: 2,
msg: '手动中断'
},
headers: {
Cookie: ctx.headers.cookie,
'X-Requested-With': 'XMLHttpRequest'
}
});
ctx.body = {
errcode: 0
};
await next();
},
};
module.exports = Api;
初来乍到还望大佬多多指教