refactor(trash): 抽取 AutoPurgeSettings 子组件,降低 TrashPage 复杂度
TrashPage 主体从约 484 行降至约 266 行。自动清理配置(可折叠设置面板: 开关 + 保留天数步进 + 保存 + 已保存反馈)整体迁入 AutoPurgeSettings 子组件, 含 6 个私有 signal(draft_days/enabled、panel_open、saving、just_saved)+ dirty memo + chevron_rotate 派生。 接口:settings(已保存配置)作为双向绑定 signal prop——子组件加载/保存时写入, 父组件读取 retention_days 供 TrashRow 的「剩余天数」展示。比 CoverUploader 的 两 signal 接口更简。 附带简化:原 settings_loaded 守卫删除——子组件挂载即触发一次自身 effect 加载 配置,不再嵌入列表翻页 effect,逻辑更清晰。 依据 dioxus-render-purity skill 的 god component 治理建议。验证:cargo build + dx check + cargo test (406) 全绿。
This commit is contained in:
parent
2b5cd5f64f
commit
a534ee436f
@ -53,18 +53,11 @@ pub fn TrashPage(page: i32) -> Element {
|
|||||||
let mut loading: Signal<bool> = use_signal(|| false);
|
let mut loading: Signal<bool> = use_signal(|| false);
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut error: Signal<Option<String>> = use_signal(|| None);
|
let mut error: Signal<Option<String>> = use_signal(|| None);
|
||||||
// 自动清理配置(含本地草稿态用于表单输入)。
|
// 自动清理配置:由子组件 AutoPurgeSettings 写入(加载/保存),本组件读取
|
||||||
|
// retention_days 供 TrashRow 的「剩余天数」展示。
|
||||||
let mut settings: Signal<TrashSettings> = use_signal(TrashSettings::default);
|
let mut settings: Signal<TrashSettings> = use_signal(TrashSettings::default);
|
||||||
let mut settings_draft_days: Signal<String> = use_signal(|| "30".to_string());
|
|
||||||
let mut settings_draft_enabled: Signal<bool> = use_signal(|| false);
|
|
||||||
let mut settings_panel_open: Signal<bool> = use_signal(|| false);
|
|
||||||
let mut saving_settings: Signal<bool> = use_signal(|| false);
|
|
||||||
// 保存成功后的短暂反馈标记(用户再次编辑时清除)。
|
|
||||||
let mut just_saved: Signal<bool> = use_signal(|| false);
|
|
||||||
// 配置只加载一次的标记,避免每次翻页 effect 重复拉取。
|
|
||||||
let mut settings_loaded: Signal<bool> = use_signal(|| false);
|
|
||||||
|
|
||||||
// 加载回收站列表;配置仅首次加载。
|
// 加载回收站列表(自动清理配置由子组件 AutoPurgeSettings 自行加载)。
|
||||||
use_effect(move || {
|
use_effect(move || {
|
||||||
let _ = current_page;
|
let _ = current_page;
|
||||||
loading.set(true);
|
loading.set(true);
|
||||||
@ -82,16 +75,6 @@ pub fn TrashPage(page: i32) -> Element {
|
|||||||
}
|
}
|
||||||
loading.set(false);
|
loading.set(false);
|
||||||
});
|
});
|
||||||
if !settings_loaded() {
|
|
||||||
settings_loaded.set(true);
|
|
||||||
spawn(async move {
|
|
||||||
if let Ok(s) = get_trash_settings().await {
|
|
||||||
settings_draft_days.set(s.retention_days.to_string());
|
|
||||||
settings_draft_enabled.set(s.auto_purge_enabled);
|
|
||||||
settings.set(s);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
{
|
{
|
||||||
@ -108,24 +91,6 @@ pub fn TrashPage(page: i32) -> Element {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 草稿相对已保存配置是否存在差异:控制保存按钮可用性与“未保存”提示。
|
|
||||||
// 派生值用 use_memo:依赖信号不变时不重算(避免每次渲染重复 parse 字符串)。
|
|
||||||
let dirty = use_memo(move || {
|
|
||||||
settings_draft_enabled() != settings().auto_purge_enabled
|
|
||||||
|| settings_draft_days()
|
|
||||||
.trim()
|
|
||||||
.parse::<i32>()
|
|
||||||
.ok()
|
|
||||||
.map(|d| d != settings().retention_days)
|
|
||||||
.unwrap_or(true)
|
|
||||||
});
|
|
||||||
// 折叠箭头旋转类(展开时翻转 180°)。
|
|
||||||
let chevron_rotate = if settings_panel_open() {
|
|
||||||
"rotate-180"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
};
|
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div { class: "space-y-6",
|
div { class: "space-y-6",
|
||||||
// 页面标题
|
// 页面标题
|
||||||
@ -134,189 +99,9 @@ pub fn TrashPage(page: i32) -> Element {
|
|||||||
span { class: "text-sm text-paper-secondary", "共 {total()} 篇" }
|
span { class: "text-sm text-paper-secondary", "共 {total()} 篇" }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动清理配置卡片:可折叠的设置面板,顶部始终显示当前状态摘要
|
// 自动清理配置卡片(抽取为子组件 AutoPurgeSettings,见文件末尾)。
|
||||||
div { class: "rounded-xl border border-paper-border overflow-hidden bg-paper-entry",
|
AutoPurgeSettings { settings }
|
||||||
// 顶部可点击摘要条:状态指示灯 + 标题 + 展开箭头
|
|
||||||
button {
|
|
||||||
class: "w-full flex items-center gap-3 px-5 py-4 text-left cursor-pointer hover:bg-paper-theme focus:outline-none focus-visible:ring-2 focus-visible:ring-paper-accent/40",
|
|
||||||
onclick: move |_| {
|
|
||||||
settings_panel_open.set(!settings_panel_open());
|
|
||||||
just_saved.set(false);
|
|
||||||
},
|
|
||||||
// 状态指示灯
|
|
||||||
{
|
|
||||||
let dot_class = if settings().auto_purge_enabled {
|
|
||||||
"w-2 h-2 rounded-full bg-paper-accent shadow-[0_0_0_3px_rgba(92,122,94,0.15)]"
|
|
||||||
} else {
|
|
||||||
"w-2 h-2 rounded-full bg-paper-tertiary"
|
|
||||||
};
|
|
||||||
rsx! {
|
|
||||||
div { class: "w-2 flex-shrink-0 flex items-center justify-center",
|
|
||||||
div { class: "{dot_class}" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 标题 + 当前状态描述
|
|
||||||
div { class: "flex-1 min-w-0",
|
|
||||||
div { class: "text-sm font-medium text-paper-primary", "自动清理" }
|
|
||||||
div { class: "text-xs text-paper-secondary mt-0.5 truncate",
|
|
||||||
if settings().auto_purge_enabled {
|
|
||||||
"已开启 · 超过 {settings().retention_days} 天的文章将被自动删除"
|
|
||||||
} else {
|
|
||||||
"已关闭"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 展开箭头(旋转动画)
|
|
||||||
svg {
|
|
||||||
class: "w-4 h-4 text-paper-secondary transition-transform duration-200 flex-shrink-0 {chevron_rotate}",
|
|
||||||
view_box: "0 0 24 24",
|
|
||||||
fill: "none",
|
|
||||||
stroke: "currentColor",
|
|
||||||
stroke_width: "2",
|
|
||||||
path {
|
|
||||||
stroke_linecap: "round",
|
|
||||||
stroke_linejoin: "round",
|
|
||||||
d: "M19 9l-7 7-7-7",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置面板(可折叠)
|
|
||||||
if settings_panel_open() {
|
|
||||||
div { class: "border-t border-paper-border p-5 space-y-6",
|
|
||||||
// 开关行:启用自动清理
|
|
||||||
div { class: "flex items-center justify-between gap-4",
|
|
||||||
div { class: "min-w-0",
|
|
||||||
div { class: "text-sm font-medium text-paper-primary",
|
|
||||||
"启用自动清理"
|
|
||||||
}
|
|
||||||
div { class: "text-xs text-paper-secondary mt-1",
|
|
||||||
"后台任务定期彻底删除超过保留期的文章"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 自定义开关(toggle switch)—— 取代原生 checkbox
|
|
||||||
button {
|
|
||||||
role: "switch",
|
|
||||||
aria_checked: "{settings_draft_enabled()}",
|
|
||||||
class: if settings_draft_enabled() { "relative w-11 h-6 flex-shrink-0 rounded-full bg-paper-accent cursor-pointer transition-colors duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-paper-accent/40" } else { "relative w-11 h-6 flex-shrink-0 rounded-full bg-paper-tertiary cursor-pointer transition-colors duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-paper-accent/40" },
|
|
||||||
onclick: move |_| {
|
|
||||||
settings_draft_enabled.set(!settings_draft_enabled());
|
|
||||||
just_saved.set(false);
|
|
||||||
},
|
|
||||||
// 滑块圆点
|
|
||||||
span { class: if settings_draft_enabled() { "absolute top-0.5 left-0.5 w-5 h-5 rounded-full bg-white shadow-sm dark:shadow-black/30 transition-transform duration-200 translate-x-5" } else { "absolute top-0.5 left-0.5 w-5 h-5 rounded-full bg-white shadow-sm dark:shadow-black/30 transition-transform duration-200" } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保留天数行
|
|
||||||
div { class: "space-y-3",
|
|
||||||
div { class: "min-w-0",
|
|
||||||
div { class: "text-sm font-medium text-paper-primary",
|
|
||||||
"保留天数"
|
|
||||||
}
|
|
||||||
div { class: "text-xs text-paper-secondary mt-1",
|
|
||||||
"文章删除后保留的时长,到期后自动彻底清除(1–365)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 数字输入 + 步进按钮 + 单位后缀
|
|
||||||
div { class: "flex items-center gap-3",
|
|
||||||
div { class: "flex items-center rounded-lg border border-paper-border bg-paper-entry overflow-hidden",
|
|
||||||
// 减号
|
|
||||||
button {
|
|
||||||
class: "w-9 h-9 flex items-center justify-center text-sm text-paper-secondary hover:text-paper-primary hover:bg-paper-theme cursor-pointer transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-paper-accent/40",
|
|
||||||
r#type: "button",
|
|
||||||
aria_label: "减少保留天数",
|
|
||||||
onclick: move |_| {
|
|
||||||
let cur: i32 = settings_draft_days().trim().parse().unwrap_or(30);
|
|
||||||
let next = cur.saturating_sub(1).max(1);
|
|
||||||
settings_draft_days.set(next.to_string());
|
|
||||||
just_saved.set(false);
|
|
||||||
},
|
|
||||||
"−"
|
|
||||||
}
|
|
||||||
// 数字输入(无边框,衔接步进按钮)
|
|
||||||
input {
|
|
||||||
r#type: "number",
|
|
||||||
min: "1",
|
|
||||||
max: "365",
|
|
||||||
class: "w-14 h-9 px-1 text-center text-sm tabular-nums text-paper-primary bg-transparent border-0 focus:outline-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
|
|
||||||
value: "{settings_draft_days()}",
|
|
||||||
oninput: move |e| {
|
|
||||||
settings_draft_days.set(e.value());
|
|
||||||
just_saved.set(false);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
// 加号
|
|
||||||
button {
|
|
||||||
class: "w-9 h-9 flex items-center justify-center text-sm text-paper-secondary hover:text-paper-primary hover:bg-paper-theme cursor-pointer transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-paper-accent/40",
|
|
||||||
r#type: "button",
|
|
||||||
aria_label: "增加保留天数",
|
|
||||||
onclick: move |_| {
|
|
||||||
let cur: i32 = settings_draft_days().trim().parse().unwrap_or(30);
|
|
||||||
let next = cur.saturating_add(1).min(365);
|
|
||||||
settings_draft_days.set(next.to_string());
|
|
||||||
just_saved.set(false);
|
|
||||||
},
|
|
||||||
"+"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
span { class: "text-xs text-paper-secondary", "天" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 底部操作行:未保存提示 + 保存按钮
|
|
||||||
div { class: "flex items-center justify-between gap-4 pt-1",
|
|
||||||
// 草稿状态提示
|
|
||||||
if just_saved() {
|
|
||||||
span { class: "inline-flex items-center gap-1.5 text-xs text-paper-accent",
|
|
||||||
svg {
|
|
||||||
class: "w-3.5 h-3.5",
|
|
||||||
view_box: "0 0 24 24",
|
|
||||||
fill: "none",
|
|
||||||
stroke: "currentColor",
|
|
||||||
stroke_width: "2.5",
|
|
||||||
path {
|
|
||||||
stroke_linecap: "round",
|
|
||||||
stroke_linejoin: "round",
|
|
||||||
d: "M5 13l4 4L19 7",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"已保存"
|
|
||||||
}
|
|
||||||
} else if dirty() {
|
|
||||||
span { class: "text-xs text-paper-secondary", "有未保存的更改" }
|
|
||||||
} else {
|
|
||||||
span { class: "text-xs text-transparent select-none",
|
|
||||||
"·"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 保存按钮:启用主题色,禁用/保存中态灰化
|
|
||||||
button {
|
|
||||||
class: if saving_settings() { "inline-flex items-center gap-1.5 px-4 py-1.5 text-sm font-medium cursor-not-allowed text-paper-secondary bg-paper-tertiary rounded-full" } else if just_saved() { "inline-flex items-center gap-1.5 px-4 py-1.5 text-sm font-medium cursor-not-allowed text-paper-secondary bg-paper-tertiary rounded-full" } else { "inline-flex items-center gap-1.5 px-4 py-1.5 text-sm font-medium text-paper-theme bg-paper-accent rounded-full hover:brightness-110 active:scale-[0.98] transition-all cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-paper-accent/40" },
|
|
||||||
disabled: saving_settings() || just_saved() || !dirty(),
|
|
||||||
onclick: move |_| {
|
|
||||||
let days: i32 = settings_draft_days().parse().unwrap_or(30);
|
|
||||||
let enabled = settings_draft_enabled();
|
|
||||||
saving_settings.set(true);
|
|
||||||
spawn(async move {
|
|
||||||
if let Ok(s) = update_trash_settings(enabled, days).await {
|
|
||||||
settings.set(s);
|
|
||||||
just_saved.set(true);
|
|
||||||
}
|
|
||||||
saving_settings.set(false);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
if saving_settings() {
|
|
||||||
"保存中…"
|
|
||||||
} else {
|
|
||||||
"保存设置"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量操作栏(选中时显示)
|
// 批量操作栏(选中时显示)
|
||||||
if !selected_ids().is_empty() {
|
if !selected_ids().is_empty() {
|
||||||
@ -530,6 +315,241 @@ pub fn TrashPage(page: i32) -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 自动清理配置子组件:可折叠的设置面板。
|
||||||
|
///
|
||||||
|
/// 封装自动清理的全部状态:表单草稿(`settings_draft_*`)、面板折叠态、保存态、
|
||||||
|
/// 已保存反馈、以及派生的 `dirty` / `chevron_rotate`。配置加载与保存均在组件
|
||||||
|
/// 内部完成。`settings`(已保存的服务端配置)由父组件传入双向绑定 signal:
|
||||||
|
/// 本组件加载/保存时写入,父组件读取 `retention_days` 供 TrashRow 的「剩余天数」。
|
||||||
|
///
|
||||||
|
/// 从 `TrashPage` 抽取以降低 god component 复杂度(见 dioxus-render-purity skill)。
|
||||||
|
#[component]
|
||||||
|
#[cfg_attr(not(target_arch = "wasm32"), allow(unused_mut, unused_variables))]
|
||||||
|
fn AutoPurgeSettings(settings: Signal<TrashSettings>) -> Element {
|
||||||
|
let mut settings_draft_days: Signal<String> = use_signal(|| "30".to_string());
|
||||||
|
let mut settings_draft_enabled: Signal<bool> = use_signal(|| false);
|
||||||
|
let mut settings_panel_open: Signal<bool> = use_signal(|| false);
|
||||||
|
let mut saving_settings: Signal<bool> = use_signal(|| false);
|
||||||
|
// 保存成功后的短暂反馈标记(用户再次编辑时清除)。
|
||||||
|
let mut just_saved: Signal<bool> = use_signal(|| false);
|
||||||
|
|
||||||
|
// 首次渲染加载服务端配置:本组件挂载即触发一次,无需 settings_loaded 守卫
|
||||||
|
//(父组件每次翻页重渲染的是列表 effect,本组件 effect 只在自身首次挂载跑)。
|
||||||
|
use_effect(move || {
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
spawn(async move {
|
||||||
|
if let Ok(s) = get_trash_settings().await {
|
||||||
|
settings_draft_days.set(s.retention_days.to_string());
|
||||||
|
settings_draft_enabled.set(s.auto_purge_enabled);
|
||||||
|
settings.set(s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 草稿相对已保存配置是否存在差异:控制保存按钮可用性与“未保存”提示。
|
||||||
|
// 派生值用 use_memo:依赖信号不变时不重算(避免每次渲染重复 parse 字符串)。
|
||||||
|
let dirty = use_memo(move || {
|
||||||
|
settings_draft_enabled() != settings().auto_purge_enabled
|
||||||
|
|| settings_draft_days()
|
||||||
|
.trim()
|
||||||
|
.parse::<i32>()
|
||||||
|
.ok()
|
||||||
|
.map(|d| d != settings().retention_days)
|
||||||
|
.unwrap_or(true)
|
||||||
|
});
|
||||||
|
// 折叠箭头旋转类(展开时翻转 180°)。
|
||||||
|
let chevron_rotate = if settings_panel_open() {
|
||||||
|
"rotate-180"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
div { class: "rounded-xl border border-paper-border overflow-hidden bg-paper-entry",
|
||||||
|
// 顶部可点击摘要条:状态指示灯 + 标题 + 展开箭头
|
||||||
|
button {
|
||||||
|
class: "w-full flex items-center gap-3 px-5 py-4 text-left cursor-pointer hover:bg-paper-theme focus:outline-none focus-visible:ring-2 focus-visible:ring-paper-accent/40",
|
||||||
|
onclick: move |_| {
|
||||||
|
settings_panel_open.set(!settings_panel_open());
|
||||||
|
just_saved.set(false);
|
||||||
|
},
|
||||||
|
// 状态指示灯
|
||||||
|
{
|
||||||
|
let dot_class = if settings().auto_purge_enabled {
|
||||||
|
"w-2 h-2 rounded-full bg-paper-accent shadow-[0_0_0_3px_rgba(92,122,94,0.15)]"
|
||||||
|
} else {
|
||||||
|
"w-2 h-2 rounded-full bg-paper-tertiary"
|
||||||
|
};
|
||||||
|
rsx! {
|
||||||
|
div { class: "w-2 flex-shrink-0 flex items-center justify-center",
|
||||||
|
div { class: "{dot_class}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 标题 + 当前状态描述
|
||||||
|
div { class: "flex-1 min-w-0",
|
||||||
|
div { class: "text-sm font-medium text-paper-primary", "自动清理" }
|
||||||
|
div { class: "text-xs text-paper-secondary mt-0.5 truncate",
|
||||||
|
if settings().auto_purge_enabled {
|
||||||
|
"已开启 · 超过 {settings().retention_days} 天的文章将被自动删除"
|
||||||
|
} else {
|
||||||
|
"已关闭"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 展开箭头(旋转动画)
|
||||||
|
svg {
|
||||||
|
class: "w-4 h-4 text-paper-secondary transition-transform duration-200 flex-shrink-0 {chevron_rotate}",
|
||||||
|
view_box: "0 0 24 24",
|
||||||
|
fill: "none",
|
||||||
|
stroke: "currentColor",
|
||||||
|
stroke_width: "2",
|
||||||
|
path {
|
||||||
|
stroke_linecap: "round",
|
||||||
|
stroke_linejoin: "round",
|
||||||
|
d: "M19 9l-7 7-7-7",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置面板(可折叠)
|
||||||
|
if settings_panel_open() {
|
||||||
|
div { class: "border-t border-paper-border p-5 space-y-6",
|
||||||
|
// 开关行:启用自动清理
|
||||||
|
div { class: "flex items-center justify-between gap-4",
|
||||||
|
div { class: "min-w-0",
|
||||||
|
div { class: "text-sm font-medium text-paper-primary",
|
||||||
|
"启用自动清理"
|
||||||
|
}
|
||||||
|
div { class: "text-xs text-paper-secondary mt-1",
|
||||||
|
"后台任务定期彻底删除超过保留期的文章"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 自定义开关(toggle switch)—— 取代原生 checkbox
|
||||||
|
button {
|
||||||
|
role: "switch",
|
||||||
|
aria_checked: "{settings_draft_enabled()}",
|
||||||
|
class: if settings_draft_enabled() { "relative w-11 h-6 flex-shrink-0 rounded-full bg-paper-accent cursor-pointer transition-colors duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-paper-accent/40" } else { "relative w-11 h-6 flex-shrink-0 rounded-full bg-paper-tertiary cursor-pointer transition-colors duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-paper-accent/40" },
|
||||||
|
onclick: move |_| {
|
||||||
|
settings_draft_enabled.set(!settings_draft_enabled());
|
||||||
|
just_saved.set(false);
|
||||||
|
},
|
||||||
|
// 滑块圆点
|
||||||
|
span { class: if settings_draft_enabled() { "absolute top-0.5 left-0.5 w-5 h-5 rounded-full bg-white shadow-sm dark:shadow-black/30 transition-transform duration-200 translate-x-5" } else { "absolute top-0.5 left-0.5 w-5 h-5 rounded-full bg-white shadow-sm dark:shadow-black/30 transition-transform duration-200" } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保留天数行
|
||||||
|
div { class: "space-y-3",
|
||||||
|
div { class: "min-w-0",
|
||||||
|
div { class: "text-sm font-medium text-paper-primary",
|
||||||
|
"保留天数"
|
||||||
|
}
|
||||||
|
div { class: "text-xs text-paper-secondary mt-1",
|
||||||
|
"文章删除后保留的时长,到期后自动彻底清除(1–365)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 数字输入 + 步进按钮 + 单位后缀
|
||||||
|
div { class: "flex items-center gap-3",
|
||||||
|
div { class: "flex items-center rounded-lg border border-paper-border bg-paper-entry overflow-hidden",
|
||||||
|
// 减号
|
||||||
|
button {
|
||||||
|
class: "w-9 h-9 flex items-center justify-center text-sm text-paper-secondary hover:text-paper-primary hover:bg-paper-theme cursor-pointer transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-paper-accent/40",
|
||||||
|
r#type: "button",
|
||||||
|
aria_label: "减少保留天数",
|
||||||
|
onclick: move |_| {
|
||||||
|
let cur: i32 = settings_draft_days().trim().parse().unwrap_or(30);
|
||||||
|
let next = cur.saturating_sub(1).max(1);
|
||||||
|
settings_draft_days.set(next.to_string());
|
||||||
|
just_saved.set(false);
|
||||||
|
},
|
||||||
|
"−"
|
||||||
|
}
|
||||||
|
// 数字输入(无边框,衔接步进按钮)
|
||||||
|
input {
|
||||||
|
r#type: "number",
|
||||||
|
min: "1",
|
||||||
|
max: "365",
|
||||||
|
class: "w-14 h-9 px-1 text-center text-sm tabular-nums text-paper-primary bg-transparent border-0 focus:outline-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
|
||||||
|
value: "{settings_draft_days()}",
|
||||||
|
oninput: move |e| {
|
||||||
|
settings_draft_days.set(e.value());
|
||||||
|
just_saved.set(false);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// 加号
|
||||||
|
button {
|
||||||
|
class: "w-9 h-9 flex items-center justify-center text-sm text-paper-secondary hover:text-paper-primary hover:bg-paper-theme cursor-pointer transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-paper-accent/40",
|
||||||
|
r#type: "button",
|
||||||
|
aria_label: "增加保留天数",
|
||||||
|
onclick: move |_| {
|
||||||
|
let cur: i32 = settings_draft_days().trim().parse().unwrap_or(30);
|
||||||
|
let next = cur.saturating_add(1).min(365);
|
||||||
|
settings_draft_days.set(next.to_string());
|
||||||
|
just_saved.set(false);
|
||||||
|
},
|
||||||
|
"+"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span { class: "text-xs text-paper-secondary", "天" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 底部操作行:未保存提示 + 保存按钮
|
||||||
|
div { class: "flex items-center justify-between gap-4 pt-1",
|
||||||
|
// 草稿状态提示
|
||||||
|
if just_saved() {
|
||||||
|
span { class: "inline-flex items-center gap-1.5 text-xs text-paper-accent",
|
||||||
|
svg {
|
||||||
|
class: "w-3.5 h-3.5",
|
||||||
|
view_box: "0 0 24 24",
|
||||||
|
fill: "none",
|
||||||
|
stroke: "currentColor",
|
||||||
|
stroke_width: "2.5",
|
||||||
|
path {
|
||||||
|
stroke_linecap: "round",
|
||||||
|
stroke_linejoin: "round",
|
||||||
|
d: "M5 13l4 4L19 7",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"已保存"
|
||||||
|
}
|
||||||
|
} else if dirty() {
|
||||||
|
span { class: "text-xs text-paper-secondary", "有未保存的更改" }
|
||||||
|
} else {
|
||||||
|
span { class: "text-xs text-transparent select-none",
|
||||||
|
"·"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 保存按钮:启用主题色,禁用/保存中态灰化
|
||||||
|
button {
|
||||||
|
class: if saving_settings() { "inline-flex items-center gap-1.5 px-4 py-1.5 text-sm font-medium cursor-not-allowed text-paper-secondary bg-paper-tertiary rounded-full" } else if just_saved() { "inline-flex items-center gap-1.5 px-4 py-1.5 text-sm font-medium cursor-not-allowed text-paper-secondary bg-paper-tertiary rounded-full" } else { "inline-flex items-center gap-1.5 px-4 py-1.5 text-sm font-medium text-paper-theme bg-paper-accent rounded-full hover:brightness-110 active:scale-[0.98] transition-all cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-paper-accent/40" },
|
||||||
|
disabled: saving_settings() || just_saved() || !dirty(),
|
||||||
|
onclick: move |_| {
|
||||||
|
let days: i32 = settings_draft_days().parse().unwrap_or(30);
|
||||||
|
let enabled = settings_draft_enabled();
|
||||||
|
saving_settings.set(true);
|
||||||
|
spawn(async move {
|
||||||
|
if let Ok(s) = update_trash_settings(enabled, days).await {
|
||||||
|
settings.set(s);
|
||||||
|
just_saved.set(true);
|
||||||
|
}
|
||||||
|
saving_settings.set(false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
if saving_settings() {
|
||||||
|
"保存中…"
|
||||||
|
} else {
|
||||||
|
"保存设置"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 计算剩余天数(保留期 - 已删除天数)。
|
/// 计算剩余天数(保留期 - 已删除天数)。
|
||||||
///
|
///
|
||||||
/// 返回 (剩余天数, 是否已过期)。基于客户端时钟计算,轻微漂移可接受。
|
/// 返回 (剩余天数, 是否已过期)。基于客户端时钟计算,轻微漂移可接受。
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user