const SUPABASE_URL = 'https://chixssrphfgxvqqigkzo.supabase.co'; const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNoaXhzc3JwaGZneHZxcWlna3pvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQ2OTE0OTEsImV4cCI6MjA5MDI2NzQ5MX0.Az_Ew2J2zdOMcSV0UNAjBS-LPqGpqhsaN4IyZ5R7iqU'; const sbClient = supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY); const examSelect = document.getElementById('examSelect'); const nameInput = document.getElementById('studentName'); const admissionInput = document.getElementById('admissionNo'); const searchButton = document.getElementById('searchBtn'); const resultsShell = document.getElementById('searchResults'); let loadedExams = []; function escapeHtml(value) { return String(value ?? '').replace(/[&<>"']/g, char => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char])); } function normalize(value) { return String(value ?? '').trim(); } function formatDate(value) { if (!value) return '-'; const date = new Date(`${value}T00:00:00`); if (Number.isNaN(date.getTime())) return value; return date.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }); } function formatScore(value) { const number = Number(value || 0); return Number.isInteger(number) ? String(number) : number.toFixed(2); } function getScoreTotal(items) { return (items || []).reduce((sum, item) => sum + Number(item.score || 0), 0); } function renderEmpty(message) { resultsShell.innerHTML = `
${escapeHtml(message)}
`; } function renderError(message) { resultsShell.innerHTML = `
${escapeHtml(message)}
`; } function renderLoading(message) { resultsShell.innerHTML = `
${escapeHtml(message)}
`; } function getSelectedExam() { return loadedExams.find(exam => exam.id === examSelect.value) || null; } async function loadExams() { examSelect.innerHTML = ''; const { data, error } = await sbClient .from('exam_exams') .select('id, exam_code, exam_name, exam_date') .eq('is_published', true) .order('exam_date', { ascending: false }); if (error) throw error; loadedExams = data || []; if (loadedExams.length === 0) { examSelect.innerHTML = ''; renderEmpty('暂无已发布考试项目,请先在 Supabase 的 exam_exams 表中新增考试,或执行 exam/schema.sql 中的示例插入语句。'); return; } examSelect.innerHTML = [ '', ...loadedExams.map(exam => ( `` )) ].join(''); } function renderResult(result, exam) { const items = [...(result.exam_score_items || [])].sort((a, b) => Number(a.sort_order || 0) - Number(b.sort_order || 0) || String(a.item_name || '').localeCompare(String(b.item_name || ''), 'zh-CN') ); const total = getScoreTotal(items); const rows = items.map(item => { const score = Number(item.score || 0); const scoreClass = score < 0 ? 'score-value negative' : 'score-value'; const fullScore = item.full_score === null || item.full_score === undefined ? '-' : formatScore(item.full_score); return ` ${escapeHtml(item.item_name)} ${escapeHtml(formatScore(item.score))} ${escapeHtml(fullScore)} `; }).join(''); resultsShell.innerHTML = `

${escapeHtml(result.student_name)}

准考证号:${escapeHtml(result.admission_no)} 班级:${escapeHtml(result.class_name || '-')}
独立成绩单

${escapeHtml(exam.exam_name)}

考试日期:${escapeHtml(formatDate(exam.exam_date))}
本场总分
${escapeHtml(formatScore(total))}
${rows || ''}
成绩项目 得分 满分
暂无成绩项目

备注:${escapeHtml(result.remark || '无')}

`; } async function handleSearch() { const exam = getSelectedExam(); const studentName = normalize(nameInput.value); const admissionNo = normalize(admissionInput.value); if (!exam || !studentName || !admissionNo) { renderEmpty('请同时选择考试项目,并输入姓名和准考证号。'); return; } renderLoading('正在连接成绩数据库并执行查询...'); try { const { data, error } = await sbClient .from('exam_results') .select(` id, student_name, admission_no, class_name, remark, exam_score_items ( id, item_name, score, full_score, sort_order ) `) .eq('exam_id', exam.id) .eq('student_name', studentName) .eq('admission_no', admissionNo) .maybeSingle(); if (error) throw error; if (!data) { renderEmpty('未查询到该考试项目下的匹配成绩,请核对考试项目、姓名和准考证号。'); return; } renderResult(data, exam); } catch (error) { renderError(`查询失败:${error.message}`); } } function applyQueryParams() { const params = new URLSearchParams(window.location.search); const presetExam = params.get('examId') || params.get('exam') || ''; const presetName = params.get('name') || ''; const presetAdmissionNo = params.get('admissionNo') || params.get('ticket') || ''; if (presetExam) { const matchedExam = loadedExams.find(exam => exam.id === presetExam || exam.exam_code === presetExam); if (matchedExam) examSelect.value = matchedExam.id; } if (presetName) nameInput.value = presetName; if (presetAdmissionNo) admissionInput.value = presetAdmissionNo; if (examSelect.value && presetName && presetAdmissionNo) handleSearch(); } async function init() { try { await loadExams(); applyQueryParams(); } catch (error) { examSelect.innerHTML = ''; renderError(`考试项目加载失败:${error.message}`); } } searchButton.addEventListener('click', handleSearch); [examSelect, nameInput, admissionInput].forEach(input => { input.addEventListener('keydown', event => { if (event.key === 'Enter') handleSearch(); }); }); init();