Просмотр исходного кода

导出excel支持多级表头并自动合并单元格
导出excel支持多级表头并自动合并单元格

Signed-off-by: Mr先生 <362270511@qq.com>

Mr先生 2 лет назад
Родитель
Сommit
a121f55a1c
1 измененных файлов с 197 добавлено и 21 удалено
  1. 197 21
      Web/src/utils/exportExcel.ts

+ 197 - 21
Web/src/utils/exportExcel.ts

@@ -39,33 +39,175 @@ export function exportExcel(jsonarr: Array<EmptyObjectType>, name: string, heade
 			},
 		},
 	};
-	var headrow = new Array();
-	header.forEach((item) => {
-		let width = 200;
-		if (item.width && !isNaN(item.width)) {
-			width = parseInt(item.width) * 0.7;
+	let headerDepth = getMaxDepth(header);
+	let headerColumns = getTotalColumns(header);
+
+	//创建表头二维数组
+	let headerArr = new Array(headerDepth);
+	for (let i = 0; i < headerArr.length; i++) {
+		headerArr[i] = new Array(headerColumns);
+	}
+	//计算列索引
+	let colIndex = 0;
+	for (let i = 0; i < header.length; i++) {
+		let col = header[i];
+
+		//获取列对应的长度
+		let colNum = getTotalColumns([col]);
+		let colDepth = getMaxDepth([col]);
+		for (let y = 0; y < colNum; y++) {
+			colIndex = colIndex + y;
+			for (let z = 0; z < colDepth; z++) {
+				headerRec(z, colIndex, y, col, headerArr);
+				// headerArr[z][colIndex]=col;
+			}
 		}
-		wpxArr.push({ wpx: width });
-		headrow.push({
-			v: item.label,
-			t: 's',
-			s: {
-				font: { bold: true },
-				alignment: { wrapText: true, horizontal: item.headerAlign ? item.headerAlign : item.align ? item.align : '', vertical: 'center' },
-				border: borderStyle,
-			},
-		});
-	});
-	data.push(headrow); //写入标题
+		colIndex++;
+	}
+
+	//填充表头列为空的列,为空的列要和本列上一行保持一致,通过一致的单元格,来合并单元格
+	for (let i = 0; i < headerArr.length; i++) {
+		let row = headerArr[i];
+		for (let j = 0; j < row.length; j++) {
+			if (headerArr[i][j] == null) {
+				headerArr[i][j] = headerArr[i - 1][j];
+			}
+		}
+	}
+
+	//递归header
+	function headerRec(rowindex: number, colindex: number, childrenindex: number, col: any, arr: any) {
+		if (rowindex > 0) {
+			if (col.children) {
+				headerRec(rowindex, colIndex, childrenindex, col.children, arr);
+			} else {
+				arr[rowindex][colindex] = col[childrenindex];
+			}
+		} else {
+			arr[rowindex][colindex] = col;
+		}
+	}
+
+	for (let i = 0; i < headerArr.length; i++) {
+		var headrow = new Array();
+		let ha = headerArr[i];
+		for (let j = 0; j < ha.length; j++) {
+			let item = ha[j];
+			let width = 200;
+			if (item.width && !isNaN(item.width)) {
+				width = parseInt(item.width) * 0.7;
+			}
+			wpxArr.push({ wpx: width });
+			headrow.push({
+				v: item.label,
+				t: 's',
+				s: {
+					font: { bold: true },
+					alignment: { wrapText: true, horizontal: item.headerAlign ? item.headerAlign : item.align ? item.align : '', vertical: 'center' },
+					border: borderStyle,
+				},
+			});
+		}
+		data.push(headrow); //写入标题
+	}
+
+	// 计算合并单元格信息
+	var mergedCells = [];
+
+	var mergedflg = false;
+	var mergedcell = { s: { r: 0, c: 0 }, e: { r: 0, c: 0 } };
+
+	//列合并
+	for (let i = 0; i < headerColumns; i++) {
+		let rowcol = headerArr[0][i];
+		for (let j = 0; j < headerDepth; j++) {
+			let col = headerArr[j][i];
+			if (col == rowcol && mergedflg == false) {
+				mergedflg = true;
+				mergedcell.s = { r: j, c: i };
+			} else if (col != rowcol && mergedflg == true) {
+				mergedcell.e = { r: j - 1, c: i };
+
+				if (mergedcell.s.r != mergedcell.e.r) {
+					mergedCells.push(JSON.parse(JSON.stringify(mergedcell)));
+					mergedflg = false;
+					mergedcell = { s: { r: 0, c: 0 }, e: { r: 0, c: 0 } };
+				} else if (j == headerDepth - 1 && mergedflg == true) {
+					mergedcell.e = { r: j, c: i };
+					if (mergedcell.s.r != mergedcell.e.r && col == rowcol) {
+						mergedCells.push(JSON.parse(JSON.stringify(mergedcell)));
+					}
+					mergedflg = false;
+					mergedcell = { s: { r: 0, c: 0 }, e: { r: 0, c: 0 } };
+				} else {
+					rowcol = col;
+					mergedflg = true;
+					mergedcell.s = { r: j, c: i };
+				}
+				// rowcol=col;
+				// mergedflg=false;
+				// mergedcell={s:{r:0,c:0},e:{r:0,c:0}}
+			} else if (j == headerDepth - 1 && mergedflg == true) {
+				mergedcell.e = { r: j, c: i };
+				if (mergedcell.s.r != mergedcell.e.r && col == rowcol) {
+					mergedCells.push(JSON.parse(JSON.stringify(mergedcell)));
+				}
+				mergedflg = false;
+				mergedcell = { s: { r: 0, c: 0 }, e: { r: 0, c: 0 } };
+			}
+		}
+	}
+
+	//行合并
+	for (let i = 0; i < headerDepth; i++) {
+		let rowcol = headerArr[i][0];
+		for (let j = 0; j < headerColumns; j++) {
+			let col = headerArr[i][j];
+			if (col == rowcol && mergedflg == false) {
+				mergedflg = true;
+				mergedcell.s = { r: i, c: j };
+			} else if (col != rowcol && mergedflg == true) {
+				mergedcell.e = { r: i, c: j - 1 };
+
+				if (mergedcell.s.c != mergedcell.e.c) {
+					mergedCells.push(JSON.parse(JSON.stringify(mergedcell)));
+					mergedflg = false;
+					mergedcell = { s: { r: 0, c: 0 }, e: { r: 0, c: 0 } };
+				} else if (j == headerColumns - 1 && mergedflg == true) {
+					mergedcell.e = { r: i, c: j };
+					if (mergedcell.s.r != mergedcell.e.r && col == rowcol) {
+						mergedCells.push(JSON.parse(JSON.stringify(mergedcell)));
+					}
+					mergedflg = false;
+					mergedcell = { s: { r: 0, c: 0 }, e: { r: 0, c: 0 } };
+				} else {
+					rowcol = col;
+					mergedflg = true;
+					mergedcell.s = { r: i, c: j };
+				}
+				// rowcol=col;
+				// mergedflg=false;
+				// mergedcell={s:{r:0,c:0},e:{r:0,c:0}}
+			} else if (j == headerColumns - 1 && mergedflg == true) {
+				mergedcell.e = { r: i, c: j };
+				if (mergedcell.s.c != mergedcell.e.c && col == rowcol) {
+					mergedCells.push(JSON.parse(JSON.stringify(mergedcell)));
+				}
+				mergedflg = false;
+				mergedcell = { s: { r: 0, c: 0 }, e: { r: 0, c: 0 } };
+			}
+		}
+	}
+
 	jsonarr.forEach((json) => {
 		var row = new Array();
-		header.forEach((item) => {
+		headerArr[headerArr.length - 1].forEach((item) => {
 			if (json.hasOwnProperty(item.prop)) {
 				let val = '';
 				if (json[item.prop] != null) {
 					if (item.formatter) {
 						var itemf = item.formatter(json);
-						val = formatterRec(itemf);//递归获取formatter信息
+						val = formatterRec(itemf); //递归获取formatter信息
 					} else {
 						val = json[item.prop];
 					}
@@ -85,12 +227,13 @@ export function exportExcel(jsonarr: Array<EmptyObjectType>, name: string, heade
 	const ws = XLSXS.utils.aoa_to_sheet(data);
 	const wb = XLSXS.utils.book_new();
 	ws['!cols'] = wpxArr;
+	ws['!merges'] = mergedCells; // 设置合并单元格信息
 	XLSXS.utils.book_append_sheet(wb, ws, sheetName);
 	/* generate file and send to client */
 	XLSXS.writeFile(wb, name + '.xlsx');
 }
 //递归formatter
-function formatterRec(itemf:any) {
+function formatterRec(itemf: any) {
 	let r = '';
 	if (itemf.children) {
 		if (itemf.children.default) {
@@ -99,10 +242,43 @@ function formatterRec(itemf:any) {
 			itemf.children.forEach((element: any) => {
 				r = r + formatterRec(element);
 			});
-			
 		}
 	} else {
 		r = itemf;
 	}
 	return r;
 }
+//获取深度
+function getMaxDepth(data: any) {
+	let maxDepth = 1;
+
+	function traverse(obj, depth) {
+		if (obj.children && obj.children.length > 0) {
+			depth++;
+			if (depth > maxDepth) {
+				maxDepth = depth;
+			}
+			obj.children.forEach((child) => traverse(child, depth));
+		}
+	}
+
+	data.forEach((obj) => traverse(obj, 1));
+
+	return maxDepth;
+}
+//获取总列数
+function getTotalColumns(data: any) {
+	let totalColumns = 0;
+
+	function traverse(obj) {
+		if (obj.children && obj.children.length > 0) {
+			obj.children.forEach((child) => traverse(child));
+		} else {
+			totalColumns++;
+		}
+	}
+
+	data.forEach((obj) => traverse(obj));
+
+	return totalColumns;
+}