Skip to main content

yggdrasil/pages/admin/settings/
upload_section.rs

1//! 素材上传并发数分区(从原 settings.rs 提取)。
2
3use dioxus::prelude::*;
4
5#[cfg(target_arch = "wasm32")]
6use crate::api::settings::{get_upload_settings, update_upload_settings};
7#[cfg(target_arch = "wasm32")]
8use crate::components::forms::{FormLabel, FormSelect};
9#[cfg(target_arch = "wasm32")]
10use crate::components::ui::{LoadingButton, ADMIN_CARD_CLASS};
11#[cfg(target_arch = "wasm32")]
12use crate::models::settings::UploadSettings;
13
14/// 素材上传并发配置分区组件。
15#[allow(non_snake_case)]
16#[component]
17pub fn UploadSection(toast: Callback<(String, bool)>) -> Element {
18    #[cfg(target_arch = "wasm32")]
19    {
20        let mut saved: Signal<UploadSettings> = use_signal(UploadSettings::default);
21        let mut draft: Signal<i32> =
22            use_signal(|| crate::models::settings::DEFAULT_UPLOAD_CONCURRENCY);
23        let loading = use_signal(|| true);
24        let mut saving = use_signal(|| false);
25        let mut just_saved = use_signal(|| false);
26
27        use_effect(move || {
28            #[cfg(target_arch = "wasm32")]
29            {
30                let mut saved = saved;
31                let mut draft = draft;
32                let mut loading = loading;
33                spawn(async move {
34                    match get_upload_settings().await {
35                        Ok(s) => {
36                            saved.set(s.clone());
37                            draft.set(s.concurrency);
38                        }
39                        Err(e) => toast.call((format!("加载失败:{e}"), true)),
40                    }
41                    loading.set(false);
42                });
43            }
44        });
45
46        let dirty = use_memo(move || draft() != saved().concurrency);
47
48        rsx! {
49            div { class: "space-y-6",
50                div { class: "{ADMIN_CARD_CLASS} p-6 md:p-8 flex flex-col gap-6",
51                    div { class: "flex items-center gap-3",
52                        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)]",
53                            svg {
54                                class: "w-5 h-5",
55                                view_box: "0 0 24 24",
56                                fill: "none",
57                                stroke: "currentColor",
58                                stroke_width: "1.8",
59                                stroke_linecap: "round",
60                                stroke_linejoin: "round",
61                                path { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }
62                                polyline { points: "17 8 12 3 7 8" }
63                                line {
64                                    x1: "12",
65                                    y1: "3",
66                                    x2: "12",
67                                    y2: "15",
68                                }
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                                "素材管理页上传弹窗同时发起的上传任务数,调高可加速批量上传。"
77                            }
78                        }
79                    }
80
81                    div { class: "flex flex-col gap-2 max-w-xl",
82                        FormLabel {
83                            label: "并发数",
84                            html_for: Some("upload-concurrency".to_string()),
85                        }
86                        FormSelect {
87                            id: Some("upload-concurrency".to_string()),
88                            value: draft(),
89                            options: vec![
90                                (1, "1(顺序上传)"),
91                                (2, "2"),
92                                (3, "3(默认)"),
93                                (4, "4"),
94                                (5, "5"),
95                                (6, "6"),
96                                (7, "7"),
97                                (8, "8"),
98                            ],
99                            onchange: move |v: i32| {
100                                draft.set(v);
101                                just_saved.set(false);
102                            },
103                        }
104                        p { class: "text-xs text-[var(--color-paper-secondary)]",
105                            "弹窗按并发数自动放大张间间隔,聚合速率始终与上传限流对齐,不会触发 429。"
106                        }
107                    }
108
109                    div { class: "flex items-center justify-between gap-4 pt-1",
110                        if just_saved() {
111                            span { class: "inline-flex items-center gap-1.5 text-xs text-[var(--color-paper-accent)]",
112                                svg {
113                                    class: "w-3.5 h-3.5",
114                                    view_box: "0 0 24 24",
115                                    fill: "none",
116                                    stroke: "currentColor",
117                                    stroke_width: "2.5",
118                                    path {
119                                        stroke_linecap: "round",
120                                        stroke_linejoin: "round",
121                                        d: "M5 13l4 4L19 7",
122                                    }
123                                }
124                                "已保存"
125                            }
126                        } else if dirty() {
127                            span { class: "text-xs text-[var(--color-paper-secondary)]",
128                                "有未保存的更改"
129                            }
130                        } else {
131                            span { class: "text-xs text-transparent select-none", "·" }
132                        }
133                        LoadingButton {
134                            label: "保存设置".to_string(),
135                            loading: saving(),
136                            disabled: loading() || just_saved() || !dirty(),
137                            onclick: move |_| {
138                                let n = draft();
139                                saving.set(true);
140                                spawn(async move {
141                                    match update_upload_settings(n).await {
142                                        Ok(s) => {
143                                            saved.set(s.clone());
144                                            draft.set(s.concurrency);
145                                            just_saved.set(true);
146                                            toast.call(("保存成功".to_string(), false));
147                                        }
148                                        Err(e) => toast.call((format!("保存失败:{e}"), true)),
149                                    }
150                                    saving.set(false);
151                                });
152                            },
153                        }
154                    }
155                }
156            }
157        }
158    }
159    #[cfg(not(target_arch = "wasm32"))]
160    {
161        rsx! {
162            div { class: "p-8 text-[var(--color-paper-secondary)]", "上传配置(前端渲染)" }
163        }
164    }
165}