diff --git a/pages/404.js b/pages/404.js index 7fd48806..64cc2dc7 100644 --- a/pages/404.js +++ b/pages/404.js @@ -6,6 +6,6 @@ import { Layout404 } from '@/themes' * @constructor */ -export default function Custom404 () { - return +export default function Custom404 (props) { + return } diff --git a/pages/about.js b/pages/about.js index af382f68..895a94f3 100644 --- a/pages/about.js +++ b/pages/about.js @@ -11,7 +11,7 @@ import { LayoutSlug } from '@/themes' */ const About = (props) => { if (!props.post) { - return + return } return } diff --git a/pages/article/[slug].js b/pages/article/[slug].js index 7903994f..bdc6d706 100644 --- a/pages/article/[slug].js +++ b/pages/article/[slug].js @@ -11,7 +11,7 @@ import Custom404 from '@/pages/404' */ const Slug = (props) => { if (!props.post) { - return + return } return } diff --git a/pages/page/[page].js b/pages/page/[page].js index 2c5cadf0..43d16aec 100644 --- a/pages/page/[page].js +++ b/pages/page/[page].js @@ -6,7 +6,7 @@ import Custom404 from '@/pages/404' const Page = (props) => { if (!props?.meta) { - return + return } return } diff --git a/pages/tag/[tag].js b/pages/tag/[tag].js index 908c329d..2c72b75f 100644 --- a/pages/tag/[tag].js +++ b/pages/tag/[tag].js @@ -16,7 +16,7 @@ export async function getStaticProps ({ params }) { latestPosts } = await getGlobalNotionData({ from, - includePage: true, + includePage: false, tagsCount: 0 }) const filteredPosts = allPosts.filter( diff --git a/themes/Hexo/Layout404.js b/themes/Hexo/Layout404.js index cd28a607..a1dfe4cd 100644 --- a/themes/Hexo/Layout404.js +++ b/themes/Hexo/Layout404.js @@ -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
- 404 Not found. -
+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 ( + +
+
+

+ + 404 +

+
+

页面无法加载,即将返回首页

+
+
+
+
+ ) } diff --git a/themes/Hexo/LayoutArchive.js b/themes/Hexo/LayoutArchive.js index e46502fc..a42b7993 100644 --- a/themes/Hexo/LayoutArchive.js +++ b/themes/Hexo/LayoutArchive.js @@ -1,5 +1,64 @@ -export const LayoutArchive = ({ posts, tags, categories, postCount }) => { - return
- Archive Page -
+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 + +
+ {Object.keys(archivePosts).map(archiveTitle => ( + + ))} +
+
+
} diff --git a/themes/Hexo/LayoutCategory.js b/themes/Hexo/LayoutCategory.js index 60e0d11a..92c5a8a1 100644 --- a/themes/Hexo/LayoutCategory.js +++ b/themes/Hexo/LayoutCategory.js @@ -1,5 +1,17 @@ -export const LayoutCategory = ({ tags, posts, category, categories, latestPosts, postCount }) => { - return
- Category - {category} -
+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 + + } diff --git a/themes/Hexo/LayoutCategoryIndex.js b/themes/Hexo/LayoutCategoryIndex.js index 467bde1c..f0d1e978 100644 --- a/themes/Hexo/LayoutCategoryIndex.js +++ b/themes/Hexo/LayoutCategoryIndex.js @@ -1,11 +1,43 @@ -export const LayoutCategoryIndex = ({ - tags, - allPosts, - categories, - postCount, - latestPosts -}) => { - return
- CategoryIndex -
+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 ( + + +
+ + {locale.COMMON.CATEGORY}: +
+
+ {Object.keys(categories).map(category => { + return ( + +
+ + {category}({categories[category]}) +
+ + ) + })} +
+
+
+ ) } diff --git a/themes/Hexo/LayoutPage.js b/themes/Hexo/LayoutPage.js index c025ff3c..d4ab7079 100644 --- a/themes/Hexo/LayoutPage.js +++ b/themes/Hexo/LayoutPage.js @@ -1,5 +1,9 @@ -export const LayoutPage = ({ page, posts, tags, meta, categories, postCount, latestPosts }) => { - return
- Page - {page} -
+import BlogPostListPage from './components/BlogPostListPage' +import LayoutBase from './LayoutBase' + +export const LayoutPage = (props) => { + const { page, posts, postCount } = props + return + + } diff --git a/themes/Hexo/LayoutSearch.js b/themes/Hexo/LayoutSearch.js index 76e4867c..77be5014 100644 --- a/themes/Hexo/LayoutSearch.js +++ b/themes/Hexo/LayoutSearch.js @@ -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
- Search {searchKey} -
+ const { locale } = useGlobal() + const meta = { + title: `${searchKey || ''} | ${locale.NAV.SEARCH} | ${BLOG.TITLE} `, + description: BLOG.DESCRIPTION, + type: 'website' + } + return + + } function getSearchKey () { diff --git a/themes/Hexo/LayoutTag.js b/themes/Hexo/LayoutTag.js index cae36f38..7f6249e1 100644 --- a/themes/Hexo/LayoutTag.js +++ b/themes/Hexo/LayoutTag.js @@ -1,5 +1,19 @@ -export const LayoutTag = ({ tags, posts, tag, categories, postCount, latestPosts }) => { - return
- Tag - {tag} -
+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 + + } diff --git a/themes/Hexo/LayoutTagIndex.js b/themes/Hexo/LayoutTagIndex.js index deb76f8e..879fe696 100644 --- a/themes/Hexo/LayoutTagIndex.js +++ b/themes/Hexo/LayoutTagIndex.js @@ -1,5 +1,36 @@ -export const LayoutTagIndex = ({ tags, categories, postCount, latestPosts }) => { - return
- TagIndex -
+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 ( + + +
+ + {locale.COMMON.TAGS}: +
+
+ {tags.map(tag => { + return ( +
+ +
+ ) + })} +
+
+
+ ) } diff --git a/themes/Hexo/components/BlogPostArchive.js b/themes/Hexo/components/BlogPostArchive.js new file mode 100644 index 00000000..5c3bc169 --- /dev/null +++ b/themes/Hexo/components/BlogPostArchive.js @@ -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
+
{archiveTitle}
+
    + {posts.map(post => ( +
  • +
    {post.date.start_date}   + + {post.title} + +
    +
  • + ))} +
+
+ } +} + +export default BlogPostArchive diff --git a/themes/Hexo/components/BlogPostListScroll.js b/themes/Hexo/components/BlogPostListScroll.js new file mode 100644 index 00000000..53a1d93f --- /dev/null +++ b/themes/Hexo/components/BlogPostListScroll.js @@ -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 + } else { + return
+ + {/* 文章列表 */} +
+ {postsToShow.map(post => ( + + ))} +
+ +
+
{ + 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} 😰`}
+
+
+ } +} + +/** + * 获取从第1页到指定页码的文章 + * @param page 第几页 + * @param totalPosts 所有文章 + * @param postsPerPage 每页文章数量 + * @returns {*} + */ +const getPostByPage = function (page, totalPosts, postsPerPage) { + return totalPosts.slice( + 0, + postsPerPage * page + ) +} +export default BlogPostListScroll diff --git a/themes/Hexo/components/CategoryGroup.js b/themes/Hexo/components/CategoryGroup.js index bc07e00a..e89159c4 100644 --- a/themes/Hexo/components/CategoryGroup.js +++ b/themes/Hexo/components/CategoryGroup.js @@ -4,6 +4,9 @@ import Link from 'next/link' import React from 'react' const CategoryGroup = ({ currentCategory, categories }) => { + if (!categories) { + return <> + } return <>
{Object.keys(categories).map(category => { diff --git a/themes/Hexo/components/PaginationNumber.js b/themes/Hexo/components/PaginationNumber.js index bec6f399..f37d8704 100644 --- a/themes/Hexo/components/PaginationNumber.js +++ b/themes/Hexo/components/PaginationNumber.js @@ -27,7 +27,7 @@ const PaginationNumber = ({ page, totalPage }) => { } } passHref > @@ -39,7 +39,7 @@ const PaginationNumber = ({ page, totalPage }) => { @@ -50,8 +50,8 @@ const PaginationNumber = ({ page, totalPage }) => { function getPageElement (page, currentPage) { return - + {page} diff --git a/themes/Hexo/config_hexo.js b/themes/Hexo/config_hexo.js index 4581a256..b0b6edf7 100644 --- a/themes/Hexo/config_hexo.js +++ b/themes/Hexo/config_hexo.js @@ -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