// 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';
}
});