添加数据库修复脚本
This commit is contained in:
171
public/js/app.js
171
public/js/app.js
@@ -43,8 +43,21 @@
|
||||
btnAdd: document.getElementById('btnAdd'),
|
||||
formMessage: document.getElementById('formMessage'),
|
||||
sourceItems: document.getElementById('sourceItems'),
|
||||
// Site Settings
|
||||
modalTabs: document.querySelectorAll('.modal-tab'),
|
||||
tabContents: document.querySelectorAll('.tab-content'),
|
||||
pageNameInput: document.getElementById('pageNameInput'),
|
||||
siteTitleInput: document.getElementById('siteTitleInput'),
|
||||
logoUrlInput: document.getElementById('logoUrlInput'),
|
||||
btnSaveSiteSettings: document.getElementById('btnSaveSiteSettings'),
|
||||
siteSettingsMessage: document.getElementById('siteSettingsMessage'),
|
||||
logoText: document.getElementById('logoText'),
|
||||
logoIconContainer: document.getElementById('logoIconContainer'),
|
||||
defaultThemeInput: document.getElementById('defaultThemeInput'),
|
||||
// Auth & Theme elements
|
||||
themeToggle: document.getElementById('themeToggle'),
|
||||
sunIcon: document.querySelector('.sun-icon'),
|
||||
moonIcon: document.querySelector('.moon-icon'),
|
||||
userSection: document.getElementById('userSection'),
|
||||
btnLogin: document.getElementById('btnLogin'),
|
||||
loginModal: document.getElementById('loginModalOverlay'),
|
||||
@@ -67,11 +80,7 @@
|
||||
updateClock();
|
||||
setInterval(updateClock, 1000);
|
||||
|
||||
// Theme initialization
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
if (savedTheme === 'light') {
|
||||
document.documentElement.classList.add('light-theme');
|
||||
}
|
||||
// Initial theme check (localStorage handled after site settings load to ensure priority)
|
||||
|
||||
// Network chart
|
||||
networkChart = new AreaChart(dom.networkCanvas);
|
||||
@@ -94,6 +103,17 @@
|
||||
if (e.target === dom.loginModal) closeLoginModal();
|
||||
});
|
||||
|
||||
// Tab switching
|
||||
dom.modalTabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const targetTab = tab.getAttribute('data-tab');
|
||||
switchTab(targetTab);
|
||||
});
|
||||
});
|
||||
|
||||
// Site settings
|
||||
dom.btnSaveSiteSettings.addEventListener('click', saveSiteSettings);
|
||||
|
||||
// Keyboard shortcut
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
@@ -108,6 +128,7 @@
|
||||
// Start data fetching
|
||||
fetchMetrics();
|
||||
fetchNetworkHistory();
|
||||
loadSiteSettings();
|
||||
setInterval(fetchMetrics, REFRESH_INTERVAL);
|
||||
setInterval(fetchNetworkHistory, NETWORK_HISTORY_INTERVAL);
|
||||
}
|
||||
@@ -115,7 +136,25 @@
|
||||
// ---- Theme Switching ----
|
||||
function toggleTheme() {
|
||||
const isLight = document.documentElement.classList.toggle('light-theme');
|
||||
localStorage.setItem('theme', isLight ? 'light' : 'dark');
|
||||
const theme = isLight ? 'light' : 'dark';
|
||||
localStorage.setItem('theme', theme);
|
||||
updateThemeIcons(theme);
|
||||
}
|
||||
|
||||
function applyTheme(theme) {
|
||||
const isLight = theme === 'light';
|
||||
document.documentElement.classList.toggle('light-theme', isLight);
|
||||
updateThemeIcons(theme);
|
||||
}
|
||||
|
||||
function updateThemeIcons(theme) {
|
||||
if (theme === 'light') {
|
||||
dom.sunIcon.style.display = 'none';
|
||||
dom.moonIcon.style.display = 'block';
|
||||
} else {
|
||||
dom.sunIcon.style.display = 'block';
|
||||
dom.moonIcon.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Auth Logic ----
|
||||
@@ -381,6 +420,126 @@
|
||||
function closeSettings() {
|
||||
dom.settingsModal.classList.remove('active');
|
||||
hideMessage();
|
||||
hideSiteMessage();
|
||||
}
|
||||
|
||||
// ---- Tab Switching ----
|
||||
function switchTab(tabId) {
|
||||
dom.modalTabs.forEach(tab => {
|
||||
tab.classList.toggle('active', tab.getAttribute('data-tab') === tabId);
|
||||
});
|
||||
dom.tabContents.forEach(content => {
|
||||
content.classList.toggle('active', content.id === `tab-${tabId}`);
|
||||
});
|
||||
}
|
||||
|
||||
// ---- Site Settings ----
|
||||
async function loadSiteSettings() {
|
||||
try {
|
||||
const response = await fetch('/api/settings');
|
||||
const settings = await response.json();
|
||||
|
||||
// Update inputs
|
||||
dom.pageNameInput.value = settings.page_name || '';
|
||||
dom.siteTitleInput.value = settings.title || '';
|
||||
dom.logoUrlInput.value = settings.logo_url || '';
|
||||
dom.defaultThemeInput.value = settings.default_theme || 'dark';
|
||||
|
||||
// Apply to UI
|
||||
applySiteSettings(settings);
|
||||
|
||||
// Handle Theme Priority: localStorage > Site Default
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
if (savedTheme) {
|
||||
applyTheme(savedTheme);
|
||||
} else if (settings.default_theme) {
|
||||
applyTheme(settings.default_theme);
|
||||
} else {
|
||||
applyTheme('dark'); // Fallback
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error loading site settings:', err);
|
||||
}
|
||||
}
|
||||
|
||||
function applySiteSettings(settings) {
|
||||
if (settings.page_name) {
|
||||
document.title = settings.page_name;
|
||||
}
|
||||
if (settings.title) {
|
||||
dom.logoText.textContent = settings.title;
|
||||
}
|
||||
|
||||
// Logo Icon
|
||||
if (settings.logo_url) {
|
||||
dom.logoIconContainer.innerHTML = `<img src="${escapeHtml(settings.logo_url)}" alt="Logo" class="logo-icon-img">`;
|
||||
} else {
|
||||
// Restore default SVG
|
||||
dom.logoIconContainer.innerHTML = `
|
||||
<svg class="logo-icon" id="logoSvg" viewBox="0 0 32 32" fill="none">
|
||||
<rect x="2" y="2" width="28" height="28" rx="8" stroke="url(#logoGrad)" stroke-width="2.5"/>
|
||||
<path d="M8 22 L12 14 L16 18 L20 10 L24 16" stroke="url(#logoGrad)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<circle cx="12" cy="14" r="2" fill="url(#logoGrad)"/>
|
||||
<circle cx="20" cy="10" r="2" fill="url(#logoGrad)"/>
|
||||
<defs>
|
||||
<linearGradient id="logoGrad" x1="0" y1="0" x2="32" y2="32">
|
||||
<stop offset="0%" stop-color="#6366f1"/>
|
||||
<stop offset="100%" stop-color="#06b6d4"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSiteSettings() {
|
||||
if (!user) {
|
||||
showSiteMessage('请先登录后操作', 'error');
|
||||
openLoginModal();
|
||||
return;
|
||||
}
|
||||
|
||||
const settings = {
|
||||
page_name: dom.pageNameInput.value.trim(),
|
||||
title: dom.siteTitleInput.value.trim(),
|
||||
logo_url: dom.logoUrlInput.value.trim(),
|
||||
default_theme: dom.defaultThemeInput.value
|
||||
};
|
||||
|
||||
dom.btnSaveSiteSettings.disabled = true;
|
||||
dom.btnSaveSiteSettings.textContent = '保存中...';
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/settings', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(settings)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
showSiteMessage('设置保存成功', 'success');
|
||||
applySiteSettings(settings);
|
||||
} else {
|
||||
const err = await response.json();
|
||||
showSiteMessage(`保存失败: ${err.error}`, 'error');
|
||||
if (response.status === 401) openLoginModal();
|
||||
}
|
||||
} catch (err) {
|
||||
showSiteMessage(`保存失败: ${err.message}`, 'error');
|
||||
} finally {
|
||||
dom.btnSaveSiteSettings.disabled = false;
|
||||
dom.btnSaveSiteSettings.textContent = '保存设置';
|
||||
}
|
||||
}
|
||||
|
||||
function showSiteMessage(text, type) {
|
||||
dom.siteSettingsMessage.textContent = text;
|
||||
dom.siteSettingsMessage.className = `form-message ${type}`;
|
||||
setTimeout(hideSiteMessage, 5000);
|
||||
}
|
||||
|
||||
function hideSiteMessage() {
|
||||
dom.siteSettingsMessage.className = 'form-message';
|
||||
}
|
||||
|
||||
async function loadSources() {
|
||||
|
||||
Reference in New Issue
Block a user