mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-16 07:26:47 +00:00
@@ -2,7 +2,7 @@ import BLOG from '@/blog.config'
|
||||
import { NotionAPI } from 'notion-client'
|
||||
import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager'
|
||||
|
||||
export async function getPostBlocks (id, from, slice) {
|
||||
export async function getPostBlocks (id, from, slice, retryCount = 3) {
|
||||
const cacheKey = 'page_block_' + id
|
||||
let pageBlock = await getDataFromCache(cacheKey)
|
||||
if (pageBlock) {
|
||||
@@ -17,6 +17,10 @@ export async function getPostBlocks (id, from, slice) {
|
||||
console.log('[请求成功]', `from:${from}`, `id:${id}`)
|
||||
} catch (error) {
|
||||
console.error('[请求失败]', `from:${from}`, `id:${id}`, `error:${error}`)
|
||||
if (retryCount && retryCount > 0) { // 重试
|
||||
console.error('[重试请求]', `from:${from}`, `id:${id}`, `剩余次数:${retryCount}`)
|
||||
return getPostBlocks(id, from, slice, retryCount - 1)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ export async function getStaticProps () {
|
||||
BLOG.POSTS_PER_PAGE * page
|
||||
)
|
||||
if (THEME_CONFIG.POST_LIST_PREVIEW || BLOG.POST_LIST_PREVIEW) {
|
||||
console.log('加载预览')
|
||||
for (const i in postsToShow) {
|
||||
const post = postsToShow[i]
|
||||
const blockMap = await getPostBlocks(post.id, 'slug', BLOG.POST_PREVIEW_LINES)
|
||||
|
||||
@@ -45,7 +45,6 @@ export async function getStaticProps ({ params: { page } }) {
|
||||
)
|
||||
// 加载预览
|
||||
if (THEME_CONFIG.POST_LIST_PREVIEW || BLOG.POST_LIST_PREVIEW) {
|
||||
console.log('加载预览')
|
||||
for (const i in postsToShow) {
|
||||
const post = postsToShow[i]
|
||||
const blockMap = await getPostBlocks(post.id, 'slug', BLOG.POST_PREVIEW_LINES)
|
||||
|
||||
120
pages/search/[keyword].js
Normal file
120
pages/search/[keyword].js
Normal file
@@ -0,0 +1,120 @@
|
||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||
import { LayoutSearch } from '@/themes'
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { getDataFromCache } from '@/lib/cache/cache_manager'
|
||||
|
||||
export async function getStaticPaths () {
|
||||
return {
|
||||
paths: [],
|
||||
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') {
|
||||
let result = ''
|
||||
textArray.forEach(textObj => {
|
||||
result = result + getTextContent(textObj)
|
||||
})
|
||||
return result
|
||||
} else if (typeof textArray === 'string') {
|
||||
return textArray
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStaticProps ({ params: { keyword } }) {
|
||||
const {
|
||||
allPosts,
|
||||
categories,
|
||||
tags,
|
||||
postCount,
|
||||
latestPosts,
|
||||
customNav
|
||||
} = await getGlobalNotionData({ from: 'search-props', pageType: ['Post'] })
|
||||
|
||||
const filterPosts = []
|
||||
for (const post of allPosts) {
|
||||
const cacheKey = 'page_block_' + post.id
|
||||
const page = await getDataFromCache(cacheKey)
|
||||
const tagContent = post.tags ? post.tags.join(' ') : ''
|
||||
const categoryContent = post.category ? post.category.join(' ') : ''
|
||||
let indexContent = [post.title, post.summary, tagContent, categoryContent]
|
||||
console.log('搜索是否命中缓存', page !== null)
|
||||
if (page !== null) {
|
||||
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')
|
||||
})
|
||||
}
|
||||
post.results = []
|
||||
let hit = false
|
||||
const re = new RegExp(`${keyword}`, 'g')
|
||||
indexContent.forEach(c => {
|
||||
const index = c.toLowerCase().indexOf(keyword.toLowerCase())
|
||||
if (index > -1) {
|
||||
hit = true
|
||||
const referText = c?.replace(re, `<span class='text-red-500'>${keyword}</span>`)
|
||||
post.results.push(`<span>${referText}</span>`)
|
||||
} else {
|
||||
post.results.push(`<span>${c}</span>`)
|
||||
}
|
||||
})
|
||||
|
||||
if (hit) {
|
||||
filterPosts.push(post)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
posts: filterPosts,
|
||||
tags,
|
||||
categories,
|
||||
postCount,
|
||||
latestPosts,
|
||||
customNav,
|
||||
keyword
|
||||
},
|
||||
revalidate: 1
|
||||
}
|
||||
}
|
||||
|
||||
const Index = (props) => {
|
||||
const { keyword } = props
|
||||
const { locale } = useGlobal()
|
||||
const meta = {
|
||||
title: `${keyword || ''} | ${locale.NAV.SEARCH} | ${BLOG.TITLE} `,
|
||||
description: BLOG.DESCRIPTION,
|
||||
type: 'website'
|
||||
}
|
||||
return <LayoutSearch {...props} meta={meta} currentSearch={keyword} />
|
||||
}
|
||||
|
||||
export default Index
|
||||
@@ -23,7 +23,7 @@ export const Layout404 = (props) => {
|
||||
<div
|
||||
className='md:-mt-20 text-black w-full h-screen text-center justify-center content-center items-center flex flex-col'>
|
||||
<div className='dark:text-gray-200'>
|
||||
<h2 className='inline-block border-r-2 border-gray-600 mr-2 px-3 py-2 align-top'><i className='mr-2 fa-spinner animate-spin'/>404</h2>
|
||||
<h2 className='inline-block border-r-2 border-gray-600 mr-2 px-3 py-2 align-top'><i className='mr-2 fas fa-spinner animate-spin'/>404</h2>
|
||||
<div className='inline-block text-left h-32 leading-10 items-center'>
|
||||
<h2 className='m-0 p-0'>页面无法加载,即将返回首页</h2>
|
||||
</div>
|
||||
|
||||
@@ -41,10 +41,16 @@ const BlogPostCard = ({ post, showSummary }) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{(!showPreview || showSummary) && <p className='mt-4 mb-24 text-gray-700 dark:text-gray-300 text-sm font-light leading-7'>
|
||||
{(!showPreview || showSummary) && !post.results && <p className='mt-4 mb-24 text-gray-700 dark:text-gray-300 text-sm font-light leading-7'>
|
||||
{post.summary}
|
||||
</p>}
|
||||
|
||||
{/* 搜索结果 */}
|
||||
{post.results && <p className='mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7'>
|
||||
{post.results.map(r => <span key={r}><span dangerouslySetInnerHTML={{ __html: r }}/>...</span>)}
|
||||
</p>
|
||||
}
|
||||
|
||||
{showPreview && post?.blockMap && <div className='overflow-ellipsis truncate'>
|
||||
<NotionRenderer
|
||||
bodyClassName='max-h-full'
|
||||
|
||||
@@ -65,7 +65,7 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_NE
|
||||
<div onClick={() => {
|
||||
handleGetMore()
|
||||
}}
|
||||
className='w-full my-4 py-4 text-center cursor-pointer glassmorphism shadow-xl rounded-xl dark:text-gray-200'
|
||||
className='w-full my-4 py-4 text-center cursor-pointer glassmorphism shadow hover:shadow-xl duration-200 dark:text-gray-200'
|
||||
> {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`} </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useImperativeHandle, useRef, useState } from 'react'
|
||||
|
||||
const SearchInput = ({ currentTag, currentSearch, cRef }) => {
|
||||
const { locale } = useGlobal()
|
||||
const [searchKey, setSearchKey] = useState(currentSearch || '')
|
||||
// const [searchKey, setSearchKey] = useState(currentSearch || '')
|
||||
const [onLoading, setLoadingState] = useState(false)
|
||||
const router = useRouter()
|
||||
const searchInputRef = useRef()
|
||||
@@ -18,9 +18,10 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => {
|
||||
const handleSearch = (key) => {
|
||||
if (key && key !== '') {
|
||||
setLoadingState(true)
|
||||
router.push({ pathname: '/search', query: { s: key } }).then(r => {
|
||||
setLoadingState(false)
|
||||
})
|
||||
// router.push({ pathname: '/search/' + key }).then(r => {
|
||||
// setLoadingState(false)
|
||||
// })
|
||||
location.href = '/search/' + key
|
||||
} else {
|
||||
router.push({ pathname: '/' }).then(r => {
|
||||
})
|
||||
@@ -35,11 +36,17 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => {
|
||||
}
|
||||
const cleanSearch = () => {
|
||||
searchInputRef.current.value = ''
|
||||
setSearchKey('')
|
||||
setShowClean(false)
|
||||
}
|
||||
|
||||
const [showClean, setShowClean] = useState(false)
|
||||
const updateSearchKey = (val) => {
|
||||
setSearchKey(val)
|
||||
searchInputRef.current.value = val
|
||||
if (val) {
|
||||
setShowClean(true)
|
||||
} else {
|
||||
setShowClean(false)
|
||||
}
|
||||
}
|
||||
|
||||
return <div className='flex border dark:border-gray-600 w-full bg-gray-100 dark:bg-gray-900'>
|
||||
@@ -50,12 +57,12 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => {
|
||||
className={'w-full text-sm pl-4 transition focus:shadow-lg font-light leading-10 border-gray-300 text-black bg-gray-100 dark:bg-gray-900 dark:text-white'}
|
||||
onKeyUp={handleKeyUp}
|
||||
onChange={e => updateSearchKey(e.target.value)}
|
||||
defaultValue={searchKey}
|
||||
defaultValue={currentSearch}
|
||||
/>
|
||||
{(searchKey && searchKey.length && <i className='fas fa-times text-gray-300 float-right m-3 cursor-pointer' onClick={cleanSearch} />)}
|
||||
{(showClean && <i className='fas fa-times text-gray-300 float-right m-3 cursor-pointer' onClick={cleanSearch} />)}
|
||||
|
||||
<div className='p-3 bg-gray-50 flex border-l dark:border-gray-700 dark:hover:bg-gray-800 dark:bg-gray-600 justify-center items-center cursor-pointer'
|
||||
onClick={() => { handleSearch(searchKey) }}>
|
||||
onClick={handleSearch}>
|
||||
<i className={`${onLoading ? 'fa-spinner animate-spin ' : 'fa-search'} fas hover:scale-125 hover:text-black transform duration-200 dark:text-gray-300 dark:hover:text-white text-gray-600 cursor-pointer`} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@ import CONFIG_NEXT from '../config_next'
|
||||
* @constructor
|
||||
*/
|
||||
const SideAreaLeft = (props) => {
|
||||
const { currentTag, post, postCount, currentSearch } = props
|
||||
const { post, postCount } = props
|
||||
const { locale } = useGlobal()
|
||||
const showToc = post && post.toc && post.toc.length > 1
|
||||
return <aside id='left' className='hidden lg:block flex-col w-60 mr-4'>
|
||||
@@ -32,7 +32,7 @@ const SideAreaLeft = (props) => {
|
||||
<MenuButtonGroup allowCollapse={true} {...props} />
|
||||
</div>
|
||||
{CONFIG_NEXT.MENU_SEARCH && <div className='px-2 pt-2 font-sans'>
|
||||
<SearchInput currentTag={currentTag} currentSearch={currentSearch} />
|
||||
<SearchInput {...props} />
|
||||
</div>}
|
||||
</section>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user