mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-30 15:10:11 +00:00
@@ -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=3.5.1
|
NEXT_PUBLIC_VERSION=3.6.0
|
||||||
@@ -115,6 +115,8 @@ yarn run start # 本地启动NextJS服务
|
|||||||
|
|
||||||
<td align="center"><a href="https://github.com/qfdk"><img src="https://avatars.githubusercontent.com/u/2404478" width="64px;" alt="qfdk"/><br/><sub><b>qfdk</b></sub></a><br/><a href="https://github.com/tangly1024/NotionNext/commits?author=qfdk" title="qfdk" >🔧 🐛</a></td>
|
<td align="center"><a href="https://github.com/qfdk"><img src="https://avatars.githubusercontent.com/u/2404478" width="64px;" alt="qfdk"/><br/><sub><b>qfdk</b></sub></a><br/><a href="https://github.com/tangly1024/NotionNext/commits?author=qfdk" title="qfdk" >🔧 🐛</a></td>
|
||||||
|
|
||||||
|
<td align="center"><a href="https://github.com/ifyz"><img src="https://avatars.githubusercontent.com/u/118271360" width="64px;" alt="ifyz"/><br/><sub><b>ifyz</b></sub></a><br/><a href="https://github.com/tangly1024/NotionNext/commits?author=ifyz" title="ifyz" >🔧 🐛</a></td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ const BLOG = {
|
|||||||
|
|
||||||
// 自定义字体示例: 请先将 CUSTOM_FONT 改为 true, 并将 CUSTOM_FONT_URL 改为你的字体CSS地址,同时在 CUSTOM_FONT_SANS 与 CUSTOM_FONT_SERIF 中指定你的 fontfamily
|
// 自定义字体示例: 请先将 CUSTOM_FONT 改为 true, 并将 CUSTOM_FONT_URL 改为你的字体CSS地址,同时在 CUSTOM_FONT_SANS 与 CUSTOM_FONT_SERIF 中指定你的 fontfamily
|
||||||
CUSTOM_FONT: true, // 是否使用自定义字体
|
CUSTOM_FONT: true, // 是否使用自定义字体
|
||||||
CUSTOM_FONT_URL: ['https://cdn.jsdelivr.net/npm/lxgw-wenkai-screen-webfont@1.6.0/lxgwwenkaiscreen.css'], // 自定义字体的CSS
|
CUSTOM_FONT_URL: ['https://npm.elemecdn.com/lxgw-wenkai-webfont@1.6.0/style.css'], // 自定义字体的CSS
|
||||||
CUSTOM_FONT_SANS: ['LXGW WenKai Screen'], // 自定义无衬线字体
|
CUSTOM_FONT_SANS: ['LXGW WenKai'], // 自定义无衬线字体
|
||||||
CUSTOM_FONT_SERIF: ['LXGW WenKai Screen'], // 自定义衬线字体
|
CUSTOM_FONT_SERIF: ['LXGW WenKai'], // 自定义衬线字体
|
||||||
|
|
||||||
// 图标库CDN(可以直接改版本号)
|
// 图标库CDN(可以直接改版本号)
|
||||||
FONT_AWESOME_PATH: 'https://cdn.bootcdn.net/ajax/libs/font-awesome/6.2.0/css/all.min.css',
|
FONT_AWESOME_PATH: 'https://cdn.bootcdn.net/ajax/libs/font-awesome/6.2.0/css/all.min.css',
|
||||||
@@ -52,11 +52,13 @@ const BLOG = {
|
|||||||
PREVIEW_TAG_COUNT: 16, // 首页最多展示的标签数量,0为不限制
|
PREVIEW_TAG_COUNT: 16, // 首页最多展示的标签数量,0为不限制
|
||||||
|
|
||||||
// 社交链接,不需要可留空白,例如 CONTACT_WEIBO:''
|
// 社交链接,不需要可留空白,例如 CONTACT_WEIBO:''
|
||||||
CONTACT_EMAIL: 'mail@tangly1024.com',
|
CONTACT_EMAIL: 'mail@tangly1024.com', // 邮箱
|
||||||
CONTACT_WEIBO: '',
|
CONTACT_WEIBO: '', // 你的微博个人主页
|
||||||
CONTACT_TWITTER: '',
|
CONTACT_TWITTER: '', // 你的twitter个人主页
|
||||||
CONTACT_GITHUB: 'https://github.com/tangly1024',
|
CONTACT_GITHUB: 'https://github.com/tangly1024', // 你的github个人主页
|
||||||
CONTACT_TELEGRAM: 'https://t.me/tangly_1024',
|
CONTACT_TELEGRAM: 'https://t.me/tangly_1024', // 你的telegram 地址 例如 https://t.me/tangly_1024
|
||||||
|
CONTACT_LINKEDIN: '', // 你的linkedIn 首页
|
||||||
|
|
||||||
|
|
||||||
// 鼠标点击烟花特效
|
// 鼠标点击烟花特效
|
||||||
FIREWORKS: process.env.NEXT_PUBLIC_FIREWORKS || false, // 鼠标点击烟花特效
|
FIREWORKS: process.env.NEXT_PUBLIC_FIREWORKS || false, // 鼠标点击烟花特效
|
||||||
|
|||||||
20
components/NotionIcon.js
Normal file
20
components/NotionIcon.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* notion的图标icon
|
||||||
|
* 可能是emoji 可能是 svg 也可能是 图片
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const NotionIcon = ({ icon }) => {
|
||||||
|
if (!icon) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
if (icon.startsWith('http')) {
|
||||||
|
// return <Image src={icon} width={30} height={30}/>
|
||||||
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
|
return <img src={icon} className='w-8 float-left mr-1'/>
|
||||||
|
}
|
||||||
|
|
||||||
|
return <span className='mr-1'>{icon}</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotionIcon
|
||||||
@@ -9,6 +9,7 @@ import Link from 'next/link'
|
|||||||
import { Code } from 'react-notion-x/build/third-party/code'
|
import { Code } from 'react-notion-x/build/third-party/code'
|
||||||
import { Pdf } from 'react-notion-x/build/third-party/pdf'
|
import { Pdf } from 'react-notion-x/build/third-party/pdf'
|
||||||
import { Equation } from 'react-notion-x/build/third-party/equation'
|
import { Equation } from 'react-notion-x/build/third-party/equation'
|
||||||
|
|
||||||
import 'prismjs/components/prism-bash.js'
|
import 'prismjs/components/prism-bash.js'
|
||||||
import 'prismjs/components/prism-markup-templating.js'
|
import 'prismjs/components/prism-markup-templating.js'
|
||||||
import 'prismjs/components/prism-markup.js'
|
import 'prismjs/components/prism-markup.js'
|
||||||
@@ -41,7 +42,15 @@ import 'prismjs/components/prism-swift.js'
|
|||||||
import 'prismjs/components/prism-wasm.js'
|
import 'prismjs/components/prism-wasm.js'
|
||||||
import 'prismjs/components/prism-yaml.js'
|
import 'prismjs/components/prism-yaml.js'
|
||||||
import 'prismjs/components/prism-r.js'
|
import 'prismjs/components/prism-r.js'
|
||||||
import mermaid from 'mermaid'
|
|
||||||
|
// 化学方程式
|
||||||
|
import '@/lib/mhchem'
|
||||||
|
|
||||||
|
// https://github.com/txs
|
||||||
|
// import PrismMac from '@/components/PrismMac'
|
||||||
|
const PrismMac = dynamic(() => import('@/components/PrismMac'), {
|
||||||
|
ssr: false
|
||||||
|
})
|
||||||
|
|
||||||
const Collection = dynamic(() =>
|
const Collection = dynamic(() =>
|
||||||
import('react-notion-x/build/third-party/collection').then((m) => m.Collection), { ssr: true }
|
import('react-notion-x/build/third-party/collection').then((m) => m.Collection), { ssr: true }
|
||||||
@@ -66,14 +75,6 @@ const NotionPage = ({ post }) => {
|
|||||||
const zoomRef = React.useRef(zoom ? zoom.clone() : null)
|
const zoomRef = React.useRef(zoom ? zoom.clone() : null)
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// 支持 Mermaid
|
|
||||||
const mermaids = document.querySelectorAll('.notion-code .language-mermaid')
|
|
||||||
for (const e of mermaids) {
|
|
||||||
const chart = e.innerText
|
|
||||||
e.parentElement.parentElement.innerHTML = `<div class="mermaid">${chart}</div>`
|
|
||||||
mermaid.contentLoaded()
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (window.location.hash) {
|
if (window.location.hash) {
|
||||||
const tocNode = document.getElementById(window.location.hash.substring(1))
|
const tocNode = document.getElementById(window.location.hash.substring(1))
|
||||||
@@ -92,13 +93,18 @@ const NotionPage = ({ post }) => {
|
|||||||
(zoomRef.current).attach(imgList[i])
|
(zoomRef.current).attach(imgList[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 相册图片点击不跳转
|
||||||
|
const cards = document.getElementsByClassName('notion-collection-card')
|
||||||
|
for (const e of cards) {
|
||||||
|
e.removeAttribute('href')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 800)
|
}, 800)
|
||||||
|
|
||||||
addWatch4Dom()
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return <div id='container' className='max-w-5xl overflow-x-hidden mx-auto'>
|
return <div id='container' className='max-w-5xl overflow-x-visible mx-auto'>
|
||||||
|
<PrismMac />
|
||||||
<NotionRenderer
|
<NotionRenderer
|
||||||
recordMap={post.blockMap}
|
recordMap={post.blockMap}
|
||||||
mapPageUrl={mapPageUrl}
|
mapPageUrl={mapPageUrl}
|
||||||
@@ -114,72 +120,6 @@ const NotionPage = ({ post }) => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 监听DOM变化
|
|
||||||
* @param {*} element
|
|
||||||
*/
|
|
||||||
function addWatch4Dom(element) {
|
|
||||||
// 选择需要观察变动的节点
|
|
||||||
const targetNode = element || document?.getElementById('container')
|
|
||||||
// 观察器的配置(需要观察什么变动)
|
|
||||||
const config = {
|
|
||||||
attributes: true,
|
|
||||||
childList: true,
|
|
||||||
subtree: true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当观察到变动时执行的回调函数
|
|
||||||
const mutationCallback = (mutations) => {
|
|
||||||
for (const mutation of mutations) {
|
|
||||||
const type = mutation.type
|
|
||||||
switch (type) {
|
|
||||||
case 'childList':
|
|
||||||
if (mutation.target.className === 'notion-code-copy') {
|
|
||||||
fixCopy(mutation.target)
|
|
||||||
} else if (mutation.target.className && typeof (mutation.target.className) === 'string' && mutation?.target?.className?.indexOf('language-') > -1) {
|
|
||||||
const copyCode = mutation.target.parentElement?.firstElementChild
|
|
||||||
if (copyCode) {
|
|
||||||
fixCopy(copyCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// console.log('A child node has been added or removed.')
|
|
||||||
break
|
|
||||||
case 'attributes':
|
|
||||||
// console.log(`The ${mutation.attributeName} attribute was modified.`)
|
|
||||||
// console.log(mutation.attributeName)
|
|
||||||
break
|
|
||||||
case 'subtree':
|
|
||||||
// console.log('The subtree was modified.')
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建一个观察器实例并传入回调函数
|
|
||||||
const observer = new MutationObserver(mutationCallback)
|
|
||||||
// console.log(observer)
|
|
||||||
// 以上述配置开始观察目标节点
|
|
||||||
if (targetNode) {
|
|
||||||
observer.observe(targetNode, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
// observer.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 复制代码后,会重复 @see https://github.com/tangly1024/NotionNext/issues/165
|
|
||||||
* @param {*} e
|
|
||||||
*/
|
|
||||||
function fixCopy(codeCopy) {
|
|
||||||
const codeE = codeCopy.parentElement.lastElementChild
|
|
||||||
const codeEnd = codeE.lastChild
|
|
||||||
if (codeEnd.nodeName === '#text' && codeE.childNodes.length > 1) {
|
|
||||||
codeEnd.nodeValue = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将id映射成博文内部链接。
|
* 将id映射成博文内部链接。
|
||||||
* @param {*} id
|
* @param {*} id
|
||||||
|
|||||||
147
components/PrismMac.js
Normal file
147
components/PrismMac.js
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Prism from 'prismjs'
|
||||||
|
import 'prismjs/plugins/toolbar/prism-toolbar'
|
||||||
|
import 'prismjs/plugins/show-language/prism-show-language'
|
||||||
|
import 'prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard'
|
||||||
|
import 'prismjs/plugins/autoloader/prism-autoloader'
|
||||||
|
import 'prismjs/plugins/line-numbers/prism-line-numbers'
|
||||||
|
import 'prismjs/plugins/line-numbers/prism-line-numbers.css'
|
||||||
|
|
||||||
|
// mermaid图
|
||||||
|
import mermaid from 'mermaid'
|
||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author https://github.com/txs/
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const PrismMac = () => {
|
||||||
|
const router = useRouter()
|
||||||
|
const { isDarkMode } = useGlobal()
|
||||||
|
const scrollTop = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
renderPrismMac()
|
||||||
|
window.scrollTo(0, scrollTop)
|
||||||
|
router.events.on('routeChangeComplete', renderPrismMac)
|
||||||
|
return () => {
|
||||||
|
router.events.off('routeChangeComplete', renderPrismMac)
|
||||||
|
}
|
||||||
|
}, [isDarkMode])
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
addWatch4Dom()
|
||||||
|
}, [])
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPrismMac() {
|
||||||
|
const container = document?.getElementById('container-inner')
|
||||||
|
const codeToolBars = container?.getElementsByClassName('code-toolbar')
|
||||||
|
|
||||||
|
Array.from(codeToolBars).forEach(item => {
|
||||||
|
const codeBlocks = item.getElementsByTagName('pre')
|
||||||
|
if (codeBlocks.length === 0) {
|
||||||
|
item.remove()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 重新渲染之前检查所有的多余text
|
||||||
|
|
||||||
|
try {
|
||||||
|
Prism.highlightAll()
|
||||||
|
// Add line numbers
|
||||||
|
const codeBlocks = container?.getElementsByTagName('pre')
|
||||||
|
Array.from(codeBlocks).forEach(item => {
|
||||||
|
if (!item.classList.contains('line-numbers')) {
|
||||||
|
item.classList.add('line-numbers')
|
||||||
|
item.style.whiteSpace = 'pre-wrap'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// Add pre-mac element for Mac Style UI
|
||||||
|
Array.from(codeToolBars).forEach(item => {
|
||||||
|
const existPreMac = item.getElementsByClassName('pre-mac')
|
||||||
|
if (existPreMac.length < codeToolBars.length) {
|
||||||
|
const preMac = document.createElement('div')
|
||||||
|
preMac.classList.add('pre-mac')
|
||||||
|
preMac.innerHTML = '<span></span><span></span><span></span>'
|
||||||
|
item?.appendChild(preMac, item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 0)
|
||||||
|
} catch (err) {
|
||||||
|
console.log('代码渲染', err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持 Mermaid
|
||||||
|
const mermaids = document.querySelectorAll('.notion-code .language-mermaid')
|
||||||
|
for (const e of mermaids) {
|
||||||
|
e.parentElement.parentElement.classList.remove('code-toolbar')
|
||||||
|
const chart = e.firstChild.textContent
|
||||||
|
if (e.firstElementChild) {
|
||||||
|
e.parentElement.parentElement.remove()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (chart) {
|
||||||
|
e.parentElement.parentElement.innerHTML = `<div class="mermaid">${chart}</div>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mermaid.contentLoaded()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听DOM变化
|
||||||
|
* @param {*} element
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
function addWatch4Dom(element) {
|
||||||
|
// 选择需要观察变动的节点
|
||||||
|
const targetNode = element || document?.getElementById('container')
|
||||||
|
// 观察器的配置(需要观察什么变动)
|
||||||
|
const config = {
|
||||||
|
attributes: true,
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当观察到变动时执行的回调函数
|
||||||
|
const mutationCallback = (mutations) => {
|
||||||
|
for (const mutation of mutations) {
|
||||||
|
const type = mutation.type
|
||||||
|
switch (type) {
|
||||||
|
case 'childList':
|
||||||
|
// console.log('A child node has been added or removed.', mutation)
|
||||||
|
if (mutation.addedNodes.length > 0) {
|
||||||
|
if (mutation.addedNodes[0].nodeName === '#text') {
|
||||||
|
mutation.addedNodes[0].remove() // 移除新增的内容
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'attributes':
|
||||||
|
// console.log(`The ${mutation.attributeName} attribute was modified.`, mutation.target)
|
||||||
|
// console.log(mutation.attributeName)
|
||||||
|
break
|
||||||
|
case 'subtree':
|
||||||
|
// console.log('The subtree was modified.', mutation.target)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个观察器实例并传入回调函数
|
||||||
|
const observer = new MutationObserver(mutationCallback)
|
||||||
|
// console.log('observer', observer)
|
||||||
|
// 以上述配置开始观察目标节点
|
||||||
|
if (targetNode) {
|
||||||
|
observer.observe(targetNode, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// observer.disconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PrismMac
|
||||||
1696
lib/mhchem.js
Normal file
1696
lib/mhchem.js
Normal file
File diff suppressed because it is too large
Load Diff
37
lib/notion/getNotion.js
Normal file
37
lib/notion/getNotion.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import BLOG from '@/blog.config'
|
||||||
|
import { idToUuid } from 'notion-utils'
|
||||||
|
import formatDate from '../formatDate'
|
||||||
|
import { getPostBlocks } from './getPostBlocks'
|
||||||
|
import { defaultMapImageUrl } from 'react-notion-x'
|
||||||
|
|
||||||
|
export async function getNotion(pageId) {
|
||||||
|
const blockMap = await getPostBlocks(pageId, 'slug')
|
||||||
|
if (!blockMap) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const postInfo = blockMap?.block?.[idToUuid(pageId)].value
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: pageId,
|
||||||
|
type: postInfo,
|
||||||
|
category: '',
|
||||||
|
tags: [],
|
||||||
|
title: postInfo?.properties?.title?.[0],
|
||||||
|
status: 'Published',
|
||||||
|
createdTime: formatDate(new Date(postInfo.created_time).toString(), BLOG.LANG),
|
||||||
|
lastEditedTime: formatDate(new Date(postInfo?.last_edited_time).toString(), BLOG.LANG),
|
||||||
|
fullWidth: false,
|
||||||
|
page_cover: getPageCover(postInfo),
|
||||||
|
date: { start_date: formatDate(new Date(postInfo?.last_edited_time).toString(), BLOG.LANG) },
|
||||||
|
blockMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPageCover(postInfo) {
|
||||||
|
const pageCover = postInfo.format?.page_cover
|
||||||
|
if (pageCover) {
|
||||||
|
if (pageCover.startsWith('/')) return 'https://www.notion.so' + pageCover
|
||||||
|
if (pageCover.startsWith('http')) return defaultMapImageUrl(pageCover, postInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import BLOG from '@/blog.config'
|
|||||||
import formatDate from '../formatDate'
|
import formatDate from '../formatDate'
|
||||||
import { defaultMapImageUrl } from 'react-notion-x'
|
import { defaultMapImageUrl } from 'react-notion-x'
|
||||||
|
|
||||||
async function getPageProperties(id, block, schema, authToken, tagOptions, siteInfo) {
|
export default async function getPageProperties(id, block, schema, authToken, tagOptions, siteInfo) {
|
||||||
const rawProperties = Object.entries(block?.[id]?.value?.properties || [])
|
const rawProperties = Object.entries(block?.[id]?.value?.properties || [])
|
||||||
const excludeProperties = ['date', 'select', 'multi_select', 'person']
|
const excludeProperties = ['date', 'select', 'multi_select', 'person']
|
||||||
const value = block[id]?.value
|
const value = block[id]?.value
|
||||||
@@ -79,6 +79,7 @@ async function getPageProperties(id, block, schema, authToken, tagOptions, siteI
|
|||||||
properties.createdTime = formatDate(new Date(value.created_time).toString(), BLOG.LANG)
|
properties.createdTime = formatDate(new Date(value.created_time).toString(), BLOG.LANG)
|
||||||
properties.lastEditedTime = formatDate(new Date(value?.last_edited_time).toString(), BLOG.LANG)
|
properties.lastEditedTime = formatDate(new Date(value?.last_edited_time).toString(), BLOG.LANG)
|
||||||
properties.fullWidth = value.format?.page_full_width ?? false
|
properties.fullWidth = value.format?.page_full_width ?? false
|
||||||
|
properties.pageIcon = getPageIcon(id, block) ?? ''
|
||||||
properties.page_cover = getPostCover(id, block) ?? siteInfo?.pageCover
|
properties.page_cover = getPostCover(id, block) ?? siteInfo?.pageCover
|
||||||
properties.content = value.content ?? []
|
properties.content = value.content ?? []
|
||||||
properties.tagItems = properties?.tags?.map(tag => {
|
properties.tagItems = properties?.tags?.map(tag => {
|
||||||
@@ -97,4 +98,11 @@ async function getPageProperties(id, block, schema, authToken, tagOptions, siteI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { getPageProperties as default }
|
function getPageIcon(id, block) {
|
||||||
|
const pageIcon = block[id].value?.format?.page_icon
|
||||||
|
if (pageIcon) {
|
||||||
|
if (pageIcon.startsWith('/')) return 'https://www.notion.so' + pageIcon
|
||||||
|
if (pageIcon.startsWith('http')) return defaultMapImageUrl(pageIcon, block[id].value)
|
||||||
|
return pageIcon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export async function getPostBlocks(id, from, slice) {
|
|||||||
* @param {*} id
|
* @param {*} id
|
||||||
* @param {*} retryAttempts
|
* @param {*} retryAttempts
|
||||||
*/
|
*/
|
||||||
async function getPageWithRetry(id, from, retryAttempts = 3) {
|
export async function getPageWithRetry(id, from, retryAttempts = 3) {
|
||||||
if (retryAttempts && retryAttempts > 0) {
|
if (retryAttempts && retryAttempts > 0) {
|
||||||
console.log('[请求API]', `from:${from}`, `id:${id}`, retryAttempts < 3 ? `剩余重试次数:${retryAttempts}` : '')
|
console.log('[请求API]', `from:${from}`, `id:${id}`, retryAttempts < 3 ? `剩余重试次数:${retryAttempts}` : '')
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "notion-next",
|
"name": "notion-next",
|
||||||
"version": "3.5.1",
|
"version": "3.6.0",
|
||||||
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -32,12 +32,13 @@
|
|||||||
"localStorage": "^1.0.4",
|
"localStorage": "^1.0.4",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"memory-cache": "^0.2.0",
|
"memory-cache": "^0.2.0",
|
||||||
"mongodb": "^4.6.0",
|
|
||||||
"mermaid": "9.2.2",
|
"mermaid": "9.2.2",
|
||||||
|
"mongodb": "^4.6.0",
|
||||||
"next": "12.1.6",
|
"next": "12.1.6",
|
||||||
"notion-client": "6.15.6",
|
"notion-client": "6.15.6",
|
||||||
"notion-utils": "6.15.6",
|
"notion-utils": "6.15.6",
|
||||||
"preact": "^10.5.15",
|
"preact": "^10.5.15",
|
||||||
|
"prism-themes": "^1.9.0",
|
||||||
"qrcode.react": "^1.0.1",
|
"qrcode.react": "^1.0.1",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-cookies": "^0.1.1",
|
"react-cookies": "^0.1.1",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import React from 'react'
|
|||||||
import { idToUuid } from 'notion-utils'
|
import { idToUuid } from 'notion-utils'
|
||||||
import Router from 'next/router'
|
import Router from 'next/router'
|
||||||
import { isBrowser } from '@/lib/utils'
|
import { isBrowser } from '@/lib/utils'
|
||||||
|
import { getNotion } from '@/lib/notion/getNotion'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据notion的slug访问页面
|
* 根据notion的slug访问页面
|
||||||
@@ -26,11 +27,11 @@ const Slug = props => {
|
|||||||
const article = document.getElementById('container')
|
const article = document.getElementById('container')
|
||||||
if (!article) {
|
if (!article) {
|
||||||
router.push('/404').then(() => {
|
router.push('/404').then(() => {
|
||||||
// console.warn('找不到页面', router.asPath)
|
console.warn('找不到页面', router.asPath)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 10000)
|
}, 20 * 1000)
|
||||||
const meta = { title: `${props?.siteInfo?.title || BLOG.TITLE} | loading`, image: siteInfo?.pageCover }
|
const meta = { title: `${props?.siteInfo?.title || BLOG.TITLE} | loading`, image: siteInfo?.pageCover }
|
||||||
return <ThemeComponents.LayoutSlug {...props} showArticleInfo={true} meta={meta} />
|
return <ThemeComponents.LayoutSlug {...props} showArticleInfo={true} meta={meta} />
|
||||||
}
|
}
|
||||||
@@ -62,7 +63,7 @@ const Slug = props => {
|
|||||||
const meta = {
|
const meta = {
|
||||||
title: `${post?.title} | ${siteInfo?.title}`,
|
title: `${post?.title} | ${siteInfo?.title}`,
|
||||||
description: post?.summary,
|
description: post?.summary,
|
||||||
type: post.type,
|
type: post?.type,
|
||||||
slug: post?.slug,
|
slug: post?.slug,
|
||||||
image: post?.page_cover,
|
image: post?.page_cover,
|
||||||
category: post?.category?.[0],
|
category: post?.category?.[0],
|
||||||
@@ -102,13 +103,23 @@ export async function getStaticProps({ params: { slug } }) {
|
|||||||
props.post = props.allPages.find((p) => {
|
props.post = props.allPages.find((p) => {
|
||||||
return p.slug === fullSlug || p.id === idToUuid(fullSlug)
|
return p.slug === fullSlug || p.id === idToUuid(fullSlug)
|
||||||
})
|
})
|
||||||
if (!props.post) {
|
|
||||||
console.warn('无效地址', fullSlug)
|
|
||||||
return { props, revalidate: 1 }
|
|
||||||
}
|
|
||||||
props.post.blockMap = await getPostBlocks(props.post.id, 'slug')
|
|
||||||
|
|
||||||
const allPosts = props.allPages.filter(page => page.type === 'Post')
|
if (!props.post) {
|
||||||
|
const pageId = slug.slice(-1)[0]
|
||||||
|
if (pageId.length < 32) {
|
||||||
|
return { props, revalidate: 1 }
|
||||||
|
}
|
||||||
|
const post = await getNotion(pageId)
|
||||||
|
if (post) {
|
||||||
|
props.post = post
|
||||||
|
} else {
|
||||||
|
return { props, revalidate: 1 }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
props.post.blockMap = await getPostBlocks(props.post.id, 'slug')
|
||||||
|
}
|
||||||
|
|
||||||
|
const allPosts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||||
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]
|
||||||
props.next = allPosts.slice(index + 1, index + 2)[0] ?? allPosts[0]
|
props.next = allPosts.slice(index + 1, index + 2)[0] ?? allPosts[0]
|
||||||
@@ -134,7 +145,7 @@ export async function getStaticProps({ params: { slug } }) {
|
|||||||
function getRecommendPost(post, allPosts, count = 6) {
|
function getRecommendPost(post, allPosts, count = 6) {
|
||||||
let recommendPosts = []
|
let recommendPosts = []
|
||||||
const postIds = []
|
const postIds = []
|
||||||
const currentTags = post.tags || []
|
const currentTags = post?.tags || []
|
||||||
for (let i = 0; i < allPosts.length; i++) {
|
for (let i = 0; i < allPosts.length; i++) {
|
||||||
const p = allPosts[i]
|
const p = allPosts[i]
|
||||||
if (p.id === post.id || p.type.indexOf('Post') < 0) {
|
if (p.id === post.id || p.type.indexOf('Post') < 0) {
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ import '@/styles/notion.css' // 重写部分样式
|
|||||||
|
|
||||||
// used for collection views (optional)
|
// used for collection views (optional)
|
||||||
// import 'rc-dropdown/assets/index.css'
|
// import 'rc-dropdown/assets/index.css'
|
||||||
import 'prismjs/themes/prism-tomorrow.min.css'
|
// import 'prismjs/themes/prism-tomorrow.min.css'
|
||||||
|
import 'prism-themes/themes/prism-one-dark.css'
|
||||||
|
import '@/styles/prism-mac-style.css' // 將 Prism 加入 mac 視窗樣式
|
||||||
|
|
||||||
// import 'react-notion-x/build/third-party/equation.css'
|
// import 'react-notion-x/build/third-party/equation.css'
|
||||||
import 'katex/dist/katex.min.css'
|
import 'katex/dist/katex.min.css'
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,13 @@ export async function getStaticProps({ params: { keyword } }) {
|
|||||||
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
|
||||||
|
// 处理分页
|
||||||
|
if (BLOG.POST_LIST_STYLE === 'scroll') {
|
||||||
|
// 滚动列表 给前端返回所有数据
|
||||||
|
} else if (BLOG.POST_LIST_STYLE === 'page') {
|
||||||
|
props.posts = props.posts?.slice(0, BLOG.POSTS_PER_PAGE - 1)
|
||||||
|
}
|
||||||
props.keyword = keyword
|
props.keyword = keyword
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
156
pages/search/[keyword]/page/[page].js
Normal file
156
pages/search/[keyword]/page/[page].js
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
||||||
|
import { useGlobal } from '@/lib/global'
|
||||||
|
import { getDataFromCache } from '@/lib/cache/cache_manager'
|
||||||
|
import * as ThemeMap from '@/themes'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
|
||||||
|
const Index = props => {
|
||||||
|
const { keyword, siteInfo } = props
|
||||||
|
const { locale } = useGlobal()
|
||||||
|
const meta = {
|
||||||
|
title: `${keyword || ''}${keyword ? ' | ' : ''}${locale.NAV.SEARCH} | ${siteInfo?.title}`,
|
||||||
|
description: siteInfo?.title,
|
||||||
|
image: siteInfo?.pageCover,
|
||||||
|
slug: 'search/' + (keyword || ''),
|
||||||
|
type: 'website'
|
||||||
|
}
|
||||||
|
const { theme } = useGlobal()
|
||||||
|
const ThemeComponents = ThemeMap[theme]
|
||||||
|
return (
|
||||||
|
<ThemeComponents.LayoutSearch
|
||||||
|
{...props}
|
||||||
|
meta={meta}
|
||||||
|
currentSearch={keyword}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务端搜索
|
||||||
|
* @param {*} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function getStaticProps({ params: { keyword, page } }) {
|
||||||
|
const props = await getGlobalNotionData({
|
||||||
|
from: 'search-props',
|
||||||
|
pageType: ['Post']
|
||||||
|
})
|
||||||
|
const { allPages } = props
|
||||||
|
const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||||
|
props.posts = await filterByMemCache(allPosts, keyword)
|
||||||
|
props.postCount = props.posts.length
|
||||||
|
// 处理分页
|
||||||
|
props.posts = props.posts.slice(BLOG.POSTS_PER_PAGE * (page - 1), BLOG.POSTS_PER_PAGE * page - 1)
|
||||||
|
props.keyword = keyword
|
||||||
|
props.page = page
|
||||||
|
return {
|
||||||
|
props,
|
||||||
|
revalidate: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
return {
|
||||||
|
paths: [{ params: { keyword: BLOG.TITLE, page: '1' } }],
|
||||||
|
fallback: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将对象的指定字段拼接到字符串
|
||||||
|
* @param sourceTextArray
|
||||||
|
* @param targetObj
|
||||||
|
* @param key
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
function appendText(sourceTextArray, targetObj, key) {
|
||||||
|
if (!targetObj) {
|
||||||
|
return sourceTextArray
|
||||||
|
}
|
||||||
|
const textArray = targetObj[key]
|
||||||
|
const text = textArray ? getTextContent(textArray) : ''
|
||||||
|
if (text && text !== 'Untitled') {
|
||||||
|
return sourceTextArray.concat(text)
|
||||||
|
}
|
||||||
|
return sourceTextArray
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归获取层层嵌套的数组
|
||||||
|
* @param {*} textArray
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function getTextContent(textArray) {
|
||||||
|
if (typeof textArray === 'object' && isIterable(textArray)) {
|
||||||
|
let result = ''
|
||||||
|
for (const textObj of textArray) {
|
||||||
|
result = result + getTextContent(textObj)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} else if (typeof textArray === 'string') {
|
||||||
|
return textArray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象是否可以遍历
|
||||||
|
* @param {*} obj
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const isIterable = obj =>
|
||||||
|
obj != null && typeof obj[Symbol.iterator] === 'function'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在内存缓存中进行全文索引
|
||||||
|
* @param {*} allPosts
|
||||||
|
* @param keyword 关键词
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async function filterByMemCache(allPosts, keyword) {
|
||||||
|
const filterPosts = []
|
||||||
|
if (keyword) {
|
||||||
|
keyword = keyword.trim()
|
||||||
|
}
|
||||||
|
for (const post of allPosts) {
|
||||||
|
const cacheKey = 'page_block_' + post.id
|
||||||
|
const page = await getDataFromCache(cacheKey, true)
|
||||||
|
const tagContent = post.tags && Array.isArray(post.tags) ? post.tags.join(' ') : ''
|
||||||
|
const categoryContent = post.category && Array.isArray(post.category) ? post.category.join(' ') : ''
|
||||||
|
const articleInfo = post.title + post.summary + tagContent + categoryContent
|
||||||
|
let hit = articleInfo.indexOf(keyword) > -1
|
||||||
|
let indexContent = [post.summary]
|
||||||
|
if (page && page.block) {
|
||||||
|
const contentIds = Object.keys(page.block)
|
||||||
|
contentIds.forEach(id => {
|
||||||
|
const properties = page?.block[id]?.value?.properties
|
||||||
|
indexContent = appendText(indexContent, properties, 'title')
|
||||||
|
indexContent = appendText(indexContent, properties, 'caption')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// console.log('全文搜索缓存', cacheKey, page != null)
|
||||||
|
post.results = []
|
||||||
|
let hitCount = 0
|
||||||
|
for (const i in indexContent) {
|
||||||
|
const c = indexContent[i]
|
||||||
|
if (!c) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const index = c.toLowerCase().indexOf(keyword.toLowerCase())
|
||||||
|
if (index > -1) {
|
||||||
|
hit = true
|
||||||
|
hitCount += 1
|
||||||
|
post.results.push(c)
|
||||||
|
} else {
|
||||||
|
if ((post.results.length - 1) / hitCount < 3 || i === 0) {
|
||||||
|
post.results.push(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hit) {
|
||||||
|
filterPosts.push(post)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filterPosts
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Index
|
||||||
@@ -1835,6 +1835,7 @@ pre[class*='language-'] {
|
|||||||
line-height: 120%;
|
line-height: 120%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
@apply dark:text-gray-200
|
||||||
}
|
}
|
||||||
|
|
||||||
.notion-collection-column-title-icon {
|
.notion-collection-column-title-icon {
|
||||||
@@ -1845,6 +1846,8 @@ pre[class*='language-'] {
|
|||||||
min-height: 14px;
|
min-height: 14px;
|
||||||
fill: var(--fg-color-2);
|
fill: var(--fg-color-2);
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
|
@apply dark:text-gray-200
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.notion-collection-column-title-body {
|
.notion-collection-column-title-body {
|
||||||
@@ -1909,4 +1912,33 @@ pre[class*='language-'] {
|
|||||||
|
|
||||||
.notion-simple-table td {
|
.notion-simple-table td {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-mac {
|
||||||
|
position: relative;
|
||||||
|
margin-top: -7px;
|
||||||
|
top: 21px;
|
||||||
|
left: 10px;
|
||||||
|
width: 100px;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-mac > span {
|
||||||
|
float: left;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-mac > span:nth-child(1) {
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-mac > span:nth-child(2) {
|
||||||
|
background: sandybrown;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-mac > span:nth-child(3) {
|
||||||
|
background: limegreen;
|
||||||
}
|
}
|
||||||
70
styles/prism-mac-style.css
Normal file
70
styles/prism-mac-style.css
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* @author https://github.com/txs
|
||||||
|
**/
|
||||||
|
.code-toolbar {
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 10px 30px 0 rgba(0, 0, 0, .4);
|
||||||
|
@apply mb-8 pt-6 w-full rounded-lg bg-black
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
position: absolute;
|
||||||
|
margin-left: 100%;
|
||||||
|
top: 8px;
|
||||||
|
right: 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
line-height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-item{
|
||||||
|
@apply whitespace-nowrap
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-item > button {
|
||||||
|
margin-top: -0.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*='language-'].line-numbers {
|
||||||
|
position: relative;
|
||||||
|
padding: 3px; /*修改*/
|
||||||
|
padding-left: 3.8em;
|
||||||
|
counter-reset: linenumber;
|
||||||
|
max-height: 400px; /*修改*/
|
||||||
|
background: black;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notion-code > code[class*='language-'],
|
||||||
|
pre[class*='language-'] {
|
||||||
|
background: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-mac {
|
||||||
|
position: absolute;
|
||||||
|
left: 15px;
|
||||||
|
@apply z-10 top-4 left-3
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-mac > span {
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 5px;
|
||||||
|
@apply float-left
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-mac > span:nth-child(1) {
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-mac > span:nth-child(2) {
|
||||||
|
background: sandybrown;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-mac > span:nth-child(3) {
|
||||||
|
background: limegreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notion-code-copy{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
|
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
import { BlogListPage } from './components/BlogListPage'
|
import { BlogListPage } from './components/BlogListPage'
|
||||||
|
import { BlogListScroll } from './components/BlogListScroll'
|
||||||
import LayoutBase from './LayoutBase'
|
import LayoutBase from './LayoutBase'
|
||||||
|
|
||||||
export const LayoutIndex = props => {
|
export const LayoutIndex = props => {
|
||||||
return (
|
return (
|
||||||
<LayoutBase {...props}>
|
<LayoutBase {...props}>
|
||||||
<BlogListPage {...props} page={1} />
|
{BLOG.POST_LIST_STYLE === 'page' ? <BlogListPage {...props} /> : <BlogListScroll {...props} />}
|
||||||
</LayoutBase>
|
</LayoutBase>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { BlogListPage } from './components/BlogListPage'
|
||||||
import Link from 'next/link'
|
import { BlogListScroll } from './components/BlogListScroll'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect } from 'react'
|
||||||
import SearchInput from './components/SearchInput'
|
import SearchInput from './components/SearchInput'
|
||||||
import LayoutBase from './LayoutBase'
|
import LayoutBase from './LayoutBase'
|
||||||
import { isBrowser } from '@/lib/utils'
|
import { isBrowser } from '@/lib/utils'
|
||||||
|
|
||||||
export const LayoutSearch = props => {
|
export const LayoutSearch = props => {
|
||||||
const { keyword, posts } = props
|
const { keyword } = props
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -21,22 +21,6 @@ export const LayoutSearch = props => {
|
|||||||
}, 100)
|
}, 100)
|
||||||
}, [router.events])
|
}, [router.events])
|
||||||
|
|
||||||
const { locale } = useGlobal()
|
|
||||||
|
|
||||||
const [page, updatePage] = useState(1)
|
|
||||||
let hasMore = false
|
|
||||||
const postsToShow = posts
|
|
||||||
? Object.assign(posts).slice(0, BLOG.POSTS_PER_PAGE * page)
|
|
||||||
: []
|
|
||||||
|
|
||||||
if (posts) {
|
|
||||||
const totalCount = posts.length
|
|
||||||
hasMore = page * BLOG.POSTS_PER_PAGE < totalCount
|
|
||||||
}
|
|
||||||
const handleGetMore = () => {
|
|
||||||
if (!hasMore) return
|
|
||||||
updatePage(page + 1)
|
|
||||||
}
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (keyword) {
|
if (keyword) {
|
||||||
@@ -59,36 +43,7 @@ export const LayoutSearch = props => {
|
|||||||
<SearchInput {...props} />
|
<SearchInput {...props} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{postsToShow.map(p => (
|
{BLOG.POST_LIST_STYLE === 'page' ? <BlogListPage {...props} /> : <BlogListScroll {...props} />}
|
||||||
<article key={p.id} className="mb-12" >
|
|
||||||
<h2 className="mb-4">
|
|
||||||
<Link href={`/${p.slug}`}>
|
|
||||||
<a className="text-black text-xl md:text-2xl no-underline hover:underline replace"> {p.title}</a>
|
|
||||||
</Link>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="mb-4 text-sm text-gray-700">
|
|
||||||
by <a href="#" className="text-gray-700">{BLOG.AUTHOR}</a> on {p.date?.start_date || p.createdTime}
|
|
||||||
<span className="font-bold mx-1"> | </span>
|
|
||||||
<a href="#" className="text-gray-700">{p.category}</a>
|
|
||||||
<span className="font-bold mx-1"> | </span>
|
|
||||||
{/* <a href="#" className="text-gray-700">2 Comments</a> */}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-gray-700 leading-normal replace">
|
|
||||||
{p.summary}
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
onClick={handleGetMore}
|
|
||||||
className="w-full my-4 py-4 text-center cursor-pointer "
|
|
||||||
>
|
|
||||||
{' '}
|
|
||||||
{hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</LayoutBase>
|
</LayoutBase>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const ArticleInfo = (props) => {
|
|||||||
|
|
||||||
return <section className="flex-wrap flex mt-2 text-gray-400 dark:text-gray-400 font-light leading-8">
|
return <section className="flex-wrap flex mt-2 text-gray-400 dark:text-gray-400 font-light leading-8">
|
||||||
<div>
|
<div>
|
||||||
{post?.type[0] !== 'Page' && <>
|
{post?.type !== 'Page' && <>
|
||||||
<Link href={`/category/${post.category}`} passHref>
|
<Link href={`/category/${post.category}`} passHref>
|
||||||
<a className="cursor-pointer text-md mr-2 hover:text-black dark:hover:text-white border-b dark:border-gray-500 border-dashed">
|
<a className="cursor-pointer text-md mr-2 hover:text-black dark:hover:text-white border-b dark:border-gray-500 border-dashed">
|
||||||
<i className="mr-1 fas fa-folder-open" />
|
<i className="mr-1 fas fa-folder-open" />
|
||||||
@@ -20,7 +20,7 @@ export const ArticleInfo = (props) => {
|
|||||||
<span className='mr-2'>|</span>
|
<span className='mr-2'>|</span>
|
||||||
</>}
|
</>}
|
||||||
|
|
||||||
{post?.type[0] !== 'Page' && (<>
|
{post?.type !== 'Page' && (<>
|
||||||
<Link
|
<Link
|
||||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||||
passHref
|
passHref
|
||||||
|
|||||||
@@ -17,35 +17,37 @@ export const BlogListPage = props => {
|
|||||||
|
|
||||||
return <div className="w-full md:pr-12 mb-12">
|
return <div className="w-full md:pr-12 mb-12">
|
||||||
|
|
||||||
{posts.map(p => (
|
<div id="container">
|
||||||
<article key={p.id} className="mb-12" >
|
{posts?.map(p => (
|
||||||
<h2 className="mb-4">
|
<article key={p.id} className="mb-12" >
|
||||||
<Link href={`/${p.slug}`}>
|
<h2 className="mb-4">
|
||||||
<a className="text-black dark:text-gray-100 text-xl md:text-2xl no-underline hover:underline"> {p.title}</a>
|
<Link href={`/${p.slug}`}>
|
||||||
</Link>
|
<a className="text-black dark:text-gray-100 text-xl md:text-2xl no-underline hover:underline"> {p.title}</a>
|
||||||
</h2>
|
</Link>
|
||||||
|
</h2>
|
||||||
|
|
||||||
<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">
|
||||||
by <a href="#" className="text-gray-700 dark:text-gray-300">{BLOG.AUTHOR}</a> on {p.date?.start_date || p.createdTime}
|
by <a href="#" className="text-gray-700 dark:text-gray-300">{BLOG.AUTHOR}</a> on {p.date?.start_date || p.createdTime}
|
||||||
<span className="font-bold mx-1"> | </span>
|
<span className="font-bold mx-1"> | </span>
|
||||||
<a href="#" className="text-gray-700 dark:text-gray-300">{p.category}</a>
|
<a href="#" className="text-gray-700 dark:text-gray-300">{p.category}</a>
|
||||||
{/* <span className="font-bold mx-1"> | </span> */}
|
{/* <span className="font-bold mx-1"> | </span> */}
|
||||||
{/* <a href="#" className="text-gray-700">2 Comments</a> */}
|
{/* <a href="#" className="text-gray-700">2 Comments</a> */}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-gray-700 dark:text-gray-400 leading-normal">
|
<p className="text-gray-700 dark:text-gray-400 leading-normal">
|
||||||
{p.summary}
|
{p.summary}
|
||||||
</p>
|
|
||||||
{/* 搜索结果 */}
|
|
||||||
{p.results && (
|
|
||||||
<p className="mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
|
||||||
{p.results.map(r => (
|
|
||||||
<span key={r}>{r}</span>
|
|
||||||
))}
|
|
||||||
</p>
|
</p>
|
||||||
)}
|
{/* 搜索结果 */}
|
||||||
</article>
|
{p.results && (
|
||||||
))}
|
<p className="mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||||
|
{p.results.map(r => (
|
||||||
|
<span key={r}>{r}</span>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between text-xs">
|
<div className="flex justify-between text-xs">
|
||||||
<Link href={{ pathname: currentPage - 1 === 1 ? `${BLOG.SUB_PATH || pagePrefix}` : `${pagePrefix}/page/${currentPage - 1}`, query: router.query.s ? { s: router.query.s } : {} }}>
|
<Link href={{ pathname: currentPage - 1 === 1 ? `${BLOG.SUB_PATH || pagePrefix}` : `${pagePrefix}/page/${currentPage - 1}`, query: router.query.s ? { s: router.query.s } : {} }}>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export const BlogListScroll = props => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return <div className="w-full md:pr-12 mb-12" ref={targetRef}>
|
return <div id="container" className="w-full md:pr-12 mb-12" ref={targetRef}>
|
||||||
{postsToShow.map(p => (
|
{postsToShow.map(p => (
|
||||||
<article key={p.id} className="mb-12" >
|
<article key={p.id} className="mb-12" >
|
||||||
<h2 className="mb-4">
|
<h2 className="mb-4">
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import LayoutBase from './LayoutBase'
|
import LayoutBase from './LayoutBase'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
import BlogListPage from './components/BlogListPage'
|
import BlogListPage from './components/BlogListPage'
|
||||||
|
import BlogListScroll from './components/BlogListScroll'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { isBrowser } from '@/lib/utils'
|
import { isBrowser } from '@/lib/utils'
|
||||||
@@ -18,6 +20,6 @@ export const LayoutSearch = (props) => {
|
|||||||
}, 100)
|
}, 100)
|
||||||
})
|
})
|
||||||
return <LayoutBase {...props} currentSearch={currentSearch}>
|
return <LayoutBase {...props} currentSearch={currentSearch}>
|
||||||
<BlogListPage {...props} />
|
{BLOG.POST_LIST_STYLE === 'page' ? <BlogListPage {...props} /> : <BlogListScroll {...props}/>}
|
||||||
</LayoutBase>
|
</LayoutBase>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
import BLOG from '@/blog.config'
|
||||||
import BlogListPage from './components/BlogListPage'
|
import BlogListPage from './components/BlogListPage'
|
||||||
|
import BlogListScroll from './components/BlogListScroll'
|
||||||
import LayoutBase from './LayoutBase'
|
import LayoutBase from './LayoutBase'
|
||||||
|
|
||||||
export const LayoutTag = (props) => {
|
export const LayoutTag = (props) => {
|
||||||
return <LayoutBase {...props}>
|
return <LayoutBase {...props}>
|
||||||
<BlogListPage {...props} />
|
{BLOG.POST_LIST_STYLE === 'page' ? <BlogListPage {...props} /> : <BlogListScroll {...props}/>}
|
||||||
</LayoutBase>
|
</LayoutBase>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default function ArticleDetail(props) {
|
|||||||
<span className='mr-2'>|</span>
|
<span className='mr-2'>|</span>
|
||||||
</>)}
|
</>)}
|
||||||
|
|
||||||
{post?.type[0] !== 'Page' && (<>
|
{post?.type !== 'Page' && (<>
|
||||||
<Link
|
<Link
|
||||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||||
passHref
|
passHref
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ const BlogListPage = ({ page = 1, posts = [], postCount }) => {
|
|||||||
return <BlogPostListEmpty />
|
return <BlogPostListEmpty />
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div id="container">
|
<div>
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
<div style={{ columnCount: colCount }}>
|
<div id="container" style={{ columnCount: colCount }}>
|
||||||
{posts?.map(post => (
|
{posts?.map(post => (
|
||||||
<div key={post.id} className='justify-center flex' style={{ breakInside: 'avoid' }}>
|
<div key={post.id} className='justify-center flex' style={{ breakInside: 'avoid' }}>
|
||||||
<BlogCard key={post.id} post={post} />
|
<BlogCard key={post.id} post={post} />
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import BLOG from '@/blog.config'
|
|||||||
export const LayoutCategory = props => {
|
export const LayoutCategory = props => {
|
||||||
const { category } = props
|
const { category } = props
|
||||||
return <LayoutBase {...props}>
|
return <LayoutBase {...props}>
|
||||||
<div className="cursor-pointer px-5 py-1 mb-2 font-light hover:underline hover:text-indigo-700 dark:hover:text-indigo-400 transform text-center dark:text-white">
|
<div className="cursor-pointer text-lg px-5 py-1 mb-2 font-light hover:text-indigo-700 dark:hover:text-indigo-400 transform dark:text-white">
|
||||||
<i className="mr-1 far fa-folder" />
|
<i className="mr-1 far fa-folder-open" />
|
||||||
{category}
|
{category}
|
||||||
</div>
|
</div>
|
||||||
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||||
import BlogPostListPage from './components/BlogPostListPage'
|
import BlogPostListPage from './components/BlogPostListPage'
|
||||||
import LayoutBase from './LayoutBase'
|
import LayoutBase from './LayoutBase'
|
||||||
import SearchInput from './components/SearchInput'
|
import SearchInput from './components/SearchInput'
|
||||||
@@ -25,8 +27,7 @@ export const LayoutSearch = props => {
|
|||||||
if (container && container.innerHTML) {
|
if (container && container.innerHTML) {
|
||||||
const re = new RegExp(`${currentSearch}`, 'gim')
|
const re = new RegExp(`${currentSearch}`, 'gim')
|
||||||
container.innerHTML = container.innerHTML.replace(
|
container.innerHTML = container.innerHTML.replace(
|
||||||
re,
|
re, `<span class='text-red-500 border-b border-dashed'>${currentSearch}</span>`
|
||||||
`<span class='text-red-500 border-b border-dashed'>${currentSearch}</span>`
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,56 +35,62 @@ export const LayoutSearch = props => {
|
|||||||
}, 100)
|
}, 100)
|
||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<LayoutBase {...props} currentSearch={currentSearch}>
|
<LayoutBase {...props} currentSearch={currentSearch}>
|
||||||
<div className="my-6 px-2">
|
{!currentSearch && <>
|
||||||
<SearchInput cRef={cRef} {...props} />
|
<div className="my-6 px-2">
|
||||||
{/* 分类 */}
|
<SearchInput cRef={cRef} {...props} />
|
||||||
<Card className="w-full mt-4">
|
{/* 分类 */}
|
||||||
<div className="dark:text-gray-200 mb-5 mx-3">
|
<Card className="w-full mt-4">
|
||||||
<i className="mr-4 fas fa-th" />
|
<div className="dark:text-gray-200 mb-5 mx-3">
|
||||||
{locale.COMMON.CATEGORY}:
|
<i className="mr-4 fas fa-th" />
|
||||||
</div>
|
{locale.COMMON.CATEGORY}:
|
||||||
<div id="category-list" className="duration-200 flex flex-wrap mx-8">
|
</div>
|
||||||
{categories?.map(category => {
|
<div id="category-list" className="duration-200 flex flex-wrap mx-8">
|
||||||
return (
|
{categories?.map(category => {
|
||||||
<Link
|
return (
|
||||||
key={category.name}
|
<Link
|
||||||
href={`/category/${category.name}`}
|
key={category.name}
|
||||||
passHref
|
href={`/category/${category.name}`}
|
||||||
>
|
passHref
|
||||||
<div
|
>
|
||||||
className={
|
<div
|
||||||
' duration-300 dark:hover:text-white rounded-lg px-5 cursor-pointer py-2 hover:bg-indigo-400 hover:text-white'
|
className={
|
||||||
}
|
' duration-300 dark:hover:text-white rounded-lg px-5 cursor-pointer py-2 hover:bg-indigo-400 hover:text-white'
|
||||||
>
|
}
|
||||||
<i className="mr-4 fas fa-folder" />
|
>
|
||||||
{category.name}({category.count})
|
<i className="mr-4 fas fa-folder" />
|
||||||
</div>
|
{category.name}({category.count})
|
||||||
</Link>
|
</div>
|
||||||
)
|
</Link>
|
||||||
})}
|
)
|
||||||
</div>
|
})}
|
||||||
</Card>
|
</div>
|
||||||
{/* 标签 */}
|
</Card>
|
||||||
<Card className="w-full mt-4">
|
{/* 标签 */}
|
||||||
<div className="dark:text-gray-200 mb-5 ml-4">
|
<Card className="w-full mt-4">
|
||||||
<i className="mr-4 fas fa-tag" />
|
<div className="dark:text-gray-200 mb-5 ml-4">
|
||||||
{locale.COMMON.TAGS}:
|
<i className="mr-4 fas fa-tag" />
|
||||||
</div>
|
{locale.COMMON.TAGS}:
|
||||||
<div id="tags-list" className="duration-200 flex flex-wrap ml-8">
|
</div>
|
||||||
{tags?.map(tag => {
|
<div id="tags-list" className="duration-200 flex flex-wrap ml-8">
|
||||||
return (
|
{tags?.map(tag => {
|
||||||
<div key={tag.name} className="p-2">
|
return (
|
||||||
<TagItemMini key={tag.name} tag={tag} />
|
<div key={tag.name} className="p-2">
|
||||||
|
<TagItemMini key={tag.name} tag={tag} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
)
|
</>}
|
||||||
})}
|
|
||||||
</div>
|
{currentSearch && <>
|
||||||
</Card>
|
<div id="container">
|
||||||
</div>
|
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
||||||
<div id="container">
|
</div>
|
||||||
<BlogPostListPage {...props} />
|
</>}
|
||||||
</div>
|
|
||||||
</LayoutBase>
|
</LayoutBase>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,23 @@ import BLOG from '@/blog.config'
|
|||||||
import BlogPostListScroll from './components/BlogPostListScroll'
|
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||||
import BlogPostListPage from './components/BlogPostListPage'
|
import BlogPostListPage from './components/BlogPostListPage'
|
||||||
import LayoutBase from './LayoutBase'
|
import LayoutBase from './LayoutBase'
|
||||||
import TagItemMini from '../next/components/TagItemMini'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
export const LayoutTag = (props) => {
|
export const LayoutTag = (props) => {
|
||||||
const currentTag = props.tags.find((t) => {
|
const tag = props.tags.find((t) => {
|
||||||
return t.name === props.tag
|
return t.name === props.tag
|
||||||
})
|
})
|
||||||
|
|
||||||
return <LayoutBase {...props}>
|
return <LayoutBase {...props}>
|
||||||
{currentTag && (
|
{tag && (
|
||||||
<div className="cursor-pointer px-5 py-1 mb-2 font-light hover:underline hover:text-indigo-700 dark:hover:text-indigo-400 transform text-center dark:text-white">
|
<div className="cursor-pointer px-3 py-2 mb-2 font-light hover:text-indigo-700 dark:hover:text-indigo-400 transform dark:text-white">
|
||||||
<TagItemMini tag={currentTag} />
|
<Link key={tag} href={`/tag/${encodeURIComponent(tag.name)}`} passHref>
|
||||||
|
<a className={`cursor-pointer inline-block rounded duration-200
|
||||||
|
mr-2 py-0.5 px-1 text-xl whitespace-nowrap ` }>
|
||||||
|
<div className='font-light dark:text-gray-400 dark:hover:text-white'> #{tag.name + (tag.count ? `(${tag.count})` : '')} </div>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default function HeaderArticle({ post, siteInfo }) {
|
|||||||
</>}
|
</>}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-center'>
|
<div className='flex justify-center'>
|
||||||
{post?.type[0] !== 'Page' && (
|
{post?.type !== 'Page' && (
|
||||||
<>
|
<>
|
||||||
<Link
|
<Link
|
||||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import Link from 'next/link'
|
|||||||
*/
|
*/
|
||||||
const NavButtonGroup = (props) => {
|
const NavButtonGroup = (props) => {
|
||||||
const { categories } = props
|
const { categories } = props
|
||||||
|
if (!categories || categories.length === 0) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
return <nav id='home-nav-button' className={'md:h-52 md:mt-6 xl:mt-32 px-5 py-2 mt-8 flex flex-wrap md:max-w-5xl space-y-2 md:space-y-0 md:flex justify-center max-h-80 overflow-auto'}>
|
return <nav id='home-nav-button' className={'md:h-52 md:mt-6 xl:mt-32 px-5 py-2 mt-8 flex flex-wrap md:max-w-5xl space-y-2 md:space-y-0 md:flex justify-center max-h-80 overflow-auto'}>
|
||||||
{categories.map(category => {
|
{categories.map(category => {
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ const SocialButton = () => {
|
|||||||
{BLOG.CONTACT_TELEGRAM && <a target='_blank' rel='noreferrer' href={BLOG.CONTACT_TELEGRAM} title={'telegram'} >
|
{BLOG.CONTACT_TELEGRAM && <a target='_blank' rel='noreferrer' href={BLOG.CONTACT_TELEGRAM} title={'telegram'} >
|
||||||
<i className='transform hover:scale-125 duration-150 fab fa-telegram dark:hover:text-indigo-400 hover:text-indigo-600'/>
|
<i className='transform hover:scale-125 duration-150 fab fa-telegram dark:hover:text-indigo-400 hover:text-indigo-600'/>
|
||||||
</a>}
|
</a>}
|
||||||
|
{BLOG.CONTACT_LINKEDIN && <a target='_blank' rel='noreferrer' href={BLOG.CONTACT_LINKEDIN} title={'linkIn'} >
|
||||||
|
<i className='transform hover:scale-125 duration-150 fab fa-linkedin dark:hover:text-indigo-400 hover:text-indigo-600'/>
|
||||||
|
</a>}
|
||||||
{BLOG.CONTACT_WEIBO && <a target='_blank' rel='noreferrer' title={'weibo'} href={BLOG.CONTACT_WEIBO} >
|
{BLOG.CONTACT_WEIBO && <a target='_blank' rel='noreferrer' title={'weibo'} href={BLOG.CONTACT_WEIBO} >
|
||||||
<i className='transform hover:scale-125 duration-150 fab fa-weibo dark:hover:text-indigo-400 hover:text-indigo-600'/>
|
<i className='transform hover:scale-125 duration-150 fab fa-weibo dark:hover:text-indigo-400 hover:text-indigo-600'/>
|
||||||
</a>}
|
</a>}
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import SearchInput from './components/SearchInput'
|
|||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import TagGroups from './components/TagGroups'
|
import TagGroups from './components/TagGroups'
|
||||||
import CategoryGroup from './components/CategoryGroup'
|
import CategoryGroup from './components/CategoryGroup'
|
||||||
import BlogPostListScroll from './components/BlogPostListScroll'
|
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { isBrowser } from '@/lib/utils'
|
import { isBrowser } from '@/lib/utils'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||||
|
import BlogPostListPage from './components/BlogPostListPage'
|
||||||
|
|
||||||
export const LayoutSearch = (props) => {
|
export const LayoutSearch = (props) => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
@@ -27,8 +29,8 @@ export const LayoutSearch = (props) => {
|
|||||||
<TagGroups {...props} />
|
<TagGroups {...props} />
|
||||||
<CategoryGroup {...props} />
|
<CategoryGroup {...props} />
|
||||||
</div>
|
</div>
|
||||||
<div id='container'>
|
<div>
|
||||||
<BlogPostListScroll {...props} />
|
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
||||||
</div>
|
</div>
|
||||||
</LayoutBase>
|
</LayoutBase>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import LayoutBase from './LayoutBase'
|
import LayoutBase from './LayoutBase'
|
||||||
import BlogPostListScroll from './components/BlogPostListScroll'
|
|
||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
|
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||||
import BlogPostListPage from './components/BlogPostListPage'
|
import BlogPostListPage from './components/BlogPostListPage'
|
||||||
|
|
||||||
export const LayoutTag = (props) => {
|
export const LayoutTag = (props) => {
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount }) => {
|
|||||||
return <BlogPostListEmpty />
|
return <BlogPostListEmpty />
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div id="container" className='w-full justify-center'>
|
<div className='w-full justify-center'>
|
||||||
|
<div id='container'>
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
{posts.map(post => (
|
{posts.map(post => (
|
||||||
<BlogPostCard key={post.id} post={post} />
|
<BlogPostCard key={post.id} post={post} />
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
<PaginationSimple page={page} totalPage={totalPage} />
|
<PaginationSimple page={page} totalPage={totalPage} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ const SocialButton = () => {
|
|||||||
{BLOG.CONTACT_TELEGRAM && <a target='_blank' rel='noreferrer' href={BLOG.CONTACT_TELEGRAM} title={'telegram'} >
|
{BLOG.CONTACT_TELEGRAM && <a target='_blank' rel='noreferrer' href={BLOG.CONTACT_TELEGRAM} title={'telegram'} >
|
||||||
<i className='fab fa-telegram transform hover:scale-125 duration-150 hover:text-green-600'/>
|
<i className='fab fa-telegram transform hover:scale-125 duration-150 hover:text-green-600'/>
|
||||||
</a>}
|
</a>}
|
||||||
|
{BLOG.CONTACT_LINKEDIN && <a target='_blank' rel='noreferrer' href={BLOG.CONTACT_LINKEDIN} title={'linkedIn'} >
|
||||||
|
<i className='transform hover:scale-125 duration-150 fab fa-linkedin dark:hover:text-indigo-400 hover:text-indigo-600'/>
|
||||||
|
</a>}
|
||||||
{BLOG.CONTACT_WEIBO && <a target='_blank' rel='noreferrer' title={'weibo'} href={BLOG.CONTACT_WEIBO} >
|
{BLOG.CONTACT_WEIBO && <a target='_blank' rel='noreferrer' title={'weibo'} href={BLOG.CONTACT_WEIBO} >
|
||||||
<i className='fab fa-weibo transform hover:scale-125 duration-150 hover:text-green-600'/>
|
<i className='fab fa-weibo transform hover:scale-125 duration-150 hover:text-green-600'/>
|
||||||
</a>}
|
</a>}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import LayoutBase from './LayoutBase'
|
import LayoutBase from './LayoutBase'
|
||||||
import StickyBar from './components/StickyBar'
|
import StickyBar from './components/StickyBar'
|
||||||
import BlogPostListScroll from './components/BlogPostListScroll'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import { isBrowser } from '@/lib/utils'
|
import { isBrowser } from '@/lib/utils'
|
||||||
|
import BlogPostListScroll from './components/BlogPostListScroll'
|
||||||
|
import BlogPostListPage from './components/BlogPostListPage'
|
||||||
|
import BLOG from '@/blog.config'
|
||||||
|
|
||||||
export const LayoutSearch = (props) => {
|
export const LayoutSearch = (props) => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
@@ -23,8 +25,10 @@ export const LayoutSearch = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</StickyBar>
|
</StickyBar>
|
||||||
<div className="md:mt-5">
|
<div className="md:mt-5">
|
||||||
<BlogPostListScroll {...props} showSummary={true} />
|
{BLOG.POST_LIST_STYLE !== 'page'
|
||||||
</div>
|
? <BlogPostListScroll {...props} showSummary={true} />
|
||||||
|
: <BlogPostListPage {...props} />
|
||||||
|
} </div>
|
||||||
</LayoutBase>
|
</LayoutBase>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ export const LayoutTag = (props) => {
|
|||||||
const { tags, tag } = props
|
const { tags, tag } = props
|
||||||
|
|
||||||
return <LayoutBase currentTag={tag} {...props}>
|
return <LayoutBase currentTag={tag} {...props}>
|
||||||
<StickyBar>
|
<StickyBar>
|
||||||
<TagList tags={tags} currentTag={tag}/>
|
<TagList tags={tags} currentTag={tag} />
|
||||||
</StickyBar>
|
</StickyBar>
|
||||||
<div className='md:mt-8'>
|
<div className='md:mt-8'>
|
||||||
{BLOG.POST_LIST_STYLE !== 'page'
|
{BLOG.POST_LIST_STYLE !== 'page'
|
||||||
? <BlogPostListScroll {...props} showSummary={true} />
|
? <BlogPostListScroll {...props} showSummary={true} />
|
||||||
: <BlogPostListPage {...props} />
|
: <BlogPostListPage {...props} />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</LayoutBase>
|
</LayoutBase>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import { useRouter } from 'next/router'
|
|||||||
import ArticleCopyright from './ArticleCopyright'
|
import ArticleCopyright from './ArticleCopyright'
|
||||||
import WordCount from './WordCount'
|
import WordCount from './WordCount'
|
||||||
import NotionPage from '@/components/NotionPage'
|
import NotionPage from '@/components/NotionPage'
|
||||||
|
import CONFIG_NEXT from '../config_next'
|
||||||
|
import NotionIcon from '@/components/NotionIcon'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -29,7 +31,7 @@ export default function ArticleDetail(props) {
|
|||||||
>
|
>
|
||||||
|
|
||||||
{showArticleInfo && <header className='animate__slideInDown animate__animated'>
|
{showArticleInfo && <header className='animate__slideInDown animate__animated'>
|
||||||
{post?.type && !post?.type.includes('Page') && post?.page_cover && (
|
{CONFIG_NEXT.POST_HEADER_IMAGE_VISIBLE && post?.type && !post?.type !== 'Page' && post?.page_cover && (
|
||||||
<div className="w-full relative md:flex-shrink-0 overflow-hidden">
|
<div className="w-full relative md:flex-shrink-0 overflow-hidden">
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
<img alt={post.title} src={post?.page_cover} className='object-center w-full' />
|
<img alt={post.title} src={post?.page_cover} className='object-center w-full' />
|
||||||
@@ -38,7 +40,7 @@ export default function ArticleDetail(props) {
|
|||||||
|
|
||||||
{/* 文章Title */}
|
{/* 文章Title */}
|
||||||
<div className="font-bold text-3xl text-black dark:text-white font-serif pt-10">
|
<div className="font-bold text-3xl text-black dark:text-white font-serif pt-10">
|
||||||
{post.title}
|
<NotionIcon icon={post.pageIcon}/>{post.title}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section className="flex-wrap flex mt-2 text-gray-400 dark:text-gray-400 font-light leading-8">
|
<section className="flex-wrap flex mt-2 text-gray-400 dark:text-gray-400 font-light leading-8">
|
||||||
@@ -51,7 +53,7 @@ export default function ArticleDetail(props) {
|
|||||||
</Link>
|
</Link>
|
||||||
<span className='mr-2'>|</span>
|
<span className='mr-2'>|</span>
|
||||||
</>}
|
</>}
|
||||||
{post?.type[0] !== 'Page' && (<>
|
{post?.type !== 'Page' && (<>
|
||||||
<Link
|
<Link
|
||||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||||
passHref
|
passHref
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Card from './Card'
|
|||||||
import TagItemMini from './TagItemMini'
|
import TagItemMini from './TagItemMini'
|
||||||
import CONFIG_NEXT from '../config_next'
|
import CONFIG_NEXT from '../config_next'
|
||||||
import NotionPage from '@/components/NotionPage'
|
import NotionPage from '@/components/NotionPage'
|
||||||
|
import NotionIcon from '@/components/NotionIcon'
|
||||||
|
|
||||||
const BlogPostCard = ({ post, showSummary }) => {
|
const BlogPostCard = ({ post, showSummary }) => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
@@ -23,7 +24,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
className={`cursor-pointer hover:underline text-3xl ${showPreview ? 'text-center' : ''
|
className={`cursor-pointer hover:underline text-3xl ${showPreview ? 'text-center' : ''
|
||||||
} leading-tight text-gray-700 dark:text-gray-100 hover:text-blue-500 dark:hover:text-blue-400`}
|
} leading-tight text-gray-700 dark:text-gray-100 hover:text-blue-500 dark:hover:text-blue-400`}
|
||||||
>
|
>
|
||||||
{post.title}
|
<NotionIcon icon={post.pageIcon} /> {post.title}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount }) => {
|
|||||||
return <BlogPostListEmpty />
|
return <BlogPostListEmpty />
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div id="container">
|
<div>
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
<div className="flex flex-wrap lg:space-y-4 space-y-1">
|
<div id="container" className="flex flex-wrap lg:space-y-4 space-y-1">
|
||||||
{posts.map(post => (
|
{posts.map(post => (
|
||||||
<BlogPostCard key={post.id} post={post} />
|
<BlogPostCard key={post.id} post={post} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_NE
|
|||||||
if (!postsToShow || postsToShow.length === 0) {
|
if (!postsToShow || postsToShow.length === 0) {
|
||||||
return <BlogPostListEmpty currentSearch={currentSearch} />
|
return <BlogPostListEmpty currentSearch={currentSearch} />
|
||||||
} else {
|
} else {
|
||||||
return <div id='container' ref={targetRef}>
|
return <div ref={targetRef}>
|
||||||
|
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
<div className='flex flex-wrap space-y-1 lg:space-y-4'>
|
<div id='container' className='flex flex-wrap space-y-1 lg:space-y-4'>
|
||||||
{postsToShow.map(post => (
|
{postsToShow.map(post => (
|
||||||
<BlogPostCard key={post.id} post={post} showSummary={showSummary} />
|
<BlogPostCard key={post.id} post={post} showSummary={showSummary} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ export default function Header(props) {
|
|||||||
`linear-gradient(rgba(0, 0, 0, 0.8), rgba(0,0,0,0.2), rgba(0, 0, 0, 0.8) ),url("${siteInfo?.pageCover}")`
|
`linear-gradient(rgba(0, 0, 0, 0.8), rgba(0,0,0,0.2), rgba(0, 0, 0, 0.8) ),url("${siteInfo?.pageCover}")`
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="absolute flex h-full items-center lg:-mt-14 justify-center w-full text-4xl md:text-7xl text-white">
|
<div className="absolute flex h-full items-center lg:-mt-14 justify-center w-full text-2xl md:text-4xl text-white">
|
||||||
<div id='typed' className='flex text-center font-serif' />
|
<div id='typed' className='flex text-center font-serif' />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import BLOG from '@/blog.config'
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
@@ -13,79 +12,78 @@ const PaginationNumber = ({ page, totalPage }) => {
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const currentPage = +page
|
const currentPage = +page
|
||||||
const showNext = page !== totalPage
|
const showNext = page !== totalPage
|
||||||
const pages = generatePages(page, currentPage, totalPage)
|
const pagePrefix = router.asPath.replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '')
|
||||||
|
const pages = generatePages(pagePrefix, page, currentPage, totalPage)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-5 flex justify-center items-end font-medium text-black hover:shadow-xl duration-500 bg-white dark:bg-gray-700 dark:text-gray-300 py-3 shadow space-x-2">
|
<div className="my-5 flex justify-center items-end font-medium text-black hover:shadow-xl duration-500 bg-white dark:bg-gray-700 dark:text-gray-300 py-3 shadow space-x-2">
|
||||||
{/* 上一页 */}
|
{/* 上一页 */}
|
||||||
<Link
|
<Link
|
||||||
href={{
|
href={{
|
||||||
pathname:
|
pathname:
|
||||||
currentPage - 1 === 1
|
currentPage - 1 === 1
|
||||||
? `${BLOG.SUB_PATH || '/'}`
|
? `${pagePrefix}}`
|
||||||
: `/page/${currentPage - 1}`,
|
: `${pagePrefix}/page/${currentPage - 1}`,
|
||||||
query: router.query.s ? { s: router.query.s } : {}
|
query: router.query.s ? { s: router.query.s } : {}
|
||||||
}}
|
}}
|
||||||
passHref
|
passHref
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
rel="prev"
|
rel="prev"
|
||||||
className={`${
|
className={`${currentPage === 1 ? 'invisible' : 'block'
|
||||||
currentPage === 1 ? 'invisible' : 'block'
|
} border-white dark:border-gray-700 hover:border-gray-400 dark:hover:border-gray-400 w-6 text-center cursor-pointer duration-200 hover:font-bold`}
|
||||||
} border-white dark:border-gray-700 hover:border-gray-400 dark:hover:border-gray-400 w-6 text-center cursor-pointer duration-200 hover:font-bold`}
|
>
|
||||||
>
|
<i className="fas fa-angle-left" />
|
||||||
<i className="fas fa-angle-left" />
|
</div>
|
||||||
</div>
|
</Link>
|
||||||
</Link>
|
|
||||||
|
|
||||||
{pages}
|
{pages}
|
||||||
|
|
||||||
{/* 下一页 */}
|
{/* 下一页 */}
|
||||||
<Link
|
<Link
|
||||||
href={{
|
href={{
|
||||||
pathname: `/page/${currentPage + 1}`,
|
pathname: `${pagePrefix}/page/${currentPage + 1}`,
|
||||||
query: router.query.s ? { s: router.query.s } : {}
|
query: router.query.s ? { s: router.query.s } : {}
|
||||||
}}
|
}}
|
||||||
passHref
|
passHref
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
rel="next"
|
rel="next"
|
||||||
className={`${
|
className={`${+showNext ? 'block' : 'invisible'
|
||||||
+showNext ? 'block' : 'invisible'
|
} border-t-2 border-white dark:border-gray-700 hover:border-gray-400 dark:hover:border-gray-400 w-6 text-center cursor-pointer duration-500 hover:font-bold`}
|
||||||
} border-t-2 border-white dark:border-gray-700 hover:border-gray-400 dark:hover:border-gray-400 w-6 text-center cursor-pointer duration-500 hover:font-bold`}
|
>
|
||||||
>
|
<i className="fas fa-angle-right" />
|
||||||
<i className="fas fa-angle-right" />
|
</div>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPageElement(page, currentPage) {
|
function getPageElement(pagePrefix, page, currentPage) {
|
||||||
|
console.log(pagePrefix, page, currentPage)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={page === 1 ? '/' : `/page/${page}`} key={page} passHref>
|
<Link href={page === 1 ? `${pagePrefix}` : `${pagePrefix}/page/${page}`} key={page} passHref>
|
||||||
<a
|
<a className={
|
||||||
className={
|
(page + '' === currentPage + ''
|
||||||
(page + '' === currentPage + ''
|
? 'font-bold bg-gray-500 dark:bg-gray-400 text-white '
|
||||||
? 'font-bold bg-gray-500 dark:bg-gray-400 text-white '
|
: 'border-t-2 duration-500 border-white hover:border-gray-400 ') +
|
||||||
: 'border-t-2 duration-500 border-white hover:border-gray-400 ') +
|
' border-white dark:border-gray-700 dark:hover:border-gray-400 cursor-pointer w-6 text-center font-light hover:font-bold'
|
||||||
' border-white dark:border-gray-700 dark:hover:border-gray-400 cursor-pointer w-6 text-center font-light hover:font-bold'
|
} >
|
||||||
}
|
{page}
|
||||||
>
|
</a>
|
||||||
{page}
|
</Link>
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
function generatePages(page, currentPage, totalPage) {
|
function generatePages(pagePrefix, page, currentPage, totalPage) {
|
||||||
const pages = []
|
const pages = []
|
||||||
const groupCount = 7 // 最多显示页签数
|
const groupCount = 7 // 最多显示页签数
|
||||||
if (totalPage <= groupCount) {
|
if (totalPage <= groupCount) {
|
||||||
for (let i = 1; i <= totalPage; i++) {
|
for (let i = 1; i <= totalPage; i++) {
|
||||||
pages.push(getPageElement(i, page))
|
pages.push(getPageElement(pagePrefix, i, page))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pages.push(getPageElement(1, page))
|
pages.push(getPageElement(pagePrefix, 1, page))
|
||||||
const dynamicGroupCount = groupCount - 2
|
const dynamicGroupCount = groupCount - 2
|
||||||
let startPage = currentPage - 2
|
let startPage = currentPage - 2
|
||||||
if (startPage <= 1) {
|
if (startPage <= 1) {
|
||||||
@@ -100,7 +98,7 @@ function generatePages(page, currentPage, totalPage) {
|
|||||||
|
|
||||||
for (let i = 0; i < dynamicGroupCount; i++) {
|
for (let i = 0; i < dynamicGroupCount; i++) {
|
||||||
if (startPage + i < totalPage) {
|
if (startPage + i < totalPage) {
|
||||||
pages.push(getPageElement(startPage + i, page))
|
pages.push(getPageElement(pagePrefix, startPage + i, page))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +106,7 @@ function generatePages(page, currentPage, totalPage) {
|
|||||||
pages.push(<div key={-2}>... </div>)
|
pages.push(<div key={-2}>... </div>)
|
||||||
}
|
}
|
||||||
|
|
||||||
pages.push(getPageElement(totalPage, page))
|
pages.push(getPageElement(pagePrefix, totalPage, page))
|
||||||
}
|
}
|
||||||
return pages
|
return pages
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ const SocialButton = () => {
|
|||||||
{BLOG.CONTACT_TELEGRAM && <a target='_blank' rel='noreferrer' href={BLOG.CONTACT_TELEGRAM} title={'telegram'} >
|
{BLOG.CONTACT_TELEGRAM && <a target='_blank' rel='noreferrer' href={BLOG.CONTACT_TELEGRAM} title={'telegram'} >
|
||||||
<i className='fab fa-telegram transform hover:scale-125 duration-150'/>
|
<i className='fab fa-telegram transform hover:scale-125 duration-150'/>
|
||||||
</a>}
|
</a>}
|
||||||
|
{BLOG.CONTACT_LINKEDIN && <a target='_blank' rel='noreferrer' href={BLOG.CONTACT_LINKEDIN} title={'linkedIn'} >
|
||||||
|
<i className='transform hover:scale-125 duration-150 fab fa-linkedin dark:hover:text-indigo-400 hover:text-indigo-600'/>
|
||||||
|
</a>}
|
||||||
{BLOG.CONTACT_WEIBO && <a target='_blank' rel='noreferrer' title={'weibo'} href={BLOG.CONTACT_WEIBO} >
|
{BLOG.CONTACT_WEIBO && <a target='_blank' rel='noreferrer' title={'weibo'} href={BLOG.CONTACT_WEIBO} >
|
||||||
<i className='fab fa-weibo transform hover:scale-125 duration-150'/>
|
<i className='fab fa-weibo transform hover:scale-125 duration-150'/>
|
||||||
</a>}
|
</a>}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ const CONFIG_NEXT = {
|
|||||||
POST_LIST_PREVIEW: true, // 显示文章预览
|
POST_LIST_PREVIEW: true, // 显示文章预览
|
||||||
POST_LIST_SUMMARY: false, // 显示用户自定义摘要,有预览时优先只展示预览
|
POST_LIST_SUMMARY: false, // 显示用户自定义摘要,有预览时优先只展示预览
|
||||||
|
|
||||||
|
POST_HEADER_IMAGE_VISIBLE: false, // 文章详情页是否显示封面图
|
||||||
|
|
||||||
// 右侧组件
|
// 右侧组件
|
||||||
RIGHT_BAR: true, // 是否显示右侧栏
|
RIGHT_BAR: true, // 是否显示右侧栏
|
||||||
RIGHT_LATEST_POSTS: true, // 右侧栏最新文章
|
RIGHT_LATEST_POSTS: true, // 右侧栏最新文章
|
||||||
|
|||||||
Reference in New Issue
Block a user