/** * 统一评估系统 - 同时生成SarcoI和SarcoII建议 */ let currentLanguage = 'zh'; // DOM加载完成后初始化 document.addEventListener('DOMContentLoaded', function() { console.log('统一评估系统已加载'); // 初始化语言 initializeLanguage(); // 初始化表单 initializeForm(); // 初始化自动计算 initializeAutoCalculation(); // 插入体力活动问卷 insertPhysicalActivitySection(); }); /** * 初始化语言功能 */ function initializeLanguage() { const savedLanguage = localStorage.getItem('language') || 'zh'; currentLanguage = savedLanguage; updateLanguageDisplay(); } function toggleLanguage() { currentLanguage = currentLanguage === 'zh' ? 'en' : 'zh'; localStorage.setItem('language', currentLanguage); updateLanguageDisplay(); } function updateLanguageDisplay() { const zhElements = document.querySelectorAll('.lang-zh'); const enElements = document.querySelectorAll('.lang-en'); const toggleText = document.getElementById('langToggleText'); if (currentLanguage === 'zh') { zhElements.forEach(el => el.style.display = ''); enElements.forEach(el => el.style.display = 'none'); if (toggleText) toggleText.textContent = 'English'; } else { zhElements.forEach(el => el.style.display = 'none'); enElements.forEach(el => el.style.display = ''); if (toggleText) toggleText.textContent = '中文'; } } /** * 初始化表单 */ function initializeForm() { const form = document.getElementById('unifiedForm'); form.addEventListener('submit', handleSubmit); // 添加输入验证 const inputs = form.querySelectorAll('input[required], select[required]'); inputs.forEach(input => { input.addEventListener('input', updateSubmitButton); }); console.log('统一评估表单初始化完成'); } /** * 初始化自动计算功能 */ function initializeAutoCalculation() { const heightInput = document.getElementById('height'); const weightInput = document.getElementById('weight'); const waistInput = document.getElementById('waist'); function updateCalculations() { const height = parseFloat(heightInput.value); const weight = parseFloat(weightInput.value); const waist = parseFloat(waistInput.value); // 计算BMI if (height && weight) { const heightInMeters = height / 100; const bmi = weight / (heightInMeters * heightInMeters); document.getElementById('bmiDisplay').textContent = bmi.toFixed(1); } else { document.getElementById('bmiDisplay').textContent = '--'; } // 计算WWI if (waist && weight) { const wwi = waist / Math.sqrt(weight); document.getElementById('wwiDisplay').textContent = wwi.toFixed(2); } else { document.getElementById('wwiDisplay').textContent = '--'; } } heightInput.addEventListener('input', updateCalculations); weightInput.addEventListener('input', updateCalculations); waistInput.addEventListener('input', updateCalculations); console.log('自动计算功能已初始化'); } /** * 插入体力活动问卷部分 */ function insertPhysicalActivitySection() { const section = document.getElementById('physicalActivitySection'); section.innerHTML = generatePhysicalActivityHTML(); // 初始化活动详情切换 initializeActivityToggles(); } /** * 生成体力活动问卷HTML */ function generatePhysicalActivityHTML() { return `
NHANES 体力活动问卷 NHANES Physical Activity Questionnaire

工作相关体力活动 Work-related Physical Activity
如搬运重物、挖掘或建筑工作等,持续至少10分钟 Such as carrying heavy loads, digging, or construction work for at least 10 minutes
如快步走或搬运轻物等,持续至少10分钟 Such as brisk walking or carrying light loads for at least 10 minutes
交通相关活动 Transportation Activity
如上学、购物、上班等出行方式,持续至少10分钟 Such as to school, shopping, work, etc. for at least 10 minutes
休闲运动活动 Recreational Physical Activity
如跑步、篮球等引起大量喘息或心率增加的运动,持续至少10分钟 Such as running, basketball that cause large increases in breathing or heart rate for at least 10 minutes
如快步走、骑车、游泳或排球等,持续至少10分钟 Such as brisk walking, bicycling, swimming, or volleyball for at least 10 minutes
久坐行为 Sedentary Behavior

包括在学校、家中、通勤、与朋友相处时的坐着时间,如办公、乘车、读书、看电视、使用电脑等(不包括睡觉时间) Include time spent at school, home, commuting, and with friends. Include time spent sitting at a desk, traveling in a car or bus, reading, playing cards, watching television, or using a computer. Do not include time spent sleeping.
`; } /** * 初始化活动详情切换功能 */ function initializeActivityToggles() { // 为所有活动选择添加事件监听器 const activitySelects = ['PAQ605', 'PAQ620', 'PAQ635', 'PAQ650', 'PAQ665']; activitySelects.forEach(selectId => { const select = document.getElementById(selectId); if (select) { select.addEventListener('change', function() { const detailsMap = { 'PAQ605': 'vigorous_work_details', 'PAQ620': 'moderate_work_details', 'PAQ635': 'transport_details', 'PAQ650': 'vigorous_rec_details', 'PAQ665': 'moderate_rec_details' }; toggleActivityDetails(selectId, detailsMap[selectId]); }); } }); } /** * 切换活动详细信息显示/隐藏 */ function toggleActivityDetails(selectId, detailsId) { const select = document.getElementById(selectId); const details = document.getElementById(detailsId); if (select && details) { if (select.value === '1') { details.style.display = 'block'; } else { details.style.display = 'none'; // 清除详细信息输入 details.querySelectorAll('input').forEach(input => input.value = '0'); } } } /** * 处理表单提交 */ async function handleSubmit(event) { event.preventDefault(); console.log('开始统一评估...'); // 收集表单数据 const formData = collectFormData(); // 显示加载状态 showLoading(); try { // 添加语言参数 formData.language = currentLanguage; // 调用完整评估API const result = await performAssessment(formData); // 显示结果 displayResults(result); } catch (error) { console.error('评估失败:', error); hideLoading(); showAlert('评估过程中出现错误:' + error.message, 'danger'); } } /** * 收集表单数据 */ function collectFormData() { const form = document.getElementById('unifiedForm'); const formData = new FormData(form); const data = {}; // 收集所有表单数据 for (let [key, value] of formData.entries()) { if (value !== '' && value !== null && value !== undefined) { // 转换数字类型 if (['number'].includes(form.querySelector(`[name="${key}"]`).type)) { data[key] = parseFloat(value); } else { data[key] = parseInt(value); } } } // 计算BMI和WWI const height = data.height; const weight = data.weight; const waist = data.waist; if (height && weight) { const heightInMeters = height / 100; data.body_mass_index = parseFloat((weight / (heightInMeters * heightInMeters)).toFixed(1)); } if (waist && weight) { data.WWI = parseFloat((waist / Math.sqrt(weight)).toFixed(2)); } // 设置sedentary_minutes等同于PAD680 data.sedentary_minutes = data.PAD680 || 480; // 计算衍生特征 const derivedFeatures = calculateDerivedFeatures(data); const finalData = { ...data, ...derivedFeatures }; console.log('收集的完整数据:', finalData); return finalData; } /** * 根据NHANES PAQ数据计算衍生特征 */ function calculateDerivedFeatures(data) { const derived = {}; // 计算各类活动的MET-分钟 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; // 计算活动分钟数 const vigorousWorkMins = (data.PAD615 || 0) * (data.PAQ610 || 0); const vigorousRecMins = (data.PAD660 || 0) * (data.PAQ655 || 0); const totalVigorousMinutesWeek = vigorousWorkMins + vigorousRecMins; 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; // 计算活动多样性指数 let activityDiversityIndex = 0; if ((data.PAQ605 || 0) == 1) activityDiversityIndex++; if ((data.PAQ620 || 0) == 1) activityDiversityIndex++; if ((data.PAQ635 || 0) == 1) activityDiversityIndex++; if ((data.PAQ650 || 0) == 1) activityDiversityIndex++; if ((data.PAQ665 || 0) == 1) activityDiversityIndex++; // 计算比例特征 const vigorousMETTotal = vigorousWorkMETs + vigorousRecMETs; const vigorousMETRatio = totalMETMinutesWeek > 0 ? vigorousMETTotal / totalMETMinutesWeek : 0; const totalActiveMinutesWeek = totalVigorousMinutesWeek + totalModerateMinutesWeek; const totalSedentaryMinutesWeek = (data.PAD680 || 480) * 7; const activitySedentaryRatio = totalSedentaryMinutesWeek > 0 ? totalActiveMinutesWeek / totalSedentaryMinutesWeek : 0; // 设置衍生特征 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)); derived.Activity_Diversity_Index = activityDiversityIndex; derived.Vigorous_MET_Ratio = parseFloat(vigorousMETRatio.toFixed(3)); derived.Activity_Sedentary_Ratio = parseFloat(activitySedentaryRatio.toFixed(3)); return derived; } /** * 执行评估 */ async function performAssessment(userData) { console.log('调用完整评估API...'); const controller = new AbortController(); const timeoutId = setTimeout(() => { controller.abort(); }, 180000); // 3分钟超时 try { const response = await fetch('/api/full_assessment', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(userData), signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) { let errorMessage = `HTTP ${response.status}: `; try { const errorData = await response.json(); errorMessage += errorData.detail || JSON.stringify(errorData); } catch { errorMessage += response.statusText || '服务器错误'; } throw new Error(errorMessage); } const result = await response.json(); console.log('评估结果:', result); return result; } catch (error) { clearTimeout(timeoutId); if (error.name === 'AbortError') { throw new Error('评估超时,请稍后重试'); } throw error; } } /** * 显示加载状态 */ function showLoading() { document.getElementById('assessmentSection').style.display = 'none'; document.getElementById('loadingSection').style.display = 'block'; // 启动进度条动画 startProgressAnimation(); document.getElementById('loadingSection').scrollIntoView({ behavior: 'smooth' }); } /** * 隐藏加载状态 */ function hideLoading() { document.getElementById('loadingSection').style.display = 'none'; // 清除进度条动画 if (window.progressInterval) { clearInterval(window.progressInterval); window.progressInterval = null; } } /** * 启动进度条动画 */ function startProgressAnimation() { let progress = 30; const progressBar = document.getElementById('progressBar'); const statusText = document.getElementById('statusText'); const timeText = document.getElementById('timeText'); const messages = [ "🧠 AI正在分析您的特征数据...", "🔍 SarcoI模型分析中...", "⚙️ SarcoII模型分析中...", "📊 DiCE生成个性化建议...", "✨ 即将完成,请稍候..." ]; let messageIndex = 0; let startTime = Date.now(); const interval = setInterval(() => { const elapsed = (Date.now() - startTime) / 1000; // 更新进度条 if (elapsed < 120) { progress = 30 + (elapsed / 120) * 60; } else { progress = Math.min(95, 90 + (elapsed - 120) / 60 * 5); } progressBar.style.width = progress + '%'; // 更新消息 if (Math.floor(elapsed / 30) > messageIndex && messageIndex < messages.length - 1) { messageIndex++; statusText.textContent = messages[messageIndex]; } // 更新时间 if (elapsed < 60) { timeText.textContent = `已运行 ${Math.floor(elapsed)} 秒`; } else { const minutes = Math.floor(elapsed / 60); const seconds = Math.floor(elapsed % 60); timeText.textContent = `已运行 ${minutes}分${seconds}秒`; } if (elapsed > 180) { clearInterval(interval); } }, 1000); window.progressInterval = interval; } /** * 显示结果 */ function displayResults(assessment) { hideLoading(); const html = buildResultsHTML(assessment); document.getElementById('resultsContent').innerHTML = html; document.getElementById('resultsSection').style.display = 'block'; // 更新语言显示 updateLanguageDisplay(); document.getElementById('resultsSection').scrollIntoView({ behavior: 'smooth' }); } /** * 构建结果HTML */ function buildResultsHTML(assessment) { const screening = assessment.screening; const advisory = assessment.advisory; return `
双模型评估完成 Dual Model Assessment Completed

基于SarcoI和SarcoII模型的综合评估结果 Comprehensive assessment results based on SarcoI and SarcoII models

SarcoI 筛查 SarcoI Screening
${getRiskLevelText(screening.sarcoI_risk)}
概率: ${(screening.sarcoI_probability * 100).toFixed(1)}% Probability: ${(screening.sarcoI_probability * 100).toFixed(1)}%
SarcoII 筛查 SarcoII Screening
${getRiskLevelText(screening.sarcoII_risk)}
概率: ${(screening.sarcoII_probability * 100).toFixed(1)}% Probability: ${(screening.sarcoII_probability * 100).toFixed(1)}%
${advisory && advisory.recommendations ? `
个性化建议 Personalized Recommendations
${advisory.recommendations.map((rec, index) => `
${rec.title}

${rec.description}

优先级: ${rec.priority} | 预期效果: ${rec.expected_impact} Priority: ${rec.priority} | Expected Impact: ${rec.expected_impact}
`).join('')}
` : `
暂无个性化建议生成 No personalized recommendations generated
`}
`; } /** * 工具函数 */ function updateSubmitButton() { const form = document.getElementById('unifiedForm'); const submitBtn = document.getElementById('submitBtn'); const inputs = form.querySelectorAll('input[required], select[required]'); let allValid = true; inputs.forEach(input => { if (!input.value || !input.checkValidity()) { allValid = false; } }); submitBtn.disabled = !allValid; } function getRiskLevelText(level) { const levelTexts = { 'zh': { 'low': '低风险', 'medium': '中风险', 'high': '高风险' }, 'en': { 'low': 'Low Risk', 'medium': 'Medium Risk', 'high': 'High Risk' } }; return levelTexts[currentLanguage][level] || level; } function showAlert(message, type) { alert(message); }