调整样式
This commit is contained in:
parent
5a8d76288c
commit
9044ac01a0
@ -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);
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user