目录
4.先看一下 我这里是封装导出方法来做的,不需要封装的可直接放到页面中去
2.使用tableId 导出 带有合计行 的表格(带有合计行并且有多级表头 底部合计行会显示错乱)
准备工作:
1.安装依赖/引入页面
npm install xlsx xlsx-style-vite file-saver --save
# 或
yarn add xlsx xlsx-style-vite file-saver
import * as XLSX from "xlsx"; //默认导出无法修改样式
import FileSaver from "file-saver";
// @ts-ignore
import * as XLSX_STYLE from "xlsx-style-vite"; // 需要自定义样式 必须安装
2.解决:导出excel合并单元格后边框缺失问题
//第一种 直接根据表格id $table 导出excel
let ws = XLSX.utils.table_to_sheet(document.querySelector($table)); //表格id $table
//第二种 自己转化自定义数据 转化为二维数组 data 再导出excel
let ws = XLSX.utils.aoa_to_sheet(data) //data 二维数组
const mergeCell = (ws) => {
const borderStyle = {
top: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
bottom: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
left: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
right: {style: 'thin', color: {rgb: 'CCCCCCCC'}}
};
// 遍历所有单元格,确保合并区域每个单元格都有边框
const range = XLSX.utils.decode_range(ws['!ref']);
for (let R = range.s.r; R <= range.e.r; R++) {
for (let C = range.s.c; C <= range.e.c; C++) {
const cellAddr = XLSX.utils.encode_cell({r: R, c: C});
if (!ws[cellAddr]) ws[cellAddr] = {};
ws[cellAddr].s = {...ws[cellAddr].s, border: borderStyle}; // 显式设置边框:ml-citation{ref="4,8" data="citationList"}
}
}
return ws
};
3.单元格样式/边框颜色/背景色/文字 等样式 设置
s: {
border: {
top: {style: 'thin', color: {rgb: '000000'}},
bottom: {style: 'thin', color: {rgb: '000000'}},
left: {style: 'thin', color: {rgb: '000000'}},
right: {style: 'thin', color: {rgb: '000000'}},
},
alignment: {
horizontal: 'center',//水平居中对齐
vertical: 'center',//垂直居中
wrapText: true,//自动换行
},
fill: {
fgColor: { rgb: 'cccccc' },
},
}
4.先看一下 我这里是封装导出方法来做的,不需要封装的可直接放到页面中去
这里有三种形式:
1.使用tableId 直接导出页面所显示表格
表格ui图
完整代码
导出按钮 以及参数详解
<script lang="ts" name="trafficStatistics" setup>
//导出exc
const exportAllBtn = () => {
//参数1:表格数据 id或者 class
//参数2:导出文件名 自定义
//参数3:是否是多级表头
//参数4:是否是需要添加表格样式
complexTableStyle('#dailyTrafficTable','流量统计表.xlsx',true,true)
};
</script>
封装的公共导出代码
import {h} from "vue";
import * as XLSX from "xlsx";
import FileSaver from "file-saver";
// @ts-ignore
import * as XLSX_STYLE from "xlsx-style-vite";
/**
************************************** 导出 start ************************************************
*/
//表格样式
const setPubExcel = (data) => {
data["!cols"] = [];
const excludes = ['!cols', '!fullref', '!merges', '!ref', '!rows'];
for (let key in data) {
//data["!cols"].push({ wpx: 180 }); //给单元格添加宽度 可以自己判断文字有多少给出对应的宽度
if (data.hasOwnProperty(key)) {
if (!excludes.includes(key)) {
console.log(data)
data[key].s = {
border: {
top: {style: 'thin', color: {rgb: '000000'}},
bottom: {style: 'thin', color: {rgb: '000000'}},
left: {style: 'thin', color: {rgb: '000000'}},
right: {style: 'thin', color: {rgb: '000000'}},
},
alignment: {
horizontal: 'center',//水平居中对齐
vertical: 'center',//垂直居中
wrapText: true,//是否换行
},
fill: {
fgColor: { rgb: 'cccccc' },
},
}
}
}
}
};
// 合并行列样式
const mergeCell = (ws) => {
const borderStyle = {
top: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
bottom: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
left: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
right: {style: 'thin', color: {rgb: 'CCCCCCCC'}}
};
// 遍历所有单元格,确保合并区域每个单元格都有边框
const range = XLSX.utils.decode_range(ws['!ref']);
for (let R = range.s.r; R <= range.e.r; R++) {
for (let C = range.s.c; C <= range.e.c; C++) {
const cellAddr = XLSX.utils.encode_cell({r: R, c: C});
if (!ws[cellAddr]) ws[cellAddr] = {};
ws[cellAddr].s = {...ws[cellAddr].s, border: borderStyle}; // 显式设置边框:ml-citation{ref="4,8" data="citationList"}
}
}
return ws
};
//字符串转ArrayBuffer
const s2ab = (s) => {
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;
};
/**
*导出 一般/复杂 表格 按照显示的表格所导出,不需要自定义表头
*可参考 复杂表格:统计-日流量统计 一般表格:统计- 机场架次航班流量统计表
* 参数1:表格数据 id或者 class
* 参数2:导出文件名 自定义
* 参数3:是否是需要添加表格样式 默认:添加表格样式
* 参数4:是否是需要添加合并单元格表格样式 默认:添加合并单元格表格样式
* 参数5:是否有合计行 默认 false
* 参数6:表头合并了几行+一页多少条 默认:0 当有合计行时 须传
*/
// 直接导出一般/多级表头 表格数据
export function complexTableStyle($table, name, setPub = true, merge = true) {
let workbook = XLSX.utils.book_new();
let wb = XLSX.utils.table_to_sheet(document.querySelector($table));
//下面直接放wb数据 可直接使用 自测
XLSX.utils.book_append_sheet(workbook, wb, 'Sheet1');
if (setPub) setPubExcel(wb); //默认带 表格样式
if (merge) mergeCell(wb) //默认带 合并单元格表格样式
let wBout = XLSX_STYLE.write(workbook, {bookType: 'xlsx', bookSST: false, type: 'binary',});
FileSaver.saveAs(new Blob([s2ab(wBout)], {type: 'application/octet-stream;charset=utf-8"'}), `${name}`)
}
/**
************************************** 导出 end ************************************************
*/
导出结果
整体表格加了 背景色 边框 (可遍历数组给指定单元格加样式)
2.使用tableId 导出 带有合计行 的表格(带有合计行并且有多级表头 底部合计行会显示错乱)
详解:参数分解
完整代码
可以不使用组件 组件中的方法直接放到页面中就可以
导出按钮执行方法
<script lang="ts" name="airportSupportFlight" setup>
import {complexTableSum} from '/@/utils/common'; //引入组件
//导出excel
const exportAllBtn = () => {
// 参数1:表格数据 id或者 class
// 参数2:导出文件名 自定义
// 参数3:是否是需要添加表格样式 默认:添加表格样式
// 参数4:是否是需要添加合并单元格表格样式 默认:添加合并单元格表格样式
// 参数5:是否有合计行 默认 false
// 参数6:表头合并了几行+一页多少条 默认:0 当有合计行时 须传
complexTableSum('#airportSupportFlight','机场保障航班架次-自定义.xlsx',true,true,true,paginationData.pageSize + 3)
};
</script>
封装组件公共代码
import {h} from "vue";
import * as XLSX from "xlsx";
import FileSaver from "file-saver";
// @ts-ignore
import * as XLSX_STYLE from "xlsx-style-vite";
/**
************************************** 导出 start ************************************************
*/
//表格样式
const setPubExcel = (data) => {
console.log(8, data)
data["!cols"] = [];
const excludes = ['!cols', '!fullref', '!merges', '!ref', '!rows'];
for (let key in data) {
//data["!cols"].push({ wpx: 180 }); //给单元格添加宽度 可以自己判断文字有多少给出对应的宽度
if (data.hasOwnProperty(key)) {
if (!excludes.includes(key)) {
data[key].s = {
border: {
top: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
bottom: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
left: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
right: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
},
alignment: {
horizontal: 'center',//水平居中对齐
vertical: 'center',//垂直居中
wrapText: true,//是否换行
},
fill: {
// fgColor: { rgb: '0070C0' },//背景色
},
}
}
}
}
};
// 合并行列样式
const mergeCell = (ws) => {
const borderStyle = {
top: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
bottom: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
left: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
right: {style: 'thin', color: {rgb: 'CCCCCCCC'}}
};
// 遍历所有单元格,确保合并区域每个单元格都有边框
const range = XLSX.utils.decode_range(ws['!ref']);
for (let R = range.s.r; R <= range.e.r; R++) {
for (let C = range.s.c; C <= range.e.c; C++) {
const cellAddr = XLSX.utils.encode_cell({r: R, c: C});
if (!ws[cellAddr]) ws[cellAddr] = {};
ws[cellAddr].s = {...ws[cellAddr].s, border: borderStyle}; // 显式设置边框:ml-citation{ref="4,8" data="citationList"}
}
}
return ws
};
//字符串转ArrayBuffer
const s2ab = (s) => {
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;
};
/**
*导出 一般/复杂 表格 按照显示的表格所导出,不需要自定义表头
*可参考 复杂表格:统计-日流量统计 一般表格:统计- 机场架次航班流量统计表
* 参数1:表格数据 id或者 class
* 参数2:导出文件名 自定义
* 参数3:是否是需要添加表格样式 默认:添加表格样式
* 参数4:是否是需要添加合并单元格表格样式 默认:添加合并单元格表格样式
* 参数5:是否有合计行 默认 false
* 参数6:表头合并了几行+一页多少条 默认:0 当有合计行时 须传
*/
// 直接导出表格数据 + 合计行
export function complexTableSum($table, name, setPub = true, merge = true, getSummaries = false, numRow = 0) {
let workbook = XLSX.utils.book_new();
let wb = XLSX.utils.table_to_sheet(document.querySelector($table));
XLSX.utils.book_append_sheet(workbook, wb, 'Sheet1');
if (getSummaries) { //有合计行
let mergesAll = wb['!merges']
let merges = []
for (let i in mergesAll) {
if (mergesAll[i].s.r < numRow) {
merges.push(mergesAll[i])
}
}
wb['!merges'] = merges
}
if (setPub) setPubExcel(wb); //默认带 表格样式
if (merge) mergeCell(wb) //默认带 合并单元格表格样式
let wBout = XLSX_STYLE.write(workbook, {bookType: 'xlsx', bookSST: false, type: 'binary',});
FileSaver.saveAs(new Blob([s2ab(wBout)], {type: 'application/octet-stream;charset=utf-8"'}), `${name}`)
}
/**
************************************** 导出 end ************************************************
*/
表格ui图:原图表格数据较长 这里显示了3分之1
导出结果:
3.自定义表头/表格内容 处理成二维数组格式 导出
参数详解
完整代码
导出执行方法 这里写死了二维数组 做测试用
//导出exc 这里只做测试 写死数据
const exportAllBtn = () => {
//自定义的表头 需要自行转换格式
let allHeader = [
[
"离港正常率排名",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"原因"
],
[
"离港机场",
"排名",
"所属航空公司",
"航班号",
"起落机场及时刻",
"计划航班数",
"正常航班数",
"航班正常率",
"公司",
"天气",
"空管",
"军事",
"时刻",
"其他",
""
]
]
//多级表头 合集prop
let filterVal = [
"flno",
"flno",
"flno",
"flno",
"flno",
"qchb",
"flystart",
"flyend",
"delayreason",
"delay1",
"godelay",
"godelay",
"godelay",
"godelay",
"reason"
]
//表格数据
let dataList = [
{flno:'11',"qchb":'222',"delay1":'333',"godelay":'444','reason':''},
{flno:'11',"qchb":'222',"delay1":'333',"godelay":'444','reason':''}
]
//合并单元格数据
let merges = ['A1:N1', 'O1:O2']
complexTableCustomStyle(allHeader, dataList, filterVal, '四大机场国内离港正常率排名后三位航班统计表.xlsx', merges)
};
封装组件公共代码
import * as XLSX from "xlsx";
import FileSaver from "file-saver";
// @ts-ignore
import * as XLSX_STYLE from "xlsx-style-vite";
/**
************************************** 导出 start ************************************************
*/
//处理数据格式为二维数组
const formatJson = function (filterVal, list) {
return list.map((v, i) => filterVal.map(j => {
return v[j]
}))
}
//表格样式
const setPubExcel = (data) => {
data["!cols"] = [];
const excludes = ['!cols', '!fullref', '!merges', '!ref', '!rows'];
for (let key in data) {
//data["!cols"].push({ wpx: 180 }); //给单元格添加宽度 可以自己判断文字有多少给出对应的宽度
if (data.hasOwnProperty(key)) {
if (!excludes.includes(key)) {
data[key].s = {
border: {
top: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
bottom: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
left: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
right: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
},
alignment: {
horizontal: 'center',//水平居中对齐
vertical: 'center',//垂直居中
wrapText: true,//是否换行
},
fill: {
// fgColor: { rgb: '0070C0' },
},
}
}
}
}
};
// 合并行列样式
const mergeCell = (ws) => {
const borderStyle = {
top: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
bottom: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
left: {style: 'thin', color: {rgb: 'CCCCCCCC'}},
right: {style: 'thin', color: {rgb: 'CCCCCCCC'}}
};
// 遍历所有单元格,确保合并区域每个单元格都有边框
const range = XLSX.utils.decode_range(ws['!ref']);
for (let R = range.s.r; R <= range.e.r; R++) {
for (let C = range.s.c; C <= range.e.c; C++) {
const cellAddr = XLSX.utils.encode_cell({r: R, c: C});
if (!ws[cellAddr]) ws[cellAddr] = {};
ws[cellAddr].s = {...ws[cellAddr].s, border: borderStyle}; // 显式设置边框:ml-citation{ref="4,8" data="citationList"}
}
}
return ws
};
//字符串转ArrayBuffer
const s2ab = (s) => {
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;
};
/**
*导出 复杂表格 自定义表头格式 多级表头
*可参考 正常性统计-正常性统计
*参数1:表格表头数据 tableHeader
*参数2:表格数据 list
*参数3:多级表头 合集prop filterVal
*参数4:导出文件名 name
*参数5:合并单元格数据 merges
*/
// 自己处理数据为 二维数组 格式 自定义表头/内容
export function complexTableCustomStyle(tableHeader, list, filterVal, name, merges) {
const data = [...formatJson(filterVal, list)]
data.unshift(tableHeader[1]);
data.unshift(tableHeader[0]);
let wb = XLSX.utils.book_new() //新建工作簿
let ws = XLSX.utils.aoa_to_sheet(data) //data 二维数组-----将二维数组转换为工作表对象,自动处理数据类型
if (merges.length > 0) { //处理合并单元格 这里手动写二维数组
if (!ws['!merges']) ws['!merges'] = [];
merges.forEach(item => {
ws['!merges'].push(XLSX.utils.decode_range(item))
})
}
XLSX.utils.book_append_sheet(wb, ws, "Sheet1") //将工作表插入到工作簿中
mergeCell(ws)
setPubExcel(ws);
const wBout = XLSX_STYLE.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
FileSaver.saveAs(new Blob([s2ab(wBout)], {type: 'application/octet-stream;charset=utf-8"'}), `${name}`)
}
/**
************************************** 导出 end ************************************************
*/