[PROMOTE] 晉升 dev 變更至 demo 環境

1. 晉升提交 610899d:優化後台管理系統側邊欄佈局與全局頁面跳轉載入機制。
   - 解決 Alpine.js 載入前的排版跳動 (CLS/FOUC)。
   - 精確過濾站內實際跳轉,避免 Loading 畫面卡死與誤觸。
   - 增加平滑的淡入淡出 Transition 與高斯模糊效果。
This commit is contained in:
sky121113 2026-06-01 09:51:51 +08:00
commit 6885b1a99f
2 changed files with 74 additions and 34 deletions

View File

@ -1,25 +1,12 @@
<div x-data="{
loading: true
loading: false
}"
x-on:show-global-loading.window="loading = true"
x-on:hide-global-loading.window="loading = false"
x-init="
const urlParams = new URLSearchParams(window.location.search);
const simulateDelay = urlParams.get('simulate_loading');
const delay = simulateDelay ? parseInt(simulateDelay) : 300;
const hideLoading = () => setTimeout(() => loading = false, delay);
if (document.readyState === 'complete') {
hideLoading();
} else {
window.addEventListener('load', hideLoading);
// 安全保險:模擬模式下為 30 秒,正常模式下為 5 秒
const safetyTimeout = simulateDelay ? 30000 : 5000;
setTimeout(hideLoading, safetyTimeout);
}
"
x-show="loading"
x-transition:enter="transition ease-out duration-150"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"

View File

@ -26,6 +26,12 @@
} else {
document.documentElement.classList.remove('dark');
}
window.applyAdminSidebarLayout = (collapsed) => {
document.documentElement.style.setProperty('--admin-sidebar-offset', collapsed ? '6rem' : '256px');
document.documentElement.style.setProperty('--admin-sidebar-width', collapsed ? '60px' : '14rem');
};
window.applyAdminSidebarLayout(localStorage.getItem('sidebarCollapsed') === 'true');
</script>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
@ -36,7 +42,12 @@
sidebarOpen: false,
sidebarCollapsed: localStorage.getItem('sidebarCollapsed') === 'true',
userDropdownOpen: false
}" x-init="$watch('sidebarCollapsed', value => localStorage.setItem('sidebarCollapsed', value))">
}" x-init="
$watch('sidebarCollapsed', value => {
localStorage.setItem('sidebarCollapsed', value);
window.applyAdminSidebarLayout?.(value);
})
">
<!-- Option A: Loading Screen -->
<x-loading-screen />
@ -44,14 +55,59 @@
<div id="top-loading-bar" class="top-loading-bar"></div>
<script>
// 僅保留最基本的導航列觸發,不使用全螢幕遮罩防止卡死
window.addEventListener('beforeunload', () => {
document.getElementById('top-loading-bar').classList.add('loading');
});
(() => {
const topLoadingBar = () => document.getElementById('top-loading-bar');
const showPageLoading = () => {
topLoadingBar()?.classList.remove('loading');
window.dispatchEvent(new CustomEvent('show-global-loading'));
};
const hidePageLoading = () => {
topLoadingBar()?.classList.remove('loading');
window.dispatchEvent(new CustomEvent('hide-global-loading'));
};
const shouldShowPageLoading = (event, link) => {
if (
event.defaultPrevented ||
event.button !== 0 ||
event.metaKey ||
event.ctrlKey ||
event.shiftKey ||
event.altKey
) {
return false;
}
window.addEventListener('pageshow', () => {
document.getElementById('top-loading-bar').classList.remove('loading');
});
if (
!link.href ||
link.hasAttribute('download') ||
link.target && link.target !== '_self'
) {
return false;
}
const url = new URL(link.href, window.location.href);
const isHashOnlyNavigation = url.pathname === window.location.pathname &&
url.search === window.location.search &&
url.hash;
return url.origin === window.location.origin &&
url.href !== window.location.href &&
!isHashOnlyNavigation &&
!url.href.startsWith('javascript:');
};
document.addEventListener('click', (event) => {
const link = event.target.closest('a[href]');
if (!link || !shouldShowPageLoading(event, link)) {
return;
}
showPageLoading();
});
window.addEventListener('beforeunload', showPageLoading);
window.addEventListener('pageshow', hidePageLoading);
})();
</script>
<!-- Sidebar Overlay (Mobile) -->
@ -63,8 +119,7 @@
<!-- ========== HEADER ========== -->
<header
class="sticky top-0 inset-x-0 flex flex-wrap sm:justify-start sm:flex-nowrap z-[48] w-full bg-white/80 backdrop-blur-md border-b border-slate-200/50 text-sm py-2.5 sm:py-4 lg:pl-[256px] dark:bg-[#0f172a]/80 dark:border-slate-800/50 shadow-sm transition-all duration-300"
:class="sidebarCollapsed ? 'lg:pl-24' : 'lg:pl-[256px]'">
class="sticky top-0 inset-x-0 flex flex-wrap sm:justify-start sm:flex-nowrap z-[48] w-full bg-white/80 backdrop-blur-md border-b border-slate-200/50 text-sm py-2.5 sm:py-4 lg:pl-[var(--admin-sidebar-offset)] dark:bg-[#0f172a]/80 dark:border-slate-800/50 shadow-sm transition-all duration-300">
<nav class="flex basis-full items-center w-full mx-auto pl-4 lg:pl-0 pr-4 sm:pr-6 md:pr-8" aria-label="Global">
<div class="mr-5 lg:mr-0 lg:hidden text-center">
<a class="flex items-center gap-x-2 flex-none text-xl font-bold dark:text-white font-display tracking-tight"
@ -384,10 +439,9 @@
<!-- Sidebar -->
<div id="application-sidebar"
class="fixed top-0 left-0 bottom-0 z-[60] bg-white dark:bg-[#0f172a] pt-5 pb-10 border-e border-slate-200 dark:border-none overflow-y-auto overflow-x-hidden transition-all duration-300 transform lg:translate-x-0 shadow-xl dark:shadow-2xl flex flex-col"
class="fixed top-0 left-0 bottom-0 z-[60] w-[var(--admin-sidebar-width)] bg-white dark:bg-[#0f172a] pt-5 pb-10 border-e border-slate-200 dark:border-none overflow-y-auto overflow-x-hidden transition-all duration-300 transform lg:translate-x-0 shadow-xl dark:shadow-2xl flex flex-col"
:class="[
sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0',
sidebarCollapsed ? 'w-[60px]' : 'w-56'
sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'
]" x-init="$nextTick(() => {
const activeItem = $el.querySelector('.active, .bg-slate-100, .dark\\:bg-white\\/5');
if (activeItem) {
@ -441,10 +495,9 @@
<!-- End Sidebar -->
<!-- Content -->
<div class="w-full pt-4 lg:pt-5 pb-12 px-4 sm:px-6 md:px-8 transition-all duration-300"
:class="sidebarCollapsed ? 'lg:pl-24' : 'lg:pl-[256px]'">
<div class="w-full pt-4 lg:pt-5 pb-12 px-4 sm:px-6 md:px-8 lg:pl-[var(--admin-sidebar-offset)]">
<x-breadcrumbs class="mb-4 hidden lg:flex" />
<main class="animate-fade-up">
<main>
@yield('content')
</main>
</div>
@ -459,4 +512,4 @@
@yield('scripts')
</body>
</html>
</html>