Skip to main content

yggdrasil/pages/admin/settings/
site_section.rs

1//! 站点公开配置分区(页脚 GitHub 链接)。
2//!
3//! 从原 `settings.rs` 提取的 GitHub 链接卡片。写入后服务端失效 moka 缓存与
4//! 全部公开页 SSR 缓存,前台下次访问立即生效。
5
6use dioxus::prelude::*;
7
8#[cfg(target_arch = "wasm32")]
9use crate::api::settings::{get_site_settings, update_site_settings};
10#[cfg(target_arch = "wasm32")]
11use crate::components::forms::{FormInput, FormLabel};
12#[cfg(target_arch = "wasm32")]
13use crate::components::ui::{LoadingButton, ADMIN_CARD_CLASS};
14#[cfg(target_arch = "wasm32")]
15use crate::models::settings::SiteSettings;
16
17/// 传递给各分区的 toast 回调类型:(消息, 是否错误)。
18type ToastCb = Callback<(String, bool)>;
19
20/// 站点公开配置分区组件。
21#[allow(non_snake_case)]
22#[component]
23pub fn SiteSection(toast: ToastCb) -> Element {
24    #[cfg(target_arch = "wasm32")]
25    {
26        let mut saved: Signal<SiteSettings> = use_signal(SiteSettings::default);
27        let mut github_draft: Signal<String> = use_signal(String::new);
28        let loading = use_signal(|| true);
29        let mut saving = use_signal(|| false);
30        let mut just_saved = use_signal(|| false);
31
32        use_effect(move || {
33            #[cfg(target_arch = "wasm32")]
34            {
35                let mut saved = saved;
36                let mut github_draft = github_draft;
37                let mut loading = loading;
38                spawn(async move {
39                    match get_site_settings().await {
40                        Ok(s) => {
41                            saved.set(s.clone());
42                            github_draft.set(s.github_url);
43                        }
44                        Err(e) => toast.call((format!("加载失败:{e}"), true)),
45                    }
46                    loading.set(false);
47                });
48            }
49        });
50
51        let dirty = use_memo(move || github_draft().trim() != saved().github_url);
52
53        rsx! {
54            div { class: "space-y-6",
55                div { class: "{ADMIN_CARD_CLASS} p-6 md:p-8 flex flex-col gap-6",
56                    div { class: "flex items-center gap-3",
57                        span { class: "inline-flex items-center justify-center w-10 h-10 rounded-full bg-[var(--color-paper-theme)] text-[var(--color-paper-primary)] border border-[var(--color-paper-border)]",
58                            svg {
59                                xmlns: "http://www.w3.org/2000/svg",
60                                width: "22",
61                                height: "22",
62                                view_box: "0 0 24 24",
63                                fill: "currentColor",
64                                path {
65                                    fill_rule: "evenodd",
66                                    clip_rule: "evenodd",
67                                    d: "M12.026 2c-5.509 0-9.974 4.465-9.974 9.974 0 4.406 2.857 8.145 6.821 9.465.499.09.679-.217.679-.481 0-.237-.008-.865-.011-1.696-2.775.602-3.361-1.338-3.361-1.338-.452-1.152-1.107-1.459-1.107-1.459-.905-.619.069-.605.069-.605 1.002.07 1.527 1.028 1.527 1.028.89 1.524 2.336 1.084 2.902.829.091-.645.351-1.085.635-1.334-2.214-.251-4.542-1.107-4.542-4.93 0-1.087.389-1.979 1.024-2.675-.101-.253-.446-1.268.099-2.64 0 0 .837-.269 2.742 1.021a9.582 9.582 0 0 1 2.496-.336 9.554 9.554 0 0 1 2.496.336c1.906-1.291 2.742-1.021 2.742-1.021.545 1.372.203 2.387.099 2.64.64.696 1.024 1.587 1.024 2.675 0 3.833-2.33 4.675-4.552 4.922.355.308.675.916.675 1.846 0 1.334-.012 2.41-.012 2.737 0 .267.178.577.687.479C19.146 20.115 22 16.379 22 11.974 22 6.465 17.535 2 12.026 2z",
68                                }
69                            }
70                        }
71                        div {
72                            h2 { class: "text-xl font-bold text-[var(--color-paper-primary)]",
73                                "页脚 GitHub 链接"
74                            }
75                            p { class: "text-sm text-[var(--color-paper-secondary)] mt-0.5",
76                                "配置后页脚右侧展示 GitHub 图标并跳转此链接;留空则不展示。"
77                            }
78                        }
79                    }
80
81                    div { class: "flex flex-col gap-2 max-w-xl",
82                        FormLabel {
83                            label: "GitHub 链接",
84                            html_for: Some("site-github-url".to_string()),
85                        }
86                        FormInput {
87                            id: Some("site-github-url".to_string()),
88                            r#type: "url",
89                            placeholder: "github.com/your/repo",
90                            value: github_draft(),
91                            oninput: move |v: String| {
92                                github_draft.set(v);
93                                just_saved.set(false);
94                            },
95                        }
96                        p { class: "text-xs text-[var(--color-paper-secondary)]",
97                            "可省略 https:// 前缀,保存时自动补全。"
98                        }
99                    }
100
101                    if !loading() {
102                        div { class: "text-sm text-[var(--color-paper-secondary)] flex items-center gap-1.5 flex-wrap",
103                            "当前:"
104                            if saved().github_url.is_empty() {
105                                span { class: "italic", "未配置(不展示图标)" }
106                            } else {
107                                a {
108                                    class: "text-[var(--color-paper-accent)] hover:underline break-all",
109                                    href: "{saved().github_url}",
110                                    target: "_blank",
111                                    rel: "noopener noreferrer",
112                                    "{saved().github_url}"
113                                }
114                            }
115                        }
116                    }
117
118                    div { class: "flex items-center justify-between gap-4 pt-1",
119                        if just_saved() {
120                            span { class: "inline-flex items-center gap-1.5 text-xs text-[var(--color-paper-accent)]",
121                                svg {
122                                    class: "w-3.5 h-3.5",
123                                    view_box: "0 0 24 24",
124                                    fill: "none",
125                                    stroke: "currentColor",
126                                    stroke_width: "2.5",
127                                    path {
128                                        stroke_linecap: "round",
129                                        stroke_linejoin: "round",
130                                        d: "M5 13l4 4L19 7",
131                                    }
132                                }
133                                "已保存"
134                            }
135                        } else if dirty() {
136                            span { class: "text-xs text-[var(--color-paper-secondary)]",
137                                "有未保存的更改"
138                            }
139                        } else {
140                            span { class: "text-xs text-transparent select-none", "·" }
141                        }
142                        LoadingButton {
143                            label: "保存设置".to_string(),
144                            loading: saving(),
145                            disabled: loading() || just_saved() || !dirty(),
146                            onclick: move |_| {
147                                let url = github_draft().clone();
148                                saving.set(true);
149                                spawn(async move {
150                                    match update_site_settings(url).await {
151                                        Ok(s) => {
152                                            saved.set(s.clone());
153                                            github_draft.set(s.github_url);
154                                            just_saved.set(true);
155                                            toast.call(("保存成功".to_string(), false));
156                                        }
157                                        Err(e) => toast.call((format!("保存失败:{e}"), true)),
158                                    }
159                                    saving.set(false);
160                                });
161                            },
162                        }
163                    }
164                }
165            }
166        }
167    }
168    #[cfg(not(target_arch = "wasm32"))]
169    {
170        rsx! {
171            div { class: "p-8 text-[var(--color-paper-secondary)]", "站点配置(前端渲染)" }
172        }
173    }
174}