using ClosedXML.Excel; using Microsoft.Extensions.Logging; using SQLVision.Core.Models; using System.Data; namespace SQLVision.Services.Exporters; public class ExcelExporter : IExportHandler { private readonly ILogger _logger; public string FormatName => "Excel"; public ExcelExporter(ILogger logger) { _logger = logger; } public async Task ExportAsync(DataTable data, string filePath, ExportOptions options) { using var workbook = new XLWorkbook(); var worksheet = workbook.Worksheets.Add("Data"); WriteDataToWorksheet(worksheet, data, options); if (options.IncludeCharts && data.Rows.Count > 0) { AddCharts(worksheet, data, options); } await Task.Run(() => workbook.SaveAs(filePath)); _logger.LogInformation("Exported {Rows} rows to Excel: {FilePath}", data.Rows.Count, filePath); } public async Task ExportToMemoryAsync(DataTable data, ExportOptions options) { using var workbook = new XLWorkbook(); var worksheet = workbook.Worksheets.Add("Data"); WriteDataToWorksheet(worksheet, data, options); using var stream = new MemoryStream(); await Task.Run(() => workbook.SaveAs(stream)); return stream.ToArray(); } private void WriteDataToWorksheet(IXLWorksheet worksheet, DataTable data, ExportOptions options) { if (options.IncludeHeaders) { for (int col = 0; col < data.Columns.Count; col++) { worksheet.Cell(1, col + 1).Value = data.Columns[col].ColumnName; worksheet.Cell(1, col + 1).Style.Font.Bold = true; } } int startRow = options.IncludeHeaders ? 2 : 1; for (int row = 0; row < data.Rows.Count; row++) { for (int col = 0; col < data.Columns.Count; col++) { var value = data.Rows[row][col]; worksheet.Cell(startRow + row, col + 1).Value = ConvertValue(value); ApplyFormatting(worksheet.Cell(startRow + row, col + 1), value); } } if (options.AutoFilter) { var endRow = startRow + data.Rows.Count - 1; worksheet.Range(1, 1, endRow, data.Columns.Count).SetAutoFilter(); } worksheet.Columns().AdjustToContents(); } private XLCellValue ConvertValue(object value) { if (value == null || value == DBNull.Value) return Blank.Value; if (value is DateTime dateTime) return dateTime; if (value is decimal || value is double || value is float) return Convert.ToDouble(value); if (value is bool b) return b; return value.ToString(); } private void ApplyFormatting(IXLCell cell, object value) { if (value is DateTime) { cell.Style.DateFormat.Format = "dd.MM.yyyy HH:mm:ss"; } else if (value is decimal || value is double || value is float) { cell.Style.NumberFormat.Format = "#,##0.00"; } } private void AddCharts(IXLWorksheet worksheet, DataTable data, ExportOptions options) { if (data.Columns.Count < 2) return; //TODO: chart /*var chartType = GetChartType(options.ChartType); var chart = worksheet.CreateChart(0, data.Columns.Count + 2, 20, data.Columns.Count + 10); chart.ChartType = chartType; // Добавление серий на основе данных for (int col = 1; col < Math.Min(5, data.Columns.Count); col++) { var series = chart.AddSeries( worksheet.Range(2, col + 1, data.Rows.Count + 1, col + 1), worksheet.Range(2, 1, data.Rows.Count + 1, 1)); series.ChartType = chartType; } */ } private XLChartType GetChartType(string? chartType) => chartType?.ToLower() switch { "line" => XLChartType.Line, "column" => XLChartType.ColumnClustered, "bar" => XLChartType.BarClustered, "pie" => XLChartType.Pie, "area" => XLChartType.Area, _ => XLChartType.Line }; }