小说区 亚洲 自拍 另类_91啪在线观看国产_2023年国漫在线观看免费_久久久久久久av_国产AV人人夜夜澡人人爽

關(guān)注

首頁 > 關(guān)注 > 正文
記錄--前端如何優(yōu)雅導(dǎo)出多表頭xlsx 全球快播
發(fā)布時間:2023-06-15 19:27:04   來源:博客園  

這里給大家分享我在網(wǎng)上總結(jié)出來的一些知識,希望對大家有所幫助

前言

xlsx導(dǎo)出是比較前后端開發(fā)過程中都比較常見的一個功能。但傳統(tǒng)的二維表格可能很難能滿足我們對業(yè)務(wù)的需求,因為當(dāng)數(shù)據(jù)的維度和層次比較多時,二維表格很難以清晰和壓縮的方式展現(xiàn)所有的信息,所以我們也就經(jīng)常能碰到多級表頭開發(fā)了。


(相關(guān)資料圖)

demo

每當(dāng)我們新使用一個插件的時候,我們都可以看著官方文檔去新建立一個demo,然后去嘗試一下效果,這有助于我們分析錯誤。

npm i xlsx -S
function exportFile() {  const ws = utils.json_to_sheet([])  const wb = utils.book_new()  utils.sheet_add_aoa(ws, [    [1, 2, 3, 4, 5, 6, 7, 8, 9],     ["a", "b", "c", "d", "e", "f", "g", "h", "i"]  ], { origin: "A1" })  utils.book_append_sheet(wb, ws, "Data")  writeFileXLSX(wb, "SheetJSVueAoO.xlsx")}exportFile()

demo已經(jīng)成功了,xlsx已經(jīng)下載下來了。

需求分析

  1. 新建一個表格
  2. 根據(jù)表頭將表格進(jìn)行合并
  3. 對合并后的表頭進(jìn)行內(nèi)容填充
  4. 填入數(shù)據(jù)內(nèi)容

效果如上圖(時間原因就先不寫xlsx的樣式了)。

需求實現(xiàn)

  1. 合并單元格: 需要指定開始的行和列以及結(jié)束的行和列,如{ "s": { "r": 0, "c": 0 }, "e": { "r": 3, "c": 0 } },計算好需要合并的單元格后統(tǒng)一賦值給!merges屬性。
  2. 合并單元格后填充內(nèi)容:由多個合并后的單元格填入內(nèi)容時,應(yīng)該也按照多個單元格填入,只是第一個有內(nèi)容,其他按空填入即可。
  3. 表頭結(jié)束后我們可以指定在某一行繼續(xù)填入內(nèi)容,即可繼續(xù)填入數(shù)據(jù)內(nèi)容。
function exportFile() {  const ws = utils.json_to_sheet([])  ws["!merges"] = [    { "s": { "r": 0, "c": 0 }, "e": { "r": 3, "c": 0 } },    { "s": { "r": 0, "c": 1 }, "e": { "r": 3, "c": 1 } },    { "s": { "r": 0, "c": 2 }, "e": { "r": 3, "c": 2 } },    { "s": { "r": 0, "c": 3 }, "e": { "r": 0, "c": 8 } },    { "s": { "r": 1, "c": 3 }, "e": { "r": 3, "c": 3 } },    { "s": { "r": 1, "c": 4 }, "e": { "r": 1, "c": 7 } },    { "s": { "r": 2, "c": 4 }, "e": { "r": 3, "c": 4 } },    { "s": { "r": 2, "c": 5 }, "e": { "r": 3, "c": 5 } },    { "s": { "r": 2, "c": 6 }, "e": { "r": 2, "c": 7 } },    { "s": { "r": 1, "c": 8 }, "e": { "r": 3, "c": 8 } },    { "s": { "r": 0, "c": 9 }, "e": { "r": 3, "c": 9 } }  ] // 合并單元格內(nèi)容  const wb = utils.book_new()  utils.book_append_sheet(wb, ws, "Data")  utils.sheet_add_aoa(ws, [    ["序號", "姓名", "性別", "公司概況", "", "", "", "", "", "備注"],    ["", "", "", "職位", "項目", "", "", "", "公司名稱"],    ["", "", "", "", "項目時長", "項目描述", "金額", ""],    ["", "", "", "", "", "", "總金額", "利潤"]  ], { origin: "A1" }) // 表頭內(nèi)容  utils.sheet_add_aoa(ws, [    [0, "張三", "男", "區(qū)域經(jīng)理", "3天", "暫無描述", 998, 9.98, "阿里巴巴", "暫無"],    [1, "李四", "女", "CEO", "30天", "穩(wěn)了", 998, 9.98, "中石油", "暫無"]  ], { origin: "A5" }) // 數(shù)據(jù)內(nèi)容  writeFileXLSX(wb, `${+new Date()}.xlsx`)}
好的,大功告成,今天就先到這里?

這東西也太丑了吧,我是一個開發(fā),我不是來這里數(shù)格子的??纯瓷厦娴拇a,我都不好意思說是我自己寫的。要不到同事電腦上提交一下吧?

數(shù)據(jù)分析

[    { "s": { "r": 0, "c": 0 }, "e": { "r": 3, "c": 0 } },    { "s": { "r": 0, "c": 1 }, "e": { "r": 3, "c": 1 } },    { "s": { "r": 0, "c": 2 }, "e": { "r": 3, "c": 2 } },    { "s": { "r": 0, "c": 3 }, "e": { "r": 0, "c": 8 } },    { "s": { "r": 1, "c": 3 }, "e": { "r": 3, "c": 3 } },    { "s": { "r": 1, "c": 4 }, "e": { "r": 1, "c": 7 } },    { "s": { "r": 2, "c": 4 }, "e": { "r": 3, "c": 4 } },    { "s": { "r": 2, "c": 5 }, "e": { "r": 3, "c": 5 } },    { "s": { "r": 2, "c": 6 }, "e": { "r": 2, "c": 7 } },    { "s": { "r": 1, "c": 8 }, "e": { "r": 3, "c": 8 } },    { "s": { "r": 0, "c": 9 }, "e": { "r": 3, "c": 9 } }]

我想要轉(zhuǎn)成上面的數(shù)據(jù)結(jié)構(gòu),r從0開始,最大值就是它的深度,c從0開始,最大值就是它的廣度。因為這是一個多級表頭,每一級都會出現(xiàn)比上一級相等或更多子級的情況,我好像已經(jīng)把答案說到嘴邊了。對,就是用樹形結(jié)構(gòu)將其轉(zhuǎn)換處理。

我們結(jié)合上面已轉(zhuǎn)換好的列表結(jié)構(gòu)和下面準(zhǔn)備轉(zhuǎn)換的樹形結(jié)構(gòu),比如現(xiàn)在要合并第一個單元格序號,我們應(yīng)該先找到起始位置,也就是0,0,這個很好確定;我們單單從當(dāng)前節(jié)點并不能判斷真正的結(jié)束位置,我們應(yīng)該找到同級節(jié)點的最大深度,也就是公司概況->項目->金額->總金額,深度為3。所以它的結(jié)束位置應(yīng)該為3,0。

當(dāng)我們要合并橫向單元格的時候,比如公司概況,它下邊有三個子節(jié)點分別是職位,項目,公司名稱,而子節(jié)點下方仍有不同的子節(jié)點,此時我們就應(yīng)該去獲取它們的每個子節(jié)點的每層子節(jié)點的總長度 - 1,為什么要 - 1,因為當(dāng)前節(jié)點和第一個子節(jié)點占用的是同一個col,因此可以需要減一。也就是說,如果公司概況的起始點為0,3,那么它的終止位置由此可推:職位+項目+公司名稱-1+項目時長+項目描述+金額-1+總金額+利潤-1= 5。所以終點位置為0,3+5=> 0,8

const mergedCells = [     { name: "序號", prop: "id" },    { name: "姓名", prop: "name" },    { name: "性別", prop: "sex" },    {         name: "公司概況",         children: [             { name: "職位", prop: "jobTitle" },            {                 name: "項目", children: [                    { name: "項目時長", prop: "projectTime" },                    { name: "項目描述", prop: "projectDesc" },                    {                         name: "金額",                         children: [                            { name: "總金額", prop: "total" },                            { name: "利潤", prop: "profit" }                        ]                     }                 ]             },             { name: "公司名稱", prop: "companyName" }        ]     },    { name: "備注", prop: "remark" } ]

思路分析

  1. 找到當(dāng)前節(jié)點的深度和廣度
  2. 根據(jù)當(dāng)前節(jié)點深度和廣度,生成當(dāng)前節(jié)點單元格開始與結(jié)束位置
  3. 根據(jù)當(dāng)前節(jié)點深度和廣度,生成表頭數(shù)據(jù)結(jié)構(gòu)
  4. 根據(jù)最大深度位置,生成表單列表數(shù)據(jù)

代碼實現(xiàn)

tips: 如果你對樹結(jié)構(gòu)的遍歷還不太熟悉,可以看看【前端不求人】樹形結(jié)構(gòu)和一維數(shù)組,一笑泯恩仇

獲取當(dāng)前節(jié)點最大廣度和最大深度

  1. 遞歸發(fā)現(xiàn)當(dāng)前已無子節(jié)點時,就返回0,然后每返回一層就遞增1,每次返回時都獲取當(dāng)前節(jié)點的最大值,這樣就能獲得最深層數(shù)。
  2. 遞歸記錄每層每個子節(jié)點的長度 - 1,這樣就能獲取當(dāng)前列表的最大寬度。
  3. 我們使用map做記錄,下次獲取就不需要重新計算了。
const map = new Map()const getCellsSize = list => {    if (map.has(list)) { return map.get(list) }    if (list?.length) {        let rows = -1, cols = list.length - 1        list.forEach(item => {            if (item.children) {                const size = getCellsSize(item.children)                rows = Math.max(size[0], rows)                cols += size[1]            }        })        map.set(list, [rows + 1, cols])        return [rows + 1, cols]    }}

合并單元格開始和結(jié)束位置

  1. 獲取當(dāng)前節(jié)點的開始和結(jié)束位置
  2. 當(dāng)前節(jié)點無子節(jié)點,單元格寬為1,高為整個根節(jié)點的最大深度
  3. 當(dāng)前節(jié)點有子節(jié)點,單元格高為1,寬為當(dāng)前節(jié)點的寬,即最大廣度
const size = getCellsSize(headers)const headerMerge = []const mergeHeadersCell = (headers, row, col) => {    for (let i = 0, len = headers.length;i < len;i++) {        const cell = headers[i]        if (!cell.children?.length) {            if (row === size[0]) { continue }            headerMerge.push({ s: { r: row, c: col + i }, e: { r: size[0], c: col + i } })        } else {            const size = map.get(cell.children)            headerMerge.push({ s: { r: row, c: col + i }, e: { r: row, c: col + size[1] + i }})            mergeHeadersCell(cell.children, row + 1, col + i)            col += size[1]        }    }}

多表頭值填充

  1. 我們聲明一個headerValue的空數(shù)組來記錄表頭內(nèi)容
  2. headerValue應(yīng)該是一個二維數(shù)組,headerValue[i][j]代表第i行第j列的內(nèi)容
  3. 當(dāng)發(fā)現(xiàn)當(dāng)前節(jié)點有children,直接獲取當(dāng)前節(jié)點的寬度,該寬度就是合并后空白單元格的個數(shù)。
  4. 當(dāng)發(fā)現(xiàn)當(dāng)前節(jié)點并沒有headerValue,表示前面的節(jié)點被縱向合并了,因此應(yīng)該直接加上這些空白單元格的節(jié)點
const headerValue = []  const getHeadersValue = (headers, row, col) => {    if (!headerValue[row]) {      headerValue[row] = new Array(col).fill("")    }    for (let i = 0, len = headers.length; i < len; i++) {      const cell = headers[i]      headerValue[row].push(cell.name)      if (cell.children?.length) {        const len = getCellsSize(cell.children)[1]        const emptyNameList = new Array(len).fill("")        headerValue[row].push(...emptyNameList)        getHeadersValue(cell.children, row + 1, col + i)      }    }  }

獲取列表prop

  1. 繼續(xù)遞歸mergedCells
  2. 收集無葉子節(jié)點的prop值
  3. 將prop值依次放進(jìn)一個數(shù)組中以備后續(xù)使用
const bodyMapList = []const getBodyMapList = list => {    if (list?.length) {        list.forEach(item => {            !item.children ? bodyMapList.push(item.prop) : getBodyMapList(item.children)        })    }}list.map(item => bodyMapList.map(key => item[key]))

以上就是核心代碼展示啦,如果想看完整代碼,可以到github觀看,歡迎star。

總結(jié)

我們通過計算當(dāng)前樹節(jié)點的大小,就可以獲取該節(jié)點的廣度和深度,通過廣度和深度又可以讓我們進(jìn)一步去演算當(dāng)前節(jié)點是否需要去合并其他單元格,是否需要生成空白單元格的數(shù)據(jù)內(nèi)容。生成表格內(nèi)容則只需要將最子層節(jié)點的prop收集,然后對應(yīng)取值即可。

本文轉(zhuǎn)載于:

https://juejin.cn/post/7243435843145678907

如果對您有所幫助,歡迎您點個關(guān)注,我會定時更新技術(shù)文檔,大家一起討論學(xué)習(xí),一起進(jìn)步。

關(guān)鍵詞:

推薦內(nèi)容

Copyright @  2015-2022 太平洋器材網(wǎng)版權(quán)所有  備案號: 豫ICP備2022016495號-17   聯(lián)系郵箱:93 96 74 66 9@qq.com