mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-06-07 15:10:32 +00:00
Merge branch 'tangly1024:main' into main
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
|
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
|
||||||
NEXT_PUBLIC_VERSION=4.0.6
|
NEXT_PUBLIC_VERSION=4.0.8
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
// 注: process.env.XX是Vercel的环境变量,配置方式见:https://docs.tangly1024.com/zh/features/personality
|
// 注: process.env.XX是Vercel的环境变量,配置方式见:https://docs.tangly1024.com/article/how-to-config-notion-next#c4768010ae7d44609b744e79e2f9959a
|
||||||
const BLOG = {
|
const BLOG = {
|
||||||
// Important page_id!!!Duplicate Template from https://www.notion.so/tanghh/02ab3b8678004aa69e9e415905ef32a5
|
// Important page_id!!!Duplicate Template from https://www.notion.so/tanghh/02ab3b8678004aa69e9e415905ef32a5
|
||||||
NOTION_PAGE_ID:
|
NOTION_PAGE_ID:
|
||||||
process.env.NOTION_PAGE_ID || '02ab3b8678004aa69e9e415905ef32a5',
|
process.env.NOTION_PAGE_ID || '02ab3b8678004aa69e9e415905ef32a5',
|
||||||
PSEUDO_STATIC: process.env.NEXT_PUBLIC_PSEUDO_STATIC || false, // 伪静态路径,开启后所有文章URL都以 .html 结尾。
|
PSEUDO_STATIC: process.env.NEXT_PUBLIC_PSEUDO_STATIC || false, // 伪静态路径,开启后所有文章URL都以 .html 结尾。
|
||||||
NEXT_REVALIDATE_SECOND: process.env.NEXT_PUBLIC_REVALIDATE_SECOND || 5, // 更新内容缓存间隔 单位(秒);即每个页面有5秒的纯静态期、此期间无论多少次访问都不会抓取notion数据;调大该值有助于节省Vercel资源、同时提升访问速率,但也会使文章更新有延迟。
|
NEXT_REVALIDATE_SECOND: process.env.NEXT_PUBLIC_REVALIDATE_SECOND || 5, // 更新内容缓存间隔 单位(秒);即每个页面有5秒的纯静态期、此期间无论多少次访问都不会抓取notion数据;调大该值有助于节省Vercel资源、同时提升访问速率,但也会使文章更新有延迟。
|
||||||
THEME: process.env.NEXT_PUBLIC_THEME || 'hexo', // 主题, 支持 ['next','hexo',"fukasawa','medium','example','matery','gitbook','simple'] @see https://preview.tangly1024.com
|
THEME: process.env.NEXT_PUBLIC_THEME || 'hexo', // 当前主题,在themes文件夹下可找到所有支持的主题;主题名称就是文件夹名,例如 example,fukasawa,gitbook,heo,hexo,landing,matery,medium,next,nobelium,plog,simple
|
||||||
THEME_SWITCH: process.env.NEXT_PUBLIC_THEME_SWITCH || false, // 是否显示切换主题按钮
|
THEME_SWITCH: process.env.NEXT_PUBLIC_THEME_SWITCH || false, // 是否显示切换主题按钮
|
||||||
LANG: process.env.NEXT_PUBLIC_LANG || 'zh-CN', // e.g 'zh-CN','en-US' see /lib/lang.js for more.
|
LANG: process.env.NEXT_PUBLIC_LANG || 'zh-CN', // e.g 'zh-CN','en-US' see /lib/lang.js for more.
|
||||||
SINCE: 2021, // e.g if leave this empty, current year will be used.
|
SINCE: 2021, // e.g if leave this empty, current year will be used.
|
||||||
@@ -21,6 +21,7 @@ const BLOG = {
|
|||||||
BIO: process.env.NEXT_PUBLIC_BIO || '一个普通的干饭人🍚', // 作者简介
|
BIO: process.env.NEXT_PUBLIC_BIO || '一个普通的干饭人🍚', // 作者简介
|
||||||
LINK: process.env.NEXT_PUBLIC_LINK || 'https://tangly1024.com', // 网站地址
|
LINK: process.env.NEXT_PUBLIC_LINK || 'https://tangly1024.com', // 网站地址
|
||||||
KEYWORDS: process.env.NEXT_PUBLIC_KEYWORD || 'Notion, 博客', // 网站关键词 英文逗号隔开
|
KEYWORDS: process.env.NEXT_PUBLIC_KEYWORD || 'Notion, 博客', // 网站关键词 英文逗号隔开
|
||||||
|
|
||||||
// 社交链接,不需要可留空白,例如 CONTACT_WEIBO:''
|
// 社交链接,不需要可留空白,例如 CONTACT_WEIBO:''
|
||||||
CONTACT_EMAIL: process.env.NEXT_PUBLIC_CONTACT_EMAIL || '', // 邮箱地址 例如mail@tangly1024.com
|
CONTACT_EMAIL: process.env.NEXT_PUBLIC_CONTACT_EMAIL || '', // 邮箱地址 例如mail@tangly1024.com
|
||||||
CONTACT_WEIBO: process.env.NEXT_PUBLIC_CONTACT_WEIBO || '', // 你的微博个人主页
|
CONTACT_WEIBO: process.env.NEXT_PUBLIC_CONTACT_WEIBO || '', // 你的微博个人主页
|
||||||
@@ -41,7 +42,7 @@ const BLOG = {
|
|||||||
FONT_STYLE: process.env.NEXT_PUBLIC_FONT_STYLE || 'font-sans', // ['font-serif','font-sans'] 两种可选,分别是衬线和无衬线: 参考 https://www.jianshu.com/p/55e410bd2115
|
FONT_STYLE: process.env.NEXT_PUBLIC_FONT_STYLE || 'font-sans', // ['font-serif','font-sans'] 两种可选,分别是衬线和无衬线: 参考 https://www.jianshu.com/p/55e410bd2115
|
||||||
// 字体CSS 例如 https://npm.elemecdn.com/lxgw-wenkai-webfont@1.6.0/style.css
|
// 字体CSS 例如 https://npm.elemecdn.com/lxgw-wenkai-webfont@1.6.0/style.css
|
||||||
FONT_URL: [
|
FONT_URL: [
|
||||||
// 'https://npm.elemecdn.com/lxgw-wenkai-webfont@1.6.0/style.css'
|
// 'https://npm.elemecdn.com/lxgw-wenkai-webfont@1.6.0/style.css',
|
||||||
'https://fonts.googleapis.com/css?family=Bitter&display=swap',
|
'https://fonts.googleapis.com/css?family=Bitter&display=swap',
|
||||||
'https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300&display=swap',
|
'https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300&display=swap',
|
||||||
'https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@300&display=swap'
|
'https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@300&display=swap'
|
||||||
@@ -144,7 +145,7 @@ const BLOG = {
|
|||||||
ALGOLIA_ADMIN_APP_KEY: process.env.ALGOLIA_ADMIN_APP_KEY || null, // 管理后台的KEY,不要暴露在代码中,在这里查看 https://dashboard.algolia.com/account/api-keys/
|
ALGOLIA_ADMIN_APP_KEY: process.env.ALGOLIA_ADMIN_APP_KEY || null, // 管理后台的KEY,不要暴露在代码中,在这里查看 https://dashboard.algolia.com/account/api-keys/
|
||||||
ALGOLIA_SEARCH_ONLY_APP_KEY: process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_ONLY_APP_KEY || null, // 客户端搜索用的KEY
|
ALGOLIA_SEARCH_ONLY_APP_KEY: process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_ONLY_APP_KEY || null, // 客户端搜索用的KEY
|
||||||
ALGOLIA_INDEX: process.env.NEXT_PUBLIC_ALGOLIA_INDEX || null, // 在Algolia中创建一个index用作数据库
|
ALGOLIA_INDEX: process.env.NEXT_PUBLIC_ALGOLIA_INDEX || null, // 在Algolia中创建一个index用作数据库
|
||||||
ALGOLIA_RECREATE_DATA: process.env.ALGOLIA_RECREATE_DATA || process.env.npm_lifecycle_event === 'build', // 为true时重新构建索引数据; 默认在build时会构建
|
// ALGOLIA_RECREATE_DATA: process.env.ALGOLIA_RECREATE_DATA || process.env.npm_lifecycle_event === 'build', // 为true时重新构建索引数据; 默认在build时会构建
|
||||||
|
|
||||||
PREVIEW_CATEGORY_COUNT: 16, // 首页最多展示的分类数量,0为不限制
|
PREVIEW_CATEGORY_COUNT: 16, // 首页最多展示的分类数量,0为不限制
|
||||||
PREVIEW_TAG_COUNT: 16, // 首页最多展示的标签数量,0为不限制
|
PREVIEW_TAG_COUNT: 16, // 首页最多展示的标签数量,0为不限制
|
||||||
@@ -179,19 +180,19 @@ const BLOG = {
|
|||||||
// 悬浮挂件
|
// 悬浮挂件
|
||||||
WIDGET_PET: process.env.NEXT_PUBLIC_WIDGET_PET || true, // 是否显示宠物挂件
|
WIDGET_PET: process.env.NEXT_PUBLIC_WIDGET_PET || true, // 是否显示宠物挂件
|
||||||
WIDGET_PET_LINK:
|
WIDGET_PET_LINK:
|
||||||
process.env.NEXT_PUBLIC_WIDGET_PET_LINK ||
|
process.env.NEXT_PUBLIC_WIDGET_PET_LINK ||
|
||||||
'https://cdn.jsdelivr.net/npm/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json', // 挂件模型地址 @see https://github.com/xiazeyu/live2d-widget-models
|
'https://cdn.jsdelivr.net/npm/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json', // 挂件模型地址 @see https://github.com/xiazeyu/live2d-widget-models
|
||||||
WIDGET_PET_SWITCH_THEME: process.env.NEXT_PUBLIC_WIDGET_PET_SWITCH_THEME || true, // 点击宠物挂件切换博客主题
|
WIDGET_PET_SWITCH_THEME: process.env.NEXT_PUBLIC_WIDGET_PET_SWITCH_THEME || true, // 点击宠物挂件切换博客主题
|
||||||
|
|
||||||
// 音乐播放插件
|
// 音乐播放插件
|
||||||
MUSIC_PLAYER: process.env.NEXT_PUBLIC_MUSIC_PLAYER || false, // 是否使用音乐播放插件
|
MUSIC_PLAYER: process.env.NEXT_PUBLIC_MUSIC_PLAYER || false, // 是否使用音乐播放插件
|
||||||
MUSIC_PLAYER_VISIBLE: process.env.NEXT_PUBLIC_MUSIC_PLAYER_VISIBLE || true, // 是否在左下角显示播放和切换,如果使用播放器,打开自动播放再隐藏,就会以类似背景音乐的方式播放,无法取消和暂停
|
MUSIC_PLAYER_VISIBLE: process.env.NEXT_PUBLIC_MUSIC_PLAYER_VISIBLE || true, // 是否在左下角显示播放和切换,如果使用播放器,打开自动播放再隐藏,就会以类似背景音乐的方式播放,无法取消和暂停
|
||||||
MUSIC_PLAYER_AUTO_PLAY:
|
MUSIC_PLAYER_AUTO_PLAY:
|
||||||
process.env.NEXT_PUBLIC_MUSIC_PLAYER_AUTO_PLAY || true, // 是否自动播放,不过自动播放时常不生效(移动设备不支持自动播放)
|
process.env.NEXT_PUBLIC_MUSIC_PLAYER_AUTO_PLAY || true, // 是否自动播放,不过自动播放时常不生效(移动设备不支持自动播放)
|
||||||
MUSIC_PLAYER_LRC_TYPE: process.env.NEXT_PUBLIC_MUSIC_PLAYER_LRC_TYPE || '0', // 歌词显示类型,可选值: 3 | 1 | 0(0:禁用 lrc 歌词,1:lrc 格式的字符串,3:lrc 文件 url)(前提是有配置歌词路径,对 meting 无效)
|
MUSIC_PLAYER_LRC_TYPE: process.env.NEXT_PUBLIC_MUSIC_PLAYER_LRC_TYPE || '0', // 歌词显示类型,可选值: 3 | 1 | 0(0:禁用 lrc 歌词,1:lrc 格式的字符串,3:lrc 文件 url)(前提是有配置歌词路径,对 meting 无效)
|
||||||
MUSIC_PLAYER_CDN_URL:
|
MUSIC_PLAYER_CDN_URL:
|
||||||
process.env.NEXT_PUBLIC_MUSIC_PLAYER_CDN_URL ||
|
process.env.NEXT_PUBLIC_MUSIC_PLAYER_CDN_URL ||
|
||||||
'https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/aplayer/1.10.1/APlayer.min.js',
|
'https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/aplayer/1.10.1/APlayer.min.js',
|
||||||
MUSIC_PLAYER_ORDER: process.env.NEXT_PUBLIC_MUSIC_PLAYER_ORDER || 'list', // 默认播放方式,顺序 list,随机 random
|
MUSIC_PLAYER_ORDER: process.env.NEXT_PUBLIC_MUSIC_PLAYER_ORDER || 'list', // 默认播放方式,顺序 list,随机 random
|
||||||
MUSIC_PLAYER_AUDIO_LIST: [
|
MUSIC_PLAYER_AUDIO_LIST: [
|
||||||
// 示例音乐列表。除了以下配置外,还可配置歌词,具体配置项看此文档 https://aplayer.js.org/#/zh-Hans/
|
// 示例音乐列表。除了以下配置外,还可配置歌词,具体配置项看此文档 https://aplayer.js.org/#/zh-Hans/
|
||||||
@@ -200,23 +201,23 @@ const BLOG = {
|
|||||||
artist: 'Falcom Sound Team jdk',
|
artist: 'Falcom Sound Team jdk',
|
||||||
url: 'https://music.163.com/song/media/outer/url?id=731419.mp3',
|
url: 'https://music.163.com/song/media/outer/url?id=731419.mp3',
|
||||||
cover:
|
cover:
|
||||||
'https://p2.music.126.net/kn6ugISTonvqJh3LHLaPtQ==/599233837187278.jpg'
|
'https://p2.music.126.net/kn6ugISTonvqJh3LHLaPtQ==/599233837187278.jpg'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '王都グランセル',
|
name: '王都グランセル',
|
||||||
artist: 'Falcom Sound Team jdk',
|
artist: 'Falcom Sound Team jdk',
|
||||||
url: 'https://music.163.com/song/media/outer/url?id=731355.mp3',
|
url: 'https://music.163.com/song/media/outer/url?id=731355.mp3',
|
||||||
cover:
|
cover:
|
||||||
'https://p1.music.126.net/kn6ugISTonvqJh3LHLaPtQ==/599233837187278.jpg'
|
'https://p1.music.126.net/kn6ugISTonvqJh3LHLaPtQ==/599233837187278.jpg'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
MUSIC_PLAYER_METING: process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING || false, // 是否要开启 MetingJS,从平台获取歌单。会覆盖自定义的 MUSIC_PLAYER_AUDIO_LIST,更多配置信息:https://github.com/metowolf/MetingJS
|
MUSIC_PLAYER_METING: process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING || false, // 是否要开启 MetingJS,从平台获取歌单。会覆盖自定义的 MUSIC_PLAYER_AUDIO_LIST,更多配置信息:https://github.com/metowolf/MetingJS
|
||||||
MUSIC_PLAYER_METING_SERVER:
|
MUSIC_PLAYER_METING_SERVER:
|
||||||
process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_SERVER || 'netease', // 音乐平台,[netease, tencent, kugou, xiami, baidu]
|
process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_SERVER || 'netease', // 音乐平台,[netease, tencent, kugou, xiami, baidu]
|
||||||
MUSIC_PLAYER_METING_ID:
|
MUSIC_PLAYER_METING_ID:
|
||||||
process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_ID || '60198', // 对应歌单的 id
|
process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_ID || '60198', // 对应歌单的 id
|
||||||
MUSIC_PLAYER_METING_LRC_TYPE:
|
MUSIC_PLAYER_METING_LRC_TYPE:
|
||||||
process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_LRC_TYPE || '1', // 可选值: 3 | 1 | 0(0:禁用 lrc 歌词,1:lrc 格式的字符串,3:lrc 文件 url)
|
process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_LRC_TYPE || '1', // 可选值: 3 | 1 | 0(0:禁用 lrc 歌词,1:lrc 格式的字符串,3:lrc 文件 url)
|
||||||
|
|
||||||
// ********挂件组件相关********
|
// ********挂件组件相关********
|
||||||
// ----> 评论互动 可同时开启多个支持 WALINE VALINE GISCUS CUSDIS UTTERRANCES GITALK
|
// ----> 评论互动 可同时开启多个支持 WALINE VALINE GISCUS CUSDIS UTTERRANCES GITALK
|
||||||
@@ -228,42 +229,42 @@ const BLOG = {
|
|||||||
|
|
||||||
// utterance
|
// utterance
|
||||||
COMMENT_UTTERRANCES_REPO:
|
COMMENT_UTTERRANCES_REPO:
|
||||||
process.env.NEXT_PUBLIC_COMMENT_UTTERRANCES_REPO || '', // 你的代码仓库名, 例如我是 'tangly1024/NotionNext'; 更多文档参考 https://utteranc.es/
|
process.env.NEXT_PUBLIC_COMMENT_UTTERRANCES_REPO || '', // 你的代码仓库名, 例如我是 'tangly1024/NotionNext'; 更多文档参考 https://utteranc.es/
|
||||||
|
|
||||||
// giscus @see https://giscus.app/
|
// giscus @see https://giscus.app/
|
||||||
COMMENT_GISCUS_REPO: process.env.NEXT_PUBLIC_COMMENT_GISCUS_REPO || '', // 你的Github仓库名 e.g 'tangly1024/NotionNext'
|
COMMENT_GISCUS_REPO: process.env.NEXT_PUBLIC_COMMENT_GISCUS_REPO || '', // 你的Github仓库名 e.g 'tangly1024/NotionNext'
|
||||||
COMMENT_GISCUS_REPO_ID: process.env.NEXT_PUBLIC_COMMENT_GISCUS_REPO_ID || '', // 你的Github Repo ID e.g ( 設定完 giscus 即可看到 )
|
COMMENT_GISCUS_REPO_ID: process.env.NEXT_PUBLIC_COMMENT_GISCUS_REPO_ID || '', // 你的Github Repo ID e.g ( 設定完 giscus 即可看到 )
|
||||||
COMMENT_GISCUS_CATEGORY_ID:
|
COMMENT_GISCUS_CATEGORY_ID:
|
||||||
process.env.NEXT_PUBLIC_COMMENT_GISCUS_CATEGORY_ID || '', // 你的Github Discussions 內的 Category ID ( 設定完 giscus 即可看到 )
|
process.env.NEXT_PUBLIC_COMMENT_GISCUS_CATEGORY_ID || '', // 你的Github Discussions 內的 Category ID ( 設定完 giscus 即可看到 )
|
||||||
COMMENT_GISCUS_MAPPING:
|
COMMENT_GISCUS_MAPPING:
|
||||||
process.env.NEXT_PUBLIC_COMMENT_GISCUS_MAPPING || 'pathname', // 你的Github Discussions 使用哪種方式來標定文章, 預設 'pathname'
|
process.env.NEXT_PUBLIC_COMMENT_GISCUS_MAPPING || 'pathname', // 你的Github Discussions 使用哪種方式來標定文章, 預設 'pathname'
|
||||||
COMMENT_GISCUS_REACTIONS_ENABLED:
|
COMMENT_GISCUS_REACTIONS_ENABLED:
|
||||||
process.env.NEXT_PUBLIC_COMMENT_GISCUS_REACTIONS_ENABLED || '1', // 你的 Giscus 是否開啟文章表情符號 '1' 開啟 "0" 關閉 預設開啟
|
process.env.NEXT_PUBLIC_COMMENT_GISCUS_REACTIONS_ENABLED || '1', // 你的 Giscus 是否開啟文章表情符號 '1' 開啟 "0" 關閉 預設開啟
|
||||||
COMMENT_GISCUS_EMIT_METADATA:
|
COMMENT_GISCUS_EMIT_METADATA:
|
||||||
process.env.NEXT_PUBLIC_COMMENT_GISCUS_EMIT_METADATA || '0', // 你的 Giscus 是否提取 Metadata '1' 開啟 '0' 關閉 預設關閉
|
process.env.NEXT_PUBLIC_COMMENT_GISCUS_EMIT_METADATA || '0', // 你的 Giscus 是否提取 Metadata '1' 開啟 '0' 關閉 預設關閉
|
||||||
COMMENT_GISCUS_INPUT_POSITION:
|
COMMENT_GISCUS_INPUT_POSITION:
|
||||||
process.env.NEXT_PUBLIC_COMMENT_GISCUS_INPUT_POSITION || 'bottom', // 你的 Giscus 發表留言位置 'bottom' 尾部 'top' 頂部, 預設 'bottom'
|
process.env.NEXT_PUBLIC_COMMENT_GISCUS_INPUT_POSITION || 'bottom', // 你的 Giscus 發表留言位置 'bottom' 尾部 'top' 頂部, 預設 'bottom'
|
||||||
COMMENT_GISCUS_LANG: process.env.NEXT_PUBLIC_COMMENT_GISCUS_LANG || 'zh-CN', // 你的 Giscus 語言 e.g 'en', 'zh-TW', 'zh-CN', 預設 'en'
|
COMMENT_GISCUS_LANG: process.env.NEXT_PUBLIC_COMMENT_GISCUS_LANG || 'zh-CN', // 你的 Giscus 語言 e.g 'en', 'zh-TW', 'zh-CN', 預設 'en'
|
||||||
COMMENT_GISCUS_LOADING:
|
COMMENT_GISCUS_LOADING:
|
||||||
process.env.NEXT_PUBLIC_COMMENT_GISCUS_LOADING || 'lazy', // 你的 Giscus 載入是否漸進式載入, 預設 'lazy'
|
process.env.NEXT_PUBLIC_COMMENT_GISCUS_LOADING || 'lazy', // 你的 Giscus 載入是否漸進式載入, 預設 'lazy'
|
||||||
COMMENT_GISCUS_CROSSORIGIN:
|
COMMENT_GISCUS_CROSSORIGIN:
|
||||||
process.env.NEXT_PUBLIC_COMMENT_GISCUS_CROSSORIGIN || 'anonymous', // 你的 Giscus 可以跨網域, 預設 'anonymous'
|
process.env.NEXT_PUBLIC_COMMENT_GISCUS_CROSSORIGIN || 'anonymous', // 你的 Giscus 可以跨網域, 預設 'anonymous'
|
||||||
|
|
||||||
COMMENT_CUSDIS_APP_ID: process.env.NEXT_PUBLIC_COMMENT_CUSDIS_APP_ID || '', // data-app-id 36位 see https://cusdis.com/
|
COMMENT_CUSDIS_APP_ID: process.env.NEXT_PUBLIC_COMMENT_CUSDIS_APP_ID || '', // data-app-id 36位 see https://cusdis.com/
|
||||||
COMMENT_CUSDIS_HOST:
|
COMMENT_CUSDIS_HOST:
|
||||||
process.env.NEXT_PUBLIC_COMMENT_CUSDIS_HOST || 'https://cusdis.com', // data-host, change this if you're using self-hosted version
|
process.env.NEXT_PUBLIC_COMMENT_CUSDIS_HOST || 'https://cusdis.com', // data-host, change this if you're using self-hosted version
|
||||||
COMMENT_CUSDIS_SCRIPT_SRC:
|
COMMENT_CUSDIS_SCRIPT_SRC:
|
||||||
process.env.NEXT_PUBLIC_COMMENT_CUSDIS_SCRIPT_SRC ||
|
process.env.NEXT_PUBLIC_COMMENT_CUSDIS_SCRIPT_SRC ||
|
||||||
'https://cusdis.com/js/cusdis.es.js', // change this if you're using self-hosted version
|
'https://cusdis.com/js/cusdis.es.js', // change this if you're using self-hosted version
|
||||||
|
|
||||||
// gitalk评论插件 更多参考 https://gitalk.github.io/
|
// gitalk评论插件 更多参考 https://gitalk.github.io/
|
||||||
COMMENT_GITALK_REPO: process.env.NEXT_PUBLIC_COMMENT_GITALK_REPO || '', // 你的Github仓库名,例如 'NotionNext'
|
COMMENT_GITALK_REPO: process.env.NEXT_PUBLIC_COMMENT_GITALK_REPO || '', // 你的Github仓库名,例如 'NotionNext'
|
||||||
COMMENT_GITALK_OWNER: process.env.NEXT_PUBLIC_COMMENT_GITALK_OWNER || '', // 你的用户名 e.g tangly1024
|
COMMENT_GITALK_OWNER: process.env.NEXT_PUBLIC_COMMENT_GITALK_OWNER || '', // 你的用户名 e.g tangly1024
|
||||||
COMMENT_GITALK_ADMIN: process.env.NEXT_PUBLIC_COMMENT_GITALK_ADMIN || '', // 管理员用户名、一般是自己 e.g 'tangly1024'
|
COMMENT_GITALK_ADMIN: process.env.NEXT_PUBLIC_COMMENT_GITALK_ADMIN || '', // 管理员用户名、一般是自己 e.g 'tangly1024'
|
||||||
COMMENT_GITALK_CLIENT_ID:
|
COMMENT_GITALK_CLIENT_ID:
|
||||||
process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_ID || '', // e.g 20位ID , 在gitalk后台获取
|
process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_ID || '', // e.g 20位ID , 在gitalk后台获取
|
||||||
COMMENT_GITALK_CLIENT_SECRET:
|
COMMENT_GITALK_CLIENT_SECRET:
|
||||||
process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_SECRET || '', // e.g 40位ID, 在gitalk后台获取
|
process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_SECRET || '', // e.g 40位ID, 在gitalk后台获取
|
||||||
COMMENT_GITALK_DISTRACTION_FREE_MODE: false, // 类似facebook的无干扰模式
|
COMMENT_GITALK_DISTRACTION_FREE_MODE: false, // 类似facebook的无干扰模式
|
||||||
COMMENT_GITALK_JS_CDN_URL: process.env.NEXT_PUBLIC_COMMENT_GITALK_JS_CDN_URL || 'https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js', // gitalk客户端 js cdn
|
COMMENT_GITALK_JS_CDN_URL: process.env.NEXT_PUBLIC_COMMENT_GITALK_JS_CDN_URL || 'https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js', // gitalk客户端 js cdn
|
||||||
COMMENT_GITALK_CSS_CDN_URL: process.env.NEXT_PUBLIC_COMMENT_GITALK_CSS_CDN_URL || 'https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css', // gitalk客户端 css cdn
|
COMMENT_GITALK_CSS_CDN_URL: process.env.NEXT_PUBLIC_COMMENT_GITALK_CSS_CDN_URL || 'https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css', // gitalk客户端 css cdn
|
||||||
@@ -277,7 +278,7 @@ const BLOG = {
|
|||||||
COMMENT_VALINE_APP_KEY: process.env.NEXT_PUBLIC_VALINE_KEY || '',
|
COMMENT_VALINE_APP_KEY: process.env.NEXT_PUBLIC_VALINE_KEY || '',
|
||||||
COMMENT_VALINE_SERVER_URLS: process.env.NEXT_PUBLIC_VALINE_SERVER_URLS || '', // 该配置适用于国内自定义域名用户, 海外版本会自动检测(无需手动填写) @see https://valine.js.org/configuration.html#serverURLs
|
COMMENT_VALINE_SERVER_URLS: process.env.NEXT_PUBLIC_VALINE_SERVER_URLS || '', // 该配置适用于国内自定义域名用户, 海外版本会自动检测(无需手动填写) @see https://valine.js.org/configuration.html#serverURLs
|
||||||
COMMENT_VALINE_PLACEHOLDER:
|
COMMENT_VALINE_PLACEHOLDER:
|
||||||
process.env.NEXT_PUBLIC_VALINE_PLACEHOLDER || '抢个沙发吧~', // 可以搭配后台管理评论 https://github.com/DesertsP/Valine-Admin 便于查看评论,以及邮件通知,垃圾评论过滤等功能
|
process.env.NEXT_PUBLIC_VALINE_PLACEHOLDER || '抢个沙发吧~', // 可以搭配后台管理评论 https://github.com/DesertsP/Valine-Admin 便于查看评论,以及邮件通知,垃圾评论过滤等功能
|
||||||
|
|
||||||
COMMENT_WALINE_SERVER_URL: process.env.NEXT_PUBLIC_WALINE_SERVER_URL || '', // 请配置完整的Waline评论地址 例如 hhttps://preview-waline.tangly1024.com @see https://waline.js.org/guide/get-started.html
|
COMMENT_WALINE_SERVER_URL: process.env.NEXT_PUBLIC_WALINE_SERVER_URL || '', // 请配置完整的Waline评论地址 例如 hhttps://preview-waline.tangly1024.com @see https://waline.js.org/guide/get-started.html
|
||||||
COMMENT_WALINE_RECENT: process.env.NEXT_PUBLIC_WALINE_RECENT || false, // 最新评论
|
COMMENT_WALINE_RECENT: process.env.NEXT_PUBLIC_WALINE_RECENT || false, // 最新评论
|
||||||
@@ -307,17 +308,17 @@ const BLOG = {
|
|||||||
ANALYTICS_GOOGLE_ID: process.env.NEXT_PUBLIC_ANALYTICS_GOOGLE_ID || '', // 谷歌Analytics的id e.g: G-XXXXXXXXXX
|
ANALYTICS_GOOGLE_ID: process.env.NEXT_PUBLIC_ANALYTICS_GOOGLE_ID || '', // 谷歌Analytics的id e.g: G-XXXXXXXXXX
|
||||||
|
|
||||||
ANALYTICS_ACKEE_TRACKER:
|
ANALYTICS_ACKEE_TRACKER:
|
||||||
process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_TRACKER || '', // e.g 'https://ackee.tangly1024.net/tracker.js'
|
process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_TRACKER || '', // e.g 'https://ackee.tangly1024.net/tracker.js'
|
||||||
ANALYTICS_ACKEE_DATA_SERVER:
|
ANALYTICS_ACKEE_DATA_SERVER:
|
||||||
process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DATA_SERVER || '', // e.g https://ackee.tangly1024.net , don't end with a slash
|
process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DATA_SERVER || '', // e.g https://ackee.tangly1024.net , don't end with a slash
|
||||||
ANALYTICS_ACKEE_DOMAIN_ID:
|
ANALYTICS_ACKEE_DOMAIN_ID:
|
||||||
process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DOMAIN_ID || '', // e.g '0e2257a8-54d4-4847-91a1-0311ea48cc7b'
|
process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DOMAIN_ID || '', // e.g '0e2257a8-54d4-4847-91a1-0311ea48cc7b'
|
||||||
|
|
||||||
SEO_GOOGLE_SITE_VERIFICATION:
|
SEO_GOOGLE_SITE_VERIFICATION:
|
||||||
process.env.NEXT_PUBLIC_SEO_GOOGLE_SITE_VERIFICATION || '', // Remove the value or replace it with your own google site verification code
|
process.env.NEXT_PUBLIC_SEO_GOOGLE_SITE_VERIFICATION || '', // Remove the value or replace it with your own google site verification code
|
||||||
|
|
||||||
SEO_BAIDU_SITE_VERIFICATION:
|
SEO_BAIDU_SITE_VERIFICATION:
|
||||||
process.env.NEXT_PUBLIC_SEO_BAIDU_SITE_VERIFICATION || '', // Remove the value or replace it with your own google site verification code
|
process.env.NEXT_PUBLIC_SEO_BAIDU_SITE_VERIFICATION || '', // Remove the value or replace it with your own google site verification code
|
||||||
|
|
||||||
// <---- 站点统计
|
// <---- 站点统计
|
||||||
|
|
||||||
@@ -336,16 +337,16 @@ const BLOG = {
|
|||||||
type_post: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_POST || 'Post', // 当type文章类型与此值相同时,为博文。
|
type_post: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_POST || 'Post', // 当type文章类型与此值相同时,为博文。
|
||||||
type_page: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_PAGE || 'Page', // 当type文章类型与此值相同时,为单页。
|
type_page: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_PAGE || 'Page', // 当type文章类型与此值相同时,为单页。
|
||||||
type_notice:
|
type_notice:
|
||||||
process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_NOTICE || 'Notice', // 当type文章类型与此值相同时,为公告。
|
process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_NOTICE || 'Notice', // 当type文章类型与此值相同时,为公告。
|
||||||
type_menu: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_MENU || 'Menu', // 当type文章类型与此值相同时,为菜单。
|
type_menu: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_MENU || 'Menu', // 当type文章类型与此值相同时,为菜单。
|
||||||
type_sub_menu:
|
type_sub_menu:
|
||||||
process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_SUB_MENU || 'SubMenu', // 当type文章类型与此值相同时,为子菜单。
|
process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_SUB_MENU || 'SubMenu', // 当type文章类型与此值相同时,为子菜单。
|
||||||
title: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TITLE || 'title', // 文章标题
|
title: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TITLE || 'title', // 文章标题
|
||||||
status: process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS || 'status',
|
status: process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS || 'status',
|
||||||
status_publish:
|
status_publish:
|
||||||
process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS_PUBLISH || 'Published', // 当status状态值与此相同时为发布,可以为中文
|
process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS_PUBLISH || 'Published', // 当status状态值与此相同时为发布,可以为中文
|
||||||
status_invisible:
|
status_invisible:
|
||||||
process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS_INVISIBLE || 'Invisible', // 当status状态值与此相同时为隐藏发布,可以为中文 , 除此之外其他页面状态不会显示在博客上
|
process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS_INVISIBLE || 'Invisible', // 当status状态值与此相同时为隐藏发布,可以为中文 , 除此之外其他页面状态不会显示在博客上
|
||||||
summary: process.env.NEXT_PUBLIC_NOTION_PROPERTY_SUMMARY || 'summary',
|
summary: process.env.NEXT_PUBLIC_NOTION_PROPERTY_SUMMARY || 'summary',
|
||||||
slug: process.env.NEXT_PUBLIC_NOTION_PROPERTY_SLUG || 'slug',
|
slug: process.env.NEXT_PUBLIC_NOTION_PROPERTY_SLUG || 'slug',
|
||||||
category: process.env.NEXT_PUBLIC_NOTION_PROPERTY_CATEGORY || 'category',
|
category: process.env.NEXT_PUBLIC_NOTION_PROPERTY_CATEGORY || 'category',
|
||||||
@@ -363,9 +364,9 @@ const BLOG = {
|
|||||||
AVATAR: process.env.NEXT_PUBLIC_AVATAR || '/avatar.svg', // 作者头像,被notion中的ICON覆盖。若无ICON则取public目录下的avatar.png
|
AVATAR: process.env.NEXT_PUBLIC_AVATAR || '/avatar.svg', // 作者头像,被notion中的ICON覆盖。若无ICON则取public目录下的avatar.png
|
||||||
TITLE: process.env.NEXT_PUBLIC_TITLE || 'NotionNext BLOG', // 站点标题 ,被notion中的页面标题覆盖;此处请勿留空白,否则服务器无法编译
|
TITLE: process.env.NEXT_PUBLIC_TITLE || 'NotionNext BLOG', // 站点标题 ,被notion中的页面标题覆盖;此处请勿留空白,否则服务器无法编译
|
||||||
HOME_BANNER_IMAGE:
|
HOME_BANNER_IMAGE:
|
||||||
process.env.NEXT_PUBLIC_HOME_BANNER_IMAGE || '/bg_image.jpg', // 首页背景大图, 会被notion中的封面图覆盖,若无封面图则会使用代码中的 /public/bg_image.jpg 文件
|
process.env.NEXT_PUBLIC_HOME_BANNER_IMAGE || '/bg_image.jpg', // 首页背景大图, 会被notion中的封面图覆盖,若无封面图则会使用代码中的 /public/bg_image.jpg 文件
|
||||||
DESCRIPTION:
|
DESCRIPTION:
|
||||||
process.env.NEXT_PUBLIC_DESCRIPTION || '这是一个由NotionNext生成的站点', // 站点描述,被notion中的页面描述覆盖
|
process.env.NEXT_PUBLIC_DESCRIPTION || '这是一个由NotionNext生成的站点', // 站点描述,被notion中的页面描述覆盖
|
||||||
|
|
||||||
// 网站图片
|
// 网站图片
|
||||||
IMG_LAZY_LOAD_PLACEHOLDER: process.env.NEXT_PUBLIC_IMG_LAZY_LOAD_PLACEHOLDER || 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==', // 懒加载占位图片地址,支持base64或url
|
IMG_LAZY_LOAD_PLACEHOLDER: process.env.NEXT_PUBLIC_IMG_LAZY_LOAD_PLACEHOLDER || 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==', // 懒加载占位图片地址,支持base64或url
|
||||||
@@ -375,7 +376,7 @@ const BLOG = {
|
|||||||
// 开发相关
|
// 开发相关
|
||||||
NOTION_ACCESS_TOKEN: process.env.NOTION_ACCESS_TOKEN || '', // Useful if you prefer not to make your database public
|
NOTION_ACCESS_TOKEN: process.env.NOTION_ACCESS_TOKEN || '', // Useful if you prefer not to make your database public
|
||||||
DEBUG: process.env.NEXT_PUBLIC_DEBUG || false, // 是否显示调试按钮
|
DEBUG: process.env.NEXT_PUBLIC_DEBUG || false, // 是否显示调试按钮
|
||||||
ENABLE_CACHE: process.env.ENABLE_CACHE || false, // 开启缓存会将Notion数据缓存在内存中,通常在开发调试中使用,正式部署开启此功能意义不大。
|
ENABLE_CACHE: process.env.ENABLE_CACHE || process.env.npm_lifecycle_event === 'build', // 缓存在开发调试和打包过程中选择性开启,正式部署开启此功能意义不大。
|
||||||
isProd: process.env.VERCEL_ENV === 'production', // distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables) isProd: process.env.VERCEL_ENV === 'production' // distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables)
|
isProd: process.env.VERCEL_ENV === 'production', // distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables) isProd: process.env.VERCEL_ENV === 'production' // distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables)
|
||||||
VERSION: process.env.NEXT_PUBLIC_VERSION // 版本号
|
VERSION: process.env.NEXT_PUBLIC_VERSION // 版本号
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { useState, useImperativeHandle } from 'react'
|
import { useState, useImperativeHandle, useRef } from 'react'
|
||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import algoliasearch from 'algoliasearch'
|
import algoliasearch from 'algoliasearch'
|
||||||
import replaceSearchResult from '@/components/Mark'
|
import replaceSearchResult from '@/components/Mark'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import throttle from 'lodash/throttle'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 结合 Algolia 实现的弹出式搜索框
|
* 结合 Algolia 实现的弹出式搜索框
|
||||||
@@ -11,6 +14,12 @@ import replaceSearchResult from '@/components/Mark'
|
|||||||
export default function AlgoliaSearchModal({ cRef }) {
|
export default function AlgoliaSearchModal({ cRef }) {
|
||||||
const [searchResults, setSearchResults] = useState([])
|
const [searchResults, setSearchResults] = useState([])
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||||
|
const [page, setPage] = useState(0)
|
||||||
|
const [keyword, setKeyword] = useState(null)
|
||||||
|
const [totalPage, setTotalPage] = useState(0)
|
||||||
|
const [totalHit, setTotalHit] = useState(0)
|
||||||
|
const [useTime, setUseTime] = useState(0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对外暴露方法
|
* 对外暴露方法
|
||||||
*/
|
*/
|
||||||
@@ -22,66 +31,108 @@ export default function AlgoliaSearchModal({ cRef }) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!BLOG.ALGOLIA_APP_ID) {
|
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_SEARCH_ONLY_APP_KEY)
|
const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_SEARCH_ONLY_APP_KEY)
|
||||||
const index = client.initIndex(BLOG.ALGOLIA_INDEX)
|
const index = client.initIndex(BLOG.ALGOLIA_INDEX)
|
||||||
|
|
||||||
const handleSearch = async (query) => {
|
/**
|
||||||
|
* 搜索
|
||||||
|
* @param {*} query
|
||||||
|
*/
|
||||||
|
const handleSearch = async (query, page) => {
|
||||||
|
setKeyword(query)
|
||||||
|
setPage(page)
|
||||||
|
setSearchResults([])
|
||||||
|
setUseTime(0)
|
||||||
|
setTotalPage(0)
|
||||||
|
setTotalHit(0)
|
||||||
|
if (!query || query === '') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await index.search(query)
|
const res = await index.search(query, { page, hitsPerPage: 10 })
|
||||||
console.log(res)
|
const { hits, nbHits, nbPages, processingTimeMS } = res
|
||||||
const { hits } = res
|
setUseTime(processingTimeMS)
|
||||||
|
setTotalPage(nbPages)
|
||||||
|
setTotalHit(nbHits)
|
||||||
setSearchResults(hits)
|
setSearchResults(hits)
|
||||||
|
|
||||||
const doms = document.getElementById('search-wrapper').getElementsByClassName('replace')
|
const doms = document.getElementById('search-wrapper').getElementsByClassName('replace')
|
||||||
replaceSearchResult({
|
|
||||||
doms,
|
setTimeout(() => {
|
||||||
search: query,
|
replaceSearchResult({
|
||||||
target: {
|
doms,
|
||||||
element: 'span',
|
search: query,
|
||||||
className: 'text-blue-600 border-b border-dashed'
|
target: {
|
||||||
}
|
element: 'span',
|
||||||
})
|
className: 'text-blue-600 border-b border-dashed'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 150)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Algolia search error:', error)
|
console.error('Algolia search error:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const throttledHandleSearch = useRef(throttle(handleSearch, 300)) // 设置节流延迟时间
|
||||||
|
|
||||||
|
// 修改input的onChange事件处理函数
|
||||||
|
const handleInputChange = (e) => {
|
||||||
|
const query = e.target.value
|
||||||
|
throttledHandleSearch.current(query, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换页码
|
||||||
|
* @param {*} page
|
||||||
|
*/
|
||||||
|
const switchPage = (page) => {
|
||||||
|
throttledHandleSearch.current(keyword, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭弹窗
|
||||||
|
*/
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
setIsModalOpen(false)
|
setIsModalOpen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!BLOG.ALGOLIA_APP_ID) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id='search-wrapper' className={`${isModalOpen ? 'opacity-100' : 'invisible opacity-0 pointer-events-none'} fixed h-screen w-screen left-0 top-0 flex items-center justify-center`}>
|
<div id='search-wrapper' className={`${isModalOpen ? 'opacity-100' : 'invisible opacity-0 pointer-events-none'} fixed h-screen w-screen left-0 top-0 mt-12 flex items-start justify-center`}>
|
||||||
{/* 内容 */}
|
|
||||||
|
{/* 模态框 */}
|
||||||
<div className={`${isModalOpen ? 'opacity-100' : 'invisible opacity-0 translate-y-10'} flex flex-col justify-between w-full min-h-[10rem] max-w-xl dark:bg-hexo-black-gray dark:border-gray-800 bg-white dark:bg- p-5 rounded-lg z-50 shadow border hover:border-blue-600 duration-300 transition-all `}>
|
<div className={`${isModalOpen ? 'opacity-100' : 'invisible opacity-0 translate-y-10'} flex flex-col justify-between w-full min-h-[10rem] max-w-xl dark:bg-hexo-black-gray dark:border-gray-800 bg-white dark:bg- p-5 rounded-lg z-50 shadow border hover:border-blue-600 duration-300 transition-all `}>
|
||||||
|
|
||||||
<div className='flex justify-between items-center'>
|
<div className='flex justify-between items-center'>
|
||||||
<div className='text-2xl text-blue-600 font-bold'>搜索</div>
|
<div className='text-2xl text-blue-600 font-bold'>搜索</div>
|
||||||
<div><i class="text-gray-600 fa-solid fa-xmark p-1 cursor-pointer hover:text-blue-600" onClick={closeModal} ></i></div>
|
<div><i className="text-gray-600 fa-solid fa-xmark p-1 cursor-pointer hover:text-blue-600" onClick={closeModal} ></i></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input type="text" placeholder="在这里输入搜索关键词..." onChange={(e) => handleSearch(e.target.value)}
|
<input type="text" placeholder="在这里输入搜索关键词..." onChange={(e) => handleInputChange(e)}
|
||||||
className="bg-gray-50 dark:bg-gray-600 outline-blue-500 w-full px-4 my-2 py-1 mb-4 border rounded-md" />
|
className="text-black dark:text-gray-200 bg-gray-50 dark:bg-gray-600 outline-blue-500 w-full px-4 my-2 py-1 mb-4 border rounded-md" />
|
||||||
|
|
||||||
{/* 标签组 */}
|
{/* 标签组 */}
|
||||||
<div>
|
<div className='mb-4'>
|
||||||
|
<TagGroups/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{searchResults.map((result) => (
|
{searchResults.map((result) => (
|
||||||
<li key={result.objectID} className="replace my-2">
|
<li key={result.objectID} className="replace my-2">
|
||||||
<a href={`${BLOG.SUB_PATH}/${result.slug}`} className="font-bold hover:text-blue-600 ">
|
<a href={`${BLOG.SUB_PATH}/${result.slug}`} className="font-bold hover:text-blue-600 text-black dark:text-gray-200">
|
||||||
{result.title}
|
{result.title}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div className='text-gray-600'><i class="fa-brands fa-algolia"></i> Algolia 提供搜索服务</div>
|
<Pagination totalPage={totalPage} page={page} switchPage={switchPage}/>
|
||||||
|
<div>{totalHit > 0 && <div>共搜索到 {totalHit} 条结果,用时 {useTime} 毫秒</div> }</div>
|
||||||
|
<div className='text-gray-600 mt-2'><span><i className="fa-brands fa-algolia"></i> Algolia 提供搜索服务</span> </div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 遮罩 */}
|
{/* 遮罩 */}
|
||||||
@@ -90,3 +141,59 @@ export default function AlgoliaSearchModal({ cRef }) {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签组
|
||||||
|
*/
|
||||||
|
function TagGroups(props) {
|
||||||
|
const { tagOptions } = useGlobal()
|
||||||
|
// 获取tagOptions数组前十个
|
||||||
|
const firstTenTags = tagOptions?.slice(0, 10)
|
||||||
|
|
||||||
|
return <div id='tags-group' className='dark:border-gray-700 space-y-2'>
|
||||||
|
{
|
||||||
|
firstTenTags?.map((tag, index) => {
|
||||||
|
return <Link passHref
|
||||||
|
key={index}
|
||||||
|
href={`/tag/${encodeURIComponent(tag.name)}`}
|
||||||
|
className={'cursor-pointer inline-block whitespace-nowrap'}>
|
||||||
|
<div className={' flex items-center text-black dark:text-gray-300 hover:bg-blue-600 dark:hover:bg-yellow-600 hover:scale-110 hover:text-white rounded-lg px-2 py-0.5 duration-150 transition-all'}>
|
||||||
|
<div className='text-lg'>{tag.name} </div>{tag.count ? <sup className='relative ml-1'>{tag.count}</sup> : <></>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</Link>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页
|
||||||
|
* @param {*} param0
|
||||||
|
*/
|
||||||
|
function Pagination(props) {
|
||||||
|
const { totalPage, page, switchPage } = props
|
||||||
|
if (totalPage <= 0) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
const pagesElement = []
|
||||||
|
|
||||||
|
for (let i = 0; i < totalPage; i++) {
|
||||||
|
const selected = page === i
|
||||||
|
pagesElement.push(getPageElement(i, selected, switchPage))
|
||||||
|
}
|
||||||
|
return <div className='flex space-x-1 w-full justify-center py-1'>
|
||||||
|
{pagesElement.map(p => p)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分页按钮
|
||||||
|
* @param {*} i
|
||||||
|
* @param {*} selected
|
||||||
|
*/
|
||||||
|
function getPageElement(i, selected, switchPage) {
|
||||||
|
return <div onClick={() => switchPage(i)} className={`${selected ? 'font-bold text-white bg-blue-600 rounded' : 'hover:text-blue-600 hover:font-bold'} text-center cursor-pointer w-6 h-6 `}>
|
||||||
|
{i + 1}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const CommonHead = ({ meta, children }) => {
|
|||||||
<>
|
<>
|
||||||
<meta
|
<meta
|
||||||
property="article:published_time"
|
property="article:published_time"
|
||||||
content={meta.publishTime}
|
content={meta.publishDay}
|
||||||
/>
|
/>
|
||||||
<meta property="article:author" content={BLOG.AUTHOR} />
|
<meta property="article:author" content={BLOG.AUTHOR} />
|
||||||
<meta property="article:section" content={category} />
|
<meta property="article:section" content={category} />
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ const DebugPanel = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleUpdateDebugTheme(newTheme) {
|
function handleUpdateDebugTheme(newTheme) {
|
||||||
console.log('切换主题', newTheme)
|
|
||||||
const query = { ...router.query, theme: newTheme }
|
const query = { ...router.query, theme: newTheme }
|
||||||
router.push({ pathname: router.pathname, query })
|
router.push({ pathname: router.pathname, query })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,8 @@ const Gitalk = ({ frontMatter }) => {
|
|||||||
// distractionFreeMode: JSON.parse(BLOG.COMMENT_GITALK_DISTRACTION_FREE_MODE)
|
// distractionFreeMode: JSON.parse(BLOG.COMMENT_GITALK_DISTRACTION_FREE_MODE)
|
||||||
// }} />
|
// }} />
|
||||||
const loadGitalk = async() => {
|
const loadGitalk = async() => {
|
||||||
const css = await loadExternalResource(BLOG.COMMENT_GITALK_CSS_CDN_URL, 'css')
|
await loadExternalResource(BLOG.COMMENT_GITALK_CSS_CDN_URL, 'css')
|
||||||
const js = await loadExternalResource(BLOG.COMMENT_GITALK_JS_CDN_URL, 'js')
|
await loadExternalResource(BLOG.COMMENT_GITALK_JS_CDN_URL, 'js')
|
||||||
|
|
||||||
console.log('gitalk 加载成功', css, js)
|
|
||||||
const Gitalk = window.Gitalk
|
const Gitalk = window.Gitalk
|
||||||
|
|
||||||
const gitalk = new Gitalk({
|
const gitalk = new Gitalk({
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export default function GoogleAdsense() {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const ads = document.getElementsByClassName('adsbygoogle')
|
const ads = document.getElementsByClassName('adsbygoogle')
|
||||||
const adsbygoogle = window.adsbygoogle
|
const adsbygoogle = window.adsbygoogle
|
||||||
console.log('google-ads', adsbygoogle)
|
|
||||||
if (ads.length > 0) {
|
if (ads.length > 0) {
|
||||||
for (let i = 0; i <= ads.length; i++) {
|
for (let i = 0; i <= ads.length; i++) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -9,10 +9,7 @@ export default async function replaceSearchResult({ doms, search, target }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url = await loadExternalResource('https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js', 'js')
|
await loadExternalResource('https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js', 'js')
|
||||||
console.log('markjs 加载成功', url, window.Mark)
|
|
||||||
console.log('------', doms)
|
|
||||||
|
|
||||||
const Mark = window.Mark
|
const Mark = window.Mark
|
||||||
if (doms instanceof HTMLCollection) {
|
if (doms instanceof HTMLCollection) {
|
||||||
for (const container of doms) {
|
for (const container of doms) {
|
||||||
|
|||||||
@@ -140,8 +140,8 @@ const renderMermaid = async() => {
|
|||||||
}
|
}
|
||||||
if (needLoad) {
|
if (needLoad) {
|
||||||
loadExternalResource(BLOG.MERMAID_CDN, 'js').then(url => {
|
loadExternalResource(BLOG.MERMAID_CDN, 'js').then(url => {
|
||||||
// console.log('mermaid加载成功', url, mermaid)
|
|
||||||
const mermaid = window.mermaid
|
const mermaid = window.mermaid
|
||||||
|
console.log('mermaid加载成功', url, mermaid)
|
||||||
mermaid.contentLoaded()
|
mermaid.contentLoaded()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -216,7 +216,6 @@ const fixCodeLineStyle = () => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const preCodes = document.querySelectorAll('pre.notion-code')
|
const preCodes = document.querySelectorAll('pre.notion-code')
|
||||||
for (const preCode of preCodes) {
|
for (const preCode of preCodes) {
|
||||||
// console.log('code', preCode)
|
|
||||||
Prism.plugins.lineNumbers.resize(preCode)
|
Prism.plugins.lineNumbers.resize(preCode)
|
||||||
}
|
}
|
||||||
}, 10)
|
}, 10)
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ const TwikooCommentCounter = (props) => {
|
|||||||
urls: posts?.map(post => post.slug), // 不包含协议、域名、参数的文章路径列表,必传参数
|
urls: posts?.map(post => post.slug), // 不包含协议、域名、参数的文章路径列表,必传参数
|
||||||
includeReply: true // 评论数是否包括回复,默认:false
|
includeReply: true // 评论数是否包括回复,默认:false
|
||||||
}).then(function (res) {
|
}).then(function (res) {
|
||||||
// console.log('查询', res)
|
|
||||||
commentsData = res
|
commentsData = res
|
||||||
updateCommentCount()
|
updateCommentCount()
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { useEffect } from 'react'
|
|||||||
const ValineComponent = ({ path }) => {
|
const ValineComponent = ({ path }) => {
|
||||||
const loadValine = async () => {
|
const loadValine = async () => {
|
||||||
try {
|
try {
|
||||||
const url = await loadExternalResource(BLOG.COMMENT_VALINE_CDN, 'js')
|
await loadExternalResource(BLOG.COMMENT_VALINE_CDN, 'js')
|
||||||
console.log('valine 加载成功', url)
|
// console.log('valine 加载成功', url)
|
||||||
const Valine = window.Valine
|
const Valine = window.Valine
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
const valine = new Valine({
|
const valine = new Valine({
|
||||||
@@ -21,7 +21,6 @@ const ValineComponent = ({ path }) => {
|
|||||||
serverURLs: BLOG.COMMENT_VALINE_SERVER_URLS,
|
serverURLs: BLOG.COMMENT_VALINE_SERVER_URLS,
|
||||||
visitor: true
|
visitor: true
|
||||||
})
|
})
|
||||||
console.log('初始化valine成功')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('twikoo 加载失败', error)
|
console.error('twikoo 加载失败', error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,26 +17,88 @@ const generateAlgoliaSearch = async({ allPages, force = false }) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传数据
|
* 上传数据
|
||||||
|
* 根据上次修改文章日期和上次更新索引数据判断是否需要更新algolia索引
|
||||||
*/
|
*/
|
||||||
const uploadDataToAlgolia = (post) => {
|
const uploadDataToAlgolia = async(post) => {
|
||||||
// Connect and authenticate with your Algolia app
|
// Connect and authenticate with your Algolia app
|
||||||
const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_ADMIN_APP_KEY)
|
const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_ADMIN_APP_KEY)
|
||||||
|
|
||||||
// Create a new index and add a record
|
// Create a new index and add a record
|
||||||
const index = client.initIndex(BLOG.ALGOLIA_INDEX)
|
const index = client.initIndex(BLOG.ALGOLIA_INDEX)
|
||||||
const record = {
|
|
||||||
objectID: post.id,
|
if (!post) {
|
||||||
title: post.title,
|
return
|
||||||
category: post.category,
|
|
||||||
tags: post.tags,
|
|
||||||
pageCover: post.pageCover,
|
|
||||||
slug: post.slug,
|
|
||||||
summary: post.summary,
|
|
||||||
content: getPageContentText(post, post.blockMap)
|
|
||||||
}
|
}
|
||||||
index.saveObject(record).wait().then(r => {
|
|
||||||
console.log('Algolia索引', r, record)
|
// 检查是否有索引
|
||||||
})
|
let existed
|
||||||
|
let needUpdateIndex = false
|
||||||
|
try {
|
||||||
|
existed = await index.getObject(post.id)
|
||||||
|
} catch (error) {
|
||||||
|
// 通常是不存在索引
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existed || !existed?.lastEditedDate || !existed?.lastIndexDate) {
|
||||||
|
needUpdateIndex = true
|
||||||
|
} else {
|
||||||
|
const lastEditedDate = new Date(existed.lastEditedDate)
|
||||||
|
const lastIndexDate = new Date(existed.lastIndexDate)
|
||||||
|
if (lastEditedDate.getTime() > lastIndexDate.getTime()) {
|
||||||
|
needUpdateIndex = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果需要更新搜索
|
||||||
|
if (needUpdateIndex) {
|
||||||
|
const record = {
|
||||||
|
objectID: post.id,
|
||||||
|
title: post.title,
|
||||||
|
category: post.category,
|
||||||
|
tags: post.tags,
|
||||||
|
pageCover: post.pageCover,
|
||||||
|
slug: post.slug,
|
||||||
|
summary: post.summary,
|
||||||
|
lastEditedDate: post.lastEditedDate, // 更新文章时间
|
||||||
|
lastIndexDate: new Date(), // 更新索引时间
|
||||||
|
content: truncate(getPageContentText(post, post.blockMap), 9000) // 索引9000个字节,因为api限制总请求内容上限1万个字节
|
||||||
|
}
|
||||||
|
// console.log('更新Algolia索引', record)
|
||||||
|
index.saveObject(record).wait().then(r => {
|
||||||
|
console.log('Algolia索引更新', r)
|
||||||
|
}).catch(err => {
|
||||||
|
console.log('Algolia异常', err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限制内容字节数
|
||||||
|
* @param {*} str
|
||||||
|
* @param {*} maxBytes
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function truncate(str, maxBytes) {
|
||||||
|
let count = 0
|
||||||
|
let result = ''
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
const code = str.charCodeAt(i)
|
||||||
|
if (code <= 0x7f) {
|
||||||
|
count += 1
|
||||||
|
} else if (code <= 0x7ff) {
|
||||||
|
count += 2
|
||||||
|
} else if (code <= 0xffff) {
|
||||||
|
count += 3
|
||||||
|
} else {
|
||||||
|
count += 4
|
||||||
|
}
|
||||||
|
if (count <= maxBytes) {
|
||||||
|
result += str[i]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export { uploadDataToAlgolia, generateAlgoliaSearch }
|
export { uploadDataToAlgolia, generateAlgoliaSearch }
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ const GlobalContext = createContext()
|
|||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
export function GlobalContextProvider({ children }) {
|
export function GlobalContextProvider(props) {
|
||||||
|
const { children, siteInfo, categoryOptions, tagOptions } = props
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [lang, updateLang] = useState(BLOG.LANG) // 默认语言
|
const [lang, updateLang] = useState(BLOG.LANG) // 默认语言
|
||||||
const [locale, updateLocale] = useState(generateLocaleDict(BLOG.LANG)) // 默认语言
|
const [locale, updateLocale] = useState(generateLocaleDict(BLOG.LANG)) // 默认语言
|
||||||
@@ -24,7 +25,7 @@ export function GlobalContextProvider({ children }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initLocale(lang, locale, updateLang, updateLocale)
|
initLocale(lang, locale, updateLang, updateLocale)
|
||||||
initDarkMode(isDarkMode, updateDarkMode)
|
initDarkMode(updateDarkMode)
|
||||||
initTheme()
|
initTheme()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -75,7 +76,10 @@ export function GlobalContextProvider({ children }) {
|
|||||||
updateDarkMode,
|
updateDarkMode,
|
||||||
theme,
|
theme,
|
||||||
setTheme,
|
setTheme,
|
||||||
switchTheme
|
switchTheme,
|
||||||
|
siteInfo,
|
||||||
|
categoryOptions,
|
||||||
|
tagOptions
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</GlobalContext.Provider>
|
</GlobalContext.Provider>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { isIterable } from '../utils'
|
|||||||
* @returns {Promise<{}|*[]>}
|
* @returns {Promise<{}|*[]>}
|
||||||
*/
|
*/
|
||||||
export function getAllCategories({ allPages, categoryOptions, sliceCount = 0 }) {
|
export function getAllCategories({ allPages, categoryOptions, sliceCount = 0 }) {
|
||||||
const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||||
if (!allPosts || !categoryOptions) {
|
if (!allPosts || !categoryOptions) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,15 @@ export default function getAllPageIds (collectionQuery, collectionId, collection
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
let pageIds = []
|
let pageIds = []
|
||||||
if (collectionQuery && Object.values(collectionQuery).length > 0) {
|
// 优先按照第一个视图排序
|
||||||
|
if (viewIds && viewIds.length > 0) {
|
||||||
|
const ids = collectionView[viewIds[0]].value.page_sort
|
||||||
|
// console.log('PageIds: 从viewId获取', viewIds)
|
||||||
|
for (const id of ids) {
|
||||||
|
pageIds.push(id)
|
||||||
|
}
|
||||||
|
// 否则按照数据库原始排序
|
||||||
|
} else if (collectionQuery && Object.values(collectionQuery).length > 0) {
|
||||||
const pageSet = new Set()
|
const pageSet = new Set()
|
||||||
Object.values(collectionQuery[collectionId]).forEach(view => {
|
Object.values(collectionQuery[collectionId]).forEach(view => {
|
||||||
view?.blockIds?.forEach(id => pageSet.add(id)) // group视图
|
view?.blockIds?.forEach(id => pageSet.add(id)) // group视图
|
||||||
@@ -12,12 +20,6 @@ export default function getAllPageIds (collectionQuery, collectionId, collection
|
|||||||
})
|
})
|
||||||
pageIds = [...pageSet]
|
pageIds = [...pageSet]
|
||||||
// console.log('PageIds: 从collectionQuery获取', collectionQuery, pageIds.length)
|
// console.log('PageIds: 从collectionQuery获取', collectionQuery, pageIds.length)
|
||||||
} else if (viewIds && viewIds.length > 0) {
|
|
||||||
const ids = collectionView[viewIds[0]].value.page_sort
|
|
||||||
// console.log('PageIds: 从viewId获取', viewIds)
|
|
||||||
for (const id of ids) {
|
|
||||||
pageIds.push(id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return pageIds
|
return pageIds
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { isIterable } from '../utils'
|
|||||||
* @returns {Promise<{}|*[]>}
|
* @returns {Promise<{}|*[]>}
|
||||||
*/
|
*/
|
||||||
export function getAllTags({ allPages, sliceCount = 0, tagOptions }) {
|
export function getAllTags({ allPages, sliceCount = 0, tagOptions }) {
|
||||||
const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||||
|
|
||||||
if (!allPosts || !tagOptions) {
|
if (!allPosts || !tagOptions) {
|
||||||
return []
|
return []
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export async function getNotion(pageId) {
|
|||||||
title: postInfo?.properties?.title?.[0],
|
title: postInfo?.properties?.title?.[0],
|
||||||
status: 'Published',
|
status: 'Published',
|
||||||
createdTime: formatDate(new Date(postInfo.created_time).toString(), BLOG.LANG),
|
createdTime: formatDate(new Date(postInfo.created_time).toString(), BLOG.LANG),
|
||||||
lastEditedTime: formatDate(new Date(postInfo?.last_edited_time).toString(), BLOG.LANG),
|
lastEditedDay: formatDate(new Date(postInfo?.last_edited_time).toString(), BLOG.LANG),
|
||||||
fullWidth: false,
|
fullWidth: false,
|
||||||
page_cover: getPageCover(postInfo),
|
page_cover: getPageCover(postInfo),
|
||||||
date: { start_date: formatDate(new Date(postInfo?.last_edited_time).toString(), BLOG.LANG) },
|
date: { start_date: formatDate(new Date(postInfo?.last_edited_time).toString(), BLOG.LANG) },
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ export async function getGlobalData({
|
|||||||
from
|
from
|
||||||
}) {
|
}) {
|
||||||
// 从notion获取
|
// 从notion获取
|
||||||
const db = deepClone(await getNotionPageData({ pageId, from }))
|
const data = await getNotionPageData({ pageId, from })
|
||||||
|
const db = deepClone(data)
|
||||||
// 不返回的敏感数据
|
// 不返回的敏感数据
|
||||||
delete db.block
|
delete db.block
|
||||||
delete db.schema
|
delete db.schema
|
||||||
@@ -48,8 +49,8 @@ function getLatestPosts({ allPages, from, latestPostCount }) {
|
|||||||
const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||||
|
|
||||||
const latestPosts = Object.create(allPosts).sort((a, b) => {
|
const latestPosts = Object.create(allPosts).sort((a, b) => {
|
||||||
const dateA = new Date(a?.lastEditedTime || a?.publishDate)
|
const dateA = new Date(a?.lastEditedDay || a?.publishDate)
|
||||||
const dateB = new Date(b?.lastEditedTime || b?.publishDate)
|
const dateB = new Date(b?.lastEditedDay || b?.publishDate)
|
||||||
return dateB - dateA
|
return dateB - dateA
|
||||||
})
|
})
|
||||||
return latestPosts.slice(0, latestPostCount)
|
return latestPosts.slice(0, latestPostCount)
|
||||||
@@ -173,31 +174,17 @@ function getSiteInfo({ collection, block }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取导航pages
|
* 获取导航用的精减文章列表
|
||||||
* 转为gitbook这类文档主题设计,精减的标题和内容
|
* gitbook主题用到,只保留文章的标题分类标签分类信息,精减掉摘要密码日期等数据
|
||||||
* 导航页面的条件,必须是Posts
|
* 导航页面的条件,必须是Posts
|
||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
*/
|
*/
|
||||||
export function getNavPages({ allPages }) {
|
export function getNavPages({ allPages }) {
|
||||||
const allNavPages = allPages.filter(post => {
|
const allNavPages = allPages?.filter(post => {
|
||||||
return post && post?.slug && (!post?.slug?.startsWith('http')) && post?.type === 'Post' && post?.status === 'Published'
|
return post && post?.slug && (!post?.slug?.startsWith('http')) && post?.type === 'Post' && post?.status === 'Published'
|
||||||
})
|
})
|
||||||
const result = allNavPages.map(item => ({ id: item.id, title: item.title || '', category: item.category || null, tags: item.tags || null, summary: item.summary || null, slug: item.slug }))
|
|
||||||
|
|
||||||
const groupedArray = result.reduce((groups, item) => {
|
return allNavPages.map(item => ({ id: item.id, title: item.title || '', pageCoverThumbnail: item.pageCoverThumbnail || '', category: item.category || null, tags: item.tags || null, summary: item.summary || null, slug: item.slug, lastEditedDate: item.lastEditedDate }))
|
||||||
const categoryName = item?.category ? item?.category : '' // 将category转换为字符串
|
|
||||||
const lastGroup = groups[groups.length - 1] // 获取最后一个分组
|
|
||||||
|
|
||||||
if (!lastGroup || lastGroup?.category !== categoryName) { // 如果当前元素的category与上一个元素不同,则创建新分组
|
|
||||||
groups.push({ category: categoryName, items: [] })
|
|
||||||
}
|
|
||||||
|
|
||||||
groups[groups.length - 1].items.push(item) // 将元素加入对应的分组
|
|
||||||
|
|
||||||
return groups
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return groupedArray
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -217,7 +204,7 @@ const EmptyData = (pageId) => {
|
|||||||
const empty = {
|
const empty = {
|
||||||
notice: null,
|
notice: null,
|
||||||
siteInfo: getSiteInfo({}),
|
siteInfo: getSiteInfo({}),
|
||||||
allPages: [{ id: 1, title: `无法获取Notion数据,请检查Notion_ID: \n 当前 ${pageId}`, summary: '访问文档获取帮助→ https://tangly1024.com/article/vercel-deploy-notion-next', status: 'Published', type: 'Post', slug: '13a171332816461db29d50e9f575b00d', date: { start_date: '2023-04-24', lastEditedTime: '2023-04-24', tagItems: [] } }],
|
allPages: [{ id: 1, title: `无法获取Notion数据,请检查Notion_ID: \n 当前 ${pageId}`, summary: '访问文档获取帮助→ https://tangly1024.com/article/vercel-deploy-notion-next', status: 'Published', type: 'Post', slug: '13a171332816461db29d50e9f575b00d', date: { start_date: '2023-04-24', lastEditedDay: '2023-04-24', tagItems: [] } }],
|
||||||
allNavPages: [],
|
allNavPages: [],
|
||||||
collection: [],
|
collection: [],
|
||||||
collectionQuery: {},
|
collectionQuery: {},
|
||||||
|
|||||||
@@ -78,8 +78,9 @@ export default async function getPageProperties(id, block, schema, authToken, ta
|
|||||||
mapProperties(properties)
|
mapProperties(properties)
|
||||||
|
|
||||||
properties.publishDate = new Date(properties?.date?.start_date || value.created_time).getTime()
|
properties.publishDate = new Date(properties?.date?.start_date || value.created_time).getTime()
|
||||||
properties.publishTime = formatDate(properties.publishDate, BLOG.LANG)
|
properties.publishDay = formatDate(properties.publishDate, BLOG.LANG)
|
||||||
properties.lastEditedTime = formatDate(new Date(value?.last_edited_time), BLOG.LANG)
|
properties.lastEditedDate = new Date(value?.last_edited_time)
|
||||||
|
properties.lastEditedDay = formatDate(new Date(value?.last_edited_time), BLOG.LANG)
|
||||||
properties.fullWidth = value.format?.page_full_width ?? false
|
properties.fullWidth = value.format?.page_full_width ?? false
|
||||||
properties.pageIcon = mapImgUrl(block[id].value?.format?.page_icon, block[id].value) ?? ''
|
properties.pageIcon = mapImgUrl(block[id].value?.format?.page_icon, block[id].value) ?? ''
|
||||||
properties.pageCover = mapImgUrl(block[id].value?.format?.page_cover, block[id].value) ?? ''
|
properties.pageCover = mapImgUrl(block[id].value?.format?.page_cover, block[id].value) ?? ''
|
||||||
@@ -141,14 +142,14 @@ function generateCustomizeUrl(postProperties) {
|
|||||||
let fullPrefix = ''
|
let fullPrefix = ''
|
||||||
const allSlugPatterns = BLOG.POST_URL_PREFIX.split('/')
|
const allSlugPatterns = BLOG.POST_URL_PREFIX.split('/')
|
||||||
allSlugPatterns.forEach((pattern, idx) => {
|
allSlugPatterns.forEach((pattern, idx) => {
|
||||||
if (pattern === '%year%' && postProperties?.publishTime) {
|
if (pattern === '%year%' && postProperties?.publishDay) {
|
||||||
const formatPostCreatedDate = new Date(postProperties?.publishTime)
|
const formatPostCreatedDate = new Date(postProperties?.publishDay)
|
||||||
fullPrefix += formatPostCreatedDate.getUTCFullYear()
|
fullPrefix += formatPostCreatedDate.getUTCFullYear()
|
||||||
} else if (pattern === '%month%' && postProperties?.publishTime) {
|
} else if (pattern === '%month%' && postProperties?.publishDay) {
|
||||||
const formatPostCreatedDate = new Date(postProperties?.publishTime)
|
const formatPostCreatedDate = new Date(postProperties?.publishDay)
|
||||||
fullPrefix += String(formatPostCreatedDate.getUTCMonth() + 1).padStart(2, 0)
|
fullPrefix += String(formatPostCreatedDate.getUTCMonth() + 1).padStart(2, 0)
|
||||||
} else if (pattern === '%day%' && postProperties?.publishTime) {
|
} else if (pattern === '%day%' && postProperties?.publishDay) {
|
||||||
const formatPostCreatedDate = new Date(postProperties?.publishTime)
|
const formatPostCreatedDate = new Date(postProperties?.publishDay)
|
||||||
fullPrefix += String(formatPostCreatedDate.getUTCDate()).padStart(2, 0)
|
fullPrefix += String(formatPostCreatedDate.getUTCDate()).padStart(2, 0)
|
||||||
} else if (pattern === '%slug%') {
|
} else if (pattern === '%slug%') {
|
||||||
fullPrefix += (postProperties.slug ?? postProperties.id)
|
fullPrefix += (postProperties.slug ?? postProperties.id)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export async function generateRss(posts) {
|
|||||||
link: `${BLOG.LINK}/${post.slug}`,
|
link: `${BLOG.LINK}/${post.slug}`,
|
||||||
description: post.summary,
|
description: post.summary,
|
||||||
content: await createFeedContent(post),
|
content: await createFeedContent(post),
|
||||||
date: new Date(post?.publishTime)
|
date: new Date(post?.publishDay)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export async function generateSitemapXml({ allPages }) {
|
|||||||
const slugWithoutLeadingSlash = post?.slug?.startsWith('/') ? post?.slug?.slice(1) : post.slug
|
const slugWithoutLeadingSlash = post?.slug?.startsWith('/') ? post?.slug?.slice(1) : post.slug
|
||||||
urls.push({
|
urls.push({
|
||||||
loc: `${BLOG.LINK}/${slugWithoutLeadingSlash}`,
|
loc: `${BLOG.LINK}/${slugWithoutLeadingSlash}`,
|
||||||
lastmod: new Date(post?.publishTime).toISOString().split('T')[0],
|
lastmod: new Date(post?.publishDay).toISOString().split('T')[0],
|
||||||
changefreq: 'daily'
|
changefreq: 'daily'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -117,7 +117,13 @@ export function deepClone(obj) {
|
|||||||
if (obj && typeof obj === 'object') {
|
if (obj && typeof obj === 'object') {
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||||
newObj[key] = (obj && typeof obj[key] === 'object') ? deepClone(obj[key]) : obj[key]
|
if (obj[key] instanceof Date) { // 判断属性值是否为 Date 对象
|
||||||
|
newObj[key] = new Date(obj[key].getTime()).toISOString() // 直接拷贝引用
|
||||||
|
} else if (obj[key] && typeof obj[key] === 'object') {
|
||||||
|
newObj[key] = deepClone(obj[key])
|
||||||
|
} else {
|
||||||
|
newObj[key] = obj[key]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "notion-next",
|
"name": "notion-next",
|
||||||
"version": "4.0.6",
|
"version": "4.0.8",
|
||||||
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { getGlobalData } from '@/lib/notion/getNotionData'
|
|||||||
import { idToUuid } from 'notion-utils'
|
import { idToUuid } from 'notion-utils'
|
||||||
import { getNotion } from '@/lib/notion/getNotion'
|
import { getNotion } from '@/lib/notion/getNotion'
|
||||||
import Slug, { getRecommendPost } from '.'
|
import Slug, { getRecommendPost } from '.'
|
||||||
|
import { uploadDataToAlgolia } from '@/lib/algolia'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据notion的slug访问页面
|
* 根据notion的slug访问页面
|
||||||
@@ -25,7 +26,7 @@ export async function getStaticPaths() {
|
|||||||
const from = 'slug-paths'
|
const from = 'slug-paths'
|
||||||
const { allPages } = await getGlobalData({ from })
|
const { allPages } = await getGlobalData({ from })
|
||||||
return {
|
return {
|
||||||
paths: allPages?.filter(row => row.slug.indexOf('/') > 0).map(row => ({ params: { prefix: row.slug.split('/')[0], slug: row.slug.split('/')[1] } })),
|
paths: allPages?.filter(row => row.slug.indexOf('/') > 0 && row.type.indexOf('Menu') < 0).map(row => ({ params: { prefix: row.slug.split('/')[0], slug: row.slug.split('/')[1] } })),
|
||||||
fallback: true
|
fallback: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,9 +64,13 @@ export async function getStaticProps({ params: { prefix, slug } }) {
|
|||||||
if (!props?.posts?.blockMap) {
|
if (!props?.posts?.blockMap) {
|
||||||
props.post.blockMap = await getPostBlocks(props.post.id, from)
|
props.post.blockMap = await getPostBlocks(props.post.id, from)
|
||||||
}
|
}
|
||||||
|
// 生成全文索引 && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA)
|
||||||
|
if (BLOG.ALGOLIA_APP_ID) {
|
||||||
|
uploadDataToAlgolia(props?.post)
|
||||||
|
}
|
||||||
|
|
||||||
// 推荐关联文章处理
|
// 推荐关联文章处理
|
||||||
const allPosts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
const allPosts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||||
if (allPosts && allPosts.length > 0) {
|
if (allPosts && allPosts.length > 0) {
|
||||||
const index = allPosts.indexOf(props.post)
|
const index = allPosts.indexOf(props.post)
|
||||||
props.prev = allPosts.slice(index - 1, index)[0] ?? allPosts.slice(-1)[0]
|
props.prev = allPosts.slice(index - 1, index)[0] ?? allPosts.slice(-1)[0]
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export async function getStaticPaths() {
|
|||||||
const from = 'slug-paths'
|
const from = 'slug-paths'
|
||||||
const { allPages } = await getGlobalData({ from })
|
const { allPages } = await getGlobalData({ from })
|
||||||
return {
|
return {
|
||||||
paths: allPages?.filter(row => row.slug.indexOf('/') < 0).map(row => ({ params: { prefix: row.slug } })),
|
paths: allPages?.filter(row => row.slug.indexOf('/') < 0 && row.type.indexOf('Menu') < 0).map(row => ({ params: { prefix: row.slug } })),
|
||||||
fallback: true
|
fallback: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,12 +129,13 @@ export async function getStaticProps({ params: { prefix } }) {
|
|||||||
props.post.blockMap = await getPostBlocks(props.post.id, from)
|
props.post.blockMap = await getPostBlocks(props.post.id, from)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BLOG.ALGOLIA_APP_ID && BLOG.ALGOLIA_APP_KEY) {
|
// 生成全文索引 && process.env.npm_lifecycle_event === 'build' && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA)
|
||||||
|
if (BLOG.ALGOLIA_APP_ID) {
|
||||||
uploadDataToAlgolia(props?.post)
|
uploadDataToAlgolia(props?.post)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 推荐关联文章处理
|
// 推荐关联文章处理
|
||||||
const allPosts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
const allPosts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||||
if (allPosts && allPosts.length > 0) {
|
if (allPosts && allPosts.length > 0) {
|
||||||
const index = allPosts.indexOf(props.post)
|
const index = allPosts.indexOf(props.post)
|
||||||
props.prev = allPosts.slice(index - 1, index)[0] ?? allPosts.slice(-1)[0]
|
props.prev = allPosts.slice(index - 1, index)[0] ?? allPosts.slice(-1)[0]
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import dynamic from 'next/dynamic'
|
|||||||
|
|
||||||
// 自定义样式css和js引入
|
// 自定义样式css和js引入
|
||||||
import ExternalScript from '@/components/ExternalScript'
|
import ExternalScript from '@/components/ExternalScript'
|
||||||
|
|
||||||
// 各种扩展插件 动画等
|
// 各种扩展插件 动画等
|
||||||
const ExternalPlugins = dynamic(() => import('@/components/ExternalPlugins'))
|
const ExternalPlugins = dynamic(() => import('@/components/ExternalPlugins'))
|
||||||
|
|
||||||
@@ -27,7 +26,7 @@ const MyApp = ({ Component, pageProps }) => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GlobalContextProvider>
|
<GlobalContextProvider {...pageProps}>
|
||||||
<ExternalScript />
|
<ExternalScript />
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
<ExternalPlugins {...pageProps} />
|
<ExternalPlugins {...pageProps} />
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const ArchiveIndex = props => {
|
|||||||
export async function getStaticProps() {
|
export async function getStaticProps() {
|
||||||
const props = await getGlobalData({ from: 'archive-index' })
|
const props = await getGlobalData({ from: 'archive-index' })
|
||||||
// 处理分页
|
// 处理分页
|
||||||
props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
props.posts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||||
delete props.allPages
|
delete props.allPages
|
||||||
|
|
||||||
const postsSortByDate = Object.create(props.posts)
|
const postsSortByDate = Object.create(props.posts)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export async function getStaticProps({ params: { category } }) {
|
|||||||
let props = await getGlobalData({ from })
|
let props = await getGlobalData({ from })
|
||||||
|
|
||||||
// 过滤状态
|
// 过滤状态
|
||||||
props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
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.posts = props.posts.filter(post => post && post.category && post.category.includes(category))
|
||||||
// 处理文章页数
|
// 处理文章页数
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export async function getStaticProps({ params: { category, page } }) {
|
|||||||
let props = await getGlobalData({ from })
|
let props = await getGlobalData({ from })
|
||||||
|
|
||||||
// 过滤状态类型
|
// 过滤状态类型
|
||||||
props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.category && post.category.includes(category))
|
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.postCount = props.posts.length
|
||||||
// 处理分页
|
// 处理分页
|
||||||
@@ -61,7 +61,7 @@ export async function getStaticPaths() {
|
|||||||
|
|
||||||
categoryOptions?.forEach(category => {
|
categoryOptions?.forEach(category => {
|
||||||
// 过滤状态类型
|
// 过滤状态类型
|
||||||
const categoryPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.category && post.category.includes(category.name))
|
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 postCount = categoryPosts.length
|
||||||
const totalPages = Math.ceil(postCount / BLOG.POSTS_PER_PAGE)
|
const totalPages = Math.ceil(postCount / BLOG.POSTS_PER_PAGE)
|
||||||
|
|||||||
@@ -3,17 +3,14 @@ import { getPostBlocks } from '@/lib/notion'
|
|||||||
import { getGlobalData } from '@/lib/notion/getNotionData'
|
import { getGlobalData } from '@/lib/notion/getNotionData'
|
||||||
import { generateRss } from '@/lib/rss'
|
import { generateRss } from '@/lib/rss'
|
||||||
import { generateRobotsTxt } from '@/lib/robots.txt'
|
import { generateRobotsTxt } from '@/lib/robots.txt'
|
||||||
|
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { getLayoutByTheme } from '@/themes/theme'
|
import { getLayoutByTheme } from '@/themes/theme'
|
||||||
import { generateAlgoliaSearch } from '@/lib/algolia'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 首页布局
|
* 首页布局
|
||||||
* @param {*} props
|
* @param {*} props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Index = props => {
|
const Index = props => {
|
||||||
// 根据页面路径加载不同Layout文件
|
// 根据页面路径加载不同Layout文件
|
||||||
const Layout = getLayoutByTheme(useRouter())
|
const Layout = getLayoutByTheme(useRouter())
|
||||||
@@ -29,7 +26,7 @@ export async function getStaticProps() {
|
|||||||
const props = await getGlobalData({ from })
|
const props = await getGlobalData({ from })
|
||||||
|
|
||||||
const { siteInfo } = props
|
const { siteInfo } = props
|
||||||
props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
props.posts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: `${siteInfo?.title} | ${siteInfo?.description}`,
|
title: `${siteInfo?.title} | ${siteInfo?.description}`,
|
||||||
@@ -64,9 +61,6 @@ export async function getStaticProps() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 生成全文索引 - 仅在 yarn build 时执行 && process.env.npm_lifecycle_event === 'build'
|
// 生成全文索引 - 仅在 yarn build 时执行 && process.env.npm_lifecycle_event === 'build'
|
||||||
if (BLOG.ALGOLIA_APP_ID && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA)) {
|
|
||||||
generateAlgoliaSearch({ allPages: props.allPages })
|
|
||||||
}
|
|
||||||
|
|
||||||
delete props.allPages
|
delete props.allPages
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export async function getStaticProps({ params: { page } }) {
|
|||||||
const from = `page-${page}`
|
const from = `page-${page}`
|
||||||
const props = await getGlobalData({ from })
|
const props = await getGlobalData({ from })
|
||||||
const { allPages } = props
|
const { allPages } = props
|
||||||
const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
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
|
props.page = page
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export async function getStaticProps({ params: { keyword } }) {
|
|||||||
pageType: ['Post']
|
pageType: ['Post']
|
||||||
})
|
})
|
||||||
const { allPages } = props
|
const { allPages } = props
|
||||||
const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||||
props.posts = await filterByMemCache(allPosts, keyword)
|
props.posts = await filterByMemCache(allPosts, keyword)
|
||||||
props.postCount = props.posts.length
|
props.postCount = props.posts.length
|
||||||
// 处理分页
|
// 处理分页
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export async function getStaticProps({ params: { keyword, page } }) {
|
|||||||
pageType: ['Post']
|
pageType: ['Post']
|
||||||
})
|
})
|
||||||
const { allPages } = props
|
const { allPages } = props
|
||||||
const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||||
props.posts = await filterByMemCache(allPosts, keyword)
|
props.posts = await filterByMemCache(allPosts, keyword)
|
||||||
props.postCount = props.posts.length
|
props.postCount = props.posts.length
|
||||||
// 处理分页
|
// 处理分页
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export async function getStaticProps() {
|
|||||||
pageType: ['Post']
|
pageType: ['Post']
|
||||||
})
|
})
|
||||||
const { allPages } = props
|
const { allPages } = props
|
||||||
props.posts = allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
props.posts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: parseInt(BLOG.NEXT_REVALIDATE_SECOND)
|
revalidate: parseInt(BLOG.NEXT_REVALIDATE_SECOND)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export const getServerSideProps = async (ctx) => {
|
|||||||
const slugWithoutLeadingSlash = post?.slug.startsWith('/') ? post?.slug?.slice(1) : post.slug
|
const slugWithoutLeadingSlash = post?.slug.startsWith('/') ? post?.slug?.slice(1) : post.slug
|
||||||
return {
|
return {
|
||||||
loc: `${BLOG.LINK}/${slugWithoutLeadingSlash}`,
|
loc: `${BLOG.LINK}/${slugWithoutLeadingSlash}`,
|
||||||
lastmod: new Date(post?.publishTime).toISOString().split('T')[0],
|
lastmod: new Date(post?.publishDay).toISOString().split('T')[0],
|
||||||
changefreq: 'daily',
|
changefreq: 'daily',
|
||||||
priority: '0.7'
|
priority: '0.7'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export async function getStaticProps({ params: { tag } }) {
|
|||||||
const props = await getGlobalData({ from })
|
const props = await getGlobalData({ from })
|
||||||
|
|
||||||
// 过滤状态
|
// 过滤状态
|
||||||
props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post?.tags && post?.tags.includes(tag))
|
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.postCount = props.posts.length
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export async function getStaticProps({ params: { tag, page } }) {
|
|||||||
const from = 'tag-page-props'
|
const from = 'tag-page-props'
|
||||||
const props = await getGlobalData({ from })
|
const props = await getGlobalData({ from })
|
||||||
// 过滤状态、标签
|
// 过滤状态、标签
|
||||||
props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post?.tags && post?.tags.includes(tag))
|
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.postCount = props.posts.length
|
||||||
// 处理分页
|
// 处理分页
|
||||||
@@ -48,7 +48,7 @@ export async function getStaticPaths() {
|
|||||||
const paths = []
|
const paths = []
|
||||||
tagOptions?.forEach(tag => {
|
tagOptions?.forEach(tag => {
|
||||||
// 过滤状态类型
|
// 过滤状态类型
|
||||||
const tagPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post?.tags && post?.tags.includes(tag.name))
|
const tagPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post?.tags && post?.tags.includes(tag.name))
|
||||||
// 处理文章页数
|
// 处理文章页数
|
||||||
const postCount = tagPosts.length
|
const postCount = tagPosts.length
|
||||||
const totalPages = Math.ceil(postCount / BLOG.POSTS_PER_PAGE)
|
const totalPages = Math.ceil(postCount / BLOG.POSTS_PER_PAGE)
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ export const ArticleInfo = (props) => {
|
|||||||
passHref
|
passHref
|
||||||
className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed">
|
className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed">
|
||||||
|
|
||||||
{post?.publishTime}
|
{post?.publishDay}
|
||||||
|
|
||||||
</Link>
|
</Link>
|
||||||
<span className='mr-2'>|</span>
|
<span className='mr-2'>|</span>
|
||||||
<span className='mx-2 text-gray-400 dark:text-gray-500'>
|
<span className='mx-2 text-gray-400 dark:text-gray-500'>
|
||||||
{locale.COMMON.LAST_EDITED_TIME}: {post?.lastEditedTime}
|
{locale.COMMON.LAST_EDITED_TIME}: {post?.lastEditedDay}
|
||||||
</span>
|
</span>
|
||||||
<span className='mr-2'>|</span>
|
<span className='mr-2'>|</span>
|
||||||
<span className="hidden busuanzi_container_page_pv font-light mr-2">
|
<span className="hidden busuanzi_container_page_pv font-light mr-2">
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ export default function BlogListGroupByDate({ archiveTitle, archivePosts }) {
|
|||||||
key={post.id}
|
key={post.id}
|
||||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
||||||
>
|
>
|
||||||
<div id={post?.publishTime}>
|
<div id={post?.publishDay}>
|
||||||
<span className="text-gray-400">
|
<span className="text-gray-400">
|
||||||
{post?.publishTime}
|
{post?.publishDay}
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
|
|
||||||
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import CONFIG from './config'
|
import CONFIG from './config'
|
||||||
import CommonHead from '@/components/CommonHead'
|
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { Header } from './components/Header'
|
import { Header } from './components/Header'
|
||||||
import { Nav } from './components/Nav'
|
import { Nav } from './components/Nav'
|
||||||
@@ -27,6 +26,7 @@ import TagItem from './components/TagItem'
|
|||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { Transition } from '@headlessui/react'
|
import { Transition } from '@headlessui/react'
|
||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
|
import CommonHead from '@/components/CommonHead'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基础布局框架
|
* 基础布局框架
|
||||||
@@ -36,7 +36,7 @@ import { Style } from './style'
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const LayoutBase = props => {
|
const LayoutBase = props => {
|
||||||
const { children, meta, slotTop } = props
|
const { children, slotTop, meta } = props
|
||||||
const { onLoading } = useGlobal()
|
const { onLoading } = useGlobal()
|
||||||
|
|
||||||
// 增加一个状态以触发 Transition 组件的动画
|
// 增加一个状态以触发 Transition 组件的动画
|
||||||
@@ -49,8 +49,10 @@ const LayoutBase = props => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div id='theme-example' className='dark:text-gray-300 bg-white dark:bg-black'>
|
<div id='theme-example' className='dark:text-gray-300 bg-white dark:bg-black'>
|
||||||
{/* 网页SEO信息 */}
|
|
||||||
<CommonHead meta={meta} />
|
{/* SEO信息 */}
|
||||||
|
<CommonHead meta={meta}/>
|
||||||
|
|
||||||
<Style/>
|
<Style/>
|
||||||
|
|
||||||
{/* 页头 */}
|
{/* 页头 */}
|
||||||
|
|||||||
@@ -59,12 +59,12 @@ export default function ArticleDetail(props) {
|
|||||||
passHref
|
passHref
|
||||||
className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed">
|
className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed">
|
||||||
|
|
||||||
{post?.publishTime}
|
{post?.publishDay}
|
||||||
|
|
||||||
</Link>
|
</Link>
|
||||||
<span className='mr-2'>|</span>
|
<span className='mr-2'>|</span>
|
||||||
<span className='mx-2 text-gray-400 dark:text-gray-500'>
|
<span className='mx-2 text-gray-400 dark:text-gray-500'>
|
||||||
{locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedTime}
|
{locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedDay}
|
||||||
</span>
|
</span>
|
||||||
</>)}
|
</>)}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const BlogArchiveItem = ({ posts = [], archiveTitle }) => {
|
|||||||
key={post.id}
|
key={post.id}
|
||||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
||||||
>
|
>
|
||||||
<div id={post?.publishTime}>
|
<div id={post?.publishDay}>
|
||||||
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import CONFIG from './config'
|
import CONFIG from './config'
|
||||||
import CommonHead from '@/components/CommonHead'
|
|
||||||
import TopNav from './components/TopNav'
|
import TopNav from './components/TopNav'
|
||||||
import AsideLeft from './components/AsideLeft'
|
import AsideLeft from './components/AsideLeft'
|
||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
@@ -21,6 +20,7 @@ import dynamic from 'next/dynamic'
|
|||||||
import { AdSlot } from '@/components/GoogleAdsense'
|
import { AdSlot } from '@/components/GoogleAdsense'
|
||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
import replaceSearchResult from '@/components/Mark'
|
import replaceSearchResult from '@/components/Mark'
|
||||||
|
import CommonHead from '@/components/CommonHead'
|
||||||
|
|
||||||
const Live2D = dynamic(() => import('@/components/Live2D'))
|
const Live2D = dynamic(() => import('@/components/Live2D'))
|
||||||
|
|
||||||
@@ -66,7 +66,8 @@ const LayoutBase = (props) => {
|
|||||||
<ThemeGlobalFukasawa.Provider value={{ isCollapsed, setIsCollapse }}>
|
<ThemeGlobalFukasawa.Provider value={{ isCollapsed, setIsCollapse }}>
|
||||||
|
|
||||||
<div id='theme-fukasawa'>
|
<div id='theme-fukasawa'>
|
||||||
<CommonHead meta={meta} />
|
{/* SEO信息 */}
|
||||||
|
<CommonHead meta={meta}/>
|
||||||
<Style/>
|
<Style/>
|
||||||
|
|
||||||
<TopNav {...props} />
|
<TopNav {...props} />
|
||||||
|
|||||||
@@ -10,12 +10,24 @@ import NavPostItem from './NavPostItem'
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const NavPostList = (props) => {
|
const NavPostList = (props) => {
|
||||||
const { filteredPostGroups } = props
|
const { filteredNavPages } = props
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
let selectedSth = false
|
let selectedSth = false
|
||||||
|
const groupedArray = filteredNavPages?.reduce((groups, item) => {
|
||||||
|
const categoryName = item?.category ? item?.category : '' // 将category转换为字符串
|
||||||
|
const lastGroup = groups[groups.length - 1] // 获取最后一个分组
|
||||||
|
|
||||||
|
if (!lastGroup || lastGroup?.category !== categoryName) { // 如果当前元素的category与上一个元素不同,则创建新分组
|
||||||
|
groups.push({ category: categoryName, items: [] })
|
||||||
|
}
|
||||||
|
|
||||||
|
groups[groups.length - 1].items.push(item) // 将元素加入对应的分组
|
||||||
|
|
||||||
|
return groups
|
||||||
|
}, [])
|
||||||
|
|
||||||
// 处理是否选中
|
// 处理是否选中
|
||||||
filteredPostGroups?.map((group) => {
|
groupedArray?.map((group) => {
|
||||||
let groupSelected = false
|
let groupSelected = false
|
||||||
for (const post of group?.items) {
|
for (const post of group?.items) {
|
||||||
if (router.asPath.split('?')[0] === '/' + post.slug) {
|
if (router.asPath.split('?')[0] === '/' + post.slug) {
|
||||||
@@ -28,16 +40,16 @@ const NavPostList = (props) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 如果都没有选中默认打开第一个
|
// 如果都没有选中默认打开第一个
|
||||||
if (!selectedSth && filteredPostGroups && filteredPostGroups?.length > 0) {
|
if (!selectedSth && groupedArray && groupedArray?.length > 0) {
|
||||||
filteredPostGroups[0].selected = true
|
groupedArray[0].selected = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!filteredPostGroups || filteredPostGroups.length === 0) {
|
if (!groupedArray || groupedArray.length === 0) {
|
||||||
return <NavPostListEmpty />
|
return <NavPostListEmpty />
|
||||||
} else {
|
} else {
|
||||||
return <div id='posts-wrapper' className='w-full flex-grow'>
|
return <div id='posts-wrapper' className='w-full flex-grow'>
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
{filteredPostGroups?.map((group, index) => <NavPostItem key={index} group={group} onHeightChange={props.onHeightChange}/>)}
|
{groupedArray?.map((group, index) => <NavPostItem key={index} group={group} onHeightChange={props.onHeightChange}/>)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import NavPostList from './NavPostList'
|
|||||||
*/
|
*/
|
||||||
const PageNavDrawer = (props) => {
|
const PageNavDrawer = (props) => {
|
||||||
const { pageNavVisible, changePageNavVisible } = useGitBookGlobal()
|
const { pageNavVisible, changePageNavVisible } = useGitBookGlobal()
|
||||||
const { filteredPostGroups } = props
|
const { filteredNavPages } = props
|
||||||
|
|
||||||
const switchVisible = () => {
|
const switchVisible = () => {
|
||||||
changePageNavVisible(!pageNavVisible)
|
changePageNavVisible(!pageNavVisible)
|
||||||
}
|
}
|
||||||
@@ -23,7 +24,7 @@ const PageNavDrawer = (props) => {
|
|||||||
' overflow-y-hidden shadow-card w-72 duration-200 fixed left-1 top-16 rounded py-2 bg-white dark:bg-gray-600'}>
|
' overflow-y-hidden shadow-card w-72 duration-200 fixed left-1 top-16 rounded py-2 bg-white dark:bg-gray-600'}>
|
||||||
<div className='dark:text-gray-400 text-gray-600 h-96 overflow-y-scroll p-3'>
|
<div className='dark:text-gray-400 text-gray-600 h-96 overflow-y-scroll p-3'>
|
||||||
{/* 所有文章列表 */}
|
{/* 所有文章列表 */}
|
||||||
<NavPostList filteredPostGroups={filteredPostGroups} />
|
<NavPostList filteredNavPages={filteredNavPages} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ let lock = false
|
|||||||
|
|
||||||
const SearchInput = ({ currentSearch, cRef, className }) => {
|
const SearchInput = ({ currentSearch, cRef, className }) => {
|
||||||
const searchInputRef = useRef()
|
const searchInputRef = useRef()
|
||||||
const { setFilterPosts, allNavPages } = useGitBookGlobal()
|
const { setFilteredNavPages, allNavPages } = useGitBookGlobal()
|
||||||
|
|
||||||
useImperativeHandle(cRef, () => {
|
useImperativeHandle(cRef, () => {
|
||||||
return {
|
return {
|
||||||
@@ -17,31 +17,44 @@ const SearchInput = ({ currentSearch, cRef, className }) => {
|
|||||||
|
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
let keyword = searchInputRef.current.value
|
let keyword = searchInputRef.current.value
|
||||||
const filterPosts = []
|
|
||||||
if (keyword) {
|
if (keyword) {
|
||||||
keyword = keyword.trim()
|
keyword = keyword.trim()
|
||||||
} else {
|
} else {
|
||||||
setFilterPosts(allNavPages)
|
setFilteredNavPages(allNavPages)
|
||||||
}
|
}
|
||||||
const filterAllNavPages = deepClone(allNavPages)
|
const filterAllNavPages = deepClone(allNavPages)
|
||||||
for (const filterGroup of filterAllNavPages) {
|
// for (const filterGroup of filterAllNavPages) {
|
||||||
for (let i = filterGroup.items.length - 1; i >= 0; i--) {
|
// for (let i = filterGroup.items.length - 1; i >= 0; i--) {
|
||||||
const post = filterGroup.items[i]
|
// const post = filterGroup.items[i]
|
||||||
const articleInfo = post.title + ''
|
// const articleInfo = post.title + ''
|
||||||
const hit = articleInfo.toLowerCase().indexOf(keyword.toLowerCase()) > -1
|
// const hit = articleInfo.toLowerCase().indexOf(keyword.toLowerCase()) > -1
|
||||||
if (!hit) {
|
// if (!hit) {
|
||||||
// 删除
|
// // 删除
|
||||||
filterGroup.items.splice(i, 1)
|
// filterGroup.items.splice(i, 1)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (filterGroup.items && filterGroup.items.length > 0) {
|
// if (filterGroup.items && filterGroup.items.length > 0) {
|
||||||
filterPosts.push(filterGroup)
|
// filterPosts.push(filterGroup)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
for (let i = filterAllNavPages.length - 1; i >= 0; i--) {
|
||||||
|
const post = filterAllNavPages[i]
|
||||||
|
const articleInfo = post.title + ''
|
||||||
|
const hit = articleInfo.toLowerCase().indexOf(keyword.toLowerCase()) > -1
|
||||||
|
if (!hit) {
|
||||||
|
// 删除
|
||||||
|
filterAllNavPages.splice(i, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新完
|
// 更新完
|
||||||
setFilterPosts(filterPosts)
|
setFilteredNavPages(filterAllNavPages)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回车键
|
||||||
|
* @param {*} e
|
||||||
|
*/
|
||||||
const handleKeyUp = (e) => {
|
const handleKeyUp = (e) => {
|
||||||
if (e.keyCode === 13) { // 回车
|
if (e.keyCode === 13) { // 回车
|
||||||
handleSearch(searchInputRef.current.value)
|
handleSearch(searchInputRef.current.value)
|
||||||
@@ -49,6 +62,10 @@ const SearchInput = ({ currentSearch, cRef, className }) => {
|
|||||||
cleanSearch()
|
cleanSearch()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理搜索
|
||||||
|
*/
|
||||||
const cleanSearch = () => {
|
const cleanSearch = () => {
|
||||||
searchInputRef.current.value = ''
|
searchInputRef.current.value = ''
|
||||||
handleSearch()
|
handleSearch()
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import CONFIG from './config'
|
|||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useEffect, useState, createContext, useContext } from 'react'
|
import { useEffect, useState, createContext, useContext } from 'react'
|
||||||
import { isBrowser } from '@/lib/utils'
|
import { isBrowser } from '@/lib/utils'
|
||||||
import CommonHead from '@/components/CommonHead'
|
|
||||||
import Footer from './components/Footer'
|
import Footer from './components/Footer'
|
||||||
import InfoCard from './components/InfoCard'
|
import InfoCard from './components/InfoCard'
|
||||||
import RevolverMaps from './components/RevolverMaps'
|
import RevolverMaps from './components/RevolverMaps'
|
||||||
@@ -31,6 +30,7 @@ import NotionPage from '@/components/NotionPage'
|
|||||||
import { ArticleLock } from './components/ArticleLock'
|
import { ArticleLock } from './components/ArticleLock'
|
||||||
import { Transition } from '@headlessui/react'
|
import { Transition } from '@headlessui/react'
|
||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
|
import CommonHead from '@/components/CommonHead'
|
||||||
|
|
||||||
// 主题全局变量
|
// 主题全局变量
|
||||||
const ThemeGlobalGitbook = createContext()
|
const ThemeGlobalGitbook = createContext()
|
||||||
@@ -43,22 +43,23 @@ export const useGitBookGlobal = () => useContext(ThemeGlobalGitbook)
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const LayoutBase = (props) => {
|
const LayoutBase = (props) => {
|
||||||
const { children, meta, post, allNavPages, slotLeft, slotRight, slotTop } = props
|
const { children, post, allNavPages, slotLeft, slotRight, slotTop, meta } = props
|
||||||
const { onLoading } = useGlobal()
|
const { onLoading } = useGlobal()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [tocVisible, changeTocVisible] = useState(false)
|
const [tocVisible, changeTocVisible] = useState(false)
|
||||||
const [pageNavVisible, changePageNavVisible] = useState(false)
|
const [pageNavVisible, changePageNavVisible] = useState(false)
|
||||||
const [filteredPostGroups, setFilteredPostGroups] = useState(allNavPages)
|
const [filteredNavPages, setFilteredNavPages] = useState(allNavPages)
|
||||||
|
|
||||||
const showTocButton = post?.toc?.length > 1
|
const showTocButton = post?.toc?.length > 1
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setFilteredPostGroups(allNavPages)
|
console.log('更新导航', allNavPages)
|
||||||
|
setFilteredNavPages(allNavPages)
|
||||||
}, [post])
|
}, [post])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeGlobalGitbook.Provider value={{ tocVisible, changeTocVisible, filteredPostGroups, setFilteredPostGroups, allNavPages, pageNavVisible, changePageNavVisible }}>
|
<ThemeGlobalGitbook.Provider value={{ tocVisible, changeTocVisible, filteredNavPages, setFilteredNavPages, allNavPages, pageNavVisible, changePageNavVisible }}>
|
||||||
<CommonHead meta={meta} />
|
<CommonHead meta={meta}/>
|
||||||
<Style/>
|
<Style/>
|
||||||
|
|
||||||
<div id='theme-gitbook' className='bg-white dark:bg-hexo-black-gray w-full h-full min-h-screen justify-center dark:text-gray-300'>
|
<div id='theme-gitbook' className='bg-white dark:bg-hexo-black-gray w-full h-full min-h-screen justify-center dark:text-gray-300'>
|
||||||
@@ -72,8 +73,10 @@ const LayoutBase = (props) => {
|
|||||||
<div className='w-72 py-14 px-6 sticky top-0 overflow-y-scroll h-screen scroll-hidden'>
|
<div className='w-72 py-14 px-6 sticky top-0 overflow-y-scroll h-screen scroll-hidden'>
|
||||||
{slotLeft}
|
{slotLeft}
|
||||||
<SearchInput className='my-3 rounded-md' />
|
<SearchInput className='my-3 rounded-md' />
|
||||||
{/* 所有文章列表 */}
|
<div className='mb-20'>
|
||||||
<NavPostList filteredPostGroups={filteredPostGroups} />
|
{/* 所有文章列表 */}
|
||||||
|
<NavPostList filteredNavPages={filteredNavPages} />
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -146,7 +149,7 @@ const LayoutBase = (props) => {
|
|||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
{/* 移动端导航抽屉 */}
|
{/* 移动端导航抽屉 */}
|
||||||
<PageNavDrawer {...props} filteredPostGroups={filteredPostGroups} />
|
<PageNavDrawer {...props} filteredNavPages={filteredNavPages} />
|
||||||
|
|
||||||
{/* 移动端底部导航栏 */}
|
{/* 移动端底部导航栏 */}
|
||||||
{/* <BottomMenuBar {...props} className='block md:hidden' /> */}
|
{/* <BottomMenuBar {...props} className='block md:hidden' /> */}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export default function ArticleAdjacent({ prev, next }) {
|
|||||||
|
|
||||||
{/* 桌面端 */}
|
{/* 桌面端 */}
|
||||||
|
|
||||||
<div id='pc-next-post' className={`hidden md:block fixed z-20 right-16 bottom-4 duration-200 transition-all ${isScrollEnd ? 'mb-0 opacity-100' : '-mb-24 opacity-0'}`}>
|
<div id='pc-next-post' className={`hidden md:block fixed z-40 right-16 bottom-4 duration-200 transition-all ${isScrollEnd ? 'mb-0 opacity-100' : '-mb-24 opacity-0'}`}>
|
||||||
<Link
|
<Link
|
||||||
href={`/${next.slug}`}
|
href={`/${next.slug}`}
|
||||||
className='cursor-pointer duration transition-all h-24 dark:bg-[#1e1e1e] border dark:border-gray-600 p-3 bg-white dark:text-gray-300 dark:hover:text-yellow-600 hover:text-white hover:font-bold hover:bg-gray-400 rounded-lg flex flex-col justify-between'
|
className='cursor-pointer duration transition-all h-24 dark:bg-[#1e1e1e] border dark:border-gray-600 p-3 bg-white dark:text-gray-300 dark:hover:text-yellow-600 hover:text-white hover:font-bold hover:bg-gray-400 rounded-lg flex flex-col justify-between'
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const Hero = props => {
|
|||||||
return (
|
return (
|
||||||
<div id="hero-wrapper" className='recent-top-post-group w-full overflow-hidden select-none px-5 mb-4'>
|
<div id="hero-wrapper" className='recent-top-post-group w-full overflow-hidden select-none px-5 mb-4'>
|
||||||
|
|
||||||
<div id="hero" style={{ zIndex: 1 }} className={'animate__animated animate__fadeIn animate__fast recent-post-top rounded-[12px] 2xl:px-5 recent-top-post-group max-w-[86rem] overflow-x-scroll w-full mx-auto flex-row flex-nowrap flex relative space-x-3'} >
|
<div id="hero" style={{ zIndex: 1 }} className={'animate__animated animate__fadeIn animate__fast recent-post-top rounded-[12px] xl:px-5 recent-top-post-group max-w-[86rem] overflow-x-scroll w-full mx-auto flex-row flex-nowrap flex xl:space-x-3 relative'} >
|
||||||
|
|
||||||
{/* 左侧banner组 */}
|
{/* 左侧banner组 */}
|
||||||
<BannerGroup {...props} />
|
<BannerGroup {...props} />
|
||||||
@@ -116,22 +116,22 @@ function TagsGroupBar() {
|
|||||||
*/
|
*/
|
||||||
function GroupMenu() {
|
function GroupMenu() {
|
||||||
return (
|
return (
|
||||||
<div className="h-[165px] select-none xl:h-20 flex flex-col w-48 justify-between xl:space-y-0 xl:flex-row xl:w-full xl:flex-nowrap xl:space-x-3">
|
<div className="h-[165px] select-none xl:h-20 flex flex-col justify-between xl:space-y-0 xl:flex-row w-28 lg:w-48 xl:w-full xl:flex-nowrap xl:space-x-3">
|
||||||
<Link href={CONFIG.HERO_CATEGORY_1?.url} className="group relative overflow-hidden bg-gradient-to-r from-blue-500 to-blue-400 flex h-20 justify-start items-center text-white rounded-xl xl:hover:w-1/2 xl:w-1/3 transition-all duration-500 ease-in">
|
<Link href={CONFIG.HERO_CATEGORY_1?.url} className="group relative overflow-hidden bg-gradient-to-r from-blue-500 to-blue-400 flex h-20 justify-start items-center text-white rounded-xl xl:hover:w-1/2 xl:w-1/3 transition-all duration-500 ease-in">
|
||||||
<div className="font-bold text-lg pl-5 relative -mt-2">
|
<div className="font-bold lg:text-lg pl-5 relative -mt-2">
|
||||||
{CONFIG.HERO_CATEGORY_1?.title}
|
{CONFIG.HERO_CATEGORY_1?.title}
|
||||||
<span className="absolute -bottom-0.5 left-5 w-5 h-0.5 bg-white rounded-full"></span>
|
<span className="absolute -bottom-0.5 left-5 w-5 h-0.5 bg-white rounded-full"></span>
|
||||||
</div>
|
</div>
|
||||||
<div className='absolute right-6 duration-700 ease-in-out transition-all scale-[2] translate-y-6 rotate-12 opacity-20 group-hover:opacity-80 group-hover:scale-100 group-hover:translate-y-0 group-hover:rotate-0'>
|
<div className='hidden lg:block absolute right-6 duration-700 ease-in-out transition-all scale-[2] translate-y-6 rotate-12 opacity-20 group-hover:opacity-80 group-hover:scale-100 group-hover:translate-y-0 group-hover:rotate-0'>
|
||||||
<i className="fa-solid fa-star text-4xl"></i>
|
<i className="fa-solid fa-star text-4xl"></i>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href={CONFIG.HERO_CATEGORY_2?.url} className="group relative overflow-hidden bg-gradient-to-r from-red-500 to-yellow-500 flex h-20 justify-start items-center text-white rounded-xl xl:hover:w-1/2 xl:w-1/3 transition-all duration-500 ease-in">
|
<Link href={CONFIG.HERO_CATEGORY_2?.url} className="group relative overflow-hidden bg-gradient-to-r from-red-500 to-yellow-500 flex h-20 justify-start items-center text-white rounded-xl xl:hover:w-1/2 xl:w-1/3 transition-all duration-500 ease-in">
|
||||||
<div className="font-bold text-lg pl-5 relative -mt-2">
|
<div className="font-bold lg:text-lg pl-5 relative -mt-2">
|
||||||
{CONFIG.HERO_CATEGORY_2?.title}
|
{CONFIG.HERO_CATEGORY_2?.title}
|
||||||
<span className="absolute -bottom-0.5 left-5 w-5 h-0.5 bg-white rounded-full"></span>
|
<span className="absolute -bottom-0.5 left-5 w-5 h-0.5 bg-white rounded-full"></span>
|
||||||
</div>
|
</div>
|
||||||
<div className='absolute right-6 duration-700 ease-in-out transition-all scale-[2] translate-y-6 rotate-12 opacity-20 group-hover:opacity-80 group-hover:scale-100 group-hover:translate-y-0 group-hover:rotate-0'>
|
<div className='hidden lg:block absolute right-6 duration-700 ease-in-out transition-all scale-[2] translate-y-6 rotate-12 opacity-20 group-hover:opacity-80 group-hover:scale-100 group-hover:translate-y-0 group-hover:rotate-0'>
|
||||||
<i className="fa-solid fa-fire-flame-curved text-4xl"></i>
|
<i className="fa-solid fa-fire-flame-curved text-4xl"></i>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -154,16 +154,20 @@ function GroupMenu() {
|
|||||||
* 置顶文章区域
|
* 置顶文章区域
|
||||||
*/
|
*/
|
||||||
function TopGroup(props) {
|
function TopGroup(props) {
|
||||||
const { latestPosts, siteInfo } = props
|
const { latestPosts, allNavPages, siteInfo } = props
|
||||||
const todayCardRef = useRef()
|
const todayCardRef = useRef()
|
||||||
function handleMouseLeave() {
|
function handleMouseLeave() {
|
||||||
todayCardRef.current.coverUp()
|
todayCardRef.current.coverUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取置顶推荐文章
|
||||||
|
const topPosts = getTopPosts({ latestPosts, allNavPages })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id='hero-right-wrapper' onMouseLeave={handleMouseLeave} className='flex-1 relative w-full'>
|
<div id='hero-right-wrapper' onMouseLeave={handleMouseLeave} className='flex-1 relative w-full'>
|
||||||
{/* 置顶最新文章 */}
|
{/* 置顶推荐文章 */}
|
||||||
<div id='top-group' className='w-full flex space-x-3 xl:space-x-0 xl:grid xl:grid-cols-3 xl:gap-3 xl:h-[342px]'>
|
<div id='top-group' className='w-full flex space-x-3 xl:space-x-0 xl:grid xl:grid-cols-3 xl:gap-3 xl:h-[342px]'>
|
||||||
{latestPosts?.map((p, index) => {
|
{topPosts?.map((p, index) => {
|
||||||
return <Link href={`${BLOG.SUB_PATH}/${p?.slug}`} key={index}>
|
return <Link href={`${BLOG.SUB_PATH}/${p?.slug}`} key={index}>
|
||||||
<div className='cursor-pointer h-[164px] group relative flex flex-col w-52 xl:w-full overflow-hidden shadow bg-white dark:bg-black dark:text-white rounded-xl'>
|
<div className='cursor-pointer h-[164px] group relative flex flex-col w-52 xl:w-full overflow-hidden shadow bg-white dark:bg-black dark:text-white rounded-xl'>
|
||||||
<LazyImage priority={index === 0} className='h-24 object-cover' alt={p?.title} src={p?.pageCoverThumbnail || siteInfo?.pageCover} />
|
<LazyImage priority={index === 0} className='h-24 object-cover' alt={p?.title} src={p?.pageCoverThumbnail || siteInfo?.pageCover} />
|
||||||
@@ -181,6 +185,42 @@ function TopGroup(props) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取推荐置顶文章
|
||||||
|
*/
|
||||||
|
function getTopPosts({ latestPosts, allNavPages }) {
|
||||||
|
// 默认展示最近更新
|
||||||
|
if (!CONFIG.HERO_RECOMMEND_POST_TAG || CONFIG.HERO_RECOMMEND_POST_TAG === '') {
|
||||||
|
return latestPosts
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示包含‘推荐’标签的文章
|
||||||
|
let sortPosts = []
|
||||||
|
|
||||||
|
// 排序方式
|
||||||
|
if (JSON.parse(CONFIG.HERO_RECOMMEND_POST_SORT_BY_UPDATE_TIME)) {
|
||||||
|
sortPosts = Object.create(allNavPages).sort((a, b) => {
|
||||||
|
const dateA = new Date(a?.lastEditedDate)
|
||||||
|
const dateB = new Date(b?.lastEditedDate)
|
||||||
|
return dateB - dateA
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
sortPosts = Object.create(allNavPages)
|
||||||
|
}
|
||||||
|
|
||||||
|
const topPosts = []
|
||||||
|
for (const post of sortPosts) {
|
||||||
|
if (topPosts.length === 6) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// 查找标签
|
||||||
|
if (post?.tags?.indexOf(CONFIG.HERO_RECOMMEND_POST_TAG) >= 0) {
|
||||||
|
topPosts.push(post)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return topPosts
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 英雄区右侧,今日卡牌
|
* 英雄区右侧,今日卡牌
|
||||||
* @returns
|
* @returns
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export default function LatestPostsGroupMini ({ latestPosts, siteInfo }) {
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div className='line-clamp-2 menu-link'>{post.title}</div>
|
<div className='line-clamp-2 menu-link'>{post.title}</div>
|
||||||
<div className="text-gray-500">{post.lastEditedTime}</div>
|
<div className="text-gray-500">{post.lastEditedDay}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -143,9 +143,9 @@ const NavBar = props => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 右侧固定 */}
|
{/* 右侧固定 */}
|
||||||
<div className='flex flex-shrink-0 justify-center items-center space-x-1'>
|
<div className='flex flex-shrink-0 justify-center items-center'>
|
||||||
<RandomPostButton {...props} />
|
<RandomPostButton {...props} />
|
||||||
<SearchButton />
|
<SearchButton {...props}/>
|
||||||
{!JSON.parse(BLOG.THEME_SWITCH) && <div className='hidden md:block'><DarkModeButton {...props} /></div>}
|
{!JSON.parse(BLOG.THEME_SWITCH) && <div className='hidden md:block'><DarkModeButton {...props} /></div>}
|
||||||
<ReadingProgress />
|
<ReadingProgress />
|
||||||
|
|
||||||
|
|||||||
@@ -79,13 +79,13 @@ export default function PostHeader({ post, siteInfo }) {
|
|||||||
href={`/archive#${formatDateFmt(post?.publishDate, 'yyyy-MM')}`}
|
href={`/archive#${formatDateFmt(post?.publishDate, 'yyyy-MM')}`}
|
||||||
passHref
|
passHref
|
||||||
className="pl-1 mr-2 cursor-pointer hover:underline">
|
className="pl-1 mr-2 cursor-pointer hover:underline">
|
||||||
<i className="fa-regular fa-calendar"></i> {post?.publishTime}
|
<i className="fa-regular fa-calendar"></i> {post?.publishDay}
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="pl-1 mr-2">
|
<div className="pl-1 mr-2">
|
||||||
<i className="fa-regular fa-calendar-check"></i> {post.lastEditedTime}
|
<i className="fa-regular fa-calendar-check"></i> {post.lastEditedDay}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { useRef } from 'react'
|
|||||||
* 搜索按钮
|
* 搜索按钮
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export default function SearchButton() {
|
export default function SearchButton(props) {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const searchModal = useRef(null)
|
const searchModal = useRef(null)
|
||||||
@@ -25,6 +25,6 @@ export default function SearchButton() {
|
|||||||
<div onClick={handleSearch} title={locale.NAV.SEARCH} alt={locale.NAV.SEARCH} className='cursor-pointer hover:bg-black hover:bg-opacity-10 rounded-full w-10 h-10 flex justify-center items-center duration-200 transition-all'>
|
<div onClick={handleSearch} title={locale.NAV.SEARCH} alt={locale.NAV.SEARCH} className='cursor-pointer hover:bg-black hover:bg-opacity-10 rounded-full w-10 h-10 flex justify-center items-center duration-200 transition-all'>
|
||||||
<i title={locale.NAV.SEARCH} className="fa-solid fa-magnifying-glass" />
|
<i title={locale.NAV.SEARCH} className="fa-solid fa-magnifying-glass" />
|
||||||
</div>
|
</div>
|
||||||
<AlgoliaSearchModal cRef={searchModal} />
|
<AlgoliaSearchModal cRef={searchModal} {...props}/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ const CONFIG = {
|
|||||||
HERO_CATEGORY_2: { title: '热门文章', url: '/tag/热门文章' },
|
HERO_CATEGORY_2: { title: '热门文章', url: '/tag/热门文章' },
|
||||||
HERO_CATEGORY_3: { title: '实用教程', url: '/tag/实用教程' },
|
HERO_CATEGORY_3: { title: '实用教程', url: '/tag/实用教程' },
|
||||||
|
|
||||||
|
// 英雄区右侧推荐文章标签, 例如 [推荐] , 最多六篇文章; 若留空白'',则推荐最近更新文章
|
||||||
|
HERO_RECOMMEND_POST_TAG: '推荐',
|
||||||
|
HERO_RECOMMEND_POST_SORT_BY_UPDATE_TIME: false, // 推荐文章排序,为`true`时将强制按最后修改时间倒序
|
||||||
|
|
||||||
// 右侧个人资料卡牌欢迎语,点击可自动切换
|
// 右侧个人资料卡牌欢迎语,点击可自动切换
|
||||||
INFOCARD_GREETINGS: [
|
INFOCARD_GREETINGS: [
|
||||||
'你好!我是',
|
'你好!我是',
|
||||||
|
|||||||
@@ -39,12 +39,12 @@ import LazyImage from '@/components/LazyImage'
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const LayoutBase = props => {
|
const LayoutBase = props => {
|
||||||
const { children, headerSlot, slotTop, slotRight, meta, siteInfo, className } = props
|
const { children, headerSlot, slotTop, slotRight, siteInfo, className, meta } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id='theme-heo' className='bg-[#f7f9fe] dark:bg-[#18171d] h-full min-h-screen flex flex-col'>
|
<div id='theme-heo' className='bg-[#f7f9fe] dark:bg-[#18171d] h-full min-h-screen flex flex-col'>
|
||||||
{/* 网页SEO */}
|
{/* SEO信息 */}
|
||||||
<CommonHead meta={meta} siteInfo={siteInfo} />
|
<CommonHead meta={meta}/>
|
||||||
<Style />
|
<Style />
|
||||||
|
|
||||||
{/* 顶部嵌入 导航栏,首页放hero,文章页放文章详情 */}
|
{/* 顶部嵌入 导航栏,首页放hero,文章页放文章详情 */}
|
||||||
@@ -93,7 +93,7 @@ const LayoutIndex = (props) => {
|
|||||||
const slotRight = <SideRight {...props} />
|
const slotRight = <SideRight {...props} />
|
||||||
|
|
||||||
return <LayoutBase {...props} slotRight={slotRight} headerSlot={headerSlot}>
|
return <LayoutBase {...props} slotRight={slotRight} headerSlot={headerSlot}>
|
||||||
<div id='post-outer-wrapper' className='px-5 lg:px-0'>
|
<div id='post-outer-wrapper' className='px-5 md:px-0'>
|
||||||
{/* 文章分类条 */}
|
{/* 文章分类条 */}
|
||||||
<CategoryBar {...props} />
|
<CategoryBar {...props} />
|
||||||
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
||||||
@@ -115,7 +115,7 @@ const LayoutPostList = (props) => {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
return <LayoutBase {...props} slotRight={slotRight} headerSlot={headerSlot}>
|
return <LayoutBase {...props} slotRight={slotRight} headerSlot={headerSlot}>
|
||||||
<div id='post-outer-wrapper' className='px-5 lg:px-0'>
|
<div id='post-outer-wrapper' className='px-5 md:px-0'>
|
||||||
{/* 文章分类条 */}
|
{/* 文章分类条 */}
|
||||||
<CategoryBar {...props} />
|
<CategoryBar {...props} />
|
||||||
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
||||||
@@ -155,7 +155,7 @@ const LayoutSearch = props => {
|
|||||||
}, [])
|
}, [])
|
||||||
return (
|
return (
|
||||||
<LayoutBase {...props} currentSearch={currentSearch} headerSlot={headerSlot}>
|
<LayoutBase {...props} currentSearch={currentSearch} headerSlot={headerSlot}>
|
||||||
<div id='post-outer-wrapper' className='px-5 lg:px-0'>
|
<div id='post-outer-wrapper' className='px-5 md:px-0'>
|
||||||
{!currentSearch
|
{!currentSearch
|
||||||
? <SearchNav {...props} />
|
? <SearchNav {...props} />
|
||||||
: <div id="posts-wrapper"> {BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />} </div>}
|
: <div id="posts-wrapper"> {BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />} </div>}
|
||||||
@@ -348,7 +348,7 @@ const LayoutCategoryIndex = props => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutBase {...props} className='mt-8' headerSlot={headerSlot}>
|
<LayoutBase {...props} className='mt-8' headerSlot={headerSlot}>
|
||||||
<div id='category-outer-wrapper' className='px-5 lg:px-0'>
|
<div id='category-outer-wrapper' className='px-5 md:px-0'>
|
||||||
<div className="text-4xl font-extrabold dark:text-gray-200 mb-5">
|
<div className="text-4xl font-extrabold dark:text-gray-200 mb-5">
|
||||||
{locale.COMMON.CATEGORY}
|
{locale.COMMON.CATEGORY}
|
||||||
</div>
|
</div>
|
||||||
@@ -386,7 +386,7 @@ const LayoutTagIndex = props => {
|
|||||||
</header>
|
</header>
|
||||||
return (
|
return (
|
||||||
<LayoutBase {...props} className='mt-8' headerSlot={headerSlot}>
|
<LayoutBase {...props} className='mt-8' headerSlot={headerSlot}>
|
||||||
<div id='tag-outer-wrapper' className='px-5 lg:px-0'>
|
<div id='tag-outer-wrapper' className='px-5 md:px-0'>
|
||||||
<div className="text-4xl font-extrabold dark:text-gray-200 mb-5">
|
<div className="text-4xl font-extrabold dark:text-gray-200 mb-5">
|
||||||
{locale.COMMON.TAGS}
|
{locale.COMMON.TAGS}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const BlogPostArchive = ({ posts = [], archiveTitle }) => {
|
|||||||
key={post.id}
|
key={post.id}
|
||||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-indigo-500 dark:hover:border-indigo-300 dark:border-indigo-400 transform duration-500"
|
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-indigo-500 dark:hover:border-indigo-300 dark:border-indigo-400 transform duration-500"
|
||||||
>
|
>
|
||||||
<div id={post?.publishTime}>
|
<div id={post?.publishDay}>
|
||||||
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export const BlogPostCardInfo = ({ post, showPreview, showPageCover, showSummary
|
|||||||
className="font-light menu-link cursor-pointer text-sm leading-4 mr-3">
|
className="font-light menu-link cursor-pointer text-sm leading-4 mr-3">
|
||||||
|
|
||||||
<i className="far fa-calendar-alt mr-1" />
|
<i className="far fa-calendar-alt mr-1" />
|
||||||
{post?.publishTime || post.lastEditedTime}
|
{post?.publishDay || post.lastEditedDay}
|
||||||
|
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ const Hero = props => {
|
|||||||
|
|
||||||
{/* 滚动按钮 */}
|
{/* 滚动按钮 */}
|
||||||
<div onClick={scrollToWrapper} className="z-10 cursor-pointer w-full text-center py-4 text-3xl absolute bottom-10 text-white">
|
<div onClick={scrollToWrapper} className="z-10 cursor-pointer w-full text-center py-4 text-3xl absolute bottom-10 text-white">
|
||||||
<div className="opacity-70 animate-bounce text-xs">{locale.COMMON.START_READING}</div>
|
<div className="opacity-70 animate-bounce text-xs">{CONFIG.SHOW_START_READING && locale.COMMON.START_READING}</div>
|
||||||
<i className='opacity-70 animate-bounce fas fa-angle-down' />
|
<i className='opacity-70 animate-bounce fas fa-angle-down' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const LatestPostsGroup = ({ latestPosts, siteInfo }) => {
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div className='line-clamp-2 menu-link'>{post.title}</div>
|
<div className='line-clamp-2 menu-link'>{post.title}</div>
|
||||||
<div className="text-gray-500">{post.lastEditedTime}</div>
|
<div className="text-gray-500">{post.lastEditedDay}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -47,13 +47,13 @@ export default function PostHeader({ post, siteInfo }) {
|
|||||||
passHref
|
passHref
|
||||||
className="pl-1 mr-2 cursor-pointer hover:underline">
|
className="pl-1 mr-2 cursor-pointer hover:underline">
|
||||||
|
|
||||||
{locale.COMMON.POST_TIME}: {post?.publishTime}
|
{locale.COMMON.POST_TIME}: {post?.publishDay}
|
||||||
|
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="pl-1 mr-2">
|
<div className="pl-1 mr-2">
|
||||||
{locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedTime}
|
{locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedDay}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ const CONFIG = {
|
|||||||
HOME_NAV_BUTTONS: true, // 首页是否显示分类大图标按钮
|
HOME_NAV_BUTTONS: true, // 首页是否显示分类大图标按钮
|
||||||
// 已知未修复bug, 在移动端开启true后会加载不出图片; 暂时建议设置为false。
|
// 已知未修复bug, 在移动端开启true后会加载不出图片; 暂时建议设置为false。
|
||||||
HOME_NAV_BACKGROUND_IMG_FIXED: false, // 首页背景图滚动时是否固定,true 则滚动时图片不懂动; false则随鼠标滚动 ;
|
HOME_NAV_BACKGROUND_IMG_FIXED: false, // 首页背景图滚动时是否固定,true 则滚动时图片不懂动; false则随鼠标滚动 ;
|
||||||
|
// 是否显示开始阅读按钮
|
||||||
|
SHOW_START_READING: true,
|
||||||
|
|
||||||
// 菜单配置
|
// 菜单配置
|
||||||
MENU_INDEX: true, // 显示首页
|
MENU_INDEX: true, // 显示首页
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const LayoutBase = props => {
|
|||||||
return (
|
return (
|
||||||
<div id='theme-hexo'>
|
<div id='theme-hexo'>
|
||||||
{/* 网页SEO */}
|
{/* 网页SEO */}
|
||||||
<CommonHead meta={meta} siteInfo={siteInfo} />
|
<CommonHead meta={meta}/>
|
||||||
<Style/>
|
<Style/>
|
||||||
|
|
||||||
{/* 顶部导航 */}
|
{/* 顶部导航 */}
|
||||||
@@ -71,7 +71,7 @@ const LayoutBase = props => {
|
|||||||
{/* 主区块 */}
|
{/* 主区块 */}
|
||||||
<main id="wrapper" className={`${CONFIG.HOME_BANNER_ENABLE ? '' : 'pt-16'} bg-hexo-background-gray dark:bg-black w-full py-8 md:px-8 lg:px-24 min-h-screen relative`}>
|
<main id="wrapper" className={`${CONFIG.HOME_BANNER_ENABLE ? '' : 'pt-16'} bg-hexo-background-gray dark:bg-black w-full py-8 md:px-8 lg:px-24 min-h-screen relative`}>
|
||||||
<div id="container-inner" className={(BLOG.LAYOUT_SIDEBAR_REVERSE ? 'flex-row-reverse' : '') + ' w-full mx-auto lg:flex lg:space-x-4 justify-center relative z-10'} >
|
<div id="container-inner" className={(BLOG.LAYOUT_SIDEBAR_REVERSE ? 'flex-row-reverse' : '') + ' w-full mx-auto lg:flex lg:space-x-4 justify-center relative z-10'} >
|
||||||
<div className={`${className || ''} w-full max-w-4xl h-full `}>
|
<div className={`${className || ''} w-full max-w-4xl h-full overflow-x-hidden`}>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
show={!onLoading}
|
show={!onLoading}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const CONFIG = {
|
|||||||
FEATURES_BLOCK_6_P: 'NotionNext,助您轻松开始写作',
|
FEATURES_BLOCK_6_P: 'NotionNext,助您轻松开始写作',
|
||||||
|
|
||||||
// 感言
|
// 感言
|
||||||
TESTIMONIALS_HEADER: '已搭建近4000个网站、浏览量突破 100,000,000+',
|
TESTIMONIALS_HEADER: '已搭建超4000个网站、浏览量突破 100,000,000+',
|
||||||
TESTIMONIALS_P: '网站内容涵盖地产、教育、建筑、医学、机械、IT、电子、软件、自媒体、数位游民、短视频、电商、学生、摄影爱好者、旅行爱好者等等各行各业',
|
TESTIMONIALS_P: '网站内容涵盖地产、教育、建筑、医学、机械、IT、电子、软件、自媒体、数位游民、短视频、电商、学生、摄影爱好者、旅行爱好者等等各行各业',
|
||||||
|
|
||||||
TESTIMONIALS_AVATAR: 'https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F22de3fcb-d90d-4271-bc01-f815f476122b%2F4FE0A0C0-E487-4C74-BF8E-6F01A27461B8-14186-000008094BC289A6.jpg?table=collection&id=a320a2cc-6ebe-4a8d-95cc-ea94e63bced9&width=200',
|
TESTIMONIALS_AVATAR: 'https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F22de3fcb-d90d-4271-bc01-f815f476122b%2F4FE0A0C0-E487-4C74-BF8E-6F01A27461B8-14186-000008094BC289A6.jpg?table=collection&id=a320a2cc-6ebe-4a8d-95cc-ea94e63bced9&width=200',
|
||||||
|
|||||||
@@ -28,11 +28,11 @@ export const ArticleInfo = (props) => {
|
|||||||
passHref
|
passHref
|
||||||
className="cursor-pointer whitespace-nowrap">
|
className="cursor-pointer whitespace-nowrap">
|
||||||
|
|
||||||
<i className='far fa-calendar-minus fa-fw'/> {locale.COMMON.POST_TIME}: {post?.publishTime}
|
<i className='far fa-calendar-minus fa-fw'/> {locale.COMMON.POST_TIME}: {post?.publishDay}
|
||||||
|
|
||||||
</Link>
|
</Link>
|
||||||
<span className='whitespace-nowrap'>
|
<span className='whitespace-nowrap'>
|
||||||
<i className='far fa-calendar-check fa-fw' />{locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedTime}
|
<i className='far fa-calendar-check fa-fw' />{locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedDay}
|
||||||
</span>
|
</span>
|
||||||
<span className="hidden busuanzi_container_page_pv font-light mr-2">
|
<span className="hidden busuanzi_container_page_pv font-light mr-2">
|
||||||
<i className='mr-1 fas fa-eye' /><span className="busuanzi_value_page_pv" />
|
<i className='mr-1 fas fa-eye' /><span className="busuanzi_value_page_pv" />
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const BlogPostArchive = ({ posts = [], archiveTitle }) => {
|
|||||||
key={post.id}
|
key={post.id}
|
||||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-indigo-500 dark:hover:border-indigo-300 dark:border-indigo-400 transform duration-500"
|
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-indigo-500 dark:hover:border-indigo-300 dark:border-indigo-400 transform duration-500"
|
||||||
>
|
>
|
||||||
<div id={post?.publishTime}>
|
<div id={post?.publishDay}>
|
||||||
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
|||||||
className="font-light hover:underline cursor-pointer text-sm leading-4 mr-3">
|
className="font-light hover:underline cursor-pointer text-sm leading-4 mr-3">
|
||||||
|
|
||||||
<i className="far fa-clock mr-1" />
|
<i className="far fa-clock mr-1" />
|
||||||
{post.date?.start_date || post.lastEditedTime}
|
{post.date?.start_date || post.lastEditedDay}
|
||||||
|
|
||||||
</Link>
|
</Link>
|
||||||
<TwikooCommentCount post={post} className='hover:underline cursor-pointer text-sm'/>
|
<TwikooCommentCount post={post} className='hover:underline cursor-pointer text-sm'/>
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ const Hero = props => {
|
|||||||
{/* 滚动按钮 */}
|
{/* 滚动按钮 */}
|
||||||
<div onClick={() => { window.scrollTo({ top: wrapperTop, behavior: 'smooth' }) }}
|
<div onClick={() => { window.scrollTo({ top: wrapperTop, behavior: 'smooth' }) }}
|
||||||
className="mt-12 border cursor-pointer w-40 text-center pt-4 pb-3 text-md text-white hover:bg-orange-600 duration-300 rounded-3xl z-40">
|
className="mt-12 border cursor-pointer w-40 text-center pt-4 pb-3 text-md text-white hover:bg-orange-600 duration-300 rounded-3xl z-40">
|
||||||
<i className='animate-bounce fas fa-angle-double-down' /> <span>{locale.COMMON.START_READING}</span>
|
<i className='animate-bounce fas fa-angle-double-down' /> <span>{CONFIG.SHOW_START_READING && locale.COMMON.START_READING}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ const CONFIG = {
|
|||||||
HOME_NAV_BUTTONS: true, // 首页是否显示分类大图标按钮
|
HOME_NAV_BUTTONS: true, // 首页是否显示分类大图标按钮
|
||||||
HOME_NAV_BACKGROUND_IMG_FIXED: false, // 首页背景图滚动时是否固定,true 则滚动时图片不懂; false则随鼠标滚动
|
HOME_NAV_BACKGROUND_IMG_FIXED: false, // 首页背景图滚动时是否固定,true 则滚动时图片不懂; false则随鼠标滚动
|
||||||
|
|
||||||
|
// 是否显示开始阅读按钮
|
||||||
|
SHOW_START_READING: true,
|
||||||
|
|
||||||
// 菜单配置
|
// 菜单配置
|
||||||
MENU_CATEGORY: true, // 显示分类
|
MENU_CATEGORY: true, // 显示分类
|
||||||
MENU_TAG: true, // 显示标签
|
MENU_TAG: true, // 显示标签
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ export default function ArticleInfo(props) {
|
|||||||
{/* meta */}
|
{/* meta */}
|
||||||
<section className="py-2 items-center text-sm px-1">
|
<section className="py-2 items-center text-sm px-1">
|
||||||
<div className='flex flex-wrap text-gray-500 py-1 dark:text-gray-600'>
|
<div className='flex flex-wrap text-gray-500 py-1 dark:text-gray-600'>
|
||||||
<span className='whitespace-nowrap'> <i className='far fa-calendar mr-2' />{post?.publishTime}</span>
|
<span className='whitespace-nowrap'> <i className='far fa-calendar mr-2' />{post?.publishDay}</span>
|
||||||
<span className='mx-1'>|</span>
|
<span className='mx-1'>|</span>
|
||||||
<span className='whitespace-nowrap mr-2'><i className='far fa-calendar-check mr-2' />{post?.lastEditedTime}</span>
|
<span className='whitespace-nowrap mr-2'><i className='far fa-calendar-check mr-2' />{post?.lastEditedDay}</span>
|
||||||
<div className="hidden busuanzi_container_page_pv font-light mr-2 whitespace-nowrap">
|
<div className="hidden busuanzi_container_page_pv font-light mr-2 whitespace-nowrap">
|
||||||
<i className="mr-1 fas fa-eye" /><span className="busuanzi_value_page_pv" />
|
<i className="mr-1 fas fa-eye" /><span className="busuanzi_value_page_pv" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default function BlogArchiveItem({ archiveTitle, archivePosts }) {
|
|||||||
<li key={post.id}
|
<li key={post.id}
|
||||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
||||||
>
|
>
|
||||||
<div id={post?.publishTime}>
|
<div id={post?.publishDay}>
|
||||||
<span className="text-gray-400">
|
<span className="text-gray-400">
|
||||||
{post.date?.start_date}
|
{post.date?.start_date}
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ function initRevolverMaps () {
|
|||||||
Promise.all([
|
Promise.all([
|
||||||
loadExternalResource('https://rf.revolvermaps.com/0/0/8.js?i=5jnp1havmh9&m=0&c=ff0000&cr1=ffffff&f=arial&l=33')
|
loadExternalResource('https://rf.revolvermaps.com/0/0/8.js?i=5jnp1havmh9&m=0&c=ff0000&cr1=ffffff&f=arial&l=33')
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
console.log('地图加载完成')
|
// console.log('地图加载完成')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import CONFIG from './config'
|
import CONFIG from './config'
|
||||||
|
|
||||||
import CommonHead from '@/components/CommonHead'
|
|
||||||
import { useState, createContext, useContext, useEffect } from 'react'
|
import { useState, createContext, useContext, useEffect } from 'react'
|
||||||
import Footer from './components/Footer'
|
import Footer from './components/Footer'
|
||||||
import InfoCard from './components/InfoCard'
|
import InfoCard from './components/InfoCard'
|
||||||
@@ -36,6 +35,7 @@ import { Transition } from '@headlessui/react'
|
|||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
import replaceSearchResult from '@/components/Mark'
|
import replaceSearchResult from '@/components/Mark'
|
||||||
import ArticleInfo from './components/ArticleInfo'
|
import ArticleInfo from './components/ArticleInfo'
|
||||||
|
import CommonHead from '@/components/CommonHead'
|
||||||
|
|
||||||
// 主题全局状态
|
// 主题全局状态
|
||||||
const ThemeGlobalMedium = createContext()
|
const ThemeGlobalMedium = createContext()
|
||||||
@@ -48,7 +48,7 @@ export const useMediumGlobal = () => useContext(ThemeGlobalMedium)
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const LayoutBase = props => {
|
const LayoutBase = props => {
|
||||||
const { children, meta, showInfoCard = true, slotRight, slotTop, siteInfo, notice } = props
|
const { children, showInfoCard = true, slotRight, slotTop, siteInfo, notice, meta } = props
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [tocVisible, changeTocVisible] = useState(false)
|
const [tocVisible, changeTocVisible] = useState(false)
|
||||||
@@ -57,7 +57,7 @@ const LayoutBase = props => {
|
|||||||
return (
|
return (
|
||||||
<ThemeGlobalMedium.Provider value={{ tocVisible, changeTocVisible }}>
|
<ThemeGlobalMedium.Provider value={{ tocVisible, changeTocVisible }}>
|
||||||
{/* SEO相关 */}
|
{/* SEO相关 */}
|
||||||
<CommonHead meta={meta} />
|
<CommonHead meta={meta}/>
|
||||||
{/* CSS样式 */}
|
{/* CSS样式 */}
|
||||||
<Style/>
|
<Style/>
|
||||||
|
|
||||||
|
|||||||
@@ -59,10 +59,10 @@ export default function ArticleDetail(props) {
|
|||||||
passHref
|
passHref
|
||||||
legacyBehavior>
|
legacyBehavior>
|
||||||
<div className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed">
|
<div className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed">
|
||||||
<i className='far fa-calendar mr-1' /> {post?.publishTime}
|
<i className='far fa-calendar mr-1' /> {post?.publishDay}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<span className='mr-2'> | <i className='far fa-calendar-check mr-2' />{post.lastEditedTime} </span>
|
<span className='mr-2'> | <i className='far fa-calendar-check mr-2' />{post.lastEditedDay} </span>
|
||||||
|
|
||||||
<div className="hidden busuanzi_container_page_pv font-light mr-2">
|
<div className="hidden busuanzi_container_page_pv font-light mr-2">
|
||||||
<i className='mr-1 fas fa-eye' />
|
<i className='mr-1 fas fa-eye' />
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const BlogPostArchive = ({ posts = [], archiveTitle }) => {
|
|||||||
key={post.id}
|
key={post.id}
|
||||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
||||||
>
|
>
|
||||||
<div id={post?.publishTime}>
|
<div id={post?.publishDay}>
|
||||||
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import CONFIG from './config'
|
import CONFIG from './config'
|
||||||
|
|
||||||
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'
|
||||||
@@ -27,6 +25,7 @@ import BlogListBar from './components/BlogListBar'
|
|||||||
import { Transition } from '@headlessui/react'
|
import { Transition } from '@headlessui/react'
|
||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
import replaceSearchResult from '@/components/Mark'
|
import replaceSearchResult from '@/components/Mark'
|
||||||
|
import CommonHead from '@/components/CommonHead'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基础布局 采用左中右三栏布局,移动端使用顶部导航栏
|
* 基础布局 采用左中右三栏布局,移动端使用顶部导航栏
|
||||||
@@ -34,7 +33,7 @@ import replaceSearchResult from '@/components/Mark'
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const LayoutBase = (props) => {
|
const LayoutBase = (props) => {
|
||||||
const { children, headerSlot, meta, floatSlot, rightAreaSlot, siteInfo } = props
|
const { children, headerSlot, floatSlot, rightAreaSlot, siteInfo, meta } = props
|
||||||
const { onLoading } = useGlobal()
|
const { onLoading } = useGlobal()
|
||||||
const targetRef = useRef(null)
|
const targetRef = useRef(null)
|
||||||
const floatButtonGroup = useRef(null)
|
const floatButtonGroup = useRef(null)
|
||||||
@@ -72,7 +71,7 @@ const LayoutBase = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div id='theme-next'>
|
<div id='theme-next'>
|
||||||
{/* SEO相关 */}
|
{/* SEO相关 */}
|
||||||
<CommonHead meta={meta} />
|
<CommonHead meta={meta}/>
|
||||||
<Style/>
|
<Style/>
|
||||||
|
|
||||||
{/* 移动端顶部导航栏 */}
|
{/* 移动端顶部导航栏 */}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const ArticleInfo = (props) => {
|
|||||||
<span className="block"> / </span>
|
<span className="block"> / </span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mr-2 mb-4 md:ml-0">
|
<div className="mr-2 mb-4 md:ml-0">
|
||||||
{post?.publishTime}
|
{post?.publishDay}
|
||||||
</div>
|
</div>
|
||||||
{post?.tags && (
|
{post?.tags && (
|
||||||
<div className="flex flex-nowrap max-w-full overflow-x-auto article-tags">
|
<div className="flex flex-nowrap max-w-full overflow-x-auto article-tags">
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export default function BlogArchiveItem({ archiveTitle, archivePosts }) {
|
|||||||
key={post.id}
|
key={post.id}
|
||||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
||||||
>
|
>
|
||||||
<div id={post?.publishTime}>
|
<div id={post?.publishDay}>
|
||||||
<span className="text-gray-400">
|
<span className="text-gray-400">
|
||||||
{post.date?.start_date}
|
{post.date?.start_date}
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const BlogPost = ({ post }) => {
|
|||||||
{post.title}
|
{post.title}
|
||||||
</h2>
|
</h2>
|
||||||
<time className="flex-shrink-0 text-gray-600 dark:text-gray-400">
|
<time className="flex-shrink-0 text-gray-600 dark:text-gray-400">
|
||||||
{formatDate(post?.publishTime || post.createdTime, BLOG.LANG)}
|
{formatDate(post?.publishDay || post.createdTime, BLOG.LANG)}
|
||||||
</time>
|
</time>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import CONFIG from './config'
|
import CONFIG from './config'
|
||||||
import CommonHead from '@/components/CommonHead'
|
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import Nav from './components/Nav'
|
import Nav from './components/Nav'
|
||||||
import { Footer } from './components/Footer'
|
import { Footer } from './components/Footer'
|
||||||
@@ -24,6 +23,7 @@ import BlogListBar from './components/BlogListBar'
|
|||||||
import { Transition } from '@headlessui/react'
|
import { Transition } from '@headlessui/react'
|
||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
import replaceSearchResult from '@/components/Mark'
|
import replaceSearchResult from '@/components/Mark'
|
||||||
|
import CommonHead from '@/components/CommonHead'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基础布局 采用左右两侧布局,移动端使用顶部导航栏
|
* 基础布局 采用左右两侧布局,移动端使用顶部导航栏
|
||||||
@@ -32,7 +32,7 @@ import replaceSearchResult from '@/components/Mark'
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const LayoutBase = props => {
|
const LayoutBase = props => {
|
||||||
const { children, meta, post, topSlot } = props
|
const { children, post, topSlot, meta } = props
|
||||||
|
|
||||||
const fullWidth = post?.fullWidth ?? false
|
const fullWidth = post?.fullWidth ?? false
|
||||||
const { onLoading } = useGlobal()
|
const { onLoading } = useGlobal()
|
||||||
@@ -40,7 +40,8 @@ const LayoutBase = props => {
|
|||||||
return (
|
return (
|
||||||
<div id='theme-nobelium' className='nobelium relative dark:text-gray-300 w-full bg-white dark:bg-black min-h-screen'>
|
<div id='theme-nobelium' className='nobelium relative dark:text-gray-300 w-full bg-white dark:bg-black min-h-screen'>
|
||||||
{/* SEO相关 */}
|
{/* SEO相关 */}
|
||||||
<CommonHead meta={meta} />
|
<CommonHead meta={meta}/>
|
||||||
|
{/* SEO相关 */}
|
||||||
<Style/>
|
<Style/>
|
||||||
|
|
||||||
{/* 顶部导航栏 */}
|
{/* 顶部导航栏 */}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const ArticleInfo = (props) => {
|
|||||||
<span className="block"> / </span>
|
<span className="block"> / </span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mr-2 mb-4 md:ml-0">
|
<div className="mr-2 mb-4 md:ml-0">
|
||||||
{post?.publishTime}
|
{post?.publishDay}
|
||||||
</div>
|
</div>
|
||||||
{post?.tags && (
|
{post?.tags && (
|
||||||
<div className="flex flex-nowrap max-w-full overflow-x-auto article-tags">
|
<div className="flex flex-nowrap max-w-full overflow-x-auto article-tags">
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export default function BlogArchiveItem({ archiveTitle, archivePosts }) {
|
|||||||
key={post.id}
|
key={post.id}
|
||||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
||||||
>
|
>
|
||||||
<div id={post?.publishTime}>
|
<div id={post?.publishDay}>
|
||||||
<span className="text-gray-400">
|
<span className="text-gray-400">
|
||||||
{post.date?.start_date}
|
{post.date?.start_date}
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import CONFIG from './config'
|
import CONFIG from './config'
|
||||||
import CommonHead from '@/components/CommonHead'
|
import { createContext, useContext, useEffect, useState } from 'react'
|
||||||
import React, { createContext, useContext, useEffect, useState } from 'react'
|
|
||||||
import Header from './components/Nav'
|
import Header from './components/Nav'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
|
|
||||||
@@ -23,6 +22,7 @@ import { saveDarkModeToCookies } from '@/themes/theme'
|
|||||||
import Modal from './components/Modal'
|
import Modal from './components/Modal'
|
||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
import replaceSearchResult from '@/components/Mark'
|
import replaceSearchResult from '@/components/Mark'
|
||||||
|
import CommonHead from '@/components/CommonHead'
|
||||||
|
|
||||||
// 主题全局状态
|
// 主题全局状态
|
||||||
const ThemeGlobalPlog = createContext()
|
const ThemeGlobalPlog = createContext()
|
||||||
@@ -35,7 +35,7 @@ export const usePlogGlobal = () => useContext(ThemeGlobalPlog)
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const LayoutBase = props => {
|
const LayoutBase = props => {
|
||||||
const { children, meta, topSlot } = props
|
const { children, topSlot, meta } = props
|
||||||
const { onLoading, updateDarkMode } = useGlobal()
|
const { onLoading, updateDarkMode } = useGlobal()
|
||||||
const [showModal, setShowModal] = useState(false)
|
const [showModal, setShowModal] = useState(false)
|
||||||
const [modalContent, setModalContent] = useState(null)
|
const [modalContent, setModalContent] = useState(null)
|
||||||
@@ -60,7 +60,7 @@ const LayoutBase = props => {
|
|||||||
<ThemeGlobalPlog.Provider value={{ showModal, setShowModal, modalContent, setModalContent }}>
|
<ThemeGlobalPlog.Provider value={{ showModal, setShowModal, modalContent, setModalContent }}>
|
||||||
<div id='theme-plog' className='plog relative dark:text-gray-300 w-full bg-black min-h-screen'>
|
<div id='theme-plog' className='plog relative dark:text-gray-300 w-full bg-black min-h-screen'>
|
||||||
{/* SEO相关 */}
|
{/* SEO相关 */}
|
||||||
<CommonHead meta={meta} />
|
<CommonHead meta={meta}/>
|
||||||
<Style/>
|
<Style/>
|
||||||
|
|
||||||
{/* 移动端顶部导航栏 */}
|
{/* 移动端顶部导航栏 */}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export const ArticleInfo = (props) => {
|
|||||||
{post?.type !== 'Page' && (<>
|
{post?.type !== 'Page' && (<>
|
||||||
<div className="mb-4 text-sm text-gray-700 dark:text-gray-300">
|
<div className="mb-4 text-sm text-gray-700 dark:text-gray-300">
|
||||||
<span> <i className="fa-regular fa-user"></i> <a href={CONFIG.AUTHOR_LINK}>{BLOG.AUTHOR}</a></span>
|
<span> <i className="fa-regular fa-user"></i> <a href={CONFIG.AUTHOR_LINK}>{BLOG.AUTHOR}</a></span>
|
||||||
<span> - <i className="fa-regular fa-clock"></i> {post?.publishTime}</span>
|
<span> - <i className="fa-regular fa-clock"></i> {post?.publishDay}</span>
|
||||||
{post?.category && <span> - <i className="fa-regular fa-folder"></i> <a href={`/category/${post?.category}`} className="hover:text-red-400 transition-all duration-200">{post?.category}</a></span>}
|
{post?.category && <span> - <i className="fa-regular fa-folder"></i> <a href={`/category/${post?.category}`} className="hover:text-red-400 transition-all duration-200">{post?.category}</a></span>}
|
||||||
{post?.tags && post?.tags?.length > 0 && post?.tags.map(t => <span key={t}> / <Link href={`/tag/${t}`}><span className=' hover:text-red-400 transition-all duration-200'>{t}</span></Link></span>)}
|
{post?.tags && post?.tags?.length > 0 && post?.tags.map(t => <span key={t}> / <Link href={`/tag/${t}`}><span className=' hover:text-red-400 transition-all duration-200'>{t}</span></Link></span>)}
|
||||||
</div>
|
</div>
|
||||||
@@ -32,12 +32,12 @@ export const ArticleInfo = (props) => {
|
|||||||
href={`/archive#${formatDateFmt(post?.publishDate, 'yyyy-MM')}`}
|
href={`/archive#${formatDateFmt(post?.publishDate, 'yyyy-MM')}`}
|
||||||
passHref
|
passHref
|
||||||
className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed">
|
className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed">
|
||||||
{post?.publishTime}
|
{post?.publishDay}
|
||||||
</Link>
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
<span className='mr-2'>|</span>
|
<span className='mr-2'>|</span>
|
||||||
<span className='mx-2 text-gray-400 dark:text-gray-500'>
|
<span className='mx-2 text-gray-400 dark:text-gray-500'>
|
||||||
{locale.COMMON.LAST_EDITED_TIME}: {post?.lastEditedTime}
|
{locale.COMMON.LAST_EDITED_TIME}: {post?.lastEditedDay}
|
||||||
</span>
|
</span>
|
||||||
<span className='mr-2'>|</span>
|
<span className='mr-2'>|</span>
|
||||||
<span className="hidden busuanzi_container_page_pv font-light mr-2">
|
<span className="hidden busuanzi_container_page_pv font-light mr-2">
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export default function BlogArchiveItem({ archiveTitle, archivePosts }) {
|
|||||||
key={post.id}
|
key={post.id}
|
||||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
||||||
>
|
>
|
||||||
<div id={post?.publishTime}>
|
<div id={post?.publishDay}>
|
||||||
<span className="text-gray-400">
|
<span className="text-gray-400">
|
||||||
{post.date?.start_date}
|
{post.date?.start_date}
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import ArticleAround from './components/ArticleAround'
|
|||||||
import ShareBar from '@/components/ShareBar'
|
import ShareBar from '@/components/ShareBar'
|
||||||
import { AdSlot } from '@/components/GoogleAdsense'
|
import { AdSlot } from '@/components/GoogleAdsense'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import CommonHead from '@/components/CommonHead'
|
|
||||||
import { TopBar } from './components/TopBar'
|
import { TopBar } from './components/TopBar'
|
||||||
import { Header } from './components/Header'
|
import { Header } from './components/Header'
|
||||||
import { NavBar } from './components/NavBar'
|
import { NavBar } from './components/NavBar'
|
||||||
@@ -25,6 +24,7 @@ import SearchInput from './components/SearchInput'
|
|||||||
import { Transition } from '@headlessui/react'
|
import { Transition } from '@headlessui/react'
|
||||||
import { Style } from './style'
|
import { Style } from './style'
|
||||||
import replaceSearchResult from '@/components/Mark'
|
import replaceSearchResult from '@/components/Mark'
|
||||||
|
import CommonHead from '@/components/CommonHead'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基础布局
|
* 基础布局
|
||||||
@@ -33,7 +33,7 @@ import replaceSearchResult from '@/components/Mark'
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutBase = props => {
|
const LayoutBase = props => {
|
||||||
const { children, meta, slotTop } = props
|
const { children, slotTop, meta } = props
|
||||||
const { onLoading } = useGlobal()
|
const { onLoading } = useGlobal()
|
||||||
|
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
@@ -41,7 +41,8 @@ const LayoutBase = props => {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div id='theme-simple' className='min-h-screen flex flex-col dark:text-gray-300 bg-white dark:bg-black'>
|
<div id='theme-simple' className='min-h-screen flex flex-col dark:text-gray-300 bg-white dark:bg-black'>
|
||||||
<CommonHead meta={meta} />
|
{/* SEO相关 */}
|
||||||
|
<CommonHead meta={meta}/>
|
||||||
<Style/>
|
<Style/>
|
||||||
|
|
||||||
{CONFIG.TOP_BAR && <TopBar {...props} />}
|
{CONFIG.TOP_BAR && <TopBar {...props} />}
|
||||||
|
|||||||
@@ -60,16 +60,25 @@ export const getLayoutNameByPath = (path) => {
|
|||||||
* @param updateDarkMode 更改主题ChangeState函数
|
* @param updateDarkMode 更改主题ChangeState函数
|
||||||
* @description 读取cookie中存的用户主题
|
* @description 读取cookie中存的用户主题
|
||||||
*/
|
*/
|
||||||
export const initDarkMode = (isDarkMode, updateDarkMode) => {
|
export const initDarkMode = (updateDarkMode) => {
|
||||||
|
// 查看用户设备浏览器是否深色模型
|
||||||
|
let newDarkMode = isPreferDark()
|
||||||
|
|
||||||
|
// 查看cookie中是否用户强制设置深色模式
|
||||||
|
const cookieDarkMode = loadDarkModeFromCookies()
|
||||||
|
if (cookieDarkMode) {
|
||||||
|
newDarkMode = JSON.parse(cookieDarkMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// url查询条件中是否深色模式
|
||||||
const queryMode = getQueryVariable('mode')
|
const queryMode = getQueryVariable('mode')
|
||||||
if (queryMode) {
|
if (queryMode) {
|
||||||
isDarkMode = queryMode === 'dark'
|
newDarkMode = queryMode === 'dark'
|
||||||
} else if (!isDarkMode) {
|
|
||||||
isDarkMode = isPreferDark()
|
|
||||||
}
|
}
|
||||||
updateDarkMode(isDarkMode)
|
|
||||||
saveDarkModeToCookies(isDarkMode)
|
updateDarkMode(newDarkMode)
|
||||||
document.getElementsByTagName('html')[0].setAttribute('class', isDarkMode ? 'dark' : 'light')
|
saveDarkModeToCookies(newDarkMode)
|
||||||
|
document.getElementsByTagName('html')[0].setAttribute('class', newDarkMode ? 'dark' : 'light')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user