515 lines
25 KiB
C#
515 lines
25 KiB
C#
using QuestPDF.Fluent;
|
||
using QuestPDF.Helpers;
|
||
using QuestPDF.Infrastructure;
|
||
using SSPCTester.Logic.Models;
|
||
|
||
namespace SSPCTester.Logic.Report;
|
||
|
||
/// <summary>专业版固态功率控制器动态参数检测报告。</summary>
|
||
public sealed class ReportGenerator
|
||
{
|
||
private const string Navy = "#17324D";
|
||
private const string Blue = "#245B82";
|
||
private const string PaleBlue = "#EAF1F6";
|
||
private const string Line = "#CBD5DF";
|
||
private const string Muted = "#667585";
|
||
private const string Text = "#1E2933";
|
||
private const string Success = "#287A4B";
|
||
private const string SuccessBg = "#E8F3EC";
|
||
private const string Danger = "#A53232";
|
||
private const string DangerBg = "#F8EAEA";
|
||
private const string Warning = "#98651A";
|
||
private const string WarningBg = "#FAF1DE";
|
||
private const string NeutralBg = "#F1F3F5";
|
||
|
||
static ReportGenerator() => QuestPDF.Settings.License = LicenseType.Community;
|
||
|
||
public Dictionary<int, CurveReportImages> CurveImages { get; set; } = new();
|
||
|
||
public void Generate(Project project, string outputPath)
|
||
{
|
||
Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!);
|
||
string reportNo = BuildReportNumber(project);
|
||
|
||
Document.Create(document =>
|
||
{
|
||
if (project.Report.Cover)
|
||
document.Page(page => ComposeCover(page, project, reportNo));
|
||
document.Page(page => ComposeBody(page, project, reportNo));
|
||
}).GeneratePdf(outputPath);
|
||
}
|
||
|
||
private static void ComposeCover(PageDescriptor page, Project p, string reportNo)
|
||
{
|
||
page.Size(PageSizes.A4);
|
||
page.MarginHorizontal(2.1f, Unit.Centimetre);
|
||
page.MarginVertical(1.8f, Unit.Centimetre);
|
||
page.DefaultTextStyle(x => x.FontFamily("Microsoft YaHei").FontSize(10).FontColor(Text));
|
||
|
||
page.Header().AlignRight().Text("SPC-DynaTest 检测系统")
|
||
.FontSize(9).FontColor(Muted).LetterSpacing(0.05f);
|
||
|
||
page.Content().Column(column =>
|
||
{
|
||
//column.Item().Height(34).Background(Navy);
|
||
column.Item().PaddingTop(54).AlignCenter().Text("固态功率控制器")
|
||
.FontSize(22).Bold().FontColor(Navy);
|
||
column.Item().PaddingTop(8).AlignCenter().Text("动态参数检测报告")
|
||
.FontSize(30).Bold().FontColor(Navy).LetterSpacing(0.08f);
|
||
// column.Item().PaddingTop(16).AlignCenter().Width(110)
|
||
// .LineHorizontal(2).LineColor(Blue);
|
||
|
||
column.Item().PaddingTop(45).Element(container => CoverIdentity(container, p, reportNo));
|
||
column.Item().PaddingTop(34).Element(container => OverallPanel(container, p));
|
||
column.Item().ExtendVertical();
|
||
column.Item().PaddingBottom(18).BorderTop(1).BorderColor(Line).PaddingTop(14).Table(table =>
|
||
{
|
||
table.ColumnsDefinition(columns =>
|
||
{
|
||
columns.RelativeColumn();
|
||
columns.RelativeColumn();
|
||
columns.RelativeColumn();
|
||
});
|
||
CoverSignCell(table, "编制", p.Site.Tester);
|
||
CoverSignCell(table, "审核", "");
|
||
CoverSignCell(table, "批准", "");
|
||
});
|
||
});
|
||
|
||
page.Footer().Row(row =>
|
||
{
|
||
row.RelativeItem().Text("本报告仅对所检样品及记录数据负责").FontSize(8).FontColor(Muted);
|
||
row.ConstantItem(180).AlignRight().Text($"生成时间:{DateTime.Now:yyyy-MM-dd HH:mm}")
|
||
.FontSize(8).FontColor(Muted);
|
||
});
|
||
}
|
||
|
||
private static void CoverIdentity(IContainer container, Project p, string reportNo)
|
||
{
|
||
container.Border(1).BorderColor(Line).Table(table =>
|
||
{
|
||
table.ColumnsDefinition(columns =>
|
||
{
|
||
columns.ConstantColumn(105);
|
||
columns.RelativeColumn();
|
||
});
|
||
IdentityRow(table, "报告编号", reportNo, true);
|
||
IdentityRow(table, "项目名称", ValueOrDash(p.Name));
|
||
IdentityRow(table, "产品名称", ValueOrDash(p.Dut.ProductName));
|
||
IdentityRow(table, "产品型号", ValueOrDash(p.Dut.Model));
|
||
IdentityRow(table, "产品编号", ValueOrDash(p.Dut.SerialNumber));
|
||
IdentityRow(table, "委托/生产单位", ValueOrDash(p.Dut.Manufacturer));
|
||
IdentityRow(table, "检测日期", p.Site.TestDate.ToString("yyyy年MM月dd日"));
|
||
});
|
||
}
|
||
|
||
private static void IdentityRow(TableDescriptor table, string label, string value, bool mono = false)
|
||
{
|
||
table.Cell().BorderBottom(1).BorderColor(Line).Background(PaleBlue)
|
||
.PaddingVertical(8).PaddingHorizontal(10).Text(label).SemiBold().FontColor(Navy);
|
||
var text = table.Cell().BorderBottom(1).BorderColor(Line)
|
||
.PaddingVertical(8).PaddingHorizontal(12).Text(value);
|
||
if (mono) text.FontFamily("Consolas");
|
||
}
|
||
|
||
private static void CoverSignCell(TableDescriptor table, string label, string value)
|
||
{
|
||
table.Cell().PaddingRight(14).Column(column =>
|
||
{
|
||
column.Item().Text(label).FontSize(9).FontColor(Muted);
|
||
column.Item().PaddingTop(16).BorderBottom(1).BorderColor(Line)
|
||
.PaddingBottom(4).Text(ValueOrDash(value)).FontSize(10);
|
||
});
|
||
}
|
||
|
||
private void ComposeBody(PageDescriptor page, Project p, string reportNo)
|
||
{
|
||
page.Size(PageSizes.A4);
|
||
page.MarginHorizontal(1.5f, Unit.Centimetre);
|
||
page.MarginVertical(1.3f, Unit.Centimetre);
|
||
page.DefaultTextStyle(x => x.FontFamily("Microsoft YaHei").FontSize(8.7f).FontColor(Text));
|
||
page.Header().Element(container => BodyHeader(container, p, reportNo));
|
||
page.Content().PaddingVertical(10).Element(container => BodyContent(container, p));
|
||
page.Footer().Element(container => Footer(container, reportNo));
|
||
}
|
||
|
||
private static void BodyHeader(IContainer container, Project p, string reportNo)
|
||
{
|
||
container.BorderBottom(1).BorderColor(Navy).PaddingBottom(7).Row(row =>
|
||
{
|
||
row.RelativeItem().Column(column =>
|
||
{
|
||
column.Item().Text("固态功率控制器动态参数检测报告")
|
||
.FontSize(12).Bold().FontColor(Navy);
|
||
column.Item().Text(ValueOrDash(p.Name)).FontSize(8).FontColor(Muted);
|
||
});
|
||
row.ConstantItem(175).AlignRight().Column(column =>
|
||
{
|
||
column.Item().AlignRight().Text($"报告编号:{reportNo}")
|
||
.FontFamily("Consolas").FontSize(8).FontColor(Muted);
|
||
column.Item().AlignRight().Text($"样品编号:{ValueOrDash(p.Dut.SerialNumber)}")
|
||
.FontSize(8).FontColor(Muted);
|
||
});
|
||
});
|
||
}
|
||
|
||
private static void Footer(IContainer container, string reportNo)
|
||
{
|
||
container.BorderTop(0.7f).BorderColor(Line).PaddingTop(6).Row(row =>
|
||
{
|
||
row.RelativeItem().Text($"SPC-DynaTest · {reportNo}").FontSize(7.5f).FontColor(Muted);
|
||
row.RelativeItem().AlignCenter().Text("未经授权不得部分复制本报告")
|
||
.FontSize(7.5f).FontColor(Muted);
|
||
row.RelativeItem().AlignRight()
|
||
.DefaultTextStyle(style => style.FontSize(7.5f).FontColor(Muted))
|
||
.Text(text =>
|
||
{
|
||
text.Span("第 ");
|
||
text.CurrentPageNumber();
|
||
text.Span(" 页 / 共 ");
|
||
text.TotalPages();
|
||
text.Span(" 页");
|
||
});
|
||
});
|
||
}
|
||
|
||
private void BodyContent(IContainer container, Project p)
|
||
{
|
||
container.Column(column =>
|
||
{
|
||
column.Spacing(12);
|
||
int section = 1;
|
||
|
||
if (p.Report.Cover)
|
||
{
|
||
Section(column, section++, "检测概况", "TEST OVERVIEW");
|
||
column.Item().Element(x => Overview(x, p));
|
||
Section(column, section++, "结果摘要", "RESULT SUMMARY");
|
||
column.Item().Element(x => ResultSummary(x, p));
|
||
}
|
||
|
||
if (p.Report.Basic && (p.BasicResults.Count > 0 || HasNote(p.TestFunction?.Basic)))
|
||
{
|
||
Section(column, section++, "基础测试", "BASIC FUNCTION TEST");
|
||
if (p.BasicResults.Count > 0)
|
||
{
|
||
column.Item().Element(x => ModuleSummary(x, "通道开合、电压、电流与关后残压", p.BasicResults.Select(r => r.Status)));
|
||
column.Item().Element(x => BasicTable(x, p));
|
||
}
|
||
ManualNote(column, p.TestFunction?.Basic);
|
||
}
|
||
|
||
if (p.Report.Curve && (p.CurveResults.Count > 0 || HasNote(p.TestFunction?.Curve)))
|
||
{
|
||
Section(column, section++, "开启与关断曲线", "SWITCHING CHARACTERISTICS");
|
||
if (p.CurveResults.Count > 0)
|
||
{
|
||
string criteria = $"判据:开启≤{p.CurveThresholds.MaxOnTimeMs:F1}ms,上升≤{p.CurveThresholds.MaxRiseTimeMs:F1}ms,关断≤{p.CurveThresholds.MaxOffTimeMs:F1}ms,下降≤{p.CurveThresholds.MaxFallTimeMs:F1}ms";
|
||
column.Item().Element(x => ModuleSummary(x, criteria, p.CurveResults.Select(r => r.Status)));
|
||
column.Item().Element(x => CurveTable(x, p));
|
||
}
|
||
ManualNote(column, p.TestFunction?.Curve);
|
||
foreach (var (channel, images) in CurveImages.OrderBy(x => x.Key))
|
||
{
|
||
AddCurveImage(column, channel, "ON", images.Rising);
|
||
AddCurveImage(column, channel, "OFF", images.Falling);
|
||
}
|
||
}
|
||
|
||
if (p.Report.Power && (p.PowerResults.Count > 0 || HasNote(p.TestFunction?.Power)))
|
||
{
|
||
Section(column, section++, "功率损耗", "POWER LOSS");
|
||
if (p.PowerResults.Count > 0)
|
||
{
|
||
column.Item().Element(x => ModuleSummary(x, "采集输入/输出两端电压与电流,计算功率损耗与效率", p.PowerResults.Select(r => r.Status)));
|
||
column.Item().Element(x => PowerTable(x, p));
|
||
}
|
||
ManualNote(column, p.TestFunction?.Power);
|
||
}
|
||
|
||
if (p.Report.Protect && (p.ProtectResults.Count > 0 || HasNote(p.TestFunction?.Protect)))
|
||
{
|
||
Section(column, section++, "保护功能", "PROTECTION FUNCTION");
|
||
if (p.ProtectResults.Count > 0)
|
||
{
|
||
column.Item().Element(x => ModuleSummary(x, "跳闸、短路、过温、过压、欠压及限流保护", p.ProtectResults.Select(r => r.Status)));
|
||
column.Item().Element(x => ProtectTable(x, p));
|
||
}
|
||
ManualNote(column, p.TestFunction?.Protect);
|
||
}
|
||
|
||
column.Item().PaddingTop(12).Element(x => Conclusion(x, p));
|
||
});
|
||
}
|
||
|
||
private static void Section(ColumnDescriptor column, int number, string title, string english)
|
||
{
|
||
column.Item().PaddingTop(4).BorderBottom(1.3f).BorderColor(Navy).PaddingBottom(5).Row(row =>
|
||
{
|
||
row.ConstantItem(34).Height(25).Background(Navy).AlignCenter().AlignMiddle()
|
||
.Text(number.ToString("00")).Bold().FontColor(Colors.White).FontFamily("Consolas");
|
||
row.RelativeItem().PaddingLeft(10).AlignMiddle().Text(title)
|
||
.FontSize(13).Bold().FontColor(Navy);
|
||
row.ConstantItem(180).AlignRight().AlignMiddle().Text(english)
|
||
.FontSize(7.5f).FontColor(Muted).LetterSpacing(0.05f);
|
||
});
|
||
}
|
||
|
||
private static void AddCurveImage(ColumnDescriptor column, int channel, string phaseLabel, byte[]? png)
|
||
{
|
||
if (png == null || png.Length == 0) return;
|
||
|
||
column.Item().ShowEntire().Border(1).BorderColor(Line).Padding(8).Column(imageColumn =>
|
||
{
|
||
imageColumn.Item().Text($"CH{channel:00} {phaseLabel} Curve").SemiBold().FontColor(Navy);
|
||
imageColumn.Item().PaddingTop(6).Image(png).FitWidth();
|
||
});
|
||
}
|
||
|
||
private static void Overview(IContainer container, Project p)
|
||
{
|
||
container.Table(table =>
|
||
{
|
||
table.ColumnsDefinition(columns =>
|
||
{
|
||
columns.ConstantColumn(82); columns.RelativeColumn();
|
||
columns.ConstantColumn(82); columns.RelativeColumn();
|
||
});
|
||
OverviewCell(table, "设备名称", p.Dut.ProductName);
|
||
OverviewCell(table, "设备型号", p.Dut.Model);
|
||
OverviewCell(table, "设备编号", p.Dut.SerialNumber);
|
||
OverviewCell(table, "生产厂家", p.Dut.Manufacturer);
|
||
OverviewCell(table, "测试人员", p.Site.Tester);
|
||
OverviewCell(table, "测试日期", p.Site.TestDate.ToString("yyyy-MM-dd"));
|
||
OverviewCell(table, "环境温度", $"{p.Site.TemperatureC:F1} ℃");
|
||
OverviewCell(table, "相对湿度", $"{p.Site.HumidityPct:F1} %RH");
|
||
OverviewCell(table, "环境气压", $"{p.Site.PressureKpa:F1} kPa");
|
||
OverviewCell(table, "项目创建", p.CreatedAt.ToString("yyyy-MM-dd HH:mm"));
|
||
table.Cell().Border(0.6f).BorderColor(Line).Background(PaleBlue).Padding(6)
|
||
.Text("测试备注").SemiBold().FontColor(Navy);
|
||
table.Cell().ColumnSpan(3).Border(0.6f).BorderColor(Line).Padding(6)
|
||
.Text(ValueOrDash(p.Site.Remark));
|
||
});
|
||
}
|
||
|
||
private static void OverviewCell(TableDescriptor table, string label, string? value)
|
||
{
|
||
table.Cell().Border(0.6f).BorderColor(Line).Background(PaleBlue)
|
||
.Padding(6).Text(label).SemiBold().FontColor(Navy);
|
||
table.Cell().Border(0.6f).BorderColor(Line).Padding(6).Text(ValueOrDash(value));
|
||
}
|
||
|
||
private static void ResultSummary(IContainer container, Project p)
|
||
{
|
||
var statuses = p.BasicResults.Select(x => x.Status)
|
||
.Concat(p.CurveResults.Select(x => x.Status))
|
||
.Concat(p.PowerResults.Select(x => x.Status))
|
||
.Concat(p.ProtectResults.Select(x => x.Status)).ToArray();
|
||
|
||
container.Row(row =>
|
||
{
|
||
row.RelativeItem().Element(x => SummaryCard(x, "检测项目", statuses.Length.ToString(), Navy, PaleBlue));
|
||
row.RelativeItem().PaddingLeft(7).Element(x => SummaryCard(x, "合格", statuses.Count(s => s == TestStatus.Pass).ToString(), Success, SuccessBg));
|
||
row.RelativeItem().PaddingLeft(7).Element(x => SummaryCard(x, "不合格", statuses.Count(s => s == TestStatus.Fail).ToString(), Danger, DangerBg));
|
||
row.RelativeItem().PaddingLeft(7).Element(x => SummaryCard(x, "已测", statuses.Count(s => s == TestStatus.Measured).ToString(), Blue, PaleBlue));
|
||
row.RelativeItem().PaddingLeft(7).Element(x => SummaryCard(x, "未测试", statuses.Count(s => s == TestStatus.Untested).ToString(), Muted, NeutralBg));
|
||
row.RelativeItem().PaddingLeft(7).Element(x => SummaryCard(x, "综合判定", StatusText(p.OverallStatus), StatusColor(p.OverallStatus), StatusBackground(p.OverallStatus)));
|
||
});
|
||
}
|
||
|
||
private static void SummaryCard(IContainer container, string title, string value, string color, string background)
|
||
{
|
||
container.MinHeight(58).Border(0.8f).BorderColor(color).Background(background)
|
||
.Padding(8).Column(column =>
|
||
{
|
||
column.Item().Text(title).FontSize(7.5f).FontColor(Muted);
|
||
column.Item().PaddingTop(4).Text(value).FontSize(13).Bold().FontColor(color);
|
||
});
|
||
}
|
||
|
||
private static void ModuleSummary(IContainer container, string description, IEnumerable<TestStatus> source)
|
||
{
|
||
var statuses = source.ToArray();
|
||
container.Background(PaleBlue).BorderLeft(3).BorderColor(Blue).Padding(7).Row(row =>
|
||
{
|
||
row.RelativeItem().Text(description).FontSize(8).FontColor(Muted);
|
||
row.ConstantItem(190).AlignRight()
|
||
.DefaultTextStyle(style => style.FontSize(8))
|
||
.Text(text =>
|
||
{
|
||
text.Span($"合格 {statuses.Count(x => x == TestStatus.Pass)}").FontColor(Success).SemiBold();
|
||
text.Span(" ");
|
||
text.Span($"不合格 {statuses.Count(x => x == TestStatus.Fail)}").FontColor(Danger).SemiBold();
|
||
text.Span(" ");
|
||
text.Span($"已测 {statuses.Count(x => x == TestStatus.Measured)}").FontColor(Blue).SemiBold();
|
||
text.Span(" ");
|
||
text.Span($"未测 {statuses.Count(x => x == TestStatus.Untested)}").FontColor(Muted);
|
||
});
|
||
});
|
||
}
|
||
|
||
private static void BasicTable(IContainer container, Project p) =>
|
||
DataTable(container, new[] { 0.65f, 1f, 1f, 1.15f, 0.8f },
|
||
new[] { "通道", "导通电压\nV", "导通电流\nA", "关后电压\nV", "判定" },
|
||
p.BasicResults.Select(r => new ReportRow(r.Status, new[]
|
||
{
|
||
$"CH{r.Channel:00}", Format(r.VoltageOn, "F2"), Format(r.CurrentOn, "F3"),
|
||
Format(r.VoltageOff, "F2"), StatusText(r.Status)
|
||
})));
|
||
|
||
private static void CurveTable(IContainer container, Project p) =>
|
||
DataTable(container, new[] { 0.62f, 1f, 1f, 1f, 1f, 0.76f },
|
||
new[] { "通道", "开启时间\nms", "上升时间\nms", "关断时间\nms", "下降时间\nms", "判定" },
|
||
p.CurveResults.Select(r => new ReportRow(r.Status, new[]
|
||
{
|
||
$"CH{r.Channel:00}", Format(r.OnTimeMs, "F3"), Format(r.RiseTimeMs, "F3"),
|
||
Format(r.OffTimeMs, "F3"), Format(r.FallTimeMs, "F3"), StatusText(r.Status)
|
||
})));
|
||
|
||
private static void PowerTable(IContainer container, Project p) =>
|
||
DataTable(container, new[] { 0.62f, 0.9f, 0.9f, 0.9f, 0.9f, 0.9f, 0.9f, 0.76f },
|
||
new[] { "通道", "输入电压\nV", "输入电流\nA", "输出电压\nV", "输出电流\nA", "功率损耗\nW", "效率\n%", "状态" },
|
||
p.PowerResults.Select(r => new ReportRow(r.Status, new[]
|
||
{
|
||
$"CH{r.Channel:00}", Format(r.Vin, "F3"), Format(r.Iin, "F3"),
|
||
Format(r.Vout, "F3"), Format(r.Iout, "F3"),
|
||
Format(r.PowerLossW, "F3"), Format(r.EfficiencyPct, "F3"), StatusText(r.Status)
|
||
})));
|
||
|
||
private static void ProtectTable(IContainer container, Project p) =>
|
||
DataTable(container, new[] { 1.2f, 1.6f, 1f, 0.72f },
|
||
new[] { "保护类型", "触发条件", "关断时间 ms", "判定" },
|
||
p.ProtectResults.Select(r => new ReportRow(r.Status, new[]
|
||
{
|
||
ValueOrDash(r.ProtectType), ValueOrDash(r.Trigger),
|
||
Format(r.TripTimeMs, "F2"), StatusText(r.Status)
|
||
})));
|
||
|
||
private static void DataTable(IContainer container, IReadOnlyList<float> widths,
|
||
IReadOnlyList<string> headers, IEnumerable<ReportRow> source)
|
||
{
|
||
var rows = source.ToArray();
|
||
container.Table(table =>
|
||
{
|
||
table.ColumnsDefinition(columns =>
|
||
{
|
||
foreach (float width in widths) columns.RelativeColumn(width);
|
||
});
|
||
table.Header(header =>
|
||
{
|
||
foreach (string text in headers)
|
||
header.Cell().Background(Navy).BorderRight(0.5f).BorderColor(Colors.White)
|
||
.PaddingVertical(6).PaddingHorizontal(4).AlignCenter().AlignMiddle()
|
||
.Text(text).FontSize(7.6f).SemiBold().FontColor(Colors.White);
|
||
});
|
||
|
||
for (int rowIndex = 0; rowIndex < rows.Length; rowIndex++)
|
||
{
|
||
var row = rows[rowIndex];
|
||
string background = row.Status == TestStatus.Fail ? DangerBg
|
||
: rowIndex % 2 == 0 ? Colors.White : "#F7F9FB";
|
||
for (int columnIndex = 0; columnIndex < row.Values.Length; columnIndex++)
|
||
{
|
||
var text = table.Cell().Background(background).BorderBottom(0.55f).BorderColor(Line)
|
||
.PaddingVertical(4.5f).PaddingHorizontal(4).AlignCenter().AlignMiddle()
|
||
.Text(row.Values[columnIndex]).FontSize(7.8f);
|
||
if (columnIndex == row.Values.Length - 1)
|
||
text.SemiBold().FontColor(StatusColor(row.Status));
|
||
else if (columnIndex == 0)
|
||
text.FontFamily("Consolas").SemiBold().FontColor(Navy);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
private static void ManualNote(ColumnDescriptor column, string? note)
|
||
{
|
||
if (!HasNote(note)) return;
|
||
column.Item().ShowEntire().Border(0.8f).BorderColor(Line).Background("#F8FAFC")
|
||
.Padding(8).Column(noteColumn =>
|
||
{
|
||
noteColumn.Item().Text("测试说明 / 现场记录").FontSize(8).SemiBold().FontColor(Navy);
|
||
noteColumn.Item().PaddingTop(5).Text(note!.Trim()).FontSize(8.2f).LineHeight(1.35f);
|
||
});
|
||
}
|
||
|
||
private static void Conclusion(IContainer container, Project p)
|
||
{
|
||
container.ShowEntire().Border(1.2f).BorderColor(StatusColor(p.OverallStatus))
|
||
.Background(StatusBackground(p.OverallStatus)).Padding(12).Column(column =>
|
||
{
|
||
column.Item().Row(row =>
|
||
{
|
||
row.RelativeItem().Text("检测结论").FontSize(11).Bold().FontColor(Navy);
|
||
row.ConstantItem(120).AlignRight().Text(StatusText(p.OverallStatus))
|
||
.FontSize(13).Bold().FontColor(StatusColor(p.OverallStatus));
|
||
});
|
||
column.Item().PaddingTop(8).Text(ConclusionText(p.OverallStatus))
|
||
.FontSize(8.5f).FontColor(Text).LineHeight(1.4f);
|
||
column.Item().PaddingTop(18).Row(row =>
|
||
{
|
||
row.RelativeItem().Text($"检测:{ValueOrDash(p.Site.Tester)}").FontSize(8).FontColor(Muted);
|
||
row.RelativeItem().Text("复核:________________").FontSize(8).FontColor(Muted);
|
||
row.RelativeItem().AlignRight().Text("日期:____年__月__日").FontSize(8).FontColor(Muted);
|
||
});
|
||
});
|
||
}
|
||
|
||
private static void OverallPanel(IContainer container, Project p)
|
||
{
|
||
container.Border(1.2f).BorderColor(StatusColor(p.OverallStatus))
|
||
.Background(StatusBackground(p.OverallStatus)).Padding(14).Row(row =>
|
||
{
|
||
row.RelativeItem().Column(column =>
|
||
{
|
||
column.Item().Text("综合检测结论").FontSize(9).FontColor(Muted);
|
||
column.Item().PaddingTop(4).Text(ConclusionText(p.OverallStatus)).FontSize(9);
|
||
});
|
||
row.ConstantItem(110).AlignRight().AlignMiddle().Text(StatusText(p.OverallStatus))
|
||
.FontSize(18).Bold().FontColor(StatusColor(p.OverallStatus));
|
||
});
|
||
}
|
||
|
||
private static string BuildReportNumber(Project p)
|
||
{
|
||
string serial = new(p.Dut.SerialNumber.Where(char.IsLetterOrDigit).ToArray());
|
||
if (string.IsNullOrWhiteSpace(serial)) serial = "UNKNOWN";
|
||
if (serial.Length > 14) serial = serial[^14..];
|
||
return $"SPC-{p.Site.TestDate:yyyyMMdd}-{serial.ToUpperInvariant()}";
|
||
}
|
||
|
||
private static string ConclusionText(TestStatus status) => status switch
|
||
{
|
||
TestStatus.Pass => "本次所选检测项目均满足当前判定要求,综合结论为合格。",
|
||
TestStatus.Fail => "本次检测存在不合格项目,综合结论为不合格;请结合异常通道记录进行复核与处置。",
|
||
TestStatus.Running => "检测尚未结束,当前结果仅供过程参考,不作为最终判定依据。",
|
||
TestStatus.Measured => "功率损耗已完成测量,报告基于输入/输出两端数据计算损耗与效率。",
|
||
_ => "检测数据尚不完整,暂不具备出具最终合格判定的条件。"
|
||
};
|
||
|
||
private static string Format(double value, string format) =>
|
||
double.IsNaN(value) || double.IsInfinity(value) ? "—" : value.ToString(format);
|
||
private static bool HasNote(string? value) => !string.IsNullOrWhiteSpace(value);
|
||
private static string ValueOrDash(string? value) => string.IsNullOrWhiteSpace(value) ? "—" : value.Trim();
|
||
private static string StatusText(TestStatus status) => status switch
|
||
{
|
||
TestStatus.Pass => "合格", TestStatus.Fail => "不合格",
|
||
TestStatus.Running => "测试中", TestStatus.Measured => "已测",
|
||
_ => "未测试"
|
||
};
|
||
private static string StatusColor(TestStatus status) => status switch
|
||
{
|
||
TestStatus.Pass => Success, TestStatus.Fail => Danger,
|
||
TestStatus.Running => Warning, TestStatus.Measured => Blue,
|
||
_ => Muted
|
||
};
|
||
private static string StatusBackground(TestStatus status) => status switch
|
||
{
|
||
TestStatus.Pass => SuccessBg, TestStatus.Fail => DangerBg,
|
||
TestStatus.Running => WarningBg, TestStatus.Measured => PaleBlue,
|
||
_ => NeutralBg
|
||
};
|
||
|
||
private sealed record ReportRow(TestStatus Status, string[] Values);
|
||
}
|