// API基础路径 const API_BASE = ''; // 页面加载完成后执行 document.addEventListener('DOMContentLoaded', () => { loadConfig(); loadAlgorithms(); loadMatches(); loadPredictions(); initTabs(); initForms(); }); // 初始化标签切换 function initTabs() { document.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); btn.classList.add('active'); const tabId = btn.dataset.tab + '-tab'; document.getElementById(tabId).classList.add('active'); }); }); } // 初始化表单 function initForms() { // 算法表单 document.getElementById('algorithm-form').addEventListener('submit', async (e) => { e.preventDefault(); const id = document.getElementById('algorithm-id').value; const data = { name: document.getElementById('algorithm-name').value, description: document.getElementById('algorithm-desc').value, is_active: parseInt(document.getElementById('algorithm-active').value) }; try { const url = id ? `${API_BASE}/api/algorithms/${id}` : `${API_BASE}/api/algorithms`; const method = id ? 'PUT' : 'POST'; const response = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (result.success) { closeModal('algorithm-modal'); loadAlgorithms(); alert(id ? '算法更新成功' : '算法添加成功'); } else { alert(result.message || '操作失败'); } } catch (error) { console.error('保存算法失败:', error); alert('操作失败,请重试'); } }); // 比赛表单 document.getElementById('match-form').addEventListener('submit', async (e) => { e.preventDefault(); const id = document.getElementById('match-id').value; const data = { match_code: document.getElementById('match-code').value, home_team: document.getElementById('home-team').value, away_team: document.getElementById('away-team').value, match_time: document.getElementById('match-time').value || null, round_name: document.getElementById('match-round').value, status: document.getElementById('match-status').value, home_score: document.getElementById('home-score').value || null, away_score: document.getElementById('away-score').value || null }; try { const url = id ? `${API_BASE}/api/matches/${id}` : `${API_BASE}/api/matches`; const method = id ? 'PUT' : 'POST'; const response = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (result.success) { closeModal('match-modal'); loadMatches(); loadPredictions(); alert(id ? '比赛更新成功' : '比赛添加成功'); } else { alert(result.message || '操作失败'); } } catch (error) { console.error('保存比赛失败:', error); alert('操作失败,请重试'); } }); // 预测表单 document.getElementById('prediction-form').addEventListener('submit', async (e) => { e.preventDefault(); const data = { match_id: document.getElementById('prediction-match').value, algorithm_id: document.getElementById('prediction-algorithm').value, predicted_home_score: document.getElementById('pred-home-score').value || null, predicted_away_score: document.getElementById('pred-away-score').value || null, predicted_winner: document.getElementById('pred-winner').value || null, confidence: document.getElementById('pred-confidence').value / 100 }; try { const response = await fetch(`${API_BASE}/api/predictions`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (result.success) { closeModal('prediction-modal'); loadPredictions(); alert('预测添加成功'); } else { alert(result.message || '操作失败'); } } catch (error) { console.error('保存预测失败:', error); alert('操作失败,请重试'); } }); // 置信度滑块 document.getElementById('pred-confidence').addEventListener('input', (e) => { document.getElementById('confidence-value').textContent = e.target.value + '%'; }); } // 加载配置 async function loadConfig() { try { const response = await fetch(`${API_BASE}/api/config`); const result = await response.json(); if (result.success) { document.getElementById('prediction-cycle').value = result.data.prediction_cycle_hours || 24; document.getElementById('last-prediction-time').value = result.data.last_prediction_time || ''; document.getElementById('world-cup-year').value = result.data.world_cup_year || '2026'; } } catch (error) { console.error('加载配置失败:', error); } } // 保存配置 async function saveConfig() { const data = { prediction_cycle_hours: document.getElementById('prediction-cycle').value, last_prediction_time: document.getElementById('last-prediction-time').value, world_cup_year: document.getElementById('world-cup-year').value }; try { const response = await fetch(`${API_BASE}/api/config`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (result.success) { alert('配置保存成功'); } else { alert('保存失败'); } } catch (error) { console.error('保存配置失败:', error); alert('保存失败,请重试'); } } // 加载算法列表 async function loadAlgorithms() { try { const response = await fetch(`${API_BASE}/api/algorithms`); const result = await response.json(); if (result.success) { const tbody = document.getElementById('algorithms-table'); if (result.data.length === 0) { tbody.innerHTML = '暂无算法数据'; return; } tbody.innerHTML = result.data.map(algo => ` ${algo.id} ${algo.name} ${algo.description || '-'} ${(algo.accuracy || 0).toFixed(1)}% ${algo.total_predictions || 0} ${algo.correct_predictions || 0} ${algo.is_active ? '启用' : '禁用'} `).join(''); } } catch (error) { console.error('加载算法失败:', error); } } // 加载比赛列表 async function loadMatches() { try { const response = await fetch(`${API_BASE}/api/matches`); const result = await response.json(); if (result.success) { const tbody = document.getElementById('matches-table'); if (result.data.length === 0) { tbody.innerHTML = '暂无比赛数据'; return; } tbody.innerHTML = result.data.map(match => ` ${match.match_code} ${match.home_team} ${match.away_team} ${match.match_time ? new Date(match.match_time).toLocaleString('zh-CN') : '待定'} ${match.home_score !== null ? `${match.home_score} : ${match.away_score}` : '-'} ${match.round_name || '小组赛'} ${match.status === 'finished' ? '已完成' : '即将进行'} `).join(''); // 更新预测管理的比赛筛选下拉框 const filterSelect = document.getElementById('prediction-match-filter'); const currentValue = filterSelect.value; filterSelect.innerHTML = '' + result.data.map(m => ``).join(''); filterSelect.value = currentValue; // 更新预测表单的比赛下拉框 const predMatchSelect = document.getElementById('prediction-match'); predMatchSelect.innerHTML = '' + result.data.map(m => ``).join(''); } } catch (error) { console.error('加载比赛失败:', error); } } // 加载预测列表 async function loadPredictions() { try { const matchFilter = document.getElementById('prediction-match-filter').value; // 先获取所有比赛 const matchesResponse = await fetch(`${API_BASE}/api/matches`); const matchesResult = await matchesResponse.json(); if (!matchesResult.success) return; // 获取所有算法 const algorithmsResponse = await fetch(`${API_BASE}/api/algorithms`); const algorithmsResult = await algorithmsResponse.json(); if (!algorithmsResult.success) return; const algorithms = algorithmsResult.data; // 获取每场比赛的预测 let allPredictions = []; for (const match of matchesResult.data) { if (matchFilter && match.id != matchFilter) continue; const predResponse = await fetch(`${API_BASE}/api/matches/${match.id}`); const predResult = await predResponse.json(); if (predResult.success && predResult.data.predictions.length > 0) { predResult.data.predictions.forEach(pred => { allPredictions.push({ ...pred, match: match }); }); } } const tbody = document.getElementById('predictions-table'); if (allPredictions.length === 0) { tbody.innerHTML = '暂无预测数据'; return; } tbody.innerHTML = allPredictions.map(pred => ` ${pred.match.home_team} vs ${pred.match.away_team} ${pred.algorithm_name} ${pred.predicted_home_score !== null ? `${pred.predicted_home_score} : ${pred.predicted_away_score}` : '-'} ${pred.predicted_winner || '-'} ${pred.confidence ? (pred.confidence * 100).toFixed(0) + '%' : '-'} ${pred.match.home_score !== null ? `${pred.match.home_score} : ${pred.match.away_score}` : '-'} ${pred.is_correct !== null ? ` ${pred.is_correct ? '正确' : '错误'} ` : '待验证' } `).join(''); // 更新预测表单的算法下拉框 const predAlgoSelect = document.getElementById('prediction-algorithm'); predAlgoSelect.innerHTML = '' + algorithms.map(a => ``).join(''); } catch (error) { console.error('加载预测失败:', error); } } // 显示添加算法弹窗 function showAddAlgorithmModal() { document.getElementById('algorithm-modal-title').textContent = '添加算法'; document.getElementById('algorithm-form').reset(); document.getElementById('algorithm-id').value = ''; document.getElementById('algorithm-modal').style.display = 'block'; } // 编辑算法 async function editAlgorithm(id) { try { const response = await fetch(`${API_BASE}/api/algorithms/${id}`); const result = await response.json(); if (result.success) { const algo = result.data.algorithm; document.getElementById('algorithm-modal-title').textContent = '编辑算法'; document.getElementById('algorithm-id').value = algo.id; document.getElementById('algorithm-name').value = algo.name; document.getElementById('algorithm-desc').value = algo.description || ''; document.getElementById('algorithm-active').value = algo.is_active ? '1' : '0'; document.getElementById('algorithm-modal').style.display = 'block'; } } catch (error) { console.error('加载算法失败:', error); } } // 删除算法 async function deleteAlgorithm(id) { if (!confirm('确定要删除这个算法吗?相关的预测数据也会被删除。')) { return; } try { const response = await fetch(`${API_BASE}/api/algorithms/${id}`, { method: 'DELETE' }); const result = await response.json(); if (result.success) { loadAlgorithms(); loadPredictions(); alert('删除成功'); } else { alert('删除失败'); } } catch (error) { console.error('删除算法失败:', error); alert('删除失败,请重试'); } } // 显示批量添加比赛弹窗 function showBatchAddMatchModal() { document.getElementById('batch-match-data').value = ''; document.getElementById('batch-match-round').value = ''; document.getElementById('batch-match-modal').style.display = 'block'; } // 批量添加比赛 async function batchAddMatches() { const dataText = document.getElementById('batch-match-data').value.trim(); const defaultRound = document.getElementById('batch-match-round').value; if (!dataText) { alert('请输入比赛数据'); return; } const lines = dataText.split('\n').filter(line => line.trim()); let successCount = 0; let failCount = 0; let errors = []; for (const line of lines) { const parts = line.split(',').map(p => p.trim()); if (parts.length < 3) { failCount++; errors.push(`格式错误: ${line}`); continue; } const matchData = { match_code: parts[0], home_team: parts[1], away_team: parts[2], match_time: parts[3] || null, round_name: parts[4] || defaultRound || '小组赛', status: parts[5] || 'upcoming', home_score: (parts[6] && parts[6] !== '-') ? parseInt(parts[6]) : null, away_score: (parts[7] && parts[7] !== '-') ? parseInt(parts[7]) : null }; try { const response = await fetch(`${API_BASE}/api/matches`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(matchData) }); const result = await response.json(); if (result.success) { successCount++; } else { failCount++; errors.push(`${matchData.match_code}: ${result.message}`); } } catch (error) { failCount++; errors.push(`${matchData.match_code}: 网络错误`); } } let message = `批量添加完成\n成功: ${successCount} 条\n失败: ${failCount} 条`; if (errors.length > 0 && errors.length <= 5) { message += '\n\n失败详情:\n' + errors.join('\n'); } else if (errors.length > 5) { message += '\n\n失败详情(前5条):\n' + errors.slice(0, 5).join('\n') + '\n...'; } alert(message); if (successCount > 0) { closeModal('batch-match-modal'); loadMatches(); loadPredictions(); } } // 显示添加比赛弹窗 function showAddMatchModal() { document.getElementById('match-modal-title').textContent = '添加比赛'; document.getElementById('match-form').reset(); document.getElementById('match-id').value = ''; document.getElementById('match-modal').style.display = 'block'; } // 编辑比赛 async function editMatch(id) { try { const response = await fetch(`${API_BASE}/api/matches/${id}`); const result = await response.json(); if (result.success) { const match = result.data.match; document.getElementById('match-modal-title').textContent = '编辑比赛'; document.getElementById('match-id').value = match.id; document.getElementById('match-code').value = match.match_code; document.getElementById('home-team').value = match.home_team; document.getElementById('away-team').value = match.away_team; document.getElementById('match-time').value = match.match_time || ''; document.getElementById('match-round').value = match.round_name || '小组赛'; document.getElementById('match-status').value = match.status || 'upcoming'; document.getElementById('home-score').value = match.home_score !== null ? match.home_score : ''; document.getElementById('away-score').value = match.away_score !== null ? match.away_score : ''; document.getElementById('match-modal').style.display = 'block'; } } catch (error) { console.error('加载比赛失败:', error); } } // 删除比赛 async function deleteMatch(id) { if (!confirm('确定要删除这场比赛吗?相关的预测数据也会被删除。')) { return; } try { const response = await fetch(`${API_BASE}/api/matches/${id}`, { method: 'DELETE' }); const result = await response.json(); if (result.success) { loadMatches(); loadPredictions(); alert('删除成功'); } else { alert('删除失败'); } } catch (error) { console.error('删除比赛失败:', error); alert('删除失败,请重试'); } } // 为比赛添加预测 function addPredictionForMatch(matchId) { document.getElementById('prediction-match').value = matchId; document.getElementById('prediction-modal').style.display = 'block'; } // 评估预测结果 async function evaluatePredictions() { if (!confirm('确定要评估所有已完成比赛的预测结果吗?')) { return; } try { const response = await fetch(`${API_BASE}/api/predictions/evaluate`, { method: 'POST' }); const result = await response.json(); if (result.success) { alert(`评估完成,共评估了 ${result.evaluated} 条预测`); loadPredictions(); loadAlgorithms(); } else { alert('评估失败'); } } catch (error) { console.error('评估预测失败:', error); alert('评估失败,请重试'); } } // 关闭弹窗 function closeModal(modalId) { document.getElementById(modalId).style.display = 'none'; } // 点击弹窗外部关闭 window.addEventListener('click', (e) => { if (e.target.classList.contains('modal')) { e.target.style.display = 'none'; } });