2405 lines
108 KiB
HTML
2405 lines
108 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加载失败');
|
||
// 尝试从备用CDN加载
|
||
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: 240px;
|
||
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: calc(100vh - 200px);
|
||
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: 5px;
|
||
text-align: center;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
}
|
||
|
||
/* 隐藏某些页面的样式 */
|
||
.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="success-count">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">最高收缩压:</span>
|
||
<span class="info-value" id="max-systolic">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">最低收缩压:</span>
|
||
<span class="info-value" id="min-systolic">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">平均收缩压:</span>
|
||
<span class="info-value" id="avg-systolic">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">平均舒张压:</span>
|
||
<span class="info-value" id="avg-diastolic">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">血压变异:</span>
|
||
<span class="info-value" id="bp-variability">--</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="info-label">成功率:</span>
|
||
<span class="info-value" id="success-rate">--</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 血压趋势图 -->
|
||
<div class="chart-container">
|
||
<div id="trend-chart" style="width: 100%; height: 100%;"></div>
|
||
</div>
|
||
|
||
<!-- 统计表格 -->
|
||
<table class="statistics-table">
|
||
<thead>
|
||
<tr>
|
||
<th rowspan="2">时段</th>
|
||
<th colspan="2">平均血压 (mmHg)</th>
|
||
<th rowspan="2">平均心率</th>
|
||
<th colspan="2">血压负荷 (%)</th>
|
||
</tr>
|
||
<tr>
|
||
<th>收缩压</th>
|
||
<th>舒张压</th>
|
||
<th>收缩压</th>
|
||
<th>舒张压</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>整天</td>
|
||
<td id="all-avg-systolic">--</td>
|
||
<td id="all-avg-diastolic">--</td>
|
||
<td id="all-avg-heartrate">--</td>
|
||
<td id="all-systolic-load">--</td>
|
||
<td id="all-diastolic-load">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>白天</td>
|
||
<td id="day-avg-systolic">--</td>
|
||
<td id="day-avg-diastolic">--</td>
|
||
<td id="day-avg-heartrate">--</td>
|
||
<td id="day-systolic-load">--</td>
|
||
<td id="day-diastolic-load">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>夜间</td>
|
||
<td id="night-avg-systolic">--</td>
|
||
<td id="night-avg-diastolic">--</td>
|
||
<td id="night-avg-heartrate">--</td>
|
||
<td id="night-systolic-load">--</td>
|
||
<td id="night-diastolic-load">--</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<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="margin-bottom: 10px; font-size: 12px; font-weight: bold;">
|
||
全天 时间:<span id="all-time-range">--</span> 共:<span id="all-duration">--</span> 有效数据:<span id="all-effective-count">--</span> 共:<span id="all-total-count">--</span>
|
||
</div>
|
||
<table class="detailed-stats-table">
|
||
<thead>
|
||
<tr>
|
||
<th style="width: 140px;">项目</th>
|
||
<th style="width: 80px;">平均</th>
|
||
<th style="width: 80px;">标准差</th>
|
||
<th style="width: 80px;">最大值</th>
|
||
<th style="width: 100px;">出现时间</th>
|
||
<th style="width: 80px;">最小值</th>
|
||
<th style="width: 100px;">出现时间</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>收缩压 (mmHg/kPa)</td>
|
||
<td id="stats-all-avg-systolic">--</td>
|
||
<td id="stats-all-std-systolic">--</td>
|
||
<td id="stats-all-max-systolic">--</td>
|
||
<td id="stats-all-max-systolic-time">--</td>
|
||
<td id="stats-all-min-systolic">--</td>
|
||
<td id="stats-all-min-systolic-time">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>舒张压 (mmHg/kPa)</td>
|
||
<td id="stats-all-avg-diastolic">--</td>
|
||
<td id="stats-all-std-diastolic">--</td>
|
||
<td id="stats-all-max-diastolic">--</td>
|
||
<td id="stats-all-max-diastolic-time">--</td>
|
||
<td id="stats-all-min-diastolic">--</td>
|
||
<td id="stats-all-min-diastolic-time">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>平均压 (mmHg/kPa)</td>
|
||
<td id="stats-all-avg-mean">--</td>
|
||
<td id="stats-all-std-mean">--</td>
|
||
<td id="stats-all-max-mean">--</td>
|
||
<td id="stats-all-max-mean-time">--</td>
|
||
<td id="stats-all-min-mean">--</td>
|
||
<td id="stats-all-min-mean-time">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>心率 (次/分)</td>
|
||
<td id="stats-all-avg-hr">--</td>
|
||
<td id="stats-all-std-hr">--</td>
|
||
<td id="stats-all-max-hr">--</td>
|
||
<td id="stats-all-max-hr-time">--</td>
|
||
<td id="stats-all-min-hr">--</td>
|
||
<td id="stats-all-min-hr-time">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>脉压 (mmHg/kPa)</td>
|
||
<td id="stats-all-avg-pulse">--</td>
|
||
<td id="stats-all-std-pulse">--</td>
|
||
<td id="stats-all-max-pulse">--</td>
|
||
<td id="stats-all-max-pulse-time">--</td>
|
||
<td id="stats-all-min-pulse">--</td>
|
||
<td id="stats-all-min-pulse-time">--</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div style="display: flex; justify-content: space-between; margin: 8px 0; font-size: 10px;">
|
||
<div>
|
||
<span>夜间比白天下降率:</span>
|
||
<span>收缩压:<span id="all-night-drop-systolic"></span>
|
||
<span style="margin-left: 20px;">舒张压:<span id="all-night-drop-diastolic"></span>
|
||
</div>
|
||
</div>
|
||
<div style="display: flex; justify-content: space-between; margin: 8px 0; font-size: 10px;">
|
||
<div>
|
||
<span>血压负荷:</span>
|
||
<span>收缩压:<span id="all-pressure-load-systolic"></span></span>
|
||
<span style="margin-left: 20px;">舒张压:<span id="all-pressure-load-diastolic"></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="display: flex; justify-content: space-between; margin: 8px 0; font-size: 10px;">
|
||
<div>
|
||
<span>血压变异系数:</span>
|
||
<span>收缩压:<span id="all-variability-systolic"></span></span>
|
||
<span style="margin-left: 20px;">舒张压:<span id="all-variability-diastolic"></span></span>
|
||
</div>
|
||
</div>
|
||
<div style="margin: 8px 0; font-size: 10px;">
|
||
<span>动脉硬化指数:--</span>
|
||
</div>
|
||
|
||
<!-- 白天统计 -->
|
||
<div style="margin: 15px 0 10px 0; font-size: 12px; font-weight: bold;">
|
||
白天 时间:<span id="day-time-range">--</span> 共:<span id="day-duration">--</span> 有效数据:<span id="day-effective-count">--</span> 共:<span id="day-total-count">--</span>
|
||
</div>
|
||
<table class="detailed-stats-table">
|
||
<thead>
|
||
<tr>
|
||
<th style="width: 140px;">项目</th>
|
||
<th style="width: 80px;">平均</th>
|
||
<th style="width: 80px;">标准差</th>
|
||
<th style="width: 80px;">最大值</th>
|
||
<th style="width: 100px;">出现时间</th>
|
||
<th style="width: 80px;">最小值</th>
|
||
<th style="width: 100px;">出现时间</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>收缩压 (mmHg/kPa)</td>
|
||
<td id="stats-day-avg-systolic">--</td>
|
||
<td id="stats-day-std-systolic">--</td>
|
||
<td id="stats-day-max-systolic">--</td>
|
||
<td id="stats-day-max-systolic-time">--</td>
|
||
<td id="stats-day-min-systolic">--</td>
|
||
<td id="stats-day-min-systolic-time">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>舒张压 (mmHg/kPa)</td>
|
||
<td id="stats-day-avg-diastolic">--</td>
|
||
<td id="stats-day-std-diastolic">--</td>
|
||
<td id="stats-day-max-diastolic">--</td>
|
||
<td id="stats-day-max-diastolic-time">--</td>
|
||
<td id="stats-day-min-diastolic">--</td>
|
||
<td id="stats-day-min-diastolic-time">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>平均压 (mmHg/kPa)</td>
|
||
<td id="stats-day-avg-mean">--</td>
|
||
<td id="stats-day-std-mean">--</td>
|
||
<td id="stats-day-max-mean">--</td>
|
||
<td id="stats-day-max-mean-time">--</td>
|
||
<td id="stats-day-min-mean">--</td>
|
||
<td id="stats-day-min-mean-time">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>心率 (次/分)</td>
|
||
<td id="stats-day-avg-hr">--</td>
|
||
<td id="stats-day-std-hr">--</td>
|
||
<td id="stats-day-max-hr">--</td>
|
||
<td id="stats-day-max-hr-time">--</td>
|
||
<td id="stats-day-min-hr">--</td>
|
||
<td id="stats-day-min-hr-time">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>脉压 (mmHg/kPa)</td>
|
||
<td id="stats-day-avg-pulse">--</td>
|
||
<td id="stats-day-std-pulse">--</td>
|
||
<td id="stats-day-max-pulse">--</td>
|
||
<td id="stats-day-max-pulse-time">--</td>
|
||
<td id="stats-day-min-pulse">--</td>
|
||
<td id="stats-day-min-pulse-time">--</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div style="display: flex; justify-content: space-between; margin: 8px 0; font-size: 10px;">
|
||
<div>
|
||
<span>血压负荷:</span>
|
||
<span>收缩压:<span id="day-pressure-load-systolic">88.46%</span></span>
|
||
<span style="margin-left: 20px;">舒张压:<span id="day-pressure-load-diastolic">88.46%</span></span>
|
||
</div>
|
||
</div>
|
||
<div style="display: flex; justify-content: space-between; margin: 8px 0; font-size: 10px;">
|
||
<div>
|
||
<span>血压变异系数:</span>
|
||
<span>收缩压:<span id="day-variability-systolic">9.99%</span></span>
|
||
<span style="margin-left: 20px;">舒张压:<span id="day-variability-diastolic">15.34%</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 夜间统计 -->
|
||
<div style="margin: 15px 0 10px 0; font-size: 12px; font-weight: bold;">
|
||
夜间 时间:<span id="night-time-range">--</span> 共:<span id="night-duration">--</span> 有效数据:<span id="night-effective-count">--</span> 共:<span id="night-total-count">--</span>
|
||
</div>
|
||
<table class="detailed-stats-table">
|
||
<thead>
|
||
<tr>
|
||
<th style="width: 140px;">项目</th>
|
||
<th style="width: 80px;">平均</th>
|
||
<th style="width: 80px;">标准差</th>
|
||
<th style="width: 80px;">最大值</th>
|
||
<th style="width: 100px;">出现时间</th>
|
||
<th style="width: 80px;">最小值</th>
|
||
<th style="width: 100px;">出现时间</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>收缩压 (mmHg/kPa)</td>
|
||
<td id="stats-night-avg-systolic">--</td>
|
||
<td id="stats-night-std-systolic">--</td>
|
||
<td id="stats-night-max-systolic">--</td>
|
||
<td id="stats-night-max-systolic-time">--</td>
|
||
<td id="stats-night-min-systolic">--</td>
|
||
<td id="stats-night-min-systolic-time">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>舒张压 (mmHg/kPa)</td>
|
||
<td id="stats-night-avg-diastolic">--</td>
|
||
<td id="stats-night-std-diastolic">--</td>
|
||
<td id="stats-night-max-diastolic">--</td>
|
||
<td id="stats-night-max-diastolic-time">--</td>
|
||
<td id="stats-night-min-diastolic">--</td>
|
||
<td id="stats-night-min-diastolic-time">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>平均压 (mmHg/kPa)</td>
|
||
<td id="stats-night-avg-mean">--</td>
|
||
<td id="stats-night-std-mean">--</td>
|
||
<td id="stats-night-max-mean">--</td>
|
||
<td id="stats-night-max-mean-time">--</td>
|
||
<td id="stats-night-min-mean">--</td>
|
||
<td id="stats-night-min-mean-time">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>心率 (次/分)</td>
|
||
<td id="stats-night-avg-hr">--</td>
|
||
<td id="stats-night-std-hr">--</td>
|
||
<td id="stats-night-max-hr">--</td>
|
||
<td id="stats-night-max-hr-time">--</td>
|
||
<td id="stats-night-min-hr">--</td>
|
||
<td id="stats-night-min-hr-time">--</td>
|
||
</tr>
|
||
<tr>
|
||
<td>脉压 (mmHg/kPa)</td>
|
||
<td id="stats-night-avg-pulse">--</td>
|
||
<td id="stats-night-std-pulse">--</td>
|
||
<td id="stats-night-max-pulse">--</td>
|
||
<td id="stats-night-max-pulse-time">--</td>
|
||
<td id="stats-night-min-pulse">--</td>
|
||
<td id="stats-night-min-pulse-time">--</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div style="display: flex; justify-content: space-between; margin: 8px 0; font-size: 10px;">
|
||
<div>
|
||
<span>血压负荷:</span>
|
||
<span>收缩压:<span id="night-pressure-load-systolic">100%</span></span>
|
||
<span style="margin-left: 20px;">舒张压:<span id="night-pressure-load-diastolic">50.0%</span></span>
|
||
</div>
|
||
</div>
|
||
<div style="display: flex; justify-content: space-between; margin: 8px 0; font-size: 10px;">
|
||
<div>
|
||
<span>血压变异系数:</span>
|
||
<span>收缩压:<span id="night-variability-systolic">7.44%</span></span>
|
||
<span style="margin-left: 20px;">舒张压:<span id="night-variability-diastolic">8.67%</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="page-footer">
|
||
第3页 | 动态血压监测报告
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 第4页:柱状图 -->
|
||
<div class="report-page" id="page-4">
|
||
<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-4">患者姓名</span></span>
|
||
<span style="flex: 1;"></span>
|
||
<span style="text-align: right;">测量时间:<span id="measurement-date-4">--</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="chart-grid">
|
||
<div class="chart-item">
|
||
<div class="chart" id="histogram-systolic-all"></div>
|
||
<div class="chart-title">收缩压分布 - 全天</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="histogram-systolic-day"></div>
|
||
<div class="chart-title">收缩压分布 - 白天</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="histogram-systolic-night"></div>
|
||
<div class="chart-title">收缩压分布 - 夜间</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="histogram-diastolic-all"></div>
|
||
<div class="chart-title">舒张压分布 - 全天</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="histogram-diastolic-day"></div>
|
||
<div class="chart-title">舒张压分布 - 白天</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="histogram-diastolic-night"></div>
|
||
<div class="chart-title">舒张压分布 - 夜间</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="histogram-hr-all"></div>
|
||
<div class="chart-title">心率分布 - 全天</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="histogram-hr-day"></div>
|
||
<div class="chart-title">心率分布 - 白天</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="histogram-hr-night"></div>
|
||
<div class="chart-title">心率分布 - 夜间</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="page-footer">
|
||
第4页 | 动态血压监测报告
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 第5页:饼图 -->
|
||
<div class="report-page" id="page-5">
|
||
<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-5">患者姓名</span></span>
|
||
<span style="flex: 1;"></span>
|
||
<span style="text-align: right;">测量时间:<span id="measurement-date-5">--</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="chart-grid">
|
||
<div class="chart-item">
|
||
<div class="chart" id="pie-systolic-day"></div>
|
||
<div class="chart-title">收缩压 - 白天</div>
|
||
<div class="chart-info">
|
||
<div>最大值:<span id="pie-day-sys-max">--</span></div>
|
||
<div>最小值:<span id="pie-day-sys-min">--</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="pie-systolic-night"></div>
|
||
<div class="chart-title">收缩压 - 夜间</div>
|
||
<div class="chart-info">
|
||
<div>最大值:<span id="pie-night-sys-max">--</span></div>
|
||
<div>最小值:<span id="pie-night-sys-min">--</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="pie-systolic-all"></div>
|
||
<div class="chart-title">收缩压 - 全天</div>
|
||
<div class="chart-info">
|
||
<div>最大值:<span id="pie-all-sys-max">--</span></div>
|
||
<div>最小值:<span id="pie-all-sys-min">--</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="pie-diastolic-day"></div>
|
||
<div class="chart-title">舒张压 - 白天</div>
|
||
<div class="chart-info">
|
||
<div>最大值:<span id="pie-day-dia-max">--</span></div>
|
||
<div>最小值:<span id="pie-day-dia-min">--</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="pie-diastolic-night"></div>
|
||
<div class="chart-title">舒张压 - 夜间</div>
|
||
<div class="chart-info">
|
||
<div>最大值:<span id="pie-night-dia-max">--</span></div>
|
||
<div>最小值:<span id="pie-night-dia-min">--</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="pie-diastolic-all"></div>
|
||
<div class="chart-title">舒张压 - 全天</div>
|
||
<div class="chart-info">
|
||
<div>最大值:<span id="pie-all-dia-max">--</span></div>
|
||
<div>最小值:<span id="pie-all-dia-min">--</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="pie-hr-day"></div>
|
||
<div class="chart-title">心率 - 白天</div>
|
||
<div class="chart-info">
|
||
<div>最大值:<span id="pie-day-hr-max">--</span></div>
|
||
<div>最小值:<span id="pie-day-hr-min">--</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="pie-hr-night"></div>
|
||
<div class="chart-title">心率 - 夜间</div>
|
||
<div class="chart-info">
|
||
<div>最大值:<span id="pie-night-hr-max">--</span></div>
|
||
<div>最小值:<span id="pie-night-hr-min">--</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="chart-item">
|
||
<div class="chart" id="pie-hr-all"></div>
|
||
<div class="chart-title">心率 - 全天</div>
|
||
<div class="chart-info">
|
||
<div>最大值:<span id="pie-all-hr-max">--</span></div>
|
||
<div>最小值:<span id="pie-all-hr-min">--</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="page-footer">
|
||
第5页 | 动态血压监测报告
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 第6页:拟合图 -->
|
||
<div class="report-page" id="page-6">
|
||
<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-6">患者姓名</span></span>
|
||
<span style="flex: 1;"></span>
|
||
<span style="text-align: right;">测量时间:<span id="measurement-date-6">--</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="scatter-chart-container">
|
||
<div class="scatter-chart-item">
|
||
<div class="chart-title">收缩压与舒张压关系</div>
|
||
<div class="chart" id="scatter-bp-relation"></div>
|
||
</div>
|
||
<div class="scatter-chart-item">
|
||
<div class="chart-title">心率与收缩压关系</div>
|
||
<div class="chart" id="scatter-hr-relation"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="page-footer">
|
||
第6页 | 动态血压监测报告
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 第7页:小时平均图 -->
|
||
<div class="report-page" id="page-7">
|
||
<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-7">患者姓名</span></span>
|
||
<span style="flex: 1;"></span>
|
||
<span style="text-align: right;">测量时间:<span id="measurement-date-7">--</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="hourly-chart-container">
|
||
<div class="chart-title" style="text-align: center; margin-bottom: 10px;">血压和心率平均值趋势图</div>
|
||
<div class="chart" id="hourly-average-chart"></div>
|
||
</div>
|
||
|
||
<div class="page-footer">
|
||
第7页 | 动态血压监测报告
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 第8页:患者数据表 -->
|
||
<div class="report-page" id="page-8">
|
||
<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-8">患者姓名</span></span>
|
||
<span style="flex: 1;"></span>
|
||
<span style="text-align: right;">测量时间:<span id="measurement-date-8">--</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="data-table-container">
|
||
<table class="data-table" id="patient-data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>序号</th>
|
||
<th>日期</th>
|
||
<th>时间</th>
|
||
<th>收缩压</th>
|
||
<th>舒张压</th>
|
||
<th>心率</th>
|
||
<th>平均压</th>
|
||
<th>血压等级</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="patient-data-tbody">
|
||
<!-- 数据行将通过JavaScript生成 -->
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="page-footer">
|
||
第8页 | 动态血压监测报告
|
||
</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 = ['systolic', 'diastolic', 'heartRate'];
|
||
for (const field of requiredChartFields) {
|
||
if (typeof firstItem[field] === 'undefined') {
|
||
showDebugInfo(`验证警告:图表数据缺少字段 ${field}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
showDebugInfo('数据验证通过');
|
||
return true;
|
||
}
|
||
|
||
// 初始化报告数据
|
||
function initializeReport(data) {
|
||
hideLoading(); // 数据渲染完成后隐藏loading
|
||
showDebugInfo('开始初始化报告数据');
|
||
console.log('开始初始化报告数据:', data);
|
||
|
||
if (!data) {
|
||
showDebugInfo('错误:未收到有效数据');
|
||
console.error('未收到有效数据');
|
||
return;
|
||
}
|
||
|
||
// 详细检查数据结构
|
||
// 数据验证
|
||
if (!validateData(data)) {
|
||
showDebugInfo('数据验证失败,停止初始化');
|
||
return;
|
||
}
|
||
|
||
showDebugInfo('数据类型检查 - data类型: ' + typeof data);
|
||
showDebugInfo('patientData存在: ' + (data.patientData ? '是' : '否'));
|
||
showDebugInfo('chartDataTable存在: ' + (data.chartDataTable ? '是' : '否'));
|
||
showDebugInfo('analysisResult存在: ' + (data.analysisResult ? '是' : '否'));
|
||
|
||
patientData = data.patientData || {};
|
||
chartDataTable = data.chartDataTable || [];
|
||
analysisResult = data.analysisResult || '';
|
||
|
||
showDebugInfo('患者姓名: ' + (patientData.name || '空'));
|
||
showDebugInfo('图表数据条数: ' + chartDataTable.length);
|
||
showDebugInfo('分析结果: ' + (analysisResult ? '有内容' : '空'));
|
||
|
||
console.log('解析后的数据:', {
|
||
patientData: patientData,
|
||
chartDataCount: chartDataTable.length,
|
||
analysisResult: analysisResult
|
||
});
|
||
|
||
// 检查ECharts是否加载
|
||
if (typeof echarts === 'undefined') {
|
||
console.error('ECharts库未加载');
|
||
return;
|
||
} else {
|
||
console.log('ECharts库已加载');
|
||
}
|
||
|
||
try {
|
||
// 填充基本信息
|
||
console.log('填充基本信息...');
|
||
fillBasicInfo();
|
||
|
||
// 填充分析内容
|
||
console.log('填充分析内容...');
|
||
fillAnalysisContent();
|
||
|
||
// 填充统计数据
|
||
console.log('填充统计数据...');
|
||
console.log('当前chartDataTable数据样本:', chartDataTable.slice(0, 3));
|
||
fillStatisticsData();
|
||
|
||
// 渲染图表
|
||
console.log('渲染图表...');
|
||
renderAllCharts();
|
||
|
||
// 填充数据表
|
||
console.log('填充数据表...');
|
||
fillDataTable();
|
||
|
||
console.log('所有数据初始化完成');
|
||
} catch (error) {
|
||
console.error('初始化过程中出错:', error);
|
||
}
|
||
}
|
||
|
||
// 填充基本信息
|
||
function fillBasicInfo() {
|
||
console.log('开始填充基本信息,患者数据:', patientData);
|
||
|
||
// 患者基本信息
|
||
const nameElements = document.querySelectorAll('[id^="patient-name"]');
|
||
console.log('找到姓名元素数量:', nameElements.length);
|
||
nameElements.forEach(el => {
|
||
el.textContent = patientData.name || '--';
|
||
console.log('设置姓名:', 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;
|
||
console.log(`设置 ${id}:`, 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);
|
||
setElementText('success-count', patientData.successReadCount);
|
||
setElementText('success-rate', patientData.successReadPercent);
|
||
|
||
// 血压统计
|
||
document.getElementById('max-systolic').textContent = patientData.maxSystolic ?
|
||
`${patientData.maxSystolic} mmHg (${patientData.maxSystolicTime})` : '--';
|
||
document.getElementById('min-systolic').textContent = patientData.minSystolic ?
|
||
`${patientData.minSystolic} mmHg (${patientData.minSystolicTime})` : '--';
|
||
document.getElementById('avg-systolic').textContent = patientData.avgSystolic ?
|
||
`${patientData.avgSystolic} mmHg` : '--';
|
||
document.getElementById('avg-diastolic').textContent = patientData.avgDiastolic ?
|
||
`${patientData.avgDiastolic} mmHg` : '--';
|
||
document.getElementById('bp-variability').textContent =
|
||
`收缩压: ${patientData.systolicVariability || '--'}, 舒张压: ${patientData.diastolicVariability || '--'}`;
|
||
|
||
// 设置测量日期
|
||
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() {
|
||
console.log('开始填充统计数据,chartDataTable长度:', chartDataTable ? chartDataTable.length : 'null');
|
||
|
||
if (!chartDataTable || chartDataTable.length === 0) {
|
||
console.warn('没有图表数据,显示默认值');
|
||
// 显示默认值而不是直接返回
|
||
showDefaultStats();
|
||
return;
|
||
}
|
||
|
||
// 计算统计数据
|
||
const stats = calculateStatistics(chartDataTable);
|
||
console.log('计算得到的统计数据:', stats);
|
||
|
||
// 填充汇总表格
|
||
document.getElementById('all-avg-systolic').textContent = stats.all.avgSystolic || '--';
|
||
document.getElementById('all-avg-diastolic').textContent = stats.all.avgDiastolic || '--';
|
||
document.getElementById('all-avg-heartrate').textContent = stats.all.avgHeartRate || '--';
|
||
document.getElementById('all-systolic-load').textContent = stats.all.systolicLoad?.toFixed(1) || '--';
|
||
document.getElementById('all-diastolic-load').textContent = stats.all.diastolicLoad?.toFixed(1) || '--';
|
||
|
||
document.getElementById('day-avg-systolic').textContent = stats.day.avgSystolic || '--';
|
||
document.getElementById('day-avg-diastolic').textContent = stats.day.avgDiastolic || '--';
|
||
document.getElementById('day-avg-heartrate').textContent = stats.day.avgHeartRate || '--';
|
||
document.getElementById('day-systolic-load').textContent = stats.day.systolicLoad?.toFixed(1) || '--';
|
||
document.getElementById('day-diastolic-load').textContent = stats.day.diastolicLoad?.toFixed(1) || '--';
|
||
|
||
document.getElementById('night-avg-systolic').textContent = stats.night.avgSystolic || '--';
|
||
document.getElementById('night-avg-diastolic').textContent = stats.night.avgDiastolic || '--';
|
||
document.getElementById('night-avg-heartrate').textContent = stats.night.avgHeartRate || '--';
|
||
document.getElementById('night-systolic-load').textContent = stats.night.systolicLoad?.toFixed(1) || '--';
|
||
document.getElementById('night-diastolic-load').textContent = stats.night.diastolicLoad?.toFixed(1) || '--';
|
||
|
||
// 填充详细统计表格
|
||
fillDetailedStats(stats);
|
||
}
|
||
|
||
// 显示默认统计数据(当没有数据时)
|
||
function showDefaultStats() {
|
||
console.log('显示默认统计数据');
|
||
|
||
// 填充汇总表格的默认值
|
||
document.getElementById('all-avg-systolic').textContent = '--';
|
||
document.getElementById('all-avg-diastolic').textContent = '--';
|
||
document.getElementById('all-avg-heartrate').textContent = '--';
|
||
document.getElementById('all-systolic-load').textContent = '--';
|
||
document.getElementById('all-diastolic-load').textContent = '--';
|
||
|
||
document.getElementById('day-avg-systolic').textContent = '--';
|
||
document.getElementById('day-avg-diastolic').textContent = '--';
|
||
document.getElementById('day-avg-heartrate').textContent = '--';
|
||
document.getElementById('day-systolic-load').textContent = '--';
|
||
document.getElementById('day-diastolic-load').textContent = '--';
|
||
|
||
document.getElementById('night-avg-systolic').textContent = '--';
|
||
document.getElementById('night-avg-diastolic').textContent = '--';
|
||
document.getElementById('night-avg-heartrate').textContent = '--';
|
||
document.getElementById('night-systolic-load').textContent = '--';
|
||
document.getElementById('night-diastolic-load').textContent = '--';
|
||
|
||
// 填充详细统计表格的默认值
|
||
fillDefaultDetailedStats();
|
||
}
|
||
|
||
// 填充默认的详细统计数据
|
||
function fillDefaultDetailedStats() {
|
||
const periods = ['all', 'day', 'night'];
|
||
const fields = ['systolic', 'diastolic', 'hr', 'mean', 'pulse'];
|
||
const stats = ['avg', 'std', 'max', 'min'];
|
||
const times = ['max-systolic-time', 'min-systolic-time', 'max-diastolic-time', 'min-diastolic-time',
|
||
'max-hr-time', 'min-hr-time', 'max-mean-time', 'min-mean-time', 'max-pulse-time', 'min-pulse-time'];
|
||
|
||
periods.forEach(period => {
|
||
// 统计头部信息
|
||
const safeSetText = (id, value) => {
|
||
const element = document.getElementById(id);
|
||
if (element) element.textContent = value;
|
||
};
|
||
|
||
safeSetText(`${period}-time-range`, '--');
|
||
safeSetText(`${period}-duration`, '--');
|
||
safeSetText(`${period}-effective-count`, '0');
|
||
safeSetText(`${period}-total-count`, '0');
|
||
|
||
// 统计数据
|
||
fields.forEach(field => {
|
||
stats.forEach(stat => {
|
||
safeSetText(`stats-${period}-${stat}-${field}`, '--');
|
||
});
|
||
});
|
||
|
||
// 时间数据
|
||
times.forEach(timeField => {
|
||
safeSetText(`stats-${period}-${timeField}`, '--');
|
||
});
|
||
|
||
// 血压负荷和变异系数
|
||
safeSetText(`${period}-pressure-load-systolic`, '--');
|
||
safeSetText(`${period}-pressure-load-diastolic`, '--');
|
||
safeSetText(`${period}-variability-systolic`, '--');
|
||
safeSetText(`${period}-variability-diastolic`, '--');
|
||
});
|
||
|
||
// 夜间下降率
|
||
const safeSetText = (id, value) => {
|
||
const element = document.getElementById(id);
|
||
if (element) element.textContent = value;
|
||
};
|
||
|
||
safeSetText('all-night-drop-systolic', '--');
|
||
safeSetText('all-night-drop-diastolic', '--');
|
||
safeSetText('all-minimum-index', '--');
|
||
}
|
||
|
||
// 填充默认时段统计数据
|
||
function fillDefaultPeriodStats(prefix) {
|
||
const fields = ['systolic', 'diastolic', 'hr', 'mean', 'pulse'];
|
||
const stats = ['avg', 'std', 'max', 'min'];
|
||
const times = ['max-systolic-time', 'min-systolic-time', 'max-diastolic-time', 'min-diastolic-time',
|
||
'max-hr-time', 'min-hr-time', 'max-mean-time', 'min-mean-time', 'max-pulse-time', 'min-pulse-time'];
|
||
|
||
const safeSetText = (id, value) => {
|
||
const element = document.getElementById(id);
|
||
if (element) element.textContent = value;
|
||
};
|
||
|
||
// 统计数据
|
||
fields.forEach(field => {
|
||
stats.forEach(stat => {
|
||
safeSetText(`${prefix}-${stat}-${field}`, '--');
|
||
});
|
||
});
|
||
|
||
// 时间数据
|
||
times.forEach(timeField => {
|
||
safeSetText(`${prefix}-${timeField}`, '--');
|
||
});
|
||
}
|
||
|
||
// 计算统计数据
|
||
function calculateStatistics(data) {
|
||
const daytimeData = data.filter(item => {
|
||
const hour = new Date(item.originalTime).getHours();
|
||
return hour >= 6 && hour < 22;
|
||
});
|
||
|
||
const nighttimeData = data.filter(item => {
|
||
const hour = new Date(item.originalTime).getHours();
|
||
return hour < 6 || hour >= 22;
|
||
});
|
||
|
||
const calculatePeriodStats = (periodData, periodType) => {
|
||
console.log(`计算${periodType}时段统计,数据长度:`, periodData.length);
|
||
|
||
if (periodData.length === 0) {
|
||
console.warn(`${periodType}时段没有数据,将显示默认值`);
|
||
return {
|
||
avgSystolic: undefined,
|
||
avgDiastolic: undefined,
|
||
avgHeartRate: undefined,
|
||
maxSystolic: undefined,
|
||
minSystolic: undefined,
|
||
maxDiastolic: undefined,
|
||
minDiastolic: undefined,
|
||
maxHeartRate: undefined,
|
||
minHeartRate: undefined,
|
||
systolicLoad: undefined,
|
||
diastolicLoad: undefined,
|
||
hasData: false // 标记是否有数据
|
||
};
|
||
}
|
||
|
||
const systolicValues = periodData.map(d => d.systolic).filter(v => v != null && !isNaN(v));
|
||
const diastolicValues = periodData.map(d => d.diastolic).filter(v => v != null && !isNaN(v));
|
||
const heartRateValues = periodData.map(d => d.heartRate).filter(v => v != null && !isNaN(v));
|
||
|
||
const avg = (arr) => arr.length > 0 ? Math.round(arr.reduce((a, b) => a + b, 0) / arr.length) : undefined;
|
||
const max = (arr) => arr.length > 0 ? Math.max(...arr) : undefined;
|
||
const min = (arr) => arr.length > 0 ? Math.min(...arr) : undefined;
|
||
|
||
// 根据时段类型设置阈值
|
||
let systolicThreshold, diastolicThreshold;
|
||
if (periodType === 'day') {
|
||
systolicThreshold = 135;
|
||
diastolicThreshold = 85;
|
||
} else if (periodType === 'night') {
|
||
systolicThreshold = 120;
|
||
diastolicThreshold = 70;
|
||
} else { // all
|
||
systolicThreshold = 140;
|
||
diastolicThreshold = 90;
|
||
}
|
||
|
||
return {
|
||
avgSystolic: avg(systolicValues),
|
||
avgDiastolic: avg(diastolicValues),
|
||
avgHeartRate: avg(heartRateValues),
|
||
maxSystolic: max(systolicValues),
|
||
minSystolic: min(systolicValues),
|
||
maxDiastolic: max(diastolicValues),
|
||
minDiastolic: min(diastolicValues),
|
||
maxHeartRate: max(heartRateValues),
|
||
minHeartRate: min(heartRateValues),
|
||
systolicLoad: systolicValues.length > 0 ? (systolicValues.filter(v => v >= systolicThreshold).length / systolicValues.length) * 100 : undefined,
|
||
diastolicLoad: diastolicValues.length > 0 ? (diastolicValues.filter(v => v >= diastolicThreshold).length / diastolicValues.length) * 100 : undefined,
|
||
hasData: true // 标记有数据
|
||
};
|
||
};
|
||
|
||
return {
|
||
all: calculatePeriodStats(data, 'all'),
|
||
day: calculatePeriodStats(daytimeData, 'day'),
|
||
night: calculatePeriodStats(nighttimeData, 'night')
|
||
};
|
||
}
|
||
|
||
// 填充详细统计数据
|
||
function fillDetailedStats(stats) {
|
||
// 计算标准差的辅助函数
|
||
const calculateStd = (values, avg) => {
|
||
if (!values || values.length === 0 || !avg || isNaN(avg)) return '--';
|
||
const variance = values.reduce((sum, val) => sum + Math.pow(val - avg, 2), 0) / values.length;
|
||
const result = Math.sqrt(variance);
|
||
return isNaN(result) ? '--' : result.toFixed(1);
|
||
};
|
||
|
||
// 格式化时间的辅助函数
|
||
const formatTime = (data, field, isMax) => {
|
||
if (!data || data.length === 0) return '--';
|
||
const values = data.map(d => d[field]).filter(v => v != null && !isNaN(v));
|
||
if (values.length === 0) return '--';
|
||
const targetValue = isMax ? Math.max(...values) : Math.min(...values);
|
||
const item = data.find(d => d[field] === targetValue);
|
||
if (!item) return '--';
|
||
return new Date(item.originalTime).toLocaleString('zh-CN', {
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
};
|
||
|
||
// 格式化数值显示(包含kPa单位)
|
||
const formatWithKPa = (mmHgValue) => {
|
||
if (mmHgValue == null || isNaN(mmHgValue) || mmHgValue === undefined) return '--';
|
||
const kPaValue = (mmHgValue / 7.5).toFixed(1);
|
||
return `${mmHgValue}/${kPaValue}`;
|
||
};
|
||
|
||
// 填充各时段的统计数据
|
||
const fillPeriodStats = (periodData, stats, prefix) => {
|
||
console.log(`填充${prefix}统计数据,数据长度:`, periodData ? periodData.length : 'null', '统计对象:', stats);
|
||
|
||
// 改进数据检查逻辑,允许部分数据缺失
|
||
if (!periodData) {
|
||
console.warn(`${prefix}时段数据为空`);
|
||
fillDefaultPeriodStats(prefix);
|
||
return;
|
||
}
|
||
|
||
if (!stats || Object.values(stats).every(v => v === undefined)) {
|
||
console.warn(`${prefix}统计数据为空或全部undefined`);
|
||
fillDefaultPeriodStats(prefix);
|
||
return;
|
||
}
|
||
|
||
const systolicValues = periodData.map(d => d.systolic).filter(v => v != null && !isNaN(v));
|
||
const diastolicValues = periodData.map(d => d.diastolic).filter(v => v != null && !isNaN(v));
|
||
const heartRateValues = periodData.map(d => d.heartRate).filter(v => v != null && !isNaN(v));
|
||
const meanPressureValues = periodData.map(d => Math.round((d.systolic + 2 * d.diastolic) / 3)).filter(v => v != null && !isNaN(v));
|
||
const pulsePressureValues = periodData.map(d => d.systolic - d.diastolic).filter(v => v != null && !isNaN(v));
|
||
|
||
// 平均值
|
||
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;
|
||
}
|
||
};
|
||
|
||
setElementText(`${prefix}-avg-systolic`, formatWithKPa(stats.avgSystolic));
|
||
setElementText(`${prefix}-avg-diastolic`, formatWithKPa(stats.avgDiastolic));
|
||
setElementText(`${prefix}-avg-hr`, stats.avgHeartRate || '--');
|
||
setElementText(`${prefix}-avg-mean`, formatWithKPa(Math.round((stats.avgSystolic + 2 * stats.avgDiastolic) / 3)));
|
||
setElementText(`${prefix}-avg-pulse`, formatWithKPa(stats.avgSystolic - stats.avgDiastolic));
|
||
|
||
// 标准差
|
||
setElementText(`${prefix}-std-systolic`, calculateStd(systolicValues, stats.avgSystolic));
|
||
setElementText(`${prefix}-std-diastolic`, calculateStd(diastolicValues, stats.avgDiastolic));
|
||
setElementText(`${prefix}-std-hr`, calculateStd(heartRateValues, stats.avgHeartRate));
|
||
setElementText(`${prefix}-std-mean`, calculateStd(meanPressureValues, (stats.avgSystolic + 2 * stats.avgDiastolic) / 3));
|
||
setElementText(`${prefix}-std-pulse`, calculateStd(pulsePressureValues, stats.avgSystolic - stats.avgDiastolic));
|
||
|
||
// 最大值
|
||
setElementText(`${prefix}-max-systolic`, formatWithKPa(stats.maxSystolic));
|
||
setElementText(`${prefix}-max-diastolic`, formatWithKPa(stats.maxDiastolic));
|
||
setElementText(`${prefix}-max-hr`, stats.maxHeartRate || '--');
|
||
setElementText(`${prefix}-max-mean`, meanPressureValues.length > 0 ? formatWithKPa(Math.max(...meanPressureValues)) : '--');
|
||
setElementText(`${prefix}-max-pulse`, pulsePressureValues.length > 0 ? formatWithKPa(Math.max(...pulsePressureValues)) : '--');
|
||
|
||
// 最小值
|
||
setElementText(`${prefix}-min-systolic`, formatWithKPa(stats.minSystolic));
|
||
setElementText(`${prefix}-min-diastolic`, formatWithKPa(stats.minDiastolic));
|
||
setElementText(`${prefix}-min-hr`, stats.minHeartRate || '--');
|
||
setElementText(`${prefix}-min-mean`, meanPressureValues.length > 0 ? formatWithKPa(Math.min(...meanPressureValues)) : '--');
|
||
setElementText(`${prefix}-min-pulse`, pulsePressureValues.length > 0 ? formatWithKPa(Math.min(...pulsePressureValues)) : '--');
|
||
|
||
// 时间
|
||
setElementText(`${prefix}-max-systolic-time`, formatTime(periodData, 'systolic', true));
|
||
setElementText(`${prefix}-min-systolic-time`, formatTime(periodData, 'systolic', false));
|
||
setElementText(`${prefix}-max-diastolic-time`, formatTime(periodData, 'diastolic', true));
|
||
setElementText(`${prefix}-min-diastolic-time`, formatTime(periodData, 'diastolic', false));
|
||
setElementText(`${prefix}-max-hr-time`, formatTime(periodData, 'heartRate', true));
|
||
setElementText(`${prefix}-min-hr-time`, formatTime(periodData, 'heartRate', false));
|
||
setElementText(`${prefix}-max-mean-time`, formatTime(periodData, 'heartRate', true)); // 简化处理
|
||
setElementText(`${prefix}-min-mean-time`, formatTime(periodData, 'heartRate', false)); // 简化处理
|
||
setElementText(`${prefix}-max-pulse-time`, formatTime(periodData, 'systolic', true)); // 简化处理
|
||
setElementText(`${prefix}-min-pulse-time`, formatTime(periodData, 'systolic', false)); // 简化处理
|
||
};
|
||
|
||
// 获取各时段数据
|
||
const daytimeData = getDaytimeData();
|
||
const nighttimeData = getNighttimeData();
|
||
|
||
// 填充三个时段的数据
|
||
fillPeriodStats(chartDataTable, stats.all, 'stats-all');
|
||
fillPeriodStats(daytimeData, stats.day, 'stats-day');
|
||
fillPeriodStats(nighttimeData, stats.night, 'stats-night');
|
||
|
||
// ====== 新增:动态填充统计区间、时长、有效数、总数 ======
|
||
// 全天
|
||
fillStatsHeader(chartDataTable, 'all');
|
||
// 白天
|
||
fillStatsHeader(daytimeData, 'day');
|
||
// 夜间
|
||
fillStatsHeader(nighttimeData, 'night');
|
||
|
||
// ====== 新增:动态填充血压负荷和变异系数 ======
|
||
fillAdditionalStats(chartDataTable, stats.all, 'all');
|
||
fillAdditionalStats(daytimeData, stats.day, 'day');
|
||
fillAdditionalStats(nighttimeData, stats.night, 'night');
|
||
|
||
// ====== 新增:动态填充夜间比白天下降率 ======
|
||
fillNightDropRates(stats.day, stats.night);
|
||
}
|
||
|
||
// 新增:动态填充统计区间、时长、有效数、总数
|
||
function fillStatsHeader(data, prefix) {
|
||
const safeSetText = (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;
|
||
}
|
||
};
|
||
|
||
if (!data || data.length === 0) {
|
||
safeSetText(prefix+'-time-range', '--');
|
||
safeSetText(prefix+'-duration', '--');
|
||
safeSetText(prefix+'-effective-count', '0');
|
||
safeSetText(prefix+'-total-count', '0');
|
||
return;
|
||
}
|
||
// 计算时间区间
|
||
const times = data.map(item => new Date(item.originalTime).getTime()).sort((a,b)=>a-b);
|
||
const minTime = times[0];
|
||
const maxTime = times[times.length-1];
|
||
const minStr = new Date(minTime).toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' });
|
||
const maxStr = new Date(maxTime).toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' });
|
||
safeSetText(prefix+'-time-range', minStr + ' - ' + maxStr);
|
||
// 计算时长
|
||
const durationMs = maxTime - minTime;
|
||
const hours = Math.floor(durationMs / (1000*60*60));
|
||
const minutes = Math.floor((durationMs % (1000*60*60)) / (1000*60));
|
||
safeSetText(prefix+'-duration', (hours > 0 ? hours+'小时' : '') + (minutes > 0 ? minutes+'分' : ''));
|
||
// 有效数
|
||
const effectiveCount = data.filter(item => item.systolic && item.diastolic && item.heartRate).length;
|
||
safeSetText(prefix+'-effective-count', effectiveCount);
|
||
// 总数
|
||
safeSetText(prefix+'-total-count', data.length);
|
||
}
|
||
|
||
// 新增:动态填充血压负荷和变异系数
|
||
function fillAdditionalStats(data, stats, prefix) {
|
||
const safeSetText = (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;
|
||
}
|
||
};
|
||
|
||
if (!data || data.length === 0 || !stats) {
|
||
// 如果没有数据,显示 --
|
||
safeSetText(`${prefix}-pressure-load-systolic`, '--');
|
||
safeSetText(`${prefix}-pressure-load-diastolic`, '--');
|
||
safeSetText(`${prefix}-variability-systolic`, '--');
|
||
safeSetText(`${prefix}-variability-diastolic`, '--');
|
||
return;
|
||
}
|
||
|
||
// 血压负荷
|
||
const systolicLoad = stats.systolicLoad;
|
||
const diastolicLoad = stats.diastolicLoad;
|
||
safeSetText(`${prefix}-pressure-load-systolic`, systolicLoad !== undefined ? systolicLoad.toFixed(1) + '%' : '--');
|
||
safeSetText(`${prefix}-pressure-load-diastolic`, diastolicLoad !== undefined ? diastolicLoad.toFixed(1) + '%' : '--');
|
||
|
||
// 计算血压变异系数
|
||
const systolicValues = data.map(d => d.systolic).filter(v => v != null && !isNaN(v));
|
||
const diastolicValues = data.map(d => d.diastolic).filter(v => v != null && !isNaN(v));
|
||
|
||
const calculateCV = (values, avg) => {
|
||
if (!values || values.length === 0 || !avg || avg === 0) return undefined;
|
||
const variance = values.reduce((sum, val) => sum + Math.pow(val - avg, 2), 0) / values.length;
|
||
const std = Math.sqrt(variance);
|
||
return (std / avg) * 100;
|
||
};
|
||
|
||
const systolicCV = calculateCV(systolicValues, stats.avgSystolic);
|
||
const diastolicCV = calculateCV(diastolicValues, stats.avgDiastolic);
|
||
|
||
safeSetText(`${prefix}-variability-systolic`, systolicCV !== undefined ? systolicCV.toFixed(2) + '%' : '--');
|
||
safeSetText(`${prefix}-variability-diastolic`, diastolicCV !== undefined ? diastolicCV.toFixed(2) + '%' : '--');
|
||
}
|
||
|
||
// 新增:动态填充夜间比白天下降率
|
||
function fillNightDropRates(dayStats, nightStats) {
|
||
const safeSetText = (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;
|
||
}
|
||
};
|
||
|
||
// 检查是否有足够的数据来计算下降率
|
||
const hasValidDayData = dayStats && dayStats.hasData && dayStats.avgSystolic && dayStats.avgDiastolic;
|
||
const hasValidNightData = nightStats && nightStats.hasData && nightStats.avgSystolic && nightStats.avgDiastolic;
|
||
|
||
if (!hasValidDayData || !hasValidNightData) {
|
||
safeSetText('all-night-drop-systolic', '--');
|
||
safeSetText('all-night-drop-diastolic', '--');
|
||
return;
|
||
}
|
||
|
||
// 计算夜间比白天下降率
|
||
const systolicDropRate = ((dayStats.avgSystolic - nightStats.avgSystolic) / dayStats.avgSystolic) * 100;
|
||
const diastolicDropRate = ((dayStats.avgDiastolic - nightStats.avgDiastolic) / dayStats.avgDiastolic) * 100;
|
||
|
||
// 判断杓型
|
||
const getSystolicDipType = (rate) => {
|
||
if (rate < 0) return '反杓型';
|
||
if (rate < 10) return '非杓型';
|
||
if (rate <= 20) return '杓型';
|
||
return '深杓型';
|
||
};
|
||
|
||
const getDiastolicDipType = (rate) => {
|
||
if (rate < 0) return '反杓型';
|
||
if (rate < 10) return '非杓型';
|
||
if (rate <= 20) return '杓型';
|
||
return '深杓型';
|
||
};
|
||
|
||
const systolicText = systolicDropRate.toFixed(2) + '% (' + getSystolicDipType(systolicDropRate) + ')';
|
||
const diastolicText = diastolicDropRate.toFixed(2) + '% (' + getDiastolicDipType(diastolicDropRate) + ')';
|
||
|
||
safeSetText('all-night-drop-systolic', systolicText);
|
||
safeSetText('all-night-drop-diastolic', diastolicText);
|
||
}
|
||
|
||
// 渲染所有图表
|
||
function renderAllCharts() {
|
||
console.log('开始渲染所有图表, 数据表长度:', chartDataTable ? chartDataTable.length : 0);
|
||
|
||
if (!chartDataTable || chartDataTable.length === 0) {
|
||
console.warn('没有数据可用于渲染图表, chartDataTable:', chartDataTable);
|
||
// 显示无数据提示
|
||
const chartContainers = document.querySelectorAll('[id$="chart"], .chart');
|
||
chartContainers.forEach(container => {
|
||
if (container) {
|
||
container.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#999;">暂无数据</div>';
|
||
}
|
||
});
|
||
return;
|
||
}
|
||
|
||
console.log('图表数据样本:', chartDataTable.slice(0, 2));
|
||
|
||
try {
|
||
// 渲染趋势图
|
||
console.log('渲染趋势图...');
|
||
renderTrendChart();
|
||
|
||
// 渲染柱状图
|
||
console.log('渲染柱状图...');
|
||
renderHistograms();
|
||
|
||
// 渲染饼图
|
||
console.log('渲染饼图...');
|
||
renderPieCharts();
|
||
|
||
// 渲染散点图
|
||
console.log('渲染散点图...');
|
||
renderScatterCharts();
|
||
|
||
// 渲染小时平均图
|
||
console.log('渲染小时平均图...');
|
||
renderHourlyChart();
|
||
|
||
console.log('所有图表渲染完成');
|
||
} catch (error) {
|
||
console.error('渲染图表过程中出错:', error);
|
||
}
|
||
}
|
||
|
||
// 渲染趋势图
|
||
function renderTrendChart() {
|
||
const chart = echarts.init(document.getElementById('trend-chart'));
|
||
|
||
const timeLabels = chartDataTable.map(item =>
|
||
new Date(item.originalTime).toLocaleDateString('zh-CN', {
|
||
month: 'short',
|
||
day: 'numeric',
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
})
|
||
);
|
||
|
||
const option = {
|
||
tooltip: { trigger: 'axis' },
|
||
legend: { data: ['收缩压', '舒张压', '心率'], top: 10 },
|
||
xAxis: { type: 'category', data: timeLabels },
|
||
yAxis: [
|
||
{ type: 'value', name: 'mmHg', position: 'left', min: 0, max: 200 },
|
||
{ type: 'value', name: '次/分', position: 'right', min: 50, max: 120 }
|
||
],
|
||
series: [
|
||
{
|
||
name: '收缩压',
|
||
type: 'line',
|
||
symbol: 'none',
|
||
lineStyle: { width: 2 },
|
||
data: chartDataTable.map(item => item.systolic),
|
||
itemStyle: { color: '#409eff' }
|
||
},
|
||
{
|
||
name: '舒张压',
|
||
type: 'line',
|
||
symbol: 'none',
|
||
lineStyle: { width: 2 },
|
||
data: chartDataTable.map(item => item.diastolic),
|
||
itemStyle: { color: '#67c23a' }
|
||
},
|
||
{
|
||
name: '心率',
|
||
type: 'line',
|
||
symbol: 'none',
|
||
lineStyle: { width: 2 },
|
||
yAxisIndex: 1,
|
||
data: chartDataTable.map(item => item.heartRate),
|
||
itemStyle: { color: '#e6a23c' }
|
||
}
|
||
]
|
||
};
|
||
|
||
chart.setOption(option);
|
||
}
|
||
|
||
// 渲染柱状图
|
||
function renderHistograms() {
|
||
// 收缩压柱状图
|
||
renderHistogram('histogram-systolic-all', chartDataTable, 'systolic', '收缩压分布 - 全天');
|
||
renderHistogram('histogram-systolic-day', getDaytimeData(), 'systolic', '收缩压分布 - 白天');
|
||
renderHistogram('histogram-systolic-night', getNighttimeData(), 'systolic', '收缩压分布 - 夜间');
|
||
|
||
// 舒张压柱状图
|
||
renderHistogram('histogram-diastolic-all', chartDataTable, 'diastolic', '舒张压分布 - 全天');
|
||
renderHistogram('histogram-diastolic-day', getDaytimeData(), 'diastolic', '舒张压分布 - 白天');
|
||
renderHistogram('histogram-diastolic-night', getNighttimeData(), 'diastolic', '舒张压分布 - 夜间');
|
||
|
||
// 心率柱状图
|
||
renderHistogram('histogram-hr-all', chartDataTable, 'heartRate', '心率分布 - 全天');
|
||
renderHistogram('histogram-hr-day', getDaytimeData(), 'heartRate', '心率分布 - 白天');
|
||
renderHistogram('histogram-hr-night', getNighttimeData(), 'heartRate', '心率分布 - 夜间');
|
||
}
|
||
|
||
// 渲染单个柱状图
|
||
function renderHistogram(elementId, data, field, title) {
|
||
const chart = echarts.init(document.getElementById(elementId));
|
||
|
||
let mmHgRanges, labels, counts, colors;
|
||
if (field === 'systolic') {
|
||
mmHgRanges = [130, 140, 150, 160, 170, 180, 190];
|
||
const kPaRanges = mmHgRanges.map(v => (v / 7.5).toFixed(1));
|
||
labels = mmHgRanges.map((v, i) => `${v} (${kPaRanges[i]})`);
|
||
counts = Array(mmHgRanges.length).fill(0);
|
||
data.forEach(item => {
|
||
const value = item[field];
|
||
for (let i = 0; i < mmHgRanges.length; i++) {
|
||
if (i === mmHgRanges.length - 1) {
|
||
if (value >= mmHgRanges[i]) counts[i]++;
|
||
} else if (value >= mmHgRanges[i] && value < mmHgRanges[i + 1]) {
|
||
counts[i]++;
|
||
break;
|
||
}
|
||
}
|
||
});
|
||
colors = '#5bc0de';
|
||
} else if (field === 'diastolic') {
|
||
mmHgRanges = [70, 80, 90, 100, 110, 120, 130, 140, 150, 160];
|
||
const kPaRanges = mmHgRanges.map(v => (v / 7.5).toFixed(1));
|
||
labels = mmHgRanges.map((v, i) => `${v} (${kPaRanges[i]})`);
|
||
counts = Array(mmHgRanges.length).fill(0);
|
||
data.forEach(item => {
|
||
const value = item[field];
|
||
for (let i = 0; i < mmHgRanges.length; i++) {
|
||
if (i === mmHgRanges.length - 1) {
|
||
if (value >= mmHgRanges[i]) counts[i]++;
|
||
} else if (value >= mmHgRanges[i] && value < mmHgRanges[i + 1]) {
|
||
counts[i]++;
|
||
break;
|
||
}
|
||
}
|
||
});
|
||
colors = '#5bc0de';
|
||
} else {
|
||
// heartRate
|
||
mmHgRanges = [60, 70, 80, 90, 100, 110, 120];
|
||
labels = mmHgRanges.map(v => `${v}`);
|
||
counts = Array(mmHgRanges.length).fill(0);
|
||
data.forEach(item => {
|
||
const value = item[field];
|
||
for (let i = 0; i < mmHgRanges.length; i++) {
|
||
if (i === mmHgRanges.length - 1) {
|
||
if (value >= mmHgRanges[i]) counts[i]++;
|
||
} else if (value >= mmHgRanges[i] && value < mmHgRanges[i + 1]) {
|
||
counts[i]++;
|
||
break;
|
||
}
|
||
}
|
||
});
|
||
colors = '#5bc0de';
|
||
}
|
||
const total = data.length;
|
||
const percentages = counts.map(count => total > 0 ? (count / total * 100).toFixed(1) : '0.0');
|
||
|
||
const option = {
|
||
xAxis: {
|
||
type: 'category',
|
||
data: labels,
|
||
axisLabel: { fontSize: 12 }
|
||
},
|
||
yAxis: {
|
||
type: 'value',
|
||
name: '百分率',
|
||
axisLabel: {
|
||
formatter: '{value}%',
|
||
fontSize: 12
|
||
}
|
||
},
|
||
series: [{
|
||
type: 'bar',
|
||
data: percentages,
|
||
barWidth: '40%',
|
||
itemStyle: { color: colors }
|
||
}],
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
formatter: params => {
|
||
const idx = params[0].dataIndex;
|
||
return `${labels[idx]}<br/>百分比: ${percentages[idx]}%<br/>数量: ${counts[idx]}`;
|
||
}
|
||
},
|
||
grid: { left: 50, right: 20, bottom: 40, top: 30 },
|
||
legend: { show: false }
|
||
};
|
||
chart.setOption(option);
|
||
}
|
||
|
||
// 渲染饼图
|
||
function renderPieCharts() {
|
||
// 收缩压饼图
|
||
renderPieChart('pie-systolic-day', getDaytimeData(), 'systolic', 135);
|
||
renderPieChart('pie-systolic-night', getNighttimeData(), 'systolic', 120);
|
||
renderPieChart('pie-systolic-all', chartDataTable, 'systolic', 140);
|
||
|
||
// 舒张压饼图
|
||
renderPieChart('pie-diastolic-day', getDaytimeData(), 'diastolic', 85);
|
||
renderPieChart('pie-diastolic-night', getNighttimeData(), 'diastolic', 70);
|
||
renderPieChart('pie-diastolic-all', chartDataTable, 'diastolic', 90);
|
||
|
||
// 心率饼图
|
||
renderPieChart('pie-hr-day', getDaytimeData(), 'heartRate', null);
|
||
renderPieChart('pie-hr-night', getNighttimeData(), 'heartRate', null);
|
||
renderPieChart('pie-hr-all', chartDataTable, 'heartRate', null);
|
||
|
||
// 填充饼图统计信息
|
||
fillPieChartStats();
|
||
}
|
||
|
||
// 渲染单个饼图
|
||
function renderPieChart(elementId, data, field, threshold) {
|
||
const chart = echarts.init(document.getElementById(elementId));
|
||
|
||
let normalCount, abnormalCount;
|
||
if (field === 'heartRate') {
|
||
normalCount = data.filter(item => item[field] >= 60 && item[field] <= 100).length;
|
||
abnormalCount = data.length - normalCount;
|
||
} else {
|
||
normalCount = data.filter(item => item[field] < threshold).length;
|
||
abnormalCount = data.length - normalCount;
|
||
}
|
||
|
||
const normalPercent = data.length > 0 ? Math.round((normalCount / data.length) * 100) : 0;
|
||
const abnormalPercent = 100 - normalPercent;
|
||
|
||
const option = {
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: '{b}: {c}次 ({d}%)'
|
||
},
|
||
series: [{
|
||
type: 'pie',
|
||
radius: '60%',
|
||
data: [
|
||
{ value: normalCount, itemStyle: { color: '#7fc7ff' } },
|
||
{ value: abnormalCount, itemStyle: { color: '#ff7f7f' } }
|
||
],
|
||
label: {
|
||
show: true,
|
||
formatter: '{b}\n{c}次\n{d}%',
|
||
fontSize: 10,
|
||
position: 'inside'
|
||
},
|
||
labelLine: {
|
||
show: false
|
||
}
|
||
}]
|
||
};
|
||
|
||
chart.setOption(option);
|
||
}
|
||
|
||
// 渲染散点图
|
||
function renderScatterCharts() {
|
||
// 线性回归计算函数
|
||
const calculateLinearRegression = (data) => {
|
||
const n = data.length;
|
||
const sumX = data.reduce((sum, point) => sum + point[0], 0);
|
||
const sumY = data.reduce((sum, point) => sum + point[1], 0);
|
||
const sumXY = data.reduce((sum, point) => sum + point[0] * point[1], 0);
|
||
const sumXX = data.reduce((sum, point) => sum + point[0] * point[0], 0);
|
||
|
||
const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
|
||
const intercept = (sumY - slope * sumX) / n;
|
||
|
||
return { slope, intercept };
|
||
};
|
||
|
||
// 收缩压与舒张压关系
|
||
const bpChart = echarts.init(document.getElementById('scatter-bp-relation'));
|
||
const bpData = chartDataTable.map(item => [item.diastolic, item.systolic]);
|
||
const bpRegression = calculateLinearRegression(bpData);
|
||
|
||
// 生成拟合线数据
|
||
const bpFitLineData = [];
|
||
for (let x = 50; x <= 120; x += 10) {
|
||
const y = bpRegression.slope * x + bpRegression.intercept;
|
||
bpFitLineData.push([x, y]);
|
||
}
|
||
|
||
const bpOption = {
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: function(params) {
|
||
if (params.seriesName === '散点数据') {
|
||
return `舒张压: ${params.data[0]}mmHg<br/>收缩压: ${params.data[1]}mmHg`;
|
||
}
|
||
return '';
|
||
}
|
||
},
|
||
grid: {
|
||
left: 60,
|
||
right: 30,
|
||
bottom: 60,
|
||
top: 30
|
||
},
|
||
xAxis: {
|
||
type: 'value',
|
||
name: '舒张压(mmHg)',
|
||
min: 50,
|
||
max: 120,
|
||
nameLocation: 'middle',
|
||
nameGap: 30,
|
||
axisLine: { show: true },
|
||
axisTick: { show: true },
|
||
splitLine: { show: true, lineStyle: { color: '#e0e0e0' } }
|
||
},
|
||
yAxis: {
|
||
type: 'value',
|
||
name: '收缩压(mmHg)',
|
||
min: 100,
|
||
max: 200,
|
||
nameLocation: 'middle',
|
||
nameGap: 40,
|
||
axisLine: { show: true },
|
||
axisTick: { show: true },
|
||
splitLine: { show: true, lineStyle: { color: '#e0e0e0' } }
|
||
},
|
||
series: [
|
||
{
|
||
name: '散点数据',
|
||
symbolSize: 8,
|
||
data: bpData,
|
||
type: 'scatter',
|
||
itemStyle: { color: '#409eff', opacity: 0.7 }
|
||
},
|
||
{
|
||
name: '拟合线',
|
||
type: 'line',
|
||
data: bpFitLineData,
|
||
lineStyle: {
|
||
color: '#e6a23c',
|
||
width: 2,
|
||
type: 'solid'
|
||
},
|
||
symbol: 'none',
|
||
smooth: false
|
||
}
|
||
]
|
||
};
|
||
bpChart.setOption(bpOption);
|
||
|
||
// 心率与收缩压关系
|
||
const hrChart = echarts.init(document.getElementById('scatter-hr-relation'));
|
||
const hrData = chartDataTable.map(item => [item.heartRate, item.systolic]);
|
||
const hrRegression = calculateLinearRegression(hrData);
|
||
|
||
// 生成拟合线数据
|
||
const hrFitLineData = [];
|
||
for (let x = 50; x <= 120; x += 10) {
|
||
const y = hrRegression.slope * x + hrRegression.intercept;
|
||
hrFitLineData.push([x, y]);
|
||
}
|
||
|
||
const hrOption = {
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: function(params) {
|
||
if (params.seriesName === '散点数据') {
|
||
return `心率: ${params.data[0]}次/分<br/>收缩压: ${params.data[1]}mmHg`;
|
||
}
|
||
return '';
|
||
}
|
||
},
|
||
grid: {
|
||
left: 60,
|
||
right: 30,
|
||
bottom: 60,
|
||
top: 30
|
||
},
|
||
xAxis: {
|
||
type: 'value',
|
||
name: '心率(次/分)',
|
||
min: 50,
|
||
max: 120,
|
||
nameLocation: 'middle',
|
||
nameGap: 30,
|
||
axisLine: { show: true },
|
||
axisTick: { show: true },
|
||
splitLine: { show: true, lineStyle: { color: '#e0e0e0' } }
|
||
},
|
||
yAxis: {
|
||
type: 'value',
|
||
name: '收缩压(mmHg)',
|
||
min: 100,
|
||
max: 200,
|
||
nameLocation: 'middle',
|
||
nameGap: 40,
|
||
axisLine: { show: true },
|
||
axisTick: { show: true },
|
||
splitLine: { show: true, lineStyle: { color: '#e0e0e0' } }
|
||
},
|
||
series: [
|
||
{
|
||
name: '散点数据',
|
||
symbolSize: 8,
|
||
data: hrData,
|
||
type: 'scatter',
|
||
itemStyle: { color: '#409eff', opacity: 0.7 }
|
||
},
|
||
{
|
||
name: '拟合线',
|
||
type: 'line',
|
||
data: hrFitLineData,
|
||
lineStyle: {
|
||
color: '#e6a23c',
|
||
width: 2,
|
||
type: 'solid'
|
||
},
|
||
symbol: 'none',
|
||
smooth: false
|
||
}
|
||
]
|
||
};
|
||
hrChart.setOption(hrOption);
|
||
}
|
||
|
||
// 渲染小时平均图
|
||
function renderHourlyChart() {
|
||
const chart = echarts.init(document.getElementById('hourly-average-chart'));
|
||
|
||
// 按小时分组计算平均值
|
||
const hourlyData = Array.from({ length: 24 }, (_, hour) => {
|
||
const hourData = chartDataTable.filter(item => new Date(item.originalTime).getHours() === hour);
|
||
|
||
if (hourData.length === 0) {
|
||
return { hour, systolic: null, diastolic: null, heartRate: null };
|
||
}
|
||
|
||
const avgSystolic = Math.round(hourData.reduce((sum, item) => sum + item.systolic, 0) / hourData.length);
|
||
const avgDiastolic = Math.round(hourData.reduce((sum, item) => sum + item.diastolic, 0) / hourData.length);
|
||
const avgHeartRate = Math.round(hourData.reduce((sum, item) => sum + item.heartRate, 0) / hourData.length);
|
||
|
||
return { hour, systolic: avgSystolic, diastolic: avgDiastolic, heartRate: avgHeartRate };
|
||
});
|
||
|
||
const option = {
|
||
tooltip: { trigger: 'axis' },
|
||
legend: { data: ['收缩压', '舒张压', '心率'], bottom: 0 },
|
||
xAxis: {
|
||
type: 'category',
|
||
data: Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0'))
|
||
},
|
||
yAxis: [
|
||
{ type: 'value', name: 'mmHg', position: 'left', min: 0, max: 200 },
|
||
{ type: 'value', name: '次/分', position: 'right', min: 50, max: 120 }
|
||
],
|
||
series: [
|
||
{
|
||
name: '收缩压',
|
||
type: 'line',
|
||
smooth: true,
|
||
data: hourlyData.map(d => d.systolic),
|
||
itemStyle: { color: '#409eff' }
|
||
},
|
||
{
|
||
name: '舒张压',
|
||
type: 'line',
|
||
smooth: true,
|
||
data: hourlyData.map(d => d.diastolic),
|
||
itemStyle: { color: '#67c23a' }
|
||
},
|
||
{
|
||
name: '心率',
|
||
type: 'line',
|
||
smooth: true,
|
||
yAxisIndex: 1,
|
||
data: hourlyData.map(d => d.heartRate),
|
||
itemStyle: { color: '#e6a23c' }
|
||
}
|
||
]
|
||
};
|
||
|
||
chart.setOption(option);
|
||
}
|
||
|
||
// 填充数据表
|
||
function fillDataTable() {
|
||
const tbody = document.getElementById('patient-data-tbody');
|
||
tbody.innerHTML = '';
|
||
|
||
chartDataTable.forEach((item, index) => {
|
||
const row = document.createElement('tr');
|
||
row.innerHTML = `
|
||
<td>${index + 1}</td>
|
||
<td>${new Date(item.originalTime).toLocaleDateString('zh-CN')}</td>
|
||
<td>${new Date(item.originalTime).toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })}</td>
|
||
<td>${item.systolic}</td>
|
||
<td>${item.diastolic}</td>
|
||
<td>${item.heartRate}</td>
|
||
<td>${item.avgBP}</td>
|
||
<td>${item.status}</td>
|
||
`;
|
||
tbody.appendChild(row);
|
||
});
|
||
}
|
||
|
||
// 填充饼图统计信息
|
||
function fillPieChartStats() {
|
||
const daytimeData = getDaytimeData();
|
||
const nighttimeData = getNighttimeData();
|
||
|
||
// 收缩压统计
|
||
fillPieStats(daytimeData, 'systolic', 'pie-day-sys');
|
||
fillPieStats(nighttimeData, 'systolic', 'pie-night-sys');
|
||
fillPieStats(chartDataTable, 'systolic', 'pie-all-sys');
|
||
|
||
// 舒张压统计
|
||
fillPieStats(daytimeData, 'diastolic', 'pie-day-dia');
|
||
fillPieStats(nighttimeData, 'diastolic', 'pie-night-dia');
|
||
fillPieStats(chartDataTable, 'diastolic', 'pie-all-dia');
|
||
|
||
// 心率统计
|
||
fillPieStats(daytimeData, 'heartRate', 'pie-day-hr');
|
||
fillPieStats(nighttimeData, 'heartRate', 'pie-night-hr');
|
||
fillPieStats(chartDataTable, 'heartRate', 'pie-all-hr');
|
||
}
|
||
|
||
// 填充单个饼图的统计信息
|
||
function fillPieStats(data, field, prefix) {
|
||
if (!data || data.length === 0) return;
|
||
|
||
const values = data.map(item => item[field]);
|
||
const maxValue = Math.max(...values);
|
||
const minValue = Math.min(...values);
|
||
|
||
// 找到最大值和最小值对应的时间
|
||
const maxItem = data.find(item => item[field] === maxValue);
|
||
const minItem = data.find(item => item[field] === minValue);
|
||
|
||
const formatTime = (item) => {
|
||
if (!item) return '--';
|
||
return new Date(item.originalTime).toLocaleString('zh-CN', {
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
};
|
||
|
||
const maxElement = document.getElementById(`${prefix}-max`);
|
||
const minElement = document.getElementById(`${prefix}-min`);
|
||
|
||
if (maxElement) {
|
||
maxElement.textContent = `${maxValue} (${formatTime(maxItem)})`;
|
||
}
|
||
|
||
if (minElement) {
|
||
minElement.textContent = `${minValue} (${formatTime(minItem)})`;
|
||
}
|
||
}
|
||
|
||
// 辅助函数
|
||
function getDaytimeData() {
|
||
return chartDataTable.filter(item => {
|
||
const hour = new Date(item.originalTime).getHours();
|
||
return hour >= 6 && hour < 22;
|
||
});
|
||
}
|
||
|
||
function getNighttimeData() {
|
||
return chartDataTable.filter(item => {
|
||
const hour = new Date(item.originalTime).getHours();
|
||
return hour < 6 || hour >= 22;
|
||
});
|
||
}
|
||
|
||
// 暴露全局函数供外部调用
|
||
window.initializeABPMReport = initializeReport;
|
||
|
||
// 页面打印函数
|
||
window.printReport = function() {
|
||
window.print();
|
||
};
|
||
|
||
// 页面导出PDF函数
|
||
window.exportToPDF = function() {
|
||
alert('PDF导出功能开发中...');
|
||
};
|
||
|
||
// 监听来自父窗口的消息
|
||
window.addEventListener('message', function(event) {
|
||
showLoading(); // 接收到数据前显示loading
|
||
showDebugInfo('收到消息: ' + JSON.stringify(event.data));
|
||
|
||
if (event.data && event.data.type === 'INIT_ABPM_REPORT') {
|
||
showDebugInfo('开始初始化ABPM报告数据');
|
||
|
||
// 详细调试信息
|
||
const receivedData = event.data.data;
|
||
showDebugInfo('接收到的数据结构: ' + JSON.stringify(receivedData, null, 2));
|
||
showDebugInfo('患者数据存在: ' + (receivedData && receivedData.patientData ? '是' : '否'));
|
||
showDebugInfo('图表数据存在: ' + (receivedData && receivedData.chartDataTable ? '是' : '否'));
|
||
showDebugInfo('图表数据条数: ' + (receivedData && receivedData.chartDataTable ? receivedData.chartDataTable.length : '0'));
|
||
showDebugInfo('分析结果存在: ' + (receivedData && receivedData.analysisResult ? '是' : '否'));
|
||
|
||
// 延迟一点时间确保页面完全加载
|
||
setTimeout(() => {
|
||
try {
|
||
initializeReport(receivedData);
|
||
showDebugInfo('报告数据初始化完成');
|
||
} catch (error) {
|
||
showDebugInfo('初始化报告数据失败: ' + error.message);
|
||
console.error('初始化报告数据失败:', error);
|
||
}
|
||
}, 500);
|
||
}
|
||
});
|
||
|
||
// 显示调试信息到页面
|
||
function showDebugInfo(message) {
|
||
const debugInfo = document.getElementById('debug-info');
|
||
const debugContent = document.getElementById('debug-content');
|
||
if (debugInfo && debugContent) {
|
||
debugInfo.style.display = 'block';
|
||
debugContent.innerHTML += '<div>' + new Date().toLocaleTimeString() + ': ' + message + '</div>';
|
||
}
|
||
console.log('调试:', message);
|
||
}
|
||
|
||
// 创建测试数据
|
||
function createTestData() {
|
||
showLoading(); // 加载测试数据前显示loading
|
||
// 检查URL参数来决定使用哪种测试数据
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
const testType = urlParams.get('test') || 'normal';
|
||
|
||
let testData;
|
||
|
||
if (testType === 'dayonly') {
|
||
// 只有白天数据的测试
|
||
testData = {
|
||
patientData: {
|
||
name: '测试患者(仅白天数据)',
|
||
gender: '男',
|
||
age: '45岁',
|
||
orgname: '测试医院',
|
||
device: '测试设备',
|
||
recordStartTime: '2024-01-01 08:00:00',
|
||
recordEndTime: '2024-01-01 20:00:00',
|
||
recordDuration: '12小时',
|
||
successReadCount: '7',
|
||
successReadPercent: '95%',
|
||
maxSystolic: '145',
|
||
maxSystolicTime: '12:00',
|
||
minSystolic: '130',
|
||
minSystolicTime: '08:00',
|
||
avgSystolic: '137',
|
||
avgDiastolic: '85',
|
||
systolicVariability: '12.5%',
|
||
diastolicVariability: '10.2%',
|
||
wearTime: '2024-01-01'
|
||
},
|
||
chartDataTable: [
|
||
// 只有白天数据 (6:00-22:00)
|
||
{ originalTime: '2024-01-01 08:00:00', systolic: 130, diastolic: 80, heartRate: 70, avgBP: 97, status: '正常', suggestion: '继续保持' },
|
||
{ originalTime: '2024-01-01 10:00:00', systolic: 140, diastolic: 90, heartRate: 75, avgBP: 107, status: '轻度高血压', suggestion: '注意饮食' },
|
||
{ originalTime: '2024-01-01 12:00:00', systolic: 145, diastolic: 95, heartRate: 80, avgBP: 112, status: '轻度高血压', suggestion: '适量运动' },
|
||
{ originalTime: '2024-01-01 14:00:00', systolic: 135, diastolic: 85, heartRate: 72, avgBP: 102, status: '正常', suggestion: '继续保持' },
|
||
{ originalTime: '2024-01-01 16:00:00', systolic: 142, diastolic: 88, heartRate: 78, avgBP: 106, status: '轻度高血压', suggestion: '注意监测' },
|
||
{ originalTime: '2024-01-01 18:00:00', systolic: 138, diastolic: 82, heartRate: 74, avgBP: 101, status: '正常', suggestion: '继续保持' },
|
||
{ originalTime: '2024-01-01 20:00:00', systolic: 132, diastolic: 78, heartRate: 69, avgBP: 96, status: '正常', suggestion: '继续保持' }
|
||
],
|
||
analysisResult: '测试分析结果:患者仅有白天血压数据,白天血压控制基本正常,但缺少夜间数据无法评估昼夜节律。'
|
||
};
|
||
} else {
|
||
// 正常的全天数据测试
|
||
testData = {
|
||
patientData: {
|
||
name: '测试患者',
|
||
gender: '男',
|
||
age: '45岁',
|
||
orgname: '测试医院',
|
||
device: '测试设备',
|
||
recordStartTime: '2024-01-01 08:00:00',
|
||
recordEndTime: '2024-01-02 08:00:00',
|
||
recordDuration: '24小时',
|
||
successReadCount: '11',
|
||
successReadPercent: '95%',
|
||
maxSystolic: '145',
|
||
maxSystolicTime: '12:00',
|
||
minSystolic: '118',
|
||
minSystolicTime: '03:00',
|
||
avgSystolic: '133',
|
||
avgDiastolic: '81',
|
||
systolicVariability: '12.5%',
|
||
diastolicVariability: '10.2%',
|
||
wearTime: '2024-01-01'
|
||
},
|
||
chartDataTable: [
|
||
// 白天数据 (6:00-22:00)
|
||
{ originalTime: '2024-01-01 08:00:00', systolic: 130, diastolic: 80, heartRate: 70, avgBP: 97, status: '正常', suggestion: '继续保持' },
|
||
{ originalTime: '2024-01-01 10:00:00', systolic: 140, diastolic: 90, heartRate: 75, avgBP: 107, status: '轻度高血压', suggestion: '注意饮食' },
|
||
{ originalTime: '2024-01-01 12:00:00', systolic: 145, diastolic: 95, heartRate: 80, avgBP: 112, status: '轻度高血压', suggestion: '适量运动' },
|
||
{ originalTime: '2024-01-01 14:00:00', systolic: 135, diastolic: 85, heartRate: 72, avgBP: 102, status: '正常', suggestion: '继续保持' },
|
||
{ originalTime: '2024-01-01 16:00:00', systolic: 142, diastolic: 88, heartRate: 78, avgBP: 106, status: '轻度高血压', suggestion: '注意监测' },
|
||
{ originalTime: '2024-01-01 18:00:00', systolic: 138, diastolic: 82, heartRate: 74, avgBP: 101, status: '正常', suggestion: '继续保持' },
|
||
{ originalTime: '2024-01-01 20:00:00', systolic: 132, diastolic: 78, heartRate: 69, avgBP: 96, status: '正常', suggestion: '继续保持' },
|
||
|
||
// 夜间数据 (22:00-6:00)
|
||
{ originalTime: '2024-01-01 23:00:00', systolic: 125, diastolic: 75, heartRate: 65, avgBP: 92, status: '正常', suggestion: '继续保持' },
|
||
{ originalTime: '2024-01-02 01:00:00', systolic: 120, diastolic: 70, heartRate: 62, avgBP: 87, status: '正常', suggestion: '继续保持' },
|
||
{ originalTime: '2024-01-02 03:00:00', systolic: 118, diastolic: 68, heartRate: 60, avgBP: 85, status: '正常', suggestion: '继续保持' },
|
||
{ originalTime: '2024-01-02 05:00:00', systolic: 122, diastolic: 72, heartRate: 63, avgBP: 89, status: '正常', suggestion: '继续保持' }
|
||
],
|
||
analysisResult: '测试分析结果:患者血压控制基本正常,建议继续监测。'
|
||
};
|
||
}
|
||
|
||
console.log(`使用测试数据初始化报告 (${testType})`);
|
||
console.log('测试数据内容:', testData);
|
||
console.log('测试数据chartDataTable长度:', testData.chartDataTable.length);
|
||
initializeReport(testData);
|
||
}
|
||
|
||
// 页面加载完成后的初始化
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
showLoading(); // 页面初始显示loading
|
||
showDebugInfo('页面DOM加载完成');
|
||
console.log('ABPM报告模板页面加载完成');
|
||
|
||
// 向父窗口发送加载完成消息
|
||
if (window.opener) {
|
||
showDebugInfo('检测到父窗口,发送加载完成消息');
|
||
window.opener.postMessage({
|
||
type: 'ABPM_TEMPLATE_LOADED'
|
||
}, '*');
|
||
} else {
|
||
// 如果不是从其他窗口打开的,使用测试数据
|
||
showDebugInfo('独立打开页面,将在3秒后加载测试数据');
|
||
console.log('独立打开页面,将在3秒后加载测试数据');
|
||
console.log('测试说明:');
|
||
console.log('- 正常测试(包含白天+夜间数据):直接打开页面');
|
||
console.log('- 仅白天数据测试:在URL后添加 ?test=dayonly');
|
||
console.log('例如:file:///path/to/abpm-report-template.html?test=dayonly');
|
||
setTimeout(() => {
|
||
createTestData();
|
||
}, 3000);
|
||
}
|
||
});
|
||
|
||
// loading 遮罩控制
|
||
function showLoading() {
|
||
const mask = document.getElementById('loading-mask');
|
||
if (mask) mask.style.display = 'flex';
|
||
}
|
||
function hideLoading() {
|
||
const mask = document.getElementById('loading-mask');
|
||
if (mask) mask.style.display = 'none';
|
||
}
|
||
</script>
|
||
</body>
|
||
|
||
</html> |