yggdrasil/src/pages/search.rs
xfy 5d018864c2 refactor: remove PageLayout from all frontend pages, delegate to FrontendLayout
- Remove PageLayout wrapper from Home, HomePage, Archives, Tags,
  TagDetail, PostDetail, Search, and About components
- Remove unused imports: use_nav_items, use_route, PageLayout, Route
- Pages now render only their content; Header/Footer are provided by
  FrontendLayout via the router's #[layout] attribute
- Skeleton screens (DelayedSkeleton) remain in data-loading branches
- This eliminates redundant Header/Footer re-mounting on every route
  change, which was the primary source of page transition flicker

Files changed:
- src/pages/home.rs: remove PageLayout, keep HomeInfo + HomePosts
- src/pages/about.rs: remove PageLayout, render content directly
- src/pages/archives.rs: remove PageLayout, keep header + ArchivesContent
- src/pages/post_detail.rs: remove PageLayout, keep PostDetailContent
- src/pages/search.rs: remove PageLayout, keep search form + results
- src/pages/tags.rs: remove PageLayout from Tags and TagDetail
2026-06-03 18:38:11 +08:00

69 lines
2.6 KiB
Rust

use dioxus::prelude::*;
use crate::api::posts::{search_posts, PostListResponse};
use crate::components::post_card::PostCard;
use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
use crate::components::skeletons::search_skeleton::SearchSkeleton;
#[component]
pub fn Search() -> Element {
let mut query = use_signal(|| "".to_string());
let mut search_res = use_signal(|| None::<Result<PostListResponse, ServerFnError>>);
let mut is_searching = use_signal(|| false);
let mut on_search = move || {
let q = query().trim().to_string();
if q.is_empty() {
return;
}
is_searching.set(true);
search_res.set(None);
spawn(async move {
let res = search_posts(q).await;
search_res.set(Some(res));
is_searching.set(false);
});
};
rsx! {
header { class: "page-header mb-6",
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
"搜索"
}
}
div { class: "mb-8",
div { class: "flex gap-2",
input {
class: "flex-1 px-4 py-2 border border-gray-200 dark:border-[#333] rounded-lg bg-white dark:bg-[#2e2e33] text-gray-900 dark:text-[#dadadb] focus:outline-none focus:border-gray-400 dark:focus:border-gray-600",
r#type: "text",
placeholder: "输入关键词搜索文章...",
value: query(),
oninput: move |e| query.set(e.value()),
onkeydown: move |e| if e.key() == Key::Enter { on_search() },
}
button {
class: "px-6 py-2 bg-gray-900 dark:bg-[#dadadb] text-white dark:text-gray-900 rounded-full font-medium hover:opacity-80 transition-opacity",
onclick: move |_| on_search(),
"搜索"
}
}
}
if is_searching() {
DelayedSkeleton { SearchSkeleton {} }
} else if let Some(Ok(PostListResponse { posts })) = search_res() {
if posts.is_empty() {
div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20",
"未找到相关文章"
}
} else {
for post in posts.iter() {
PostCard { post: post.clone() }
}
}
} else if let Some(Err(e)) = search_res() {
div { class: "text-center text-red-500 dark:text-red-400 py-20",
"搜索失败: {e}"
}
}
}
}