EnergySpectrumAnalyer/src/analysisReport/reportgenerator.cpp

687 lines
28 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "reportgenerator.h"
#include <QFile>
#include <QTextStream>
#include <QLocale>
ReportGenerator::ReportGenerator()
{
}
void ReportGenerator::setData(const SpectrumReportData &data)
{
m_data = data;
}
QString ReportGenerator::formatScientific(double value, int precision) const
{
if (value == 0.0) {
return QString("0.0000E+00");
}
// 格式化为科学计数法,类似 2.7056E-01
QString result = QString::number(value, 'E', precision);
// 确保指数部分有两位
QStringList parts = result.split('E');
if (parts.size() == 2) {
QString mantissa = parts[0];
QString exponent = parts[1];
// 处理指数符号和位数
QChar sign = exponent[0];
QString num = exponent.mid(1);
bool ok;
int expNum = num.toInt(&ok);
if (ok) {
exponent = QString("%1%2").arg(sign).arg(QString::number(expNum).rightJustified(2, '0'));
}
result = QString("%1E%2").arg(mantissa).arg(exponent);
}
return result;
}
QString ReportGenerator::formatDecimal(double value, int precision) const
{
return QString::number(value, 'f', precision);
}
QString ReportGenerator::formatDateTime(const QDateTime &dt) const
{
return dt.toString("yyyy/M/d hh:mm:ss");
}
QString ReportGenerator::generateHtmlHeader() const
{
return R"(
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title></title>
<style>
body {
font-family: "SimSun", "宋体", serif;
font-size: 12px;
margin: 20px;
line-height: 1.6;
}
.report-title {
text-align: center;
font-size: 18px;
font-weight: bold;
margin-bottom: 20px;
}
.section-title {
font-weight: bold;
font-size: 14px;
margin-top: 15px;
margin-bottom: 10px;
border-bottom: 1px solid #333;
padding-bottom: 5px;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 10px;
}
table, th, td {
border: 1px solid #333;
}
th, td {
padding: 6px 8px;
text-align: left;
font-size: 12px;
}
th {
background-color: #f0f0f0;
font-weight: bold;
}
.param-table td:first-child {
width: 150px;
font-weight: bold;
}
.peak-table {
font-size: 11px;
}
.peak-table th {
text-align: center;
background-color: #e8e8e8;
}
.peak-table td {
text-align: center;
}
.signature-section {
margin-top: 30px;
text-align: right;
}
.signature-line {
margin-bottom: 8px;
}
.spectrum-file-header {
text-align: center;
font-weight: bold;
margin-bottom: 10px;
}
.legend {
margin-top: 10px;
font-size: 11px;
color: #555;
}
.legend-item {
margin-bottom: 3px;
}
</style>
</head>
<body>
)";
}
QString ReportGenerator::generateMeasurementSection() const
{
QString html;
html += QString("<div class='spectrum-file-header'>能谱文件:%1</div>")
.arg(m_data.measurement.spectrumFile);
html += "<div class='section-title'>测量参数</div>";
html += "<table class='param-table'>";
html += QString("<tr><td>开始时间:</td><td>%1</td>"
"<td>活时间S</td><td>%2</td></tr>")
.arg(formatDateTime(m_data.measurement.startTime))
.arg(formatDecimal(m_data.measurement.liveTime, 0));
html += QString("<tr><td>实时间S</td><td>%1</td>"
"<td>死时间:</td><td>%2 %</td></tr>")
.arg(formatDecimal(m_data.measurement.realTime, 0))
.arg(formatDecimal(m_data.measurement.deadTime, 2));
html += "</table>";
return html;
}
QString ReportGenerator::generateCalibrationSection() const
{
QString html;
html += "<div class='section-title'>刻度</div>";
html += "<table class='param-table'>";
html += QString("<tr><td>文件:</td><td>%1</td>"
"<td>刻度标准源:</td><td>%2</td></tr>")
.arg(m_data.calibration.fileName)
.arg(m_data.calibration.standardSource);
html += "</table>";
// 能量刻度
html += "<div style='margin-left: 20px; margin-top: 10px;'>";
html += "<div style='font-weight: bold;'>能量刻度</div>";
html += "<table class='param-table'>";
html += QString("<tr><td>生成时间:</td><td>%1</td>"
"<td>截距:</td><td>%2 keV</td></tr>")
.arg(formatDateTime(m_data.calibration.energy.generateTime))
.arg(formatDecimal(m_data.calibration.energy.intercept, 3));
html += QString("<tr><td>斜率:</td><td>%1 keV/ch</td>"
"<td>二次项系数:</td><td>%2 keV/(ch^2)</td></tr>")
.arg(formatDecimal(m_data.calibration.energy.slope, 3))
.arg(formatScientific(m_data.calibration.energy.quadratic, 3));
html += "</table>";
html += "</div>";
// 效率刻度
html += "<div style='margin-left: 20px; margin-top: 10px;'>";
html += "<div style='font-weight: bold;'>效率刻度</div>";
html += "<table class='param-table'>";
html += QString("<tr><td>生成时间:</td><td>%1</td>"
"<td>拐点能量keV</td><td>%2</td></tr>")
.arg(formatDateTime(m_data.calibration.efficiency.generateTime))
.arg(formatDecimal(m_data.calibration.efficiency.inflectionEnergy, 2));
html += QString("<tr><td>拐点以上:</td><td>%1</td>"
"<td>误差:</td><td>%2%</td></tr>")
.arg(m_data.calibration.efficiency.aboveInflection)
.arg(formatDecimal(m_data.calibration.efficiency.aboveError, 2));
html += QString("<tr><td>拐点以下:</td><td>%1</td>"
"<td>误差:</td><td>%2%</td></tr>")
.arg(m_data.calibration.efficiency.belowInflection)
.arg(formatDecimal(m_data.calibration.efficiency.belowError, 2));
html += QString("<tr><td>效率对数方程式:</td><td colspan='3'>%1</td></tr>")
.arg(m_data.calibration.efficiency.logEquation);
html += "</table>";
html += "</div>";
return html;
}
QString ReportGenerator::generateNuclideLibSection() const
{
QString html;
html += "<div class='section-title'>核素库</div>";
html += "<table class='param-table'>";
html += QString("<tr><td>文件:</td><td>%1</td>"
"<td>峰匹配宽度keV</td><td>%2 * FWHM</td></tr>")
.arg(m_data.nuclideLib.fileName)
.arg(formatDecimal(m_data.nuclideLib.peakMatchWidth, 3));
html += "</table>";
return html;
}
QString ReportGenerator::generateAnalysisSection() const
{
QString html;
html += "<div class='section-title'>分析参数</div>";
html += "<table class='param-table'>";
html += QString("<tr><td>起始能量(keV)</td><td>%1</td>"
"<td>终止能量keV</td><td>%2</td></tr>")
.arg(formatDecimal(m_data.analysis.startEnergy, 2))
.arg(formatDecimal(m_data.analysis.endEnergy, 2));
html += QString("<tr><td>统计误差限值:</td><td>%1%</td>"
"<td>活度比例系数:</td><td>%2</td></tr>")
.arg(formatDecimal(m_data.analysis.statErrorLimit, 3))
.arg(formatScientific(m_data.analysis.activityRatio, 4));
html += QString("<tr><td>探测限计算方法:</td><td colspan='3'>%1</td></tr>")
.arg(m_data.analysis.mdaMethod);
html += "</table>";
return html;
}
QString ReportGenerator::generatePeakTableSection() const
{
QString html;
html += "<div class='section-title'>能 谱 峰 一 览 表</div>";
html += "<table class='peak-table'>";
// 表头 - 第一行
html += "<tr>";
html += "<th rowspan='2'>核素</th>";
html += "<th rowspan='2'>平均活度贝克Bq</th>";
html += "<th rowspan='2'>不确定度</th>";
html += "<th colspan='4'>峰</th>";
html += "<th rowspan='2'>备注</th>";
html += "</tr>";
// 表头 - 第二行
html += "<tr>";
html += "<th>能量keV</th>"; // 峰子项1
html += "<th>活度贝克Bq</th>"; // 峰子项2
html += "<th>不确定度</th>"; // 峰子项3
html += "<th>最小可探测活度MDA贝克Bq</th>"; // 峰子项4
html += "</tr>";
// 数据行8列与表头对应
for (const auto& peak : m_data.peaks) {
html += "<tr>";
html += QString("<td>%1</td>").arg(peak.nuclide);
html += QString("<td>%1</td>").arg(formatScientific(peak.avgActivity, 4));
html += QString("<td>%1%</td>").arg(formatDecimal(peak.avgUncertainty, 2));
html += QString("<td>%1</td>").arg(formatDecimal(peak.energy, 2));
html += QString("<td>%1</td>").arg(formatScientific(peak.peakActivity, 3));
html += QString("<td>%1%</td>").arg(formatDecimal(peak.peakUncertainty, 2));
html += QString("<td>%1</td>").arg(formatScientific(peak.mda, 3));
html += QString("<td>%1</td>").arg(peak.remark);
html += "</tr>";
}
html += "</table>";
// 峰提示码说明
html += "<div class='legend'>";
html += "<div style='font-weight: bold; margin-bottom: 5px;'>峰提示码说明:</div>";
html += "<div class='legend-item'>G - γ射线</div>";
html += "<div class='legend-item'>X - X射线</div>";
html += "<div class='legend-item'>ec - 内转换电子</div>";
html += "</div>";
return html;
}
QString ReportGenerator::generateSignatureSection() const
{
QString html;
html += "<div class='signature-section'>";
html += QString("<div class='signature-line'>分析员:%1</div>")
.arg(m_data.signature.analyst);
html += QString("<div class='signature-line'>审查员:%1</div>")
.arg(m_data.signature.reviewer);
html += QString("<div class='signature-line'>实验室:%1</div>")
.arg(m_data.signature.laboratory);
html += QString("<div class='signature-line'>日 期:%1</div>")
.arg(m_data.signature.reportDate.toString("yyyy年MM月dd日"));
html += "</div>";
return html;
}
QString ReportGenerator::generateHtmlFooter() const
{
return "</body>\n</html>";
}
QString ReportGenerator::generateHtmlReport() const
{
QString html;
html += generateHtmlHeader();
html += "<div class='report-title'>符合能谱分析报告</div>";
html += generateMeasurementSection();
html += generateCalibrationSection();
html += generateNuclideLibSection();
html += generateAnalysisSection();
html += generatePeakTableSection();
html += generateSignatureSection();
html += generateHtmlFooter();
return html;
}
QString ReportGenerator::generateTextReport() const
{
QString text;
QTextStream stream(&text);
stream << QStringLiteral(u"符合能谱分析报告\n");
stream << "========================================\n\n";
stream << QStringLiteral(u"能谱文件: ") << m_data.measurement.spectrumFile << "\n\n";
stream << QStringLiteral(u"测量参数\n");
stream << "----------------------------------------\n";
stream << QStringLiteral(u" 开始时间: ") << formatDateTime(m_data.measurement.startTime) << "\n";
stream << QStringLiteral(u" 活时间: ") << formatDecimal(m_data.measurement.liveTime, 0) << "\n";
stream << QStringLiteral(u" 实时间: ") << formatDecimal(m_data.measurement.realTime, 0) << "\n";
stream << QStringLiteral(u" 死时间: ") << formatDecimal(m_data.measurement.deadTime, 2) << " %\n\n";
stream << QStringLiteral(u"刻度\n");
stream << "----------------------------------------\n";
stream << QStringLiteral(u" 文件: ") << m_data.calibration.fileName << "\n\n";
stream << QStringLiteral(u" 能量刻度\n");
stream << QStringLiteral(u" 生成时间: ") << formatDateTime(m_data.calibration.energy.generateTime) << "\n";
stream << QStringLiteral(u" 截距: ") << formatDecimal(m_data.calibration.energy.intercept, 3) << " keV\n";
stream << QStringLiteral(u" 斜率: ") << formatDecimal(m_data.calibration.energy.slope, 3) << " keV/ch\n";
stream << QStringLiteral(u" 二次项系数: ") << formatScientific(m_data.calibration.energy.quadratic, 3) << " keV/(ch^2)\n\n";
stream << QStringLiteral(u" 效率刻度\n");
stream << QStringLiteral(u" 生成时间: ") << formatDateTime(m_data.calibration.efficiency.generateTime) << "\n";
stream << QStringLiteral(u" 拐点能量: " )<< formatDecimal(m_data.calibration.efficiency.inflectionEnergy, 2) << " keV\n";
stream << QStringLiteral(u" 拐点以上: ") << m_data.calibration.efficiency.aboveInflection
<< QStringLiteral(u" 误差 = ") << formatDecimal(m_data.calibration.efficiency.aboveError, 2) << " %\n";
stream << QStringLiteral(u" 拐点以下: ") << m_data.calibration.efficiency.belowInflection
<< QStringLiteral(u" 误差 = ") << formatDecimal(m_data.calibration.efficiency.belowError, 2) << " %\n";
stream << QStringLiteral(u" 效率对数方程式: ") << m_data.calibration.efficiency.logEquation << "\n\n";
stream << QStringLiteral(u"核素库\n");
stream << "----------------------------------------\n";
stream << QStringLiteral(u" 文件名: ") << m_data.nuclideLib.fileName << "\n";
stream << QStringLiteral(u" 峰匹配宽度: ") << formatDecimal(m_data.nuclideLib.peakMatchWidth, 3) << " * FWHM (keV)\n\n";
stream << QStringLiteral(u"分析参数\n");
stream << QStringLiteral(u"----------------------------------------\n");
stream << QStringLiteral(u" 起始能量: " )<< formatDecimal(m_data.analysis.startEnergy, 2) << " keV\n";
stream << QStringLiteral(u" 终止能量: ") << formatDecimal(m_data.analysis.endEnergy, 2) << " keV\n";
stream << QStringLiteral(u" 统计误差限值: ") << formatDecimal(m_data.analysis.statErrorLimit, 3) << "%\n";
stream << QStringLiteral(u" 活度比例系数: ") << formatScientific(m_data.analysis.activityRatio, 4) << "\n";
stream << QStringLiteral(u" 探测限计算方法: " )<< m_data.analysis.mdaMethod << "\n\n";
stream << QStringLiteral(u"********************* 能 谱 峰 一 览 表 ******************\n\n");
stream << QString("%1 %2 %3 %4 %5 %6 %7\n")
.arg("核素", -10)
.arg("平均活度(Bq)", -15)
.arg("能量(keV)", -12)
.arg("峰活度(Bq)", -15)
.arg("MDA(Bq)", -15)
.arg("提示码", -8)
.arg("备注", -6);
stream << "------------------------------------------------------------------------\n";
for (const auto& peak : m_data.peaks) {
stream << QString("%1 %2 %3 %4 %5 %6 %7\n")
.arg(peak.nuclide, -10)
.arg(formatScientific(peak.avgActivity, 4), -15)
.arg(formatDecimal(peak.energy, 2), -12)
.arg(formatScientific(peak.peakActivity, 3), -15)
.arg(formatScientific(peak.mda, 3), -15)
.arg(peak.peakCode, -8)
.arg(peak.remark, -6);
}
stream << "\n\n";
stream << QStringLiteral(u"分析员: " )<< m_data.signature.analyst << "\n";
stream << QStringLiteral(u"审查员: " )<< m_data.signature.reviewer << "\n";
stream << QStringLiteral(u"实验室: " )<< m_data.signature.laboratory << "\n";
stream << QStringLiteral(u"日 期: ") << m_data.signature.reportDate.toString("yyyy年MM月dd日") << "\n";
return text;
}
bool ReportGenerator::saveToFile(const QString &filePath) const
{
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return false;
}
QTextStream out(&file);
out.setCodec("UTF-8");
if (filePath.endsWith(".html", Qt::CaseInsensitive) ||
filePath.endsWith(".htm", Qt::CaseInsensitive)) {
out << generateHtmlReport();
} else {
out << generateTextReport();
}
file.close();
return true;
}
QString ReportGenerator::generateWordReport() const
{
QString html;
QTextStream stream(&html);
stream << "<html xmlns:o='urn:schemas-microsoft-com:office:office'\n";
stream << " xmlns:w='urn:schemas-microsoft-com:office:word'\n";
stream << " xmlns='http://www.w3.org/TR/REC-html40'>\n";
stream << "<head>\n";
stream << "<meta charset='UTF-8'>\n";
stream << "<title>符合能谱分析报告</title>\n";
stream << "<!--[if gte mso 9]>\n";
stream << "<xml>\n";
stream << "<w:WordDocument>\n";
stream << "<w:View>Print</w:View>\n";
stream << "<w:Zoom>100</w:Zoom>\n";
stream << "<w:DoNotOptimizeForBrowser/>\n";
stream << "</w:WordDocument>\n";
stream << "</xml>\n";
stream << "<![endif]-->\n";
stream << "<style>\n";
stream << " body {\n";
stream << " font-family: 'SimSun', '宋体', serif;\n";
stream << " font-size: 12pt;\n";
stream << " line-height: 1.5;\n";
stream << " margin: 2.54cm 3.17cm;\n";
stream << " }\n";
stream << " .report-title {\n";
stream << " text-align: center;\n";
stream << " font-size: 18pt;\n";
stream << " font-weight: bold;\n";
stream << " margin-bottom: 20pt;\n";
stream << " }\n";
stream << " .section-title {\n";
stream << " font-weight: bold;\n";
stream << " font-size: 14pt;\n";
stream << " margin-top: 15pt;\n";
stream << " margin-left: 0pt;\n";
stream << " margin-bottom: 8pt;\n";
stream << " border-bottom: 1px solid #000;\n";
stream << " padding-bottom: 3pt;\n";
stream << " }\n";
stream << " table {\n";
stream << " width: 100%;\n";
stream << " border-collapse: collapse;\n";
stream << " margin-bottom: 8pt;\n";
stream << " font-size: 11pt;\n";
stream << " }\n";
stream << " table, th, td {\n";
stream << " border: 1px solid #000;\n";
stream << " }\n";
stream << " th, td {\n";
stream << " padding: 5pt 8pt;\n";
stream << " text-align: left;\n";
stream << " }\n";
stream << " th {\n";
stream << " background-color: #f0f0f0;\n";
stream << " font-weight: bold;\n";
stream << " text-align: center;\n";
stream << " }\n";
stream << " .param-table td:first-child {\n";
stream << " width: 140pt;\n";
stream << " font-weight: bold;\n";
stream << " }\n";
stream << " .peak-table {\n";
stream << " font-size: 10pt;\n";
stream << " }\n";
stream << " .peak-table td {\n";
stream << " text-align: center;\n";
stream << " }\n";
stream << " .signature-section {\n";
stream << " margin-top: 30pt;\n";
stream << " text-align: right;\n";
stream << " font-size: 12pt;\n";
stream << " }\n";
stream << " .signature-line {\n";
stream << " margin-bottom: 8pt;\n";
stream << " }\n";
stream << " .spectrum-file-header {\n";
stream << " text-align: center;\n";
stream << " font-weight: bold;\n";
stream << " font-size: 13pt;\n";
stream << " margin-bottom: 10pt;\n";
stream << " }\n";
stream << " .calibration-sub {\n";
stream << " margin-left: 20pt;\n";
stream << " margin-top: 8pt;\n";
stream << " }\n";
stream << " .calibration-sub-title {\n";
stream << " font-weight: bold;\n";
stream << " margin-bottom: 5pt;\n";
stream << " }\n";
stream << " .legend {\n";
stream << " margin-top: 8pt;\n";
stream << " font-size: 10pt;\n";
stream << " color: #333;\n";
stream << " }\n";
stream << " .legend-title {\n";
stream << " font-weight: bold;\n";
stream << " margin-bottom: 3pt;\n";
stream << " }\n";
stream << "</style>\n";
stream << "</head>\n";
stream << "<body>\n";
// 报告标题
stream << QStringLiteral(u"<div class='report-title'>符合能谱分析报告</div>\n");
// 能谱文件
stream << QStringLiteral(u"<div class='spectrum-file-header'>能谱文件:%1</div>\n")
.arg(m_data.measurement.spectrumFile);
// 测量参数
stream << QStringLiteral(u"<div class='section-title'>测量参数</div>\n");
stream << "<table class='param-table'>\n";
stream << QStringLiteral(u"<tr><td>开始时间:</td><td>%1</td><td>活时间S</td><td>%2</td></tr>\n")
.arg(formatDateTime(m_data.measurement.startTime))
.arg(formatDecimal(m_data.measurement.liveTime, 0));
stream << QStringLiteral(u"<tr><td>实时间S</td><td>%1</td><td>死时间:</td><td>%2 %</td></tr>\n")
.arg(formatDecimal(m_data.measurement.realTime, 0))
.arg(formatDecimal(m_data.measurement.deadTime, 2));
stream << "</table>\n";
// 刻度
stream << QStringLiteral(u"<div class='section-title'>刻度</div>\n");
stream << "<table class='param-table'>\n";
stream << QStringLiteral(u"<tr><td>文件:</td><td>%1</td><td>刻度标准源:</td><td>%2</td></tr>\n")
.arg(m_data.calibration.fileName)
.arg(m_data.calibration.standardSource);
stream << "</table>\n";
// 能量刻度
stream << "<div class='calibration-sub'>\n";
stream << QStringLiteral(u"<div class='calibration-sub-title'>能量刻度</div>\n");
stream << "<table class='param-table'>\n";
stream << QStringLiteral(u"<tr><td>生成时间:</td><td>%1</td><td>截距:</td><td>%2 keV</td></tr>\n")
.arg(formatDateTime(m_data.calibration.energy.generateTime))
.arg(formatDecimal(m_data.calibration.energy.intercept, 3));
stream << QStringLiteral(u"<tr><td>斜率:</td><td>%1 keV/ch</td><td>二次项系数:</td><td>%2 keV/(ch^2)</td></tr>\n")
.arg(formatDecimal(m_data.calibration.energy.slope, 3))
.arg(formatScientific(m_data.calibration.energy.quadratic, 3));
stream << "</table>\n";
stream << "</div>\n";
// 效率刻度
stream << "<div class='calibration-sub'>\n";
stream << QStringLiteral(u"<div class='calibration-sub-title'>效率刻度</div>\n");
stream << "<table class='param-table'>\n";
stream << QStringLiteral(u"<tr><td>生成时间:</td><td>%1</td><td>拐点能量keV</td><td>%2</td></tr>\n")
.arg(formatDateTime(m_data.calibration.efficiency.generateTime))
.arg(formatDecimal(m_data.calibration.efficiency.inflectionEnergy, 2));
stream << QStringLiteral(u"<tr><td>拐点以上:</td><td>%1</td><td>误差:</td><td>%2%</td></tr>\n")
.arg(m_data.calibration.efficiency.aboveInflection)
.arg(formatDecimal(m_data.calibration.efficiency.aboveError, 2));
stream << QStringLiteral(u"<tr><td>拐点以下:</td><td>%1</td><td>误差:</td><td>%2%</td></tr>\n")
.arg(m_data.calibration.efficiency.belowInflection)
.arg(formatDecimal(m_data.calibration.efficiency.belowError, 2));
stream << QStringLiteral(u"<tr><td>效率对数方程式:</td><td colspan='3'>%1</td></tr>\n")
.arg(m_data.calibration.efficiency.logEquation);
stream << "</table>\n";
stream << "</div>\n";
// 核素库
stream << QStringLiteral(u"<div class='section-title'>核素库</div>\n");
stream << "<table class='param-table'>\n";
stream << QStringLiteral(u"<tr><td>文件:</td><td>%1</td><td>峰匹配宽度keV</td><td>%2 * FWHM</td></tr>\n")
.arg(m_data.nuclideLib.fileName)
.arg(formatDecimal(m_data.nuclideLib.peakMatchWidth, 3));
stream << "</table>\n";
// 分析参数
stream << QStringLiteral(u"<div class='section-title'>分析参数</div>\n");
stream << "<table class='param-table'>\n";
stream << QStringLiteral(u"<tr><td>起始能量(keV)</td><td>%1</td><td>终止能量keV</td><td>%2</td></tr>\n")
.arg(formatDecimal(m_data.analysis.startEnergy, 2))
.arg(formatDecimal(m_data.analysis.endEnergy, 2));
stream << QStringLiteral(u"<tr><td>统计误差限值:</td><td>%1%</td><td>活度比例系数:</td><td>%2</td></tr>\n")
.arg(formatDecimal(m_data.analysis.statErrorLimit, 3))
.arg(formatScientific(m_data.analysis.activityRatio, 4));
stream << QStringLiteral(u"<tr><td>探测限计算方法:</td><td colspan='3'>%1</td></tr>\n")
.arg(m_data.analysis.mdaMethod);
stream << "</table>\n";
// 能谱峰一览表
stream << QStringLiteral(u"<div class='section-title'>能 谱 峰 一 览 表</div>\n");
stream << QStringLiteral(u"<table class='peak-table'>\n");
stream << QStringLiteral(u"<tr>\n");
stream << QStringLiteral(u"<th rowspan='2'>核素</th>\n");
stream << QStringLiteral(u"<th rowspan='2' colspan='1'>平均活度贝克Bq</th>\n");
stream << QStringLiteral(u"<th rowspan='2' colspan='1'>不确定度</th>\n");
stream << QStringLiteral(u"<th colspan='4'>峰</th>\n");
stream << QStringLiteral(u"<th rowspan='2'>备注</th>\n");
stream << QStringLiteral(u"</tr>\n");
stream << QStringLiteral(u"<tr>\n");
stream << QStringLiteral(u"<th>能量keV</th>\n");
stream << QStringLiteral(u"<th>活度贝克Bq</th>\n");
stream << QStringLiteral(u"<th>不确定度</th>\n");
stream << QStringLiteral(u"<th>最小可探测活度MDA贝克Bq</th>\n");
stream << QStringLiteral(u"</tr>\n");
for (const auto& peak : m_data.peaks) {
stream << QStringLiteral(u"<tr>\n");
stream << QStringLiteral(u"<td>%1</td>\n").arg(peak.nuclide);
stream << QStringLiteral(u"<td>%1</td>\n").arg(formatScientific(peak.avgActivity, 4));
stream << QStringLiteral(u"<td>%1%</td>\n").arg(formatDecimal(peak.avgUncertainty, 2));
stream << QStringLiteral(u"<td>%1</td>\n").arg(formatDecimal(peak.energy, 2));
stream << QStringLiteral(u"<td>%1</td>\n").arg(formatScientific(peak.peakActivity, 3));
stream << QStringLiteral(u"<td>%1%</td>\n").arg(formatDecimal(peak.peakUncertainty, 2));
stream << QStringLiteral(u"<td>%1</td>\n").arg(formatScientific(peak.mda, 3));
stream << QStringLiteral(u"<td>%1</td>\n").arg(peak.remark);
stream << QStringLiteral(u"</tr>\n");
}
stream << QStringLiteral(u"</table>\n");
// 峰提示码说明
stream << QStringLiteral(u"<div class='legend'>\n");
stream << QStringLiteral(u"<div class='legend-title'>峰提示码说明:</div>\n");
stream << QStringLiteral(u"<div>G - γ射线</div>\n");
stream << QStringLiteral(u"<div>X - X射线</div>\n");
stream << QStringLiteral(u"<div>ec - 内转换电子</div>\n");
stream << QStringLiteral(u"</div>\n");
// 签名
stream << "<div class='signature-section'>\n";
stream << QStringLiteral(u"<div class='signature-line'>分析员:%1</div>\n")
.arg(m_data.signature.analyst);
stream << QStringLiteral(u"<div class='signature-line'>审查员:%1</div>\n")
.arg(m_data.signature.reviewer);
stream << QStringLiteral(u"<div class='signature-line'>实验室:%1</div>\n")
.arg(m_data.signature.laboratory);
stream << QStringLiteral(u"<div class='signature-line'>日 期:%1</div>\n")
.arg(m_data.signature.reportDate.toString("yyyy年MM月dd日"));
stream << "</div>\n";
stream << "</body>\n";
stream << "</html>\n";
return html;
}
bool ReportGenerator::saveToWordFile(const QString &filePath) const
{
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return false;
}
QTextStream out(&file);
out.setCodec("UTF-8");
out << generateWordReport();
file.close();
return true;
}