目录
需求
layui/easyui表格的公共导出方法,能自动获取表头配置
顺便写了无表格的数据导出方法
导出方法实现
/**
* 导出excel列表数据
* @param {*} data 数据列表
* @param {*} dataFieldMap 数据对应字段 {field1: 'field1'}
* @param {*} unshiftMap 表头字段名
* 单行表头 {field1: '标题1'}
* 多行,空值也需要设置字段 [{field1: '标题1','field2': '标题2'},{field1: '标题1-1','field2': ''}]
* @param {*} fileName 导出文件名 不带后缀
* @param {*} headN 表头行数 不传默认1
* @param {*} mergeM 导出合并单元格设置 [['A1', 'E1'],['A2', 'D4']]
* @param {*} colW 导出列宽设置 不传默认defaultW {'A': 40,'C': 120}
* @param {*} defaultW 导出默认列宽 不传默认150
*/
function exportExcelL(data, dataFieldMap, unshiftMap, fileName, headN, mergeM, colW, defaultW) {
layui.config({
base: '你的excel.js位置'
}).use(['jquery', 'excel', 'layer'], function () {
var $ = layui.jquery, layer = layui.layer, excel = layui.excel;
// 列数
var colnum = countProps(dataFieldMap);
data = exportDataSetting(data, dataFieldMap, unshiftMap, headN, excel);
if(undefined == mergeM || "" == mergeM || null == mergeM){
mergeM = [];
}
var mergeConf = excel.makeMergeConfig(mergeM);
if(undefined == defaultW || "" == defaultW || null == defaultW){
defaultW = 150;
}
if(undefined == colW || "" == colW || null == colW){
colW = {};
for(var i=0; i < colnum; i++){
colW[createCellPos(i)] = defaultW;
}
} else {
if(!colW.hasOwnProperty(createCellPos(colnum - 1))){
colW[createCellPos(colnum - 1)] = defaultW;
}
}
var colConf = excel.makeColConfig(colW, defaultW);
excel.exportExcel({
sheet1: data
}, fileName + '.xlsx', 'xlsx', {
extend: {
'!merges': mergeConf,
'!cols': colConf
}
}
)
})
}
/**
* 导出表格设置
* 默认样式表格加框线,表头加粗
* @param {*} data 数据列表
* @param {*} dataFieldMap 数据对应字段 {field1: 'field1'}
* @param {*} unshiftMap 表头字段名
* 单行表头 {field1: '标题1'}
* 多行,空值也需要设置字段 [{field1: '标题1','field2': '标题2'},{field1: '标题1-1','field2': ''}]
* @param {*} headN 表头行数 不传默认1
* @param {*} excel layui excel对象
*/
function exportDataSetting(data, dataFieldMap, unshiftMap, headN, excel) {
if(undefined == headN || "" == headN || null == headN){
headN = 1;
}
// 总行数
var size = data.length + headN;
// 列数
var colnum = countProps(dataFieldMap);
// 最后一列对应excel列号
var col = createCellPos(colnum - 1);
data = excel.filterExportData(data, dataFieldMap);
if(undefined == unshiftMap.length){
data.unshift(unshiftMap);
} else {
$.each(unshiftMap.reverse(), function (i, map) {
data.unshift(map);
});
}
excel.setExportCellStyle(data, 'A1:' + col + size, {
s: {
border: {
top: {style: 'THIN'},
bottom: {style: 'THIN'},
left: {style: 'THIN'},
right: {style: 'THIN'}
},
alignment: {
vertical: 'center',
wrapText: true
}
}
});
excel.setExportCellStyle(data, 'A1:' + col + headN, {
s: {
font: {bold: 'bold'},
alignment: {
horizontal: 'center',
vertical: 'center'
}
}
});
return data;
}
// 数字转换为EXCEL列号 从0开始
function createCellPos(n){
var ordA = 'A'.charCodeAt(0);
var ordZ = 'Z'.charCodeAt(0);
var len = ordZ - ordA + 1;
var s = "";
while( n >= 0 ) {
s = String.fromCharCode(n % len + ordA) + s;
n = Math.floor(n / len) - 1;
}
return s;
}
// 获取对象中字段个数
function countProps(obj) {
var l = 0;
for (p in obj) l++;
return l;
}
获取导出数据列表
-
后台数据列表
表格分页后只能直接获取当前页的数据,所以从后台获取全部数据。
也可以用来获取非表格导出数据。
//导出数据列表
function exportList(url, params){
// 分页参数默认传最大值
// 也可以在表格加载成功后获取到全部数据条数传到后台
// pageSize 为后台分页查询时接收的查询条数限制参数名
params.pageSize = 2147483647;
var result;
if("" != url){
$.ajax({
url: url,
data: params,
async: false,
success: function (data) {
// layui表格返回数据格式
result = data.data;
if (undefined === result) {
// easyui表格返回数据格式
result = data.records;
}
if (undefined === result) {
// 非表格返回数据格式
result = data;
}
}
});
}
delete params.pageSize;
return result;
}
//导出数据列表 layui
//listTable 全局变量 layuitable实例
function exportListLay(){
if(undefined === listTable){
return;
}
var listUrl = listTable.config.url;
var listParams = listTable.config.where;
return exportList(listUrl, listParams);
}
-
layui未分页数据列表
var data = layui.table.cache["layTableId"];
-
easyui treegrid数据列表
//treegrid数据列表
function getTreeGridList(obj) {
var rows = [];
var datas = $(obj).treegrid("getData");
$.each(datas, function (i, data) {
rows.push(data);
getTreeGridRows(data);
});
/**
* 获取easyui treegrid所有节点数据
* @param data 所有第一级数据 treegrid("getData");
*/
function getTreeGridRows(data){
if(data.children != undefined && data.children != null){
for(var i = 0; i < data.children.length;i++){
rows.push(data.children[i]);
getTreeGridRows(data.children[i]);
}
}
}
return rows;
}
没有贴easyui datagrid数据获取方法,但跟layui思路是一样的,不分页的表格直接获取页面数据,分页的表格通过url和传参到后台查询数据。
简单表头获取
//导出数据表头配置 自定义属性exHide 为true时不导出该列
//只能获取单行表头
function exportHead(tableId, type){
var headMap = [];
var dataFieldMap = {};
var unshiftMap = {};
if(undefined === type || "layui" === type) {
layui.table.eachCols(tableId, function (index, item) {
if (item.field && item.type === 'normal' && !item.hide && !item.exHide) {
dataFieldMap[item.field] = item.field;
if(item.title.indexOf("<") > 0){
item.title = item.title.substring(0, item.title.indexOf("<"))
}
unshiftMap[item.field] = item.title;
}
});
} else if("easyui" === type) {
var cols = $(tableId).datagrid('options').columns;
$.each(cols, function (index, col) {
$.each(col, function (i, item) {
if (item.field && !item.checkbox && !item.hidden && !item.exHide) {
dataFieldMap[item.field] = item.field;
if(item.title.indexOf("<") > 0){
item.title = item.title.substring(0, item.title.indexOf("<"))
}
unshiftMap[item.field] = item.title;
}
});
});
}
headMap["dataFieldMap"] = dataFieldMap;
headMap["unshiftMap"] = unshiftMap;
return headMap;
}
获取LAYUI表头
/**
* 导出数据表头配置Layui表格
* 自定义属性exHide 为true时不导出该列
* 自定义属性exShow 为true时不判断类型和字段名field
* 支持Layui多行表头
* @param tableModel layui table实例
* @returns {Array} 表头配置map
*/
function exportHeadLay(tableModel){
var headMap = [];
// 字段对应值
var dataFieldMap = {};
// 显示的表头字段数组
var dataFieldValues = [];
// 字段对应表头名
var unshiftMaps = [];
// 计算合并单元格map
var mergeMaps = [];
// 计算列宽度map
var colW = {};
layui.table.eachCols(tableModel.config.id, function (index, item) {
if (((item.field && item.type === 'normal') || item.exShow) && !item.hide && !item.exHide) {
// 获取最终显示列字段
dataFieldMap[item.field] = item.field;
dataFieldValues.push(item.field);
}
});
var cols = tableModel.config.cols;
$.each(cols, function (index, col) {
var unshiftMap = {};
//表头顺序设置为数据顺序
for (var key in dataFieldMap) {
unshiftMap[key] = "";
}
var ri = index + 1; //当前单元格行号
var ci = 0; //当前单元格列号
$.each(col, function (i, item) {
if (((item.field && item.type === 'normal') || item.exShow) && !item.hide && !item.exHide) {
if(item.title.indexOf("<") > 0){
item.title = item.title.substring(0, item.title.indexOf("<"))
}
if (dataFieldMap.hasOwnProperty(item.field)) {
unshiftMap[item.field] = item.title;
} else {
// 当前字段不在导出显示列中,说明是合并单元格字段,在相同位置写入表头名,合并单元格后才能显示表头名字
unshiftMap[dataFieldValues[ci]] = item.title;
}
// 通过列设置width计算表格宽度
if(typeof(item.width) === "string" && item.width.endsWith("%")){
item.width = Number(item.width.replace("%","")) * 10;
}
if(typeof(item.minWidth) === "string" && item.minWidth.endsWith("%")){
item.minWidth = Number(item.minWidth.replace("%","")) * 10;
}
var width = item.width;
if((item.minWidth && width < item.minWidth) || width === undefined){
width = item.minWidth;
}
if(width > 150 && (!colW.hasOwnProperty(createCellPos(ci)) || width > colW[createCellPos(ci)])){
colW[createCellPos(ci)] = width;
} else if(width === 0 && !colW.hasOwnProperty(createCellPos(ci))){
colW[createCellPos(ci)] = 260;
}
var mergeMap = [];
item.rowspan = parseInt(item.rowspan) || 1; //跨行默认1行
item.colspan = parseInt(item.colspan) || 1; //跨列默认1列
// 通过列设置colspan/rowspan计算表格合并单元格号
if(item.colspan > 1 || item.rowspan > 1) {
var startCell = createCellPos(ci) + ri;
if (item.colspan > 1) {
ci = ci + item.colspan;
} else {
ci++;
}
if (item.rowspan > 1) {
ri = ri + item.rowspan - 1;
}
var endCell = createCellPos(ci - 1) + ri;
mergeMap.push(startCell);
mergeMap.push(endCell);
mergeMaps.push(mergeMap);
} else {
ci++;
}
ri = index + 1;
}
});
unshiftMaps.push(unshiftMap);
});
headMap["dataFieldMap"] = dataFieldMap;
if(unshiftMaps.length > 1){
headMap["unshiftMap"] = unshiftMaps;
} else {
headMap["unshiftMap"] = unshiftMaps[0];
}
headMap["headN"] = cols.length;
headMap["mergeM"] = mergeMaps;
headMap["colW"] = colW;
return headMap;
}
获取EASYUI表头
/**
* 导出数据表头配置Easyui表格
* 自定义属性exHide 为true时不导出该列
* 支持Easyui多行表头
* @param tableId easyui 表格页面元素选择器 例'#id'
* @returns {Array} 表头配置map
*/
function exportHeadEasy(tableId){
var headMap = [];
var dataFieldMap = {};
var dataFieldValues = [];
var unshiftMaps = [];
var mergeMaps = [];
var colW = {};
var fields = $(tableId).datagrid('getColumnFields');
$.each(fields, function (i, field) {
var option = $(tableId).datagrid('getColumnOption', field);
if (option.field && !option.checkbox && !option.hidden && !option.exHide) { //过滤勾选框和隐藏列
dataFieldMap[field] = field;
dataFieldValues.push(field);
}
});
var cols = $(tableId).datagrid('options').columns;
$.each(cols, function (index, col) {
var unshiftMap = {};
//表头顺序设置为数据顺序
for (var key in dataFieldMap) {
unshiftMap[key] = "";
}
var ri = index + 1; //当前单元格行号
var ci = 0; //当前单元格列号
$.each(col, function (i, item) {
if (item.field && !item.checkbox && !item.hidden && !item.exHide) {
if(item.title.indexOf("<") > 0){
item.title = item.title.substring(0, item.title.indexOf("<"))
}
if (dataFieldMap.hasOwnProperty(item.field)) {
unshiftMap[item.field] = item.title;
} else {
unshiftMap[dataFieldValues[ci]] = item.title;
}
if(item.width && item.width > 150 && (!colW.hasOwnProperty(createCellPos(ci) || item.width > colW[createCellPos(ci)]))){
colW[createCellPos(ci)] = item.width;
}
var mergeMap = [];
item.rowspan = parseInt(item.rowspan) || 1; //跨行默认1行
item.colspan = parseInt(item.colspan) || 1; //跨列默认1列
if(item.colspan > 1 || item.rowspan > 1) {
var startCell = createCellPos(ci) + ri;
if (item.colspan > 1) {
ci = ci + item.colspan;
} else {
ci++;
}
if (item.rowspan > 1) {
ri = ri + item.rowspan - 1;
}
var endCell = createCellPos(ci - 1) + ri;
mergeMap.push(startCell);
mergeMap.push(endCell);
mergeMaps.push(mergeMap);
} else {
ci++;
}
ri = index + 1;
}
});
unshiftMaps.push(unshiftMap);
});
headMap["dataFieldMap"] = dataFieldMap;
if(unshiftMaps.length > 1){
headMap["unshiftMap"] = unshiftMaps;
} else {
headMap["unshiftMap"] = unshiftMaps[0];
}
headMap["headN"] = cols.length;
headMap["mergeM"] = mergeMaps;
headMap["colW"] = colW;
return headMap;
}
易用
这边需求是所有列表页面都需要加导出,页面较多为方便需要的页面新增按钮,写了一个增加按钮的方法,按需修改。只要调用导出方法就行。
//增加导出按钮
function exportBtn(){
$(".btns:last-child").after(' <button type="button" id="exportList" onclick="exportTable()" class="btns importBtn">\n' +
' <i class="layui-icon layui-icon-export"></i>导出\n' +
'</button>');
}
为了方便不同需求调用,抽出来一些公共方法
/**
* 导出数据默认方法 layui表格
* listTable layui表格实例对象
* document.title 默认页面名
* 需自定义在页面重写exportTable方法
*/
function exportTable(){
exportTableLay(document.title);
}
//导出数据 layui表格自定义文件名
function exportTableLay(fileName){
var data = exportListLay();
if (data == null || data.length == 0) {
Msg.error("未获取到数据");
return;
}
exportExcelLay(data, fileName);
}
/**
* 导出数据 easyui TreeGrid表格
* @param tableId easyui表格id 例:'#id'
* @param fileName 导出文件名 不传默认html页面名
*/
function exportTreeGrid(tableId, fileName){
var data = getTreeGridList(tableId);
if (data == null || data.length == 0) {
Msg.error("未获取到数据");
return;
}
exportExcelEasy(data, tableId, fileName);
}
//导出数据 layui表格自定义文件名
function exportExcelLay(data, fileName){
var headMap = exportHeadLay(listTable);
if(undefined === fileName){
fileName = document.title;
}
exportExcelL(data, headMap.dataFieldMap, headMap.unshiftMap, fileName, headMap.headN, headMap.mergeM, headMap.colW);
}
//导出数据 easyui表格自定义文件名
function exportExcelEasy(data, tableId, fileName){
var headMap = exportHeadEasy(tableId);
if(undefined === fileName){
fileName = document.title;
}
exportExcelL(data, headMap.dataFieldMap, headMap.unshiftMap, fileName, headMap.headN, headMap.mergeM, headMap.colW);
}
使用
-
layui表格
//自定义文件名
function exportTable(){
exportTableLay("文件名");
}
-
easyui treegrid 树表
function exportTable(){
exportTreeGrid('#treeId');
}
//自定义文件名
function exportTable(){
exportTreeGrid('#treeId',"文件名");
}
-
需修改数据显示
easyui同理
function exportTable(){
var data = exportListLay();
if (data == null || data.length == 0) {
Msg.error("未获取到数据");
return;
}
$.each(data, function (index, item) {
//修改数据代码
});
exportExcelLay(data);
}
-
通过表头设置格式化数据
根据配置自行修改,这边这个显示值是加了<span>标签以及一些style,所以去掉所有的html标签即可。
function exportTable(){
var data = getTreeGridList("#treeId");
if (data == null || data.length == 0) {
Msg.error("未获取到数据");
return;
}
$.each(data, function (index, item) {
for (var key in item) {
var option = $("#treeId").datagrid('getColumnOption', key);
if(option) {
var result = option.formatter(item[key], item, index);
if(result){
//删除html标签
item[key] = result.replace(/\<.*?\>/g,'');
} else {
item[key] = "默认值";
}
} else {
item[key] = "默认值";
}
}
});
exportExcelEasy(data, "#treeId");
}
-
修改获取的表头数据
有些表格在渲染完成后可能通过js代码hide或者修改了表头名,这种情况下通过cols设置是获取不到这些表头的,只能在获取之后手动修改了
function exportTable(){
var data = exportListLay();
if (data == null || data.length == 0) {
Msg.error("未获取到数据");
return;
}
$.each(data, function (index, item) {
//修改数据代码
});
var headMap = exportHeadLay(listTable);
if(删除条件){
delete headMap.dataFieldMap.字段名;
delete headMap.unshiftMap.字段名;
}
if(修改条件){
headMap.dataFieldMap["字段名"] = "字段名";
headMap.unshiftMap["字段名"] = "表头名";
}
exportExcelL(data, headMap.dataFieldMap, headMap.unshiftMap, document.title, headMap.headN, headMap.mergeM, headMap.colW);
}
-
多个sheet页导出
这种页面很少,所以没有去封装方法
function exportTable(){
var data1 = layui.table.cache["table1"];
var data2 = getTreeGridList('#table2');
if ((data1 == null || data1.length == 0)
&& (data2 == null || data2.length == 0)) {
Msg.error("未获取到数据");
return;
}
var headMap1 = exportHead("table1");
var headMap2 = exportHead("#table2", "easyui");
layui.config({
base: '你的excel.js位置'
}).use(['jquery', 'excel', 'layer'], function () {
var $ = layui.jquery, layer = layui.layer, excel = layui.excel;
data1 = exportDataSetting(data1, headMap1.dataFieldMap, headMap1.unshiftMap, 1, excel);
data2 = exportDataSetting(data2, headMap2.dataFieldMap, headMap2.unshiftMap, 1, excel);
var colConf = excel.makeColConfig({
'A': 240,
'G': 180,
}, 150);
var colConf2 = excel.makeColConfig({
'B': 240,
'E': 240,
'I': 240,
'J': 240,
}, 150);
excel.exportExcel({
'sheet1': data1,
'sheet2': data2,
}, '文件名.xlsx', 'xlsx', {
extend: {
'sheet1': {
'!cols': colConf
},
'sheet2': {
'!cols': colConf2
}
}
}
)
});
}
}
拓展
-
判断字符串以数字结尾
var reg = new RegExp("(\\d+)$");
var keyEnd = reg.test(key);
if(keyEnd){
//do
}
-
替换字符串中括号及内容
// js
// 将<>及内容替换为空
str.replace(/\<.*?\>/g,'');
// java
// 匹配[]中内容
String regex = "\\[.*?\\]";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
List<String> keys = new ArrayList<>();
while (matcher.find()) {
if (!keys.contains(matcher.group())) {
keys.add(matcher.group());
}
}
for (String key : keys) {
//[]内容
String content = key.substring(1, key.length() - 1);
//替换为空
str = str.replace(key, "");
}
存在问题
当合并表头有隐藏列时无法正确计算合并单元格,目前需求中没有这种列表数据,之后有时间再试着解决
有时间再放点示例图
参考链接
JavaScript中将Excel的Cell单元格列标字母转化为数字