mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-13 23:16:47 +00:00
初步完成HEXO主题
This commit is contained in:
@@ -6,6 +6,6 @@ import { Layout404 } from '@/themes'
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
export default function Custom404 () {
|
||||
return <Layout404 />
|
||||
export default function Custom404 (props) {
|
||||
return <Layout404 {...props}/>
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { LayoutSlug } from '@/themes'
|
||||
*/
|
||||
const About = (props) => {
|
||||
if (!props.post) {
|
||||
return <Custom404 />
|
||||
return <Custom404 {...props} />
|
||||
}
|
||||
return <LayoutSlug {...props} />
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import Custom404 from '@/pages/404'
|
||||
*/
|
||||
const Slug = (props) => {
|
||||
if (!props.post) {
|
||||
return <Custom404 />
|
||||
return <Custom404 {...props} />
|
||||
}
|
||||
return <LayoutSlug {...props} />
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import Custom404 from '@/pages/404'
|
||||
|
||||
const Page = (props) => {
|
||||
if (!props?.meta) {
|
||||
return <Custom404 />
|
||||
return <Custom404 {...props} />
|
||||
}
|
||||
return <LayoutPage {...props} />
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export async function getStaticProps ({ params }) {
|
||||
latestPosts
|
||||
} = await getGlobalNotionData({
|
||||
from,
|
||||
includePage: true,
|
||||
includePage: false,
|
||||
tagsCount: 0
|
||||
})
|
||||
const filteredPosts = allPosts.filter(
|
||||
|
||||
@@ -1,6 +1,39 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { faSpinner } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const Layout404 = () => {
|
||||
return <div>
|
||||
404 Not found.
|
||||
</div>
|
||||
export const Layout404 = props => {
|
||||
const router = useRouter()
|
||||
useEffect(() => {
|
||||
// 延时3秒如果加载失败就返回首页
|
||||
setTimeout(() => {
|
||||
if (window) {
|
||||
const article = document.getElementById('container')
|
||||
if (!article) {
|
||||
router.push('/').then(() => {
|
||||
console.log('找不到页面', router.asPath)
|
||||
})
|
||||
}
|
||||
}
|
||||
}, 3000)
|
||||
})
|
||||
|
||||
return (
|
||||
<LayoutBase {...props} meta={{ title: `${BLOG.TITLE} | 页面找不到啦` }}>
|
||||
<div className="text-black w-full h-screen text-center justify-center content-center items-center flex flex-col">
|
||||
<div className="dark:text-gray-200">
|
||||
<h2 className="inline-block border-r-2 border-gray-600 mr-2 px-3 py-2 align-top">
|
||||
<FontAwesomeIcon icon={faSpinner} spin={true} className="mr-2" />
|
||||
404
|
||||
</h2>
|
||||
<div className="inline-block text-left h-32 leading-10 items-center">
|
||||
<h2 className="m-0 p-0">页面无法加载,即将返回首页</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LayoutBase>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,64 @@
|
||||
export const LayoutArchive = ({ posts, tags, categories, postCount }) => {
|
||||
return <div>
|
||||
Archive Page
|
||||
</div>
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useEffect } from 'react'
|
||||
import BlogPostArchive from './components/BlogPostArchive'
|
||||
import Card from './components/Card'
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutArchive = (props) => {
|
||||
const { posts } = props
|
||||
const { locale } = useGlobal()
|
||||
// 深拷贝
|
||||
const postsSortByDate = Object.create(posts)
|
||||
|
||||
// 时间排序
|
||||
postsSortByDate.sort((a, b) => {
|
||||
const dateA = new Date(a?.date.start_date || a.createdTime)
|
||||
const dateB = new Date(b?.date.start_date || b.createdTime)
|
||||
return dateB - dateA
|
||||
})
|
||||
|
||||
const meta = {
|
||||
title: `${locale.NAV.ARCHIVE} | ${BLOG.TITLE}`,
|
||||
description: BLOG.DESCRIPTION,
|
||||
type: 'website'
|
||||
}
|
||||
|
||||
const archivePosts = {}
|
||||
|
||||
postsSortByDate.forEach(post => {
|
||||
const date = post.date.start_date.slice(0, 7)
|
||||
if (archivePosts[date]) {
|
||||
archivePosts[date].push(post)
|
||||
} else {
|
||||
archivePosts[date] = [post]
|
||||
}
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (window) {
|
||||
const anchor = window.location.hash
|
||||
if (anchor) {
|
||||
setTimeout(() => {
|
||||
const anchorElement = document.getElementById(anchor.substring(1))
|
||||
if (anchorElement) {
|
||||
anchorElement.scrollIntoView({ block: 'start', behavior: 'smooth' })
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
return <LayoutBase {...props} meta={meta}>
|
||||
<Card className='w-full'>
|
||||
<div className="mb-10 pb-20 bg-white md:p-12 p-3 dark:bg-gray-800 min-h-full">
|
||||
{Object.keys(archivePosts).map(archiveTitle => (
|
||||
<BlogPostArchive
|
||||
key={archiveTitle}
|
||||
posts={archivePosts[archiveTitle]}
|
||||
archiveTitle={archiveTitle}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
</LayoutBase>
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
export const LayoutCategory = ({ tags, posts, category, categories, latestPosts, postCount }) => {
|
||||
return <div>
|
||||
Category - {category}
|
||||
</div>
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutCategory = (props) => {
|
||||
const { tags, posts, category } = props
|
||||
const { locale } = useGlobal()
|
||||
const meta = {
|
||||
title: `${category} | ${locale.COMMON.CATEGORY} | ${BLOG.TITLE}`,
|
||||
description: BLOG.DESCRIPTION,
|
||||
type: 'website'
|
||||
}
|
||||
return <LayoutBase {...props} meta={meta}>
|
||||
<BlogPostListScroll posts={posts} tags={tags} currentCategory={category}/>
|
||||
</LayoutBase>
|
||||
}
|
||||
|
||||
@@ -1,11 +1,43 @@
|
||||
export const LayoutCategoryIndex = ({
|
||||
tags,
|
||||
allPosts,
|
||||
categories,
|
||||
postCount,
|
||||
latestPosts
|
||||
}) => {
|
||||
return <div>
|
||||
CategoryIndex
|
||||
</div>
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { faFolder, faTh } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import Link from 'next/link'
|
||||
import Card from './components/Card'
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutCategoryIndex = props => {
|
||||
const { categories } = props
|
||||
const { locale } = useGlobal()
|
||||
const meta = {
|
||||
title: `${locale.COMMON.CATEGORY} | ${BLOG.TITLE}`,
|
||||
description: BLOG.DESCRIPTION,
|
||||
type: 'website'
|
||||
}
|
||||
return (
|
||||
<LayoutBase {...props} meta={meta}>
|
||||
<Card className="bg-white dark:bg-gray-700 w-full min-h-screen">
|
||||
<div className="dark:text-gray-200 mb-5 mx-3">
|
||||
<FontAwesomeIcon icon={faTh} className="mr-4" />
|
||||
{locale.COMMON.CATEGORY}:
|
||||
</div>
|
||||
<div id="category-list" className="duration-200 flex flex-wrap mx-8">
|
||||
{Object.keys(categories).map(category => {
|
||||
return (
|
||||
<Link key={category} href={`/category/${category}`} passHref>
|
||||
<div
|
||||
className={
|
||||
' duration-300 dark:hover:text-white rounded-lg px-5 cursor-pointer py-2 hover:bg-blue-600 hover:text-white'
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon icon={faFolder} className="mr-4" />
|
||||
{category}({categories[category]})
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</Card>
|
||||
</LayoutBase>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
export const LayoutPage = ({ page, posts, tags, meta, categories, postCount, latestPosts }) => {
|
||||
return <div>
|
||||
Page - {page}
|
||||
</div>
|
||||
import BlogPostListPage from './components/BlogPostListPage'
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutPage = (props) => {
|
||||
const { page, posts, postCount } = props
|
||||
return <LayoutBase {...props}>
|
||||
<BlogPostListPage page={page} posts={posts} postCount={postCount} />
|
||||
</LayoutBase>
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useRouter } from 'next/router'
|
||||
import BlogPostListPage from './components/BlogPostListPage'
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutSearch = ({
|
||||
posts,
|
||||
tags,
|
||||
categories,
|
||||
postCount
|
||||
}) => {
|
||||
export const LayoutSearch = (props) => {
|
||||
const { posts } = props
|
||||
let filteredPosts
|
||||
const searchKey = getSearchKey()
|
||||
if (searchKey) {
|
||||
@@ -18,11 +18,15 @@ export const LayoutSearch = ({
|
||||
filteredPosts = posts
|
||||
}
|
||||
|
||||
console.log(filteredPosts)
|
||||
|
||||
return <div>
|
||||
Search {searchKey}
|
||||
</div>
|
||||
const { locale } = useGlobal()
|
||||
const meta = {
|
||||
title: `${searchKey || ''} | ${locale.NAV.SEARCH} | ${BLOG.TITLE} `,
|
||||
description: BLOG.DESCRIPTION,
|
||||
type: 'website'
|
||||
}
|
||||
return <LayoutBase {...props} meta={meta}>
|
||||
<BlogPostListPage {...props} posts={filteredPosts}/>
|
||||
</LayoutBase>
|
||||
}
|
||||
|
||||
function getSearchKey () {
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
export const LayoutTag = ({ tags, posts, tag, categories, postCount, latestPosts }) => {
|
||||
return <div>
|
||||
Tag - {tag}
|
||||
</div>
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutTag = (props) => {
|
||||
const { tags, posts, tag } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
const meta = {
|
||||
title: `${tag} | ${locale.COMMON.TAGS} | ${BLOG.TITLE}`,
|
||||
description: BLOG.DESCRIPTION,
|
||||
type: 'website'
|
||||
}
|
||||
|
||||
return <LayoutBase {...props} meta={meta}>
|
||||
<BlogPostListScroll posts={posts} tags={tags} currentTag={tag}/>
|
||||
</LayoutBase>
|
||||
}
|
||||
|
||||
@@ -1,5 +1,36 @@
|
||||
export const LayoutTagIndex = ({ tags, categories, postCount, latestPosts }) => {
|
||||
return <div>
|
||||
TagIndex
|
||||
</div>
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { faTag } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import Card from './components/Card'
|
||||
import TagItemMini from './components/TagItemMini'
|
||||
import LayoutBase from './LayoutBase'
|
||||
|
||||
export const LayoutTagIndex = props => {
|
||||
const { tags } = props
|
||||
const { locale } = useGlobal()
|
||||
const meta = {
|
||||
title: `${locale.COMMON.TAGS} | ${BLOG.TITLE}`,
|
||||
description: BLOG.DESCRIPTION,
|
||||
type: 'website'
|
||||
}
|
||||
return (
|
||||
<LayoutBase {...props} meta={meta}>
|
||||
<Card className='w-full'>
|
||||
<div className="dark:text-gray-200 mb-5 ml-4">
|
||||
<FontAwesomeIcon icon={faTag} className="mr-4" />
|
||||
{locale.COMMON.TAGS}:
|
||||
</div>
|
||||
<div id="tags-list" className="duration-200 flex flex-wrap ml-8">
|
||||
{tags.map(tag => {
|
||||
return (
|
||||
<div key={tag.name} className="px-2">
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</Card>
|
||||
</LayoutBase>
|
||||
)
|
||||
}
|
||||
|
||||
32
themes/Hexo/components/BlogPostArchive.js
Normal file
32
themes/Hexo/components/BlogPostArchive.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import BLOG from '@/blog.config'
|
||||
/**
|
||||
* 博客归档列表
|
||||
* @param posts 所有文章
|
||||
* @param archiveTitle 归档标题
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const BlogPostArchive = ({ posts = [], archiveTitle }) => {
|
||||
if (!posts || posts.length === 0) {
|
||||
return <></>
|
||||
} else {
|
||||
return <div>
|
||||
<div className='pt-16 pb-4 text-3xl dark:text-gray-300' id={archiveTitle}>{archiveTitle}</div>
|
||||
<ul>
|
||||
{posts.map(post => (
|
||||
<li key={post.id} className='border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500'>
|
||||
<div id={post?.date?.start_date}><span className='text-gray-400'>{post.date.start_date}</span>
|
||||
<Link href={`${BLOG.PATH}/article/${post.slug}`} passHref>
|
||||
<a className='dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600'>{post.title}</a>
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
export default BlogPostArchive
|
||||
88
themes/Hexo/components/BlogPostListScroll.js
Normal file
88
themes/Hexo/components/BlogPostListScroll.js
Normal file
@@ -0,0 +1,88 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import BlogPostCard from './BlogPostCard'
|
||||
import BlogPostListEmpty from './BlogPostListEmpty'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import throttle from 'lodash.throttle'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import CONFIG_HEXO from '../config_hexo'
|
||||
|
||||
/**
|
||||
* 博客列表滚动分页
|
||||
* @param posts 所有文章
|
||||
* @param tags 所有标签
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_HEXO.POST_LIST_SUMMARY }) => {
|
||||
const postsPerPage = BLOG.POSTS_PER_PAGE
|
||||
const [page, updatePage] = useState(1)
|
||||
const postsToShow = getPostByPage(page, posts, postsPerPage)
|
||||
|
||||
let hasMore = false
|
||||
if (posts) {
|
||||
const totalCount = posts.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 { locale } = useGlobal()
|
||||
|
||||
if (!postsToShow || postsToShow.length === 0) {
|
||||
return <BlogPostListEmpty currentSearch={currentSearch} />
|
||||
} else {
|
||||
return <div id='container' ref={targetRef} className='w-full'>
|
||||
|
||||
{/* 文章列表 */}
|
||||
<div className='flex flex-wrap space-y-1 lg:space-y-4'>
|
||||
{postsToShow.map(post => (
|
||||
<BlogPostCard key={post.id} post={post} showSummary={showSummary}/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div onClick={() => {
|
||||
handleGetMore()
|
||||
}}
|
||||
className='w-full my-4 py-4 text-center cursor-pointer glassmorphism shadow-xl rounded-xl dark:text-gray-200'
|
||||
> {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`} </div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取从第1页到指定页码的文章
|
||||
* @param page 第几页
|
||||
* @param totalPosts 所有文章
|
||||
* @param postsPerPage 每页文章数量
|
||||
* @returns {*}
|
||||
*/
|
||||
const getPostByPage = function (page, totalPosts, postsPerPage) {
|
||||
return totalPosts.slice(
|
||||
0,
|
||||
postsPerPage * page
|
||||
)
|
||||
}
|
||||
export default BlogPostListScroll
|
||||
@@ -4,6 +4,9 @@ import Link from 'next/link'
|
||||
import React from 'react'
|
||||
|
||||
const CategoryGroup = ({ currentCategory, categories }) => {
|
||||
if (!categories) {
|
||||
return <></>
|
||||
}
|
||||
return <>
|
||||
<div id='category-list' className='dark:border-gray-600 flex flex-wrap font-sans mx-4'>
|
||||
{Object.keys(categories).map(category => {
|
||||
|
||||
@@ -27,7 +27,7 @@ const PaginationNumber = ({ page, totalPage }) => {
|
||||
} } passHref >
|
||||
<div
|
||||
rel='prev'
|
||||
className={`${currentPage === 1 ? 'invisible' : 'block'} border-white dark:border-gray-700 hover:border-gray-400 dark:hover:border-gray-400 w-6 text-center cursor-pointer duration-200 hover:font-bold`}
|
||||
className={`${currentPage === 1 ? 'invisible' : 'block'} border-white dark:border-blue-700 hover:border-blue-400 dark:hover:border-blue-400 w-6 text-center cursor-pointer duration-200 hover:font-bold`}
|
||||
>
|
||||
<FontAwesomeIcon icon={faAngleLeft}/>
|
||||
</div>
|
||||
@@ -39,7 +39,7 @@ const PaginationNumber = ({ page, totalPage }) => {
|
||||
<Link href={ { pathname: `/page/${currentPage + 1}`, query: router.query.s ? { s: router.query.s } : {} } } passHref>
|
||||
<div
|
||||
rel='next'
|
||||
className={`${+showNext ? 'block' : 'invisible'} border-t-2 border-white dark:border-gray-700 hover:border-gray-400 dark:hover:border-gray-400 w-6 text-center cursor-pointer duration-500 hover:font-bold`}
|
||||
className={`${+showNext ? 'block' : 'invisible'} border-t-2 border-white dark:border-blue-700 hover:border-blue-400 dark:hover:border-blue-400 w-6 text-center cursor-pointer duration-500 hover:font-bold`}
|
||||
>
|
||||
<FontAwesomeIcon icon={faAngleRight}/>
|
||||
</div>
|
||||
@@ -50,8 +50,8 @@ const PaginationNumber = ({ page, totalPage }) => {
|
||||
|
||||
function getPageElement (page, currentPage) {
|
||||
return <Link href={page === 1 ? '/' : `/page/${page}`} key={page} passHref>
|
||||
<a className={(page + '' === currentPage + '' ? 'font-bold bg-blue-500 dark:bg-blue-400 text-white ' : 'border-t-2 duration-500 border-white hover:border-gray-400 ') +
|
||||
' border-white dark:border-gray-700 dark:hover:border-gray-400 cursor-pointer w-6 text-center font-light hover:font-bold'}>
|
||||
<a className={(page + '' === currentPage + '' ? 'font-bold bg-blue-500 dark:bg-blue-400 text-white ' : 'border-t-2 duration-500 border-white hover:border-blue-400 ') +
|
||||
' border-white dark:border-blue-700 dark:hover:border-blue-400 cursor-pointer w-6 text-center font-light hover:font-bold'}>
|
||||
{page}
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
@@ -10,6 +10,7 @@ const CONFIG_HEXO = {
|
||||
MENU_ARCHIVE: true, // 显示归档
|
||||
MENU_SEARCH: true, // 显示搜索
|
||||
|
||||
POST_LIST_COVER: true // 文章封面
|
||||
POST_LIST_COVER: true, // 文章封面
|
||||
POST_LIST_SUMMARY: true // 文章摘要
|
||||
}
|
||||
export default CONFIG_HEXO
|
||||
|
||||
Reference in New Issue
Block a user