feat: add TagsSkeleton/TagDetailSkeleton and replace generic fallback on tags pages

This commit is contained in:
xfy 2026-06-03 17:25:47 +08:00
parent 5becd73ebc
commit c2e1fbe3ac
2 changed files with 54 additions and 10 deletions

View File

@ -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 {}
}
}
}
}

View File

@ -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 处理
}
}
}