This commit is contained in:
CN-JS-HuiBai
2026-04-06 17:27:56 +08:00
parent 131c011c5c
commit 94ed27199a
4 changed files with 74 additions and 10 deletions

View File

@@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="LDNET-GA"> <meta name="description" content="LDNET-GA">
<title>LDNET-GA</title> <title>LDNET-GA</title>
<link rel="icon" id="siteFavicon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📊</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link <link
@@ -448,8 +449,16 @@
<input type="text" id="siteTitleInput" placeholder="例:数据可视化展示大屏"> <input type="text" id="siteTitleInput" placeholder="例:数据可视化展示大屏">
</div> </div>
<div class="form-group" style="margin-top: 15px;"> <div class="form-group" style="margin-top: 15px;">
<label for="logoUrlInput">Logo URL (图片链接,为空则显示默认图标)</label> <label for="logoUrlInput">Logo URL (白天/默认,支持图片链接)</label>
<input type="url" id="logoUrlInput" placeholder="https://example.com/logo.png"> <input type="url" id="logoUrlInput" placeholder="https://example.com/logo_light.png">
</div>
<div class="form-group" style="margin-top: 15px;">
<label for="logoUrlDarkInput">Logo URL (黑夜模式,可为空则使用默认)</label>
<input type="url" id="logoUrlDarkInput" placeholder="https://example.com/logo_dark.png">
</div>
<div class="form-group" style="margin-top: 15px;">
<label for="faviconUrlInput">Favicon URL (浏览器标签页图标)</label>
<input type="url" id="faviconUrlInput" placeholder="https://example.com/favicon.ico">
</div> </div>
<div class="settings-section" style="margin-top: 25px; border-top: 1px solid var(--border-color); padding-top: 20px;"> <div class="settings-section" style="margin-top: 25px; border-top: 1px solid var(--border-color); padding-top: 20px;">
<h4 style="font-size: 0.85rem; color: var(--accent-indigo); margin-bottom: 15px; text-transform: uppercase; letter-spacing: 0.5px;">界面外观 (Appearance)</h4> <h4 style="font-size: 0.85rem; color: var(--accent-indigo); margin-bottom: 15px; text-transform: uppercase; letter-spacing: 0.5px;">界面外观 (Appearance)</h4>

View File

@@ -41,6 +41,8 @@
formMessage: document.getElementById('formMessage'), formMessage: document.getElementById('formMessage'),
sourceItems: document.getElementById('sourceItems'), sourceItems: document.getElementById('sourceItems'),
serverSourceOption: document.getElementById('serverSourceOption'), serverSourceOption: document.getElementById('serverSourceOption'),
faviconUrlInput: document.getElementById('faviconUrlInput'),
logoUrlDarkInput: document.getElementById('logoUrlDarkInput'),
// Site Settings // Site Settings
modalTabs: document.querySelectorAll('.modal-tab'), modalTabs: document.querySelectorAll('.modal-tab'),
tabContents: document.querySelectorAll('.tab-content'), tabContents: document.querySelectorAll('.tab-content'),
@@ -53,6 +55,7 @@
logoIconContainer: document.getElementById('logoIconContainer'), logoIconContainer: document.getElementById('logoIconContainer'),
defaultThemeInput: document.getElementById('defaultThemeInput'), defaultThemeInput: document.getElementById('defaultThemeInput'),
show95BandwidthInput: document.getElementById('show95BandwidthInput'), show95BandwidthInput: document.getElementById('show95BandwidthInput'),
siteFavicon: document.getElementById('siteFavicon'),
// Auth & Theme elements // Auth & Theme elements
themeToggle: document.getElementById('themeToggle'), themeToggle: document.getElementById('themeToggle'),
sunIcon: document.querySelector('.sun-icon'), sunIcon: document.querySelector('.sun-icon'),
@@ -487,6 +490,8 @@
if (dom.p95TypeSelect) dom.p95TypeSelect.value = window.SITE_SETTINGS.p95_type || 'tx'; if (dom.p95TypeSelect) dom.p95TypeSelect.value = window.SITE_SETTINGS.p95_type || 'tx';
if (dom.icpFilingInput) dom.icpFilingInput.value = window.SITE_SETTINGS.icp_filing || ''; if (dom.icpFilingInput) dom.icpFilingInput.value = window.SITE_SETTINGS.icp_filing || '';
if (dom.psFilingInput) dom.psFilingInput.value = window.SITE_SETTINGS.ps_filing || ''; if (dom.psFilingInput) dom.psFilingInput.value = window.SITE_SETTINGS.ps_filing || '';
if (dom.logoUrlDarkInput) dom.logoUrlDarkInput.value = window.SITE_SETTINGS.logo_url_dark || '';
if (dom.faviconUrlInput) dom.faviconUrlInput.value = window.SITE_SETTINGS.favicon_url || '';
// Latency routes loaded separately in openSettings or on startup // Latency routes loaded separately in openSettings or on startup
} }
@@ -551,6 +556,10 @@
localStorage.setItem('theme', theme); localStorage.setItem('theme', theme);
updateThemeIcons(theme); updateThemeIcons(theme);
updateMap2DTheme(theme); updateMap2DTheme(theme);
// After theme toggle, re-apply site settings to handle potential logo change
if (window.SITE_SETTINGS) {
applySiteSettings(window.SITE_SETTINGS);
}
} }
function applyTheme(theme) { function applyTheme(theme) {
@@ -564,6 +573,19 @@
document.documentElement.classList.toggle('light-theme', isLight); document.documentElement.classList.toggle('light-theme', isLight);
updateThemeIcons(actualTheme); updateThemeIcons(actualTheme);
updateMap2DTheme(actualTheme); updateMap2DTheme(actualTheme);
// After theme change, re-apply site settings to handle potential logo change
if (window.SITE_SETTINGS) {
applySiteSettings(window.SITE_SETTINGS);
}
}
function updateFavicon(url) {
if (!url) return;
const link = dom.siteFavicon || document.querySelector("link[rel*='icon']");
if (link) {
link.href = url;
}
} }
function updateThemeIcons(theme) { function updateThemeIcons(theme) {
@@ -1655,6 +1677,8 @@
} }
if (dom.icpFilingInput) dom.icpFilingInput.value = settings.icp_filing || ''; if (dom.icpFilingInput) dom.icpFilingInput.value = settings.icp_filing || '';
if (dom.psFilingInput) dom.psFilingInput.value = settings.ps_filing || ''; if (dom.psFilingInput) dom.psFilingInput.value = settings.ps_filing || '';
if (dom.logoUrlDarkInput) dom.logoUrlDarkInput.value = settings.logo_url_dark || '';
if (dom.faviconUrlInput) dom.faviconUrlInput.value = settings.favicon_url || '';
// Apply to UI // Apply to UI
applySiteSettings(settings); applySiteSettings(settings);
@@ -1693,8 +1717,14 @@
} }
// Logo Icon // Logo Icon
if (settings.logo_url) { let logoToUse = settings.logo_url;
dom.logoIconContainer.innerHTML = `<img src="${escapeHtml(settings.logo_url)}" alt="Logo" class="logo-icon-img">`; const currentTheme = document.documentElement.classList.contains('light-theme') ? 'light' : 'dark';
if (currentTheme === 'dark' && settings.logo_url_dark) {
logoToUse = settings.logo_url_dark;
}
if (logoToUse) {
dom.logoIconContainer.innerHTML = `<img src="${escapeHtml(logoToUse)}" alt="Logo" class="logo-icon-img">`;
} else { } else {
// Restore default SVG // Restore default SVG
dom.logoIconContainer.innerHTML = ` dom.logoIconContainer.innerHTML = `
@@ -1713,6 +1743,9 @@
`; `;
} }
// Favicon
updateFavicon(settings.favicon_url);
// P95 setting // P95 setting
if (settings.show_95_bandwidth !== undefined || settings.p95_type !== undefined) { if (settings.show_95_bandwidth !== undefined || settings.p95_type !== undefined) {
if (networkChart) { if (networkChart) {
@@ -1786,7 +1819,9 @@
show_95_bandwidth: dom.show95BandwidthInput.value === "1" ? 1 : 0, show_95_bandwidth: dom.show95BandwidthInput.value === "1" ? 1 : 0,
p95_type: dom.p95TypeSelect.value, p95_type: dom.p95TypeSelect.value,
ps_filing: dom.psFilingInput ? dom.psFilingInput.value : '', ps_filing: dom.psFilingInput ? dom.psFilingInput.value : '',
icp_filing: dom.icpFilingInput ? dom.icpFilingInput.value : '' icp_filing: dom.icpFilingInput ? dom.icpFilingInput.value : '',
logo_url_dark: dom.logoUrlDarkInput ? dom.logoUrlDarkInput.value.trim() : '',
favicon_url: dom.faviconUrlInput ? dom.faviconUrlInput.value.trim() : ''
}; };
// If user sets default to auto, we should clear their manual override or set it to auto // If user sets default to auto, we should clear their manual override or set it to auto

View File

@@ -97,6 +97,16 @@ async function checkAndFixDatabase() {
await db.query("ALTER TABLE site_settings ADD COLUMN ps_filing VARCHAR(255) AFTER icp_filing"); await db.query("ALTER TABLE site_settings ADD COLUMN ps_filing VARCHAR(255) AFTER icp_filing");
console.log(`[Database Integrity] ✅ Column 'ps_filing' added.`); console.log(`[Database Integrity] ✅ Column 'ps_filing' added.`);
} }
if (!columnNames.includes('logo_url_dark')) {
console.log(`[Database Integrity] ⚠️ Missing column 'logo_url_dark' in 'site_settings'. Adding it...`);
await db.query("ALTER TABLE site_settings ADD COLUMN logo_url_dark TEXT AFTER logo_url");
console.log(`[Database Integrity] ✅ Column 'logo_url_dark' added.`);
}
if (!columnNames.includes('favicon_url')) {
console.log(`[Database Integrity] ⚠️ Missing column 'favicon_url' in 'site_settings'. Adding it...`);
await db.query("ALTER TABLE site_settings ADD COLUMN favicon_url TEXT AFTER logo_url_dark");
console.log(`[Database Integrity] ✅ Column 'favicon_url' added.`);
}
} catch (err) { } catch (err) {
console.error('[Database Integrity] ❌ Error checking integrity:', err.message); console.error('[Database Integrity] ❌ Error checking integrity:', err.message);
} }
@@ -135,6 +145,8 @@ async function createTable(tableName) {
page_name VARCHAR(255) DEFAULT '数据可视化展示大屏', page_name VARCHAR(255) DEFAULT '数据可视化展示大屏',
title VARCHAR(255) DEFAULT '数据可视化展示大屏', title VARCHAR(255) DEFAULT '数据可视化展示大屏',
logo_url TEXT, logo_url TEXT,
logo_url_dark TEXT,
favicon_url TEXT,
default_theme VARCHAR(20) DEFAULT 'dark', default_theme VARCHAR(20) DEFAULT 'dark',
show_95_bandwidth TINYINT(1) DEFAULT 0, show_95_bandwidth TINYINT(1) DEFAULT 0,
p95_type VARCHAR(20) DEFAULT 'tx', p95_type VARCHAR(20) DEFAULT 'tx',

View File

@@ -262,6 +262,8 @@ app.post('/api/setup/init', async (req, res) => {
page_name VARCHAR(255) DEFAULT '数据可视化展示大屏', page_name VARCHAR(255) DEFAULT '数据可视化展示大屏',
title VARCHAR(255) DEFAULT '数据可视化展示大屏', title VARCHAR(255) DEFAULT '数据可视化展示大屏',
logo_url TEXT, logo_url TEXT,
logo_url_dark TEXT,
favicon_url TEXT,
default_theme VARCHAR(20) DEFAULT 'dark', default_theme VARCHAR(20) DEFAULT 'dark',
show_95_bandwidth TINYINT(1) DEFAULT 0, show_95_bandwidth TINYINT(1) DEFAULT 0,
p95_type VARCHAR(20) DEFAULT 'tx', p95_type VARCHAR(20) DEFAULT 'tx',
@@ -409,7 +411,9 @@ const serveIndex = async (req, res) => {
let settings = { let settings = {
page_name: '数据可视化展示大屏', page_name: '数据可视化展示大屏',
title: '数据可视化展示大屏', title: '数据可视化展示大屏',
logo_url: null, logo_url: null,
logo_url_dark: null,
favicon_url: null,
default_theme: 'dark', default_theme: 'dark',
blackbox_source_id: null, blackbox_source_id: null,
latency_source: null, latency_source: null,
@@ -563,6 +567,8 @@ app.get('/api/settings', async (req, res) => {
page_name: '数据可视化展示大屏', page_name: '数据可视化展示大屏',
title: '数据可视化展示大屏', title: '数据可视化展示大屏',
logo_url: null, logo_url: null,
logo_url_dark: null,
favicon_url: null,
show_95_bandwidth: 0, show_95_bandwidth: 0,
p95_type: 'tx', p95_type: 'tx',
blackbox_source_id: null, blackbox_source_id: null,
@@ -582,15 +588,17 @@ app.get('/api/settings', async (req, res) => {
// Update site settings // Update site settings
app.post('/api/settings', requireAuth, async (req, res) => { app.post('/api/settings', requireAuth, async (req, res) => {
const { page_name, title, logo_url, default_theme, show_95_bandwidth, p95_type, blackbox_source_id, latency_source, latency_dest, latency_target, icp_filing, ps_filing } = req.body; const { page_name, title, logo_url, logo_url_dark, favicon_url, default_theme, show_95_bandwidth, p95_type, blackbox_source_id, latency_source, latency_dest, latency_target, icp_filing, ps_filing } = req.body;
try { try {
await db.query( await db.query(
`INSERT INTO site_settings (id, page_name, title, logo_url, default_theme, show_95_bandwidth, p95_type, blackbox_source_id, latency_source, latency_dest, latency_target, icp_filing, ps_filing) `INSERT INTO site_settings (id, page_name, title, logo_url, logo_url_dark, favicon_url, default_theme, show_95_bandwidth, p95_type, blackbox_source_id, latency_source, latency_dest, latency_target, icp_filing, ps_filing)
VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
page_name = VALUES(page_name), page_name = VALUES(page_name),
title = VALUES(title), title = VALUES(title),
logo_url = VALUES(logo_url), logo_url = VALUES(logo_url),
logo_url_dark = VALUES(logo_url_dark),
favicon_url = VALUES(favicon_url),
default_theme = VALUES(default_theme), default_theme = VALUES(default_theme),
show_95_bandwidth = VALUES(show_95_bandwidth), show_95_bandwidth = VALUES(show_95_bandwidth),
p95_type = VALUES(p95_type), p95_type = VALUES(p95_type),
@@ -601,7 +609,7 @@ app.post('/api/settings', requireAuth, async (req, res) => {
icp_filing = VALUES(icp_filing), icp_filing = VALUES(icp_filing),
ps_filing = VALUES(ps_filing)`, ps_filing = VALUES(ps_filing)`,
[ [
page_name, title, logo_url, default_theme, page_name, title, logo_url, logo_url_dark, favicon_url, default_theme,
show_95_bandwidth ? 1 : 0, p95_type || 'tx', show_95_bandwidth ? 1 : 0, p95_type || 'tx',
blackbox_source_id || null, latency_source || null, blackbox_source_id || null, latency_source || null,
latency_dest || null, latency_target || null, latency_dest || null, latency_target || null,