| |
|
|
| function showToast(message, type = 'info', title = '') { |
| const icons = { success: '✅', error: '❌', warning: '⚠️', info: 'ℹ️' }; |
| const titles = { success: '成功', error: '错误', warning: '警告', info: '提示' }; |
| const toast = document.createElement('div'); |
| toast.className = `toast ${type}`; |
| |
| const safeTitle = escapeHtml(title || titles[type]); |
| const safeMessage = escapeHtml(message); |
| toast.innerHTML = ` |
| <div class="toast-icon">${icons[type]}</div> |
| <div class="toast-content"> |
| <div class="toast-title">${safeTitle}</div> |
| <div class="toast-message">${safeMessage}</div> |
| </div> |
| `; |
| document.body.appendChild(toast); |
| setTimeout(() => { |
| toast.style.animation = 'slideOut 0.3s ease'; |
| setTimeout(() => toast.remove(), 300); |
| }, 3000); |
| } |
|
|
| function showConfirm(message, title = '确认操作') { |
| return new Promise((resolve) => { |
| const modal = document.createElement('div'); |
| modal.className = 'modal'; |
| |
| const safeTitle = escapeHtml(title); |
| const safeMessage = escapeHtml(message); |
| modal.innerHTML = ` |
| <div class="modal-content"> |
| <div class="modal-title">${safeTitle}</div> |
| <div class="modal-message">${safeMessage}</div> |
| <div class="modal-actions"> |
| <button class="btn btn-secondary" onclick="this.closest('.modal').remove(); window.modalResolve(false)">取消</button> |
| <button class="btn btn-danger" onclick="this.closest('.modal').remove(); window.modalResolve(true)">确定</button> |
| </div> |
| </div> |
| `; |
| document.body.appendChild(modal); |
| modal.onclick = (e) => { if (e.target === modal) { modal.remove(); resolve(false); } }; |
| window.modalResolve = resolve; |
| }); |
| } |
|
|
| function showLoading(text = '处理中...') { |
| const overlay = document.createElement('div'); |
| overlay.className = 'loading-overlay'; |
| overlay.id = 'loadingOverlay'; |
| |
| const safeText = escapeHtml(text); |
| overlay.innerHTML = `<div class="spinner"></div><div class="loading-text">${safeText}</div>`; |
| document.body.appendChild(overlay); |
| } |
|
|
| function hideLoading() { |
| const overlay = document.getElementById('loadingOverlay'); |
| if (overlay) overlay.remove(); |
| } |
|
|
| function switchTab(tab, saveState = true) { |
| |
| if (tab === 'settings') { |
| document.documentElement.classList.add('tab-settings'); |
| } else { |
| document.documentElement.classList.remove('tab-settings'); |
| } |
| |
| |
| document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); |
| |
| |
| const targetTab = document.querySelector(`.tab[data-tab="${tab}"]`); |
| if (targetTab) { |
| targetTab.classList.add('active'); |
| } |
| |
| const tokensPage = document.getElementById('tokensPage'); |
| const settingsPage = document.getElementById('settingsPage'); |
| |
| |
| tokensPage.classList.add('hidden'); |
| tokensPage.classList.remove('page-enter'); |
| settingsPage.classList.add('hidden'); |
| settingsPage.classList.remove('page-enter'); |
| |
| |
| if (tab === 'tokens') { |
| tokensPage.classList.remove('hidden'); |
| |
| void tokensPage.offsetWidth; |
| tokensPage.classList.add('page-enter'); |
| } else if (tab === 'settings') { |
| settingsPage.classList.remove('hidden'); |
| |
| void settingsPage.offsetWidth; |
| settingsPage.classList.add('page-enter'); |
| loadConfig(); |
| } |
| |
| |
| if (saveState) { |
| localStorage.setItem('currentTab', tab); |
| } |
| } |
|
|
| |
| function restoreTabState() { |
| const savedTab = localStorage.getItem('currentTab'); |
| if (savedTab && (savedTab === 'tokens' || savedTab === 'settings')) { |
| switchTab(savedTab, false); |
| } |
| } |
|
|