前言
【**负责人 A
】:现在报表部分基于接口的Excel
的导入和导出功能有点慢,前端这边能不能实现一下这个功能,然后我们在比对看看效果!**
【**切图仔 B
**】:接口这边不能优化一下吗?比如排查下慢的原因什么的。
【**负责人 A
】:现在后端开发任务比较重,处理的核心任务也多还会涉及一些架构上的调整,所以想着前端这边可以处理一下,然后看看整体效果。**
【**切图仔 B
**】:OK,试试 就 ~ Shi Shi ~
下面就基于 **`xlsx`**[2] 这个第三方库封装一个<ExcelUpload />
组件实现表格导入,以及json
数据导出Excel
功能的json2Excel()
工具方法。
选择 **`xlsx`**[3] 的原因如下图所示:
Excel 解析为 JSON
基本内容
组件效果和结构
组件内容是很简单的结构和视图,直接查看如下的页面效果和代码即可:
<template><inputtype="file"ref="excelRef":accept="props.accept"@change="onChange"class="excel"/><h1>解析数据:</h1><h2><code>{{excelData}}</code></h2></template><scriptsetuplang="ts">import{reactive,ref}from'vue'import*asXLSXfrom'xlsx'interfaceProps{accept:string}constprops=withDefaults(defineProps<Props>(),{accept:'.csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',})constexcelRef:any=ref(null)constexcelData:any[]=reactive([])constonChange=(event:any)=>{//获取文件对象constfile=event.target.files[0]console.log(file)}</script>复制代码
Excel 数据格式
Excel
数据格式有两种,一种是有表头说明的,另一种是无表头说明的,具体内容如下:
有表头说明
无表头说明
实现 Excel 转 JSON 功能
核心步骤
通过FileReader
以二进制的方式读取Excel
文件,即fileReader.readAsBinaryString(file)
将对应的二进制数据通过XLSX.read(fileData, { type: "binary" })
方法生成workbook
对象
workbook.SheetNames[0]
获取第一个Sheet
的名称wsname
,因为表格是有序列表,因此可以有多个Sheet
通过XLSX.utils.sheet_to_json(workbook.Sheets[wsname])
方法将对应的Sheet
内容转换为JSON
数据
效果演示
有表头说明
无表头说明
具体代码
//读取对应表格文件constreaderExcel=(file:File)=>{constfileReader=newFileReader();//以二进制的方式读取表格内容fileReader.readAsBinaryString(file);//表格内容读取完成fileReader.onload=(event:any)=>{try{constfileData=event.target.result;constworkbook=XLSX.read(fileData,{type:"binary",});//表格是有序列表,因此可以取多个Sheet,这里取第一个Sheetconstwsname=workbook.SheetNames[0];//将表格内容生成json数据constsheetJson=XLSX.utils.sheet_to_json(workbook.Sheets[wsname]);console.log(sheetJson);//得到的表格JSON内容}catch(e){console.log(e);returnfalse;}};};//文件变化时触发constonChange=(event)=>{//获取文件对象constfile=event.target.files[0];//读取文件内容readerExcel(file);//清除数据clearFile();};constclearFile=()=>{excelRef.value.value="";};复制代码
格式化 JSON 数据
这里需要考虑有表头说明和无表头说明的情况,为了方便统一处理,作如下规定:
通过将有表头说明的数据格式统一转化为无表头说明的数据格式
统一将无表头说明的数据格式转化为标准的接口入参,即{ key: value }
,这里需要建立一个name -> key
的映射关系:
constexcelNameToKey={'姓名':"name",'年龄':"age",'特长':"skill",'电话':"telephone",'地址':"address",};复制代码
格式化如下:
核心代码如下:
<scriptsetuplang='ts'>import{reactive,ref}from"vue";import*asXLSXfrom"xlsx";constexcelNameToKey={姓名:"name",年龄:"age",特长:"skill",电话:"telephone",地址:"address",};interfaceProps{accept:string;}constprops=withDefaults(defineProps<Props>(),{accept:".csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",});constexcelRef:any=ref(null);constexcelData:any[]=reactive([]);constclearFile=()=>{excelRef.value.value="";};//标准化JSON数据constexcelDataToJson=(sheetJson:any[])=>{if(!sheetJson.length)return;letresult=sheetJson;consthasTableHead=!!sheetJson[0].__EMPTY;//拥有表头的数据,需要取正确的值if(hasTableHead){constheader=sheetJson.shift();constdata:any[]=[];Object.keys(header).forEach((key)=>{sheetJson.forEach((item,index)=>{constobj=data[index]||{};obj[header[key]]=item[key];data[index]=obj;});});console.log("【】【】",data);result=data;}//将表格对应的文字转换为keyresult.forEach((item)=>{constnewItem:any={};Object.keys(item).forEach((key)=>{newItem.title=key;newItem[excelNameToKey[key]]=item[key];});excelData.push(newItem);});};//读取对应表格文件constreaderExcel=(file:File)=>{constfileReader=newFileReader();//以二进制的方式读取表格内容fileReader.readAsBinaryString(file);//表格内容读取完成fileReader.onload=(event:any)=>{try{constfileData=event.target.result;constworkbook=XLSX.read(fileData,{type:"binary",});//表格是有序列表,因此可以取多个Sheet,这里取第一个Sheetconstwsname=workbook.SheetNames[0];//将表格内容生成json数据constsheetJson=XLSX.utils.sheet_to_json(workbook.Sheets[wsname]);console.log(sheetJson);//标准化JSON数据excelDataToJson(sheetJson);}catch(e){console.log(e);returnfalse;}};};//文件变化时触发constonChange=(event)=>{//获取文件对象constfile=event.target.files[0];//读取文件内容readerExcel(file);//清除数据clearFile();};</script>复制代码
JSON 导出为 Excel
基本结构
页面内容也非常简单,具体如下:
<template><divid="container"><h1>JSON 数据:</h1><h2><code>{{jsonData}}</code></h2><button@click="exportExcel">导出Excel</button></div></template>复制代码
导出功能
导出其实也很简单,首先创建src/utils/json2Excel.ts
文件里面就是具体导出的实现,具体内容如下:
//src/utils/json2Excel.tsimport*asXLSXfrom"xlsx";exportdefault(data:any[],sheetName:string="sheet1",fileName:string="json2Excel.xlsx")=>{constjsonWorkSheet=XLSX.utils.json_to_sheet(data);constworkBook={SheetNames:[sheetName],//指定有序sheet的nameSheets:{[sheetName]:jsonWorkSheet,//表格数据内容},};returnXLSX.writeFile(workBook,fileName);//向文件系统写出文件};复制代码
然后在App.vue
中使用,具体如下:
//src/App.vue<scriptsetuplang="ts">importjson2Excelfrom"./utils/json2Excel";//测试的JSON数据constjsonData=[{name:"张三1",age:18,skill:"干饭1",telephone:2025,address:"宇宙尽头1",},{name:"张三2",age:19,skill:"干饭2",telephone:2026,address:"宇宙尽头2",},{name:"张三3",age:20,skill:"干饭3",telephone:2027,address:"宇宙尽头3",},{name:"张三4",age:21,skill:"干饭4",telephone:2028,address:"宇宙尽头4",},{name:"张三5",age:22,skill:"干饭5",telephone:2029,address:"宇宙尽头5",},{name:"张三6",age:23,skill:"干饭6",telephone:2030,address:"宇宙尽头6",},{name:"张三7",age:24,skill:"干饭7",telephone:2031,address:"宇宙尽头7",},{name:"张三8",age:25,skill:"干饭8",telephone:2032,address:"宇宙尽头8",},{name:"张三9",age:26,skill:"干饭9",telephone:2033,address:"宇宙尽头9",},{name:"张三10",age:27,skill:"干饭10",telephone:2034,address:"宇宙尽头10",},];//key->name的映射constexcelKeyToName={name:"姓名",age:"年龄",skill:"特长",telephone:"电话",address:"地址",};//导出Excel文件constexportExcel=()=>{//格式化参数constdata=jsonData.map((item)=>{constnewItem:any={};Object.keys(item).forEach(key=>{newItem[excelKeyToName[key]]=item[key];});returnnewItem;});//导出Exceljson2Excel(data);};</script>复制代码
效果演示
最后
以上只是实现了简单的单个导入、导出功能,可以将其完善为批量操作,但是要注意批量操作带来的耗时性,将对应的耗时部分通过webworker
等方式处理,这样页面就不需要一直等待当前的操作完成。
另外,如果有要求在导出Excel
时有表格样式(如:行列宽高设置等)可以通过 **`xlsx-populate`**[4] 来实现。
以上就是本文的全部内容,希望上述内容可以给大家带来一些思路,可以在评论区贡献更优质的方案。
关于本文
作者:熊的猫
/post/7135945969425711111
往期回顾
#
如何使用 TypeScript 开发 React 函数式组件?
#
11 个需要避免的 React 错误用法
#
6 个 Vue3 开发必备的 VSCode 插件
#
3 款非常实用的 Node.js 版本管理工具
#
6 个你必须明白 Vue3 的 ref 和 reactive 问题
#
6 个意想不到的 JavaScript 问题
#
试着换个角度理解低代码平台设计的本质