diff --git a/README.md b/README.md index 66928aff..3df2f387 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@

演示地址:[https://tangly1024.com/](https://tangly1024.com/) + ## 亮点 ✨ **🚀  秒开,设备全适配** @@ -44,12 +45,14 @@ **🕸  网址美观、搜索引擎优化** +## 更多特性 +欢迎移步[我的博客](https://tangly1024.com/article/notion-next)查看 ## 快速起步 - 给这个项目点个小星星 😉 - 将 [这个 Notion 模板](https://tanghh.notion.site/02ab3b8678004aa69e9e415905ef32a5) 制作副本,并分享这个页面给所有人 - [Fork](https://github.com/tangly1024/NotionNext/fork) 这个项目 -- 在 `blog.config.js` 配置相关选项 +- 在 `blog.config.js` 配置相关选项,更多关于配置的说明,请移步[NotionNext文档](https://docs.tangly1024.com/zh) - _(可选)_ 用自己的图片替换 `/public` 文件夹里的 `avatar.jpg`、`favicon.svg` 和 `favicon.ico` - 在 [Vercel](https://vercel.com) 上部署这个项目, 设定一下环境变量: - `NOTION_PAGE_ID`: 你刚刚分享出去的 Notion 页面网址中的页面 ID,通常是网址中工作区地址后的 32 位字符串 diff --git a/blog.config.js b/blog.config.js index 3df0b0aa..059ad0a9 100644 --- a/blog.config.js +++ b/blog.config.js @@ -1,52 +1,61 @@ const BLOG = { - title: '小唐笔记', - author: 'tangly1024', - email: 'tlyong1992@hotmail.com', - link: 'https://tangly1024.com', - description: '分享编程技术与记录生活', - headerStrings: ['Hi,我是一个程序员', 'Hi,我是一个打工人', 'Hi,我是一个干饭人', '欢迎来到我的博客🎉'], // 首页文字 - bannerImage: './bg_image.jpg', // 首图 + 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', // 背景图地址 + showPostCover: false, // 文章列表显示封面图 + showPreview: true, // 列表展示文章预览 + showSummary: false // 显示用户自定义摘要 + }, 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 + font: 'font-serif tracking-wider subpixel-antialiased', // 文章字体 ['font-sans', 'font-serif', 'font-mono'] @see https://www.tailwindcss.cn/docs/font-family + 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, - showAbout: true, // WIP 是否显示关于 - showArchive: true, // WIP 是否显示归档 - autoCollapsedNavBar: false, // the automatically collapsed navigation bar - socialLink: { + topNavType: 'normal', // ['fixed','autoCollapse','normal'] 分别是固定顶部、固定底部滑动时自动折叠,不固定 + menu: { // 菜单栏设置 + showAbout: false, // 显示关于 + showCategory: true, // 显示分类 + showTag: true, // 显示标签 + showArchive: true, // 显示归档 + showSearch: true // 显示搜索 + }, + 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: true, // 相关文章推荐 + showCopyRight: true, // 文章版权声明 + showLatestPost: false, // 右侧边栏显示最近更新 + showCategoryList: false, // 右侧边栏显示文章分类列表 + showTagList: false // 右侧边栏显示标签分类列表 + }, + socialLink: { // 社交链接,如不需要展示可以留空白,例如 weibo:'' weibo: 'https://weibo.com/tangly1024', - twitter: '', + twitter: 'https://twitter.com/troy1024_1', github: 'https://github.com/tangly1024', - telegram: '' + telegram: 'https://t.me/tangly_1024' }, - seo: { - keywords: ['Notion', '写作', '博客'], - googleSiteVerification: '' // Remove the value or replace it with your own google site verification code - }, - 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: 'cusdis', // 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', @@ -62,14 +71,31 @@ const BLOG = { }, utterancesConfig: { repo: 'tangly1024/NotionNext' + }, + gitter: '', // gitter聊天室 + 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 + }, googleAdsenseId: 'ca-pub-2708419466378217', // 谷歌广告ID - DaoVoiceId: '', // 在线聊天 DaoVoice http://dashboard.daovoice.io/get-started - TidioId: '', // 在线聊天 https://www.tidio.com/ - isProd: process.env.VERCEL_ENV === 'production', // distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables) - showPet: true // 详情页是否显示宠物挂件 - + isProd: process.env.VERCEL_ENV === 'production'// distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables) } // export default BLOG module.exports = BLOG diff --git a/components/ArticleCopyright.js b/components/ArticleCopyright.js index d2063055..61f8f6d9 100644 --- a/components/ArticleCopyright.js +++ b/components/ArticleCopyright.js @@ -1,11 +1,14 @@ +import BLOG from '@/blog.config' import { useGlobal } from '@/lib/global' import Link from 'next/link' export default function ArticleCopyright ({ author, url }) { + if (!BLOG.widget?.showCopyRight) { + return <> + } const { locale } = useGlobal() return
-
{locale.COMMON.COPYRIGHT}
-
diff --git a/components/ArticleDetail.js b/components/ArticleDetail.js index 053c8af2..097a5242 100644 --- a/components/ArticleDetail.js +++ b/components/ArticleDetail.js @@ -19,7 +19,7 @@ import 'prismjs/components/prism-javascript' import 'prismjs/components/prism-markup' import 'prismjs/components/prism-python' import 'prismjs/components/prism-typescript' -import { useRef } from 'react' +import { useEffect, useRef } from 'react' import { Code, Collection, CollectionRow, Equation, NotionRenderer } from 'react-notion-x' import ArticleCopyright from './ArticleCopyright' import Live2D from './Live2D' @@ -30,7 +30,7 @@ import WordCount from './WordCount' * @param {*} param0 * @returns */ -export default function ArticleDetail ({ post, blockMap, recommendPosts, prev, next }) { +export default function ArticleDetail ({ post, recommendPosts, prev, next }) { const targetRef = useRef(null) const drawerRight = useRef(null) const url = BLOG.link + useRouter().asPath @@ -43,26 +43,40 @@ export default function ArticleDetail ({ post, blockMap, recommendPosts, prev, n margin: getMediumZoomMargin() }) const zoomRef = useRef(zoom ? zoom.clone() : null) - function attachZoom (image) { - if (zoomRef.current) { - (zoomRef.current).attach(image) + + useEffect(() => { + // 将所有container下的所有图片添加medium-zoom + const container = document.getElementById('container') + const imgList = container.getElementsByTagName('img') + if (imgList && zoomRef.current) { + for (let i = 0; i < imgList.length; i++) { + (zoomRef.current).attach(imgList[i]) + } } - } - const attachZoomRef = attachZoom + }) return (<> -
+
- {post.type && !post.type.includes('Page') && post?.page_cover && ( + {post.type && !post.type.includes('Page') && post?.page_cover && (
{/* eslint-disable-next-line @next/next/no-img-element */} - {post.title} + {post.title} + {/*
+ {post.title} +
*/}
- )} + )} {/* 文章Title */}
@@ -111,10 +125,9 @@ export default function ArticleDetail ({ post, blockMap, recommendPosts, prev, n {/* Notion文章主体 */}
- {blockMap && ( + {post.blockMap && (
- {/* 推荐文章 */} - - {/* 版权声明 */} + {/* 推荐文章 */} + + {/* 标签列表 */}
{post.tagItems && ( @@ -165,7 +178,7 @@ export default function ArticleDetail ({ post, blockMap, recommendPosts, prev, n
{/* 评论互动 */} -
+
@@ -177,7 +190,6 @@ export default function ArticleDetail ({ post, blockMap, recommendPosts, prev, n
{/* 宠物 */} - {BLOG.showPet && } ) diff --git a/components/BlogPostCard.js b/components/BlogPostCard.js index 5785168e..6811ff93 100644 --- a/components/BlogPostCard.js +++ b/components/BlogPostCard.js @@ -3,24 +3,25 @@ import Link from 'next/link' import React from 'react' import Image from 'next/image' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faFolder } from '@fortawesome/free-solid-svg-icons' +import { faAngleDoubleRight, faFolder } from '@fortawesome/free-solid-svg-icons' import TagItemMini from './TagItemMini' +import { Code, Collection, CollectionRow, Equation, NotionRenderer } from 'react-notion-x' +import { useGlobal } from '@/lib/global' -const BlogPostCard = ({ post, tags }) => { +const BlogPostCard = ({ post, showSummary }) => { + const { locale } = useGlobal() return ( -
-
+
- + {post.title} -

{post.summary}

- - - {post?.page_cover && ( + {BLOG.home?.showPostCover && post?.page_cover && ( -
+
{post.title}
@@ -50,4 +76,8 @@ const BlogPostCard = ({ post, tags }) => { ) } +const mapPageUrl = id => { + return 'https://www.notion.so/' + id.replace(/-/g, '') +} + export default BlogPostCard diff --git a/components/BlogPostListPage.js b/components/BlogPostListPage.js index fe5fb6b0..4305f986 100644 --- a/components/BlogPostListPage.js +++ b/components/BlogPostListPage.js @@ -1,8 +1,6 @@ import BlogPostCard from '@/components/BlogPostCard' -import Pagination from '@/components/Pagination' +import PaginationNumber from './PaginationNumber' import BLOG from '@/blog.config' - -import { useRouter } from 'next/router' import BlogPostListEmpty from '@/components/BlogPostListEmpty' /** @@ -13,58 +11,23 @@ import BlogPostListEmpty from '@/components/BlogPostListEmpty' * @returns {JSX.Element} * @constructor */ -const BlogPostListPage = ({ page = 1, posts = [], tags }) => { - let filteredBlogPosts = posts +const BlogPostListPage = ({ page = 1, posts = [], postCount }) => { + const totalPage = Math.ceil(postCount / BLOG.postsPerPage) - // 处理查询过滤 支持标签、关键词过滤 - let currentSearch = '' - const router = useRouter() - if (router.query && router.query.s) { - currentSearch = router.query.s - filteredBlogPosts = posts.filter(post => { - const tagContent = post.tags ? post.tags.join(' ') : '' - const searchContent = post.title + post.summary + tagContent + post.slug - return searchContent.toLowerCase().includes(currentSearch.toLowerCase()) - }) - } - - // 处理分页 - const totalPages = Math.ceil(filteredBlogPosts.length / BLOG.postsPerPage) - const postsToShow = filteredBlogPosts.slice( - BLOG.postsPerPage * (page - 1), - BLOG.postsPerPage * page - ) - let showNext = false - if (filteredBlogPosts) { - const totalPosts = filteredBlogPosts.length - showNext = page * BLOG.postsPerPage < totalPosts - } - - if (!postsToShow || postsToShow.length === 0) { + if (!posts || posts.length === 0) { return } else { - return
- {(!page || page === 1) && (
)} - - {(page && page !== 1) && ( -
-
- {page && page !== 1 && (页 {page} / {totalPages})} -
-
- )} - -
+ return ( +
{/* 文章列表 */} -
- {postsToShow.map(post => ( - +
+ {posts.map(post => ( + ))}
- - +
-
+ ) } } diff --git a/components/BlogPostListScroll.js b/components/BlogPostListScroll.js index e272ed49..07fd9be1 100644 --- a/components/BlogPostListScroll.js +++ b/components/BlogPostListScroll.js @@ -1,10 +1,9 @@ -import BlogPostCard from '@/components/BlogPostCard' import BLOG from '@/blog.config' - -import React, { useCallback, useEffect, useRef, useState } from 'react' -import throttle from 'lodash.throttle' +import BlogPostCard from '@/components/BlogPostCard' import BlogPostListEmpty from '@/components/BlogPostListEmpty' import { useGlobal } from '@/lib/global' +import throttle from 'lodash.throttle' +import React, { useCallback, useEffect, useRef, useState } from 'react' /** * 博客列表滚动分页 @@ -14,7 +13,7 @@ import { useGlobal } from '@/lib/global' * @returns {JSX.Element} * @constructor */ -const BlogPostListScroll = ({ posts = [], tags, currentSearch, currentCategory, currentTag }) => { +const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = BLOG.home.showSummary }) => { const postsPerPage = BLOG.postsPerPage const [page, updatePage] = useState(1) const postsToShow = getPostByPage(page, posts, postsPerPage) @@ -53,12 +52,12 @@ const BlogPostListScroll = ({ posts = [], tags, currentSearch, currentCategory, if (!postsToShow || postsToShow.length === 0) { return } else { - return
+ return
{/* 文章列表 */} -
+
{postsToShow.map(post => ( - + ))}
diff --git a/components/CategoryList.js b/components/CategoryList.js index 1f7ed378..cc876f82 100644 --- a/components/CategoryList.js +++ b/components/CategoryList.js @@ -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
    -
  • {locale.COMMON.CATEGORY}
  • +
  • {locale.COMMON.CATEGORY}
  • {Object.keys(categories).map(category => { const selected = category === currentCategory return ( diff --git a/components/Comment.js b/components/Comment.js index d41d7de4..ea747509 100644 --- a/components/Comment.js +++ b/components/Comment.js @@ -26,42 +26,46 @@ const Comment = ({ frontMatter }) => { const router = useRouter() const { theme } = useGlobal() - return
    - {BLOG.comment.provider === 'gitalk' && (
    - -
    )} - {BLOG.comment.provider === 'utterances' && (
    - -
    - )} - {BLOG.comment.provider === 'cusdis' && (<> - + )} + {/* 代码统计 */} {BLOG.isProd && (<> diff --git a/components/Toc.js b/components/Toc.js index 8bc6877e..73df6bd7 100644 --- a/components/Toc.js +++ b/components/Toc.js @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react' +import React from 'react' import throttle from 'lodash.throttle' import { uuidToId } from 'notion-utils' import Progress from './Progress' @@ -12,8 +12,9 @@ import Progress from './Progress' */ const Toc = ({ toc, targetRef }) => { // 无目录就直接返回空 - if (!toc || toc.length < 1) return <> - + if (!toc || toc.length < 1) { + return <> + } // 监听滚动事件 React.useEffect(() => { window.addEventListener('scroll', actionSectionScrollSpy) @@ -26,7 +27,7 @@ const Toc = ({ toc, targetRef }) => { // 同步选中目录事件 const [activeSection, setActiveSection] = React.useState(null) const throttleMs = 100 - const actionSectionScrollSpy = useCallback(throttle(() => { + const actionSectionScrollSpy = React.useCallback(throttle(() => { const sections = document.getElementsByClassName('notion-h') let prevBBox = null let currentSectionId = activeSection @@ -63,14 +64,14 @@ const Toc = ({ toc, targetRef }) => { key={id} href={`#${id}`} className={`notion-table-of-contents-item duration-300 transform font-light - notion-table-of-contents-item-indent-level-${tocItem.indentLevel} - ${activeSection === id && ' font-bold text-gray-600 dark:text-gray-300'}`} + notion-table-of-contents-item-indent-level-${tocItem.indentLevel} `} > {tocItem.text} diff --git a/components/TocDrawerButton.js b/components/TocDrawerButton.js index f0ea1386..9a884286 100644 --- a/components/TocDrawerButton.js +++ b/components/TocDrawerButton.js @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react' import { useGlobal } from '@/lib/global' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faListOl } from '@fortawesome/free-solid-svg-icons' +import BLOG from '@/blog.config' /** * 点击召唤目录抽屉 @@ -11,6 +12,9 @@ import { faListOl } from '@fortawesome/free-solid-svg-icons' * @constructor */ const TocDrawerButton = (props) => { + if (!BLOG.widget?.showToc) { + return <> + } const { locale } = useGlobal() const [show, switchShow] = useState(false) const scrollListener = () => { @@ -27,10 +31,10 @@ const TocDrawerButton = (props) => { }) return ( -
    -
    -
    - +
    +
    +
    +
    diff --git a/components/TopNav.js b/components/TopNav.js index 5d557007..20fe43d8 100644 --- a/components/TopNav.js +++ b/components/TopNav.js @@ -3,10 +3,10 @@ import SideBarDrawer from '@/components/SideBarDrawer' import { useGlobal } from '@/lib/global' import { faBars, faSearch } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import Link from 'next/link' -import { useCallback, useEffect, useRef } from 'react' -import SearchDrawer from './SearchDrawer' import throttle from 'lodash.throttle' +import { useCallback, useEffect, useRef } from 'react' +import Logo from './Logo' +import SearchDrawer from './SearchDrawer' let windowTop = 0 @@ -15,7 +15,7 @@ let windowTop = 0 * @param {*} param0 * @returns */ -const TopNav = ({ tags, currentTag, post, posts, categories, currentCategory, autoHide = true }) => { +const TopNav = ({ tags, currentTag, post, slot, categories, currentCategory, autoHide = true }) => { const drawer = useRef() const { locale } = useGlobal() const searchDrawer = useRef() @@ -35,38 +35,36 @@ const TopNav = ({ tags, currentTag, post, posts, categories, currentCategory, au // 监听滚动 useEffect(() => { - if (autoHide) { + if (BLOG.topNavType === 'autoCollapse') { scrollTrigger() window.addEventListener('scroll', scrollTrigger) } return () => { - autoHide && window.removeEventListener('scroll', scrollTrigger) + BLOG.autoCollapsedNavBar && window.removeEventListener('scroll', scrollTrigger) } }, []) - return (
    + return (
    {/* 侧面抽屉 */} - + {/* 导航栏 */} -