--- import Live2DWidget from "@components/features/Live2DWidget.astro"; import OverlayWallpaper from "@components/features/OverlayWallpaper.astro"; import SpineModel from "@components/features/SpineModel.astro"; import Footer from "@components/layout/Footer.astro"; import Navbar from "@components/layout/Navbar.astro"; import SideBar from "@components/layout/SideBar.astro"; import type { MarkdownHeading } from "astro"; import { Icon } from "astro-icon/components"; import ImageWrapper from "@/components/common/ImageWrapper.astro"; import FloatingControls from "@/components/controls/FloatingControls.astro"; import TypewriterText from "@/components/features/TypewriterText.astro"; import { backgroundWallpaper, live2dModelConfig, sidebarLayoutConfig, siteConfig, } from "@/config"; import { BANNER_HEIGHT, BANNER_HEIGHT_EXTEND, MAIN_PANEL_OVERLAPS_BANNER_HEIGHT, } from "@/constants/constants"; import { getImageQuality } from "@/utils/image-utils"; import { getBackgroundImages, isHomePage } from "@/utils/layout-utils"; import { generateGridClasses, generateMainContentClasses, generateRightSidebarClasses, generateSidebarClasses, getResponsiveSidebarConfig, type ResponsiveSidebarConfig, } from "@/utils/responsive-utils"; import Layout from "./Layout.astro"; interface Props { title?: string; banner?: string; description?: string; lang?: string; setOGTypeArticle?: boolean; postSlug?: string; headings?: MarkdownHeading[]; } const backgroundImages = getBackgroundImages(); const { title, banner, description, lang, setOGTypeArticle, postSlug, headings = [], } = Astro.props; // 检查背景壁纸模式和是否允许切换 const isBannerMode = backgroundWallpaper.mode === "banner"; const isOverlayMode = backgroundWallpaper.mode === "overlay"; const isBackgroundEnabled = backgroundWallpaper.mode !== "none"; const isWallpaperSwitchable = backgroundWallpaper.switchable ?? true; // 检查是否启用波浪动画效果 const wavesConfig = backgroundWallpaper.banner?.waves?.enable; // 处理分设备控制的波浪动画 const wavesEnabledOnDesktop = typeof wavesConfig === "object" ? wavesConfig.desktop : wavesConfig; const wavesEnabledOnMobile = typeof wavesConfig === "object" ? wavesConfig.mobile : wavesConfig; // 处理分设备控制的横幅信贷显示 const creditEnableConfig = backgroundWallpaper.banner?.credit?.enable; const creditEnabledOnDesktop = typeof creditEnableConfig === "object" ? creditEnableConfig.desktop : creditEnableConfig; const creditEnabledOnMobile = typeof creditEnableConfig === "object" ? creditEnableConfig.mobile : creditEnableConfig; const hasBannerCredit = isBannerMode && isBackgroundEnabled && creditEnableConfig; // 处理分设备控制的横幅信贷文本和链接 const creditTextConfig = backgroundWallpaper.banner?.credit?.text; const creditTextDesktop = typeof creditTextConfig === "object" ? creditTextConfig.desktop : creditTextConfig; const creditTextMobile = typeof creditTextConfig === "object" ? creditTextConfig.mobile : creditTextConfig; const creditUrlConfig = backgroundWallpaper.banner?.credit?.url; const creditUrlDesktop = typeof creditUrlConfig === "object" ? creditUrlConfig.desktop : creditUrlConfig; const creditUrlMobile = typeof creditUrlConfig === "object" ? creditUrlConfig.mobile : creditUrlConfig; // 检查是否为首页 const isHomePageCheck = isHomePage(Astro.url.pathname); // 检查是否在文章详情页 const isPostPage = !!postSlug; // 随机选择副标题(当打字机关闭且为数组时) const getRandomSubtitle = () => { const subtitle = backgroundWallpaper.banner?.homeText?.subtitle; if (Array.isArray(subtitle)) { const randomIndex = Math.floor(Math.random() * subtitle.length); return subtitle[randomIndex]; } return subtitle; }; const randomSubtitle = getRandomSubtitle(); // 主页横幅文本只在首页且全局开关为 true 时显示,不区分设备 const homeTextEnable = backgroundWallpaper.banner?.homeText?.enable; const showHomeText = isBannerMode && !!homeTextEnable && isHomePageCheck; // 手机端非首页不显示banner的CSS类 const mobileNonHomeBannerClass = !isHomePageCheck ? "mobile-hide-banner" : ""; // 计算主内容区域位置,考虑手机端非首页时banner被隐藏 const mainPanelTop = isBannerMode && isBackgroundEnabled ? `calc(${BANNER_HEIGHT}vh - ${MAIN_PANEL_OVERLAPS_BANNER_HEIGHT}rem)` : "5.5rem"; // 当banner模式被禁用时,主内容区域应该始终从顶栏下面开始 // 非首页在小于1024px时会通过 mobile-main-no-banner CSS类覆盖top值为5.5rem const finalMainPanelTop = isBannerMode && isBackgroundEnabled ? mainPanelTop : "5.5rem"; // 获取响应式侧边栏配置 const sidebarConfig = getResponsiveSidebarConfig(); // 在文章页面且启用了showRightSidebarOnPostPage时,要确保有右侧组件并参与网格计算 const shouldShowRightSidebarOnPostPage: boolean = isPostPage && sidebarLayoutConfig.position === "left" && !!sidebarLayoutConfig.showRightSidebarOnPostPage; const effectiveIsBothSidebars: boolean = sidebarConfig.isBothSidebars || shouldShowRightSidebarOnPostPage; const effectiveHasRightComponents: boolean = sidebarConfig.hasRightComponents || (shouldShowRightSidebarOnPostPage && sidebarLayoutConfig.rightComponents.some((comp) => comp.enable)); // 使用effective值重新生成网格类 const updatedGridConfig: ResponsiveSidebarConfig = { ...sidebarConfig, isBothSidebars: effectiveIsBothSidebars, hasRightComponents: effectiveHasRightComponents, }; const { gridCols } = generateGridClasses(updatedGridConfig); const sidebarClass = generateSidebarClasses(); const rightSidebarClass = effectiveIsBothSidebars ? generateRightSidebarClasses() : ""; const mainContentClass = generateMainContentClasses(updatedGridConfig); const footerClass = [ "footer", "col-span-1", "md:col-span-2", "onload-animation", ]; if ( updatedGridConfig.isBothSidebars && updatedGridConfig.hasLeftComponents && updatedGridConfig.hasRightComponents ) { footerClass.push("xl:col-start-2 xl:col-span-1"); } else if ( updatedGridConfig.hasLeftComponents && !updatedGridConfig.hasRightComponents ) { footerClass.push("xl:col-start-2 xl:col-span-1"); } else if ( !updatedGridConfig.hasLeftComponents && updatedGridConfig.hasRightComponents ) { footerClass.push("xl:col-start-1 xl:col-span-1"); } else { footerClass.push("xl:col-start-1 xl:col-span-1"); } const footerClassName = footerClass.join(" "); // 检查是否应该启用半透明效果 const shouldEnableTransparency = isOverlayMode && isBackgroundEnabled; // 为组件添加半透明效果的CSS类 const transparentClass = shouldEnableTransparency ? "wallpaper-transparent" : ""; const navbarWidthFull = siteConfig.navbar.widthFull ?? false; // 获取图片质量配置 const configQuality = getImageQuality(); const mobileQuality = Math.round(configQuality * 0.9); --- {(isWallpaperSwitchable || isOverlayMode) && ( )} {shouldEnableTransparency && ( )}
{(isWallpaperSwitchable || isBannerMode) && ( )}
{hasBannerCredit && creditEnabledOnDesktop && } {hasBannerCredit && creditEnabledOnMobile && } {/* 渲染侧边栏 - 769px及以上显示 */} {/* 主内容区 - 始终渲染,确保 Swup 容器存在 */}
{/* 携带网格布局类名,用于JS更新父容器 */} {/* 备用 h1 标题 - 当主页横幅文本未启用时,提供隐藏的主标题 */} {isHomePageCheck && !showHomeText && (

{siteConfig.title}

)}
{/* 右侧边栏 - 仅双侧边栏模式 */} {/* 如果全局配置为双侧栏(position: both),则使用静态容器(不被swup替换),避免闪烁。 如果全局配置为单侧栏(position: left),则使用动态容器(被swup替换),以便在文章页显示右侧栏。 注意:为了满足swup要求所有containers必须存在,我们在两种模式下都必须渲染 #right-sidebar-dynamic。 右侧边栏在1280px以下隐藏(CSS处理)。 */} {sidebarLayoutConfig.position === "both" ? ( <> ) : ( )} {/* 移动端底部组件 - 768px及以下显示 */} {sidebarLayoutConfig.mobileBottomComponents && sidebarLayoutConfig.mobileBottomComponents.length > 0 && (
)}
{live2dModelConfig.enable && }