group-nav-page

This commit is contained in:
tangly1024
2023-06-23 23:35:03 +08:00
parent b5c1752666
commit c14ca330eb
16 changed files with 182 additions and 219 deletions

View File

@@ -13,9 +13,8 @@ const DarkModeButton = (props) => {
htmlElement.classList?.add(newStatus ? 'dark' : 'light')
}
return <div className={'text-gray-200 z-10 duration-200 text-xl py-2 ' + props.className}>
<i id='darkModeButton' className={`hover:scale-125 cursor-pointer transform duration-200 fas ${isDarkMode ? 'fa-sun' : 'fa-moon'}`}
onClick={handleChangeDarkMode} />
return <div onClick={handleChangeDarkMode} className={'px-1 dark:text-gray-200 text-gray-800 z-10 duration-200 text-xl hover:scale-125 cursor-pointer transform ' + props.className}>
<i id='darkModeButton' className={`${isDarkMode ? 'far fa-sun' : 'far fa-moon'}`}/>
</div>
}
export default DarkModeButton

View File

@@ -26,6 +26,7 @@ export async function getGlobalNotionData({
}) {
// 获取Notion数据
const notionPageData = deepClone(await getNotionPageData({ pageId, from }))
notionPageData.allNavPages = getNavPages({ allPages: notionPageData.allPages })
delete notionPageData.block
delete notionPageData.schema
delete notionPageData.rawMetadata
@@ -168,6 +169,37 @@ function getSiteInfo({ collection, block }) {
return { title, description, pageCover, icon }
}
/**
* 获取导航pages
* 转为gitbook这类文档主题设计精减的标题和内容
* 导航页面的条件必须是Posts
* @param {*} param0
*/
export function getNavPages({ allPages }) {
const allNavPages = allPages.filter(post => {
return post && post?.slug && (!post?.slug?.startsWith('http')) && post?.type === 'Post' && post?.status === 'Published'
})
const result = allNavPages.map(item => ({ id: item.id, title: item.title, category: item.category || null, tags: item.tags || null, summary: item.summary || null, slug: item.slug }))
const groupedArray = result.reduce((groups, item) => {
const categoryName = item.category ? item.category.join('/') : '' // 将category转换为字符串
const lastGroup = groups[groups.length - 1] // 获取最后一个分组
if (!lastGroup || lastGroup.category !== categoryName) { // 如果当前元素的category与上一个元素不同则创建新分组
groups.push({ category: categoryName, items: [] })
}
groups[groups.length - 1].items.push(item) // 将元素加入对应的分组
return groups
}, [])
return groupedArray
}
/**
* 获取公告
*/
async function getNotice(post) {
if (!post) {
return null
@@ -183,6 +215,7 @@ const EmptyData = (pageId) => {
notice: null,
siteInfo: getSiteInfo({}),
allPages: [{ id: 1, title: `无法获取Notion数据请检查Notion_ID \n 当前 ${pageId}`, summary: '访问文档获取帮助→ https://tangly1024.com/article/vercel-deploy-notion-next', status: 'Published', type: 'Post', slug: '13a171332816461db29d50e9f575b00d', date: { start_date: '2023-04-24', lastEditedTime: '2023-04-24', tagItems: [] } }],
allNavPages: [],
collection: [],
collectionQuery: {},
collectionId: null,
@@ -275,11 +308,13 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
// 新的菜单
const customMenu = await getCustomMenu({ collectionData })
const latestPosts = getLatestPosts({ allPages, from, latestPostCount: 5 })
const allNavPages = getNavPages({ allPages })
return {
notice,
siteInfo,
allPages,
allNavPages,
collection,
collectionQuery,
collectionId,

View File

@@ -4,11 +4,10 @@ import { getGlobalNotionData } from '@/lib/notion/getNotionData'
import { useEffect, useState } from 'react'
import { idToUuid } from 'notion-utils'
import { useRouter } from 'next/router'
import { isBrowser } from '@/lib/utils'
import { getNotion } from '@/lib/notion/getNotion'
import { getPageTableOfContents } from '@/lib/notion/getPageTableOfContents'
import md5 from 'js-md5'
import { getLayoutByTheme } from '@/themes/theme'
import md5 from 'js-md5'
/**
* 根据notion的slug访问页面
@@ -19,16 +18,13 @@ const Slug = props => {
const { post, siteInfo } = props
const router = useRouter()
// 根据页面路径加载不同Layout文件
const Layout = getLayoutByTheme(useRouter())
// 文章锁🔐
const [lock, setLock] = useState(post?.password && post?.password !== '')
/**
* 验证文章密码
* @param {*} result
*/
* 验证文章密码
* @param {*} result
*/
const validPassword = passInput => {
const encrypt = md5(post.slug + passInput)
if (passInput && encrypt === post.password) {
@@ -41,28 +37,28 @@ const Slug = props => {
// 文章加载
useEffect(() => {
// 404
if (!post) {
setTimeout(() => {
if (isBrowser()) {
const article = document.getElementById('notion-article')
if (!article) {
router.push('/404').then(() => {
console.warn('找不到页面', router.asPath)
})
}
}
}, 8 * 1000) // 404时长 8秒
}
// if (!post) {
// setTimeout(() => {
// if (isBrowser()) {
// const article = document.getElementById('notion-article')
// if (!article) {
// router.push('/404').then(() => {
// console.warn('找不到页面', router.asPath)
// })
// }
// }
// }, 8 * 1000) // 404时长 8秒
// }
// 文章加密
if (post?.password && post?.password !== '') {
setLock(true)
} else {
setLock(false)
if (!lock && post?.blockMap?.block) {
post.content = Object.keys(post.blockMap.block).filter(key => post.blockMap.block[key]?.value?.parent_id === post.id)
post.toc = getPageTableOfContents(post, post.blockMap)
}
setLock(false)
}
router.events.on('routeChangeComplete', () => {
window.scrollTo({ top: 0, behavior: 'smooth' })
@@ -79,7 +75,8 @@ const Slug = props => {
tags: post?.tags
}
props = { ...props, lock, meta, setLock, validPassword }
// 根据页面路径加载不同Layout文件
const Layout = getLayoutByTheme(useRouter())
return <Layout {...props} />
}

View File

@@ -1142,7 +1142,6 @@ code[class*='language-'] {
overflow: hidden;
text-overflow: ellipsis;
@apply text-blue-600 dark:text-blue-200
}
.notion-table-of-contents-item:hover {

View File

@@ -1,19 +1,19 @@
import CommonHead from '@/components/CommonHead'
import { useState, createContext, useContext } from 'react'
import { useState, createContext, useContext, useEffect } from 'react'
import Footer from './components/Footer'
import InfoCard from './components/InfoCard'
import RevolverMaps from './components/RevolverMaps'
import CONFIG_MEDIUM from './config_medium'
import Tabs from '@/components/Tabs'
import TopNavBar from './components/TopNavBar'
import SearchInput from './components/SearchInput'
import BottomMenuBar from './components/BottomMenuBar'
import { useGlobal } from '@/lib/global'
import { useRouter } from 'next/router'
import Live2D from '@/components/Live2D'
import BLOG from '@/blog.config'
import BlogPostListScroll from './components/BlogPostListScroll'
import ArticleInfo from './components/ArticleInfo'
import Catalog from './components/Catalog'
import { useRouter } from 'next/router'
const ThemeGlobalMedium = createContext()
/**
@@ -22,12 +22,16 @@ const ThemeGlobalMedium = createContext()
* @returns {JSX.Element}
* @constructor
*/
const LayoutBase = props => {
const { children, meta, posts, post, showInfoCard = true, slotLeft, slotRight, slotTop, siteInfo } = props
const { locale } = useGlobal()
const router = useRouter()
const LayoutBase = (props) => {
const { children, meta, post, allNavPages, slotLeft, slotRight, slotTop, siteInfo } = props
const [tocVisible, changeTocVisible] = useState(false)
const [filterPosts, setFilterPosts] = useState(allNavPages)
const { onLoading } = useGlobal()
const router = useRouter()
useEffect(() => {
setFilterPosts(allNavPages)
}, [post])
const LoadingCover = <div id='cover-loading' className={`${onLoading ? 'z-50 opacity-50' : '-z-10 opacity-0'} pointer-events-none transition-all duration-300`}>
<div className='w-full h-screen flex justify-center items-center'>
@@ -36,7 +40,7 @@ const LayoutBase = props => {
</div>
return (
<ThemeGlobalMedium.Provider value={{ tocVisible, changeTocVisible }}>
<ThemeGlobalMedium.Provider value={{ tocVisible, changeTocVisible, filterPosts, setFilterPosts, allNavPages }}>
<CommonHead meta={meta} />
<div id='theme-medium' className='bg-white dark:bg-hexo-black-gray w-full h-full min-h-screen justify-center dark:text-gray-300'>
@@ -46,21 +50,21 @@ const LayoutBase = props => {
<main id='wrapper' className={(BLOG.LAYOUT_SIDEBAR_REVERSE ? 'flex-row-reverse' : '') + 'relative flex justify-between w-full h-full mx-auto'}>
{/* 左侧推拉抽屉 */}
<div className={`hidden xl:block border-l dark:border-transparent w-96 relative z-10 ${CONFIG_MEDIUM.RIGHT_PANEL_DARK ? 'bg-hexo-black-gray dark' : ''}`}>
<div style={{ width: '32rem' }} className={`font-sans hidden xl:block border-r dark:border-transparent relative z-10 ${CONFIG_MEDIUM.RIGHT_PANEL_DARK ? 'bg-hexo-black-gray dark' : ''}`}>
<div className='py-14 px-6 sticky top-0 overflow-y-scroll h-screen'>
{slotLeft}
{router.pathname !== '/search' && <SearchInput className='mt-6 mb-12' />}
<SearchInput className='my-3' />
{/* 所有文章列表 */}
<BlogPostListScroll posts={posts} />
<BlogPostListScroll posts={filterPosts} />
</div>
</div>
<div id='center-wrapper' className='flex flex-col justify-between w-full relative z-10 pt-12 min-h-screen'>
<div id='container-inner' className='w-full px-7 max-w-5xl justify-center mx-auto'>
<div id='container-inner' className='w-full px-7 max-w-3xl justify-center mx-auto'>
{slotTop}
{onLoading ? LoadingCover : children}
@@ -81,15 +85,20 @@ const LayoutBase = props => {
</div>
{/* 右侧侧推拉抽屉 */}
<div className={`hidden xl:block dark:border-transparent w-96 relative z-10 ${CONFIG_MEDIUM.RIGHT_PANEL_DARK ? 'bg-hexo-black-gray dark' : ''}`}>
<div style={{ width: '32rem' }} className={`hidden xl:block dark:border-transparent relative z-10 ${CONFIG_MEDIUM.RIGHT_PANEL_DARK ? 'bg-hexo-black-gray dark' : ''}`}>
<div className='py-14 px-6 sticky top-0'>
{slotRight}
<ArticleInfo post={props?.post ? props.post : props.notice }/>
<div className='pt-12'>
<InfoCard {...props} />
{CONFIG_MEDIUM.WIDGET_REVOLVER_MAPS === 'true' && <RevolverMaps />}
<ArticleInfo post={props?.post ? props.post : props.notice} />
<div className='pt-6'>
<Catalog {...props} />
{slotRight}
{router.route === '/' && <>
<InfoCard {...props} />
{CONFIG_MEDIUM.WIDGET_REVOLVER_MAPS === 'true' && <RevolverMaps />}
<Live2D />
</>}
</div>
<Live2D />
</div>
</div>

View File

@@ -1,6 +1,5 @@
import LayoutBase from './LayoutBase'
import Announcement from './components/Announcement'
import ArticleInfo from './components/ArticleInfo'
export const LayoutIndex = (props) => {
return <LayoutBase {...props}>

View File

@@ -30,7 +30,7 @@ export const LayoutSearch = (props) => {
}
},
100)
})
}, [currentSearch])
return <LayoutBase {...props}>
<div className='py-12'>

View File

@@ -1,11 +1,6 @@
import LayoutBase from './LayoutBase'
import { useGlobal } from '@/lib/global'
import React from 'react'
import Catalog from './components/Catalog'
import { ArticleLock } from './components/ArticleLock'
import formatDate from '@/lib/formatDate'
import BLOG from '@/blog.config'
import Link from 'next/link'
import NotionPage from '@/components/NotionPage'
import CONFIG_MEDIUM from './config_medium'
import Comment from '@/components/Comment'
@@ -16,22 +11,14 @@ import TagItemMini from './components/TagItemMini'
import ShareBar from '@/components/ShareBar'
export const LayoutSlug = (props) => {
const { post, prev, next, siteInfo, lock, validPassword } = props
const { locale } = useGlobal()
const date = formatDate(
post?.date?.start_date || post?.createdTime,
locale.LOCALE
)
const slotRight = post?.toc && post?.toc?.length > 3 && <Catalog toc={post.toc} />
console.log(slotRight, post, 'jhhh')
const { post, prev, next, lock, validPassword } = props
if (!post) {
return <LayoutBase {...props}/>
}
return (
<LayoutBase slogRight={slotRight} {...props} >
<LayoutBase {...props} >
{/* 文章锁 */}
{lock && <ArticleLock validPassword={validPassword} />}
@@ -40,30 +27,8 @@ export const LayoutSlug = (props) => {
{/* title */}
<h1 className="text-3xl pt-12 dark:text-gray-300">{post?.title}</h1>
{/* meta */}
<section className="py-2 items-center text-sm px-1">
<div className='flex flex-wrap text-gray-500 py-1 dark:text-gray-600'>
<span className='whitespace-nowrap'> <i className='far fa-calendar mr-2' />{date}</span>
<span className='mx-1'>|</span>
<span className='whitespace-nowrap mr-2'><i className='far fa-calendar-check mr-2' />{post.lastEditedTime}</span>
<div className="hidden busuanzi_container_page_pv font-light mr-2 whitespace-nowrap">
<i className="mr-1 fas fa-eye" /><span className="busuanzi_value_page_pv" />
</div>
</div>
<Link href="/about" passHref legacyBehavior>
<div className='flex pt-2'>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={siteInfo?.icon} className='rounded-full cursor-pointer' width={22} alt={BLOG.AUTHOR} />
<div className="mr-3 ml-2 my-auto text-green-500 cursor-pointer">
{BLOG.AUTHOR}
</div>
</div>
</Link>
</section>
{/* Notion文章主体 */}
<section id="notion-article" className="px-1 max-w-4xl">
<section id="notion-article" className="px-1">
{post && (<NotionPage post={post} />)}
</section>
@@ -73,13 +38,13 @@ export const LayoutSlug = (props) => {
<ShareBar post={post} />
{/* 文章分类和标签信息 */}
<div className='flex justify-between'>
{CONFIG_MEDIUM.POST_DETAIL_CATEGORY && post.category && <CategoryItem category={post.category} />}
{CONFIG_MEDIUM.POST_DETAIL_CATEGORY && post?.category && <CategoryItem category={post.category} />}
<div>
{CONFIG_MEDIUM.POST_DETAIL_TAG && post?.tagItems?.map(tag => <TagItemMini key={tag.name} tag={tag} />)}
</div>
</div>
{post.type === 'Post' && <ArticleAround prev={prev} next={next} />}
{post?.type === 'Post' && <ArticleAround prev={prev} next={next} />}
<Comment frontMatter={post} />
</section>

View File

@@ -1,27 +1,25 @@
import BLOG from '@/blog.config'
import Link from 'next/link'
import { useRouter } from 'next/router'
import React from 'react'
const BlogPostCard = ({ post, showSummary }) => {
const BlogPostCard = ({ post }) => {
const router = useRouter()
const currentSelected = router.asPath.split('?')[0] === '/' + post.slug
return (
<div
key={post.id}
className="mb-6"
>
<div key={post.id} className="py-0.5">
<div className="flex flex-col w-full">
<Link
href={`${BLOG.SUB_PATH}/${post.slug}`}
passHref
className={
'cursor-pointer font-bold hover:underline leading-tight text-gray-700 dark:text-gray-300 hover:text-green-500 dark:hover:text-green-400'
`${currentSelected ? 'bg-gray-500 text-white' : 'text-gray-700 dark:text-gray-300 '} hover:font-bold py-0.5 px-1 text-sm cursor-pointer`
}>
<div>
{post.category} - {post.title}
</div>
</Link>
</div>
</div>
)

View File

@@ -1,9 +1,6 @@
import BLOG from '@/blog.config'
import BlogPostCard from './BlogPostCard'
import BlogPostListEmpty from './BlogPostListEmpty'
import throttle from 'lodash.throttle'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { useRouter } from 'next/router'
import React, { useRef } from 'react'
/**
* 博客列表滚动分页
@@ -13,85 +10,19 @@ import { useRouter } from 'next/router'
* @constructor
*/
const BlogPostListScroll = ({ posts = [], currentSearch }) => {
const postsPerPage = BLOG.POSTS_PER_PAGE
const [page, updatePage] = useState(1)
let filteredPosts = Object.assign(posts)
const router = useRouter()
const searchKey = getSearchKey(router)
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())
})
}
const postsToShow = getPostByPage(page, filteredPosts, postsPerPage)
let hasMore = false
if (filteredPosts) {
const totalCount = filteredPosts.length
hasMore = page * postsPerPage < totalCount
}
const handleGetMore = () => {
if (!hasMore) return
updatePage(page + 1)
}
// 监听滚动自动分页加载
const scrollTrigger = useCallback(throttle(() => {
const scrollS = window.scrollY + window.outerHeight
const clientHeight = targetRef ? (targetRef.current ? (targetRef.current.clientHeight) : 0) : 0
if (scrollS > clientHeight + 100) {
handleGetMore()
}
}, 500))
// 监听滚动
useEffect(() => {
window.addEventListener('scroll', scrollTrigger)
return () => {
window.removeEventListener('scroll', scrollTrigger)
}
})
const targetRef = useRef(null)
const filteredPosts = Object.assign(posts)
if (!postsToShow || postsToShow.length === 0) {
if (!filteredPosts || filteredPosts.length === 0) {
return <BlogPostListEmpty currentSearch={currentSearch} />
} else {
return <div id='container' ref={targetRef} className='w-full'>
{/* 文章列表 */}
<div className='space-y-1 lg:space-y-4'>
{postsToShow?.map(post => (
<BlogPostCard key={post.id} post={post} showSummary={true} />
))}
</div>
</div>
{/* 文章列表 */}
{filteredPosts?.map(post => (
<BlogPostCard key={post.id} post={post} showSummary={true} />
))}
</div>
}
}
/**
* 获取从第1页到指定页码的文章
* @param page 第几页
* @param totalPosts 所有文章
* @param postsPerPage 每页文章数量
* @returns {*}
*/
const getPostByPage = function (page, totalPosts, postsPerPage) {
return totalPosts.slice(
0,
postsPerPage * page
)
}
function getSearchKey(router) {
if (router.query && router.query.s) {
return router.query.s
}
return null
}
export default BlogPostListScroll

View File

@@ -1,7 +1,7 @@
import { useCallback, useEffect, useRef, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import throttle from 'lodash.throttle'
import { uuidToId } from 'notion-utils'
import Progress from './Progress'
import { isBrowser } from '@/lib/utils'
/**
* 目录导航组件
@@ -9,11 +9,9 @@ import Progress from './Progress'
* @returns {JSX.Element}
* @constructor
*/
const Catalog = ({ toc }) => {
const Catalog = ({ post }) => {
const tocIds = []
// 目录自动滚动
const tRef = useRef(null)
const toc = post?.toc
// 同步选中目录事件
const [activeSection, setActiveSection] = useState(null)
@@ -24,7 +22,7 @@ const Catalog = ({ toc }) => {
return () => {
window.removeEventListener('scroll', actionSectionScrollSpy)
}
}, [])
}, [post])
const throttleMs = 200
const actionSectionScrollSpy = useCallback(throttle(() => {
@@ -51,19 +49,18 @@ const Catalog = ({ toc }) => {
}
setActiveSection(currentSectionId)
const index = tocIds.indexOf(currentSectionId) || 0
tRef?.current?.scrollTo({ top: 28 * index, behavior: 'smooth' })
if (isBrowser()) {
document?.getElementById('toc-wrapper')?.scrollTo({ top: 28 * index, behavior: 'smooth' })
}
}, throttleMs))
// 无目录就直接返回空
if (!toc || toc.length < 1) {
return <></>
return null
}
return <div className='px-3'>
<div className='w-full mt-2 mb-4'>
<Progress />
</div>
<div className='overflow-y-auto max-h-96 overscroll-none scroll-hidden' ref={tRef}>
return <>
<div id='toc-wrapper' className='overflow-y-auto max-h-96 overscroll-none scroll-hidden'>
<nav className='h-full text-black'>
{toc.map((tocItem) => {
const id = uuidToId(tocItem.id)
@@ -76,7 +73,7 @@ const Catalog = ({ toc }) => {
notion-table-of-contents-item-indent-level-${tocItem.indentLevel} `}
>
<span style={{ display: 'inline-block', marginLeft: tocItem.indentLevel * 16 }}
className={`${activeSection === id && ' font-bold text-green-500 underline'}`}
className={`${activeSection === id && ' font-bold text-gray-500 underline'}`}
>
{tocItem.text}
</span>
@@ -85,7 +82,7 @@ const Catalog = ({ toc }) => {
})}
</nav>
</div>
</div>
</>
}
export default Catalog

View File

@@ -5,7 +5,7 @@ import DarkModeButton from '@/components/DarkModeButton'
const Footer = ({ title }) => {
const d = new Date()
const currentYear = d.getFullYear()
const copyrightDate = (function() {
const copyrightDate = (function () {
if (Number.isInteger(BLOG.SINCE) && BLOG.SINCE < currentYear) {
return BLOG.SINCE + '-' + currentYear
}
@@ -13,22 +13,26 @@ const Footer = ({ title }) => {
})()
return (
<footer
className='z-10 dark:bg-hexo-black-gray flex-shrink-0 justify-center text-center mx-auto w-full leading-6 text-sm px-6 pb-6 relative'
>
<DarkModeButton/>
<i className='fas fa-copyright' /> {`${copyrightDate}`} <span><i className='mx-1 animate-pulse fas fa-heart'/> <a href={BLOG.LINK} className='underline font-bold text-gray-500 dark:text-gray-300 '>{BLOG.AUTHOR}</a>.<br/>
<footer
className='max-w-3xl z-10 dark:bg-hexo-black-gray flex-shrink-0 justify-center text-center mx-auto w-full leading-6 text-sm px-6 pb-6 relative'
>
<hr />
{BLOG.BEI_AN && <><i className='fas fa-shield-alt'/> <a href='https://beian.miit.gov.cn/' className='mr-2'>{BLOG.BEI_AN}</a><br/></>}
<div className='flex justify-between'>
<div className='flex'>© {`${copyrightDate}`} <div><i className='mx-1 animate-pulse fas fa-heart' /> <a href={BLOG.LINK} className='underline font-bold text-gray-500 dark:text-gray-300 '>{BLOG.AUTHOR}</a>.<br /></div></div>
<div className='text-xs font-serif'><a href='https://github.com/tangly1024/NotionNext' className='underline text-gray-500 dark:text-gray-300'>NotionNext {BLOG.VERSION}</a></div>
</div>
<span className='hidden busuanzi_container_site_pv'>
<i className='fas fa-eye'/><span className='px-1 busuanzi_value_site_pv'> </span> </span>
<span className='pl-2 hidden busuanzi_container_site_uv'>
<i className='fas fa-users'/> <span className='px-1 busuanzi_value_site_uv'> </span> </span>
<br/>
<h1>{title}</h1>
<span className='text-xs font-serif'>Powered by <a href='https://github.com/tangly1024/NotionNext' className='underline text-gray-500 dark:text-gray-300'>NotionNext {BLOG.VERSION}</a>.</span></span>
</footer>
{BLOG.BEI_AN && <><i className='fas fa-shield-alt' /> <a href='https://beian.miit.gov.cn/' className='mr-2'>{BLOG.BEI_AN}</a><br /></>}
<span className='hidden busuanzi_container_site_pv'>
<i className='fas fa-eye' /><span className='px-1 busuanzi_value_site_pv'> </span> </span>
<span className='pl-2 hidden busuanzi_container_site_uv'>
<i className='fas fa-users' /> <span className='px-1 busuanzi_value_site_uv'> </span> </span>
<br />
<h1>{title}</h1>
</footer>
)
}

View File

@@ -17,7 +17,7 @@ export const MenuItemDrop = ({ link }) => {
return <li className='cursor-pointer list-none items-center flex mx-2' onMouseOver={() => changeShow(true)} onMouseOut={() => changeShow(false)} >
{hasSubMenu &&
<div className={'px-3 h-full whitespace-nowrap duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
<div className={'px-2 h-full whitespace-nowrap duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
(selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600')}>
<div>
{link?.icon && <i className={link?.icon} />} {link?.name}
@@ -27,7 +27,7 @@ export const MenuItemDrop = ({ link }) => {
}
{!hasSubMenu &&
<div className={'px-3 h-full whitespace-nowrap duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
<div className={'px-2 h-full whitespace-nowrap duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
(selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600')}>
<Link href={link?.to}>
{link?.icon && <i className={link?.icon} />} {link?.name}

View File

@@ -7,7 +7,7 @@ export default function RevolverMaps () {
initRevolverMaps()
changeLoad(true)
}
})
}, [])
return <div id="revolvermaps" className='p-4'/>
}

View File

@@ -1,11 +1,11 @@
import { useRouter } from 'next/router'
import { useImperativeHandle, useRef, useState } from 'react'
import { useMediumGlobal } from '../LayoutBase'
let lock = false
const SearchInput = ({ currentTag, currentSearch, cRef, className }) => {
const [onLoading, setLoadingState] = useState(false)
const router = useRouter()
const SearchInput = ({ currentSearch, cRef, className }) => {
const searchInputRef = useRef()
const { setFilterPosts, allNavPages } = useMediumGlobal()
useImperativeHandle(cRef, () => {
return {
focus: () => {
@@ -15,15 +15,43 @@ const SearchInput = ({ currentTag, currentSearch, cRef, className }) => {
})
const handleSearch = () => {
const key = searchInputRef.current.value
if (key && key !== '') {
setLoadingState(true)
location.href = '/search/' + key
let keyword = searchInputRef.current.value
const filterPosts = []
if (keyword) {
keyword = keyword.trim()
} else {
router.push({ pathname: '/' }).then(r => {
})
setFilterPosts(allNavPages)
}
for (const post of allNavPages) {
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.toLowerCase().indexOf(keyword) > -1
const indexContent = [post.summary]
// 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)
}
}
setFilterPosts(filterPosts)
}
const handleKeyUp = (e) => {
if (e.keyCode === 13) { // 回车
@@ -72,7 +100,7 @@ const SearchInput = ({ currentTag, currentSearch, cRef, className }) => {
<div className='-ml-8 cursor-pointer float-right items-center justify-center py-2'
onClick={handleSearch}>
<i className={`hover:text-black transform duration-200 text-gray-500 dark:hover:text-gray-300 cursor-pointer fas ${onLoading ? 'fa-spinner animate-spin' : 'fa-search'} `} />
<i className={'hover:text-black transform duration-200 text-gray-500 dark:hover:text-gray-300 cursor-pointer fas fa-search'} />
</div>
{(showClean &&

View File

@@ -6,6 +6,7 @@ import { useGlobal } from '@/lib/global'
import CONFIG_MEDIUM from '../config_medium'
import BLOG from '@/blog.config'
import { MenuItemDrop } from './MenuItemDrop'
import DarkModeButton from '@/components/DarkModeButton'
/**
* 顶部导航栏 + 菜单
@@ -67,6 +68,7 @@ export default function TopNavBar(props) {
{/* 桌面端顶部菜单 */}
<div className='hidden md:flex'>
{links && links?.map(link => <MenuItemDrop key={link?.id} link={link}/>)}
<DarkModeButton className='text-sm flex items-center h-full' />
</div>
</div>
</div>