| --- |
| import OverlayWallpaper from "@components/features/OverlayWallpaper.astro"; |
| import Navbar from "@components/layout/Navbar.astro"; |
| import ImageWrapper from "@/components/common/ImageWrapper.astro"; |
| import { backgroundWallpaper, siteConfig } from "@/config"; |
| import { getBackgroundImages } from "@/utils/layout-utils"; |
| import Layout from "./Layout.astro"; |
|
|
| interface Props { |
| title?: string; |
| description?: string; |
| } |
|
|
| const { title, description } = Astro.props; |
| const backgroundImages = getBackgroundImages(); |
| const bannerImage = |
| typeof backgroundImages.desktop === "string" |
| ? backgroundImages.desktop |
| : typeof backgroundImages.mobile === "string" |
| ? backgroundImages.mobile |
| : ""; |
| const isOverlayMode = backgroundWallpaper.mode === "overlay"; |
| const isWallpaperSwitchable = backgroundWallpaper.switchable ?? true; |
| --- |
|
|
| <Layout title={title} description={description}> |
| {(isWallpaperSwitchable || isOverlayMode) && ( |
| <OverlayWallpaper |
| config={{ |
| src: { |
| desktop: |
| typeof backgroundImages.desktop === "string" |
| ? backgroundImages.desktop |
| : "", |
| mobile: |
| typeof backgroundImages.mobile === "string" |
| ? backgroundImages.mobile |
| : "", |
| }, |
| zIndex: backgroundWallpaper.overlay?.zIndex, |
| opacity: backgroundWallpaper.overlay?.opacity, |
| blur: backgroundWallpaper.overlay?.blur, |
| }} |
| className={isOverlayMode ? undefined : "hidden opacity-0"} |
| /> |
| )} |
|
|
| <slot slot="head" name="head" /> |
|
|
| <div |
| id="admin-top-row" |
| class:list={[ |
| "z-50 pointer-events-none relative transition-all duration-700 mx-auto", |
| siteConfig.navbar.widthFull ? "" : "max-w-(--page-width) px-0 md:px-4", |
| ]} |
| > |
| <div id="navbar-wrapper" class="pointer-events-auto sticky top-0 transition-all"> |
| <Navbar /> |
| </div> |
| </div> |
|
|
| <section class="relative z-20 w-full px-2 pt-6 md:px-4 md:pt-8"> |
| <div class="mx-auto max-w-(--page-width)"> |
| <div class="card-base admin-hero overflow-hidden"> |
| <div class="admin-hero-frame"> |
| {bannerImage ? ( |
| <ImageWrapper |
| src={bannerImage} |
| alt="Admin banner image" |
| class="admin-hero-media absolute inset-0 h-full w-full" |
| loading="eager" |
| fetchpriority="high" |
| layout="none" |
| widths={[1280, 1920]} |
| sizes="100vw" |
| /> |
| ) : ( |
| <div class="admin-hero-fallback absolute inset-0" /> |
| )} |
| <div class="absolute inset-0 admin-hero-overlay" /> |
| <div class="absolute inset-0 admin-hero-grid gap-5 p-6 text-white md:p-8"> |
| <div class="admin-hero-copy flex max-w-3xl flex-col gap-4"> |
| <div class="inline-flex w-fit items-center gap-2 rounded-full bg-white/15 px-3 py-1 text-sm font-medium backdrop-blur-md"> |
| <span class="h-2.5 w-2.5 rounded-full bg-[oklch(0.75_0.14_var(--hue))]" /> |
| Firefly Admin |
| </div> |
| <div class="max-w-3xl"> |
| <h1 class="banner-title text-3xl font-bold md:text-5xl"> |
| 博客后台管理 |
| </h1> |
| <p class="banner-subtitle mt-3 max-w-2xl text-sm text-white/85 md:text-lg"> |
| 统一管理站点配置、文章、图片与重建状态,界面风格和前台博客保持一致。 |
| </p> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </section> |
|
|
| <main class="relative z-30 -mt-5 w-full px-2 pb-10 pt-0 md:-mt-6 md:px-4"> |
| <div class="mx-auto max-w-(--page-width)"> |
| <slot /> |
| </div> |
| </main> |
| </Layout> |
|
|
| <style> |
| #admin-top-row { |
| height: auto; |
| } |
|
|
| .admin-hero-overlay { |
| background: |
| linear-gradient(135deg, rgb(9 23 30 / 0.86), rgb(18 38 48 / 0.56)), |
| linear-gradient(180deg, rgb(0 0 0 / 0.12), rgb(0 0 0 / 0.42)); |
| } |
|
|
| .admin-hero-frame { |
| position: relative; |
| height: clamp(15rem, 26vw, 20rem); |
| } |
|
|
| .admin-hero-media { |
| position: absolute; |
| inset: 0; |
| } |
|
|
| .admin-hero :global(picture), |
| .admin-hero :global(img[data-astro-image]), |
| .admin-hero :global(img) { |
| display: block; |
| width: 100%; |
| height: 100% !important; |
| max-width: none; |
| object-fit: cover; |
| } |
|
|
| .admin-hero-fallback { |
| background: |
| radial-gradient(circle at top right, rgb(255 255 255 / 0.18), transparent 36%), |
| linear-gradient(135deg, oklch(0.32 0.06 calc(var(--hue) * 1deg)), oklch(0.42 0.08 calc(var(--hue) * 1deg))); |
| } |
|
|
| .admin-hero-grid { |
| display: flex; |
| align-items: flex-start; |
| } |
|
|
| .admin-hero-copy { |
| padding-top: clamp(0.5rem, 2vw, 1.25rem); |
| } |
|
|
| @media (max-width: 959px) { |
| .admin-hero-frame { |
| height: auto; |
| min-height: 14rem; |
| } |
| } |
| </style> |
|
|