這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
前言
xlsx匯出是比較前后端開發程序中都比較常見的一個功能,但傳統的二維表格可能很難能滿足我們對業務的需求,因為當資料的維度和層次比較多時,二維表格很難以清晰和壓縮的方式展現所有的資訊,所以我們也就經常能碰到多級表頭開發了,
demo
每當我們新使用一個插件的時候,我們都可以看著官方檔案去新建立一個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已經成功了,xlsx已經下載下來了,
需求分析
- 新建一個表格
- 根據表頭將表格進行合并
- 對合并后的表頭進行內容填充
- 填入資料內容

效果如上圖(時間原因就先不寫xlsx的樣式了),
需求實作
- 合并單元格: 需要指定開始的行和列以及結束的行和列,如
{ 's': { 'r': 0, 'c': 0 }, 'e': { 'r': 3, 'c': 0 } },計算好需要合并的單元格后統一賦值給!merges屬性, - 合并單元格后填充內容:由多個合并后的單元格填入內容時,應該也按照多個單元格填入,只是第一個有內容,其他按空填入即可,
- 表頭結束后我們可以指定在某一行繼續填入內容,即可繼續填入資料內容,
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 } }
] // 合并單元格內容
const wb = utils.book_new()
utils.book_append_sheet(wb, ws, 'Data')
utils.sheet_add_aoa(ws, [
['序號', '姓名', '性別', '公司概況', '', '', '', '', '', '備注'],
['', '', '', '職位', '專案', '', '', '', '公司名稱'],
['', '', '', '', '專案時長', '專案描述', '金額', ''],
['', '', '', '', '', '', '總金額', '利潤']
], { origin: 'A1' }) // 表頭內容
utils.sheet_add_aoa(ws, [
[0, '張三', '男', '區域經理', '3天', '暫無描述', 998, 9.98, '阿里巴巴', '暫無'],
[1, '李四', '女', 'CEO', '30天', '穩了', 998, 9.98, '中石油', '暫無']
], { origin: 'A5' }) // 資料內容
writeFileXLSX(wb, `${+new Date()}.xlsx`)
}
好的,大功告成,今天就先到這里?
這東西也太丑了吧,我是一個開發,我不是來這里數格子的,看看上面的代碼,我都不好意思說是我自己寫的,要不到同事電腦上提交一下吧?
資料分析
[
{ '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 } }
]

我想要轉成上面的資料結構,r從0開始,最大值就是它的深度,c從0開始,最大值就是它的廣度,因為這是一個多級表頭,每一級都會出現比上一級相等或更多子級的情況,我好像已經把答案說到嘴邊了,對,就是用樹形結構將其轉換處理,
我們結合上面已轉換好的串列結構和下面準備轉換的樹形結構,比如現在要合并第一個單元格序號,我們應該先找到起始位置,也就是0,0,這個很好確定;我們單單從當前節點并不能判斷真正的結束位置,我們應該找到同級節點的最大深度,也就是公司概況->專案->金額->總金額,深度為3,所以它的結束位置應該為3,0,
當我們要合并橫向單元格的時候,比如公司概況,它下邊有三個子節點分別是職位,專案,公司名稱,而子節點下方仍有不同的子節點,此時我們就應該去獲取它們的每個子節點的每層子節點的總長度 - 1,為什么要 - 1,因為當前節點和第一個子節點占用的是同一個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' }
]
思路分析
- 找到當前節點的深度和廣度
- 根據當前節點深度和廣度,生成當前節點單元格開始與結束位置
- 根據當前節點深度和廣度,生成表頭資料結構
- 根據最大深度位置,生成表單串列資料
代碼實作
tips: 如果你對樹結構的遍歷還不太熟悉,可以看看【前端不求人】樹形結構和一維陣列,一笑泯恩仇
獲取當前節點最大廣度和最大深度
- 遞回發現當前已無子節點時,就回傳0,然后每回傳一層就遞增1,每次回傳時都獲取當前節點的最大值,這樣就能獲得最深層數,
- 遞回記錄每層每個子節點的長度 - 1,這樣就能獲取當前串列的最大寬度,
- 我們使用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]
}
}
合并單元格開始和結束位置
- 獲取當前節點的開始和結束位置
- 當前節點無子節點,單元格寬為1,高為整個根節點的最大深度
- 當前節點有子節點,單元格高為1,寬為當前節點的寬,即最大廣度
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]
}
}
}
多表頭值填充
- 我們宣告一個headerValue的空陣列來記錄表頭內容
- headerValue應該是一個二維陣列,headerValue[i][j]代表第i行第j列的內容
- 當發現當前節點有children,直接獲取當前節點的寬度,該寬度就是合并后空白單元格的個數,
- 當發現當前節點并沒有headerValue,表示前面的節點被縱向合并了,因此應該直接加上這些空白單元格的節點
const headerValue = https://www.cnblogs.com/smileZAZ/archive/2023/06/15/[]
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
- 繼續遞回mergedCells
- 收集無葉子節點的prop值
- 將prop值依次放進一個陣列中以備后續使用
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,
總結
我們通過計算當前樹節點的大小,就可以獲取該節點的廣度和深度,通過廣度和深度又可以讓我們進一步去演算當前節點是否需要去合并其他單元格,是否需要生成空白單元格的資料內容,生成表格內容則只需要將最子層節點的prop收集,然后對應取值即可,
本文轉載于:
https://juejin.cn/post/7243435843145678907
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/555293.html
標籤:其他
下一篇:返回列表

