From 2490d8920dcdf44333f88227f580fc0826972480 Mon Sep 17 00:00:00 2001 From: ssongliu Date: Wed, 5 Aug 2026 11:55:12 +0800 Subject: [PATCH 1/2] fix: improve community restore loading state --- .../views/setting/license-required/index.vue | 172 +++++++++++++++--- 1 file changed, 149 insertions(+), 23 deletions(-) diff --git a/frontend/src/views/setting/license-required/index.vue b/frontend/src/views/setting/license-required/index.vue index e18a75798578..a599b1448656 100644 --- a/frontend/src/views/setting/license-required/index.vue +++ b/frontend/src/views/setting/license-required/index.vue @@ -1,10 +1,5 @@ @@ -130,7 +139,6 @@ const { isEnterpriseLicensed, isFxplay, isIntl, - isLoading: restoring, isLogin, isMasterProductPro, isOffline, @@ -140,6 +148,7 @@ const { themeConfig, } = useGlobalStore(); const loading = ref(false); +const restoring = ref(isOnRestart.value); const communityRestoreRef = ref>(); const uploadRef = ref(); const uploaderFiles = ref([]); @@ -205,10 +214,6 @@ const applyLoginThemeColor = () => { '--login-btn-link-hover-color', adjustColorToRGBA(loginBtnLinkColor, -10, 80), ); - document.documentElement.style.setProperty( - '--login-loading-mask-color', - adjustColorToRGBA(loginBtnLinkColor, 0, 60), - ); }; const loadLoginTheme = async () => { @@ -400,6 +405,19 @@ onMounted(async () => { window.addEventListener('resize', updateSize); loading.value = true; try { + try { + await loadRestoreInfo(); + } catch { + if (restoring.value) { + await waitForCommunityService(); + return; + } + } + if (restoring.value) { + scheduleRestorePoll(); + return; + } + const shouldLoadLicense = await loadLoginTheme(); if (!shouldLoadLicense) { return; @@ -407,12 +425,6 @@ onMounted(async () => { await loadXpackLoginTheme(); await loadBackground(); await loadLicenseInfo(); - if (!isEnterpriseLicensed.value) { - await loadRestoreInfo(); - if (restoring.value) { - scheduleRestorePoll(); - } - } } finally { loading.value = false; } @@ -429,6 +441,10 @@ onUnmounted(() => { border-radius: 0; } +.license-required-page { + isolation: isolate; +} + .community-restore-link { position: absolute; top: 16px; @@ -554,15 +570,125 @@ onUnmounted(() => { line-height: 20px; } -:deep(.el-loading-mask) { - background-color: var(--login-loading-mask-color) !important; +.restore-mask { + position: fixed; + z-index: 2000; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + background: rgb(15 23 42 / 48%); + backdrop-filter: blur(8px) saturate(90%); +} + +.restore-status-card { + display: flex; + width: min(400px, 100%); + flex-direction: column; + align-items: center; + padding: 32px 36px; + border: 1px solid rgb(255 255 255 / 70%); + border-radius: 16px; + background: rgb(255 255 255 / 96%); + box-shadow: + 0 24px 64px rgb(15 23 42 / 24%), + 0 4px 16px rgb(15 23 42 / 10%); + text-align: center; +} - .el-loading-spinner .path { - stroke: #ffffff; +.restore-spinner { + position: relative; + width: 54px; + height: 54px; + margin-bottom: 22px; + + &::before, + &::after { + position: absolute; + inset: 0; + border: 3px solid rgb(148 163 184 / 22%); + border-radius: 50%; + content: ''; } - .el-loading-text { - color: #ffffff; + &::after { + border-color: var(--login-btn-link-color, #005eeb) transparent transparent; + animation: restore-spin 0.9s linear infinite; + } + + span { + position: absolute; + inset: 19px; + border-radius: 50%; + background: var(--login-btn-link-color, #005eeb); + box-shadow: 0 0 0 6px color-mix(in srgb, var(--login-btn-link-color, #005eeb) 10%, transparent); + animation: restore-pulse 1.5s ease-in-out infinite; + } +} + +.restore-status-title { + color: var(--el-text-color-primary, #1f2937); + font-size: 18px; + font-weight: 600; + line-height: 26px; +} + +.restore-status-description { + max-width: 320px; + margin-top: 8px; + color: var(--el-text-color-secondary, #64748b); + font-size: 14px; + line-height: 22px; +} + +.restore-mask-enter-active, +.restore-mask-leave-active { + transition: opacity 0.22s ease; + + .restore-status-card { + transition: + opacity 0.22s ease, + transform 0.22s ease; + } +} + +.restore-mask-enter-from, +.restore-mask-leave-to { + opacity: 0; + + .restore-status-card { + opacity: 0; + transform: translateY(8px) scale(0.98); + } +} + +@keyframes restore-spin { + to { + transform: rotate(360deg); + } +} + +@keyframes restore-pulse { + 0%, + 100% { + opacity: 0.75; + transform: scale(0.9); + } + + 50% { + opacity: 1; + transform: scale(1); + } +} + +@media (prefers-reduced-motion: reduce) { + .restore-spinner::after { + animation-duration: 1.8s; + } + + .restore-spinner span { + animation: none; } } From afaa6c14d21153f547e51f4e89249b4160c98e2c Mon Sep 17 00:00:00 2001 From: ssongliu Date: Wed, 5 Aug 2026 13:39:47 +0800 Subject: [PATCH 2/2] fix: resume license page initialization after restore --- .../views/setting/license-required/index.vue | 48 +++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/frontend/src/views/setting/license-required/index.vue b/frontend/src/views/setting/license-required/index.vue index a599b1448656..af93dd3f3684 100644 --- a/frontend/src/views/setting/license-required/index.vue +++ b/frontend/src/views/setting/license-required/index.vue @@ -156,6 +156,8 @@ const licenseInfo = reactive({ deviceID: '', }); let restoreTimer: number | undefined; +let pageInitialized = false; +let pageInitialization: Promise | undefined; const defaultLoginImage = new URL('@/assets/images/1panel-login-enterprise.png', import.meta.url).href; const defaultLoginBgImage = new URL('@/assets/images/1panel-login-bg.jpg', import.meta.url).href; const loadedLoginImage = ref(null); @@ -288,6 +290,40 @@ const loadLicenseInfo = async () => { isEnterpriseLicensed.value = false; }; +const initializePage = () => { + if (pageInitialized) { + return Promise.resolve(); + } + if (pageInitialization) { + return pageInitialization; + } + + loading.value = true; + pageInitialization = (async () => { + const shouldLoadLicense = await loadLoginTheme(); + if (!shouldLoadLicense) { + pageInitialized = true; + return; + } + await loadXpackLoginTheme(); + await loadBackground(); + await loadLicenseInfo(); + pageInitialized = true; + })().finally(() => { + loading.value = false; + pageInitialization = undefined; + }); + return pageInitialization; +}; + +const resumePageInitialization = async () => { + try { + await initializePage(); + } catch { + // Request errors are already presented by the shared API interceptor. + } +}; + const loadRestoreInfo = async () => { const res = await getCommunityRestoreStatus(); restoring.value = res.data.state === 'Running'; @@ -336,13 +372,16 @@ const pollRestoreStatus = async () => { restoring.value = false; isOnRestart.value = false; MsgError(res.data.message); + await resumePageInitialization(); return; } restoring.value = res.data.state === 'Running'; isOnRestart.value = restoring.value; if (restoring.value) { scheduleRestorePoll(); + return; } + await resumePageInitialization(); } catch { await waitForCommunityService(); } @@ -417,17 +456,10 @@ onMounted(async () => { scheduleRestorePoll(); return; } - - const shouldLoadLicense = await loadLoginTheme(); - if (!shouldLoadLicense) { - return; - } - await loadXpackLoginTheme(); - await loadBackground(); - await loadLicenseInfo(); } finally { loading.value = false; } + await initializePage(); }); onUnmounted(() => {