fix: Fix image loading issue (#9164)

This commit is contained in:
2025-06-18 15:16:57 +08:00 committed by GitHub
parent 8b22acce27
commit 47547ad4e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 112 additions and 52 deletions

View file

@ -2,19 +2,21 @@
<div class="logo" style="cursor: pointer" @click="goHome">
<template v-if="isCollapse">
<img
v-if="globalStore.themeConfig.logo"
v-if="globalStore.themeConfig.logo && !logoLoadFailed"
:src="`/api/v2/images/logo?t=${Date.now()}`"
style="cursor: pointer"
alt="logo"
@error="logoLoadFailed = true"
/>
<MenuLogo v-else />
</template>
<template v-else>
<img
v-if="globalStore.themeConfig.logoWithText"
v-if="globalStore.themeConfig.logoWithText && !logoWithTextLoadFailed"
:src="`/api/v2/images/logoWithText?t=${Date.now()}`"
style="cursor: pointer"
alt="logo"
@error="logoWithTextLoadFailed = true"
/>
<PrimaryLogo v-else />
</template>
@ -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 = () => {

View file

@ -810,3 +810,12 @@ export const toLink = (link: string) => {
window.open(link, '_blank');
} catch (e) {}
};
export const preloadImage = (url: string): Promise<string> => {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => resolve(url);
img.onerror = () => resolve(url);
img.src = url;
});
};

View file

@ -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 = `
<svg width="24" height="24" viewBox="0 0 24 24" fill="${themeColor}" xmlns="http://www.w3.org/2000/svg">
<path d="M11.1451 18.8875L5.66228 15.7224V8.40336L3.5376 7.1759V16.9488L9.02038 20.114L11.1451 18.8875Z" />
<path d="M18.3397 15.7224L12.0005 19.3819L9.87683 20.6083L12.0005 21.8348L20.4644 16.9488L18.3397 15.7224Z" />
<path d="M12.0015 4.74388L14.1252 3.5174L12.0005 2.28995L3.5376 7.17591L5.66228 8.40337L12.0005 4.74388H12.0015Z" />
<path d="M14.9816 4.01077L12.8569 5.23723L18.3397 8.40336V15.7224L20.4634 16.9488V7.1759L14.9816 4.01077Z" />
<path d="M11.9995 1.02569L21.5576 6.54428V17.5795L11.9995 23.0971L2.44343 17.5795V6.54428L11.9995 1.02569ZM11.9995 0.72728L2.18182 6.39707V17.7366L11.9995 23.4064L21.8182 17.7366V6.39707L11.9995 0.72728Z" />
<path d="M12.3079 6.78001L12.9564 7.16695V17.105L12.3079 17.48V6.78001Z" />
<path d="M12.3078 6.78001L9.10889 8.6222V9.86954H10.2359V16.2854L12.3059 17.481L12.3078 6.78001Z" />
</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(`
<svg width="24" height="24" viewBox="0 0 24 24" fill="${themeColor}" xmlns="http://www.w3.org/2000/svg">
<path d="M11.1451 18.8875L5.66228 15.7224V8.40336L3.5376 7.1759V16.9488L9.02038 20.114L11.1451 18.8875Z" />
<path d="M18.3397 15.7224L12.0005 19.3819L9.87683 20.6083L12.0005 21.8348L20.4644 16.9488L18.3397 15.7224Z" />
<path d="M12.0015 4.74388L14.1252 3.5174L12.0005 2.28995L3.5376 7.17591L5.66228 8.40337L12.0005 4.74388H12.0015Z" />
<path d="M14.9816 4.01077L12.8569 5.23723L18.3397 8.40336V15.7224L20.4634 16.9488V7.1759L14.9816 4.01077Z" />
<path d="M11.9995 1.02569L21.5576 6.54428V17.5795L11.9995 23.0971L2.44343 17.5795V6.54428L11.9995 1.02569ZM11.9995 0.72728L2.18182 6.39707V17.7366L11.9995 23.4064L21.8182 17.7366V6.39707L11.9995 0.72728Z" />
<path d="M12.3079 6.78001L12.9564 7.16695V17.105L12.3079 17.48V6.78001Z" />
<path d="M12.3078 6.78001L9.10889 8.6222V9.86954H10.2359V16.2854L12.3059 17.481L12.3078 6.78001Z" />
</svg>
`)}`
: '/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() {

View file

@ -1,13 +1,6 @@
<template>
<div class="flex items-center justify-center min-h-screen relative bg-gray-100">
<div
class="absolute inset-0 bg-cover bg-center bg-no-repeat"
:style="
globalStore.themeConfig.loginBgType === 'color'
? { backgroundColor: globalStore.themeConfig.loginBackground }
: { backgroundImage: `url(${loadImage('loginBackground')})` }
"
></div>
<div class="absolute inset-0 bg-cover bg-center bg-no-repeat" :style="backgroundStyle"></div>
<div
:style="{ opacity: backgroundOpacity, width: containerWidth, height: containerHeight }"
class="bg-white shadow-lg relative z-10 border border-gray-200 flex overflow-hidden"
@ -15,9 +8,12 @@
<div class="grid grid-cols-1 md:grid-cols-2 items-stretch w-full">
<div v-if="showLogo" class="flex justify-center" :style="{ height: containerHeight }">
<img
v-show="imgLoaded"
:src="loadImage('loginImage')"
class="max-w-full max-h-full object-cover bg-cover bg-center"
alt="1panel"
@load="onImgLoad"
@error="onImgError"
/>
</div>
<div :class="loginFormClass">
@ -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<string | null>(null);
const loadedBackgroundImage = ref<string | null>(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;

View file

@ -5,9 +5,11 @@
<div style="text-align: center; margin-top: 20px">
<div style="justify-self: center" class="logo">
<img
v-if="globalStore.themeConfig.logo"
v-if="globalStore.themeConfig.logo && !logoLoadFailed"
style="width: 80px"
:src="`/api/v2/images/logo?t=${Date.now()}`"
@error="logoLoadFailed = true"
alt=""
/>
<PrimaryLogo v-else />
</div>
@ -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');