网站首页 网站源码
website
站点相关全部源代码,隐藏了一些关于服务器的信息
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using NPOI.XSSF.UserModel;

namespace Dpz.Core.Infrastructure.Excel;

/// <summary>
/// Excel导出扩展方法类
/// </summary>
public static class ExcelExtensions
{
    /// <summary>
    /// 根据标记导出Excel
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="columns">列配置</param>
    /// <param name="sheetName">工作表名称</param>
    /// <param name="startRow">起始行</param>
    /// <param name="action">自定义处理操作</param>
    /// <returns>Excel文件字节数组</returns>
    public static byte[] ExportForMark<T>(
        this IList<T> source,
        IList<ExcelColumn> columns,
        string sheetName,
        int startRow = 0,
        Action<ISheet>? action = null
    )
        where T : new()
    {
        var markProperties = GetMarkProperties(typeof(T));

        using var workbook = new XSSFWorkbook();

        var cellStyle = CreateCellStyle(workbook);

        var sheet = workbook.CreateSheet(sheetName);

        var rowCount = source.Count;
        columns = columns
            .Where(x => x.Show)
            .IntersectBy(
                markProperties.Select(x => x.Mark.MarkName ?? x.Property.Name),
                x => x.ColumnName
            )
            .ToList();
        var columnCount = columns.Any() ? columns.Count : markProperties.Count;

        var row = sheet.CreateRow(startRow);

        #region add column headers

        var headerStyle = workbook.CreateCellStyle();
        headerStyle.CloneStyleFrom(cellStyle);
        headerStyle.FillForegroundColor = IndexedColors.Yellow.Index;
        headerStyle.FillPattern = FillPattern.SolidForeground;
        for (var i = 0; i < columnCount; i++)
        {
            string headValue;
            if (columns.Any())
            {
                var (mark, _) = markProperties.FirstOrDefault(x =>
                    string.IsNullOrEmpty(x.Mark.MarkName)
                        ? x.Property.Name == columns[i].ColumnName
                        : x.Mark.MarkName == columns[i].ColumnName
                );
                headValue = mark.MarkValue;
            }
            else
            {
                headValue = markProperties[i].Mark.MarkValue;
            }

            var cell = row.CreateCell(i);
            cell.CellStyle = headerStyle;
            cell.SetCellValue(headValue);
            sheet.SetColumnWidth(i, 256 * 20);
        }

        #endregion

        #region add data rows

        for (var rowIndex = 0; rowIndex < rowCount; rowIndex++)
        {
            var sheetRow = sheet.CreateRow(rowIndex + startRow + 1);

            var item = source[rowIndex];
            for (var columnIndex = 0; columnIndex < columnCount; columnIndex++)
            {
                var (mark, property) = columns.Any()
                    ? markProperties.FirstOrDefault(x =>
                        (x.Mark.MarkName ?? x.Property.Name) == columns[columnIndex].ColumnName
                    )
                    : markProperties[columnIndex];
                var value = property.GetValue(item);

                var cell = sheetRow.CreateCell(columnIndex);
                cell.CellStyle = cellStyle;
                SetCellValue(cell, value, mark);
                if (columnIndex == columnCount)
                {
                    sheet.AutoSizeColumn(columnIndex);
                }
            }
        }

        #endregion

        if (action != null)
        {
            action(sheet);
        }
        else
        {
            sheet.SetAutoFilter(new CellRangeAddress(0, 0, 0, columns.Count - 1));
            sheet.CreateFreezePane(0, 1);
        }

        using var stream = new MemoryStream();
        workbook.Write(stream);
        return stream.ToArray();
    }

    /// <summary>
    /// 根据标记导出Excel(简化版)
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="sheetName">工作表名称</param>
    /// <param name="startRow">起始行</param>
    /// <param name="action">自定义处理操作</param>
    /// <returns>Excel文件字节数组</returns>
    public static byte[] ExportForMark<T>(
        this IList<T> source,
        string sheetName,
        int startRow = 0,
        Action<ISheet>? action = null
    )
    {
        using var workbook = new XSSFWorkbook();
        ProcessExcelExport(source, sheetName, startRow, action, workbook);
        using var stream = new MemoryStream();
        workbook.Write(stream);
        return stream.ToArray();
    }

    /// <summary>
    /// 处理Excel导出
    /// </summary>
    private static void ProcessExcelExport<T>(
        IList<T> source,
        string sheetName,
        int startRow,
        Action<ISheet>? action,
        XSSFWorkbook workbook
    )
    {
        var markProperties = GetMarkProperties(typeof(T));
        var cellStyle = CreateCellStyle(workbook);
        var sheet = workbook.CreateSheet(sheetName);
        var rowCount = source.Count;
        var columnCount = markProperties.Count;
        CreateHeaderRow(startRow, sheet, workbook, cellStyle, columnCount, markProperties);
        FillDataRows(source, startRow, rowCount, sheet, columnCount, markProperties, cellStyle);
        if (action != null)
        {
            action(sheet);
        }
        else
        {
            sheet.SetAutoFilter(new CellRangeAddress(0, 0, 0, columnCount - 1));
            sheet.CreateFreezePane(0, 1);
        }
    }

    /// <summary>
    /// 创建单元格样式
    /// </summary>
    private static ICellStyle CreateCellStyle(IWorkbook workbook)
    {
        var font = workbook.CreateFont();
        font.FontName = "Calibri";
        var cellStyle = workbook.CreateCellStyle();

        // Set borders
        cellStyle.BorderBottom = BorderStyle.Thin;
        cellStyle.BottomBorderColor = IndexedColors.Black.Index;
        cellStyle.BorderLeft = BorderStyle.Thin;
        cellStyle.LeftBorderColor = IndexedColors.Black.Index;
        cellStyle.BorderRight = BorderStyle.Thin;
        cellStyle.RightBorderColor = IndexedColors.Black.Index;
        cellStyle.BorderTop = BorderStyle.Thin;
        cellStyle.TopBorderColor = IndexedColors.Black.Index;

        cellStyle.SetFont(font);
        return cellStyle;
    }

    private static readonly ConcurrentDictionary<Type, MethodInfo?> ProcessExcelExportMethodCache =
        new();

    /// <summary>
    /// 导出多个工作表
    /// </summary>
    /// <param name="options">导出选项集合</param>
    /// <returns>Excel文件字节数组</returns>
    public static byte[]? ExportMultipleForMark(this IEnumerable<ExportMultipleSheet> options)
    {
        // 创建新的Excel工作簿
        using var workbook = new XSSFWorkbook();
        var hasValidGenericCollection = false;
        foreach (var item in options)
        {
            // 获取数据源的类型
            var sourceType = item.Source.GetType();
            // 检查是否为泛型类型
            if (sourceType.GetGenericArguments().Length <= 0)
            {
                continue;
            }
            // 获取泛型参数类型
            var elementType = sourceType.GetGenericArguments()[0];
            // 从缓存中获取或创建处理方法
            var method = ProcessExcelExportMethodCache.GetOrAdd(
                elementType,
                type =>
                    typeof(ExcelExtensions)
                        .GetMethod(
                            nameof(ProcessExcelExport),
                            BindingFlags.NonPublic | BindingFlags.Static
                        )
                        ?.MakeGenericMethod(type)
            );

            // 如果找到处理方法,则执行导出
            if (method != null)
            {
                hasValidGenericCollection = true;
                method.Invoke(
                    null,
                    [item.Source, item.SheetName, item.StartRow, item.Action, workbook]
                );
            }
        }

        if (!hasValidGenericCollection)
        {
            return null;
        }

        using var stream = new MemoryStream();
        workbook.Write(stream);
        return stream.ToArray();
    }

    /// <summary>
    /// 填充数据行
    /// </summary>
    private static void FillDataRows<T>(
        IList<T> source,
        int startRow,
        int rowCount,
        ISheet sheet,
        int columnCount,
        List<MarkProperty> markProperties,
        ICellStyle cellStyle
    )
    {
        #region add data rows

        for (var rowIndex = 0; rowIndex < rowCount; rowIndex++)
        {
            var sheetRow = sheet.CreateRow(rowIndex + startRow + 1);

            var item = source[rowIndex];
            for (var columnIndex = 0; columnIndex < columnCount; columnIndex++)
            {
                var (mark, property) = markProperties[columnIndex];
                var value = property.GetValue(item);

                var cell = sheetRow.CreateCell(columnIndex);
                cell.CellStyle = cellStyle;
                SetCellValue(cell, value, mark);
                if (columnIndex == columnCount)
                {
                    sheet.AutoSizeColumn(columnIndex);
                }
            }
        }

        #endregion
    }

    /// <summary>
    /// 创建表头行
    /// </summary>
    private static void CreateHeaderRow(
        int startRow,
        ISheet sheet,
        XSSFWorkbook workbook,
        ICellStyle cellStyle,
        int columnCount,
        List<MarkProperty> markProperties
    )
    {
        var row = sheet.CreateRow(startRow);

        #region add column headers

        var headerStyle = workbook.CreateCellStyle();
        headerStyle.CloneStyleFrom(cellStyle);
        headerStyle.FillForegroundColor = IndexedColors.Yellow.Index;
        headerStyle.FillPattern = FillPattern.SolidForeground;
        for (var i = 0; i < columnCount; i++)
        {
            var headValue = markProperties[i].Mark.MarkValue;
            var cell = row.CreateCell(i);
            cell.CellStyle = headerStyle;
            cell.SetCellValue(headValue);
            sheet.SetColumnWidth(i, 256 * 20);
        }

        #endregion
    }

    private readonly record struct MarkProperty(ExportMarkAttribute Mark, PropertyInfo Property);

    private static readonly ConcurrentDictionary<Type, List<MarkProperty>> MarkPropertiesCache =
        new();

    /// <summary>
    /// 获取标记属性
    /// </summary>
    private static List<MarkProperty> GetMarkProperties(Type type)
    {
        if (MarkPropertiesCache.TryGetValue(type, out var markProperties))
        {
            return markProperties;
        }

        markProperties = type.GetProperties()
            .Select(x =>
            {
                var markAttr = x.GetCustomAttribute<ExportMarkAttribute>();
                if (markAttr == null)
                {
                    return (MarkProperty?)null;
                }

                return new MarkProperty(markAttr, x);
            })
            .Where(x => x != null)
            .Select(x => x!.Value)
            .ToList();
        MarkPropertiesCache.TryAdd(type, markProperties);
        return markProperties;
    }

    /// <summary>
    /// 设置单元格值
    /// </summary>
    private static void SetCellValue(ICell cell, object? value, ExportMarkAttribute mark)
    {
        try
        {
            if (value is null || (value is string str && string.IsNullOrEmpty(str)))
            {
                cell.SetCellValue((string?)null);
                return;
            }

            if (value is DateTime dateTimeValue)
            {
                var format = mark.Format ?? "yyyy-MM-dd";
                cell.SetCellValue(dateTimeValue.ToString(format));
            }
            else if (value is int intValue)
            {
                if (string.IsNullOrEmpty(mark.Format))
                {
                    cell.SetCellValue(intValue);
                }
                else
                {
                    cell.SetCellValue(intValue.ToString(mark.Format));
                }
            }
            else if (value is decimal decimalValue)
            {
                var dValue = (double)decimalValue;
                if (string.IsNullOrEmpty(mark.Format))
                {
                    cell.SetCellValue(dValue);
                }
                else
                {
                    cell.SetCellValue(dValue.ToString(mark.Format));
                }
            }
            else if (value is double doubleValue)
            {
                if (string.IsNullOrEmpty(mark.Format))
                {
                    cell.SetCellValue(doubleValue);
                }
                else
                {
                    cell.SetCellValue(doubleValue.ToString(mark.Format));
                }
            }
            else if (value is bool boolValue)
            {
                cell.SetCellValue(boolValue);
            }
            else if (value is string strValue)
            {
                cell.SetCellValue(strValue);
            }
            else if (value is IEnumerable enumerable)
            {
                var enumerableResult = enumerable
                    .Cast<string>()
                    .Where(x => !string.IsNullOrWhiteSpace(x))
                    .ToList();
                var enumerableSplit = mark.EnumerableSplit ?? "\\";
                var fillValue = string.Join(enumerableSplit, enumerableResult);
                cell.SetCellValue(fillValue);
            }
            else
            {
                cell.SetCellValue(value.ToString());
            }
        }
        catch (Exception)
        {
            cell.SetCellValue("");
        }
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

上述代码是一个用于导出Excel文件的扩展方法类,使用了NPOI库来处理Excel文件的创建和写入。这个类提供了多种方法来根据特定的标记属性导出数据到Excel工作表中。以下是代码的主要功能和结构的详细解释:

主要功能

  1. 导出Excel文件

    • 提供了多个重载的ExportForMark方法,允许用户根据指定的列配置和数据源导出Excel文件。
    • 支持自定义工作表名称、起始行和自定义操作(如设置过滤器、冻结窗格等)。
  2. 标记属性处理

    • 使用ExportMarkAttribute特性来标记需要导出的属性。通过反射获取这些标记属性,并根据它们的配置生成Excel的列头和数据行。
  3. 样式设置

    • 提供了创建单元格样式的方法,包括边框、字体等样式设置,以确保生成的Excel文件具有良好的可读性。
  4. 多工作表导出

    • 提供了ExportMultipleForMark方法,允许用户一次性导出多个工作表。每个工作表的数据源可以是不同类型的集合。
  5. 数据填充

    • 通过FillDataRowsCreateHeaderRow等方法,分别处理数据行和表头的创建与填充。
  6. 单元格值设置

    • 提供了SetCellValue方法,根据不同的数据类型(如字符串、日期、数字、布尔值等)设置单元格的值,并支持格式化。

代码结构

  • 类和方法

    • ExcelExtensions类是一个静态类,包含多个静态方法。
    • ExportForMark<T>:根据标记导出Excel,支持列配置。
    • ExportMultipleForMark:导出多个工作表。
    • ProcessExcelExport:处理Excel导出的核心逻辑。
    • CreateCellStyle:创建单元格样式。
    • GetMarkProperties:获取标记属性。
    • SetCellValue:设置单元格的值。
  • 数据结构

    • MarkProperty:一个记录结构,包含标记属性和对应的属性信息。
    • MarkPropertiesCache:用于缓存标记属性,以提高性能。

使用场景

这个类可以用于需要将数据导出为Excel文件的场景,例如:

  • 报表生成
  • 数据导出功能
  • 数据分析工具

总结

总体来说,这段代码提供了一个灵活且功能强大的Excel导出工具,能够根据用户定义的标记属性和配置生成格式良好的Excel文件,适用于多种数据导出需求。

loading