最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue2項目使用exceljs多表頭導出全過程

 更新時間:2025年06月21日 11:16:07   作者:玉笛聲聲青靈舞  
這篇文章主要介紹了vue2項目使用exceljs多表頭導出全過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

前言

因為項目需求要用到前端導出功能,粗略比較了幾個導出插件之后,決定使用exceljs插件,至少能夠滿足樣式、數(shù)據(jù)格式等的需求。

鑒于個人水平和時間限制,目前只是初步實現(xiàn)了組件化,將表頭、數(shù)據(jù)按要求格式傳遞到組件中即可導出需要的excel文件。代碼質(zhì)量、實現(xiàn)思路存在一定的缺陷,后續(xù)有時間會繼續(xù)優(yōu)化,如果大家有更好的實現(xiàn),感謝留言!

組件導出文件效果如下:

  • 一級表頭

  • 二級表頭

  • 三級表頭

提示:以下是本篇文章正文內(nèi)容,下面案例可供參考

一、插件安裝

yarn add exceljs
yarn add file-saver

二、數(shù)據(jù)準備

1、前臺表格表頭數(shù)據(jù)

const data = [
    {
      prop: "AA",
      label: "地區(qū)",
      minWidth: "150px"
    },
    {
      prop: "BB",
      label: "歸集額",
      minWidth: "100px"
    },
    {
      label: "對下調(diào)入",
      children: [
        {
          prop: "CC",
          label: "資金",
          minWidth: "90px"
        },
        {
          prop: "DD",
          label: "占比",
          minWidth: "80px"
        },
      ],
    },
    {
      label: "本級調(diào)入",
      children: [
        {
          prop: "EE",
          label: "資金",
          minWidth: "100px"
        },
        {
          prop: "FF",
          label: "占比",
          minWidth: "80px"
        }
      ]
    }
  ]

2、傳遞到組件前的數(shù)據(jù)處理

// 表頭數(shù)據(jù)預處理函數(shù)
const headersPreprocess = function (businessAttr) {
  let columns = deepClone(businessAttr);
  let maxLevel = 0;
  let baseCols = [];
  function columnsProcess(columns = [], level = 0, path = "") {
    ++level;
    maxLevel = maxLevel < level ? level : maxLevel;
    for (let i = 0; i < columns.length; i++) {
      // 獲取列的路徑,用于后面表頭數(shù)據(jù)格式處理
      columns[i].path = emptyCheck(path) ? path + "_" + columns[i].label : columns[i].label;
      columnProcess(columns[i]);
      if (columns[i].children) {
        columnsProcess(columns[i].children, level, columns[i].path);
      } else {
        baseCols.push({
          prop: columns[i].prop,    // 當前字段的prop值
          path: columns[i].path,    // 當前字段的路徑
          level: level,             // 當前列所處層級
          width: columns[i].width || columns[i].minWidth,    // 列寬
        });
        delete columns[i].path;
      }
    }
  }
  columnsProcess(columns, 0);
  return {
    columns: columns,     // 前臺表格表頭用到的數(shù)據(jù)
    depth: maxLevel,      // 樹形結構表頭的最大層級數(shù)--導出功能所需
    baseCols: baseCols    // 底層字段數(shù)據(jù)集--導出功能所需
  };
};

得到的baseCols數(shù)據(jù)

//得到的baseCols數(shù)據(jù)
[
    {
        level: 1,
        path:  "地區(qū)",
        prop: "AA",
        width: "150px"
    },
    {
        level: 1,
        path:  "資金歸集額",
        prop: "BB",
        width: "100px"
    },
    {
        level: 2,
        path:  "對下調(diào)入_資金",
        prop: "CC",
        width: "90px"
    },
    {
        level: 2,
        path:  "對下調(diào)入_占比",
        prop: "DD",
        width: "80px"
    },
    {
        level: 2,
        path:  "本級調(diào)入_資金",
        prop: "DD",
        width: "100px"
    },
    {
        level: 2,
        path:  "本級調(diào)入_占比",
        prop: "DD",
        width: "80px"
    },
]

三、插件引入

1、新建ExcelJS.js文件

2、ExcelJS.js文件中引入需要的庫

import ExcelJS from "exceljs";
import FileSaver from "file-saver";

四、導出前數(shù)據(jù)處理

1、按exceljs格式創(chuàng)建導出函數(shù)

conf參數(shù)包含的就是上面獲取到的baseCols和depth數(shù)據(jù),及自定義的其他屬性,如文件名等

/**
 * @description: excel導出
 * @param  dataList 需要導出的數(shù)據(jù)集
 * @param  conf 導出函數(shù)配置數(shù)據(jù),包括上面處理得到的底層字段信息baseCols,表頭層級數(shù)據(jù)depth,文件名fileName
 * @return 
 */
export const downloadExcel = function (dataList = [], conf = {}) {
  const workbook = new ExcelJS.Workbook();
  // 設置信息
  workbook.creator = "Me";
  workbook.title = conf.fileName;
  workbook.created = new Date();
  workbook.modified = new Date();
  // 創(chuàng)建工作表
  const worksheet = workbook.addWorksheet(conf.fileName)
  // 表頭信息處理
  let columns = columnsPreprocess(conf)    
  // 表頭數(shù)據(jù)填充及樣式設置
  headerFillAndSet(columns.headers, columns.widthList, worksheet) 
  // 橫向單元格合并處理
  sheetMergesForCross(worksheet, columns.headers);
  // 縱向單元格合并處理
  sheetMergesForVertical(worksheet, columns.headers);
  // 添加數(shù)據(jù)集
  worksheetDataProcess(conf.baseCols, dataList, worksheet)
  const _titleCell = titleStyleProcess(worksheet);
  // 寫入文件
  workbook.xlsx.writeBuffer().then((buffer) => {
    let _file = new Blob([buffer], {
      type: "application/octet-stream",
    });
    FileSaver.saveAs(_file, "ExcelJS.xlsx");
  });
};

2、表頭及列寬數(shù)據(jù)預處理

2.1 處理邏輯

// 獲取表頭及列寬數(shù)據(jù)集
function columnsPreprocess(conf = {}) {
  let underlayCols = conf.baseCols;
  let paths = [];
  let widthArr = [];
  // 將各列的路徑信息及列寬分別放到數(shù)組中
  for (let i = 0; i < underlayCols.length; i++) {
    paths.push(underlayCols[i].path);
    // 列寬放入數(shù)組前,使用excelColumnWidthProcess函數(shù)提前處理
    widthArr.push(excelColumnWidthProcess(underlayCols[i].width));
  }
  return {
    // excel使用到的表頭數(shù)據(jù)沒有可以直接用到的,需要用到headersProcess進行轉(zhuǎn)換
    headers: paths.length > 0 ? headersProcess(paths, conf.depth) : [],
    widthList: widthArr,
  };
}
// 表頭數(shù)據(jù)處理
// 從最上面的界面表格的表頭樹形結構數(shù)據(jù)可以看到,有6個底層列,數(shù)據(jù)結構有2層,轉(zhuǎn)為excel數(shù)據(jù)格式的 // 話,就需要轉(zhuǎn)為2行6列。即表頭層級有多少,這里就轉(zhuǎn)為多少行,表頭底層字段有多少,這里就有多少列。
function headersProcess(pathList = [], depth = 1) {
  let headers = [];
  for (let i = 0; i < depth; i++) {
    headers.push([]);
  }
  let paths = [];
  for (let i = 0; i < pathList.length; i++) {
    let arr = pathList[i].split("_");
    for (let j = arr.length; j < depth; j++) {
      arr.push("");
    }
    paths.push(arr);
  }
  for (let i = 0; i < depth; i++) {
    for (let j = 0; j < paths.length; j++) {
      headers[i].push(paths[j][i]);
    }
  }
  return headers;
}
// 列寬處理
// 寬度數(shù)據(jù)需要轉(zhuǎn)為數(shù)字,同時前端的寬度與excel中的列寬存在較大的區(qū)別,所以此處除以了5
function excelColumnWidthProcess(width) {
  let result = 40;
  if (emptyCheck(width)) {
    if (getDataType(width) === "string") {
      if (width.includes("px")) {
        width = width.replace("px", "");
        width = parseFloat(width);
        result = parseInt(width / 5);
      }
    } else if (getDataType(width) === "number") {
      result = parseInt(width / 5);
    }
  }
  return result;
};

2.2 處理結果

// 得到的headers處理結果
[
  ['地區(qū)', '資金歸集額', '省對下調(diào)入', '省對下調(diào)入', '本級調(diào)入', '本級調(diào)入'],
  ['',     '',          '資金',      '占比',       '資金',     '占比']
]

3、單元格樣式處理函數(shù)

// 單元格樣式處理,按需設置
const titleStyleProcess = function (sheet, index) {
  const titleCell = sheet.getRow(index);
  titleCell.eachCell({ includeEmpty: true }, (cell, colNumber) => {
    titleCell.getCell(colNumber).fill = {
      type: "pattern",
      pattern: "solid",
      fgColor: { argb: "FFF5F7FA" },
    };
    titleCell.getCell(colNumber).border = {
      top: { style: "thin" },
      left: { style: "thin" },
      bottom: { style: "thin" },
      right: { style: "thin" },
    };
  });
  titleCell.height = 30;
  titleCell.font = {
    name: "黑體",
    bold: true,
    size: 14,
    color: {
      argb: "FF999999",
    },
  };
  // // 設置第一行的對齊方式(水平垂直)
  titleCell.alignment = {
    vertical: "middle",
    horizontal: "center",
  };
  return titleCell;
};

4、表頭數(shù)據(jù)填充及樣式設置

// 設置表頭的方法還有 worksheet.columns = headers等方式,但似乎只適用于只有一層的表頭,也可能是 // 自己沒有找對方法。在排除直接設置的方式后,直接將表頭數(shù)據(jù)當做普通行數(shù)據(jù)進行處理。同時進行行樣式設 // 置,此時也就可以理解為表頭樣式設置。
function headerFillAndSet(headers = [], widthList = [], worksheet) {
  for (let i = 1; i <= headers.length; i++) {
    worksheet.getRow(i).values = headers[i - 1];
    titleStyleProcess(worksheet, i);
  }
  for (let i = 1; i <= widthList.length; i++) {
    worksheet.getColumn(i).width = widthList[i - 1];
  }
}

5、橫向單元格合并處理

// Excel的列名集合,用于合并單元格時定位單元格位置
const colNames = [
  "A",
  "B",
  ...,
  "Y",
  "Z",
  "AA",
  "AB",
  ...,
  "AY",
  "AZ",
  ....
];
// 
function sheetMergesForCross(worksheet, headers) {
  for (let i = 0; i < headers.length; i++) {
    let arr = [null, null];
    for (let j = 1; j <= headers[i].length; j++) {
      if (headers[i][j - 1] === headers[i][j]) {    // 前后元素相同,表示可以合并
        if (!emptyCheck(arr[0]) && emptyCheck(headers[i][j - 1])) {
          arr[0] = colNames[j - 1] + (i + 1);
        }
        arr[1] = colNames[j] + (i + 1);
      } else {    // 前后元素不相同,j是從1開始,所以表示沒有相同列或者此次相同列已結束
        if (emptyCheck(arr[0]) && emptyCheck(arr[1])) { // arr[0]或者arr[1]為空,表示相鄰元素不同,均有值表示有相同列,arr[1]便是最后一個相同的列
          worksheet.mergeCells(arr[0] + ":" + arr[1]);
        }
        arr = [null, null];    // 相鄰元素不同,arr重置,準備下一批可合并元素
      }
    }
  }
}

6、縱向單元格合并處理

function sheetMergesForVertical(worksheet, headers) {
  let col = headers[0];
  // 第一層循環(huán)具體元素
  for (let i = 0; i < col.length; i++) {
    let sd = ""; // 開始元素
    let ed = ""; // 結束元素
    // 第二層循環(huán),比較層級不同下標相同的元素
    for (let j = 1; j < headers.length; j++) {
      if (headers[j][i] === "") {  // 元素為空,表示可與上層元素合并
        sd = emptyCheck(sd) ? sd : colNames[i] + j;
        ed = colNames[i] + (j + 1);
      }
    }
    if (emptyCheck(sd) && emptyCheck(ed)) {
      worksheet.mergeCells(sd + ":" + ed);
    }
  }
}

7、添加數(shù)據(jù)集

function worksheetDataProcess(columns = [], dataList = [], worksheet = null) {
  let len = 1;
  for (let i = 0; i < columns.length; i++) {
    len = len < columns[i].level ? columns[i].level : len;
  }
  for (let i = 0; i < dataList.length; i++) {
    let list = [];
    for (let j = 0; j < columns.length; j++) {
      list.push(dataList[i][columns[j].prop]);
    }
    worksheet.getRow(len + i + 1).values = list;
  }
}

8、公共函數(shù)補充

// 非空判斷
export const emptyCheck = function (val) {
  if (val === null || val === "" || val === undefined) {
    return false;
  }
  let dataType = getDataType(val);
  switch (dataType) {
    case "string":
      return val.trim().length !== 0;
    case "array":
      return val.length !== 0;
    case "object":
      return Object.keys(val).length !== 0;
  }
  return true;
};

// 獲取數(shù)據(jù)類型
export const getDataType = function (val) {
  return Object.prototype.toString
    .call(val)
    .replace(/^\[object (\S+)\]$/, "$1")
    .toLowerCase();
};

五、調(diào)用導出函數(shù)

// conf = {
//   baseCols: baseCols,    表頭信息數(shù)組
//   depth: 2,    // 表頭最大層級數(shù)
//   fileName: 'xxxx',  導出文件名、sheet名等,自定
//   ....  其他自定義屬性
// }

// dataList 需要導出的數(shù)據(jù)集
downloadExcel(dataList, conf)

總結

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • vue如何監(jiān)聽元素橫向滾動到最右側

    vue如何監(jiān)聽元素橫向滾動到最右側

    這篇文章主要介紹了vue如何監(jiān)聽元素橫向滾動到最右側問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • 聊聊Vue.js的template編譯的問題

    聊聊Vue.js的template編譯的問題

    這篇文章主要介紹了聊聊Vue.js的template編譯的問題,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • Vue利用draggable實現(xiàn)多選拖拽效果

    Vue利用draggable實現(xiàn)多選拖拽效果

    這篇文章主要為大家詳細介紹了如何利用vue中的draggable插件實現(xiàn)多選拖拽效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • vue+element-ui監(jiān)聽滾動實現(xiàn)錨點定位方式(雙向),錨點問題

    vue+element-ui監(jiān)聽滾動實現(xiàn)錨點定位方式(雙向),錨點問題

    這篇文章主要介紹了vue+element-ui監(jiān)聽滾動實現(xiàn)錨點定位方式(雙向),錨點問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • 使用Vue自定義數(shù)字鍵盤組件(體驗度極好)

    使用Vue自定義數(shù)字鍵盤組件(體驗度極好)

    最近做 Vue 開發(fā),因為有不少頁面涉及到金額輸入,產(chǎn)品老是覺得用原生的 input 進行金額輸入的話 體驗很不好,于是自己動手寫了一個使用Vue自定義數(shù)字鍵盤組件,具體實現(xiàn)代碼大家參考下本文
    2017-12-12
  • axios+vue請求時攜帶cookie的方法實例

    axios+vue請求時攜帶cookie的方法實例

    做項目時遇到一個需求,后端需要在接口請求時,對用戶登陸狀態(tài)進行判斷,需要在請求時攜帶Cookie,下面這篇文章主要給大家介紹了關于axios+vue請求時攜帶cookie的相關資料,需要的朋友可以參考下
    2022-09-09
  • Vue3組件間通信機制方法深入理解

    Vue3組件間通信機制方法深入理解

    組件通信指的是在Vue應用中,不同組件之間傳遞數(shù)據(jù)或觸發(fā)行為的過程,這篇文章主要介紹了Vue3組件間通信機制方法的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2025-12-12
  • Vue組件化編程詳解

    Vue組件化編程詳解

    文章介紹了Vue中組件的基本使用、定義、注冊、書寫標簽和嵌套等關鍵步驟和注意事項,詳細解釋了組件的配置選項、如何通過Vue.extend創(chuàng)建組件以及組件實例對象與Vue實例對象的區(qū)別,還提及了單文件組件的結構和編寫邏輯,并強調(diào)了組件原型鏈的重要性
    2025-10-10
  • Vue+Element使用富文本編輯器的示例代碼

    Vue+Element使用富文本編輯器的示例代碼

    本篇文章主要介紹了Vue+Element使用富文本編輯器的示例代碼,具有一定的參考價值,有興趣的可以了解一下
    2017-08-08
  • Vue通過vue-router實現(xiàn)頁面跳轉(zhuǎn)的全過程

    Vue通過vue-router實現(xiàn)頁面跳轉(zhuǎn)的全過程

    這篇文章主要介紹了Vue通過vue-router實現(xiàn)頁面跳轉(zhuǎn)的操作步驟,文中有詳細的代碼示例和圖文供大家參考,對大家的學習或工作有一定的幫助,感興趣的朋友可以參考下
    2024-04-04

最新評論

墨竹工卡县| 旺苍县| 宜春市| 东乌| 信丰县| 都安| 铅山县| 大埔区| 滦平县| 临海市| 万盛区| 黔西| 施秉县| 香格里拉县| 新田县| 子长县| 天镇县| 车致| 枝江市| 余庆县| 白河县| 黄陵县| 柞水县| 珠海市| 刚察县| 新源县| 新建县| 海宁市| 鄂尔多斯市| 江阴市| 磐石市| 临漳县| 芷江| 定州市| 双流县| 南平市| 孙吴县| 北辰区| 阳朔县| 石门县| 积石山|