From 7285717e346cef0ed8f59aa6d057b7386e793846 Mon Sep 17 00:00:00 2001 From: sagitchu Date: Fri, 27 Feb 2026 18:23:18 +0800 Subject: [PATCH] feat(frontend): improve favicon loading with fallback to config API - Add synchronous config API call in index.html when localStorage cache is empty - Prevents favicon flash on login page during first load - Enhance getCachedConfigs() to fetch public configs as fallback - Preserve existing siteConfig values when config keys are missing --- vite-frontend/index.html | 29 +++++++++++++++ vite-frontend/src/config/site.ts | 62 +++++++++++++++++++++++++++++--- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/vite-frontend/index.html b/vite-frontend/index.html index 66a6184..a50be27 100644 --- a/vite-frontend/index.html +++ b/vite-frontend/index.html @@ -22,6 +22,35 @@ cachedFavicon = ''; } + // 无缓存时,尝试通过公开配置接口同步读取,避免登录页闪烁 + if (!cachedFavicon.trim()) { + try { + const xhr = new XMLHttpRequest(); + xhr.open('POST', '/api/v1/config/get', false); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.send(JSON.stringify({ name: 'app_favicon' })); + + if (xhr.status === 200) { + const resp = JSON.parse(xhr.responseText || '{}'); + const value = + resp && + typeof resp === 'object' && + resp.code === 0 && + resp.data && + typeof resp.data.value === 'string' + ? resp.data.value.trim() + : ''; + + if (value) { + cachedFavicon = value; + try { + localStorage.setItem('vite_config_app_favicon', value); + } catch (_) {} + } + } + } catch (_) {} + } + const faviconHref = cachedFavicon.trim() || defaultFavicon; let faviconLink = document.head.querySelector('link#app-favicon'); diff --git a/vite-frontend/src/config/site.ts b/vite-frontend/src/config/site.ts index 383830a..f9b8245 100644 --- a/vite-frontend/src/config/site.ts +++ b/vite-frontend/src/config/site.ts @@ -128,6 +128,33 @@ export const getCachedConfigs = async (): Promise> => { } }); + const fetchPublicConfigs = async (): Promise> => { + const publicConfigMap: Record = {}; + + await Promise.all( + configKeys.map(async (key) => { + try { + const response = await getConfigByName(key); + + if ( + response.code === 0 && + response.data && + typeof response.data.value === "string" + ) { + const value = response.data.value; + + publicConfigMap[key] = value; + configCache.set(key, value); + } + } catch { + // ignore single key fetch error + } + }), + ); + + return publicConfigMap; + }; + // 从API获取最新配置 try { const response = await getConfigs(); @@ -142,14 +169,20 @@ export const getCachedConfigs = async (): Promise> => { return configs; } + + if (hasCachedData) { + return cachedConfigs; + } + + return await fetchPublicConfigs(); } catch { // API失败时返回缓存的数据 if (hasCachedData) { return cachedConfigs; } - } - return {}; + return await fetchPublicConfigs(); + } }; const updateDocumentFavicon = (faviconUrl: string) => { @@ -210,9 +243,28 @@ export const updateSiteConfig = async (configMap?: Record) => { configCache.set(key, String(value)); }); - const appName = (resolvedConfigMap.app_name || "").trim(); - const appLogo = (resolvedConfigMap.app_logo || "").trim(); - const appFavicon = (resolvedConfigMap.app_favicon || "").trim(); + const hasAppName = Object.prototype.hasOwnProperty.call( + resolvedConfigMap, + "app_name", + ); + const hasAppLogo = Object.prototype.hasOwnProperty.call( + resolvedConfigMap, + "app_logo", + ); + const hasAppFavicon = Object.prototype.hasOwnProperty.call( + resolvedConfigMap, + "app_favicon", + ); + + const appName = hasAppName + ? String(resolvedConfigMap.app_name || "").trim() + : siteConfig.name; + const appLogo = hasAppLogo + ? String(resolvedConfigMap.app_logo || "").trim() + : (siteConfig.app_logo || "").trim(); + const appFavicon = hasAppFavicon + ? String(resolvedConfigMap.app_favicon || "").trim() + : (siteConfig.app_favicon || "").trim(); if (appName && appName !== siteConfig.name) { siteConfig.name = appName;