| |
| 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`; |
| } |
| } |