// Survey renderer - 使用JSON数据渲染页面 class SurveyRenderer { constructor() { this.data = null; this.init(); } async init() { try { // 从URL查询参数获取ID const urlParams = new URLSearchParams(window.location.search); const id = urlParams.get('id'); if (!id) { throw new Error('缺少报告ID参数'); } // 调用本地API获取数据 const response = await fetch(`/api/report/${id}`, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); if (!response.ok) { throw new Error(`API请求失败: ${response.status}`); } this.data = await response.json(); // 渲染页面 this.render(); } catch (error) { console.error('加载数据失败:', error); document.getElementById('content').innerHTML = `

⚠️ 报告生成失败

${error.message}

返回首页
`; } } render() { // 设置固定页面标题 const fixedTitle = "尚逸基石学科能力测评报告"; document.getElementById('page-title').textContent = fixedTitle; document.getElementById('report-title').textContent = fixedTitle; if (this.data.footer && this.data.footer.copyright) { document.getElementById('footer-text').textContent = this.data.footer.copyright; } // 获取report数据 const report = this.data.report; // 渲染内容 const content = document.getElementById('content'); content.innerHTML = ` ${this.renderOverview(this.data)} ${this.renderErrorAnalysis(report.errorAnalysis)} ${this.renderKnowledgeAnalysis(report.knowledgeAnalysis)} ${this.renderCognitiveAnalysis(report.cognitiveAnalysis)} ${this.renderLearningPlan(report.learningPlan)} `; // 渲染雷达图 this.renderRadarChart(report.radarData); } renderOverview(data) { const report = data.report; return `
${this.renderStudentInfo(data.studentInfo)} ${this.renderCoreDashboard(report.summaryData)}
`; } renderStudentInfo(studentInfo) { return `

📋 学员信息概览

姓名 ${studentInfo.name}
测评时间 ${studentInfo.testDate}
测评科目 ${studentInfo.subject}
学校 ${studentInfo.school}
`; } renderCoreDashboard(summaryData) { return `

📊 核心数据看板

总分/等级

${summaryData.totalScore}
${summaryData.level}

得分率

${summaryData.scoreRate}
${summaryData.scoreRateDescription}

群体位置

${summaryData.groupPosition}
${summaryData.groupPositionDescription}

专家点评

${summaryData.summary}

五维能力雷达图

`; } renderErrorAnalysis(errorAnalysis) { return `

${errorAnalysis.title}

${errorAnalysis.errors.map(error => `
${error.questionNumber} ${error.questionTitle}
${error.errorType}
原题核心考查点:${error.corePoint}
学生错误选项:${error.wrongOption}
正确选项:${error.correctOption}
错题分析:${error.analysis}
正确思路引导:${error.guidance}
`).join('')}

${errorAnalysis.tagLibrary.title}

${errorAnalysis.tagLibrary.tags.map(tag => `

${tag.emoji} ${tag.name}

${tag.description}

`).join('')}
`; } renderKnowledgeAnalysis(knowledgeAnalysis) { return `

${knowledgeAnalysis.title}

${knowledgeAnalysis.chapters.map(chapter => `

${chapter.title}

${chapter.score}

${chapter.description}

${chapter.note}

`).join('')}
`; } renderCognitiveAnalysis(cognitiveAnalysis) { return `

${cognitiveAnalysis.title}

${cognitiveAnalysis.dimensions.map(dimension => `

${dimension.emoji} ${dimension.name}

${dimension.score}

${dimension.description}

`).join('')}

${cognitiveAnalysis.behaviorInsight.title}

${cognitiveAnalysis.behaviorInsight.content}

`; } renderLearningPlan(learningPlan) { return `

${learningPlan.title}

${learningPlan.plans.map(plan => `

${plan.emoji} ${plan.title}

目标:${plan.goal}

${plan.sections.map(section => `

${section.title}

    ${section.items.map(item => `
  • ${item}
  • `).join('')}
`).join('')}
`).join('')}

${learningPlan.parentAdvice.emoji} ${learningPlan.parentAdvice.title}

`; } renderRadarChart(radarData) { const canvas = document.getElementById('radarChart'); if (!canvas) return; // 根据屏幕大小调整canvas尺寸 const containerWidth = canvas.parentElement.offsetWidth; const maxSize = Math.min(containerWidth, 500); const size = window.innerWidth < 768 ? Math.min(containerWidth, 300) : maxSize; canvas.width = size; canvas.height = size; const ctx = canvas.getContext('2d'); const centerX = canvas.width / 2; const centerY = canvas.height / 2; const radius = size * 0.32; // 响应式半径 // 绘制雷达图 function drawRadarChart() { // 清空画布 ctx.clearRect(0, 0, canvas.width, canvas.height); // 绘制网格 ctx.strokeStyle = '#e0e0e0'; ctx.lineWidth = 1; for (let i = 1; i <= 5; i++) { ctx.beginPath(); const r = radius * i / 5; for (let j = 0; j < 5; j++) { const angle = (Math.PI * 2 / 5) * j - Math.PI / 2; const x = centerX + r * Math.cos(angle); const y = centerY + r * Math.sin(angle); if (j === 0) { ctx.moveTo(x, y); } else { ctx.lineTo(x, y); } } ctx.closePath(); ctx.stroke(); } // 绘制轴线 for (let i = 0; i < 5; i++) { const angle = (Math.PI * 2 / 5) * i - Math.PI / 2; ctx.beginPath(); ctx.moveTo(centerX, centerY); ctx.lineTo(centerX + radius * Math.cos(angle), centerY + radius * Math.sin(angle)); ctx.stroke(); } // 绘制数据区域 ctx.beginPath(); ctx.fillStyle = 'rgba(255, 126, 95, 0.25)'; ctx.strokeStyle = '#ff7e5f'; ctx.lineWidth = 2; for (let i = 0; i < 5; i++) { const angle = (Math.PI * 2 / 5) * i - Math.PI / 2; const value = radarData.abilities[i].value / 100; const x = centerX + radius * value * Math.cos(angle); const y = centerY + radius * value * Math.sin(angle); if (i === 0) { ctx.moveTo(x, y); } else { ctx.lineTo(x, y); } } ctx.closePath(); ctx.fill(); ctx.stroke(); // 绘制数据点 for (let i = 0; i < 5; i++) { const angle = (Math.PI * 2 / 5) * i - Math.PI / 2; const value = radarData.abilities[i].value / 100; const x = centerX + radius * value * Math.cos(angle); const y = centerY + radius * value * Math.sin(angle); // 外圈橙色 ctx.beginPath(); ctx.fillStyle = '#ff7e5f'; ctx.arc(x, y, 5, 0, Math.PI * 2); ctx.fill(); // 内圈深绿 ctx.beginPath(); ctx.fillStyle = '#2d5a3d'; ctx.arc(x, y, 3, 0, Math.PI * 2); ctx.fill(); } // 绘制标签 - 响应式字体大小 const fontSize = window.innerWidth < 768 ? 10 : 13; const labelOffset = window.innerWidth < 768 ? 15 : 20; const lineHeight = window.innerWidth < 768 ? 5 : 7; ctx.fillStyle = '#333'; ctx.font = `${fontSize}px Microsoft YaHei`; for (let i = 0; i < 5; i++) { const angle = (Math.PI * 2 / 5) * i - Math.PI / 2; const labelRadius = radius + labelOffset; const x = centerX + labelRadius * Math.cos(angle); const y = centerY + labelRadius * Math.sin(angle); // 根据角度调整文本对齐方式 if (angle < -Math.PI / 2 || angle > Math.PI / 2) { ctx.textAlign = 'right'; } else { ctx.textAlign = 'left'; } // 智能换行处理 const name = radarData.abilities[i].name; if (name.includes('与')) { const lines = name.split('与'); // 第一行:第一个词 + "与" ctx.fillText(lines[0] + '与', x, y - lineHeight); // 第二行:第二个词 ctx.fillText(lines[1], x, y + lineHeight); } else { // 如果没有"与",直接显示完整名称 ctx.fillText(name, x, y + 1); } } // 绘制刻度标签 ctx.textAlign = 'center'; ctx.fillStyle = '#666'; const scaleFontSize = window.innerWidth < 768 ? 8 : 10; ctx.font = `${scaleFontSize}px Microsoft YaHei`; for (let i = 1; i <= 5; i++) { const value = i * 20; ctx.fillText(value, centerX - 12, centerY - radius * i / 5 + 3); } } // 初始化雷达图 drawRadarChart(); // 响应式调整 - 重新绘制而不是缩放 window.addEventListener('resize', function() { // 重新设置canvas尺寸并重绘 const containerWidth = canvas.parentElement.offsetWidth; const maxSize = Math.min(containerWidth, 500); const size = window.innerWidth < 768 ? Math.min(containerWidth, 300) : maxSize; canvas.width = size; canvas.height = size; drawRadarChart(); }); // 触发初始调整 window.dispatchEvent(new Event('resize')); } } // 检查可重新生成的报告 async function checkRegenerationOptions() { const container = document.getElementById('regeneration-options'); try { const response = await fetch('/api/sessions-can-regenerate', { method: 'GET', headers: { 'Content-Type': 'application/json' } }); if (!response.ok) { throw new Error(`请求失败: ${response.status}`); } const data = await response.json(); if (data.success && data.sessions.length > 0) { // 显示可重新生成的会话列表 container.style.display = 'block'; container.innerHTML = `

🔄 可重新生成的报告

${data.sessions.map(session => `
${session.name} ${session.school} - ${session.grade}
创建时间: ${new Date(session.created_at).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })}
`).join('')}
`; } else { // 没有可重新生成的报告 container.style.display = 'block'; container.innerHTML = `

暂无可重新生成的报告

`; } } catch (error) { container.style.display = 'block'; container.innerHTML = `

检查可重新生成报告失败: ${error.message}

`; } } // 重新生成报告 async function regenerateReport(sessionId) { try { const response = await fetch('/api/regenerate-report', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId: sessionId }) }); const result = await response.json(); if (result.success) { // 重新生成成功,跳转到报告页面 if (result.reportId) { window.location.href = `/report.html?id=${result.reportId}`; } else { alert('报告重新生成成功!请前往报告列表查看。'); window.location.href = '/'; } } else { alert('重新生成失败: ' + result.message); } } catch (error) { alert('重新生成失败: ' + error.message); } } // 初始化渲染器 document.addEventListener('DOMContentLoaded', function() { new SurveyRenderer(); });