/** * SarcoAdvisor-BSU 前端交互脚本 * 处理用户输入、API调用和结果显示 */ // 全局变量 let currentAssessment = null; let currentLanguage = 'zh'; // 默认中文 // DOM加载完成后初始化 document.addEventListener('DOMContentLoaded', function() { console.log('SarcoAdvisor-BSU 前端已加载'); // 初始化表单 initializeForm(); // 检查系统健康状态 checkSystemHealth(); // 添加输入验证 addInputValidation(); // 初始化语言 initializeLanguage(); // 初始化自动计算 initializeAutoCalculation(); // 初始化PAQ问卷逻辑 initializePAQLogic(); }); /** * 初始化表单 */ function initializeForm() { const form = document.getElementById('assessmentForm'); const submitBtn = document.getElementById('submitBtn'); // 添加表单提交事件监听 form.addEventListener('submit', handleFormSubmit); // 添加实时验证 const inputs = form.querySelectorAll('input[required], select[required]'); inputs.forEach(input => { input.addEventListener('blur', validateInput); input.addEventListener('input', updateSubmitButton); }); console.log('表单初始化完成'); } /** * 检查系统健康状态 */ async function checkSystemHealth() { try { const response = await fetch('/health'); const health = await response.json(); if (health.status === 'healthy') { console.log('系统状态正常:', health); } else { console.warn('系统状态异常:', health); const warningMsg = currentLanguage === 'zh' ? '系统部分功能可能不可用,但基础评估功能正常' : 'Some system functions may be unavailable, but basic assessment functions are normal'; showAlert(warningMsg, 'warning'); } } catch (error) { console.error('健康检查失败:', error); const errorMsg = currentLanguage === 'zh' ? '无法连接到服务器,请稍后重试' : 'Unable to connect to server, please try again later'; showAlert(errorMsg, 'danger'); } } /** * 处理表单提交 */ async function handleFormSubmit(event) { event.preventDefault(); console.log('开始处理表单提交'); // 验证表单 if (!validateForm()) { return; } // 收集表单数据 const formData = collectFormData(); // 显示加载状态 showLoading(); try { // 调用完整评估API const result = await performFullAssessment(formData); // 显示结果 displayResults(result); // 保存当前评估 currentAssessment = result; } catch (error) { console.error('评估失败:', error); hideLoading(); // 显示详细错误信息 let errorMessage = currentLanguage === 'zh' ? '评估过程中出现错误' : 'An error occurred during assessment'; if (error.message) { errorMessage += currentLanguage === 'zh' ? ':' + error.message : ': ' + error.message; } errorMessage += currentLanguage === 'zh' ? '。请检查输入数据后重试。' : '. Please check your input data and try again.'; showAlert(errorMessage, 'danger'); // 同时在控制台输出用户数据用于调试 console.error('失败的用户数据:', formData); } } /** * 验证表单 */ function validateForm() { const form = document.getElementById('assessmentForm'); const inputs = form.querySelectorAll('input[required], select[required]'); let isValid = true; inputs.forEach(input => { if (!validateInput({ target: input })) { isValid = false; } }); return isValid; } /** * 验证单个输入 */ function validateInput(event) { const input = event.target; const value = input.value; const type = input.type; const min = parseFloat(input.min); const max = parseFloat(input.max); let isValid = true; let errorMessage = ''; // 必填验证 if (input.required && !value) { isValid = false; errorMessage = currentLanguage === 'zh' ? '此字段为必填项' : 'This field is required'; } // 数值范围验证 if (type === 'number' && value) { const numValue = parseFloat(value); if (isNaN(numValue)) { isValid = false; errorMessage = currentLanguage === 'zh' ? '请输入有效数字' : 'Please enter a valid number'; } else if (!isNaN(min) && numValue < min) { isValid = false; errorMessage = currentLanguage === 'zh' ? `值不能小于 ${min}` : `Value cannot be less than ${min}`; } else if (!isNaN(max) && numValue > max) { isValid = false; errorMessage = currentLanguage === 'zh' ? `值不能大于 ${max}` : `Value cannot be greater than ${max}`; } } // 显示验证结果 showInputValidation(input, isValid, errorMessage); return isValid; } /** * 显示输入验证结果 */ function showInputValidation(input, isValid, errorMessage) { // 移除旧的验证状态 input.classList.remove('is-valid', 'is-invalid'); // 移除旧的错误信息 const oldFeedback = input.parentNode.querySelector('.invalid-feedback'); if (oldFeedback) { oldFeedback.remove(); } if (!isValid && errorMessage) { // 添加错误状态 input.classList.add('is-invalid'); // 添加错误信息 const feedback = document.createElement('div'); feedback.className = 'invalid-feedback'; feedback.textContent = errorMessage; input.parentNode.appendChild(feedback); } else if (input.value) { // 添加成功状态 input.classList.add('is-valid'); } } /** * 更新提交按钮状态 */ function updateSubmitButton() { const form = document.getElementById('assessmentForm'); const submitBtn = document.getElementById('submitBtn'); const requiredInputs = form.querySelectorAll('input[required], select[required]'); let allValid = true; requiredInputs.forEach(input => { if (!input.value || input.classList.contains('is-invalid')) { allValid = false; } }); submitBtn.disabled = !allValid; } /** * 收集表单数据并计算衍生特征 */ function collectFormData() { const rawData = {}; const form = document.getElementById('assessmentForm'); const formElements = form.elements; // 收集原始表单数据 for (let element of formElements) { if (element.name) { let value = element.value; // 处理空值 if (value === '' || value === null || value === undefined) { // 为必填字段提供默认值 if (element.hasAttribute('required')) { if (element.name === 'arthritis' || element.name === 'diabetes') { value = '0'; // 默认无疾病史 } else if (element.type === 'select-one' && element.name.startsWith('PAQ')) { value = '2'; // PAQ问题默认选择"否" } } } if (value !== '' && value !== null && value !== undefined) { // 转换数字类型 if (element.type === 'number') { value = parseFloat(value); } else if (element.type === 'select-one') { value = parseInt(value); } rawData[element.name] = value; } } } // 确保BMI和WWI有值(从自动计算获取) if (!rawData.body_mass_index) { const height = parseFloat(document.getElementById('height').value); const weight = parseFloat(document.getElementById('weight').value); if (height && weight) { const heightInMeters = height / 100; rawData.body_mass_index = parseFloat((weight / (heightInMeters * heightInMeters)).toFixed(1)); } } if (!rawData.WWI) { const waist = parseFloat(document.getElementById('waist').value); const weight = parseFloat(document.getElementById('weight').value); if (waist && weight) { rawData.WWI = parseFloat((waist / Math.sqrt(weight)).toFixed(2)); } } // 确保所有PAQ字段都有默认值(避免Pydantic验证错误) const requiredPAQFields = { // 必需的选择字段 'PAQ605': 2, 'PAQ620': 2, 'PAQ635': 2, 'PAQ650': 2, 'PAQ665': 2, // 可选的天数字段(默认0) 'PAQ610': 0, 'PAQ625': 0, 'PAQ640': 0, 'PAQ655': 0, 'PAQ670': 0, // 可选的时长字段(默认0) 'PAD615': 0, 'PAD630': 0, 'PAD645': 0, 'PAD660': 0, 'PAD675': 0, // 久坐时间(默认8小时) 'PAD680': 480, // 医疗史字段(默认否) 'arthritis': 0, 'diabetes': 0 }; // 为缺失的字段设置默认值 for (const [field, defaultValue] of Object.entries(requiredPAQFields)) { if (!(field in rawData)) { rawData[field] = defaultValue; } } // 使用PAD680作为sedentary_minutes rawData.sedentary_minutes = rawData.PAD680 || 480; // 计算衍生特征 const derivedFeatures = calculateDerivedFeatures(rawData); // 合并原始数据和衍生特征 const formData = { ...rawData, ...derivedFeatures }; console.log('收集的表单数据(含默认值):', formData); return formData; } /** * 根据NHANES PAQ数据计算衍生特征 * 完全按照create_pa_derived_features.py的逻辑实现 */ function calculateDerivedFeatures(data) { const derived = {}; // ===================================================== // A. 活动总量/剂量特征 (Activity Volume/Dose) // ===================================================== // A1. 每周总MET-分钟 (Total MET-minutes/week) const vigorousWorkMETs = 8.0 * (data.PAD615 || 0) * (data.PAQ610 || 0); const moderateWorkMETs = 4.0 * (data.PAD630 || 0) * (data.PAQ625 || 0); const transportationMETs = 4.0 * (data.PAD645 || 0) * (data.PAQ640 || 0); const vigorousRecMETs = 8.0 * (data.PAD660 || 0) * (data.PAQ655 || 0); const moderateRecMETs = 4.0 * (data.PAD675 || 0) * (data.PAQ670 || 0); const totalMETMinutesWeek = vigorousWorkMETs + moderateWorkMETs + transportationMETs + vigorousRecMETs + moderateRecMETs; // A2. 每周高强度活动总分钟数 (Total Vigorous Minutes/week) const vigorousWorkMins = (data.PAD615 || 0) * (data.PAQ610 || 0); const vigorousRecMins = (data.PAD660 || 0) * (data.PAQ655 || 0); const totalVigorousMinutesWeek = vigorousWorkMins + vigorousRecMins; // A3. 每周中等强度活动总分钟数 (Total Moderate Minutes/week) const moderateWorkMins = (data.PAD630 || 0) * (data.PAQ625 || 0); const transportationMins = (data.PAD645 || 0) * (data.PAQ640 || 0); const moderateRecMins = (data.PAD675 || 0) * (data.PAQ670 || 0); const totalModerateMinutesWeek = moderateWorkMins + transportationMins + moderateRecMins; // ===================================================== // B. 活动模式/行为特征 (Activity Pattern/Behavior) // ===================================================== // B1. 平均每次高强度活动时长 const totalVigorousDays = (data.PAQ610 || 0) + (data.PAQ655 || 0); const avgVigorousDurationPerBout = totalVigorousDays > 0 ? totalVigorousMinutesWeek / totalVigorousDays : 0; // B2. 平均每次中等强度活动时长 const totalModerateDays = (data.PAQ625 || 0) + (data.PAQ640 || 0) + (data.PAQ670 || 0); const avgModerateDurationPerBout = totalModerateDays > 0 ? totalModerateMinutesWeek / totalModerateDays : 0; // B3. 活动多样性指数 let activityDiversityIndex = 0; if ((data.PAQ605 || 0) > 0) activityDiversityIndex++; // 工作高强度 if ((data.PAQ620 || 0) > 0) activityDiversityIndex++; // 工作中等强度 if ((data.PAQ635 || 0) > 0) activityDiversityIndex++; // 交通活动 if ((data.PAQ650 || 0) > 0) activityDiversityIndex++; // 休闲高强度 if ((data.PAQ665 || 0) > 0) activityDiversityIndex++; // 休闲中等强度 // ===================================================== // C. 活动比例/构成特征 (Activity Ratio/Composition) // ===================================================== // C1. 高强度活动MET-分钟占比 const vigorousMETTotal = vigorousWorkMETs + vigorousRecMETs; const vigorousMETRatio = totalMETMinutesWeek > 0 ? vigorousMETTotal / totalMETMinutesWeek : 0; // C2. 活动/久坐比 const totalActiveMinutesWeek = totalVigorousMinutesWeek + totalModerateMinutesWeek; const totalSedentaryMinutesWeek = (data.sedentary_minutes || data.PAD680 || 480) * 7; // 每日久坐 × 7天 const activitySedentaryRatio = totalSedentaryMinutesWeek > 0 ? totalActiveMinutesWeek / totalSedentaryMinutesWeek : 0; // ===================================================== // D. 指南达标特征 (Guideline Adherence) // ===================================================== // D1. 每周中等强度等效总分钟数 const totalModerateEquivalentMinutes = totalModerateMinutesWeek + (2 * totalVigorousMinutesWeek); // D2. 是否达到WHO体力活动推荐量 const guidelineAdherenceBinary = totalModerateEquivalentMinutes >= 150 ? 1 : 0; // D3. 体力活动水平分级 let activityLevelCategorical; if (totalModerateEquivalentMinutes >= 300) { activityLevelCategorical = 3; // 非常活跃 } else if (totalModerateEquivalentMinutes >= 150) { activityLevelCategorical = 2; // 活跃 } else if (totalModerateEquivalentMinutes > 0) { activityLevelCategorical = 1; // 低度活跃 } else { activityLevelCategorical = 0; // 不活跃 } // ===================================================== // 整理最终衍生特征 // ===================================================== // A. 活动总量/剂量特征 derived.Total_MET_minutes_week = parseFloat(totalMETMinutesWeek.toFixed(2)); derived.Total_Vigorous_Minutes_week = parseFloat(totalVigorousMinutesWeek.toFixed(2)); derived.Total_Moderate_Minutes_week = parseFloat(totalModerateMinutesWeek.toFixed(2)); // B. 活动模式/行为特征 derived.Avg_Vigorous_Duration_Per_Bout = parseFloat(avgVigorousDurationPerBout.toFixed(2)); derived.Avg_Moderate_Duration_Per_Bout = parseFloat(avgModerateDurationPerBout.toFixed(2)); derived.Activity_Diversity_Index = activityDiversityIndex; // C. 活动比例/构成特征 derived.Vigorous_MET_Ratio = parseFloat(vigorousMETRatio.toFixed(3)); derived.Activity_Sedentary_Ratio = parseFloat(activitySedentaryRatio.toFixed(3)); // D. 指南达标特征 derived.Total_Moderate_Equivalent_Minutes = parseFloat(totalModerateEquivalentMinutes.toFixed(2)); derived.Guideline_Adherence_Binary = guidelineAdherenceBinary; derived.Activity_Level_Categorical = activityLevelCategorical; console.log('NHANES PAQ原始数据:', { PAQ610: data.PAQ610, PAD615: data.PAD615, PAQ625: data.PAQ625, PAD630: data.PAD630, PAQ640: data.PAQ640, PAD645: data.PAD645, PAQ655: data.PAQ655, PAD660: data.PAD660, PAQ670: data.PAQ670, PAD675: data.PAD675, PAD680: data.PAD680 }); console.log('计算的中间值:', {vigorousWorkMETs, moderateWorkMETs, transportationMETs, vigorousRecMETs, moderateRecMETs}); console.log('计算的时间总量:', {totalVigorousMinutesWeek, totalModerateMinutesWeek, totalActiveMinutesWeek}); console.log('最终衍生特征:', derived); return derived; } /** * 执行完整评估 */ async function performFullAssessment(userData) { console.log('开始完整评估...'); const response = await fetch('/api/full_assessment', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(userData) }); if (!response.ok) { let errorMessage = `HTTP ${response.status}: `; try { const errorData = await response.json(); console.error('服务器错误详情:', errorData); if (response.status === 422 && errorData.detail) { // 处理数据验证错误 if (Array.isArray(errorData.detail)) { const validationErrors = errorData.detail.map(err => `字段"${err.loc.join('.')}"错误: ${err.msg}` ).join('; '); errorMessage += validationErrors; } else { errorMessage += JSON.stringify(errorData.detail); } } else { errorMessage += errorData.detail || JSON.stringify(errorData); } } catch (parseError) { errorMessage += response.statusText || '服务器错误'; } throw new Error(errorMessage); } const result = await response.json(); console.log('评估结果:', result); return result; } /** * 显示加载状态 */ function showLoading() { document.getElementById('loadingSection').style.display = 'block'; document.getElementById('resultsSection').style.display = 'none'; // 滚动到加载区域 document.getElementById('loadingSection').scrollIntoView({ behavior: 'smooth' }); } /** * 隐藏加载状态 */ function hideLoading() { document.getElementById('loadingSection').style.display = 'none'; } /** * 显示评估结果 */ function displayResults(assessment) { hideLoading(); const resultsSection = document.getElementById('resultsSection'); const resultsContent = document.getElementById('resultsContent'); // 构建结果HTML const html = buildResultsHTML(assessment); resultsContent.innerHTML = html; // 显示结果区域 resultsSection.style.display = 'block'; resultsSection.classList.add('fade-in-up'); // 滚动到结果区域 resultsSection.scrollIntoView({ behavior: 'smooth' }); console.log('结果显示完成'); } /** * 构建结果HTML */ function buildResultsHTML(assessment) { const screening = assessment.screening; const advisory = assessment.advisory; const explanation = assessment.risk_explanation; let html = `

风险评估结果

综合风险评估: ${getRiskLevelText(screening.overall_risk)}
系统置信度: ${(screening.confidence * 100).toFixed(1)}%
详细评估结果
筛查阶段 (高召回率 - 不漏诊)
SarcoI 筛查
RandomForest 模型
风险等级: ${getRiskLevelText(screening.sarcoI_risk)}
肌少症特征相似度: ${(screening.sarcoI_probability * 100).toFixed(1)}% (筛查阈值: 50%)
Recall: 91.14%
Precision: 43.05%
SarcoII 筛查
CatBoost 模型
风险等级: ${getRiskLevelText(screening.sarcoII_risk)}
肌少症特征相似度: ${(screening.sarcoII_probability * 100).toFixed(1)}% (筛查阈值: 50%)
Precision: 25.48%
Recall: 89.83%
建议阶段 (高精确率 - 减少误诊)
SarcoI 建议
CatBoost 模型
风险等级: ${screening.sarcoI_advisory_risk ? getRiskLevelText(screening.sarcoI_advisory_risk) : '未评估'}
肌少症特征相似度: ${screening.sarcoI_advisory_probability ? (screening.sarcoI_advisory_probability * 100).toFixed(1) + '%' : 'N/A'} (建议阈值: 36%)
Precision: 高精确率
Recall: DiCE优化
SarcoII 建议
RandomForest 模型
风险等级: ${screening.sarcoII_advisory_risk ? getRiskLevelText(screening.sarcoII_advisory_risk) : '未评估'}
肌少症特征相似度: ${screening.sarcoII_advisory_probability ? (screening.sarcoII_advisory_probability * 100).toFixed(1) + '%' : 'N/A'} (建议阈值: 52%)
Precision: 高精确率
Recall: DiCE优化
${explanation.title}

${explanation.description}


建议: ${explanation.recommendation}

`; // 如果有个性化建议,显示建议内容 if (advisory && assessment.needs_advisory) { html += buildAdvisoryHTML(advisory); } // 添加处理时间信息 html += `
评估耗时: ${assessment.total_processing_time.toFixed(2)} 秒
`; return html; } /** * 构建建议HTML */ function buildAdvisoryHTML(advisory) { let html = `

个性化建议

`; // 优先级行动 if (advisory.priority_actions && advisory.priority_actions.length > 0) { html += `
优先建议
    ${advisory.priority_actions.map(action => `
  • ${action}
  • `).join('')}
`; } // SarcoI建议 if (advisory.sarcoI_recommendations && advisory.sarcoI_recommendations.length > 0) { html += `
SarcoI相关建议
${advisory.sarcoI_recommendations.map(rec => buildRecommendationCard(rec)).join('')}
`; } // SarcoII建议 if (advisory.sarcoII_recommendations && advisory.sarcoII_recommendations.length > 0) { html += `
SarcoII相关建议
${advisory.sarcoII_recommendations.map(rec => buildRecommendationCard(rec)).join('')}
`; } // 目标指标 if (advisory.target_metrics && Object.keys(advisory.target_metrics).length > 0) { html += `
目标指标
${Object.entries(advisory.target_metrics).map(([key, value]) => `
${key}: ${value}
`).join('')}
`; } // 降级提示 if (advisory.fallback_used) { html += `
部分建议基于规则生成,建议咨询专业医生获取更详细的个性化指导。
`; } return html; } /** * 构建单个建议卡片 */ function buildRecommendationCard(recommendation) { // 获取优先级文本 const getPriorityText = (priority, lang) => { const priorityTexts = { 'zh': { 'High': '高', 'Medium': '中', 'Low': '低' }, 'en': { 'High': 'High', 'Medium': 'Medium', 'Low': 'Low' } }; return priorityTexts[lang]?.[priority] || priority; }; return `
${recommendation.title}
${recommendation.description}
${recommendation.target_change ? `
${recommendation.target_change}
` : ''}
${getPriorityText(recommendation.priority, 'zh')}优先级 ${recommendation.expected_impact ? ` ${recommendation.expected_impact} ` : ''}
`; } /** * 获取风险等级文本 */ function getRiskLevelText(level, language = null) { const lang = language || currentLanguage; const levels = { 'zh': { 'low': '低风险', 'medium': '中等风险', 'high': '高风险' }, 'en': { 'low': 'Low Risk', 'medium': 'Medium Risk', 'high': 'High Risk' } }; return levels[lang]?.[level] || level; } /** * 显示警告信息 */ function showAlert(message, type = 'info') { const alertHTML = ` `; // 在表单上方插入警告 const form = document.getElementById('assessmentForm'); const alertContainer = document.createElement('div'); alertContainer.innerHTML = alertHTML; form.parentNode.insertBefore(alertContainer, form); // 5秒后自动关闭 setTimeout(() => { const alert = alertContainer.querySelector('.alert'); if (alert) { alert.remove(); } }, 5000); } /** * 获取警告图标 */ function getAlertIcon(type) { const icons = { 'info': 'info-circle', 'warning': 'exclamation-triangle', 'danger': 'exclamation-circle', 'success': 'check-circle' }; return icons[type] || 'info-circle'; } /** * 添加输入验证 */ function addInputValidation() { // 身高合理性检查 const heightInput = document.getElementById('height'); if (heightInput) { heightInput.addEventListener('input', function() { const height = parseFloat(this.value); if (height && (height < 120 || height > 220)) { const message = currentLanguage === 'zh' ? '身高数值请确认是否正确' : 'Please confirm if height value is correct'; showInputWarning(this, message); } else { hideInputWarning(this); } }); } // 体重合理性检查 const weightInput = document.getElementById('weight'); if (weightInput) { weightInput.addEventListener('input', function() { const weight = parseFloat(this.value); if (weight && (weight < 35 || weight > 150)) { const message = currentLanguage === 'zh' ? '体重数值请确认是否正确' : 'Please confirm if weight value is correct'; showInputWarning(this, message); } else { hideInputWarning(this); } }); } // 腰围合理性检查 const waistInput = document.getElementById('waist'); if (waistInput) { waistInput.addEventListener('input', function() { const waist = parseFloat(this.value); if (waist && (waist < 60 || waist > 150)) { const message = currentLanguage === 'zh' ? '腰围数值请确认是否正确' : 'Please confirm if waist circumference is correct'; showInputWarning(this, message); } else { hideInputWarning(this); } }); } // 年龄合理性检查 const ageInput = document.getElementById('age_years'); if (ageInput) { ageInput.addEventListener('input', function() { const age = parseFloat(this.value); if (age && age > 80) { const message = currentLanguage === 'zh' ? '高龄用户建议咨询专业医生' : 'Elderly users are advised to consult professional doctors'; showInputWarning(this, message); } else { hideInputWarning(this); } }); } } /** * 显示输入警告 */ function showInputWarning(input, message) { let warning = input.parentNode.querySelector('.input-warning'); if (!warning) { warning = document.createElement('div'); warning.className = 'input-warning text-warning small mt-1'; input.parentNode.appendChild(warning); } warning.innerHTML = `${message}`; } /** * 隐藏输入警告 */ function hideInputWarning(input) { const warning = input.parentNode.querySelector('.input-warning'); if (warning) { warning.remove(); } } /** * 导出评估结果 (未来功能) */ function exportResults() { if (!currentAssessment) { const noResultMsg = currentLanguage === 'zh' ? '没有可导出的评估结果' : 'No assessment results to export'; showAlert(noResultMsg, 'warning'); return; } // TODO: 实现结果导出功能 console.log('导出评估结果:', currentAssessment); const devMsg = currentLanguage === 'zh' ? '导出功能正在开发中' : 'Export function is under development'; showAlert(devMsg, 'info'); } /** * 初始化语言设置 */ function initializeLanguage() { // 从localStorage读取语言设置 const savedLang = localStorage.getItem('sarco-language'); if (savedLang) { currentLanguage = savedLang; if (currentLanguage === 'en') { switchToEnglish(); } } } /** * 切换语言 */ function toggleLanguage() { if (currentLanguage === 'zh') { switchToEnglish(); } else { switchToChinese(); } } /** * 切换到英文 */ function switchToEnglish() { currentLanguage = 'en'; document.querySelectorAll('.lang-zh').forEach(el => el.style.display = 'none'); document.querySelectorAll('.lang-en').forEach(el => el.style.display = 'inline'); document.getElementById('langToggleText').textContent = '中文'; document.documentElement.lang = 'en'; document.title = 'SarcoAdvisor-BSU - Sarcopenia Risk Assessment System'; // 更新select选项 updateSelectOptions('en'); // 如果有已显示的结果,重新渲染 if (currentAssessment) { displayResults(currentAssessment); } // 保存设置 localStorage.setItem('sarco-language', 'en'); } /** * 切换到中文 */ function switchToChinese() { currentLanguage = 'zh'; document.querySelectorAll('.lang-en').forEach(el => el.style.display = 'none'); document.querySelectorAll('.lang-zh').forEach(el => el.style.display = 'inline'); document.getElementById('langToggleText').textContent = 'English'; document.documentElement.lang = 'zh-CN'; document.title = 'SarcoAdvisor-BSU - 肌少症风险评估系统'; // 更新select选项 updateSelectOptions('zh'); // 如果有已显示的结果,重新渲染 if (currentAssessment) { displayResults(currentAssessment); } // 保存设置 localStorage.setItem('sarco-language', 'zh'); } /** * 更新select选项的显示 */ function updateSelectOptions(lang) { const select = document.getElementById('race_ethnicity'); const options = select.querySelectorAll('option'); options.forEach(option => { if (option.value === '') { option.textContent = lang === 'zh' ? '请选择' : 'Please select'; } else if (option.value === '0') { option.textContent = lang === 'zh' ? '美洲原住民' : 'Native American'; } else if (option.value === '1') { option.textContent = lang === 'zh' ? '亚洲人' : 'Asian'; } else if (option.value === '2') { option.textContent = lang === 'zh' ? '非洲裔美国人' : 'African American'; } else if (option.value === '3') { option.textContent = lang === 'zh' ? '西班牙裔' : 'Hispanic'; } else if (option.value === '4') { option.textContent = lang === 'zh' ? '白人' : 'White'; } }); } /** * 初始化自动计算功能 */ function initializeAutoCalculation() { // 获取输入元素 const heightInput = document.getElementById('height'); const weightInput = document.getElementById('weight'); const waistInput = document.getElementById('waist'); const bmiInput = document.getElementById('body_mass_index'); const wwiInput = document.getElementById('WWI'); // 添加事件监听器 if (heightInput && weightInput && waistInput) { heightInput.addEventListener('input', calculateBMIAndWWI); weightInput.addEventListener('input', calculateBMIAndWWI); waistInput.addEventListener('input', calculateBMIAndWWI); console.log('自动计算功能已初始化'); } else { console.warn('未找到身体测量输入字段'); } } /** * 计算BMI和WWI */ function calculateBMIAndWWI() { const height = parseFloat(document.getElementById('height').value); const weight = parseFloat(document.getElementById('weight').value); const waist = parseFloat(document.getElementById('waist').value); const bmiInput = document.getElementById('body_mass_index'); const wwiInput = document.getElementById('WWI'); // 计算BMI if (height && weight && height > 0) { const heightInMeters = height / 100; // 转换为米 const bmi = weight / (heightInMeters * heightInMeters); bmiInput.value = bmi.toFixed(1); // 添加BMI颜色指示 updateBMIStatus(bmi); } else { bmiInput.value = ''; bmiInput.className = 'form-control bg-light'; } // 计算WWI if (waist && weight && weight > 0) { const wwi = waist / Math.sqrt(weight); wwiInput.value = wwi.toFixed(2); // 添加WWI颜色指示 updateWWIStatus(wwi); } else { wwiInput.value = ''; wwiInput.className = 'form-control bg-light'; } // 触发表单验证更新 updateSubmitButton(); } /** * 更新BMI状态指示 */ function updateBMIStatus(bmi) { const bmiInput = document.getElementById('body_mass_index'); // 移除旧的状态类 bmiInput.className = 'form-control bg-light'; if (bmi < 18.5) { bmiInput.classList.add('border-info'); // 偏瘦 } else if (bmi >= 18.5 && bmi < 24) { bmiInput.classList.add('border-success'); // 正常 } else if (bmi >= 24 && bmi < 28) { bmiInput.classList.add('border-warning'); // 超重 } else if (bmi >= 28) { bmiInput.classList.add('border-danger'); // 肥胖 } } /** * 更新WWI状态指示 */ function updateWWIStatus(wwi) { const wwiInput = document.getElementById('WWI'); // 移除旧的状态类 wwiInput.className = 'form-control bg-light'; // WWI正常范围大约在9-12之间 if (wwi < 9) { wwiInput.classList.add('border-info'); // 较低 } else if (wwi >= 9 && wwi <= 12) { wwiInput.classList.add('border-success'); // 正常 } else if (wwi > 12 && wwi <= 14) { wwiInput.classList.add('border-warning'); // 较高 } else if (wwi > 14) { wwiInput.classList.add('border-danger'); // 很高 } } /** * 初始化PAQ问卷条件显示逻辑 */ function initializePAQLogic() { // 工作高强度活动条件显示 const paq605 = document.getElementById('PAQ605'); const vigorousWorkDetails = document.getElementById('vigorous_work_details'); if (paq605 && vigorousWorkDetails) { paq605.addEventListener('change', function() { if (this.value === '1') { vigorousWorkDetails.style.display = 'block'; document.getElementById('PAQ610').required = true; document.getElementById('PAD615').required = true; } else { vigorousWorkDetails.style.display = 'none'; document.getElementById('PAQ610').required = false; document.getElementById('PAD615').required = false; document.getElementById('PAQ610').value = ''; document.getElementById('PAD615').value = ''; } }); } // 工作中等强度活动条件显示 const paq620 = document.getElementById('PAQ620'); const moderateWorkDetails = document.getElementById('moderate_work_details'); if (paq620 && moderateWorkDetails) { paq620.addEventListener('change', function() { if (this.value === '1') { moderateWorkDetails.style.display = 'block'; document.getElementById('PAQ625').required = true; document.getElementById('PAD630').required = true; } else { moderateWorkDetails.style.display = 'none'; document.getElementById('PAQ625').required = false; document.getElementById('PAD630').required = false; document.getElementById('PAQ625').value = ''; document.getElementById('PAD630').value = ''; } }); } // 交通活动条件显示 const paq635 = document.getElementById('PAQ635'); const transportDetails = document.getElementById('transport_details'); if (paq635 && transportDetails) { paq635.addEventListener('change', function() { if (this.value === '1') { transportDetails.style.display = 'block'; document.getElementById('PAQ640').required = true; document.getElementById('PAD645').required = true; } else { transportDetails.style.display = 'none'; document.getElementById('PAQ640').required = false; document.getElementById('PAD645').required = false; document.getElementById('PAQ640').value = ''; document.getElementById('PAD645').value = ''; } }); } // 休闲高强度活动条件显示 const paq650 = document.getElementById('PAQ650'); const vigorousRecDetails = document.getElementById('vigorous_rec_details'); if (paq650 && vigorousRecDetails) { paq650.addEventListener('change', function() { if (this.value === '1') { vigorousRecDetails.style.display = 'block'; document.getElementById('PAQ655').required = true; document.getElementById('PAD660').required = true; } else { vigorousRecDetails.style.display = 'none'; document.getElementById('PAQ655').required = false; document.getElementById('PAD660').required = false; document.getElementById('PAQ655').value = ''; document.getElementById('PAD660').value = ''; } }); } // 休闲中等强度活动条件显示 const paq665 = document.getElementById('PAQ665'); const moderateRecDetails = document.getElementById('moderate_rec_details'); if (paq665 && moderateRecDetails) { paq665.addEventListener('change', function() { if (this.value === '1') { moderateRecDetails.style.display = 'block'; document.getElementById('PAQ670').required = true; document.getElementById('PAD675').required = true; } else { moderateRecDetails.style.display = 'none'; document.getElementById('PAQ670').required = false; document.getElementById('PAD675').required = false; document.getElementById('PAQ670').value = ''; document.getElementById('PAD675').value = ''; } }); } console.log('PAQ问卷条件显示逻辑已初始化'); } // 导出到全局作用域 window.SarcoAdvisor = { exportResults, showAlert, getCurrentAssessment: () => currentAssessment, toggleLanguage };