xfy 0398cc6c66 perf(render): 列表加显式 key + release 启用 panic=abort
依据 Dioxus 0.7 optimizing 指南做两项改进:

- 为数据驱动列表渲染补全显式 key(13 处,9 个文件):
  home/archives/tags/search 文章与标签列表、post_card/post_footer
  标签、header 桌面/移动导航、admin dashboard/posts/write 列表。
  使用各类型稳定唯一字段(post.id / tag / tag.name / item.label /
  err.id / year_group.year / month_group.month_en)作为 key,
  让 Dioxus diff 正确识别元素身份。骨架屏 for _ in 0..N 静态占位
  循环与已带 key 的评论树/admin 行不动。

- [profile.release] 加 panic = "abort":WASM 去掉 unwind 元数据减小
  体积;server 端错误处理走 Result+?(见 error.rs),不依赖 panic
  unwind,无副作用。

其余文件为本机格式化(单行折叠/多行展开),一并提交。

验证:dx check 通过、cargo test 405 passed。
2026-06-25 18:07:22 +08:00

94 lines
3.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 表单控件组件
//!
//! 提供登录、注册、评论等页面共享的输入框、按钮与提示框样式常量与组件。
use dioxus::prelude::*;
/// 输入框基础 CSS 类统一文本框、邮箱框、URL 框等样式。
pub const INPUT_CLASS: &str = "w-full px-4 py-2 border border-paper-border rounded-lg bg-paper-entry text-paper-primary placeholder:text-paper-tertiary focus:outline-none focus:border-paper-accent focus:ring-1 focus:ring-paper-accent/30 transition-colors duration-200";
/// 主按钮 CSS 类,用于表单提交等主操作按钮。
pub const BUTTON_PRIMARY_CLASS: &str = "w-full py-2.5 px-4 bg-paper-accent text-white font-medium rounded-full hover:brightness-110 active:scale-[0.98] transition-all duration-200 cursor-pointer";
/// 表单输入框组件。
///
/// Props
/// - `id`input 元素 id用于与 label 关联
/// - `r#type`input 类型(如 `"text"`、`"email"`、`"password"`
/// - `placeholder`:占位提示文本
/// - `value`:当前值
/// - `disabled`:是否禁用
/// - `oninput`:输入事件回调,返回新的字符串值
/// - `onkeydown`:可选的键盘事件回调
#[component]
pub fn FormInput(
id: Option<String>,
r#type: &'static str,
placeholder: &'static str,
value: String,
disabled: bool,
oninput: EventHandler<String>,
onkeydown: Option<EventHandler<KeyboardEvent>>,
) -> Element {
let disabled_class = if disabled {
"opacity-60 cursor-not-allowed"
} else {
""
};
rsx! {
input {
id: id.unwrap_or_default(),
class: "{INPUT_CLASS} {disabled_class}",
r#type: "{r#type}",
placeholder: "{placeholder}",
value: "{value}",
disabled,
oninput: move |e| oninput.call(e.value()),
onkeydown: move |e| {
if let Some(ref handler) = onkeydown {
handler.call(e);
}
},
}
}
}
/// 表单标签组件。
///
/// Props
/// - `label`:标签文本
/// - `html_for`:关联的 input id
#[component]
pub fn FormLabel(label: &'static str, html_for: Option<String>) -> Element {
rsx! {
label {
class: "block text-sm font-medium text-paper-secondary mb-1",
r#for: html_for.unwrap_or_default(),
"{label}"
}
}
}
/// 提示框组件,用于显示成功、错误等状态消息。
///
/// Props
/// - `message`:提示文本
/// - `variant`:风格类型,支持 `"error"`、`"success"` 与其他默认类型
#[component]
pub fn AlertBox(message: String, variant: &'static str) -> Element {
let (bg_class, text_class) = match variant {
"error" => (
"bg-red-100 dark:bg-red-900/30",
"text-red-700 dark:text-red-300",
),
"success" => (
"bg-green-100 dark:bg-green-900/30",
"text-green-700 dark:text-green-300",
),
_ => ("bg-paper-code-bg", "text-paper-secondary"),
};
rsx! {
div { class: "mb-4 p-3 {bg_class} {text_class} rounded-lg text-center", "{message}" }
}
}