yggdrasil/pages/admin/settings/
cache_section.rs1use dioxus::prelude::*;
6
7#[cfg(target_arch = "wasm32")]
8use crate::api::settings::{get_image_cache_settings, update_image_cache_settings};
9#[cfg(target_arch = "wasm32")]
10use crate::components::forms::{FormLabel, INPUT_CLASS};
11#[cfg(target_arch = "wasm32")]
12use crate::components::ui::{LoadingButton, ADMIN_CARD_CLASS};
13#[cfg(target_arch = "wasm32")]
14use crate::models::settings::ImageCacheSettings;
15
16#[allow(non_snake_case)]
18#[component]
19pub fn CacheSection(toast: Callback<(String, bool)>) -> Element {
20 #[cfg(target_arch = "wasm32")]
21 {
22 let mut saved: Signal<ImageCacheSettings> = use_signal(ImageCacheSettings::default);
23 let mut mb_draft: Signal<u32> = use_signal(|| 1024);
24 let mut hours_draft: Signal<u32> = use_signal(|| 168);
25 let loading = use_signal(|| true);
26 let mut saving = use_signal(|| false);
27 let mut just_saved = use_signal(|| false);
28
29 use_effect(move || {
30 #[cfg(target_arch = "wasm32")]
31 {
32 let mut saved = saved;
33 let mut mb_draft = mb_draft;
34 let mut hours_draft = hours_draft;
35 let mut loading = loading;
36 spawn(async move {
37 match get_image_cache_settings().await {
38 Ok(s) => {
39 saved.set(s.clone());
40 mb_draft.set(s.disk_cache_max_mb);
41 hours_draft.set(s.disk_cache_max_age_hours);
42 }
43 Err(e) => toast.call((format!("加载失败:{e}"), true)),
44 }
45 loading.set(false);
46 });
47 }
48 });
49
50 let dirty = use_memo(move || {
51 mb_draft() != saved().disk_cache_max_mb
52 || hours_draft() != saved().disk_cache_max_age_hours
53 });
54
55 rsx! {
56 div { class: "space-y-6",
57 div { class: "{ADMIN_CARD_CLASS} p-6 md:p-8 flex flex-col gap-6",
58 div { class: "flex items-center gap-3",
59 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)]",
60 svg {
61 class: "w-5 h-5",
62 view_box: "0 0 24 24",
63 fill: "none",
64 stroke: "currentColor",
65 stroke_width: "1.8",
66 stroke_linecap: "round",
67 stroke_linejoin: "round",
68 path { d: "M21 8v13H3V8 M1 3h22v5H1z M10 12h4" }
69 }
70 }
71 div {
72 h2 { class: "text-xl font-bold text-[var(--color-paper-primary)]",
73 "图片磁盘缓存"
74 }
75 p { class: "text-sm text-[var(--color-paper-secondary)] mt-0.5",
76 "uploads/.cache/ 的容量与保留策略,清理任务每小时扫描一次。"
77 }
78 }
79 }
80
81 div { class: "flex flex-col gap-2 max-w-xl",
82 FormLabel {
83 label: "最大容量(MB)",
84 html_for: Some("cache-max-mb".to_string()),
85 }
86 input {
87 id: "cache-max-mb",
88 r#type: "number",
89 min: "1",
90 class: "{INPUT_CLASS}",
91 value: "{mb_draft()}",
92 oninput: move |e: Event<FormData>| {
93 let v = e.value();
94 if let Ok(n) = v.parse::<u32>() {
95 mb_draft.set(n);
96 }
97 just_saved.set(false);
98 },
99 }
100 p { class: "text-xs text-[var(--color-paper-secondary)]",
101 "超限时按修改时间删除最旧的缓存文件。默认 1024 MB。"
102 }
103 }
104
105 div { class: "flex flex-col gap-2 max-w-xl",
106 FormLabel {
107 label: "最大保留时长(小时)",
108 html_for: Some("cache-max-age".to_string()),
109 }
110 input {
111 id: "cache-max-age",
112 r#type: "number",
113 min: "1",
114 class: "{INPUT_CLASS}",
115 value: "{hours_draft()}",
116 oninput: move |e: Event<FormData>| {
117 let v = e.value();
118 if let Ok(n) = v.parse::<u32>() {
119 hours_draft.set(n);
120 }
121 just_saved.set(false);
122 },
123 }
124 p { class: "text-xs text-[var(--color-paper-secondary)]",
125 "超期的缓存文件优先删除。默认 168 小时(7 天)。"
126 }
127 }
128
129 div { class: "flex items-center justify-between gap-4 pt-1",
130 if just_saved() {
131 span { class: "inline-flex items-center gap-1.5 text-xs text-[var(--color-paper-accent)]",
132 svg {
133 class: "w-3.5 h-3.5",
134 view_box: "0 0 24 24",
135 fill: "none",
136 stroke: "currentColor",
137 stroke_width: "2.5",
138 path {
139 stroke_linecap: "round",
140 stroke_linejoin: "round",
141 d: "M5 13l4 4L19 7",
142 }
143 }
144 "已保存"
145 }
146 } else if dirty() {
147 span { class: "text-xs text-[var(--color-paper-secondary)]",
148 "有未保存的更改"
149 }
150 } else {
151 span { class: "text-xs text-transparent select-none", "·" }
152 }
153 LoadingButton {
154 label: "保存设置".to_string(),
155 loading: saving(),
156 disabled: loading() || just_saved() || !dirty(),
157 onclick: move |_| {
158 let mb = mb_draft();
159 let hours = hours_draft();
160 saving.set(true);
161 spawn(async move {
162 match update_image_cache_settings(mb, hours).await {
163 Ok(s) => {
164 saved.set(s.clone());
165 mb_draft.set(s.disk_cache_max_mb);
166 hours_draft.set(s.disk_cache_max_age_hours);
167 just_saved.set(true);
168 toast.call(("保存成功".to_string(), false));
169 }
170 Err(e) => toast.call((format!("保存失败:{e}"), true)),
171 }
172 saving.set(false);
173 });
174 },
175 }
176 }
177 }
178 }
179 }
180 }
181 #[cfg(not(target_arch = "wasm32"))]
182 {
183 rsx! {
184 div { class: "p-8 text-[var(--color-paper-secondary)]", "缓存配置(前端渲染)" }
185 }
186 }
187}