From 2385ad868f88ca98c8b7c22a82285a405892291a Mon Sep 17 00:00:00 2001 From: "tangly1024.com" Date: Sun, 28 Apr 2024 15:03:48 +0800 Subject: [PATCH] =?UTF-8?q?Example=E4=B8=BB=E9=A2=98=E8=B0=83=E6=95=B4?= =?UTF-8?q?=EF=BC=8C=E8=A7=84=E8=8C=83=E7=BB=84=E4=BB=B6=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- themes/example/components/Announcement.js | 28 +- themes/example/components/ArticleInfo.js | 51 --- themes/example/components/ArticleLock.js | 52 --- themes/example/components/BlogItem.js | 87 +++++ themes/example/components/BlogListArchive.js | 42 +++ .../example/components/BlogListGroupByDate.js | 38 --- themes/example/components/BlogListPage.js | 68 ++-- themes/example/components/BlogListScroll.js | 64 ++-- themes/example/components/BlogPostCard.js | 57 ---- themes/example/components/CategoryItem.js | 20 -- .../components/ExampleRecentComments.js | 35 -- themes/example/components/Header.js | 31 +- themes/example/components/JumpToTopButton.js | 18 - themes/example/components/LoadingCover.js | 8 - themes/example/components/MenuItemDrop.js | 5 + themes/example/components/MenuList.js | 73 ++++ themes/example/components/Nav.js | 47 --- themes/example/components/NormalMenuItem.js | 14 - themes/example/components/PostLock.js | 63 ++++ themes/example/components/PostMeta.js | 51 +++ .../components/RecentCommentListForExample.js | 62 ++++ themes/example/components/SearchInput.js | 7 +- themes/example/components/SideBar.js | 136 ++++---- themes/example/components/TagItem.js | 18 - themes/example/components/Title.js | 20 -- themes/example/index.js | 316 +++++++++++------- 26 files changed, 773 insertions(+), 638 deletions(-) delete mode 100644 themes/example/components/ArticleInfo.js delete mode 100644 themes/example/components/ArticleLock.js create mode 100644 themes/example/components/BlogItem.js create mode 100644 themes/example/components/BlogListArchive.js delete mode 100644 themes/example/components/BlogListGroupByDate.js delete mode 100644 themes/example/components/BlogPostCard.js delete mode 100644 themes/example/components/CategoryItem.js delete mode 100644 themes/example/components/ExampleRecentComments.js delete mode 100644 themes/example/components/JumpToTopButton.js delete mode 100644 themes/example/components/LoadingCover.js create mode 100644 themes/example/components/MenuList.js delete mode 100644 themes/example/components/Nav.js delete mode 100644 themes/example/components/NormalMenuItem.js create mode 100644 themes/example/components/PostLock.js create mode 100644 themes/example/components/PostMeta.js create mode 100644 themes/example/components/RecentCommentListForExample.js delete mode 100644 themes/example/components/TagItem.js delete mode 100644 themes/example/components/Title.js diff --git a/themes/example/components/Announcement.js b/themes/example/components/Announcement.js index 1f265417..9ffba0e9 100644 --- a/themes/example/components/Announcement.js +++ b/themes/example/components/Announcement.js @@ -3,20 +3,30 @@ import dynamic from 'next/dynamic' const NotionPage = dynamic(() => import('@/components/NotionPage')) +/** + * 公告模块 + * 其实就是一篇文章 + * @param {*} param0 + * @returns + */ const Announcement = ({ post, className }) => { const { locale } = useGlobal() if (!post || Object.keys(post).length === 0) { return <> } - return + ) } export default Announcement diff --git a/themes/example/components/ArticleInfo.js b/themes/example/components/ArticleInfo.js deleted file mode 100644 index b502a9c9..00000000 --- a/themes/example/components/ArticleInfo.js +++ /dev/null @@ -1,51 +0,0 @@ -import Link from 'next/link' -import { useGlobal } from '@/lib/global' -import { formatDateFmt } from '@/lib/utils/formatDate' - -export const ArticleInfo = (props) => { - const { post } = props - - const { locale } = useGlobal() - - return ( -
-
- {post?.type !== 'Page' && <> - - - - {post?.category} - - - | - } - - {post?.type !== 'Page' && (<> - - - {post?.publishDay} - - - | - - {locale.COMMON.LAST_EDITED_TIME}: {post?.lastEditedDay} - - | - - -   - - - )} - -
- -
- ) -} diff --git a/themes/example/components/ArticleLock.js b/themes/example/components/ArticleLock.js deleted file mode 100644 index 3744c183..00000000 --- a/themes/example/components/ArticleLock.js +++ /dev/null @@ -1,52 +0,0 @@ -import { useGlobal } from '@/lib/global' -import { useEffect, useRef } from 'react' - -/** - * 加密文章校验组件 - * @param {password, validPassword} props - * @param password 正确的密码 - * @param validPassword(bool) 回调函数,校验正确回调入参为true - * @returns - */ -export const ArticleLock = props => { - const { validPassword } = props - const { locale } = useGlobal() - - const submitPassword = () => { - const p = document.getElementById('password') - if (!validPassword(p?.value)) { - const tips = document.getElementById('tips') - if (tips) { - tips.innerHTML = '' - tips.innerHTML = `
${locale.COMMON.PASSWORD_ERROR}
` - } - } - } - const passwordInputRef = useRef(null) - useEffect(() => { - // 选中密码输入框并将其聚焦 - passwordInputRef.current.focus() - }, []) - - return
-
-
{locale.COMMON.ARTICLE_LOCK_TIPS}
-
- { - if (e.key === 'Enter') { - submitPassword() - } - }} - ref={passwordInputRef} // 绑定ref到passwordInputRef变量 - className='outline-none w-full text-sm pl-5 rounded-l transition focus:shadow-lg font-light leading-10 text-black dark:bg-gray-500 bg-gray-50' - > -
-  {locale.COMMON.SUBMIT} -
-
-
-
-
-
-} diff --git a/themes/example/components/BlogItem.js b/themes/example/components/BlogItem.js new file mode 100644 index 00000000..f1e54ffe --- /dev/null +++ b/themes/example/components/BlogItem.js @@ -0,0 +1,87 @@ +import LazyImage from '@/components/LazyImage' +import NotionIcon from '@/components/NotionIcon' +import TwikooCommentCount from '@/components/TwikooCommentCount' +import { siteConfig } from '@/lib/config' +import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils' +import Link from 'next/link' +import CONFIG from '../config' + +/** + * 博客列表的单个卡片 + * @param {*} param0 + * @returns + */ +const BlogItem = ({ post }) => { + const showPageCover = + siteConfig('EXAMPLE_POST_LIST_COVER', null, CONFIG) && + post?.pageCoverThumbnail + const url = checkContainHttp(post.slug) + ? sliceUrlFromHttp(post.slug) + : `${siteConfig('SUB_PATH', '')}/${post.slug}` + + return ( +
+
+

+ + {siteConfig('POST_TITLE_ICON') && ( + + )} + {post?.title} + +

+ +
+ by{' '} + + {siteConfig('AUTHOR')} + {' '} + on {post.date?.start_date || post.createdTime} + + {post.category && ( + <> + | + + {post.category} + + + )} + {/* | */} + {/* 2 Comments */} +
+ + {!post.results && ( +

+ {post.summary} +

+ )} + {/* 搜索结果 */} + {post.results && ( +

+ {post.results.map((r, index) => ( + {r} + ))} +

+ )} +
+ {/* 图片封面 */} + {showPageCover && ( +
+ + + +
+ )} +
+ ) +} + +export default BlogItem diff --git a/themes/example/components/BlogListArchive.js b/themes/example/components/BlogListArchive.js new file mode 100644 index 00000000..2f4b1d68 --- /dev/null +++ b/themes/example/components/BlogListArchive.js @@ -0,0 +1,42 @@ +import { siteConfig } from '@/lib/config' +import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils' +import Link from 'next/link' + +/** + * 博客归档列表;仅归档页面使用 + * 按照日期将文章分组 + * @param {*} param0 + * @returns + */ +export default function BlogListArchive({ archiveTitle, archivePosts }) { + return ( +
+
+ {archiveTitle} +
+ +
    + {archivePosts[archiveTitle].map(post => { + const url = checkContainHttp(post.slug) + ? sliceUrlFromHttp(post.slug) + : `${siteConfig('SUB_PATH', '')}/${post.slug}` + + return ( +
  • +
    + {post?.publishDay}   + + {post.title} + +
    +
  • + ) + })} +
+
+ ) +} diff --git a/themes/example/components/BlogListGroupByDate.js b/themes/example/components/BlogListGroupByDate.js deleted file mode 100644 index 7a8fe083..00000000 --- a/themes/example/components/BlogListGroupByDate.js +++ /dev/null @@ -1,38 +0,0 @@ -import { siteConfig } from '@/lib/config' -import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils' -import Link from 'next/link' - -/** - * 按照日期将文章分组 - * 归档页面用到 - * @param {*} param0 - * @returns - */ -export default function BlogListGroupByDate({ archiveTitle, archivePosts }) { - return
-
- {archiveTitle} -
- -
    - {archivePosts[archiveTitle].map(post => { - const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}` - - return
  • -
    - - {post?.publishDay} - {' '} -   - - {post.title} - -
    -
  • - })} -
-
-} diff --git a/themes/example/components/BlogListPage.js b/themes/example/components/BlogListPage.js index 5ae2cf5c..d1068ffd 100644 --- a/themes/example/components/BlogListPage.js +++ b/themes/example/components/BlogListPage.js @@ -1,45 +1,61 @@ - import { siteConfig } from '@/lib/config' import { useGlobal } from '@/lib/global' -import { useRouter } from 'next/router' import Link from 'next/link' +import { useRouter } from 'next/router' import CONFIG from '../config' -import BlogPostCard from './BlogPostCard' - +import BlogItem from './BlogItem' +/** + * 使用分页插件的博客列表 + * @param {*} props + * @returns + */ export const BlogListPage = props => { const { page = 1, posts, postCount } = props const { locale } = useGlobal() const router = useRouter() - const totalPage = Math.ceil(postCount / parseInt(siteConfig('POSTS_PER_PAGE'))) + const totalPage = Math.ceil( + postCount / parseInt(siteConfig('POSTS_PER_PAGE')) + ) const currentPage = +page const showPrev = currentPage > 1 const showNext = page < totalPage - const pagePrefix = router.asPath.split('?')[0].replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '') + const pagePrefix = router.asPath + .split('?')[0] + .replace(/\/page\/[1-9]\d*/, '') + .replace(/\/$/, '') const showPageCover = siteConfig('EXAMPLE_POST_LIST_COVER', null, CONFIG) return ( -
+
+
+ {posts?.map(post => ( + + ))} +
-
- {posts?.map(post => ( - - ))} -
- -
- - {locale.PAGINATION.PREV} - - - {locale.PAGINATION.NEXT} - -
-
+
+ + {locale.PAGINATION.PREV} + + + {locale.PAGINATION.NEXT} + +
+
) } diff --git a/themes/example/components/BlogListScroll.js b/themes/example/components/BlogListScroll.js index e876f312..ea883975 100644 --- a/themes/example/components/BlogListScroll.js +++ b/themes/example/components/BlogListScroll.js @@ -1,10 +1,14 @@ import { siteConfig } from '@/lib/config' import { useGlobal } from '@/lib/global' -import { useCallback, useEffect, useRef, useState } from 'react' import throttle from 'lodash.throttle' -import BlogPostCard from './BlogPostCard' +import { useCallback, useEffect, useRef, useState } from 'react' import CONFIG from '../config' - +import BlogItem from './BlogItem' +/** + * 使用滚动无限加载的博客列表 + * @param {*} props + * @returns + */ export const BlogListScroll = props => { const { posts } = props const { locale } = useGlobal() @@ -13,7 +17,10 @@ export const BlogListScroll = props => { let hasMore = false const postsToShow = posts - ? Object.assign(posts).slice(0, parseInt(siteConfig('POSTS_PER_PAGE')) * page) + ? Object.assign(posts).slice( + 0, + parseInt(siteConfig('POSTS_PER_PAGE')) * page + ) : [] if (posts) { @@ -28,13 +35,19 @@ export const BlogListScroll = props => { const targetRef = useRef(null) // 监听滚动自动分页加载 - 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)) + 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) + ) const showPageCover = siteConfig('EXAMPLE_POST_LIST_COVER', null, CONFIG) useEffect(() => { @@ -46,21 +59,20 @@ export const BlogListScroll = props => { }) return ( +
+ {postsToShow?.map(post => ( + + ))} -
- - {postsToShow?.map(post => ( - - ))} - -
- {' '} - {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '} -
- -
+
+ {' '} + {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '} +
+
) } diff --git a/themes/example/components/BlogPostCard.js b/themes/example/components/BlogPostCard.js deleted file mode 100644 index 723f8192..00000000 --- a/themes/example/components/BlogPostCard.js +++ /dev/null @@ -1,57 +0,0 @@ -import { siteConfig } from '@/lib/config' -import CONFIG from '../config' -import Link from 'next/link' -import TwikooCommentCount from '@/components/TwikooCommentCount' -import LazyImage from '@/components/LazyImage' -import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils' -import NotionIcon from '@/components/NotionIcon' - -const BlogPostCard = ({ post }) => { - const showPageCover = siteConfig('EXAMPLE_POST_LIST_COVER', null, CONFIG) && post?.pageCoverThumbnail - const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}` - - return
-
-

- - {siteConfig('POST_TITLE_ICON') && }{post?.title} - -

- -
- by {siteConfig('AUTHOR')} on {post.date?.start_date || post.createdTime} - - {post.category && <> - | - {post.category} - } - {/* | */} - {/* 2 Comments */} -
- - {!post.results &&

- {post.summary} -

} - {/* 搜索结果 */} - {post.results && ( -

- {post.results.map((r, index) => ( - {r} - ))} -

- )} -
- {/* 图片封面 */} - {showPageCover && ( -
- - - -
- )} -
-} - -export default BlogPostCard diff --git a/themes/example/components/CategoryItem.js b/themes/example/components/CategoryItem.js deleted file mode 100644 index d69de464..00000000 --- a/themes/example/components/CategoryItem.js +++ /dev/null @@ -1,20 +0,0 @@ -import Link from 'next/link' - -/** - * 文章分类 - * @param {*} param0 - * @returns - */ -export default function CategoryItem({ category }) { - return ( - -
- {category.name}({category.count}) -
- - ) -} diff --git a/themes/example/components/ExampleRecentComments.js b/themes/example/components/ExampleRecentComments.js deleted file mode 100644 index 9dbdfa7f..00000000 --- a/themes/example/components/ExampleRecentComments.js +++ /dev/null @@ -1,35 +0,0 @@ -import { useEffect, useState } from 'react' -import { siteConfig } from '@/lib/config' -import Link from 'next/link' -import { RecentComments } from '@waline/client' - -/** - * @see https://waline.js.org/guide/get-started.html - * @param {*} props - * @returns - */ -const ExampleRecentComments = (props) => { - const [comments, updateComments] = useState([]) - const [onLoading, changeLoading] = useState(true) - useEffect(() => { - RecentComments({ - serverURL: siteConfig('COMMENT_WALINE_SERVER_URL'), - count: 5 - }).then(({ comments }) => { - changeLoading(false) - updateComments(comments) - }) - }, []) - - return <> - {onLoading &&
Loading...
} - {!onLoading && comments && comments.length === 0 &&
No Comments
} - {!onLoading && comments && comments.length > 0 && comments.map((comment) =>
-
-
--{comment.nick}
-
)} - - -} - -export default ExampleRecentComments diff --git a/themes/example/components/Header.js b/themes/example/components/Header.js index 2ec1e27b..fdb049e3 100644 --- a/themes/example/components/Header.js +++ b/themes/example/components/Header.js @@ -1,24 +1,27 @@ -import Link from 'next/link' import { siteConfig } from '@/lib/config' +import Link from 'next/link' +import { MenuList } from './MenuList' /** * 网站顶部 * @returns */ -export const Header = (props) => { +export const Header = props => { return ( -
-
- +
+
+ + {siteConfig('TITLE')} + +
+ {/* 右侧文字 */} +
+
- {siteConfig('TITLE')} - -
- {/* 右侧文字 */} -
-
-
+ {/* 菜单 */} + + ) } diff --git a/themes/example/components/JumpToTopButton.js b/themes/example/components/JumpToTopButton.js deleted file mode 100644 index f5e22b61..00000000 --- a/themes/example/components/JumpToTopButton.js +++ /dev/null @@ -1,18 +0,0 @@ -import { useGlobal } from '@/lib/global' - -/** - * 跳转到网页顶部 - * 当屏幕下滑500像素后会出现该控件 - * @param targetRef 关联高度的目标html标签 - * @param showPercent 是否显示百分比 - * @returns {JSX.Element} - * @constructor - */ -const JumpToTopButton = () => { - const { locale } = useGlobal() - return
window.scrollTo({ top: 0, behavior: 'smooth' })} - > -
-} - -export default JumpToTopButton diff --git a/themes/example/components/LoadingCover.js b/themes/example/components/LoadingCover.js deleted file mode 100644 index 75976180..00000000 --- a/themes/example/components/LoadingCover.js +++ /dev/null @@ -1,8 +0,0 @@ - -export default function LoadingCover() { - return
-
- -
-
-} diff --git a/themes/example/components/MenuItemDrop.js b/themes/example/components/MenuItemDrop.js index 94d70605..a097abeb 100644 --- a/themes/example/components/MenuItemDrop.js +++ b/themes/example/components/MenuItemDrop.js @@ -1,6 +1,11 @@ import Link from 'next/link' import { useState } from 'react' +/** + * 支持下拉二级的菜单 + * @param {*} param0 + * @returns + */ export const MenuItemDrop = ({ link }) => { const [show, changeShow] = useState(false) const hasSubMenu = link?.subMenus?.length > 0 diff --git a/themes/example/components/MenuList.js b/themes/example/components/MenuList.js new file mode 100644 index 00000000..09613b1a --- /dev/null +++ b/themes/example/components/MenuList.js @@ -0,0 +1,73 @@ +import { siteConfig } from '@/lib/config' +import { useGlobal } from '@/lib/global' +import CONFIG from '../config' +import { MenuItemDrop } from './MenuItemDrop' + +/** + * 导航菜单列表 + * @param {*} props + * @returns + */ +export const MenuList = props => { + const { customNav, customMenu } = props + const { locale } = useGlobal() + + let links = [ + { + id: 1, + icon: 'fas fa-search', + name: locale.NAV.SEARCH, + to: '/search', + show: siteConfig('EXAMPLE_MENU_SEARCH', null, CONFIG) + }, + { + id: 2, + icon: 'fas fa-archive', + name: locale.NAV.ARCHIVE, + to: '/archive', + show: siteConfig('EXAMPLE_MENU_ARCHIVE', null, CONFIG) + }, + { + id: 3, + icon: 'fas fa-folder', + name: locale.COMMON.CATEGORY, + to: '/category', + show: siteConfig('EXAMPLE_MENU_CATEGORY', null, CONFIG) + }, + { + id: 4, + icon: 'fas fa-tag', + name: locale.COMMON.TAGS, + to: '/tag', + show: siteConfig('EXAMPLE_MENU_TAG', null, CONFIG) + } + ] + + if (customNav) { + links = links.concat(customNav) + } + + // 如果 开启自定义菜单,则不再使用 Page生成菜单。 + if (siteConfig('CUSTOM_MENU')) { + links = customMenu + } + + if (!links || links.length === 0) { + return null + } + + return ( + + ) +} diff --git a/themes/example/components/Nav.js b/themes/example/components/Nav.js deleted file mode 100644 index 01fbe03f..00000000 --- a/themes/example/components/Nav.js +++ /dev/null @@ -1,47 +0,0 @@ -import { siteConfig } from '@/lib/config' -import { useGlobal } from '@/lib/global' -import CONFIG from '../config' -import { MenuItemDrop } from './MenuItemDrop' - -/** - * 菜单导航 - * @param {*} props - * @returns - */ -export const Nav = (props) => { - const { customNav, customMenu } = props - const { locale } = useGlobal() - - let links = [ - { id: 1, icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: siteConfig('EXAMPLE_MENU_SEARCH', null, CONFIG) }, - { id: 2, icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', show: siteConfig('EXAMPLE_MENU_ARCHIVE', null, CONFIG) }, - { id: 3, icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('EXAMPLE_MENU_CATEGORY', null, CONFIG) }, - { id: 4, icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('EXAMPLE_MENU_TAG', null, CONFIG) } - ] - - if (customNav) { - links = links.concat(customNav) - } - - // 如果 开启自定义菜单,则不再使用 Page生成菜单。 - if (siteConfig('CUSTOM_MENU')) { - links = customMenu - } - - if (!links || links.length === 0) { - return null - } - - return ( - - ) -} diff --git a/themes/example/components/NormalMenuItem.js b/themes/example/components/NormalMenuItem.js deleted file mode 100644 index aec74837..00000000 --- a/themes/example/components/NormalMenuItem.js +++ /dev/null @@ -1,14 +0,0 @@ -import Link from 'next/link' - -/** - * 旧的普通菜单 - * @param {*} props - * @returns - */ -export const NormalMenuItem = (props) => { - const { link } = props - return link?.show && - {link.name} - -} diff --git a/themes/example/components/PostLock.js b/themes/example/components/PostLock.js new file mode 100644 index 00000000..ca20fc77 --- /dev/null +++ b/themes/example/components/PostLock.js @@ -0,0 +1,63 @@ +import { useGlobal } from '@/lib/global' +import { useEffect, useRef } from 'react' + +/** + * 文章锁;通过此组件校验密码访问文章 + * @param {password, validPassword} props + * @param password 正确的密码 + * @param validPassword(bool) 回调函数,校验正确回调入参为true + * @returns + */ +export const PostLock = props => { + const { validPassword } = props + const { locale } = useGlobal() + + const submitPassword = () => { + const p = document.getElementById('password') + if (!validPassword(p?.value)) { + const tips = document.getElementById('tips') + if (tips) { + tips.innerHTML = '' + tips.innerHTML = `
${locale.COMMON.PASSWORD_ERROR}
` + } + } + } + const passwordInputRef = useRef(null) + useEffect(() => { + // 选中密码输入框并将其聚焦 + passwordInputRef.current.focus() + }, []) + + return ( +
+
+
{locale.COMMON.ARTICLE_LOCK_TIPS}
+
+ { + if (e.key === 'Enter') { + submitPassword() + } + }} + ref={passwordInputRef} // 绑定ref到passwordInputRef变量 + className='outline-none w-full text-sm pl-5 rounded-l transition focus:shadow-lg font-light leading-10 text-black dark:bg-gray-500 bg-gray-50'> +
+ +  {locale.COMMON.SUBMIT} + +
+
+
+
+
+ ) +} diff --git a/themes/example/components/PostMeta.js b/themes/example/components/PostMeta.js new file mode 100644 index 00000000..19a6cab2 --- /dev/null +++ b/themes/example/components/PostMeta.js @@ -0,0 +1,51 @@ +import { useGlobal } from '@/lib/global' +import { formatDateFmt } from '@/lib/utils/formatDate' +import Link from 'next/link' +/** + * 文章详情的元信息 + * 标题、作者、分类、标签、创建日期等等。 + */ +export const PostMeta = props => { + const { post } = props + const { locale } = useGlobal() + + return ( +
+
+ {post?.type !== 'Page' && ( + <> + + + {post?.category} + + | + + )} + + {post?.type !== 'Page' && ( + <> + + {post?.publishDay} + + | + + {locale.COMMON.LAST_EDITED_TIME}: {post?.lastEditedDay} + + | + + +   + + + + )} +
+
+ ) +} diff --git a/themes/example/components/RecentCommentListForExample.js b/themes/example/components/RecentCommentListForExample.js new file mode 100644 index 00000000..e6c97c4c --- /dev/null +++ b/themes/example/components/RecentCommentListForExample.js @@ -0,0 +1,62 @@ +import { siteConfig } from '@/lib/config' +import { RecentComments } from '@waline/client' +import Link from 'next/link' +import { useEffect, useState } from 'react' + +/** + * 最近评论列表 + * 基于Waline实现 + * @see https://waline.js.org/guide/get-started.html + * @param {*} props + * @returns + */ +const RecentCommentListForExample = props => { + const [comments, updateComments] = useState([]) + const [onLoading, changeLoading] = useState(true) + useEffect(() => { + RecentComments({ + serverURL: siteConfig('COMMENT_WALINE_SERVER_URL'), + count: 5 + }).then(({ comments }) => { + changeLoading(false) + updateComments(comments) + }) + }, []) + + return ( + <> + {onLoading && ( +
+ Loading... + +
+ )} + {!onLoading && comments && comments.length === 0 && ( +
No Comments
+ )} + {!onLoading && + comments && + comments.length > 0 && + comments.map(comment => ( +
+
+
+ + --{comment.nick} + +
+
+ ))} + + ) +} + +export default RecentCommentListForExample diff --git a/themes/example/components/SearchInput.js b/themes/example/components/SearchInput.js index 4f375d4e..419e9984 100644 --- a/themes/example/components/SearchInput.js +++ b/themes/example/components/SearchInput.js @@ -1,9 +1,14 @@ -import { useRouter } from 'next/router' import { useGlobal } from '@/lib/global' +import { useRouter } from 'next/router' import { useImperativeHandle, useRef, useState } from 'react' let lock = false +/** + * 搜索输入框 + * @param {*} param0 + * @returns + */ const SearchInput = ({ currentTag, keyword, cRef }) => { const { locale } = useGlobal() const router = useRouter() diff --git a/themes/example/components/SideBar.js b/themes/example/components/SideBar.js index ea0531b7..5cfe9d9f 100644 --- a/themes/example/components/SideBar.js +++ b/themes/example/components/SideBar.js @@ -1,68 +1,92 @@ -import { siteConfig } from '@/lib/config' import Live2D from '@/components/Live2D' +import { siteConfig } from '@/lib/config' import { useGlobal } from '@/lib/global' -import Link from 'next/link' import dynamic from 'next/dynamic' +import Link from 'next/link' import Announcement from './Announcement' -const ExampleRecentComments = dynamic(() => import('./ExampleRecentComments')) +const ExampleRecentComments = dynamic( + () => import('./RecentCommentListForExample') +) -export const SideBar = (props) => { +/** + * 侧边栏 + */ +export const SideBar = props => { const { locale } = useGlobal() const { latestPosts, categoryOptions, notice } = props return ( -
- - - - - - - - {siteConfig('COMMENT_WALINE_SERVER_URL') && siteConfig('COMMENT_WALINE_RECENT') && } - - +
+ + + {/* 公告栏 */} + + + {/* 最近评论 */} + {siteConfig('COMMENT_WALINE_SERVER_URL') && + siteConfig('COMMENT_WALINE_RECENT') && ( + + )} + +
) } diff --git a/themes/example/components/TagItem.js b/themes/example/components/TagItem.js deleted file mode 100644 index 5d12dfdb..00000000 --- a/themes/example/components/TagItem.js +++ /dev/null @@ -1,18 +0,0 @@ -import Link from 'next/link' - -/** - * 标签 - * @param {*} param0 - * @returns - */ -export default function TagItem({ tag }) { - return
- -
{tag.name + (tag.count ? `(${tag.count})` : '')}
- -
-} diff --git a/themes/example/components/Title.js b/themes/example/components/Title.js deleted file mode 100644 index a073f1cc..00000000 --- a/themes/example/components/Title.js +++ /dev/null @@ -1,20 +0,0 @@ -import NotionIcon from '@/components/NotionIcon' -import { siteConfig } from '@/lib/config' - -/** - * 标题栏 - * @param {*} props - * @returns - */ -export const Title = (props) => { - const { post } = props - const title = post?.title || siteConfig('TITLE') - const description = post?.description || siteConfig('AUTHOR') - - return
-

{siteConfig('POST_TITLE_ICON') && }{title}

-

- {description} -

-
-} diff --git a/themes/example/index.js b/themes/example/index.js index 2bb24981..b1893935 100644 --- a/themes/example/index.js +++ b/themes/example/index.js @@ -1,31 +1,28 @@ 'use client' -import CONFIG from './config' +import Comment from '@/components/Comment' +import replaceSearchResult from '@/components/Mark' +import NotionIcon from '@/components/NotionIcon' +import NotionPage from '@/components/NotionPage' +import ShareBar from '@/components/ShareBar' +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 { useEffect } from 'react' -import { Header } from './components/Header' -import { Nav } from './components/Nav' -import { Footer } from './components/Footer' -import { Title } from './components/Title' -import { SideBar } from './components/SideBar' +import BlogListArchive from './components/BlogListArchive' import { BlogListPage } from './components/BlogListPage' import { BlogListScroll } from './components/BlogListScroll' -import { useGlobal } from '@/lib/global' -import { ArticleLock } from './components/ArticleLock' -import { ArticleInfo } from './components/ArticleInfo' -import JumpToTopButton from './components/JumpToTopButton' -import NotionPage from '@/components/NotionPage' -import Comment from '@/components/Comment' -import ShareBar from '@/components/ShareBar' +import { Footer } from './components/Footer' +import { Header } from './components/Header' +import { PostLock } from './components/PostLock' +import { PostMeta } from './components/PostMeta' import SearchInput from './components/SearchInput' -import replaceSearchResult from '@/components/Mark' -import { isBrowser } from '@/lib/utils' -import BlogListGroupByDate from './components/BlogListGroupByDate' -import CategoryItem from './components/CategoryItem' -import TagItem from './components/TagItem' -import { useRouter } from 'next/router' -import { Transition } from '@headlessui/react' +import { SideBar } from './components/SideBar' +import CONFIG from './config' import { Style } from './style' -import { siteConfig } from '@/lib/config' /** * 基础布局框架 @@ -36,84 +33,105 @@ import { siteConfig } from '@/lib/config' */ const LayoutBase = props => { const { children } = props - const { onLoading, fullWidth } = useGlobal() + const { onLoading, fullWidth, locale } = useGlobal() const router = useRouter() - const { category, tag } = props + const { post, category, tag } = props + + const title = post?.title || siteConfig('TITLE') + const description = post?.description || siteConfig('AUTHOR') + // 顶部如果是按照分类或标签查看文章列表,列表顶部嵌入一个横幅 // 如果是搜索,则列表顶部嵌入 搜索框 let slotTop = null if (category) { - slotTop =
{category}
+ slotTop = ( +
+ + {category} +
+ ) } else if (tag) { slotTop =
#{tag}
} else if (props.slotTop) { slotTop = props.slotTop } else if (router.route === '/search') { // 嵌入一个搜索框在顶部 - slotTop =
+ slotTop = ( +
+ +
+ ) } - // 增加一个状态以触发 Transition 组件的动画 - // const [showTransition, setShowTransition] = useState(true) - // useEffect(() => { - // // 当 location 或 children 发生变化时,触发动画 - // setShowTransition(false) - // setTimeout(() => setShowTransition(true), 5) - // }, [onLoading]) - return ( -
+
+