当前位置: 首页 > Web前端 > vue.js

前端开发如何实现Excel导出保存

时间:2023-03-31 22:35:46 vue.js

我们的项目前端使用vue,服务端使用node.js。我们已经使用这个excel导出工具一年了。我们目前使用的是图片导出,文本导出,以及调整excel单元格间距等前端训练。这个node端的封装一直被同事审阅(感谢同事),我也一直在不断的修改和优化代码。也是第一次接触node.js。我只想告诉你,使用它是安全的!Node.js服务端代码1.获取到需要导出的数据如何使用(假数据模拟,下面是页面)constructor(prop){super(prop)//定义excel的表头数据this.header=[{header:'游戏',key:'gameInfo',width:30},{header:'宣传图',key:'图片',width:15},{header:'游戏详情页',key:'path',width:15},{header:'status',key:'activeStatus',width:30},{header:'sortweight',key:'sort',width:30},{header:'lastedittime',key:'updateTime',width:30},{header:'recenteditor',key:'operatorName',width:30},]}/**导出游戏管理数据*/asyncexportGameEndGameManage(){const{list}=awaitthis.commonGameEndGameManage(true)console.log(list,'list')constbaseExcelInfo={data:list,filename:'gameManageList',header:this.header,sheetName:'游戏管理列表',imageKeys:[{name:'image',imgWidth:'100',imgHeight:'100',},],}awaitthis.service.common.exportFile.exportExcel(baseExcelInfo)}copycodelist为获取的数据,打印如下)header表示表头sheetName表示excel表的名称imageKeys:图片信息:字段名称,图片的宽高,但是只要有图片就必须设置名称。很重要的一点是假设从表中的数据返回如果status是1,那么我确定export不能是1,应该是对应的中文,所以在导出之前,应该进行处理,这个处理应该在服务器端做,而不是前端,然后为了导出这个功能再做一个例子/**公共游戏管理数据@param{Boolean}isExport是否导出*/asynccommonGameEndGameManage(isExport){constactiveStatus={//逻辑上写在构造函数中1:'Open',2:'close',}const{ctx,app}=thisconst{limit,offset}=this.paginationDeal(ctx.request.query)constisPagi=isExport?{}:{limit,offset}const{list,total}=awaitctx.service.operateManage.gameEndPage.getGameEndGameManage({isPagi})constdata=list.map(node=>{const{status,...params}=node.toJSON()constactiveStatus=activeStatus[status]return{activeStatus,status,...params}})return{list:data,total}}复制代码2.导出Excel包先安装对应包npminstallexceljs--保存复制下面的代码就好'usestrict'constService=require('egg').Service//importexceljsconstExcel=require('exceljs')//exportfilerelatedservicesclassexportFileServiceextendsService{构造函数(prop){super(prop)this.defaultViews=[{x:0,y:0,width:10000,height:20000,firstSheet:0,activeTab:1,visibility:'visible',},]this.fontName='ArialUnicodeMS'this.font={name:this.fontName,family:4,size:13}this.fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF8DB4E2'}}this.border={style:'thin',color:{argb:'cccccc'}}}/**exportexcel@param{Object}config传入的excel对象@param{Array}config.dataexcel数据@param{String}config.filenameexcel文件名@param{Array}config.headerexcel表头@param{String}config.sheetName表名@param{Array}config.imageKeyskey@param待转换图片{String}config.creator创建表的人@param{String}config.lastModifiedBy最后修改表的人@param{String}config.imageKeys.imgWidth图片的宽度@param{String}config.imageKeys.imgHeight图片高度*/asyncexportExcel({data=[],filename='file',header,sheetName='sheet1',imageKeys=[],creator='me',lastModifiedBy='her',}){const{ctx}=thisconstworkbook=newExcel.Workbook()//设置属性-创建和最后修改的人constnow=newDate()workbook.created=nowworkbook.modified=nowworkbook.lastPrinted=nowconstworksheet=workbook.addWorksheet(sheetName)//设置打开时的视图-设置位置workbook.views=this.defaultViews//使worksheet可见工作表。state='visible'worksheet.columns=headerfor(leti=1;i<=header.length;i++){worksheet.getColumn(i).alignment={vertical:'middle',horizo??ntal:'center'}工作表。getColumn(i).font={name:'ArialUnicodeMS'}}worksheet.addRows(data)//处理图片constimageList=this.getImageList(imageKeys,data,header)//添加图片到sheetawaitthis.addPicToSheet(imageList,imageKeys,workbook,worksheet)//多级表头const表头OPtion=header.filter((item,index)=>{if(item.type&&item.type==='multi'){header.splice(index,1)returnitem}returnitem.type&&item.type==='multi'})//多级表头重新设置表头if(headerOPtion.length){headerOPtion[0].headerText.forEach((text,index)=>{constborderAttr={top:this.border,左:this.border,下:this.border,右:this.border,index}constheaderAttr=[{attr:'values',value:text,},{attr:'font',value:this.font,},{attr:'fill',value:this.fill,},{attr:'border',value:borderAttr,},]headerAttr.map(item=>{worksheet.getRow(index+1)[item.attr]=item.valuereturnworksheet})})headerOPtion[0].mergeOption.forEach(merge=>{worksheet.mergeCells(merge)})}else{//设置表格样式worksheet.getRow(1).font=this.fontworksheet.getRow(1).fill=this.fill}constbufferContent=awaitworkbook.xlsx.writeBuffer()//setctx.set('Content-disposition',`attachment;filename=${filename}.xlsx`)//返回文件bufferctx.body=bufferContent}//设置图片大小getImageList(imageKeys,data,header){returnimageKeys.map(key=>data.map((item,index)=>({key,url:item[key.name],col:this.app.utils.index.getIndexByKey(header,key.name)+1,row:index+2,width:key.imgWidth,height:key.imgHeight,})))}//添加图片到sheetasyncaddPicToSheet(imageList,imageKeys,workbook,worksheet){if(imageKeys.length>0){awaitPromise.all(imageList.map(asyncimgArr=>{returnawaitPromise.all(imgArr.map(item=>{const{url,width,height,row,col}=item//因为有些图片不存在,需要判断if(url){returnthis.app.utils.index.getBase64(url,this.ctx).然后(res=>{if(!url)returnconstimgType=url.split('?')[0].substring(url.split('?')[0].lastIndexOf('.')+1).toLowerCase()constid=workbook.addImage({base64:res,extension:imgType,})worksheet.addImage(id,{tl:{col:col-1,row:row-1},ext:{width,height},})worksheet.getRow(row).height=height////去掉背景链接worksheet.getRow(row).getCell(item.key.name).value=''})}returnitem}))}))}}}module.exports=exportFileService复制代码3.调用下载接口后,查看返回的信息node.js在前端得到的是二进制文件流前端代码接口//文件导出导出函数exportFile(url,params){returnrequest({responseType:'blob',headers:{'Content-Type':'application/json',},timeout:1000*60,url:url,method:'get',params:{query:qs.stringify(params),},})}复制代码utils/**save文件本地导出@param{Object}Obj导出文件参数对象@param{Blob}file文件资源@param{String}fileName文件名(注:包含后缀)*/exportfunctionloacalSaveFile({文件,文件名,选项={type:'application/vnd.ms-excel'}}){constieKit=judgeBrowser('ie')constblobData=newBlob([file],option)//生成Blob文件if(ieKit&&navigator.msSaveBlob){navigator.msSaveBlob&&navigator.msSaveBlob(blobData,fileName)}else{//otherconstsave_link=document.createElement('a')consturl=URL.createObjectURL(file)//创建urlsave_link.href=urlsave_link.download=fileNamedocument.body.appendChild(save_link)save_link.click()setTimeout(()=>{document.body.removeChild(save_link)window.URL.revokeObjectURL(url)//回收url},0)}}复制代码调用constfile=awaitexportFile(this.exportItem.apiUrl,data)loacalSaveFile({file,fileName:${this.exportItem.fileName}.xlsx})复制代码效果