t
This commit is contained in:
@@ -1,45 +1,106 @@
|
|||||||
const app = getApp();
|
const app = getApp();
|
||||||
const math = require('../../utils/math.js');
|
|
||||||
|
// 添加正态分布相关的数学函数
|
||||||
|
const normalDistribution = {
|
||||||
|
// 误差函数的近似计算
|
||||||
|
erf(x) {
|
||||||
|
// 误差函数的系数
|
||||||
|
const a1 = 0.254829592;
|
||||||
|
const a2 = -0.284496736;
|
||||||
|
const a3 = 1.421413741;
|
||||||
|
const a4 = -1.453152027;
|
||||||
|
const a5 = 1.061405429;
|
||||||
|
const p = 0.3275911;
|
||||||
|
|
||||||
|
// 取绝对值
|
||||||
|
const sign = (x >= 0) ? 1 : -1;
|
||||||
|
x = Math.abs(x);
|
||||||
|
|
||||||
|
// 误差函数近似公式的计算
|
||||||
|
const t = 1.0 / (1.0 + p * x);
|
||||||
|
const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);
|
||||||
|
|
||||||
|
return sign * y;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 计算百分位数
|
||||||
|
calculatePercentile(sds) {
|
||||||
|
return (1 + this.erf(sds / Math.sqrt(2))) / 2 * 100;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
|
// 基础数据
|
||||||
age: '',
|
age: '',
|
||||||
height: '',
|
height: '',
|
||||||
gender: 'male',
|
gender: 'male',
|
||||||
|
// 计算结果
|
||||||
showResult: false,
|
showResult: false,
|
||||||
sdsValue: null,
|
sdsValue: null,
|
||||||
percentile: null,
|
calculationDetails: null,
|
||||||
growthEvaluation: '',
|
growthEvaluation: '',
|
||||||
chartData: null
|
// 参考数据
|
||||||
|
standardData: {
|
||||||
|
male: [
|
||||||
|
{ age: 1, mean: 76.5, sd: 3.0 },
|
||||||
|
{ age: 2, mean: 88.5, sd: 3.5 },
|
||||||
|
{ age: 3, mean: 96.8, sd: 3.8 },
|
||||||
|
{ age: 4, mean: 103.5, sd: 4.2 },
|
||||||
|
{ age: 5, mean: 110.0, sd: 4.5 },
|
||||||
|
{ age: 6, mean: 116.5, sd: 4.8 },
|
||||||
|
{ age: 7, mean: 124.0, sd: 5.0 },
|
||||||
|
{ age: 8, mean: 129.5, sd: 5.2 },
|
||||||
|
{ age: 9, mean: 134.5, sd: 5.5 },
|
||||||
|
{ age: 10, mean: 138.5, sd: 5.8 },
|
||||||
|
{ age: 11, mean: 143.0, sd: 6.0 },
|
||||||
|
{ age: 12, mean: 150.0, sd: 6.5 },
|
||||||
|
{ age: 13, mean: 156.5, sd: 7.0 },
|
||||||
|
{ age: 14, mean: 163.0, sd: 7.2 },
|
||||||
|
{ age: 15, mean: 168.0, sd: 6.2 },
|
||||||
|
{ age: 16, mean: 171.5, sd: 5.8 },
|
||||||
|
{ age: 17, mean: 173.5, sd: 5.5 },
|
||||||
|
{ age: 18, mean: 174.5, sd: 5.2 }
|
||||||
|
],
|
||||||
|
female: [
|
||||||
|
{ age: 1, mean: 75.0, sd: 2.9 },
|
||||||
|
{ age: 2, mean: 87.2, sd: 3.4 },
|
||||||
|
{ age: 3, mean: 95.6, sd: 3.7 },
|
||||||
|
{ age: 4, mean: 102.5, sd: 4.0 },
|
||||||
|
{ age: 5, mean: 108.5, sd: 4.3 },
|
||||||
|
{ age: 6, mean: 115.0, sd: 4.6 },
|
||||||
|
{ age: 7, mean: 122.5, sd: 4.8 },
|
||||||
|
{ age: 8, mean: 127.5, sd: 5.0 },
|
||||||
|
{ age: 9, mean: 132.5, sd: 5.2 },
|
||||||
|
{ age: 10, mean: 137.0, sd: 5.5 },
|
||||||
|
{ age: 11, mean: 142.0, sd: 5.8 },
|
||||||
|
{ age: 12, mean: 148.0, sd: 6.0 },
|
||||||
|
{ age: 13, mean: 154.0, sd: 6.2 },
|
||||||
|
{ age: 14, mean: 158.5, sd: 5.8 },
|
||||||
|
{ age: 15, mean: 160.5, sd: 5.5 },
|
||||||
|
{ age: 16, mean: 161.5, sd: 5.2 },
|
||||||
|
{ age: 17, mean: 162.0, sd: 5.0 },
|
||||||
|
{ age: 18, mean: 162.5, sd: 4.8 }
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 输入年龄
|
// 输入处理
|
||||||
inputAge(e) {
|
inputAge(e) {
|
||||||
this.setData({
|
this.setData({
|
||||||
age: Number(e.detail.value) // 确保age是数字
|
age: Number(e.detail.value)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 输入身高
|
|
||||||
inputHeight(e) {
|
inputHeight(e) {
|
||||||
this.setData({
|
this.setData({
|
||||||
height: Number(e.detail.value) // 确保height是数字
|
height: Number(e.detail.value)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 选择性别
|
|
||||||
selectGender(e) {
|
selectGender(e) {
|
||||||
const gender = e.currentTarget.dataset.gender;
|
|
||||||
this.setData({ gender });
|
|
||||||
},
|
|
||||||
|
|
||||||
// 重置表单
|
|
||||||
reset() {
|
|
||||||
this.setData({
|
this.setData({
|
||||||
age: '',
|
gender: e.currentTarget.dataset.gender
|
||||||
height: '',
|
|
||||||
gender: 'male',
|
|
||||||
showResult: false
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -48,53 +109,45 @@ Page({
|
|||||||
const { age, height, gender } = this.data;
|
const { age, height, gender } = this.data;
|
||||||
|
|
||||||
// 输入验证
|
// 输入验证
|
||||||
if (!age || !height) {
|
if (!this.validateInput(age, height)) return;
|
||||||
wx.showToast({
|
|
||||||
title: '请填写完整信息',
|
try {
|
||||||
icon: 'none'
|
// 获取标准数据
|
||||||
});
|
const standardData = this.getStandardData(age, gender);
|
||||||
return;
|
if (!standardData) {
|
||||||
|
throw new Error('未找到对应年龄的标准数据');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 直接使用本地生长数据
|
// 计算SDS
|
||||||
this.useLocalGrowthData();
|
const sds = this.calculateSDS(
|
||||||
},
|
Number(height),
|
||||||
|
Number(standardData.mean),
|
||||||
|
Number(standardData.sd)
|
||||||
|
);
|
||||||
|
|
||||||
// 处理生长数据
|
// 计算中间值,用于显示
|
||||||
processGrowthData(growthData) {
|
const difference = Number(height) - Number(standardData.mean);
|
||||||
try {
|
|
||||||
const { age, height, gender } = this.data;
|
|
||||||
|
|
||||||
// 计算SDS值并获取标准数据
|
// 获取评价
|
||||||
const { sds, standard } = this.calculateSDS(height, age, gender, growthData);
|
|
||||||
console.log('SDS计算详情:', {
|
|
||||||
height,
|
|
||||||
age,
|
|
||||||
gender,
|
|
||||||
mean: standard.mean,
|
|
||||||
sd: standard.sd,
|
|
||||||
sds
|
|
||||||
});
|
|
||||||
|
|
||||||
// 显示计算公式
|
|
||||||
console.log(`计算公式:SDS = (${height} - ${standard.mean}) / ${standard.sd} = ${sds.toFixed(2)}`);
|
|
||||||
|
|
||||||
// 计算百分位数
|
|
||||||
const percentile = this.calculatePercentile(sds);
|
|
||||||
|
|
||||||
// 获取生长评价
|
|
||||||
const evaluation = this.getGrowthEvaluation(sds);
|
const evaluation = this.getGrowthEvaluation(sds);
|
||||||
|
|
||||||
// 更新结果
|
// 显示结果
|
||||||
this.setData({
|
this.setData({
|
||||||
showResult: true,
|
showResult: true,
|
||||||
sdsValue: Number(sds.toFixed(2)), // 确保sdsValue是数字类型
|
sdsValue: Number(sds.toFixed(2)),
|
||||||
percentile: percentile,
|
calculationDetails: {
|
||||||
|
height: Number(height).toFixed(1),
|
||||||
|
mean: Number(standardData.mean).toFixed(1),
|
||||||
|
sd: Number(standardData.sd).toFixed(1),
|
||||||
|
formula: `身高SDS = (${height} - ${standardData.mean}) / ${standardData.sd}`,
|
||||||
|
calculation: ` = (${height} - ${standardData.mean}) / ${standardData.sd}`,
|
||||||
|
difference: ` = ${difference.toFixed(1)} / ${standardData.sd}`,
|
||||||
|
result: ` = ${sds.toFixed(2)}`,
|
||||||
|
interpretation: this.getInterpretation(sds)
|
||||||
|
},
|
||||||
growthEvaluation: evaluation
|
growthEvaluation: evaluation
|
||||||
});
|
});
|
||||||
|
|
||||||
// 绘制生长曲线
|
|
||||||
this.drawGrowthChart(growthData);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
wx.showToast({
|
wx.showToast({
|
||||||
title: error.message || '计算失败',
|
title: error.message || '计算失败',
|
||||||
@@ -103,70 +156,118 @@ Page({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 使用本地生长数据
|
// 输入验证
|
||||||
useLocalGrowthData() {
|
validateInput(age, height) {
|
||||||
try {
|
if (!age || !height) {
|
||||||
const localData = require('./growthData.js');
|
|
||||||
// 确保数据类型一致
|
|
||||||
const processedData = localData.map(item => ({
|
|
||||||
...item,
|
|
||||||
age: Number(item.age)
|
|
||||||
}));
|
|
||||||
this.processGrowthData(processedData);
|
|
||||||
} catch (error) {
|
|
||||||
wx.showToast({
|
wx.showToast({
|
||||||
title: '本地数据加载失败',
|
title: '请填写完整信息',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
});
|
});
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (age < 1 || age > 18) {
|
||||||
|
wx.showToast({
|
||||||
|
title: '年龄必须在1-18岁之间',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (height <= 0) {
|
||||||
|
wx.showToast({
|
||||||
|
title: '身高必须大于0',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取标准数据
|
||||||
|
getStandardData(age, gender) {
|
||||||
|
return this.data.standardData[gender].find(item => item.age === age);
|
||||||
},
|
},
|
||||||
|
|
||||||
// 计算SDS值
|
// 计算SDS值
|
||||||
calculateSDS(height, age, gender, growthData) {
|
calculateSDS(height, mean, sd) {
|
||||||
// 根据年龄和性别获取标准值
|
// 输入验证
|
||||||
const standard = growthData.find(d =>
|
if (typeof height !== 'number' || typeof mean !== 'number' || typeof sd !== 'number') {
|
||||||
Number(d.age) === Number(age) &&
|
throw new Error('输入参数必须为数字');
|
||||||
d.gender === gender
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!standard) {
|
|
||||||
throw new Error('未找到对应年龄和性别的标准数据');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SDS = (实际值 - 平均值) / 标准差
|
// SDS = (个体身高 - 平均身高) / 标准差
|
||||||
const sds = (height - standard.mean) / standard.sd;
|
const sds = (height - mean) / sd;
|
||||||
return { sds, standard };
|
|
||||||
},
|
|
||||||
|
|
||||||
// 计算百分位数
|
// 验证计算结果
|
||||||
calculatePercentile(sds) {
|
if (isNaN(sds) || !isFinite(sds)) {
|
||||||
// 使用标准正态分布计算百分位数
|
throw new Error('计算结果无效');
|
||||||
const percentile = 100 * (1 + math.erf(sds / Math.sqrt(2))) / 2;
|
}
|
||||||
return percentile.toFixed(1);
|
|
||||||
|
// 添加计算过程日志
|
||||||
|
console.log('SDS计算过程:', {
|
||||||
|
个体身高: height.toFixed(1),
|
||||||
|
平均身高: mean.toFixed(1),
|
||||||
|
标准差: sd.toFixed(1),
|
||||||
|
计算公式: `(${height.toFixed(1)} - ${mean.toFixed(1)}) / ${sd.toFixed(1)}`,
|
||||||
|
计算结果: sds.toFixed(2)
|
||||||
|
});
|
||||||
|
|
||||||
|
return sds;
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取生长评价
|
// 获取生长评价
|
||||||
getGrowthEvaluation(sds) {
|
getGrowthEvaluation(sds) {
|
||||||
|
// 根据标准更新评价标准
|
||||||
if (sds < -2) {
|
if (sds < -2) {
|
||||||
return '生长迟缓';
|
return '可能存在生长迟缓';
|
||||||
} else if (sds >= -2 && sds <= 2) {
|
} else if (sds > 2) {
|
||||||
return '正常范围';
|
return '可能存在生长过快';
|
||||||
} else {
|
} else {
|
||||||
return '生长过快';
|
return '正常范围';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 绘制生长曲线
|
// 重置表单
|
||||||
drawGrowthChart(growthData) {
|
reset() {
|
||||||
const ctx = wx.createCanvasContext('growthChart');
|
this.setData({
|
||||||
|
age: '',
|
||||||
// 绘制逻辑...
|
height: '',
|
||||||
|
gender: 'male',
|
||||||
ctx.draw();
|
showResult: false,
|
||||||
|
sdsValue: null,
|
||||||
|
calculationDetails: null,
|
||||||
|
growthEvaluation: ''
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 返回
|
// 返回上一页
|
||||||
onClickLeft() {
|
onClickLeft() {
|
||||||
wx.navigateBack();
|
wx.navigateBack();
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改计算百分位数的方法
|
||||||
|
calculatePercentile(sds) {
|
||||||
|
const percentile = normalDistribution.calculatePercentile(sds);
|
||||||
|
return percentile.toFixed(1);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取解释文本
|
||||||
|
getInterpretation(sds) {
|
||||||
|
const percentile = this.calculatePercentile(sds);
|
||||||
|
let interpretation = `身高SDS值:${sds.toFixed(2)}\n`;
|
||||||
|
interpretation += `该儿童身高位于同年龄同性别儿童的第${percentile}百分位\n`;
|
||||||
|
|
||||||
|
// 根据SDS值添加解释
|
||||||
|
if (sds === 0) {
|
||||||
|
interpretation += '身高等于平均值';
|
||||||
|
} else if (sds > 0) {
|
||||||
|
interpretation += `高于平均值${(Math.abs(sds) * 100).toFixed(1)}%`;
|
||||||
|
} else {
|
||||||
|
interpretation += `低于平均值${(Math.abs(sds) * 100).toFixed(1)}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return interpretation;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -61,21 +61,20 @@
|
|||||||
<view class="result-container" wx:if="{{showResult}}">
|
<view class="result-container" wx:if="{{showResult}}">
|
||||||
<text class="result-title">计算结果</text>
|
<text class="result-title">计算结果</text>
|
||||||
|
|
||||||
<view class="result-item">
|
<!-- 计算过程 -->
|
||||||
<text class="result-label">身高SDS值</text>
|
<view class="calculation-process">
|
||||||
<text class="result-value {{sdsValue >= 0 ? 'green' : 'red'}}">
|
<text class="process-title">计算过程:</text>
|
||||||
{{sdsValue.toFixed(2)}}
|
<view class="process-steps">
|
||||||
</text>
|
<text>{{calculationDetails.formula}}</text>
|
||||||
|
<text>{{calculationDetails.calculation}}</text>
|
||||||
|
<text>{{calculationDetails.difference}}</text>
|
||||||
|
<text>{{calculationDetails.result}}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="result-item">
|
<!-- 结果解释 -->
|
||||||
<text class="result-label">百分位数</text>
|
<view class="result-interpretation">
|
||||||
<text class="result-value">{{percentile}}%</text>
|
<text class="interpretation-text">{{calculationDetails.interpretation}}</text>
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="result-item">
|
|
||||||
<text class="result-label">生长评价</text>
|
|
||||||
<text class="result-value">{{growthEvaluation}}</text>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="tips">
|
<view class="tips">
|
||||||
|
|||||||
@@ -133,3 +133,43 @@
|
|||||||
margin-top: 20rpx;
|
margin-top: 20rpx;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.calculation-process {
|
||||||
|
margin: 20rpx 0;
|
||||||
|
padding: 20rpx;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-steps {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-steps text {
|
||||||
|
margin: 5rpx 0;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-interpretation {
|
||||||
|
margin: 20rpx 0;
|
||||||
|
padding: 20rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interpretation-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user