mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-06-09 07:26:47 +00:00
OpenWrite适配所有主题
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { siteConfig } from '@/lib/config'
|
import { siteConfig } from '@/lib/config'
|
||||||
import { isBrowser, loadExternalResource } from '@/lib/utils'
|
import { isBrowser, loadExternalResource } from '@/lib/utils'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useEffect, useRef } from 'react'
|
import { useEffect } from 'react'
|
||||||
/**
|
/**
|
||||||
* OpenWrite公众号导流插件
|
* OpenWrite公众号导流插件
|
||||||
* 使用介绍:https://openwrite.cn/guide/readmore/readmore.html#%E4%BA%8C%E3%80%81%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8
|
* 使用介绍:https://openwrite.cn/guide/readmore/readmore.html#%E4%BA%8C%E3%80%81%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8
|
||||||
@@ -20,8 +20,6 @@ const OpenWrite = () => {
|
|||||||
'原创不易,完成人机检测,阅读全文'
|
'原创不易,完成人机检测,阅读全文'
|
||||||
)
|
)
|
||||||
|
|
||||||
const hasLoaded = useRef(false)
|
|
||||||
|
|
||||||
const loadOpenWrite = async () => {
|
const loadOpenWrite = async () => {
|
||||||
try {
|
try {
|
||||||
await loadExternalResource(
|
await loadExternalResource(
|
||||||
@@ -48,11 +46,10 @@ const OpenWrite = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isBrowser && blogId && !hasLoaded.current) {
|
if (isBrowser && blogId) {
|
||||||
loadOpenWrite()
|
loadOpenWrite()
|
||||||
hasLoaded.current = true
|
|
||||||
}
|
}
|
||||||
}, [router])
|
})
|
||||||
|
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,10 +179,12 @@ const LayoutSlug = props => {
|
|||||||
{lock ? (
|
{lock ? (
|
||||||
<PostLock validPassword={validPassword} />
|
<PostLock validPassword={validPassword} />
|
||||||
) : (
|
) : (
|
||||||
<div id='article-wrapper'>
|
<div>
|
||||||
<PostMeta post={post} />
|
<PostMeta post={post} />
|
||||||
<NotionPage post={post} />
|
<div id='article-wrapper'>
|
||||||
<ShareBar post={post} />
|
<NotionPage post={post} />
|
||||||
|
<ShareBar post={post} />
|
||||||
|
</div>
|
||||||
<Comment frontMatter={post} />
|
<Comment frontMatter={post} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -341,8 +341,10 @@ const LayoutSlug = props => {
|
|||||||
|
|
||||||
{/* Notion文章主体 */}
|
{/* Notion文章主体 */}
|
||||||
{post && (
|
{post && (
|
||||||
<section id='article-wrapper' className='px-1'>
|
<section className='px-1'>
|
||||||
<NotionPage post={post} />
|
<div id='article-wrapper'>
|
||||||
|
<NotionPage post={post} />
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 分享 */}
|
{/* 分享 */}
|
||||||
<ShareBar post={post} />
|
<ShareBar post={post} />
|
||||||
|
|||||||
@@ -288,14 +288,17 @@ const LayoutSlug = props => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className={`article h-full w-full ${fullWidth ? '' : 'xl:max-w-5xl'} ${hasCode ? 'xl:w-[73.15vw]' : ''} lg:hover:shadow lg:border rounded-2xl lg:px-2 lg:py-4 bg-white dark:bg-[#18171d] dark:border-gray-600`}>
|
className={`article h-full w-full ${fullWidth ? '' : 'xl:max-w-5xl'} ${hasCode ? 'xl:w-[73.15vw]' : ''} bg-white dark:bg-[#18171d] dark:border-gray-600 lg:hover:shadow lg:border rounded-2xl lg:px-2 lg:py-4 `}>
|
||||||
{/* 文章锁 */}
|
{/* 文章锁 */}
|
||||||
{lock && <PostLock validPassword={validPassword} />}
|
{lock && <PostLock validPassword={validPassword} />}
|
||||||
|
|
||||||
{!lock && (
|
{!lock && (
|
||||||
<div id='article-wrapper' className='mx-auto md:w-full md:px-5'>
|
<div className='mx-auto md:w-full md:px-5'>
|
||||||
{/* 文章主体 */}
|
{/* 文章主体 */}
|
||||||
<article itemScope itemType='https://schema.org/Movie'>
|
<article
|
||||||
|
id='article-wrapper'
|
||||||
|
itemScope
|
||||||
|
itemType='https://schema.org/Movie'>
|
||||||
{/* Notion文章主体 */}
|
{/* Notion文章主体 */}
|
||||||
<section
|
<section
|
||||||
className='wow fadeInUp p-5 justify-center mx-auto'
|
className='wow fadeInUp p-5 justify-center mx-auto'
|
||||||
|
|||||||
@@ -287,10 +287,9 @@ const LayoutSlug = props => {
|
|||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
{!lock && (
|
{!lock && (
|
||||||
<div
|
<div className='overflow-x-auto flex-grow mx-auto md:w-full md:px-5 '>
|
||||||
id='article-wrapper'
|
|
||||||
className='overflow-x-auto flex-grow mx-auto md:w-full md:px-5 '>
|
|
||||||
<article
|
<article
|
||||||
|
id='article-wrapper'
|
||||||
itemScope
|
itemScope
|
||||||
itemType='https://schema.org/Movie'
|
itemType='https://schema.org/Movie'
|
||||||
className='subpixel-antialiased overflow-y-hidden'>
|
className='subpixel-antialiased overflow-y-hidden'>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -7,21 +6,21 @@
|
|||||||
* 2. 内容大部分是在此文件中写死,notion数据从props参数中传进来
|
* 2. 内容大部分是在此文件中写死,notion数据从props参数中传进来
|
||||||
* 3. 您可在此网站找到更多喜欢的组件 https://www.tailwind-kit.com/
|
* 3. 您可在此网站找到更多喜欢的组件 https://www.tailwind-kit.com/
|
||||||
*/
|
*/
|
||||||
|
import Loading from '@/components/Loading'
|
||||||
import NotionPage from '@/components/NotionPage'
|
import NotionPage from '@/components/NotionPage'
|
||||||
import Header from './components/Header'
|
import { siteConfig } from '@/lib/config'
|
||||||
import Footer from './components/Footer'
|
import { isBrowser } from '@/lib/utils'
|
||||||
import Hero from './components/Hero'
|
import { useRouter } from 'next/router'
|
||||||
|
import { useEffect } from 'react'
|
||||||
import Features from './components/Features'
|
import Features from './components/Features'
|
||||||
import FeaturesBlocks from './components/FeaturesBlocks'
|
import FeaturesBlocks from './components/FeaturesBlocks'
|
||||||
import Testimonials from './components/Testimonials'
|
import Footer from './components/Footer'
|
||||||
|
import Header from './components/Header'
|
||||||
|
import Hero from './components/Hero'
|
||||||
import Newsletter from './components/Newsletter'
|
import Newsletter from './components/Newsletter'
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import CONFIG from './config'
|
|
||||||
import Loading from '@/components/Loading'
|
|
||||||
import { isBrowser } from '@/lib/utils'
|
|
||||||
import { siteConfig } from '@/lib/config'
|
|
||||||
import { Pricing } from './components/Pricing'
|
import { Pricing } from './components/Pricing'
|
||||||
import { useEffect } from 'react'
|
import Testimonials from './components/Testimonials'
|
||||||
|
import CONFIG from './config'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 布局框架
|
* 布局框架
|
||||||
@@ -30,22 +29,23 @@ import { useEffect } from 'react'
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutBase = (props) => {
|
const LayoutBase = props => {
|
||||||
const { children } = props
|
const { children } = props
|
||||||
|
|
||||||
return <div id='theme-landing' className={`${siteConfig('FONT_STYLE')} scroll-smooth overflow-hidden flex flex-col justify-between bg-white dark:bg-black`}>
|
return (
|
||||||
|
<div
|
||||||
|
id='theme-landing'
|
||||||
|
className={`${siteConfig('FONT_STYLE')} scroll-smooth overflow-hidden flex flex-col justify-between bg-white dark:bg-black`}>
|
||||||
|
{/* 顶部导航栏 */}
|
||||||
|
<Header />
|
||||||
|
|
||||||
{/* 顶部导航栏 */}
|
{/* 内容 */}
|
||||||
<Header />
|
<div id='content-wrapper'>{children}</div>
|
||||||
|
|
||||||
{/* 内容 */}
|
{/* 底部页脚 */}
|
||||||
<div id='content-wrapper'>
|
<Footer />
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 底部页脚 */}
|
|
||||||
<Footer />
|
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,16 +53,16 @@ const LayoutBase = (props) => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutIndex = (props) => {
|
const LayoutIndex = props => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Hero />
|
<Hero />
|
||||||
<Features />
|
<Features />
|
||||||
<FeaturesBlocks />
|
<FeaturesBlocks />
|
||||||
<Testimonials />
|
<Testimonials />
|
||||||
<Pricing/>
|
<Pricing />
|
||||||
<Newsletter />
|
<Newsletter />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ const LayoutIndex = (props) => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutSlug = (props) => {
|
const LayoutSlug = props => {
|
||||||
const { post } = props
|
const { post } = props
|
||||||
|
|
||||||
// 如果 是 /article/[slug] 的文章路径则进行重定向到另一个域名
|
// 如果 是 /article/[slug] 的文章路径则进行重定向到另一个域名
|
||||||
@@ -79,49 +79,90 @@ const LayoutSlug = (props) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 404
|
// 404
|
||||||
if (!post) {
|
if (!post) {
|
||||||
setTimeout(() => {
|
setTimeout(
|
||||||
if (isBrowser) {
|
() => {
|
||||||
const article = document.getElementById('notion-article')
|
if (isBrowser) {
|
||||||
if (!article) {
|
const article = document.getElementById('notion-article')
|
||||||
router.push('/404').then(() => {
|
if (!article) {
|
||||||
console.warn('找不到页面', router.asPath)
|
router.push('/404').then(() => {
|
||||||
})
|
console.warn('找不到页面', router.asPath)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}, siteConfig('POST_WAITING_TIME_FOR_404') * 1000)
|
siteConfig('POST_WAITING_TIME_FOR_404') * 1000
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}, [post])
|
}, [post])
|
||||||
|
|
||||||
if (JSON.parse(siteConfig('LANDING_POST_REDIRECT_ENABLE', null, CONFIG)) && isBrowser && router.route === '/[prefix]/[slug]') {
|
if (
|
||||||
const redirectUrl = siteConfig('LANDING_POST_REDIRECT_URL', null, CONFIG) + router.asPath.replace('?theme=landing', '')
|
JSON.parse(siteConfig('LANDING_POST_REDIRECT_ENABLE', null, CONFIG)) &&
|
||||||
|
isBrowser &&
|
||||||
|
router.route === '/[prefix]/[slug]'
|
||||||
|
) {
|
||||||
|
const redirectUrl =
|
||||||
|
siteConfig('LANDING_POST_REDIRECT_URL', null, CONFIG) +
|
||||||
|
router.asPath.replace('?theme=landing', '')
|
||||||
router.push(redirectUrl)
|
router.push(redirectUrl)
|
||||||
return <div id='theme-landing'><Loading /></div>
|
return (
|
||||||
|
<div id='theme-landing'>
|
||||||
|
<Loading />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>
|
return (
|
||||||
<div id='container-inner' className='mx-auto max-w-screen-lg p-12'>
|
<>
|
||||||
<NotionPage {...props} />
|
<div id='container-inner' className='mx-auto max-w-screen-lg p-12'>
|
||||||
|
<div id='article-wrapper'>
|
||||||
|
<NotionPage {...props} />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 其他布局暂时留空
|
// 其他布局暂时留空
|
||||||
const LayoutSearch = (props) => <><Hero /></>
|
const LayoutSearch = props => (
|
||||||
const LayoutArchive = (props) => <><Hero /></>
|
<>
|
||||||
const Layout404 = (props) => <><Hero /></>
|
<Hero />
|
||||||
const LayoutCategoryIndex = (props) => <><Hero /></>
|
</>
|
||||||
const LayoutPostList = (props) => <><Hero /></>
|
)
|
||||||
const LayoutTagIndex = (props) => <><Hero /></>
|
const LayoutArchive = props => (
|
||||||
|
<>
|
||||||
|
<Hero />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
const Layout404 = props => (
|
||||||
|
<>
|
||||||
|
<Hero />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
const LayoutCategoryIndex = props => (
|
||||||
|
<>
|
||||||
|
<Hero />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
const LayoutPostList = props => (
|
||||||
|
<>
|
||||||
|
<Hero />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
const LayoutTagIndex = props => (
|
||||||
|
<>
|
||||||
|
<Hero />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
export {
|
export {
|
||||||
CONFIG as THEME_CONFIG,
|
|
||||||
LayoutBase,
|
|
||||||
LayoutIndex,
|
|
||||||
LayoutSearch,
|
|
||||||
LayoutArchive,
|
|
||||||
LayoutSlug,
|
|
||||||
Layout404,
|
Layout404,
|
||||||
LayoutPostList,
|
LayoutArchive,
|
||||||
|
LayoutBase,
|
||||||
LayoutCategoryIndex,
|
LayoutCategoryIndex,
|
||||||
LayoutTagIndex
|
LayoutIndex,
|
||||||
|
LayoutPostList,
|
||||||
|
LayoutSearch,
|
||||||
|
LayoutSlug,
|
||||||
|
LayoutTagIndex,
|
||||||
|
CONFIG as THEME_CONFIG
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -254,9 +254,7 @@ const LayoutSlug = props => {
|
|||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
{!lock && (
|
{!lock && (
|
||||||
<div
|
<div className='overflow-x-auto md:w-full px-3 '>
|
||||||
id='article-wrapper'
|
|
||||||
className='overflow-x-auto md:w-full px-3 '>
|
|
||||||
{/* 文章信息 */}
|
{/* 文章信息 */}
|
||||||
{post?.type && post?.type === 'Post' && (
|
{post?.type && post?.type === 'Post' && (
|
||||||
<>
|
<>
|
||||||
@@ -268,7 +266,7 @@ const LayoutSlug = props => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='lg:px-10 subpixel-antialiased'>
|
<div className='lg:px-10 subpixel-antialiased'>
|
||||||
<article itemScope>
|
<article id='article-wrapper' itemScope>
|
||||||
{/* Notion文章主体 */}
|
{/* Notion文章主体 */}
|
||||||
<section
|
<section
|
||||||
data-wow-delay='.1s'
|
data-wow-delay='.1s'
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
import CONFIG from './config'
|
import Comment from '@/components/Comment'
|
||||||
import { useState, createContext, useContext, useEffect } from 'react'
|
|
||||||
import Footer from './components/Footer'
|
|
||||||
import InfoCard from './components/InfoCard'
|
|
||||||
import RevolverMaps from './components/RevolverMaps'
|
|
||||||
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 Live2D from '@/components/Live2D'
|
||||||
import Announcement from './components/Announcement'
|
import replaceSearchResult from '@/components/Mark'
|
||||||
import JumpToTopButton from './components/JumpToTopButton'
|
import NotionPage from '@/components/NotionPage'
|
||||||
import BlogPostListPage from './components/BlogPostListPage'
|
import ShareBar from '@/components/ShareBar'
|
||||||
import BlogPostListScroll from './components/BlogPostListScroll'
|
import Tabs from '@/components/Tabs'
|
||||||
import Catalog from './components/Catalog'
|
import { siteConfig } from '@/lib/config'
|
||||||
import { ArticleLock } from './components/ArticleLock'
|
import { useGlobal } from '@/lib/global'
|
||||||
import TagGroups from './components/TagGroups'
|
|
||||||
import CategoryGroup from './components/CategoryGroup'
|
|
||||||
import { isBrowser } from '@/lib/utils'
|
import { isBrowser } from '@/lib/utils'
|
||||||
|
import { Transition } from '@headlessui/react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { createContext, useContext, useEffect, useState } from 'react'
|
||||||
|
import Announcement from './components/Announcement'
|
||||||
|
import ArticleAround from './components/ArticleAround'
|
||||||
|
import ArticleInfo from './components/ArticleInfo'
|
||||||
|
import { ArticleLock } from './components/ArticleLock'
|
||||||
import BlogArchiveItem from './components/BlogArchiveItem'
|
import BlogArchiveItem from './components/BlogArchiveItem'
|
||||||
import BlogPostBar from './components/BlogPostBar'
|
import BlogPostBar from './components/BlogPostBar'
|
||||||
import NotionPage from '@/components/NotionPage'
|
import BlogPostListPage from './components/BlogPostListPage'
|
||||||
import Comment from '@/components/Comment'
|
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||||
import ArticleAround from './components/ArticleAround'
|
import BottomMenuBar from './components/BottomMenuBar'
|
||||||
import TocDrawer from './components/TocDrawer'
|
import Catalog from './components/Catalog'
|
||||||
|
import CategoryGroup from './components/CategoryGroup'
|
||||||
import CategoryItem from './components/CategoryItem'
|
import CategoryItem from './components/CategoryItem'
|
||||||
|
import Footer from './components/Footer'
|
||||||
|
import InfoCard from './components/InfoCard'
|
||||||
|
import JumpToTopButton from './components/JumpToTopButton'
|
||||||
|
import RevolverMaps from './components/RevolverMaps'
|
||||||
|
import SearchInput from './components/SearchInput'
|
||||||
|
import TagGroups from './components/TagGroups'
|
||||||
import TagItemMini from './components/TagItemMini'
|
import TagItemMini from './components/TagItemMini'
|
||||||
import ShareBar from '@/components/ShareBar'
|
import TocDrawer from './components/TocDrawer'
|
||||||
import Link from 'next/link'
|
import TopNavBar from './components/TopNavBar'
|
||||||
import { Transition } from '@headlessui/react'
|
import CONFIG from './config'
|
||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
import replaceSearchResult from '@/components/Mark'
|
|
||||||
import ArticleInfo from './components/ArticleInfo'
|
|
||||||
import { siteConfig } from '@/lib/config'
|
|
||||||
|
|
||||||
// 主题全局状态
|
// 主题全局状态
|
||||||
const ThemeGlobalMedium = createContext()
|
const ThemeGlobalMedium = createContext()
|
||||||
@@ -51,86 +51,96 @@ const LayoutBase = props => {
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [tocVisible, changeTocVisible] = useState(false)
|
const [tocVisible, changeTocVisible] = useState(false)
|
||||||
const { onLoading, fullWidth } = useGlobal()
|
const { onLoading, fullWidth } = useGlobal()
|
||||||
const [slotRight, setSlotRight] = useState(null);
|
const [slotRight, setSlotRight] = useState(null)
|
||||||
|
|
||||||
useEffect(()=> {
|
useEffect(() => {
|
||||||
if (post?.toc?.length > 0) {
|
if (post?.toc?.length > 0) {
|
||||||
setSlotRight(
|
setSlotRight(
|
||||||
<div key={locale.COMMON.TABLE_OF_CONTENTS}>
|
<div key={locale.COMMON.TABLE_OF_CONTENTS}>
|
||||||
<Catalog toc={post?.toc} />
|
<Catalog toc={post?.toc} />
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
} else {
|
} else {
|
||||||
setSlotRight(null);
|
setSlotRight(null)
|
||||||
}
|
}
|
||||||
},[post])
|
}, [post])
|
||||||
|
|
||||||
const slotTop = <BlogPostBar {...props} />
|
const slotTop = <BlogPostBar {...props} />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeGlobalMedium.Provider value={{ tocVisible, changeTocVisible }}>
|
<ThemeGlobalMedium.Provider value={{ tocVisible, changeTocVisible }}>
|
||||||
{/* CSS样式 */}
|
{/* CSS样式 */}
|
||||||
<Style />
|
<Style />
|
||||||
|
|
||||||
<div id='theme-medium' className={`${siteConfig('FONT_STYLE')} bg-white dark:bg-hexo-black-gray w-full h-full min-h-screen justify-center dark:text-gray-300 scroll-smooth`}>
|
<div
|
||||||
|
id='theme-medium'
|
||||||
|
className={`${siteConfig('FONT_STYLE')} bg-white dark:bg-hexo-black-gray w-full h-full min-h-screen justify-center dark:text-gray-300 scroll-smooth`}>
|
||||||
|
<main
|
||||||
|
id='wrapper'
|
||||||
|
className={
|
||||||
|
(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE'))
|
||||||
|
? 'flex-row-reverse'
|
||||||
|
: '') + 'relative flex justify-between w-full h-full mx-auto'
|
||||||
|
}>
|
||||||
|
{/* 桌面端左侧菜单 */}
|
||||||
|
{/* <LeftMenuBar/> */}
|
||||||
|
|
||||||
<main id='wrapper' className={(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE')) ? 'flex-row-reverse' : '') + 'relative flex justify-between w-full h-full mx-auto'}>
|
{/* 主区 */}
|
||||||
{/* 桌面端左侧菜单 */}
|
<div id='container-wrapper' className='w-full relative z-10'>
|
||||||
{/* <LeftMenuBar/> */}
|
{/* 顶部导航栏 */}
|
||||||
|
<TopNavBar {...props} />
|
||||||
|
|
||||||
{/* 主区 */}
|
<div
|
||||||
<div id='container-wrapper' className='w-full relative z-10'>
|
id='container-inner'
|
||||||
|
className={`px-7 ${fullWidth ? '' : 'max-w-5xl'} justify-center mx-auto min-h-screen`}>
|
||||||
|
<Transition
|
||||||
|
show={!onLoading}
|
||||||
|
appear={true}
|
||||||
|
enter='transition ease-in-out duration-700 transform order-first'
|
||||||
|
enterFrom='opacity-0 translate-y-16'
|
||||||
|
enterTo='opacity-100'
|
||||||
|
leave='transition ease-in-out duration-300 transform'
|
||||||
|
leaveFrom='opacity-100'
|
||||||
|
leaveTo='opacity-0 -translate-y-16'
|
||||||
|
unmount={false}>
|
||||||
|
{slotTop}
|
||||||
|
{children}
|
||||||
|
</Transition>
|
||||||
|
|
||||||
{/* 顶部导航栏 */}
|
<JumpToTopButton />
|
||||||
<TopNavBar {...props} />
|
|
||||||
|
|
||||||
<div id='container-inner' className={`px-7 ${fullWidth ? '' : 'max-w-5xl'} justify-center mx-auto min-h-screen`}>
|
|
||||||
<Transition
|
|
||||||
show={!onLoading}
|
|
||||||
appear={true}
|
|
||||||
enter="transition ease-in-out duration-700 transform order-first"
|
|
||||||
enterFrom="opacity-0 translate-y-16"
|
|
||||||
enterTo="opacity-100"
|
|
||||||
leave="transition ease-in-out duration-300 transform"
|
|
||||||
leaveFrom="opacity-100"
|
|
||||||
leaveTo="opacity-0 -translate-y-16"
|
|
||||||
unmount={false}
|
|
||||||
>
|
|
||||||
{slotTop}
|
|
||||||
{children}
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
<JumpToTopButton />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 底部 */}
|
|
||||||
<Footer title={siteConfig('TITLE')} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 桌面端右侧 */}
|
|
||||||
{fullWidth
|
|
||||||
? null
|
|
||||||
: <div className={`hidden xl:block border-l dark:border-transparent w-80 flex-shrink-0 relative z-10 ${siteConfig('MEDIUM_RIGHT_PANEL_DARK', null, CONFIG) ? 'bg-hexo-black-gray dark' : ''}`}>
|
|
||||||
<div className='py-14 px-6 sticky top-0'>
|
|
||||||
<Tabs>
|
|
||||||
{slotRight}
|
|
||||||
<div key={locale.NAV.ABOUT}>
|
|
||||||
{router.pathname !== '/search' && <SearchInput className='mt-6 mb-12' />}
|
|
||||||
{showInfoCard && <InfoCard {...props} />}
|
|
||||||
{siteConfig('MEDIUM_WIDGET_REVOLVER_MAPS', null, CONFIG) === 'true' && <RevolverMaps />}
|
|
||||||
</div>
|
|
||||||
</Tabs>
|
|
||||||
<Announcement post={notice} />
|
|
||||||
<Live2D />
|
|
||||||
</div>
|
|
||||||
</div>}
|
|
||||||
|
|
||||||
</main>
|
|
||||||
|
|
||||||
{/* 移动端底部导航栏 */}
|
|
||||||
<BottomMenuBar {...props} className='block md:hidden' />
|
|
||||||
</div>
|
</div>
|
||||||
</ThemeGlobalMedium.Provider>
|
|
||||||
|
{/* 底部 */}
|
||||||
|
<Footer title={siteConfig('TITLE')} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 桌面端右侧 */}
|
||||||
|
{fullWidth ? null : (
|
||||||
|
<div
|
||||||
|
className={`hidden xl:block border-l dark:border-transparent w-80 flex-shrink-0 relative z-10 ${siteConfig('MEDIUM_RIGHT_PANEL_DARK', null, CONFIG) ? 'bg-hexo-black-gray dark' : ''}`}>
|
||||||
|
<div className='py-14 px-6 sticky top-0'>
|
||||||
|
<Tabs>
|
||||||
|
{slotRight}
|
||||||
|
<div key={locale.NAV.ABOUT}>
|
||||||
|
{router.pathname !== '/search' && (
|
||||||
|
<SearchInput className='mt-6 mb-12' />
|
||||||
|
)}
|
||||||
|
{showInfoCard && <InfoCard {...props} />}
|
||||||
|
{siteConfig('MEDIUM_WIDGET_REVOLVER_MAPS', null, CONFIG) ===
|
||||||
|
'true' && <RevolverMaps />}
|
||||||
|
</div>
|
||||||
|
</Tabs>
|
||||||
|
<Announcement post={notice} />
|
||||||
|
<Live2D />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{/* 移动端底部导航栏 */}
|
||||||
|
<BottomMenuBar {...props} className='block md:hidden' />
|
||||||
|
</div>
|
||||||
|
</ThemeGlobalMedium.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +150,7 @@ const LayoutBase = props => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutIndex = (props) => {
|
const LayoutIndex = props => {
|
||||||
return <LayoutPostList {...props} />
|
return <LayoutPostList {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,10 +158,16 @@ const LayoutIndex = (props) => {
|
|||||||
* 博客列表
|
* 博客列表
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutPostList = (props) => {
|
const LayoutPostList = props => {
|
||||||
return <>
|
return (
|
||||||
{siteConfig('POST_LIST_STYLE') === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
<>
|
||||||
|
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
||||||
|
<BlogPostListPage {...props} />
|
||||||
|
) : (
|
||||||
|
<BlogPostListScroll {...props} />
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -163,64 +179,72 @@ const LayoutSlug = props => {
|
|||||||
const { post, prev, next, lock, validPassword } = props
|
const { post, prev, next, lock, validPassword } = props
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const slotRight = post?.toc && post?.toc?.length >= 3 && (
|
const slotRight = post?.toc && post?.toc?.length >= 3 && (
|
||||||
<div key={locale.COMMON.TABLE_OF_CONTENTS} >
|
<div key={locale.COMMON.TABLE_OF_CONTENTS}>
|
||||||
<Catalog toc={post?.toc} />
|
<Catalog toc={post?.toc} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 404
|
// 404
|
||||||
if (!post) {
|
if (!post) {
|
||||||
setTimeout(() => {
|
setTimeout(
|
||||||
if (isBrowser) {
|
() => {
|
||||||
const article = document.getElementById('notion-article')
|
if (isBrowser) {
|
||||||
if (!article) {
|
const article = document.getElementById('notion-article')
|
||||||
router.push('/404').then(() => {
|
if (!article) {
|
||||||
console.warn('找不到页面', router.asPath)
|
router.push('/404').then(() => {
|
||||||
})
|
console.warn('找不到页面', router.asPath)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}, siteConfig('POST_WAITING_TIME_FOR_404') * 1000)
|
siteConfig('POST_WAITING_TIME_FOR_404') * 1000
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}, [post])
|
}, [post])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...props} >
|
<div {...props}>
|
||||||
{/* 文章锁 */}
|
{/* 文章锁 */}
|
||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
{!lock && <div id='article-wrapper'>
|
{!lock && (
|
||||||
|
<div>
|
||||||
|
{/* 文章信息 */}
|
||||||
|
<ArticleInfo {...props} />
|
||||||
|
|
||||||
{/* 文章信息 */}
|
{/* Notion文章主体 */}
|
||||||
<ArticleInfo {...props} />
|
<article id='article-wrapper' className='px-1 max-w-4xl'>
|
||||||
|
{post && <NotionPage post={post} />}
|
||||||
|
</article>
|
||||||
|
|
||||||
{/* Notion文章主体 */}
|
{/* 文章底部区域 */}
|
||||||
<section className="px-1 max-w-4xl">
|
<section>
|
||||||
{post && (<NotionPage post={post} />)}
|
{/* 分享 */}
|
||||||
</section>
|
<ShareBar post={post} />
|
||||||
|
{/* 文章分类和标签信息 */}
|
||||||
|
<div className='flex justify-between'>
|
||||||
|
{siteConfig('MEDIUM_POST_DETAIL_CATEGORY', null, CONFIG) &&
|
||||||
|
post?.category && <CategoryItem category={post?.category} />}
|
||||||
|
<div>
|
||||||
|
{siteConfig('MEDIUM_POST_DETAIL_TAG', null, CONFIG) &&
|
||||||
|
post?.tagItems?.map(tag => (
|
||||||
|
<TagItemMini key={tag.name} tag={tag} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* 上一篇下一篇文章 */}
|
||||||
|
{post?.type === 'Post' && <ArticleAround prev={prev} next={next} />}
|
||||||
|
{/* 评论区 */}
|
||||||
|
<Comment frontMatter={post} />
|
||||||
|
</section>
|
||||||
|
|
||||||
{/* 文章底部区域 */}
|
{/* 移动端目录 */}
|
||||||
<section>
|
<TocDrawer {...props} />
|
||||||
{/* 分享 */}
|
|
||||||
<ShareBar post={post} />
|
|
||||||
{/* 文章分类和标签信息 */}
|
|
||||||
<div className='flex justify-between'>
|
|
||||||
{siteConfig('MEDIUM_POST_DETAIL_CATEGORY', null, CONFIG) && post?.category && <CategoryItem category={post?.category} />}
|
|
||||||
<div>
|
|
||||||
{siteConfig('MEDIUM_POST_DETAIL_TAG', null, CONFIG) && post?.tagItems?.map(tag => <TagItemMini key={tag.name} tag={tag} />)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* 上一篇下一篇文章 */}
|
|
||||||
{post?.type === 'Post' && <ArticleAround prev={prev} next={next} />}
|
|
||||||
{/* 评论区 */}
|
|
||||||
<Comment frontMatter={post} />
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* 移动端目录 */}
|
|
||||||
<TocDrawer {...props} />
|
|
||||||
</div>}
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +253,7 @@ const LayoutSlug = props => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutSearch = (props) => {
|
const LayoutSearch = props => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const { keyword } = props
|
const { keyword } = props
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -248,23 +272,32 @@ const LayoutSearch = (props) => {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return <>
|
return (
|
||||||
|
<>
|
||||||
|
{/* 搜索导航栏 */}
|
||||||
|
<div className='py-12'>
|
||||||
|
<div className='pb-4 w-full'>{locale.NAV.SEARCH}</div>
|
||||||
|
<SearchInput currentSearch={currentSearch} {...props} />
|
||||||
|
{!currentSearch && (
|
||||||
|
<>
|
||||||
|
<TagGroups {...props} />
|
||||||
|
<CategoryGroup {...props} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 搜索导航栏 */}
|
{/* 文章列表 */}
|
||||||
<div className='py-12'>
|
{currentSearch && (
|
||||||
<div className='pb-4 w-full'>{locale.NAV.SEARCH}</div>
|
<div>
|
||||||
<SearchInput currentSearch={currentSearch} {...props} />
|
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
||||||
{!currentSearch && <>
|
<BlogPostListPage {...props} />
|
||||||
<TagGroups {...props} />
|
) : (
|
||||||
<CategoryGroup {...props} />
|
<BlogPostListScroll {...props} />
|
||||||
</>}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
{/* 文章列表 */}
|
|
||||||
{currentSearch && <div>
|
|
||||||
{siteConfig('POST_LIST_STYLE') === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
|
||||||
</div>}
|
|
||||||
</>
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -275,12 +308,17 @@ const LayoutSearch = (props) => {
|
|||||||
const LayoutArchive = props => {
|
const LayoutArchive = props => {
|
||||||
const { archivePosts } = props
|
const { archivePosts } = props
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mb-10 pb-20 md:py-12 py-3 min-h-full">
|
<div className='mb-10 pb-20 md:py-12 py-3 min-h-full'>
|
||||||
{Object.keys(archivePosts)?.map(archiveTitle => <BlogArchiveItem key={archiveTitle} archiveTitle={archiveTitle} archivePosts={archivePosts} />
|
{Object.keys(archivePosts)?.map(archiveTitle => (
|
||||||
)}
|
<BlogArchiveItem
|
||||||
</div>
|
key={archiveTitle}
|
||||||
</>
|
archiveTitle={archiveTitle}
|
||||||
|
archivePosts={archivePosts}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,9 +328,13 @@ const LayoutArchive = props => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const Layout404 = props => {
|
const Layout404 = props => {
|
||||||
return <>
|
return (
|
||||||
<div className='w-full h-96 py-80 flex justify-center items-center'>404 Not found.</div>
|
<>
|
||||||
|
<div className='w-full h-96 py-80 flex justify-center items-center'>
|
||||||
|
404 Not found.
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -300,33 +342,37 @@ const Layout404 = props => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutCategoryIndex = (props) => {
|
const LayoutCategoryIndex = props => {
|
||||||
const { categoryOptions } = props
|
const { categoryOptions } = props
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='bg-white dark:bg-gray-700 py-10'>
|
<div className='bg-white dark:bg-gray-700 py-10'>
|
||||||
<div className='dark:text-gray-200 mb-5'>
|
<div className='dark:text-gray-200 mb-5'>
|
||||||
<i className='mr-4 fas fa-th' />{locale.COMMON.CATEGORY}:
|
<i className='mr-4 fas fa-th' />
|
||||||
|
{locale.COMMON.CATEGORY}:
|
||||||
|
</div>
|
||||||
|
<div id='category-list' className='duration-200 flex flex-wrap'>
|
||||||
|
{categoryOptions?.map(category => {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={category.name}
|
||||||
|
href={`/category/${category.name}`}
|
||||||
|
passHref
|
||||||
|
legacyBehavior>
|
||||||
|
<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.name}({category.count})
|
||||||
</div>
|
</div>
|
||||||
<div id='category-list' className='duration-200 flex flex-wrap'>
|
</Link>
|
||||||
{categoryOptions?.map(category => {
|
)
|
||||||
return (
|
})}
|
||||||
<Link
|
</div>
|
||||||
key={category.name}
|
</div>
|
||||||
href={`/category/${category.name}`}
|
</>
|
||||||
passHref
|
|
||||||
legacyBehavior>
|
|
||||||
<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.name}({category.count})
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,35 +385,35 @@ const LayoutTagIndex = props => {
|
|||||||
const { tagOptions } = props
|
const { tagOptions } = props
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="bg-white dark:bg-gray-700 py-10">
|
<div className='bg-white dark:bg-gray-700 py-10'>
|
||||||
<div className="dark:text-gray-200 mb-5">
|
<div className='dark:text-gray-200 mb-5'>
|
||||||
<i className="mr-4 fas fa-tag" />
|
<i className='mr-4 fas fa-tag' />
|
||||||
{locale.COMMON.TAGS}:
|
{locale.COMMON.TAGS}:
|
||||||
</div>
|
</div>
|
||||||
<div id="tags-list" className="duration-200 flex flex-wrap">
|
<div id='tags-list' className='duration-200 flex flex-wrap'>
|
||||||
{tagOptions?.map(tag => {
|
{tagOptions?.map(tag => {
|
||||||
return (
|
return (
|
||||||
<div key={tag.name} className="p-2">
|
<div key={tag.name} className='p-2'>
|
||||||
<TagItemMini key={tag.name} tag={tag} />
|
<TagItemMini key={tag.name} tag={tag} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
CONFIG as THEME_CONFIG,
|
Layout404,
|
||||||
|
LayoutArchive,
|
||||||
LayoutBase,
|
LayoutBase,
|
||||||
|
LayoutCategoryIndex,
|
||||||
LayoutIndex,
|
LayoutIndex,
|
||||||
LayoutPostList,
|
LayoutPostList,
|
||||||
LayoutSearch,
|
LayoutSearch,
|
||||||
LayoutArchive,
|
|
||||||
LayoutSlug,
|
LayoutSlug,
|
||||||
Layout404,
|
LayoutTagIndex,
|
||||||
LayoutCategoryIndex,
|
CONFIG as THEME_CONFIG
|
||||||
LayoutTagIndex
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -290,8 +290,10 @@ const LayoutSlug = props => {
|
|||||||
|
|
||||||
{/* Notion文章主体 */}
|
{/* Notion文章主体 */}
|
||||||
{post && (
|
{post && (
|
||||||
<section id='article-wrapper' className='px-1'>
|
<section className='px-1'>
|
||||||
<NotionPage post={post} />
|
<div id='article-wrapper'>
|
||||||
|
<NotionPage post={post} />
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 分享 */}
|
{/* 分享 */}
|
||||||
{/* <ShareBar post={post} /> */}
|
{/* <ShareBar post={post} /> */}
|
||||||
|
|||||||
@@ -35,9 +35,7 @@ export default function ArticleDetail(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className='shadow md:hover:shadow-2xl overflow-x-auto flex-grow mx-auto w-screen md:w-full '>
|
||||||
id='article-wrapper'
|
|
||||||
className='shadow md:hover:shadow-2xl overflow-x-auto flex-grow mx-auto w-screen md:w-full '>
|
|
||||||
<div
|
<div
|
||||||
itemScope
|
itemScope
|
||||||
itemType='https://schema.org/Movie'
|
itemType='https://schema.org/Movie'
|
||||||
@@ -100,7 +98,7 @@ export default function ArticleDetail(props) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Notion内容主体 */}
|
{/* Notion内容主体 */}
|
||||||
<article className='mx-auto'>
|
<article id='article-wrapper' className='mx-auto'>
|
||||||
<WWAds className='w-full' orientation='horizontal' />
|
<WWAds className='w-full' orientation='horizontal' />
|
||||||
{post && <NotionPage post={post} />}
|
{post && <NotionPage post={post} />}
|
||||||
<WWAds className='w-full' orientation='horizontal' />
|
<WWAds className='w-full' orientation='horizontal' />
|
||||||
|
|||||||
@@ -1,34 +1,37 @@
|
|||||||
import CONFIG from './config'
|
import replaceSearchResult from '@/components/Mark'
|
||||||
|
import { siteConfig } from '@/lib/config'
|
||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import { isBrowser } from '@/lib/utils'
|
||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { createContext, useContext, useEffect, useRef, useState } from 'react'
|
||||||
|
import Announcement from './components/Announcement'
|
||||||
|
import ArticleDetail from './components/ArticleDetail'
|
||||||
|
import { ArticleLock } from './components/ArticleLock'
|
||||||
|
import BlogListBar from './components/BlogListBar'
|
||||||
|
import BlogPostArchive from './components/BlogPostArchive'
|
||||||
|
import BlogPostListPage from './components/BlogPostListPage'
|
||||||
|
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||||
|
import Card from './components/Card'
|
||||||
import FloatDarkModeButton from './components/FloatDarkModeButton'
|
import FloatDarkModeButton from './components/FloatDarkModeButton'
|
||||||
import Footer from './components/Footer'
|
import Footer from './components/Footer'
|
||||||
import JumpToBottomButton from './components/JumpToBottomButton'
|
import JumpToBottomButton from './components/JumpToBottomButton'
|
||||||
import JumpToTopButton from './components/JumpToTopButton'
|
import JumpToTopButton from './components/JumpToTopButton'
|
||||||
import SideAreaLeft from './components/SideAreaLeft'
|
import SideAreaLeft from './components/SideAreaLeft'
|
||||||
import SideAreaRight from './components/SideAreaRight'
|
import SideAreaRight from './components/SideAreaRight'
|
||||||
import TopNav from './components/TopNav'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
import { createContext, useContext, useEffect, useRef, useState } from 'react'
|
|
||||||
import BlogPostListScroll from './components/BlogPostListScroll'
|
|
||||||
import BlogPostListPage from './components/BlogPostListPage'
|
|
||||||
import StickyBar from './components/StickyBar'
|
import StickyBar from './components/StickyBar'
|
||||||
import { isBrowser } from '@/lib/utils'
|
|
||||||
import TocDrawerButton from './components/TocDrawerButton'
|
|
||||||
import TocDrawer from './components/TocDrawer'
|
|
||||||
import { ArticleLock } from './components/ArticleLock'
|
|
||||||
import BlogPostArchive from './components/BlogPostArchive'
|
|
||||||
import TagItem from './components/TagItem'
|
import TagItem from './components/TagItem'
|
||||||
import { useRouter } from 'next/router'
|
import TocDrawer from './components/TocDrawer'
|
||||||
import ArticleDetail from './components/ArticleDetail'
|
import TocDrawerButton from './components/TocDrawerButton'
|
||||||
import Link from 'next/link'
|
import TopNav from './components/TopNav'
|
||||||
import BlogListBar from './components/BlogListBar'
|
import CONFIG from './config'
|
||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
import replaceSearchResult from '@/components/Mark'
|
|
||||||
import { siteConfig } from '@/lib/config'
|
|
||||||
import Announcement from './components/Announcement'
|
|
||||||
import Card from './components/Card'
|
|
||||||
import dynamic from 'next/dynamic'
|
|
||||||
|
|
||||||
const AlgoliaSearchModal = dynamic(() => import('@/components/AlgoliaSearchModal'), { ssr: false })
|
const AlgoliaSearchModal = dynamic(
|
||||||
|
() => import('@/components/AlgoliaSearchModal'),
|
||||||
|
{ ssr: false }
|
||||||
|
)
|
||||||
|
|
||||||
// 主题全局状态
|
// 主题全局状态
|
||||||
const ThemeGlobalNext = createContext()
|
const ThemeGlobalNext = createContext()
|
||||||
@@ -39,7 +42,7 @@ export const useNextGlobal = () => useContext(ThemeGlobalNext)
|
|||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const LayoutBase = (props) => {
|
const LayoutBase = props => {
|
||||||
const { children, headerSlot, rightAreaSlot, post } = props
|
const { children, headerSlot, rightAreaSlot, post } = props
|
||||||
const targetRef = useRef(null)
|
const targetRef = useRef(null)
|
||||||
const floatButtonGroup = useRef(null)
|
const floatButtonGroup = useRef(null)
|
||||||
@@ -50,7 +53,7 @@ const LayoutBase = (props) => {
|
|||||||
const clientHeight = targetRef?.clientHeight
|
const clientHeight = targetRef?.clientHeight
|
||||||
const scrollY = window.pageYOffset
|
const scrollY = window.pageYOffset
|
||||||
const fullHeight = clientHeight - window.outerHeight
|
const fullHeight = clientHeight - window.outerHeight
|
||||||
let per = parseFloat(((scrollY / fullHeight * 100)).toFixed(0))
|
let per = parseFloat(((scrollY / fullHeight) * 100).toFixed(0))
|
||||||
if (per > 100) per = 100
|
if (per > 100) per = 100
|
||||||
const shouldShow = scrollY > 100 && per > 0
|
const shouldShow = scrollY > 100 && per > 0
|
||||||
|
|
||||||
@@ -75,11 +78,15 @@ const LayoutBase = (props) => {
|
|||||||
|
|
||||||
// 悬浮抽屉
|
// 悬浮抽屉
|
||||||
const drawerRight = useRef(null)
|
const drawerRight = useRef(null)
|
||||||
const floatSlot = <div className='block lg:hidden'>
|
const floatSlot = (
|
||||||
<TocDrawerButton onClick={() => {
|
<div className='block lg:hidden'>
|
||||||
drawerRight?.current?.handleSwitchVisible()
|
<TocDrawerButton
|
||||||
}} />
|
onClick={() => {
|
||||||
</div>
|
drawerRight?.current?.handleSwitchVisible()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
const tocRef = isBrowser ? document.getElementById('article-wrapper') : null
|
const tocRef = isBrowser ? document.getElementById('article-wrapper') : null
|
||||||
|
|
||||||
@@ -87,53 +94,77 @@ const LayoutBase = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeGlobalNext.Provider value={{ searchModal }}>
|
<ThemeGlobalNext.Provider value={{ searchModal }}>
|
||||||
<div id='theme-next' className={`${siteConfig('FONT_STYLE')} dark:bg-black scroll-smooth`}>
|
<div
|
||||||
<Style/>
|
id='theme-next'
|
||||||
|
className={`${siteConfig('FONT_STYLE')} dark:bg-black scroll-smooth`}>
|
||||||
|
<Style />
|
||||||
|
|
||||||
{/* 移动端顶部导航栏 */}
|
{/* 移动端顶部导航栏 */}
|
||||||
<TopNav {...props} />
|
<TopNav {...props} />
|
||||||
|
|
||||||
<AlgoliaSearchModal cRef={searchModal} {...props}/>
|
<AlgoliaSearchModal cRef={searchModal} {...props} />
|
||||||
|
|
||||||
<>{headerSlot}</>
|
<>{headerSlot}</>
|
||||||
|
|
||||||
{/* 顶部黑线装饰 */}
|
{/* 顶部黑线装饰 */}
|
||||||
<div className='h-0.5 w-full bg-gray-700 dark:bg-gray-600 hidden lg:block' />
|
<div className='h-0.5 w-full bg-gray-700 dark:bg-gray-600 hidden lg:block' />
|
||||||
|
|
||||||
{/* 主区 */}
|
{/* 主区 */}
|
||||||
<main id='wrapper' className={(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE')) ? 'flex-row-reverse' : '') + ' next relative flex justify-center flex-1 pb-12'}>
|
<main
|
||||||
{/* 左侧栏样式 */}
|
id='wrapper'
|
||||||
<SideAreaLeft targetRef={targetRef} {...props} />
|
className={
|
||||||
|
(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE'))
|
||||||
|
? 'flex-row-reverse'
|
||||||
|
: '') + ' next relative flex justify-center flex-1 pb-12'
|
||||||
|
}>
|
||||||
|
{/* 左侧栏样式 */}
|
||||||
|
<SideAreaLeft targetRef={targetRef} {...props} />
|
||||||
|
|
||||||
{/* 中央内容 */}
|
{/* 中央内容 */}
|
||||||
<section id='container-inner' className={`${siteConfig('NEXT_NAV_TYPE', null, CONFIG) !== 'normal' ? 'mt-24' : ''} lg:max-w-3xl xl:max-w-4xl flex-grow md:mt-0 min-h-screen w-full relative z-10`} ref={targetRef}>
|
<section
|
||||||
{children}
|
id='container-inner'
|
||||||
</section>
|
className={`${siteConfig('NEXT_NAV_TYPE', null, CONFIG) !== 'normal' ? 'mt-24' : ''} lg:max-w-3xl xl:max-w-4xl flex-grow md:mt-0 min-h-screen w-full relative z-10`}
|
||||||
|
ref={targetRef}>
|
||||||
|
{children}
|
||||||
|
</section>
|
||||||
|
|
||||||
{/* 右侧栏样式 */}
|
{/* 右侧栏样式 */}
|
||||||
{siteConfig('NEXT_RIGHT_BAR', null, CONFIG) && <SideAreaRight targetRef={targetRef} slot={rightAreaSlot} {...props} />}
|
{siteConfig('NEXT_RIGHT_BAR', null, CONFIG) && (
|
||||||
|
<SideAreaRight
|
||||||
|
targetRef={targetRef}
|
||||||
|
slot={rightAreaSlot}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</main>
|
||||||
|
|
||||||
</main>
|
{/* 悬浮目录按钮 */}
|
||||||
|
{post && (
|
||||||
|
<div className='block lg:hidden'>
|
||||||
|
<TocDrawer post={post} cRef={drawerRight} targetRef={tocRef} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 悬浮目录按钮 */}
|
{/* 右下角悬浮 */}
|
||||||
{post && <div className='block lg:hidden'>
|
<div
|
||||||
<TocDrawer post={post} cRef={drawerRight} targetRef={tocRef} />
|
ref={floatButtonGroup}
|
||||||
</div>}
|
className='right-8 bottom-12 lg:right-2 fixed justify-end z-20 '>
|
||||||
|
<div
|
||||||
{/* 右下角悬浮 */}
|
className={
|
||||||
<div ref={floatButtonGroup} className='right-8 bottom-12 lg:right-2 fixed justify-end z-20 '>
|
(showRightFloat ? 'animate__animated ' : 'hidden') +
|
||||||
<div className={(showRightFloat ? 'animate__animated ' : 'hidden') + ' animate__fadeInUp rounded-md glassmorphism justify-center duration-500 animate__faster flex space-x-2 items-center cursor-pointer '}>
|
' animate__fadeInUp rounded-md glassmorphism justify-center duration-500 animate__faster flex space-x-2 items-center cursor-pointer '
|
||||||
<JumpToTopButton percent={percent} />
|
}>
|
||||||
<JumpToBottomButton />
|
<JumpToTopButton percent={percent} />
|
||||||
<FloatDarkModeButton />
|
<JumpToBottomButton />
|
||||||
{floatSlot}
|
<FloatDarkModeButton />
|
||||||
</div>
|
{floatSlot}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 页脚 */}
|
|
||||||
<Footer title={siteConfig('TITLE')} />
|
|
||||||
</div>
|
</div>
|
||||||
</ThemeGlobalNext.Provider>
|
|
||||||
|
{/* 页脚 */}
|
||||||
|
<Footer title={siteConfig('TITLE')} />
|
||||||
|
</div>
|
||||||
|
</ThemeGlobalNext.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,21 +174,24 @@ const LayoutBase = (props) => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutIndex = (props) => {
|
const LayoutIndex = props => {
|
||||||
const { notice } = props
|
const { notice } = props
|
||||||
return <>
|
return (
|
||||||
{/* 首页移动端顶部显示公告 */}
|
<>
|
||||||
<Card className='my-2 lg:hidden'>
|
{/* 首页移动端顶部显示公告 */}
|
||||||
<Announcement post={notice} />
|
<Card className='my-2 lg:hidden'>
|
||||||
</Card>
|
<Announcement post={notice} />
|
||||||
|
</Card>
|
||||||
|
|
||||||
<BlogListBar {...props} />
|
<BlogListBar {...props} />
|
||||||
|
|
||||||
{siteConfig('POST_LIST_STYLE') !== 'page'
|
{siteConfig('POST_LIST_STYLE') !== 'page' ? (
|
||||||
? <BlogPostListScroll {...props} showSummary={true} />
|
<BlogPostListScroll {...props} showSummary={true} />
|
||||||
: <BlogPostListPage {...props} />
|
) : (
|
||||||
}
|
<BlogPostListPage {...props} />
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,16 +199,18 @@ const LayoutIndex = (props) => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutPostList = (props) => {
|
const LayoutPostList = props => {
|
||||||
return <>
|
return (
|
||||||
|
<>
|
||||||
|
<BlogListBar {...props} />
|
||||||
|
|
||||||
<BlogListBar {...props} />
|
{siteConfig('POST_LIST_STYLE') !== 'page' ? (
|
||||||
|
<BlogPostListScroll {...props} showSummary={true} />
|
||||||
{siteConfig('POST_LIST_STYLE') !== 'page'
|
) : (
|
||||||
? <BlogPostListScroll {...props} showSummary={true} />
|
<BlogPostListPage {...props} />
|
||||||
: <BlogPostListPage {...props} />
|
)}
|
||||||
}
|
|
||||||
</>
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,7 +218,7 @@ const LayoutPostList = (props) => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutSearch = (props) => {
|
const LayoutSearch = props => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const { posts, keyword } = props
|
const { posts, keyword } = props
|
||||||
|
|
||||||
@@ -200,20 +236,21 @@ const LayoutSearch = (props) => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StickyBar>
|
<StickyBar>
|
||||||
<div className="p-4 dark:text-gray-200">
|
<div className='p-4 dark:text-gray-200'>
|
||||||
<i className="mr-1 fas fa-search" />{' '}
|
<i className='mr-1 fas fa-search' /> {posts?.length}{' '}
|
||||||
{posts?.length} {locale.COMMON.RESULT_OF_SEARCH}
|
{locale.COMMON.RESULT_OF_SEARCH}
|
||||||
</div>
|
</div>
|
||||||
</StickyBar>
|
</StickyBar>
|
||||||
<div className="md:mt-5">
|
<div className='md:mt-5'>
|
||||||
{siteConfig('POST_LIST_STYLE') !== 'page'
|
{siteConfig('POST_LIST_STYLE') !== 'page' ? (
|
||||||
? <BlogPostListScroll {...props} showSummary={true} />
|
<BlogPostListScroll {...props} showSummary={true} />
|
||||||
: <BlogPostListPage {...props} />
|
) : (
|
||||||
}
|
<BlogPostListPage {...props} />
|
||||||
</div>
|
)}
|
||||||
</>
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,16 +273,21 @@ const Layout404 = props => {
|
|||||||
}, 3000)
|
}, 3000)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return <>
|
return (
|
||||||
<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'>
|
<div className='md:-mt-20 text-black w-full h-screen text-center justify-center content-center items-center flex flex-col'>
|
||||||
<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='dark:text-gray-200'>
|
||||||
<div className='inline-block text-left h-32 leading-10 items-center'>
|
<h2 className='inline-block border-r-2 border-gray-600 mr-2 px-3 py-2 align-top'>
|
||||||
<h2 className='m-0 p-0'>页面无法加载,即将返回首页</h2>
|
<i className='mr-2 fas fa-spinner animate-spin' />
|
||||||
</div>
|
404
|
||||||
</div>
|
</h2>
|
||||||
|
<div className='inline-block text-left h-32 leading-10 items-center'>
|
||||||
|
<h2 className='m-0 p-0'>页面无法加载,即将返回首页</h2>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -253,21 +295,21 @@ const Layout404 = props => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutArchive = (props) => {
|
const LayoutArchive = props => {
|
||||||
const { archivePosts } = props
|
const { archivePosts } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mb-10 pb-20 bg-white md:p-12 p-3 dark:bg-hexo-black-gray shadow-md min-h-full">
|
<div className='mb-10 pb-20 bg-white md:p-12 p-3 dark:bg-hexo-black-gray shadow-md min-h-full'>
|
||||||
{Object.keys(archivePosts).map(archiveTitle => (
|
{Object.keys(archivePosts).map(archiveTitle => (
|
||||||
<BlogPostArchive
|
<BlogPostArchive
|
||||||
key={archiveTitle}
|
key={archiveTitle}
|
||||||
posts={archivePosts[archiveTitle]}
|
posts={archivePosts[archiveTitle]}
|
||||||
archiveTitle={archiveTitle}
|
archiveTitle={archiveTitle}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,33 +318,34 @@ const LayoutArchive = (props) => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutSlug = (props) => {
|
const LayoutSlug = props => {
|
||||||
const { post, lock, validPassword } = props
|
const { post, lock, validPassword } = props
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 404
|
// 404
|
||||||
if (!post) {
|
if (!post) {
|
||||||
setTimeout(() => {
|
setTimeout(
|
||||||
if (isBrowser) {
|
() => {
|
||||||
const article = document.getElementById('notion-article')
|
if (isBrowser) {
|
||||||
if (!article) {
|
const article = document.getElementById('notion-article')
|
||||||
router.push('/404').then(() => {
|
if (!article) {
|
||||||
console.warn('找不到页面', router.asPath)
|
router.push('/404').then(() => {
|
||||||
})
|
console.warn('找不到页面', router.asPath)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}, siteConfig('POST_WAITING_TIME_FOR_404') * 1000)
|
siteConfig('POST_WAITING_TIME_FOR_404') * 1000
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}, [post])
|
}, [post])
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{post && !lock && <ArticleDetail {...props} />}
|
||||||
|
|
||||||
{post && !lock && <ArticleDetail {...props} />}
|
{post && lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
</>
|
||||||
{post && lock && <ArticleLock validPassword={validPassword} />}
|
|
||||||
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,33 +354,37 @@ const LayoutSlug = (props) => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutCategoryIndex = (props) => {
|
const LayoutCategoryIndex = props => {
|
||||||
const { allPosts, categoryOptions } = props
|
const { allPosts, categoryOptions } = props
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
return (
|
return (
|
||||||
<div totalPosts={allPosts} {...props}>
|
<div totalPosts={allPosts} {...props}>
|
||||||
<div className='bg-white dark:bg-hexo-black-gray px-10 py-10 shadow h-full'>
|
<div className='bg-white dark:bg-hexo-black-gray px-10 py-10 shadow h-full'>
|
||||||
<div className='dark:text-gray-200 mb-5'>
|
<div className='dark:text-gray-200 mb-5'>
|
||||||
<i className='mr-4 fas faTh' />{locale.COMMON.CATEGORY}:
|
<i className='mr-4 fas faTh' />
|
||||||
</div>
|
{locale.COMMON.CATEGORY}:
|
||||||
<div id='category-list' className='duration-200 flex flex-wrap'>
|
|
||||||
{categoryOptions?.map(category => {
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
key={category.name}
|
|
||||||
href={`/category/${category.name}`}
|
|
||||||
passHref
|
|
||||||
legacyBehavior>
|
|
||||||
<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.name}({category.count})
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id='category-list' className='duration-200 flex flex-wrap'>
|
||||||
|
{categoryOptions?.map(category => {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={category.name}
|
||||||
|
href={`/category/${category.name}`}
|
||||||
|
passHref
|
||||||
|
legacyBehavior>
|
||||||
|
<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.name}({category.count})
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,30 +393,39 @@ const LayoutCategoryIndex = (props) => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutTagIndex = (props) => {
|
const LayoutTagIndex = props => {
|
||||||
const { tagOptions } = props
|
const { tagOptions } = props
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
return <>
|
return (
|
||||||
<div className='bg-white dark:bg-hexo-black-gray 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 className='bg-white dark:bg-hexo-black-gray px-10 py-10 shadow h-full'>
|
||||||
<div id='tags-list' className='duration-200 flex flex-wrap'>
|
<div className='dark:text-gray-200 mb-5'>
|
||||||
{tagOptions.map(tag => {
|
<i className='fas fa-tags mr-4' />
|
||||||
return <div key={tag.name} className='p-2'><TagItem key={tag.name} tag={tag} /></div>
|
{locale.COMMON.TAGS}:
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id='tags-list' className='duration-200 flex flex-wrap'>
|
||||||
|
{tagOptions.map(tag => {
|
||||||
|
return (
|
||||||
|
<div key={tag.name} className='p-2'>
|
||||||
|
<TagItem key={tag.name} tag={tag} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
CONFIG as THEME_CONFIG,
|
|
||||||
LayoutBase,
|
|
||||||
LayoutIndex,
|
|
||||||
LayoutSearch,
|
|
||||||
LayoutArchive,
|
|
||||||
LayoutSlug,
|
|
||||||
Layout404,
|
Layout404,
|
||||||
|
LayoutArchive,
|
||||||
|
LayoutBase,
|
||||||
LayoutCategoryIndex,
|
LayoutCategoryIndex,
|
||||||
|
LayoutIndex,
|
||||||
LayoutPostList,
|
LayoutPostList,
|
||||||
LayoutTagIndex
|
LayoutSearch,
|
||||||
|
LayoutSlug,
|
||||||
|
LayoutTagIndex,
|
||||||
|
CONFIG as THEME_CONFIG
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,35 @@
|
|||||||
import CONFIG from './config'
|
import Comment from '@/components/Comment'
|
||||||
import { createContext, useEffect, useState, useContext, useRef } from 'react'
|
|
||||||
import Nav from './components/Nav'
|
|
||||||
import { Footer } from './components/Footer'
|
|
||||||
import JumpToTopButton from './components/JumpToTopButton'
|
|
||||||
import Live2D from '@/components/Live2D'
|
import Live2D from '@/components/Live2D'
|
||||||
|
import replaceSearchResult from '@/components/Mark'
|
||||||
|
import NotionPage from '@/components/NotionPage'
|
||||||
|
import ShareBar from '@/components/ShareBar'
|
||||||
|
import { siteConfig } from '@/lib/config'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import { deepClone, isBrowser } from '@/lib/utils'
|
||||||
|
import { Transition } from '@headlessui/react'
|
||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { createContext, useContext, useEffect, useRef, useState } from 'react'
|
||||||
import Announcement from './components/Announcement'
|
import Announcement from './components/Announcement'
|
||||||
|
import { ArticleFooter } from './components/ArticleFooter'
|
||||||
|
import { ArticleInfo } from './components/ArticleInfo'
|
||||||
|
import { ArticleLock } from './components/ArticleLock'
|
||||||
|
import BlogArchiveItem from './components/BlogArchiveItem'
|
||||||
|
import BlogListBar from './components/BlogListBar'
|
||||||
import { BlogListPage } from './components/BlogListPage'
|
import { BlogListPage } from './components/BlogListPage'
|
||||||
import { BlogListScroll } from './components/BlogListScroll'
|
import { BlogListScroll } from './components/BlogListScroll'
|
||||||
import { deepClone, isBrowser } from '@/lib/utils'
|
import { Footer } from './components/Footer'
|
||||||
|
import JumpToTopButton from './components/JumpToTopButton'
|
||||||
|
import Nav from './components/Nav'
|
||||||
import SearchNavBar from './components/SearchNavBar'
|
import SearchNavBar from './components/SearchNavBar'
|
||||||
import BlogArchiveItem from './components/BlogArchiveItem'
|
import CONFIG from './config'
|
||||||
import { ArticleLock } from './components/ArticleLock'
|
|
||||||
import NotionPage from '@/components/NotionPage'
|
|
||||||
import { ArticleInfo } from './components/ArticleInfo'
|
|
||||||
import Comment from '@/components/Comment'
|
|
||||||
import { ArticleFooter } from './components/ArticleFooter'
|
|
||||||
import ShareBar from '@/components/ShareBar'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import BlogListBar from './components/BlogListBar'
|
|
||||||
import { Transition } from '@headlessui/react'
|
|
||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
import replaceSearchResult from '@/components/Mark'
|
|
||||||
import { siteConfig } from '@/lib/config'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import dynamic from 'next/dynamic'
|
|
||||||
|
|
||||||
const AlgoliaSearchModal = dynamic(() => import('@/components/AlgoliaSearchModal'), { ssr: false })
|
const AlgoliaSearchModal = dynamic(
|
||||||
|
() => import('@/components/AlgoliaSearchModal'),
|
||||||
|
{ ssr: false }
|
||||||
|
)
|
||||||
|
|
||||||
// 主题全局状态
|
// 主题全局状态
|
||||||
const ThemeGlobalNobelium = createContext()
|
const ThemeGlobalNobelium = createContext()
|
||||||
@@ -45,56 +48,56 @@ const LayoutBase = props => {
|
|||||||
const searchModal = useRef(null)
|
const searchModal = useRef(null)
|
||||||
// 在列表中进行实时过滤
|
// 在列表中进行实时过滤
|
||||||
const [filterKey, setFilterKey] = useState('')
|
const [filterKey, setFilterKey] = useState('')
|
||||||
const topSlot = <BlogListBar {...props}/>
|
const topSlot = <BlogListBar {...props} />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeGlobalNobelium.Provider value={{ searchModal, filterKey, setFilterKey }}>
|
<ThemeGlobalNobelium.Provider
|
||||||
<div id='theme-nobelium' className={`${siteConfig('FONT_STYLE')} nobelium relative dark:text-gray-300 w-full bg-white dark:bg-black min-h-screen flex flex-col scroll-smooth`}>
|
value={{ searchModal, filterKey, setFilterKey }}>
|
||||||
|
<div
|
||||||
|
id='theme-nobelium'
|
||||||
|
className={`${siteConfig('FONT_STYLE')} nobelium relative dark:text-gray-300 w-full bg-white dark:bg-black min-h-screen flex flex-col scroll-smooth`}>
|
||||||
|
<Style />
|
||||||
|
|
||||||
<Style />
|
{/* 顶部导航栏 */}
|
||||||
|
<Nav {...props} />
|
||||||
|
|
||||||
{/* 顶部导航栏 */}
|
{/* 主区 */}
|
||||||
<Nav {...props} />
|
<main
|
||||||
|
id='out-wrapper'
|
||||||
|
className={`relative m-auto flex-grow w-full transition-all ${!fullWidth ? 'max-w-2xl px-4' : 'px-4 md:px-24'}`}>
|
||||||
|
<Transition
|
||||||
|
show={!onLoading}
|
||||||
|
appear={true}
|
||||||
|
enter='transition ease-in-out duration-700 transform order-first'
|
||||||
|
enterFrom='opacity-0 translate-y-16'
|
||||||
|
enterTo='opacity-100'
|
||||||
|
leave='transition ease-in-out duration-300 transform'
|
||||||
|
leaveFrom='opacity-100 translate-y-0'
|
||||||
|
leaveTo='opacity-0 -translate-y-16'
|
||||||
|
unmount={false}>
|
||||||
|
{/* 顶部插槽 */}
|
||||||
|
{topSlot}
|
||||||
|
{children}
|
||||||
|
</Transition>
|
||||||
|
</main>
|
||||||
|
|
||||||
{/* 主区 */}
|
{/* 页脚 */}
|
||||||
<main id='out-wrapper' className={`relative m-auto flex-grow w-full transition-all ${!fullWidth ? 'max-w-2xl px-4' : 'px-4 md:px-24'}`}>
|
<Footer {...props} />
|
||||||
|
|
||||||
<Transition
|
{/* 右下悬浮 */}
|
||||||
show={!onLoading}
|
<div className='fixed right-4 bottom-4'>
|
||||||
appear={true}
|
<JumpToTopButton />
|
||||||
enter="transition ease-in-out duration-700 transform order-first"
|
</div>
|
||||||
enterFrom="opacity-0 translate-y-16"
|
|
||||||
enterTo="opacity-100"
|
|
||||||
leave="transition ease-in-out duration-300 transform"
|
|
||||||
leaveFrom="opacity-100 translate-y-0"
|
|
||||||
leaveTo="opacity-0 -translate-y-16"
|
|
||||||
unmount={false}
|
|
||||||
>
|
|
||||||
{/* 顶部插槽 */}
|
|
||||||
{topSlot}
|
|
||||||
{children}
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
</main>
|
{/* 左下悬浮 */}
|
||||||
|
<div className='bottom-4 -left-14 fixed justify-end z-40'>
|
||||||
|
<Live2D />
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 页脚 */}
|
{/* 搜索框 */}
|
||||||
<Footer {...props} />
|
<AlgoliaSearchModal cRef={searchModal} {...props} />
|
||||||
|
</div>
|
||||||
{/* 右下悬浮 */}
|
</ThemeGlobalNobelium.Provider>
|
||||||
<div className='fixed right-4 bottom-4'>
|
|
||||||
<JumpToTopButton />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 左下悬浮 */}
|
|
||||||
<div className="bottom-4 -left-14 fixed justify-end z-40">
|
|
||||||
<Live2D />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 搜索框 */}
|
|
||||||
<AlgoliaSearchModal cRef={searchModal} {...props}/>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</ThemeGlobalNobelium.Provider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,9 +108,7 @@ const LayoutBase = props => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutIndex = props => {
|
const LayoutIndex = props => {
|
||||||
return (
|
return <LayoutPostList {...props} topSlot={<Announcement {...props} />} />
|
||||||
<LayoutPostList {...props} topSlot={<Announcement {...props} />} />
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -130,11 +131,15 @@ const LayoutPostList = props => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{topSlot}
|
{topSlot}
|
||||||
{tag && <SearchNavBar {...props} />}
|
{tag && <SearchNavBar {...props} />}
|
||||||
{siteConfig('POST_LIST_STYLE') === 'page' ? <BlogListPage {...props} posts={filteredBlogPosts} /> : <BlogListScroll {...props} posts={filteredBlogPosts} />}
|
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
||||||
</>
|
<BlogListPage {...props} posts={filteredBlogPosts} />
|
||||||
|
) : (
|
||||||
|
<BlogListScroll {...props} posts={filteredBlogPosts} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,10 +177,16 @@ const LayoutSearch = props => {
|
|||||||
filteredBlogPosts = deepClone(posts)
|
filteredBlogPosts = deepClone(posts)
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>
|
return (
|
||||||
<SearchNavBar {...props} />
|
<>
|
||||||
{siteConfig('POST_LIST_STYLE') === 'page' ? <BlogListPage {...props} posts={filteredBlogPosts} /> : <BlogListScroll {...props} posts={filteredBlogPosts} />}
|
<SearchNavBar {...props} />
|
||||||
</>
|
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
||||||
|
<BlogListPage {...props} posts={filteredBlogPosts} />
|
||||||
|
) : (
|
||||||
|
<BlogListScroll {...props} posts={filteredBlogPosts} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -186,11 +197,17 @@ const LayoutSearch = props => {
|
|||||||
const LayoutArchive = props => {
|
const LayoutArchive = props => {
|
||||||
const { archivePosts } = props
|
const { archivePosts } = props
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mb-10 pb-20 md:py-12 p-3 min-h-screen w-full">
|
<div className='mb-10 pb-20 md:py-12 p-3 min-h-screen w-full'>
|
||||||
{Object.keys(archivePosts).map(archiveTitle => <BlogArchiveItem key={archiveTitle} archiveTitle={archiveTitle} archivePosts={archivePosts} />)}
|
{Object.keys(archivePosts).map(archiveTitle => (
|
||||||
</div>
|
<BlogArchiveItem
|
||||||
</>
|
key={archiveTitle}
|
||||||
|
archiveTitle={archiveTitle}
|
||||||
|
archivePosts={archivePosts}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,34 +222,39 @@ const LayoutSlug = props => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 404
|
// 404
|
||||||
if (!post) {
|
if (!post) {
|
||||||
setTimeout(() => {
|
setTimeout(
|
||||||
if (isBrowser) {
|
() => {
|
||||||
const article = document.getElementById('notion-article')
|
if (isBrowser) {
|
||||||
if (!article) {
|
const article = document.getElementById('notion-article')
|
||||||
router.push('/404').then(() => {
|
if (!article) {
|
||||||
console.warn('找不到页面', router.asPath)
|
router.push('/404').then(() => {
|
||||||
})
|
console.warn('找不到页面', router.asPath)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}, siteConfig('POST_WAITING_TIME_FOR_404') * 1000)
|
siteConfig('POST_WAITING_TIME_FOR_404') * 1000
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}, [post])
|
}, [post])
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{!lock && (
|
||||||
|
<div className='px-2'>
|
||||||
{!lock && <div id="article-wrapper" className="px-2">
|
<>
|
||||||
<>
|
<ArticleInfo post={post} />
|
||||||
<ArticleInfo post={post} />
|
<div id='article-wrapper'>
|
||||||
<NotionPage post={post} />
|
<NotionPage post={post} />
|
||||||
<ShareBar post={post} />
|
</div>
|
||||||
<Comment frontMatter={post} />
|
<ShareBar post={post} />
|
||||||
<ArticleFooter />
|
<Comment frontMatter={post} />
|
||||||
</>
|
<ArticleFooter />
|
||||||
</div>}
|
</>
|
||||||
|
</div>
|
||||||
</>
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,10 +263,8 @@ const LayoutSlug = props => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const Layout404 = (props) => {
|
const Layout404 = props => {
|
||||||
return <>
|
return <>404 Not found.</>
|
||||||
404 Not found.
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -252,28 +272,31 @@ const Layout404 = (props) => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutCategoryIndex = (props) => {
|
const LayoutCategoryIndex = props => {
|
||||||
const { categoryOptions } = props
|
const { categoryOptions } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div id='category-list' className='duration-200 flex flex-wrap'>
|
<div id='category-list' className='duration-200 flex flex-wrap'>
|
||||||
{categoryOptions?.map(category => {
|
{categoryOptions?.map(category => {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
key={category.name}
|
key={category.name}
|
||||||
href={`/category/${category.name}`}
|
href={`/category/${category.name}`}
|
||||||
passHref
|
passHref
|
||||||
legacyBehavior>
|
legacyBehavior>
|
||||||
<div
|
<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'}>
|
className={
|
||||||
<i className='mr-4 fas fa-folder' />{category.name}({category.count})
|
'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'
|
||||||
</div>
|
}>
|
||||||
</Link>
|
<i className='mr-4 fas fa-folder' />
|
||||||
)
|
{category.name}({category.count})
|
||||||
})}
|
</div>
|
||||||
</div>
|
</Link>
|
||||||
</>
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,37 +305,43 @@ const LayoutCategoryIndex = (props) => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutTagIndex = (props) => {
|
const LayoutTagIndex = props => {
|
||||||
const { tagOptions } = props
|
const { tagOptions } = props
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<div id='tags-list' className='duration-200 flex flex-wrap'>
|
<div id='tags-list' className='duration-200 flex flex-wrap'>
|
||||||
{tagOptions.map(tag => {
|
{tagOptions.map(tag => {
|
||||||
return (
|
return (
|
||||||
<div key={tag.name} className='p-2'>
|
<div key={tag.name} className='p-2'>
|
||||||
<Link key={tag} href={`/tag/${encodeURIComponent(tag.name)}`} passHref
|
<Link
|
||||||
className={`cursor-pointer inline-block rounded hover:bg-gray-500 hover:text-white duration-200 mr-2 py-1 px-2 text-xs whitespace-nowrap dark:hover:text-white text-gray-600 hover:shadow-xl dark:border-gray-400 notion-${tag.color}_background dark:bg-gray-800`}>
|
key={tag}
|
||||||
<div className='font-light dark:text-gray-400'><i className='mr-1 fas fa-tag' /> {tag.name + (tag.count ? `(${tag.count})` : '')} </div>
|
href={`/tag/${encodeURIComponent(tag.name)}`}
|
||||||
</Link>
|
passHref
|
||||||
</div>
|
className={`cursor-pointer inline-block rounded hover:bg-gray-500 hover:text-white duration-200 mr-2 py-1 px-2 text-xs whitespace-nowrap dark:hover:text-white text-gray-600 hover:shadow-xl dark:border-gray-400 notion-${tag.color}_background dark:bg-gray-800`}>
|
||||||
)
|
<div className='font-light dark:text-gray-400'>
|
||||||
})}
|
<i className='mr-1 fas fa-tag' />{' '}
|
||||||
</div>
|
{tag.name + (tag.count ? `(${tag.count})` : '')}{' '}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</Link>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
CONFIG as THEME_CONFIG,
|
|
||||||
LayoutBase,
|
|
||||||
LayoutIndex,
|
|
||||||
LayoutSearch,
|
|
||||||
LayoutArchive,
|
|
||||||
LayoutSlug,
|
|
||||||
Layout404,
|
Layout404,
|
||||||
LayoutPostList,
|
LayoutArchive,
|
||||||
|
LayoutBase,
|
||||||
LayoutCategoryIndex,
|
LayoutCategoryIndex,
|
||||||
LayoutTagIndex
|
LayoutIndex,
|
||||||
|
LayoutPostList,
|
||||||
|
LayoutSearch,
|
||||||
|
LayoutSlug,
|
||||||
|
LayoutTagIndex,
|
||||||
|
CONFIG as THEME_CONFIG
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
import CONFIG from './config'
|
import Comment from '@/components/Comment'
|
||||||
import { createContext, useContext, useEffect, useState } from 'react'
|
import replaceSearchResult from '@/components/Mark'
|
||||||
import Header from './components/Nav'
|
import NotionPage from '@/components/NotionPage'
|
||||||
import { useGlobal } from '@/lib/global'
|
import ShareBar from '@/components/ShareBar'
|
||||||
import { siteConfig } from '@/lib/config'
|
import { siteConfig } from '@/lib/config'
|
||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import { isBrowser } from '@/lib/utils'
|
||||||
|
import { Transition } from '@headlessui/react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { createContext, useContext, useEffect, useState } from 'react'
|
||||||
|
import { ArticleFooter } from './components/ArticleFooter'
|
||||||
|
import { ArticleInfo } from './components/ArticleInfo'
|
||||||
|
import { ArticleLock } from './components/ArticleLock'
|
||||||
|
import BlogArchiveItem from './components/BlogArchiveItem'
|
||||||
import { BlogListPage } from './components/BlogListPage'
|
import { BlogListPage } from './components/BlogListPage'
|
||||||
import { BlogListScroll } from './components/BlogListScroll'
|
import { BlogListScroll } from './components/BlogListScroll'
|
||||||
import { isBrowser } from '@/lib/utils'
|
|
||||||
import SearchNavBar from './components/SearchNavBar'
|
|
||||||
import BlogArchiveItem from './components/BlogArchiveItem'
|
|
||||||
import { ArticleLock } from './components/ArticleLock'
|
|
||||||
import NotionPage from '@/components/NotionPage'
|
|
||||||
import { ArticleInfo } from './components/ArticleInfo'
|
|
||||||
import Comment from '@/components/Comment'
|
|
||||||
import { ArticleFooter } from './components/ArticleFooter'
|
|
||||||
import ShareBar from '@/components/ShareBar'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import { Transition } from '@headlessui/react'
|
|
||||||
import BottomNav from './components/BottomNav'
|
import BottomNav from './components/BottomNav'
|
||||||
import Modal from './components/Modal'
|
import Modal from './components/Modal'
|
||||||
|
import Header from './components/Nav'
|
||||||
|
import SearchNavBar from './components/SearchNavBar'
|
||||||
|
import CONFIG from './config'
|
||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
import replaceSearchResult from '@/components/Mark'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
|
|
||||||
// 主题全局状态
|
// 主题全局状态
|
||||||
const ThemeGlobalPlog = createContext()
|
const ThemeGlobalPlog = createContext()
|
||||||
@@ -52,42 +52,45 @@ const LayoutBase = props => {
|
|||||||
}, [router.events])
|
}, [router.events])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeGlobalPlog.Provider value={{ showModal, setShowModal, modalContent, setModalContent }}>
|
<ThemeGlobalPlog.Provider
|
||||||
<div id='theme-plog' className={`${siteConfig('FONT_STYLE')} plog relative dark:text-gray-300 w-full dark:bg-black min-h-screen scroll-smooth`} >
|
value={{ showModal, setShowModal, modalContent, setModalContent }}>
|
||||||
|
<div
|
||||||
|
id='theme-plog'
|
||||||
|
className={`${siteConfig('FONT_STYLE')} plog relative dark:text-gray-300 w-full dark:bg-black min-h-screen scroll-smooth`}>
|
||||||
|
<Style />
|
||||||
|
|
||||||
<Style/>
|
{/* 移动端顶部导航栏 */}
|
||||||
|
<Header {...props} />
|
||||||
|
|
||||||
{/* 移动端顶部导航栏 */}
|
{/* 主区 */}
|
||||||
<Header {...props} />
|
<main
|
||||||
|
id='out-wrapper'
|
||||||
|
className={
|
||||||
|
'relative m-auto flex-grow w-full transition-all pb-16 pt-16 md:pt-0'
|
||||||
|
}>
|
||||||
|
<Transition
|
||||||
|
show={!onLoading}
|
||||||
|
appear={true}
|
||||||
|
enter='transition ease-in-out duration-700 transform order-first'
|
||||||
|
enterFrom='opacity-0 translate-y-16'
|
||||||
|
enterTo='opacity-100'
|
||||||
|
leave='transition ease-in-out duration-300 transform'
|
||||||
|
leaveFrom='opacity-100 translate-y-0'
|
||||||
|
leaveTo='opacity-0 -translate-y-16'
|
||||||
|
unmount={false}>
|
||||||
|
{/* 顶部插槽 */}
|
||||||
|
{topSlot}
|
||||||
|
{children}
|
||||||
|
</Transition>
|
||||||
|
</main>
|
||||||
|
|
||||||
{/* 主区 */}
|
{/* 弹出框 - 用于放大显示首页图片等作用 */}
|
||||||
<main id='out-wrapper' className={'relative m-auto flex-grow w-full transition-all pb-16 pt-16 md:pt-0'}>
|
<Modal {...props} />
|
||||||
|
|
||||||
<Transition
|
{/* 桌面端底部导航栏 */}
|
||||||
show={!onLoading}
|
<BottomNav {...props} />
|
||||||
appear={true}
|
</div>
|
||||||
enter="transition ease-in-out duration-700 transform order-first"
|
</ThemeGlobalPlog.Provider>
|
||||||
enterFrom="opacity-0 translate-y-16"
|
|
||||||
enterTo="opacity-100"
|
|
||||||
leave="transition ease-in-out duration-300 transform"
|
|
||||||
leaveFrom="opacity-100 translate-y-0"
|
|
||||||
leaveTo="opacity-0 -translate-y-16"
|
|
||||||
unmount={false}
|
|
||||||
>
|
|
||||||
{/* 顶部插槽 */}
|
|
||||||
{topSlot}
|
|
||||||
{children}
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
</main>
|
|
||||||
|
|
||||||
{/* 弹出框 - 用于放大显示首页图片等作用 */}
|
|
||||||
<Modal {...props}/>
|
|
||||||
|
|
||||||
{/* 桌面端底部导航栏 */}
|
|
||||||
<BottomNav {...props} />
|
|
||||||
</div>
|
|
||||||
</ThemeGlobalPlog.Provider >
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,9 +101,7 @@ const LayoutBase = props => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutIndex = props => {
|
const LayoutIndex = props => {
|
||||||
return (
|
return <LayoutPostList {...props} />
|
||||||
<LayoutPostList {...props} />
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -110,9 +111,13 @@ const LayoutIndex = props => {
|
|||||||
*/
|
*/
|
||||||
const LayoutPostList = props => {
|
const LayoutPostList = props => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{siteConfig('POST_LIST_STYLE') === 'page' ? <BlogListPage {...props} /> : <BlogListScroll {...props} />}
|
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
||||||
</>
|
<BlogListPage {...props} />
|
||||||
|
) : (
|
||||||
|
<BlogListScroll {...props} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,11 +154,17 @@ const LayoutSearch = props => {
|
|||||||
const LayoutArchive = props => {
|
const LayoutArchive = props => {
|
||||||
const { archivePosts } = props
|
const { archivePosts } = props
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mb-10 pb-20 md:py-12 p-3 min-h-screen w-full">
|
<div className='mb-10 pb-20 md:py-12 p-3 min-h-screen w-full'>
|
||||||
{Object.keys(archivePosts).map(archiveTitle => <BlogArchiveItem key={archiveTitle} archiveTitle={archiveTitle} archivePosts={archivePosts} />)}
|
{Object.keys(archivePosts).map(archiveTitle => (
|
||||||
</div>
|
<BlogArchiveItem
|
||||||
</>
|
key={archiveTitle}
|
||||||
|
archiveTitle={archiveTitle}
|
||||||
|
archivePosts={archivePosts}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,34 +179,39 @@ const LayoutSlug = props => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 404
|
// 404
|
||||||
if (!post) {
|
if (!post) {
|
||||||
setTimeout(() => {
|
setTimeout(
|
||||||
if (isBrowser) {
|
() => {
|
||||||
const article = document.getElementById('notion-article')
|
if (isBrowser) {
|
||||||
if (!article) {
|
const article = document.getElementById('notion-article')
|
||||||
router.push('/404').then(() => {
|
if (!article) {
|
||||||
console.warn('找不到页面', router.asPath)
|
router.push('/404').then(() => {
|
||||||
})
|
console.warn('找不到页面', router.asPath)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}, siteConfig('POST_WAITING_TIME_FOR_404') * 1000)
|
siteConfig('POST_WAITING_TIME_FOR_404') * 1000
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}, [post])
|
}, [post])
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{!lock && (
|
||||||
|
<div className='px-2 my-16 max-w-6xl mx-auto'>
|
||||||
{!lock && <div id="article-wrapper" className="px-2 my-16 max-w-6xl mx-auto">
|
<>
|
||||||
<>
|
<ArticleInfo post={post} />
|
||||||
<ArticleInfo post={post} />
|
<div id='article-wrapper'>
|
||||||
<NotionPage post={post} />
|
<NotionPage post={post} />
|
||||||
<ShareBar post={post} />
|
</div>
|
||||||
<Comment frontMatter={post} />
|
<ShareBar post={post} />
|
||||||
<ArticleFooter />
|
<Comment frontMatter={post} />
|
||||||
</>
|
<ArticleFooter />
|
||||||
</div>}
|
</>
|
||||||
|
</div>
|
||||||
</>
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,10 +220,8 @@ const LayoutSlug = props => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const Layout404 = (props) => {
|
const Layout404 = props => {
|
||||||
return <>
|
return <>404 Not found.</>
|
||||||
404 Not found.
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -215,28 +229,31 @@ const Layout404 = (props) => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutCategoryIndex = (props) => {
|
const LayoutCategoryIndex = props => {
|
||||||
const { categoryOptions } = props
|
const { categoryOptions } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div id='category-list' className='duration-200 flex flex-wrap'>
|
<div id='category-list' className='duration-200 flex flex-wrap'>
|
||||||
{categoryOptions?.map(category => {
|
{categoryOptions?.map(category => {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
key={category.name}
|
key={category.name}
|
||||||
href={`/category/${category.name}`}
|
href={`/category/${category.name}`}
|
||||||
passHref
|
passHref
|
||||||
legacyBehavior>
|
legacyBehavior>
|
||||||
<div
|
<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'}>
|
className={
|
||||||
<i className='mr-4 fas fa-folder' />{category.name}({category.count})
|
'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'
|
||||||
</div>
|
}>
|
||||||
</Link>
|
<i className='mr-4 fas fa-folder' />
|
||||||
)
|
{category.name}({category.count})
|
||||||
})}
|
</div>
|
||||||
</div>
|
</Link>
|
||||||
</>
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,37 +262,43 @@ const LayoutCategoryIndex = (props) => {
|
|||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutTagIndex = (props) => {
|
const LayoutTagIndex = props => {
|
||||||
const { tagOptions } = props
|
const { tagOptions } = props
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<div id='tags-list' className='duration-200 flex flex-wrap'>
|
<div id='tags-list' className='duration-200 flex flex-wrap'>
|
||||||
{tagOptions.map(tag => {
|
{tagOptions.map(tag => {
|
||||||
return (
|
return (
|
||||||
<div key={tag.name} className='p-2'>
|
<div key={tag.name} className='p-2'>
|
||||||
<Link key={tag} href={`/tag/${encodeURIComponent(tag.name)}`} passHref
|
<Link
|
||||||
className={`cursor-pointer inline-block rounded hover:bg-gray-500 hover:text-white duration-200 mr-2 py-1 px-2 text-xs whitespace-nowrap dark:hover:text-white text-gray-600 hover:shadow-xl dark:border-gray-400 notion-${tag.color}_background dark:bg-gray-800`}>
|
key={tag}
|
||||||
<div className='font-light dark:text-gray-400'><i className='mr-1 fas fa-tag' /> {tag.name + (tag.count ? `(${tag.count})` : '')} </div>
|
href={`/tag/${encodeURIComponent(tag.name)}`}
|
||||||
</Link>
|
passHref
|
||||||
</div>
|
className={`cursor-pointer inline-block rounded hover:bg-gray-500 hover:text-white duration-200 mr-2 py-1 px-2 text-xs whitespace-nowrap dark:hover:text-white text-gray-600 hover:shadow-xl dark:border-gray-400 notion-${tag.color}_background dark:bg-gray-800`}>
|
||||||
)
|
<div className='font-light dark:text-gray-400'>
|
||||||
})}
|
<i className='mr-1 fas fa-tag' />{' '}
|
||||||
</div>
|
{tag.name + (tag.count ? `(${tag.count})` : '')}{' '}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</Link>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
CONFIG as THEME_CONFIG,
|
|
||||||
LayoutBase,
|
|
||||||
LayoutIndex,
|
|
||||||
LayoutSearch,
|
|
||||||
LayoutArchive,
|
|
||||||
LayoutSlug,
|
|
||||||
Layout404,
|
Layout404,
|
||||||
LayoutPostList,
|
LayoutArchive,
|
||||||
|
LayoutBase,
|
||||||
LayoutCategoryIndex,
|
LayoutCategoryIndex,
|
||||||
LayoutTagIndex
|
LayoutIndex,
|
||||||
|
LayoutPostList,
|
||||||
|
LayoutSearch,
|
||||||
|
LayoutSlug,
|
||||||
|
LayoutTagIndex,
|
||||||
|
CONFIG as THEME_CONFIG
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,9 +224,7 @@ const LayoutSlug = props => {
|
|||||||
<>
|
<>
|
||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
<div
|
<div className={`px-2 ${fullWidth ? '' : 'xl:max-w-4xl 2xl:max-w-6xl'}`}>
|
||||||
id='article-wrapper'
|
|
||||||
className={`px-2 ${fullWidth ? '' : 'xl:max-w-4xl 2xl:max-w-6xl'}`}>
|
|
||||||
{/* 文章信息 */}
|
{/* 文章信息 */}
|
||||||
<ArticleInfo post={post} />
|
<ArticleInfo post={post} />
|
||||||
|
|
||||||
@@ -234,8 +232,10 @@ const LayoutSlug = props => {
|
|||||||
{/* <AdSlot type={'in-article'} /> */}
|
{/* <AdSlot type={'in-article'} /> */}
|
||||||
<WWAds orientation='horizontal' className='w-full' />
|
<WWAds orientation='horizontal' className='w-full' />
|
||||||
|
|
||||||
{/* Notion文章主体 */}
|
<div id='article-wrapper'>
|
||||||
{!lock && <NotionPage post={post} />}
|
{/* Notion文章主体 */}
|
||||||
|
{!lock && <NotionPage post={post} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 分享 */}
|
{/* 分享 */}
|
||||||
<ShareBar post={post} />
|
<ShareBar post={post} />
|
||||||
|
|||||||
@@ -143,8 +143,8 @@ const LayoutSlug = props => {
|
|||||||
<Banner title={post?.title} description={post?.summary} />
|
<Banner title={post?.title} description={post?.summary} />
|
||||||
<div className='container grow'>
|
<div className='container grow'>
|
||||||
<div className='flex flex-wrap justify-center -mx-4'>
|
<div className='flex flex-wrap justify-center -mx-4'>
|
||||||
<div className='w-full p-4'>
|
<div id='container-inner' className='w-full p-4'>
|
||||||
<div id='container-inner' className='mx-auto'>
|
<div id='article-wrapper' className='mx-auto'>
|
||||||
<NotionPage {...props} />
|
<NotionPage {...props} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user