diff --git a/Helpers/ExportHelper.cs b/Helpers/ExportHelper.cs index a771ade..b105548 100644 --- a/Helpers/ExportHelper.cs +++ b/Helpers/ExportHelper.cs @@ -27,31 +27,84 @@ public static class ExportHelper package.SaveAs(new FileInfo(filePath)); } + //public static void ExportPoreDistribution(PoreDistributionEntity entity, string filePath) + //{ + // using var package = new ExcelPackage(); + // var sheet = package.Workbook.Worksheets.Add("孔分布测试"); + // sheet.Cells["A1"].Value = "工位"; sheet.Cells["B1"].Value = entity.StationId; + // sheet.Cells["A2"].Value = "测试日期"; sheet.Cells["B2"].Value = entity.TestDate.ToString("yyyy-MM-dd HH:mm:ss"); + // sheet.Cells["A3"].Value = "测试者"; sheet.Cells["B3"].Value = entity.Tester; + // sheet.Cells["A4"].Value = "样品类型"; sheet.Cells["B4"].Value = entity.SampleType; + // sheet.Cells["A5"].Value = "规格"; sheet.Cells["B5"].Value = entity.SampleSpec; + // sheet.Cells["A6"].Value = "室温(°C)"; sheet.Cells["B6"].Value = entity.RoomTemperature; + // sheet.Cells["A7"].Value = "浸润时间(h)"; sheet.Cells["B7"].Value = entity.SoakingTime; + // sheet.Cells["A8"].Value = "测试液体"; sheet.Cells["B8"].Value = entity.LiquidName; + // sheet.Cells["A9"].Value = "液体生产厂家"; sheet.Cells["B9"].Value = entity.LiquidManufacturer; + // sheet.Cells["A10"].Value = "泡点压力"; sheet.Cells["B10"].Value = $"{entity.BubblePointPressure} {entity.PressureUnit}"; + // sheet.Cells["A11"].Value = "平均孔径(μm)"; sheet.Cells["B11"].Value = entity.AveragePoreSize; + + // // 数据点表格 + // sheet.Cells["A13"].Value = "压力"; sheet.Cells["B13"].Value = "湿膜流量(L/min)"; sheet.Cells["C13"].Value = "干膜流量(L/min)"; + // int row = 14; + // foreach (var dp in entity.DataPoints) + // { + // sheet.Cells[$"A{row}"].Value = dp.Pressure; + // sheet.Cells[$"B{row}"].Value = dp.WetFlow; + // sheet.Cells[$"C{row}"].Value = dp.DryFlow; + // row++; + // } + + // package.SaveAs(new FileInfo(filePath)); + //} + public static void ExportPoreDistribution(PoreDistributionEntity entity, string filePath) { using var package = new ExcelPackage(); - var sheet = package.Workbook.Worksheets.Add("孔分布测试"); - sheet.Cells["A1"].Value = "工位"; sheet.Cells["B1"].Value = entity.StationId; - sheet.Cells["A2"].Value = "测试日期"; sheet.Cells["B2"].Value = entity.TestDate.ToString("yyyy-MM-dd HH:mm:ss"); - sheet.Cells["A3"].Value = "测试者"; sheet.Cells["B3"].Value = entity.Tester; - sheet.Cells["A4"].Value = "样品类型"; sheet.Cells["B4"].Value = entity.SampleType; - sheet.Cells["A5"].Value = "规格"; sheet.Cells["B5"].Value = entity.SampleSpec; - sheet.Cells["A6"].Value = "室温(°C)"; sheet.Cells["B6"].Value = entity.RoomTemperature; - sheet.Cells["A7"].Value = "浸润时间(h)"; sheet.Cells["B7"].Value = entity.SoakingTime; - sheet.Cells["A8"].Value = "测试液体"; sheet.Cells["B8"].Value = entity.LiquidName; - sheet.Cells["A9"].Value = "液体生产厂家"; sheet.Cells["B9"].Value = entity.LiquidManufacturer; - sheet.Cells["A10"].Value = "泡点压力"; sheet.Cells["B10"].Value = $"{entity.BubblePointPressure} {entity.PressureUnit}"; - sheet.Cells["A11"].Value = "平均孔径(μm)"; sheet.Cells["B11"].Value = entity.AveragePoreSize; - // 数据点表格 - sheet.Cells["A13"].Value = "压力"; sheet.Cells["B13"].Value = "湿膜流量(L/min)"; sheet.Cells["C13"].Value = "干膜流量(L/min)"; - int row = 14; - foreach (var dp in entity.DataPoints) + // 公共信息工作表(可选) + var infoSheet = package.Workbook.Worksheets.Add("测试信息"); + infoSheet.Cells["A1"].Value = "工位"; infoSheet.Cells["B1"].Value = entity.StationId; + infoSheet.Cells["A2"].Value = "测试日期"; infoSheet.Cells["B2"].Value = entity.TestDate.ToString("yyyy-MM-dd HH:mm:ss"); + infoSheet.Cells["A3"].Value = "测试者"; infoSheet.Cells["B3"].Value = entity.Tester; + infoSheet.Cells["A4"].Value = "样品类型"; infoSheet.Cells["B4"].Value = entity.SampleType; + infoSheet.Cells["A5"].Value = "规格"; infoSheet.Cells["B5"].Value = entity.SampleSpec; + infoSheet.Cells["A6"].Value = "室温(°C)"; infoSheet.Cells["B6"].Value = entity.RoomTemperature; + infoSheet.Cells["A7"].Value = "浸润时间(h)"; infoSheet.Cells["B7"].Value = entity.SoakingTime; + infoSheet.Cells["A8"].Value = "测试液体"; infoSheet.Cells["B8"].Value = entity.LiquidName; + infoSheet.Cells["A9"].Value = "液体生产厂家"; infoSheet.Cells["B9"].Value = entity.LiquidManufacturer; + infoSheet.Cells["A10"].Value = "压力单位"; infoSheet.Cells["B10"].Value = entity.PressureUnit; + infoSheet.Cells["A11"].Value = "平均孔径(μm)"; infoSheet.Cells["B11"].Value = entity.AveragePoreSize; + + // 湿膜数据表(仅保留湿膜流量 > 0 的点) + var wetPoints = entity.DataPoints.Where(dp => dp.WetFlow > 0).OrderBy(dp => dp.Pressure).ToList(); + if (wetPoints.Any()) { - sheet.Cells[$"A{row}"].Value = dp.Pressure; - sheet.Cells[$"B{row}"].Value = dp.WetFlow; - sheet.Cells[$"C{row}"].Value = dp.DryFlow; - row++; + var wetSheet = package.Workbook.Worksheets.Add("湿膜数据"); + wetSheet.Cells["A1"].Value = "压力"; + wetSheet.Cells["B1"].Value = "湿膜流量(L/min)"; + int row = 2; + foreach (var dp in wetPoints) + { + wetSheet.Cells[$"A{row}"].Value = dp.Pressure; + wetSheet.Cells[$"B{row}"].Value = dp.WetFlow; + row++; + } + } + + // 干膜数据表(仅保留干膜流量 > 0 的点) + var dryPoints = entity.DataPoints.Where(dp => dp.DryFlow > 0).OrderBy(dp => dp.Pressure).ToList(); + if (dryPoints.Any()) + { + var drySheet = package.Workbook.Worksheets.Add("干膜数据"); + drySheet.Cells["A1"].Value = "压力"; + drySheet.Cells["B1"].Value = "干膜流量(L/min)"; + int row = 2; + foreach (var dp in dryPoints) + { + drySheet.Cells[$"A{row}"].Value = dp.Pressure; + drySheet.Cells[$"B{row}"].Value = dp.DryFlow; + row++; + } } package.SaveAs(new FileInfo(filePath)); diff --git a/Helpers/ReportGenerator.cs b/Helpers/ReportGenerator.cs index f52176c..457b7cf 100644 --- a/Helpers/ReportGenerator.cs +++ b/Helpers/ReportGenerator.cs @@ -1,227 +1,444 @@ 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.Xps.Packaging; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using FontWeights = System.Windows.FontWeights; +using HorizontalAlignment = System.Windows.HorizontalAlignment; namespace MembranePoreTester.Helpers { + /// + /// 膜测试报告生成器(泡点法 + 孔分布) + /// 排版规范:A4自适应、居中对齐、表格规整、图表清晰、数据准确 + /// public static class ReportGenerator { + #region 公开调用方法 + /// + /// 生成泡点法测试报告 + /// public static void GenerateBubblePointReport(BubblePointRecord record) { - FlowDocument doc = CreateBubblePointDocument(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, "泡点法测试报告"); } - public static void GeneratePoreDistributionReport(PoreDistributionRecord record) + /// + /// 生成孔分布测试报告 + /// + public static void GeneratePoreDistributionReport(PoreDistributionRecord record, PlotModel plotModel) { - FlowDocument doc = CreatePoreDistributionDocument(record); + 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 - private static FlowDocument CreateBubblePointDocument(BubblePointRecord record) + #region 通用排版模块 + /// + /// 设置A4标准打印页面 + /// + private static void SetA4PageSettings(FlowDocument doc) { - FlowDocument doc = new FlowDocument(); - doc.PageWidth = 800; // 适应打印 - - // 标题 - doc.Blocks.Add(new Paragraph(new Run("泡点法测试报告")) - { - FontSize = 24, - FontWeight = FontWeights.Bold, - TextAlignment = TextAlignment.Center - }); - - // 日期/测试者 - doc.Blocks.Add(new Paragraph(new Run($"测试日期: {record.TestDate:yyyy-MM-dd} 测试者: {record.Tester}")) - { - TextAlignment = TextAlignment.Right - }); - - // 样品信息表格 - Table table = new Table(); - table.Columns.Add(new TableColumn { Width = new GridLength(150) }); - table.Columns.Add(new TableColumn { Width = new GridLength(200) }); - - TableRowGroup group = new TableRowGroup(); - AddRow(group, "膜类型", record.SampleType); - AddRow(group, "规格", record.SampleSpec); - AddRow(group, "室温(°C)", record.RoomTemperature.ToString("F1")); - AddRow(group, "浸润时间(h)", record.SoakingTime.ToString("F1")); - AddRow(group, "测试液体", record.Liquid?.Name); - AddRow(group, "液体生产厂家", record.LiquidManufacturer); - AddRow(group, "泡点压力", $"{record.BubblePointPressure} {record.PressureUnit}"); - AddRow(group, "最大孔径(μm)", record.MaxPoreSize.ToString("F3")); - - table.RowGroups.Add(group); - doc.Blocks.Add(table); - - doc.Blocks.Add(new Paragraph(new Run("本报告依据 GB/T 32361-2015 生成。")) - { - FontStyle = FontStyles.Italic, - TextAlignment = TextAlignment.Center, - Margin = new Thickness(0, 20, 0, 0) - }); - - return doc; + doc.PageWidth = 793; // A4宽度 + doc.PageHeight = 1122; // A4高度 + doc.PagePadding = new Thickness(40); + doc.ColumnWidth = 793; } - private static FlowDocument CreatePoreDistributionDocument(PoreDistributionRecord record) + /// + /// 添加报告标题(居中、加粗、大字体) + /// + private static void AddReportTitle(FlowDocument doc, string title) { - FlowDocument doc = new FlowDocument(); - doc.PageWidth = 800; - - // 标题 - doc.Blocks.Add(new Paragraph(new Run("孔分布测试报告")) + var titlePara = new Paragraph(new Run(title)) { - FontSize = 24, + FontSize = 26, FontWeight = FontWeights.Bold, - TextAlignment = TextAlignment.Center - }); - - doc.Blocks.Add(new Paragraph(new Run($"测试日期: {record.TestDate:yyyy-MM-dd} 测试者: {record.Tester}")) - { - TextAlignment = TextAlignment.Right - }); - - // 样品信息表格 - Table infoTable = new Table(); - infoTable.Columns.Add(new TableColumn { Width = new GridLength(150) }); - infoTable.Columns.Add(new TableColumn { Width = new GridLength(200) }); - TableRowGroup group = new TableRowGroup(); - AddRow(group, "膜类型", record.SampleType); - AddRow(group, "规格", record.SampleSpec); - AddRow(group, "室温(°C)", record.RoomTemperature.ToString("F1")); - AddRow(group, "浸润时间(h)", record.SoakingTime.ToString("F1")); - AddRow(group, "测试液体", record.Liquid?.Name); - AddRow(group, "液体生产厂家", record.LiquidManufacturer); - infoTable.RowGroups.Add(group); - doc.Blocks.Add(infoTable); - - // 计算结果 - doc.Blocks.Add(new Paragraph(new Run("测试结果")) - { - FontSize = 18, - FontWeight = FontWeights.Bold, - Margin = new Thickness(0, 10, 0, 5) - }); - - Table resultTable = new Table(); - resultTable.Columns.Add(new TableColumn { Width = new GridLength(150) }); - resultTable.Columns.Add(new TableColumn { Width = new GridLength(200) }); - TableRowGroup resultGroup = new TableRowGroup(); - AddRow(resultGroup, "泡点压力", $"{record.BubblePointPressure} {record.PressureUnit}"); - AddRow(resultGroup, "平均孔径(μm)", record.AveragePoreSize.ToString("F3")); - resultTable.RowGroups.Add(resultGroup); - doc.Blocks.Add(resultTable); - - // 数据点列表 - doc.Blocks.Add(new Paragraph(new Run("压力-流量数据")) - { - FontSize = 14, - FontWeight = FontWeights.Bold, - Margin = new Thickness(0, 10, 0, 5) - }); - - Table dataTable = new Table(); - dataTable.Columns.Add(new TableColumn { Width = new GridLength(80) }); - dataTable.Columns.Add(new TableColumn { Width = new GridLength(80) }); - dataTable.Columns.Add(new TableColumn { Width = new GridLength(80) }); - dataTable.CellSpacing = 2; - - TableRowGroup dataGroup = new TableRowGroup(); - - // 表头 - TableRow headerRow = new TableRow(); - headerRow.Cells.Add(new TableCell(new Paragraph(new Run("压力")))); - headerRow.Cells.Add(new TableCell(new Paragraph(new Run("湿膜流量(L/min)")))); - headerRow.Cells.Add(new TableCell(new Paragraph(new Run("干膜流量(L/min)")))); - headerRow.FontWeight = FontWeights.Bold; - dataGroup.Rows.Add(headerRow); - - foreach (var dp in record.DataPoints) - { - TableRow row = new TableRow(); - row.Cells.Add(new TableCell(new Paragraph(new Run(dp.Pressure.ToString("F2"))))); - row.Cells.Add(new TableCell(new Paragraph(new Run(dp.WetFlow.ToString("F3"))))); - row.Cells.Add(new TableCell(new Paragraph(new Run(dp.DryFlow.ToString("F3"))))); - dataGroup.Rows.Add(row); - } - - dataTable.RowGroups.Add(dataGroup); - doc.Blocks.Add(dataTable); - - // 孔分布结果 - if (record.PoreDistributions != null && record.PoreDistributions.Any()) - { - doc.Blocks.Add(new Paragraph(new Run("孔分布")) - { - FontSize = 14, - FontWeight = FontWeights.Bold, - Margin = new Thickness(0, 10, 0, 5) - }); - - Table distTable = new Table(); - distTable.Columns.Add(new TableColumn { Width = new GridLength(80) }); - distTable.Columns.Add(new TableColumn { Width = new GridLength(80) }); - distTable.Columns.Add(new TableColumn { Width = new GridLength(80) }); - - TableRowGroup distGroup = new TableRowGroup(); - - TableRow distHeader = new TableRow(); - distHeader.Cells.Add(new TableCell(new Paragraph(new Run("孔径下限(μm)")))); - distHeader.Cells.Add(new TableCell(new Paragraph(new Run("孔径上限(μm)")))); - distHeader.Cells.Add(new TableCell(new Paragraph(new Run("百分比(%)")))); - distHeader.FontWeight = FontWeights.Bold; - distGroup.Rows.Add(distHeader); - - foreach (var pd in record.PoreDistributions) - { - TableRow row = new TableRow(); - row.Cells.Add(new TableCell(new Paragraph(new Run(pd.LowerPore.ToString("F3"))))); - row.Cells.Add(new TableCell(new Paragraph(new Run(pd.UpperPore.ToString("F3"))))); - row.Cells.Add(new TableCell(new Paragraph(new Run(pd.Percentage.ToString("F1"))))); - distGroup.Rows.Add(row); - } - - distTable.RowGroups.Add(distGroup); - doc.Blocks.Add(distTable); - } - - doc.Blocks.Add(new Paragraph(new Run("本报告依据 GB/T 32361-2015 生成。")) - { - FontStyle = FontStyles.Italic, TextAlignment = TextAlignment.Center, - Margin = new Thickness(0, 20, 0, 0) - }); - - return doc; + Margin = new Thickness(0, 0, 0, 25) + }; + doc.Blocks.Add(titlePara); } - private static void AddRow(TableRowGroup group, string label, string value) + /// + /// 添加测试基础信息(日期、测试人) + /// + private static void AddTestInfo(FlowDocument doc, dynamic record) { - TableRow row = new TableRow(); - row.Cells.Add(new TableCell(new Paragraph(new Run(label)))); - row.Cells.Add(new TableCell(new Paragraph(new Run(value ?? "")))); + 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); + } + + /// + /// 添加标准备注(依据国标) + /// + 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); + } + + /// + /// 创建标准两列表格 + /// + 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; + } + + /// + /// 向表格添加一行(名称 + 值) + /// + 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); } - private static void ShowPrintPreview(FlowDocument document, string title) + /// + /// 创建表格单元格 + /// + private static TableCell CreateTableCell(string text, bool isBold) { - PrintDialog printDialog = new PrintDialog(); - if (printDialog.ShowDialog() == true) + var para = new Paragraph(new Run(text)) { - // 调整文档页面大小以匹配打印机 - document.PageHeight = printDialog.PrintableAreaHeight; - document.PageWidth = printDialog.PrintableAreaWidth; - document.PagePadding = new Thickness(50); + FontSize = 13, + Padding = new Thickness(8, 6, 8, 6), + Margin = new Thickness(0) + }; + if (isBold) para.FontWeight = FontWeights.Bold; + return new TableCell(para); + } + #endregion - IDocumentPaginatorSource dps = document; - printDialog.PrintDocument(dps.DocumentPaginator, title); + #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); + } + + /// + /// 添加压力-流量曲线图(带图例:红线=干膜,蓝线=湿膜) + /// + 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); + } + + /// + /// 添加湿膜/干膜数据表(有效数据、排序、排版规整) + /// + 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); } } + + /// + /// 添加孔分布表格 + /// + 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 打印预览 + /// + /// 显示打印对话框并打印 + /// + 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 } } \ No newline at end of file diff --git a/ViewModels/PoreDistributionViewModel.cs b/ViewModels/PoreDistributionViewModel.cs index 40120e6..37314c2 100644 --- a/ViewModels/PoreDistributionViewModel.cs +++ b/ViewModels/PoreDistributionViewModel.cs @@ -106,12 +106,12 @@ namespace MembranePoreTester.ViewModels if (TestMode.Contains("湿膜")) { float rawFlow = await _plcService.ReadWetFlowAsync(); - flow = Math.Round(ConvertFlowByMode(rawFlow), 2); + flow = Math.Round(ConvertFlowByMode(rawFlow), 3); } else { float rawFlow = await _plcService.ReadDryFlowAsync(); - flow = Math.Round(ConvertFlowByMode(rawFlow), 2); + flow = Math.Round(ConvertFlowByMode(rawFlow), 3); } if (pressure <= 0 || flow <= 0) @@ -424,7 +424,7 @@ namespace MembranePoreTester.ViewModels if (TestMode == "湿膜") { float rawWet = await _plcService.ReadWetFlowAsync(); - newPoint.WetFlow = ConvertFlowByMode(ConvertFlowByMode(rawWet)); + newPoint.WetFlow = ConvertFlowByMode(rawWet); } else { @@ -577,9 +577,95 @@ namespace MembranePoreTester.ViewModels .ToList(); } + //private void GenerateReport() + //{ + // ReportGenerator.GeneratePoreDistributionReport(Record); + //} + private void GenerateReport() { - ReportGenerator.GeneratePoreDistributionReport(Record); + // 可选:先清洗数据再生成报告(但不改变原集合) + var cleanedPoints = CleanDataPoints(Record.DataPoints); + if (cleanedPoints.Count < 2) + { + MessageBox.Show("有效数据点不足,无法生成报告。"); + return; + } + // 临时替换 Record 中的数据点用于报告(不影响界面) + var originalPoints = Record.DataPoints.ToList(); + Record.DataPoints.Clear(); + foreach (var p in cleanedPoints) + Record.DataPoints.Add(p); + + + + + + ReportGenerator.GeneratePoreDistributionReport(Record, PlotModel); + + // 恢复原始数据 + Record.DataPoints.Clear(); + foreach (var p in originalPoints) + Record.DataPoints.Add(p); + + + + + + + + + //// 清空现有数据 + //Record.DataPoints.Clear(); + + //Random rand = new Random(); + + //// 生成湿膜数据(模拟压力从0到100,流量先增后减) + //for (double pressure = 0; pressure <= 100; pressure += 5) + //{ + // // 模拟流量曲线:先上升后下降,峰值在40-60之间 + // double wetFlow = 0; + // if (pressure < 30) + // wetFlow = pressure * 1.5; // 上升段 + // else if (pressure < 60) + // wetFlow = 45 - (pressure - 30) * 0.5; // 下降段 + // else + // wetFlow = 30 - (pressure - 60) * 0.8; // 继续下降 + + // // 添加随机波动 (±15%) + // wetFlow = wetFlow * (0.85 + rand.NextDouble() * 0.3); + // wetFlow = Math.Round(Math.Max(0, wetFlow), 2); + + // // 干膜数据(稍低于湿膜) + // double dryFlow = wetFlow * (0.6 + rand.NextDouble() * 0.3); + // dryFlow = Math.Round(dryFlow, 2); + + // Record.DataPoints.Add(new Models.DataPoint + // { + // Pressure = Math.Round(pressure, 2), + // WetFlow = wetFlow, + // DryFlow = dryFlow + // }); + //} + + //// 设置一些测试基本参数 + //Record.SampleType = "平板膜"; + //Record.SampleSpec = "φ47mm"; + //Record.RoomTemperature = 22.5; + //Record.SoakingTime = 2.0; + //Record.Liquid = TestLiquid.Predefined.FirstOrDefault(); + //Record.LiquidManufacturer = "测试厂家"; + //Record.PressureUnit = "kPa"; + //Record.BubblePointPressure = 45.6; + //Record.AveragePoreSize = 0.856; + //Record.TestDate = DateTime.Now; + //Record.Tester = "测试员"; + + //// 刷新曲线显示 + //UpdatePlot(); + + //ReportGenerator.GeneratePoreDistributionReport(Record, PlotModel); + } private void OpenFlowCalibration() @@ -725,17 +811,45 @@ namespace MembranePoreTester.ViewModels private void ExportToExcel() { + // 使用当前数据点(已通过手动删除或自动清洗,保证有效性) + var dataPoints = Record.DataPoints.ToList(); + if (dataPoints.Count == 0) + { + MessageBox.Show("没有数据可导出。", "提示", MessageBoxButton.OK, MessageBoxImage.Information); + return; + } + var saveFileDialog = new SaveFileDialog { Filter = "Excel文件|*.xlsx", - FileName = $"泡点法_工位{StationId}_{DateTime.Now:yyyyMMddHHmmss}.xlsx" + FileName = $"孔分布_工位{StationId}_{DateTime.Now:yyyyMMddHHmmss}.xlsx" }; if (saveFileDialog.ShowDialog() == true) { - // 转换为Entity后导出 - var entity = new BubblePointEntity { /* 从Record复制 */ }; - ExportHelper.ExportBubblePoint(entity, saveFileDialog.FileName); - MessageBox.Show("导出成功"); + var entity = new PoreDistributionEntity + { + StationId = this.StationId, + TestDate = Record.TestDate, + Tester = Record.Tester ?? "系统操作员", + SampleType = Record.SampleType, + SampleSpec = Record.SampleSpec ?? "缺省值", + RoomTemperature = Record.RoomTemperature, + SoakingTime = Record.SoakingTime, + LiquidName = Record.Liquid?.Name, + LiquidSurfaceTension = Record.Liquid?.SurfaceTension ?? 0, + LiquidManufacturer = Record.LiquidManufacturer ?? "缺省值", + PressureUnit = Record.PressureUnit, + BubblePointPressure = Record.BubblePointPressure, + AveragePoreSize = AveragePoreSize, // 已计算的平均孔径 + DataPoints = dataPoints.Select(dp => new DataPointEntity + { + Pressure = dp.Pressure, + WetFlow = dp.WetFlow, + DryFlow = dp.DryFlow + }).ToList() + }; + ExportHelper.ExportPoreDistribution(entity, saveFileDialog.FileName); + MessageBox.Show("导出成功", "提示", MessageBoxButton.OK, MessageBoxImage.Information); } } diff --git a/Views/PoreDistributionView.xaml b/Views/PoreDistributionView.xaml index a2eceaa..791b5ad 100644 --- a/Views/PoreDistributionView.xaml +++ b/Views/PoreDistributionView.xaml @@ -185,7 +185,7 @@