refactor: remove PageLayout from all frontend pages, delegate to FrontendLayout
- Remove PageLayout wrapper from Home, HomePage, Archives, Tags, TagDetail, PostDetail, Search, and About components - Remove unused imports: use_nav_items, use_route, PageLayout, Route - Pages now render only their content; Header/Footer are provided by FrontendLayout via the router's #[layout] attribute - Skeleton screens (DelayedSkeleton) remain in data-loading branches - This eliminates redundant Header/Footer re-mounting on every route change, which was the primary source of page transition flicker Files changed: - src/pages/home.rs: remove PageLayout, keep HomeInfo + HomePosts - src/pages/about.rs: remove PageLayout, render content directly - src/pages/archives.rs: remove PageLayout, keep header + ArchivesContent - src/pages/post_detail.rs: remove PageLayout, keep PostDetailContent - src/pages/search.rs: remove PageLayout, keep search form + results - src/pages/tags.rs: remove PageLayout from Tags and TagDetail
This commit is contained in:
parent
8845577958
commit
5d018864c2
@ -1,40 +1,31 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::components::nav::use_nav_items;
|
||||
use crate::components::page_layout::PageLayout;
|
||||
use crate::router::Route;
|
||||
|
||||
#[component]
|
||||
pub fn About() -> Element {
|
||||
let route = use_route::<Route>();
|
||||
let nav_items = use_nav_items(route);
|
||||
|
||||
rsx! {
|
||||
PageLayout { nav_items,
|
||||
header { class: "page-header mb-6",
|
||||
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
||||
"关于"
|
||||
}
|
||||
header { class: "page-header mb-6",
|
||||
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
||||
"关于"
|
||||
}
|
||||
article { class: "prose dark:prose-invert max-w-none text-gray-800 dark:text-[#c9cacc] leading-relaxed",
|
||||
p { "Yggdrasil 是一个以文字为主的简约博客系统。" }
|
||||
p { "它使用 Rust + Dioxus 构建,采用 PostgreSQL 作为数据库,支持 Markdown 写作、标签管理和暗色模式。" }
|
||||
h2 { class: "text-xl font-bold text-gray-900 dark:text-[#dadadb] 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-gray-900 dark:text-[#dadadb] mt-8 mb-4", "特性" }
|
||||
ul { class: "list-disc pl-5 space-y-1",
|
||||
li { "Markdown 写作与实时预览" }
|
||||
li { "文章标签与归档" }
|
||||
li { "暗色/亮色主题切换" }
|
||||
li { "响应式设计" }
|
||||
li { "文章搜索" }
|
||||
}
|
||||
}
|
||||
article { class: "prose dark:prose-invert max-w-none text-gray-800 dark:text-[#c9cacc] leading-relaxed",
|
||||
p { "Yggdrasil 是一个以文字为主的简约博客系统。" }
|
||||
p { "它使用 Rust + Dioxus 构建,采用 PostgreSQL 作为数据库,支持 Markdown 写作、标签管理和暗色模式。" }
|
||||
h2 { class: "text-xl font-bold text-gray-900 dark:text-[#dadadb] 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-gray-900 dark:text-[#dadadb] mt-8 mb-4", "特性" }
|
||||
ul { class: "list-disc pl-5 space-y-1",
|
||||
li { "Markdown 写作与实时预览" }
|
||||
li { "文章标签与归档" }
|
||||
li { "暗色/亮色主题切换" }
|
||||
li { "响应式设计" }
|
||||
li { "文章搜索" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::api::posts::{list_published_posts, PostListResponse};
|
||||
use crate::components::nav::use_nav_items;
|
||||
use crate::components::page_layout::PageLayout;
|
||||
use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
|
||||
use crate::components::skeletons::archive_skeleton::ArchiveSkeleton;
|
||||
use crate::models::post::Post;
|
||||
use crate::router::Route;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
struct YearGroup {
|
||||
@ -80,18 +77,13 @@ fn group_posts(posts: &[Post]) -> Vec<YearGroup> {
|
||||
|
||||
#[component]
|
||||
pub fn Archives() -> Element {
|
||||
let route = use_route::<Route>();
|
||||
let nav_items = use_nav_items(route);
|
||||
|
||||
rsx! {
|
||||
PageLayout { nav_items,
|
||||
header { class: "page-header mb-6",
|
||||
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
||||
"归档"
|
||||
}
|
||||
header { class: "page-header mb-6",
|
||||
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
||||
"归档"
|
||||
}
|
||||
ArchivesContent {}
|
||||
}
|
||||
ArchivesContent {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::api::posts::{list_published_posts, PostListResponse};
|
||||
use crate::components::nav::use_nav_items;
|
||||
use crate::components::page_layout::PageLayout;
|
||||
use crate::components::post_card::PostCard;
|
||||
use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
|
||||
use crate::components::skeletons::home_skeleton::HomeSkeleton;
|
||||
use crate::router::Route;
|
||||
|
||||
const POSTS_PER_PAGE: i32 = 10;
|
||||
|
||||
@ -17,15 +14,11 @@ pub fn Home() -> Element {
|
||||
|
||||
#[component]
|
||||
pub fn HomePage(page: i32) -> Element {
|
||||
let route = use_route::<Route>();
|
||||
let nav_items = use_nav_items(route);
|
||||
let current_page = page.max(1);
|
||||
|
||||
rsx! {
|
||||
PageLayout { nav_items,
|
||||
HomeInfo {}
|
||||
HomePosts { current_page }
|
||||
}
|
||||
HomeInfo {}
|
||||
HomePosts { current_page }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::api::posts::{get_post_by_slug, SinglePostResponse};
|
||||
use crate::components::nav::use_nav_items;
|
||||
use crate::components::page_layout::PageLayout;
|
||||
use crate::components::post::post_content::PostContent;
|
||||
use crate::components::post::post_cover::PostCover;
|
||||
use crate::components::post::post_footer::PostFooter;
|
||||
@ -10,17 +8,11 @@ use crate::components::post::post_header::PostHeader;
|
||||
use crate::components::post::post_toc::PostToc;
|
||||
use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
|
||||
use crate::components::skeletons::post_detail_skeleton::PostDetailSkeleton;
|
||||
use crate::router::Route;
|
||||
|
||||
#[component]
|
||||
pub fn PostDetail(slug: String) -> Element {
|
||||
let route = use_route::<Route>();
|
||||
let nav_items = use_nav_items(route);
|
||||
|
||||
rsx! {
|
||||
PageLayout { nav_items,
|
||||
PostDetailContent { slug: slug.clone() }
|
||||
}
|
||||
PostDetailContent { slug: slug.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,20 +1,15 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::api::posts::{search_posts, PostListResponse};
|
||||
use crate::components::nav::use_nav_items;
|
||||
use crate::components::page_layout::PageLayout;
|
||||
use crate::components::post_card::PostCard;
|
||||
use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
|
||||
use crate::components::skeletons::search_skeleton::SearchSkeleton;
|
||||
use crate::router::Route;
|
||||
|
||||
#[component]
|
||||
pub fn Search() -> Element {
|
||||
let route = use_route::<Route>();
|
||||
let mut query = use_signal(|| "".to_string());
|
||||
let mut search_res = use_signal(|| None::<Result<PostListResponse, ServerFnError>>);
|
||||
let mut is_searching = use_signal(|| false);
|
||||
let nav_items = use_nav_items(route);
|
||||
let mut on_search = move || {
|
||||
let q = query().trim().to_string();
|
||||
if q.is_empty() {
|
||||
@ -30,45 +25,43 @@ pub fn Search() -> Element {
|
||||
};
|
||||
|
||||
rsx! {
|
||||
PageLayout { nav_items,
|
||||
header { class: "page-header mb-6",
|
||||
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
||||
header { class: "page-header mb-6",
|
||||
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
||||
"搜索"
|
||||
}
|
||||
}
|
||||
div { class: "mb-8",
|
||||
div { class: "flex gap-2",
|
||||
input {
|
||||
class: "flex-1 px-4 py-2 border border-gray-200 dark:border-[#333] rounded-lg bg-white dark:bg-[#2e2e33] text-gray-900 dark:text-[#dadadb] focus:outline-none focus:border-gray-400 dark:focus:border-gray-600",
|
||||
r#type: "text",
|
||||
placeholder: "输入关键词搜索文章...",
|
||||
value: query(),
|
||||
oninput: move |e| query.set(e.value()),
|
||||
onkeydown: move |e| if e.key() == Key::Enter { on_search() },
|
||||
}
|
||||
button {
|
||||
class: "px-6 py-2 bg-gray-900 dark:bg-[#dadadb] text-white dark:text-gray-900 rounded-full font-medium hover:opacity-80 transition-opacity",
|
||||
onclick: move |_| on_search(),
|
||||
"搜索"
|
||||
}
|
||||
}
|
||||
div { class: "mb-8",
|
||||
div { class: "flex gap-2",
|
||||
input {
|
||||
class: "flex-1 px-4 py-2 border border-gray-200 dark:border-[#333] rounded-lg bg-white dark:bg-[#2e2e33] text-gray-900 dark:text-[#dadadb] focus:outline-none focus:border-gray-400 dark:focus:border-gray-600",
|
||||
r#type: "text",
|
||||
placeholder: "输入关键词搜索文章...",
|
||||
value: query(),
|
||||
oninput: move |e| query.set(e.value()),
|
||||
onkeydown: move |e| if e.key() == Key::Enter { on_search() },
|
||||
}
|
||||
button {
|
||||
class: "px-6 py-2 bg-gray-900 dark:bg-[#dadadb] text-white dark:text-gray-900 rounded-full font-medium hover:opacity-80 transition-opacity",
|
||||
onclick: move |_| on_search(),
|
||||
"搜索"
|
||||
}
|
||||
}
|
||||
if is_searching() {
|
||||
DelayedSkeleton { SearchSkeleton {} }
|
||||
} else if let Some(Ok(PostListResponse { posts })) = search_res() {
|
||||
if posts.is_empty() {
|
||||
div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20",
|
||||
"未找到相关文章"
|
||||
}
|
||||
} else {
|
||||
for post in posts.iter() {
|
||||
PostCard { post: post.clone() }
|
||||
}
|
||||
}
|
||||
if is_searching() {
|
||||
DelayedSkeleton { SearchSkeleton {} }
|
||||
} else if let Some(Ok(PostListResponse { posts })) = search_res() {
|
||||
if posts.is_empty() {
|
||||
div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20",
|
||||
"未找到相关文章"
|
||||
}
|
||||
} else {
|
||||
for post in posts.iter() {
|
||||
PostCard { post: post.clone() }
|
||||
}
|
||||
}
|
||||
} else if let Some(Err(e)) = search_res() {
|
||||
div { class: "text-center text-red-500 dark:text-red-400 py-20",
|
||||
"搜索失败: {e}"
|
||||
}
|
||||
} else if let Some(Err(e)) = search_res() {
|
||||
div { class: "text-center text-red-500 dark:text-red-400 py-20",
|
||||
"搜索失败: {e}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,27 +1,19 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::api::posts::{get_posts_by_tag, list_tags, PostListResponse, TagListResponse};
|
||||
use crate::components::nav::use_nav_items;
|
||||
use crate::components::page_layout::PageLayout;
|
||||
use crate::components::post_card::PostCard;
|
||||
use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
|
||||
use crate::components::skeletons::tags_skeleton::{TagsSkeleton, TagDetailSkeleton};
|
||||
use crate::router::Route;
|
||||
|
||||
#[component]
|
||||
pub fn Tags() -> Element {
|
||||
let route = use_route::<Route>();
|
||||
let nav_items = use_nav_items(route);
|
||||
|
||||
rsx! {
|
||||
PageLayout { nav_items,
|
||||
header { class: "page-header mb-6",
|
||||
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
||||
"标签"
|
||||
}
|
||||
header { class: "page-header mb-6",
|
||||
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
||||
"标签"
|
||||
}
|
||||
TagsContent {}
|
||||
}
|
||||
TagsContent {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,18 +72,13 @@ fn TagsContent() -> Element {
|
||||
|
||||
#[component]
|
||||
pub fn TagDetail(tag: String) -> Element {
|
||||
let route = use_route::<Route>();
|
||||
let nav_items = use_nav_items(route);
|
||||
|
||||
rsx! {
|
||||
PageLayout { nav_items,
|
||||
header { class: "page-header mb-6",
|
||||
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
||||
"{tag}"
|
||||
}
|
||||
header { class: "page-header mb-6",
|
||||
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
||||
"{tag}"
|
||||
}
|
||||
TagDetailContent { tag: tag.clone() }
|
||||
}
|
||||
TagDetailContent { tag: tag.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user