CloudBuilder/CloudBuilder.Core/Report/DataToExcelHelper.cs
owenchen 1c0c83af5f ow
2026-05-12 10:09:31 +08:00

399 lines
18 KiB
C#

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<T>
{
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<TableAttribute, string>(x => x.Name);
List<TemplateHeader> headers = new List<TemplateHeader>();
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<DatabaseGeneratedAttribute, DatabaseGeneratedOption>(x => x.DatabaseGeneratedOption, p.Name);
//if (option == DatabaseGeneratedOption.Identity)
//{
// continue;
//}
string displayName = typeof(T).GetCustomAttributeValue<ColumnAttribute, string>(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<string> 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<string, Dictionary<string, object>> mappings)
{
return ToExcel(worksheetName, title, null, headers, datas, mappings);
}
public IActionResult ToExcel(string worksheetName, string title, List<string> subTitle, TemplateHeader[] headers, T[] datas, Dictionary<string, Dictionary<string, object>> mappings)
{
return ToExcel(worksheetName, title, subTitle, headers, datas, mappings, null!, null!, null!);
}
public IActionResult ToExcel(string worksheetName, string title, List<string> subTitle, TemplateHeader[] headers, T[] datas, Dictionary<string, Dictionary<string, object>> mappings, DataValueFormatOperation dataValueOperation)
{
return ToExcel(worksheetName, title, subTitle, headers, datas, mappings, dataValueOperation, null!, null!);
}
public IActionResult ToExcel(string worksheetName, string title, List<string> subTitle, TemplateHeader[] headers, T[] datas, Dictionary<string, Dictionary<string, object>> mappings, DataValueFormatOperation dataValueOperation,
CellTypeFormatOperation cellOperation)
{
return ToExcel(worksheetName, title, subTitle, headers, datas, mappings, dataValueOperation, cellOperation, null!);
}
public IActionResult ToExcel(string worksheetName, string title, List<string> subTitle, TemplateHeader[] headers, T[] datas, Dictionary<string, Dictionary<string, object>> 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<string> subTitle, TemplateHeader[] headers, T[] datas)
{
PrintExcel(worksheetName, title, subTitle, headers, datas, null!, null!, null!, null!);
}
public void PrintExcel(string worksheetName, string title, List<string> subTitle, TemplateHeader[] headers, T[] datas, Dictionary<string, Dictionary<string, object>> mappings)
{
PrintExcel(worksheetName, title, subTitle, headers, datas, mappings, null!, null!, null!);
}
public void PrintExcel(string worksheetName, string title, List<string> subTitle, TemplateHeader[] headers, T[] datas, Dictionary<string, Dictionary<string, object>> mappings, DataValueFormatOperation dataValueOperation)
{
PrintExcel(worksheetName, title, subTitle, headers, datas, mappings, dataValueOperation, null!, null!);
}
public void PrintExcel(string worksheetName, string title, List<string> subTitle, TemplateHeader[] headers, T[] datas, Dictionary<string, Dictionary<string, object>> mappings, DataValueFormatOperation dataValueOperation,
CellTypeFormatOperation cellOperation)
{
PrintExcel(worksheetName, title, subTitle, headers, datas, mappings, dataValueOperation, cellOperation, null!);
}
public void PrintExcel(string worksheetName, string title, List<string> subTitle, TemplateHeader[] headers, T[] datas, Dictionary<string, Dictionary<string, object>> 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<string> subTitle, TemplateHeader[] headers, T[] datas, Dictionary<string, Dictionary<string, object>> 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<string> subTitle, TemplateHeader[] headers, T[] datas, Dictionary<string, Dictionary<string, object>> mappings)
{
return PackWorksheet(excel, worksheetName, title, subTitle, headers, datas, mappings, null!, null!, null!);
}
public IXLWorksheet PackWorksheet(XLWorkbook excel, string worksheetName, string title, List<string> subTitle, TemplateHeader[] headers, T[] datas, Dictionary<string, Dictionary<string, object>> 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<string> subTitle, TemplateHeader[] headers, T[] datas, Dictionary<string, Dictionary<string, object>> 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<string> subTitle, TemplateHeader[] headers, T[] datas, Dictionary<string, Dictionary<string, object>> mappings, DataValueFormatOperation dataValueOperation,
CellTypeFormatOperation cellOperation, XLWorksheetOperation xLWorksheetOperation)
{
List<ExportRow> ExportDatas = new List<ExportRow>();
int rowIndex = 1;
var worksheet = excel.Worksheets.Add(worksheetName);
List<string> headerNames = new List<string>();
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<List<string>>();
listOfList.Add(headerNames);
worksheet.Cell(rowIndex++, 1).InsertData(listOfList);
object[] rowData;
int dataIndex = rowIndex;
//Handle on empty dataset
var listOfArr = new List<object[]>();
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<PropertyInfo>(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;
}
}
}
}