feat(admin): 后台迁移至 paper-* 主题变量对齐前台

将 /admin 路由下硬编码的 Tailwind gray + hex 配色统一迁移到
input.css 定义的 paper-* 主题变量系统,使后台视觉完全对齐前台:

- 底色:bg-white → bg-paper-theme(米白,与前台一致)
- 卡片/表格:bg-white dark:bg-[#2e2e33] → bg-paper-entry
- 主操作按钮(写文章/保存/分页):bg-gray-900 黑 → bg-paper-accent 鼠尾草绿
- 文字/边框/hover:gray-* + dark:hex → text-paper-*/border-paper-*
- 清理 trash 页散落的 #5c7a5e/#7da97f/#e8f0e8 等 hex,路由到变量
- 骨架占位统一复用 ADMIN_* 常量与 SkeletonBox

保留 green/amber/red 状态语义色(通过/待审/删除)与 toggle 白色滑块。
统一采用 bg-paper-* 工具类语法,与 header/post_card 主流写法一致。

dx check + clippy + 402 tests 全通过。
This commit is contained in:
xfy 2026-06-25 14:33:48 +08:00
parent df85df3d2e
commit 519c0ae3a9
9 changed files with 112 additions and 107 deletions

View File

@ -79,7 +79,7 @@ pub fn AdminLayout() -> Element {
div { class: "flex items-center gap-3", div { class: "flex items-center gap-3",
ThemeToggle {} ThemeToggle {}
button { button {
class: "text-sm text-gray-600 dark:text-[#9b9c9d] hover:text-gray-900 dark:hover:text-[#dadadb] transition-colors", class: "text-sm text-paper-secondary hover:text-paper-primary transition-colors",
onclick: move |_| { onclick: move |_| {
spawn(async move { spawn(async move {
let _ = logout().await; let _ = logout().await;
@ -103,9 +103,9 @@ pub fn AdminLayout() -> Element {
// Write 路由:页面固定高度,不滚动,由编辑器内部处理滚动 // Write 路由:页面固定高度,不滚动,由编辑器内部处理滚动
let root_class = if is_write_route { let root_class = if is_write_route {
"h-dvh flex flex-col overflow-hidden bg-white dark:bg-[#1d1e20]" "h-dvh flex flex-col overflow-hidden bg-paper-theme"
} else { } else {
"min-h-screen flex flex-col bg-white dark:bg-[#1d1e20]" "min-h-screen flex flex-col bg-paper-theme"
}; };
// 根据校验状态与用户状态渲染真实布局、跳转提示或骨架屏 // 根据校验状态与用户状态渲染真实布局、跳转提示或骨架屏
@ -125,7 +125,7 @@ pub fn AdminLayout() -> Element {
rsx! { rsx! {
div { class: "{root_class}", div { class: "{root_class}",
div { class: "flex-1 flex items-center justify-center", div { class: "flex-1 flex items-center justify-center",
p { class: "text-gray-600 dark:text-[#9b9c9d]", "未登录,正在跳转..." } p { class: "text-paper-secondary", "未登录,正在跳转..." }
} }
} }
} }

View File

@ -17,7 +17,7 @@ pub fn AdminDashboardSkeleton() -> Element {
// 统计卡片骨架 // 统计卡片骨架
div { class: "grid grid-cols-1 md:grid-cols-3 gap-6", div { class: "grid grid-cols-1 md:grid-cols-3 gap-6",
for _ in 0..3 { for _ in 0..3 {
div { class: "rounded-xl bg-white dark:bg-[#2e2e33] border border-gray-200 dark:border-[#333] p-6 text-center space-y-3", div { class: "rounded-xl bg-paper-entry border border-paper-border p-6 text-center space-y-3",
SkeletonBox { class: "h-9 w-16 mx-auto rounded" } SkeletonBox { class: "h-9 w-16 mx-auto rounded" }
SkeletonBox { class: "h-4 w-20 mx-auto rounded" } SkeletonBox { class: "h-4 w-20 mx-auto rounded" }
} }
@ -35,7 +35,7 @@ pub fn AdminDashboardSkeleton() -> Element {
SkeletonBox { class: "h-6 w-24 rounded" } SkeletonBox { class: "h-6 w-24 rounded" }
div { class: "space-y-0", div { class: "space-y-0",
for _ in 0..5 { for _ in 0..5 {
div { class: "flex justify-between items-center py-3 border-b border-gray-100 dark:border-[#333]", div { class: "flex justify-between items-center py-3 border-b border-paper-border",
SkeletonBox { class: "h-4 w-[45%] rounded" } SkeletonBox { class: "h-4 w-[45%] rounded" }
SkeletonBox { class: "h-3 w-20 rounded" } SkeletonBox { class: "h-3 w-20 rounded" }
} }

View File

@ -5,6 +5,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use crate::components::skeletons::atoms::SkeletonBox; use crate::components::skeletons::atoms::SkeletonBox;
use crate::components::ui::ADMIN_TABLE_CLASS;
/// 后台文章管理列表骨架屏组件。 /// 后台文章管理列表骨架屏组件。
/// ///
@ -12,10 +13,10 @@ use crate::components::skeletons::atoms::SkeletonBox;
#[component] #[component]
pub fn PostsSkeleton() -> Element { pub fn PostsSkeleton() -> Element {
rsx! { rsx! {
div { class: "bg-white dark:bg-[#2e2e33] rounded-xl border border-gray-200 dark:border-[#333] overflow-hidden", div { class: "{ADMIN_TABLE_CLASS}",
table { class: "w-full text-sm", table { class: "w-full text-sm",
thead { thead {
tr { class: "border-b border-gray-200 dark:border-[#333]", tr { class: "border-b border-paper-border",
th { class: "px-4 py-3", SkeletonBox { class: "h-3 w-10" } } th { class: "px-4 py-3", SkeletonBox { class: "h-3 w-10" } }
th { class: "px-4 py-3 w-24", SkeletonBox { class: "h-3 w-10 mx-auto" } } th { class: "px-4 py-3 w-24", SkeletonBox { class: "h-3 w-10 mx-auto" } }
th { class: "px-4 py-3 w-32", SkeletonBox { class: "h-3 w-10" } } th { class: "px-4 py-3 w-32", SkeletonBox { class: "h-3 w-10" } }
@ -24,7 +25,7 @@ pub fn PostsSkeleton() -> Element {
} }
tbody { tbody {
for _ in 0..10 { for _ in 0..10 {
tr { class: "border-b border-gray-100 dark:border-[#333] last:border-0", tr { class: "border-b border-paper-border last:border-0",
td { class: "px-4 py-3", SkeletonBox { class: "h-4 w-1/3" } } td { class: "px-4 py-3", SkeletonBox { class: "h-4 w-1/3" } }
td { class: "px-4 py-3", SkeletonBox { class: "h-5 w-14 mx-auto rounded" } } td { class: "px-4 py-3", SkeletonBox { class: "h-5 w-14 mx-auto rounded" } }
td { class: "px-4 py-3", SkeletonBox { class: "h-4 w-20" } } td { class: "px-4 py-3", SkeletonBox { class: "h-4 w-20" } }

View File

@ -17,18 +17,18 @@ use crate::router::Route;
/// Admin 卡片容器:白底圆角描边,亮/暗双模式。用于 stat 卡片、面板等。 /// Admin 卡片容器:白底圆角描边,亮/暗双模式。用于 stat 卡片、面板等。
pub const ADMIN_CARD_CLASS: &str = pub const ADMIN_CARD_CLASS: &str =
"bg-white dark:bg-[#2e2e33] rounded-xl border border-gray-200 dark:border-[#333]"; "bg-paper-entry rounded-xl border border-paper-border";
/// Admin 表格容器:在卡片基础上加 `overflow-hidden`,圆角裁剪表格。 /// Admin 表格容器:在卡片基础上加 `overflow-hidden`,圆角裁剪表格。
pub const ADMIN_TABLE_CLASS: &str = pub const ADMIN_TABLE_CLASS: &str =
"bg-white dark:bg-[#2e2e33] rounded-xl border border-gray-200 dark:border-[#333] overflow-hidden"; "bg-paper-entry rounded-xl border border-paper-border overflow-hidden";
/// Admin 表格行 hover 态:底部分割线 + 悬停背景。 /// Admin 表格行 hover 态:底部分割线 + 悬停背景。
pub const ADMIN_ROW_HOVER: &str = pub const ADMIN_ROW_HOVER: &str =
"border-b border-gray-100 dark:border-[#333] last:border-0 hover:bg-gray-50 dark:hover:bg-[#2a2a2a] transition-colors"; "border-b border-paper-border last:border-0 hover:bg-paper-entry transition-colors";
/// 列表复选框统一样式(全选表头 + 行内)。 /// 列表复选框统一样式(全选表头 + 行内)。
pub const CHECKBOX_CLASS: &str = "rounded border-gray-300 dark:border-[#555]"; pub const CHECKBOX_CLASS: &str = "rounded border-paper-border";
/// 状态徽章外层:小号圆角胶囊。 /// 状态徽章外层:小号圆角胶囊。
pub const BADGE_BASE: &str = pub const BADGE_BASE: &str =
@ -56,7 +56,7 @@ pub const BTN_TEXT_AMBER: &str = "text-xs text-amber-600 hover:text-amber-800 da
pub const BTN_TEXT_RED: &str = pub const BTN_TEXT_RED: &str =
"text-xs text-red-500 hover:text-red-700 dark:hover:text-red-300 transition-colors cursor-pointer"; "text-xs text-red-500 hover:text-red-700 dark:hover:text-red-300 transition-colors cursor-pointer";
/// 主题绿(鼠尾草)文字小按钮(行内恢复)。 /// 主题绿(鼠尾草)文字小按钮(行内恢复)。
pub const BTN_TEXT_ACCENT: &str = "text-xs text-[#5c7a5e] hover:text-[#3d5a3f] dark:text-[#7da97f] dark:hover:text-[#9dc79f] transition-colors cursor-pointer"; pub const BTN_TEXT_ACCENT: &str = "text-xs text-paper-accent hover:text-paper-primary transition-colors cursor-pointer";
// =========================================================================== // ===========================================================================
// 组件 // 组件
@ -95,7 +95,7 @@ pub fn Pagination(
let is_admin = variant == "admin"; let is_admin = variant == "admin";
let (link_class, link_extra_next) = if is_admin { let (link_class, link_extra_next) = if is_admin {
( (
"inline-flex items-center px-4 py-2 text-sm text-white bg-gray-900 dark:bg-[#dadadb] dark:text-gray-900 rounded-full hover:opacity-80 transition-opacity cursor-pointer", "inline-flex items-center px-4 py-2 text-sm text-paper-theme bg-paper-accent rounded-full hover:brightness-110 active:scale-[0.98] transition-all duration-200 cursor-pointer",
"", "",
) )
} else { } else {
@ -105,7 +105,7 @@ pub fn Pagination(
) )
}; };
let disabled_class = let disabled_class =
"inline-flex items-center px-4 py-2 text-sm text-gray-400 bg-gray-100 dark:bg-[#2a2a2a] rounded-full cursor-not-allowed"; "inline-flex items-center px-4 py-2 text-sm text-paper-secondary bg-paper-tertiary rounded-full cursor-not-allowed";
// admin 首尾页渲染禁用态frontend 首尾页直接不渲染。 // admin 首尾页渲染禁用态frontend 首尾页直接不渲染。
rsx! { rsx! {
@ -126,7 +126,7 @@ pub fn Pagination(
// admin 显示页码计数。 // admin 显示页码计数。
if is_admin { if is_admin {
span { class: "text-sm text-gray-500 dark:text-[#9b9c9d] self-center", span { class: "text-sm text-paper-secondary self-center",
"{current_page} / {total_pages} 页 (共 {total} {unit})" "{current_page} / {total_pages} 页 (共 {total} {unit})"
} }
} }
@ -177,7 +177,7 @@ pub fn StatusBadge(color_class: &'static str, label: String) -> Element {
pub fn EmptyState(message: &'static str, variant: &'static str) -> Element { pub fn EmptyState(message: &'static str, variant: &'static str) -> Element {
let class = match variant { let class = match variant {
"error" => "text-center text-red-500 dark:text-red-400 py-20", "error" => "text-center text-red-500 dark:text-red-400 py-20",
_ => "text-center py-20 text-gray-500 dark:text-[#9b9c9d]", _ => "text-center py-20 text-paper-secondary",
}; };
rsx! { rsx! {
div { class: "{class}", "{message}" } div { class: "{class}", "{message}" }

View File

@ -14,10 +14,12 @@ use crate::api::comments::trash_comment;
use crate::api::comments::{approve_comment, batch_update_comment_status, spam_comment}; use crate::api::comments::{approve_comment, batch_update_comment_status, spam_comment};
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use crate::api::comments::{get_all_comments, AllCommentsResponse}; use crate::api::comments::{get_all_comments, AllCommentsResponse};
use crate::components::skeletons::atoms::SkeletonBox;
use crate::components::skeletons::delayed_skeleton::DelayedSkeleton; use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
use crate::components::ui::{ use crate::components::ui::{
EmptyState, Pagination, StatusBadge, ADMIN_ROW_HOVER, ADMIN_TABLE_CLASS, BTN_TEXT_AMBER, EmptyState, Pagination, StatusBadge, ADMIN_CARD_CLASS, ADMIN_ROW_HOVER, ADMIN_TABLE_CLASS,
BTN_TEXT_GREEN, BTN_TEXT_RED, BTN_SOLID_AMBER, BTN_SOLID_GREEN, BTN_SOLID_RED, CHECKBOX_CLASS, BTN_TEXT_AMBER, BTN_TEXT_GREEN, BTN_TEXT_RED, BTN_SOLID_AMBER, BTN_SOLID_GREEN, BTN_SOLID_RED,
CHECKBOX_CLASS,
}; };
use crate::models::comment::{AdminComment, CommentStatus}; use crate::models::comment::{AdminComment, CommentStatus};
use crate::router::Route; use crate::router::Route;
@ -122,17 +124,17 @@ pub fn AdminCommentsPage(page: i32) -> Element {
rsx! { rsx! {
div { class: "space-y-6", div { class: "space-y-6",
h1 { class: "text-2xl font-bold text-gray-900 dark:text-[#dadadb]", h1 { class: "text-2xl font-bold text-paper-primary",
"评论管理" "评论管理"
} }
div { class: "flex gap-1 border-b border-gray-200 dark:border-[#333]", div { class: "flex gap-1 border-b border-paper-border",
for (status, label) in [("", "全部"), ("pending", "待审核"), ("approved", "已通过"), ("spam", "垃圾箱")] { for (status, label) in [("", "全部"), ("pending", "待审核"), ("approved", "已通过"), ("spam", "垃圾箱")] {
button { button {
class: if active_filter() == status { class: if active_filter() == status {
"px-4 py-2 text-sm font-medium border-b-2 border-gray-900 dark:border-[#dadadb] text-gray-900 dark:text-[#dadadb]" "px-4 py-2 text-sm font-medium border-b-2 border-paper-accent text-paper-primary"
} else { } else {
"px-4 py-2 text-sm font-medium text-gray-500 dark:text-[#9b9c9d] hover:text-gray-700 dark:hover:text-[#dadadb] transition-colors" "px-4 py-2 text-sm font-medium text-paper-secondary hover:text-paper-primary transition-colors"
}, },
onclick: move |_| active_filter.set(status.to_string()), onclick: move |_| active_filter.set(status.to_string()),
"{label}" "{label}"
@ -142,8 +144,8 @@ pub fn AdminCommentsPage(page: i32) -> Element {
if !selected_ids().is_empty() { if !selected_ids().is_empty() {
{ rsx! { { rsx! {
div { class: "flex items-center gap-3 p-3 bg-gray-50 dark:bg-[#2a2a2a] rounded-lg", div { class: "flex items-center gap-3 p-3 bg-paper-theme rounded-lg",
span { class: "text-sm text-gray-600 dark:text-[#9b9c9d]", span { class: "text-sm text-paper-secondary",
"已选择 {selected_ids().len()} 条" "已选择 {selected_ids().len()} 条"
} }
button { button {
@ -203,13 +205,13 @@ pub fn AdminCommentsPage(page: i32) -> Element {
} else if loading() && comments().is_empty() { } else if loading() && comments().is_empty() {
rsx! { rsx! {
DelayedSkeleton { DelayedSkeleton {
div { class: "bg-white dark:bg-[#2e2e33] rounded-xl border border-gray-200 dark:border-[#333] p-6 space-y-4", div { class: "{ADMIN_CARD_CLASS} p-6 space-y-4",
for _ in 0..5 { for _ in 0..5 {
div { class: "flex items-center gap-4", div { class: "flex items-center gap-4",
div { class: "h-4 w-4 bg-gray-200 dark:bg-[#2a2a2a] rounded" } SkeletonBox { class: "h-4 w-4 rounded" }
div { class: "h-8 w-8 bg-gray-200 dark:bg-[#2a2a2a] rounded-full" } SkeletonBox { class: "h-8 w-8 rounded-full" }
div { class: "h-4 w-32 bg-gray-200 dark:bg-[#2a2a2a] rounded" } SkeletonBox { class: "h-4 w-32 rounded" }
div { class: "h-4 flex-1 bg-gray-200 dark:bg-[#2a2a2a] rounded" } SkeletonBox { class: "h-4 flex-1 rounded" }
} }
} }
} }
@ -226,7 +228,7 @@ pub fn AdminCommentsPage(page: i32) -> Element {
div { class: "overflow-x-auto", div { class: "overflow-x-auto",
table { class: "w-full text-sm", table { class: "w-full text-sm",
thead { thead {
tr { class: "border-b border-gray-200 dark:border-[#333] text-left text-gray-500 dark:text-[#9b9c9d]", tr { class: "border-b border-paper-border text-left text-paper-secondary",
th { class: "px-4 py-3 font-medium w-10", th { class: "px-4 py-3 font-medium w-10",
input { input {
r#type: "checkbox", r#type: "checkbox",
@ -372,23 +374,23 @@ fn CommentRow(
alt: "{comment.author_name}", alt: "{comment.author_name}",
} }
div { class: "min-w-0", div { class: "min-w-0",
div { class: "text-sm font-medium text-gray-900 dark:text-[#dadadb] truncate", div { class: "text-sm font-medium text-paper-primary truncate",
"{comment.author_name}" "{comment.author_name}"
} }
div { class: "text-xs text-gray-400 dark:text-[#666] truncate", div { class: "text-xs text-paper-secondary truncate",
"{comment.author_email}" "{comment.author_email}"
} }
} }
} }
} }
td { class: "px-4 py-3 max-w-xs", td { class: "px-4 py-3 max-w-xs",
p { class: "text-sm text-gray-600 dark:text-[#9b9c9d] truncate", p { class: "text-sm text-paper-secondary truncate",
"{preview}" "{preview}"
} }
} }
td { class: "px-4 py-3", td { class: "px-4 py-3",
Link { Link {
class: "text-sm text-gray-700 dark:text-[#dadadb] hover:opacity-80 transition-opacity", class: "text-sm text-paper-primary hover:text-paper-accent transition-colors",
to: Route::PostDetail { slug: comment.post_slug.clone() }, to: Route::PostDetail { slug: comment.post_slug.clone() },
"{comment.post_title}" "{comment.post_title}"
} }
@ -405,7 +407,7 @@ fn CommentRow(
label: status_label, label: status_label,
} }
} }
td { class: "px-4 py-3 text-sm text-gray-500 dark:text-[#9b9c9d]", td { class: "px-4 py-3 text-sm text-paper-secondary",
"{date_str}" "{date_str}"
} }
td { class: "px-4 py-3 text-right", td { class: "px-4 py-3 text-right",

View File

@ -13,6 +13,7 @@ use crate::api::posts::{get_post_stats, list_posts};
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use crate::api::posts::{PostListResponse, PostStatsResponse}; use crate::api::posts::{PostListResponse, PostStatsResponse};
use crate::components::ui::ADMIN_CARD_CLASS; use crate::components::ui::ADMIN_CARD_CLASS;
use crate::components::skeletons::atoms::SkeletonBox;
use crate::models::post::{PostListItem, PostStats}; use crate::models::post::{PostListItem, PostStats};
use crate::router::Route; use crate::router::Route;
@ -70,9 +71,9 @@ pub fn Admin() -> Element {
None => { None => {
rsx! { rsx! {
for _ in 0..3 { for _ in 0..3 {
div { class: "rounded-xl bg-white dark:bg-[#2e2e33] border border-gray-200 dark:border-[#333] p-6 text-center space-y-3 animate-pulse", div { class: "{ADMIN_CARD_CLASS} p-6 text-center space-y-3 animate-pulse",
div { class: "h-9 w-16 mx-auto bg-gray-200 dark:bg-[#2a2a2a] rounded" } SkeletonBox { class: "h-9 w-16 mx-auto rounded" }
div { class: "h-4 w-20 mx-auto bg-gray-200 dark:bg-[#2a2a2a] rounded" } SkeletonBox { class: "h-4 w-20 mx-auto rounded" }
} }
} }
} }
@ -81,7 +82,7 @@ pub fn Admin() -> Element {
} }
Link { Link {
class: "block rounded-xl bg-white dark:bg-[#2e2e33] border border-gray-200 dark:border-[#333] p-6 text-center hover:border-gray-300 dark:hover:border-[#555] transition-colors", class: "block {ADMIN_CARD_CLASS} p-6 text-center hover:border-paper-accent transition-colors",
to: Route::AdminComments {}, to: Route::AdminComments {},
match pending_count() { match pending_count() {
Some(count) => { Some(count) => {
@ -89,15 +90,15 @@ pub fn Admin() -> Element {
div { class: "text-3xl font-bold text-amber-600 dark:text-amber-400", div { class: "text-3xl font-bold text-amber-600 dark:text-amber-400",
"{count}" "{count}"
} }
div { class: "text-sm text-gray-500 dark:text-[#9b9c9d] mt-2", div { class: "text-sm text-paper-secondary mt-2",
"待审核评论" "待审核评论"
} }
} }
} }
None => { None => {
rsx! { rsx! {
div { class: "h-9 w-16 mx-auto bg-gray-200 dark:bg-[#2a2a2a] rounded animate-pulse" } SkeletonBox { class: "h-9 w-16 mx-auto rounded" }
div { class: "h-4 w-20 mx-auto bg-gray-200 dark:bg-[#2a2a2a] rounded mt-3 animate-pulse" } SkeletonBox { class: "h-4 w-20 mx-auto rounded mt-3" }
} }
} }
} }
@ -105,19 +106,19 @@ pub fn Admin() -> Element {
div { class: "grid grid-cols-1 md:grid-cols-2 gap-4", div { class: "grid grid-cols-1 md:grid-cols-2 gap-4",
Link { Link {
class: "bg-gray-900 dark:bg-[#dadadb] text-white dark:text-gray-900 rounded-full px-6 py-3 text-center font-medium hover:opacity-80 transition-opacity cursor-pointer", class: "bg-paper-accent text-paper-theme rounded-full px-6 py-3 text-center font-medium hover:brightness-110 active:scale-[0.98] transition-all duration-200 cursor-pointer",
to: Route::Write {}, to: Route::Write {},
"写文章" "写文章"
} }
Link { Link {
class: "bg-gray-200 dark:bg-[#333] text-gray-700 dark:text-[#dadadb] rounded-full px-6 py-3 text-center font-medium hover:opacity-80 transition-opacity cursor-pointer", class: "bg-paper-tertiary text-paper-primary rounded-full px-6 py-3 text-center font-medium hover:opacity-80 transition-opacity cursor-pointer",
to: Route::Posts {}, to: Route::Posts {},
"管理文章" "管理文章"
} }
} }
div { class: "mb-8", div { class: "mb-8",
h2 { class: "text-xl font-bold text-gray-900 dark:text-[#dadadb] mb-4", h2 { class: "text-xl font-bold text-paper-primary mb-4",
"最近文章" "最近文章"
} }
match recent_posts() { match recent_posts() {
@ -134,9 +135,9 @@ pub fn Admin() -> Element {
rsx! { rsx! {
div { class: "space-y-4 animate-pulse", div { class: "space-y-4 animate-pulse",
for _ in 0..5 { for _ in 0..5 {
div { class: "flex justify-between items-center py-3 border-b border-gray-100 dark:border-[#333]", div { class: "flex justify-between items-center py-3 border-b border-paper-border",
div { class: "h-4 w-[45%] bg-gray-200 dark:bg-[#2a2a2a] rounded" } SkeletonBox { class: "h-4 w-[45%] rounded" }
div { class: "h-3 w-20 bg-gray-200 dark:bg-[#2a2a2a] rounded" } SkeletonBox { class: "h-3 w-20 rounded" }
} }
} }
} }
@ -153,10 +154,10 @@ pub fn Admin() -> Element {
fn StatCard(value: String, label: String) -> Element { fn StatCard(value: String, label: String) -> Element {
rsx! { rsx! {
div { class: "{ADMIN_CARD_CLASS} p-6 text-center", div { class: "{ADMIN_CARD_CLASS} p-6 text-center",
div { class: "text-3xl font-bold text-gray-900 dark:text-[#dadadb]", div { class: "text-3xl font-bold text-paper-primary",
"{value}" "{value}"
} }
div { class: "text-sm text-gray-500 dark:text-[#9b9c9d] mt-2", div { class: "text-sm text-paper-secondary mt-2",
"{label}" "{label}"
} }
} }
@ -171,16 +172,16 @@ fn RecentPostItem(post: PostListItem) -> Element {
let status_class = post.status_class(); let status_class = post.status_class();
rsx! { rsx! {
div { class: "flex justify-between items-center py-3 border-b border-gray-100 dark:border-[#333]", div { class: "flex justify-between items-center py-3 border-b border-paper-border",
div { class: "flex items-center gap-3", div { class: "flex items-center gap-3",
span { class: "text-gray-700 dark:text-[#dadadb]", span { class: "text-paper-primary",
"{post.title}" "{post.title}"
} }
span { class: "text-xs {status_class}", span { class: "text-xs {status_class}",
"{status_label}" "{status_label}"
} }
} }
span { class: "text-sm text-gray-400 dark:text-[#9b9c9d]", span { class: "text-sm text-paper-secondary",
"{date_str}" "{date_str}"
} }
} }

View File

@ -101,42 +101,42 @@ pub fn PostsPage(page: i32) -> Element {
rsx! { rsx! {
div { class: "space-y-6", div { class: "space-y-6",
div { class: "flex items-center justify-between", div { class: "flex items-center justify-between",
h1 { class: "text-2xl font-bold text-gray-900 dark:text-[#dadadb]", h1 { class: "text-2xl font-bold text-paper-primary",
"文章管理" "文章管理"
} }
div { class: "flex items-center gap-3", div { class: "flex items-center gap-3",
div { class: "group relative", div { class: "group relative",
button { button {
class: if rebuilding() { class: if rebuilding() {
"px-4 py-2 rounded-full text-sm font-medium cursor-not-allowed text-gray-400 dark:text-[#666] border border-gray-300 dark:border-[#444]" "px-4 py-2 rounded-full text-sm font-medium cursor-not-allowed text-paper-secondary border border-paper-border"
} else { } else {
"px-4 py-2 rounded-full text-sm font-medium cursor-pointer text-gray-700 dark:text-[#b0b0b1] border border-gray-300 dark:border-[#444] hover:border-gray-900 dark:hover:border-[#dadadb] hover:text-gray-900 dark:hover:text-[#dadadb] transition-all" "px-4 py-2 rounded-full text-sm font-medium cursor-pointer text-paper-primary border border-paper-border hover:border-paper-accent hover:text-paper-accent transition-all"
}, },
disabled: rebuilding(), disabled: rebuilding(),
onclick: move |_| do_rebuild(false), onclick: move |_| do_rebuild(false),
if rebuilding() { "重建中..." } else { "重建内容" } if rebuilding() { "重建中..." } else { "重建内容" }
} }
div { class: "pointer-events-none absolute top-full left-1/2 -translate-x-1/2 mt-2 px-3 py-1.5 text-xs font-medium whitespace-nowrap rounded-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 bg-gray-900 dark:bg-[#dadadb] text-white dark:text-[#1a1a1a] shadow-lg z-50", div { class: "pointer-events-none absolute top-full left-1/2 -translate-x-1/2 mt-2 px-3 py-1.5 text-xs font-medium whitespace-nowrap rounded-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 bg-paper-primary text-paper-theme shadow-lg z-50",
"重建 content_html 为空的文章渲染缓存" "重建 content_html 为空的文章渲染缓存"
} }
} }
div { class: "group relative", div { class: "group relative",
button { button {
class: if rebuilding() { class: if rebuilding() {
"px-4 py-2 rounded-full text-sm font-medium cursor-not-allowed text-gray-400 dark:text-[#666] border border-gray-300 dark:border-[#444]" "px-4 py-2 rounded-full text-sm font-medium cursor-not-allowed text-paper-secondary border border-paper-border"
} else { } else {
"px-4 py-2 rounded-full text-sm font-medium cursor-pointer text-gray-700 dark:text-[#b0b0b1] border border-gray-300 dark:border-[#444] hover:border-gray-900 dark:hover:border-[#dadadb] hover:text-gray-900 dark:hover:text-[#dadadb] transition-all" "px-4 py-2 rounded-full text-sm font-medium cursor-pointer text-paper-primary border border-paper-border hover:border-paper-accent hover:text-paper-accent transition-all"
}, },
disabled: rebuilding(), disabled: rebuilding(),
onclick: move |_| do_rebuild(true), onclick: move |_| do_rebuild(true),
if rebuilding() { "重建中..." } else { "重建全部" } if rebuilding() { "重建中..." } else { "重建全部" }
} }
div { class: "pointer-events-none absolute top-full left-1/2 -translate-x-1/2 mt-2 px-3 py-1.5 text-xs font-medium whitespace-nowrap rounded-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 bg-gray-900 dark:bg-[#dadadb] text-white dark:text-[#1a1a1a] shadow-lg z-50", div { class: "pointer-events-none absolute top-full left-1/2 -translate-x-1/2 mt-2 px-3 py-1.5 text-xs font-medium whitespace-nowrap rounded-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 bg-paper-primary text-paper-theme shadow-lg z-50",
"重建所有文章的渲染缓存(含已有内容)" "重建所有文章的渲染缓存(含已有内容)"
} }
} }
Link { Link {
class: "px-4 py-2 bg-gray-900 dark:bg-[#dadadb] text-white dark:text-gray-900 rounded-full text-sm font-medium hover:opacity-80 transition-opacity cursor-pointer", class: "px-4 py-2 bg-paper-accent text-paper-theme rounded-full text-sm font-medium hover:brightness-110 active:scale-[0.98] transition-all duration-200 cursor-pointer",
to: Route::Write {}, to: Route::Write {},
"+ 写文章" "+ 写文章"
} }
@ -144,7 +144,7 @@ pub fn PostsPage(page: i32) -> Element {
} }
if let Some(msg) = rebuild_result() { if let Some(msg) = rebuild_result() {
div { class: "text-sm text-gray-600 dark:text-[#9b9c9d] px-1", div { class: "text-sm text-paper-secondary px-1",
"{msg}" "{msg}"
} }
} }
@ -157,7 +157,7 @@ pub fn PostsPage(page: i32) -> Element {
div { class: "{ADMIN_TABLE_CLASS}", div { class: "{ADMIN_TABLE_CLASS}",
table { class: "w-full text-sm", table { class: "w-full text-sm",
thead { thead {
tr { class: "border-b border-gray-200 dark:border-[#333] text-left text-gray-500 dark:text-[#9b9c9d]", tr { class: "border-b border-paper-border text-left text-paper-secondary",
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-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", "日期" } th { class: "px-4 py-3 font-medium w-32", "日期" }
@ -222,7 +222,7 @@ fn PostRow(post: PostListItem, deleting: bool, on_delete: EventHandler<i32>) ->
tr { class: "{ADMIN_ROW_HOVER}", tr { class: "{ADMIN_ROW_HOVER}",
td { class: "px-4 py-3", td { class: "px-4 py-3",
Link { Link {
class: "text-gray-900 dark:text-[#dadadb] hover:opacity-80 transition-opacity", class: "text-paper-primary hover:text-paper-accent transition-colors cursor-pointer",
to: Route::PostDetail { slug: post.slug.clone() }, to: Route::PostDetail { slug: post.slug.clone() },
"{post.title}" "{post.title}"
} }
@ -233,19 +233,19 @@ fn PostRow(post: PostListItem, deleting: bool, on_delete: EventHandler<i32>) ->
label: post.status_label().to_string(), label: post.status_label().to_string(),
} }
} }
td { class: "px-4 py-3 text-gray-500 dark:text-[#9b9c9d]", td { class: "px-4 py-3 text-paper-secondary",
"{date_str}" "{date_str}"
} }
td { class: "px-4 py-3 text-right", td { class: "px-4 py-3 text-right",
div { class: "flex justify-end gap-3", div { class: "flex justify-end gap-3",
Link { Link {
class: "text-xs text-gray-600 dark:text-[#9b9c9d] hover:text-gray-900 dark:hover:text-[#dadadb] transition-colors cursor-pointer", class: "text-xs text-paper-secondary hover:text-paper-primary transition-colors cursor-pointer",
to: Route::WriteEdit { id: post.id }, to: Route::WriteEdit { id: post.id },
"编辑" "编辑"
} }
button { button {
class: if deleting { class: if deleting {
"text-xs text-gray-400 cursor-not-allowed" "text-xs text-paper-secondary cursor-not-allowed"
} else { } else {
BTN_TEXT_RED BTN_TEXT_RED
}, },

View File

@ -18,10 +18,11 @@ use crate::api::posts::{
use crate::api::posts::{list_deleted_posts, PostListResponse}; use crate::api::posts::{list_deleted_posts, PostListResponse};
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::api::settings::{get_trash_settings, update_trash_settings}; use crate::api::settings::{get_trash_settings, update_trash_settings};
use crate::components::skeletons::atoms::SkeletonBox;
use crate::components::skeletons::delayed_skeleton::DelayedSkeleton; use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
use crate::components::ui::{ use crate::components::ui::{
EmptyState, Pagination, StatusBadge, ADMIN_ROW_HOVER, ADMIN_TABLE_CLASS, BTN_SOLID_GREEN, EmptyState, Pagination, StatusBadge, ADMIN_CARD_CLASS, ADMIN_ROW_HOVER, ADMIN_TABLE_CLASS,
BTN_SOLID_RED, BTN_TEXT_ACCENT, BTN_TEXT_RED, CHECKBOX_CLASS, BTN_SOLID_GREEN, BTN_SOLID_RED, BTN_TEXT_ACCENT, BTN_TEXT_RED, CHECKBOX_CLASS,
}; };
use crate::models::post::PostListItem; use crate::models::post::PostListItem;
use crate::models::settings::TrashSettings; use crate::models::settings::TrashSettings;
@ -124,27 +125,27 @@ pub fn TrashPage(page: i32) -> Element {
div { class: "space-y-6", div { class: "space-y-6",
// 页面标题 // 页面标题
div { class: "flex items-center gap-3", div { class: "flex items-center gap-3",
h1 { class: "text-2xl font-bold text-gray-900 dark:text-[#dadadb]", "回收站" } h1 { class: "text-2xl font-bold text-paper-primary", "回收站" }
span { class: "text-sm text-gray-500 dark:text-[#9b9c9d]", span { class: "text-sm text-paper-secondary",
"共 {total()} 篇" "共 {total()} 篇"
} }
} }
// 自动清理配置卡片:可折叠的设置面板,顶部始终显示当前状态摘要 // 自动清理配置卡片:可折叠的设置面板,顶部始终显示当前状态摘要
div { div {
class: "rounded-xl border border-gray-200 dark:border-[#333] overflow-hidden bg-white dark:bg-[#2e2e33]", class: "rounded-xl border border-paper-border overflow-hidden bg-paper-entry",
// 顶部可点击摘要条:状态指示灯 + 标题 + 展开箭头 // 顶部可点击摘要条:状态指示灯 + 标题 + 展开箭头
button { button {
class: "w-full flex items-center gap-3 px-5 py-4 text-left cursor-pointer hover:bg-gray-50 dark:hover:bg-[#34343a] transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#5c7a5e]/40 dark:focus-visible:ring-[#7da97f]/40", 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 |_| { onclick: move |_| {
settings_panel_open.set(!settings_panel_open()); settings_panel_open.set(!settings_panel_open());
just_saved.set(false); just_saved.set(false);
}, },
// 状态指示灯 // 状态指示灯
{let dot_class = if settings().auto_purge_enabled { {let dot_class = if settings().auto_purge_enabled {
"w-2 h-2 rounded-full bg-[#5c7a5e] dark:bg-[#7da97f] shadow-[0_0_0_3px_rgba(92,122,94,0.15)]" "w-2 h-2 rounded-full bg-paper-accent shadow-[0_0_0_3px_rgba(92,122,94,0.15)]"
} else { } else {
"w-2 h-2 rounded-full bg-gray-300 dark:bg-[#666]" "w-2 h-2 rounded-full bg-paper-tertiary"
}; };
rsx! { rsx! {
div { class: "w-2 flex-shrink-0 flex items-center justify-center", div { class: "w-2 flex-shrink-0 flex items-center justify-center",
@ -153,10 +154,10 @@ pub fn TrashPage(page: i32) -> Element {
}} }}
// 标题 + 当前状态描述 // 标题 + 当前状态描述
div { class: "flex-1 min-w-0", div { class: "flex-1 min-w-0",
div { class: "text-sm font-medium text-gray-900 dark:text-[#dadadb]", div { class: "text-sm font-medium text-paper-primary",
"自动清理" "自动清理"
} }
div { class: "text-xs text-gray-500 dark:text-[#9b9c9d] mt-0.5 truncate", div { class: "text-xs text-paper-secondary mt-0.5 truncate",
if settings().auto_purge_enabled { if settings().auto_purge_enabled {
"已开启 · 超过 {settings().retention_days} 天的文章将被自动删除" "已开启 · 超过 {settings().retention_days} 天的文章将被自动删除"
} else { } else {
@ -166,7 +167,7 @@ pub fn TrashPage(page: i32) -> Element {
} }
// 展开箭头(旋转动画) // 展开箭头(旋转动画)
svg { svg {
class: "w-4 h-4 text-gray-400 dark:text-[#9b9c9d] transition-transform duration-200 flex-shrink-0 {chevron_rotate}", class: "w-4 h-4 text-paper-secondary transition-transform duration-200 flex-shrink-0 {chevron_rotate}",
view_box: "0 0 24 24", view_box: "0 0 24 24",
fill: "none", fill: "none",
stroke: "currentColor", stroke: "currentColor",
@ -177,15 +178,15 @@ pub fn TrashPage(page: i32) -> Element {
// 设置面板(可折叠) // 设置面板(可折叠)
if settings_panel_open() { if settings_panel_open() {
div { class: "border-t border-gray-100 dark:border-[#3a3a3e] p-5 space-y-6", div { class: "border-t border-paper-border p-5 space-y-6",
// 开关行:启用自动清理 // 开关行:启用自动清理
div { div {
class: "flex items-center justify-between gap-4", 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-gray-900 dark:text-[#dadadb]", div { class: "text-sm font-medium text-paper-primary",
"启用自动清理" "启用自动清理"
} }
div { class: "text-xs text-gray-500 dark:text-[#9b9c9d] mt-1", div { class: "text-xs text-paper-secondary mt-1",
"后台任务定期彻底删除超过保留期的文章" "后台任务定期彻底删除超过保留期的文章"
} }
} }
@ -194,9 +195,9 @@ pub fn TrashPage(page: i32) -> Element {
role: "switch", role: "switch",
aria_checked: "{settings_draft_enabled()}", aria_checked: "{settings_draft_enabled()}",
class: if settings_draft_enabled() { class: if settings_draft_enabled() {
"relative w-11 h-6 flex-shrink-0 rounded-full bg-[#5c7a5e] dark:bg-[#7da97f] cursor-pointer transition-colors duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#5c7a5e]/40 dark:focus-visible:ring-[#7da97f]/40" "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 { } else {
"relative w-11 h-6 flex-shrink-0 rounded-full bg-gray-300 dark:bg-[#555] cursor-pointer transition-colors duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#5c7a5e]/40 dark:focus-visible:ring-[#7da97f]/40" "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 |_| { onclick: move |_| {
settings_draft_enabled.set(!settings_draft_enabled()); settings_draft_enabled.set(!settings_draft_enabled());
@ -216,19 +217,19 @@ pub fn TrashPage(page: i32) -> Element {
// 保留天数行 // 保留天数行
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-gray-900 dark:text-[#dadadb]", div { class: "text-sm font-medium text-paper-primary",
"保留天数" "保留天数"
} }
div { class: "text-xs text-gray-500 dark:text-[#9b9c9d] mt-1", div { class: "text-xs text-paper-secondary mt-1",
"文章删除后保留的时长到期后自动彻底清除1365" "文章删除后保留的时长到期后自动彻底清除1365"
} }
} }
// 数字输入 + 步进按钮 + 单位后缀 // 数字输入 + 步进按钮 + 单位后缀
div { class: "flex items-center gap-3", div { class: "flex items-center gap-3",
div { class: "flex items-center rounded-lg border border-gray-300 dark:border-[#444] bg-white dark:bg-[#1d1e20] overflow-hidden", div { class: "flex items-center rounded-lg border border-paper-border bg-paper-entry overflow-hidden",
// 减号 // 减号
button { button {
class: "w-9 h-9 flex items-center justify-center text-sm text-gray-500 dark:text-[#9b9c9d] hover:text-gray-900 dark:hover:text-[#dadadb] hover:bg-gray-50 dark:hover:bg-[#2a2a2a] cursor-pointer transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#5c7a5e]/40 dark:focus-visible:ring-[#7da97f]/40", 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", r#type: "button",
aria_label: "减少保留天数", aria_label: "减少保留天数",
onclick: move |_| { onclick: move |_| {
@ -244,7 +245,7 @@ pub fn TrashPage(page: i32) -> Element {
r#type: "number", r#type: "number",
min: "1", min: "1",
max: "365", max: "365",
class: "w-14 h-9 px-1 text-center text-sm tabular-nums text-gray-900 dark:text-[#dadadb] bg-transparent border-0 focus:outline-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none", 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()}", value: "{settings_draft_days()}",
oninput: move |e| { oninput: move |e| {
settings_draft_days.set(e.value()); settings_draft_days.set(e.value());
@ -253,7 +254,7 @@ pub fn TrashPage(page: i32) -> Element {
} }
// 加号 // 加号
button { button {
class: "w-9 h-9 flex items-center justify-center text-sm text-gray-500 dark:text-[#9b9c9d] hover:text-gray-900 dark:hover:text-[#dadadb] hover:bg-gray-50 dark:hover:bg-[#2a2a2a] cursor-pointer transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#5c7a5e]/40 dark:focus-visible:ring-[#7da97f]/40", 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", r#type: "button",
aria_label: "增加保留天数", aria_label: "增加保留天数",
onclick: move |_| { onclick: move |_| {
@ -265,7 +266,7 @@ pub fn TrashPage(page: i32) -> Element {
"+" "+"
} }
} }
span { class: "text-xs text-gray-400 dark:text-[#666]", "" } span { class: "text-xs text-paper-secondary", "" }
} }
} }
@ -273,14 +274,14 @@ pub fn TrashPage(page: i32) -> Element {
div { class: "flex items-center justify-between gap-4 pt-1", div { class: "flex items-center justify-between gap-4 pt-1",
// 草稿状态提示 // 草稿状态提示
if just_saved() { if just_saved() {
span { class: "inline-flex items-center gap-1.5 text-xs text-[#5c7a5e] dark:text-[#7da97f]", 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", 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" } path { stroke_linecap: "round", stroke_linejoin: "round", d: "M5 13l4 4L19 7" }
} }
"已保存" "已保存"
} }
} else if dirty { } else if dirty {
span { class: "text-xs text-gray-400 dark:text-[#666]", span { class: "text-xs text-paper-secondary",
"有未保存的更改" "有未保存的更改"
} }
} else { } else {
@ -289,11 +290,11 @@ pub fn TrashPage(page: i32) -> Element {
// 保存按钮:启用主题色,禁用/保存中态灰化 // 保存按钮:启用主题色,禁用/保存中态灰化
button { button {
class: if saving_settings() { class: if saving_settings() {
"inline-flex items-center gap-1.5 px-4 py-1.5 text-sm font-medium cursor-not-allowed text-gray-400 dark:text-[#666] bg-gray-100 dark:bg-[#2a2a2a] rounded-full" "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() { } else if just_saved() {
"inline-flex items-center gap-1.5 px-4 py-1.5 text-sm font-medium cursor-not-allowed text-gray-400 dark:text-[#666] bg-gray-100 dark:bg-[#2a2a2a] rounded-full" "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 { } else {
"inline-flex items-center gap-1.5 px-4 py-1.5 text-sm font-medium text-white bg-[#5c7a5e] dark:bg-[#7da97f] dark:text-[#1d1e20] rounded-full hover:brightness-110 active:scale-[0.98] transition-all cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-[#5c7a5e]/40 dark:focus-visible:ring-[#7da97f]/40" "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, disabled: saving_settings() || just_saved() || !dirty,
onclick: move |_| { onclick: move |_| {
@ -317,8 +318,8 @@ pub fn TrashPage(page: i32) -> Element {
// 批量操作栏(选中时显示) // 批量操作栏(选中时显示)
if !selected_ids().is_empty() { if !selected_ids().is_empty() {
div { class: "flex items-center gap-3 p-3 bg-gray-50 dark:bg-[#2a2a2a] rounded-lg", div { class: "flex items-center gap-3 p-3 bg-paper-theme rounded-lg",
span { class: "text-sm text-gray-600 dark:text-[#9b9c9d]", span { class: "text-sm text-paper-secondary",
"已选择 {selected_ids().len()} 条" "已选择 {selected_ids().len()} 条"
} }
button { button {
@ -363,9 +364,9 @@ pub fn TrashPage(page: i32) -> Element {
} else if loading() && posts().is_empty() { } else if loading() && posts().is_empty() {
rsx! { rsx! {
DelayedSkeleton { DelayedSkeleton {
div { class: "bg-white dark:bg-[#2e2e33] rounded-xl border border-gray-200 dark:border-[#333] p-6 space-y-4", div { class: "{ADMIN_CARD_CLASS} p-6 space-y-4",
for _ in 0..5 { for _ in 0..5 {
div { class: "h-10 bg-gray-200 dark:bg-[#2a2a2a] rounded" } SkeletonBox { class: "h-10 rounded" }
} }
} }
} }
@ -381,7 +382,7 @@ pub fn TrashPage(page: i32) -> Element {
div { class: "overflow-x-auto", div { class: "overflow-x-auto",
table { class: "w-full text-sm", table { class: "w-full text-sm",
thead { thead {
tr { class: "border-b border-gray-200 dark:border-[#333] text-left text-gray-500 dark:text-[#9b9c9d]", tr { class: "border-b border-paper-border text-left text-paper-secondary",
th { class: "px-4 py-3 font-medium w-10", th { class: "px-4 py-3 font-medium w-10",
input { input {
r#type: "checkbox", r#type: "checkbox",
@ -535,9 +536,9 @@ fn TrashRow(
let badge_class = if expired { let badge_class = if expired {
"bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400" "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"
} else if remaining <= 7 { } else if remaining <= 7 {
"bg-[#e8f0e8] text-[#5c7a5e] dark:bg-[#1e2e1e] dark:text-[#7da97f]" "bg-paper-accent-soft text-paper-accent"
} else { } else {
"bg-gray-100 text-gray-600 dark:bg-[#333] dark:text-[#9b9c9d]" "bg-paper-tertiary text-paper-secondary"
}; };
let badge_text = if expired { "待清理".to_string() } else { format!("{remaining}") }; let badge_text = if expired { "待清理".to_string() } else { format!("{remaining}") };
let deleted_str = post let deleted_str = post
@ -556,7 +557,7 @@ fn TrashRow(
} }
} }
td { class: "px-4 py-3", td { class: "px-4 py-3",
div { class: "text-sm font-medium text-gray-900 dark:text-[#dadadb] truncate max-w-xs", div { class: "text-sm font-medium text-paper-primary truncate max-w-xs",
"{post.title}" "{post.title}"
} }
} }
@ -566,7 +567,7 @@ fn TrashRow(
label: post.status_label().to_string(), label: post.status_label().to_string(),
} }
} }
td { class: "px-4 py-3 text-sm text-gray-500 dark:text-[#9b9c9d]", td { class: "px-4 py-3 text-sm text-paper-secondary",
"{deleted_str}" "{deleted_str}"
} }
td { class: "px-4 py-3 text-center", td { class: "px-4 py-3 text-center",

View File

@ -423,7 +423,7 @@ fn write_editor(post_id: Option<i32>) -> Element {
rsx! { rsx! {
div { class: "relative flex flex-col flex-1 min-h-0 overflow-hidden", div { class: "relative flex flex-col flex-1 min-h-0 overflow-hidden",
if loading() { if loading() {
div { class: "absolute inset-0 z-10 bg-white dark:bg-[#1d1e20]", div { class: "absolute inset-0 z-10 bg-paper-theme",
WriteSkeleton {} WriteSkeleton {}
} }
} }