yggdrasil/pages/admin/settings/
site_section.rs1use 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
17type ToastCb = Callback<(String, bool)>;
19
20#[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}