--- import type { MarkdownHeading } from "astro"; import Advertisement from "@/components/widget/Advertisement.astro"; import Announcement from "@/components/widget/Announcement.astro"; import Calendar from "@/components/widget/Calendar.astro"; import Categories from "@/components/widget/Categories.astro"; import Music from "@/components/widget/Music.astro"; import Profile from "@/components/widget/Profile.astro"; import SidebarTOC from "@/components/widget/SidebarTOC.astro"; import SiteStats from "@/components/widget/SiteStats.astro"; import Tags from "@/components/widget/Tags.astro"; import { sidebarLayoutConfig } from "@/config"; import type { MobileBottomComponentConfig, WidgetComponentConfig, WidgetComponentType, } from "@/types/config"; interface Props { class?: string; headings?: MarkdownHeading[]; side?: "left" | "right" | "bottom"; } // 侧边栏位置常量 const SIDEBAR_SIDE = { LEFT: "left", RIGHT: "right", BOTTOM: "bottom", } as const; // 组件位置常量 const COMPONENT_POSITION = { TOP: "top", STICKY: "sticky", } as const; // 动画延迟配置 const ANIMATION_DELAY_UNIT = 50; // ms // 组件映射表 const componentMap = { profile: Profile, announcement: Announcement, categories: Categories, tags: Tags, sidebarToc: SidebarTOC, advertisement: Advertisement, stats: SiteStats, calendar: Calendar, music: Music, } satisfies Record; // 获取侧边栏位置 const side = (Astro.props.side || SIDEBAR_SIDE.LEFT) as (typeof SIDEBAR_SIDE)[keyof typeof SIDEBAR_SIDE]; const className = Astro.props.class; // 根据 side 属性获取对应的组件列表 const getComponents = (): ( | WidgetComponentConfig | MobileBottomComponentConfig )[] => { if (side === SIDEBAR_SIDE.LEFT) { return sidebarLayoutConfig.leftComponents; } if (side === SIDEBAR_SIDE.RIGHT) { return sidebarLayoutConfig.rightComponents; } if (side === SIDEBAR_SIDE.BOTTOM) { return sidebarLayoutConfig.mobileBottomComponents; } return []; }; // 过滤并排序组件(不再进行 showOnPostPage 过滤,交由客户端处理) const filterAndSortComponents = ( components: (WidgetComponentConfig | MobileBottomComponentConfig)[], ) => { return components.filter((comp) => comp.enable); }; // 分离 top 和 sticky 位置的组件(保持原始数组顺序) const getComponentsByPosition = ( components: (WidgetComponentConfig | MobileBottomComponentConfig)[], ) => { const topComponents = components.filter( (c) => "position" in c && c.position === COMPONENT_POSITION.TOP, ) as WidgetComponentConfig[]; const stickyComponents = components.filter( (c) => "position" in c && c.position === COMPONENT_POSITION.STICKY, ) as WidgetComponentConfig[]; return { topComponents, stickyComponents }; }; // 获取动画延迟(硬编码) const getAnimationDelay = (index: number): string => { return `${index * ANIMATION_DELAY_UNIT}ms`; }; // 动态构建组件 props const getComponentProps = ( config: WidgetComponentConfig | MobileBottomComponentConfig, index: number, ): Record => { const baseProps: Record = { class: "onload-animation", style: `animation-delay: ${getAnimationDelay(index)}`, }; // 添加 showOnPostPage 和 showOnNonPostPage 标记class(仅WidgetComponentConfig有这些属性) if ("showOnPostPage" in config && config.showOnPostPage === false) { baseProps.class = `${baseProps.class} widget-hide-on-post`; } if ("showOnNonPostPage" in config && config.showOnNonPostPage === false) { baseProps.class = `${baseProps.class} widget-hide-on-non-post`; } // 特殊处理 SidebarTOC 组件 if (config.type === "sidebarToc") { return { ...baseProps, headings: Astro.props.headings || [] }; } // 特殊处理 Advertisement 组件 if ( config.type === "advertisement" && "configId" in config && config.configId ) { return { ...baseProps, configId: config.configId }; } return baseProps; }; // 获取所有需要渲染的组件 const allComponents = getComponents(); const filteredComponents = filterAndSortComponents(allComponents); // 对于移动端底部组件,直接使用所有组件,不进行position分组 const isMobileBottom = side === SIDEBAR_SIDE.BOTTOM; const { topComponents, stickyComponents } = !isMobileBottom ? getComponentsByPosition(filteredComponents) : { topComponents: [], stickyComponents: [] }; const bottomComponents = isMobileBottom ? filteredComponents : []; --- { (topComponents.length > 0 || stickyComponents.length > 0 || bottomComponents.length > 0) && (
{/* Mobile bottom components - 直接渲染所有组件,不分组 */} {isMobileBottom ? (
{bottomComponents.map((comp, index) => { const Component = componentMap[comp.type]; if (!Component) return null; const props = getComponentProps(comp, index) as any; return ; })}
) : ( <> {/* Top components */} {topComponents.length > 0 && (
{topComponents.map((comp, index) => { const Component = componentMap[comp.type]; if (!Component) return null; const props = getComponentProps(comp, index) as any; return ; })}
)} {/* Sticky components */} {stickyComponents.length > 0 && (
0 ? "top-4 gap-4" : "top-0", ]} > {stickyComponents.map((comp, index) => { const Component = componentMap[comp.type]; if (!Component) return null; const props = getComponentProps( comp, topComponents.length + index ) as any; return ; })}
)} )}
) }