diff --git a/lib/notion/getAllPosts.js b/lib/notion/getAllPosts.js
index d13ff5f2..6f38bf79 100644
--- a/lib/notion/getAllPosts.js
+++ b/lib/notion/getAllPosts.js
@@ -25,7 +25,13 @@ export async function getAllPosts ({ notionPageData, from, includePage = false }
const collectionQuery = notionPageData.collectionQuery
const data = []
+ if (!collectionQuery || collectionQuery.toString === '{}') {
+ console.warn('列表查询条件为空', notionPageData)
+ }
const pageIds = getAllPageIds(collectionQuery)
+ if (!pageIds || pageIds.length === 0) {
+ console.warn('页面ID列表为空')
+ }
for (let i = 0; i < pageIds.length; i++) {
const id = pageIds[i]
const properties = (await getPageProperties(id, pageBlock, schema)) || null
@@ -59,6 +65,9 @@ export async function getAllPosts ({ notionPageData, from, includePage = false }
}
})
+ if (!posts || posts.length === 0) {
+ console.warn('文章列表为空')
+ }
// Sort by date
if (BLOG.POSTS_SORT_BY === 'date') {
posts.sort((a, b) => {
diff --git a/themes/Hexo/LayoutBase.js b/themes/Hexo/LayoutBase.js
index 8975f09f..26f55831 100644
--- a/themes/Hexo/LayoutBase.js
+++ b/themes/Hexo/LayoutBase.js
@@ -1,7 +1,11 @@
import CommonHead from '@/components/CommonHead'
+import { useEffect, useState } from 'react'
import Footer from './components/Footer'
+import JumpToTopButton from './components/JumpToTopButton'
import SideRight from './components/SideRight'
+import TopNav from './components/TopNav'
+import smoothscroll from 'smoothscroll-polyfill'
/**
* 基础布局 采用左右两侧布局,移动端使用顶部导航栏
@@ -10,10 +14,33 @@ import SideRight from './components/SideRight'
* @constructor
*/
const LayoutBase = (props) => {
- const { children, headerSlot, meta } = props
+ const { children, headerSlot, floatSlot, meta } = props
+ const [show, switchShow] = useState(false)
+ const [percent, changePercent] = useState(0) // 页面阅读百分比
+
+ const scrollListener = () => {
+ const targetRef = document.getElementById('wrapper')
+ const clientHeight = targetRef?.clientHeight
+ const scrollY = window.pageYOffset
+ const fullHeight = clientHeight - window.outerHeight
+ let per = parseFloat(((scrollY / fullHeight * 100)).toFixed(0))
+ if (per > 100) per = 100
+ const shouldShow = scrollY > 100 && per > 0
+
+ if (shouldShow !== show) {
+ switchShow(shouldShow)
+ }
+ changePercent(per)
+ }
+ useEffect(() => {
+ smoothscroll.polyfill()
+ document.addEventListener('scroll', scrollListener)
+ return () => document.removeEventListener('scroll', scrollListener)
+ }, [show])
return (
+
{headerSlot}
@@ -26,6 +53,14 @@ const LayoutBase = (props) => {
+ {/* 右下角悬浮 */}
+
+
)
diff --git a/themes/Hexo/components/Collapse.js b/themes/Hexo/components/Collapse.js
new file mode 100644
index 00000000..7607b732
--- /dev/null
+++ b/themes/Hexo/components/Collapse.js
@@ -0,0 +1,38 @@
+import React, { useEffect, useRef } from 'react'
+
+const Collapse = props => {
+ const collapseRef = useRef(null)
+ const collapseSection = element => {
+ const sectionHeight = element.scrollHeight
+ requestAnimationFrame(function () {
+ element.style.height = sectionHeight + 'px'
+ requestAnimationFrame(function () {
+ element.style.height = 0 + 'px'
+ })
+ })
+ }
+ const expandSection = element => {
+ const sectionHeight = element.scrollHeight
+ element.style.height = sectionHeight + 'px'
+ const clearTime = setTimeout(() => {
+ element.style.height = 'auto'
+ }, 400)
+ clearTimeout(clearTime)
+ }
+ useEffect(() => {
+ const element = collapseRef.current
+ if (props.isOpen) {
+ expandSection(element)
+ } else {
+ collapseSection(element)
+ }
+ }, [props.isOpen])
+ return (
+
+ {props.children}
+
+ )
+}
+Collapse.defaultProps = { isOpen: false }
+
+export default Collapse
diff --git a/themes/Hexo/components/JumpToTopButton.js b/themes/Hexo/components/JumpToTopButton.js
new file mode 100644
index 00000000..f680f549
--- /dev/null
+++ b/themes/Hexo/components/JumpToTopButton.js
@@ -0,0 +1,28 @@
+import { useGlobal } from '@/lib/global'
+import { faArrowUp } from '@fortawesome/free-solid-svg-icons'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
+import React from 'react'
+import CONFIG_HEXO from '../config_hexo'
+
+/**
+ * 跳转到网页顶部
+ * 当屏幕下滑500像素后会出现该控件
+ * @param targetRef 关联高度的目标html标签
+ * @param showPercent 是否显示百分比
+ * @returns {JSX.Element}
+ * @constructor
+ */
+const JumpToTopButton = ({ showPercent = true, percent }) => {
+ if (!CONFIG_HEXO.WIDGET_TO_TOP) {
+ return <>>
+ }
+ const { locale } = useGlobal()
+ return ( window.scrollTo({ top: 0, behavior: 'smooth' })} >
+
+
+
+ {showPercent && (
{percent}%
)}
+
)
+}
+
+export default JumpToTopButton
diff --git a/themes/Hexo/components/Logo.js b/themes/Hexo/components/Logo.js
new file mode 100644
index 00000000..a2852287
--- /dev/null
+++ b/themes/Hexo/components/Logo.js
@@ -0,0 +1,12 @@
+import Link from 'next/link'
+import BLOG from '@/blog.config'
+import React from 'react'
+
+const Logo = () => {
+ return
+
+
+}
+export default Logo
diff --git a/themes/Hexo/components/SearchDrawer.js b/themes/Hexo/components/SearchDrawer.js
new file mode 100644
index 00000000..3d107e0f
--- /dev/null
+++ b/themes/Hexo/components/SearchDrawer.js
@@ -0,0 +1,36 @@
+import { Router } from 'next/router'
+import { useImperativeHandle, useRef } from 'react'
+import SearchInput from './SearchInput'
+const SearchDrawer = ({ cRef, slot }) => {
+ const searchDrawer = useRef()
+ const searchInputRef = useRef()
+ useImperativeHandle(cRef, () => {
+ return {
+ show: () => {
+ searchDrawer?.current?.classList?.remove('hidden')
+ searchInputRef?.current?.focus()
+ }
+ }
+ })
+ const hidden = () => {
+ searchDrawer?.current?.classList?.add('hidden')
+ }
+ Router.events.on('routeChangeComplete', (...args) => {
+ hidden()
+ })
+ return (
+
+ )
+}
+
+export default SearchDrawer
diff --git a/themes/Hexo/components/TopNav.js b/themes/Hexo/components/TopNav.js
new file mode 100644
index 00000000..fc94ff5b
--- /dev/null
+++ b/themes/Hexo/components/TopNav.js
@@ -0,0 +1,123 @@
+import { useGlobal } from '@/lib/global'
+import { faAngleDoubleRight, faBars, faSearch, faTag, faThList, faTimes } from '@fortawesome/free-solid-svg-icons'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
+import throttle from 'lodash.throttle'
+import Link from 'next/link'
+import { useCallback, useEffect, useRef, useState } from 'react'
+import CategoryGroup from './CategoryGroup'
+import Collapse from './Collapse'
+import Logo from './Logo'
+import MenuButtonGroup from './MenuButtonGroup'
+import SearchDrawer from './SearchDrawer'
+import TagGroups from './TagGroups'
+import CONFIG_HEXO from '../config_hexo'
+
+let windowTop = 0
+
+/**
+ * 顶部导航
+ * @param {*} param0
+ * @returns
+ */
+const TopNav = ({ tags, currentTag, categories, currentCategory, postCount }) => {
+ const { locale } = useGlobal()
+ const searchDrawer = useRef()
+
+ const scrollTrigger = useCallback(throttle(() => {
+ const scrollS = window.scrollY
+ if (scrollS >= windowTop && scrollS > 10) {
+ const nav = document.querySelector('#sticky-nav')
+ nav && nav.classList.replace('top-0', '-top-40')
+ windowTop = scrollS
+ } else {
+ const nav = document.querySelector('#sticky-nav')
+ nav && nav.classList.replace('-top-40', 'top-0')
+ windowTop = scrollS
+ }
+ }, 200), [])
+
+ // 监听滚动
+ useEffect(() => {
+ if (CONFIG_HEXO.NAV_TYPE === 'autoCollapse') {
+ scrollTrigger()
+ window.addEventListener('scroll', scrollTrigger)
+ }
+ return () => {
+ CONFIG_HEXO.NAV_TYPE === 'autoCollapse' && window.removeEventListener('scroll', scrollTrigger)
+ }
+ }, [])
+
+ const [isOpen, changeShow] = useState(false)
+
+ const toggleMenuOpen = () => {
+ changeShow(!isOpen)
+ }
+
+ const searchDrawerSlot = <>
+ { categories && (
+
+ ) }
+
+ { tags && (
+
+ ) }
+ >
+
+ return (
+
+
+ {/* 导航栏 */}
+
+
+ {/* 左侧LOGO 标题 */}
+
+
+
+
+
+
+ {/* 右侧功能 */}
+
+
{ searchDrawer?.current?.show() }}>
+ {locale.NAV.SEARCH}
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default TopNav
diff --git a/themes/Hexo/config_hexo.js b/themes/Hexo/config_hexo.js
index b0b6edf7..5a85be89 100644
--- a/themes/Hexo/config_hexo.js
+++ b/themes/Hexo/config_hexo.js
@@ -11,6 +11,8 @@ const CONFIG_HEXO = {
MENU_SEARCH: true, // 显示搜索
POST_LIST_COVER: true, // 文章封面
- POST_LIST_SUMMARY: true // 文章摘要
+ POST_LIST_SUMMARY: true, // 文章摘要
+ NAV_TYPE: 'autoCollapse', // ['fixed','autoCollapse','normal'] 分别是固定屏幕顶部、屏幕顶部自动折叠,不固定
+ WIDGET_TO_TOP: true
}
export default CONFIG_HEXO