Merge pull request #65 from tangly1024/feature-search

Feature search
This commit is contained in:
tangly1024
2022-03-01 13:20:30 +08:00
committed by GitHub
33 changed files with 155 additions and 211 deletions

View File

@@ -14,6 +14,7 @@ const BLOG = {
BEI_AN: process.env.NEXT_PUBLIC_BEI_AN || '', // 备案号 闽ICP备XXXXXXX
APPEARANCE: 'auto', // ['light', 'dark', 'auto'],
FONT: 'font-serif tracking-wider subpixel-antialiased', // 文章字体 ['font-sans', 'font-serif', 'font-mono'] @see https://www.tailwindcss.cn/docs/font-family
FONT_AWESOME_PATH: 'https://cdn.bootcdn.net/ajax/libs/font-awesome/5.15.4/css/all.min.css', // 图标库CDN 国内推荐BootCDN国外推荐 CloudFlare https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css
BACKGROUND_LIGHT: '#eeeeee', // use hex value, don't forget '#' e.g #fffefc
BACKGROUND_DARK: '#111827', // use hex value, don't forget '#'
PATH: '', // leave this empty unless you want to deploy in a folder
@@ -24,6 +25,9 @@ const BLOG = {
POSTS_PER_PAGE: 6, // post counts per page
POSTS_SORT_BY: 'notion', // 排序方式 'date'按时间,'notion'由notion控制
PREVIEW_CATEGORY_COUNT: 16, // 首页最多展示的分类数量0为不限制
PREVIEW_TAG_COUNT: 16, // 首页最多展示的标签数量0为不限制
// 社交链接,不需要可留空白,例如 CONTACT_WEIBO:''
CONTACT_EMAIL: 'tlyong1992@hotmail.com',
CONTACT_WEIBO: '',

View File

@@ -3,11 +3,11 @@
* @param allPosts
* @returns {Promise<{}|*[]>}
*/
export async function getAllCategories (allPosts) {
if (!allPosts) {
export async function getAllCategories ({ allPosts, categoryOptions, sliceCount = 0 }) {
if (!allPosts || !categoryOptions) {
return []
}
// 计数
let categories = allPosts.map(p => p.category)
categories = [...categories.flat()]
const categoryObj = {}
@@ -18,5 +18,19 @@ export async function getAllCategories (allPosts) {
categoryObj[category] = 1
}
})
return categoryObj
const list = []
categoryOptions.forEach(c => {
const count = categoryObj[c.value]
if (count) {
list.push({ id: c.id, name: c.value, color: c.color, count })
}
})
// 按照数量排序
// list.sort((a, b) => b.count - a.count)
if (sliceCount && sliceCount > 0) {
return list.slice(0, sliceCount)
} else {
return list
}
}

View File

@@ -6,15 +6,13 @@
* @param tagOptions tags的下拉选项
* @returns {Promise<{}|*[]>}
*/
export async function getAllTags ({ allPosts, sliceCount = 16, tagOptions }) {
if (!allPosts) {
export async function getAllTags ({ allPosts, sliceCount = 0, tagOptions }) {
if (!allPosts || !tagOptions) {
return []
}
// 计数
let tags = allPosts.map(p => p.tags)
tags = [...tags.flat()]
// 标签计数
const tagObj = {}
tags.forEach(tag => {
if (tag in tagObj) {
@@ -23,13 +21,16 @@ export async function getAllTags ({ allPosts, sliceCount = 16, tagOptions }) {
tagObj[tag] = 1
}
})
// 按照标签数量排序
const list = Object.keys(tagObj).map((tag) => {
const color = tagOptions.find(option => option.value === tag)?.color || 'gray'
return { name: tag, count: tagObj[tag], color }
const list = []
tagOptions.forEach(c => {
const count = tagObj[c.value]
if (count) {
list.push({ id: c.id, name: c.value, color: c.color, count })
}
})
list.sort((a, b) => b.count - a.count)
// 按照数量排序
// list.sort((a, b) => b.count - a.count)
if (sliceCount && sliceCount > 0) {
return list.slice(0, sliceCount)
} else {

View File

@@ -11,6 +11,7 @@ import { getAllTags } from './getAllTags'
* @param {*} pageId
* @param {*} from
* @param latestPostCount 截取最新文章数量
* @param categoryCount
* @param tagsCount 截取标签数量
* @param pageType 过滤的文章类型,数组格式 ['Page','Post']
* @returns {
@@ -27,15 +28,17 @@ export async function getGlobalNotionData ({
pageId = BLOG.NOTION_PAGE_ID,
from,
latestPostCount = 5,
tagsCount = 16,
categoryCount = BLOG.PREVIEW_CATEGORY_COUNT,
tagsCount = BLOG.PREVIEW_TAG_COUNT,
pageType = ['Post']
}) {
const notionPageData = await getNotionPageData({ pageId, from })
const tagOptions = notionPageData.tagOptions
const categoryOptions = notionPageData.categoryOptions
const allPosts = await getAllPosts({ notionPageData, from, pageType })
const postCount = await getAllPostCount({ notionPageData, from })
const customNav = await getCustomNav({ notionPageData })
const categories = await getAllCategories(allPosts)
const categories = await getAllCategories({ allPosts, categoryOptions, sliceCount: categoryCount })
const tags = await getAllTags({ allPosts, tagOptions, sliceCount: tagsCount })
const latestPosts = await getLatestPosts({ notionPageData, from, latestPostCount })
return { allPosts, latestPosts, categories, postCount, customNav, tags }
@@ -110,6 +113,11 @@ function getTagOptions (schema) {
return tagSchema?.options || {}
}
function getCategoryOptions (schema) {
const categorySchema = Object.values(schema).find(e => e.name === 'category')
return categorySchema?.options || {}
}
/**
* 调用NotionAPI获取Page数据
* @returns {Promise<JSX.Element|null|*>}
@@ -119,7 +127,6 @@ async function getPageRecordMapByNotionAPI ({ pageId, from }) {
if (!pageRecordMap) {
return []
}
pageId = idToUuid(pageId)
const collection = Object.values(pageRecordMap.collection)[0]?.value
const collectionQuery = pageRecordMap.collection_query
@@ -127,6 +134,7 @@ async function getPageRecordMapByNotionAPI ({ pageId, from }) {
const schema = collection?.schema
const rawMetadata = block[pageId].value
const tagOptions = getTagOptions(schema)
const categoryOptions = getCategoryOptions(schema)
// Check Type Page-Database和Inline-Database
if (
@@ -143,6 +151,7 @@ async function getPageRecordMapByNotionAPI ({ pageId, from }) {
block,
schema,
tagOptions,
categoryOptions,
rawMetadata
}
}

View File

@@ -27,7 +27,8 @@ const MyApp = ({ Component, pageProps }) => {
{BLOG.ANALYTICS_GOOGLE_ID && <Gtag />}
{JSON.parse(BLOG.ANALYTICS_BUSUANZI_ENABLE) && <Busuanzi/>}
{BLOG.ADSENSE_GOOGLE_ID && <GoogleAdsense/>}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ==" crossOrigin="anonymous" referrerPolicy="no-referrer" />
{/* FontawesomeCDN */}
<link href={BLOG.FONT_AWESOME_PATH} rel="stylesheet" referrerPolicy="no-referrer" />
<Component {...pageProps} />
</GlobalContextProvider>
)

View File

@@ -8,7 +8,7 @@ export default function Category (props) {
export async function getStaticProps () {
const from = 'category-index-props'
const { allPosts, categories, tags, postCount, latestPosts, customNav } = await getGlobalNotionData({ from })
const { allPosts, categories, tags, postCount, latestPosts, customNav } = await getGlobalNotionData({ from, categoryCount: 0 })
return {
props: {

View File

@@ -1,5 +1,8 @@
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
import { LayoutSearch } from '@/themes'
import BLOG from '@/blog.config'
import { useGlobal } from '@/lib/global'
import { useRouter } from 'next/router'
export async function getStaticProps () {
const {
@@ -24,7 +27,36 @@ export async function getStaticProps () {
}
const Search = (props) => {
return <LayoutSearch {...props} />
const { posts } = props
let filteredPosts
const searchKey = getSearchKey()
// 静态过滤
if (searchKey) {
filteredPosts = posts.filter(post => {
const tagContent = post.tags ? post.tags.join(' ') : ''
const categoryContent = post.category ? post.category.join(' ') : ''
const searchContent = post.title + post.summary + tagContent + categoryContent
return searchContent.toLowerCase().includes(searchKey.toLowerCase())
})
} else {
filteredPosts = posts
}
const { locale } = useGlobal()
const meta = {
title: `${searchKey || ''} | ${locale.NAV.SEARCH} | ${BLOG.TITLE} `,
description: BLOG.DESCRIPTION,
type: 'website'
}
return <LayoutSearch {...props} posts={filteredPosts} meta={meta} currentSearch={searchKey} />
}
function getSearchKey () {
const router = useRouter()
if (router.query && router.query.s) {
return router.query.s
}
return null
}
export default Search

View File

@@ -1,31 +1,6 @@
import { useRouter } from 'next/router'
import LayoutBase from './LayoutBase'
export const LayoutSearch = (props) => {
const { posts } = props
let filteredPosts
const searchKey = getSearchKey()
if (searchKey) {
filteredPosts = posts.filter(post => {
const tagContent = post.tags ? post.tags.join(' ') : ''
const searchContent = post.title + post.summary + tagContent
return searchContent.toLowerCase().includes(searchKey.toLowerCase())
})
} else {
filteredPosts = posts
}
console.log(filteredPosts)
return <LayoutBase {...props}>
Search {searchKey}
</LayoutBase>
}
function getSearchKey () {
const router = useRouter()
if (router.query && router.query.s) {
return router.query.s
}
return null
}

View File

@@ -17,11 +17,11 @@ export const LayoutCategoryIndex = (props) => {
<i className='mr-4 fas fa-th' />{locale.COMMON.CATEGORY}:
</div>
<div id='category-list' className='duration-200 flex flex-wrap'>
{Object.keys(categories).map(category => {
return <Link key={category} href={`/category/${category}`} passHref>
{categories && categories.map(category => {
return <Link key={category.name} href={`/category/${category.name}`} passHref>
<div
className={'hover:text-black dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600 px-5 cursor-pointer py-2 hover:bg-gray-100'}>
<i className='mr-4 fas fa-folder' />{category}({categories[category]})
<i className='mr-4 fas fa-folder' />{category.name}({category.count})
</div>
</Link>
})}

View File

@@ -3,8 +3,6 @@ import LayoutBase from './LayoutBase'
export const LayoutIndex = (props) => {
return <LayoutBase {...props}>
<BlogListPage posts={props.posts} postCount={props.postCount}/>
<BlogListPage {...props}/>
</LayoutBase>
}

View File

@@ -1,30 +1,8 @@
import { useRouter } from 'next/router'
import LayoutBase from './LayoutBase'
import BlogListPage from './components/BlogListPage'
export const LayoutSearch = (props) => {
let filteredPosts
const searchKey = getSearchKey()
if (searchKey) {
filteredPosts = props.posts.filter(post => {
const tagContent = post.tags ? post.tags.join(' ') : ''
const searchContent = post.title + post.summary + tagContent
return searchContent.toLowerCase().includes(searchKey.toLowerCase())
})
} else {
filteredPosts = props.posts
}
console.log(filteredPosts)
return <LayoutBase {...props}>
Search {searchKey}
<BlogListPage {...props}/>
</LayoutBase>
}
function getSearchKey () {
const router = useRouter()
if (router.query && router.query.s) {
return router.query.s
}
return null
}

View File

@@ -9,8 +9,6 @@ import Catalog from './Catalog'
function AsideLeft (props) {
const { tags, currentTag, categories, currentCategory, post } = props
console.log(post)
return <div className='w-72 bg-white min-h-screen px-10 py-14 hidden lg:block'>
<Logo />

View File

@@ -14,7 +14,7 @@ import PaginationSimple from './PaginationSimple'
*/
const BlogListPage = ({ page = 1, posts = [], postCount }) => {
const totalPage = Math.ceil(postCount / BLOG.POSTS_PER_PAGE)
const showNext = page < totalPage && posts.length < postCount
const showNext = page < totalPage && posts.length <= BLOG.POSTS_PER_PAGE && posts.length < postCount
const [colCount, changeCol] = useState(3)
function updateCol () {

View File

@@ -8,14 +8,14 @@ function GroupCategory ({ currentCategory, categories }) {
return <>
<div id='category-list' className='dark:border-gray-600 flex flex-wrap'>
{Object.keys(categories).map(category => {
const selected = currentCategory === category
return <Link key={category} href={`/category/${category}`} passHref>
{categories.map(category => {
const selected = currentCategory === category.name
return <Link key={category.name} href={`/category/${category.name}`} passHref>
<a className={(selected
? 'hover:text-white dark:hover:text-white bg-gray-600 text-white '
: 'dark:text-gray-400 text-gray-500 hover:text-white hover:bg-gray-500 dark:hover:text-white') +
' text-sm w-full items-center duration-300 px-2 cursor-pointer py-1 font-light'}>
<i className={`${selected ? 'text-white fa-folder-open' : 'fa-folder text-gray-400'} fas mr-2`} />{category}({categories[category]})
<i className={`${selected ? 'text-white fa-folder-open' : 'fa-folder text-gray-400'} fas mr-2`} />{category.name}({category.count})
</a>
</Link>
})}

View File

@@ -20,16 +20,16 @@ export const LayoutCategoryIndex = props => {
{locale.COMMON.CATEGORY}:
</div>
<div id="category-list" className="duration-200 flex flex-wrap mx-8">
{Object.keys(categories).map(category => {
{categories.map(category => {
return (
<Link key={category} href={`/category/${category}`} passHref>
<Link key={category.name} href={`/category/${category.name}`} passHref>
<div
className={
' duration-300 dark:hover:text-white rounded-lg px-5 cursor-pointer py-2 hover:bg-blue-600 hover:text-white'
' duration-300 dark:hover:text-white rounded-lg px-5 cursor-pointer py-2 hover:bg-blue-400 hover:text-white'
}
>
<i className="mr-4 fas fa-folder" />
{category}({categories[category]})
{category.name}({category.count})
</div>
</Link>
)

View File

@@ -1,38 +1,9 @@
import BLOG from '@/blog.config'
import { useGlobal } from '@/lib/global'
import { useRouter } from 'next/router'
import BlogPostListPage from './components/BlogPostListPage'
import LayoutBase from './LayoutBase'
export const LayoutSearch = (props) => {
const { posts } = props
let filteredPosts
const searchKey = getSearchKey()
if (searchKey) {
filteredPosts = posts.filter(post => {
const tagContent = post.tags ? post.tags.join(' ') : ''
const searchContent = post.title + post.summary + tagContent
return searchContent.toLowerCase().includes(searchKey.toLowerCase())
})
} else {
filteredPosts = posts
}
const { locale } = useGlobal()
const meta = {
title: `${searchKey || ''} | ${locale.NAV.SEARCH} | ${BLOG.TITLE} `,
description: BLOG.DESCRIPTION,
type: 'website'
}
return <LayoutBase {...props} meta={meta}>
<BlogPostListPage {...props} posts={filteredPosts}/>
return <LayoutBase {...props}>
<BlogPostListPage {...props}/>
</LayoutBase>
}
function getSearchKey () {
const router = useRouter()
if (router.query && router.query.s) {
return router.query.s
}
return null
}

View File

@@ -7,14 +7,14 @@ const CategoryGroup = ({ currentCategory, categories }) => {
}
return <>
<div id='category-list' className='dark:border-gray-600 flex flex-wrap font-sans mx-4'>
{Object.keys(categories).map(category => {
const selected = currentCategory === category
return <Link key={category} href={`/category/${category}`} passHref>
{categories.map(category => {
const selected = currentCategory === category.name
return <Link key={category.name} href={`/category/${category.name}`} passHref>
<a className={(selected
? 'hover:text-white dark:hover:text-white bg-blue-600 text-white '
: 'dark:text-gray-400 text-gray-500 hover:text-white dark:hover:text-white hover:bg-blue-600') +
' text-sm w-full items-center duration-300 px-2 cursor-pointer py-1 font-light'}>
<div> <i className={`mr-2 fas ${selected ? 'fa-folder-open' : 'fa-folder'}`} />{category}({categories[category]})</div>
<div> <i className={`mr-2 fas ${selected ? 'fa-folder-open' : 'fa-folder'}`} />{category.name}({category.count})</div>
</a>
</Link>
})}

View File

@@ -11,11 +11,11 @@ const MenuButtonGroup = (props) => {
const archiveSlot = <div className='bg-blue-300 dark:bg-blue-500 rounded-md text-gray-50 px-1 text-xs'>{postCount}</div>
let links = [
{ id: 0, icon: 'fas fa-home', name: locale.NAV.INDEX, to: '/' || '/', show: true },
{ id: 1, icon: 'fas fa-th', name: locale.COMMON.CATEGORY, to: '/category', show: CONFIG_HEXO.MENU_CATEGORY },
{ id: 2, icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: CONFIG_HEXO.MENU_TAG },
{ id: 3, icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', slot: archiveSlot, show: CONFIG_HEXO.MENU_ARCHIVE },
{ id: 4, icon: 'fas fa-user', name: locale.NAV.ABOUT, to: '/about', show: CONFIG_HEXO.MENU_ABOUT }
{ icon: 'fas fa-home', name: locale.NAV.INDEX, to: '/' || '/', show: true },
{ icon: 'fas fa-th', name: locale.COMMON.CATEGORY, to: '/category', show: CONFIG_HEXO.MENU_CATEGORY },
{ icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: CONFIG_HEXO.MENU_TAG },
{ icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', slot: archiveSlot, show: CONFIG_HEXO.MENU_ARCHIVE },
{ icon: 'fas fa-user', name: locale.NAV.ABOUT, to: '/about', show: CONFIG_HEXO.MENU_ABOUT }
]
if (customNav) {
links = links.concat(customNav)
@@ -24,11 +24,11 @@ const MenuButtonGroup = (props) => {
{links.map(link => {
if (link.show) {
const selected = (router.pathname === link.to) || (router.asPath === link.to)
return <Link key={`${link.id}-${link.to}`} title={link.to} href={link.to} >
return <Link key={`${link.to}`} title={link.to} href={link.to} >
<a className={'py-1.5 my-1 px-5 duration-300 text-base justify-between hover:bg-blue-400 rounded-md hover:text-white hover:shadow-lg cursor-pointer flex flex-nowrap items-center ' +
(selected ? 'bg-blue-400 rounded-md text-white' : ' ')} >
<div className='my-auto items-center justify-center flex '>
<i className={link.icon} />
<i className={`${link.icon} w-4 text-center`} />
<div className={'ml-4'}>{link.name}</div>
</div>
{link.slot}

View File

@@ -44,10 +44,10 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => {
<input
ref={searchInputRef}
type='text'
className={'w-full rounded-lg text-sm pl-2 transition focus:shadow-lg font-light leading-10 text-black bg-gray-100'}
className={'w-full rounded-lg text-sm pl-5 transition focus:shadow-lg font-light leading-10 text-black bg-gray-100'}
onKeyUp={handleKeyUp}
onChange={e => updateSearchKey(e.target.value)}
defaultValue={searchKey}
defaultValue={currentSearch}
/>
<div className='-ml-8 cursor-pointer dark:hover:bg-gray-800 float-right items-center justify-center py-2'

View File

@@ -1,5 +1,4 @@
import LayoutBase from './LayoutBase'
import BlogPostListPage from './components/BlogPostListPage'
import SearchInput from './components/SearchInput'
import { useGlobal } from '@/lib/global'
import TagGroups from './components/TagGroups'

View File

@@ -45,10 +45,10 @@ const BlogPostCard = ({ post, showSummary }) => {
collection: Collection
}}
/>
<div className='article-cover pointer-events-none'>
<div className='pointer-events-none border-t pt-8 border-dashed'>
<div className='w-full justify-start flex'>
<Link href={`${BLOG.PATH}/article/${post.slug}`} passHref>
<a className='hover:bg-opacity-100 hover:scale-105 duration-200 pointer-events-auto transform text-red-500 cursor-pointer'>
<a className='hover:bg-opacity-100 hover:scale-105 duration-200 pointer-events-auto transform font-bold text-green-500 cursor-pointer'>
{locale.COMMON.ARTICLE_DETAIL}
<i className='ml-1 fas fa-angle-right'/></a>
@@ -58,7 +58,6 @@ const BlogPostCard = ({ post, showSummary }) => {
</div> }
</div>
<hr className='w-full'/>
</div>
)

View File

@@ -78,7 +78,7 @@ const Catalog = ({ toc }) => {
)
})}
</nav>
<JumpToTopButton className='text-gray-400 hover:bg-gray-100 py-1 duration-200'/>
<JumpToTopButton className='text-gray-400 hover:text-green-500 hover:bg-gray-100 py-1 duration-200'/>
</div>
}

View File

@@ -8,10 +8,9 @@ const CategoryGroup = ({ currentCategory, categories }) => {
return <div id='category-list' className='pt-4'>
<div className='mb-2'><i className='mr-2 fas fa-th' />分类</div>
<div className='flex flex-wrap'>
{Object.keys(categories).map(category => {
const selected = currentCategory === category
const categoryCount = +categories[category]
return <CategoryItem key={category} selected={selected} category={category} categoryCount={categoryCount}/>
{categories.map(category => {
const selected = currentCategory === category.name
return <CategoryItem key={category.name} selected={selected} category={category.name} categoryCount={category.count}/>
})}
</div>
</div>

View File

@@ -6,7 +6,7 @@ export default function CategoryItem ({ selected, category, categoryCount }) {
? 'hover:text-white dark:hover:text-white bg-green-600 text-white '
: 'dark:text-green-400 text-gray-500 hover:text-white dark:hover:text-white hover:bg-green-600') +
' flex text-sm items-center duration-300 cursor-pointer py-1 font-light px-2 whitespace-nowrap'}>
<div><i className={`mr-2 fas ${selected ? 'fa-folder-open' : 'fa-folder'}`} />{category} {categoryCount && (categoryCount)}
<div><i className={`mr-2 fas ${selected ? 'fa-folder-open' : 'fa-folder'}`} />{category} {categoryCount && `(${categoryCount})`}
</div>
</a>
</Link>

View File

@@ -12,16 +12,16 @@ export const LayoutCategoryIndex = (props) => {
type: 'website'
}
return <LayoutBase meta={meta} totalPosts={allPosts} {...props}>
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow'>
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow h-full'>
<div className='dark:text-gray-200 mb-5'>
<i className='mr-4 fas faTh' />{locale.COMMON.CATEGORY}:
</div>
<div id='category-list' className='duration-200 flex flex-wrap'>
{Object.keys(categories).map(category => {
return <Link key={category} href={`/category/${category}`} passHref>
{categories.map(category => {
return <Link key={category.name} href={`/category/${category.name}`} passHref>
<div
className={'hover:text-black dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600 px-5 cursor-pointer py-2 hover:bg-gray-100'}>
<i className='mr-4 fas fa-folder' />{category}({categories[category]})
<i className='mr-4 fas fa-folder' />{category.name}({category.count})
</div>
</Link>
})}

View File

@@ -2,52 +2,22 @@ import LayoutBase from './LayoutBase'
import StickyBar from './components/StickyBar'
import BlogPostListScroll from './components/BlogPostListScroll'
import { useGlobal } from '@/lib/global'
import BLOG from '@/blog.config'
import { useRouter } from 'next/router'
export const LayoutSearch = (props) => {
const { posts, tags } = props
let filteredPosts
const searchKey = getSearchKey()
if (searchKey) {
filteredPosts = posts.filter(post => {
const tagContent = post.tags ? post.tags.join(' ') : ''
const searchContent = post.title + post.summary + tagContent
return searchContent.toLowerCase().includes(searchKey.toLowerCase())
})
} else {
filteredPosts = posts
}
const { locale } = useGlobal()
const meta = {
title: `${searchKey || ''} | ${locale.NAV.SEARCH} | ${BLOG.TITLE} `,
description: BLOG.DESCRIPTION,
type: 'website'
}
const { posts } = props
return (
<LayoutBase
meta={meta}
currentSearch={searchKey}
{...props}
>
<LayoutBase {...props} >
<StickyBar>
<div className="p-4 dark:text-gray-200">
<i className="mr-1 fas fa-search" />{' '}
{filteredPosts.length} {locale.COMMON.RESULT_OF_SEARCH}
{posts?.length} {locale.COMMON.RESULT_OF_SEARCH}
</div>
</StickyBar>
<div className="md:mt-5">
<BlogPostListScroll posts={filteredPosts} tags={tags} showSummary={true}/>
<BlogPostListScroll {...props} showSummary={true}/>
</div>
</LayoutBase>
)
}
function getSearchKey () {
const router = useRouter()
if (router.query && router.query.s) {
return router.query.s
}
return null
}

View File

@@ -12,7 +12,7 @@ export const LayoutTagIndex = (props) => {
type: 'website'
}
return <LayoutBase meta={meta} {...props}>
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow'>
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow h-full'>
<div className='dark:text-gray-200 mb-5'><i className='fas fa-tags mr-4'/>{locale.COMMON.TAGS}:</div>
<div id='tags-list' className='duration-200 flex flex-wrap'>
{ tags.map(tag => {

View File

@@ -59,9 +59,9 @@ const BlogPostCard = ({ post, showSummary }) => {
/>
</div> }
<div className='article-cover pointer-events-none text-right'>
<div className='text-right border-t pt-8 border-dashed'>
<Link href={`${BLOG.PATH}/article/${post.slug}`}>
<a className='hover:bg-opacity-100 hover:scale-105 hover:underline pointer-events-auto transform duration-300 p-3 text-white bg-gray-800 cursor-pointer'>
<a className='hover:bg-opacity-100 hover:underline transform duration-300 p-3 text-white bg-gray-800 dark:bg-black cursor-pointer'>
{locale.COMMON.ARTICLE_DETAIL}
<i className='ml-1 fas fa-angle-right' /></a>
</Link>

View File

@@ -2,16 +2,17 @@ import Link from 'next/link'
import React from 'react'
const CategoryGroup = ({ currentCategory, categories }) => {
if (!categories) return <></>
return <>
<div id='category-list' className='dark:border-gray-600 flex flex-wrap'>
{Object.keys(categories).map(category => {
const selected = currentCategory === category
return <Link key={category} href={`/category/${category}`} passHref>
{categories.map(category => {
const selected = currentCategory === category.name
return <Link key={category.name} href={`/category/${category.name}`} passHref>
<a className={(selected
? 'hover:text-white dark:hover:text-white bg-gray-600 text-white '
: 'dark:text-gray-400 text-gray-500 hover:text-white hover:bg-gray-500 dark:hover:text-white') +
' text-sm w-full items-center duration-300 px-2 cursor-pointer py-1 font-light'}>
<i className={`${selected ? 'text-white fa-folder-open ' : 'text-gray-400 fa-folder '} mr-2 fas`} />{category}({categories[category]})
<i className={`${selected ? 'text-white fa-folder-open ' : 'text-gray-400 fa-folder '} mr-2 fas`} />{category.name}({category.count})
</a>
</Link>
})}

View File

@@ -10,10 +10,10 @@ const CategoryList = ({ currentCategory, categories }) => {
return <ul className='flex py-1 space-x-3'>
<li className='w-16 py-2 dark:text-gray-200 whitespace-nowrap'>{locale.COMMON.CATEGORY}</li>
{Object.keys(categories).map(category => {
const selected = category === currentCategory
{categories.map(category => {
const selected = category.name === currentCategory
return (
<Link key={category} href={`/category/${category}`} passHref>
<Link key={category.name} href={`/category/${category.name}`} passHref>
<li
className={`cursor-pointer border rounded-xl duration-200 mr-1 my-1 px-2 py-1 font-light text-sm whitespace-nowrap dark:text-gray-300
${selected
@@ -23,7 +23,7 @@ const CategoryList = ({ currentCategory, categories }) => {
>
<a>
<i className={`${selected ? 'fa-folder-open ' : 'fa-folder '} fas mr-1`}/>
{`${category} `}
{`${category.name} (${category.count})`}
</a>
</li>
</Link>)

View File

@@ -47,7 +47,7 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => {
ref={searchInputRef}
type='text'
placeholder={currentTag ? `${locale.SEARCH.TAGS} #${currentTag}` : `${locale.SEARCH.ARTICLES}`}
className={'w-full text-sm pl-2 transition focus:shadow-lg font-light leading-10 border-gray-300 text-black bg-gray-100 dark:bg-gray-900 dark:text-white'}
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}

View File

@@ -5,6 +5,7 @@ import Card from './Card'
import CategoryGroup from './CategoryGroup'
import TagGroups from './TagGroups'
import CONFIG_NEXT from '../config_next'
import { useRouter } from 'next/router'
/**
* 侧边平铺
@@ -16,18 +17,13 @@ import CONFIG_NEXT from '../config_next'
* @returns {JSX.Element}
* @constructor
*/
const SideAreaRight = ({
tags,
currentTag,
slot,
categories,
currentCategory
}) => {
const SideAreaRight = (props) => {
const { tags, currentTag, slot, categories, currentCategory } = props
const { locale } = useGlobal()
if (!CONFIG_NEXT.RIGHT_BAR) {
return <></>
}
const router = useRouter()
return (<aside id='right' className='hidden 2xl:block flex-col w-60 ml-4'>
{CONFIG_NEXT.RIGHT_AD && <Card className='mb-2'>
@@ -44,15 +40,16 @@ const SideAreaRight = ({
</Card>}
<div className="sticky top-4">
{slot}
{/* 分类 */}
{CONFIG_NEXT.RIGHT_CATEGORY_LIST && categories && (
{CONFIG_NEXT.RIGHT_CATEGORY_LIST && router.asPath !== '/category' && categories && (
<Card>
<div className='text-sm px-2 flex flex-nowrap justify-between font-light'>
<div className='pb-1 text-gray-600 dark:text-gray-300'><i icon={faThList} className='mr-2' />{locale.COMMON.CATEGORY}</div>
<div className='pb-2 text-gray-600 dark:text-gray-300'><i className='mr-2 fas fa-th-list' />{locale.COMMON.CATEGORY}</div>
<Link href={'/category'} passHref>
<a className='text-gray-400 hover:text-black dark:text-gray-400 dark:hover:text-white hover:underline cursor-pointer'>
{locale.COMMON.MORE} <i icon={faAngleRight} />
{locale.COMMON.MORE} <i className='fas fa-angle-right' />
</a>
</Link>
</div>
@@ -60,9 +57,7 @@ const SideAreaRight = ({
</Card>
)}
{slot}
{CONFIG_NEXT.RIGHT_TAG_LIST && tags && (
{CONFIG_NEXT.RIGHT_TAG_LIST && router.asPath !== '/tag' && tags && (
<Card>
<div className="text-sm pb-1 px-2 flex flex-nowrap justify-between font-light dark:text-gray-200">
<div className="text-gray-600 dark:text-gray-200">

View File

@@ -8,14 +8,14 @@ const CONFIG_NEXT = {
POST_LIST_TYPE: 'page', // ['page','scroll] 文章列表样式:页码分页、单页滚动加载
POST_LIST_COVER: false, // 文章列表显示封面图
POST_LIST_PREVIEW: true, // 显示文章预览
POST_LIST_PREVIEW: false, // 显示文章预览
POST_LIST_SUMMARY: false, // 显示用户自定义摘要,有预览时优先只展示预览
// 右侧组件
RIGHT_BAR: false, // 是否显示右侧栏
RIGHT_BAR: true, // 是否显示右侧栏
RIGHT_LATEST_POSTS: false, // 右侧栏最新文章
RIGHT_CATEGORY_LIST: false, // 右侧边栏文章分类列表
RIGHT_TAG_LIST: false, // 右侧边栏标签分类列表
RIGHT_CATEGORY_LIST: true, // 右侧边栏文章分类列表
RIGHT_TAG_LIST: true, // 右侧边栏标签分类列表
RIGHT_AD: false, // 右侧广告
// 菜单