diff --git a/themes/Fukasawa/LayoutArchive.js b/themes/Fukasawa/LayoutArchive.js index 5f4b05df8a8..c1125e56806 100644 --- a/themes/Fukasawa/LayoutArchive.js +++ b/themes/Fukasawa/LayoutArchive.js @@ -1,7 +1,61 @@ +import BLOG from '@/blog.config' +import { useGlobal } from '@/lib/global' +import { useEffect } from 'react' +import BlogArchiveItem from './components/BlogPostArchive' import LayoutBase from './LayoutBase' export const LayoutArchive = (props) => { - return - Archive Page + const { locale } = useGlobal() + const { posts } = props + // 深拷贝 + 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/Fukasawa/LayoutBase.js b/themes/Fukasawa/LayoutBase.js index 9250b7133d7..1d3f3a57e9b 100644 --- a/themes/Fukasawa/LayoutBase.js +++ b/themes/Fukasawa/LayoutBase.js @@ -1,4 +1,5 @@ import CommonHead from '@/components/CommonHead' +import TopNav from './components/TopNav' import AsideLeft from './components/AsideLeft' /** @@ -15,27 +16,23 @@ import AsideLeft from './components/AsideLeft' * @returns {JSX.Element} * @constructor */ -const LayoutBase = ({ - children, - headerSlot, - tags, - meta, - post, - postCount, - sideBarSlot, - floatSlot, - rightAreaSlot, - currentSearch, - currentCategory, - currentTag, - categories -}) => { +const LayoutBase = (props) => { + const { + children, + headerSlot, + tags, + meta, + currentCategory, + currentTag, + categories + } = props return (<> -
+ +
-
-
+
+
{headerSlot}
{children}
diff --git a/themes/Fukasawa/LayoutCategory.js b/themes/Fukasawa/LayoutCategory.js index 3b810ce924a..03dae8707be 100644 --- a/themes/Fukasawa/LayoutCategory.js +++ b/themes/Fukasawa/LayoutCategory.js @@ -1,7 +1,8 @@ +import BlogListPage from './components/BlogListPage' import LayoutBase from './LayoutBase' export const LayoutCategory = (props) => { return - Category + } diff --git a/themes/Fukasawa/LayoutCategoryIndex.js b/themes/Fukasawa/LayoutCategoryIndex.js index 9a3168a8aa1..20ed84f2ad8 100644 --- a/themes/Fukasawa/LayoutCategoryIndex.js +++ b/themes/Fukasawa/LayoutCategoryIndex.js @@ -1,7 +1,32 @@ +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 LayoutBase from './LayoutBase' export const LayoutCategoryIndex = (props) => { - return - Category - + const { locale } = useGlobal() + const { categories } = props + 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/Fukasawa/LayoutTag.js b/themes/Fukasawa/LayoutTag.js index 53167b65ae6..8593783e273 100644 --- a/themes/Fukasawa/LayoutTag.js +++ b/themes/Fukasawa/LayoutTag.js @@ -1,7 +1,8 @@ +import BlogListPage from './components/BlogListPage' import LayoutBase from './LayoutBase' export const LayoutTag = (props) => { return - Tag - {props.tag} + } diff --git a/themes/Fukasawa/LayoutTagIndex.js b/themes/Fukasawa/LayoutTagIndex.js index 48ad8d2d53f..dce81384312 100644 --- a/themes/Fukasawa/LayoutTagIndex.js +++ b/themes/Fukasawa/LayoutTagIndex.js @@ -1,7 +1,27 @@ +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 TagItem from './components/TagItem' import LayoutBase from './LayoutBase' export const LayoutTagIndex = (props) => { - return - Tag - {props.tag} + const { locale } = useGlobal() + const { tags } = props + 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/Fukasawa/components/ArticleArount.js b/themes/Fukasawa/components/ArticleAround.js similarity index 88% rename from themes/Fukasawa/components/ArticleArount.js rename to themes/Fukasawa/components/ArticleAround.js index ccf37621a09..7f42e575fea 100644 --- a/themes/Fukasawa/components/ArticleArount.js +++ b/themes/Fukasawa/components/ArticleAround.js @@ -13,12 +13,12 @@ export default function ArticleAround ({ prev, next }) { } return
- + {prev.title} - {next.title} + {next.title} diff --git a/themes/Fukasawa/components/ArticleDetail.js b/themes/Fukasawa/components/ArticleDetail.js index 2ee3978008c..cdc46ff7f0a 100644 --- a/themes/Fukasawa/components/ArticleDetail.js +++ b/themes/Fukasawa/components/ArticleDetail.js @@ -13,7 +13,7 @@ import 'prismjs/components/prism-python' import 'prismjs/components/prism-typescript' import { useEffect, useRef } from 'react' import { Code, Collection, CollectionRow, Equation, NotionRenderer } from 'react-notion-x' -import ArticleAround from './ArticleArount' +import ArticleAround from './ArticleAround' /** * @@ -42,17 +42,18 @@ export default function ArticleDetail ({ post, recommendPosts, prev, next }) { } }) - return (
+ return (
+ {post.type && !post.type.includes('Page') && post?.page_cover && ( +
+ {/* eslint-disable-next-line @next/next/no-img-element */} + {post.title} +
+ )}
- {post.type && !post.type.includes('Page') && post?.page_cover && ( -
- {post.title} -
- )} {/* 文章Title */}
diff --git a/themes/Fukasawa/components/AsideLeft.js b/themes/Fukasawa/components/AsideLeft.js index 8c8d13dcdd0..cab8d964607 100644 --- a/themes/Fukasawa/components/AsideLeft.js +++ b/themes/Fukasawa/components/AsideLeft.js @@ -7,7 +7,7 @@ import SearchInput from './SearchInput' import SiteInfo from './SiteInfo' function AsideLeft ({ tags, currentTag, categories, currentCategory }) { - return
+ return
diff --git a/themes/Fukasawa/components/BlogCard.js b/themes/Fukasawa/components/BlogCard.js index 9d80617e579..3299811fbe8 100644 --- a/themes/Fukasawa/components/BlogCard.js +++ b/themes/Fukasawa/components/BlogCard.js @@ -8,7 +8,7 @@ import Card from './Card' const BlogCard = ({ post, showSummary }) => { const showPreview = CONFIG_FUKA.POST_LIST_PREVIEW && post.blockMap return ( - +
diff --git a/themes/Fukasawa/components/BlogListPage.js b/themes/Fukasawa/components/BlogListPage.js index 776092c7c94..1b9faac0ec9 100644 --- a/themes/Fukasawa/components/BlogListPage.js +++ b/themes/Fukasawa/components/BlogListPage.js @@ -1,4 +1,5 @@ import BLOG from '@/blog.config' +import { useEffect, useState } from 'react' import BlogCard from './BlogCard' import BlogPostListEmpty from './BlogListEmpty' import PaginationSimple from './PaginationSimple' @@ -13,14 +14,32 @@ import PaginationSimple from './PaginationSimple' */ const BlogListPage = ({ page = 1, posts = [], postCount }) => { const totalPage = Math.ceil(postCount / BLOG.postsPerPage) - const showNext = page < totalPage + const showNext = page < totalPage && posts.length < postCount + const [colCount, changeCol] = useState(3) + + function updateCol () { + if (window.outerWidth > 1200) { + changeCol(3) + } else { + changeCol(1) + } + } + + useEffect(() => { + updateCol() + window.addEventListener('resize', updateCol) + return () => { + window.removeEventListener('resize', updateCol) + } + }) + if (!posts || posts.length === 0) { return } else { return (
{/* 文章列表 */} -
+
{posts.map(post => (
diff --git a/themes/Fukasawa/components/BlogPostArchive.js b/themes/Fukasawa/components/BlogPostArchive.js new file mode 100644 index 00000000000..c8952a6aafb --- /dev/null +++ b/themes/Fukasawa/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 BlogArchiveItem = ({ posts = [], archiveTitle }) => { + if (!posts || posts.length === 0) { + return <> + } else { + return
+
{archiveTitle}
+
    + {posts.map(post => ( +
  • +
    {post.date.start_date}   + + {post.title} + +
    +
  • + ))} +
+
+ } +} + +export default BlogArchiveItem diff --git a/themes/Fukasawa/components/Collapse.js b/themes/Fukasawa/components/Collapse.js new file mode 100644 index 00000000000..7607b732478 --- /dev/null +++ b/themes/Fukasawa/components/Collapse.js @@ -0,0 +1,38 @@ +import React, { useEffect, useRef } from 'react' + +const Collapse = props => { + const collapseRef = useRef(null) + const collapseSection = element => { + const sectionHeight = element.scrollHeight + requestAnimationFrame(function () { + element.style.height = sectionHeight + 'px' + requestAnimationFrame(function () { + element.style.height = 0 + 'px' + }) + }) + } + const expandSection = element => { + const sectionHeight = element.scrollHeight + element.style.height = sectionHeight + 'px' + const clearTime = setTimeout(() => { + element.style.height = 'auto' + }, 400) + clearTimeout(clearTime) + } + useEffect(() => { + const element = collapseRef.current + if (props.isOpen) { + expandSection(element) + } else { + collapseSection(element) + } + }, [props.isOpen]) + return ( +
+ {props.children} +
+ ) +} +Collapse.defaultProps = { isOpen: false } + +export default Collapse diff --git a/themes/Fukasawa/components/TagItem.js b/themes/Fukasawa/components/TagItem.js new file mode 100644 index 00000000000..d972e0cd080 --- /dev/null +++ b/themes/Fukasawa/components/TagItem.js @@ -0,0 +1,26 @@ +import { faTag } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import Link from 'next/link' +import React from 'react' +import { useGlobal } from '@/lib/global' + +const TagItem = ({ tag, selected }) => { + const { locale } = useGlobal() + if (!tag) { +
{ locale.COMMON.NOTAG }
+ } + return ( + +
  • +
    + {selected && } {`${tag.name} `} {tag.count ? `(${tag.count})` : ''} +
    +
  • + + ) +} + +export default TagItem diff --git a/themes/Fukasawa/components/TopNav.js b/themes/Fukasawa/components/TopNav.js new file mode 100644 index 00000000000..f3e196aae2c --- /dev/null +++ b/themes/Fukasawa/components/TopNav.js @@ -0,0 +1,51 @@ +import { faBars, faTimes } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { useState } from 'react' +import Collapse from './Collapse' +import GroupMenu from './GroupMenu' +import Logo from './Logo' + +/** + * 顶部导航 + * @param {*} param0 + * @returns + */ +const TopNav = ({ tags, currentTag, categories, currentCategory, postCount }) => { + const [isOpen, changeShow] = useState(false) + + const toggleMenuOpen = () => { + changeShow(!isOpen) + } + + return (
    + + {/* 导航栏 */} + + +
    ) +} + +export default TopNav diff --git a/themes/NEXT/components/BlogPostArchive.js b/themes/NEXT/components/BlogPostArchive.js index 4f2457b1c52..de39657d442 100644 --- a/themes/NEXT/components/BlogPostArchive.js +++ b/themes/NEXT/components/BlogPostArchive.js @@ -1,4 +1,4 @@ -import React, { useRef } from 'react' +import React from 'react' import Link from 'next/link' import BLOG from '@/blog.config' /** @@ -9,11 +9,10 @@ import BLOG from '@/blog.config' * @constructor */ const BlogPostArchive = ({ posts = [], archiveTitle }) => { - const targetRef = useRef(null) if (!posts || posts.length === 0) { return <> } else { - return
    + return
    {archiveTitle}
      {posts.map(post => (