<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用 Node.js + Vue 實現(xiàn) Excel 導(dǎo)出與保存的功能

          共 8042字,需瀏覽 17分鐘

           ·

          2021-10-22 03:25

          我們的項目是前端用vue,服務(wù)端用node.js,這個excel導(dǎo)出我們已經(jīng)用了一年,我們目前用到的無非是圖片導(dǎo)出,文本導(dǎo)出,調(diào)調(diào)excel單元格距離等.

          這個node端的封裝是經(jīng)過同事不斷的review(感謝同事),俺不斷的修改優(yōu)化后的代碼,當(dāng)時也是我第一次接觸node.js,只想告訴你,用它,穩(wěn)穩(wěn)的!

          node.js服務(wù)端代碼

          1.拿到需要導(dǎo)出的數(shù)據(jù)之后如何使用(假數(shù)據(jù)模擬,下面是頁面)

          image.png
          ?constructor(prop)?{
          ????super(prop)
          ????//?定義excel頭部數(shù)據(jù)
          ????this.header?=?[
          ??????{?header:?'游戲',?key:?'gameInfo',?width:?30?},
          ??????{?header:?'宣傳圖片',?key:?'image',?width:?15?},
          ??????{?header:?'游戲詳情頁',?key:?'path',?width:?15?},
          ??????{?header:?'狀態(tài)',?key:?'activeStatus',?width:?30?},
          ??????{?header:?'排序權(quán)重',?key:?'sort',?width:?30?},
          ??????{?header:?'最近編輯時間',?key:?'updateTime',?width:?30?},
          ??????{?header:?'最近編輯人',?key:?'operatorName',?width:?30?},
          ????]
          ??}
          ?/**
          ???*?導(dǎo)出游戲管理數(shù)據(jù)
          ???*/

          ??async?exportGameEndGameManage()?{
          ????const?{?list?}?=?await?this.commonGameEndGameManage(true)
          ????console.log(list,?'list')
          ????const?baseExcelInfo?=?{
          ??????data:?list,
          ??????filename:?'gameManageList',
          ??????header:?this.header,
          ??????sheetName:?'游戲管理列表',
          ??????imageKeys:?[
          ????????{
          ??????????name:?'image',
          ??????????imgWidth:?'100',
          ??????????imgHeight:?'100',
          ????????},
          ??????],
          ????}
          ????await?this.service.common.exportFile.exportExcel(baseExcelInfo)
          ??}
          復(fù)制代碼

          list就是拿到的數(shù)據(jù),打印如下
          baseExcelInfo用來定義基本的參數(shù)
          data 表示的是excel的數(shù)據(jù)來源
          filename 是文件名(但是前端的excel導(dǎo)出會進(jìn)行覆蓋)
          header表示的是表格的頭部
          sheetName表示的是excel的表名
          imageKeys:圖片的信息:字段名稱,圖片的寬高,但是只要有圖片,name必須設(shè)

          image.png

          很重要的一點就是,假設(shè)從表里面的數(shù)據(jù)返回的status是1,那么我肯定導(dǎo)出的不能1,應(yīng)該是對應(yīng)的一個中文,所以在導(dǎo)出前,應(yīng)該進(jìn)行處理,這個處理應(yīng)該是在服務(wù)端來做,而不是前端做一遍,然后為了導(dǎo)出這個功能重新做一遍舉個例子

          /**
          ???*??公共游戲管理數(shù)據(jù)
          ???*??@param?{?Boolean?}?isExport?是否導(dǎo)出
          ???*/

          ??async?commonGameEndGameManage(isExport)?{
          ????const?activeStatus?=?{?//?這個按道理寫在constructor里面哈
          ??????1:?'打開',
          ??????2:?'關(guān)閉',
          ????}
          ????const?{?ctx,?app?}?=?this
          ????const?{?limit,?offset?}?=?this.paginationDeal(ctx.request.query)
          ????const?isPagi?=?isExport???{}?:?{?limit,?offset?}
          ????const?{?list,?total?}?=?await?ctx.service.operateManage.gameEndPage.
          ????getGameEndGameManage({?isPagi?})
          ????const?data?=?list.map(node?=>?{
          ??????const?{?status,?...params?}?=?node.toJSON()
          ??????const?activeStatus?=?activeStatus[status]
          ??????return?{?activeStatus,?status,?...params?}
          ????})
          ????return?{?list:?data,?total?}
          ??}
          復(fù)制代碼

          2.exportExcel的封裝

          首先安裝對應(yīng)的包 npm install exceljs --save
          然后復(fù)制下面的代碼就好了


          'use?strict'

          const?Service?=?require('egg').Service
          //?引入exceljs
          const?Excel?=?require('exceljs')

          //?導(dǎo)出文件相關(guān)服務(wù)
          class?exportFileService?extends?Service?{
          ??constructor(prop)?{
          ????super(prop)
          ????this.defaultViews?=?[
          ??????{
          ????????x:?0,
          ????????y:?0,
          ????????width:?10000,
          ????????height:?20000,
          ????????firstSheet:?0,
          ????????activeTab:?1,
          ????????visibility:?'visible',
          ??????},
          ????]
          ????this.fontName?=?'Arial?Unicode?MS'
          ????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'?}?}
          ??}
          ??/**
          ???*?導(dǎo)出excel
          ???*?@param?{?Object?}?config?傳入的excel對象
          ???*?@param?{?Array?}?config.data?excel的數(shù)據(jù)
          ???*?@param?{?String?}?config.filename?excel文件名
          ???*?@param?{?Array?}?config.header?excel的頭部
          ???*?@param?{?String?}?config.sheetName?表名
          ???*?@param?{?Array?}?config.imageKeys?需要轉(zhuǎn)化圖片的key
          ???*?@param?{?String?}?config.creator?創(chuàng)建表的人
          ???*?@param?{?String?}?config.lastModifiedBy?最后修改表的人
          ???*?@param?{?String?}?config.imageKeys.imgWidth?圖片的寬度
          ???*?@param?{?String?}?config.imageKeys.imgHeight?圖片的高度
          ???*?*/

          ??async?exportExcel({
          ????data?=?[],
          ????filename?=?'file',
          ????header,
          ????sheetName?=?'sheet1',
          ????imageKeys?=?[],
          ????creator?=?'me',
          ????lastModifiedBy?=?'her',
          ??})?{
          ????const?{?ctx?}?=?this
          ????const?workbook?=?new?Excel.Workbook()
          ????//?設(shè)置屬性?-創(chuàng)建著以及最后修改的人
          ????workbook.creator?=?creator
          ????workbook.lastModifiedBy?=?lastModifiedBy

          ????//?時間獲取一次就好
          ????const?now?=?new?Date()
          ????workbook.created?=?now
          ????workbook.modified?=?now
          ????workbook.lastPrinted?=?now
          ????const?worksheet?=?workbook.addWorksheet(sheetName)
          ????//?設(shè)置打開時候的視圖-設(shè)置位置
          ????workbook.views?=?this.defaultViews
          ????//?使工作表可見
          ????worksheet.state?=?'visible'
          ????worksheet.columns?=?header

          ????for?(let?i?=?1;?i?<=?header.length;?i++)?{
          ??????worksheet.getColumn(i).alignment?=?{?vertical:?'middle',?horizontal:?'center'?}
          ??????worksheet.getColumn(i).font?=?{?name:?'Arial?Unicode?MS'?}
          ????}
          ????worksheet.addRows(data)
          ????//?處理圖片
          ????const?imageList?=?this.getImageList(imageKeys,?data,?header)
          ????//?添加圖片到sheet
          ????await?this.addPicToSheet(imageList,?imageKeys,?workbook,?worksheet)
          ????//?多級表頭
          ????const?headerOPtion?=?header.filter((item,?index)?=>?{
          ??????if?(item.type?&&?item.type?===?'multi')?{
          ????????header.splice(index,?1)
          ????????return?item
          ??????}
          ??????return?item.type?&&?item.type?===?'multi'
          ????})
          ????//?多級表頭重置設(shè)置表頭
          ????if?(headerOPtion.length)?{
          ??????headerOPtion[0].headerText.forEach((text,?index)?=>?{
          ????????const?borderAttr?=?{?top:?this.border,?left:?
          ????????this.border,?bottom:?this.border,?right:?this.border,?index?}
          ????????const?headerAttr?=?[
          ??????????{
          ????????????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.value
          ??????????return?worksheet
          ????????})
          ??????})
          ??????headerOPtion[0].mergeOption.forEach(merge?=>?{
          ????????worksheet.mergeCells(merge)
          ??????})
          ????}?else?{
          ??????//?設(shè)置表頭樣式
          ??????worksheet.getRow(1).font?=?this.font
          ??????worksheet.getRow(1).fill?=?this.fill
          ????}
          ????const?bufferContent?=?await?workbook.xlsx.writeBuffer()

          ????//?設(shè)置
          ????ctx.set('Content-disposition',?`attachment;filename=${filename}.xlsx`)
          ????//?返回文件buffer
          ????ctx.body?=?bufferContent
          ??}
          ??//?設(shè)置圖片大小
          ??getImageList(imageKeys,?data,?header)?{
          ????return?imageKeys.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,
          ????????})
          ??????)
          ????)
          ??}
          ??//?添加圖片到sheet
          ??async?addPicToSheet(imageList,?imageKeys,?workbook,?worksheet)?{
          ????if?(imageKeys.length?>?0)?{
          ??????await?Promise.all(imageList.map(async?imgArr?=>?{
          ????????return?await?Promise.all(imgArr.map(item?=>?{
          ??????????const?{?url,?width,?height,?row,?col?}?=?item
          ??????????//?因為有的圖片是不存在的需要判斷
          ??????????if?(url)?{
          ????????????return?this.app.utils.index.getBase64(url,?this.ctx).then(res?=>?{
          ??????????????if?(!url)?return
          ??????????????const?imgType?=?url.split('?')[0].substring(url.split('?')[0].
          ??????????????lastIndexOf('.')?+?1).toLowerCase()
          ??????????????const?id?=?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?=?''
          ????????????})
          ??????????}
          ??????????return?item
          ????????}))
          ??????}))
          ????}
          ??}
          }

          module.exports?=?exportFileService
          復(fù)制代碼

          3.調(diào)用下載接口后node.js返回的信息

          前端看到的就是一個二進(jìn)制文件流

          image.png
          image.png

          推薦了解 content-type: application/octet-stream

          前端代碼

          接口

          //?文件導(dǎo)出
          export?function?exportFile(url,?params)?{
          ??return?request({
          ????responseType:?'blob',
          ????headers:?{
          ??????'Content-Type':?'application/json',
          ????},
          ????timeout:?1000?*?60,
          ????url:?url,
          ????method:?'get',
          ????params:?{
          ??????query:?qs.stringify(params),
          ????},
          ??})
          }
          復(fù)制代碼

          utils

          /**
          ?*?本地保存文件并導(dǎo)出
          ?*?@param?{?Object?}?Obj?導(dǎo)出文件參數(shù)對象
          ?*?@param?{?Blob?}?file?文件資源
          ?*?@param?{?String?}?fileName 文件名稱(注意:包含后綴)
          ?*/

          export?function?loacalSaveFile({?file,?fileName,?option?=?{?type:?'application/vnd.ms-excel'?}})?{
          ??const?ieKit?=?judgeBrowser('ie')
          ??const?blobData?=?new?Blob([file],?option)?//?生成?Blob文件
          ??if?(ieKit?&&?navigator.msSaveBlob)?{
          ????navigator.msSaveBlob?&&?navigator.msSaveBlob(blobData,?fileName)
          ??}?else?{
          ????//?其他
          ????const?save_link?=?document.createElement('a')
          ????const?url?=?URL.createObjectURL(file)?//?創(chuàng)建url
          ????save_link.href?=?url
          ????save_link.download?=?fileName
          ????document.body.appendChild(save_link)
          ????save_link.click()
          ????setTimeout(()?=>?{
          ??????document.body.removeChild(save_link)
          ??????window.URL.revokeObjectURL(url)?//?回收url
          ????},?0)
          ??}
          }
          復(fù)制代碼

          調(diào)用

          const?file?=?await?exportFile(this.exportItem.apiUrl,?data)
          loacalSaveFile({?file,?fileName:?`${this.exportItem.fileName}.xlsx`?})
          復(fù)制代碼

          效果


          - EOF -

          瀏覽 36
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  911香蕉视频 | 午夜成人视频网址 | 国产又粗又硬又黄的视频 | 精品免费一区二区三区四区 | 97电影在线观看无码视频 |