<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>

          可以實現(xiàn)一個前端的 Excel 導(dǎo)入和導(dǎo)出功能嗎?

          共 13391字,需瀏覽 27分鐘

           ·

          2022-10-21 14:19

          點擊上方 前端Q,關(guān)注公眾號

          回復(fù)加群,加入前端Q技術(shù)交流


          前言

          【**負(fù)責(zé)人 A】:現(xiàn)在報表部分基于接口的 Excel 的導(dǎo)入和導(dǎo)出功能有點慢,前端這邊能不能實現(xiàn)一下這個功能,然后我們在比對看看效果!**

          【**切圖仔 B**】:接口這邊不能優(yōu)化一下嗎?比如排查下慢的原因什么的。

          【**負(fù)責(zé)人 A】:現(xiàn)在后端開發(fā)任務(wù)比較重,處理的核心任務(wù)也多還會涉及一些架構(gòu)上的調(diào)整,所以想著前端這邊可以處理一下,然后看看整體效果。**

          【**切圖仔 B**】:OK,試試 就 ~ Shi Shi ~

          下面就基于 **`xlsx`**[2] 這個第三方庫封裝一個 <ExcelUpload /> 組件實現(xiàn)表格導(dǎo)入,以及 json 數(shù)據(jù)導(dǎo)出 Excel 功能的 json2Excel() 工具方法。

          選擇 **`xlsx`**[3] 的原因如下圖所示:

          Excel 解析為 JSON

          基本內(nèi)容

          組件效果和結(jié)構(gòu)

          組件內(nèi)容是很簡單的結(jié)構(gòu)和視圖,直接查看如下的頁面效果和代碼即可:

          <template>
            <input
              type="file"
              ref="excelRef"
              :accept="props.accept"
              @change="onChange"
              class="excel"
            />

            <h1>解析數(shù)據(jù):</h1>
            <h2>
              <code>{{ excelData }}</code>
            </h2>
          </template>

          <script setup lang="ts">
          import { reactive, ref } from 'vue'
          import * as XLSX from 'xlsx'

          interface Props {
            accept: string
          }

          const props = withDefaults(defineProps<Props>(), {
            accept:
              '.csv, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
          })

          const excelRef: any = ref(null)
          const excelData: any[] = reactive([])

          const onChange = (event: any) => {
            // 獲取文件對象
            const file = event.target.files[0]
            console.log(file)
          }
          </script>

          復(fù)制代碼

          Excel 數(shù)據(jù)格式

          Excel 數(shù)據(jù)格式有兩種,一種是 有表頭說明 的,另一種是 無表頭說明 的,具體內(nèi)容如下:

          • 有表頭說明

          • 無表頭說明

          實現(xiàn) Excel 轉(zhuǎn) JSON 功能

          核心步驟

          • 通過 FileReader 以二進(jìn)制的方式讀取 Excel 文件,即 fileReader.readAsBinaryString(file)

          • 將對應(yīng)的二進(jìn)制數(shù)據(jù)通過 XLSX.read(fileData, { type: "binary" }) 方法生成 workbook 對象

          • workbook.SheetNames[0] 獲取第一個 Sheet 的名稱 wsname,因為表格是有序列表,因此可以有多個 Sheet

            image.png
          • 通過 XLSX.utils.sheet_to_json(workbook.Sheets[wsname]) 方法將對應(yīng)的 Sheet 內(nèi)容轉(zhuǎn)換為 JSON 數(shù)據(jù)

          效果演示

          • 有表頭說明

          • 無表頭說明

          具體代碼

          // 讀取對應(yīng)表格文件
          const readerExcel = (file: File) => {
            const fileReader = new FileReader();

            // 以二進(jìn)制的方式讀取表格內(nèi)容
            fileReader.readAsBinaryString(file);

            // 表格內(nèi)容讀取完成
            fileReader.onload = (event: any) => {
              try {
                const fileData = event.target.result;
                const workbook = XLSX.read(fileData, {
                  type"binary",
                });

                // 表格是有序列表,因此可以取多個 Sheet,這里取第一個 Sheet
                const wsname = workbook.SheetNames[0];
                // 將表格內(nèi)容生成 json 數(shù)據(jù)
                const sheetJson = XLSX.utils.sheet_to_json(workbook.Sheets[wsname]);

                console.log(sheetJson); // 得到的表格 JSON 內(nèi)容
                
              } catch (e) {
                console.log(e);
                return false;
              }
            };
          };

          // 文件變化時觸發(fā)
          const onChange = (event) => {
            // 獲取文件對象
            const file = event.target.files[0];

            // 讀取文件內(nèi)容
            readerExcel(file);

            // 清除數(shù)據(jù)
            clearFile();
          };

          const clearFile = () => {
            excelRef.value.value = "";
          };
          復(fù)制代碼

          格式化 JSON 數(shù)據(jù)

          這里需要考慮 有表頭說明無表頭說明 的情況,為了方便統(tǒng)一處理,作如下規(guī)定:

          • 通過將 有表頭說明 的數(shù)據(jù)格式統(tǒng)一轉(zhuǎn)化為 無表頭說明 的數(shù)據(jù)格式
          • 統(tǒng)一將 無表頭說明 的數(shù)據(jù)格式轉(zhuǎn)化為標(biāo)準(zhǔn)的接口入?yún)ⅲ?{ key: value },這里需要建立一個 name -> key 的映射關(guān)系:
            const excelNameToKey = {
            '姓名'"name",
            '年齡'"age",
            '特長'"skill",
            '電話'"telephone",
            '地址'"address",
            };
            復(fù)制代碼

          格式化如下:

          核心代碼如下:

          <script setup lang='ts'>
          import { reactive, ref } from "vue";
          import * as XLSX from "xlsx";

          const excelNameToKey = {
            姓名: "name",
            年齡: "age",
            特長: "skill",
            電話: "telephone",
            地址: "address",
          };

          interface Props {
            accept: string;
          }

          const props = withDefaults(defineProps<Props>(), {
            accept:
              ".csv, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
          });

          const excelRef: any = ref(null);
          const excelData: any[] = reactive([]);

          const clearFile = () => {
            excelRef.value.value = "";
          };

          // 標(biāo)準(zhǔn)化 JSON 數(shù)據(jù)
          const excelDataToJson = (sheetJson: any[]) => {
            if (!sheetJson.length) return;

            let result = sheetJson;
            const hasTableHead = !!sheetJson[0].__EMPTY;

            // 擁有表頭的數(shù)據(jù),需要取正確的值
            if (hasTableHead) {
              const header = sheetJson.shift();
              const data: any[] = [];
              Object.keys(header).forEach((key) => {
                sheetJson.forEach((item, index) => {
                  const obj = data[index] || {};
                  obj[header[key]] = item[key];
                  data[index] = obj;
                });
              });

              console.log("【】【】", data);
              result = data;
            }

            // 將表格對應(yīng)的文字轉(zhuǎn)換為 key
            result.forEach((item) => {
              const newItem: any = {};
              Object.keys(item).forEach((key) => {
                newItem.title = key;
                newItem[excelNameToKey[key]] = item[key];
              });
              excelData.push(newItem);
            });
          };

          // 讀取對應(yīng)表格文件
          const readerExcel = (file: File) => {
            const fileReader = new FileReader();

            // 以二進(jìn)制的方式讀取表格內(nèi)容
            fileReader.readAsBinaryString(file);

            // 表格內(nèi)容讀取完成
            fileReader.onload = (event: any) => {
              try {
                const fileData = event.target.result;
                const workbook = XLSX.read(fileData, {
                  type"binary",
                });

                // 表格是有序列表,因此可以取多個 Sheet,這里取第一個 Sheet
                const wsname = workbook.SheetNames[0];
                // 將表格內(nèi)容生成 json 數(shù)據(jù)
                const sheetJson = XLSX.utils.sheet_to_json(workbook.Sheets[wsname]);

                console.log(sheetJson);

                // 標(biāo)準(zhǔn)化 JSON 數(shù)據(jù)
                excelDataToJson(sheetJson);
              } catch (e) {
                console.log(e);
                return false;
              }
            };
          };

          // 文件變化時觸發(fā)
          const onChange = (event) => {
            // 獲取文件對象
            const file = event.target.files[0];

            // 讀取文件內(nèi)容
            readerExcel(file);

            // 清除數(shù)據(jù)
            clearFile();
          };
          </script>
          復(fù)制代碼

          JSON 導(dǎo)出為 Excel

          基本結(jié)構(gòu)

          頁面內(nèi)容也非常簡單,具體如下:

          <template>
            <div id="container">
              <h1>JSON 數(shù)據(jù):</h1>
              <h2>
                <code>
                  {{ jsonData }}
                </code>
              </h2>

              <button @click="exportExcel">導(dǎo)出 Excel</button>
            </div>

          </template>
          復(fù)制代碼

          導(dǎo)出功能

          導(dǎo)出其實也很簡單,首先創(chuàng)建 src/utils/json2Excel.ts 文件里面就是具體導(dǎo)出的實現(xiàn),具體內(nèi)容如下:

          // src/utils/json2Excel.ts

          import * as XLSX from "xlsx";

          export default (
            data: any[],
            sheetName: string = "sheet1",
            fileName: string = "json2Excel.xlsx"
          ) => {
            const jsonWorkSheet = XLSX.utils.json_to_sheet(data);
            
            const workBook = {
              SheetNames: [sheetName], // 指定有序 sheet 的 name
              Sheets: {
                [sheetName]: jsonWorkSheet, // 表格數(shù)據(jù)內(nèi)容
              },
            };

            return XLSX.writeFile(workBook, fileName); // 向文件系統(tǒng)寫出文件
          };
          復(fù)制代碼

          然后在 App.vue 中使用,具體如下:

          // src/App.vue

          <script setup lang="ts">

          import json2Excel from "./utils/json2Excel";

          // 測試的 JSON 數(shù)據(jù)
          const jsonData = [
            {
              name"張三1",
              age18,
              skill"干飯1",
              telephone20200825,
              address"宇宙盡頭1",
            },
            {
              name"張三2",
              age19,
              skill"干飯2",
              telephone20200826,
              address"宇宙盡頭2",
            },
            {
              name"張三3",
              age20,
              skill"干飯3",
              telephone20200827,
              address"宇宙盡頭3",
            },
            {
              name"張三4",
              age21,
              skill"干飯4",
              telephone20200828,
              address"宇宙盡頭4",
            },
            {
              name"張三5",
              age22,
              skill"干飯5",
              telephone20200829,
              address"宇宙盡頭5",
            },
            {
              name"張三6",
              age23,
              skill"干飯6",
              telephone20200830,
              address"宇宙盡頭6",
            },
            {
              name"張三7",
              age24,
              skill"干飯7",
              telephone20200831,
              address"宇宙盡頭7",
            },
            {
              name"張三8",
              age25,
              skill"干飯8",
              telephone20200832,
              address"宇宙盡頭8",
            },
            {
              name"張三9",
              age26,
              skill"干飯9",
              telephone20200833,
              address"宇宙盡頭9",
            },
            {
              name"張三10",
              age27,
              skill"干飯10",
              telephone20200834,
              address"宇宙盡頭10",
            },
          ];

          // key -> name 的映射
          const excelKeyToName = {
            name"姓名",
            age"年齡",
            skill"特長",
            telephone"電話",
            address"地址",
          };

          // 導(dǎo)出 Excel 文件
          const exportExcel = () => {
            // 格式化參數(shù)
            const data = jsonData.map((item) => {
              const newItem: any = {};
              Object.keys(item).forEach(key => {
                newItem[excelKeyToName[key]] = item[key];
              });
              return newItem;
            });
           
            // 導(dǎo)出 Excel
            json2Excel(data);
          };
          </script>
          復(fù)制代碼

          效果演示

          最后

          以上只是實現(xiàn)了簡單的單個導(dǎo)入、導(dǎo)出功能,可以將其完善為 批量操作,但是要注意批量操作帶來的耗時性,將對應(yīng)的耗時部分通過 webworker 等方式處理,這樣頁面就不需要一直等待當(dāng)前的操作完成。

          另外,如果有要求在導(dǎo)出 Excel 時有表格樣式(如:行列寬高設(shè)置等)可以通過 **`xlsx-populate`**[4] 來實現(xiàn)。

          以上就是本文的全部內(nèi)容,希望上述內(nèi)容可以給大家?guī)硪恍┧悸罚梢栽谠u論區(qū)貢獻(xiàn)更優(yōu)質(zhì)的方案。


          關(guān)于本文

          作者:熊的貓

          https://juejin.cn/post/7135945969425711111

          往期推薦


          前端架構(gòu)帶你 封裝axios,一次封裝終身受益「美團(tuán)后端連連點贊」
          5 種瀑布流場景的實現(xiàn)原理解析
          純前端實現(xiàn)「羊了個羊」小游戲

          最后


          • 歡迎加我微信,拉你進(jìn)技術(shù)群,長期交流學(xué)習(xí)...

          • 歡迎關(guān)注「前端Q」,認(rèn)真學(xué)前端,做個專業(yè)的技術(shù)人...

          點個在看支持我吧

          瀏覽 32
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  九九九九九九九九九九九精品 | 亚洲人妻无码视频 | 日韩天天操 | 色之综合天天综合色天天素质 | 91插插插插插插 |