diff --git a/lib/notion/getAllCategories.js b/lib/notion/getAllCategories.js new file mode 100644 index 00000000..2225b965 --- /dev/null +++ b/lib/notion/getAllCategories.js @@ -0,0 +1,49 @@ +import { isIterable } from '../utils' + +/** + * 获取所有文章的标签 + * @param allPosts + * @param sliceCount 默认截取数量为12,若为0则返回全部 + * @param tagOptions tags的下拉选项 + * @returns {Promise<{}|*[]>} + */ + +/** + * 获取所有文章的分类 + * @param allPosts + * @returns {Promise<{}|*[]>} + */ +export function getAllCategories({ allPages, categoryOptions, sliceCount = 0 }) { + const allPosts = allPages.filter(page => page.type === 'Post') + if (!allPosts || !categoryOptions) { + return [] + } + // 计数 + let categories = allPosts.map(p => p.category) + categories = [...categories.flat()] + const categoryObj = {} + categories.forEach(category => { + if (category in categoryObj) { + categoryObj[category]++ + } else { + categoryObj[category] = 1 + } + }) + const list = [] + if (isIterable(categoryOptions)) { + for (const c of categoryOptions) { + const count = categoryObj[c.value] + if (count) { + list.push({ id: c.id, name: c.value, color: c.color, count }) + } + } + } + + // 按照数量排序 + // list.sort((a, b) => b.count - a.count) + if (sliceCount && sliceCount > 0) { + return list.slice(0, sliceCount) + } else { + return list + } +} diff --git a/lib/notion/getNotionData.js b/lib/notion/getNotionData.js index bcd2df5e..3da64da3 100644 --- a/lib/notion/getNotionData.js +++ b/lib/notion/getNotionData.js @@ -3,7 +3,8 @@ import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager' import { getPostBlocks } from '@/lib/notion/getPostBlocks' import { idToUuid } from 'notion-utils' import { defaultMapImageUrl } from 'react-notion-x' -import { deepClone, isIterable } from '../utils' +import { deepClone } from '../utils' +import { getAllCategories } from './getAllCategories' import getAllPageIds from './getAllPageIds' import { getAllTags } from './getAllTags' import getPageProperties from './getPageProperties' @@ -31,8 +32,6 @@ export async function getGlobalNotionData({ delete notionPageData.schema delete notionPageData.rawMetadata delete notionPageData.pageIds - delete notionPageData.tagOptions - delete notionPageData.categoryOptions return notionPageData } @@ -117,46 +116,6 @@ function getCategoryOptions(schema) { return categorySchema?.options || [] } -/** - * 获取所有文章的分类 - * @param allPosts - * @returns {Promise<{}|*[]>} - */ -function getAllCategories({ allPages, categoryOptions, sliceCount = 0 }) { - const allPosts = allPages.filter(page => page.type === 'Post') - if (!allPosts || !categoryOptions) { - return [] - } - // 计数 - let categories = allPosts.map(p => p.category) - categories = [...categories.flat()] - const categoryObj = {} - categories.forEach(category => { - if (category in categoryObj) { - categoryObj[category]++ - } else { - categoryObj[category] = 1 - } - }) - const list = [] - if (isIterable(categoryOptions)) { - for (const c of categoryOptions) { - const count = categoryObj[c.value] - if (count) { - list.push({ id: c.id, name: c.value, color: c.color, count }) - } - } - } - - // 按照数量排序 - // list.sort((a, b) => b.count - a.count) - if (sliceCount && sliceCount > 0) { - return list.slice(0, sliceCount) - } else { - return list - } -} - /** * 站点信息 * @param notionPageData @@ -279,7 +238,7 @@ async function getPageRecordMapByNotionAPI({ pageId, from }) { const customNav = getCustomNav({ allPages }) const categories = getAllCategories({ allPages, categoryOptions, sliceCount: BLOG.PREVIEW_CATEGORY_COUNT }) - const tags = getAllTags({ allPages, tagOptions, sliceCount: BLOG.PREVIEW_TAG_COUNT }) + const tags = getAllTags({ allPages, sliceCount: BLOG.PREVIEW_TAG_COUNT, tagOptions }) const latestPosts = getLatestPosts({ allPages, from, latestPostCount: 5 }) return { diff --git a/lib/utils.js b/lib/utils.js index 1119bdf9..9df42aaf 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -105,3 +105,17 @@ export const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) * @returns {boolean} */ export const isBrowser = () => typeof window !== 'undefined' + +/** + * 获取从第1页到指定页码的文章 + * @param pageIndex 第几页 + * @param list 所有文章 + * @param pageSize 每页文章数量 + * @returns {*} + */ +export const getListByPage = function (list, pageIndex, pageSize) { + return list.slice( + 0, + pageIndex * pageSize + ) +} diff --git a/pages/archive/index.js b/pages/archive/index.js index ed40b48a..6e8f115a 100644 --- a/pages/archive/index.js +++ b/pages/archive/index.js @@ -20,10 +20,10 @@ const ArchiveIndex = props => { export async function getStaticProps() { const props = await getGlobalNotionData({ from: 'archive-index' }) - const { allPages } = props - const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published') // 处理分页 - props.posts = allPosts + props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published') + delete props.allPages + return { props, revalidate: 1 diff --git a/pages/category/[category].js b/pages/category/[category]/index.js similarity index 64% rename from pages/category/[category].js rename to pages/category/[category]/index.js index 6082c336..a6c4f1af 100644 --- a/pages/category/[category].js +++ b/pages/category/[category]/index.js @@ -2,7 +2,13 @@ import { getGlobalNotionData } from '@/lib/notion/getNotionData' import React from 'react' import { useGlobal } from '@/lib/global' import * as ThemeMap from '@/themes' +import BLOG from '@/blog.config' +/** + * 分类页 + * @param {*} props + * @returns + */ export default function Category(props) { const { theme } = useGlobal() const ThemeComponents = ThemeMap[theme] @@ -26,12 +32,23 @@ export default function Category(props) { export async function getStaticProps({ params: { category } }) { const from = 'category-props' let props = await getGlobalNotionData({ from }) - const { allPages } = props - const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published') - const posts = allPosts.filter( - post => post && post.category && post.category.includes(category) - ) - props = { ...props, posts, category } + + // 过滤状态 + props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published') + // 处理过滤 + props.posts = props.posts.filter(post => post && post.category && post.category.includes(category)) + // 处理文章页数 + props.postCount = props.posts.length + // 处理分页 + if (BLOG.POST_LIST_STYLE === 'scroll') { + // 滚动列表 给前端返回所有数据 + } else if (BLOG.POST_LIST_STYLE === 'page') { + props.posts = props.posts?.slice(0, BLOG.POSTS_PER_PAGE - 1) + } + + delete props.allPages + + props = { ...props, category } return { props, diff --git a/pages/category/[category]/page/[page].js b/pages/category/[category]/page/[page].js new file mode 100644 index 00000000..122d48f5 --- /dev/null +++ b/pages/category/[category]/page/[page].js @@ -0,0 +1,76 @@ +import { getGlobalNotionData } from '@/lib/notion/getNotionData' +import React from 'react' +import { useGlobal } from '@/lib/global' +import * as ThemeMap from '@/themes' +import BLOG from '@/blog.config' + +/** + * 分类页 + * @param {*} props + * @returns + */ +export default function Category(props) { + const { theme } = useGlobal() + const ThemeComponents = ThemeMap[theme] + const { siteInfo, posts } = props + const { locale } = useGlobal() + if (!posts) { + return + } + const meta = { + title: `${props.category} | ${locale.COMMON.CATEGORY} | ${ + siteInfo?.title || '' + }`, + description: siteInfo?.description, + slug: 'category/' + props.category, + image: siteInfo?.pageCover, + type: 'website' + } + return +} + +export async function getStaticProps({ params: { category, page } }) { + const from = 'category-page-props' + let props = await getGlobalNotionData({ from }) + + // 过滤状态类型 + props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.category && post.category.includes(category)) + // 处理文章页数 + props.postCount = props.posts.length + // 处理分页 + props.posts = props.posts.slice(BLOG.POSTS_PER_PAGE * (page - 1), BLOG.POSTS_PER_PAGE * page - 1) + + delete props.allPages + props.page = page + + props = { ...props, category, page } + + return { + props, + revalidate: 1 + } +} + +export async function getStaticPaths() { + const from = 'category-paths' + const { categories, allPages } = await getGlobalNotionData({ from }) + const paths = [] + + categories?.forEach(category => { + // 过滤状态类型 + const categoryPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.category && post.category.includes(category.name)) + // 处理文章页数 + const postCount = categoryPosts.length + const totalPages = Math.ceil(postCount / BLOG.POSTS_PER_PAGE) + if (totalPages > 1) { + for (let i = 1; i <= totalPages; i++) { + paths.push({ params: { category: category.name, page: '' + i } }) + } + } + }) + + return { + paths, + fallback: true + } +} diff --git a/pages/category/index.js b/pages/category/index.js index 13f95eee..bb219bb5 100644 --- a/pages/category/index.js +++ b/pages/category/index.js @@ -2,7 +2,13 @@ import { getGlobalNotionData } from '@/lib/notion/getNotionData' import React from 'react' import { useGlobal } from '@/lib/global' import * as ThemeMap from '@/themes' +import { getAllCategories } from '@/lib/notion/getAllCategories' +/** + * 分类首页 + * @param {*} props + * @returns + */ export default function Category(props) { const { theme } = useGlobal() const ThemeComponents = ThemeMap[theme] @@ -19,10 +25,9 @@ export default function Category(props) { } export async function getStaticProps() { - const props = await getGlobalNotionData({ - from: 'category-index-props', - categoryCount: 0 - }) + const props = await getGlobalNotionData({ from: 'category-index-props' }) + props.categories = getAllCategories({ allPages: props.allPages, categoryOptions: props.categoryOptions, sliceCount: 0 }) + delete props.categoryOptions return { props, revalidate: 1 diff --git a/pages/feed.js b/pages/feed/index.js similarity index 100% rename from pages/feed.js rename to pages/feed/index.js diff --git a/pages/index.js b/pages/index.js index 92a70474..067b9a32 100644 --- a/pages/index.js +++ b/pages/index.js @@ -12,8 +12,8 @@ const Index = props => { export async function getStaticProps() { const from = 'index' const props = await getGlobalNotionData({ from }) - const { allPages, siteInfo } = props - const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published') + const { siteInfo } = props + props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published') const meta = { title: `${siteInfo?.title} | ${siteInfo?.description}`, description: siteInfo?.description, @@ -21,35 +21,23 @@ export async function getStaticProps() { slug: '', type: 'website' } - // 处理分页 - const page = 1 - let postsToShow - if (BLOG.POST_LIST_STYLE !== 'page') { - postsToShow = Array.from(allPosts) - } else { - postsToShow = allPosts?.slice( - BLOG.POSTS_PER_PAGE * (page - 1), - BLOG.POSTS_PER_PAGE * page - ) - if (BLOG.POST_LIST_PREVIEW === 'true') { - for (const i in postsToShow) { - const post = postsToShow[i] - if (post.password && post.password !== '') { - continue - } - const blockMap = await getPostBlocks( - post.id, - 'slug', - BLOG.POST_PREVIEW_LINES - ) - if (blockMap) { - post.blockMap = blockMap - } + if (BLOG.POST_LIST_STYLE === 'scroll') { + // 滚动列表默认给前端返回所有数据 + } else if (BLOG.POST_LIST_STYLE === 'page') { + props.posts = props.posts?.slice(0, BLOG.POSTS_PER_PAGE) + } + + // 预览文章内容 + if (BLOG.POST_LIST_PREVIEW === 'true') { + for (const i in props.posts) { + const post = props.posts[i] + if (post.password && post.password !== '') { + continue } + post.blockMap = await getPostBlocks(post.id, 'slug', BLOG.POST_PREVIEW_LINES) } } - props.posts = postsToShow return { props: { diff --git a/pages/page/[page].js b/pages/page/[page].js index a09f08d1..3162250b 100644 --- a/pages/page/[page].js +++ b/pages/page/[page].js @@ -37,28 +37,20 @@ export async function getStaticPaths() { export async function getStaticProps({ params: { page } }) { const from = `page-${page}` const props = await getGlobalNotionData({ from }) - props.page = page const { allPages } = props const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published') // 处理分页 - props.posts = allPosts.slice( - BLOG.POSTS_PER_PAGE * (page - 1), - BLOG.POSTS_PER_PAGE * page - ) + props.posts = allPosts.slice(BLOG.POSTS_PER_PAGE * (page - 1), BLOG.POSTS_PER_PAGE * page) + props.page = page + + // 处理预览 if (BLOG.POST_LIST_PREVIEW === 'true') { for (const i in props.posts) { const post = props.posts[i] if (post.password && post.password !== '') { continue } - const blockMap = await getPostBlocks( - post.id, - 'slug', - BLOG.POST_PREVIEW_LINES - ) - if (blockMap) { - post.blockMap = blockMap - } + post.blockMap = await getPostBlocks(post.id, 'slug', BLOG.POST_PREVIEW_LINES) } } diff --git a/pages/tag/[tag].js b/pages/tag/[tag]/index.js similarity index 68% rename from pages/tag/[tag].js rename to pages/tag/[tag]/index.js index 875a8768..ca52f8f1 100644 --- a/pages/tag/[tag].js +++ b/pages/tag/[tag]/index.js @@ -1,6 +1,7 @@ import { useGlobal } from '@/lib/global' import { getGlobalNotionData } from '@/lib/notion/getNotionData' import * as ThemeMap from '@/themes' +import BLOG from '@/blog.config' const Tag = props => { const { theme } = useGlobal() @@ -23,16 +24,22 @@ const Tag = props => { } export async function getStaticProps({ params: { tag } }) { - const props = await getGlobalNotionData({ - from: 'tag-props', - includePage: false, - tagsCount: 0 - }) - const { allPages } = props - const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published') - props.posts = allPosts.filter( - post => post && post.tags && post.tags.includes(tag) - ) + const from = 'tag-props' + const props = await getGlobalNotionData({ from }) + + // 过滤状态 + props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.tags && post.tags.includes(tag)) + + // 处理文章页数 + props.postCount = props.posts.length + + // 处理分页 + if (BLOG.POST_LIST_STYLE === 'scroll') { + // 滚动列表 给前端返回所有数据 + } else if (BLOG.POST_LIST_STYLE === 'page') { + props.posts = props.posts?.slice(0, BLOG.POSTS_PER_PAGE - 1) + } + props.tag = tag return { props, diff --git a/pages/tag/[tag]/page/[page].js b/pages/tag/[tag]/page/[page].js new file mode 100644 index 00000000..6317c377 --- /dev/null +++ b/pages/tag/[tag]/page/[page].js @@ -0,0 +1,67 @@ +import { useGlobal } from '@/lib/global' +import { getGlobalNotionData } from '@/lib/notion/getNotionData' +import * as ThemeMap from '@/themes' +import BLOG from '@/blog.config' + +const Tag = props => { + const { theme } = useGlobal() + const ThemeComponents = ThemeMap[theme] + const { locale } = useGlobal() + const { tag, siteInfo, posts } = props + + if (!posts) { + return + } + + const meta = { + title: `${tag} | ${locale.COMMON.TAGS} | ${siteInfo?.title}`, + description: siteInfo?.description, + image: siteInfo?.pageCover, + slug: 'tag/' + tag, + type: 'website' + } + return +} + +export async function getStaticProps({ params: { tag, page } }) { + const from = 'tag-page-props' + const props = await getGlobalNotionData({ from }) + // 过滤状态、标签 + props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.tags && post.tags.includes(tag)) + // 处理文章数 + props.postCount = props.posts.length + // 处理分页 + props.posts = props.posts.slice(BLOG.POSTS_PER_PAGE * (page - 1), BLOG.POSTS_PER_PAGE * page - 1) + + props.tag = tag + props.page = page + delete props.allPages + return { + props, + revalidate: 1 + } +} + +export async function getStaticPaths() { + const from = 'tag-page-static-path' + const { tags, allPages } = await getGlobalNotionData({ from }) + const paths = [] + tags?.forEach(tag => { + // 过滤状态类型 + const categoryPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.category && post.tags.includes(tag.name)) + // 处理文章页数 + const postCount = categoryPosts.length + const totalPages = Math.ceil(postCount / BLOG.POSTS_PER_PAGE) + if (totalPages > 1) { + for (let i = 1; i <= totalPages; i++) { + paths.push({ params: { tag: tag.name, page: '' + i } }) + } + } + }) + return { + paths: paths, + fallback: true + } +} + +export default Tag diff --git a/pages/tag/index.js b/pages/tag/index.js index 1ceb0f07..93965586 100644 --- a/pages/tag/index.js +++ b/pages/tag/index.js @@ -2,7 +2,13 @@ import { getGlobalNotionData } from '@/lib/notion/getNotionData' import React from 'react' import { useGlobal } from '@/lib/global' import * as ThemeMap from '@/themes' +import { getAllTags } from '@/lib/notion' +/** + * 标签首页 + * @param {*} props + * @returns + */ const TagIndex = props => { const { theme } = useGlobal() const ThemeComponents = ThemeMap[theme] @@ -21,6 +27,8 @@ const TagIndex = props => { export async function getStaticProps() { const from = 'tag-index-props' const props = await getGlobalNotionData({ from }) + props.tags = getAllTags({ allPages: props.allPages, sliceCount: 0, tagOptions: props.tagOptions }) + delete props.tagOptions return { props, revalidate: 1 diff --git a/themes/example/LayoutCategory.js b/themes/example/LayoutCategory.js index a9c99d90..6d819185 100644 --- a/themes/example/LayoutCategory.js +++ b/themes/example/LayoutCategory.js @@ -1,61 +1,10 @@ import BLOG from '@/blog.config' -import { useGlobal } from '@/lib/global' -import Link from 'next/link' -import { useState } from 'react' +import { BlogListPage } from './components/BlogListPage' +import { BlogListScroll } from './components/BlogListScroll' import LayoutBase from './LayoutBase' export const LayoutCategory = props => { - const { category, posts } = props - const { locale } = useGlobal() - - const [page, updatePage] = useState(1) - let hasMore = false - const postsToShow = posts - ? Object.assign(posts).slice(0, BLOG.POSTS_PER_PAGE * page) - : [] - - if (posts) { - const totalCount = posts.length - hasMore = page * BLOG.POSTS_PER_PAGE < totalCount - } - const handleGetMore = () => { - if (!hasMore) return - updatePage(page + 1) - } - return -
-
{category}
- - {postsToShow.map(p => ( - - ))} - -
- {' '} - {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '} -
-
+ {BLOG.POST_LIST_STYLE === 'page' ? : }
} diff --git a/themes/example/LayoutCategoryIndex.js b/themes/example/LayoutCategoryIndex.js index 7690b316..28b98b6e 100644 --- a/themes/example/LayoutCategoryIndex.js +++ b/themes/example/LayoutCategoryIndex.js @@ -5,7 +5,6 @@ export const LayoutCategoryIndex = (props) => { const { categories } = props return -
{categories && categories.map(category => { return @@ -16,6 +15,5 @@ export const LayoutCategoryIndex = (props) => { })}
-
} diff --git a/themes/example/LayoutIndex.js b/themes/example/LayoutIndex.js index 7d6a17a6..17b77b4a 100644 --- a/themes/example/LayoutIndex.js +++ b/themes/example/LayoutIndex.js @@ -1,11 +1,11 @@ -import { BlogList } from './components/BlogList' +import { BlogListPage } from './components/BlogListPage' import LayoutBase from './LayoutBase' export const LayoutIndex = props => { return ( - + ) } diff --git a/themes/example/LayoutPage.js b/themes/example/LayoutPage.js index fc19035f..15355018 100644 --- a/themes/example/LayoutPage.js +++ b/themes/example/LayoutPage.js @@ -1,10 +1,10 @@ -import { BlogList } from './components/BlogList' +import { BlogListPage } from './components/BlogListPage' import LayoutBase from './LayoutBase' export const LayoutPage = props => { return ( - + ) } diff --git a/themes/example/LayoutTag.js b/themes/example/LayoutTag.js index f60653e3..06a6d778 100644 --- a/themes/example/LayoutTag.js +++ b/themes/example/LayoutTag.js @@ -1,59 +1,10 @@ import BLOG from '@/blog.config' -import { useGlobal } from '@/lib/global' -import Link from 'next/link' -import { useState } from 'react' +import { BlogListPage } from './components/BlogListPage' +import { BlogListScroll } from './components/BlogListScroll' import LayoutBase from './LayoutBase' export const LayoutTag = props => { - const { posts } = props - const { locale } = useGlobal() - - const [page, updatePage] = useState(1) - - let hasMore = false - const postsToShow = posts - ? Object.assign(posts).slice(0, BLOG.POSTS_PER_PAGE * page) - : [] - - if (posts) { - const totalCount = posts.length - hasMore = page * BLOG.POSTS_PER_PAGE < totalCount - } - const handleGetMore = () => { - if (!hasMore) return - updatePage(page + 1) - } - - return - {postsToShow.map(p => ( - - ))} - -
- {' '} - {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '} -
- + return + {BLOG.POST_LIST_STYLE === 'page' ? : } } diff --git a/themes/example/components/BlogList.js b/themes/example/components/BlogListPage.js similarity index 76% rename from themes/example/components/BlogList.js rename to themes/example/components/BlogListPage.js index 1a45d6f7..5eb89ece 100644 --- a/themes/example/components/BlogList.js +++ b/themes/example/components/BlogListPage.js @@ -4,20 +4,17 @@ import { useGlobal } from '@/lib/global' import { useRouter } from 'next/router' import Link from 'next/link' -export const BlogList = (props) => { - const { page, posts, postCount } = props - +export const BlogListPage = props => { + const { page = 1, posts, postCount } = props const { locale } = useGlobal() const router = useRouter() const totalPage = Math.ceil(postCount / BLOG.POSTS_PER_PAGE) - - const showNext = - page < totalPage && - posts.length === BLOG.POSTS_PER_PAGE && - posts.length < postCount - const currentPage = +page + const showPrev = currentPage > 1 + const showNext = page < totalPage + const pagePrefix = router.asPath.replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '') + return
{posts.map(p => ( @@ -51,10 +48,10 @@ export const BlogList = (props) => { ))} diff --git a/themes/example/components/BlogListScroll.js b/themes/example/components/BlogListScroll.js new file mode 100644 index 00000000..07da67d3 --- /dev/null +++ b/themes/example/components/BlogListScroll.js @@ -0,0 +1,78 @@ +import BLOG from '@/blog.config' +import { useGlobal } from '@/lib/global' +import Link from 'next/link' +import React from 'react' +import throttle from 'lodash.throttle' + +export const BlogListScroll = props => { + const { posts } = props + const { locale } = useGlobal() + + const [page, updatePage] = React.useState(1) + + let hasMore = false + const postsToShow = posts + ? Object.assign(posts).slice(0, BLOG.POSTS_PER_PAGE * page) + : [] + + if (posts) { + const totalCount = posts.length + hasMore = page * BLOG.POSTS_PER_PAGE < totalCount + } + const handleGetMore = () => { + if (!hasMore) return + updatePage(page + 1) + } + + const targetRef = React.useRef(null) + + // 监听滚动自动分页加载 + const scrollTrigger = React.useCallback(throttle(() => { + const scrollS = window.scrollY + window.outerHeight + const clientHeight = targetRef ? (targetRef.current ? (targetRef.current.clientHeight) : 0) : 0 + if (scrollS > clientHeight + 100) { + handleGetMore() + } + }, 500)) + + React.useEffect(() => { + window.addEventListener('scroll', scrollTrigger) + + return () => { + window.removeEventListener('scroll', scrollTrigger) + } + }) + + return
+ {postsToShow.map(p => ( + + ))} + +
+ {' '} + {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '} +
+ +
+} diff --git a/themes/fukasawa/LayoutCategory.js b/themes/fukasawa/LayoutCategory.js index 8b3f3b04..fa6c8c0d 100644 --- a/themes/fukasawa/LayoutCategory.js +++ b/themes/fukasawa/LayoutCategory.js @@ -1,8 +1,10 @@ +import BLOG from '@/blog.config' import BlogListPage from './components/BlogListPage' +import BlogListScroll from './components/BlogListScroll' import LayoutBase from './LayoutBase' export const LayoutCategory = props => { return - + {BLOG.POST_LIST_STYLE === 'page' ? : } } diff --git a/themes/fukasawa/LayoutIndex.js b/themes/fukasawa/LayoutIndex.js index 7559d65b..47ae1bce 100644 --- a/themes/fukasawa/LayoutIndex.js +++ b/themes/fukasawa/LayoutIndex.js @@ -1,8 +1,10 @@ +import BLOG from '@/blog.config' import BlogListPage from './components/BlogListPage' +import BlogListScroll from './components/BlogListScroll' import LayoutBase from './LayoutBase' export const LayoutIndex = (props) => { return - + {BLOG.POST_LIST_STYLE === 'page' ? : } } diff --git a/themes/fukasawa/components/BlogListPage.js b/themes/fukasawa/components/BlogListPage.js index 04ef2b47..47650b5b 100644 --- a/themes/fukasawa/components/BlogListPage.js +++ b/themes/fukasawa/components/BlogListPage.js @@ -14,7 +14,7 @@ import PaginationSimple from './PaginationSimple' */ const BlogListPage = ({ page = 1, posts = [], postCount }) => { const totalPage = Math.ceil(postCount / BLOG.POSTS_PER_PAGE) - const showNext = page < totalPage && posts.length === BLOG.POSTS_PER_PAGE && posts.length < postCount + const showNext = page < totalPage const [colCount, changeCol] = useState(1) function updateCol() { diff --git a/themes/fukasawa/components/BlogListScroll.js b/themes/fukasawa/components/BlogListScroll.js new file mode 100644 index 00000000..522d3f0e --- /dev/null +++ b/themes/fukasawa/components/BlogListScroll.js @@ -0,0 +1,93 @@ +import BLOG from '@/blog.config' +import React from 'react' +import BlogCard from './BlogCard' +import BlogPostListEmpty from './BlogListEmpty' +import { useGlobal } from '@/lib/global' +import throttle from 'lodash.throttle' +/** + * 文章列表分页表格 + * @param page 当前页 + * @param posts 所有文章 + * @param tags 所有标签 + * @returns {JSX.Element} + * @constructor + */ +const BlogListScroll = props => { + const { posts = [] } = props + const [colCount, changeCol] = React.useState(1) + const { locale } = useGlobal() + + function updateCol() { + if (window.outerWidth > 1200) { + changeCol(3) + } else if (window.outerWidth > 900) { + changeCol(2) + } else { + changeCol(1) + } + } + const targetRef = React.useRef(null) + + const [page, updatePage] = React.useState(1) + + let hasMore = false + const postsToShow = posts + ? Object.assign(posts).slice(0, BLOG.POSTS_PER_PAGE * page) + : [] + + if (posts) { + const totalCount = posts.length + hasMore = page * BLOG.POSTS_PER_PAGE < totalCount + } + const handleGetMore = () => { + if (!hasMore) return + updatePage(page + 1) + } + + // 监听滚动自动分页加载 + const scrollTrigger = React.useCallback(throttle(() => { + const scrollS = window.scrollY + window.outerHeight + const clientHeight = targetRef ? (targetRef.current ? (targetRef.current.clientHeight) : 0) : 0 + if (scrollS > clientHeight + 100) { + handleGetMore() + } + }, 500)) + + React.useEffect(() => { + updateCol() + window.addEventListener('scroll', scrollTrigger) + + window.addEventListener('resize', updateCol) + return () => { + window.removeEventListener('resize', updateCol) + window.removeEventListener('scroll', scrollTrigger) + } + }) + + if (!posts || posts.length === 0) { + return + } else { + return ( +
+ {/* 文章列表 */} +
+ {postsToShow?.map(post => ( +
+ +
+ ))} +
+ +
+ {' '} + {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '} +
+
+ ) + } +} + +export default BlogListScroll diff --git a/themes/fukasawa/components/PaginationSimple.js b/themes/fukasawa/components/PaginationSimple.js index 11eb837d..71ba8b8f 100644 --- a/themes/fukasawa/components/PaginationSimple.js +++ b/themes/fukasawa/components/PaginationSimple.js @@ -1,4 +1,3 @@ -import BLOG from '@/blog.config' import Link from 'next/link' import { useRouter } from 'next/router' import { useGlobal } from '@/lib/global' @@ -14,14 +13,16 @@ const PaginationSimple = ({ page, showNext }) => { const { locale } = useGlobal() const router = useRouter() const currentPage = +page + const pagePrefix = router.asPath.replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '') + return (
{ { - const { tags, posts, category } = props + const { category } = props return
{category}
- + {BLOG.POST_LIST_STYLE === 'page' ? : }
} diff --git a/themes/hexo/LayoutTag.js b/themes/hexo/LayoutTag.js index 7380aef0..75c308e5 100644 --- a/themes/hexo/LayoutTag.js +++ b/themes/hexo/LayoutTag.js @@ -1,10 +1,21 @@ +import BLOG from '@/blog.config' import BlogPostListScroll from './components/BlogPostListScroll' +import BlogPostListPage from './components/BlogPostListPage' import LayoutBase from './LayoutBase' +import TagItemMini from '../next/components/TagItemMini' +import React from 'react' export const LayoutTag = (props) => { - const { tags, posts, tag } = props + const currentTag = props.tags.find((t) => { + return t.name === props.tag + }) return - - + {currentTag && ( +
+ +
+ )} + {BLOG.POST_LIST_STYLE === 'page' ? : } + } diff --git a/themes/hexo/components/BlogPostListPage.js b/themes/hexo/components/BlogPostListPage.js index 4964f024..3662aee9 100644 --- a/themes/hexo/components/BlogPostListPage.js +++ b/themes/hexo/components/BlogPostListPage.js @@ -14,8 +14,7 @@ import BlogPostListEmpty from './BlogPostListEmpty' const BlogPostListPage = ({ page = 1, posts = [], postCount }) => { const totalPage = Math.ceil(postCount / BLOG.POSTS_PER_PAGE) const showPagination = postCount >= BLOG.POSTS_PER_PAGE - - if (!posts || posts.length === 0) { + if (!posts || posts.length === 0 || page > totalPage) { return } else { return ( diff --git a/themes/hexo/components/BlogPostListScroll.js b/themes/hexo/components/BlogPostListScroll.js index 4c56ff8e..828e58aa 100644 --- a/themes/hexo/components/BlogPostListScroll.js +++ b/themes/hexo/components/BlogPostListScroll.js @@ -5,6 +5,7 @@ import { useGlobal } from '@/lib/global' import throttle from 'lodash.throttle' import React, { useCallback, useEffect, useRef, useState } from 'react' import CONFIG_HEXO from '../config_hexo' +import { getListByPage } from '@/lib/utils' /** * 博客列表滚动分页 @@ -16,7 +17,7 @@ import CONFIG_HEXO from '../config_hexo' const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_HEXO.POST_LIST_SUMMARY }) => { const postsPerPage = BLOG.POSTS_PER_PAGE const [page, updatePage] = useState(1) - const postsToShow = getPostByPage(page, posts, postsPerPage) + const postsToShow = getListByPage(posts, page, postsPerPage) let hasMore = false if (posts) { @@ -62,9 +63,7 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_HE
-
{ - handleGetMore() - }} +
{ handleGetMore() }} className='w-full my-4 py-4 text-center cursor-pointer rounded-xl dark:text-gray-200' > {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE}`}
@@ -72,17 +71,4 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_HE } } -/** - * 获取从第1页到指定页码的文章 - * @param page 第几页 - * @param totalPosts 所有文章 - * @param postsPerPage 每页文章数量 - * @returns {*} - */ -const getPostByPage = function (page, totalPosts, postsPerPage) { - return totalPosts.slice( - 0, - postsPerPage * page - ) -} export default BlogPostListScroll diff --git a/themes/hexo/components/PaginationNumber.js b/themes/hexo/components/PaginationNumber.js index a2b446a8..96207d43 100644 --- a/themes/hexo/components/PaginationNumber.js +++ b/themes/hexo/components/PaginationNumber.js @@ -1,4 +1,3 @@ -import BLOG from '@/blog.config' import Link from 'next/link' import { useRouter } from 'next/router' @@ -13,78 +12,66 @@ const PaginationNumber = ({ page, totalPage }) => { const router = useRouter() const currentPage = +page const showNext = page < totalPage - const pages = generatePages(page, currentPage, totalPage) + const pagePrefix = router.asPath.replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '') + const pages = generatePages(pagePrefix, page, currentPage, totalPage) return ( -
- {/* 上一页 */} - - - +
+ {/* 上一页 */} + + + - {pages} + {pages} - {/* 下一页 */} - - - -
) } -function getPageElement(page, currentPage) { +function getPageElement(page, currentPage, pagePrefix) { return ( - - - {page} - - + + + {page} + + ) } -function generatePages(page, currentPage, totalPage) { +function generatePages(pagePrefix, page, currentPage, totalPage) { const pages = [] const groupCount = 7 // 最多显示页签数 if (totalPage <= groupCount) { for (let i = 1; i <= totalPage; i++) { - pages.push(getPageElement(i, page)) + pages.push(getPageElement(i, page, pagePrefix)) } } else { - pages.push(getPageElement(1, page)) + pages.push(getPageElement(1, page, pagePrefix)) const dynamicGroupCount = groupCount - 2 let startPage = currentPage - 2 if (startPage <= 1) { @@ -99,7 +86,7 @@ function generatePages(page, currentPage, totalPage) { for (let i = 0; i < dynamicGroupCount; i++) { if (startPage + i < totalPage) { - pages.push(getPageElement(startPage + i, page)) + pages.push(getPageElement(startPage + i, page, pagePrefix)) } } @@ -107,7 +94,7 @@ function generatePages(page, currentPage, totalPage) { pages.push(
...
) } - pages.push(getPageElement(totalPage, page)) + pages.push(getPageElement(totalPage, page, pagePrefix)) } return pages } diff --git a/themes/medium/LayoutCategory.js b/themes/medium/LayoutCategory.js index 0cd835da..a3018144 100644 --- a/themes/medium/LayoutCategory.js +++ b/themes/medium/LayoutCategory.js @@ -1,11 +1,13 @@ import LayoutBase from './LayoutBase' import BlogPostListScroll from './components/BlogPostListScroll' +import BlogPostListPage from './components/BlogPostListPage' +import BLOG from '@/blog.config' export const LayoutCategory = props => { const { category } = props - const slotTop =
分类:
{category}
+ const slotTop =
分类:
{category}
return - - + {BLOG.POST_LIST_STYLE === 'page' ? : } + } diff --git a/themes/medium/LayoutTag.js b/themes/medium/LayoutTag.js index 0e6bd79b..dda44141 100644 --- a/themes/medium/LayoutTag.js +++ b/themes/medium/LayoutTag.js @@ -1,11 +1,13 @@ import LayoutBase from './LayoutBase' import BlogPostListScroll from './components/BlogPostListScroll' +import BLOG from '@/blog.config' +import BlogPostListPage from './components/BlogPostListPage' export const LayoutTag = (props) => { const { tag } = props const slotTop =
标签:
{tag}
return - + {BLOG.POST_LIST_STYLE === 'page' ? : } } diff --git a/themes/medium/components/PaginationSimple.js b/themes/medium/components/PaginationSimple.js index 080a899f..a8f685d8 100644 --- a/themes/medium/components/PaginationSimple.js +++ b/themes/medium/components/PaginationSimple.js @@ -1,4 +1,3 @@ -import BLOG from '@/blog.config' import Link from 'next/link' import { useRouter } from 'next/router' import { useGlobal } from '@/lib/global' @@ -15,14 +14,16 @@ const PaginationSimple = ({ page, totalPage }) => { const router = useRouter() const currentPage = +page const showNext = currentPage < totalPage + const pagePrefix = router.asPath.replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '') + return (
{ { - const { tags, posts, category, categories } = props + const { category, categories } = props return
- + {BLOG.POST_LIST_STYLE !== 'page' + ? + : + }
} diff --git a/themes/next/LayoutTag.js b/themes/next/LayoutTag.js index b50f801a..7594ba8c 100644 --- a/themes/next/LayoutTag.js +++ b/themes/next/LayoutTag.js @@ -2,16 +2,21 @@ import LayoutBase from './LayoutBase' import StickyBar from './components/StickyBar' import TagList from './components/TagList' import BlogPostListScroll from './components/BlogPostListScroll' +import BlogPostListPage from './components/BlogPostListPage' +import BLOG from '@/blog.config' export const LayoutTag = (props) => { - const { tags, posts, tag } = props + const { tags, tag } = props return
- + {BLOG.POST_LIST_STYLE !== 'page' + ? + : + }
} diff --git a/themes/next/components/StickyBar.js b/themes/next/components/StickyBar.js index fdcba64d..2f56dc4a 100644 --- a/themes/next/components/StickyBar.js +++ b/themes/next/components/StickyBar.js @@ -39,7 +39,7 @@ const StickyBar = ({ children }) => { return (