Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
Y
yifu-study-front-share
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
CI / CD
CI / CD
Pipelines
Schedules
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
yifu-study
front
yifu-study-front-share
Commits
8e477e08
Commit
8e477e08
authored
May 24, 2022
by
liyan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
5月分享
parent
95a67035
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
515 additions
and
0 deletions
+515
-0
用xlsx-style玩转Excel导出——拿走即用的Excel导出方法封装.md
source/_posts/用xlsx-style玩转Excel导出——拿走即用的Excel导出方法封装.md
+515
-0
No files found.
source/_posts/用xlsx-style玩转Excel导出——拿走即用的Excel导出方法封装.md
0 → 100644
View file @
8e477e08
---
title
:
用xlsx-style玩转Excel导出——拿走即用的Excel导出方法封装
date
:
2022/5/24 09:15:20
tags
:
-
前端
-
xlsx-style
-
JavaScript
author
:
李燕
---
在
[
# 用xlsx-style玩转Excel导出——像手动操作Excel表格一样用JS操作Excel
](
https://juejin.cn/post/7090185856832503844
)
一文中,我们详细介绍了xlsx-style插件相关的概念、属性和方法,今天,我们将利用xlsx-style插件封装出一个通用的、可自定义的Excel导出方法,以期能够满足日常Web项目的导出需求。
<!-- more -->
**一、常见自定义需求**
在正式开始封装之前,我们先梳理一下,我们Excel导出一般都有那些自定义需求。
1.
导出的Excel文件名自定义
2.
支持单sheet、多sheet导出
3.
sheet名自定义
4.
表头内容自定义
5.
表格内容自定义
6.
表格样式自定义
7.
支持合并单元格
8.
支持冻结单元格
**二、抽象导出方法名及参数**
根据上述自定义需求,我们可以初步将Excel导出方法及参数定义如下:
```
js
/**
* 自定义导出excel
* @param { 导出的数据 } exportData
* @param { 工作簿配置 } workBookConfig
* @param { 导出文件名 } fileName
*/
function
exportExcel
(
exportData
,
workBookConfig
=
defaultWorkBook
,
fileName
=
'未命名'
)
{
}
```
**三、参数介绍及处理**
(一)exportData
参数exportData是一个对象数组,可以满足单sheet、多sheet的导出,同时可以配置每个sheet的表头、表格内容、样式等,格式如下:
```
js
[
{
header
:
[]
,
// 表头
dataSource
:
[],
// 数据源
workSheetConfig
:
{},
// 工作表配置
cellConfig
:
{}
,
// 单元格配置
sheetName
:
''
,
// sheet名
}
]
```
+
header
header是一个对象数组,用来设置表头,格式如下:
```
js
[
{
title
:
'分站'
,
dataIndex
:
'subName'
,
width
:
140
},
{
title
:
'企业总数'
,
dataIndex
:
'enterpriseCount'
,
width
:
140
},
]
```
接收到header后,需将header内容处理成表格语言,具体如下:
```js
const _header = header.map((item, i) =>
Object.assign({}, {
key: item.dataIndex,
title: item.title,
// 定位单元格
position: getCharCol(i) + 1,
// 设置表头样式
s: data.cellConfig && data.cellConfig.headerStyle ? data.cellConfig.headerStyle : defaultCellStyle.headerStyle,
})
).reduce((prev, next) =>
Object.assign({}, prev, {
[next.position]: { v: next.title, key: next.key,s: next.s },
}), {},
)
```
其中,_header的打印格式如下:

从上述打印可以看出,header已经被处理成worksheet Object,而其中很重要一步,就是定位单元格,而我们Excel是用字母+数字的形式定位单元格的,所以我们可以通过如下代码定位单元格。
```
js
position
:
getCharCol
(
i
)
+
1
```
其中,getCharCol()方法是用来生成单元格对应的字母的,代码如下:
```
js
/**
* 生成ASCll值 从A开始
* @param {*} n
*/
function
getCharCol
(
n
)
{
if
(
n
>
25
)
{
let
s
=
''
let
m
=
0
while
(
n
>
0
)
{
m
=
n
%
26
+
1
s
=
String
.
fromCharCode
(
m
+
64
)
+
s
n
=
(
n
-
m
)
/
26
}
return
s
}
return
String
.
fromCharCode
(
65
+
n
)
}
```
+
cellConfig
cellConfig是一个对象,用来配置单元格的样式,格式如下:
```
js
{
headerStyle
:
{
// 表头区域样式配置
border
:
{}
,
font
:
{},
alignment
:
{},
fill
:
{},
},
dataStyle
:
{
// 内容样式配置
border
:
{}
,
font
:
{},
alignment
:
{},
fill
:
{},
}
}
```
+
dataSource
dataSource是一个对象数组,用来表示表格内容,格式如下:
```
js
[
{
subName
:
'合肥'
},
{
enterpriseCount
:
'100'
},
]
```
同样,接收到dataSource后,需将dataSource内容处理成表格语言,具体如下:
```js
let _data = {};
// 处理表格内容及样式
dataSource.forEach((item, i) => {
header.forEach((obj, index) => {
const key = getCharCol(index) + (i + 2);
const key_t = obj.dataIndex;
_data[key] = {
v: item[key_t],
s: cellConfig.dataStyle? cellConfig.dataStyle : defaultCellStyle.dataStyle
}
})
});
```
+
workSheetConfig
workSheetConfig是一个对象,用来配置工作表的合并单元格、冻结单元格等功能,格式如下:
```
js
{
merges
:
[
// 合并单元格
{
s
:
{
c
:
1
,
r
:
1
},
e
:
{
c
:
3
,
r
:
3
}
}
],
freeze
:
{
// 冻结单元格
xSplit
:
'1'
,
ySplit
:
'1'
,
topLeftCell
:
'B2'
,
state
:
'frozen'
,
}
}
```
(二)workBookConfig
workBookConfig是一个对象,用来配置工作簿的类型等,格式如下:
```
js
{
bookType
:
'xlsx'
,
// 工作簿的类型
bookSST
:
false
,
type
:
'binary'
,
// 输出数据类型
}
```
**四、生成workbook object**
```
js
const
wb
=
{
SheetNames
:
[],
Sheets
:
{}}
exportData
.
forEach
(
(
data
,
index
)
=>
{
const
output
=
Object
.
assign
({},
_headers
,
_data
);
const
outputPos
=
Object
.
keys
(
output
);
// 设置单元格宽度
const
colWidth
=
data
.
header
.
map
(
item
=>
{
wpx
:
item
.
width
||
60
})
const
merges
=
data
.
workSheetConfig
&&
data
.
workSheetConfig
.
merges
const
freeze
=
data
.
workSheetConfig
&&
data
.
workSheetConfig
.
freeze
// 处理sheet名
wb
.
SheetNames
[
index
]
=
data
.
sheetName
?
data
.
sheetName
:
'Sheet'
+
(
index
+
1
)
// 处理sheet数据
wb
.
Sheets
[
wb
.
SheetNames
[
index
]]
=
Object
.
assign
({},
output
,
//导出的内容
{
'!ref'
:
`
${
outputPos
[
0
]}
:
${
outputPos
[
outputPos
.
length
-
1
]}
`
,
'!cols'
:
[...
colWidth
],
'!merges'
:
merges
?
[...
merges
]
:
undefined
,
'!freeze'
:
freeze
?
[...
freeze
]
:
undefined
,
}
)
})
```
**五、new Blob转换成二进制类型的对象**
```
js
const
tmpDown
=
new
Blob
(
[
s2ab
(
XLSX
.
write
(
wb
,
workBookConfig
))],
{
type
:
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
}
)
// 字符串转字符流---转化为二进制的数据流
function
s2ab
(
s
)
{
if
(
typeof
ArrayBuffer
!==
'undefined'
)
{
const
buf
=
new
ArrayBuffer
(
s
.
length
);
const
view
=
new
Uint8Array
(
buf
);
for
(
let
i
=
0
;
i
!==
s
.
length
;
++
i
)
view
[
i
]
=
s
.
charCodeAt
(
i
)
&
0xff
;
return
buf
;
}
else
{
const
buf
=
new
Array
(
s
.
length
);
for
(
let
i
=
0
;
i
!==
s
.
length
;
++
i
)
buf
[
i
]
=
s
.
charCodeAt
(
i
)
&
0xff
;
return
buf
;
}
}
```
**六、下载Excel**
```
js
downExcel
(
tmpDown
,
`
${
fileName
+
'.'
}${
workBookConfig
.
bookType
==
'biff2'
?
'xls'
:
workBookConfig
.
bookType
}
`
)
function
downExcel
(
obj
,
fileName
)
{
const
a_node
=
document
.
createElement
(
'a'
);
a_node
.
download
=
fileName
;
// 兼容ie
if
(
'msSaveOrOpenBlob'
in
navigator
)
{
window
.
navigator
.
msSaveOrOpenBlob
(
obj
,
fileName
);
}
else
{
// 新的对象URL指向执行的File对象或者是Blob对象.
a_node
.
href
=
URL
.
createObjectURL
(
obj
);
}
a_node
.
click
();
setTimeout
(()
=>
{
URL
.
revokeObjectURL
(
obj
);
},
100
);
}
```
**七、默认配置**
```
js
// 默认工作簿配置
const
defaultWorkBook
=
{
bookType
:
'xlsx'
,
bookSST
:
false
,
type
:
'binary'
,
}
// 默认样式配置
const
borderAll
=
{
//单元格外侧框线
top
:
{
style
:
'thin'
,
},
bottom
:
{
style
:
'thin'
},
left
:
{
style
:
'thin'
},
right
:
{
style
:
'thin'
}
};
const
defaultCellStyle
=
{
headerStyle
:
{
// 表头区域样式配置
border
:
borderAll
,
font
:
{
name
:
'宋体'
,
sz
:
11
,
italic
:
false
,
underline
:
false
,
bold
:
true
},
alignment
:
{
vertical
:
'center'
,
horizontal
:
'center'
},
fill
:
{
fgColor
:
{
rgb
:
'FFFFFF'
}
},
},
dataStyle
:
{
// 内容样式配置
border
:
borderAll
,
font
:
{
name
:
'宋体'
,
sz
:
11
,
italic
:
false
,
underline
:
false
},
alignment
:
{
vertical
:
'center'
,
horizontal
:
'left'
,
wrapText
:
true
},
fill
:
{
fgColor
:
{
rgb
:
'FFFFFF'
}
},
}
}
```
**八、完整代码**
```
js
import
XLSX
from
'xlsx-style'
// 默认工作簿配置
const
defaultWorkBook
=
{
bookType
:
'xlsx'
,
bookSST
:
false
,
type
:
'binary'
,
}
// 默认样式配置
const
borderAll
=
{
top
:
{
style
:
'thin'
,
},
bottom
:
{
style
:
'thin'
,
},
left
:
{
style
:
'thin'
,
},
right
:
{
style
:
'thin'
,
},
}
const
defaultCellStyle
=
{
// 表头区域样式配置
headerStyle
:
{
border
:
borderAll
,
font
:
{
name
:
'宋体'
,
sz
:
11
,
italic
:
false
,
underline
:
false
,
bold
:
true
},
alignment
:
{
vertical
:
'center'
,
horizontal
:
'center'
},
fill
:
{
fgColor
:
{
rgb
:
'FFFFFF'
}
},
},
// 内容区域样式配置
dataStyle
:
{
border
:
borderAll
,
font
:
{
name
:
'宋体'
,
sz
:
11
,
italic
:
false
,
underline
:
false
},
alignment
:
{
vertical
:
'center'
,
horizontal
:
'left'
,
wrapText
:
true
},
fill
:
{
fgColor
:
{
rgb
:
'FFFFFF'
}
},
},
}
function
exportExcel
(
exportData
,
workBookConfig
=
defaultWorkBook
,
fileName
=
'未命名'
)
{
if
(
!
(
exportData
&&
exportData
.
length
))
{
return
}
// 定义工作簿对象
const
wb
=
{
SheetNames
:
[],
Sheets
:
{}
}
exportData
.
forEach
((
data
,
index
)
=>
{
// 处理sheet表头
const
_header
=
data
.
header
.
map
((
item
,
i
)
=>
Object
.
assign
({},
{
key
:
item
.
dataIndex
,
title
:
item
.
title
,
// 定位单元格
position
:
getCharCol
(
i
)
+
1
,
// 设置表头样式
s
:
data
.
cellConfig
&&
data
.
cellConfig
.
headerStyle
?
data
.
cellConfig
.
headerStyle
:
defaultCellStyle
.
headerStyle
,
})
).
reduce
((
prev
,
next
)
=>
Object
.
assign
({},
prev
,
{
[
next
.
position
]:
{
v
:
next
.
title
,
key
:
next
.
key
,
s
:
next
.
s
},
}),
{}
)
// 处理sheet内容
const
_data
=
{}
data
.
dataSource
.
forEach
((
item
,
i
)
=>
{
data
.
header
.
forEach
((
obj
,
index
)
=>
{
const
key
=
getCharCol
(
index
)
+
(
i
+
2
)
const
key_t
=
obj
.
dataIndex
_data
[
key
]
=
{
v
:
item
[
key_t
],
s
:
data
.
cellConfig
&&
data
.
cellConfig
.
dataStyle
?
data
.
cellConfig
.
dataStyle
:
defaultCellStyle
.
dataStyle
,
}
})
})
const
output
=
Object
.
assign
({},
_header
,
_data
)
const
outputPos
=
Object
.
keys
(
output
)
// 设置单元格宽度
const
colWidth
=
data
.
header
.
map
(
item
=>
{
return
{
wpx
:
item
.
width
||
80
}
})
const
merges
=
data
.
workSheetConfig
&&
data
.
workSheetConfig
.
merges
const
freeze
=
data
.
workSheetConfig
&&
data
.
workSheetConfig
.
freeze
// 处理sheet名
wb
.
SheetNames
[
index
]
=
data
.
sheetName
?
data
.
sheetName
:
'Sheet'
+
(
index
+
1
)
// 处理sheet数据
wb
.
Sheets
[
wb
.
SheetNames
[
index
]]
=
Object
.
assign
({},
output
,
// 导出的内容
{
'!ref'
:
`
${
outputPos
[
0
]}
:
${
outputPos
[
outputPos
.
length
-
1
]}
`
,
'!cols'
:
[...
colWidth
],
'!merges'
:
merges
?
[...
merges
]
:
undefined
,
'!freeze'
:
freeze
?
[...
freeze
]
:
undefined
,
}
)
})
// 转成二进制对象
const
tmpDown
=
new
Blob
(
[
s2ab
(
XLSX
.
write
(
wb
,
workBookConfig
))],
{
type
:
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
}
)
// 下载表格
downExcel
(
tmpDown
,
`
${
fileName
+
'.'
}${
workBookConfig
.
bookType
===
'biff2'
?
'xls'
:
workBookConfig
.
bookType
}
`
)
}
/**
* 生成ASCll值 从A开始
* @param {*} n
*/
function
getCharCol
(
n
)
{
if
(
n
>
25
)
{
let
s
=
''
let
m
=
0
while
(
n
>
0
)
{
m
=
n
%
26
+
1
s
=
String
.
fromCharCode
(
m
+
64
)
+
s
n
=
(
n
-
m
)
/
26
}
return
s
}
return
String
.
fromCharCode
(
65
+
n
)
}
// 字符串转字符流---转化为二进制的数据流
function
s2ab
(
s
)
{
if
(
typeof
ArrayBuffer
!==
'undefined'
)
{
const
buf
=
new
ArrayBuffer
(
s
.
length
)
const
view
=
new
Uint8Array
(
buf
)
for
(
let
i
=
0
;
i
!==
s
.
length
;
++
i
)
{
view
[
i
]
=
s
.
charCodeAt
(
i
)
&
0xff
}
return
buf
}
else
{
const
buf
=
new
Array
(
s
.
length
)
for
(
let
i
=
0
;
i
!==
s
.
length
;
++
i
)
{
buf
[
i
]
=
s
.
charCodeAt
(
i
)
&
0xff
}
return
buf
}
}
function
downExcel
(
obj
,
fileName
)
{
const
a_node
=
document
.
createElement
(
'a'
)
a_node
.
download
=
fileName
// 兼容ie
if
(
'msSaveOrOpenBlob'
in
navigator
)
{
window
.
navigator
.
msSaveOrOpenBlob
(
obj
,
fileName
)
}
else
{
// 新的对象URL指向执行的File对象或者是Blob对象.
a_node
.
href
=
URL
.
createObjectURL
(
obj
)
}
a_node
.
click
()
setTimeout
(()
=>
{
URL
.
revokeObjectURL
(
obj
)
},
100
)
}
export
{
exportExcel
,
}
```
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment