ryui-vue
Version:
ry公共组件库
170 lines (161 loc) • 5.02 kB
JavaScript
import {utils, writeFile} from 'xlsx';
/**
* 导出excel表格
* @param {Object} options
* @param {Array} options.columns 表格列规则
* @param {Array} options.data 表格数据 - 例:this.$refs.table._data.datas
* @param {String} options.fileName 导出文件名 - *.xlsx/*.csv
* @param {Array} options.excludes 排除列 - 不需要导出的列, ['index']
* @param {Object} options.formatters 格式化函数 - 自定义formatter()
* @return
*/
export default function exportExcel(options) {
let {
columns,
data,
fileName,
excludes = [],
formatters = {}
} = options
// 排除项
excludes = ['action', ...excludes];
// 获取不包含排除列的表格列规则
const headerList = columns.filter(
(v) => v.prop && !excludes.includes(v.prop)
);
//
const depthArr = [...new Array(tree(headerList)).keys()];
let obj = [];
// 获取多级表头标题数据
for (let depth of depthArr) {
obj[depth] = traverse(headerList, depth, 0, []);
}
// 始化用于存储表头和数据的对象 并赋值[表头标题]-----导出所需要的数据结构 --> 二维数组 [[表头标题], [每一行数据],[每一行数据]...]
// let array = [[...headerList.map((v) => v.label)]];
let array = [...obj];
// 获取列表数据
data.forEach((v) => {
let list = [];
getData(v, headerList, list, formatters);
array.push(list);
});
// 获取合并单元格规则
const merges = mergeCells(headerList);
const sheet = utils.aoa_to_sheet(array);
sheet['!merges'] = merges;
// 无效
sheet['A1']['s'] = {
alignment: {
horizontal: 'center', // 水平居中
vertical: 'center' // 垂直居中
}
};
// 打印(导出)方法
writeFile(
{
SheetNames: ['Sheet1'],
Sheets: {
Sheet1: sheet
}
},
fileName
);
}
// 计算多级表头最大深度(层级)
function tree(data) {
let depth = 1;
data.forEach((v) => {
if (v.children) {
const curDepth = tree(v.children) + 1;
depth = Math.max(depth, curDepth);
}
});
return depth;
}
// 递归遍历多级表头,生成表头数据
function traverse(array, depth, level, list) {
array.map((v) => {
if (level === depth && v?.children) {
list.push(v.label);
const arr = v.children.filter((v) => v.children);
const arr2 = new Array(v.children.length - arr.length - 1).fill(null);
arr2.push(...arr);
traverse(arr2, depth, level + 1, list);
} else if (level === depth && !v?.children) {
return list.push(v.label);
} else if (level !== depth && !v?.children) {
return list.push(null);
} else if (level !== depth && v?.children) {
traverse(v?.children, depth, level + 1, list);
}
});
return list;
}
// 根据数据和表头规则提取数据,同时根据自定义格式化规则formatters格式化数据
function getData(v, headerList, list = [], formatters) {
headerList.forEach((r) => {
// 获取列配置字段名
let propChildren = r.prop.split('.').at(-1);
if (r.children) {
getData(v[propChildren], r.children, list, formatters);
} else {
// 获取exportExcel工具函数自定义的formatter格式化规则
const formatterFun = formatters[propChildren];
// 自定义优先级最高
if (formatterFun) {
try {
list.push(formatterFun(v, r, v[propChildren]));
} catch (e) {
list.push(v[propChildren]);
}
// 列配置上定义的formatter格式化规则
} else if (r.formatter) {
try {
list.push(r.formatter(v, r, v[propChildren]));
} catch (e) {
list.push(v[propChildren]);
}
} else {
list.push(v[propChildren]);
}
}
});
}
// 动态生成多级表头合并需要的合并单元格规则
function mergeCells(columns) {
const maxDepth = tree(columns) === 1 ? 0 : tree(columns);
const merges = [];
let startCol = {index: 0};
processColumns(columns, 0, maxDepth, merges, startCol);
return merges;
}
// 辅助函数 - 根据columns规则动态生成处理多级表头合并需要的数据结构
function processColumns(columns, level, depth, merges, startCol) {
for (let column of columns) {
// const nextStartCol = startCol + colIndex;
const length = getChildrenLength(column.children || []);
if (column.children) {
merges.push({
s: {r: level, c: startCol.index},
e: {r: level, c: startCol.index + (length - 1)}
});
processColumns(column.children, level + 1, depth, merges, startCol);
} else {
merges.push({
s: {r: level, c: startCol.index},
e: {r: depth, c: startCol.index}
});
startCol.index++;
}
}
}
// 获取子级的长度---需要合并的列数
function getChildrenLength(columns) {
return columns.reduce((pre, cru) => {
if (cru.children) {
return pre + getChildrenLength(cru.children);
} else {
return pre + 1;
}
}, 0);
}