mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-18 23:16:49 +00:00
@@ -1,70 +1,58 @@
|
||||
const BLOG = {
|
||||
title: '小唐笔记',
|
||||
author: 'tangly1024',
|
||||
email: 'tlyong1992@hotmail.com',
|
||||
link: 'https://tangly1024.com',
|
||||
description: '分享编程技术与记录生活',
|
||||
keywords: ['Notion', '写作', '博客'],
|
||||
title: '小唐笔记', // 站点标题
|
||||
description: '分享编程技术与记录生活', // 站点描述
|
||||
author: 'tangly1024', // 作者
|
||||
bio: '一个普通的干饭人🍚', // 作者简介
|
||||
email: 'tlyong1992@hotmail.com', // 联系邮箱
|
||||
link: 'https://tangly1024.com', // 网站地址
|
||||
keywords: ['Notion', '写作', '博客'], // 网站关键词
|
||||
home: { // 首页
|
||||
showHomeBanner: false, // 首页是否显示大图及标语 [true,false]
|
||||
homeBannerStrings: ['Hi,我是一个程序员', 'Hi,我是一个打工人', 'Hi,我是一个干饭人', '欢迎来到我的博客🎉'], // 首页大图标语文字
|
||||
homeBannerImage: './bg_image.jpg' // 首图
|
||||
homeBannerImage: './bg_image.jpg' // 背景图地址
|
||||
},
|
||||
lang: 'zh-CN', // ['zh-CN','en-US'] default lang => see /lib/lang.js for more.
|
||||
notionPageId: process.env.NOTION_PAGE_ID || 'bee1fccfa3bd47a1a7be83cc71372d83', // Important page_id!!!
|
||||
notionAccessToken: process.env.NOTION_ACCESS_TOKEN || '', // Useful if you prefer not to make your database public
|
||||
appearance: 'auto', // ['light', 'dark', 'auto'],
|
||||
font: 'font-sans tracking-wider subpixel-antialiased', // 文章字体 ['font-sans', 'font-serif', 'font-mono'] @see https://www.tailwindcss.cn/docs/font-family
|
||||
lightBackground: '#ffffff', // use hex value, don't forget '#' e.g #fffefc
|
||||
lightBackground: '#eeeeee', // use hex value, don't forget '#' e.g #fffefc
|
||||
darkBackground: '#111827', // use hex value, don't forget '#'
|
||||
path: '', // leave this empty unless you want to deploy in a folder
|
||||
since: 2020, // if leave this empty, current year will be used.
|
||||
postListStyle: 'page', // ['page','scroll] 文章列表样式:页码分页、单页滚动加载
|
||||
postsPerPage: 6, // post counts per page
|
||||
sortByDate: false,
|
||||
autoCollapsedNavBar: true, // the automatically collapsed navigation bar
|
||||
menu: { // menu config
|
||||
showAbout: false,
|
||||
showCategory: true,
|
||||
showTag: true,
|
||||
showArchive: true,
|
||||
showSearch: true
|
||||
menu: { // 菜单栏设置
|
||||
showAbout: false, // 显示关于
|
||||
showCategory: true, // 显示分类
|
||||
showTag: true, // 显示标签
|
||||
showArchive: true, // 显示归档
|
||||
showSearch: true // 显示搜索
|
||||
},
|
||||
widget: {
|
||||
widget: { // 挂件及组件设置
|
||||
showPet: false, // 是否显示宠物挂件
|
||||
petLink: 'https://cdn.jsdelivr.net/npm/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json', // 模型地址 @see https://github.com/xiazeyu/live2d-widget-models
|
||||
showToTop: true,
|
||||
showToBottom: true,
|
||||
showDarkMode: true,
|
||||
showToc: true,
|
||||
showShareBar: false,
|
||||
showRelatePosts: false,
|
||||
showCopyRight: false,
|
||||
showLatestPost: false,
|
||||
showCategoryList: false,
|
||||
showTagList: false
|
||||
petLink: 'https://cdn.jsdelivr.net/npm/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json', // 挂件模型地址 @see https://github.com/xiazeyu/live2d-widget-models
|
||||
showToTop: true, // 是否显示回顶
|
||||
showToBottom: true, // 显示回底
|
||||
showDarkMode: true, // 显示日间/夜间模式切换
|
||||
showToc: true, // 移动端显示悬浮目录
|
||||
showShareBar: false, // 文章分享功能
|
||||
showRelatePosts: false, // 关联文章推荐
|
||||
showCopyRight: false, // 显示版权声明
|
||||
showLatestPost: false, // 右侧边栏显示最近更新
|
||||
showCategoryList: false, // 右侧边栏显示文章分类列表
|
||||
showTagList: false // 右侧边栏显示标签分类列表
|
||||
},
|
||||
socialLink: { // 社交链接
|
||||
socialLink: { // 社交链接,如不需要展示可以留空白,例如 weibo:''
|
||||
weibo: 'https://weibo.com/tangly1024',
|
||||
twitter: 'https://twitter.com/troy1024_1',
|
||||
github: 'https://github.com/tangly1024',
|
||||
telegram: 'https://t.me/tangly_1024'
|
||||
},
|
||||
analytics: {
|
||||
provider: 'ga', // Currently we support Google Analytics and Ackee, please fill with 'ga' or 'ackee', leave it empty to disable it.
|
||||
ackeeConfig: {
|
||||
tracker: '', // e.g 'https://ackee.tangly1024.net/tracker.js'
|
||||
dataAckeeServer: '', // e.g https://ackee.tangly1024.net , don't end with a slash
|
||||
domainId: '' // e.g '0e2257a8-54d4-4847-91a1-0311ea48cc7b'
|
||||
},
|
||||
gaConfig: {
|
||||
measurementId: 'G-68EK0W049N' // e.g: G-XXXXXXXXXX
|
||||
},
|
||||
baiduAnalytics: 'f683ef76f06bb187cbed5546f6f28f28', // e.g only need xxxxx -> https://hm.baidu.com/hm.js?[xxxxx]
|
||||
busuanzi: true, // 展示网站阅读量访问数 see http://busuanzi.ibruce.info/
|
||||
cnzzAnalytics: '' // 站长统计id only need xxxxxxxx -> https://s9.cnzz.com/z_stat.php?id=[xxxxxxxx]&web_id=[xxxxxxx]
|
||||
},
|
||||
comment: { // support provider: gitalk, utterances, cusdis
|
||||
provider: '', // leave it empty if you don't need any comment plugin
|
||||
comment: { // 评论插件,支持 gitalk, utterances, cusdis
|
||||
provider: '', // 不需要则留空白
|
||||
gitalkConfig: {
|
||||
repo: 'NotionNext', // The repository of store comments
|
||||
owner: 'tangly1024',
|
||||
@@ -84,6 +72,21 @@ const BLOG = {
|
||||
DaoVoiceId: '', // DaoVoice http://dashboard.daovoice.io/get-started
|
||||
TidioId: '' // https://www.tidio.com/
|
||||
},
|
||||
// --- 高级设置
|
||||
analytics: { // 文章访问量统计
|
||||
busuanzi: true, // 展示网站阅读量、访问数 see http://busuanzi.ibruce.info/
|
||||
provider: 'ga', // 支持 Google Analytics and Ackee, please fill with 'ga' or 'ackee', leave it empty to disable it.
|
||||
baiduAnalytics: 'f683ef76f06bb187cbed5546f6f28f28', // e.g only need xxxxx -> https://hm.baidu.com/hm.js?[xxxxx]
|
||||
cnzzAnalytics: '', // 站长统计id only need xxxxxxxx -> https://s9.cnzz.com/z_stat.php?id=[xxxxxxxx]&web_id=[xxxxxxx]
|
||||
gaConfig: {
|
||||
measurementId: 'G-68EK0W049N' // e.g: G-XXXXXXXXXX
|
||||
},
|
||||
ackeeConfig: {
|
||||
tracker: '', // e.g 'https://ackee.tangly1024.net/tracker.js'
|
||||
dataAckeeServer: '', // e.g https://ackee.tangly1024.net , don't end with a slash
|
||||
domainId: '' // e.g '0e2257a8-54d4-4847-91a1-0311ea48cc7b'
|
||||
}
|
||||
},
|
||||
seo: {
|
||||
googleSiteVerification: '' // Remove the value or replace it with your own google site verification code
|
||||
},
|
||||
|
||||
@@ -51,7 +51,7 @@ export default function ArticleDetail ({ post, blockMap, recommendPosts, prev, n
|
||||
const attachZoomRef = attachZoom
|
||||
|
||||
return (<>
|
||||
<div id="article-wrapper" ref={targetRef} className="shadow md:hover:shadow-2xl duration-300 overflow-x-auto flex-grow mx-auto w-screen md:w-full ">
|
||||
<div id="container" ref={targetRef} className="shadow md:hover:shadow-2xl duration-300 overflow-x-auto flex-grow mx-auto w-screen md:w-full ">
|
||||
<article itemScope itemType="https://schema.org/Movie"
|
||||
className="subpixel-antialiased py-10 px-5 lg:pt-24 md:px-24 dark:border-gray-700 bg-white dark:bg-gray-800"
|
||||
>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import BlogPostCard from '@/components/BlogPostCard'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import PaginationNumber from './PaginationNumber'
|
||||
import BLOG from '@/blog.config'
|
||||
|
||||
import { useRouter } from 'next/router'
|
||||
@@ -29,7 +29,7 @@ const BlogPostListPage = ({ page = 1, posts = [], tags }) => {
|
||||
}
|
||||
|
||||
// 处理分页
|
||||
const totalPages = Math.ceil(filteredBlogPosts.length / BLOG.postsPerPage)
|
||||
const totalPage = Math.ceil(filteredBlogPosts.length / BLOG.postsPerPage)
|
||||
const postsToShow = filteredBlogPosts.slice(
|
||||
BLOG.postsPerPage * (page - 1),
|
||||
BLOG.postsPerPage * page
|
||||
@@ -43,28 +43,18 @@ const BlogPostListPage = ({ page = 1, posts = [], tags }) => {
|
||||
if (!postsToShow || postsToShow.length === 0) {
|
||||
return <BlogPostListEmpty />
|
||||
} else {
|
||||
return <div id='post-list-wrapper' className='pt-16 md:pt-28 px-2 md:px-20'>
|
||||
{(!page || page === 1) && (<div className='py-5' />)}
|
||||
|
||||
{(page && page !== 1) && (
|
||||
<div className='pb-5'>
|
||||
<div className='dark:text-gray-200 flex justify-between py-1'>
|
||||
{page && page !== 1 && (<span>页 {page} / {totalPages}</span>)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
return (
|
||||
<div id="container" className='mt-10'>
|
||||
{/* 文章列表 */}
|
||||
<div className='flex flex-wrap'>
|
||||
<div className="flex flex-wrap space-y-8 mx-5 md:mx-0">
|
||||
{postsToShow.map(post => (
|
||||
<BlogPostCard key={post.id} post={post} tags={tags} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Pagination page={page} showNext={showNext} />
|
||||
<PaginationNumber page={page} showNext={showNext} totalPage={totalPage} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ const BlogPostListScroll = ({ posts = [], tags, currentSearch, currentCategory,
|
||||
if (!postsToShow || postsToShow.length === 0) {
|
||||
return <BlogPostListEmpty currentSearch={currentSearch} />
|
||||
} else {
|
||||
return <div id='post-list-wrapper' className='mt-10 md:mt-0' ref={targetRef}>
|
||||
return <div id='container' className='mt-10' ref={targetRef}>
|
||||
|
||||
{/* 文章列表 */}
|
||||
<div className='flex flex-wrap space-y-8 mx-5 md:mx-0'>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faFolder, faFolderOpen, faThList } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faFolder, faFolderOpen } from '@fortawesome/free-solid-svg-icons'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
const CategoryList = ({ currentCategory, categories }) => {
|
||||
@@ -11,7 +11,7 @@ const CategoryList = ({ currentCategory, categories }) => {
|
||||
const { locale } = useGlobal()
|
||||
|
||||
return <ul className='flex py-1 space-x-3'>
|
||||
<li className='w-16 py-2 dark:text-gray-200 whitespace-nowrap'><FontAwesomeIcon className='mr-2' icon={faThList} />{locale.COMMON.CATEGORY}</li>
|
||||
<li className='w-16 py-2 dark:text-gray-200 whitespace-nowrap'>{locale.COMMON.CATEGORY}</li>
|
||||
{Object.keys(categories).map(category => {
|
||||
const selected = category === currentCategory
|
||||
return (
|
||||
|
||||
@@ -9,7 +9,7 @@ const InfoCard = ({ postCount }) => {
|
||||
<div className='flex flex-col items-center justify-center '>
|
||||
<div className='hover:rotate-45 hover:scale-125 transform duration-200 cursor-pointer' onClick={ () => { Router.push('/about') }}>
|
||||
<Image
|
||||
alt={BLOG.title}
|
||||
alt={BLOG.author}
|
||||
width={120}
|
||||
height={120}
|
||||
loading='lazy'
|
||||
@@ -17,8 +17,8 @@ const InfoCard = ({ postCount }) => {
|
||||
className='rounded-full'
|
||||
/>
|
||||
</div>
|
||||
<div className='text-3xl font-serif dark:text-white py-2 hover:scale-105 transform duration-200'>{BLOG.title}</div>
|
||||
<div className='font-light dark:text-white py-2 hover:scale-105 transform duration-200'>{BLOG.description}</div>
|
||||
<div className='text-2xl font-serif dark:text-white py-2 hover:scale-105 transform duration-200'>{BLOG.author}</div>
|
||||
<div className='font-light dark:text-white py-2 hover:scale-105 transform duration-200'>{BLOG.bio}</div>
|
||||
<SocialButton/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -3,7 +3,11 @@ import BLOG from '@/blog.config'
|
||||
|
||||
let hasLoad = false
|
||||
export default function Live2D () {
|
||||
if (BLOG.widget?.showPet && typeof window !== 'undefined' && !hasLoad) {
|
||||
if (!BLOG.widget?.showPet) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && !hasLoad) {
|
||||
initLive2D()
|
||||
hasLoad = true
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import React from 'react'
|
||||
|
||||
const Logo = () => {
|
||||
return <Link href='/' passHref>
|
||||
<div title={BLOG.title} className='mx-auto border dark:border-gray-600 text-center cursor-pointer text-xl dark:text-gray-300 font-semibold dark:hover:bg-gray-600 text-white p-2 hover:scale-105 hover:shadow-2xl duration-200 transform'>
|
||||
<span className='text-red-600'>Tangly</span>
|
||||
<span className='text-blue-400'>1024</span>
|
||||
</div>
|
||||
<div className='flex flex-col justify-center items-center cursor-pointer bg-gray-800 space-y-3 h-32 font-bold mb-4'>
|
||||
<div className='font-serif text-xl text-white'> {BLOG.title}</div>
|
||||
<div className='text-sm text-gray-300 font-light'> {BLOG.description}</div>
|
||||
</div>
|
||||
</Link>
|
||||
}
|
||||
export default Logo
|
||||
|
||||
@@ -20,9 +20,9 @@ const MenuButtonGroup = ({ allowCollapse = false }) => {
|
||||
{links.map(link => {
|
||||
if (link.show) {
|
||||
const selected = (router.pathname === link.to) || (router.asPath === link.to)
|
||||
return <Link key={link.id + link.icon} title={link.to} href={link.to} >
|
||||
return <Link key={`${link.id}-${link.to}`} title={link.to} href={link.to} >
|
||||
<a className={'py-1 my-1 px-5 mx-2 duration-300 text-base hover:bg-gray-500 hover:text-white hover:shadow-lg cursor-pointer font-light flex flex-nowrap items-center ' +
|
||||
(selected ? 'bg-gray-700 text-white ' : ' ')} >
|
||||
(selected ? 'bg-gray-200 text-black' : ' ')} >
|
||||
<div className='my-auto justify-center flex '>
|
||||
<FontAwesomeIcon icon={link.icon} />
|
||||
</div>
|
||||
@@ -30,7 +30,7 @@ const MenuButtonGroup = ({ allowCollapse = false }) => {
|
||||
</a>
|
||||
</Link>
|
||||
} else {
|
||||
return <></>
|
||||
return null
|
||||
}
|
||||
})}
|
||||
</nav>
|
||||
|
||||
95
components/PaginationNumber.js
Normal file
95
components/PaginationNumber.js
Normal file
@@ -0,0 +1,95 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faAngleLeft, faAngleRight } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
/**
|
||||
* 数字翻页插件
|
||||
* @param page 当前页码
|
||||
* @param showNext 是否有下一页
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const PaginationNumber = ({ page, showNext, totalPage }) => {
|
||||
const router = useRouter()
|
||||
const currentPage = +page
|
||||
const pages = generatePages(page, currentPage, totalPage)
|
||||
|
||||
return (
|
||||
<div className='my-5 flex justify-center items-end font-medium text-black hover:shadow-xl duration-500 bg-white dark:bg-gray-700 dark:text-gray-300 py-3 shadow space-x-2'>
|
||||
|
||||
{/* 上一页 */}
|
||||
<Link
|
||||
href={ {
|
||||
pathname: (currentPage - 1 === 1 ? `${BLOG.path || '/'}` : `/page/${currentPage - 1}`), query: router.query.s ? { s: router.query.s } : {}
|
||||
} } 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`}
|
||||
>
|
||||
<FontAwesomeIcon icon={faAngleLeft}/>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{pages}
|
||||
|
||||
{/* 下一页 */}
|
||||
<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`}
|
||||
>
|
||||
<FontAwesomeIcon icon={faAngleRight}/>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function getPageElement (page, currentPage) {
|
||||
console.log(page, currentPage)
|
||||
return <Link href={`/page/${page}`} key={page} passHref>
|
||||
<div className={(page + '' === currentPage ? 'font-bold bg-gray-500 dark:bg-gray-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'}>
|
||||
{page}
|
||||
</div>
|
||||
</Link>
|
||||
}
|
||||
function generatePages (page, currentPage, totalPage) {
|
||||
const pages = []
|
||||
const startPage = 1 // 分组开始页码
|
||||
const groupCount = 5 // 页码分组
|
||||
if (totalPage <= 10) {
|
||||
for (let i = 1; i <= totalPage; i++) {
|
||||
pages.push(getPageElement(i, page))
|
||||
}
|
||||
} else {
|
||||
pages.push(getPageElement(1, page))
|
||||
|
||||
let pageLength = 0
|
||||
if (groupCount + startPage > totalPage) {
|
||||
pageLength = totalPage
|
||||
} else {
|
||||
pageLength = groupCount + startPage
|
||||
}
|
||||
|
||||
if (currentPage >= groupCount) {
|
||||
pages.push(<div key={-1}>... </div>)
|
||||
}
|
||||
|
||||
for (let i = startPage; i < pageLength; i++) {
|
||||
if (i <= totalPage - 1 && i > 1) {
|
||||
pages.push(getPageElement(i, page))
|
||||
}
|
||||
}
|
||||
|
||||
if (totalPage - startPage >= groupCount + 1) {
|
||||
pages.push(<div key={-2}>... </div>)
|
||||
}
|
||||
|
||||
pages.push(getPageElement(totalPage, page))
|
||||
}
|
||||
return pages
|
||||
}
|
||||
export default PaginationNumber
|
||||
@@ -4,13 +4,13 @@ import { useRouter } from 'next/router'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 翻页插件
|
||||
* 简易翻页插件
|
||||
* @param page 当前页码
|
||||
* @param showNext 是否有下一页
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const Pagination = ({ page, showNext }) => {
|
||||
const PaginationSimple = ({ page, showNext }) => {
|
||||
const { locale } = useGlobal()
|
||||
const router = useRouter()
|
||||
const currentPage = +page
|
||||
@@ -39,4 +39,4 @@ const Pagination = ({ page, showNext }) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default Pagination
|
||||
export default PaginationSimple
|
||||
@@ -21,7 +21,7 @@ const SearchDrawer = ({ cRef }) => {
|
||||
return (
|
||||
<div id='search-drawer-wrapper' ref={searchDrawer} className='hidden'>
|
||||
<div className='flex absolute px-5 w-full h-full left-0 top-14 z-50 justify-center'>
|
||||
<div className='md:max-w-3xl w-full mx-auto'>
|
||||
<div className='md:max-w-3xl w-full mx-auto animate__animated animate__faster animate__fadeIn'>
|
||||
<SearchInput cRef={searchInputRef} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ import React from 'react'
|
||||
import Analytics from './Analytics'
|
||||
import Tabs from '@/components/Tabs'
|
||||
import BLOG from '@/blog.config'
|
||||
import Logo from './Logo'
|
||||
|
||||
/**
|
||||
* 侧边平铺
|
||||
@@ -26,16 +27,19 @@ const SideAreaLeft = ({ title, tags, currentTag, post, posts, categories, curren
|
||||
const postCount = posts?.length || 0
|
||||
return <aside id='left' className='hidden lg:block flex-col w-60 mr-4'>
|
||||
|
||||
<section className='sticky top-8 w-60'>
|
||||
|
||||
<section className='w-60'>
|
||||
{/* 菜单 */}
|
||||
<section className='shadow hidden lg:block mb-5 py-4 bg-white dark:bg-gray-800 hover:shadow-xl duration-200'>
|
||||
<section className='shadow hidden lg:block mb-5 pb-4 bg-white dark:bg-gray-800 hover:shadow-xl duration-200'>
|
||||
<Logo/>
|
||||
<MenuButtonGroup allowCollapse={true} />
|
||||
{BLOG.menu.showSearch && <div className='px-5 pt-2'>
|
||||
<SearchInput currentTag={currentTag} currentSearch={currentSearch} />
|
||||
</div>}
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<section className='sticky top-4'>
|
||||
<Tabs>
|
||||
{showToc && (
|
||||
<div key={locale.COMMON.TABLE_OF_CONTENTS} className='dark:text-gray-400 text-gray-600 bg-white dark:bg-gray-800 duration-200'>
|
||||
@@ -48,7 +52,6 @@ const SideAreaLeft = ({ title, tags, currentTag, post, posts, categories, curren
|
||||
<Analytics postCount={postCount}/>
|
||||
</div>
|
||||
</Tabs>
|
||||
|
||||
</section>
|
||||
|
||||
</aside>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import React from 'react'
|
||||
import MenuButtonGroup from '@/components/MenuButtonGroup'
|
||||
import InfoCard from '@/components/InfoCard'
|
||||
import TagGroups from '@/components/TagGroups'
|
||||
import LatestPostsGroup from '@/components/LatestPostsGroup'
|
||||
import CategoryGroup from '@/components/CategoryGroup'
|
||||
import SearchInput from '@/components/SearchInput'
|
||||
import Link from 'next/link'
|
||||
import InfoCard from '@/components/InfoCard'
|
||||
import LatestPostsGroup from '@/components/LatestPostsGroup'
|
||||
import TagGroups from '@/components/TagGroups'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faAngleDoubleRight, faArchive, faTags, faThList } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import Logo from './Logo'
|
||||
|
||||
/**
|
||||
* 侧边栏
|
||||
@@ -24,30 +23,15 @@ import { faAngleDoubleRight, faArchive, faTags, faThList } from '@fortawesome/fr
|
||||
*/
|
||||
const SideBar = ({ title, tags, currentTag, post, posts, categories, currentCategory, currentSearch }) => {
|
||||
const { locale } = useGlobal()
|
||||
return <aside id='sidebar' className='pt-5 bg-white dark:bg-gray-900 w-80 z-10 dark:border-gray-500 border-gray-200 scroll-hidden h-full'>
|
||||
|
||||
<InfoCard />
|
||||
return <aside id='sidebar' className='bg-white dark:bg-gray-900 w-80 z-10 dark:border-gray-500 border-gray-200 scroll-hidden h-full'>
|
||||
|
||||
<div className={(!post ? 'sticky top-0' : '') + ' bg-white dark:bg-gray-900 pb-4'}>
|
||||
|
||||
<section className='hidden lg:block'>
|
||||
<MenuButtonGroup allowCollapse={true} />
|
||||
<section className='py-5'>
|
||||
<InfoCard />
|
||||
</section>
|
||||
|
||||
<section className='p-5'>
|
||||
<SearchInput currentTag={currentTag} currentSearch={currentSearch} />
|
||||
</section>
|
||||
|
||||
{/* 最新文章 */}
|
||||
{posts && (
|
||||
<section className='mt-4'>
|
||||
<div className='text-sm pb-4 px-5 flex flex-nowrap justify-between'>
|
||||
<div className='font-light text-gray-600 dark:text-gray-200'><FontAwesomeIcon icon={faArchive} className='mr-2' />{locale.COMMON.LATEST_POSTS}</div>
|
||||
</div>
|
||||
<LatestPostsGroup posts={posts} />
|
||||
</section>
|
||||
)}
|
||||
|
||||
<Logo/>
|
||||
{/* 分类 */}
|
||||
{categories && (
|
||||
<section className='mt-8'>
|
||||
@@ -65,7 +49,7 @@ const SideBar = ({ title, tags, currentTag, post, posts, categories, currentCate
|
||||
|
||||
{/* 标签云 */}
|
||||
{tags && (
|
||||
<section className='mt-8'>
|
||||
<section className='mt-4'>
|
||||
<div className='text-sm py-2 px-5 flex flex-nowrap justify-between font-light dark:text-gray-200'>
|
||||
<div className='text-gray-600 dark:text-gray-200'><FontAwesomeIcon icon={faTags} className='mr-2'/>{locale.COMMON.TAGS}</div>
|
||||
<Link href='/tag' passHref>
|
||||
@@ -79,6 +63,16 @@ const SideBar = ({ title, tags, currentTag, post, posts, categories, currentCate
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* 最新文章 */}
|
||||
{posts && (
|
||||
<section className='mt-8'>
|
||||
<div className='text-sm pb-4 px-5 flex flex-nowrap justify-between'>
|
||||
<div className='font-light text-gray-600 dark:text-gray-200'><FontAwesomeIcon icon={faArchive} className='mr-2' />{locale.COMMON.LATEST_POSTS}</div>
|
||||
</div>
|
||||
<LatestPostsGroup posts={posts} />
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<section className='bg-white dark:bg-gray-900'>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import SideBar from '@/components/SideBar'
|
||||
import React, { useEffect, useImperativeHandle, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useEffect, useImperativeHandle } from 'react'
|
||||
|
||||
/**
|
||||
* 侧边栏抽屉面板,可以从侧面拉出
|
||||
@@ -10,7 +11,7 @@ const SideBarDrawer = ({ post, currentTag, cRef, tags, posts, categories, curren
|
||||
// 暴露给父组件 通过cRef.current.handleMenuClick 调用
|
||||
useImperativeHandle(cRef, () => {
|
||||
return {
|
||||
handleSwitchSideDrawerVisible: () => switchSideDrawerVisible()
|
||||
handleSwitchSideDrawerVisible: () => switchSideDrawerVisible(true)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -19,11 +20,19 @@ const SideBarDrawer = ({ post, currentTag, cRef, tags, posts, categories, curren
|
||||
sideBarWrapperElement?.classList?.remove('hidden')
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
useEffect(() => {
|
||||
const sideBarDrawerRouteListener = url => {
|
||||
switchSideDrawerVisible(false)
|
||||
}
|
||||
router.events.on('routeChangeComplete', sideBarDrawerRouteListener)
|
||||
return () => {
|
||||
router.events.off('routeChangeComplete', sideBarDrawerRouteListener)
|
||||
}
|
||||
}, [router.events])
|
||||
|
||||
// 点击按钮更改侧边抽屉状态
|
||||
const [isShow, changeHiddenStatus] = useState(false)
|
||||
const switchSideDrawerVisible = () => {
|
||||
const showStatus = !isShow
|
||||
changeHiddenStatus(showStatus)
|
||||
const switchSideDrawerVisible = (showStatus) => {
|
||||
if (window) {
|
||||
const sideBarDrawer = window.document.getElementById('sidebar-drawer')
|
||||
const sideBarDrawerBackground = window.document.getElementById('sidebar-drawer-background')
|
||||
@@ -39,11 +48,11 @@ const SideBarDrawer = ({ post, currentTag, cRef, tags, posts, categories, curren
|
||||
}
|
||||
|
||||
return <div id='sidebar-wrapper' className='hidden'>
|
||||
<div id='sidebar-drawer' className='-ml-80 bg-white dark:bg-gray-900 flex flex-col duration-300 fixed h-full left-0 overflow-y-scroll scroll-hidden top-0 z-50'>
|
||||
<div id='sidebar-drawer' className='-ml-80 bg-white dark:bg-gray-900 flex flex-col duration-300 fixed h-full left-0 overflow-y-scroll scroll-hidden top-0 z-50 shadow-2xl'>
|
||||
<SideBar tags={tags} post={post} posts={posts} categories={categories} currentCategory={currentCategory} />
|
||||
</div>
|
||||
{/* 背景蒙版 */}
|
||||
<div id='sidebar-drawer-background' onClick={switchSideDrawerVisible} className='hidden fixed top-0 left-0 z-30 w-full h-full bg-black bg-opacity-30'/>
|
||||
<div id='sidebar-drawer-background' onClick={() => { switchSideDrawerVisible(false) }} className='hidden animate__animated animate__fadeIn fixed top-0 duration-300 left-0 z-30 w-full h-full glassmorphism'/>
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ const StickyBar = ({ children }) => {
|
||||
|
||||
return (
|
||||
<div id='sticky-bar' className='sticky flex-grow justify-center top-14 md:top-0 duration-500 z-10 pb-16'>
|
||||
<div className='glassmorphism dark:border-gray-600 px-5 absolute rounded-none md:rounded-xl shadow-xl border w-full hidden-scroll'>
|
||||
<div className='glassmorphism dark:border-gray-600 px-5 absolute shadow-xl border w-full hidden-scroll'>
|
||||
<div id='tag-container' className="md:pl-3 overflow-x-auto">
|
||||
{ children }
|
||||
</div>
|
||||
|
||||
@@ -12,12 +12,12 @@ const TagItem = ({ tag, selected }) => {
|
||||
return (
|
||||
<Link href={selected ? '/' : `/tag/${encodeURIComponent(tag.name)}`} passHref>
|
||||
<li
|
||||
className={`notion-${tag.color}_background list-none cursor-pointer rounded-md
|
||||
className={`notion-${tag.color}_background dark:bg-gray-700 list-none cursor-pointer rounded-md
|
||||
duration-200 mr-1 my-1 px-2 py-1 text-sm whitespace-nowrap
|
||||
text-gray-600 hover:bg-gray-200 dark:hover:bg-gray-800 dark:hover:text-white`}>
|
||||
<a>
|
||||
hover:bg-gray-200 dark:hover:bg-gray-800 `}>
|
||||
<div className='text-gray-600 dark:text-gray-300 dark:hover:text-white'>
|
||||
{selected && <FontAwesomeIcon icon={faTag} className='mr-1'/>} {`${tag.name} `} {tag.count ? `(${tag.count})` : ''}
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</Link>
|
||||
)
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import React from 'react'
|
||||
import TagItem from '@/components/TagItem'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faTags } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
/**
|
||||
* 横向的标签列表
|
||||
* @param tags
|
||||
@@ -15,7 +12,7 @@ const TagList = ({ tags, currentTag }) => {
|
||||
return <></>
|
||||
}
|
||||
return <ul className='flex py-1 space-x-3'>
|
||||
<li className='w-20 py-2 dark:text-gray-200 whitespace-nowrap'><FontAwesomeIcon icon={faTags} className='mr-2'/>标签:</li>
|
||||
<li className='w-20 py-2 dark:text-gray-200 whitespace-nowrap'>标签:</li>
|
||||
{tags.map(tag => {
|
||||
const selected = tag.name === currentTag
|
||||
return <TagItem key={tag.name} tag={tag} selected={selected}/>
|
||||
|
||||
@@ -53,7 +53,9 @@ const BaseLayout = ({
|
||||
|
||||
<>{headerSlot}</>
|
||||
|
||||
<main id='wrapper' className='flex justify-center flex-1 mx-auto md:pt-14 pb-12'>
|
||||
<div className='h-0.5 w-full bg-gray-700 dark:bg-gray-600'></div>
|
||||
|
||||
<main id='wrapper' className='flex justify-center flex-1 mx-auto pb-12'>
|
||||
<SideAreaLeft targetRef={targetRef} post={post} posts={totalPosts} tags={tags} currentSearch={currentSearch} currentTag={currentTag} categories={categories} currentCategory={currentCategory}/>
|
||||
<section id='center' className='flex-grow mt-14 md:mt-0 max-w-5xl min-h-screen' ref={targetRef}>
|
||||
{onLoading
|
||||
|
||||
2
lib/cache/memory_cache.js
vendored
2
lib/cache/memory_cache.js
vendored
@@ -1,7 +1,7 @@
|
||||
import cache from 'memory-cache'
|
||||
import BLOG from 'blog.config'
|
||||
|
||||
const cacheTime = BLOG.isProd ? 60 : 60 * 60
|
||||
const cacheTime = BLOG.isProd ? 20 : 120 * 60 // 120 minutes for dev,10 minutes for prod
|
||||
|
||||
export async function getCacheFromMemory (key, options) {
|
||||
return cache.get(key)
|
||||
|
||||
@@ -16,12 +16,12 @@ export default function Custom404 () {
|
||||
// 延时3秒如果加载失败就返回首页
|
||||
setTimeout(() => {
|
||||
if (window) {
|
||||
const article = document.getElementById('article-wrapper')
|
||||
const article = document.getElementById('container')
|
||||
if (!article) {
|
||||
router.push('/')
|
||||
}
|
||||
}
|
||||
}, 3000)
|
||||
}, 30000000)
|
||||
})
|
||||
|
||||
return <BaseLayout meta={{ title: `${BLOG.title} | 页面找不到啦` }}>
|
||||
|
||||
@@ -18,7 +18,7 @@ class MyDocument extends Document {
|
||||
<ThirdPartyScript />
|
||||
</Head>
|
||||
|
||||
<body className='bg-gray-100 dark:bg-gray-900 duration-200'>
|
||||
<body className='bg-day dark:bg-night duration-200'>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
|
||||
@@ -4,6 +4,7 @@ import BaseLayout from '@/layouts/BaseLayout'
|
||||
import BlogPostListScroll from '@/components/BlogPostListScroll'
|
||||
import { getNotionPageData } from '@/lib/notion/getNotionData'
|
||||
import Header from '@/components/Header'
|
||||
import BlogPostListPage from '@/components/BlogPostListPage'
|
||||
|
||||
export async function getStaticProps () {
|
||||
const from = 'index'
|
||||
@@ -37,7 +38,11 @@ const Index = ({ allPosts, tags, meta, categories }) => {
|
||||
totalPosts={allPosts}
|
||||
categories={categories}
|
||||
>
|
||||
<BlogPostListScroll posts={allPosts} tags={tags} />
|
||||
{BLOG.postListStyle !== 'page'
|
||||
? (<BlogPostListScroll posts={allPosts} tags={tags} />)
|
||||
: (<BlogPostListPage posts={allPosts} tags={tags} />)
|
||||
}
|
||||
|
||||
</BaseLayout>
|
||||
)
|
||||
}
|
||||
|
||||
65
pages/page/[page].js
Normal file
65
pages/page/[page].js
Normal file
@@ -0,0 +1,65 @@
|
||||
import { getAllCategories, getAllPosts, getAllTags } from '@/lib/notion'
|
||||
import BLOG from '@/blog.config'
|
||||
import BaseLayout from '@/layouts/BaseLayout'
|
||||
import { getNotionPageData } from '@/lib/notion/getNotionData'
|
||||
import Header from '@/components/Header'
|
||||
import Custom404 from '../404'
|
||||
import BlogPostListPage from '@/components/BlogPostListPage'
|
||||
|
||||
const Page = ({ page, allPosts, tags, meta, categories }) => {
|
||||
if (!meta || BLOG.postListStyle !== 'page') {
|
||||
return <Custom404/>
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseLayout
|
||||
headerSlot={BLOG.home.showHomeBanner && <Header />}
|
||||
meta={meta}
|
||||
tags={tags}
|
||||
totalPosts={allPosts}
|
||||
categories={categories}
|
||||
>
|
||||
<BlogPostListPage page={page} posts={allPosts} tags={tags} />
|
||||
</BaseLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticPaths () {
|
||||
const from = 'page'
|
||||
const notionPageData = await getNotionPageData({ from })
|
||||
const allPosts = await getAllPosts({ notionPageData, from })
|
||||
const totalPages = Math.ceil(allPosts / BLOG.postsPerPage)
|
||||
return {
|
||||
// remove first page, we 're not gonna handle that.
|
||||
paths: Array.from({ length: totalPages - 1 }, (_, i) => ({
|
||||
params: { page: '' + (i + 2) }
|
||||
})),
|
||||
fallback: true
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStaticProps ({ params: { page } }) {
|
||||
const from = 'page'
|
||||
const notionPageData = await getNotionPageData({ from })
|
||||
const allPosts = await getAllPosts({ notionPageData, from })
|
||||
const categories = await getAllCategories(allPosts)
|
||||
const tagOptions = notionPageData.tagOptions
|
||||
const tags = await getAllTags({ allPosts, tagOptions })
|
||||
const meta = {
|
||||
title: `${BLOG.title}`,
|
||||
description: BLOG.description,
|
||||
type: 'website'
|
||||
}
|
||||
return {
|
||||
props: {
|
||||
page,
|
||||
allPosts,
|
||||
tags,
|
||||
categories,
|
||||
meta
|
||||
},
|
||||
revalidate: 1
|
||||
}
|
||||
}
|
||||
|
||||
export default Page
|
||||
@@ -649,7 +649,7 @@ svg.notion-page-icon {
|
||||
}
|
||||
|
||||
.notion-list li {
|
||||
padding: 6px 0;
|
||||
padding: 1px 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user