444 lines
16 KiB
C#
444 lines
16 KiB
C#
using MembranePoreTester.Models;
|
||
using OxyPlot;
|
||
using OxyPlot.Legends;
|
||
using OxyPlot.Wpf;
|
||
using System;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Documents;
|
||
using System.Windows.Media;
|
||
using System.Windows.Media.Imaging;
|
||
using FontWeights = System.Windows.FontWeights;
|
||
using HorizontalAlignment = System.Windows.HorizontalAlignment;
|
||
|
||
namespace MembranePoreTester.Helpers
|
||
{
|
||
/// <summary>
|
||
/// 膜测试报告生成器(泡点法 + 孔分布)
|
||
/// 排版规范:A4自适应、居中对齐、表格规整、图表清晰、数据准确
|
||
/// </summary>
|
||
public static class ReportGenerator
|
||
{
|
||
#region 公开调用方法
|
||
/// <summary>
|
||
/// 生成泡点法测试报告
|
||
/// </summary>
|
||
public static void GenerateBubblePointReport(BubblePointRecord record)
|
||
{
|
||
if (record == null)
|
||
{
|
||
MessageBox.Show("无测试数据,无法生成报告");
|
||
return;
|
||
}
|
||
|
||
var doc = new FlowDocument();
|
||
SetA4PageSettings(doc);
|
||
AddReportTitle(doc, "泡点法测试报告");
|
||
AddTestInfo(doc, record);
|
||
AddBubblePointDataTable(doc, record);
|
||
AddStandardRemark(doc);
|
||
|
||
ShowPrintPreview(doc, "泡点法测试报告");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成孔分布测试报告
|
||
/// </summary>
|
||
public static void GeneratePoreDistributionReport(PoreDistributionRecord record, PlotModel plotModel)
|
||
{
|
||
if (record == null || record.DataPoints == null || !record.DataPoints.Any())
|
||
{
|
||
MessageBox.Show("无测试数据,无法生成报告");
|
||
return;
|
||
}
|
||
|
||
var doc = new FlowDocument();
|
||
SetA4PageSettings(doc);
|
||
AddReportTitle(doc, "孔分布测试报告");
|
||
AddTestInfo(doc, record);
|
||
AddPoreDistributionResult(doc, record);
|
||
AddPressureFlowChart(doc, plotModel);
|
||
AddTestDataTables(doc, record);
|
||
AddPoreDistributionTable(doc, record);
|
||
AddStandardRemark(doc);
|
||
|
||
ShowPrintPreview(doc, "孔分布测试报告");
|
||
}
|
||
#endregion
|
||
|
||
#region 通用排版模块
|
||
/// <summary>
|
||
/// 设置A4标准打印页面
|
||
/// </summary>
|
||
private static void SetA4PageSettings(FlowDocument doc)
|
||
{
|
||
doc.PageWidth = 793; // A4宽度
|
||
doc.PageHeight = 1122; // A4高度
|
||
doc.PagePadding = new Thickness(40);
|
||
doc.ColumnWidth = 793;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加报告标题(居中、加粗、大字体)
|
||
/// </summary>
|
||
private static void AddReportTitle(FlowDocument doc, string title)
|
||
{
|
||
var titlePara = new Paragraph(new Run(title))
|
||
{
|
||
FontSize = 26,
|
||
FontWeight = FontWeights.Bold,
|
||
TextAlignment = TextAlignment.Center,
|
||
Margin = new Thickness(0, 0, 0, 25)
|
||
};
|
||
doc.Blocks.Add(titlePara);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加测试基础信息(日期、测试人)
|
||
/// </summary>
|
||
private static void AddTestInfo(FlowDocument doc, dynamic record)
|
||
{
|
||
var infoPara = new Paragraph(new Run(
|
||
$"测试日期:{record.TestDate:yyyy-MM-dd HH:mm} 测试人员:{record.Tester}"))
|
||
{
|
||
FontSize = 13,
|
||
Margin = new Thickness(0, 0, 0, 20)
|
||
};
|
||
doc.Blocks.Add(infoPara);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加标准备注(依据国标)
|
||
/// </summary>
|
||
private static void AddStandardRemark(FlowDocument doc)
|
||
{
|
||
var remark = new Paragraph(new Run("本报告依据 GB/T 32361-2015《膜材料孔径测试 泡点法》生成"))
|
||
{
|
||
FontSize = 12,
|
||
FontStyle = FontStyles.Italic,
|
||
TextAlignment = TextAlignment.Center,
|
||
Margin = new Thickness(0, 30, 0, 0)
|
||
};
|
||
doc.Blocks.Add(remark);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建标准两列表格
|
||
/// </summary>
|
||
private static Table CreateStandardTable()
|
||
{
|
||
var table = new Table
|
||
{
|
||
CellSpacing = 0,
|
||
Margin = new Thickness(0, 10, 0, 20),
|
||
BorderThickness = new Thickness(1)
|
||
};
|
||
table.Columns.Add(new TableColumn { Width = new GridLength(180) });
|
||
table.Columns.Add(new TableColumn { Width = new GridLength(500) });
|
||
table.BorderBrush = Brushes.Black;
|
||
return table;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 向表格添加一行(名称 + 值)
|
||
/// </summary>
|
||
private static void AddTableRow(TableRowGroup group, string label, string value)
|
||
{
|
||
var row = new TableRow();
|
||
row.Cells.Add(CreateTableCell(label, true));
|
||
row.Cells.Add(CreateTableCell(value ?? "-", false));
|
||
group.Rows.Add(row);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建表格单元格
|
||
/// </summary>
|
||
private static TableCell CreateTableCell(string text, bool isBold)
|
||
{
|
||
var para = new Paragraph(new Run(text))
|
||
{
|
||
FontSize = 13,
|
||
Padding = new Thickness(8, 6, 8, 6),
|
||
Margin = new Thickness(0)
|
||
};
|
||
if (isBold) para.FontWeight = FontWeights.Bold;
|
||
return new TableCell(para);
|
||
}
|
||
#endregion
|
||
|
||
#region 泡点法报告内容
|
||
private static void AddBubblePointDataTable(FlowDocument doc, BubblePointRecord record)
|
||
{
|
||
var table = CreateStandardTable();
|
||
var group = new TableRowGroup();
|
||
|
||
AddTableRow(group, "膜类型", record.SampleType);
|
||
AddTableRow(group, "样品规格", record.SampleSpec);
|
||
AddTableRow(group, "测试温度", $"{record.RoomTemperature:F1} ℃");
|
||
AddTableRow(group, "浸润时间", $"{record.SoakingTime:F1} h");
|
||
AddTableRow(group, "测试液体", record.Liquid?.Name);
|
||
AddTableRow(group, "液体厂家", record.LiquidManufacturer);
|
||
AddTableRow(group, "泡点压力", $"{record.BubblePointPressure} {record.PressureUnit}");
|
||
AddTableRow(group, "最大孔径", $"{record.MaxPoreSize:F3} μm");
|
||
|
||
table.RowGroups.Add(group);
|
||
doc.Blocks.Add(table);
|
||
}
|
||
#endregion
|
||
|
||
#region 孔分布报告内容
|
||
private static void AddPoreDistributionResult(FlowDocument doc, PoreDistributionRecord record)
|
||
{
|
||
var title = new Paragraph(new Run("测试计算结果"))
|
||
{
|
||
FontSize = 18,
|
||
FontWeight = FontWeights.Bold,
|
||
Margin = new Thickness(0, 15, 0, 10)
|
||
};
|
||
doc.Blocks.Add(title);
|
||
|
||
var table = CreateStandardTable();
|
||
var group = new TableRowGroup();
|
||
|
||
AddTableRow(group, "泡点压力", $"{record.BubblePointPressure} {record.PressureUnit}");
|
||
AddTableRow(group, "平均孔径", $"{record.AveragePoreSize:F3} μm");
|
||
AddTableRow(group, "测试液体", record.Liquid?.Name ?? "-");
|
||
AddTableRow(group, "液体表面张力", record.Liquid?.SurfaceTension.ToString() ?? "-");
|
||
AddTableRow(group, "液体厂家", record.LiquidManufacturer ?? "-");
|
||
AddTableRow(group, "测试温度", $"{record.RoomTemperature:F1} ℃");
|
||
AddTableRow(group, "浸润时间", $"{record.SoakingTime:F1} h");
|
||
AddTableRow(group, "压力单位", record.PressureUnit);
|
||
AddTableRow(group, "数据点数", record.DataPoints?.Count.ToString() ?? "0");
|
||
|
||
table.RowGroups.Add(group);
|
||
doc.Blocks.Add(table);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加压力-流量曲线图(带图例:红线=干膜,蓝线=湿膜)
|
||
/// </summary>
|
||
private static void AddPressureFlowChart(FlowDocument doc, PlotModel plotModel)
|
||
{
|
||
if (plotModel == null) return;
|
||
|
||
var title = new Paragraph(new Run("压力 - 流量曲线图"))
|
||
{
|
||
FontSize = 16,
|
||
FontWeight = FontWeights.Bold,
|
||
Margin = new Thickness(0, 20, 0, 15)
|
||
};
|
||
doc.Blocks.Add(title);
|
||
|
||
// 确保图表显示图例(红线=干膜,蓝线=湿膜)
|
||
plotModel.IsLegendVisible = true;
|
||
//plotModel.LegendPosition = LegendPosition.RightTop;
|
||
|
||
using var stream = new MemoryStream();
|
||
var exporter = new PngExporter { Width = 700, Height = 400, Resolution = 96 };
|
||
exporter.Export(plotModel, stream);
|
||
stream.Position = 0;
|
||
|
||
var bitmap = new BitmapImage();
|
||
bitmap.BeginInit();
|
||
bitmap.StreamSource = stream;
|
||
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
||
bitmap.EndInit();
|
||
bitmap.Freeze();
|
||
|
||
var image = new Image
|
||
{
|
||
Source = bitmap,
|
||
Width = 700,
|
||
Height = 400,
|
||
Stretch = Stretch.Uniform,
|
||
HorizontalAlignment = HorizontalAlignment.Center
|
||
};
|
||
|
||
var container = new BlockUIContainer(image)
|
||
{
|
||
Margin = new Thickness(0, 0, 0, 20)
|
||
};
|
||
doc.Blocks.Add(container);
|
||
|
||
var legend = new Paragraph(new Run("■ 红色线:干膜流量 ■ 蓝色线:湿膜流量"))
|
||
{
|
||
FontSize = 14,
|
||
FontWeight = FontWeights.Bold,
|
||
TextAlignment = TextAlignment.Center,
|
||
Margin = new Thickness(0, 0, 0, 20)
|
||
};
|
||
doc.Blocks.Add(legend);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加湿膜/干膜数据表(有效数据、排序、排版规整)
|
||
/// </summary>
|
||
private static void AddTestDataTables(FlowDocument doc, PoreDistributionRecord record)
|
||
{
|
||
// 湿膜数据
|
||
var wetPoints = record.DataPoints
|
||
.Where(x => x.WetFlow > 0)
|
||
.OrderBy(x => x.Pressure)
|
||
.ToList();
|
||
|
||
if (wetPoints.Any())
|
||
{
|
||
AddDataTableTitle(doc, "湿膜测试数据");
|
||
var wetTable = CreateTwoColumnDataTable("压力", "湿膜流量 (L/min)");
|
||
var wetGroup = new TableRowGroup();
|
||
foreach (var p in wetPoints)
|
||
{
|
||
var row = new TableRow();
|
||
row.Cells.Add(CreateDataCell(p.Pressure.ToString("F2")));
|
||
row.Cells.Add(CreateDataCell(p.WetFlow.ToString("F3")));
|
||
wetGroup.Rows.Add(row);
|
||
}
|
||
wetTable.RowGroups.Add(wetGroup);
|
||
doc.Blocks.Add(wetTable);
|
||
}
|
||
|
||
// 干膜数据
|
||
var dryPoints = record.DataPoints
|
||
.Where(x => x.DryFlow > 0)
|
||
.OrderBy(x => x.Pressure)
|
||
.ToList();
|
||
|
||
if (dryPoints.Any())
|
||
{
|
||
AddDataTableTitle(doc, "干膜测试数据");
|
||
var dryTable = CreateTwoColumnDataTable("压力", "干膜流量 (L/min)");
|
||
var dryGroup = new TableRowGroup();
|
||
foreach (var p in dryPoints)
|
||
{
|
||
var row = new TableRow();
|
||
row.Cells.Add(CreateDataCell(p.Pressure.ToString("F2")));
|
||
row.Cells.Add(CreateDataCell(p.DryFlow.ToString("F3")));
|
||
dryGroup.Rows.Add(row);
|
||
}
|
||
dryTable.RowGroups.Add(dryGroup);
|
||
doc.Blocks.Add(dryTable);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加孔分布表格
|
||
/// </summary>
|
||
private static void AddPoreDistributionTable(FlowDocument doc, PoreDistributionRecord record)
|
||
{
|
||
if (record.PoreDistributions == null || !record.PoreDistributions.Any()) return;
|
||
|
||
AddDataTableTitle(doc, "孔径分布结果");
|
||
|
||
var table = new Table
|
||
{
|
||
CellSpacing = 0,
|
||
Margin = new Thickness(0, 10, 0, 20),
|
||
BorderThickness = new Thickness(1),
|
||
BorderBrush = Brushes.Black
|
||
};
|
||
table.Columns.Add(new TableColumn { Width = new GridLength(220) });
|
||
table.Columns.Add(new TableColumn { Width = new GridLength(220) });
|
||
table.Columns.Add(new TableColumn { Width = new GridLength(220) });
|
||
|
||
var group = new TableRowGroup();
|
||
// 表头
|
||
var header = new TableRow();
|
||
header.Cells.Add(CreateDataCell("孔径下限 (μm)", true));
|
||
header.Cells.Add(CreateDataCell("孔径上限 (μm)", true));
|
||
header.Cells.Add(CreateDataCell("分布百分比 (%)", true));
|
||
group.Rows.Add(header);
|
||
|
||
// 数据行
|
||
foreach (var item in record.PoreDistributions.OrderBy(x => x.LowerPore))
|
||
{
|
||
var row = new TableRow();
|
||
row.Cells.Add(CreateDataCell(item.LowerPore.ToString("F3")));
|
||
row.Cells.Add(CreateDataCell(item.UpperPore.ToString("F3")));
|
||
row.Cells.Add(CreateDataCell(item.Percentage.ToString("F1")));
|
||
group.Rows.Add(row);
|
||
}
|
||
|
||
table.RowGroups.Add(group);
|
||
doc.Blocks.Add(table);
|
||
}
|
||
|
||
// 辅助:表格标题
|
||
private static void AddDataTableTitle(FlowDocument doc, string title)
|
||
{
|
||
var para = new Paragraph(new Run(title))
|
||
{
|
||
FontSize = 15,
|
||
FontWeight = FontWeights.Bold,
|
||
Margin = new Thickness(0, 20, 0, 8)
|
||
};
|
||
doc.Blocks.Add(para);
|
||
}
|
||
|
||
// 辅助:创建双列数据表格
|
||
private static Table CreateTwoColumnDataTable(string col1, string col2)
|
||
{
|
||
var table = new Table
|
||
{
|
||
CellSpacing = 0,
|
||
Margin = new Thickness(0, 0, 0, 15),
|
||
BorderThickness = new Thickness(1),
|
||
BorderBrush = Brushes.Black
|
||
};
|
||
table.Columns.Add(new TableColumn { Width = new GridLength(330) });
|
||
table.Columns.Add(new TableColumn { Width = new GridLength(330) });
|
||
|
||
var headerGroup = new TableRowGroup();
|
||
var header = new TableRow();
|
||
header.Cells.Add(CreateDataCell(col1, true));
|
||
header.Cells.Add(CreateDataCell(col2, true));
|
||
headerGroup.Rows.Add(header);
|
||
table.RowGroups.Add(headerGroup);
|
||
|
||
return table;
|
||
}
|
||
|
||
// 辅助:数据单元格
|
||
private static TableCell CreateDataCell(string text, bool isHeader = false)
|
||
{
|
||
var para = new Paragraph(new Run(text))
|
||
{
|
||
FontSize = 12,
|
||
Padding = new Thickness(6),
|
||
Margin = new Thickness(0),
|
||
TextAlignment = TextAlignment.Center
|
||
};
|
||
if (isHeader) para.FontWeight = FontWeights.Bold;
|
||
return new TableCell(para);
|
||
}
|
||
#endregion
|
||
|
||
#region 打印预览
|
||
/// <summary>
|
||
/// 显示打印对话框并打印
|
||
/// </summary>
|
||
private static void ShowPrintPreview(FlowDocument document, string title)
|
||
{
|
||
try
|
||
{
|
||
var dialog = new PrintDialog();
|
||
if (dialog.ShowDialog() == true)
|
||
{
|
||
document.PageWidth = dialog.PrintableAreaWidth;
|
||
document.PageHeight = dialog.PrintableAreaHeight;
|
||
document.PagePadding = new Thickness(40);
|
||
|
||
// 修复:正确获取分页器
|
||
IDocumentPaginatorSource paginatorSource = document;
|
||
dialog.PrintDocument(paginatorSource.DocumentPaginator, title);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"打印失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
}
|
||
#endregion
|
||
}
|
||
} |