277 lines
9.2 KiB
C#
277 lines
9.2 KiB
C#
using OfficeOpenXml;
|
||
using OfficeOpenXml.Style;
|
||
using SixLabors.ImageSharp;
|
||
using SixLabors.ImageSharp.Processing;
|
||
using SixLabors.ImageSharp.Formats.Jpeg;
|
||
using WorkCameraExport.Models;
|
||
|
||
namespace WorkCameraExport.Services
|
||
{
|
||
/// <summary>
|
||
/// Excel 服务类 - 生成带图片的 Excel 文件
|
||
/// </summary>
|
||
public class ExcelService
|
||
{
|
||
// 图片尺寸配置
|
||
private const int ImageWidth = 100;
|
||
private const int ImageHeight = 60;
|
||
private const int JpegQuality = 50;
|
||
|
||
// Excel 列配置
|
||
private static readonly string[] Headers = new[]
|
||
{
|
||
"序号", "部门名称", "拍照时间", "经度", "纬度", "位置",
|
||
"工作内容", "施工人员", "状态", "施工图片", "创建时间", "更新时间"
|
||
};
|
||
|
||
private static readonly int[] ColumnWidths = new[]
|
||
{
|
||
8, 20, 18, 12, 12, 30, 40, 20, 10, 15, 18, 18
|
||
};
|
||
|
||
static ExcelService()
|
||
{
|
||
// 设置 EPPlus 许可证(非商业用途)
|
||
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成 Excel 文件
|
||
/// </summary>
|
||
/// <param name="filePath">输出文件路径</param>
|
||
/// <param name="records">工作记录列表</param>
|
||
/// <param name="images">图片数据字典(URL -> 图片字节数组)</param>
|
||
/// <param name="progressCallback">进度回调(当前行, 总行数)</param>
|
||
public void GenerateExcel(
|
||
string filePath,
|
||
List<WorkRecordExportDto> records,
|
||
Dictionary<string, byte[]> images,
|
||
Action<int, int>? progressCallback = null)
|
||
{
|
||
using var package = new ExcelPackage();
|
||
var worksheet = package.Workbook.Worksheets.Add("工作记录");
|
||
|
||
// 设置表头
|
||
SetupHeaders(worksheet);
|
||
|
||
// 填充数据
|
||
int rowIndex = 2;
|
||
int totalRows = records.Count;
|
||
|
||
foreach (var record in records)
|
||
{
|
||
FillRecordRow(worksheet, rowIndex, record, images);
|
||
progressCallback?.Invoke(rowIndex - 1, totalRows);
|
||
rowIndex++;
|
||
}
|
||
|
||
// 设置列宽
|
||
SetColumnWidths(worksheet);
|
||
|
||
// 冻结首行
|
||
worksheet.View.FreezePanes(2, 1);
|
||
|
||
// 保存文件
|
||
package.SaveAs(new FileInfo(filePath));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置表头
|
||
/// </summary>
|
||
private void SetupHeaders(ExcelWorksheet worksheet)
|
||
{
|
||
for (int i = 0; i < Headers.Length; i++)
|
||
{
|
||
var cell = worksheet.Cells[1, i + 1];
|
||
cell.Value = Headers[i];
|
||
cell.Style.Font.Bold = true;
|
||
cell.Style.Fill.PatternType = ExcelFillStyle.Solid;
|
||
cell.Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.FromArgb(0, 122, 204));
|
||
cell.Style.Font.Color.SetColor(System.Drawing.Color.White);
|
||
cell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
|
||
cell.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
|
||
cell.Style.Border.BorderAround(ExcelBorderStyle.Thin);
|
||
}
|
||
|
||
// 设置表头行高
|
||
worksheet.Row(1).Height = 25;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 填充单行数据
|
||
/// </summary>
|
||
private void FillRecordRow(
|
||
ExcelWorksheet worksheet,
|
||
int rowIndex,
|
||
WorkRecordExportDto record,
|
||
Dictionary<string, byte[]> images)
|
||
{
|
||
// 序号
|
||
SetCellValue(worksheet, rowIndex, 1, rowIndex - 1);
|
||
|
||
// 部门名称
|
||
SetCellValue(worksheet, rowIndex, 2, record.DeptName);
|
||
|
||
// 拍照时间
|
||
SetCellValue(worksheet, rowIndex, 3, record.RecordTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "");
|
||
|
||
// 经度
|
||
SetCellValue(worksheet, rowIndex, 4, record.Longitude);
|
||
|
||
// 纬度
|
||
SetCellValue(worksheet, rowIndex, 5, record.Latitude);
|
||
|
||
// 位置
|
||
SetCellValue(worksheet, rowIndex, 6, record.Address);
|
||
|
||
// 工作内容
|
||
SetCellValue(worksheet, rowIndex, 7, record.Content);
|
||
worksheet.Cells[rowIndex, 7].Style.WrapText = true;
|
||
|
||
// 施工人员
|
||
SetCellValue(worksheet, rowIndex, 8, string.Join(", ", record.Workers));
|
||
|
||
// 状态
|
||
SetCellValue(worksheet, rowIndex, 9, record.StatusName);
|
||
|
||
// 施工图片 - 嵌入图片
|
||
EmbedImages(worksheet, rowIndex, 10, record.Images, images);
|
||
|
||
// 创建时间
|
||
SetCellValue(worksheet, rowIndex, 11, record.CreateTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "");
|
||
|
||
// 更新时间
|
||
SetCellValue(worksheet, rowIndex, 12, record.UpdateTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "");
|
||
|
||
// 设置行高(根据图片数量调整)
|
||
int imageCount = record.Images.Count;
|
||
if (imageCount > 0)
|
||
{
|
||
// 每张图片高度 + 间距
|
||
double rowHeight = Math.Max(20, imageCount * (ImageHeight + 5));
|
||
worksheet.Row(rowIndex).Height = rowHeight;
|
||
}
|
||
else
|
||
{
|
||
worksheet.Row(rowIndex).Height = 20;
|
||
}
|
||
|
||
// 设置边框
|
||
for (int col = 1; col <= Headers.Length; col++)
|
||
{
|
||
worksheet.Cells[rowIndex, col].Style.Border.BorderAround(ExcelBorderStyle.Thin);
|
||
worksheet.Cells[rowIndex, col].Style.VerticalAlignment = ExcelVerticalAlignment.Center;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置单元格值
|
||
/// </summary>
|
||
private void SetCellValue(ExcelWorksheet worksheet, int row, int col, object value)
|
||
{
|
||
worksheet.Cells[row, col].Value = value;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 嵌入图片到单元格
|
||
/// </summary>
|
||
private void EmbedImages(
|
||
ExcelWorksheet worksheet,
|
||
int rowIndex,
|
||
int colIndex,
|
||
List<string> imageUrls,
|
||
Dictionary<string, byte[]> images)
|
||
{
|
||
if (imageUrls.Count == 0)
|
||
{
|
||
SetCellValue(worksheet, rowIndex, colIndex, "无图片");
|
||
return;
|
||
}
|
||
|
||
int imageOffset = 0;
|
||
int successCount = 0;
|
||
|
||
foreach (var url in imageUrls)
|
||
{
|
||
if (!images.TryGetValue(url, out var imageData) || imageData == null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 压缩和调整图片大小
|
||
var processedImage = ProcessImage(imageData);
|
||
if (processedImage == null) continue;
|
||
|
||
// 添加图片到 Excel
|
||
using var stream = new MemoryStream(processedImage);
|
||
var picture = worksheet.Drawings.AddPicture(
|
||
$"img_{rowIndex}_{colIndex}_{successCount}",
|
||
stream);
|
||
|
||
// 设置图片位置
|
||
picture.SetPosition(rowIndex - 1, imageOffset, colIndex - 1, 2);
|
||
picture.SetSize(ImageWidth, ImageHeight);
|
||
|
||
imageOffset += ImageHeight + 5;
|
||
successCount++;
|
||
}
|
||
catch
|
||
{
|
||
// 忽略单张图片处理失败
|
||
}
|
||
}
|
||
|
||
// 如果没有成功嵌入任何图片,显示文本
|
||
if (successCount == 0)
|
||
{
|
||
SetCellValue(worksheet, rowIndex, colIndex, $"共{imageUrls.Count}张图片(加载失败)");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理图片(调整大小和压缩)
|
||
/// </summary>
|
||
private byte[]? ProcessImage(byte[] imageData)
|
||
{
|
||
try
|
||
{
|
||
using var image = SixLabors.ImageSharp.Image.Load(imageData);
|
||
|
||
// 调整大小
|
||
image.Mutate(x => x.Resize(new ResizeOptions
|
||
{
|
||
Size = new SixLabors.ImageSharp.Size(ImageWidth, ImageHeight),
|
||
Mode = ResizeMode.Max
|
||
}));
|
||
|
||
// 压缩为 JPEG
|
||
using var outputStream = new MemoryStream();
|
||
var encoder = new JpegEncoder
|
||
{
|
||
Quality = JpegQuality
|
||
};
|
||
image.Save(outputStream, encoder);
|
||
|
||
return outputStream.ToArray();
|
||
}
|
||
catch
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置列宽
|
||
/// </summary>
|
||
private void SetColumnWidths(ExcelWorksheet worksheet)
|
||
{
|
||
for (int i = 0; i < ColumnWidths.Length; i++)
|
||
{
|
||
worksheet.Column(i + 1).Width = ColumnWidths[i];
|
||
}
|
||
}
|
||
}
|
||
}
|