mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-06-02 07:26:45 +00:00
Merge pull request #393 from tangly1024/feature/index-button-category
Feature/index button category
This commit is contained in:
@@ -31,6 +31,9 @@ const BLOG = {
|
|||||||
BACKGROUND_DARK: '#000000', // use hex value, don't forget '#'
|
BACKGROUND_DARK: '#000000', // use hex value, don't forget '#'
|
||||||
SUB_PATH: '', // leave this empty unless you want to deploy in a folder
|
SUB_PATH: '', // leave this empty unless you want to deploy in a folder
|
||||||
|
|
||||||
|
POST_URL_PREFIX: process.env.NEXT_PUBLIC_POST_URL_PREFIX || 'article', // POST类型文章的默认路径前缀,例如默认POST类型的路径是 /article/[slug]
|
||||||
|
// 如果此项配置为 '' 空, 则文章将没有前缀路径,使用场景: 希望 文章前缀路径为 /post 的情况 支持多级
|
||||||
|
|
||||||
POST_LIST_STYLE: 'page', // ['page','scroll] 文章列表样式:页码分页、单页滚动加载
|
POST_LIST_STYLE: 'page', // ['page','scroll] 文章列表样式:页码分页、单页滚动加载
|
||||||
POST_LIST_PREVIEW: process.env.NEXT_PUBLIC_POST_PREVIEW || 'false', // 是否在列表加载文章预览
|
POST_LIST_PREVIEW: process.env.NEXT_PUBLIC_POST_PREVIEW || 'false', // 是否在列表加载文章预览
|
||||||
POST_PREVIEW_LINES: 12, // 预览博客行数
|
POST_PREVIEW_LINES: 12, // 预览博客行数
|
||||||
@@ -128,20 +131,22 @@ const BLOG = {
|
|||||||
|
|
||||||
ADSENSE_GOOGLE_ID: process.env.NEXT_PUBLIC_ADSENSE_GOOGLE_ID || '', // 谷歌广告ID e.g ca-pub-xxxxxxxxxxxxxxxx
|
ADSENSE_GOOGLE_ID: process.env.NEXT_PUBLIC_ADSENSE_GOOGLE_ID || '', // 谷歌广告ID e.g ca-pub-xxxxxxxxxxxxxxxx
|
||||||
|
|
||||||
// 无关紧要的配置
|
|
||||||
// 自定义配置notion数据库字段名
|
// 自定义配置notion数据库字段名
|
||||||
NOTION_PROPERTY_NAME: {
|
NOTION_PROPERTY_NAME: {
|
||||||
password: 'password',
|
password: process.env.NEXT_PUBLIC_NOTION_PROPERTY_PASSWORD || 'password',
|
||||||
type: 'type',
|
type: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE || 'type',
|
||||||
title: 'title',
|
title: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TITLE || 'title',
|
||||||
status: 'status',
|
status: process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS || 'status',
|
||||||
summary: 'summary',
|
summary: process.env.NEXT_PUBLIC_NOTION_PROPERTY_SUMMARY || 'summary',
|
||||||
slug: 'slug',
|
slug: process.env.NEXT_PUBLIC_NOTION_PROPERTY_SLUG || 'slug',
|
||||||
category: 'category',
|
category: process.env.NEXT_PUBLIC_NOTION_PROPERTY_CATEGORY || 'category',
|
||||||
date: 'date',
|
date: process.env.NEXT_PUBLIC_NOTION_PROPERTY_DATE || 'date',
|
||||||
tags: 'tags',
|
tags: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TAGS || 'tags',
|
||||||
icon: 'icon'
|
icon: process.env.NEXT_PUBLIC_NOTION_PROPERTY_ICON || 'icon'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ENABLE_CACHE: false, // 开启缓存 会将Notion数据缓存在内存中,稍微提升访问速度,但要更新内容需要多次刷新页面
|
||||||
|
|
||||||
AVATAR: '/avatar.png', // 作者头像,被notion中的ICON覆盖。如果没有ICON则取public目录下的avatar.png
|
AVATAR: '/avatar.png', // 作者头像,被notion中的ICON覆盖。如果没有ICON则取public目录下的avatar.png
|
||||||
TITLE: process.env.NEXT_PUBLIC_TITLE || 'NotionNext BLOG', // 站点标题 ,被notion中的页面标题覆盖
|
TITLE: process.env.NEXT_PUBLIC_TITLE || 'NotionNext BLOG', // 站点标题 ,被notion中的页面标题覆盖
|
||||||
DESCRIPTION:
|
DESCRIPTION:
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const CommonHead = ({ meta, children }) => {
|
|||||||
<meta name="twitter:description" content={description} />
|
<meta name="twitter:description" content={description} />
|
||||||
<meta name="twitter:title" content={title} />
|
<meta name="twitter:title" content={title} />
|
||||||
{JSON.parse(BLOG.ANALYTICS_BUSUANZI_ENABLE) && <meta name="referrer" content="no-referrer-when-downgrade" />}
|
{JSON.parse(BLOG.ANALYTICS_BUSUANZI_ENABLE) && <meta name="referrer" content="no-referrer-when-downgrade" />}
|
||||||
{meta?.type === 'article' && (
|
{meta?.type === 'Post' && (
|
||||||
<>
|
<>
|
||||||
<meta
|
<meta
|
||||||
property="article:published_time"
|
property="article:published_time"
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ function fixCopy(codeCopy) {
|
|||||||
*/
|
*/
|
||||||
const mapPageUrl = id => {
|
const mapPageUrl = id => {
|
||||||
// return 'https://www.notion.so/' + id.replace(/-/g, '')
|
// return 'https://www.notion.so/' + id.replace(/-/g, '')
|
||||||
return '/article/' + id.replace(/-/g, '')
|
return '/' + id.replace(/-/g, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMediumZoomMargin() {
|
function getMediumZoomMargin() {
|
||||||
|
|||||||
21
lib/cache/cache_manager.js
vendored
21
lib/cache/cache_manager.js
vendored
@@ -1,7 +1,7 @@
|
|||||||
import MemoryCache from './memory_cache'
|
import MemoryCache from './memory_cache'
|
||||||
import FileCache from './local_file_cache'
|
import FileCache from './local_file_cache'
|
||||||
import MongoCache from './mongo_db_cache'
|
import MongoCache from './mongo_db_cache'
|
||||||
const enableCache = true
|
import BLOG from '@/blog.config'
|
||||||
|
|
||||||
let api
|
let api
|
||||||
if (process.env.MONGO_DB_URL && process.env.MONGO_DB_NAME) {
|
if (process.env.MONGO_DB_URL && process.env.MONGO_DB_NAME) {
|
||||||
@@ -17,26 +17,27 @@ if (process.env.MONGO_DB_URL && process.env.MONGO_DB_NAME) {
|
|||||||
* @param {*} key
|
* @param {*} key
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export async function getDataFromCache(key) {
|
export async function getDataFromCache(key, force) {
|
||||||
if (!enableCache) {
|
if (BLOG.ENABLE_CACHE || force) {
|
||||||
|
const dataFromCache = await api.getCache(key)
|
||||||
|
if (JSON.stringify(dataFromCache) === '[]') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return api.getCache(key)
|
||||||
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const dataFromCache = await api.getCache(key)
|
|
||||||
if (JSON.stringify(dataFromCache) === '[]') {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return api.getCache(key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setDataToCache(key, data) {
|
export async function setDataToCache(key, data) {
|
||||||
if (!enableCache || !data) {
|
if (!data) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await api.setCache(key, data)
|
await api.setCache(key, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function delCacheData(key) {
|
export async function delCacheData(key) {
|
||||||
if (!enableCache) {
|
if (!BLOG.ENABLE_CACHE) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await api.delCache(key)
|
await api.delCache(key)
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ export default function getAllPageIds (collectionQuery, collectionId, collection
|
|||||||
view?.collection_group_results?.blockIds?.forEach(id => pageSet.add(id)) // table视图
|
view?.collection_group_results?.blockIds?.forEach(id => pageSet.add(id)) // table视图
|
||||||
})
|
})
|
||||||
pageIds = [...pageSet]
|
pageIds = [...pageSet]
|
||||||
console.log('PageIds: 从collectionQuery获取', collectionQuery)
|
// console.log('PageIds: 从collectionQuery获取', collectionQuery, pageIds.length)
|
||||||
} else if (viewIds && viewIds.length > 0) {
|
} else if (viewIds && viewIds.length > 0) {
|
||||||
const ids = collectionView[viewIds[0]].value.page_sort
|
const ids = collectionView[viewIds[0]].value.page_sort
|
||||||
console.log('PageIds: 从viewId获取', viewIds)
|
// console.log('PageIds: 从viewId获取', viewIds)
|
||||||
for (const id of ids) {
|
for (const id of ids) {
|
||||||
pageIds.push(id)
|
pageIds.push(id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import { isIterable } from '../utils'
|
|||||||
* @param tagOptions tags的下拉选项
|
* @param tagOptions tags的下拉选项
|
||||||
* @returns {Promise<{}|*[]>}
|
* @returns {Promise<{}|*[]>}
|
||||||
*/
|
*/
|
||||||
export function getAllTags({ allPosts, sliceCount = 0, tagOptions }) {
|
export function getAllTags({ allPages, sliceCount = 0, tagOptions }) {
|
||||||
|
const allPosts = allPages.filter(page => page.type === 'Post')
|
||||||
|
|
||||||
if (!allPosts || !tagOptions) {
|
if (!allPosts || !tagOptions) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ import getPageProperties from './getPageProperties'
|
|||||||
*/
|
*/
|
||||||
export async function getGlobalNotionData({
|
export async function getGlobalNotionData({
|
||||||
pageId = BLOG.NOTION_PAGE_ID,
|
pageId = BLOG.NOTION_PAGE_ID,
|
||||||
from,
|
from
|
||||||
pageType = ['Post']
|
|
||||||
}) {
|
}) {
|
||||||
// 获取Notion数据
|
// 获取Notion数据
|
||||||
const notionPageData = deepClone(await getNotionPageData({ pageId, from }))
|
const notionPageData = deepClone(await getNotionPageData({ pageId, from }))
|
||||||
@@ -42,7 +41,9 @@ export async function getGlobalNotionData({
|
|||||||
* @param {*}} param0
|
* @param {*}} param0
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function getLatestPosts({ allPosts, from, latestPostCount }) {
|
function getLatestPosts({ allPages, from, latestPostCount }) {
|
||||||
|
const allPosts = allPages.filter(page => page.type === 'Post')
|
||||||
|
|
||||||
const latestPosts = Object.create(allPosts).sort((a, b) => {
|
const latestPosts = Object.create(allPosts).sort((a, b) => {
|
||||||
const dateA = new Date(a?.lastEditedTime || a?.createdTime || a?.date?.start_date)
|
const dateA = new Date(a?.lastEditedTime || a?.createdTime || a?.date?.start_date)
|
||||||
const dateB = new Date(b?.lastEditedTime || b?.createdTime || b?.date?.start_date)
|
const dateB = new Date(b?.lastEditedTime || b?.createdTime || b?.date?.start_date)
|
||||||
@@ -82,7 +83,7 @@ function getCustomNav({ allPages }) {
|
|||||||
const customNav = []
|
const customNav = []
|
||||||
if (allPages && allPages.length > 0) {
|
if (allPages && allPages.length > 0) {
|
||||||
allPages.forEach(p => {
|
allPages.forEach(p => {
|
||||||
if (p?.status?.[0] === 'Published') {
|
if (p?.status === 'Published' && p?.type === 'Page') {
|
||||||
if (p?.slug?.indexOf('http') === 0) {
|
if (p?.slug?.indexOf('http') === 0) {
|
||||||
customNav.push({ icon: p.icon || null, name: p.title, to: p.slug, show: true })
|
customNav.push({ icon: p.icon || null, name: p.title, to: p.slug, show: true })
|
||||||
} else {
|
} else {
|
||||||
@@ -101,7 +102,7 @@ function getCustomNav({ allPages }) {
|
|||||||
*/
|
*/
|
||||||
function getTagOptions(schema) {
|
function getTagOptions(schema) {
|
||||||
if (!schema) return {}
|
if (!schema) return {}
|
||||||
const tagSchema = Object.values(schema).find(e => e.name === 'tags')
|
const tagSchema = Object.values(schema).find(e => e.name === BLOG.NOTION_PROPERTY_NAME.tags)
|
||||||
return tagSchema?.options || []
|
return tagSchema?.options || []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +113,7 @@ function getTagOptions(schema) {
|
|||||||
*/
|
*/
|
||||||
function getCategoryOptions(schema) {
|
function getCategoryOptions(schema) {
|
||||||
if (!schema) return {}
|
if (!schema) return {}
|
||||||
const categorySchema = Object.values(schema).find(e => e.name === 'category')
|
const categorySchema = Object.values(schema).find(e => e.name === BLOG.NOTION_PROPERTY_NAME.category)
|
||||||
return categorySchema?.options || []
|
return categorySchema?.options || []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +122,8 @@ function getCategoryOptions(schema) {
|
|||||||
* @param allPosts
|
* @param allPosts
|
||||||
* @returns {Promise<{}|*[]>}
|
* @returns {Promise<{}|*[]>}
|
||||||
*/
|
*/
|
||||||
function getAllCategories({ allPosts, categoryOptions, sliceCount = 0 }) {
|
function getAllCategories({ allPages, categoryOptions, sliceCount = 0 }) {
|
||||||
|
const allPosts = allPages.filter(page => page.type === 'Post')
|
||||||
if (!allPosts || !categoryOptions) {
|
if (!allPosts || !categoryOptions) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -220,7 +222,7 @@ async function getPageRecordMapByNotionAPI({ pageId, from }) {
|
|||||||
// Check Type Page-Database和Inline-Database
|
// Check Type Page-Database和Inline-Database
|
||||||
if (
|
if (
|
||||||
rawMetadata?.type !== 'collection_view_page' &&
|
rawMetadata?.type !== 'collection_view_page' &&
|
||||||
rawMetadata?.type !== 'collection_view'
|
rawMetadata?.type !== 'collection_view'
|
||||||
) {
|
) {
|
||||||
console.warn(`pageId "${pageId}" is not a database`)
|
console.warn(`pageId "${pageId}" is not a database`)
|
||||||
return null
|
return null
|
||||||
@@ -251,17 +253,24 @@ async function getPageRecordMapByNotionAPI({ pageId, from }) {
|
|||||||
collectionData.push(properties)
|
collectionData.push(properties)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 读取映射 配置
|
||||||
|
let postCount = 0
|
||||||
|
|
||||||
const allPages = collectionData.filter(post => {
|
const allPages = collectionData.filter(post => {
|
||||||
return post.title && ['Page'].indexOf(post?.type?.[0]) > -1 && (post?.status?.[0] === 'Published' || post?.status?.[0] === 'Invisible')
|
if (post.type === 'Post' && (post.status === 'Published' || post.status === 'Invisible')) {
|
||||||
})
|
postCount++
|
||||||
const allPosts = collectionData.filter(post => {
|
}
|
||||||
return post.title && ['Post'].indexOf(post?.type?.[0]) > -1 && post?.status?.[0] === 'Published'
|
|
||||||
})
|
return post &&
|
||||||
|
post.type &&
|
||||||
|
(post.type === 'Post' || post.type === 'Page') &&
|
||||||
|
(post.status === 'Published' || post.status === 'Invisible')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Sort by date
|
// Sort by date
|
||||||
if (BLOG.POSTS_SORT_BY === 'date') {
|
if (BLOG.POSTS_SORT_BY === 'date') {
|
||||||
allPosts.sort((a, b) => {
|
allPages.sort((a, b) => {
|
||||||
const dateA = new Date(a?.date?.start_date || a.createdTime)
|
const dateA = new Date(a?.date?.start_date || a.createdTime)
|
||||||
const dateB = new Date(b?.date?.start_date || b.createdTime)
|
const dateB = new Date(b?.date?.start_date || b.createdTime)
|
||||||
return dateB - dateA
|
return dateB - dateA
|
||||||
@@ -269,15 +278,13 @@ async function getPageRecordMapByNotionAPI({ pageId, from }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const customNav = getCustomNav({ allPages })
|
const customNav = getCustomNav({ allPages })
|
||||||
const postCount = allPosts?.length || 0
|
const categories = getAllCategories({ allPages, categoryOptions, sliceCount: BLOG.PREVIEW_CATEGORY_COUNT })
|
||||||
const categories = getAllCategories({ allPosts, categoryOptions, sliceCount: BLOG.PREVIEW_CATEGORY_COUNT })
|
const tags = getAllTags({ allPages, tagOptions, sliceCount: BLOG.PREVIEW_TAG_COUNT })
|
||||||
const tags = getAllTags({ allPosts, tagOptions, sliceCount: BLOG.PREVIEW_TAG_COUNT })
|
const latestPosts = getLatestPosts({ allPages, from, latestPostCount: 5 })
|
||||||
const latestPosts = getLatestPosts({ allPosts, from, latestPostCount: 5 })
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
siteInfo,
|
siteInfo,
|
||||||
allPages,
|
allPages,
|
||||||
allPosts,
|
|
||||||
collection,
|
collection,
|
||||||
collectionQuery,
|
collectionQuery,
|
||||||
collectionId,
|
collectionId,
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ async function getPageProperties(id, block, schema, authToken, tagOptions, siteI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置自定义字段
|
// 设置自定义字段
|
||||||
const fieldNames = BLOG.NOTION_PROPERTY_NAME
|
const fieldNames = BLOG.NOTION_PROPERTY_NAME
|
||||||
if (fieldNames) {
|
if (fieldNames) {
|
||||||
@@ -65,7 +66,16 @@ async function getPageProperties(id, block, schema, authToken, tagOptions, siteI
|
|||||||
if (fieldNames[key] && properties[fieldNames[key]]) properties[key] = properties[fieldNames[key]]
|
if (fieldNames[key] && properties[fieldNames[key]]) properties[key] = properties[fieldNames[key]]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
properties.slug = properties.slug ?? properties.id
|
|
||||||
|
properties.type = properties.type[0]
|
||||||
|
properties.status = properties.status[0]
|
||||||
|
|
||||||
|
if (properties.type === 'Post') {
|
||||||
|
properties.slug = BLOG.POST_URL_PREFIX + '/' + (properties.slug ?? properties.id)
|
||||||
|
} else {
|
||||||
|
properties.slug = (properties.slug ?? properties.id)
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ async function getPageWithRetry(id, from, retryAttempts = 3) {
|
|||||||
const authToken = BLOG.NOTION_ACCESS_TOKEN || null
|
const authToken = BLOG.NOTION_ACCESS_TOKEN || null
|
||||||
const api = new NotionAPI({ authToken, userTimeZone: 'Asia/ShangHai' })
|
const api = new NotionAPI({ authToken, userTimeZone: 'Asia/ShangHai' })
|
||||||
const pageData = await api.getPage(id)
|
const pageData = await api.getPage(id)
|
||||||
console.info('[响应成功]:', `from:${from}`, `id:${id}`)
|
console.info('[响应成功]:', `from:${from}`)
|
||||||
return pageData
|
return pageData
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[响应异常]:', e)
|
console.warn('[响应异常]:', e)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export async function generateRss(posts) {
|
|||||||
feed.addItem({
|
feed.addItem({
|
||||||
title: post.title,
|
title: post.title,
|
||||||
guid: `${post.id}`,
|
guid: `${post.id}`,
|
||||||
link: `${BLOG.LINK}/article/${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?.date?.start_date || post?.createdTime)
|
date: new Date(post?.date?.start_date || post?.createdTime)
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ const Slug = props => {
|
|||||||
const meta = {
|
const meta = {
|
||||||
title: `${post?.title} | ${siteInfo?.title}`,
|
title: `${post?.title} | ${siteInfo?.title}`,
|
||||||
description: post?.summary,
|
description: post?.summary,
|
||||||
type: 'article',
|
type: post.type,
|
||||||
slug: 'article/' + post?.slug,
|
slug: post?.slug,
|
||||||
image: post?.page_cover,
|
image: post?.page_cover,
|
||||||
category: post?.category?.[0],
|
category: post?.category?.[0],
|
||||||
tags: post?.tags
|
tags: post?.tags
|
||||||
@@ -95,17 +95,20 @@ export async function getStaticPaths() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getStaticProps({ params: { slug } }) {
|
export async function getStaticProps({ params: { slug } }) {
|
||||||
const from = `slug-props-${slug}`
|
// slug 是个数组
|
||||||
|
const fullSlug = slug.join('/')
|
||||||
|
const from = `slug-props-${fullSlug}`
|
||||||
const props = await getGlobalNotionData({ from, pageType: ['Post'] })
|
const props = await getGlobalNotionData({ from, pageType: ['Post'] })
|
||||||
const allPosts = props.allPosts
|
props.post = props.allPages.find((p) => {
|
||||||
props.post = props.allPosts.find((p) => {
|
return p.slug === fullSlug || p.id === idToUuid(fullSlug)
|
||||||
return p.slug === slug || p.id === idToUuid(slug)
|
|
||||||
})
|
})
|
||||||
if (!props.post) {
|
if (!props.post) {
|
||||||
|
console.warn('无效地址', fullSlug)
|
||||||
return { props, revalidate: 1 }
|
return { props, revalidate: 1 }
|
||||||
}
|
}
|
||||||
props.post.blockMap = await getPostBlocks(props.post.id, 'slug')
|
props.post.blockMap = await getPostBlocks(props.post.id, 'slug')
|
||||||
|
|
||||||
|
const allPosts = props.allPages.filter(page => page.type === 'Post')
|
||||||
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]
|
||||||
@@ -114,6 +117,7 @@ export async function getStaticProps({ params: { slug } }) {
|
|||||||
allPosts,
|
allPosts,
|
||||||
BLOG.POST_RECOMMEND_COUNT
|
BLOG.POST_RECOMMEND_COUNT
|
||||||
)
|
)
|
||||||
|
delete props.allPages
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: 1
|
revalidate: 1
|
||||||
115
pages/[slug].js
115
pages/[slug].js
@@ -1,115 +0,0 @@
|
|||||||
import BLOG from '@/blog.config'
|
|
||||||
import { getPostBlocks } from '@/lib/notion'
|
|
||||||
import { getGlobalNotionData } from '@/lib/notion/getNotionData'
|
|
||||||
import { useGlobal } from '@/lib/global'
|
|
||||||
import * as ThemeMap from '@/themes'
|
|
||||||
import React from 'react'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { isBrowser } from '@/lib/utils'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据notion的slug访问页面,针对类型为Page的页面
|
|
||||||
* @param {*} props
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const Slug = props => {
|
|
||||||
const { theme, changeLoadingState } = useGlobal()
|
|
||||||
const ThemeComponents = ThemeMap[theme]
|
|
||||||
const { post } = props
|
|
||||||
|
|
||||||
if (!post) {
|
|
||||||
changeLoadingState(true)
|
|
||||||
const router = useRouter()
|
|
||||||
setTimeout(() => {
|
|
||||||
if (isBrowser()) {
|
|
||||||
const article = document.getElementById('container')
|
|
||||||
if (!article) {
|
|
||||||
router.push('/404').then(() => {
|
|
||||||
console.warn('找不到页面', router.asPath)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 5000)
|
|
||||||
const meta = { title: `${props?.siteInfo?.title || BLOG.TITLE} | loading` }
|
|
||||||
return <ThemeComponents.LayoutSlug {...props} showArticleInfo={true} meta={meta} />
|
|
||||||
}
|
|
||||||
|
|
||||||
changeLoadingState(false)
|
|
||||||
|
|
||||||
// 文章锁🔐
|
|
||||||
const [lock, setLock] = React.useState(post.password && post.password !== '')
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (post.password && post.password !== '') {
|
|
||||||
setLock(true)
|
|
||||||
} else {
|
|
||||||
setLock(false)
|
|
||||||
}
|
|
||||||
}, [post])
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证文章密码
|
|
||||||
* @param {*} result
|
|
||||||
*/
|
|
||||||
const validPassword = result => {
|
|
||||||
if (result) {
|
|
||||||
setLock(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { siteInfo } = props
|
|
||||||
const meta = {
|
|
||||||
title: `${post?.title} | ${siteInfo?.title}`,
|
|
||||||
description: post?.summary,
|
|
||||||
type: 'article',
|
|
||||||
slug: 'article/' + post?.slug,
|
|
||||||
image: post?.page_cover,
|
|
||||||
category: post?.category?.[0],
|
|
||||||
tags: post?.tags
|
|
||||||
}
|
|
||||||
|
|
||||||
props = { ...props, meta, lock, setLock, validPassword }
|
|
||||||
|
|
||||||
return <ThemeComponents.LayoutSlug {...props} showArticleInfo={false} />
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
|
||||||
if (!BLOG.isProd) {
|
|
||||||
return {
|
|
||||||
paths: [],
|
|
||||||
fallback: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const from = 'slug-paths'
|
|
||||||
const { allPages } = await getGlobalNotionData({ from, pageType: ['Page'] })
|
|
||||||
|
|
||||||
return {
|
|
||||||
paths: allPages.map(row => ({ params: { slug: row.slug } })),
|
|
||||||
fallback: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getStaticProps({ params: { slug } }) {
|
|
||||||
const from = `slug-props-${slug}`
|
|
||||||
const props = await getGlobalNotionData({ from, pageType: ['Page'] })
|
|
||||||
const { allPages } = props
|
|
||||||
const page = allPages?.find(p => p.slug === slug)
|
|
||||||
if (!page) {
|
|
||||||
return { props: {}, revalidate: 1 }
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
page.blockMap = await getPostBlocks(page.id, 'slug')
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取文章详情失败', error)
|
|
||||||
}
|
|
||||||
|
|
||||||
props.post = page
|
|
||||||
|
|
||||||
return {
|
|
||||||
props,
|
|
||||||
revalidate: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Slug
|
|
||||||
@@ -20,7 +20,10 @@ const ArchiveIndex = props => {
|
|||||||
|
|
||||||
export async function getStaticProps() {
|
export async function getStaticProps() {
|
||||||
const props = await getGlobalNotionData({ from: 'archive-index' })
|
const props = await getGlobalNotionData({ from: 'archive-index' })
|
||||||
props.posts = props.allPosts
|
const { allPages } = props
|
||||||
|
const allPosts = allPages.filter(page => page.type === 'Post')
|
||||||
|
// 处理分页
|
||||||
|
props.posts = allPosts
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: 1
|
revalidate: 1
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ export default function Category(props) {
|
|||||||
export async function getStaticProps({ params: { category } }) {
|
export async function getStaticProps({ params: { category } }) {
|
||||||
const from = 'category-props'
|
const from = 'category-props'
|
||||||
let props = await getGlobalNotionData({ from })
|
let props = await getGlobalNotionData({ from })
|
||||||
const posts = props.allPosts.filter(
|
const { allPages } = props
|
||||||
|
const allPosts = allPages.filter(page => page.type === 'Post')
|
||||||
|
const posts = allPosts.filter(
|
||||||
post => post && post.category && post.category.includes(category)
|
post => post && post.category && post.category.includes(category)
|
||||||
)
|
)
|
||||||
props = { ...props, posts, category }
|
props = { ...props, posts, category }
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ const Index = props => {
|
|||||||
|
|
||||||
export async function getStaticProps() {
|
export async function getStaticProps() {
|
||||||
const from = 'index'
|
const from = 'index'
|
||||||
const props = await getGlobalNotionData({ from, pageType: ['Post'] })
|
const props = await getGlobalNotionData({ from })
|
||||||
const { allPosts, siteInfo } = props
|
const { allPages, siteInfo } = props
|
||||||
|
const allPosts = allPages.filter(page => page.type === 'Post')
|
||||||
const meta = {
|
const meta = {
|
||||||
title: `${siteInfo?.title} | ${siteInfo?.description}`,
|
title: `${siteInfo?.title} | ${siteInfo?.description}`,
|
||||||
description: siteInfo?.description,
|
description: siteInfo?.description,
|
||||||
|
|||||||
@@ -38,8 +38,10 @@ export async function getStaticProps({ params: { page } }) {
|
|||||||
const from = `page-${page}`
|
const from = `page-${page}`
|
||||||
const props = await getGlobalNotionData({ from })
|
const props = await getGlobalNotionData({ from })
|
||||||
props.page = page
|
props.page = page
|
||||||
|
const { allPages } = props
|
||||||
|
const allPosts = allPages.filter(page => page.type === 'Post')
|
||||||
// 处理分页
|
// 处理分页
|
||||||
props.posts = props.allPosts.slice(
|
props.posts = allPosts.slice(
|
||||||
BLOG.POSTS_PER_PAGE * (page - 1),
|
BLOG.POSTS_PER_PAGE * (page - 1),
|
||||||
BLOG.POSTS_PER_PAGE * page
|
BLOG.POSTS_PER_PAGE * page
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -35,7 +35,9 @@ export async function getStaticProps({ params: { keyword } }) {
|
|||||||
from: 'search-props',
|
from: 'search-props',
|
||||||
pageType: ['Post']
|
pageType: ['Post']
|
||||||
})
|
})
|
||||||
props.posts = await filterByMemCache(props.allPosts, keyword)
|
const { allPages } = props
|
||||||
|
const allPosts = allPages.filter(page => page.type === 'Post')
|
||||||
|
props.posts = await filterByMemCache(allPosts, keyword)
|
||||||
props.keyword = keyword
|
props.keyword = keyword
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
@@ -107,7 +109,7 @@ async function filterByMemCache(allPosts, keyword) {
|
|||||||
}
|
}
|
||||||
for (const post of allPosts) {
|
for (const post of allPosts) {
|
||||||
const cacheKey = 'page_block_' + post.id
|
const cacheKey = 'page_block_' + post.id
|
||||||
const page = await getDataFromCache(cacheKey)
|
const page = await getDataFromCache(cacheKey, true)
|
||||||
const tagContent = post.tags && Array.isArray(post.tags) ? post.tags.join(' ') : ''
|
const tagContent = post.tags && Array.isArray(post.tags) ? post.tags.join(' ') : ''
|
||||||
const categoryContent = post.category && Array.isArray(post.category) ? post.category.join(' ') : ''
|
const categoryContent = post.category && Array.isArray(post.category) ? post.category.join(' ') : ''
|
||||||
const articleInfo = post.title + post.summary + tagContent + categoryContent
|
const articleInfo = post.title + post.summary + tagContent + categoryContent
|
||||||
@@ -121,7 +123,7 @@ async function filterByMemCache(allPosts, keyword) {
|
|||||||
indexContent = appendText(indexContent, properties, 'caption')
|
indexContent = appendText(indexContent, properties, 'caption')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
console.log('全文搜索缓存', cacheKey, page != null)
|
// console.log('全文搜索缓存', cacheKey, page != null)
|
||||||
post.results = []
|
post.results = []
|
||||||
let hitCount = 0
|
let hitCount = 0
|
||||||
for (const i in indexContent) {
|
for (const i in indexContent) {
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ export async function getStaticProps() {
|
|||||||
from: 'search-props',
|
from: 'search-props',
|
||||||
pageType: ['Post']
|
pageType: ['Post']
|
||||||
})
|
})
|
||||||
props.posts = props.allPosts
|
const { allPages } = props
|
||||||
|
const allPosts = allPages.filter(page => page.type === 'Post')
|
||||||
|
props.posts = allPosts
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: 1
|
revalidate: 1
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ export async function getStaticProps({ params: { tag } }) {
|
|||||||
includePage: false,
|
includePage: false,
|
||||||
tagsCount: 0
|
tagsCount: 0
|
||||||
})
|
})
|
||||||
const { allPosts } = props
|
const { allPages } = props
|
||||||
|
const allPosts = allPages.filter(page => page.type === 'Post')
|
||||||
props.posts = allPosts.filter(
|
props.posts = allPosts.filter(
|
||||||
post => post && post.tags && post.tags.includes(tag)
|
post => post && post.tags && post.tags.includes(tag)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -388,13 +388,11 @@
|
|||||||
|
|
||||||
.notion-h {
|
.notion-h {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: block;
|
||||||
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
padding: 3px 2px;
|
padding: 3px 2px;
|
||||||
margin-bottom: 1px;
|
margin-bottom: 1px;
|
||||||
|
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
@@ -655,6 +653,10 @@ svg.notion-page-icon {
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notion-list-numbered > .notion-list-numbered {
|
||||||
|
list-style-type: lower-alpha;
|
||||||
|
}
|
||||||
|
|
||||||
.notion-list-disc li {
|
.notion-list-disc li {
|
||||||
padding-left: 0.1em;
|
padding-left: 0.1em;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export const LayoutArchive = props => {
|
|||||||
</span>{' '}
|
</span>{' '}
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`${BLOG.SUB_PATH}/article/${post.slug}`}
|
href={`${BLOG.SUB_PATH}/${post.slug}`}
|
||||||
passHref
|
passHref
|
||||||
>
|
>
|
||||||
<a className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
<a className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const LayoutCategory = props => {
|
|||||||
{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">
|
||||||
<Link href={`/article/${p.slug}`}>
|
<Link href={`/${p.slug}`}>
|
||||||
<a className="text-black text-xl md:text-2xl no-underline hover:underline"> {p.title}</a>
|
<a className="text-black text-xl md:text-2xl no-underline hover:underline"> {p.title}</a>
|
||||||
</Link>
|
</Link>
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export const LayoutSearch = props => {
|
|||||||
{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">
|
||||||
<Link href={`/article/${p.slug}`}>
|
<Link href={`/${p.slug}`}>
|
||||||
<a className="text-black text-xl md:text-2xl no-underline hover:underline replace"> {p.title}</a>
|
<a className="text-black text-xl md:text-2xl no-underline hover:underline replace"> {p.title}</a>
|
||||||
</Link>
|
</Link>
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const LayoutTag = props => {
|
|||||||
{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">
|
||||||
<Link href={`/article/${p.slug}`}>
|
<Link href={`/${p.slug}`}>
|
||||||
<a className="text-black text-xl md:text-2xl no-underline hover:underline"> {p.title}</a>
|
<a className="text-black text-xl md:text-2xl no-underline hover:underline"> {p.title}</a>
|
||||||
</Link>
|
</Link>
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export const BlogList = (props) => {
|
|||||||
{posts.map(p => (
|
{posts.map(p => (
|
||||||
<article key={p.id} className="mb-12" >
|
<article key={p.id} className="mb-12" >
|
||||||
<h2 className="mb-4">
|
<h2 className="mb-4">
|
||||||
<Link href={`/article/${p.slug}`}>
|
<Link href={`/${p.slug}`}>
|
||||||
<a className="text-black dark:text-gray-100 text-xl md:text-2xl no-underline hover:underline"> {p.title}</a>
|
<a className="text-black dark:text-gray-100 text-xl md:text-2xl no-underline hover:underline"> {p.title}</a>
|
||||||
</Link>
|
</Link>
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const SideBar = (props) => {
|
|||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<ul className="list-reset leading-normal">
|
<ul className="list-reset leading-normal">
|
||||||
{latestPosts?.map(p => {
|
{latestPosts?.map(p => {
|
||||||
return <Link key={p.id} href={`/article/${p.slug}`} passHref>
|
return <Link key={p.id} href={`/${p.slug}`} passHref>
|
||||||
<li> <a href="#" className="text-gray-darkest text-sm">{p.title}</a></li>
|
<li> <a href="#" className="text-gray-darkest text-sm">{p.title}</a></li>
|
||||||
</Link>
|
</Link>
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ export default function ArticleAround ({ prev, next }) {
|
|||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
return <section className='text-gray-800 h-28 flex items-center justify-between space-x-5 my-4'>
|
return <section className='text-gray-800 h-28 flex items-center justify-between space-x-5 my-4'>
|
||||||
<Link href={`/article/${prev.slug}`} passHref>
|
<Link href={`/${prev.slug}`} passHref>
|
||||||
<a className='text-sm cursor-pointer justify-center items-center flex w-full h-full bg-white bg-opacity-40 hover:bg-hexo-black-gray dark:bg-hexo-black-gray dark:text-gray-200 hover:text-white duration-300'>
|
<a className='text-sm cursor-pointer justify-center items-center flex w-full h-full bg-white bg-opacity-40 hover:bg-hexo-black-gray dark:bg-hexo-black-gray dark:text-gray-200 hover:text-white duration-300'>
|
||||||
<i className='mr-1 fas fa-angle-double-left' />{prev.title}
|
<i className='mr-1 fas fa-angle-double-left' />{prev.title}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href={`/article/${next.slug}`} passHref>
|
<Link href={`/${next.slug}`} passHref>
|
||||||
<a className='text-sm cursor-pointer justify-center items-center flex w-full h-full bg-white bg-opacity-40 hover:bg-hexo-black-gray dark:bg-hexo-black-gray dark:text-gray-200 hover:text-white duration-300'>{next.title}
|
<a className='text-sm cursor-pointer justify-center items-center flex w-full h-full bg-white bg-opacity-40 hover:bg-hexo-black-gray dark:bg-hexo-black-gray dark:text-gray-200 hover:text-white duration-300'>{next.title}
|
||||||
<i className='ml-1 my-1 fas fa-angle-double-right' />
|
<i className='ml-1 my-1 fas fa-angle-double-right' />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const BlogCard = ({ post, showSummary }) => {
|
|||||||
className="animate__animated animate__fadeIn flex flex-col-reverse justify-between duration-300"
|
className="animate__animated animate__fadeIn flex flex-col-reverse justify-between duration-300"
|
||||||
>
|
>
|
||||||
<div className="p-2 flex flex-col w-full">
|
<div className="p-2 flex flex-col w-full">
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref>
|
||||||
<a
|
<a
|
||||||
className={`cursor-pointer font-bold hover:underline text-xl ${showPreview ? 'justify-center' : 'justify-start'
|
className={`cursor-pointer font-bold hover:underline text-xl ${showPreview ? 'justify-center' : 'justify-start'
|
||||||
} 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`}
|
||||||
@@ -30,7 +30,7 @@ const BlogCard = ({ post, showSummary }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{CONFIG_FUKA.POST_LIST_COVER && post?.page_cover && (
|
{CONFIG_FUKA.POST_LIST_COVER && post?.page_cover && (
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref>
|
||||||
<div className="h-40 w-full relative duration-200 cursor-pointer transform overflow-hidden">
|
<div className="h-40 w-full relative duration-200 cursor-pointer transform overflow-hidden">
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const BlogArchiveItem = ({ posts = [], archiveTitle }) => {
|
|||||||
<div id={post?.date?.start_date}>
|
<div id={post?.date?.start_date}>
|
||||||
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
||||||
|
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref>
|
||||||
<a className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
<a className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||||
{post.title}
|
{post.title}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ export default function ArticleAdjacent ({ prev, next }) {
|
|||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
return <section className='text-gray-800 items-center text-xs md:text-sm flex justify-between m-1 '>
|
return <section className='text-gray-800 items-center text-xs md:text-sm flex justify-between m-1 '>
|
||||||
<Link href={`/article/${prev.slug}`} passHref>
|
<Link href={`/${prev.slug}`} passHref>
|
||||||
<a className='py-1 cursor-pointer hover:underline justify-start items-center dark:text-white flex w-full h-full duration-200'>
|
<a className='py-1 cursor-pointer hover:underline justify-start items-center dark:text-white flex w-full h-full duration-200'>
|
||||||
<i className='mr-1 fas fa-angle-left' />{prev.title}
|
<i className='mr-1 fas fa-angle-left' />{prev.title}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href={`/article/${next.slug}`} passHref>
|
<Link href={`/${next.slug}`} passHref>
|
||||||
<a className='py-1 cursor-pointer hover:underline justify-end items-center dark:text-white flex w-full h-full duration-200'>{next.title}
|
<a className='py-1 cursor-pointer hover:underline justify-end items-center dark:text-white flex w-full h-full duration-200'>{next.title}
|
||||||
<i className='ml-1 my-1 fas fa-angle-right' />
|
<i className='ml-1 my-1 fas fa-angle-right' />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default function ArticleRecommend({ recommendPosts, siteInfo }) {
|
|||||||
<Link
|
<Link
|
||||||
key={post.id}
|
key={post.id}
|
||||||
title={post.title}
|
title={post.title}
|
||||||
href={`${BLOG.SUB_PATH}/article/${post.slug}`}
|
href={`${BLOG.SUB_PATH}/${post.slug}`}
|
||||||
passHref
|
passHref
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const BlogPostArchive = ({ posts = [], archiveTitle }) => {
|
|||||||
<div id={post?.date?.start_date}>
|
<div id={post?.date?.start_date}>
|
||||||
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
||||||
|
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref>
|
||||||
<a className="dark:text-gray-400 dark:hover:text-indigo-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
<a className="dark:text-gray-400 dark:hover:text-indigo-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||||
{post.title}
|
{post.title}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
className="animate__animated animate__fadeIn flex flex-col-reverse lg:flex-row justify-between duration-300"
|
className="animate__animated animate__fadeIn flex flex-col-reverse lg:flex-row justify-between duration-300"
|
||||||
>
|
>
|
||||||
<div className="lg:p-8 p-4 flex flex-col w-full">
|
<div className="lg:p-8 p-4 flex flex-col w-full">
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref>
|
||||||
<a
|
<a
|
||||||
className={`replace cursor-pointer hover:underline text-2xl font-sans ${showPreview ? 'text-center' : ''
|
className={`replace cursor-pointer hover:underline text-2xl font-sans ${showPreview ? 'text-center' : ''
|
||||||
} leading-tight text-gray-700 dark:text-gray-100 hover:text-indigo-700 dark:hover:text-indigo-400`}
|
} leading-tight text-gray-700 dark:text-gray-100 hover:text-indigo-700 dark:hover:text-indigo-400`}
|
||||||
@@ -79,7 +79,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{CONFIG_HEXO.POST_LIST_COVER && !showPreview && post?.page_cover && !post.results && (
|
{CONFIG_HEXO.POST_LIST_COVER && !showPreview && post?.page_cover && !post.results && (
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref>
|
||||||
<div className="flex w-full relative duration-200 rounded-t-xl lg:rounded-r-xl lg:rounded-t-none cursor-pointer transform overflow-hidden">
|
<div className="flex w-full relative duration-200 rounded-t-xl lg:rounded-r-xl lg:rounded-t-none cursor-pointer transform overflow-hidden">
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import Typed from 'typed.js'
|
import Typed from 'typed.js'
|
||||||
import CONFIG_HEXO from '../config_hexo'
|
import CONFIG_HEXO from '../config_hexo'
|
||||||
|
import NavButtonGroup from './NavButtonGroup'
|
||||||
|
|
||||||
let wrapperTop = 0
|
let wrapperTop = 0
|
||||||
let windowTop = 0
|
let windowTop = 0
|
||||||
@@ -74,7 +75,7 @@ const Header = props => {
|
|||||||
className="duration-500 md:bg-fixed w-full bg-cover bg-center h-screen bg-black text-white"
|
className="duration-500 md:bg-fixed w-full bg-cover bg-center h-screen bg-black text-white"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
`linear-gradient(rgba(0, 0, 0, 0.8), rgba(0,0,0,0.2), rgba(0,0,0,0.2), rgba(0,0,0,0.2), rgba(0, 0, 0, 0.8) ),url("${siteInfo?.pageCover}")`
|
`linear-gradient(rgba(0, 0, 0, 0.9), rgba(0,0,0,0.5), rgba(0,0,0,0.3), rgba(0,0,0,0.5), rgba(0, 0, 0, 0.9) ),url("${siteInfo?.pageCover}")`
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="absolute flex flex-col h-full items-center justify-center w-full font-sans">
|
<div className="absolute flex flex-col h-full items-center justify-center w-full font-sans">
|
||||||
@@ -82,6 +83,10 @@ const Header = props => {
|
|||||||
<div className='mt-2 h-12 items-center text-center shadow-text text-white text-lg'>
|
<div className='mt-2 h-12 items-center text-center shadow-text text-white text-lg'>
|
||||||
<span id='typed'/>
|
<span id='typed'/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 首页导航插件 */}
|
||||||
|
{ CONFIG_HEXO.HOME_NAV_BUTTONS && <NavButtonGroup {...props}/>}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const LatestPostsGroup = ({ latestPosts, siteInfo }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{latestPosts.map(post => {
|
{latestPosts.map(post => {
|
||||||
const selected = currentPath === `${BLOG.SUB_PATH}/article/${post.slug}`
|
const selected = currentPath === `${BLOG.SUB_PATH}/${post.slug}`
|
||||||
const headerImage = post?.page_cover
|
const headerImage = post?.page_cover
|
||||||
? `url("${post.page_cover}")`
|
? `url("${post.page_cover}")`
|
||||||
: `url("${siteInfo?.pageCover}")`
|
: `url("${siteInfo?.pageCover}")`
|
||||||
@@ -34,7 +34,7 @@ const LatestPostsGroup = ({ latestPosts, siteInfo }) => {
|
|||||||
<Link
|
<Link
|
||||||
key={post.id}
|
key={post.id}
|
||||||
title={post.title}
|
title={post.title}
|
||||||
href={`${BLOG.SUB_PATH}/article/${post.slug}`}
|
href={`${BLOG.SUB_PATH}/${post.slug}`}
|
||||||
passHref
|
passHref
|
||||||
>
|
>
|
||||||
<a className={'my-1 flex font-sans'}>
|
<a className={'my-1 flex font-sans'}>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const Logo = props => {
|
|||||||
const { siteInfo } = props
|
const { siteInfo } = props
|
||||||
return <Link href='/' passHref>
|
return <Link href='/' passHref>
|
||||||
<div className='flex flex-col justify-center items-center cursor-pointer space-y-3'>
|
<div className='flex flex-col justify-center items-center cursor-pointer space-y-3'>
|
||||||
<div className='font-sans text-lg p-1.5 rounded bg-black text-white dark:border-white border-black border'> {siteInfo?.title || BLOG.TITLE}</div>
|
<div className='font-sans text-lg p-1.5 rounded dark:border-white hover:scale-110 transform duration-200'> {siteInfo?.title || BLOG.TITLE}</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ const MenuButtonGroupTop = (props) => {
|
|||||||
|
|
||||||
let links = [
|
let links = [
|
||||||
{ icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: CONFIG_HEXO.MENU_SEARCH },
|
{ icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: CONFIG_HEXO.MENU_SEARCH },
|
||||||
{ icon: 'fas fa-archive', name: locale.COMMON.ARTICLE, to: '/archive', show: CONFIG_HEXO.MENU_ARCHIVE },
|
{ icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', show: CONFIG_HEXO.MENU_ARCHIVE }
|
||||||
{ icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category', show: CONFIG_HEXO.MENU_CATEGORY },
|
// { icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category', show: CONFIG_HEXO.MENU_CATEGORY },
|
||||||
{ icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: CONFIG_HEXO.MENU_TAG }
|
// { icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: CONFIG_HEXO.MENU_TAG }
|
||||||
]
|
]
|
||||||
|
|
||||||
if (customNav) {
|
if (customNav) {
|
||||||
@@ -22,8 +22,8 @@ const MenuButtonGroupTop = (props) => {
|
|||||||
{links.map(link => {
|
{links.map(link => {
|
||||||
if (link.show) {
|
if (link.show) {
|
||||||
return <Link key={`${link.to}`} title={link.to} href={link.to} >
|
return <Link key={`${link.to}`} title={link.to} href={link.to} >
|
||||||
<a target={link.to.indexOf('http') === 0 ? '_blank' : '_self'} className={'py-1.5 my-1 px-3 duration-300 text-base justify-center items-center cursor-pointer'} >
|
<a target={link.to.indexOf('http') === 0 ? '_blank' : '_self'} className={'py-1.5 my-1 px-3 text-base justify-center items-center cursor-pointer'} >
|
||||||
<div className='w-full flex text-sm items-center justify-center hover:scale-105 transform'>
|
<div className='w-full flex text-sm items-center justify-center hover:scale-125 duration-200 transform'>
|
||||||
<i className={`${link.icon} mr-1`}/>
|
<i className={`${link.icon} mr-1`}/>
|
||||||
<div className='text-center'>{link.name}</div>
|
<div className='text-center'>{link.name}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
21
themes/hexo/components/NavButtonGroup.js
Normal file
21
themes/hexo/components/NavButtonGroup.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 首页导航大按钮组件
|
||||||
|
* @param {*} props
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const NavButtonGroup = (props) => {
|
||||||
|
const { categories } = props
|
||||||
|
|
||||||
|
return <nav id='home-nav-button' className={'py-5 md:mt-24 mt-12 flex md:max-w-3xl md:space-x-12 md:flex justify-center'}>
|
||||||
|
{categories.map(category => {
|
||||||
|
return <Link key={`${category.name}`} title={`${category.name}`} href={`/category/${category.name}`} passHref>
|
||||||
|
<a className='text-center w-full md:w-40 md:h-20 h-14 mr-4 mb-4 justify-center items-center flex border-white border-2 cursor-pointer rounded-lg font-serif hover:bg-white hover:text-black duration-200 font-bold hover:scale-110 transform'>{category.name}</a>
|
||||||
|
</Link>
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
}
|
||||||
|
export default NavButtonGroup
|
||||||
@@ -30,6 +30,7 @@ const TopNav = props => {
|
|||||||
const nav = document.querySelector('#sticky-nav')
|
const nav = document.querySelector('#sticky-nav')
|
||||||
const header = document.querySelector('#header')
|
const header = document.querySelector('#header')
|
||||||
const showNav = scrollS <= windowTop || scrollS < 5 || (header && scrollS <= header.clientHeight)// 非首页无大图时影藏顶部 滚动条置顶时隐藏
|
const showNav = scrollS <= windowTop || scrollS < 5 || (header && scrollS <= header.clientHeight)// 非首页无大图时影藏顶部 滚动条置顶时隐藏
|
||||||
|
// 是否将导航栏透明
|
||||||
const navTransparent = (scrollS < document.documentElement.clientHeight - 12 && router.route === '/') || scrollS < 300 // 透明导航条的条件
|
const navTransparent = (scrollS < document.documentElement.clientHeight - 12 && router.route === '/') || scrollS < 300 // 透明导航条的条件
|
||||||
|
|
||||||
if (header && navTransparent) {
|
if (header && navTransparent) {
|
||||||
@@ -37,11 +38,13 @@ const TopNav = props => {
|
|||||||
nav && nav.classList.replace('text-black', 'text-white')
|
nav && nav.classList.replace('text-black', 'text-white')
|
||||||
nav && nav.classList.replace('border', 'border-transparent')
|
nav && nav.classList.replace('border', 'border-transparent')
|
||||||
nav && nav.classList.replace('shadow-md', 'shadow-none')
|
nav && nav.classList.replace('shadow-md', 'shadow-none')
|
||||||
|
nav && nav.classList.replace('dark:bg-hexo-black-gray', 'transparent')
|
||||||
} else {
|
} else {
|
||||||
nav && nav.classList.replace('bg-none', 'bg-white')
|
nav && nav.classList.replace('bg-none', 'bg-white')
|
||||||
nav && nav.classList.replace('text-white', 'text-black')
|
nav && nav.classList.replace('text-white', 'text-black')
|
||||||
nav && nav.classList.replace('border-transparent', 'border')
|
nav && nav.classList.replace('border-transparent', 'border')
|
||||||
nav && nav.classList.replace('shadow-none', 'shadow-md')
|
nav && nav.classList.replace('shadow-none', 'shadow-md')
|
||||||
|
nav && nav.classList.replace('transparent', 'dark:bg-hexo-black-gray')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!showNav) {
|
if (!showNav) {
|
||||||
@@ -118,7 +121,7 @@ const TopNav = props => {
|
|||||||
<SearchDrawer cRef={searchDrawer} slot={searchDrawerSlot}/>
|
<SearchDrawer cRef={searchDrawer} slot={searchDrawerSlot}/>
|
||||||
|
|
||||||
{/* 导航栏 */}
|
{/* 导航栏 */}
|
||||||
<div id='sticky-nav' className={'top-0 shadow-md fixed bg-none animate__animated animate__fadeIn dark:bg-hexo-black-gray dark:text-gray-200 text-black w-full z-20 transform duration-200 font-san border-transparent dark:border-transparent'}>
|
<div id='sticky-nav' className={'top-0 shadow-md fixed bg-none animate__animated animate__fadeIn dark:bg-hexo-black-gray dark:text-gray-200 text-black w-full z-20 transform duration-200 font-san border-transparent dark:border-transparent'}>
|
||||||
<div className='w-full flex justify-between items-center px-4 py-2'>
|
<div className='w-full flex justify-between items-center px-4 py-2'>
|
||||||
<div className='flex'>
|
<div className='flex'>
|
||||||
<Logo {...props}/>
|
<Logo {...props}/>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ const CONFIG_HEXO = {
|
|||||||
HOME_BANNER_ENABLE: true,
|
HOME_BANNER_ENABLE: true,
|
||||||
HOME_BANNER_GREETINGS: ['Hi,我是一个程序员', 'Hi,我是一个打工人', 'Hi,我是一个干饭人', '欢迎来到我的博客🎉'], // 首页大图标语文字
|
HOME_BANNER_GREETINGS: ['Hi,我是一个程序员', 'Hi,我是一个打工人', 'Hi,我是一个干饭人', '欢迎来到我的博客🎉'], // 首页大图标语文字
|
||||||
|
|
||||||
|
HOME_NAV_BUTTONS: true, // 首页是否显示分类大图标按钮
|
||||||
|
|
||||||
// 菜单配置
|
// 菜单配置
|
||||||
MENU_CATEGORY: true, // 显示分类
|
MENU_CATEGORY: true, // 显示分类
|
||||||
MENU_TAG: true, // 显示标签
|
MENU_TAG: true, // 显示标签
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export const LayoutArchive = props => {
|
|||||||
</span>{' '}
|
</span>{' '}
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`${BLOG.SUB_PATH}/article/${post.slug}`}
|
href={`${BLOG.SUB_PATH}/${post.slug}`}
|
||||||
passHref
|
passHref
|
||||||
>
|
>
|
||||||
<a className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
<a className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ export default function ArticleAround ({ prev, next }) {
|
|||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
return <section className='text-gray-800 h-12 flex items-center justify-between space-x-5 my-4'>
|
return <section className='text-gray-800 h-12 flex items-center justify-between space-x-5 my-4'>
|
||||||
<Link href={`/article/${prev.slug}`} passHref>
|
<Link href={`/${prev.slug}`} passHref>
|
||||||
<a className='text-sm cursor-pointer justify-start items-center flex hover:underline duration-300'>
|
<a className='text-sm cursor-pointer justify-start items-center flex hover:underline duration-300'>
|
||||||
<i className='mr-1 fas fa-angle-double-left' />{prev.title}
|
<i className='mr-1 fas fa-angle-double-left' />{prev.title}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href={`/article/${next.slug}`} passHref>
|
<Link href={`/${next.slug}`} passHref>
|
||||||
<a className='text-sm cursor-pointer justify-end items-center flex hover:underline duration-300'>{next.title}
|
<a className='text-sm cursor-pointer justify-end items-center flex hover:underline duration-300'>{next.title}
|
||||||
<i className='ml-1 my-1 fas fa-angle-double-right' />
|
<i className='ml-1 my-1 fas fa-angle-double-right' />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
className="animate__animated animate__fadeIn duration-300 mb-6 max-w-7xl border-b dark:border-gray-800 "
|
className="animate__animated animate__fadeIn duration-300 mb-6 max-w-7xl border-b dark:border-gray-800 "
|
||||||
>
|
>
|
||||||
<div className="lg:p-8 p-4 flex flex-col w-full">
|
<div className="lg:p-8 p-4 flex flex-col w-full">
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref>
|
||||||
<a
|
<a
|
||||||
className={
|
className={
|
||||||
'cursor-pointer font-bold font-sans hover:underline text-3xl leading-tight text-gray-700 dark:text-gray-100 hover:text-green-500 dark:hover:text-green-400'
|
'cursor-pointer font-bold font-sans hover:underline text-3xl leading-tight text-gray-700 dark:text-gray-100 hover:text-green-500 dark:hover:text-green-400'
|
||||||
@@ -54,7 +54,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
<NotionPage post={post} />
|
<NotionPage post={post} />
|
||||||
<div className="pointer-events-none border-t pt-8 border-dashed">
|
<div className="pointer-events-none border-t pt-8 border-dashed">
|
||||||
<div className="w-full justify-start flex">
|
<div className="w-full justify-start flex">
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref>
|
||||||
<a className="hover:bg-opacity-100 hover:scale-105 duration-200 pointer-events-auto transform font-bold text-green-500 cursor-pointer">
|
<a className="hover:bg-opacity-100 hover:scale-105 duration-200 pointer-events-auto transform font-bold text-green-500 cursor-pointer">
|
||||||
{locale.COMMON.ARTICLE_DETAIL}
|
{locale.COMMON.ARTICLE_DETAIL}
|
||||||
<i className="ml-1 fas fa-angle-right" />
|
<i className="ml-1 fas fa-angle-right" />
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ export default function BlogAround ({ prev, next }) {
|
|||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
return <section className='text-gray-800 border-t dark:text-gray-300 flex flex-wrap lg:flex-nowrap lg:space-x-10 justify-between py-2'>
|
return <section className='text-gray-800 border-t dark:text-gray-300 flex flex-wrap lg:flex-nowrap lg:space-x-10 justify-between py-2'>
|
||||||
{prev && <Link href={`/article/${prev.slug}`} passHref>
|
{prev && <Link href={`/${prev.slug}`} passHref>
|
||||||
<a className='text-sm py-3 text-gray-400 hover:underline cursor-pointer'>
|
<a className='text-sm py-3 text-gray-400 hover:underline cursor-pointer'>
|
||||||
<i className='mr-1 fas fa-angle-double-left' />{prev.title}
|
<i className='mr-1 fas fa-angle-double-left' />{prev.title}
|
||||||
</a>
|
</a>
|
||||||
</Link>}
|
</Link>}
|
||||||
{next && <Link href={`/article/${next.slug}`} passHref>
|
{next && <Link href={`/${next.slug}`} passHref>
|
||||||
<a className='text-sm flex py-3 text-gray-400 hover:underline cursor-pointer'>{next.title}
|
<a className='text-sm flex py-3 text-gray-400 hover:underline cursor-pointer'>{next.title}
|
||||||
<i className='ml-1 my-1 fas fa-angle-double-right' />
|
<i className='ml-1 my-1 fas fa-angle-double-right' />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const BlogPostArchive = ({ posts = [], archiveTitle }) => {
|
|||||||
<div id={post?.date?.start_date}>
|
<div id={post?.date?.start_date}>
|
||||||
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
||||||
|
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref>
|
||||||
<a className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
<a className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||||
{post.title}
|
{post.title}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
className="flex flex-col-reverse justify-between duration-300"
|
className="flex flex-col-reverse justify-between duration-300"
|
||||||
>
|
>
|
||||||
<div className="lg:p-8 p-4 flex flex-col w-full">
|
<div className="lg:p-8 p-4 flex flex-col w-full">
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref>
|
||||||
<a
|
<a
|
||||||
className={`cursor-pointer font-bold hover:underline text-3xl ${showPreview ? 'text-center' : ''
|
className={`cursor-pointer font-bold 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`}
|
||||||
@@ -84,7 +84,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="text-right border-t pt-8 border-dashed">
|
<div className="text-right border-t pt-8 border-dashed">
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`}>
|
<Link href={`${BLOG.SUB_PATH}/${post.slug}`}>
|
||||||
<a className="hover:bg-opacity-100 hover:underline transform duration-300 p-3 text-white bg-gray-800 dark:bg-black cursor-pointer">
|
<a className="hover:bg-opacity-100 hover:underline transform duration-300 p-3 text-white bg-gray-800 dark:bg-black cursor-pointer">
|
||||||
{locale.COMMON.ARTICLE_DETAIL}
|
{locale.COMMON.ARTICLE_DETAIL}
|
||||||
<i className="ml-1 fas fa-angle-right" />
|
<i className="ml-1 fas fa-angle-right" />
|
||||||
@@ -94,7 +94,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{CONFIG_NEXT.POST_LIST_COVER && post?.page_cover && (
|
{CONFIG_NEXT.POST_LIST_COVER && post?.page_cover && (
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref>
|
||||||
<div className="h-72 w-full relative duration-200 cursor-pointer transform overflow-hidden">
|
<div className="h-72 w-full relative duration-200 cursor-pointer transform overflow-hidden">
|
||||||
<Image
|
<Image
|
||||||
className="hover:scale-105 transform duration-500"
|
className="hover:scale-105 transform duration-500"
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ const LatestPostsGroup = ({ latestPosts }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{latestPosts.map(post => {
|
{latestPosts.map(post => {
|
||||||
const selected = currentPath === `${BLOG.SUB_PATH}/article/${post.slug}`
|
const selected = currentPath === `${BLOG.SUB_PATH}/${post.slug}`
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
key={post.id}
|
key={post.id}
|
||||||
title={post.title}
|
title={post.title}
|
||||||
href={`${BLOG.SUB_PATH}/article/${post.slug}`}
|
href={`${BLOG.SUB_PATH}/${post.slug}`}
|
||||||
passHref
|
passHref
|
||||||
>
|
>
|
||||||
<a className={'my-1 flex font-light'}>
|
<a className={'my-1 flex font-light'}>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const RecommendPosts = ({ recommendPosts }) => {
|
|||||||
<ul className="font-light text-sm">
|
<ul className="font-light text-sm">
|
||||||
{recommendPosts.map(post => (
|
{recommendPosts.map(post => (
|
||||||
<li className="py-1" key={post.id}>
|
<li className="py-1" key={post.id}>
|
||||||
<Link href={`/article/${post.slug}`}>
|
<Link href={`/${post.slug}`}>
|
||||||
<a className="cursor-pointer hover:underline">
|
<a className="cursor-pointer hover:underline">
|
||||||
{post.title}
|
{post.title}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user