yggdrasil/pages/
search.rs1use dioxus::prelude::*;
12
13use crate::api::posts::{search_posts, PostListResponse};
14use crate::components::empty_state::EmptyState;
15use crate::components::post_card::PostCard;
16use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
17use crate::components::skeletons::search_skeleton::SearchSkeleton;
18
19#[component]
24pub fn Search() -> Element {
25 let mut query = use_signal(|| "".to_string());
27 let mut search_res = use_signal(|| None::<Result<PostListResponse, ServerFnError>>);
29 let mut is_searching = use_signal(|| false);
31 let mut on_search = move || {
33 let q = query().trim().to_string();
34 if q.is_empty() {
35 return;
36 }
37 is_searching.set(true);
38 search_res.set(None);
39 spawn(async move {
40 let res = search_posts(q).await;
41 search_res.set(Some(res));
42 is_searching.set(false);
43 });
44 };
45
46 rsx! {
47 div { class: "animate-page-enter",
48 header { class: "page-header mb-6",
49 h1 { class: "text-4xl font-bold text-paper-primary tracking-tight",
50 "搜索"
51 }
52 }
53 div { class: "mb-8",
54 div { class: "flex gap-2",
55 input {
56 class: "flex-1 px-4 py-2 border border-paper-border rounded-lg bg-paper-entry text-paper-primary placeholder:text-paper-tertiary focus:outline-none focus:border-paper-accent focus:ring-1 focus:ring-paper-accent/30",
57 r#type: "text",
58 placeholder: "输入关键词搜索文章...",
59 value: query(),
60 oninput: move |e| query.set(e.value()),
61 onkeydown: move |e| {
62 if e.key() == Key::Enter {
63 on_search()
64 }
65 },
66 }
67 button {
68 class: "px-6 py-2 bg-paper-accent text-white rounded-full font-medium hover:brightness-110 active:scale-[0.98] transition-all duration-200",
69 onclick: move |_| on_search(),
70 "搜索"
71 }
72 }
73 }
74 if is_searching() {
76 DelayedSkeleton { SearchSkeleton {} }
77 } else if let Some(Ok(PostListResponse { posts, total: _ })) = search_res() {
78 if posts.is_empty() {
79 EmptyState {
80 title: "未找到相关文章",
81 description: "换个关键词再试一次吧",
82 }
83 } else {
84 for post in posts.iter() {
85 PostCard { key: "{post.id}", post: post.clone() }
86 }
87 }
88 } else if search_res().as_ref().map(|r| r.is_err()).unwrap_or(false) {
89 div { class: "text-center text-red-500 dark:text-red-400 py-20", "搜索失败" }
90 } else {
91 div { class: "flex flex-col items-center justify-center mt-24 mb-12",
92 img {
93 class: "w-56 h-auto rounded-lg select-none dark:brightness-90",
94 src: "/images/xiantiaoxiaogou_02.webp",
95 alt: "空状态提示",
96 draggable: "false",
97 }
98 }
99 }
100 }
101 }
102}