diff --git a/frontend/src/layout/components/Sidebar/components/Logo.vue b/frontend/src/layout/components/Sidebar/components/Logo.vue
index 34ee9251e..1e1f1b1ad 100644
--- a/frontend/src/layout/components/Sidebar/components/Logo.vue
+++ b/frontend/src/layout/components/Sidebar/components/Logo.vue
@@ -2,19 +2,21 @@
@@ -26,9 +28,12 @@ import router from '@/routers';
import { GlobalStore } from '@/store';
import PrimaryLogo from '@/assets/images/1panel-logo.svg?component';
import MenuLogo from '@/assets/images/1panel-menu-logo.svg?component';
+import { ref } from 'vue';
defineProps<{ isCollapse: boolean }>();
+const logoLoadFailed = ref(false);
+const logoWithTextLoadFailed = ref(false);
const globalStore = GlobalStore();
const goHome = () => {
diff --git a/frontend/src/utils/util.ts b/frontend/src/utils/util.ts
index 0e6d076de..5dc6de0b1 100644
--- a/frontend/src/utils/util.ts
+++ b/frontend/src/utils/util.ts
@@ -810,3 +810,12 @@ export const toLink = (link: string) => {
window.open(link, '_blank');
} catch (e) {}
};
+
+export const preloadImage = (url: string): Promise
=> {
+ return new Promise((resolve) => {
+ const img = new Image();
+ img.onload = () => resolve(url);
+ img.onerror = () => resolve(url);
+ img.src = url;
+ });
+};
diff --git a/frontend/src/utils/xpack.ts b/frontend/src/utils/xpack.ts
index dbc33f59a..ce82b6c92 100644
--- a/frontend/src/utils/xpack.ts
+++ b/frontend/src/utils/xpack.ts
@@ -13,30 +13,44 @@ export function resetXSetting() {
export function initFavicon() {
document.title = globalStore.themeConfig.panelName;
- let favicon = globalStore.themeConfig.favicon;
- const link = (document.querySelector("link[rel*='icon']") || document.createElement('link')) as HTMLLinkElement;
- link.type = 'image/x-icon';
- link.rel = 'shortcut icon';
- let goldLink = new URL(`../assets/images/favicon.svg`, import.meta.url).href;
- if (globalStore.isProductPro) {
- const themeColor = globalStore.themeConfig.primary;
- const svg = `
-
- `;
- goldLink = `data:image/svg+xml,${encodeURIComponent(svg)}`;
- link.href = favicon ? `/api/v2/images/favicon?t=${Date.now()}` : goldLink;
+ const favicon = globalStore.themeConfig.favicon;
+ const isPro = globalStore.isProductPro;
+ const themeColor = globalStore.themeConfig.primary;
+
+ const customFaviconUrl = `/api/v2/images/favicon?t=${Date.now()}`;
+ const fallbackSvg = isPro
+ ? `data:image/svg+xml,${encodeURIComponent(`
+
+ `)}`
+ : '/public/favicon.png';
+
+ const setLink = (href: string) => {
+ let link = document.querySelector("link[rel*='icon']") as HTMLLinkElement;
+ if (!link) {
+ link = document.createElement('link');
+ link.rel = 'shortcut icon';
+ link.type = 'image/x-icon';
+ document.head.appendChild(link);
+ }
+ link.href = href;
+ };
+
+ if (favicon) {
+ const testImg = new Image();
+ testImg.onload = () => setLink(customFaviconUrl);
+ testImg.onerror = () => setLink(fallbackSvg);
+ testImg.src = customFaviconUrl;
} else {
- link.href = favicon ? `/api/v2/images/favicon?t=${Date.now()}` : '/public/favicon.png';
+ setLink(fallbackSvg);
}
- document.head.appendChild(link);
}
export async function getXpackSetting() {
diff --git a/frontend/src/views/login/index.vue b/frontend/src/views/login/index.vue
index b2d0632eb..343ecda94 100644
--- a/frontend/src/views/login/index.vue
+++ b/frontend/src/views/login/index.vue
@@ -1,13 +1,6 @@
-
+
@@ -32,10 +28,20 @@
import LoginForm from './components/login-form.vue';
import { ref, onMounted } from 'vue';
import { GlobalStore } from '@/store';
+import { preloadImage } from '@/utils/util';
const globalStore = GlobalStore();
const backgroundOpacity = ref(1);
+const defaultLoginImage = new URL('@/assets/images/1panel-login.jpg', import.meta.url).href;
+const defaultLoginBgImage = new URL('@/assets/images/1panel-login-bg.jpg', import.meta.url).href;
+const loadedLoginImage = ref
(null);
+const loadedBackgroundImage = ref(null);
+const backgroundStyle = ref<{ backgroundImage?: string; backgroundColor?: string }>({});
+const imgLoaded = ref(false);
+function onImgLoad() {
+ imgLoaded.value = true;
+}
const mySafetyCode = defineProps({
code: {
type: String,
@@ -51,29 +57,52 @@ const getStatus = async () => {
};
const loadImage = (name: string) => {
- switch (name) {
- case 'loginImage':
- if (globalStore.themeConfig.loginImage === 'loginImage') {
- return `/api/v2/images/loginImage?t=${Date.now()}`;
- }
- return new URL('@/assets/images/1panel-login.jpg', import.meta.url).href;
- case 'loginBackground':
- if (globalStore.themeConfig.loginBgType === 'image') {
- if (globalStore.themeConfig.loginBackground === 'loginBackground') {
- return `/api/v2/images/loginBackground?t=${Date.now()}`;
- }
- return new URL('@/assets/images/1panel-login-bg.jpg', import.meta.url).href;
- } else if (globalStore.themeConfig.loginBgType === 'color') {
- return globalStore.themeConfig.loginBackground;
- } else {
- return new URL('@/assets/images/1panel-login-bg.jpg', import.meta.url).href;
- }
+ const { loginImage, loginBackground, loginBgType } = globalStore.themeConfig;
+ if (name === 'loginImage') {
+ return loginImage === 'loginImage' ? loadedLoginImage.value : defaultLoginImage;
+ }
+ if (name === 'loginBackground') {
+ if (loginBgType === 'image') {
+ return loginBackground === 'loginBackground' ? loadedBackgroundImage.value : defaultLoginBgImage;
+ }
+ if (loginBgType === 'color') {
+ return loginBackground;
+ }
+ return defaultLoginBgImage;
}
return '';
};
-onMounted(() => {
- getStatus();
+const onImgError = (event: any) => {
+ event.target.src = defaultLoginImage;
+ imgLoaded.value = true;
+};
+
+onMounted(async () => {
+ await getStatus();
+ const loginImageUrl = `/api/v2/images/loginImage?t=${Date.now()}`;
+ const backgroundImageUrl = `/api/v2/images/loginBackground?t=${Date.now()}`;
+ loadedLoginImage.value = await preloadImage(loginImageUrl);
+ loadedBackgroundImage.value = await preloadImage(backgroundImageUrl);
+ if (globalStore.themeConfig.loginBgType === 'color') {
+ backgroundStyle.value = {
+ backgroundColor: globalStore.themeConfig.loginBackground,
+ };
+ } else {
+ const img = new Image();
+ const url = loadImage('loginBackground');
+ img.onload = () => {
+ backgroundStyle.value = {
+ backgroundImage: `url(${url})`,
+ };
+ };
+ img.onerror = () => {
+ backgroundStyle.value = {
+ backgroundImage: `url(${defaultLoginBgImage})`, // 你定义的默认图
+ };
+ };
+ img.src = url;
+ }
});
const FIXED_WIDTH = 1000;
diff --git a/frontend/src/views/setting/about/index.vue b/frontend/src/views/setting/about/index.vue
index d8c57ea03..aee283596 100644
--- a/frontend/src/views/setting/about/index.vue
+++ b/frontend/src/views/setting/about/index.vue
@@ -5,9 +5,11 @@
@@ -49,6 +51,7 @@ import { storeToRefs } from 'pinia';
const globalStore = GlobalStore();
const { docsUrl } = storeToRefs(globalStore);
const loading = ref();
+const logoLoadFailed = ref(false);
const toDoc = () => {
window.open(docsUrl.value, '_blank', 'noopener,noreferrer');