|
|
|
let refreshInterval = 60; |
|
let autoRefreshEnabled = true; |
|
let chartInstances = {}; |
|
let darkModeEnabled = localStorage.getItem('theme') === 'dark'; |
|
|
|
|
|
function formatChartNumber(value) { |
|
if (value >= 1000000000) { |
|
return (value / 1000000000).toFixed(1) + 'G'; |
|
} else if (value >= 1000000) { |
|
return (value / 1000000).toFixed(1) + 'M'; |
|
} else if (value >= 1000) { |
|
return (value / 1000).toFixed(1) + 'K'; |
|
} |
|
return value; |
|
} |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
|
initializeCharts(); |
|
|
|
|
|
setupAutoRefresh(); |
|
|
|
|
|
setupThemeToggle(); |
|
|
|
|
|
loadSavedTheme(); |
|
|
|
|
|
enhanceTableInteraction(); |
|
|
|
|
|
setupSaveStatsButton(); |
|
|
|
|
|
updateFooterInfo(); |
|
|
|
|
|
const table = document.getElementById('history-table'); |
|
if (table) { |
|
const headers = table.querySelectorAll('th[data-sort]'); |
|
const rows = Array.from(table.querySelectorAll('tbody tr')); |
|
const rowsPerPage = 10; |
|
let currentPage = 1; |
|
let filteredRows = [...rows]; |
|
|
|
|
|
function initPagination() { |
|
const totalPages = Math.ceil(filteredRows.length / rowsPerPage); |
|
document.getElementById('total-pages').textContent = totalPages; |
|
document.getElementById('current-page').textContent = currentPage; |
|
document.getElementById('prev-page').disabled = currentPage === 1; |
|
document.getElementById('next-page').disabled = currentPage === totalPages || totalPages === 0; |
|
|
|
|
|
const startIndex = (currentPage - 1) * rowsPerPage; |
|
const endIndex = startIndex + rowsPerPage; |
|
|
|
rows.forEach(row => row.style.display = 'none'); |
|
filteredRows.slice(startIndex, endIndex).forEach(row => row.style.display = ''); |
|
} |
|
|
|
|
|
headers.forEach(header => { |
|
header.addEventListener('click', () => { |
|
const sortBy = header.getAttribute('data-sort'); |
|
const isAscending = header.classList.contains('asc'); |
|
|
|
|
|
headers.forEach(h => { |
|
h.classList.remove('asc', 'desc'); |
|
h.querySelector('i').className = 'fas fa-sort'; |
|
}); |
|
|
|
|
|
if (isAscending) { |
|
header.classList.add('desc'); |
|
header.querySelector('i').className = 'fas fa-sort-down'; |
|
} else { |
|
header.classList.add('asc'); |
|
header.querySelector('i').className = 'fas fa-sort-up'; |
|
} |
|
|
|
|
|
filteredRows.sort((a, b) => { |
|
let aValue, bValue; |
|
|
|
if (sortBy === 'id') { |
|
aValue = a.cells[0].getAttribute('title'); |
|
bValue = b.cells[0].getAttribute('title'); |
|
} else if (sortBy === 'timestamp') { |
|
aValue = a.cells[1].textContent; |
|
bValue = b.cells[1].textContent; |
|
} else if (sortBy === 'duration' || sortBy === 'total') { |
|
const aText = a.cells[sortBy === 'duration' ? 5 : 6].textContent; |
|
const bText = b.cells[sortBy === 'duration' ? 5 : 6].textContent; |
|
aValue = aText === '-' ? 0 : parseInt(aText.replace(/,/g, '').replace(/[KMG]/g, '')); |
|
bValue = bText === '-' ? 0 : parseInt(bText.replace(/,/g, '').replace(/[KMG]/g, '')); |
|
} else { |
|
aValue = a.cells[sortBy === 'model' ? 2 : (sortBy === 'account' ? 3 : 4)].textContent; |
|
bValue = b.cells[sortBy === 'model' ? 2 : (sortBy === 'account' ? 3 : 4)].textContent; |
|
} |
|
|
|
if (aValue < bValue) return isAscending ? -1 : 1; |
|
if (aValue > bValue) return isAscending ? 1 : -1; |
|
return 0; |
|
}); |
|
|
|
|
|
currentPage = 1; |
|
initPagination(); |
|
}); |
|
}); |
|
|
|
|
|
const searchInput = document.getElementById('history-search'); |
|
if (searchInput) { |
|
searchInput.addEventListener('input', function() { |
|
const searchTerm = this.value.toLowerCase(); |
|
|
|
filteredRows = rows.filter(row => { |
|
const rowText = Array.from(row.cells).map(cell => cell.textContent.toLowerCase()).join(' '); |
|
return rowText.includes(searchTerm); |
|
}); |
|
|
|
currentPage = 1; |
|
initPagination(); |
|
}); |
|
} |
|
|
|
|
|
const prevPageBtn = document.getElementById('prev-page'); |
|
const nextPageBtn = document.getElementById('next-page'); |
|
|
|
if (prevPageBtn) { |
|
prevPageBtn.addEventListener('click', () => { |
|
if (currentPage > 1) { |
|
currentPage--; |
|
initPagination(); |
|
} |
|
}); |
|
} |
|
|
|
if (nextPageBtn) { |
|
nextPageBtn.addEventListener('click', () => { |
|
const totalPages = Math.ceil(filteredRows.length / rowsPerPage); |
|
if (currentPage < totalPages) { |
|
currentPage++; |
|
initPagination(); |
|
} |
|
}); |
|
} |
|
|
|
|
|
initPagination(); |
|
} |
|
|
|
|
|
const refreshBtn = document.getElementById('refresh-btn'); |
|
if (refreshBtn) { |
|
refreshBtn.addEventListener('click', () => { |
|
location.reload(); |
|
}); |
|
} |
|
}); |
|
|
|
|
|
function initializeCharts() { |
|
try { |
|
|
|
Chart.register(ChartDataLabels); |
|
|
|
|
|
Chart.defaults.font.family = 'Nunito, sans-serif'; |
|
Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--text-color'); |
|
|
|
|
|
const dailyChartElement = document.getElementById('dailyChart'); |
|
if (dailyChartElement) { |
|
const labels = JSON.parse(dailyChartElement.dataset.labels || '[]'); |
|
const values = JSON.parse(dailyChartElement.dataset.values || '[]'); |
|
|
|
const dailyChart = new Chart(dailyChartElement, { |
|
type: 'line', |
|
data: { |
|
labels: labels, |
|
datasets: [{ |
|
label: '请求数', |
|
data: values, |
|
backgroundColor: 'rgba(52, 152, 219, 0.2)', |
|
borderColor: 'rgba(52, 152, 219, 1)', |
|
borderWidth: 2, |
|
pointBackgroundColor: 'rgba(52, 152, 219, 1)', |
|
pointRadius: 4, |
|
tension: 0.3, |
|
fill: true |
|
}] |
|
}, |
|
options: { |
|
responsive: true, |
|
maintainAspectRatio: false, |
|
plugins: { |
|
legend: { |
|
display: false |
|
}, |
|
tooltip: { |
|
mode: 'index', |
|
intersect: false, |
|
backgroundColor: 'rgba(0, 0, 0, 0.7)', |
|
titleFont: { |
|
size: 14 |
|
}, |
|
bodyFont: { |
|
size: 13 |
|
}, |
|
padding: 10, |
|
displayColors: false |
|
}, |
|
datalabels: { |
|
display: false |
|
} |
|
}, |
|
scales: { |
|
x: { |
|
grid: { |
|
display: false |
|
}, |
|
ticks: { |
|
maxRotation: 45, |
|
minRotation: 45 |
|
} |
|
}, |
|
y: { |
|
beginAtZero: true, |
|
grid: { |
|
color: 'rgba(200, 200, 200, 0.1)' |
|
}, |
|
ticks: { |
|
precision: 0 |
|
} |
|
} |
|
} |
|
} |
|
}); |
|
|
|
chartInstances['dailyChart'] = dailyChart; |
|
} |
|
|
|
|
|
const modelChartElement = document.getElementById('modelChart'); |
|
if (modelChartElement) { |
|
const labels = JSON.parse(modelChartElement.dataset.labels || '[]'); |
|
const values = JSON.parse(modelChartElement.dataset.values || '[]'); |
|
|
|
const modelChart = new Chart(modelChartElement, { |
|
type: 'pie', |
|
data: { |
|
labels: labels, |
|
datasets: [{ |
|
label: '模型使用次数', |
|
data: values, |
|
backgroundColor: [ |
|
'rgba(255, 99, 132, 0.5)', |
|
'rgba(54, 162, 235, 0.5)', |
|
'rgba(255, 206, 86, 0.5)', |
|
'rgba(75, 192, 192, 0.5)', |
|
'rgba(153, 102, 255, 0.5)', |
|
'rgba(255, 159, 64, 0.5)', |
|
'rgba(199, 199, 199, 0.5)', |
|
'rgba(83, 102, 255, 0.5)', |
|
'rgba(40, 159, 64, 0.5)', |
|
'rgba(210, 199, 199, 0.5)' |
|
], |
|
borderColor: [ |
|
'rgba(255, 99, 132, 1)', |
|
'rgba(54, 162, 235, 1)', |
|
'rgba(255, 206, 86, 1)', |
|
'rgba(75, 192, 192, 1)', |
|
'rgba(153, 102, 255, 1)', |
|
'rgba(255, 159, 64, 1)', |
|
'rgba(199, 199, 199, 1)', |
|
'rgba(83, 102, 255, 1)', |
|
'rgba(40, 159, 64, 1)', |
|
'rgba(210, 199, 199, 1)' |
|
], |
|
borderWidth: 1 |
|
}] |
|
}, |
|
options: { |
|
responsive: true, |
|
maintainAspectRatio: false, |
|
plugins: { |
|
tooltip: { |
|
callbacks: { |
|
label: function(context) { |
|
let label = context.label || ''; |
|
if (label) { |
|
label += ': '; |
|
} |
|
label += formatChartNumber(context.parsed); |
|
return label; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
}); |
|
|
|
chartInstances['modelChart'] = modelChart; |
|
} |
|
} catch (error) { |
|
console.error('初始化图表失败:', error); |
|
} |
|
} |
|
|
|
|
|
function setupAutoRefresh() { |
|
|
|
const progressBar = document.getElementById('refresh-progress-bar'); |
|
const countdownElement = document.getElementById('countdown'); |
|
let countdownTimer; |
|
|
|
|
|
let countdown = refreshInterval; |
|
|
|
function startCountdown() { |
|
if (countdownTimer) clearInterval(countdownTimer); |
|
|
|
countdown = refreshInterval; |
|
countdownElement.textContent = countdown; |
|
|
|
|
|
progressBar.style.width = '100%'; |
|
|
|
if (autoRefreshEnabled) { |
|
|
|
progressBar.style.transition = `width ${refreshInterval}s linear`; |
|
progressBar.style.width = '0%'; |
|
|
|
countdownTimer = setInterval(function() { |
|
countdown--; |
|
if (countdown <= 0) { |
|
countdown = refreshInterval; |
|
location.reload(); |
|
} |
|
countdownElement.textContent = countdown; |
|
}, 1000); |
|
} else { |
|
|
|
progressBar.style.transition = 'none'; |
|
progressBar.style.width = '0%'; |
|
} |
|
} |
|
|
|
|
|
startCountdown(); |
|
} |
|
|
|
|
|
function setupThemeToggle() { |
|
|
|
const themeToggleBtn = document.getElementById('theme-toggle-btn'); |
|
if (themeToggleBtn) { |
|
themeToggleBtn.addEventListener('click', function() { |
|
document.body.classList.toggle('dark-mode'); |
|
darkModeEnabled = document.body.classList.contains('dark-mode'); |
|
|
|
localStorage.setItem('theme', darkModeEnabled ? 'dark' : 'light'); |
|
|
|
|
|
updateChartsTheme(); |
|
}); |
|
} |
|
} |
|
|
|
|
|
function loadSavedTheme() { |
|
if (darkModeEnabled) { |
|
document.body.classList.add('dark-mode'); |
|
const themeToggleBtn = document.querySelector('#theme-toggle-btn i'); |
|
if (themeToggleBtn) { |
|
themeToggleBtn.classList.remove('fa-moon'); |
|
themeToggleBtn.classList.add('fa-sun'); |
|
} |
|
} |
|
} |
|
|
|
|
|
function updateChartsTheme() { |
|
|
|
Object.values(chartInstances).forEach(chart => { |
|
|
|
if (chart.options.scales && chart.options.scales.y) { |
|
chart.options.scales.y.grid.color = darkModeEnabled ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; |
|
chart.options.scales.x.grid.color = darkModeEnabled ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; |
|
|
|
|
|
chart.options.scales.y.ticks.color = darkModeEnabled ? '#ddd' : '#666'; |
|
chart.options.scales.x.ticks.color = darkModeEnabled ? '#ddd' : '#666'; |
|
} |
|
|
|
|
|
if (chart.options.plugins && chart.options.plugins.legend) { |
|
chart.options.plugins.legend.labels.color = darkModeEnabled ? '#ddd' : '#666'; |
|
} |
|
|
|
chart.update(); |
|
}); |
|
} |
|
|
|
|
|
function setupSaveStatsButton() { |
|
const saveButton = document.querySelector('.save-button'); |
|
if (saveButton) { |
|
|
|
saveButton.addEventListener('click', function() { |
|
this.classList.add('saving'); |
|
setTimeout(() => { |
|
this.classList.remove('saving'); |
|
}, 1000); |
|
}); |
|
} |
|
} |
|
|
|
|
|
function enhanceTableInteraction() { |
|
|
|
const historyRows = document.querySelectorAll('#history-table tbody tr'); |
|
historyRows.forEach(row => { |
|
row.addEventListener('mouseenter', function() { |
|
this.classList.add('highlight'); |
|
}); |
|
|
|
row.addEventListener('mouseleave', function() { |
|
this.classList.remove('highlight'); |
|
}); |
|
}); |
|
} |
|
|
|
|
|
function updateFooterInfo() { |
|
const footer = document.querySelector('.main-footer'); |
|
if (!footer) return; |
|
|
|
|
|
const currentYear = new Date().getFullYear(); |
|
|
|
|
|
const copyrightText = footer.querySelector('p:first-child'); |
|
if (copyrightText) { |
|
copyrightText.textContent = `© ${currentYear} 2API 统计面板 | 版本 1.0.1`; |
|
} |
|
} |