mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-06-08 07:26:47 +00:00
feature: 剥离主题目录
This commit is contained in:
@@ -1,22 +0,0 @@
|
|||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 统计网站信息
|
|
||||||
* @param {*} param0
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export default function Analytics ({ postCount }) {
|
|
||||||
const { locale } = useGlobal()
|
|
||||||
|
|
||||||
return <>
|
|
||||||
{/* <div className='px-5 text-sm font-light pb-1 text-gray-600 dark:text-gray-200'><FontAwesomeIcon icon={faChartBar} className='mr-2' />{locale.COMMON.ANALYTICS}</div> */}
|
|
||||||
<div className='mt-2 text-center dark:text-gray-300 font-light text-xs'>
|
|
||||||
<span className='px-1 '>
|
|
||||||
<strong className='font-medium'>{postCount}</strong>{locale.COMMON.POSTS}</span>
|
|
||||||
<span className='px-1 busuanzi_container_site_uv hidden'>
|
|
||||||
| <strong className='pl-1 busuanzi_value_site_uv font-medium'></strong>{locale.COMMON.VISITORS}</span>
|
|
||||||
{/* <span className='px-1 busuanzi_container_site_pv hidden'>
|
|
||||||
| <strong className='pl-1 busuanzi_value_site_pv font-medium'></strong>{locale.COMMON.VIEWS}</span> */}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ import BLOG from '@/blog.config'
|
|||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const ThirdPartyScript = () => {
|
const CommonScript = () => {
|
||||||
return (<>
|
return (<>
|
||||||
{BLOG.comment?.DaoVoiceId && (<>
|
{BLOG.comment?.DaoVoiceId && (<>
|
||||||
{/* DaoVoice 反馈 */}
|
{/* DaoVoice 反馈 */}
|
||||||
@@ -109,4 +109,4 @@ const ThirdPartyScript = () => {
|
|||||||
</>)
|
</>)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ThirdPartyScript
|
export default CommonScript
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
"@/*": ["./*"],
|
"@/*": ["./*"],
|
||||||
"@/components/*": ["components/*"],
|
"@/components/*": ["components/*"],
|
||||||
"@/data/*": ["data/*"],
|
"@/data/*": ["data/*"],
|
||||||
"@/layouts/*": ["layouts/*"],
|
"@/layouts/*": ["theme/*"],
|
||||||
"@/lib/*": ["lib/*"],
|
"@/lib/*": ["lib/*"],
|
||||||
"@/styles/*": ["styles/*"]
|
"@/styles/*": ["styles/*"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ import { getAllTags } from './getAllTags'
|
|||||||
* 获取博客数据
|
* 获取博客数据
|
||||||
* @param {*} pageId
|
* @param {*} pageId
|
||||||
* @param {*} from
|
* @param {*} from
|
||||||
* @returns
|
* @param latestPostCount 截取最新文章数量
|
||||||
|
* @param tagsCount 截取标签数量
|
||||||
|
* @param includePage 是否包含PAGE类型
|
||||||
|
* @returns { allPosts: '文章列表', latestPosts: ’最新文章, categories:‘分类列表’, postCount:'文章总数',tags:'标签列表' }
|
||||||
* allPosts 所有博客
|
* allPosts 所有博客
|
||||||
* categories 所有分类
|
* categories 所有分类
|
||||||
* tags 所有标签
|
* tags 所有标签
|
||||||
|
|||||||
33
pages/404.js
33
pages/404.js
@@ -1,38 +1,11 @@
|
|||||||
|
import { Custom404Layout } from '@/themes'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义404界面
|
* 自定义404界面
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
import { useEffect } from 'react'
|
|
||||||
import BaseLayout from '@/layouts/BaseLayout'
|
|
||||||
import BLOG from '@/blog.config'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
||||||
import { faSpinner } from '@fortawesome/free-solid-svg-icons'
|
|
||||||
|
|
||||||
export default function Custom404 () {
|
export default function Custom404 () {
|
||||||
const router = useRouter()
|
return <Custom404Layout />
|
||||||
useEffect(() => {
|
|
||||||
// 延时3秒如果加载失败就返回首页
|
|
||||||
setTimeout(() => {
|
|
||||||
if (window) {
|
|
||||||
const article = document.getElementById('container')
|
|
||||||
if (!article) {
|
|
||||||
router.push('/')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 30000000)
|
|
||||||
})
|
|
||||||
|
|
||||||
return <BaseLayout meta={{ title: `${BLOG.title} | 页面找不到啦` }}>
|
|
||||||
<div
|
|
||||||
className='md:-mt-20 text-black w-full h-screen text-center justify-center content-center items-center flex flex-col'>
|
|
||||||
<div className='dark:text-gray-200'>
|
|
||||||
<h2 className='inline-block border-r-2 border-gray-600 mr-2 px-3 py-2 align-top'><FontAwesomeIcon icon={faSpinner} spin={true} className='mr-2'/>404</h2>
|
|
||||||
<div className='inline-block text-left h-32 leading-10 items-center'>
|
|
||||||
<h2 className='m-0 p-0'>页面无法加载,即将返回首页</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</BaseLayout>
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// eslint-disable-next-line @next/next/no-document-import-in-page
|
// eslint-disable-next-line @next/next/no-document-import-in-page
|
||||||
import Document, { Html, Head, Main, NextScript } from 'next/document'
|
import Document, { Html, Head, Main, NextScript } from 'next/document'
|
||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import ThirdPartyScript from '@/components/ThirdPartyScript'
|
import CommonScript from '@/components/CommonScript'
|
||||||
|
|
||||||
class MyDocument extends Document {
|
class MyDocument extends Document {
|
||||||
static async getInitialProps (ctx) {
|
static async getInitialProps (ctx) {
|
||||||
@@ -15,7 +15,7 @@ class MyDocument extends Document {
|
|||||||
<Head>
|
<Head>
|
||||||
<link rel='icon' href='/favicon.ico' />
|
<link rel='icon' href='/favicon.ico' />
|
||||||
<link rel='icon' href='/favicon.svg' type='image/svg+xml' />
|
<link rel='icon' href='/favicon.svg' type='image/svg+xml' />
|
||||||
<ThirdPartyScript />
|
<CommonScript />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<body className={`${BLOG.font} bg-day dark:bg-night duration-200`}>
|
<body className={`${BLOG.font} bg-day dark:bg-night duration-200`}>
|
||||||
|
|||||||
@@ -1,58 +1,40 @@
|
|||||||
import BLOG from '@/blog.config'
|
import { getPostBlocks } from '@/lib/notion'
|
||||||
import ArticleDetail from '@/components/ArticleDetail'
|
|
||||||
import BaseLayout from '@/layouts/BaseLayout'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
import {
|
|
||||||
getPostBlocks
|
|
||||||
} from '@/lib/notion'
|
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
import Custom404 from '@/pages/404'
|
import Custom404 from '@/pages/404'
|
||||||
import 'prismjs'
|
|
||||||
import 'prismjs/components/prism-bash'
|
|
||||||
import 'prismjs/components/prism-javascript'
|
|
||||||
import 'prismjs/components/prism-markup'
|
|
||||||
import 'prismjs/components/prism-python'
|
|
||||||
import 'prismjs/components/prism-typescript'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { ArticleLayout } from '@/themes'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关于页面,默认取notion中slug为about的文章
|
* 关于页面,默认取notion中slug为about的文章
|
||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const About = ({ post, tags, prev, next, postCount, categories }) => {
|
const About = (props) => {
|
||||||
if (!post) {
|
if (!props.post) {
|
||||||
return <Custom404 />
|
return <Custom404 />
|
||||||
}
|
}
|
||||||
const { locale } = useGlobal()
|
return <ArticleLayout {...props} />
|
||||||
post.title = locale.NAV.ABOUT
|
|
||||||
const meta = {
|
|
||||||
title: `${locale.NAV.ABOUT} | ${BLOG.title} `,
|
|
||||||
description: post.summary,
|
|
||||||
type: 'post',
|
|
||||||
tags: []
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseLayout
|
|
||||||
meta={meta}
|
|
||||||
tags={tags}
|
|
||||||
post={post}
|
|
||||||
postCount={postCount}
|
|
||||||
categories={categories}
|
|
||||||
>
|
|
||||||
<ArticleDetail post={post} prev={prev} next={next} />
|
|
||||||
</BaseLayout>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStaticProps () {
|
export async function getStaticProps () {
|
||||||
const from = 'about-props'
|
const from = 'about-props'
|
||||||
const { allPosts, categories, tags, postCount, latestPosts } = await getGlobalNotionData({ from, includePage: true })
|
const {
|
||||||
|
allPosts,
|
||||||
|
categories,
|
||||||
|
tags,
|
||||||
|
postCount,
|
||||||
|
latestPosts
|
||||||
|
} = await getGlobalNotionData({
|
||||||
|
from,
|
||||||
|
includePage: true
|
||||||
|
})
|
||||||
const post = allPosts.find(p => p.slug === 'about')
|
const post = allPosts.find(p => p.slug === 'about')
|
||||||
|
|
||||||
if (!post) {
|
if (!post) {
|
||||||
return { props: {}, revalidate: 1 }
|
return {
|
||||||
|
props: {},
|
||||||
|
revalidate: 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
post.blockMap = await getPostBlocks(post.id, 'slug')
|
post.blockMap = await getPostBlocks(post.id, 'slug')
|
||||||
@@ -62,7 +44,15 @@ export async function getStaticProps () {
|
|||||||
const next = allPosts.slice(index + 1, index + 2)[0] ?? allPosts[0]
|
const next = allPosts.slice(index + 1, index + 2)[0] ?? allPosts[0]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: { post, tags, prev, next, categories, postCount, latestPosts },
|
props: {
|
||||||
|
post,
|
||||||
|
tags,
|
||||||
|
prev,
|
||||||
|
next,
|
||||||
|
categories,
|
||||||
|
postCount,
|
||||||
|
latestPosts
|
||||||
|
},
|
||||||
revalidate: 1
|
revalidate: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
import BLOG from '@/blog.config'
|
|
||||||
import BlogPostArchive from '@/components/BlogPostArchive'
|
|
||||||
import Live2D from '@/components/Live2D'
|
|
||||||
import BaseLayout from '@/layouts/BaseLayout'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
import React, { useEffect } from 'react'
|
import React from 'react'
|
||||||
|
import { ArchiveLayout } from '@/themes'
|
||||||
|
|
||||||
export async function getStaticProps () {
|
export async function getStaticProps () {
|
||||||
const from = 'index'
|
|
||||||
const { allPosts, categories, tags, postCount } =
|
const { allPosts, categories, tags, postCount } =
|
||||||
await getGlobalNotionData({ from })
|
await getGlobalNotionData({ from: 'archive-index' })
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
@@ -22,63 +17,8 @@ export async function getStaticProps () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Index = ({ posts, tags, categories, postCount }) => {
|
const ArchiveIndex = (props) => {
|
||||||
const { locale } = useGlobal()
|
return <ArchiveLayout {...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 (
|
|
||||||
<BaseLayout meta={meta} tags={tags} categories={categories} postCount={postCount}>
|
|
||||||
<div className="mb-10 pb-20 bg-white md:p-12 p-3 dark:bg-gray-800 shadow-md min-h-full">
|
|
||||||
{Object.keys(archivePosts).map(archiveTitle => (
|
|
||||||
<BlogPostArchive
|
|
||||||
key={archiveTitle}
|
|
||||||
posts={archivePosts[archiveTitle]}
|
|
||||||
archiveTitle={archiveTitle}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<Live2D />
|
|
||||||
</BaseLayout>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Index
|
export default ArchiveIndex
|
||||||
|
|||||||
@@ -1,78 +1,19 @@
|
|||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import ArticleDetail from '@/components/ArticleDetail'
|
|
||||||
import Card from '@/components/Card'
|
|
||||||
import LatestPostsGroup from '@/components/LatestPostsGroup'
|
|
||||||
import Live2D from '@/components/Live2D'
|
|
||||||
import TocDrawer from '@/components/TocDrawer'
|
|
||||||
import TocDrawerButton from '@/components/TocDrawerButton'
|
|
||||||
import BaseLayout from '@/layouts/BaseLayout'
|
|
||||||
import { getPostBlocks } from '@/lib/notion'
|
import { getPostBlocks } from '@/lib/notion'
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
|
import { ArticleLayout } from '@/themes'
|
||||||
import Custom404 from '@/pages/404'
|
import Custom404 from '@/pages/404'
|
||||||
import { getPageTableOfContents } from 'notion-utils'
|
|
||||||
import { useRef } from 'react'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据notion的slug访问页面
|
* 根据notion的slug访问页面
|
||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const Slug = ({
|
const Slug = (props) => {
|
||||||
post,
|
if (!props.post) {
|
||||||
tags,
|
|
||||||
prev,
|
|
||||||
next,
|
|
||||||
recommendPosts,
|
|
||||||
categories,
|
|
||||||
postCount,
|
|
||||||
latestPosts
|
|
||||||
}) => {
|
|
||||||
if (!post) {
|
|
||||||
return <Custom404 />
|
return <Custom404 />
|
||||||
}
|
}
|
||||||
const meta = {
|
return <ArticleLayout {...props}/>
|
||||||
title: `${post.title} | ${BLOG.title}`,
|
|
||||||
description: post.summary,
|
|
||||||
type: 'article',
|
|
||||||
tags: post.tags
|
|
||||||
}
|
|
||||||
|
|
||||||
const drawerRight = useRef(null)
|
|
||||||
const targetRef = typeof window !== 'undefined' ? document.getElementById('container') : null
|
|
||||||
post.content = Object.keys(post?.blockMap?.block)
|
|
||||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
|
||||||
const floatSlot = post?.toc?.length > 1 ? <div className="block lg:hidden"><TocDrawerButton onClick={() => { drawerRight?.current?.handleSwitchVisible() }} /></div> : null
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseLayout
|
|
||||||
meta={meta}
|
|
||||||
tags={tags}
|
|
||||||
post={post}
|
|
||||||
postCount={postCount}
|
|
||||||
latestPosts={latestPosts}
|
|
||||||
categories={categories}
|
|
||||||
floatSlot={floatSlot}
|
|
||||||
rightAreaSlot={
|
|
||||||
BLOG.widget?.showLatestPost && <Card><LatestPostsGroup posts={latestPosts} /></Card>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ArticleDetail
|
|
||||||
post={post}
|
|
||||||
recommendPosts={recommendPosts}
|
|
||||||
prev={prev}
|
|
||||||
next={next}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* 悬浮目录按钮 */}
|
|
||||||
<div className="block lg:hidden">
|
|
||||||
<TocDrawer post={post} cRef={drawerRight} targetRef={targetRef} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 宠物 */}
|
|
||||||
<Live2D/>
|
|
||||||
|
|
||||||
</BaseLayout>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStaticPaths () {
|
export async function getStaticPaths () {
|
||||||
|
|||||||
@@ -1,33 +1,21 @@
|
|||||||
import BLOG from '@/blog.config'
|
|
||||||
import BlogPostListScroll from '@/components/BlogPostListScroll'
|
|
||||||
import CategoryList from '@/components/CategoryList'
|
|
||||||
import StickyBar from '@/components/StickyBar'
|
|
||||||
import BaseLayout from '@/layouts/BaseLayout'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { CategoryLayout } from '@/themes'
|
||||||
|
|
||||||
export default function Category ({ tags, posts, category, categories, latestPosts, postCount }) {
|
export default function Category (props) {
|
||||||
const { locale } = useGlobal()
|
return <CategoryLayout {...props} />
|
||||||
const meta = {
|
|
||||||
title: `${category} | ${locale.COMMON.CATEGORY} | ${BLOG.title}`,
|
|
||||||
description: BLOG.description,
|
|
||||||
type: 'website'
|
|
||||||
}
|
|
||||||
return <BaseLayout meta={meta} tags={tags} currentCategory={category} postCount={postCount} latestPosts={latestPosts} categories={categories}>
|
|
||||||
<StickyBar>
|
|
||||||
<CategoryList currentCategory={category} categories={categories} />
|
|
||||||
</StickyBar>
|
|
||||||
<div className='md:mt-8'>
|
|
||||||
<BlogPostListScroll posts={posts} tags={tags} currentCategory={category}/>
|
|
||||||
</div>
|
|
||||||
</BaseLayout>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStaticProps ({ params }) {
|
export async function getStaticProps ({ params }) {
|
||||||
const from = 'category-props'
|
const from = 'category-props'
|
||||||
const category = params.category
|
const category = params.category
|
||||||
const { allPosts, categories, tags, postCount, latestPosts } = await getGlobalNotionData({ from })
|
const {
|
||||||
|
allPosts,
|
||||||
|
categories,
|
||||||
|
tags,
|
||||||
|
postCount,
|
||||||
|
latestPosts
|
||||||
|
} = await getGlobalNotionData({ from })
|
||||||
const filteredPosts = allPosts.filter(
|
const filteredPosts = allPosts.filter(
|
||||||
post => post && post.category && post.category.includes(category)
|
post => post && post.category && post.category.includes(category)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,32 +1,9 @@
|
|||||||
import BLOG from '@/blog.config'
|
|
||||||
import BaseLayout from '@/layouts/BaseLayout'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
import { faFolder, faThList } from '@fortawesome/free-solid-svg-icons'
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { CategoryIndexLayout } from '@/themes'
|
||||||
|
|
||||||
export default function Category ({ tags, allPosts, categories, postCount, latestPosts }) {
|
export default function Category (props) {
|
||||||
const { locale } = useGlobal()
|
return <CategoryIndexLayout {...props}/>
|
||||||
const meta = {
|
|
||||||
title: `${locale.COMMON.CATEGORY} | ${BLOG.title}`,
|
|
||||||
description: BLOG.description,
|
|
||||||
type: 'website'
|
|
||||||
}
|
|
||||||
return <BaseLayout meta={meta} totalPosts={allPosts} tags={tags} postCount={postCount} latestPosts={latestPosts}>
|
|
||||||
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow'>
|
|
||||||
<div className='dark:text-gray-200 mb-5'><FontAwesomeIcon icon={faThList} className='mr-4' />{locale.COMMON.CATEGORY}:</div>
|
|
||||||
<div id='category-list' className='duration-200 flex flex-wrap'>
|
|
||||||
{Object.keys(categories).map(category => {
|
|
||||||
return <Link key={category} href={`/category/${category}`} passHref>
|
|
||||||
<div className={'hover:text-black dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600 px-5 cursor-pointer py-2 hover:bg-gray-100'}>
|
|
||||||
<FontAwesomeIcon icon={faFolder} className='mr-4' />{category}({categories[category]})</div>
|
|
||||||
</Link>
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</BaseLayout>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStaticProps () {
|
export async function getStaticProps () {
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import BlogPostListPage from '@/components/BlogPostListPage'
|
|
||||||
import BlogPostListScroll from '@/components/BlogPostListScroll'
|
|
||||||
import Card from '@/components/Card'
|
|
||||||
import Header from '@/components/Header'
|
|
||||||
import LatestPostsGroup from '@/components/LatestPostsGroup'
|
|
||||||
import BaseLayout from '@/layouts/BaseLayout'
|
|
||||||
import { getPostBlocks } from '@/lib/notion'
|
import { getPostBlocks } from '@/lib/notion'
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
|
import { IndexLayout } from '@/themes'
|
||||||
|
|
||||||
|
const Index = (props) => {
|
||||||
|
return <IndexLayout {...props}/>
|
||||||
|
}
|
||||||
|
|
||||||
export async function getStaticProps () {
|
export async function getStaticProps () {
|
||||||
const from = 'index'
|
const from = 'index'
|
||||||
@@ -49,28 +48,4 @@ export async function getStaticProps () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Index = ({ posts, tags, meta, categories, postCount, latestPosts }) => {
|
|
||||||
return (
|
|
||||||
<BaseLayout
|
|
||||||
headerSlot={BLOG.home.showHomeBanner && <Header />}
|
|
||||||
meta={meta}
|
|
||||||
tags={tags}
|
|
||||||
sideBarSlot={<LatestPostsGroup posts={latestPosts} />}
|
|
||||||
rightAreaSlot={
|
|
||||||
BLOG.widget?.showLatestPost && <Card><LatestPostsGroup posts={latestPosts} /></Card>
|
|
||||||
}
|
|
||||||
postCount={postCount}
|
|
||||||
categories={categories}
|
|
||||||
>
|
|
||||||
{BLOG.postListStyle !== 'page'
|
|
||||||
? (
|
|
||||||
<BlogPostListScroll posts={posts} tags={tags} showSummary={true} />
|
|
||||||
)
|
|
||||||
: (
|
|
||||||
<BlogPostListPage posts={posts} tags={tags} postCount={postCount} />
|
|
||||||
)}
|
|
||||||
</BaseLayout>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Index
|
export default Index
|
||||||
|
|||||||
@@ -1,30 +1,10 @@
|
|||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import BlogPostListPage from '@/components/BlogPostListPage'
|
|
||||||
import Header from '@/components/Header'
|
|
||||||
import LatestPostsGroup from '@/components/LatestPostsGroup'
|
|
||||||
import BaseLayout from '@/layouts/BaseLayout'
|
|
||||||
import { getPostBlocks } from '@/lib/notion'
|
import { getPostBlocks } from '@/lib/notion'
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
import Custom404 from '../404'
|
import { PageLayout } from '@/themes'
|
||||||
|
|
||||||
const Page = ({ page, posts, tags, meta, categories, postCount, latestPosts }) => {
|
const Page = (props) => {
|
||||||
if (!meta || BLOG.postListStyle !== 'page') {
|
return <PageLayout {...props} />
|
||||||
return <Custom404/>
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseLayout
|
|
||||||
headerSlot={BLOG.home.showHomeBanner && <Header />}
|
|
||||||
meta={meta}
|
|
||||||
tags={tags}
|
|
||||||
sideBarSlot={<LatestPostsGroup posts={latestPosts} />}
|
|
||||||
rightAreaSlot={BLOG.widget?.showLatestPost && <LatestPostsGroup posts={latestPosts} />}
|
|
||||||
postCount={postCount}
|
|
||||||
categories={categories}
|
|
||||||
>
|
|
||||||
<BlogPostListPage page={page} posts={posts} postCount={postCount} />
|
|
||||||
</BaseLayout>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStaticPaths () {
|
export async function getStaticPaths () {
|
||||||
@@ -40,7 +20,13 @@ export async function getStaticPaths () {
|
|||||||
|
|
||||||
export async function getStaticProps ({ params: { page } }) {
|
export async function getStaticProps ({ params: { page } }) {
|
||||||
const from = `page-${page}`
|
const from = `page-${page}`
|
||||||
const { allPosts, latestPosts, categories, tags, postCount } = await getGlobalNotionData({ from })
|
const {
|
||||||
|
allPosts,
|
||||||
|
latestPosts,
|
||||||
|
categories,
|
||||||
|
tags,
|
||||||
|
postCount
|
||||||
|
} = await getGlobalNotionData({ from })
|
||||||
const meta = {
|
const meta = {
|
||||||
title: `${page} | Page | ${BLOG.title}`,
|
title: `${page} | Page | ${BLOG.title}`,
|
||||||
description: BLOG.description,
|
description: BLOG.description,
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
import BLOG from '@/blog.config'
|
|
||||||
import BlogPostListScroll from '@/components/BlogPostListScroll'
|
|
||||||
import StickyBar from '@/components/StickyBar'
|
|
||||||
import BaseLayout from '@/layouts/BaseLayout'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
import { faSearch } from '@fortawesome/free-solid-svg-icons'
|
import { SearchLayout } from '@/themes'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
|
|
||||||
export async function getStaticProps () {
|
export async function getStaticProps () {
|
||||||
const from = 'search-props'
|
const {
|
||||||
const { allPosts, categories, tags, postCount, latestPosts } =
|
allPosts,
|
||||||
await getGlobalNotionData({ from })
|
categories,
|
||||||
|
tags,
|
||||||
|
postCount,
|
||||||
|
latestPosts
|
||||||
|
} = await getGlobalNotionData({ from: 'search-props' })
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
posts: allPosts,
|
posts: allPosts,
|
||||||
@@ -25,51 +21,8 @@ export async function getStaticProps () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Search = ({ posts, tags, categories, postCount }) => {
|
const Search = (props) => {
|
||||||
let filteredPosts = []
|
return <SearchLayout {...props} />
|
||||||
const searchKey = getSearchKey()
|
|
||||||
if (searchKey) {
|
|
||||||
filteredPosts = posts.filter(post => {
|
|
||||||
const tagContent = post.tags ? post.tags.join(' ') : ''
|
|
||||||
const searchContent = post.title + post.summary + tagContent
|
|
||||||
return searchContent.toLowerCase().includes(searchKey.toLowerCase())
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
filteredPosts = posts
|
|
||||||
}
|
|
||||||
|
|
||||||
const { locale } = useGlobal()
|
|
||||||
const meta = {
|
|
||||||
title: `${searchKey || ''} | ${locale.NAV.SEARCH} | ${BLOG.title} `,
|
|
||||||
description: BLOG.description,
|
|
||||||
type: 'website'
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<BaseLayout
|
|
||||||
meta={meta}
|
|
||||||
tags={tags}
|
|
||||||
postCount={postCount}
|
|
||||||
currentSearch={searchKey}
|
|
||||||
categories={categories}
|
|
||||||
>
|
|
||||||
<StickyBar>
|
|
||||||
<div className="p-4 dark:text-gray-200">
|
|
||||||
<FontAwesomeIcon icon={faSearch} className="mr-1" />{' '}
|
|
||||||
{filteredPosts.length} {locale.COMMON.RESULT_OF_SEARCH}
|
|
||||||
</div>
|
|
||||||
</StickyBar>
|
|
||||||
<div className="md:mt-5">
|
|
||||||
<BlogPostListScroll posts={filteredPosts} tags={tags} showSummary={true}/>
|
|
||||||
</div>
|
|
||||||
</BaseLayout>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSearchKey () {
|
|
||||||
const router = useRouter()
|
|
||||||
if (router.query && router.query.s) {
|
|
||||||
return router.query.s
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
export default Search
|
export default Search
|
||||||
|
|||||||
@@ -1,39 +1,24 @@
|
|||||||
import BLOG from '@/blog.config'
|
|
||||||
import BlogPostListScroll from '@/components/BlogPostListScroll'
|
|
||||||
import StickyBar from '@/components/StickyBar'
|
|
||||||
import TagList from '@/components/TagList'
|
|
||||||
import BaseLayout from '@/layouts/BaseLayout'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
|
import { TagLayout } from '@/themes'
|
||||||
|
|
||||||
export default function Tag ({ tags, posts, tag, categories, postCount, latestPosts }) {
|
const Tag = (props) => {
|
||||||
const { locale } = useGlobal()
|
return <TagLayout {...props} />
|
||||||
|
|
||||||
const meta = {
|
|
||||||
title: `${tag} | ${locale.COMMON.TAGS} | ${BLOG.title}`,
|
|
||||||
description: BLOG.description,
|
|
||||||
type: 'website'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将当前选中的标签置顶🔝
|
|
||||||
if (!tags) tags = []
|
|
||||||
const currentTag = tags?.find(r => r?.name === tag)
|
|
||||||
const newTags = currentTag ? [currentTag].concat(tags.filter(r => r?.name !== tag)) : tags.filter(r => r?.name !== tag)
|
|
||||||
|
|
||||||
return <BaseLayout meta={meta} tags={tags} currentTag={tag} categories={categories} postCount={postCount} latestPosts={latestPosts}>
|
|
||||||
<StickyBar>
|
|
||||||
<TagList tags={newTags} currentTag={tag}/>
|
|
||||||
</StickyBar>
|
|
||||||
<div className='md:mt-8'>
|
|
||||||
<BlogPostListScroll posts={posts} tags={tags} currentTag={tag}/>
|
|
||||||
</div>
|
|
||||||
</BaseLayout>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStaticProps ({ params }) {
|
export async function getStaticProps ({ params }) {
|
||||||
const tag = params.tag
|
const tag = params.tag
|
||||||
const from = 'tag-props'
|
const from = 'tag-props'
|
||||||
const { allPosts, categories, tags, postCount, latestPosts } = await getGlobalNotionData({ from, includePage: true, tagsCount: 0 })
|
const {
|
||||||
|
allPosts,
|
||||||
|
categories,
|
||||||
|
tags,
|
||||||
|
postCount,
|
||||||
|
latestPosts
|
||||||
|
} = await getGlobalNotionData({
|
||||||
|
from,
|
||||||
|
includePage: true,
|
||||||
|
tagsCount: 0
|
||||||
|
})
|
||||||
const filteredPosts = allPosts.filter(
|
const filteredPosts = allPosts.filter(
|
||||||
post => post && post.tags && post.tags.includes(tag)
|
post => post && post.tags && post.tags.includes(tag)
|
||||||
)
|
)
|
||||||
@@ -65,7 +50,10 @@ function getTagNames (tags) {
|
|||||||
|
|
||||||
export async function getStaticPaths () {
|
export async function getStaticPaths () {
|
||||||
const from = 'tag-static-path'
|
const from = 'tag-static-path'
|
||||||
const { tags } = await getGlobalNotionData({ from, tagsCount: 0 })
|
const { tags } = await getGlobalNotionData({
|
||||||
|
from,
|
||||||
|
tagsCount: 0
|
||||||
|
})
|
||||||
const tagNames = getTagNames(tags)
|
const tagNames = getTagNames(tags)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -73,3 +61,5 @@ export async function getStaticPaths () {
|
|||||||
fallback: true
|
fallback: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Tag
|
||||||
|
|||||||
@@ -1,34 +1,23 @@
|
|||||||
import BLOG from '@/blog.config'
|
|
||||||
import TagItem from '@/components/TagItem'
|
|
||||||
import BaseLayout from '@/layouts/BaseLayout'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
import { faTags } from '@fortawesome/free-solid-svg-icons'
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { TagIndexLayout } from '@/themes'
|
||||||
|
|
||||||
export default function Tag ({ tags, categories, postCount, latestPosts }) {
|
const TagIndex = (props) => {
|
||||||
const { locale } = useGlobal()
|
return <TagIndexLayout {...props} />
|
||||||
const meta = {
|
|
||||||
title: `${locale.COMMON.TAGS} | ${BLOG.title}`,
|
|
||||||
description: BLOG.description,
|
|
||||||
type: 'website'
|
|
||||||
}
|
|
||||||
return <BaseLayout meta={meta} categories={categories} postCount={postCount} latestPosts={latestPosts}>
|
|
||||||
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow'>
|
|
||||||
<div className='dark:text-gray-200 mb-5'><FontAwesomeIcon icon={faTags} className='mr-4'/>{locale.COMMON.TAGS}:</div>
|
|
||||||
<div id='tags-list' className='duration-200 flex flex-wrap'>
|
|
||||||
{ tags.map(tag => {
|
|
||||||
return <div key={tag.name} className='p-2'><TagItem key={tag.name} tag={tag} /></div>
|
|
||||||
}) }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</BaseLayout>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStaticProps () {
|
export async function getStaticProps () {
|
||||||
const from = 'tag-index-props'
|
const from = 'tag-index-props'
|
||||||
const { categories, tags, postCount, latestPosts } = await getGlobalNotionData({ from, includePage: true, tagsCount: 0 })
|
const {
|
||||||
|
categories,
|
||||||
|
tags,
|
||||||
|
postCount,
|
||||||
|
latestPosts
|
||||||
|
} = await getGlobalNotionData({
|
||||||
|
from,
|
||||||
|
includePage: true,
|
||||||
|
tagsCount: 0
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
@@ -40,3 +29,5 @@ export async function getStaticProps () {
|
|||||||
revalidate: 1
|
revalidate: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default TagIndex
|
||||||
|
|||||||
67
themes/NEXT/ArchiveLayout.js
Normal file
67
themes/NEXT/ArchiveLayout.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
import React, { useEffect } from 'react'
|
||||||
|
import BaseLayout from './BaseLayout'
|
||||||
|
import BlogPostArchive from './components/BlogPostArchive'
|
||||||
|
import Live2D from './components/Live2D'
|
||||||
|
|
||||||
|
const ArchiveLayout = ({ posts, tags, categories, postCount }) => {
|
||||||
|
const { locale } = useGlobal()
|
||||||
|
// 深拷贝
|
||||||
|
const postsSortByDate = Object.create(posts)
|
||||||
|
|
||||||
|
// 时间排序
|
||||||
|
postsSortByDate.sort((a, b) => {
|
||||||
|
const dateA = new Date(a?.date.start_date || a.createdTime)
|
||||||
|
const dateB = new Date(b?.date.start_date || b.createdTime)
|
||||||
|
return dateB - dateA
|
||||||
|
})
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: `${locale.NAV.ARCHIVE} | ${BLOG.title}`,
|
||||||
|
description: BLOG.description,
|
||||||
|
type: 'website'
|
||||||
|
}
|
||||||
|
|
||||||
|
const archivePosts = {}
|
||||||
|
|
||||||
|
postsSortByDate.forEach(post => {
|
||||||
|
const date = post.date.start_date.slice(0, 7)
|
||||||
|
if (archivePosts[date]) {
|
||||||
|
archivePosts[date].push(post)
|
||||||
|
} else {
|
||||||
|
archivePosts[date] = [post]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (window) {
|
||||||
|
const anchor = window.location.hash
|
||||||
|
if (anchor) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const anchorElement = document.getElementById(anchor.substring(1))
|
||||||
|
if (anchorElement) {
|
||||||
|
anchorElement.scrollIntoView({ block: 'start', behavior: 'smooth' })
|
||||||
|
}
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseLayout meta={meta} tags={tags} categories={categories} postCount={postCount}>
|
||||||
|
<div className="mb-10 pb-20 bg-white md:p-12 p-3 dark:bg-gray-800 shadow-md min-h-full">
|
||||||
|
{Object.keys(archivePosts).map(archiveTitle => (
|
||||||
|
<BlogPostArchive
|
||||||
|
key={archiveTitle}
|
||||||
|
posts={archivePosts[archiveTitle]}
|
||||||
|
archiveTitle={archiveTitle}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<Live2D />
|
||||||
|
</BaseLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ArchiveLayout
|
||||||
78
themes/NEXT/ArticleLayout.js
Normal file
78
themes/NEXT/ArticleLayout.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import BLOG from '@/blog.config'
|
||||||
|
import { getPageTableOfContents } from 'notion-utils'
|
||||||
|
import TocDrawerButton from './components/TocDrawerButton'
|
||||||
|
import BaseLayout from './BaseLayout'
|
||||||
|
import Card from './components/Card'
|
||||||
|
import LatestPostsGroup from './components/LatestPostsGroup'
|
||||||
|
import ArticleDetail from './components/ArticleDetail'
|
||||||
|
import TocDrawer from './components/TocDrawer'
|
||||||
|
import Live2D from './components/Live2D'
|
||||||
|
import { useRef } from 'react'
|
||||||
|
import 'prismjs'
|
||||||
|
import 'prismjs/components/prism-bash'
|
||||||
|
import 'prismjs/components/prism-javascript'
|
||||||
|
import 'prismjs/components/prism-markup'
|
||||||
|
import 'prismjs/components/prism-python'
|
||||||
|
import 'prismjs/components/prism-typescript'
|
||||||
|
import _NEXT from './_NEXT'
|
||||||
|
|
||||||
|
const ArticleLayout = ({
|
||||||
|
post,
|
||||||
|
tags,
|
||||||
|
prev,
|
||||||
|
next,
|
||||||
|
recommendPosts,
|
||||||
|
categories,
|
||||||
|
postCount,
|
||||||
|
latestPosts
|
||||||
|
}) => {
|
||||||
|
const meta = {
|
||||||
|
title: `${post.title} | ${BLOG.title}`,
|
||||||
|
description: post.summary,
|
||||||
|
type: 'article',
|
||||||
|
tags: post.tags
|
||||||
|
}
|
||||||
|
|
||||||
|
const drawerRight = useRef(null)
|
||||||
|
const targetRef = typeof window !== 'undefined' ? document.getElementById('container') : null
|
||||||
|
post.content = Object.keys(post?.blockMap?.block)
|
||||||
|
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||||
|
const floatSlot = post?.toc?.length > 1
|
||||||
|
? <div className='block lg:hidden'><TocDrawerButton onClick={() => {
|
||||||
|
drawerRight?.current?.handleSwitchVisible()
|
||||||
|
}} /></div>
|
||||||
|
: null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseLayout
|
||||||
|
meta={meta}
|
||||||
|
tags={tags}
|
||||||
|
post={post}
|
||||||
|
postCount={postCount}
|
||||||
|
latestPosts={latestPosts}
|
||||||
|
categories={categories}
|
||||||
|
floatSlot={floatSlot}
|
||||||
|
rightAreaSlot={
|
||||||
|
_NEXT.RIGHT_LATEST_POSTS && <Card><LatestPostsGroup posts={latestPosts} /></Card>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ArticleDetail
|
||||||
|
post={post}
|
||||||
|
recommendPosts={recommendPosts}
|
||||||
|
prev={prev}
|
||||||
|
next={next}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 悬浮目录按钮 */}
|
||||||
|
<div className='block lg:hidden'>
|
||||||
|
<TocDrawer post={post} cRef={drawerRight} targetRef={targetRef} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 宠物 */}
|
||||||
|
<Live2D />
|
||||||
|
|
||||||
|
</BaseLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ArticleLayout
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import CommonHead from '@/components/CommonHead'
|
import CommonHead from '@/components/CommonHead'
|
||||||
import FloatDarkModeButton from '@/components/FloatDarkModeButton'
|
import FloatDarkModeButton from './components/FloatDarkModeButton'
|
||||||
import Footer from '@/components/Footer'
|
import Footer from './components/Footer'
|
||||||
import JumpToBottomButton from '@/components/JumpToBottomButton'
|
import JumpToBottomButton from './components/JumpToBottomButton'
|
||||||
import JumpToTopButton from '@/components/JumpToTopButton'
|
import JumpToTopButton from './components/JumpToTopButton'
|
||||||
import LoadingCover from '@/components/LoadingCover'
|
import LoadingCover from './components/LoadingCover'
|
||||||
import SideAreaLeft from '@/components/SideAreaLeft'
|
import SideAreaLeft from './components/SideAreaLeft'
|
||||||
import SideAreaRight from '@/components/SideAreaRight'
|
import SideAreaRight from './components/SideAreaRight'
|
||||||
import TopNav from '@/components/TopNav'
|
import TopNav from './components/TopNav'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import React, { useEffect, useRef, useState } from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
@@ -81,7 +81,7 @@ const BaseLayout = ({
|
|||||||
|
|
||||||
<>{headerSlot}</>
|
<>{headerSlot}</>
|
||||||
|
|
||||||
<div className='h-0.5 w-full bg-gray-700 dark:bg-gray-600 hidden lg:block'></div>
|
<div className='h-0.5 w-full bg-gray-700 dark:bg-gray-600 hidden lg:block'/>
|
||||||
|
|
||||||
<main id='wrapper' className='flex justify-center flex-1 pb-12'>
|
<main id='wrapper' className='flex justify-center flex-1 pb-12'>
|
||||||
<SideAreaLeft targetRef={targetRef} post={post} postCount={postCount} tags={tags} currentSearch={currentSearch} currentTag={currentTag} categories={categories} currentCategory={currentCategory}/>
|
<SideAreaLeft targetRef={targetRef} post={post} postCount={postCount} tags={tags} currentSearch={currentSearch} currentTag={currentTag} categories={categories} currentCategory={currentCategory}/>
|
||||||
40
themes/NEXT/CategoryIndexLayout.js
Normal file
40
themes/NEXT/CategoryIndexLayout.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
import BaseLayout from './BaseLayout'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
import { faFolder, faThList } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
const CategoryIndexLayout = ({
|
||||||
|
tags,
|
||||||
|
allPosts,
|
||||||
|
categories,
|
||||||
|
postCount,
|
||||||
|
latestPosts
|
||||||
|
}) => {
|
||||||
|
const { locale } = useGlobal()
|
||||||
|
const meta = {
|
||||||
|
title: `${locale.COMMON.CATEGORY} | ${BLOG.title}`,
|
||||||
|
description: BLOG.description,
|
||||||
|
type: 'website'
|
||||||
|
}
|
||||||
|
return <BaseLayout meta={meta} totalPosts={allPosts} tags={tags} postCount={postCount} latestPosts={latestPosts}>
|
||||||
|
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow'>
|
||||||
|
<div className='dark:text-gray-200 mb-5'>
|
||||||
|
<FontAwesomeIcon icon={faThList} className='mr-4' />{locale.COMMON.CATEGORY}:
|
||||||
|
</div>
|
||||||
|
<div id='category-list' className='duration-200 flex flex-wrap'>
|
||||||
|
{Object.keys(categories).map(category => {
|
||||||
|
return <Link key={category} href={`/category/${category}`} passHref>
|
||||||
|
<div
|
||||||
|
className={'hover:text-black dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600 px-5 cursor-pointer py-2 hover:bg-gray-100'}>
|
||||||
|
<FontAwesomeIcon icon={faFolder} className='mr-4' />{category}({categories[category]})
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BaseLayout>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CategoryIndexLayout
|
||||||
25
themes/NEXT/CategoryLayout.js
Normal file
25
themes/NEXT/CategoryLayout.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
import BaseLayout from '@/themes/NEXT/BaseLayout'
|
||||||
|
import StickyBar from './components/StickyBar'
|
||||||
|
import CategoryList from './components/CategoryList'
|
||||||
|
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||||
|
|
||||||
|
const Category = ({ tags, posts, category, categories, latestPosts, postCount }) => {
|
||||||
|
const { locale } = useGlobal()
|
||||||
|
const meta = {
|
||||||
|
title: `${category} | ${locale.COMMON.CATEGORY} | ${BLOG.title}`,
|
||||||
|
description: BLOG.description,
|
||||||
|
type: 'website'
|
||||||
|
}
|
||||||
|
return <BaseLayout meta={meta} tags={tags} currentCategory={category} postCount={postCount} latestPosts={latestPosts} categories={categories}>
|
||||||
|
<StickyBar>
|
||||||
|
<CategoryList currentCategory={category} categories={categories} />
|
||||||
|
</StickyBar>
|
||||||
|
<div className='md:mt-8'>
|
||||||
|
<BlogPostListScroll posts={posts} tags={tags} currentCategory={category}/>
|
||||||
|
</div>
|
||||||
|
</BaseLayout>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Category
|
||||||
37
themes/NEXT/Custom404Layout.js
Normal file
37
themes/NEXT/Custom404Layout.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import BaseLayout from './BaseLayout'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
import { faSpinner } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
|
const Custom404Layout = () => {
|
||||||
|
const router = useRouter()
|
||||||
|
useEffect(() => {
|
||||||
|
// 延时3秒如果加载失败就返回首页
|
||||||
|
setTimeout(() => {
|
||||||
|
if (window) {
|
||||||
|
const article = document.getElementById('container')
|
||||||
|
if (!article) {
|
||||||
|
router.push('/').then(() => {
|
||||||
|
console.log('找不到页面', router.asPath)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 30000000)
|
||||||
|
})
|
||||||
|
|
||||||
|
return <BaseLayout meta={{ title: `${BLOG.title} | 页面找不到啦` }}>
|
||||||
|
<div
|
||||||
|
className='md:-mt-20 text-black w-full h-screen text-center justify-center content-center items-center flex flex-col'>
|
||||||
|
<div className='dark:text-gray-200'>
|
||||||
|
<h2 className='inline-block border-r-2 border-gray-600 mr-2 px-3 py-2 align-top'><FontAwesomeIcon icon={faSpinner} spin={true} className='mr-2'/>404</h2>
|
||||||
|
<div className='inline-block text-left h-32 leading-10 items-center'>
|
||||||
|
<h2 className='m-0 p-0'>页面无法加载,即将返回首页</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BaseLayout>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Custom404Layout
|
||||||
31
themes/NEXT/IndexLayout.js
Normal file
31
themes/NEXT/IndexLayout.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import BaseLayout from './BaseLayout'
|
||||||
|
import Header from './components/Header'
|
||||||
|
import LatestPostsGroup from './components/LatestPostsGroup'
|
||||||
|
import Card from './components/Card'
|
||||||
|
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||||
|
import BlogPostListPage from './components/BlogPostListPage'
|
||||||
|
import _NEXT from './_NEXT'
|
||||||
|
|
||||||
|
const IndexLayout = ({ posts, tags, meta, categories, postCount, latestPosts }) => {
|
||||||
|
return <BaseLayout
|
||||||
|
headerSlot={_NEXT.HOME_BANNER && <Header />}
|
||||||
|
meta={meta}
|
||||||
|
tags={tags}
|
||||||
|
sideBarSlot={<LatestPostsGroup posts={latestPosts} />}
|
||||||
|
rightAreaSlot={
|
||||||
|
_NEXT.RIGHT_LATEST_POSTS && <Card><LatestPostsGroup posts={latestPosts} /></Card>
|
||||||
|
}
|
||||||
|
postCount={postCount}
|
||||||
|
categories={categories}
|
||||||
|
>
|
||||||
|
{_NEXT.POSTS_LIST_TYPE !== 'page'
|
||||||
|
? (
|
||||||
|
<BlogPostListScroll posts={posts} tags={tags} showSummary={true} />
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<BlogPostListPage posts={posts} tags={tags} postCount={postCount} />
|
||||||
|
)}
|
||||||
|
</BaseLayout>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IndexLayout
|
||||||
21
themes/NEXT/PageLayout.js
Normal file
21
themes/NEXT/PageLayout.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import BaseLayout from './BaseLayout'
|
||||||
|
import LatestPostsGroup from './components/LatestPostsGroup'
|
||||||
|
import BlogPostListPage from './components/BlogPostListPage'
|
||||||
|
import _NEXT from './_NEXT'
|
||||||
|
|
||||||
|
const PageLayout = ({ page, posts, tags, meta, categories, postCount, latestPosts }) => {
|
||||||
|
return (
|
||||||
|
<BaseLayout
|
||||||
|
meta={meta}
|
||||||
|
tags={tags}
|
||||||
|
sideBarSlot={<LatestPostsGroup posts={latestPosts} />}
|
||||||
|
rightAreaSlot={_NEXT.RIGHT_LATEST_POSTS && <LatestPostsGroup posts={latestPosts} />}
|
||||||
|
postCount={postCount}
|
||||||
|
categories={categories}
|
||||||
|
>
|
||||||
|
<BlogPostListPage page={page} posts={posts} postCount={postCount} />
|
||||||
|
</BaseLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PageLayout
|
||||||
58
themes/NEXT/SearchLayout.js
Normal file
58
themes/NEXT/SearchLayout.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import BaseLayout from './BaseLayout'
|
||||||
|
import StickyBar from './components/StickyBar'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
import { faSearch } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
|
const SearchLayout = ({ posts, tags, categories, postCount }) => {
|
||||||
|
let filteredPosts = []
|
||||||
|
const searchKey = getSearchKey()
|
||||||
|
if (searchKey) {
|
||||||
|
filteredPosts = posts.filter(post => {
|
||||||
|
const tagContent = post.tags ? post.tags.join(' ') : ''
|
||||||
|
const searchContent = post.title + post.summary + tagContent
|
||||||
|
return searchContent.toLowerCase().includes(searchKey.toLowerCase())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
filteredPosts = posts
|
||||||
|
}
|
||||||
|
|
||||||
|
const { locale } = useGlobal()
|
||||||
|
const meta = {
|
||||||
|
title: `${searchKey || ''} | ${locale.NAV.SEARCH} | ${BLOG.title} `,
|
||||||
|
description: BLOG.description,
|
||||||
|
type: 'website'
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<BaseLayout
|
||||||
|
meta={meta}
|
||||||
|
tags={tags}
|
||||||
|
postCount={postCount}
|
||||||
|
currentSearch={searchKey}
|
||||||
|
categories={categories}
|
||||||
|
>
|
||||||
|
<StickyBar>
|
||||||
|
<div className="p-4 dark:text-gray-200">
|
||||||
|
<FontAwesomeIcon icon={faSearch} className="mr-1" />{' '}
|
||||||
|
{filteredPosts.length} {locale.COMMON.RESULT_OF_SEARCH}
|
||||||
|
</div>
|
||||||
|
</StickyBar>
|
||||||
|
<div className="md:mt-5">
|
||||||
|
<BlogPostListScroll posts={filteredPosts} tags={tags} showSummary={true}/>
|
||||||
|
</div>
|
||||||
|
</BaseLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SearchLayout
|
||||||
|
|
||||||
|
function getSearchKey () {
|
||||||
|
const router = useRouter()
|
||||||
|
if (router.query && router.query.s) {
|
||||||
|
return router.query.s
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
26
themes/NEXT/TagIndexLayout.js
Normal file
26
themes/NEXT/TagIndexLayout.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
import BaseLayout from './BaseLayout'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
import { faTags } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import TagItem from './components/TagItem'
|
||||||
|
|
||||||
|
const TagIndexLayout = ({ tags, categories, postCount, latestPosts }) => {
|
||||||
|
const { locale } = useGlobal()
|
||||||
|
const meta = {
|
||||||
|
title: `${locale.COMMON.TAGS} | ${BLOG.title}`,
|
||||||
|
description: BLOG.description,
|
||||||
|
type: 'website'
|
||||||
|
}
|
||||||
|
return <BaseLayout meta={meta} categories={categories} postCount={postCount} latestPosts={latestPosts}>
|
||||||
|
<div className='bg-white dark:bg-gray-700 px-10 py-10 shadow'>
|
||||||
|
<div className='dark:text-gray-200 mb-5'><FontAwesomeIcon icon={faTags} className='mr-4'/>{locale.COMMON.TAGS}:</div>
|
||||||
|
<div id='tags-list' className='duration-200 flex flex-wrap'>
|
||||||
|
{ tags.map(tag => {
|
||||||
|
return <div key={tag.name} className='p-2'><TagItem key={tag.name} tag={tag} /></div>
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BaseLayout>
|
||||||
|
}
|
||||||
|
export default TagIndexLayout
|
||||||
32
themes/NEXT/TagLayout.js
Normal file
32
themes/NEXT/TagLayout.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
import BaseLayout from './BaseLayout'
|
||||||
|
import StickyBar from './components/StickyBar'
|
||||||
|
import TagList from './components/TagList'
|
||||||
|
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||||
|
|
||||||
|
const TagLayout = ({ tags, posts, tag, categories, postCount, latestPosts }) => {
|
||||||
|
const { locale } = useGlobal()
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: `${tag} | ${locale.COMMON.TAGS} | ${BLOG.title}`,
|
||||||
|
description: BLOG.description,
|
||||||
|
type: 'website'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将当前选中的标签置顶🔝
|
||||||
|
if (!tags) tags = []
|
||||||
|
const currentTag = tags?.find(r => r?.name === tag)
|
||||||
|
const newTags = currentTag ? [currentTag].concat(tags.filter(r => r?.name !== tag)) : tags.filter(r => r?.name !== tag)
|
||||||
|
|
||||||
|
return <BaseLayout meta={meta} tags={tags} currentTag={tag} categories={categories} postCount={postCount} latestPosts={latestPosts}>
|
||||||
|
<StickyBar>
|
||||||
|
<TagList tags={newTags} currentTag={tag}/>
|
||||||
|
</StickyBar>
|
||||||
|
<div className='md:mt-8'>
|
||||||
|
<BlogPostListScroll posts={posts} tags={tags} currentTag={tag}/>
|
||||||
|
</div>
|
||||||
|
</BaseLayout>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TagLayout
|
||||||
78
themes/NEXT/_NEXT.js
Normal file
78
themes/NEXT/_NEXT.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
const _NEXT = {
|
||||||
|
// 首页相关配置
|
||||||
|
HOME_BANNER: false, // 首页是否显示大图及标语 [true,false]
|
||||||
|
POSTS_LIST_TYPE: 'page', // ['page','scroll] 文章列表样式:页码分页、单页滚动加载
|
||||||
|
// 右侧组件
|
||||||
|
RIGHT_LATEST_POSTS: false,
|
||||||
|
|
||||||
|
home: { // 首页
|
||||||
|
showHomeBanner: false,
|
||||||
|
homeBannerStrings: ['Hi,我是一个程序员', 'Hi,我是一个打工人', 'Hi,我是一个干饭人', '欢迎来到我的博客🎉'], // 首页大图标语文字
|
||||||
|
homeBannerImage: './bg_image.jpg', // 背景图地址
|
||||||
|
showPostCover: false, // 文章列表显示封面图
|
||||||
|
showPreview: true, // 列表展示文章预览
|
||||||
|
previewLines: 12, // 预览文章的篇幅
|
||||||
|
showSummary: false // 显示用户自定义摘要
|
||||||
|
},
|
||||||
|
appearance: 'auto', // ['light', 'dark', 'auto'],
|
||||||
|
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,
|
||||||
|
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: false, // 显示回底
|
||||||
|
showDarkMode: false, // 显示日间/夜间模式切换
|
||||||
|
showToc: true, // 移动端显示悬浮目录
|
||||||
|
showShareBar: false, // 文章分享功能
|
||||||
|
showRelatePosts: true, // 相关文章推荐
|
||||||
|
showCopyRight: true, // 文章版权声明
|
||||||
|
showLatestPost: false, // 右侧边栏显示最近更新
|
||||||
|
showCategoryList: false, // 右侧边栏显示文章分类列表
|
||||||
|
showTagList: false // 右侧边栏显示标签分类列表
|
||||||
|
},
|
||||||
|
socialLink: { // 社交链接,如不需要展示可以留空白,例如 weibo:''
|
||||||
|
weibo: 'https://weibo.com/tangly1024',
|
||||||
|
twitter: 'https://twitter.com/troy1024_1',
|
||||||
|
github: 'https://github.com/tangly1024',
|
||||||
|
telegram: 'https://t.me/tangly_1024'
|
||||||
|
},
|
||||||
|
comment: { // 评论插件,支持 gitalk, utterances, cusdis
|
||||||
|
provider: 'gitalk', // 不需要则留空白
|
||||||
|
gitalkConfig: {
|
||||||
|
repo: 'NotionNext', // The repository of store comments
|
||||||
|
owner: 'tangly1024',
|
||||||
|
admin: ['tangly1024'],
|
||||||
|
clientID: process.env.GITALK_ID || 'be7864a16b693e256f8f',
|
||||||
|
clientSecret: process.env.GITALK_SECRET || 'dbd0f6d9ceea8940f6ed20936b415274b8fe66a2',
|
||||||
|
distractionFreeMode: false
|
||||||
|
},
|
||||||
|
cusdisConfig: {
|
||||||
|
appId: '445ba48e-f751-487f-b22f-cdbe3310d28f', // data-app-id
|
||||||
|
host: 'https://cusdis.com', // data-host, change this if you're using self-hosted version
|
||||||
|
scriptSrc: 'https://cusdis.com/js/cusdis.es.js' // change this if you're using self-hosted version
|
||||||
|
},
|
||||||
|
utterancesConfig: {
|
||||||
|
repo: 'tangly1024/NotionNext'
|
||||||
|
},
|
||||||
|
gitter: '', // gitter聊天室
|
||||||
|
DaoVoiceId: '', // DaoVoice http://dashboard.daovoice.io/get-started
|
||||||
|
TidioId: '' // https://www.tidio.com/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// export default BLOG
|
||||||
|
module.exports = _NEXT
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import BlogAround from '@/components/BlogAround'
|
import BlogAround from '@/themes/NEXT/components/BlogAround'
|
||||||
import Comment from '@/components/Comment'
|
import Comment from '@/components/Comment'
|
||||||
import RecommendPosts from '@/components/RecommendPosts'
|
import RecommendPosts from '@/themes/NEXT/components/RecommendPosts'
|
||||||
import ShareBar from '@/components/ShareBar'
|
import ShareBar from '@/themes/NEXT/components/ShareBar'
|
||||||
import TagItem from '@/components/TagItem'
|
import TagItem from '@/themes/NEXT/components/TagItem'
|
||||||
import formatDate from '@/lib/formatDate'
|
import formatDate from '@/lib/formatDate'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import { faEye, faFolderOpen } from '@fortawesome/free-solid-svg-icons'
|
import { faEye, faFolderOpen } from '@fortawesome/free-solid-svg-icons'
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import BlogPostCard from '@/components/BlogPostCard'
|
import BlogPostCard from '@/themes/NEXT/components/BlogPostCard'
|
||||||
import PaginationNumber from './PaginationNumber'
|
import PaginationNumber from './PaginationNumber'
|
||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import BlogPostListEmpty from '@/components/BlogPostListEmpty'
|
import BlogPostListEmpty from '@/themes/NEXT/components/BlogPostListEmpty'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文章列表分页表格
|
* 文章列表分页表格
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import BlogPostCard from '@/components/BlogPostCard'
|
import BlogPostCard from '@/themes/NEXT/components/BlogPostCard'
|
||||||
import BlogPostListEmpty from '@/components/BlogPostListEmpty'
|
import BlogPostListEmpty from '@/themes/NEXT/components/BlogPostListEmpty'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import throttle from 'lodash.throttle'
|
import throttle from 'lodash.throttle'
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import throttle from 'lodash.throttle'
|
import throttle from 'lodash.throttle'
|
||||||
import DarkModeButton from '@/components/DarkModeButton'
|
import DarkModeButton from '@/themes/NEXT/components/DarkModeButton'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faBars } from '@fortawesome/free-solid-svg-icons'
|
import { faBars } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import ShareBar from '@/components/ShareBar'
|
import ShareBar from '@/themes/NEXT/components/ShareBar'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faShareAltSquare } from '@fortawesome/free-solid-svg-icons'
|
import { faShareAltSquare } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
import InfoCard from '@/components/InfoCard'
|
import InfoCard from '@/themes/NEXT/components/InfoCard'
|
||||||
import MenuButtonGroup from '@/components/MenuButtonGroup'
|
import MenuButtonGroup from '@/themes/NEXT/components/MenuButtonGroup'
|
||||||
import SearchInput from '@/components/SearchInput'
|
import SearchInput from '@/themes/NEXT/components/SearchInput'
|
||||||
import Toc from '@/components/Toc'
|
import Toc from '@/themes/NEXT/components/Toc'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Analytics from './Analytics'
|
import Tabs from '@/themes/NEXT/components/Tabs'
|
||||||
import Tabs from '@/components/Tabs'
|
|
||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import Logo from './Logo'
|
import Logo from './Logo'
|
||||||
import Card from './Card'
|
import Card from './Card'
|
||||||
@@ -50,7 +49,17 @@ const SideAreaLeft = ({ title, tags, currentTag, post, postCount, categories, cu
|
|||||||
|
|
||||||
<div key={locale.NAV.ABOUT} className='mb-5 bg-white dark:bg-gray-800 duration-200 py-6'>
|
<div key={locale.NAV.ABOUT} className='mb-5 bg-white dark:bg-gray-800 duration-200 py-6'>
|
||||||
<InfoCard />
|
<InfoCard />
|
||||||
<Analytics postCount={postCount}/>
|
<>
|
||||||
|
{/* <div className='px-5 text-sm font-light pb-1 text-gray-600 dark:text-gray-200'><FontAwesomeIcon icon={faChartBar} className='mr-2' />{locale.COMMON.ANALYTICS}</div> */}
|
||||||
|
<div className='mt-2 text-center dark:text-gray-300 font-light text-xs'>
|
||||||
|
<span className='px-1 '>
|
||||||
|
<strong className='font-medium'>{postCount}</strong>{locale.COMMON.POSTS}</span>
|
||||||
|
<span className='px-1 busuanzi_container_site_uv hidden'>
|
||||||
|
| <strong className='pl-1 busuanzi_value_site_uv font-medium'></strong>{locale.COMMON.VISITORS}</span>
|
||||||
|
{/* <span className='px-1 busuanzi_container_site_pv hidden'>
|
||||||
|
| <strong className='pl-1 busuanzi_value_site_pv font-medium'></strong>{locale.COMMON.VIEWS}</span> */}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
</div>
|
</div>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import CategoryGroup from '@/components/CategoryGroup'
|
import CategoryGroup from '@/themes/NEXT/components/CategoryGroup'
|
||||||
import InfoCard from '@/components/InfoCard'
|
import InfoCard from '@/themes/NEXT/components/InfoCard'
|
||||||
import TagGroups from '@/components/TagGroups'
|
import TagGroups from '@/themes/NEXT/components/TagGroups'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import { faAngleDoubleRight, faTag, faThList } from '@fortawesome/free-solid-svg-icons'
|
import { faAngleDoubleRight, faTag, faThList } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import SideBar from '@/components/SideBar'
|
import SideBar from '@/themes/NEXT/components/SideBar'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import React, { useEffect, useImperativeHandle } from 'react'
|
import React, { useEffect, useImperativeHandle } from 'react'
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import TagItemMini from '@/components/TagItemMini'
|
import TagItemMini from '@/themes/NEXT/components/TagItemMini'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标签组
|
* 标签组
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import TagItem from '@/components/TagItem'
|
import TagItem from '@/themes/NEXT/components/TagItem'
|
||||||
/**
|
/**
|
||||||
* 横向的标签列表
|
* 横向的标签列表
|
||||||
* @param tags
|
* @param tags
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import Toc from '@/components/Toc'
|
import Toc from '@/themes/NEXT/components/Toc'
|
||||||
import React, { useImperativeHandle, useState } from 'react'
|
import React, { useImperativeHandle, useState } from 'react'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
|
|
||||||
12
themes/index.js
Normal file
12
themes/index.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
|
||||||
|
export const IndexLayout = dynamic(() => import('./NEXT/IndexLayout'), { ssr: false })
|
||||||
|
export const SearchLayout = dynamic(() => import('./NEXT/SearchLayout'), { ssr: false })
|
||||||
|
export const ArchiveLayout = dynamic(() => import('./NEXT/ArchiveLayout'), { ssr: false })
|
||||||
|
export const ArticleLayout = dynamic(() => import('./NEXT/ArticleLayout'), { ssr: false })
|
||||||
|
export const Custom404Layout = dynamic(() => import('./NEXT/Custom404Layout'), { ssr: false })
|
||||||
|
export const CategoryLayout = dynamic(() => import('./NEXT/CategoryLayout'), { ssr: false })
|
||||||
|
export const CategoryIndexLayout = dynamic(() => import('./NEXT/CategoryIndexLayout'), { ssr: false })
|
||||||
|
export const PageLayout = dynamic(() => import('./NEXT/PageLayout'), { ssr: false })
|
||||||
|
export const TagLayout = dynamic(() => import('./NEXT/TagLayout'), { ssr: false })
|
||||||
|
export const TagIndexLayout = dynamic(() => import('./NEXT/TagIndexLayout'), { ssr: false })
|
||||||
Reference in New Issue
Block a user