Skip to main content

yggdrasil/components/
empty_state.rs

1//! 空状态组件。
2//!
3//! 当列表(首页、标签、搜索等)无数据时展示:插画配图 + 标题 + 副文案,
4//! 可选行动按钮。视觉语言沿用项目 Forest 调色板(鼠尾草绿强调色)与
5//! Source Serif 4 衬线标题,留白克制,与首页 HomeInfo 标题区风格一致。
6//!
7//! 配图默认为「线条小狗」插画(`public/images/xiaotiaoxiaogou_01.webp`),
8//! 可通过 `image` prop 覆盖(如搜索页初始引导态用 `xiantiaoxiaogou_02`)。
9//! 通过 `<img>` 引用绝对路径,由 Dioxus 的静态资源服务直接返回。
10
11use dioxus::prelude::*;
12use dioxus::router::components::Link;
13
14use crate::components::ui::BTN_PRIMARY;
15
16/// 空状态行动按钮。
17#[derive(Props, Clone, PartialEq)]
18pub struct EmptyStateAction {
19    /// 按钮文案。
20    #[props(into)]
21    pub label: String,
22    /// 跳转目标路由。
23    pub to: crate::router::Route,
24}
25
26/// 空状态组件。
27///
28/// 默认渲染「线条小狗」配图;`title` / `description` / `image` / `action` 均可覆盖默认。
29/// 所有元素垂直居中,配图下方留白,与首页 `HomeInfo` 的居中布局对齐。
30///
31/// Props 由 `#[component]` 宏自动生成(均为可选)。
32#[component]
33pub fn EmptyState(
34    /// 主标题(通常为「还没有文章」之类)。
35    #[props(into, default = "还没有文章".to_string())]
36    title: String,
37    /// 副文案,说明当前状态或引导用户。
38    #[props(into, default = String::new())]
39    description: String,
40    /// 配图路径(缺省「线条小狗」持相机插画)。
41    #[props(into, default = "/images/xiaotiaoxiaogou_01.webp".to_string())]
42    image: String,
43    /// 可选的行动按钮。
44    #[props(default)]
45    action: Option<EmptyStateAction>,
46) -> Element {
47    rsx! {
48        div { class: "flex flex-col items-center justify-center text-center py-20 px-4 page-enter",
49            // 配图。
50            img {
51                class: "w-48 h-auto rounded-lg select-none dark:brightness-90",
52                src: "{image}",
53                alt: "线条小狗插画",
54                draggable: "false",
55            }
56            // 主标题:衬线字体,与首页 H1 风格呼应但更轻量。
57            h2 { class: "mt-8 text-2xl font-bold tracking-tight text-paper-primary",
58                "{title}"
59            }
60            // 副文案:次要色,限宽保证可读性。
61            if !description.is_empty() {
62                p { class: "mt-3 text-sm leading-relaxed text-paper-secondary max-w-md",
63                    "{description}"
64                }
65            }
66            // 行动按钮:复用全站统一主操作按钮样式(BTN_PRIMARY)。
67            if let Some(act) = action {
68                Link { class: "{BTN_PRIMARY} mt-8", to: act.to, "{act.label}" }
69            }
70        }
71    }
72}