From 8a1bf132f2692a50a5b5464373b243fe7a001480 Mon Sep 17 00:00:00 2001 From: tangly Date: Fri, 18 Nov 2022 13:12:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=90=9C=E7=B4=A2=E9=A1=B5=E9=9D=A2=E5=88=86?= =?UTF-8?q?=E9=A1=B5=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{[keyword].js => [keyword]/index.js} | 7 + pages/search/[keyword]/page/[page].js | 156 ++++++++++++++++++ themes/example/LayoutIndex.js | 4 +- themes/example/LayoutSearch.js | 55 +----- themes/example/components/BlogListPage.js | 54 +++--- themes/example/components/BlogListScroll.js | 2 +- themes/fukasawa/LayoutSearch.js | 4 +- themes/fukasawa/LayoutTag.js | 4 +- themes/fukasawa/components/BlogListPage.js | 4 +- themes/hexo/LayoutSearch.js | 111 +++++++------ themes/medium/LayoutSearch.js | 8 +- themes/medium/LayoutTag.js | 2 +- themes/medium/components/BlogPostListPage.js | 4 +- themes/next/LayoutSearch.js | 10 +- themes/next/LayoutTag.js | 20 +-- themes/next/components/BlogPostListPage.js | 4 +- themes/next/components/BlogPostListScroll.js | 4 +- themes/next/components/PaginationNumber.js | 116 +++++++------ 18 files changed, 354 insertions(+), 215 deletions(-) rename pages/search/{[keyword].js => [keyword]/index.js} (93%) create mode 100644 pages/search/[keyword]/page/[page].js diff --git a/pages/search/[keyword].js b/pages/search/[keyword]/index.js similarity index 93% rename from pages/search/[keyword].js rename to pages/search/[keyword]/index.js index e478920d..91b55de6 100644 --- a/pages/search/[keyword].js +++ b/pages/search/[keyword]/index.js @@ -38,6 +38,13 @@ export async function getStaticProps({ params: { keyword } }) { const { allPages } = props const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published') props.posts = await filterByMemCache(allPosts, keyword) + props.postCount = props.posts.length + // 处理分页 + if (BLOG.POST_LIST_STYLE === 'scroll') { + // 滚动列表 给前端返回所有数据 + } else if (BLOG.POST_LIST_STYLE === 'page') { + props.posts = props.posts?.slice(0, BLOG.POSTS_PER_PAGE - 1) + } props.keyword = keyword return { props, diff --git a/pages/search/[keyword]/page/[page].js b/pages/search/[keyword]/page/[page].js new file mode 100644 index 00000000..6d4cf070 --- /dev/null +++ b/pages/search/[keyword]/page/[page].js @@ -0,0 +1,156 @@ +import { getGlobalNotionData } from '@/lib/notion/getNotionData' +import { useGlobal } from '@/lib/global' +import { getDataFromCache } from '@/lib/cache/cache_manager' +import * as ThemeMap from '@/themes' +import BLOG from '@/blog.config' + +const Index = props => { + const { keyword, siteInfo } = props + const { locale } = useGlobal() + const meta = { + title: `${keyword || ''}${keyword ? ' | ' : ''}${locale.NAV.SEARCH} | ${siteInfo?.title}`, + description: siteInfo?.title, + image: siteInfo?.pageCover, + slug: 'search/' + (keyword || ''), + type: 'website' + } + const { theme } = useGlobal() + const ThemeComponents = ThemeMap[theme] + return ( + + ) +} + +/** + * 服务端搜索 + * @param {*} param0 + * @returns + */ +export async function getStaticProps({ params: { keyword, page } }) { + const props = await getGlobalNotionData({ + from: 'search-props', + pageType: ['Post'] + }) + const { allPages } = props + const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published') + props.posts = await filterByMemCache(allPosts, keyword) + props.postCount = props.posts.length + // 处理分页 + props.posts = props.posts.slice(BLOG.POSTS_PER_PAGE * (page - 1), BLOG.POSTS_PER_PAGE * page - 1) + props.keyword = keyword + props.page = page + return { + props, + revalidate: 1 + } +} + +export async function getStaticPaths() { + return { + paths: [{ params: { keyword: BLOG.TITLE, page: '1' } }], + fallback: true + } +} + +/** + * 将对象的指定字段拼接到字符串 + * @param sourceTextArray + * @param targetObj + * @param key + * @returns {*} + */ +function appendText(sourceTextArray, targetObj, key) { + if (!targetObj) { + return sourceTextArray + } + const textArray = targetObj[key] + const text = textArray ? getTextContent(textArray) : '' + if (text && text !== 'Untitled') { + return sourceTextArray.concat(text) + } + return sourceTextArray +} + +/** + * 递归获取层层嵌套的数组 + * @param {*} textArray + * @returns + */ +function getTextContent(textArray) { + if (typeof textArray === 'object' && isIterable(textArray)) { + let result = '' + for (const textObj of textArray) { + result = result + getTextContent(textObj) + } + return result + } else if (typeof textArray === 'string') { + return textArray + } +} + +/** + * 对象是否可以遍历 + * @param {*} obj + * @returns + */ +const isIterable = obj => + obj != null && typeof obj[Symbol.iterator] === 'function' + +/** + * 在内存缓存中进行全文索引 + * @param {*} allPosts + * @param keyword 关键词 + * @returns + */ +async function filterByMemCache(allPosts, keyword) { + const filterPosts = [] + if (keyword) { + keyword = keyword.trim() + } + for (const post of allPosts) { + const cacheKey = 'page_block_' + post.id + const page = await getDataFromCache(cacheKey, true) + const tagContent = post.tags && Array.isArray(post.tags) ? post.tags.join(' ') : '' + const categoryContent = post.category && Array.isArray(post.category) ? post.category.join(' ') : '' + const articleInfo = post.title + post.summary + tagContent + categoryContent + let hit = articleInfo.indexOf(keyword) > -1 + let indexContent = [post.summary] + if (page && page.block) { + const contentIds = Object.keys(page.block) + contentIds.forEach(id => { + const properties = page?.block[id]?.value?.properties + indexContent = appendText(indexContent, properties, 'title') + indexContent = appendText(indexContent, properties, 'caption') + }) + } + // console.log('全文搜索缓存', cacheKey, page != null) + post.results = [] + let hitCount = 0 + for (const i in indexContent) { + const c = indexContent[i] + if (!c) { + continue + } + const index = c.toLowerCase().indexOf(keyword.toLowerCase()) + if (index > -1) { + hit = true + hitCount += 1 + post.results.push(c) + } else { + if ((post.results.length - 1) / hitCount < 3 || i === 0) { + post.results.push(c) + } + } + } + if (hit) { + filterPosts.push(post) + } + } + return filterPosts +} + +export default Index diff --git a/themes/example/LayoutIndex.js b/themes/example/LayoutIndex.js index 17b77b4a..94eaac2f 100644 --- a/themes/example/LayoutIndex.js +++ b/themes/example/LayoutIndex.js @@ -1,11 +1,13 @@ +import BLOG from '@/blog.config' import { BlogListPage } from './components/BlogListPage' +import { BlogListScroll } from './components/BlogListScroll' import LayoutBase from './LayoutBase' export const LayoutIndex = props => { return ( - + {BLOG.POST_LIST_STYLE === 'page' ? : } ) } diff --git a/themes/example/LayoutSearch.js b/themes/example/LayoutSearch.js index 7c5dbafb..984e47e7 100644 --- a/themes/example/LayoutSearch.js +++ b/themes/example/LayoutSearch.js @@ -1,14 +1,14 @@ import BLOG from '@/blog.config' -import { useGlobal } from '@/lib/global' -import Link from 'next/link' +import { BlogListPage } from './components/BlogListPage' +import { BlogListScroll } from './components/BlogListScroll' import { useRouter } from 'next/router' -import { useEffect, useState } from 'react' +import { useEffect } from 'react' import SearchInput from './components/SearchInput' import LayoutBase from './LayoutBase' import { isBrowser } from '@/lib/utils' export const LayoutSearch = props => { - const { keyword, posts } = props + const { keyword } = props const router = useRouter() useEffect(() => { @@ -21,22 +21,6 @@ export const LayoutSearch = props => { }, 100) }, [router.events]) - const { locale } = useGlobal() - - const [page, updatePage] = useState(1) - let hasMore = false - const postsToShow = posts - ? Object.assign(posts).slice(0, BLOG.POSTS_PER_PAGE * page) - : [] - - if (posts) { - const totalCount = posts.length - hasMore = page * BLOG.POSTS_PER_PAGE < totalCount - } - const handleGetMore = () => { - if (!hasMore) return - updatePage(page + 1) - } useEffect(() => { setTimeout(() => { if (keyword) { @@ -59,36 +43,7 @@ export const LayoutSearch = props => { - {postsToShow.map(p => ( - - ))} - -
-
- {' '} - {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '} -
-
} diff --git a/themes/example/components/BlogListPage.js b/themes/example/components/BlogListPage.js index 5eb89ece..2feab00f 100644 --- a/themes/example/components/BlogListPage.js +++ b/themes/example/components/BlogListPage.js @@ -17,35 +17,37 @@ export const BlogListPage = props => { return
- {posts.map(p => ( -
-

- - {p.title} - -

+
+ {posts?.map(p => ( + - ))} + {/* 搜索结果 */} + {p.results && ( +

+ {p.results.map(r => ( + {r} + ))} +

+ )} +
+ ))} +
diff --git a/themes/example/components/BlogListScroll.js b/themes/example/components/BlogListScroll.js index 07da67d3..c9859c0b 100644 --- a/themes/example/components/BlogListScroll.js +++ b/themes/example/components/BlogListScroll.js @@ -43,7 +43,7 @@ export const BlogListScroll = props => { } }) - return
+ return
{postsToShow.map(p => (

diff --git a/themes/fukasawa/LayoutSearch.js b/themes/fukasawa/LayoutSearch.js index e4723c41..9329846d 100644 --- a/themes/fukasawa/LayoutSearch.js +++ b/themes/fukasawa/LayoutSearch.js @@ -1,5 +1,7 @@ import LayoutBase from './LayoutBase' +import BLOG from '@/blog.config' import BlogListPage from './components/BlogListPage' +import BlogListScroll from './components/BlogListScroll' import { useRouter } from 'next/router' import { useEffect } from 'react' import { isBrowser } from '@/lib/utils' @@ -18,6 +20,6 @@ export const LayoutSearch = (props) => { }, 100) }) return - + {BLOG.POST_LIST_STYLE === 'page' ? : } } diff --git a/themes/fukasawa/LayoutTag.js b/themes/fukasawa/LayoutTag.js index 8593783e..3a546fad 100644 --- a/themes/fukasawa/LayoutTag.js +++ b/themes/fukasawa/LayoutTag.js @@ -1,8 +1,10 @@ +import BLOG from '@/blog.config' import BlogListPage from './components/BlogListPage' +import BlogListScroll from './components/BlogListScroll' import LayoutBase from './LayoutBase' export const LayoutTag = (props) => { return - + {BLOG.POST_LIST_STYLE === 'page' ? : } } diff --git a/themes/fukasawa/components/BlogListPage.js b/themes/fukasawa/components/BlogListPage.js index 47650b5b..98dfedff 100644 --- a/themes/fukasawa/components/BlogListPage.js +++ b/themes/fukasawa/components/BlogListPage.js @@ -39,9 +39,9 @@ const BlogListPage = ({ page = 1, posts = [], postCount }) => { return } else { return ( -
+
{/* 文章列表 */} -
+
{posts?.map(post => (
diff --git a/themes/hexo/LayoutSearch.js b/themes/hexo/LayoutSearch.js index 9f003d2c..6454ef3a 100644 --- a/themes/hexo/LayoutSearch.js +++ b/themes/hexo/LayoutSearch.js @@ -1,5 +1,7 @@ import { useRouter } from 'next/router' import { useEffect, useRef } from 'react' +import BLOG from '@/blog.config' +import BlogPostListScroll from './components/BlogPostListScroll' import BlogPostListPage from './components/BlogPostListPage' import LayoutBase from './LayoutBase' import SearchInput from './components/SearchInput' @@ -25,8 +27,7 @@ export const LayoutSearch = props => { if (container && container.innerHTML) { const re = new RegExp(`${currentSearch}`, 'gim') container.innerHTML = container.innerHTML.replace( - re, - `${currentSearch}` + re, `${currentSearch}` ) } } @@ -34,56 +35,62 @@ export const LayoutSearch = props => { }, 100) }) return ( - -
- - {/* 分类 */} - -
- - {locale.COMMON.CATEGORY}: -
-
- {categories?.map(category => { - return ( - -
- - {category.name}({category.count}) -
- - ) - })} -
-
- {/* 标签 */} - -
- - {locale.COMMON.TAGS}: -
-
- {tags?.map(tag => { - return ( -
- + + {!currentSearch && <> +
+ + {/* 分类 */} + +
+ + {locale.COMMON.CATEGORY}: +
+
+ {categories?.map(category => { + return ( + +
+ + {category.name}({category.count}) +
+ + ) + })} +
+
+ {/* 标签 */} + +
+ + {locale.COMMON.TAGS}: +
+
+ {tags?.map(tag => { + return ( +
+ +
+ ) + })} +
+
- ) - })} -
- -
-
- -
- + } + + {currentSearch && <> +
+ {BLOG.POST_LIST_STYLE === 'page' ? : } +
+ } + + ) } diff --git a/themes/medium/LayoutSearch.js b/themes/medium/LayoutSearch.js index 6aa2ec5a..9a34bcec 100644 --- a/themes/medium/LayoutSearch.js +++ b/themes/medium/LayoutSearch.js @@ -3,9 +3,11 @@ import SearchInput from './components/SearchInput' import { useGlobal } from '@/lib/global' import TagGroups from './components/TagGroups' import CategoryGroup from './components/CategoryGroup' -import BlogPostListScroll from './components/BlogPostListScroll' import { useEffect } from 'react' import { isBrowser } from '@/lib/utils' +import BLOG from '@/blog.config' +import BlogPostListScroll from './components/BlogPostListScroll' +import BlogPostListPage from './components/BlogPostListPage' export const LayoutSearch = (props) => { const { locale } = useGlobal() @@ -27,8 +29,8 @@ export const LayoutSearch = (props) => {
-
- +
+ {BLOG.POST_LIST_STYLE === 'page' ? : }
} diff --git a/themes/medium/LayoutTag.js b/themes/medium/LayoutTag.js index dda44141..6f072aa9 100644 --- a/themes/medium/LayoutTag.js +++ b/themes/medium/LayoutTag.js @@ -1,6 +1,6 @@ import LayoutBase from './LayoutBase' -import BlogPostListScroll from './components/BlogPostListScroll' import BLOG from '@/blog.config' +import BlogPostListScroll from './components/BlogPostListScroll' import BlogPostListPage from './components/BlogPostListPage' export const LayoutTag = (props) => { diff --git a/themes/medium/components/BlogPostListPage.js b/themes/medium/components/BlogPostListPage.js index fe91b9b0..ad78969b 100644 --- a/themes/medium/components/BlogPostListPage.js +++ b/themes/medium/components/BlogPostListPage.js @@ -18,11 +18,13 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount }) => { return } else { return ( -
+
+
{/* 文章列表 */} {posts.map(post => ( ))} +
) diff --git a/themes/next/LayoutSearch.js b/themes/next/LayoutSearch.js index b1e0be2f..d4ec1bb8 100644 --- a/themes/next/LayoutSearch.js +++ b/themes/next/LayoutSearch.js @@ -1,8 +1,10 @@ import LayoutBase from './LayoutBase' import StickyBar from './components/StickyBar' -import BlogPostListScroll from './components/BlogPostListScroll' import { useGlobal } from '@/lib/global' import { isBrowser } from '@/lib/utils' +import BlogPostListScroll from './components/BlogPostListScroll' +import BlogPostListPage from './components/BlogPostListPage' +import BLOG from '@/blog.config' export const LayoutSearch = (props) => { const { locale } = useGlobal() @@ -23,8 +25,10 @@ export const LayoutSearch = (props) => {
- -
+ {BLOG.POST_LIST_STYLE !== 'page' + ? + : + }
) } diff --git a/themes/next/LayoutTag.js b/themes/next/LayoutTag.js index 7594ba8c..fdb47239 100644 --- a/themes/next/LayoutTag.js +++ b/themes/next/LayoutTag.js @@ -9,14 +9,14 @@ export const LayoutTag = (props) => { const { tags, tag } = props return - - - -
- {BLOG.POST_LIST_STYLE !== 'page' - ? - : - } -
-
+ + + +
+ {BLOG.POST_LIST_STYLE !== 'page' + ? + : + } +
+ } diff --git a/themes/next/components/BlogPostListPage.js b/themes/next/components/BlogPostListPage.js index ac252d8e..6d10433b 100644 --- a/themes/next/components/BlogPostListPage.js +++ b/themes/next/components/BlogPostListPage.js @@ -18,9 +18,9 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount }) => { return } else { return ( -
+
{/* 文章列表 */} -
+
{posts.map(post => ( ))} diff --git a/themes/next/components/BlogPostListScroll.js b/themes/next/components/BlogPostListScroll.js index d3b78fa6..5fb8e274 100644 --- a/themes/next/components/BlogPostListScroll.js +++ b/themes/next/components/BlogPostListScroll.js @@ -52,10 +52,10 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_NE if (!postsToShow || postsToShow.length === 0) { return } else { - return
+ return
{/* 文章列表 */} -
+
{postsToShow.map(post => ( ))} diff --git a/themes/next/components/PaginationNumber.js b/themes/next/components/PaginationNumber.js index b74bc550..75f048cf 100644 --- a/themes/next/components/PaginationNumber.js +++ b/themes/next/components/PaginationNumber.js @@ -1,4 +1,3 @@ -import BLOG from '@/blog.config' import Link from 'next/link' import { useRouter } from 'next/router' @@ -13,79 +12,78 @@ const PaginationNumber = ({ page, totalPage }) => { const router = useRouter() const currentPage = +page const showNext = page !== totalPage - const pages = generatePages(page, currentPage, totalPage) + const pagePrefix = router.asPath.replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '') + const pages = generatePages(pagePrefix, page, currentPage, totalPage) return ( -
- {/* 上一页 */} - - - +
+ {/* 上一页 */} + + + - {pages} + {pages} - {/* 下一页 */} - - - -
) } -function getPageElement(page, currentPage) { +function getPageElement(pagePrefix, page, currentPage) { + console.log(pagePrefix, page, currentPage) + return ( - - - {page} - - + + + {page} + + ) } -function generatePages(page, currentPage, totalPage) { +function generatePages(pagePrefix, page, currentPage, totalPage) { const pages = [] const groupCount = 7 // 最多显示页签数 if (totalPage <= groupCount) { for (let i = 1; i <= totalPage; i++) { - pages.push(getPageElement(i, page)) + pages.push(getPageElement(pagePrefix, i, page)) } } else { - pages.push(getPageElement(1, page)) + pages.push(getPageElement(pagePrefix, 1, page)) const dynamicGroupCount = groupCount - 2 let startPage = currentPage - 2 if (startPage <= 1) { @@ -100,7 +98,7 @@ function generatePages(page, currentPage, totalPage) { for (let i = 0; i < dynamicGroupCount; i++) { if (startPage + i < totalPage) { - pages.push(getPageElement(startPage + i, page)) + pages.push(getPageElement(pagePrefix, startPage + i, page)) } } @@ -108,7 +106,7 @@ function generatePages(page, currentPage, totalPage) { pages.push(
...
) } - pages.push(getPageElement(totalPage, page)) + pages.push(getPageElement(pagePrefix, totalPage, page)) } return pages }