关于帮老婆在前端导数据这件小事
作者:互联网
一个普通的晚上,普通的我听着普通disco回到普通的家,不普通的老婆让我做一件普通的事情:导数据
因为种种原因,只能在前端通过控制台脚本导数据,而且有这几种类型的数据:
1. 查询列表接口,并导出一个 Excel 表格;
2. 查询列表接口,分别将每一行数据导出一个文本文件;
3. 查询列表接口,基于每一行数据的 ID 查询详情接口,再将详情数据导出为文本文件。
为了晚上不用睡地板,我赶紧抄起键盘一顿操作
一、接口请求
由于是通过控制台脚本请求,所以只能用原生 JavaScript 编写,也没办法引入 npm 库
好在不用担心兼容性问题,于是 fetch 成为发起请求的首选方案
首先是 GET 请求,比较简单,简单配一下请求头就行:
function GET(url) { return fetch(url, { method: "GET", headers: new Headers({ 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/x-www-form-urlencoded', }), credentials: 'include', }) .then(response => response.json()) .then(json => new Promise((res) => { res(json.data) }) ) }
POST 请求会稍微复杂一点,需要根据 Content-Type 调整请求体 body
如果 Content-Type 接受 application/json 就还好,直接通过 JSON.stringify 处理即可
而对于 application/x-www-form-urlencoded 类型,通常是通过 qs.stringify 处理
但由于不能使用 npm 仓库,只好手写一个 stringify 函数
function POST(url, data) { return fetch(url, { method: 'POST', headers: new Headers({ Accept: 'application/json, text/plain, */*', 'Content-Type': 'application/x-www-form-urlencoded', }), credentials: 'include', body: stringify(data), }) .then((response) => response.json()) .then( (json) => new Promise((res) => { res(json.data); }), ); } function stringify(data) { return Object.keys(data) .map((k) => `${k}=${data[k]}`) .join('&'); }
二、文件导出
和服务端不同,前端应用本身不具备读写文件系统的能力,只能通过特定的页面元素调用浏览器 API 实现
比如读取文件只能通过主动触发 <input type="file"> 实现,写入文件只能通过 <a href="" download="" /> 触发下载功能
// 通过 <a/> 下载文件,content 可能是一段富文本 function writeFile(fileName, content) { const a = document.createElement('a'); const div = document.createElement('div'); div.innerHTML = content; // 通过 innerText 过滤掉富文本中的 html 标签,得到纯文本 const text = div.innerText || ''; const blob = new Blob([text], { type: 'text/plain' }); a.download = fileName; a.href = URL.createObjectURL(blob); a.click(); }
在这次的导出需求中,会同时创建多个下载任务。首次触发时,谷歌浏览器会提示“是否允许下载多个文件”,选择“允许”就好
然而即便允许多文件下载,浏览器最多同时创建 10 个下载任务,这个问题可以通过异步创建下载任务的方式解决
// 就是有点费回车键
三、导出表格
准备就绪,首先处理第一种需求:查询列表接口,并导出一个 Excel 表格
在正常的工作环境下,可以使用 js-xlsx 生成 .xlsx文件。现在一切从简,可以用一个替代方案:制表符 tab
如果我们复制下图这样的表格
然后粘贴到记事本或者任何文本编辑器,会得到这样的结果
反过来,如果我们生成这样的文本,再复制粘贴到 Excel 中,也能得到对应的表格
于是就有了这样的代码:
function exportTable() { const HEADER = ['序号', '名称', '描述', '备注']; fetchList().then(({ list }) => { const temp = [renderRow(HEADER)]; list.forEach((x) => { const row = renderRow([x.key1, x.key2, x.key3, x.key4]); temp.push(row); }); writeFile('表格', temp.join('\n')); }); } function fetchList() { return get('/api/list'); } function renderTxt(v) { return v || '-'; } function renderRow(arr) { return arr.map((x) => renderTxt(x)).join(' '); }
四、导出详情
除了导出表格以外,还有两种情况:
1. 查询列表接口,分别将每一行数据导出一个文本文件;
2. 查询列表接口,基于每一行数据的 ID 查询详情接口,再将详情数据导出为文本文件。
这两个情况的处理思路很类似,只是第二种情况需要再请求一次接口
但有个细节需要注意:列表的数据很多,会同时创建多个下载任务,需要通过异步的方式绕开浏览器的下载任务上限
function exportDetail() { fetchList().then(({ list }) => { list.forEach((item, index) => { fetchDetail(item.id).then((detail) => { setTimeout(() => { // 通过 setTimeout 异步创建下载任务,绕过浏览器的下载上限 writeFile(item.title, renderContent(detail)) }, index * 500); }); }); }); } function fetchList() { return get('/api/list'); } function fetchDetail(id) { return get(`/api/detail/${id}`); } // 排版 function renderContent(detail) { const { title, desc, content } = detail || {}; return `<h1>${title}\n</h1><div>${desc}\n</div>${content}`; }
终于避免睡地板的惨剧,收工~
标签:function,return,老婆,前端,导出,接口,json,小事,const 来源: https://www.cnblogs.com/wisewrong/p/16094626.html