1501 lines
68 KiB
HTML
1501 lines
68 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh">
|
||
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>动态血糖监测报告</title>
|
||
<!-- 引入ECharts -->
|
||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
|
||
<script>
|
||
// 检查ECharts是否成功加载
|
||
window.addEventListener('load', function() {
|
||
if (typeof echarts !== 'undefined') {
|
||
console.log('ECharts已成功加载, 版本:', echarts.version);
|
||
} else {
|
||
console.error('ECharts加载失败');
|
||
var script = document.createElement('script');
|
||
script.src = 'https://unpkg.com/echarts@5.4.3/dist/echarts.min.js';
|
||
script.onload = function() {
|
||
console.log('从备用CDN加载ECharts成功');
|
||
};
|
||
script.onerror = function() {
|
||
console.error('备用CDN也无法加载ECharts');
|
||
};
|
||
document.head.appendChild(script);
|
||
}
|
||
});
|
||
</script>
|
||
<style>
|
||
body {
|
||
margin: 0;
|
||
padding: 0;
|
||
font-family: 'Microsoft YaHei', Arial, sans-serif;
|
||
background-color: #f5f5f5;
|
||
color: #000;
|
||
}
|
||
|
||
.report-container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
padding: 20px;
|
||
}
|
||
|
||
.report-page {
|
||
margin-bottom: 40px;
|
||
border: 1px solid #ccc;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||
width: 210mm;
|
||
height: 297mm;
|
||
margin: 0 auto 40px;
|
||
position: relative;
|
||
background: white;
|
||
padding: 20mm;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.page-header {
|
||
text-align: center;
|
||
margin-bottom: 20px;
|
||
border-bottom: 2px solid #ccc;
|
||
padding-bottom: 15px;
|
||
}
|
||
|
||
.page-header h1 {
|
||
margin: 0;
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
color: #000;
|
||
}
|
||
|
||
.page-header .patient-info {
|
||
margin-top: 10px;
|
||
font-size: 14px;
|
||
color: #000;
|
||
}
|
||
|
||
.basic-info-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 10px;
|
||
margin-bottom: 20px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.info-label {
|
||
font-weight: bold;
|
||
margin-right: 8px;
|
||
color: #000;
|
||
min-width: 60px;
|
||
}
|
||
|
||
.info-value {
|
||
color: #000;
|
||
}
|
||
|
||
.chart-container {
|
||
width: 100%;
|
||
height: 450px;
|
||
margin: 20px 0;
|
||
border: 1px solid #000;
|
||
}
|
||
|
||
.statistics-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin: 20px 0;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.statistics-table th,
|
||
.statistics-table td {
|
||
border: 1px solid #ccc;
|
||
padding: 6px;
|
||
text-align: center;
|
||
}
|
||
|
||
.statistics-table th {
|
||
background-color: #f5f5f5;
|
||
font-weight: bold;
|
||
color: #000;
|
||
}
|
||
|
||
.analysis-content {
|
||
margin: 20px 0;
|
||
padding: 15px;
|
||
background-color: #fafafa;
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
line-height: 1.6;
|
||
white-space: pre-line;
|
||
color: #000;
|
||
}
|
||
|
||
.detailed-stats-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin: 15px 0;
|
||
font-size: 10px;
|
||
}
|
||
|
||
.detailed-stats-table th,
|
||
.detailed-stats-table td {
|
||
border: 1px solid #ccc;
|
||
padding: 4px;
|
||
text-align: center;
|
||
}
|
||
|
||
.detailed-stats-table th {
|
||
background-color: #f5f5f5;
|
||
font-weight: bold;
|
||
color: #000;
|
||
}
|
||
|
||
.chart-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 12px;
|
||
margin: 15px 0;
|
||
}
|
||
|
||
.chart-item {
|
||
text-align: center;
|
||
}
|
||
|
||
.chart-item .chart {
|
||
width: 100%;
|
||
height: 200px;
|
||
border: 1px solid #000;
|
||
}
|
||
|
||
.chart-item .chart-title {
|
||
font-size: 11px;
|
||
font-weight: bold;
|
||
margin: 5px 0;
|
||
color: #000;
|
||
}
|
||
|
||
.chart-item .chart-info {
|
||
font-size: 10px;
|
||
color: #000;
|
||
margin: 5px 0;
|
||
}
|
||
|
||
.scatter-chart-container {
|
||
display: grid;
|
||
grid-template-rows: 1fr 1fr;
|
||
gap: 15px;
|
||
margin: 20px 0;
|
||
height: calc(100% - 120px);
|
||
}
|
||
|
||
.scatter-chart-item {
|
||
text-align: center;
|
||
}
|
||
|
||
.scatter-chart-item .chart {
|
||
width: 100%;
|
||
height: 400px;
|
||
border: 1px solid #000;
|
||
}
|
||
|
||
.hourly-chart-container {
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.hourly-chart-container .chart {
|
||
width: 100%;
|
||
height: 420px;
|
||
border: 1px solid #000;
|
||
}
|
||
|
||
.data-table-container {
|
||
max-height: 1000px;
|
||
overflow-y: auto;
|
||
margin: 20px 0;
|
||
height: auto;
|
||
}
|
||
|
||
.data-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.data-table th,
|
||
.data-table td {
|
||
border: 1px solid #000;
|
||
padding: 3px;
|
||
text-align: center;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.data-table th {
|
||
background-color: #f5f5f5;
|
||
font-weight: bold;
|
||
position: sticky;
|
||
top: 0;
|
||
color: #000;
|
||
}
|
||
|
||
.page-footer {
|
||
position: absolute;
|
||
bottom: 15mm;
|
||
left: 20mm;
|
||
right: 20mm;
|
||
text-align: center;
|
||
font-size: 10px;
|
||
color: #000;
|
||
border-top: 1px solid #ccc;
|
||
padding-top: 5px;
|
||
}
|
||
|
||
/* 打印样式 */
|
||
@media print {
|
||
body {
|
||
background-color: white;
|
||
}
|
||
|
||
.report-container {
|
||
padding: 0;
|
||
max-width: none;
|
||
}
|
||
|
||
.report-page {
|
||
page-break-after: always;
|
||
break-after: page;
|
||
margin: 0;
|
||
border: 1px solid #ccc;
|
||
border-radius: 0;
|
||
box-shadow: none;
|
||
width: 210mm;
|
||
height: 297mm;
|
||
padding: 20mm;
|
||
}
|
||
|
||
.report-page:last-child {
|
||
page-break-after: auto;
|
||
break-after: auto;
|
||
}
|
||
|
||
@page {
|
||
margin: 0;
|
||
size: A4 portrait;
|
||
}
|
||
|
||
/* 强制显示图例颜色 */
|
||
div[style*="background-color: #ff7f7f"] {
|
||
background-color: #ff7f7f !important;
|
||
-webkit-print-color-adjust: exact !important;
|
||
color-adjust: exact !important;
|
||
}
|
||
|
||
div[style*="background-color: #7fc7ff"] {
|
||
background-color: #7fc7ff !important;
|
||
-webkit-print-color-adjust: exact !important;
|
||
color-adjust: exact !important;
|
||
}
|
||
|
||
div[style*="background-color: #e6a23c"] {
|
||
background-color: #e6a23c !important;
|
||
-webkit-print-color-adjust: exact !important;
|
||
color-adjust: exact !important;
|
||
}
|
||
}
|
||
|
||
/* 隐藏某些页面的样式 */
|
||
.page-hidden {
|
||
display: none;
|
||
}
|
||
|
||
/* 测量时间字体颜色为黑色 */
|
||
[id^="measurement-date"] {
|
||
color: #000 !important;
|
||
}
|
||
</style>
|
||
</head>
|
||
|
||
<body>
|
||
<div id="loading-mask" style="position: fixed; left: 0; top: 0; right: 0; bottom: 0; background: rgba(255,255,255,0.85); z-index: 9999; display: flex; align-items: center; justify-content: center; font-size: 22px; color: #409eff; letter-spacing: 2px; transition: opacity 0.3s;">加载中...</div>
|
||
<div class="report-container">
|
||
<!-- 第1页:基本信息和趋势图 -->
|
||
<div class="report-page" id="page-1">
|
||
<div class="page-header">
|
||
<h1>动态血糖监测报告</h1>
|
||
</div>
|
||
|
||
<!-- 基本信息 -->
|
||
<div class="basic-info-grid">
|
||
<div class="info-item">
|
||
<span class="info-label">姓名:</span>
|
||
<span class="info-value" id="patient-name">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">性别:</span>
|
||
<span class="info-value" id="patient-gender">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">年龄:</span>
|
||
<span class="info-value" id="patient-age">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">机构:</span>
|
||
<span class="info-value" id="patient-org">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">设备:</span>
|
||
<span class="info-value" id="patient-device">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">开始时间:</span>
|
||
<span class="info-value" id="record-start-time">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">结束时间:</span>
|
||
<span class="info-value" id="record-end-time">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">监测时长:</span>
|
||
<span class="info-value" id="record-duration">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">平均血糖:</span>
|
||
<span class="info-value" id="avg-glucose">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">最高血糖:</span>
|
||
<span class="info-value" id="max-glucose">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">最低血糖:</span>
|
||
<span class="info-value" id="min-glucose">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">血糖变异系数(CV%):</span>
|
||
<span class="info-value" id="glucose-cv">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">葡萄糖管理指标(GMI):</span>
|
||
<span class="info-value" id="glucose-gmi">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">目标范围内时间(TIR%):</span>
|
||
<span class="info-value" id="tir-percent">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">低血糖时间(TAR%):</span>
|
||
<span class="info-value" id="tar-percent">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">高血糖时间(THR%):</span>
|
||
<span class="info-value" id="thr-percent">--</span>
|
||
</div>
|
||
</div>
|
||
<!-- 血糖趋势图 -->
|
||
<div style="margin: 20px 0;">
|
||
<div class="chart-container" id="glucose-trend-chart" style="height: 350px; width: 100%; border: 1px solid #ddd; border-radius: 4px;"></div>
|
||
</div>
|
||
<!-- 血糖统计数据表格 -->
|
||
<div style="margin: 20px 0;">
|
||
<h3 style="text-align: center; margin-bottom: 20px; color: #333; font-size: 16px;">血糖统计数据</h3>
|
||
|
||
<!-- 时段统计表格 -->
|
||
<table class="statistics-table" style="margin-bottom: 25px;">
|
||
<thead>
|
||
<tr>
|
||
<th rowspan="2" style="background-color: #e8f4fd;">时段</th>
|
||
<th rowspan="2" style="background-color: #e8f4fd;">平均血糖<br>(mmol/L)</th>
|
||
<th rowspan="2" style="background-color: #e8f4fd;">血糖标准差</th>
|
||
<th colspan="3" style="background-color: #e8f4fd;">血糖分层统计 (%)</th>
|
||
</tr>
|
||
<tr>
|
||
<th style="background-color: #f0f9ff;">正常血糖<br>(3.9-10.0)</th>
|
||
<th style="background-color: #f0f9ff;">低血糖<br>(<3.9)</th>
|
||
<th style="background-color: #f0f9ff;">高血糖<br>(≥10.0)</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td style="font-weight: bold; background-color: #fafafa;">全天 (00:00-24:00)</td>
|
||
<td id="all-avg-glucose-main">--</td>
|
||
<td id="all-std-glucose-main">--</td>
|
||
<td id="all-tir-percent-main">--</td>
|
||
<td id="all-tar-percent-main">--</td>
|
||
<td id="all-thr-percent-main">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td style="font-weight: bold; background-color: #fafafa;">白天 (08:00-22:00)</td>
|
||
<td id="day-avg-glucose-main">--</td>
|
||
<td id="day-std-glucose-main">--</td>
|
||
<td id="day-tir-percent-main">--</td>
|
||
<td id="day-tar-percent-main">--</td>
|
||
<td id="day-thr-percent-main">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td style="font-weight: bold; background-color: #fafafa;">夜间 (22:00-08:00)</td>
|
||
<td id="night-avg-glucose-main">--</td>
|
||
<td id="night-std-glucose-main">--</td>
|
||
<td id="night-tir-percent-main">--</td>
|
||
<td id="night-tar-percent-main">--</td>
|
||
<td id="night-thr-percent-main">--</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- 极值统计表格 -->
|
||
<table class="statistics-table">
|
||
<thead>
|
||
<tr>
|
||
<th style="background-color: #e8f4fd;">血糖极值</th>
|
||
<th style="background-color: #e8f4fd;">数值 (mmol/L)</th>
|
||
<th style="background-color: #e8f4fd;">发生时间</th>
|
||
<th style="background-color: #e8f4fd;">时段分类</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td style="font-weight: bold; background-color: #fff2f0; color: #d32f2f;">最高血糖</td>
|
||
<td id="max-glucose-value-main" style="color: #d32f2f; font-weight: bold;">--</td>
|
||
<td id="max-glucose-time-main">--</td>
|
||
<td id="max-glucose-period-main">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td style="font-weight: bold; background-color: #f3e5f5; color: #7b1fa2;">最低血糖</td>
|
||
<td id="min-glucose-value-main" style="color: #7b1fa2; font-weight: bold;">--</td>
|
||
<td id="min-glucose-time-main">--</td>
|
||
<td id="min-glucose-period-main">--</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
|
||
|
||
<div class="page-footer">
|
||
第1页 | 动态血糖监测报告
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 第2页:分析报告文字 -->
|
||
<div class="report-page" id="page-2">
|
||
<div class="page-header">
|
||
<h1>动态血糖监测分析报告</h1>
|
||
</div>
|
||
|
||
<div class="analysis-content" id="analysis-text">
|
||
<!-- 分析内容将通过JavaScript填充 -->
|
||
暂无分析内容
|
||
</div>
|
||
|
||
<div style="margin-top: 40px;">
|
||
<div style="display: flex; justify-content: flex-end; align-items: center;">
|
||
<div style="text-align: right;">
|
||
<p><strong>报告医生:</strong>_________________</p>
|
||
<p><strong>报告日期:</strong><span id="report-date">--</span></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="page-footer">
|
||
第2页 | 动态血糖监测报告
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 第3页:饼图 -->
|
||
<div class="report-page" id="page-3">
|
||
<div class="page-header">
|
||
<h1>血糖分层统计</h1>
|
||
<div class="patient-info" style="display: flex; justify-content: space-between; align-items: center;">
|
||
<span style="text-align: left;">姓名:<span id="patient-name-3">患者姓名</span></span>
|
||
<span style="flex: 1;"></span>
|
||
<span style="text-align: right;">测量时间:<span id="measurement-date">--</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="display: flex; flex-direction: column; gap: 30px; margin: 20px 0;">
|
||
<div style="text-align: center;">
|
||
<div style="font-size: 18px; font-weight: bold; margin-bottom: 20px; color: #333;">白天血糖统计 (08:00-22:00)</div>
|
||
<div style="display: flex; justify-content: center; align-items: center; gap: 20px;">
|
||
<div id="pie-glucose-day" style="width: 220px; height: 220px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);"></div>
|
||
<div style="font-size: 13px; min-width: 180px;">
|
||
<div style="display: flex; align-items: center; justify-content: space-between; margin: 15px 0; padding: 8px; border-radius: 4px; transition: background-color 0.2s;" onmouseover="this.style.backgroundColor='#f5f5f5'" onmouseout="this.style.backgroundColor='transparent'">
|
||
<div style="display: flex; align-items: center;">
|
||
<div style="width: 20px; height: 20px; background-color: #ff7f7f; margin-right: 12px; border-radius: 3px; border: 1px solid #ddd;"></div>
|
||
<span style="font-weight: 500; color: #333;">低血糖 (<3.9 mmol/L)</span>
|
||
</div>
|
||
<span id="day-low-percent" style="font-weight: 600; color: #666; font-size: 12px;">--</span>
|
||
</div>
|
||
<div style="display: flex; align-items: center; justify-content: space-between; margin: 15px 0; padding: 8px; border-radius: 4px; transition: background-color 0.2s;" onmouseover="this.style.backgroundColor='#f5f5f5'" onmouseout="this.style.backgroundColor='transparent'">
|
||
<div style="display: flex; align-items: center;">
|
||
<div style="width: 20px; height: 20px; background-color: #7fc7ff; margin-right: 12px; border-radius: 3px; border: 1px solid #ddd;"></div>
|
||
<span style="font-weight: 500; color: #333;">正常 (3.9-10.0 mmol/L)</span>
|
||
</div>
|
||
<span id="day-normal-percent" style="font-weight: 600; color: #666; font-size: 12px;">--</span>
|
||
</div>
|
||
<div style="display: flex; align-items: center; justify-content: space-between; margin: 15px 0; padding: 8px; border-radius: 4px; transition: background-color 0.2s;" onmouseover="this.style.backgroundColor='#f5f5f5'" onmouseout="this.style.backgroundColor='transparent'">
|
||
<div style="display: flex; align-items: center;">
|
||
<div style="width: 20px; height: 20px; background-color: #e6a23c; margin-right: 12px; border-radius: 3px; border: 1px solid #ddd;"></div>
|
||
<span style="font-weight: 500; color: #333;">高血糖 (≥10.0 mmol/L)</span>
|
||
</div>
|
||
<span id="day-high-percent" style="font-weight: 600; color: #666; font-size: 12px;">--</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="text-align: center;">
|
||
<div style="font-size: 18px; font-weight: bold; margin-bottom: 20px; color: #333;">夜间血糖统计 (22:00-08:00)</div>
|
||
<div style="display: flex; justify-content: center; align-items: center; gap: 20px;">
|
||
<div id="pie-glucose-night" style="width: 220px; height: 220px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);"></div>
|
||
<div style="font-size: 13px; min-width: 180px;">
|
||
<div style="display: flex; align-items: center; justify-content: space-between; margin: 15px 0; padding: 8px; border-radius: 4px; transition: background-color 0.2s;" onmouseover="this.style.backgroundColor='#f5f5f5'" onmouseout="this.style.backgroundColor='transparent'">
|
||
<div style="display: flex; align-items: center;">
|
||
<div style="width: 20px; height: 20px; background-color: #ff7f7f; margin-right: 12px; border-radius: 3px; border: 1px solid #ddd;"></div>
|
||
<span style="font-weight: 500; color: #333;">低血糖 (<3.9 mmol/L)</span>
|
||
</div>
|
||
<span id="night-low-percent" style="font-weight: 600; color: #666; font-size: 12px;">--</span>
|
||
</div>
|
||
<div style="display: flex; align-items: center; justify-content: space-between; margin: 15px 0; padding: 8px; border-radius: 4px; transition: background-color 0.2s;" onmouseover="this.style.backgroundColor='#f5f5f5'" onmouseout="this.style.backgroundColor='transparent'">
|
||
<div style="display: flex; align-items: center;">
|
||
<div style="width: 20px; height: 20px; background-color: #7fc7ff; margin-right: 12px; border-radius: 3px; border: 1px solid #ddd;"></div>
|
||
<span style="font-weight: 500; color: #333;">正常 (3.9-10.0 mmol/L)</span>
|
||
</div>
|
||
<span id="night-normal-percent" style="font-weight: 600; color: #666; font-size: 12px;">--</span>
|
||
</div>
|
||
<div style="display: flex; align-items: center; justify-content: space-between; margin: 15px 0; padding: 8px; border-radius: 4px; transition: background-color 0.2s;" onmouseover="this.style.backgroundColor='#f5f5f5'" onmouseout="this.style.backgroundColor='transparent'">
|
||
<div style="display: flex; align-items: center;">
|
||
<div style="width: 20px; height: 20px; background-color: #e6a23c; margin-right: 12px; border-radius: 3px; border: 1px solid #ddd;"></div>
|
||
<span style="font-weight: 500; color: #333;">高血糖 (≥10.0 mmol/L)</span>
|
||
</div>
|
||
<span id="night-high-percent" style="font-weight: 600; color: #666; font-size: 12px;">--</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="text-align: center;">
|
||
<div style="font-size: 18px; font-weight: bold; margin-bottom: 20px; color: #333;">全天血糖统计 (00:00-24:00)</div>
|
||
<div style="display: flex; justify-content: center; align-items: center; gap: 20px;">
|
||
<div id="pie-glucose-all" style="width: 220px; height: 220px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);"></div>
|
||
<div style="font-size: 13px; min-width: 180px;">
|
||
<div style="display: flex; align-items: center; justify-content: space-between; margin: 15px 0; padding: 8px; border-radius: 4px; transition: background-color 0.2s;" onmouseover="this.style.backgroundColor='#f5f5f5'" onmouseout="this.style.backgroundColor='transparent'">
|
||
<div style="display: flex; align-items: center;">
|
||
<div style="width: 20px; height: 20px; background-color: #ff7f7f; margin-right: 12px; border-radius: 3px; border: 1px solid #ddd;"></div>
|
||
<span style="font-weight: 500; color: #333;">低血糖 (<3.9 mmol/L)</span>
|
||
</div>
|
||
<span id="all-low-percent" style="font-weight: 600; color: #666; font-size: 12px;">--</span>
|
||
</div>
|
||
<div style="display: flex; align-items: center; justify-content: space-between; margin: 15px 0; padding: 8px; border-radius: 4px; transition: background-color 0.2s;" onmouseover="this.style.backgroundColor='#f5f5f5'" onmouseout="this.style.backgroundColor='transparent'">
|
||
<div style="display: flex; align-items: center;">
|
||
<div style="width: 20px; height: 20px; background-color: #7fc7ff; margin-right: 12px; border-radius: 3px; border: 1px solid #ddd;"></div>
|
||
<span style="font-weight: 500; color: #333;">正常 (3.9-10.0 mmol/L)</span>
|
||
</div>
|
||
<span id="all-normal-percent" style="font-weight: 600; color: #666; font-size: 12px;">--</span>
|
||
</div>
|
||
<div style="display: flex; align-items: center; justify-content: space-between; margin: 15px 0; padding: 8px; border-radius: 4px; transition: background-color 0.2s;" onmouseover="this.style.backgroundColor='#f5f5f5'" onmouseout="this.style.backgroundColor='transparent'">
|
||
<div style="display: flex; align-items: center;">
|
||
<div style="width: 20px; height: 20px; background-color: #e6a23c; margin-right: 12px; border-radius: 3px; border: 1px solid #ddd;"></div>
|
||
<span style="font-weight: 500; color: #333;">高血糖 (≥10.0 mmol/L)</span>
|
||
</div>
|
||
<span id="all-high-percent" style="font-weight: 600; color: #666; font-size: 12px;">--</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="page-footer">
|
||
第3页 | 动态血糖监测报告
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 全局变量
|
||
let patientData = {};
|
||
let chartDataTable = [];
|
||
let analysisResult = '';
|
||
|
||
// 数据验证函数
|
||
function validateData(data) {
|
||
showDebugInfo('开始数据验证');
|
||
|
||
if (!data) {
|
||
showDebugInfo('验证失败:数据为空');
|
||
return false;
|
||
}
|
||
|
||
if (!data.patientData) {
|
||
showDebugInfo('验证失败:缺少患者数据');
|
||
return false;
|
||
}
|
||
|
||
if (!data.chartDataTable || !Array.isArray(data.chartDataTable)) {
|
||
showDebugInfo('验证失败:图表数据不是数组');
|
||
return false;
|
||
}
|
||
|
||
const requiredFields = ['name', 'gender', 'age'];
|
||
for (const field of requiredFields) {
|
||
if (!data.patientData[field]) {
|
||
showDebugInfo(`验证警告:缺少患者字段 ${field}`);
|
||
}
|
||
}
|
||
|
||
if (data.chartDataTable.length > 0) {
|
||
const firstItem = data.chartDataTable[0];
|
||
const requiredChartFields = ['glucose'];
|
||
for (const field of requiredChartFields) {
|
||
if (typeof firstItem[field] === 'undefined') {
|
||
showDebugInfo(`验证警告:图表数据缺少字段 ${field}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
showDebugInfo('数据验证通过');
|
||
return true;
|
||
}
|
||
|
||
// 初始化报告数据
|
||
function initializeReport(data) {
|
||
hideLoading();
|
||
showDebugInfo('开始初始化报告数据');
|
||
|
||
if (!data) {
|
||
showDebugInfo('错误:未收到有效数据');
|
||
console.error('未收到有效数据');
|
||
return;
|
||
}
|
||
|
||
if (!validateData(data)) {
|
||
showDebugInfo('数据验证失败,停止初始化');
|
||
return;
|
||
}
|
||
|
||
patientData = data.patientData || {};
|
||
chartDataTable = data.chartDataTable || [];
|
||
analysisResult = data.analysisResult || '';
|
||
|
||
if (typeof echarts === 'undefined') {
|
||
console.error('ECharts库未加载');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
fillBasicInfo();
|
||
fillAnalysisContent();
|
||
fillStatisticsData();
|
||
renderAllCharts();
|
||
} catch (error) {
|
||
console.error('初始化过程中出错:', error);
|
||
}
|
||
}
|
||
|
||
// 计算CGM特有指标
|
||
function calculateCGMMetrics(data) {
|
||
if (!data || data.length === 0) {
|
||
return {
|
||
avgGlucose: null,
|
||
maxGlucose: null,
|
||
maxGlucoseTime: null,
|
||
minGlucose: null,
|
||
minGlucoseTime: null,
|
||
glucoseCV: null,
|
||
glucoseGMI: null,
|
||
tirPercent: null,
|
||
tarPercent: null,
|
||
thrPercent: null
|
||
};
|
||
}
|
||
|
||
const glucoseValues = data.map(item => item.glucose).filter(v => v != null && !isNaN(v));
|
||
|
||
if (glucoseValues.length === 0) {
|
||
return {
|
||
avgGlucose: null,
|
||
maxGlucose: null,
|
||
maxGlucoseTime: null,
|
||
minGlucose: null,
|
||
minGlucoseTime: null,
|
||
glucoseCV: null,
|
||
glucoseGMI: null,
|
||
tirPercent: null,
|
||
tarPercent: null,
|
||
thrPercent: null
|
||
};
|
||
}
|
||
|
||
// 计算平均血糖
|
||
const avgGlucose = glucoseValues.reduce((sum, val) => sum + val, 0) / glucoseValues.length;
|
||
|
||
// 计算标准差
|
||
const variance = glucoseValues.reduce((sum, val) => sum + Math.pow(val - avgGlucose, 2), 0) / glucoseValues.length;
|
||
const stdDev = Math.sqrt(variance);
|
||
|
||
// 计算变异系数 (CV%)
|
||
const glucoseCV = avgGlucose > 0 ? (stdDev / avgGlucose) * 100 : null;
|
||
|
||
// 计算葡萄糖管理指标 (GMI) - 基于平均血糖的估算HbA1c
|
||
// GMI (%) = 3.31 + 0.02392 × 平均血糖(mg/dL)
|
||
// 将mmol/L转换为mg/dL: mmol/L × 18.018
|
||
const avgGlucoseMgDl = avgGlucose * 18.018;
|
||
const glucoseGMI = 3.31 + 0.02392 * avgGlucoseMgDl;
|
||
|
||
// 找到最高和最低血糖及其时间
|
||
const validData = data.filter(item => item.glucose != null && !isNaN(item.glucose) && item.originalTime);
|
||
const maxItem = validData.reduce((max, current) => current.glucose > max.glucose ? current : max, validData[0]);
|
||
const minItem = validData.reduce((min, current) => current.glucose < min.glucose ? current : min, validData[0]);
|
||
|
||
// 格式化时间
|
||
const formatTime = (timeStr) => {
|
||
const date = new Date(timeStr);
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
const hours = String(date.getHours()).padStart(2, '0');
|
||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||
return `${month}/${day} ${hours}:${minutes}`;
|
||
};
|
||
|
||
// 血糖分层阈值 (mmol/L)
|
||
const LOW_GLUCOSE_THRESHOLD = 3.9;
|
||
const HIGH_GLUCOSE_THRESHOLD = 10.0;
|
||
|
||
const tirCount = glucoseValues.filter(v => v >= LOW_GLUCOSE_THRESHOLD && v < HIGH_GLUCOSE_THRESHOLD).length;
|
||
const tarCount = glucoseValues.filter(v => v < LOW_GLUCOSE_THRESHOLD).length;
|
||
const thrCount = glucoseValues.filter(v => v >= HIGH_GLUCOSE_THRESHOLD).length;
|
||
const totalCount = glucoseValues.length;
|
||
|
||
return {
|
||
avgGlucose: avgGlucose,
|
||
maxGlucose: maxItem ? maxItem.glucose : null,
|
||
maxGlucoseTime: maxItem ? formatTime(maxItem.originalTime) : null,
|
||
minGlucose: minItem ? minItem.glucose : null,
|
||
minGlucoseTime: minItem ? formatTime(minItem.originalTime) : null,
|
||
glucoseCV: glucoseCV,
|
||
glucoseGMI: glucoseGMI,
|
||
tirPercent: totalCount > 0 ? (tirCount / totalCount) * 100 : null,
|
||
tarPercent: totalCount > 0 ? (tarCount / totalCount) * 100 : null,
|
||
thrPercent: totalCount > 0 ? (thrCount / totalCount) * 100 : null
|
||
};
|
||
}
|
||
|
||
// 填充基本信息
|
||
function fillBasicInfo() {
|
||
const nameElements = document.querySelectorAll('[id^="patient-name"]');
|
||
nameElements.forEach(el => {
|
||
el.textContent = patientData.name || '--';
|
||
});
|
||
|
||
const setElementText = (id, value) => {
|
||
const element = document.getElementById(id);
|
||
if (element) {
|
||
const safeValue = (value == null || value === undefined || value === 'undefined' ||
|
||
(typeof value === 'number' && isNaN(value)) ||
|
||
(typeof value === 'string' && (value.includes('undefined') || value.includes('NaN'))))
|
||
? '--' : value;
|
||
element.textContent = safeValue;
|
||
} else {
|
||
console.warn(`未找到元素: ${id}`);
|
||
}
|
||
};
|
||
|
||
setElementText('patient-gender', patientData.gender);
|
||
setElementText('patient-age', patientData.age);
|
||
setElementText('patient-org', patientData.orgname);
|
||
setElementText('patient-device', patientData.device);
|
||
setElementText('record-start-time', patientData.recordStartTime);
|
||
setElementText('record-end-time', patientData.recordEndTime);
|
||
setElementText('record-duration', patientData.recordDuration);
|
||
|
||
// 计算CGM特有指标
|
||
const cgmMetrics = calculateCGMMetrics(chartDataTable);
|
||
|
||
// 优先使用计算出的值,如果没有则使用patientData中的值
|
||
setElementText('avg-glucose', cgmMetrics.avgGlucose ? `${cgmMetrics.avgGlucose.toFixed(1)} mmol/L` :
|
||
(patientData.avgGlucose ? `${patientData.avgGlucose} mmol/L` : '--'));
|
||
setElementText('max-glucose', cgmMetrics.maxGlucose ? `${cgmMetrics.maxGlucose.toFixed(1)} mmol/L (${cgmMetrics.maxGlucoseTime})` :
|
||
(patientData.maxGlucose ? `${patientData.maxGlucose} mmol/L (${patientData.maxGlucoseTime})` : '--'));
|
||
setElementText('min-glucose', cgmMetrics.minGlucose ? `${cgmMetrics.minGlucose.toFixed(1)} mmol/L (${cgmMetrics.minGlucoseTime})` :
|
||
(patientData.minGlucose ? `${patientData.minGlucose} mmol/L (${patientData.minGlucoseTime})` : '--'));
|
||
setElementText('glucose-cv', cgmMetrics.glucoseCV ? `${cgmMetrics.glucoseCV.toFixed(1)}%` :
|
||
(patientData.glucoseCV ? `${patientData.glucoseCV}%` : '--'));
|
||
setElementText('glucose-gmi', cgmMetrics.glucoseGMI ? `${cgmMetrics.glucoseGMI.toFixed(1)}%` :
|
||
(patientData.glucoseGMI ? `${patientData.glucoseGMI}%` : '--'));
|
||
setElementText('tir-percent', cgmMetrics.tirPercent ? `${cgmMetrics.tirPercent.toFixed(1)}%` :
|
||
(patientData.tirPercent ? `${patientData.tirPercent}%` : '--'));
|
||
setElementText('tar-percent', cgmMetrics.tarPercent ? `${cgmMetrics.tarPercent.toFixed(1)}%` :
|
||
(patientData.tarPercent ? `${patientData.tarPercent}%` : '--'));
|
||
setElementText('thr-percent', cgmMetrics.thrPercent ? `${cgmMetrics.thrPercent.toFixed(1)}%` :
|
||
(patientData.thrPercent ? `${patientData.thrPercent}%` : '--'));
|
||
|
||
const measurementDate = patientData.wearTime ?
|
||
new Date(patientData.wearTime).toLocaleDateString('zh-CN') : '--';
|
||
const measurementDateElements = document.querySelectorAll('[id^="measurement-date"]');
|
||
measurementDateElements.forEach(el => {
|
||
el.textContent = measurementDate;
|
||
});
|
||
|
||
document.getElementById('report-date').textContent = new Date().toLocaleDateString('zh-CN');
|
||
}
|
||
|
||
// 填充分析内容
|
||
function fillAnalysisContent() {
|
||
document.getElementById('analysis-text').textContent = analysisResult || '暂无分析内容';
|
||
}
|
||
|
||
// 填充统计数据
|
||
function fillStatisticsData() {
|
||
if (!chartDataTable || chartDataTable.length === 0) {
|
||
console.warn('没有图表数据,显示默认值');
|
||
showDefaultStats();
|
||
return;
|
||
}
|
||
|
||
const stats = calculateStatistics(chartDataTable);
|
||
const extremeValues = calculateExtremeValues(chartDataTable);
|
||
|
||
const setElementText = (id, value) => {
|
||
const element = document.getElementById(id);
|
||
if (element) {
|
||
const safeValue = (value == null || value === undefined || value === 'undefined' ||
|
||
(typeof value === 'number' && isNaN(value)) ||
|
||
(typeof value === 'string' && (value.includes('undefined') || value.includes('NaN'))))
|
||
? '--' : value;
|
||
element.textContent = safeValue;
|
||
} else {
|
||
console.warn(`未找到元素: ${id}`);
|
||
}
|
||
};
|
||
|
||
// 填充时段统计表格
|
||
setElementText('all-avg-glucose-main', stats.all.avgGlucose ? `${stats.all.avgGlucose.toFixed(1)}` : '--');
|
||
setElementText('all-std-glucose-main', stats.all.stdGlucose ? `${stats.all.stdGlucose.toFixed(2)}` : '--');
|
||
setElementText('all-tir-percent-main', stats.all.tirPercent ? `${stats.all.tirPercent.toFixed(1)}%` : '--');
|
||
setElementText('all-tar-percent-main', stats.all.tarPercent ? `${stats.all.tarPercent.toFixed(1)}%` : '--');
|
||
setElementText('all-thr-percent-main', stats.all.thrPercent ? `${stats.all.thrPercent.toFixed(1)}%` : '--');
|
||
|
||
setElementText('day-avg-glucose-main', stats.day.avgGlucose ? `${stats.day.avgGlucose.toFixed(1)}` : '--');
|
||
setElementText('day-std-glucose-main', stats.day.stdGlucose ? `${stats.day.stdGlucose.toFixed(2)}` : '--');
|
||
setElementText('day-tir-percent-main', stats.day.tirPercent ? `${stats.day.tirPercent.toFixed(1)}%` : '--');
|
||
setElementText('day-tar-percent-main', stats.day.tarPercent ? `${stats.day.tarPercent.toFixed(1)}%` : '--');
|
||
setElementText('day-thr-percent-main', stats.day.thrPercent ? `${stats.day.thrPercent.toFixed(1)}%` : '--');
|
||
|
||
setElementText('night-avg-glucose-main', stats.night.avgGlucose ? `${stats.night.avgGlucose.toFixed(1)}` : '--');
|
||
setElementText('night-std-glucose-main', stats.night.stdGlucose ? `${stats.night.stdGlucose.toFixed(2)}` : '--');
|
||
setElementText('night-tir-percent-main', stats.night.tirPercent ? `${stats.night.tirPercent.toFixed(1)}%` : '--');
|
||
setElementText('night-tar-percent-main', stats.night.tarPercent ? `${stats.night.tarPercent.toFixed(1)}%` : '--');
|
||
setElementText('night-thr-percent-main', stats.night.thrPercent ? `${stats.night.thrPercent.toFixed(1)}%` : '--');
|
||
|
||
// 填充极值统计表格
|
||
setElementText('max-glucose-value-main', extremeValues.maxValue ? `${extremeValues.maxValue.toFixed(1)}` : '--');
|
||
setElementText('max-glucose-time-main', extremeValues.maxTime || '--');
|
||
setElementText('max-glucose-period-main', extremeValues.maxPeriod || '--');
|
||
|
||
setElementText('min-glucose-value-main', extremeValues.minValue ? `${extremeValues.minValue.toFixed(1)}` : '--');
|
||
setElementText('min-glucose-time-main', extremeValues.minTime || '--');
|
||
setElementText('min-glucose-period-main', extremeValues.minPeriod || '--');
|
||
|
||
// 保持原有的统计表格填充(第二页)
|
||
setElementText('all-avg-glucose', stats.all.avgGlucose ? `${stats.all.avgGlucose.toFixed(1)}` : '--');
|
||
setElementText('all-std-glucose', stats.all.stdGlucose ? `${stats.all.stdGlucose.toFixed(2)}` : '--');
|
||
setElementText('all-tir-percent', stats.all.tirPercent ? `${stats.all.tirPercent.toFixed(1)}%` : '--');
|
||
setElementText('all-tar-percent', stats.all.tarPercent ? `${stats.all.tarPercent.toFixed(1)}%` : '--');
|
||
setElementText('all-thr-percent', stats.all.thrPercent ? `${stats.all.thrPercent.toFixed(1)}%` : '--');
|
||
|
||
setElementText('day-avg-glucose', stats.day.avgGlucose ? `${stats.day.avgGlucose.toFixed(1)}` : '--');
|
||
setElementText('day-std-glucose', stats.day.stdGlucose ? `${stats.day.stdGlucose.toFixed(2)}` : '--');
|
||
setElementText('day-tir-percent', stats.day.tirPercent ? `${stats.day.tirPercent.toFixed(1)}%` : '--');
|
||
setElementText('day-tar-percent', stats.day.tarPercent ? `${stats.day.tarPercent.toFixed(1)}%` : '--');
|
||
setElementText('day-thr-percent', stats.day.thrPercent ? `${stats.day.thrPercent.toFixed(1)}%` : '--');
|
||
|
||
setElementText('night-avg-glucose', stats.night.avgGlucose ? `${stats.night.avgGlucose.toFixed(1)}` : '--');
|
||
setElementText('night-std-glucose', stats.night.stdGlucose ? `${stats.night.stdGlucose.toFixed(2)}` : '--');
|
||
setElementText('night-tir-percent', stats.night.tirPercent ? `${stats.night.tirPercent.toFixed(1)}%` : '--');
|
||
setElementText('night-tar-percent', stats.night.tarPercent ? `${stats.night.tarPercent.toFixed(1)}%` : '--');
|
||
setElementText('night-thr-percent', stats.night.thrPercent ? `${stats.night.thrPercent.toFixed(1)}%` : '--');
|
||
}
|
||
|
||
// 显示默认统计数据(当没有数据时)
|
||
function showDefaultStats() {
|
||
const setElementText = (id, value) => {
|
||
const element = document.getElementById(id);
|
||
if (element) element.textContent = value;
|
||
};
|
||
|
||
const ids = [
|
||
'all-avg-glucose', 'all-std-glucose', 'all-tir-percent', 'all-tar-percent', 'all-thr-percent',
|
||
'day-avg-glucose', 'day-std-glucose', 'day-tir-percent', 'day-tar-percent', 'day-thr-percent',
|
||
'night-avg-glucose', 'night-std-glucose', 'night-tir-percent', 'night-tar-percent', 'night-thr-percent',
|
||
'all-avg-glucose-main', 'all-std-glucose-main', 'all-tir-percent-main', 'all-tar-percent-main', 'all-thr-percent-main',
|
||
'day-avg-glucose-main', 'day-std-glucose-main', 'day-tir-percent-main', 'day-tar-percent-main', 'day-thr-percent-main',
|
||
'night-avg-glucose-main', 'night-std-glucose-main', 'night-tir-percent-main', 'night-tar-percent-main', 'night-thr-percent-main',
|
||
'max-glucose-value-main', 'max-glucose-time-main', 'max-glucose-period-main',
|
||
'min-glucose-value-main', 'min-glucose-time-main', 'min-glucose-period-main'
|
||
];
|
||
ids.forEach(id => setElementText(id, '--'));
|
||
}
|
||
|
||
// 计算血糖极值和时间点
|
||
function calculateExtremeValues(data) {
|
||
if (!data || data.length === 0) {
|
||
return {
|
||
maxValue: null,
|
||
maxTime: null,
|
||
maxPeriod: null,
|
||
minValue: null,
|
||
minTime: null,
|
||
minPeriod: null
|
||
};
|
||
}
|
||
|
||
const validData = data.filter(item => item.glucose != null && !isNaN(item.glucose) && item.originalTime);
|
||
|
||
if (validData.length === 0) {
|
||
return {
|
||
maxValue: null,
|
||
maxTime: null,
|
||
maxPeriod: null,
|
||
minValue: null,
|
||
minTime: null,
|
||
minPeriod: null
|
||
};
|
||
}
|
||
|
||
// 找到最高血糖
|
||
const maxItem = validData.reduce((max, current) =>
|
||
current.glucose > max.glucose ? current : max
|
||
);
|
||
|
||
// 找到最低血糖
|
||
const minItem = validData.reduce((min, current) =>
|
||
current.glucose < min.glucose ? current : min
|
||
);
|
||
|
||
// 判断时段分类的函数
|
||
const getPeriodName = (timeStr) => {
|
||
const hour = new Date(timeStr).getHours();
|
||
if (hour >= 8 && hour < 22) {
|
||
return '白天';
|
||
} else {
|
||
return '夜间';
|
||
}
|
||
};
|
||
|
||
// 格式化时间显示
|
||
const formatTime = (timeStr) => {
|
||
const date = new Date(timeStr);
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
const hours = String(date.getHours()).padStart(2, '0');
|
||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||
return `${month}/${day} ${hours}:${minutes}`;
|
||
};
|
||
|
||
return {
|
||
maxValue: maxItem.glucose,
|
||
maxTime: formatTime(maxItem.originalTime),
|
||
maxPeriod: getPeriodName(maxItem.originalTime),
|
||
minValue: minItem.glucose,
|
||
minTime: formatTime(minItem.originalTime),
|
||
minPeriod: getPeriodName(minItem.originalTime)
|
||
};
|
||
}
|
||
|
||
// 计算统计数据
|
||
function calculateStatistics(data) {
|
||
const daytimeData = data.filter(item => {
|
||
const hour = new Date(item.originalTime).getHours();
|
||
return hour >= 8 && hour < 22; // 白天定义为8点到22点
|
||
});
|
||
|
||
const nighttimeData = data.filter(item => {
|
||
const hour = new Date(item.originalTime).getHours();
|
||
return hour < 8 || hour >= 22; // 夜间定义为22点到次日8点
|
||
});
|
||
|
||
const calculatePeriodStats = (periodData) => {
|
||
if (periodData.length === 0) {
|
||
return {
|
||
avgGlucose: undefined,
|
||
stdGlucose: undefined,
|
||
tirPercent: undefined,
|
||
tarPercent: undefined,
|
||
thrPercent: undefined,
|
||
};
|
||
}
|
||
|
||
const glucoseValues = periodData.map(d => d.glucose).filter(v => v != null && !isNaN(v));
|
||
|
||
const avg = (arr) => arr.length > 0 ? arr.reduce((a, b) => a + b, 0) / arr.length : undefined;
|
||
const std = (arr, avgVal) => {
|
||
if (arr.length === 0 || avgVal === undefined) return undefined;
|
||
const variance = arr.reduce((sum, val) => sum + Math.pow(val - avgVal, 2), 0) / arr.length;
|
||
return Math.sqrt(variance);
|
||
};
|
||
|
||
const avgGlc = avg(glucoseValues);
|
||
const stdGlc = std(glucoseValues, avgGlc);
|
||
|
||
// 血糖分层阈值 (mmol/L)
|
||
const LOW_GLUCOSE_THRESHOLD = 3.9;
|
||
const HIGH_GLUCOSE_THRESHOLD = 10.0;
|
||
|
||
const tirCount = glucoseValues.filter(v => v >= LOW_GLUCOSE_THRESHOLD && v < HIGH_GLUCOSE_THRESHOLD).length;
|
||
const tarCount = glucoseValues.filter(v => v < LOW_GLUCOSE_THRESHOLD).length;
|
||
const thrCount = glucoseValues.filter(v => v >= HIGH_GLUCOSE_THRESHOLD).length;
|
||
const totalCount = glucoseValues.length;
|
||
|
||
return {
|
||
avgGlucose: avgGlc,
|
||
stdGlucose: stdGlc,
|
||
tirPercent: totalCount > 0 ? (tirCount / totalCount) * 100 : 0,
|
||
tarPercent: totalCount > 0 ? (tarCount / totalCount) * 100 : 0,
|
||
thrPercent: totalCount > 0 ? (thrCount / totalCount) * 100 : 0,
|
||
};
|
||
};
|
||
|
||
return {
|
||
all: calculatePeriodStats(data),
|
||
day: calculatePeriodStats(daytimeData),
|
||
night: calculatePeriodStats(nighttimeData)
|
||
};
|
||
}
|
||
|
||
// 渲染所有图表
|
||
function renderAllCharts() {
|
||
if (!chartDataTable || chartDataTable.length === 0) {
|
||
const chartContainers = document.querySelectorAll('[id$="chart"], .chart, [id^="pie-glucose"]');
|
||
chartContainers.forEach(container => {
|
||
if (container) {
|
||
container.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#999;">暂无数据</div>';
|
||
}
|
||
});
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 渲染第一页的血糖趋势图
|
||
renderGlucoseTrendChart();
|
||
// 第一页现在使用统计表格,不需要渲染饼图
|
||
renderDetailPagePieCharts();
|
||
} catch (error) {
|
||
console.error('渲染图表过程中出错:', error);
|
||
}
|
||
}
|
||
|
||
// 渲染血糖趋势图
|
||
function renderGlucoseTrendChart() {
|
||
const chartContainer = document.getElementById('glucose-trend-chart');
|
||
if (!chartContainer) {
|
||
console.warn('未找到血糖趋势图容器');
|
||
return;
|
||
}
|
||
|
||
const chart = echarts.init(chartContainer);
|
||
|
||
if (!chartDataTable || chartDataTable.length === 0) {
|
||
const option = {
|
||
title: { text: '暂无数据', left: 'center', top: 'middle' },
|
||
grid: { left: 50, right: 20, bottom: 40, top: 30 }
|
||
};
|
||
chart.setOption(option);
|
||
return;
|
||
}
|
||
|
||
// 按时间排序数据
|
||
const sortedData = [...chartDataTable].sort((a, b) => {
|
||
return new Date(a.originalTime).getTime() - new Date(b.originalTime).getTime();
|
||
});
|
||
|
||
// 提取时间和血糖值
|
||
const timeData = sortedData.map(item => {
|
||
const date = new Date(item.originalTime);
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
const hours = String(date.getHours()).padStart(2, '0');
|
||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||
return `${month}-${day} ${hours}:${minutes}`;
|
||
});
|
||
const glucoseData = sortedData.map(item => item.glucose);
|
||
|
||
// 血糖分类函数
|
||
const getGlucoseStatus = (glucoseValue) => {
|
||
if (glucoseValue < 3.9) return '低血糖';
|
||
if (glucoseValue > 10.0) return '高血糖';
|
||
return '正常血糖';
|
||
};
|
||
|
||
const option = {
|
||
title: {
|
||
text: '血糖趋势统计图',
|
||
left: 'center',
|
||
textStyle: {
|
||
fontSize: 18,
|
||
fontWeight: 'bold'
|
||
}
|
||
},
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
position: function (pt) {
|
||
return [pt[0], '10%'];
|
||
},
|
||
formatter: function (params) {
|
||
const dataIndex = params[0].dataIndex;
|
||
const time = timeData[dataIndex];
|
||
const value = params[0].value;
|
||
const status = getGlucoseStatus(value);
|
||
return `时间: ${time}<br/>血糖值: ${value} mmol/L<br/>状态: ${status}`;
|
||
}
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
boundaryGap: false,
|
||
data: timeData
|
||
},
|
||
yAxis: {
|
||
type: 'value',
|
||
boundaryGap: [0, '100%'],
|
||
name: '血糖值 (mmol/L)'
|
||
},
|
||
series: [
|
||
{
|
||
name: '血糖值',
|
||
type: 'line',
|
||
symbol: 'circle',
|
||
symbolSize: 4,
|
||
sampling: 'lttb',
|
||
itemStyle: {
|
||
color: 'rgb(255, 70, 131)'
|
||
},
|
||
areaStyle: {
|
||
color: {
|
||
type: 'linear',
|
||
x: 0,
|
||
y: 0,
|
||
x2: 0,
|
||
y2: 1,
|
||
colorStops: [
|
||
{
|
||
offset: 0,
|
||
color: 'rgba(255, 158, 68, 0.8)'
|
||
},
|
||
{
|
||
offset: 1,
|
||
color: 'rgba(255, 70, 131, 0.1)'
|
||
}
|
||
]
|
||
}
|
||
},
|
||
data: glucoseData
|
||
}
|
||
]
|
||
};
|
||
|
||
chart.setOption(option);
|
||
}
|
||
|
||
// 渲染第三页的饼图
|
||
function renderDetailPagePieCharts() {
|
||
renderPieChart('pie-glucose-day', getDaytimeData());
|
||
renderPieChart('pie-glucose-night', getNighttimeData());
|
||
renderPieChart('pie-glucose-all', chartDataTable);
|
||
}
|
||
|
||
// 渲染单个饼图
|
||
function renderPieChart(elementId, data) {
|
||
const chart = echarts.init(document.getElementById(elementId));
|
||
|
||
const glucoseValues = data.map(item => item.glucose).filter(v => v != null && !isNaN(v));
|
||
|
||
// 确定图例前缀
|
||
let legendPrefix = '';
|
||
if (elementId === 'pie-glucose-day') {
|
||
legendPrefix = 'day';
|
||
} else if (elementId === 'pie-glucose-night') {
|
||
legendPrefix = 'night';
|
||
} else if (elementId === 'pie-glucose-all') {
|
||
legendPrefix = 'all';
|
||
}
|
||
|
||
if (glucoseValues.length === 0) {
|
||
const option = {
|
||
title: { text: '暂无数据', left: 'center', top: 'middle' },
|
||
grid: { left: 50, right: 20, bottom: 40, top: 30 }
|
||
};
|
||
chart.setOption(option);
|
||
// 更新图例显示为无数据
|
||
if (legendPrefix) {
|
||
document.getElementById(`${legendPrefix}-low-percent`).textContent = '占0%';
|
||
document.getElementById(`${legendPrefix}-normal-percent`).textContent = '占0%';
|
||
document.getElementById(`${legendPrefix}-high-percent`).textContent = '占0%';
|
||
}
|
||
return;
|
||
}
|
||
|
||
const LOW_GLUCOSE_THRESHOLD = 3.9;
|
||
const HIGH_GLUCOSE_THRESHOLD = 10.0;
|
||
|
||
const lowCount = glucoseValues.filter(v => v < LOW_GLUCOSE_THRESHOLD).length;
|
||
const targetCount = glucoseValues.filter(v => v >= LOW_GLUCOSE_THRESHOLD && v < HIGH_GLUCOSE_THRESHOLD).length;
|
||
const highCount = glucoseValues.filter(v => v >= HIGH_GLUCOSE_THRESHOLD).length;
|
||
const totalCount = glucoseValues.length;
|
||
|
||
// 计算百分比
|
||
const lowPercentage = ((lowCount / totalCount) * 100).toFixed(1);
|
||
const normalPercentage = ((targetCount / totalCount) * 100).toFixed(1);
|
||
const highPercentage = ((highCount / totalCount) * 100).toFixed(1);
|
||
|
||
// 更新图例百分比显示
|
||
if (legendPrefix) {
|
||
document.getElementById(`${legendPrefix}-low-percent`).textContent = `占${lowPercentage}%`;
|
||
document.getElementById(`${legendPrefix}-normal-percent`).textContent = `占${normalPercentage}%`;
|
||
document.getElementById(`${legendPrefix}-high-percent`).textContent = `占${highPercentage}%`;
|
||
}
|
||
|
||
const pieData = [];
|
||
if (lowCount > 0) {
|
||
pieData.push({
|
||
value: lowCount,
|
||
name: '低血糖',
|
||
itemStyle: { color: '#ff7f7f' },
|
||
label: {
|
||
formatter: `低血糖: ${lowCount} (${lowPercentage}%)`
|
||
}
|
||
});
|
||
}
|
||
if (targetCount > 0) {
|
||
pieData.push({
|
||
value: targetCount,
|
||
name: '正常血糖',
|
||
itemStyle: { color: '#7fc7ff' },
|
||
label: {
|
||
formatter: `正常血糖: ${targetCount} (${normalPercentage}%)`
|
||
}
|
||
});
|
||
}
|
||
if (highCount > 0) {
|
||
pieData.push({
|
||
value: highCount,
|
||
name: '高血糖',
|
||
itemStyle: { color: '#e6a23c' },
|
||
label: {
|
||
formatter: `高血糖: ${highCount} (${highPercentage}%)`
|
||
}
|
||
});
|
||
}
|
||
|
||
if (pieData.length === 0) pieData.push({ value: 1, name: '暂无数据', itemStyle: { color: '#f0f0f0' } });
|
||
|
||
const option = {
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: function(params) {
|
||
if (params.name === '暂无数据') {
|
||
return '暂无数据';
|
||
}
|
||
const percentage = ((params.value / totalCount) * 100).toFixed(1);
|
||
return `${params.name}: ${params.value} (${percentage}%)`;
|
||
}
|
||
},
|
||
legend: {
|
||
show: false // 隐藏图例,使用右侧的自定义图例
|
||
},
|
||
series: [{
|
||
type: 'pie',
|
||
radius: ['40%', '70%'], // 调整环形饼图大小
|
||
center: ['50%', '50%'], // 居中显示
|
||
data: pieData,
|
||
label: {
|
||
show: false // 隐藏饼图内部的百分比显示
|
||
},
|
||
labelLine: {
|
||
show: false
|
||
},
|
||
emphasis: {
|
||
itemStyle: {
|
||
shadowBlur: 10,
|
||
shadowOffsetX: 0,
|
||
shadowColor: 'rgba(0, 0, 0, 0.3)'
|
||
},
|
||
scale: true,
|
||
scaleSize: 5
|
||
},
|
||
animationType: 'scale',
|
||
animationEasing: 'elasticOut',
|
||
animationDelay: function (idx) {
|
||
return Math.random() * 200;
|
||
}
|
||
}]
|
||
};
|
||
|
||
chart.setOption(option);
|
||
}
|
||
|
||
// 辅助函数
|
||
function getDaytimeData() {
|
||
return chartDataTable.filter(item => {
|
||
const hour = new Date(item.originalTime).getHours();
|
||
return hour >= 8 && hour < 22;
|
||
});
|
||
}
|
||
|
||
function getNighttimeData() {
|
||
return chartDataTable.filter(item => {
|
||
const hour = new Date(item.originalTime).getHours();
|
||
return hour < 8 || hour >= 22;
|
||
});
|
||
}
|
||
|
||
// 暴露全局函数供外部调用
|
||
window.initializeCGMReport = initializeReport;
|
||
|
||
// 页面打印函数
|
||
window.printReport = function() {
|
||
window.print();
|
||
};
|
||
|
||
// 页面导出PDF函数
|
||
window.exportToPDF = function() {
|
||
alert('PDF导出功能开发中...');
|
||
};
|
||
|
||
// 监听来自父窗口的消息
|
||
window.addEventListener('message', function(event) {
|
||
showLoading();
|
||
if (event.data && event.data.type === 'INIT_CGM_REPORT') {
|
||
const receivedData = event.data.data;
|
||
setTimeout(() => {
|
||
try {
|
||
initializeReport(receivedData);
|
||
} catch (error) {
|
||
console.error('初始化报告数据失败:', error);
|
||
}
|
||
}, 500);
|
||
}
|
||
});
|
||
|
||
// 创建测试数据
|
||
function createTestData() {
|
||
showLoading();
|
||
const testData = {
|
||
patientData: {
|
||
name: '张三',
|
||
gender: '男',
|
||
age: '50岁',
|
||
orgname: '健康中心',
|
||
device: 'CGM设备',
|
||
recordStartTime: '2025-07-20 00:00:00',
|
||
recordEndTime: '2025-07-21 23:59:59',
|
||
recordDuration: '48小时',
|
||
avgGlucose: 7.5,
|
||
maxGlucose: 15.2,
|
||
maxGlucoseTime: '10:30',
|
||
minGlucose: 2.8,
|
||
minGlucoseTime: '03:15',
|
||
glucoseCV: 30.5,
|
||
glucoseGMI: 7.2,
|
||
tirPercent: 70.0,
|
||
tarPercent: 5.0,
|
||
thrPercent: 25.0,
|
||
wearTime: '2025-07-20'
|
||
},
|
||
chartDataTable: [
|
||
{ originalTime: '2025-07-20 00:00:00', glucose: 6.5 },
|
||
{ originalTime: '2025-07-20 01:00:00', glucose: 5.8 },
|
||
{ originalTime: '2025-07-20 02:00:00', glucose: 4.2 },
|
||
{ originalTime: '2025-07-20 03:00:00', glucose: 3.5 },
|
||
{ originalTime: '2025-07-20 03:15:00', glucose: 2.8 }, // 最低血糖
|
||
{ originalTime: '2025-07-20 04:00:00', glucose: 3.0 },
|
||
{ originalTime: '2025-07-20 05:00:00', glucose: 3.8 },
|
||
{ originalTime: '2025-07-20 06:00:00', glucose: 4.5 },
|
||
{ originalTime: '2025-07-20 07:00:00', glucose: 5.2 },
|
||
{ originalTime: '2025-07-20 08:00:00', glucose: 6.0 },
|
||
{ originalTime: '2025-07-20 09:00:00', glucose: 8.5 },
|
||
{ originalTime: '2025-07-20 10:00:00', glucose: 12.0 },
|
||
{ originalTime: '2025-07-20 10:30:00', glucose: 15.2 }, // 最高血糖
|
||
{ originalTime: '2025-07-20 11:00:00', glucose: 13.0 },
|
||
{ originalTime: '2025-07-20 12:00:00', glucose: 9.5 },
|
||
{ originalTime: '2025-07-20 13:00:00', glucose: 7.0 },
|
||
{ originalTime: '2025-07-20 14:00:00', glucose: 6.8 },
|
||
{ originalTime: '2025-07-20 15:00:00', glucose: 7.2 },
|
||
{ originalTime: '2025-07-20 16:00:00', glucose: 8.0 },
|
||
{ originalTime: '2025-07-20 17:00:00', glucose: 9.0 },
|
||
{ originalTime: '2025-07-20 18:00:00', glucose: 11.0 },
|
||
{ originalTime: '2025-07-20 19:00:00', glucose: 10.5 },
|
||
{ originalTime: '2025-07-20 20:00:00', glucose: 9.8 },
|
||
{ originalTime: '2025-07-20 21:00:00', glucose: 8.2 },
|
||
{ originalTime: '2025-07-20 22:00:00', glucose: 7.0 },
|
||
{ originalTime: '2025-07-20 23:00:00', glucose: 6.0 },
|
||
{ originalTime: '2025-07-21 00:00:00', glucose: 5.5 },
|
||
{ originalTime: '2025-07-21 01:00:00', glucose: 5.0 },
|
||
{ originalTime: '2025-07-21 02:00:00', glucose: 4.0 },
|
||
{ originalTime: '2025-07-21 03:00:00', glucose: 3.2 },
|
||
{ originalTime: '2025-07-21 04:00:00', glucose: 3.5 },
|
||
{ originalTime: '2025-07-21 05:00:00', glucose: 4.0 },
|
||
{ originalTime: '2025-07-21 06:00:00', glucose: 5.0 },
|
||
{ originalTime: '2025-07-21 07:00:00', glucose: 6.0 },
|
||
{ originalTime: '2025-07-21 08:00:00', glucose: 7.0 },
|
||
{ originalTime: '2025-07-21 09:00:00', glucose: 9.0 },
|
||
{ originalTime: '2025-07-21 10:00:00', glucose: 11.0 },
|
||
{ originalTime: '2025-07-21 11:00:00', glucose: 10.0 },
|
||
{ originalTime: '2025-07-21 12:00:00', glucose: 8.0 },
|
||
{ originalTime: '2025-07-21 13:00:00', glucose: 7.5 },
|
||
{ originalTime: '2025-07-21 14:00:00', glucose: 7.0 },
|
||
{ originalTime: '2025-07-21 15:00:00', glucose: 7.8 },
|
||
{ originalTime: '2025-07-21 16:00:00', glucose: 8.5 },
|
||
{ originalTime: '2025-07-21 17:00:00', glucose: 9.2 },
|
||
{ originalTime: '2025-07-21 18:00:00', glucose: 10.0 },
|
||
{ originalTime: '2025-07-21 19:00:00', glucose: 9.5 },
|
||
{ originalTime: '2025-07-21 20:00:00', glucose: 8.8 },
|
||
{ originalTime: '2025-07-21 21:00:00', glucose: 7.5 },
|
||
{ originalTime: '2025-07-21 22:00:00', glucose: 6.5 },
|
||
{ originalTime: '2025-07-21 23:00:00', glucose: 5.8 },
|
||
{ originalTime: '2025-07-21 23:59:59', glucose: 5.2 }
|
||
],
|
||
analysisResult: '该患者动态血糖监测报告显示,血糖波动较大,存在高血糖和低血糖风险。建议进一步调整饮食和运动,并密切监测血糖。'
|
||
};
|
||
initializeReport(testData);
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
showLoading();
|
||
if (window.opener) {
|
||
window.opener.postMessage({
|
||
type: 'CGM_TEMPLATE_LOADED'
|
||
}, '*');
|
||
} else {
|
||
setTimeout(() => {
|
||
createTestData();
|
||
}, 3000);
|
||
}
|
||
});
|
||
|
||
function showLoading() {
|
||
const mask = document.getElementById('loading-mask');
|
||
if (mask) mask.style.display = 'flex';
|
||
document.body.style.overflow = 'hidden';
|
||
}
|
||
function hideLoading() {
|
||
const mask = document.getElementById('loading-mask');
|
||
if (mask) mask.style.display = 'none';
|
||
document.body.style.overflow = '';
|
||
}
|
||
|
||
function showDebugInfo(message) {
|
||
// For debugging purposes, can be removed in final version
|
||
// console.log(message);
|
||
}
|
||
</script>
|
||
</body>
|
||
|
||
</html>
|
||
|