diff --git a/src/components/skeletons/tags_skeleton.rs b/src/components/skeletons/tags_skeleton.rs index 6fa0644..3595803 100644 --- a/src/components/skeletons/tags_skeleton.rs +++ b/src/components/skeletons/tags_skeleton.rs @@ -1,8 +1,54 @@ use dioxus::prelude::*; +use crate::components::skeletons::post_card_skeleton::PostCardSkeleton; + +/// 标签列表页骨架屏 +/// 结构:统计行("共 N 个标签,M 篇文章") + 标签云(flex wrap 的 pill 列表) #[component] pub fn TagsSkeleton() -> Element { rsx! { - div { class: "animate-pulse", "Loading..." } + div { class: "animate-pulse", + // 统计行占位 + div { class: "mt-2 mb-6", + div { class: "h-5 w-48 bg-gray-200 dark:bg-[#2a2a2a] rounded" } + } + + // 标签云占位 (flex wrap gap-4) + div { class: "flex flex-wrap gap-4 mt-6", + // 生成 24 个不同宽度的标签 pill + for i in 0..24 { + div { + class: "h-8 bg-gray-200 dark:bg-[#2a2a2a] rounded-lg", + // 不同宽度模拟标签名长短 + style: match i % 6 { + 0 => "width: 60px;", + 1 => "width: 80px;", + 2 => "width: 50px;", + 3 => "width: 100px;", + 4 => "width: 70px;", + _ => "width: 90px;", + }, + } + } + } + } + } +} + +/// 标签详情页骨架屏 - 与首页文章列表结构相同 +#[component] +pub fn TagDetailSkeleton() -> Element { + rsx! { + div { class: "animate-pulse", + // 统计行占位 + div { class: "mt-2 mb-6", + div { class: "h-5 w-32 bg-gray-200 dark:bg-[#2a2a2a] rounded" } + } + + // 文章卡片列表 + for _ in 0..5 { + PostCardSkeleton {} + } + } } } diff --git a/src/pages/tags.rs b/src/pages/tags.rs index e312130..4c93af6 100644 --- a/src/pages/tags.rs +++ b/src/pages/tags.rs @@ -4,7 +4,7 @@ use crate::api::posts::{get_posts_by_tag, list_tags, PostListResponse, TagListRe use crate::components::nav::use_nav_items; use crate::components::page_layout::PageLayout; use crate::components::post_card::PostCard; -use crate::components::suspense_wrapper::SuspenseWrapper; +use crate::components::skeletons::tags_skeleton::{TagsSkeleton, TagDetailSkeleton}; use crate::router::Route; #[component] @@ -19,7 +19,8 @@ pub fn Tags() -> Element { "标签" } } - SuspenseWrapper { + SuspenseBoundary { + fallback: |_| rsx! { TagsSkeleton {} }, TagsContent {} } } @@ -73,9 +74,7 @@ fn TagsContent() -> Element { } _ => { rsx! { - div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20", - "加载中..." - } + // 骨架屏由 SuspenseBoundary fallback 处理 } } } @@ -93,7 +92,8 @@ pub fn TagDetail(tag: String) -> Element { "{tag}" } } - SuspenseWrapper { + SuspenseBoundary { + fallback: |_| rsx! { TagDetailSkeleton {} }, TagDetailContent { tag: tag.clone() } } } @@ -131,9 +131,7 @@ fn TagDetailContent(tag: String) -> Element { } _ => { rsx! { - div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20", - "加载中..." - } + // 骨架屏由 SuspenseBoundary fallback 处理 } } }