diff --git a/public/abpm-report-template.html b/public/abpm-report-template.html
index 8e49199..5f75da1 100644
--- a/public/abpm-report-template.html
+++ b/public/abpm-report-template.html
@@ -204,7 +204,7 @@
.scatter-chart-item .chart {
width: 100%;
- height: 320px;
+ height: 400px;
border: 1px solid #000;
}
@@ -910,7 +910,6 @@
@@ -1657,7 +1656,17 @@
// 计算血压数据的最大值(收缩压和舒张压合并考虑)
const bpValues = [...systolicValues, ...diastolicValues];
const bpMaxValue = getYAxisMax(bpValues, 20);
-
+ // 新增:计算刻度间隔为10、20、30、40
+ let bpInterval = 10;
+ if (bpMaxValue > 300) {
+ bpInterval = 40;
+ } else if (bpMaxValue > 200) {
+ bpInterval = 30;
+ } else if (bpMaxValue > 100) {
+ bpInterval = 20;
+ } else {
+ bpInterval = 10;
+ }
// 计算心率数据的最大值
const hrMaxValue = getYAxisMax(heartRateValues, 10);
@@ -1824,8 +1833,8 @@
axisLabel: {
formatter: (value) => {
const date = new Date(value);
- // 只在整点显示
- if (date.getMinutes() === 0) {
+ // 只在整点且为偶数小时显示
+ if (date.getMinutes() === 0 && date.getHours() % 2 === 0) {
return `${date.getHours().toString().padStart(2, '0')}`;
}
return '';
@@ -1859,7 +1868,7 @@
position: 'left',
min: 0,
max: bpMaxValue,
- interval: Math.max(10, Math.round(bpMaxValue / 10)),
+ interval: bpInterval,
axisLabel: {
color: '#666'
},
@@ -1989,60 +1998,86 @@
function renderHistogram(elementId, data, field, title) {
const chart = echarts.init(document.getElementById(elementId));
+ // 获取有效数据
+ const validData = data.filter(item => item[field] != null && !isNaN(item[field]));
+
+ if (validData.length === 0) {
+ // 如果没有有效数据,显示空图表
+ const option = {
+ title: { text: '暂无数据', left: 'center', top: 'middle' },
+ grid: { left: 50, right: 20, bottom: 40, top: 30 }
+ };
+ chart.setOption(option);
+ return;
+ }
+
+ // 计算数据的最小值和最大值
+ const values = validData.map(item => item[field]);
+ const minValue = Math.min(...values);
+ const maxValue = Math.max(...values);
+
let mmHgRanges, labels, counts, colors;
+
+ // 根据字段类型和实际数据范围动态生成区间
if (field === 'systolic') {
- mmHgRanges = [130, 140, 150, 160, 170, 180, 190];
+ // 收缩压:从实际最小值开始,以10为间隔,确保覆盖所有数据
+ const start = Math.floor(minValue / 10) * 10;
+ const end = Math.ceil(maxValue / 10) * 10;
+ mmHgRanges = [];
+ for (let i = start; i <= end; i += 10) {
+ mmHgRanges.push(i);
+ }
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];
+ // 舒张压:从实际最小值开始,以10为间隔,确保覆盖所有数据
+ const start = Math.floor(minValue / 10) * 10;
+ const end = Math.ceil(maxValue / 10) * 10;
+ mmHgRanges = [];
+ for (let i = start; i <= end; i += 10) {
+ mmHgRanges.push(i);
+ }
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];
+ // 心率:从实际最小值开始,以10为间隔,确保覆盖所有数据
+ const start = Math.floor(minValue / 10) * 10;
+ const end = Math.ceil(maxValue / 10) * 10;
+ mmHgRanges = [];
+ for (let i = start; i <= end; i += 10) {
+ mmHgRanges.push(i);
+ }
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]) {
+ colors = '#5bc0de';
+ }
+
+ // 初始化计数数组
+ counts = Array(mmHgRanges.length).fill(0);
+
+ // 统计每个区间的数据
+ validData.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]++;
+ break;
+ }
+ } else {
+ // 其他区间:[当前值, 下一个值)
+ if (value >= mmHgRanges[i] && value < mmHgRanges[i + 1]) {
counts[i]++;
break;
}
}
- });
- colors = '#5bc0de';
- }
- const total = data.length;
+ }
+ });
+ // 计算百分比
+ const total = validData.length;
const percentages = counts.map(count => total > 0 ? (count / total * 100).toFixed(1) : '0.0');
const option = {
@@ -2193,8 +2228,9 @@
const bpChart = echarts.init(document.getElementById('scatter-bp-relation'));
const bpData = chartDataTable.map(item => [item.diastolic, item.systolic]);
const bpRegression = calculateLinearRegression(bpData);
- const bpXArr = chartDataTable.map(item => item.diastolic).filter(v => v != null && !isNaN(v));
- const bpXAxisMax = getAxisMax(bpXArr);
+ // const bpXArr = chartDataTable.map(item => item.diastolic).filter(v => v != null && !isNaN(v));
+ // const bpXAxisMax = getAxisMax(bpXArr);
+ const bpXAxisMax = 110; // 固定X轴最大值为110
// 生成拟合线数据
const bpFitLineData = [];
for (let x = 40; x <= bpXAxisMax; x += 10) {
@@ -2220,8 +2256,11 @@
xAxis: {
type: 'value',
name: '舒张压(mmHg)',
+ nameTextStyle: {
+ color: '#000'
+ }, // 设置为黑色
min: 40,
- max: bpXAxisMax,
+ max: bpXAxisMax, // 固定为110
interval: 10,
minorTick: { show: true, splitNumber: 5 },
minorSplitLine: { show: false },
@@ -2229,13 +2268,17 @@
nameGap: 30,
axisLine: { show: true },
axisTick: { show: true },
- splitLine: { show: true, lineStyle: { color: '#e0e0e0' } }
+ splitLine: { show: false } // 隐藏竖线
},
yAxis: {
type: 'value',
name: '收缩压(mmHg)',
+ nameTextStyle: {
+ color: '#000' // 设置为黑色
+ },
min: 100,
- max: 200,
+ max: 180, // 固定为180
+ interval: 10, // 步长改为10
nameLocation: 'middle',
nameGap: 40,
axisLine: { show: true },
@@ -2269,8 +2312,9 @@
const hrChart = echarts.init(document.getElementById('scatter-hr-relation'));
const hrData = chartDataTable.map(item => [item.heartRate, item.systolic]);
const hrRegression = calculateLinearRegression(hrData);
- const hrXArr = chartDataTable.map(item => item.heartRate).filter(v => v != null && !isNaN(v));
- const hrXAxisMax = getAxisMax(hrXArr);
+ // const hrXArr = chartDataTable.map(item => item.heartRate).filter(v => v != null && !isNaN(v));
+ // const hrXAxisMax = getAxisMax(hrXArr);
+ const hrXAxisMax = 110; // 固定X轴最大值为110
// 生成拟合线数据
const hrFitLineData = [];
for (let x = 40; x <= hrXAxisMax; x += 10) {
@@ -2296,8 +2340,11 @@
xAxis: {
type: 'value',
name: '心率(次/分)',
+ nameTextStyle: {
+ color: '#000' // 设置为黑色
+ },
min: 40,
- max: hrXAxisMax,
+ max: hrXAxisMax, // 固定为110
interval: 10,
minorTick: { show: true, splitNumber: 5 },
minorSplitLine: { show: false },
@@ -2305,13 +2352,17 @@
nameGap: 30,
axisLine: { show: true },
axisTick: { show: true },
- splitLine: { show: true, lineStyle: { color: '#e0e0e0' } }
+ splitLine: { show: false } // 隐藏竖线
},
yAxis: {
type: 'value',
name: '收缩压(mmHg)',
+ nameTextStyle: {
+ color: '#000' // 设置为黑色
+ },
min: 100,
- max: 200,
+ max: 180, // 固定为180
+ interval: 10, // 步长改为10
nameLocation: 'middle',
nameGap: 40,
axisLine: { show: true },
@@ -2346,77 +2397,148 @@
// 渲染小时平均图
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 };
});
-
+ // 计算Y轴最大值和间隔(与第一页一致)
+ const systolicArr = hourlyData.map(d => d.systolic).filter(v => v != null && !isNaN(v));
+ const diastolicArr = hourlyData.map(d => d.diastolic).filter(v => v != null && !isNaN(v));
+ const heartRateArr = hourlyData.map(d => d.heartRate).filter(v => v != null && !isNaN(v));
+ const bpValues = [...systolicArr, ...diastolicArr];
+ const getYAxisMax = (arr, step = 20) => {
+ if (!arr || arr.length === 0) return step === 20 ? 200 : 120;
+ const validValues = arr.filter(v => v != null && !isNaN(v));
+ if (validValues.length === 0) return step === 20 ? 200 : 120;
+ const max = Math.max(...validValues);
+ return Math.ceil(max / step) * step;
+ };
+ const bpMaxValue = getYAxisMax(bpValues, 20);
+ let bpInterval = 10;
+ if (bpMaxValue > 300) {
+ bpInterval = 40;
+ } else if (bpMaxValue > 200) {
+ bpInterval = 30;
+ } else if (bpMaxValue > 100) {
+ bpInterval = 20;
+ } else {
+ bpInterval = 10;
+ }
+ const hrMaxValue = getYAxisMax(heartRateArr, 10);
+ const hrMinValue = (() => {
+ if (!heartRateArr.length) return 50;
+ const minHR = Math.min(...heartRateArr);
+ return Math.max(0, Math.floor(minHR / 10) * 10 - 10);
+ })();
+ // X轴只显示偶数小时
+ const xAxisLabels = Array.from({ length: 24 }, (_, i) => (i % 2 === 0 ? String(i).padStart(2, '0') : ''));
const option = {
- tooltip: { trigger: 'axis' },
- legend: { data: ['收缩压', '舒张压', '心率'], bottom: 0 },
+ grid: { left: 50, right: 50, bottom: 60, top: 50, containLabel: true },
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: { type: 'cross' },
+ backgroundColor: 'rgba(50,50,50,0.7)',
+ borderWidth: 0,
+ textStyle: { color: '#fff' },
+ formatter: function(params) {
+ if (params && params.length > 0) {
+ let html = '';
+ params.forEach((param) => {
+ html += `${param.seriesName}: ${param.data || '--'}
`;
+ if (param.seriesIndex === 0) {
+ html += `小时: ${param.axisValue}
`;
+ }
+ });
+ return html;
+ }
+ return '';
+ }
+ },
+ legend: { data: ['收缩压', '舒张压', '心率'], top: 10, left: 'center' },
xAxis: {
type: 'category',
- data: Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0'))
+ data: Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0')),
+ axisLabel: {
+ formatter: (value) => {
+ const hour = parseInt(value, 10);
+ return hour % 2 === 0 ? value : '';
+ },
+ color: '#666'
+ },
+ axisLine: { lineStyle: { color: '#e0e0e0' } },
+ axisTick: { show: false },
+ splitLine: { show: false }
},
yAxis: [
- { type: 'value', name: 'mmHg', position: 'left', min: 0, max: 200 },
- { type: 'value', name: '次/分', position: 'right', min: 50, max: 120 }
+ {
+ type: 'value',
+ name: 'mmHg',
+ nameTextStyle: { color: '#000' },
+ position: 'left',
+ min: 0,
+ max: bpMaxValue,
+ interval: bpInterval,
+ axisLabel: { color: '#666' },
+ axisLine: { lineStyle: { color: '#e0e0e0' } },
+ splitLine: { show: true, lineStyle: { color: '#f0f0f0', type: 'dashed' } }
+ },
+ {
+ type: 'value',
+ name: '次/分',
+ nameTextStyle: { color: '#000' },
+ position: 'right',
+ min: hrMinValue,
+ max: hrMaxValue,
+ interval: Math.max(5, Math.round(hrMaxValue / 10)),
+ axisLabel: { color: '#666' },
+ axisLine: { lineStyle: { color: '#e0e0e0' } },
+ splitLine: { show: false }
+ }
],
series: [
{
name: '收缩压',
type: 'line',
- smooth: true,
symbol: 'circle',
- symbolSize: 6, // 增加点的大小
+ symbolSize: 6,
+ lineStyle: { width: 2 },
+ connectNulls: false,
+ smooth: false,
data: hourlyData.map(d => d.systolic),
- itemStyle: {
- color: '#409eff',
- borderWidth: 1,
- borderColor: '#fff' // 添加白色边框使点更明显
- }
+ itemStyle: { color: '#409eff', borderWidth: 1, borderColor: '#fff' }
},
{
name: '舒张压',
type: 'line',
- smooth: true,
symbol: 'circle',
- symbolSize: 6, // 增加点的大小
+ symbolSize: 6,
+ lineStyle: { width: 2 },
+ connectNulls: false,
+ smooth: false,
data: hourlyData.map(d => d.diastolic),
- itemStyle: {
- color: '#67c23a',
- borderWidth: 1,
- borderColor: '#fff' // 添加白色边框使点更明显
- }
+ itemStyle: { color: '#67c23a', borderWidth: 1, borderColor: '#fff' }
},
{
name: '心率',
type: 'line',
- smooth: true,
symbol: 'circle',
- symbolSize: 6, // 增加点的大小
+ symbolSize: 6,
+ lineStyle: { width: 2 },
+ connectNulls: false,
+ smooth: false,
yAxisIndex: 1,
data: hourlyData.map(d => d.heartRate),
- itemStyle: {
- color: '#e6a23c',
- borderWidth: 1,
- borderColor: '#fff' // 添加白色边框使点更明显
- }
+ itemStyle: { color: '#e6a23c', borderWidth: 1, borderColor: '#fff' }
}
]
};
-
chart.setOption(option);
}