Skip to main content

yggdrasil/pages/
register.rs

1//! 注册页面
2//!
3//! 提供新用户注册表单。首个注册成功的用户将自动成为管理员,
4//! 后续注册请求会被服务端拒绝。
5
6use dioxus::prelude::*;
7use dioxus::router::components::Link;
8
9use crate::api::auth::{register, AuthResponse};
10use crate::components::forms::{AlertBox, FormInput, FormLabel, BUTTON_PRIMARY_CLASS};
11use crate::router::Route;
12
13/// 注册页面组件
14#[component]
15pub fn Register() -> Element {
16    // 表单输入状态
17    let mut username = use_signal(|| "".to_string());
18    let mut email = use_signal(|| "".to_string());
19    let mut password = use_signal(|| "".to_string());
20    let mut confirm_password = use_signal(|| "".to_string());
21    // 错误提示、成功提示与加载状态
22    let mut error = use_signal(|| None::<String>);
23    let mut success = use_signal(|| false);
24    let mut loading = use_signal(|| false);
25
26    // 提交注册表单
27    let on_submit = Callback::new(move |_| {
28        if loading() {
29            return;
30        }
31        error.set(None);
32        success.set(false);
33
34        // 前端基础校验:密码长度与一致性
35        if password().len() < 8 {
36            error.set(Some("密码长度至少 8 位".to_string()));
37            return;
38        }
39        if password() != confirm_password() {
40            error.set(Some("两次输入的密码不一致".to_string()));
41            return;
42        }
43
44        let username_val = username();
45        let email_val = email();
46        let password_val = password();
47
48        loading.set(true);
49
50        // 在异步任务中调用 server function 注册
51        spawn(async move {
52            match register(username_val, email_val, password_val).await {
53                Ok(AuthResponse { success: true, .. }) => {
54                    success.set(true);
55                }
56                Ok(AuthResponse {
57                    success: false,
58                    message,
59                    ..
60                }) => {
61                    error.set(Some(message));
62                }
63                Err(e) => {
64                    error.set(Some(format!("请求失败: {}", e)));
65                }
66            }
67            loading.set(false);
68        });
69    });
70
71    let is_loading = loading();
72
73    rsx! {
74        div { class: "min-h-screen flex items-center justify-center bg-paper-theme",
75            div { class: "w-full max-w-md p-8 bg-paper-entry rounded-2xl border border-paper-border shadow-sm",
76                h1 { class: "text-2xl font-bold text-center text-paper-primary mb-2",
77                    "注册"
78                }
79                p { class: "text-sm text-center text-paper-secondary mb-6",
80                    "首个注册账号将自动成为管理员"
81                }
82
83                if success() {
84                    div { class: "mb-4 p-3 bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300 rounded-lg text-center",
85                        "注册成功!"
86                        Link {
87                            class: "block mt-2 text-paper-accent hover:underline cursor-pointer",
88                            to: Route::Login {},
89                            "去登录"
90                        }
91                    }
92                }
93
94                if let Some(err) = error() {
95                    AlertBox { message: err, variant: "error" }
96                }
97
98                div { class: "space-y-4",
99                    div {
100                        FormLabel {
101                            label: "用户名",
102                            html_for: Some("register-username".to_string()),
103                        }
104                        FormInput {
105                            id: Some("register-username".to_string()),
106                            r#type: "text",
107                            placeholder: "3-50 位字符",
108                            value: username(),
109                            disabled: is_loading,
110                            oninput: move |v: String| username.set(v),
111                            // 回车键触发提交
112                            onkeydown: Some(
113                                EventHandler::new(move |e: KeyboardEvent| {
114                                    if e.key() == Key::Enter {
115                                        on_submit(())
116                                    }
117                                }),
118                            ),
119                        }
120                    }
121                    div {
122                        FormLabel {
123                            label: "邮箱",
124                            html_for: Some("register-email".to_string()),
125                        }
126                        FormInput {
127                            id: Some("register-email".to_string()),
128                            r#type: "email",
129                            placeholder: "your@email.com",
130                            value: email(),
131                            disabled: is_loading,
132                            oninput: move |v: String| email.set(v),
133                            // 回车键触发提交
134                            onkeydown: Some(
135                                EventHandler::new(move |e: KeyboardEvent| {
136                                    if e.key() == Key::Enter {
137                                        on_submit(())
138                                    }
139                                }),
140                            ),
141                        }
142                    }
143                    div {
144                        FormLabel {
145                            label: "密码",
146                            html_for: Some("register-password".to_string()),
147                        }
148                        FormInput {
149                            id: Some("register-password".to_string()),
150                            r#type: "password",
151                            placeholder: "至少 8 位",
152                            value: password(),
153                            disabled: is_loading,
154                            oninput: move |v: String| password.set(v),
155                            // 回车键触发提交
156                            onkeydown: Some(
157                                EventHandler::new(move |e: KeyboardEvent| {
158                                    if e.key() == Key::Enter {
159                                        on_submit(())
160                                    }
161                                }),
162                            ),
163                        }
164                    }
165                    div {
166                        FormLabel {
167                            label: "确认密码",
168                            html_for: Some("register-confirm-password".to_string()),
169                        }
170                        FormInput {
171                            id: Some("register-confirm-password".to_string()),
172                            r#type: "password",
173                            placeholder: "再次输入密码",
174                            value: confirm_password(),
175                            disabled: is_loading,
176                            oninput: move |v: String| confirm_password.set(v),
177                            // 回车键触发提交
178                            onkeydown: Some(
179                                EventHandler::new(move |e: KeyboardEvent| {
180                                    if e.key() == Key::Enter {
181                                        on_submit(())
182                                    }
183                                }),
184                            ),
185                        }
186                    }
187                    button {
188                        class: "{BUTTON_PRIMARY_CLASS}",
189                        class: if is_loading { "opacity-60 cursor-not-allowed" },
190                        disabled: is_loading,
191                        onclick: move |_| on_submit(()),
192                        if is_loading {
193                            "注册中..."
194                        } else {
195                            "注册"
196                        }
197                    }
198                }
199                p { class: "mt-4 text-center text-sm text-paper-secondary",
200                    "已有账号?"
201                    Link {
202                        class: "text-paper-accent hover:underline cursor-pointer",
203                        to: Route::Login {},
204                        "去登录"
205                    }
206                }
207            }
208        }
209    }
210}