using ClosedXML.Excel; using CloudBuilder.Core.DatabaseAccessor.Entity; using CloudBuilder.Core.Extensions; using CloudBuilder.Core.Service; using DocumentFormat.OpenXml.Spreadsheet; using DocumentFormat.OpenXml; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Data.SqlTypes; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Diagnostics; namespace CloudBuilder.Core.Report { public class DataToExcelHelper { private readonly IApplicationService service; public delegate object DataValueFormatOperation(string functionName, object value); public delegate void CellTypeFormatOperation(IXLWorksheet xLWorksheet, string functionName, object cellValue, int rowIndex, int ColIndex, T[] datas); public delegate void XLWorksheetOperation(IXLWorksheet xLWorksheet, T[] datas); public DataToExcelHelper(IApplicationService service) { this.service = service; } public IActionResult ToExcel(T[] datas) { using (var excel = new XLWorkbook()) { IXLWorksheet worksheet = GetExcelWorksheet(excel, datas); using (MemoryStream stream = new MemoryStream()) { excel.SaveAs(stream); return new FileStreamResult(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ); } } } public IXLWorksheet GetExcelWorksheet(XLWorkbook excel, T[] datas) { string tableName = typeof(T).GetCustomAttributeValue(x => x.Name); List headers = new List(); PropertyInfo[] pArray = typeof(T).GetProperties(); string format = null; foreach (PropertyInfo p in pArray) { object value = p.GetType(); format = string.Empty; if (value is DateTime || p.PropertyType.FullName.ToLower().Contains("datetime")) { format = TemplateHeader.DateTimeFormat1; } else if (value is int || p.PropertyType.FullName.ToLower().Contains("int")) { format = TemplateHeader.IntFormat1; } else if (value is decimal || p.PropertyType.FullName.ToLower().Contains("decimal")) { format = TemplateHeader.DecimalFormat1; } //DatabaseGeneratedOption option = typeof(StockOrderEntity).GetCustomAttributeValue(x => x.DatabaseGeneratedOption, p.Name); //if (option == DatabaseGeneratedOption.Identity) //{ // continue; //} string displayName = typeof(T).GetCustomAttributeValue(x => x.Name, p.Name); if (string.IsNullOrEmpty(displayName)) continue; headers.Add(new TemplateHeader { DisplayName = displayName, BindingName = p.Name, Format = format, Width = TemplateHeader.WidthAutoFit }); } return PackWorksheet(excel, tableName, tableName, null, headers.ToArray(), datas, null); } public IActionResult ToExcel(string worksheetName, string title, TemplateHeader[] headers, T[] datas) { return ToExcel(worksheetName, title, null, headers, datas, null); } public IActionResult ToExcel(string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas) { return ToExcel(worksheetName, title, subTitle, headers, datas, null); } public IActionResult ToExcel(string worksheetName, string title, TemplateHeader[] headers, T[] datas, Dictionary> mappings) { return ToExcel(worksheetName, title, null, headers, datas, mappings); } public IActionResult ToExcel(string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas, Dictionary> mappings) { return ToExcel(worksheetName, title, subTitle, headers, datas, mappings, null!, null!, null!); } public IActionResult ToExcel(string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas, Dictionary> mappings, DataValueFormatOperation dataValueOperation) { return ToExcel(worksheetName, title, subTitle, headers, datas, mappings, dataValueOperation, null!, null!); } public IActionResult ToExcel(string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas, Dictionary> mappings, DataValueFormatOperation dataValueOperation, CellTypeFormatOperation cellOperation) { return ToExcel(worksheetName, title, subTitle, headers, datas, mappings, dataValueOperation, cellOperation, null!); } public IActionResult ToExcel(string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas, Dictionary> mappings, DataValueFormatOperation dataValueOperation, CellTypeFormatOperation cellOperation, XLWorksheetOperation xLWorksheetOperation) { string path = ToExcelFile(worksheetName, title, subTitle, headers, datas, mappings, dataValueOperation, cellOperation, xLWorksheetOperation); var stream = System.IO.File.OpenRead(path); return new FileStreamResult(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); } public void PrintExcel(string worksheetName, string title, TemplateHeader[] headers, T[] datas) { PrintExcel(worksheetName, title, null!, headers, datas, null!, null!, null!, null!); } public void PrintExcel(string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas) { PrintExcel(worksheetName, title, subTitle, headers, datas, null!, null!, null!, null!); } public void PrintExcel(string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas, Dictionary> mappings) { PrintExcel(worksheetName, title, subTitle, headers, datas, mappings, null!, null!, null!); } public void PrintExcel(string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas, Dictionary> mappings, DataValueFormatOperation dataValueOperation) { PrintExcel(worksheetName, title, subTitle, headers, datas, mappings, dataValueOperation, null!, null!); } public void PrintExcel(string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas, Dictionary> mappings, DataValueFormatOperation dataValueOperation, CellTypeFormatOperation cellOperation) { PrintExcel(worksheetName, title, subTitle, headers, datas, mappings, dataValueOperation, cellOperation, null!); } public void PrintExcel(string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas, Dictionary> mappings, DataValueFormatOperation dataValueOperation, CellTypeFormatOperation cellOperation, XLWorksheetOperation xLWorksheetOperation) { string fileFullname = ToExcelFile(worksheetName, title, subTitle, headers, datas, mappings, dataValueOperation, cellOperation, xLWorksheetOperation); Process.Start(new ProcessStartInfo { FileName = fileFullname, UseShellExecute = true // 使用Windows Shell执行 }); } public string ToExcelFile(string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas, Dictionary> mappings, DataValueFormatOperation dataValueOperation, CellTypeFormatOperation cellOperation, XLWorksheetOperation xLWorksheetOperation) { string tempPath = Path.GetTempPath() + $"{DateTime.Now.ToString("yyyyMMdd")}"; if (!Directory.Exists(tempPath)) Directory.CreateDirectory(tempPath); string fileFullname = Path.Combine(tempPath, worksheetName.Replace(" ", "") + $"{DateTime.Now.ToString("yyyyMMddHHmmssffff")}.xlsx"); if (service != null) { fileFullname = Path.Combine(service.Configuration["FileServiceSettings:ExcelOutputDirectory"], $"{DateTime.Now.ToString("yyyyMMdd")}", typeof(T).Name + $"{DateTime.Now.ToString("yyyyMMddHHmmssffff")}.xlsx"); } using (var excel = new XLWorkbook()) { if (string.IsNullOrEmpty(worksheetName)) worksheetName = "Report"; PackWorksheet(excel, worksheetName, title, subTitle, headers, datas, mappings, dataValueOperation, cellOperation, xLWorksheetOperation); excel.SaveAs(fileFullname); } return fileFullname; } public IXLWorksheet PackWorksheet(XLWorkbook excel, string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas, Dictionary> mappings) { return PackWorksheet(excel, worksheetName, title, subTitle, headers, datas, mappings, null!, null!, null!); } public IXLWorksheet PackWorksheet(XLWorkbook excel, string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas, Dictionary> mappings, DataValueFormatOperation dataValueOperation) { return PackWorksheet(excel, worksheetName, title, subTitle, headers, datas, mappings, dataValueOperation, null!, null!); } public IXLWorksheet PackWorksheet(XLWorkbook excel, string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas, Dictionary> mappings, DataValueFormatOperation dataValueOperation, CellTypeFormatOperation cellOperation) { return PackWorksheet(excel, worksheetName, title, subTitle, headers, datas, mappings, dataValueOperation, cellOperation, null!); } public IXLWorksheet PackWorksheet(XLWorkbook excel, string worksheetName, string title, List subTitle, TemplateHeader[] headers, T[] datas, Dictionary> mappings, DataValueFormatOperation dataValueOperation, CellTypeFormatOperation cellOperation, XLWorksheetOperation xLWorksheetOperation) { List ExportDatas = new List(); int rowIndex = 1; var worksheet = excel.Worksheets.Add(worksheetName); List headerNames = new List(); int i = 1; foreach (TemplateHeader header in headers) { if (!string.IsNullOrEmpty(header.Format)) worksheet.Column(i).Style.NumberFormat.Format = header.Format; worksheet.Column(i).Width = header.DisplayName.Length * 1.5; if (header.Width > 0) worksheet.Column(i).Width = header.Width; headerNames.Add(header.DisplayName); i++; } worksheet.Row(rowIndex).Style.Font.FontSize = 20; worksheet.Row(rowIndex).Height = 30; worksheet.Range(rowIndex, 1, rowIndex, headers.Length).Merge(); worksheet.Cell(rowIndex++, 1).Value = title; //空行 rowIndex++; worksheet.Row(rowIndex).Height = 20; if (subTitle != null && subTitle.Count > 0) { foreach (string st in subTitle) { worksheet.Range(rowIndex, 1, rowIndex, headers.Length).Merge(); worksheet.Cell(rowIndex++, 1).Value = st; } //空行 rowIndex++; } //設置列名 var listOfList = new List>(); listOfList.Add(headerNames); worksheet.Cell(rowIndex++, 1).InsertData(listOfList); object[] rowData; int dataIndex = rowIndex; //Handle on empty dataset var listOfArr = new List(); if (datas != null && datas.Length > 0) { //綁定明細 var t = datas[0].GetType(); //里面是所有公共属性 PropertyInfo[] pArray = t.GetProperties(); foreach (T d in datas) { rowData = new object[headers.Length]; i = 0; foreach (TemplateHeader header in headers) { rowData[i] = null; Array.ForEach(pArray, p => { if (header.BindingName == p.Name) { object value = p.GetValue(d); if (value == null || value == DBNull.Value) { rowData[i] = null; } else if (header.DataValueFormatName != null && dataValueOperation != null) { rowData[i] = dataValueOperation(header.DataValueFormatName, value); } else if (value is DateTime) { if (value == DBNull.Value) { rowData[i] = null; } else if (((DateTime)value).Year > 1900) rowData[i] = (DateTime)value; } else if (value is SqlDateTime) { if (!((SqlDateTime)value).IsNull) { if (((SqlDateTime)value).Value.Year > 1900) rowData[i] = ((SqlDateTime)value).Value; } else { rowData[i] = null; } } else { if (mappings != null && mappings.ContainsKey(header.BindingName)) { if (mappings[header.BindingName].ContainsKey((string)value)) rowData[i] = mappings[header.BindingName][(string)value]; else rowData[i] = value; } else rowData[i] = value; } } }); i++; } listOfArr.Add(rowData); } } worksheet.Cell(rowIndex++, 1).InsertData(listOfArr); if (datas != null && datas.Length > 0) { foreach (T d in datas) { i = 1; foreach (TemplateHeader header in headers) { if (header.CellTypeFormatName != null) cellOperation(worksheet, header.CellTypeFormatName, d, dataIndex, i, datas); i++; } dataIndex++; } } rowIndex = rowIndex + listOfArr.Count(); // 3 is title line + space line + header line int headerRow = 3 + (subTitle == null || subTitle.Count == 0 ? 0 : subTitle.Count + 1); worksheet.Range(headerRow, 1, headerRow, headers.Length).Style.Fill.PatternType = XLFillPatternValues.Solid; worksheet.Range(headerRow, 1, headerRow, headers.Length).Style.Fill.SetBackgroundColor(XLColor.Silver); var range = worksheet.Range(headerRow, 1, headerRow + datas.Length, headers.Length); range.Style.Border.TopBorder = XLBorderStyleValues.Thin; range.Style.Border.BottomBorder = XLBorderStyleValues.Thin; range.Style.Border.LeftBorder = XLBorderStyleValues.Thin; range.Style.Border.RightBorder = XLBorderStyleValues.Thin; range.Style.Alignment.WrapText = true; range.Style.Alignment.Vertical = XLAlignmentVerticalValues.Top; i = 1; foreach (TemplateHeader header in headers) { if (header.Width == TemplateHeader.WidthAutoFit) worksheet.Column(i).AdjustToContents(headerRow, headerRow + datas.Length, 10, 100); i++; } if (xLWorksheetOperation != null) xLWorksheetOperation(worksheet, datas); return worksheet; } private enum ExportDisplayStyle { Header, Subtotal, Detail, Total }; private class ExportRow { public object[] values; public ExportDisplayStyle displayStyle; public ExportRow(object[] values, ExportDisplayStyle displayStyle) { this.values = values; this.displayStyle = displayStyle; } } } }