调整样式

This commit is contained in:
Flow 2025-07-18 17:40:31 +08:00
parent 5a8d76288c
commit 9044ac01a0

View File

@ -204,7 +204,7 @@
.scatter-chart-item .chart {
width: 100%;
height: 320px;
height: 400px;
border: 1px solid #000;
}
@ -910,7 +910,6 @@
</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>
@ -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 || '--'}<br/>`;
if (param.seriesIndex === 0) {
html += `小时: ${param.axisValue}<br/>`;
}
});
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);
}