feat: add pagination for search result.
This commit is contained in:
parent
388edd7655
commit
d003aaccb0
883
package-lock.json
generated
883
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -44,12 +44,12 @@
|
||||
"@astrojs/mdx": "^3.1.2",
|
||||
"@astrojs/node": "^8.3.2",
|
||||
"@astrojs/rss": "^4.0.7",
|
||||
"astro": "^4.11.3",
|
||||
"astro": "^4.11.5",
|
||||
"drizzle-orm": "^0.31.2",
|
||||
"fuse.js": "^7.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.4.4",
|
||||
"marked": "^13.0.1",
|
||||
"marked": "^13.0.2",
|
||||
"pg": "^8.12.0",
|
||||
"qrcode-svg": "^1.1.0",
|
||||
"ultrahtml": "^1.5.3"
|
||||
@ -60,7 +60,7 @@
|
||||
"@napi-rs/canvas": "^0.1.53",
|
||||
"@types/lodash": "^4.17.6",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/node": "^20.14.9",
|
||||
"@types/node": "^20.14.10",
|
||||
"@types/pg": "^8.11.6",
|
||||
"@types/qrcode-svg": "^1.1.4",
|
||||
"@types/unist": "^3.0.2",
|
||||
@ -73,7 +73,7 @@
|
||||
"prettier-plugin-organize-imports": "^4.0.0",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"resize-sensor": "^0.0.6",
|
||||
"rimraf": "^5.0.7",
|
||||
"rimraf": "^5.0.8",
|
||||
"sharp": "^0.33.4",
|
||||
"typescript": "^5.5.3",
|
||||
"unist-util-select": "^5.1.0"
|
||||
|
@ -91,7 +91,7 @@ if (typeof searchSidebar !== 'undefined' && searchSidebar !== null) {
|
||||
|
||||
const query = event.target.value;
|
||||
event.target.value = '';
|
||||
location.href = `/search?q=${encodeURIComponent(query)}`;
|
||||
location.href = `/search/${encodeURIComponent(query)}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -108,6 +108,15 @@ document.querySelector('.global-search-close').addEventListener('click', (event)
|
||||
searchPopup.classList.toggle('nice-popup-open', false);
|
||||
});
|
||||
|
||||
searchPopup.querySelector('.search-dialog').addEventListener('submit', (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const formData = new FormData(event.target);
|
||||
const query = formData.get('q');
|
||||
location.href = `/search/${encodeURIComponent(query)}`;
|
||||
});
|
||||
|
||||
// Loading the comments.
|
||||
const comments = document.querySelector('#comments');
|
||||
if (typeof comments !== 'undefined' && comments !== null) {
|
||||
|
@ -2,7 +2,7 @@
|
||||
import Logo from '@/components/header/Logo.astro';
|
||||
import LogoLarge from '@/components/header/LogoLarge.astro';
|
||||
import QRDialog from '@/components/image/QRDialog.astro';
|
||||
import SearchDialog from '@/components/search/SearchDialog.astro';
|
||||
import SearchIcon from '@/components/search/SearchIcon.astro';
|
||||
import options from '@/options';
|
||||
---
|
||||
|
||||
@ -50,7 +50,7 @@ import options from '@/options';
|
||||
}
|
||||
})
|
||||
}
|
||||
<SearchDialog />
|
||||
<SearchIcon />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
@ -1,13 +1,3 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div class="global-search btn btn-dark btn-icon btn-circle site-search-toggler button-social">
|
||||
<span>
|
||||
<i class="iconfont icon-search"></i>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="global-search-popup nice-popup nice-popup-center nice-popup-md">
|
||||
<div class="nice-popup-overlay"></div>
|
||||
<div class="nice-popup-body">
|
||||
@ -16,7 +6,7 @@
|
||||
<span class="svg-dark"></span>
|
||||
</div>
|
||||
<div class="nice-popup-content">
|
||||
<form class="text-center p-3 p-md-5" action={'/search'}>
|
||||
<form class="search-dialog text-center p-3 p-md-5" action="/search">
|
||||
<div class="mb-3 mb-md-4">
|
||||
<input
|
||||
class="form-control form-control-lg text-center"
|
||||
|
5
src/components/search/SearchIcon.astro
Normal file
5
src/components/search/SearchIcon.astro
Normal file
@ -0,0 +1,5 @@
|
||||
<div class="global-search btn btn-dark btn-icon btn-circle site-search-toggler button-social">
|
||||
<span>
|
||||
<i class="iconfont icon-search"></i>
|
||||
</span>
|
||||
</div>
|
@ -10,6 +10,7 @@ import '@/assets/styles/opposans.css';
|
||||
import Footer from '@/components/footer/Footer.astro';
|
||||
import Header from '@/components/header/Header.astro';
|
||||
import PageMeta from '@/components/meta/PageMeta.astro';
|
||||
import SearchDialog from '@/components/search/SearchDialog.astro';
|
||||
import options from '@/options';
|
||||
|
||||
interface Props {
|
||||
@ -67,5 +68,6 @@ const description = Astro.props.description || options.description;
|
||||
<script>
|
||||
import '../assets/scripts/yufan.me.js';
|
||||
</script>
|
||||
<SearchDialog />
|
||||
</body>
|
||||
</html>
|
||||
|
@ -4,8 +4,7 @@ import PostCards from '@/components/page/post/PostCards.astro';
|
||||
import PinnedCategories from '@/components/page/post/PostCategory.astro';
|
||||
import Sidebar from '@/components/sidebar/Sidebar.astro';
|
||||
import type { Post, Tag } from '@/helpers/schema';
|
||||
|
||||
import BaseLayout from '../BaseLayout.astro';
|
||||
import BaseLayout from '@/layouts/BaseLayout.astro';
|
||||
|
||||
interface Props {
|
||||
posts: Post[];
|
||||
|
@ -1,14 +1,31 @@
|
||||
---
|
||||
import Pagination from '@/components/page/pagination/Pagination.astro';
|
||||
import PostSquare from '@/components/page/post/PostSquare.astro';
|
||||
import { slicePosts } from '@/helpers/formatter';
|
||||
import type { Post } from '@/helpers/schema';
|
||||
import BaseLayout from '@/layouts/BaseLayout.astro';
|
||||
import options from '@/options';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
query: string;
|
||||
posts: Post[];
|
||||
pageNum: number;
|
||||
}
|
||||
|
||||
const { title, posts } = Astro.props;
|
||||
const { title, posts, query, pageNum } = Astro.props;
|
||||
|
||||
// Redirect to the homepage.
|
||||
if (pageNum < 1) {
|
||||
return Astro.redirect('/');
|
||||
}
|
||||
|
||||
const results = slicePosts(posts, pageNum, options.settings.pagination.search);
|
||||
if (!results) {
|
||||
return Astro.redirect('/404');
|
||||
}
|
||||
|
||||
const { currentPosts, totalPage } = results;
|
||||
---
|
||||
|
||||
<BaseLayout {title}>
|
||||
@ -18,7 +35,7 @@ const { title, posts } = Astro.props;
|
||||
<h1>{title}</h1>
|
||||
</div>
|
||||
{
|
||||
posts.length === 0 ? (
|
||||
currentPosts.length === 0 ? (
|
||||
<div class="data-null">
|
||||
<div class="my-auto">
|
||||
<h1 class="font-number">404</h1>
|
||||
@ -26,11 +43,18 @@ const { title, posts } = Astro.props;
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div class="row g-2 g-md-3 g-xxl-4 list-grouped">
|
||||
{posts.map((post, index) => (
|
||||
<PostSquare post={post} first={index === 0} />
|
||||
))}
|
||||
</div>
|
||||
<>
|
||||
<div class="row g-2 g-md-3 g-xxl-4 list-grouped">
|
||||
{currentPosts.map((post, index) => (
|
||||
<PostSquare post={post} first={index === 0} />
|
||||
))}
|
||||
</div>
|
||||
{totalPage > 1 && (
|
||||
<div class="mt-4 mt-lg-5">
|
||||
<Pagination current={pageNum} total={totalPage} rootPath={`/search/${query}`} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
19
src/pages/search/[keyword]/index.astro
Normal file
19
src/pages/search/[keyword]/index.astro
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
import { posts } from '@/helpers/schema';
|
||||
import { searchPosts } from '@/helpers/search';
|
||||
import SearchLayout from '@/layouts/posts/SearchLayout.astro';
|
||||
|
||||
const { keyword } = Astro.params;
|
||||
const query = keyword || '';
|
||||
if (query === '') {
|
||||
return Astro.redirect('/');
|
||||
}
|
||||
|
||||
const title = `【${query}】搜索结果`;
|
||||
|
||||
const searchResults = searchPosts(query)
|
||||
.map((slug) => posts.find((post) => post.slug === slug))
|
||||
.filter((post) => post !== undefined);
|
||||
---
|
||||
|
||||
<SearchLayout {title} {query} pageNum={1} posts={searchResults} />
|
32
src/pages/search/[keyword]/page/[num].astro
Normal file
32
src/pages/search/[keyword]/page/[num].astro
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
import { posts } from '@/helpers/schema';
|
||||
import { searchPosts } from '@/helpers/search';
|
||||
import SearchLayout from '@/layouts/posts/SearchLayout.astro';
|
||||
import options from '@/options';
|
||||
|
||||
const { keyword, num } = Astro.params;
|
||||
const query = keyword || '';
|
||||
if (query === '' || !num) {
|
||||
return Astro.redirect('/');
|
||||
}
|
||||
|
||||
const pageNum = Number.parseInt(num);
|
||||
if (pageNum <= 1) {
|
||||
return Astro.redirect('/');
|
||||
}
|
||||
|
||||
const title = `【${query}】搜索结果`;
|
||||
|
||||
const searchResults = searchPosts(query)
|
||||
.map((slug) => posts.find((post) => post.slug === slug))
|
||||
.filter((post) => post !== undefined);
|
||||
|
||||
const pageSize = options.settings.pagination.tags;
|
||||
const total = Math.ceil(searchResults.length / pageSize);
|
||||
|
||||
if (pageNum > total) {
|
||||
return Astro.redirect('/404');
|
||||
}
|
||||
---
|
||||
|
||||
<SearchLayout {title} {query} {pageNum} posts={searchResults} />
|
3
src/pages/search/[keyword]/page/index.astro
Normal file
3
src/pages/search/[keyword]/page/index.astro
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
return Astro.redirect('/');
|
||||
---
|
@ -1,20 +1,3 @@
|
||||
---
|
||||
import { posts } from '@/helpers/schema';
|
||||
import { searchPosts } from '@/helpers/search';
|
||||
import SearchLayout from '@/layouts/posts/SearchLayout.astro';
|
||||
import options from '@/options';
|
||||
|
||||
const query = Astro.url.searchParams.get('q') || '';
|
||||
if (query === '') {
|
||||
return Astro.redirect('/');
|
||||
}
|
||||
|
||||
const title = `${query} 查询结果`;
|
||||
|
||||
const searchResults = searchPosts(query)
|
||||
.map((slug) => posts.find((post) => post.slug === slug))
|
||||
.filter((post) => post !== undefined)
|
||||
.slice(0, options.settings.pagination.search);
|
||||
return Astro.redirect('/');
|
||||
---
|
||||
|
||||
<SearchLayout {title} posts={searchResults} />
|
||||
|
Loading…
Reference in New Issue
Block a user