教练端在学生报告页录入体质数据并生成报告,新增报告查看页;学员端从报告页查看最新评估结果、体测指标与运动建议。请求统一走现有 QA 后端并携带登录令牌。 教学资源改用后台音频管理器,支持进度拖动、曲目切换、时长探测及网络异常时下载恢复;修正跳绳和开合跳动作提示文案。 验证:相关 JS 语法、页面 JSON 解析和 git diff --cached --check 均通过。
151 lines
5.7 KiB
JavaScript
151 lines
5.7 KiB
JavaScript
import { generatePostureReport } from '../../../utils/serve/posture';
|
|
|
|
const bodyFields = [
|
|
{ key: 'heightCm', label: '身高', unit: 'cm' },
|
|
{ key: 'weightKg', label: '体重', unit: 'kg' },
|
|
{ key: 'waistCm', label: '腰围', unit: 'cm' },
|
|
{ key: 'bodyFatPercent', label: '体脂率', unit: '%' },
|
|
{ key: 'muscleMassKg', label: '肌肉量', unit: 'kg' },
|
|
];
|
|
|
|
const testFields = [
|
|
{ key: 'sprint50mSeconds', label: '50米跑', unit: '秒' },
|
|
{ key: 'standingLongJumpCm', label: '立定跳远', unit: 'cm' },
|
|
{ key: 'plankSeconds', label: '平板支撑', unit: '秒' },
|
|
{ key: 'gripStrengthKg', label: '握力', unit: 'kg' },
|
|
{ key: 'ropeSkipCount', label: '一分钟跳绳', unit: '个' },
|
|
{ key: 'ropeSkip30SecCount', label: '30秒跳绳', unit: '个' },
|
|
{ key: 'vitalCapacityMl', label: '肺活量', unit: 'ml' },
|
|
{ key: 'restingHeartRateBpm', label: '静息心率', unit: '次/分' },
|
|
{ key: 'stepTestHeartRateBpm', label: '台阶测试恢复心率', unit: '次/分' },
|
|
{ key: 'obstacleRun15mSeconds', label: '15米绕障碍跑', unit: '秒' },
|
|
{ key: 'quadrantJumpSeconds', label: '十字象限跳', unit: '秒' },
|
|
{ key: 'reactionTimeSeconds', label: '选择反应时', unit: '秒' },
|
|
{ key: 'balanceBeamSeconds', label: '走平衡木', unit: '秒' },
|
|
{ key: 'singleLegStandSeconds', label: '闭眼单脚站立', unit: '秒' },
|
|
{ key: 'sitReachCm', label: '坐位体前屈', unit: 'cm' },
|
|
];
|
|
|
|
const allFields = bodyFields.concat(testFields);
|
|
const today = () => {
|
|
const date = new Date();
|
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
|
};
|
|
|
|
function normalizeSex(value) {
|
|
const text = String(value || '').toLowerCase();
|
|
if (text === '1' || text === '男' || text === 'male') return 0;
|
|
if (text === '2' || text === '女' || text === 'female') return 1;
|
|
return -1;
|
|
}
|
|
|
|
Page({
|
|
data: {
|
|
studentNo: '',
|
|
studentName: '',
|
|
className: '',
|
|
birthDate: '',
|
|
testDate: today(),
|
|
sexIndex: -1,
|
|
sexOptions: ['男', '女'],
|
|
gradeIndex: -1,
|
|
gradeOptions: ['一年级', '二年级', '三年级', '四年级', '五年级', '六年级', '七年级', '八年级', '九年级', '高一', '高二', '高三'],
|
|
bodyFields,
|
|
testFields,
|
|
values: {},
|
|
submitting: false,
|
|
},
|
|
|
|
onLoad(options) {
|
|
const gradeId = Number(decodeURIComponent(options.gradeId || ''));
|
|
this.setData({
|
|
studentNo: decodeURIComponent(options.studentNo || ''),
|
|
studentName: decodeURIComponent(options.studentName || ''),
|
|
className: decodeURIComponent(options.className || ''),
|
|
sexIndex: normalizeSex(decodeURIComponent(options.sex || '')),
|
|
gradeIndex: Number.isInteger(gradeId) && gradeId >= 1 && gradeId <= 12 ? gradeId - 1 : -1,
|
|
});
|
|
},
|
|
|
|
onBirthDateChange(e) { this.setData({ birthDate: e.detail.value }); },
|
|
onTestDateChange(e) { this.setData({ testDate: e.detail.value }); },
|
|
onSexChange(e) { this.setData({ sexIndex: Number(e.detail.value) }); },
|
|
onGradeChange(e) { this.setData({ gradeIndex: Number(e.detail.value) }); },
|
|
|
|
onNumberInput(e) {
|
|
const key = e.currentTarget.dataset.key;
|
|
this.setData({ [`values.${key}`]: e.detail.value });
|
|
},
|
|
|
|
buildPayload() {
|
|
const { studentNo, studentName, className, birthDate, testDate, sexIndex, gradeIndex, values } = this.data;
|
|
if (!studentNo || !studentName || !birthDate || !testDate || sexIndex < 0 || gradeIndex < 0) {
|
|
throw new Error('请补齐出生日期、测试日期、性别和年级');
|
|
}
|
|
if (testDate < birthDate) throw new Error('测试日期不能早于出生日期');
|
|
|
|
const numbers = {};
|
|
allFields.forEach(field => {
|
|
const raw = values[field.key];
|
|
if (raw === undefined || String(raw).trim() === '') return;
|
|
const value = Number(raw);
|
|
if (!Number.isFinite(value) || (field.key !== 'sitReachCm' && value <= 0)) {
|
|
throw new Error(`请检查${field.label}的数值`);
|
|
}
|
|
numbers[field.key] = value;
|
|
});
|
|
if (!numbers.heightCm || !numbers.weightKg) throw new Error('请填写身高和体重');
|
|
|
|
const tests = {};
|
|
testFields.forEach(field => {
|
|
if (numbers[field.key] !== undefined) tests[field.key] = numbers[field.key];
|
|
});
|
|
return {
|
|
assessmentType: 'student',
|
|
studentName,
|
|
studentId: studentNo,
|
|
birthDate,
|
|
testDate,
|
|
sex: sexIndex === 0 ? 'male' : 'female',
|
|
grade: String(gradeIndex + 1),
|
|
className,
|
|
heightCm: numbers.heightCm,
|
|
weightKg: numbers.weightKg,
|
|
...(numbers.waistCm !== undefined ? { waistCm: numbers.waistCm } : {}),
|
|
bodyComposition: {
|
|
...(numbers.bodyFatPercent !== undefined ? { bodyFatPercent: numbers.bodyFatPercent } : {}),
|
|
...(numbers.muscleMassKg !== undefined ? { muscleMassKg: numbers.muscleMassKg } : {}),
|
|
},
|
|
tests,
|
|
};
|
|
},
|
|
|
|
async submit() {
|
|
if (this.data.submitting) return;
|
|
let payload;
|
|
try {
|
|
payload = this.buildPayload();
|
|
} catch (e) {
|
|
wx.showToast({ title: e.message, icon: 'none' });
|
|
return;
|
|
}
|
|
this.setData({ submitting: true });
|
|
wx.showLoading({ title: '生成报告中', mask: true });
|
|
try {
|
|
const response = await generatePostureReport(this.data.studentNo, payload);
|
|
if (!response.data || !response.data.success || !response.data.result) {
|
|
throw new Error('评价服务未返回有效报告');
|
|
}
|
|
wx.hideLoading();
|
|
wx.redirectTo({
|
|
url: '/subpackage/parent/posture-report/posture-report?mode=teacher&studentNo=' + encodeURIComponent(this.data.studentNo),
|
|
});
|
|
} catch (e) {
|
|
wx.hideLoading();
|
|
wx.showModal({ title: '生成失败', content: e.message || '请稍后重试', showCancel: false });
|
|
} finally {
|
|
this.setData({ submitting: false });
|
|
}
|
|
},
|
|
});
|