style: format files and update about page content

- Update about page content

- Format code in admin pages, tags, and theme modules
This commit is contained in:
xfy 2026-06-29 14:30:59 +08:00
parent d1b5b8ee4e
commit fbd706b198
6 changed files with 138 additions and 165 deletions

View File

@ -17,26 +17,7 @@ pub fn About() -> Element {
h1 { class: "text-4xl font-bold text-paper-primary tracking-tight", "关于" } h1 { class: "text-4xl font-bold text-paper-primary tracking-tight", "关于" }
} }
article { class: "prose dark:prose-invert max-w-none text-paper-content leading-relaxed", article { class: "prose dark:prose-invert max-w-none text-paper-content leading-relaxed",
p { "Yggdrasil 是一个以文字为主的简约博客系统。" } p { "世界……遗忘我……" }
p {
"它使用 Rust + Dioxus 构建,采用 PostgreSQL 作为数据库,支持 Markdown 写作、标签管理和暗色模式。"
}
h2 { class: "text-xl font-bold text-paper-primary mt-8 mb-4", "技术栈" }
ul { class: "list-disc pl-5 space-y-1",
li { "Rust + Dioxus 0.7 (全栈 Web 框架)" }
li { "PostgreSQL + tokio-postgres (数据库)" }
li { "Tailwind CSS (样式)" }
li { "Tiptap Editor (富文本编辑器)" }
li { "pulldown-cmark (Markdown 渲染)" }
}
h2 { class: "text-xl font-bold text-paper-primary mt-8 mb-4", "特性" }
ul { class: "list-disc pl-5 space-y-1",
li { "Markdown 写作与实时预览" }
li { "文章标签与归档" }
li { "暗色/亮色主题切换" }
li { "响应式设计" }
li { "文章搜索" }
}
} }
} }
} }

View File

@ -99,7 +99,7 @@ pub fn PostsPage(page: i32) -> Element {
action: EmptyStateAction { action: EmptyStateAction {
label: "写文章".to_string(), label: "写文章".to_string(),
to: Route::Write {}, to: Route::Write {},
} },
} }
} else { } else {
div { class: "{ADMIN_TABLE_CLASS}", div { class: "{ADMIN_TABLE_CLASS}",

View File

@ -106,7 +106,6 @@ pub fn TrashPage(page: i32) -> Element {
// 自动清理配置卡片(抽取为子组件 AutoPurgeSettings见文件末尾 // 自动清理配置卡片(抽取为子组件 AutoPurgeSettings见文件末尾
AutoPurgeSettings { settings } AutoPurgeSettings { settings }
// 批量操作栏(选中时显示) // 批量操作栏(选中时显示)
if !selected_ids().is_empty() { if !selected_ids().is_empty() {
div { class: "flex items-center gap-3 p-3 bg-paper-theme rounded-lg", div { class: "flex items-center gap-3 p-3 bg-paper-theme rounded-lg",
@ -133,9 +132,9 @@ pub fn TrashPage(page: i32) -> Element {
if web_sys::window() if web_sys::window()
.and_then(|w| { .and_then(|w| {
w.confirm_with_message( w.confirm_with_message(
"确定要彻底删除选中的文章吗?此操作不可恢复。", "确定要彻底删除选中的文章吗?此操作不可恢复。",
) )
.ok() .ok()
}) })
.unwrap_or(false) .unwrap_or(false)
{ {
@ -214,7 +213,7 @@ pub fn TrashPage(page: i32) -> Element {
} }
} }
th { class: "px-4 py-3 font-medium", "标题" } th { class: "px-4 py-3 font-medium", "标题" }
th { class: "px-4 py-3 font-medium", "原状态" } // 底部:清空回收站 + 分页 th { class: "px-4 py-3 font-medium", "原状态" }
th { class: "px-4 py-3 font-medium w-28", "删除时间" } th { class: "px-4 py-3 font-medium w-28", "删除时间" }
th { class: "px-4 py-3 font-medium w-24 text-center", "剩余" } th { class: "px-4 py-3 font-medium w-24 text-center", "剩余" }
th { class: "px-4 py-3 font-medium w-32 text-right", "操作" } th { class: "px-4 py-3 font-medium w-32 text-right", "操作" }
@ -287,9 +286,9 @@ pub fn TrashPage(page: i32) -> Element {
if web_sys::window() if web_sys::window()
.and_then(|w| { .and_then(|w| {
w.confirm_with_message( w.confirm_with_message(
"确定要清空回收站吗?所有已删除文章将被彻底移除,此操作不可恢复。", "确定要清空回收站吗?所有已删除文章将被彻底移除,此操作不可恢复。",
) )
.ok() .ok()
}) })
.unwrap_or(false) .unwrap_or(false)
{ {
@ -425,145 +424,141 @@ fn AutoPurgeSettings(settings: Signal<TrashSettings>) -> Element {
// 设置面板(可折叠带平滑动画) // 设置面板(可折叠带平滑动画)
div { div {
class: "grid transition-all duration-300 ease-in-out", class: "grid transition-all duration-300 ease-in-out",
style: if settings_panel_open() { style: if settings_panel_open() { "grid-template-rows: 1fr; opacity: 1; pointer-events: auto;" } else { "grid-template-rows: 0fr; opacity: 0; pointer-events: none;" },
"grid-template-rows: 1fr; opacity: 1; pointer-events: auto;"
} else {
"grid-template-rows: 0fr; opacity: 0; pointer-events: none;"
},
div { class: "overflow-hidden min-h-0", div { class: "overflow-hidden min-h-0",
div { class: "border-t border-paper-border p-5 space-y-6", div { class: "border-t border-paper-border p-5 space-y-6",
// 开关行:启用自动清理 // 开关行:启用自动清理
div { class: "flex items-center justify-between gap-4", div { class: "flex items-center justify-between gap-4",
div { class: "min-w-0", div { class: "min-w-0",
div { class: "text-sm font-medium text-paper-primary", div { class: "text-sm font-medium text-paper-primary",
"启用自动清理" "启用自动清理"
}
div { class: "text-xs text-paper-secondary mt-1",
"后台任务定期彻底删除超过保留期的文章"
}
} }
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" } }
} }
} }
// 自定义开关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: "space-y-3",
div { class: "min-w-0", div { class: "min-w-0",
div { class: "text-sm font-medium text-paper-primary", div { class: "text-sm font-medium text-paper-primary",
"保留天数" "保留天数"
}
div { class: "text-xs text-paper-secondary mt-1",
"文章删除后保留的时长到期后自动彻底清除1365"
}
}
// 数字输入 + 步进按钮 + 单位后缀
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);
},
""
} }
// 数字输入(无边框,衔接步进按钮) div { class: "text-xs text-paper-secondary mt-1",
input { "文章删除后保留的时长到期后自动彻底清除1365"
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 gap-3",
} div { class: "flex items-center rounded-lg border border-paper-border bg-paper-entry overflow-hidden",
// 减号
// 底部操作行:未保存提示 + 保存按钮 button {
div { class: "flex items-center justify-between gap-4 pt-1", 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",
if just_saved() { aria_label: "减少保留天数",
span { class: "inline-flex items-center gap-1.5 text-xs text-paper-accent", onclick: move |_| {
svg { let cur: i32 = settings_draft_days().trim().parse().unwrap_or(30);
class: "w-3.5 h-3.5", let next = cur.saturating_sub(1).max(1);
view_box: "0 0 24 24", settings_draft_days.set(next.to_string());
fill: "none", just_saved.set(false);
stroke: "currentColor", },
stroke_width: "2.5", ""
path { }
stroke_linecap: "round", // 数字输入(无边框,衔接步进按钮)
stroke_linejoin: "round", input {
d: "M5 13l4 4L19 7", 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", "" }
}
} 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" }, div { class: "flex items-center justify-between gap-4 pt-1",
disabled: saving_settings() || just_saved() || !dirty(), // 草稿状态提示
onclick: move |_| { if just_saved() {
let days: i32 = settings_draft_days().parse().unwrap_or(30); span { class: "inline-flex items-center gap-1.5 text-xs text-paper-accent",
let enabled = settings_draft_enabled(); svg {
saving_settings.set(true); class: "w-3.5 h-3.5",
spawn(async move { view_box: "0 0 24 24",
if let Ok(s) = update_trash_settings(enabled, days).await { fill: "none",
settings.set(s); stroke: "currentColor",
just_saved.set(true); stroke_width: "2.5",
path {
stroke_linecap: "round",
stroke_linejoin: "round",
d: "M5 13l4 4L19 7",
}
} }
saving_settings.set(false); "已保存"
}); }
}, } else if dirty() {
if saving_settings() { span { class: "text-xs text-paper-secondary", "有未保存的更改" }
"保存中…"
} else { } 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 {
"保存设置"
}
} }
} }
} }
} }
} }
}
} }
} }
} }

View File

@ -656,9 +656,7 @@ fn CoverUploader(cover_image: Signal<String>, cover_uploading: Signal<bool>) ->
// —— 上传中:骨架占位 + 文案 —— // —— 上传中:骨架占位 + 文案 ——
if cover_uploading() { if cover_uploading() {
div { class: "absolute inset-0 flex flex-col items-center justify-center gap-3 bg-[var(--color-paper-tertiary)]/30 animate-pulse", div { class: "absolute inset-0 flex flex-col items-center justify-center gap-3 bg-[var(--color-paper-tertiary)]/30 animate-pulse",
span { class: "text-sm text-[var(--color-paper-secondary)]", span { class: "text-sm text-[var(--color-paper-secondary)]", "上传中..." }
"上传中..."
}
} }
} }
@ -712,9 +710,7 @@ fn CoverUploader(cover_image: Signal<String>, cover_uploading: Signal<bool>) ->
} }
// 底部渐变遮罩 + "更换封面"提示hover 出现)。 // 底部渐变遮罩 + "更换封面"提示hover 出现)。
div { class: "absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-black/50 to-transparent opacity-0 group-hover/cover:opacity-100 transition-opacity flex items-end justify-center pb-2 pointer-events-none", div { class: "absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-black/50 to-transparent opacity-0 group-hover/cover:opacity-100 transition-opacity flex items-end justify-center pb-2 pointer-events-none",
span { class: "text-xs text-white/90", span { class: "text-xs text-white/90", "点击下方区域可重新上传" }
"点击下方区域可重新上传"
}
} }
} }

View File

@ -54,7 +54,7 @@ fn TagsContent() -> Element {
rsx! { rsx! {
EmptyState { EmptyState {
title: "暂无标签", title: "暂无标签",
description: "当前还没有任何标签。" description: "当前还没有任何标签。",
} }
} }
} else { } else {
@ -76,7 +76,9 @@ fn TagsContent() -> Element {
tag: tag.name.clone(), tag: tag.name.clone(),
}, },
"{tag.name}" "{tag.name}"
sup { class: "ml-1 text-sm text-paper-secondary", "{tag.post_count}" } sup { class: "ml-1 text-sm text-paper-secondary",
"{tag.post_count}"
}
} }
} }
} }
@ -130,7 +132,7 @@ fn TagDetailContent(tag: String) -> Element {
rsx! { rsx! {
EmptyState { EmptyState {
title: "暂无文章", title: "暂无文章",
description: "该标签下还没有发布任何文章。" description: "该标签下还没有发布任何文章。",
} }
} }
} else { } else {

View File

@ -181,15 +181,14 @@ pub fn ThemeToggle() -> Element {
let x = coords.x; let x = coords.x;
let y = coords.y; let y = coords.y;
// JS 从 DOM 现状推导目标主题(不传 isDark),避免与 Signal 状态不同步。 // JS 从 DOM 现状推导目标主题(不传 isDark),避免与 Signal 状态不同步。
let _ = js_sys::eval(&format!( let _ = js_sys::eval(
"if (window.__startThemeTransition) \ &format!(
window.__startThemeTransition({x}, {y});", "if (window.__startThemeTransition) \
x = x, window.__startThemeTransition({x}, {y});", // theme.set 推迟到动画结束:其触发的 Dioxus 微任务重渲染会打断 VT。
y = y, x = x,
)); y = y,
// theme.set 推迟到动画结束:其触发的 Dioxus 微任务重渲染会打断 VT。 ),
// gen 确保连续点击只有最新回调 set。JS 已自治切换 dark class, );
// 动画完成后再更新 Dioxus 状态,避免组件重渲染可能带来的干扰
let gen = click_gen() + 1; let gen = click_gen() + 1;
click_gen.set(gen); click_gen.set(gen);
let mut theme_clone = theme; let mut theme_clone = theme;