mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-13 23:16:47 +00:00
@@ -1,5 +1,5 @@
|
||||
# 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables
|
||||
NEXT_PUBLIC_VERSION=4.4.6
|
||||
NEXT_PUBLIC_VERSION=4.5.0
|
||||
|
||||
|
||||
# 可在此添加环境变量,去掉最左边的(# )注释即可
|
||||
|
||||
@@ -26,9 +26,12 @@ const Equation = dynamic(
|
||||
{ ssr: false }
|
||||
)
|
||||
|
||||
const Pdf = dynamic(() => import('react-notion-x/build/third-party/pdf').then(m => m.Pdf), {
|
||||
ssr: false
|
||||
})
|
||||
const Pdf = dynamic(
|
||||
() => import('react-notion-x/build/third-party/pdf').then(m => m.Pdf),
|
||||
{
|
||||
ssr: false
|
||||
}
|
||||
)
|
||||
|
||||
// https://github.com/txs
|
||||
// import PrismMac from '@/components/PrismMac'
|
||||
@@ -46,13 +49,25 @@ const TweetEmbed = dynamic(() => import('react-tweet-embed'), {
|
||||
/**
|
||||
* 文内google广告
|
||||
*/
|
||||
const AdEmbed = dynamic(() => import('@/components/GoogleAdsense').then(m => m.AdEmbed), { ssr: true })
|
||||
const AdEmbed = dynamic(
|
||||
() => import('@/components/GoogleAdsense').then(m => m.AdEmbed),
|
||||
{ ssr: true }
|
||||
)
|
||||
|
||||
const Collection = dynamic(() => import('react-notion-x/build/third-party/collection').then(m => m.Collection), {
|
||||
ssr: true
|
||||
})
|
||||
const Collection = dynamic(
|
||||
() =>
|
||||
import('react-notion-x/build/third-party/collection').then(
|
||||
m => m.Collection
|
||||
),
|
||||
{
|
||||
ssr: true
|
||||
}
|
||||
)
|
||||
|
||||
const Modal = dynamic(() => import('react-notion-x/build/third-party/modal').then(m => m.Modal), { ssr: false })
|
||||
const Modal = dynamic(
|
||||
() => import('react-notion-x/build/third-party/modal').then(m => m.Modal),
|
||||
{ ssr: false }
|
||||
)
|
||||
|
||||
const Tweet = ({ id }) => {
|
||||
return <TweetEmbed tweetId={id} />
|
||||
@@ -83,19 +98,22 @@ const NotionPage = ({ post, className }) => {
|
||||
// 将相册gallery下的图片加入放大功能
|
||||
if (siteConfig('POST_DISABLE_GALLERY_CLICK')) {
|
||||
setTimeout(() => {
|
||||
if (isBrowser) {
|
||||
const imgList = document?.querySelectorAll('.notion-collection-card-cover img')
|
||||
if (imgList && zoomRef.current) {
|
||||
for (let i = 0; i < imgList.length; i++) {
|
||||
zoomRef.current.attach(imgList[i])
|
||||
}
|
||||
}
|
||||
const imgList = document?.querySelectorAll(
|
||||
'.notion-asset-wrapper-image img'
|
||||
)
|
||||
|
||||
const cards = document.getElementsByClassName('notion-collection-card')
|
||||
for (const e of cards) {
|
||||
e.removeAttribute('href')
|
||||
console.log('放大', imgList)
|
||||
|
||||
if (imgList && zoomRef.current) {
|
||||
for (let i = 0; i < imgList.length; i++) {
|
||||
zoomRef.current.attach(imgList[i])
|
||||
}
|
||||
}
|
||||
|
||||
const cards = document.getElementsByClassName('notion-collection-card')
|
||||
for (const e of cards) {
|
||||
e.removeAttribute('href')
|
||||
}
|
||||
}, 800)
|
||||
}
|
||||
|
||||
@@ -108,10 +126,16 @@ const NotionPage = ({ post, className }) => {
|
||||
const allAnchorTags = document.getElementsByTagName('a') // 或者使用 document.querySelectorAll('a') 获取 NodeList
|
||||
for (const anchorTag of allAnchorTags) {
|
||||
if (anchorTag?.target === '_blank') {
|
||||
const hrefWithoutQueryHash = anchorTag.href.split('?')[0].split('#')[0]
|
||||
const hrefWithRelativeHash = currentURL.split('#')[0] + anchorTag.href.split('#')[1]
|
||||
const hrefWithoutQueryHash = anchorTag.href
|
||||
.split('?')[0]
|
||||
.split('#')[0]
|
||||
const hrefWithRelativeHash =
|
||||
currentURL.split('#')[0] + anchorTag.href.split('#')[1]
|
||||
|
||||
if (currentURL === hrefWithoutQueryHash || currentURL === hrefWithRelativeHash) {
|
||||
if (
|
||||
currentURL === hrefWithoutQueryHash ||
|
||||
currentURL === hrefWithRelativeHash
|
||||
) {
|
||||
anchorTag.target = '_self'
|
||||
}
|
||||
}
|
||||
@@ -121,14 +145,20 @@ const NotionPage = ({ post, className }) => {
|
||||
// 放大图片:调整图片质量
|
||||
const observer = new MutationObserver((mutationsList, observer) => {
|
||||
mutationsList.forEach(mutation => {
|
||||
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
||||
if (
|
||||
mutation.type === 'attributes' &&
|
||||
mutation.attributeName === 'class'
|
||||
) {
|
||||
if (mutation.target.classList.contains('medium-zoom-image--opened')) {
|
||||
// 等待动画完成后替换为更高清的图像
|
||||
setTimeout(() => {
|
||||
// 获取该元素的 src 属性
|
||||
const src = mutation?.target?.getAttribute('src')
|
||||
// 替换为更高清的图像
|
||||
mutation?.target?.setAttribute('src', compressImage(src, siteConfig('IMAGE_ZOOM_IN_WIDTH', 1200)))
|
||||
mutation?.target?.setAttribute(
|
||||
'src',
|
||||
compressImage(src, siteConfig('IMAGE_ZOOM_IN_WIDTH', 1200))
|
||||
)
|
||||
}, 800)
|
||||
}
|
||||
}
|
||||
@@ -136,21 +166,23 @@ const NotionPage = ({ post, className }) => {
|
||||
})
|
||||
|
||||
// 监视整个文档中的元素和属性的变化
|
||||
observer.observe(document.body, { attributes: true, subtree: true, attributeFilter: ['class'] })
|
||||
observer.observe(document.body, {
|
||||
attributes: true,
|
||||
subtree: true,
|
||||
attributeFilter: ['class']
|
||||
})
|
||||
|
||||
return () => {
|
||||
observer.disconnect()
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (!post || !post.blockMap) {
|
||||
return <>{post?.summary || ''}</>
|
||||
}
|
||||
}, [post])
|
||||
|
||||
return (
|
||||
<div id='notion-article' className={`mx-auto overflow-hidden ${className || ''}`}>
|
||||
<div
|
||||
id='notion-article'
|
||||
className={`mx-auto overflow-hidden ${className || ''}`}>
|
||||
<NotionRenderer
|
||||
recordMap={post.blockMap}
|
||||
recordMap={post?.blockMap}
|
||||
mapPageUrl={mapPageUrl}
|
||||
mapImageUrl={mapImgUrl}
|
||||
components={{
|
||||
|
||||
@@ -156,19 +156,11 @@ function getCustomNav({ allPages }) {
|
||||
if (allPages && allPages.length > 0) {
|
||||
allPages.forEach(p => {
|
||||
p.to = p.slug
|
||||
if (p?.slug?.indexOf('http') === 0) {
|
||||
p.target = '_blank'
|
||||
} else {
|
||||
p.target = '_self'
|
||||
if (p?.slug?.indexOf('/') !== 0) {
|
||||
p.to = '/' + p.slug
|
||||
}
|
||||
}
|
||||
customNav.push({
|
||||
icon: p.icon || null,
|
||||
name: p.title,
|
||||
to: p.slug,
|
||||
target: '_blank',
|
||||
href: p.href,
|
||||
target: p.target,
|
||||
show: true
|
||||
})
|
||||
})
|
||||
@@ -192,15 +184,6 @@ function getCustomMenu({ collectionData, NOTION_CONFIG }) {
|
||||
if (menuPages && menuPages.length > 0) {
|
||||
menuPages.forEach(e => {
|
||||
e.show = true
|
||||
if (e?.slug?.indexOf('http') === 0) {
|
||||
e.target = '_blank'
|
||||
e.to = e.slug
|
||||
} else {
|
||||
e.target = '_self'
|
||||
if (e?.slug?.indexOf('http') !== 0 && e?.slug?.indexOf('/') !== 0) {
|
||||
e.to = '/' + e.slug
|
||||
}
|
||||
}
|
||||
if (e.type === BLOG.NOTION_PROPERTY_NAME.type_menu) {
|
||||
menus.push(e)
|
||||
} else if (e.type === BLOG.NOTION_PROPERTY_NAME.type_sub_menu) {
|
||||
@@ -312,6 +295,7 @@ export function getNavPages({ allPages }) {
|
||||
tags: item.tags || null,
|
||||
summary: item.summary || null,
|
||||
slug: item.slug,
|
||||
href: item.href,
|
||||
pageIcon: item.pageIcon || '',
|
||||
lastEditedDate: item.lastEditedDate,
|
||||
publishDate: item.publishDate,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { getDateValue, getTextContent } from 'notion-utils'
|
||||
import formatDate from '../utils/formatDate'
|
||||
// import { createHash } from 'crypto'
|
||||
import md5 from 'js-md5'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '../utils'
|
||||
import { mapImgUrl } from './mapImage'
|
||||
|
||||
/**
|
||||
@@ -106,7 +107,7 @@ export default async function getPageProperties(
|
||||
properties.pageCover = mapImgUrl(value?.format?.page_cover, value) ?? ''
|
||||
properties.pageCoverThumbnail =
|
||||
mapImgUrl(value?.format?.page_cover, value, 'block') ?? ''
|
||||
properties.ext = converToJSON(properties?.ext)
|
||||
properties.ext = convertToJSON(properties?.ext)
|
||||
properties.content = value.content ?? []
|
||||
properties.tagItems =
|
||||
properties?.tags?.map(tag => {
|
||||
@@ -118,27 +119,43 @@ export default async function getPageProperties(
|
||||
delete properties.content
|
||||
|
||||
// 处理URL
|
||||
// 1.按照用户配置的URL_PREFIX 转换一下slug
|
||||
// 2.为文章添加一个href字段,存储最终调整的路径
|
||||
if (properties.type === 'Post') {
|
||||
properties.slug = BLOG.POST_URL_PREFIX
|
||||
? generateCustomizeUrl(properties)
|
||||
: properties.slug ?? properties.id
|
||||
if (BLOG.POST_URL_PREFIX) {
|
||||
properties.slug = generateCustomizeSlug(properties)
|
||||
}
|
||||
properties.href = properties.slug ?? properties.id
|
||||
} else if (properties.type === 'Page') {
|
||||
properties.slug = properties.slug ?? properties.id
|
||||
properties.href = properties.slug ?? properties.id
|
||||
} else if (properties.type === 'Menu' || properties.type === 'SubMenu') {
|
||||
// 菜单路径为空、作为可展开菜单使用
|
||||
properties.to = properties.slug ?? '#'
|
||||
properties.href = properties.slug ?? '#'
|
||||
properties.name = properties.title ?? ''
|
||||
}
|
||||
|
||||
// 开启伪静态路径
|
||||
if (JSON.parse(BLOG.PSEUDO_STATIC)) {
|
||||
if (
|
||||
!properties?.slug?.endsWith('.html') &&
|
||||
!properties?.slug?.startsWith('http')
|
||||
!properties?.href?.endsWith('.html') &&
|
||||
!properties?.href?.startsWith('http')
|
||||
) {
|
||||
properties.slug += '.html'
|
||||
properties.href += '.html'
|
||||
}
|
||||
}
|
||||
|
||||
// 最终检查超链接
|
||||
properties.href = checkContainHttp(properties?.href)
|
||||
? sliceUrlFromHttp(properties?.href)
|
||||
: `/${properties.href}`
|
||||
|
||||
// 设置链接在页内或新页面打开
|
||||
if (properties.href?.indexOf('http') === 0) {
|
||||
properties.target = '_blank'
|
||||
} else {
|
||||
properties.target = '_self'
|
||||
}
|
||||
|
||||
// 密码字段md5
|
||||
properties.password = properties.password
|
||||
? md5(properties.slug + properties.password)
|
||||
@@ -151,7 +168,7 @@ export default async function getPageProperties(
|
||||
* @param {*} str
|
||||
* @returns
|
||||
*/
|
||||
function converToJSON(str) {
|
||||
function convertToJSON(str) {
|
||||
if (!str) {
|
||||
return {}
|
||||
}
|
||||
@@ -188,11 +205,11 @@ function mapProperties(properties) {
|
||||
/**
|
||||
* 获取自定义URL
|
||||
* 可以根据变量生成URL
|
||||
* 支持:%year%/%month%/%day%/%slug%
|
||||
* 支持:%category%/%year%/%month%/%day%/%slug%
|
||||
* @param {*} postProperties
|
||||
* @returns
|
||||
*/
|
||||
function generateCustomizeUrl(postProperties) {
|
||||
function generateCustomizeSlug(postProperties) {
|
||||
let fullPrefix = ''
|
||||
const allSlugPatterns = BLOG.POST_URL_PREFIX.split('/')
|
||||
allSlugPatterns.forEach((pattern, idx) => {
|
||||
@@ -210,6 +227,8 @@ function generateCustomizeUrl(postProperties) {
|
||||
fullPrefix += String(formatPostCreatedDate.getUTCDate()).padStart(2, 0)
|
||||
} else if (pattern === '%slug%') {
|
||||
fullPrefix += postProperties.slug ?? postProperties.id
|
||||
} else if (pattern === '%category%' && postProperties?.category) {
|
||||
fullPrefix += postProperties.category
|
||||
} else if (!pattern.includes('%')) {
|
||||
fullPrefix += pattern
|
||||
} else {
|
||||
|
||||
90
lib/utils/post.js
Normal file
90
lib/utils/post.js
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 文章相关工具
|
||||
*/
|
||||
import { checkContainHttp } from '.'
|
||||
|
||||
/**
|
||||
* 获取文章的关联推荐文章列表,目前根据标签关联性筛选
|
||||
* @param post
|
||||
* @param {*} allPosts
|
||||
* @param {*} count
|
||||
* @returns
|
||||
*/
|
||||
export function getRecommendPost(post, allPosts, count = 6) {
|
||||
let recommendPosts = []
|
||||
const postIds = []
|
||||
const currentTags = post?.tags || []
|
||||
for (let i = 0; i < allPosts.length; i++) {
|
||||
const p = allPosts[i]
|
||||
if (p.id === post.id || p.type.indexOf('Post') < 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (let j = 0; j < currentTags.length; j++) {
|
||||
const t = currentTags[j]
|
||||
if (postIds.indexOf(p.id) > -1) {
|
||||
continue
|
||||
}
|
||||
if (p.tags && p.tags.indexOf(t) > -1) {
|
||||
recommendPosts.push(p)
|
||||
postIds.push(p.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recommendPosts.length > count) {
|
||||
recommendPosts = recommendPosts.slice(0, count)
|
||||
}
|
||||
return recommendPosts
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认slug中不包含 / 符号
|
||||
* @param {*} row
|
||||
* @returns
|
||||
*/
|
||||
export function checkSlugHasNoSlash(row) {
|
||||
let slug = row.slug
|
||||
if (slug.startsWith('/')) {
|
||||
slug = slug.substring(1)
|
||||
}
|
||||
return (
|
||||
(slug.match(/\//g) || []).length === 0 &&
|
||||
!checkContainHttp(slug) &&
|
||||
row.type.indexOf('Menu') < 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查url中包含一个 /
|
||||
* @param {*} row
|
||||
* @returns
|
||||
*/
|
||||
export function checkSlugHasOneSlash(row) {
|
||||
let slug = row.slug
|
||||
if (slug.startsWith('/')) {
|
||||
slug = slug.substring(1)
|
||||
}
|
||||
return (
|
||||
(slug.match(/\//g) || []).length === 1 &&
|
||||
!checkContainHttp(slug) &&
|
||||
row.type.indexOf('Menu') < 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查url中包含两个及以上的 /
|
||||
* @param {*} row
|
||||
* @returns
|
||||
*/
|
||||
export function checkSlugHasMorThanTwoSlash(row) {
|
||||
let slug = row.slug
|
||||
if (slug.startsWith('/')) {
|
||||
slug = slug.substring(1)
|
||||
}
|
||||
return (
|
||||
(slug.match(/\//g) || []).length >= 2 &&
|
||||
row.type.indexOf('Menu') < 0 &&
|
||||
!checkContainHttp(slug)
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "notion-next",
|
||||
"version": "4.4.6",
|
||||
"version": "4.5.0",
|
||||
"homepage": "https://github.com/tangly1024/NotionNext.git",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -2,9 +2,9 @@ import BLOG from '@/blog.config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { getGlobalData, getPost, getPostBlocks } from '@/lib/db/getSiteData'
|
||||
import { uploadDataToAlgolia } from '@/lib/plugins/algolia'
|
||||
import { checkContainHttp } from '@/lib/utils'
|
||||
import { checkSlugHasMorThanTwoSlash, getRecommendPost } from '@/lib/utils/post'
|
||||
import { idToUuid } from 'notion-utils'
|
||||
import Slug, { getRecommendPost } from '..'
|
||||
import Slug from '..'
|
||||
|
||||
/**
|
||||
* 根据notion的slug访问页面
|
||||
@@ -33,7 +33,7 @@ export async function getStaticPaths() {
|
||||
|
||||
return {
|
||||
paths: allPages
|
||||
?.filter(row => checkSlug(row))
|
||||
?.filter(row => checkSlugHasMorThanTwoSlash(row))
|
||||
.map(row => ({
|
||||
params: {
|
||||
prefix: row.slug.split('/')[0],
|
||||
@@ -54,20 +54,18 @@ export async function getStaticProps({
|
||||
params: { prefix, slug, suffix },
|
||||
locale
|
||||
}) {
|
||||
let fullSlug = prefix + '/' + slug + '/' + suffix.join('/')
|
||||
const fullSlug = prefix + '/' + slug + '/' + suffix.join('/')
|
||||
const from = `slug-props-${fullSlug}`
|
||||
const props = await getGlobalData({ from, locale })
|
||||
if (siteConfig('PSEUDO_STATIC', BLOG.PSEUDO_STATIC, props.NOTION_CONFIG)) {
|
||||
if (!fullSlug.endsWith('.html')) {
|
||||
fullSlug += '.html'
|
||||
}
|
||||
}
|
||||
|
||||
// 在列表内查找文章
|
||||
props.post = props?.allPages?.find(p => {
|
||||
return (
|
||||
p.type.indexOf('Menu') < 0 &&
|
||||
(p.slug === fullSlug || p.id === idToUuid(fullSlug))
|
||||
(p.slug === suffix ||
|
||||
p.slug === fullSlug.substring(fullSlug.lastIndexOf('/') + 1) ||
|
||||
p.slug === fullSlug ||
|
||||
p.id === idToUuid(fullSlug))
|
||||
)
|
||||
})
|
||||
|
||||
@@ -132,16 +130,4 @@ export async function getStaticProps({
|
||||
}
|
||||
}
|
||||
|
||||
function checkSlug(row) {
|
||||
let slug = row.slug
|
||||
if (slug.startsWith('/')) {
|
||||
slug = slug.substring(1)
|
||||
}
|
||||
return (
|
||||
(slug.match(/\//g) || []).length >= 2 &&
|
||||
row.type.indexOf('Menu') < 0 &&
|
||||
!checkContainHttp(slug)
|
||||
)
|
||||
}
|
||||
|
||||
export default PrefixSlug
|
||||
|
||||
@@ -2,9 +2,13 @@ import BLOG from '@/blog.config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { getGlobalData, getPost, getPostBlocks } from '@/lib/db/getSiteData'
|
||||
import { uploadDataToAlgolia } from '@/lib/plugins/algolia'
|
||||
import { checkContainHttp } from '@/lib/utils'
|
||||
import {
|
||||
checkSlugHasNoSlash,
|
||||
checkSlugHasOneSlash,
|
||||
getRecommendPost
|
||||
} from '@/lib/utils/post'
|
||||
import { idToUuid } from 'notion-utils'
|
||||
import Slug, { getRecommendPost } from '..'
|
||||
import Slug from '..'
|
||||
|
||||
/**
|
||||
* 根据notion的slug访问页面
|
||||
@@ -26,12 +30,26 @@ export async function getStaticPaths() {
|
||||
|
||||
const from = 'slug-paths'
|
||||
const { allPages } = await getGlobalData({ from })
|
||||
|
||||
// 根据slug中的 / 分割成prefix和slug两个字段 ; 例如 article/test
|
||||
// 最终用户可以通过 [domain]/[prefix]/[slug] 路径访问,即这里的 [domain]/article/test
|
||||
const paths = allPages
|
||||
?.filter(row => checkSlug(row))
|
||||
?.filter(row => checkSlugHasOneSlash(row))
|
||||
.map(row => ({
|
||||
params: { prefix: row.slug.split('/')[0], slug: row.slug.split('/')[1] }
|
||||
}))
|
||||
|
||||
// 增加一种访问路径 允许通过 [category]/[slug] 访问文章
|
||||
// 例如文章slug 是 test ,然后文章的分类category是 production
|
||||
// 则除了 [domain]/[slug] 以外,还支持分类名访问: [domain]/[category]/[slug]
|
||||
console.log(
|
||||
allPages
|
||||
?.filter(row => checkSlugHasNoSlash(row) && row.category)
|
||||
.map(row => ({
|
||||
params: { prefix: row.category, slug: row.slug }
|
||||
}))
|
||||
)
|
||||
|
||||
return {
|
||||
paths: paths,
|
||||
fallback: true
|
||||
@@ -39,20 +57,15 @@ export async function getStaticPaths() {
|
||||
}
|
||||
|
||||
export async function getStaticProps({ params: { prefix, slug }, locale }) {
|
||||
let fullSlug = prefix + '/' + slug
|
||||
const fullSlug = prefix + '/' + slug
|
||||
const from = `slug-props-${fullSlug}`
|
||||
const props = await getGlobalData({ from, locale })
|
||||
|
||||
if (siteConfig('PSEUDO_STATIC', BLOG.PSEUDO_STATIC, props.NOTION_CONFIG)) {
|
||||
if (!fullSlug.endsWith('.html')) {
|
||||
fullSlug += '.html'
|
||||
}
|
||||
}
|
||||
// 在列表内查找文章
|
||||
props.post = props?.allPages?.find(p => {
|
||||
return (
|
||||
p.type.indexOf('Menu') < 0 &&
|
||||
(p.slug === fullSlug || p.id === idToUuid(fullSlug))
|
||||
(p.slug === slug || p.slug === fullSlug || p.id === idToUuid(fullSlug))
|
||||
)
|
||||
})
|
||||
|
||||
@@ -116,15 +129,5 @@ export async function getStaticProps({ params: { prefix, slug }, locale }) {
|
||||
)
|
||||
}
|
||||
}
|
||||
function checkSlug(row) {
|
||||
let slug = row.slug
|
||||
if (slug.startsWith('/')) {
|
||||
slug = slug.substring(1)
|
||||
}
|
||||
return (
|
||||
(slug.match(/\//g) || []).length === 1 &&
|
||||
!checkContainHttp(slug) &&
|
||||
row.type.indexOf('Menu') < 0
|
||||
)
|
||||
}
|
||||
|
||||
export default PrefixSlug
|
||||
|
||||
@@ -3,7 +3,7 @@ import { siteConfig } from '@/lib/config'
|
||||
import { getGlobalData, getPost, getPostBlocks } from '@/lib/db/getSiteData'
|
||||
import { getPageTableOfContents } from '@/lib/notion/getPageTableOfContents'
|
||||
import { uploadDataToAlgolia } from '@/lib/plugins/algolia'
|
||||
import { checkContainHttp } from '@/lib/utils'
|
||||
import { checkSlugHasNoSlash, getRecommendPost } from '@/lib/utils/post'
|
||||
import { getLayoutByTheme } from '@/themes/theme'
|
||||
import md5 from 'js-md5'
|
||||
import { useRouter } from 'next/router'
|
||||
@@ -71,7 +71,7 @@ export async function getStaticPaths() {
|
||||
const from = 'slug-paths'
|
||||
const { allPages } = await getGlobalData({ from })
|
||||
const paths = allPages
|
||||
?.filter(row => checkSlug(row))
|
||||
?.filter(row => checkSlugHasNoSlash(row))
|
||||
.map(row => ({ params: { prefix: row.slug } }))
|
||||
return {
|
||||
paths: paths,
|
||||
@@ -158,51 +158,4 @@ export async function getStaticProps({ params: { prefix }, locale }) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文章的关联推荐文章列表,目前根据标签关联性筛选
|
||||
* @param post
|
||||
* @param {*} allPosts
|
||||
* @param {*} count
|
||||
* @returns
|
||||
*/
|
||||
export function getRecommendPost(post, allPosts, count = 6) {
|
||||
let recommendPosts = []
|
||||
const postIds = []
|
||||
const currentTags = post?.tags || []
|
||||
for (let i = 0; i < allPosts.length; i++) {
|
||||
const p = allPosts[i]
|
||||
if (p.id === post.id || p.type.indexOf('Post') < 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (let j = 0; j < currentTags.length; j++) {
|
||||
const t = currentTags[j]
|
||||
if (postIds.indexOf(p.id) > -1) {
|
||||
continue
|
||||
}
|
||||
if (p.tags && p.tags.indexOf(t) > -1) {
|
||||
recommendPosts.push(p)
|
||||
postIds.push(p.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recommendPosts.length > count) {
|
||||
recommendPosts = recommendPosts.slice(0, count)
|
||||
}
|
||||
return recommendPosts
|
||||
}
|
||||
|
||||
function checkSlug(row) {
|
||||
let slug = row.slug
|
||||
if (slug.startsWith('/')) {
|
||||
slug = slug.substring(1)
|
||||
}
|
||||
return (
|
||||
(slug.match(/\//g) || []).length === 0 &&
|
||||
!checkContainHttp(slug) &&
|
||||
row.type.indexOf('Menu') < 0
|
||||
)
|
||||
}
|
||||
|
||||
export default Slug
|
||||
|
||||
@@ -62,7 +62,7 @@ const Footer = props => {
|
||||
<Link
|
||||
key={`${menu.name}`}
|
||||
title={`${menu.name}`}
|
||||
href={`${menu.to}`}
|
||||
href={`${menu.href}`}
|
||||
passHref>
|
||||
{menu.name}
|
||||
</Link>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import LogoBar from './LogoBar'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import Collapse from '@/components/Collapse'
|
||||
import { MenuBarMobile } from './MenuBarMobile'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import throttle from 'lodash.throttle'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import CONFIG from '../config'
|
||||
import LogoBar from './LogoBar'
|
||||
import { MenuBarMobile } from './MenuBarMobile'
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
|
||||
/**
|
||||
* 顶部导航栏 + 菜单
|
||||
@@ -21,10 +21,30 @@ export default function Header(props) {
|
||||
const { locale } = useGlobal()
|
||||
|
||||
const defaultLinks = [
|
||||
{ icon: 'fas fa-th', name: locale.COMMON.CATEGORY, to: '/category', show: CONFIG.MENU_CATEGORY },
|
||||
{ icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: CONFIG.MENU_TAG },
|
||||
{ icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', show: CONFIG.MENU_ARCHIVE },
|
||||
{ icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: CONFIG.MENU_SEARCH }
|
||||
{
|
||||
icon: 'fas fa-th',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
href: '/category',
|
||||
show: CONFIG.MENU_CATEGORY
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
href: '/tag',
|
||||
show: CONFIG.MENU_TAG
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: CONFIG.MENU_ARCHIVE
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
href: '/search',
|
||||
show: CONFIG.MENU_SEARCH
|
||||
}
|
||||
]
|
||||
|
||||
let links = defaultLinks.concat(customNav)
|
||||
@@ -65,32 +85,52 @@ export default function Header(props) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <div id='top-navbar-wrapper' className={'sticky top-0 w-full z-40 shadow bg-white dark:bg-hexo-black-gray '}>
|
||||
return (
|
||||
<div
|
||||
id='top-navbar-wrapper'
|
||||
className={
|
||||
'sticky top-0 w-full z-40 shadow bg-white dark:bg-hexo-black-gray '
|
||||
}>
|
||||
{/* 导航栏菜单内容 */}
|
||||
<div
|
||||
id='top-navbar'
|
||||
className='px-4 flex w-full mx-auto max-w-screen-xl h-24 transition-all duration-200 items-between'>
|
||||
{/* 左侧图标Logo */}
|
||||
<LogoBar {...props} />
|
||||
|
||||
{/* 导航栏菜单内容 */}
|
||||
<div id="top-navbar" className='px-4 flex w-full mx-auto max-w-screen-xl h-24 transition-all duration-200 items-between'>
|
||||
|
||||
{/* 左侧图标Logo */}
|
||||
<LogoBar {...props} />
|
||||
|
||||
{/* 移动端折叠按钮 */}
|
||||
<div className='mr-1 flex md:hidden justify-end items-center text-lg space-x-4 font-serif dark:text-gray-200'>
|
||||
<div onClick={toggleMenuOpen} className='cursor-pointer'>
|
||||
{isOpen ? <i className='fas fa-times' /> : <i className='fas fa-bars' />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 桌面端顶部菜单 */}
|
||||
<div className='hidden md:flex items-center'>
|
||||
{links && links?.map(link => <MenuItemDrop key={link?.id} link={link} />)}
|
||||
</div>
|
||||
{/* 移动端折叠按钮 */}
|
||||
<div className='mr-1 flex md:hidden justify-end items-center text-lg space-x-4 font-serif dark:text-gray-200'>
|
||||
<div onClick={toggleMenuOpen} className='cursor-pointer'>
|
||||
{isOpen ? (
|
||||
<i className='fas fa-times' />
|
||||
) : (
|
||||
<i className='fas fa-bars' />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 移动端折叠菜单 */}
|
||||
<Collapse type='vertical' collapseRef={collapseRef} isOpen={isOpen} className='md:hidden'>
|
||||
<div className='bg-white dark:bg-hexo-black-gray pt-1 py-2 lg:hidden '>
|
||||
<MenuBarMobile {...props} onHeightChange={(param) => collapseRef.current?.updateCollapseHeight(param)} />
|
||||
</div>
|
||||
</Collapse>
|
||||
{/* 桌面端顶部菜单 */}
|
||||
<div className='hidden md:flex items-center'>
|
||||
{links &&
|
||||
links?.map(link => <MenuItemDrop key={link?.id} link={link} />)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 移动端折叠菜单 */}
|
||||
<Collapse
|
||||
type='vertical'
|
||||
collapseRef={collapseRef}
|
||||
isOpen={isOpen}
|
||||
className='md:hidden'>
|
||||
<div className='bg-white dark:bg-hexo-black-gray pt-1 py-2 lg:hidden '>
|
||||
<MenuBarMobile
|
||||
{...props}
|
||||
onHeightChange={param =>
|
||||
collapseRef.current?.updateCollapseHeight(param)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
|
||||
export const MenuBarMobile = (props) => {
|
||||
export const MenuBarMobile = props => {
|
||||
const { customMenu, customNav } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
let links = [
|
||||
// { name: locale.NAV.INDEX, to: '/' || '/', show: true },
|
||||
{ name: locale.COMMON.CATEGORY, to: '/category', show: CONFIG.MENU_CATEGORY },
|
||||
{ name: locale.COMMON.TAGS, to: '/tag', show: CONFIG.MENU_TAG },
|
||||
{ name: locale.NAV.ARCHIVE, to: '/archive', show: CONFIG.MENU_ARCHIVE }
|
||||
// { name: locale.NAV.SEARCH, to: '/search', show: CONFIG.MENU_SEARCH }
|
||||
// { name: locale.NAV.INDEX, href: '/' || '/', show: true },
|
||||
{
|
||||
name: locale.COMMON.CATEGORY,
|
||||
href: '/category',
|
||||
show: CONFIG.MENU_CATEGORY
|
||||
},
|
||||
{ name: locale.COMMON.TAGS, href: '/tag', show: CONFIG.MENU_TAG },
|
||||
{ name: locale.NAV.ARCHIVE, href: '/archive', show: CONFIG.MENU_ARCHIVE }
|
||||
// { name: locale.NAV.SEARCH, href: '/search', show: CONFIG.MENU_SEARCH }
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
@@ -30,7 +34,13 @@ export const MenuBarMobile = (props) => {
|
||||
|
||||
return (
|
||||
<nav id='nav' className=' text-md'>
|
||||
{links?.map(link => <MenuItemCollapse onHeightChange={props.onHeightChange} key={link?.id} link={link}/>)}
|
||||
{links?.map(link => (
|
||||
<MenuItemCollapse
|
||||
onHeightChange={props.onHeightChange}
|
||||
key={link?.id}
|
||||
link={link}
|
||||
/>
|
||||
))}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,19 +14,19 @@ const MenuGroupCard = props => {
|
||||
const links = [
|
||||
{
|
||||
name: locale.COMMON.ARTICLE,
|
||||
to: '/archive',
|
||||
href: '/archive',
|
||||
slot: archiveSlot,
|
||||
show: CONFIG.MENU_ARCHIVE
|
||||
},
|
||||
{
|
||||
name: locale.COMMON.CATEGORY,
|
||||
to: '/category',
|
||||
href: '/category',
|
||||
slot: categorySlot,
|
||||
show: CONFIG.MENU_CATEGORY
|
||||
},
|
||||
{
|
||||
name: locale.COMMON.TAGS,
|
||||
to: '/tag',
|
||||
href: '/tag',
|
||||
slot: tagSlot,
|
||||
show: CONFIG.MENU_TAG
|
||||
}
|
||||
@@ -46,9 +46,9 @@ const MenuGroupCard = props => {
|
||||
if (link.show) {
|
||||
return (
|
||||
<Link
|
||||
key={`${link.to}`}
|
||||
title={link.to}
|
||||
href={link.to}
|
||||
key={`${link.slug}`}
|
||||
title={link.name}
|
||||
href={link.href}
|
||||
target={link?.target}
|
||||
className={
|
||||
'py-1.5 my-1 px-2 duration-300 text-base justify-center items-center cursor-pointer'
|
||||
|
||||
@@ -33,7 +33,7 @@ export const MenuItemCollapse = props => {
|
||||
onClick={toggleShow}>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className='hover:text-[#D2232A] font-extralight flex justify-between pl-2 pr-4 dark:text-gray-200 no-underline tracking-widest pb-1'>
|
||||
<span className=' transition-all items-center duration-200'>
|
||||
@@ -64,7 +64,7 @@ export const MenuItemCollapse = props => {
|
||||
<div
|
||||
key={index}
|
||||
className='dark:bg-black dark:text-gray-200 text-left px-10 justify-start bg-gray-50 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 py-3 pr-6'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-sm ml-4 whitespace-nowrap'>
|
||||
{link?.icon && <i className={sLink.icon + ' mr-2'} />}{' '}
|
||||
{sLink.title}
|
||||
|
||||
@@ -2,10 +2,15 @@ import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useState } from 'react'
|
||||
|
||||
/**
|
||||
* 下拉菜单
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
export const MenuItemDrop = ({ link }) => {
|
||||
const [show, changeShow] = useState(false)
|
||||
const hasSubMenu = link?.subMenus?.length > 0
|
||||
const selected = useRouter().asPath === link?.to
|
||||
const selected = useRouter().asPath === link?.href
|
||||
|
||||
if (!link || !link.show) {
|
||||
return null
|
||||
@@ -18,7 +23,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
className='h-full'>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className={`${selected && 'border-b-2 border-[#D2232A]'} h-full flex space-x-1 whitespace-nowrap items-center font-sans menu-link pl-2 pr-4 dark:text-gray-200 no-underline tracking-widest pb-1`}>
|
||||
{link?.icon && <i className={link?.icon} />} <div>{link?.name}</div>
|
||||
@@ -45,7 +50,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
<li
|
||||
key={index}
|
||||
className='cursor-pointer hover:bg-red-300 text-gray-900 hover:text-black tracking-widest transition-all duration-200 dark:border-gray-800 py-1 pr-6 pl-3'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-sm text-nowrap font-extralight'>
|
||||
{link?.icon && <i className={sLink?.icon}> </i>}
|
||||
{sLink.title}
|
||||
|
||||
@@ -1,17 +1,37 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
|
||||
export const MenuListSide = (props) => {
|
||||
export const MenuListSide = props => {
|
||||
const { customNav, customMenu } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
let links = [
|
||||
{ icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', show: CONFIG.MENU_ARCHIVE },
|
||||
{ icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: CONFIG.MENU_SEARCH },
|
||||
{ icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category', show: CONFIG.MENU_CATEGORY },
|
||||
{ icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: CONFIG.MENU_TAG }
|
||||
{
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: CONFIG.MENU_ARCHIVE
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
href: '/search',
|
||||
show: CONFIG.MENU_SEARCH
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-folder',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
href: '/category',
|
||||
show: CONFIG.MENU_CATEGORY
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
href: '/tag',
|
||||
show: CONFIG.MENU_TAG
|
||||
}
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
@@ -34,9 +54,11 @@ export const MenuListSide = (props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<nav>
|
||||
{/* {links.map(link => <MenuItemNormal key={link?.id} link={link} />)} */}
|
||||
{links?.map(link => <MenuItemCollapse key={link?.id} link={link} />)}
|
||||
</nav>
|
||||
<nav>
|
||||
{/* {links.map(link => <MenuItemNormal key={link?.id} link={link} />)} */}
|
||||
{links?.map(link => (
|
||||
<MenuItemCollapse key={link?.id} link={link} />
|
||||
))}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,36 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
|
||||
export const MenuListTop = (props) => {
|
||||
export const MenuListTop = props => {
|
||||
const { customNav, customMenu } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
let links = [
|
||||
{ id: 1, icon: 'fa-solid fa-house', name: locale.NAV.INDEX, to: '/', show: CONFIG.MENU_INDEX },
|
||||
{ id: 2, icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: CONFIG.MENU_SEARCH },
|
||||
{ id: 3, icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', show: CONFIG.MENU_ARCHIVE }
|
||||
// { icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category', show: CONFIG.MENU_CATEGORY },
|
||||
// { icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: CONFIG.MENU_TAG }
|
||||
{
|
||||
id: 1,
|
||||
icon: 'fa-solid fa-house',
|
||||
name: locale.NAV.INDEX,
|
||||
href: '/',
|
||||
show: CONFIG.MENU_INDEX
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
href: '/search',
|
||||
show: CONFIG.MENU_SEARCH
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: CONFIG.MENU_ARCHIVE
|
||||
}
|
||||
// { icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, href: '/category', show: CONFIG.MENU_CATEGORY },
|
||||
// { icon: 'fas fa-tag', name: locale.COMMON.TAGS, href: '/tag', show: CONFIG.MENU_TAG }
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
@@ -34,9 +52,16 @@ export const MenuListTop = (props) => {
|
||||
return null
|
||||
}
|
||||
|
||||
return (<>
|
||||
<nav id='nav-mobile' className='leading-8 justify-center font-light w-full flex'>
|
||||
{links?.map(link => link && link.show && <MenuItemDrop key={link?.id} link={link} />)}
|
||||
</nav>
|
||||
</>)
|
||||
return (
|
||||
<>
|
||||
<nav
|
||||
id='nav-mobile'
|
||||
className='leading-8 justify-center font-light w-full flex'>
|
||||
{links?.map(
|
||||
link =>
|
||||
link && link.show && <MenuItemDrop key={link?.id} link={link} />
|
||||
)}
|
||||
</nav>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import LazyImage from '@/components/LazyImage'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
|
||||
@@ -15,9 +14,6 @@ const BlogItem = ({ post }) => {
|
||||
const showPageCover =
|
||||
siteConfig('EXAMPLE_POST_LIST_COVER', null, CONFIG) &&
|
||||
post?.pageCoverThumbnail
|
||||
const url = checkContainHttp(post.slug)
|
||||
? sliceUrlFromHttp(post.slug)
|
||||
: `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return (
|
||||
<article
|
||||
@@ -25,7 +21,7 @@ const BlogItem = ({ post }) => {
|
||||
<div className={`${showPageCover ? 'md:w-7/12' : ''}`}>
|
||||
<h2 className='mb-4'>
|
||||
<Link
|
||||
href={`/${post.slug}`}
|
||||
href={post?.href}
|
||||
className='text-black dark:text-gray-100 text-xl md:text-2xl no-underline hover:underline'>
|
||||
{siteConfig('POST_TITLE_ICON') && (
|
||||
<NotionIcon icon={post.pageIcon} />
|
||||
@@ -72,7 +68,7 @@ const BlogItem = ({ post }) => {
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && (
|
||||
<div className='md:w-5/12 w-full h-44 overflow-hidden p-1'>
|
||||
<Link href={url} passHref legacyBehavior>
|
||||
<Link href={post?.href} passHref legacyBehavior>
|
||||
<LazyImage
|
||||
src={post?.pageCoverThumbnail}
|
||||
className='w-full bg-cover hover:scale-110 duration-200'
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
|
||||
/**
|
||||
@@ -17,10 +15,6 @@ export default function BlogListArchive({ archiveTitle, archivePosts }) {
|
||||
|
||||
<ul>
|
||||
{archivePosts[archiveTitle].map(post => {
|
||||
const url = checkContainHttp(post.slug)
|
||||
? sliceUrlFromHttp(post.slug)
|
||||
: `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return (
|
||||
<li
|
||||
key={post.id}
|
||||
@@ -28,7 +22,7 @@ export default function BlogListArchive({ archiveTitle, archivePosts }) {
|
||||
<div id={post?.publishDay}>
|
||||
<span className='text-gray-400'>{post?.publishDay}</span>
|
||||
<Link
|
||||
href={url}
|
||||
href={post?.href}
|
||||
className='dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600'>
|
||||
{post.title}
|
||||
</Link>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { MenuList } from './MenuList'
|
||||
*/
|
||||
export const Header = props => {
|
||||
return (
|
||||
<header className='w-full px-6 bg-white dark:bg-black relative z-10'>
|
||||
<header className='w-full px-6 bg-white dark:bg-black relative z-20'>
|
||||
<div className='container mx-auto max-w-4xl md:flex justify-between items-center'>
|
||||
<Link
|
||||
href='/'
|
||||
|
||||
@@ -17,7 +17,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
onMouseOut={() => changeShow(false)}>
|
||||
{!hasSubMenu && (
|
||||
<div className='rounded px-2 md:pl-0 md:mr-3 my-4 md:pr-3 text-gray-700 dark:text-gray-200 no-underline md:border-r border-gray-light'>
|
||||
<Link href={link?.to} target={link?.target}>
|
||||
<Link href={link?.href} target={link?.target}>
|
||||
{link?.icon && <i className={link?.icon} />} {link?.name}
|
||||
{hasSubMenu && <i className='px-2 fa fa-angle-down'></i>}
|
||||
</Link>
|
||||
@@ -41,7 +41,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
<li
|
||||
key={index}
|
||||
className='not:last-child:border-b-0 border-b text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 py-3 pr-6 pl-3'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-sm text-nowrap font-extralight'>
|
||||
{link?.icon && <i className={sLink?.icon}> </i>}
|
||||
{sLink.title}
|
||||
|
||||
@@ -17,28 +17,28 @@ export const MenuList = props => {
|
||||
id: 1,
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
to: '/search',
|
||||
href: '/search',
|
||||
show: siteConfig('EXAMPLE_MENU_SEARCH', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
to: '/archive',
|
||||
href: '/archive',
|
||||
show: siteConfig('EXAMPLE_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: 'fas fa-folder',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
to: '/category',
|
||||
href: '/category',
|
||||
show: siteConfig('EXAMPLE_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
to: '/tag',
|
||||
href: '/tag',
|
||||
show: siteConfig('EXAMPLE_MENU_TAG', null, CONFIG)
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
import TagItemMini from './TagItemMini'
|
||||
@@ -41,10 +40,6 @@ const BlogCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
}
|
||||
: {}
|
||||
|
||||
const url = checkContainHttp(post.slug)
|
||||
? sliceUrlFromHttp(post.slug)
|
||||
: `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return (
|
||||
<article
|
||||
{...aosProps}
|
||||
@@ -53,7 +48,7 @@ const BlogCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
<div className='flex flex-col justify-between h-full'>
|
||||
{/* 封面图 */}
|
||||
{showPageCover && (
|
||||
<Link href={url} passHref legacyBehavior>
|
||||
<Link href={post?.href} passHref legacyBehavior>
|
||||
<div className='flex-grow mb-3 w-full duration-200 cursor-pointer transform overflow-hidden'>
|
||||
<LazyImage
|
||||
src={post?.pageCoverThumbnail}
|
||||
@@ -69,9 +64,12 @@ const BlogCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
<h2>
|
||||
<Link
|
||||
passHref
|
||||
href={url}
|
||||
href={post?.href}
|
||||
className={`break-words 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`}>
|
||||
{siteConfig('POST_TITLE_ICON') && <NotionIcon icon={post.pageIcon} />} {post.title}
|
||||
{siteConfig('POST_TITLE_ICON') && (
|
||||
<NotionIcon icon={post.pageIcon} />
|
||||
)}{' '}
|
||||
{post.title}
|
||||
</Link>
|
||||
</h2>
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import Link from 'next/link'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* 博客归档
|
||||
@@ -16,31 +14,28 @@ const BlogArchiveItem = ({ posts = [], archiveTitle }) => {
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className="pt-16 pb-4 text-3xl dark:text-gray-300"
|
||||
id={archiveTitle}
|
||||
>
|
||||
className='pt-16 pb-4 text-3xl dark:text-gray-300'
|
||||
id={archiveTitle}>
|
||||
{archiveTitle}
|
||||
</div>
|
||||
<ul>
|
||||
{posts?.map(post => {
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
return <li
|
||||
key={post.id}
|
||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
||||
>
|
||||
<div id={post?.publishDay}>
|
||||
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
||||
|
||||
<Link
|
||||
href={url}
|
||||
passHref
|
||||
className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||
|
||||
{post.title}
|
||||
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
return (
|
||||
<li
|
||||
key={post.id}
|
||||
className='border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500'>
|
||||
<div id={post?.publishDay}>
|
||||
<span className='text-gray-400'>{post.date?.start_date}</span>{' '}
|
||||
|
||||
<Link
|
||||
href={post?.href}
|
||||
passHref
|
||||
className='dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600'>
|
||||
{post.title}
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,7 @@ export const MenuItemCollapse = props => {
|
||||
return null
|
||||
}
|
||||
|
||||
const selected = router.pathname === link.to || router.asPath === link.to
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
|
||||
const toggleShow = () => {
|
||||
changeShow(!show)
|
||||
@@ -43,7 +43,7 @@ export const MenuItemCollapse = props => {
|
||||
onClick={toggleShow}>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className='dark:text-gray-200 py-2 w-full my-auto items-center justify-between flex '>
|
||||
<div>
|
||||
@@ -79,7 +79,7 @@ export const MenuItemCollapse = props => {
|
||||
className='whitespace-nowrap dark:text-gray-200
|
||||
not:last-child:border-b-0 border-b dark:border-gray-800 py-2 px-14 cursor-pointer hover:bg-gray-100
|
||||
font-extralight dark:bg-black text-left justify-start text-gray-600 bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<div>
|
||||
<div
|
||||
className={`${sLink.icon} text-center w-3 mr-3 text-xs`}
|
||||
|
||||
@@ -12,7 +12,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
className='relative py-1 duration-500 justify-between text-gray-500 dark:text-gray-300 hover:text-black hover:underline cursor-pointer flex flex-nowrap items-center '>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className='w-full my-auto items-center justify-between flex '>
|
||||
<div>
|
||||
@@ -47,7 +47,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
return (
|
||||
<li key={index}>
|
||||
<Link
|
||||
href={sLink.to}
|
||||
href={sLink.href}
|
||||
target={link?.target}
|
||||
className='my-auto py-1 px-2 items-center justify-start flex text-gray-500 dark:text-gray-300 hover:text-black hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 '>
|
||||
{sLink.icon && (
|
||||
|
||||
@@ -4,21 +4,23 @@ import { useRouter } from 'next/router'
|
||||
export const MenuItemNormal = props => {
|
||||
const { link } = props
|
||||
const router = useRouter()
|
||||
const selected = (router.pathname === link.to) || (router.asPath === link.to)
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
if (!link || !link.show) {
|
||||
return null
|
||||
}
|
||||
return <Link
|
||||
key={`${link.to}`}
|
||||
title={link.to}
|
||||
href={link.to}
|
||||
className={'py-0.5 duration-500 justify-between text-gray-500 dark:text-gray-300 hover:text-black hover:underline cursor-pointer flex flex-nowrap items-center ' +
|
||||
(selected ? 'text-black' : ' ')}>
|
||||
|
||||
<div className='my-auto items-center justify-center flex '>
|
||||
<div className={'hover:text-black'}>{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={`${link.href}`}
|
||||
title={link.href}
|
||||
href={link.href}
|
||||
className={
|
||||
'py-0.5 duration-500 justify-between text-gray-500 dark:text-gray-300 hover:text-black hover:underline cursor-pointer flex flex-nowrap items-center ' +
|
||||
(selected ? 'text-black' : ' ')
|
||||
}>
|
||||
<div className='my-auto items-center justify-center flex '>
|
||||
<div className={'hover:text-black'}>{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,39 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
|
||||
export const MenuList = (props) => {
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
/**
|
||||
* 菜单列表
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
export const MenuList = props => {
|
||||
const { customNav, customMenu } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
let links = [
|
||||
{ name: locale.NAV.INDEX, to: '/' || '/', show: true },
|
||||
{ name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('FUKASAWA_MENU_CATEGORY', null, CONFIG) },
|
||||
{ name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('FUKASAWA_MENU_TAG', null, CONFIG) },
|
||||
{ name: locale.NAV.ARCHIVE, to: '/archive', show: siteConfig('FUKASAWA_MENU_ARCHIVE', null, CONFIG) },
|
||||
{ name: locale.NAV.SEARCH, to: '/search', show: siteConfig('FUKASAWA_MENU_SEARCH', null, CONFIG) }
|
||||
{ name: locale.NAV.INDEX, href: '/' || '/', show: true },
|
||||
{
|
||||
name: locale.COMMON.CATEGORY,
|
||||
href: '/category',
|
||||
show: siteConfig('FUKASAWA_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.COMMON.TAGS,
|
||||
href: '/tag',
|
||||
show: siteConfig('FUKASAWA_MENU_TAG', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: siteConfig('FUKASAWA_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.NAV.SEARCH,
|
||||
href: '/search',
|
||||
show: siteConfig('FUKASAWA_MENU_SEARCH', null, CONFIG)
|
||||
}
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
@@ -29,14 +49,22 @@ export const MenuList = (props) => {
|
||||
return null
|
||||
}
|
||||
|
||||
return (<>
|
||||
<menu id='nav-pc' className='hidden md:block text-sm z-10'>
|
||||
{links?.map((link, index) => <MenuItemDrop key={index} link={link} />)}
|
||||
</menu>
|
||||
<menu id='nav-mobile' className='block md:hidden text-sm z-10 pb-1'>
|
||||
{links?.map((link, index) => <MenuItemCollapse key={index} link={link} onHeightChange={props.onHeightChange}/>)}
|
||||
</menu>
|
||||
return (
|
||||
<>
|
||||
<menu id='nav-pc' className='hidden md:block text-sm z-10'>
|
||||
{links?.map((link, index) => (
|
||||
<MenuItemDrop key={index} link={link} />
|
||||
))}
|
||||
</menu>
|
||||
<menu id='nav-mobile' className='block md:hidden text-sm z-10 pb-1'>
|
||||
{links?.map((link, index) => (
|
||||
<MenuItemCollapse
|
||||
key={index}
|
||||
link={link}
|
||||
onHeightChange={props.onHeightChange}
|
||||
/>
|
||||
))}
|
||||
</menu>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import Link from 'next/link'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* 归档分组文章
|
||||
@@ -9,36 +7,31 @@ import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
*/
|
||||
export default function BlogArchiveItem({ archiveTitle, archivePosts }) {
|
||||
return (
|
||||
<div key={archiveTitle}>
|
||||
<div id={archiveTitle} className="pt-16 pb-4 text-3xl dark:text-gray-300" >
|
||||
{archiveTitle}
|
||||
</div>
|
||||
<div key={archiveTitle}>
|
||||
<div id={archiveTitle} className='pt-16 pb-4 text-3xl dark:text-gray-300'>
|
||||
{archiveTitle}
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
{archivePosts[archiveTitle].map(post => {
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return <li
|
||||
key={post.id}
|
||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
||||
>
|
||||
<div id={post?.publishDay}>
|
||||
<span className="text-gray-400">
|
||||
{post.date?.start_date}
|
||||
</span>{' '}
|
||||
|
||||
<Link
|
||||
href={url}
|
||||
passHref
|
||||
className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||
|
||||
{post.title}
|
||||
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<ul>
|
||||
{archivePosts[archiveTitle].map(post => {
|
||||
return (
|
||||
<li
|
||||
key={post.id}
|
||||
className='border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500'>
|
||||
<div id={post?.publishDay}>
|
||||
<span className='text-gray-400'>{post.date?.start_date}</span>{' '}
|
||||
|
||||
<Link
|
||||
href={post?.href}
|
||||
passHref
|
||||
className='dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600'>
|
||||
{post.title}
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
import Link from 'next/link'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import Link from 'next/link'
|
||||
|
||||
const BlogPost = ({ post }) => {
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
const showPreview = siteConfig('POST_LIST_PREVIEW') && post.blockMap
|
||||
|
||||
return (
|
||||
(<Link href={url}>
|
||||
|
||||
<article key={post.id} className="mb-6 md:mb-8">
|
||||
<header className="flex flex-col justify-between md:flex-row md:items-baseline">
|
||||
<h2 className="text-lg md:text-xl font-medium mb-2 cursor-pointer text-black dark:text-gray-100">
|
||||
{siteConfig('POST_TITLE_ICON') && <NotionIcon icon={post.pageIcon} />}{post.title}
|
||||
<Link href={post?.href}>
|
||||
<article key={post.id} className='mb-6 md:mb-8'>
|
||||
<header className='flex flex-col justify-between md:flex-row md:items-baseline'>
|
||||
<h2 className='text-lg md:text-xl font-medium mb-2 cursor-pointer text-black dark:text-gray-100'>
|
||||
{siteConfig('POST_TITLE_ICON') && (
|
||||
<NotionIcon icon={post.pageIcon} />
|
||||
)}
|
||||
{post.title}
|
||||
</h2>
|
||||
<time className="flex-shrink-0 text-gray-600 dark:text-gray-400">
|
||||
<time className='flex-shrink-0 text-gray-600 dark:text-gray-400'>
|
||||
{post?.publishDay}
|
||||
</time>
|
||||
</header>
|
||||
<main>
|
||||
{!showPreview && <p className="hidden md:block leading-8 text-gray-700 dark:text-gray-300">
|
||||
{post.summary}
|
||||
</p>}
|
||||
{!showPreview && (
|
||||
<p className='hidden md:block leading-8 text-gray-700 dark:text-gray-300'>
|
||||
{post.summary}
|
||||
</p>
|
||||
)}
|
||||
{showPreview && post?.blockMap && (
|
||||
<div className="overflow-ellipsis truncate">
|
||||
<div className='overflow-ellipsis truncate'>
|
||||
<NotionPage post={post} />
|
||||
<hr className='border-dashed py-4'/>
|
||||
<hr className='border-dashed py-4' />
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
</article>
|
||||
|
||||
</Link>)
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { AdSlot } from '@/components/GoogleAdsense'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, deepClone, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import { deepClone } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
import CONFIG from '../config'
|
||||
@@ -139,14 +139,12 @@ const GameItem = ({ item, isLargeCard }) => {
|
||||
const { title } = item
|
||||
const img = item.pageCoverThumbnail
|
||||
const [showType, setShowType] = useState('img') // img or video
|
||||
const url = checkContainHttp(item.slug)
|
||||
? sliceUrlFromHttp(item.slug)
|
||||
: `${siteConfig('SUB_PATH', '')}/${item.slug}`
|
||||
|
||||
const video = item?.ext?.video
|
||||
return (
|
||||
<Link
|
||||
title={title}
|
||||
href={`${url}`}
|
||||
href={`${item?.href}`}
|
||||
className={`card-single ${isLargeCard ? 'h-80 ' : 'h-full text-xs'} w-full transition-all duration-200 shadow-md md:hover:scale-105 md:hover:shadow-lg relative rounded-lg overflow-hidden flex justify-center items-center
|
||||
group hover:border-purple-400`}
|
||||
onMouseOver={() => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, deepClone, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import { deepClone } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
|
||||
@@ -43,12 +42,11 @@ const GameItem = ({ item }) => {
|
||||
const { title } = item
|
||||
const img = item.pageCoverThumbnail
|
||||
const [showType, setShowType] = useState('img') // img or video
|
||||
const url = checkContainHttp(item.slug) ? sliceUrlFromHttp(item.slug) : `${siteConfig('SUB_PATH', '')}/${item.slug}`
|
||||
const video = item?.ext?.video
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={`${url}`}
|
||||
href={`${item?.href}`}
|
||||
onMouseOver={() => {
|
||||
setShowType('video')
|
||||
}}
|
||||
@@ -58,17 +56,27 @@ const GameItem = ({ item }) => {
|
||||
title={title}
|
||||
className={`card-single h-28 w-28 relative shadow rounded-md overflow-hidden flex justify-center items-center
|
||||
group hover:border-purple-400`}>
|
||||
<div className='absolute text-sm bottom-2 transition-all duration-200 text-white z-30'>{title}</div>
|
||||
<div className='absolute text-sm bottom-2 transition-all duration-200 text-white z-30'>
|
||||
{title}
|
||||
</div>
|
||||
<div className='h-1/2 w-full absolute left-0 bottom-0 z-20 opacity-75 transition-all duration-200'>
|
||||
<div className='h-full w-full absolute bg-gradient-to-b from-transparent to-black'></div>
|
||||
</div>
|
||||
|
||||
{showType === 'video' && (
|
||||
<video className='z-10 object-cover w-auto h-28 absolute overflow-hidden' loop='true' autoPlay preload='none'>
|
||||
<video
|
||||
className='z-10 object-cover w-auto h-28 absolute overflow-hidden'
|
||||
loop='true'
|
||||
autoPlay
|
||||
preload='none'>
|
||||
<source src={video} type='video/mp4' />
|
||||
</video>
|
||||
)}
|
||||
<img className='w-full h-full absolute object-cover' src={img} alt={title} />
|
||||
<img
|
||||
className='w-full h-full absolute object-cover'
|
||||
src={img}
|
||||
alt={title}
|
||||
/>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, deepClone, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import { deepClone } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
|
||||
@@ -43,16 +42,12 @@ export const GameListRelate = ({ posts }) => {
|
||||
const GameItem = ({ item }) => {
|
||||
const { title } = item
|
||||
const [showType, setShowType] = useState('img') // img or video
|
||||
const url = checkContainHttp(item.slug)
|
||||
? sliceUrlFromHttp(item.slug)
|
||||
: `${siteConfig('SUB_PATH', '')}/${item.slug}`
|
||||
|
||||
const img = item?.pageCoverThumbnail
|
||||
const video = item?.ext?.video
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={`${url}`}
|
||||
href={`${item?.href}`}
|
||||
onMouseOver={() => {
|
||||
setShowType('video')
|
||||
}}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, deepClone, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import { deepClone } from '@/lib/utils'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useState } from 'react'
|
||||
import { useGameGlobal } from '..'
|
||||
@@ -53,10 +52,6 @@ const GameItem = ({ item }) => {
|
||||
const { recentGames, setRecentGames } = useGameGlobal()
|
||||
const { title } = item || {}
|
||||
const [showType, setShowType] = useState('img') // img or video
|
||||
const url = checkContainHttp(item.slug)
|
||||
? sliceUrlFromHttp(item.slug)
|
||||
: `${siteConfig('SUB_PATH', '')}/${item.slug}`
|
||||
|
||||
const [isClockVisible, setClockVisible] = useState(true)
|
||||
const toggleIcons = () => {
|
||||
setClockVisible(!isClockVisible)
|
||||
@@ -77,7 +72,7 @@ const GameItem = ({ item }) => {
|
||||
}
|
||||
|
||||
const handleButtonClick = () => {
|
||||
router.push(url) // 如果是 Next.js
|
||||
router.push(item?.href) // 如果是 Next.js
|
||||
}
|
||||
|
||||
const img = item?.pageCoverThumbnail
|
||||
|
||||
@@ -33,7 +33,7 @@ export const MenuItemCollapse = props => {
|
||||
onClick={toggleShow}>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className='font-extralight flex justify-between pl-2 pr-4 dark:text-gray-200 no-underline tracking-widest pb-1'>
|
||||
<span className=' hover:text-red-400 transition-all items-center duration-200'>
|
||||
@@ -71,7 +71,7 @@ export const MenuItemCollapse = props => {
|
||||
<div
|
||||
key={index}
|
||||
className='font-extralight dark:bg-black text-left px-10 justify-start bg-gray-50 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 border-b dark:border-gray-800 py-3 pr-6'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-xs'>{sLink.title}</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
{!hasSubMenu && (
|
||||
<div className='dark:text-gray-50 nav hover:scale-105 transition-transform duration-200'>
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
className='flex flex-nowrap'
|
||||
target={link?.target}>
|
||||
<div className='w-6 mr-2 text-center'>
|
||||
@@ -47,7 +47,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
<div
|
||||
key={index}
|
||||
className='text-gray-700 dark:text-gray-200 tracking-widest transition-all duration-200 '>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-sm text-nowrap font-extralight'>
|
||||
{link?.icon && <i className={sLink?.icon}> </i>}
|
||||
{sLink.title}
|
||||
|
||||
@@ -17,21 +17,21 @@ export const MenuList = props => {
|
||||
id: 1,
|
||||
icon: 'fas fa-home',
|
||||
name: locale.NAV.INDEX,
|
||||
to: '/' || '/',
|
||||
href: '/' || '/',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: 'fas fa-th',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
to: '/category',
|
||||
href: '/category',
|
||||
show: siteConfig('GAME_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
to: '/tag',
|
||||
href: '/tag',
|
||||
show: siteConfig('GAME_MENU_TAG', null, CONFIG)
|
||||
}
|
||||
]
|
||||
|
||||
@@ -5,27 +5,25 @@ import Link from 'next/link'
|
||||
* @param {prev,next} param0
|
||||
* @returns
|
||||
*/
|
||||
export default function ArticleAround ({ prev, next }) {
|
||||
export default function ArticleAround({ prev, next }) {
|
||||
if (!prev || !next) {
|
||||
return <></>
|
||||
}
|
||||
return (
|
||||
<section className='text-gray-800 dark:text-gray-400 h-12 flex items-center justify-between space-x-5 my-4'>
|
||||
<Link
|
||||
href={`/${prev.slug}`}
|
||||
href={prev.href}
|
||||
passHref
|
||||
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}
|
||||
</Link>
|
||||
<Link
|
||||
href={`/${next.slug}`}
|
||||
href={next.href}
|
||||
passHref
|
||||
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' />
|
||||
|
||||
</Link>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import Link from 'next/link'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* 归档分组
|
||||
@@ -9,31 +7,30 @@ import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
*/
|
||||
export default function BlogArchiveItem({ archiveTitle, archivePosts }) {
|
||||
return (
|
||||
<div key={archiveTitle}>
|
||||
<div id={archiveTitle} className="pt-16 pb-4 text-3xl dark:text-gray-300" >
|
||||
{archiveTitle}
|
||||
</div>
|
||||
<ul>
|
||||
{archivePosts[archiveTitle]?.map(post => {
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return <li key={post.id}
|
||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
||||
>
|
||||
<div id={post?.publishDay}>
|
||||
<span className="text-gray-400">
|
||||
{post.date?.start_date}
|
||||
</span>{' '}
|
||||
|
||||
|
||||
<Link passHref href={url}
|
||||
className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||
{post.title}
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<div key={archiveTitle}>
|
||||
<div id={archiveTitle} className='pt-16 pb-4 text-3xl dark:text-gray-300'>
|
||||
{archiveTitle}
|
||||
</div>
|
||||
<ul>
|
||||
{archivePosts[archiveTitle]?.map(post => {
|
||||
return (
|
||||
<li
|
||||
key={post.id}
|
||||
className='border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500'>
|
||||
<div id={post?.publishDay}>
|
||||
<span className='text-gray-400'>{post.date?.start_date}</span>{' '}
|
||||
|
||||
<Link
|
||||
passHref
|
||||
href={post?.href}
|
||||
className='dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600'>
|
||||
{post.title}
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
import Badge from '@/components/Badge'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import CONFIG from '../config'
|
||||
|
||||
const BlogPostCard = ({ post, className }) => {
|
||||
const router = useRouter()
|
||||
const currentSelected = router.asPath.split('?')[0] === '/' + post.slug
|
||||
const url = checkContainHttp(post.slug)
|
||||
? sliceUrlFromHttp(post.slug)
|
||||
: `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
const currentSelected = router.asPath.split('?')[0] === post?.href
|
||||
return (
|
||||
<Link href={url} passHref>
|
||||
<Link href={post?.href} passHref>
|
||||
<div
|
||||
key={post.id}
|
||||
className={`${className} relative py-1.5 cursor-pointer px-1.5 hover:bg-gray-50 rounded-md dark:hover:bg-yellow-100 dark:hover:text-yellow-600
|
||||
|
||||
@@ -24,25 +24,25 @@ export default function Header(props) {
|
||||
{
|
||||
icon: 'fas fa-th',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
to: '/category',
|
||||
href: '/category',
|
||||
show: siteConfig('GITBOOK_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
to: '/tag',
|
||||
href: '/tag',
|
||||
show: siteConfig('GITBOOK_BOOK_MENU_TAG', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
to: '/archive',
|
||||
href: '/archive',
|
||||
show: siteConfig('GITBOOK_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
to: '/search',
|
||||
href: '/search',
|
||||
show: siteConfig('GITBOOK_MENU_SEARCH', null, CONFIG)
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
|
||||
export const MenuBarMobile = (props) => {
|
||||
export const MenuBarMobile = props => {
|
||||
const { customMenu, customNav } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
let links = [
|
||||
// { name: locale.NAV.INDEX, to: '/' || '/', show: true },
|
||||
{ name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('GITBOOK_MENU_CATEGORY', null, CONFIG) },
|
||||
{ name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('GITBOOK_BOOK_MENU_TAG', null, CONFIG) },
|
||||
{ name: locale.NAV.ARCHIVE, to: '/archive', show: siteConfig('GITBOOK_MENU_ARCHIVE', null, CONFIG) }
|
||||
// { name: locale.NAV.SEARCH, to: '/search', show: siteConfig('MENU_SEARCH', null, CONFIG) }
|
||||
// { name: locale.NAV.INDEX, href: '/' || '/', show: true },
|
||||
{
|
||||
name: locale.COMMON.CATEGORY,
|
||||
href: '/category',
|
||||
show: siteConfig('GITBOOK_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.COMMON.TAGS,
|
||||
href: '/tag',
|
||||
show: siteConfig('GITBOOK_BOOK_MENU_TAG', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: siteConfig('GITBOOK_MENU_ARCHIVE', null, CONFIG)
|
||||
}
|
||||
// { name: locale.NAV.SEARCH, href: '/search', show: siteConfig('MENU_SEARCH', null, CONFIG) }
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
@@ -30,7 +42,13 @@ export const MenuBarMobile = (props) => {
|
||||
|
||||
return (
|
||||
<nav id='nav' className=' text-md'>
|
||||
{links?.map((link, index) => <MenuItemCollapse onHeightChange={props.onHeightChange} key={index} link={link}/>)}
|
||||
{links?.map((link, index) => (
|
||||
<MenuItemCollapse
|
||||
onHeightChange={props.onHeightChange}
|
||||
key={index}
|
||||
link={link}
|
||||
/>
|
||||
))}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export const MenuItemCollapse = props => {
|
||||
return null
|
||||
}
|
||||
|
||||
const selected = router.pathname === link.to || router.asPath === link.to
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
|
||||
const toggleShow = () => {
|
||||
changeShow(!show)
|
||||
@@ -43,7 +43,7 @@ export const MenuItemCollapse = props => {
|
||||
onClick={toggleShow}>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className='py-2 w-full my-auto items-center justify-between flex '>
|
||||
<div>
|
||||
@@ -79,7 +79,7 @@ export const MenuItemCollapse = props => {
|
||||
className='
|
||||
not:last-child:border-b-0 border-b dark:border-gray-800 py-2 px-14 cursor-pointer hover:bg-gray-100 dark:text-gray-200
|
||||
font-extralight dark:bg-black text-left justify-start text-gray-600 bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<div>
|
||||
<div
|
||||
className={`${sLink.icon} text-center w-3 mr-3 text-xs`}
|
||||
|
||||
@@ -10,7 +10,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
return null
|
||||
}
|
||||
const hasSubMenu = link?.subMenus?.length > 0
|
||||
const selected = router.pathname === link.to || router.asPath === link.to
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
return (
|
||||
<li
|
||||
className='cursor-pointer list-none items-center flex mx-2'
|
||||
@@ -42,7 +42,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
? 'bg-green-600 text-white hover:text-white'
|
||||
: 'hover:text-green-600')
|
||||
}>
|
||||
<Link href={link?.to} target={link?.target}>
|
||||
<Link href={link?.href} target={link?.target}>
|
||||
{link?.icon && <i className={link?.icon} />} {link?.name}
|
||||
</Link>
|
||||
</div>
|
||||
@@ -57,7 +57,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
<li
|
||||
key={index}
|
||||
className='not:last-child:border-b-0 border-b text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 py-3 pr-6 pl-3'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-xs font-extralight'>
|
||||
{link?.icon && <i className={sLink?.icon}> </i>}
|
||||
{sLink.title}
|
||||
|
||||
@@ -9,19 +9,21 @@ export const NormalMenu = props => {
|
||||
return null
|
||||
}
|
||||
|
||||
const selected = (router.pathname === link.to) || (router.asPath === link.to)
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
|
||||
return <Link
|
||||
key={`${link.to}`}
|
||||
title={link.to}
|
||||
href={link.to}
|
||||
className={'py-0.5 duration-500 justify-between text-gray-500 dark:text-gray-300 hover:text-black hover:underline cursor-pointer flex flex-nowrap items-center ' +
|
||||
(selected ? 'text-black' : ' ')}>
|
||||
|
||||
<div className='my-auto items-center justify-center flex '>
|
||||
<div className={ 'hover:text-black'}>{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
|
||||
</Link>
|
||||
return (
|
||||
<Link
|
||||
key={link?.href}
|
||||
title={link.name}
|
||||
href={link.href}
|
||||
className={
|
||||
'py-0.5 duration-500 justify-between text-gray-500 dark:text-gray-300 hover:text-black hover:underline cursor-pointer flex flex-nowrap items-center ' +
|
||||
(selected ? 'text-black' : ' ')
|
||||
}>
|
||||
<div className='my-auto items-center justify-center flex '>
|
||||
<div className={'hover:text-black'}>{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,21 +4,27 @@ import { useRouter } from 'next/router'
|
||||
export const MenuItemPCNormal = props => {
|
||||
const { link } = props
|
||||
const router = useRouter()
|
||||
const selected = (router.pathname === link.to) || (router.asPath === link.to)
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
if (!link || !link.show) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <Link
|
||||
key={`${link.id}-${link.to}`}
|
||||
title={link.to}
|
||||
href={link.to}
|
||||
className={'px-2 duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
|
||||
(selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600')}>
|
||||
<div className='items-center justify-center flex '>
|
||||
<i className={link.icon} />
|
||||
<div className='ml-2 whitespace-nowrap'>{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
return (
|
||||
<Link
|
||||
key={`${link.id}-${link.slug}`}
|
||||
title={link.name}
|
||||
href={link.href}
|
||||
className={
|
||||
'px-2 duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
|
||||
(selected
|
||||
? 'bg-green-600 text-white hover:text-white'
|
||||
: 'hover:text-green-600')
|
||||
}>
|
||||
<div className='items-center justify-center flex '>
|
||||
<i className={link.icon} />
|
||||
<div className='ml-2 whitespace-nowrap'>{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ function getNavPagesWithLatest(allNavPages, latestPosts, post) {
|
||||
tags: item.tags || null,
|
||||
summary: item.summary || null,
|
||||
slug: item.slug,
|
||||
href: item.href,
|
||||
pageIcon: item.pageIcon || '',
|
||||
lastEditedDate: item.lastEditedDate
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
|
||||
/**
|
||||
* 关联推荐文章
|
||||
@@ -15,53 +14,52 @@ export default function ArticleRecommend({ recommendPosts, siteInfo }) {
|
||||
|
||||
if (
|
||||
!siteConfig('HEO_ARTICLE_RECOMMEND', null, CONFIG) ||
|
||||
!recommendPosts ||
|
||||
recommendPosts.length === 0
|
||||
!recommendPosts ||
|
||||
recommendPosts.length === 0
|
||||
) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="pt-8 hidden md:block">
|
||||
|
||||
{/* 推荐文章 */}
|
||||
<div className=" mb-2 px-1 flex flex-nowrap justify-between">
|
||||
<div className='dark:text-gray-300 text-lg font-bold'>
|
||||
<i className="mr-2 fas fa-thumbs-up" />
|
||||
{locale.COMMON.RELATE_POSTS}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 文章列表 */}
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
{recommendPosts.map(post => {
|
||||
const headerImage = post?.pageCoverThumbnail
|
||||
? post.pageCoverThumbnail
|
||||
: siteInfo?.pageCover
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return (
|
||||
(<Link
|
||||
key={post.id}
|
||||
title={post.title}
|
||||
href={url}
|
||||
passHref
|
||||
className="flex h-40 cursor-pointer overflow-hidden rounded-2xl">
|
||||
|
||||
<div className="h-full w-full relative group">
|
||||
<div className="flex items-center justify-center w-full h-full duration-300 ">
|
||||
<div className="z-10 text-lg px-4 font-bold text-white text-center shadow-text select-none">
|
||||
{post.title}
|
||||
</div>
|
||||
</div>
|
||||
<LazyImage src={headerImage} className='absolute top-0 w-full h-full object-cover object-center group-hover:scale-110 group-hover:brightness-50 transform duration-200'/>
|
||||
</div>
|
||||
|
||||
</Link>)
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className='pt-8 hidden md:block'>
|
||||
{/* 推荐文章 */}
|
||||
<div className=' mb-2 px-1 flex flex-nowrap justify-between'>
|
||||
<div className='dark:text-gray-300 text-lg font-bold'>
|
||||
<i className='mr-2 fas fa-thumbs-up' />
|
||||
{locale.COMMON.RELATE_POSTS}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 文章列表 */}
|
||||
|
||||
<div className='grid grid-cols-2 md:grid-cols-3 gap-4'>
|
||||
{recommendPosts.map(post => {
|
||||
const headerImage = post?.pageCoverThumbnail
|
||||
? post?.pageCoverThumbnail
|
||||
: siteInfo?.pageCover
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={post?.id}
|
||||
title={post?.title}
|
||||
href={post?.href}
|
||||
passHref
|
||||
className='flex h-40 cursor-pointer overflow-hidden rounded-2xl'>
|
||||
<div className='h-full w-full relative group'>
|
||||
<div className='flex items-center justify-center w-full h-full duration-300 '>
|
||||
<div className='z-10 text-lg px-4 font-bold text-white text-center shadow-text select-none'>
|
||||
{post.title}
|
||||
</div>
|
||||
</div>
|
||||
<LazyImage
|
||||
src={headerImage}
|
||||
className='absolute top-0 w-full h-full object-cover object-center group-hover:scale-110 group-hover:brightness-50 transform duration-200'
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
import TagItemMini from './TagItemMini'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* 博客归档列表
|
||||
@@ -17,72 +16,89 @@ const BlogPostArchive = ({ posts = [], archiveTitle, siteInfo }) => {
|
||||
return <></>
|
||||
} else {
|
||||
return (
|
||||
<div className=''>
|
||||
<div
|
||||
className="pb-4 dark:text-gray-300"
|
||||
id={archiveTitle}
|
||||
>
|
||||
{archiveTitle}
|
||||
</div>
|
||||
<ul>
|
||||
{posts?.map(post => {
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
<div className=''>
|
||||
<div className='pb-4 dark:text-gray-300' id={archiveTitle}>
|
||||
{archiveTitle}
|
||||
</div>
|
||||
<ul>
|
||||
{posts?.map(post => {
|
||||
const showPreview =
|
||||
siteConfig('HEO_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
if (
|
||||
post &&
|
||||
!post.pageCoverThumbnail &&
|
||||
siteConfig('HEO_POST_LIST_COVER_DEFAULT', null, CONFIG)
|
||||
) {
|
||||
post.pageCoverThumbnail = siteInfo?.pageCover
|
||||
}
|
||||
const showPageCover =
|
||||
siteConfig('HEO_POST_LIST_COVER', null, CONFIG) &&
|
||||
post?.pageCoverThumbnail &&
|
||||
!showPreview
|
||||
return (
|
||||
<div
|
||||
key={post.id}
|
||||
className={
|
||||
'cursor-pointer flex flex-row mb-4 h-24 md:flex-row group w-full dark:border-gray-600 hover:border-indigo-600 dark:hover:border-yellow-600 duration-300 transition-colors justify-between overflow-hidden'
|
||||
}>
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && (
|
||||
<div>
|
||||
<Link href={post?.href} passHref legacyBehavior>
|
||||
<LazyImage
|
||||
className={'rounded-xl bg-center bg-cover w-40 h-24'}
|
||||
src={post?.pageCoverThumbnail}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
const showPreview = siteConfig('HEO_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
if (post && !post.pageCoverThumbnail && siteConfig('HEO_POST_LIST_COVER_DEFAULT', null, CONFIG)) {
|
||||
post.pageCoverThumbnail = siteInfo?.pageCover
|
||||
}
|
||||
const showPageCover = siteConfig('HEO_POST_LIST_COVER', null, CONFIG) && post?.pageCoverThumbnail && !showPreview
|
||||
return <div key={post.id} className={'cursor-pointer flex flex-row mb-4 h-24 md:flex-row group w-full dark:border-gray-600 hover:border-indigo-600 dark:hover:border-yellow-600 duration-300 transition-colors justify-between overflow-hidden'}>
|
||||
{/* 文字区块 */}
|
||||
<div className={'flex px-2 flex-col justify-between w-full'}>
|
||||
<div>
|
||||
{/* 分类 */}
|
||||
{post?.category && (
|
||||
<div
|
||||
className={`flex items-center ${showPreview ? 'justify-center' : 'justify-start'} hidden md:block flex-wrap dark:text-gray-500 text-gray-600 `}>
|
||||
<Link
|
||||
passHref
|
||||
href={`/category/${post.category}`}
|
||||
className='cursor-pointer text-xs font-normal menu-link hover:text-indigo-700 dark:text-gray-600 transform'>
|
||||
{post.category}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && (
|
||||
<div>
|
||||
<Link href={url} passHref legacyBehavior>
|
||||
<LazyImage className={'rounded-xl bg-center bg-cover w-40 h-24'} src={post?.pageCoverThumbnail}/>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
{/* 标题 */}
|
||||
<Link
|
||||
href={post?.href}
|
||||
passHref
|
||||
className={
|
||||
' group-hover:text-indigo-700 group-hover:dark:text-indigo-400 text-black dark:text-gray-100 dark:group-hover:text-yellow-600 line-clamp-2 replace cursor-pointer text-xl font-extrabold leading-tight'
|
||||
}>
|
||||
<span className='menu-link '>{post.title}</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* 文字区块 */}
|
||||
<div className={'flex px-2 flex-col justify-between w-full'}>
|
||||
<div>
|
||||
{/* 分类 */}
|
||||
{post?.category && <div className={`flex items-center ${showPreview ? 'justify-center' : 'justify-start'} hidden md:block flex-wrap dark:text-gray-500 text-gray-600 `}>
|
||||
<Link passHref href={`/category/${post.category}`}
|
||||
className="cursor-pointer text-xs font-normal menu-link hover:text-indigo-700 dark:text-gray-600 transform">
|
||||
{post.category}
|
||||
</Link>
|
||||
</div>}
|
||||
|
||||
{/* 标题 */}
|
||||
<Link
|
||||
href={url}
|
||||
passHref
|
||||
className={' group-hover:text-indigo-700 group-hover:dark:text-indigo-400 text-black dark:text-gray-100 dark:group-hover:text-yellow-600 line-clamp-2 replace cursor-pointer text-xl font-extrabold leading-tight'}>
|
||||
<span className='menu-link '>{post.title}</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* 摘要 */}
|
||||
{/* <p className="line-clamp-1 replace my-3 2xl:my-0 text-gray-700 dark:text-gray-300 text-xs font-light leading-tight">
|
||||
{/* 摘要 */}
|
||||
{/* <p className="line-clamp-1 replace my-3 2xl:my-0 text-gray-700 dark:text-gray-300 text-xs font-light leading-tight">
|
||||
{post.summary}
|
||||
</p> */}
|
||||
|
||||
<div className="md:flex-nowrap flex-wrap md:justify-start inline-block">
|
||||
<div>
|
||||
{' '}
|
||||
{post.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<div className='md:flex-nowrap flex-wrap md:justify-start inline-block'>
|
||||
<div>
|
||||
{' '}
|
||||
{post.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +1,97 @@
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
import TagItemMini from './TagItemMini'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
|
||||
const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
const showPreview = siteConfig('HEO_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
if (post && !post.pageCoverThumbnail && siteConfig('HEO_POST_LIST_COVER_DEFAULT', null, CONFIG)) {
|
||||
const showPreview =
|
||||
siteConfig('HEO_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
if (
|
||||
post &&
|
||||
!post.pageCoverThumbnail &&
|
||||
siteConfig('HEO_POST_LIST_COVER_DEFAULT', null, CONFIG)
|
||||
) {
|
||||
post.pageCoverThumbnail = siteInfo?.pageCover
|
||||
}
|
||||
const showPageCover = siteConfig('HEO_POST_LIST_COVER', null, CONFIG) && post?.pageCoverThumbnail && !showPreview
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
const showPageCover =
|
||||
siteConfig('HEO_POST_LIST_COVER', null, CONFIG) &&
|
||||
post?.pageCoverThumbnail &&
|
||||
!showPreview
|
||||
return (
|
||||
<article className={` ${siteConfig('HEO_POST_LIST_COVER_HOVER_ENLARGE', null, CONFIG) ? ' hover:scale-110 transition-all duration-150' : ''}`} >
|
||||
|
||||
<div data-wow-delay=".2s"
|
||||
className={'wow fadeInUp border bg-white dark:bg-[#1e1e1e] flex mb-4 flex-col h-[23rem] md:h-52 md:flex-row 2xl:h-96 2xl:flex-col group w-full dark:border-gray-600 hover:border-indigo-600 dark:hover:border-yellow-600 duration-300 transition-colors justify-between overflow-hidden rounded-xl'}>
|
||||
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && (
|
||||
<Link href={url} passHref legacyBehavior>
|
||||
<div className="w-full md:w-5/12 2xl:w-full overflow-hidden">
|
||||
<LazyImage priority={index === 0} src={post?.pageCoverThumbnail} alt={post?.title} className='h-60 w-full object-cover group-hover:scale-105 group-hover:brightness-75 transition-all duration-300' />
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{/* 文字区块 */}
|
||||
<div className={'flex p-6 2xl:p-4 flex-col justify-between h-48 md:h-full 2xl:h-48 w-full md:w-7/12 2xl:w-full'}>
|
||||
<header>
|
||||
{/* 分类 */}
|
||||
{post?.category && <div className={`flex mb-1 items-center ${showPreview ? 'justify-center' : 'justify-start'} hidden md:block flex-wrap dark:text-gray-500 text-gray-600 `}>
|
||||
<Link passHref href={`/category/${post.category}`}
|
||||
className="cursor-pointer text-xs font-normal menu-link hover:text-indigo-700 dark:hover:text-yellow-700 dark:text-gray-600 transform">
|
||||
{post.category}
|
||||
</Link>
|
||||
</div>}
|
||||
|
||||
{/* 标题 */}
|
||||
<Link
|
||||
href={url}
|
||||
passHref
|
||||
className={' group-hover:text-indigo-700 dark:hover:text-yellow-700 dark:group-hover:text-yellow-600 text-black dark:text-gray-100 line-clamp-2 replace cursor-pointer text-xl font-extrabold leading-tight'}>
|
||||
{siteConfig('POST_TITLE_ICON') && <NotionIcon icon={post.pageIcon} />}<span className='menu-link '>{post.title}</span>
|
||||
</Link>
|
||||
</header>
|
||||
|
||||
{/* 摘要 */}
|
||||
{(!showPreview || showSummary) && (
|
||||
<main className="line-clamp-2 replace my-3 2xl:my-1 text-gray-700 dark:text-gray-300 text-sm font-light leading-tight">
|
||||
{post.summary}
|
||||
</main>
|
||||
)}
|
||||
|
||||
<div className="md:flex-nowrap flex-wrap md:justify-start inline-block">
|
||||
<div>
|
||||
{' '}
|
||||
{post.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<article
|
||||
className={` ${siteConfig('HEO_POST_LIST_COVER_HOVER_ENLARGE', null, CONFIG) ? ' hover:scale-110 transition-all duration-150' : ''}`}>
|
||||
<div
|
||||
data-wow-delay='.2s'
|
||||
className={
|
||||
'wow fadeInUp border bg-white dark:bg-[#1e1e1e] flex mb-4 flex-col h-[23rem] md:h-52 md:flex-row 2xl:h-96 2xl:flex-col group w-full dark:border-gray-600 hover:border-indigo-600 dark:hover:border-yellow-600 duration-300 transition-colors justify-between overflow-hidden rounded-xl'
|
||||
}>
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && (
|
||||
<Link href={post?.href} passHref legacyBehavior>
|
||||
<div className='w-full md:w-5/12 2xl:w-full overflow-hidden'>
|
||||
<LazyImage
|
||||
priority={index === 0}
|
||||
src={post?.pageCoverThumbnail}
|
||||
alt={post?.title}
|
||||
className='h-60 w-full object-cover group-hover:scale-105 group-hover:brightness-75 transition-all duration-300'
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
</article>
|
||||
{/* 文字区块 */}
|
||||
<div
|
||||
className={
|
||||
'flex p-6 2xl:p-4 flex-col justify-between h-48 md:h-full 2xl:h-48 w-full md:w-7/12 2xl:w-full'
|
||||
}>
|
||||
<header>
|
||||
{/* 分类 */}
|
||||
{post?.category && (
|
||||
<div
|
||||
className={`flex mb-1 items-center ${showPreview ? 'justify-center' : 'justify-start'} hidden md:block flex-wrap dark:text-gray-500 text-gray-600 `}>
|
||||
<Link
|
||||
passHref
|
||||
href={`/category/${post.category}`}
|
||||
className='cursor-pointer text-xs font-normal menu-link hover:text-indigo-700 dark:hover:text-yellow-700 dark:text-gray-600 transform'>
|
||||
{post.category}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 标题 */}
|
||||
<Link
|
||||
href={post?.href}
|
||||
passHref
|
||||
className={
|
||||
' group-hover:text-indigo-700 dark:hover:text-yellow-700 dark:group-hover:text-yellow-600 text-black dark:text-gray-100 line-clamp-2 replace cursor-pointer text-xl font-extrabold leading-tight'
|
||||
}>
|
||||
{siteConfig('POST_TITLE_ICON') && (
|
||||
<NotionIcon icon={post.pageIcon} />
|
||||
)}
|
||||
<span className='menu-link '>{post.title}</span>
|
||||
</Link>
|
||||
</header>
|
||||
|
||||
{/* 摘要 */}
|
||||
{(!showPreview || showSummary) && (
|
||||
<main className='line-clamp-2 replace my-3 2xl:my-1 text-gray-700 dark:text-gray-300 text-sm font-light leading-tight'>
|
||||
{post.summary}
|
||||
</main>
|
||||
)}
|
||||
|
||||
<div className='md:flex-nowrap flex-wrap md:justify-start inline-block'>
|
||||
<div>
|
||||
{' '}
|
||||
{post.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import Link from 'next/link'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* 最新文章列表
|
||||
@@ -16,36 +14,38 @@ const LatestPostsGroup = ({ latestPosts, siteInfo }) => {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return <div className='grid grid-cols-2 gap-4'>
|
||||
{latestPosts.map(post => {
|
||||
const headerImage = post?.pageCoverThumbnail ? post.pageCoverThumbnail : siteInfo?.pageCover
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
return (
|
||||
<div className='grid grid-cols-2 gap-4'>
|
||||
{latestPosts.map(post => {
|
||||
const headerImage = post?.pageCoverThumbnail
|
||||
? post.pageCoverThumbnail
|
||||
: siteInfo?.pageCover
|
||||
|
||||
return (
|
||||
(<Link key={post.id} passHref
|
||||
title={post.title}
|
||||
href={url}
|
||||
return (
|
||||
<Link
|
||||
key={post.id}
|
||||
passHref
|
||||
title={post.title}
|
||||
href={post?.href}
|
||||
className={'my-3 flex flex-col w-full'}>
|
||||
<div className='w-full h-24 md:h-60 overflow-hidden relative rounded-lg mb-2'>
|
||||
<LazyImage
|
||||
src={`${headerImage}`}
|
||||
className='object-cover w-full h-full'
|
||||
/>
|
||||
</div>
|
||||
|
||||
className={'my-3 flex flex-col w-full'}>
|
||||
|
||||
<div className="w-full h-24 md:h-60 overflow-hidden relative rounded-lg mb-2">
|
||||
<LazyImage src={`${headerImage}`} className='object-cover w-full h-full' />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={
|
||||
' font-bold overflow-x-hidden dark:text-white hover:text-indigo-600 px-2 duration-200 w-full rounded ' +
|
||||
' hover:text-indigo-400 cursor-pointer'
|
||||
}
|
||||
>
|
||||
|
||||
<div className='line-clamp-2 menu-link'>{post.title}</div>
|
||||
|
||||
</div>
|
||||
|
||||
</Link>)
|
||||
)
|
||||
})}
|
||||
<div
|
||||
className={
|
||||
' font-bold overflow-x-hidden dark:text-white hover:text-indigo-600 px-2 duration-200 w-full rounded ' +
|
||||
' hover:text-indigo-400 cursor-pointer'
|
||||
}>
|
||||
<div className='line-clamp-2 menu-link'>{post.title}</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default LatestPostsGroup
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useGlobal } from '@/lib/global'
|
||||
// import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* 最新文章列表
|
||||
@@ -12,7 +11,7 @@ import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
* @param sliceCount 截取展示的数量 默认6
|
||||
* @constructor
|
||||
*/
|
||||
export default function LatestPostsGroupMini ({ latestPosts, siteInfo }) {
|
||||
export default function LatestPostsGroupMini({ latestPosts, siteInfo }) {
|
||||
// 获取当前路径
|
||||
const currentPath = useRouter().asPath
|
||||
const { locale } = useGlobal()
|
||||
@@ -21,44 +20,48 @@ export default function LatestPostsGroupMini ({ latestPosts, siteInfo }) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return <>
|
||||
<div className=" mb-2 px-1 flex flex-nowrap justify-between">
|
||||
<div>
|
||||
<i className="mr-2 fas fas fa-history" />
|
||||
{locale.COMMON.LATEST_POSTS}
|
||||
</div>
|
||||
return (
|
||||
<>
|
||||
<div className=' mb-2 px-1 flex flex-nowrap justify-between'>
|
||||
<div>
|
||||
<i className='mr-2 fas fas fa-history' />
|
||||
{locale.COMMON.LATEST_POSTS}
|
||||
</div>
|
||||
{latestPosts.map(post => {
|
||||
const selected = currentPath === `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
const headerImage = post?.pageCoverThumbnail ? post.pageCoverThumbnail : siteInfo?.pageCover
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
</div>
|
||||
{latestPosts.map(post => {
|
||||
const selected =
|
||||
currentPath === `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
const headerImage = post?.pageCoverThumbnail
|
||||
? post.pageCoverThumbnail
|
||||
: siteInfo?.pageCover
|
||||
|
||||
return (
|
||||
(<Link
|
||||
key={post.id}
|
||||
title={post.title}
|
||||
href={url}
|
||||
passHref
|
||||
className={'my-3 flex'}>
|
||||
|
||||
<div className="w-20 h-14 overflow-hidden relative">
|
||||
<LazyImage src={`${headerImage}`} className='object-cover w-full h-full rounded-lg'/>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
(selected ? ' text-indigo-400 ' : 'dark:text-gray-400 ') +
|
||||
' text-sm overflow-x-hidden hover:text-indigo-600 px-2 duration-200 w-full rounded ' +
|
||||
' hover:text-indigo-400 cursor-pointer items-center flex'
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<div className='line-clamp-2 menu-link'>{post.title}</div>
|
||||
<div className="text-gray-500">{post.lastEditedDay}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Link>)
|
||||
)
|
||||
})}
|
||||
return (
|
||||
<Link
|
||||
key={post.id}
|
||||
title={post.title}
|
||||
href={post?.href}
|
||||
passHref
|
||||
className={'my-3 flex'}>
|
||||
<div className='w-20 h-14 overflow-hidden relative'>
|
||||
<LazyImage
|
||||
src={`${headerImage}`}
|
||||
className='object-cover w-full h-full rounded-lg'
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
(selected ? ' text-indigo-400 ' : 'dark:text-gray-400 ') +
|
||||
' text-sm overflow-x-hidden hover:text-indigo-600 px-2 duration-200 w-full rounded ' +
|
||||
' hover:text-indigo-400 cursor-pointer items-center flex'
|
||||
}>
|
||||
<div>
|
||||
<div className='line-clamp-2 menu-link'>{post.title}</div>
|
||||
<div className='text-gray-500'>{post.lastEditedDay}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,19 +15,19 @@ const MenuGroupCard = props => {
|
||||
const links = [
|
||||
{
|
||||
name: locale.COMMON.ARTICLE,
|
||||
to: '/archive',
|
||||
href: '/archive',
|
||||
slot: archiveSlot,
|
||||
show: siteConfig('HEO_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.COMMON.CATEGORY,
|
||||
to: '/category',
|
||||
href: '/category',
|
||||
slot: categorySlot,
|
||||
show: siteConfig('HEO_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.COMMON.TAGS,
|
||||
to: '/tag',
|
||||
href: '/tag',
|
||||
slot: tagSlot,
|
||||
show: siteConfig('HEO_MENU_TAG', null, CONFIG)
|
||||
}
|
||||
@@ -40,8 +40,8 @@ const MenuGroupCard = props => {
|
||||
return (
|
||||
<div key={index} className=''>
|
||||
<Link
|
||||
title={link.to}
|
||||
href={link.to}
|
||||
title={link.href}
|
||||
href={link.href}
|
||||
target={link?.target}
|
||||
className={
|
||||
'w-full flex items-center justify-between py-1 hover:scale-105 duration-200 transform dark:hover:text-indigo-400 hover:text-indigo-600 px-2 cursor-pointer'
|
||||
|
||||
@@ -32,7 +32,7 @@ export const MenuItemCollapse = ({ link }) => {
|
||||
onClick={toggleShow}>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className='font-extralight flex justify-between pl-2 pr-4 dark:text-gray-200 no-underline tracking-widest'>
|
||||
<span className=' transition-all items-center duration-200'>
|
||||
@@ -63,7 +63,7 @@ export const MenuItemCollapse = ({ link }) => {
|
||||
<div
|
||||
key={index}
|
||||
className='dark:bg-black dark:text-gray-200 text-left px-3 justify-start bg-gray-50 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 py-3 pr-6'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-sm ml-4 whitespace-nowrap'>
|
||||
{link?.icon && <i className={sLink.icon + ' mr-2'} />}{' '}
|
||||
{sLink.title}
|
||||
|
||||
@@ -17,7 +17,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
target={link?.target}
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
className=' hover:bg-black hover:bg-opacity-10 rounded-2xl flex justify-center items-center px-3 py-1 no-underline tracking-widest'>
|
||||
{link?.icon && <i className={link?.icon} />} {link?.name}
|
||||
</Link>
|
||||
@@ -42,7 +42,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
<li
|
||||
key={index}
|
||||
className='cursor-pointer hover:bg-blue-600 hover:text-white text-gray-900 tracking-widest transition-all duration-200 dark:border-gray-700 py-1 pr-6 pl-3'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-sm text-nowrap font-extralight'>
|
||||
{link?.icon && <i className={sLink?.icon}> </i>}
|
||||
{sLink.title}
|
||||
|
||||
@@ -1,17 +1,37 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
|
||||
export const MenuListSide = (props) => {
|
||||
export const MenuListSide = props => {
|
||||
const { customNav, customMenu } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
let links = [
|
||||
{ icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', show: siteConfig('HEO_MENU_ARCHIVE', null, CONFIG) },
|
||||
{ icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: siteConfig('HEO_MENU_SEARCH', null, CONFIG) },
|
||||
{ icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('HEO_MENU_CATEGORY', null, CONFIG) },
|
||||
{ icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('HEO_MENU_TAG', null, CONFIG) }
|
||||
{
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: siteConfig('HEO_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
href: '/search',
|
||||
show: siteConfig('HEO_MENU_SEARCH', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-folder',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
href: '/category',
|
||||
show: siteConfig('HEO_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
href: '/tag',
|
||||
show: siteConfig('HEO_MENU_TAG', null, CONFIG)
|
||||
}
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
@@ -28,8 +48,10 @@ export const MenuListSide = (props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className='flex-col space-y-2'>
|
||||
{links?.map((link, index) => <MenuItemCollapse key={index} link={link} />)}
|
||||
</nav>
|
||||
<nav className='flex-col space-y-2'>
|
||||
{links?.map((link, index) => (
|
||||
<MenuItemCollapse key={index} link={link} />
|
||||
))}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,34 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
|
||||
export const MenuListTop = (props) => {
|
||||
export const MenuListTop = props => {
|
||||
const { customNav, customMenu } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
let links = [
|
||||
{ id: 1, icon: 'fa-solid fa-house', name: locale.NAV.INDEX, to: '/', show: siteConfig('HEO_MENU_INDEX', null, CONFIG) },
|
||||
{ id: 2, icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: siteConfig('HEO_MENU_SEARCH', null, CONFIG) },
|
||||
{ id: 3, icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', show: siteConfig('HEO_MENU_ARCHIVE', null, CONFIG) }
|
||||
{
|
||||
id: 1,
|
||||
icon: 'fa-solid fa-house',
|
||||
name: locale.NAV.INDEX,
|
||||
href: '/',
|
||||
show: siteConfig('HEO_MENU_INDEX', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
href: '/search',
|
||||
show: siteConfig('HEO_MENU_SEARCH', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: siteConfig('HEO_MENU_ARCHIVE', null, CONFIG)
|
||||
}
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
@@ -26,9 +44,16 @@ export const MenuListTop = (props) => {
|
||||
return null
|
||||
}
|
||||
|
||||
return (<>
|
||||
<nav id='nav-mobile' className='leading-8 justify-center font-light w-full flex'>
|
||||
{links?.map((link, index) => link && link.show && <MenuItemDrop key={index} link={link} />)}
|
||||
</nav>
|
||||
</>)
|
||||
return (
|
||||
<>
|
||||
<nav
|
||||
id='nav-mobile'
|
||||
className='leading-8 justify-center font-light w-full flex'>
|
||||
{links?.map(
|
||||
(link, index) =>
|
||||
link && link.show && <MenuItemDrop key={index} link={link} />
|
||||
)}
|
||||
</nav>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
|
||||
@@ -13,7 +12,11 @@ import CONFIG from '../config'
|
||||
export default function ArticleRecommend({ recommendPosts, siteInfo }) {
|
||||
const { locale } = useGlobal()
|
||||
|
||||
if (!siteConfig('HEXO_ARTICLE_RECOMMEND', null, CONFIG) || !recommendPosts || recommendPosts.length === 0) {
|
||||
if (
|
||||
!siteConfig('HEXO_ARTICLE_RECOMMEND', null, CONFIG) ||
|
||||
!recommendPosts ||
|
||||
recommendPosts.length === 0
|
||||
) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
@@ -27,16 +30,15 @@ export default function ArticleRecommend({ recommendPosts, siteInfo }) {
|
||||
</div>
|
||||
<div className='grid grid-cols-2 md:grid-cols-3 gap-4'>
|
||||
{recommendPosts.map(post => {
|
||||
const headerImage = post?.pageCoverThumbnail ? post.pageCoverThumbnail : siteInfo?.pageCover
|
||||
const url = checkContainHttp(post.slug)
|
||||
? sliceUrlFromHttp(post.slug)
|
||||
: `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
const headerImage = post?.pageCoverThumbnail
|
||||
? post.pageCoverThumbnail
|
||||
: siteInfo?.pageCover
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={post.id}
|
||||
title={post.title}
|
||||
href={url}
|
||||
href={post?.href}
|
||||
passHref
|
||||
className='flex h-40 cursor-pointer overflow-hidden'>
|
||||
<div className='h-full w-full relative group'>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import Link from 'next/link'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* 博客归档列表
|
||||
@@ -16,31 +14,28 @@ const BlogPostArchive = ({ posts = [], archiveTitle }) => {
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className="pt-16 pb-4 text-3xl dark:text-gray-300"
|
||||
id={archiveTitle}
|
||||
>
|
||||
className='pt-16 pb-4 text-3xl dark:text-gray-300'
|
||||
id={archiveTitle}>
|
||||
{archiveTitle}
|
||||
</div>
|
||||
<ul>
|
||||
{posts?.map(post => {
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
return <li
|
||||
key={post.id}
|
||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-indigo-500 dark:hover:border-indigo-300 dark:border-indigo-400 transform duration-500"
|
||||
>
|
||||
<div id={post?.publishDay}>
|
||||
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
||||
|
||||
<Link
|
||||
href={url}
|
||||
passHref
|
||||
className="dark:text-gray-400 dark:hover:text-indigo-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||
|
||||
{post.title}
|
||||
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
return (
|
||||
<li
|
||||
key={post.id}
|
||||
className='border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-indigo-500 dark:hover:border-indigo-300 dark:border-indigo-400 transform duration-500'>
|
||||
<div id={post?.publishDay}>
|
||||
<span className='text-gray-400'>{post.date?.start_date}</span>{' '}
|
||||
|
||||
<Link
|
||||
href={post?.href}
|
||||
passHref
|
||||
className='dark:text-gray-400 dark:hover:text-indigo-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600'>
|
||||
{post.title}
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
import { BlogPostCardInfo } from './BlogPostCardInfo'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
|
||||
const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
const showPreview = siteConfig('HEXO_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
@@ -12,7 +11,6 @@ const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
}
|
||||
const showPageCover = siteConfig('HEXO_POST_LIST_COVER', null, CONFIG) && post?.pageCoverThumbnail && !showPreview
|
||||
// const delay = (index % 2) * 200
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return (
|
||||
|
||||
@@ -33,7 +31,7 @@ const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && (
|
||||
<div className="md:w-5/12 overflow-hidden">
|
||||
<Link href={url} passHref legacyBehavior>
|
||||
<Link href={post?.href} passHref legacyBehavior>
|
||||
<LazyImage priority={index === 1} src={post?.pageCoverThumbnail} className='h-56 w-full object-cover object-center group-hover:scale-110 duration-500' />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -1,102 +1,110 @@
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import Link from 'next/link'
|
||||
import TagItemMini from './TagItemMini'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { formatDateFmt } from '@/lib/utils/formatDate'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import Link from 'next/link'
|
||||
import TagItemMini from './TagItemMini'
|
||||
|
||||
/**
|
||||
* 博客列表的文字内容
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
export const BlogPostCardInfo = ({ post, showPreview, showPageCover, showSummary }) => {
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
return <article className={`flex flex-col justify-between lg:p-6 p-4 ${showPageCover && !showPreview ? 'md:w-7/12 w-full md:max-h-60' : 'w-full'}`}>
|
||||
<div>
|
||||
<header>
|
||||
<h2>
|
||||
{/* 标题 */}
|
||||
<Link
|
||||
href={url}
|
||||
passHref
|
||||
className={`line-clamp-2 replace cursor-pointer text-2xl ${showPreview ? 'text-center' : ''
|
||||
} leading-tight font-normal text-gray-600 dark:text-gray-100 hover:text-indigo-700 dark:hover:text-indigo-400`}>
|
||||
|
||||
{siteConfig('POST_TITLE_ICON') && <NotionIcon icon={post.pageIcon} />}<span className='menu-link '>{post.title}</span>
|
||||
|
||||
</Link>
|
||||
</h2>
|
||||
|
||||
{/* 分类 */}
|
||||
{ post?.category && <div
|
||||
className={`flex mt-2 items-center ${showPreview ? 'justify-center' : 'justify-start'
|
||||
} flex-wrap dark:text-gray-500 text-gray-400 `}
|
||||
>
|
||||
<Link
|
||||
href={`/category/${post.category}`}
|
||||
passHref
|
||||
className="cursor-pointer font-light text-sm menu-link hover:text-indigo-700 dark:hover:text-indigo-400 transform">
|
||||
|
||||
<i className="mr-1 far fa-folder" />
|
||||
{post.category}
|
||||
|
||||
</Link>
|
||||
|
||||
<TwikooCommentCount className='text-sm hover:text-indigo-700 dark:hover:text-indigo-400' post={post}/>
|
||||
</div>}
|
||||
</header>
|
||||
|
||||
{/* 摘要 */}
|
||||
{(!showPreview || showSummary) && !post.results && (
|
||||
<main className="line-clamp-2 replace my-3 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{post.summary}
|
||||
</main>
|
||||
)}
|
||||
|
||||
{/* 搜索结果 */}
|
||||
{post.results && (
|
||||
<p className="line-clamp-2 mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{post.results.map((r, index) => (
|
||||
<span key={index}>{r}</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* 预览 */}
|
||||
{showPreview && (
|
||||
<div className="overflow-ellipsis truncate">
|
||||
<NotionPage post={post} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{/* 日期标签 */}
|
||||
<div className="text-gray-400 justify-between flex">
|
||||
{/* 日期 */}
|
||||
export const BlogPostCardInfo = ({
|
||||
post,
|
||||
showPreview,
|
||||
showPageCover,
|
||||
showSummary
|
||||
}) => {
|
||||
return (
|
||||
<article
|
||||
className={`flex flex-col justify-between lg:p-6 p-4 ${showPageCover && !showPreview ? 'md:w-7/12 w-full md:max-h-60' : 'w-full'}`}>
|
||||
<div>
|
||||
<header>
|
||||
<h2>
|
||||
{/* 标题 */}
|
||||
<Link
|
||||
href={`/archive#${formatDateFmt(post?.publishDate, 'yyyy-MM')}`}
|
||||
passHref
|
||||
className="font-light menu-link cursor-pointer text-sm leading-4 mr-3">
|
||||
|
||||
<i className="far fa-calendar-alt mr-1" />
|
||||
{post?.publishDay || post.lastEditedDay}
|
||||
|
||||
href={post?.href}
|
||||
passHref
|
||||
className={`line-clamp-2 replace cursor-pointer text-2xl ${
|
||||
showPreview ? 'text-center' : ''
|
||||
} leading-tight font-normal text-gray-600 dark:text-gray-100 hover:text-indigo-700 dark:hover:text-indigo-400`}>
|
||||
{siteConfig('POST_TITLE_ICON') && (
|
||||
<NotionIcon icon={post.pageIcon} />
|
||||
)}
|
||||
<span className='menu-link '>{post.title}</span>
|
||||
</Link>
|
||||
</h2>
|
||||
|
||||
<div className="md:flex-nowrap flex-wrap md:justify-start inline-block">
|
||||
<div>
|
||||
{' '}
|
||||
{post.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
{/* 分类 */}
|
||||
{post?.category && (
|
||||
<div
|
||||
className={`flex mt-2 items-center ${
|
||||
showPreview ? 'justify-center' : 'justify-start'
|
||||
} flex-wrap dark:text-gray-500 text-gray-400 `}>
|
||||
<Link
|
||||
href={`/category/${post.category}`}
|
||||
passHref
|
||||
className='cursor-pointer font-light text-sm menu-link hover:text-indigo-700 dark:hover:text-indigo-400 transform'>
|
||||
<i className='mr-1 far fa-folder' />
|
||||
{post.category}
|
||||
</Link>
|
||||
|
||||
<TwikooCommentCount
|
||||
className='text-sm hover:text-indigo-700 dark:hover:text-indigo-400'
|
||||
post={post}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
|
||||
{/* 摘要 */}
|
||||
{(!showPreview || showSummary) && !post.results && (
|
||||
<main className='line-clamp-2 replace my-3 text-gray-700 dark:text-gray-300 text-sm font-light leading-7'>
|
||||
{post.summary}
|
||||
</main>
|
||||
)}
|
||||
|
||||
{/* 搜索结果 */}
|
||||
{post.results && (
|
||||
<p className='line-clamp-2 mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7'>
|
||||
{post.results.map((r, index) => (
|
||||
<span key={index}>{r}</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* 预览 */}
|
||||
{showPreview && (
|
||||
<div className='overflow-ellipsis truncate'>
|
||||
<NotionPage post={post} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{/* 日期标签 */}
|
||||
<div className='text-gray-400 justify-between flex'>
|
||||
{/* 日期 */}
|
||||
<Link
|
||||
href={`/archive#${formatDateFmt(post?.publishDate, 'yyyy-MM')}`}
|
||||
passHref
|
||||
className='font-light menu-link cursor-pointer text-sm leading-4 mr-3'>
|
||||
<i className='far fa-calendar-alt mr-1' />
|
||||
{post?.publishDay || post.lastEditedDay}
|
||||
</Link>
|
||||
|
||||
<div className='md:flex-nowrap flex-wrap md:justify-start inline-block'>
|
||||
<div>
|
||||
{' '}
|
||||
{post.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
// import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* 最新文章列表
|
||||
@@ -21,45 +19,48 @@ const LatestPostsGroup = ({ latestPosts, siteInfo }) => {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return <>
|
||||
<div className=" mb-2 px-1 flex flex-nowrap justify-between">
|
||||
<div>
|
||||
<i className="mr-2 fas fas fa-history" />
|
||||
{locale.COMMON.LATEST_POSTS}
|
||||
</div>
|
||||
return (
|
||||
<>
|
||||
<div className=' mb-2 px-1 flex flex-nowrap justify-between'>
|
||||
<div>
|
||||
<i className='mr-2 fas fas fa-history' />
|
||||
{locale.COMMON.LATEST_POSTS}
|
||||
</div>
|
||||
{latestPosts.map(post => {
|
||||
const headerImage = post?.pageCoverThumbnail ? post.pageCoverThumbnail : siteInfo?.pageCover
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
const selected = currentPath === url
|
||||
</div>
|
||||
{latestPosts.map(post => {
|
||||
const headerImage = post?.pageCoverThumbnail
|
||||
? post.pageCoverThumbnail
|
||||
: siteInfo?.pageCover
|
||||
const selected = currentPath === post?.href
|
||||
|
||||
return (
|
||||
(<Link
|
||||
key={post.id}
|
||||
title={post.title}
|
||||
href={url}
|
||||
passHref
|
||||
className={'my-3 flex'}>
|
||||
|
||||
<div className="w-20 h-14 overflow-hidden relative">
|
||||
<LazyImage src={`${headerImage}`} className='object-cover w-full h-full'/>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
(selected ? ' text-indigo-400 ' : 'dark:text-gray-400 ') +
|
||||
' text-sm overflow-x-hidden hover:text-indigo-600 px-2 duration-200 w-full rounded ' +
|
||||
' hover:text-indigo-400 cursor-pointer items-center flex'
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<div className='line-clamp-2 menu-link'>{post.title}</div>
|
||||
<div className="text-gray-500">{post.lastEditedDay}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Link>)
|
||||
)
|
||||
})}
|
||||
return (
|
||||
<Link
|
||||
key={post.id}
|
||||
title={post.title}
|
||||
href={post?.href}
|
||||
passHref
|
||||
className={'my-3 flex'}>
|
||||
<div className='w-20 h-14 overflow-hidden relative'>
|
||||
<LazyImage
|
||||
src={`${headerImage}`}
|
||||
className='object-cover w-full h-full'
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
(selected ? ' text-indigo-400 ' : 'dark:text-gray-400 ') +
|
||||
' text-sm overflow-x-hidden hover:text-indigo-600 px-2 duration-200 w-full rounded ' +
|
||||
' hover:text-indigo-400 cursor-pointer items-center flex'
|
||||
}>
|
||||
<div>
|
||||
<div className='line-clamp-2 menu-link'>{post.title}</div>
|
||||
<div className='text-gray-500'>{post.lastEditedDay}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default LatestPostsGroup
|
||||
|
||||
@@ -15,19 +15,19 @@ const MenuGroupCard = props => {
|
||||
const links = [
|
||||
{
|
||||
name: locale.COMMON.ARTICLE,
|
||||
to: '/archive',
|
||||
href: '/archive',
|
||||
slot: archiveSlot,
|
||||
show: siteConfig('HEXO_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.COMMON.CATEGORY,
|
||||
to: '/category',
|
||||
href: '/category',
|
||||
slot: categorySlot,
|
||||
show: siteConfig('HEXO_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.COMMON.TAGS,
|
||||
to: '/tag',
|
||||
href: '/tag',
|
||||
slot: tagSlot,
|
||||
show: siteConfig('HEXO_MENU_TAG', null, CONFIG)
|
||||
}
|
||||
@@ -47,9 +47,9 @@ const MenuGroupCard = props => {
|
||||
if (link.show) {
|
||||
return (
|
||||
<Link
|
||||
key={`${link.to}`}
|
||||
title={link.to}
|
||||
href={link.to}
|
||||
key={`${link.href}`}
|
||||
title={link.href}
|
||||
href={link.href}
|
||||
target={link?.target}
|
||||
className={
|
||||
'py-1.5 my-1 px-2 duration-300 text-base justify-center items-center cursor-pointer'
|
||||
|
||||
@@ -33,7 +33,7 @@ export const MenuItemCollapse = props => {
|
||||
onClick={toggleShow}>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className=' font-extralight flex justify-between pl-2 pr-4 dark:text-gray-200 no-underline tracking-widest pb-1'>
|
||||
<span className=' transition-all items-center duration-200'>
|
||||
@@ -64,7 +64,7 @@ export const MenuItemCollapse = props => {
|
||||
<div
|
||||
key={index}
|
||||
className='dark:hover:bg-indigo-500 hover:bg-indigo-500 hover:text-white dark:bg-black dark:text-gray-200 text-left px-10 justify-start bg-gray-50 tracking-widest transition-all duration-200 py-3 pr-6'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-sm ml-4 whitespace-nowrap'>
|
||||
{link?.icon && <i className={sLink.icon + ' mr-2'} />}{' '}
|
||||
{sLink.title}
|
||||
|
||||
@@ -19,7 +19,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
onMouseOut={() => changeShow(false)}>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className=' menu-link pl-2 pr-4 no-underline tracking-widest pb-1'>
|
||||
{link?.icon && <i className={link?.icon} />} {link?.name}
|
||||
@@ -47,7 +47,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
<li
|
||||
key={index}
|
||||
className='cursor-pointer hover:bg-indigo-500 hover:text-white tracking-widest transition-all duration-200 dark:border-gray-800 py-1 pr-6 pl-3'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-sm text-nowrap font-extralight'>
|
||||
{link?.icon && <i className={sLink?.icon}> </i>}
|
||||
{sLink.title}
|
||||
|
||||
@@ -15,25 +15,25 @@ export const MenuListSide = props => {
|
||||
{
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
to: '/archive',
|
||||
href: '/archive',
|
||||
show: siteConfig('HEXO_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
to: '/search',
|
||||
href: '/search',
|
||||
show: siteConfig('HEXO_MENU_SEARCH', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-folder',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
to: '/category',
|
||||
href: '/category',
|
||||
show: siteConfig('HEXO_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
to: '/tag',
|
||||
href: '/tag',
|
||||
show: siteConfig('HEXO_MENU_TAG', null, CONFIG)
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,18 +1,36 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
|
||||
export const MenuListTop = (props) => {
|
||||
export const MenuListTop = props => {
|
||||
const { customNav, customMenu } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
let links = [
|
||||
{ id: 1, icon: 'fa-solid fa-house', name: locale.NAV.INDEX, to: '/', show: siteConfig('HEXO_MENU_INDEX', null, CONFIG) },
|
||||
{ id: 2, icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: siteConfig('HEXO_MENU_SEARCH', null, CONFIG) },
|
||||
{ id: 3, icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', show: siteConfig('HEXO_MENU_ARCHIVE', null, CONFIG) }
|
||||
// { icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('MENU_CATEGORY', null, CONFIG) },
|
||||
// { icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('MENU_TAG', null, CONFIG) }
|
||||
{
|
||||
id: 1,
|
||||
icon: 'fa-solid fa-house',
|
||||
name: locale.NAV.INDEX,
|
||||
href: '/',
|
||||
show: siteConfig('HEXO_MENU_INDEX', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
href: '/search',
|
||||
show: siteConfig('HEXO_MENU_SEARCH', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: siteConfig('HEXO_MENU_ARCHIVE', null, CONFIG)
|
||||
}
|
||||
// { icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, href: '/category', show: siteConfig('MENU_CATEGORY', null, CONFIG) },
|
||||
// { icon: 'fas fa-tag', name: locale.COMMON.TAGS, href: '/tag', show: siteConfig('MENU_TAG', null, CONFIG) }
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
@@ -34,9 +52,16 @@ export const MenuListTop = (props) => {
|
||||
return null
|
||||
}
|
||||
|
||||
return (<>
|
||||
<nav id='nav-mobile' className='leading-8 justify-center font-light w-full flex'>
|
||||
{links?.map((link, index) => link && link.show && <MenuItemDrop key={index} link={link} />)}
|
||||
</nav>
|
||||
</>)
|
||||
return (
|
||||
<>
|
||||
<nav
|
||||
id='nav-mobile'
|
||||
className='leading-8 justify-center font-light w-full flex'>
|
||||
{links?.map(
|
||||
(link, index) =>
|
||||
link && link.show && <MenuItemDrop key={index} link={link} />
|
||||
)}
|
||||
</nav>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
|
||||
/**
|
||||
* 关联推荐文章
|
||||
@@ -15,54 +14,53 @@ export default function ArticleRecommend({ recommendPosts, siteInfo }) {
|
||||
|
||||
if (
|
||||
!siteConfig('MATERY_ARTICLE_RECOMMEND', null, CONFIG) ||
|
||||
!recommendPosts ||
|
||||
recommendPosts.length === 0
|
||||
!recommendPosts ||
|
||||
recommendPosts.length === 0
|
||||
) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-2">
|
||||
<div className="mb-2 px-1 flex flex-nowrap justify-between">
|
||||
<div>
|
||||
<i className="mr-2 fas fa-thumbs-up" />
|
||||
{locale.COMMON.RELATE_POSTS}
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
{recommendPosts.map(post => {
|
||||
const headerImage = post?.pageCoverThumbnail
|
||||
? post.pageCoverThumbnail
|
||||
: siteInfo?.pageCover
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return (
|
||||
(<Link
|
||||
key={post.id}
|
||||
title={post.title}
|
||||
href={url}
|
||||
passHref
|
||||
className="flex h-40 cursor-pointer overflow-hidden">
|
||||
|
||||
<div className="h-full w-full group" >
|
||||
|
||||
<LazyImage src={headerImage} className="h-full w-full object-cover object-center group-hover:scale-110 transform duration-200" />
|
||||
|
||||
<div className="flex items-center justify-center bg-black bg-opacity-60 hover:bg-opacity-10 w-full h-full duration-300 ">
|
||||
<div className="text-sm text-white text-center shadow-text">
|
||||
<div>
|
||||
<i className="fas fa-calendar-alt mr-1" />
|
||||
{post.date?.start_date}
|
||||
</div>
|
||||
<div className="hover:underline">{post.title}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Link>)
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className='p-2'>
|
||||
<div className='mb-2 px-1 flex flex-nowrap justify-between'>
|
||||
<div>
|
||||
<i className='mr-2 fas fa-thumbs-up' />
|
||||
{locale.COMMON.RELATE_POSTS}
|
||||
</div>
|
||||
</div>
|
||||
<div className='grid grid-cols-2 md:grid-cols-3 gap-4'>
|
||||
{recommendPosts.map(post => {
|
||||
const headerImage = post?.pageCoverThumbnail
|
||||
? post.pageCoverThumbnail
|
||||
: siteInfo?.pageCover
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={post.id}
|
||||
title={post.title}
|
||||
href={post?.href}
|
||||
passHref
|
||||
className='flex h-40 cursor-pointer overflow-hidden'>
|
||||
<div className='h-full w-full group'>
|
||||
<LazyImage
|
||||
src={headerImage}
|
||||
className='h-full w-full object-cover object-center group-hover:scale-110 transform duration-200'
|
||||
/>
|
||||
|
||||
<div className='flex items-center justify-center bg-black bg-opacity-60 hover:bg-opacity-10 w-full h-full duration-300 '>
|
||||
<div className='text-sm text-white text-center shadow-text'>
|
||||
<div>
|
||||
<i className='fas fa-calendar-alt mr-1' />
|
||||
{post.date?.start_date}
|
||||
</div>
|
||||
<div className='hover:underline'>{post.title}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import Link from 'next/link'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* 博客归档列表
|
||||
@@ -16,30 +14,28 @@ const BlogPostArchive = ({ posts = [], archiveTitle }) => {
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className="pt-16 pb-4 text-3xl dark:text-gray-300"
|
||||
id={archiveTitle}
|
||||
>
|
||||
className='pt-16 pb-4 text-3xl dark:text-gray-300'
|
||||
id={archiveTitle}>
|
||||
{archiveTitle}
|
||||
</div>
|
||||
<ul>
|
||||
{posts?.map(post => {
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return <li
|
||||
key={post.id}
|
||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-indigo-500 dark:hover:border-indigo-300 dark:border-indigo-400 transform duration-500"
|
||||
>
|
||||
<div id={post?.publishDay}>
|
||||
<span className="text-gray-400">{post.date?.start_date}</span>{' '}
|
||||
|
||||
<Link href={url} passHref
|
||||
className="dark:text-gray-400 dark:hover:text-indigo-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||
|
||||
{post.title}
|
||||
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
return (
|
||||
<li
|
||||
key={post.id}
|
||||
className='border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-indigo-500 dark:hover:border-indigo-300 dark:border-indigo-400 transform duration-500'>
|
||||
<div id={post?.publishDay}>
|
||||
<span className='text-gray-400'>{post.date?.start_date}</span>{' '}
|
||||
|
||||
<Link
|
||||
href={post?.href}
|
||||
passHref
|
||||
className='dark:text-gray-400 dark:hover:text-indigo-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600'>
|
||||
{post.title}
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,6 @@ import LazyImage from '@/components/LazyImage'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import { formatDateFmt } from '@/lib/utils/formatDate'
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
@@ -24,9 +23,6 @@ const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
siteConfig('MATERY_POST_LIST_COVER', null, CONFIG) &&
|
||||
post?.pageCoverThumbnail
|
||||
const delay = (index % 3) * 300
|
||||
const url = checkContainHttp(post.slug)
|
||||
? sliceUrlFromHttp(post.slug)
|
||||
: `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -40,7 +36,7 @@ const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
<header className='group flex flex-col h-80 justify-between'>
|
||||
{/* 头部图片 填充卡片 */}
|
||||
{showPageCover && (
|
||||
<Link href={url} passHref legacyBehavior>
|
||||
<Link href={post?.href} passHref legacyBehavior>
|
||||
<div className='flex flex-grow w-full relative duration-200 = rounded-t-md cursor-pointer transform overflow-hidden'>
|
||||
<LazyImage
|
||||
src={post?.pageCoverThumbnail}
|
||||
|
||||
@@ -13,19 +13,19 @@ const MenuGroupCard = props => {
|
||||
const links = [
|
||||
{
|
||||
name: locale.COMMON.ARTICLE,
|
||||
to: '/archive',
|
||||
href: '/archive',
|
||||
slot: archiveSlot,
|
||||
show: siteConfig('MATERY_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.COMMON.CATEGORY,
|
||||
to: '/category',
|
||||
href: '/category',
|
||||
slot: categorySlot,
|
||||
show: siteConfig('MATERY_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.COMMON.TAGS,
|
||||
to: '/tag',
|
||||
href: '/tag',
|
||||
slot: tagSlot,
|
||||
show: siteConfig('MATERY_MENU_TAG', null, CONFIG)
|
||||
}
|
||||
@@ -37,9 +37,9 @@ const MenuGroupCard = props => {
|
||||
if (link.show) {
|
||||
return (
|
||||
<Link
|
||||
key={`${link.to}`}
|
||||
title={link.to}
|
||||
href={link.to}
|
||||
key={`${link.href}`}
|
||||
title={link.href}
|
||||
href={link.href}
|
||||
target={link?.target}
|
||||
className={
|
||||
'py-1.5 my-1 px-2 duration-300 text-base justify-center items-center cursor-pointer'
|
||||
|
||||
@@ -27,7 +27,7 @@ export const MenuItemCollapse = ({ link }) => {
|
||||
return null
|
||||
}
|
||||
|
||||
const selected = router.pathname === link.to || router.asPath === link.to
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -40,7 +40,7 @@ export const MenuItemCollapse = ({ link }) => {
|
||||
: ' text-black dark:text-white ')
|
||||
}>
|
||||
{!hasSubMenu && (
|
||||
<Link href={link?.to} target={link?.target}>
|
||||
<Link href={link?.href} target={link?.target}>
|
||||
<div className='my-auto items-center justify-between flex '>
|
||||
{link.icon && (
|
||||
<i className={`${link.icon} w-4 mr-6 text-center`} />
|
||||
@@ -73,7 +73,7 @@ export const MenuItemCollapse = ({ link }) => {
|
||||
<div
|
||||
key={index}
|
||||
className='cursor-pointer whitespace-nowrap dark:text-gray-200 w-full font-extralight dark:bg-black text-left px-5 justify-start bg-gray-100 hover:bg-indigo-500 dark:hover:bg-indigo-500 hover:text-white tracking-widest transition-all duration-200 border-b dark:border-gray-800 py-3 pr-6'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-sm'>
|
||||
<i className={`${sLink.icon} w-4 mr-3 text-center`} />
|
||||
{sLink.title}
|
||||
|
||||
@@ -18,7 +18,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
onMouseOut={() => changeShow(false)}>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className=' menu-link pl-2 pr-4 no-underline tracking-widest pb-1'>
|
||||
{link?.icon && <i className={link?.icon} />} {link?.name}
|
||||
@@ -46,7 +46,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
<li
|
||||
key={index}
|
||||
className='cursor-pointer hover:bg-indigo-500 text-gray-900 hover:text-white tracking-widest transition-all duration-200 dark:border-gray-800 py-1 pr-6 pl-3'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-sm text-nowrap font-extralight'>
|
||||
{link?.icon && <i className={sLink?.icon}> </i>}
|
||||
{sLink.title}
|
||||
|
||||
@@ -5,20 +5,24 @@ export const MenuItemNormal = props => {
|
||||
const router = useRouter()
|
||||
|
||||
const { link } = props
|
||||
const selected = (router.pathname === link.to) || (router.asPath === link.to)
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
|
||||
return <Link
|
||||
key={link.to}
|
||||
title={link.to}
|
||||
href={link.to}
|
||||
className={'py-2 px-5 duration-300 text-base justify-between hover:bg-gray-700 hover:text-white hover:shadow-lg cursor-pointer font-light flex flex-nowrap items-center ' +
|
||||
(selected ? 'bg-indigo-500 text-white ' : ' text-black dark:text-white ')}>
|
||||
|
||||
<div className='my-auto items-center justify-between flex '>
|
||||
return (
|
||||
<Link
|
||||
key={link.href}
|
||||
title={link.href}
|
||||
href={link.href}
|
||||
className={
|
||||
'py-2 px-5 duration-300 text-base justify-between hover:bg-gray-700 hover:text-white hover:shadow-lg cursor-pointer font-light flex flex-nowrap items-center ' +
|
||||
(selected
|
||||
? 'bg-indigo-500 text-white '
|
||||
: ' text-black dark:text-white ')
|
||||
}>
|
||||
<div className='my-auto items-center justify-between flex '>
|
||||
<i className={`${link.icon} w-4 ml-3 mr-6 text-center`} />
|
||||
<div >{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
|
||||
</Link>
|
||||
<div>{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,21 +1,51 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
|
||||
const MenuList = (props) => {
|
||||
const MenuList = props => {
|
||||
const { postCount, customNav } = props
|
||||
const { locale } = useGlobal()
|
||||
const router = useRouter()
|
||||
const archiveSlot = <div className='bg-gray-300 dark:bg-gray-500 rounded-md text-gray-50 px-1 text-xs'>{postCount}</div>
|
||||
const archiveSlot = (
|
||||
<div className='bg-gray-300 dark:bg-gray-500 rounded-md text-gray-50 px-1 text-xs'>
|
||||
{postCount}
|
||||
</div>
|
||||
)
|
||||
|
||||
let links = [
|
||||
{ icon: 'fas fa-home', name: locale.NAV.INDEX, to: '/' || '/', show: true },
|
||||
{ icon: 'fas fa-th', name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('MATERY_MENU_CATEGORY', null, CONFIG) },
|
||||
{ icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('MATERY_MENU_TAG', null, CONFIG) },
|
||||
{ icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', slot: archiveSlot, show: siteConfig('MATERY_MENU_ARCHIVE', null, CONFIG) },
|
||||
{ icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: siteConfig('MATERY_MENU_SEARCH', null, CONFIG) }
|
||||
{
|
||||
icon: 'fas fa-home',
|
||||
name: locale.NAV.INDEX,
|
||||
href: '/' || '/',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-th',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
href: '/category',
|
||||
show: siteConfig('MATERY_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
href: '/tag',
|
||||
show: siteConfig('MATERY_MENU_TAG', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
slot: archiveSlot,
|
||||
show: siteConfig('MATERY_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
href: '/search',
|
||||
show: siteConfig('MATERY_MENU_SEARCH', null, CONFIG)
|
||||
}
|
||||
]
|
||||
if (customNav) {
|
||||
links = links.concat(customNav)
|
||||
@@ -25,21 +55,22 @@ const MenuList = (props) => {
|
||||
<nav id='nav' className='leading-8 text-gray-500 dark:text-gray-300 '>
|
||||
{links.map(link => {
|
||||
if (link && link.show) {
|
||||
const selected = (router.pathname === link.to) || (router.asPath === link.to)
|
||||
const selected =
|
||||
router.pathname === link.href || router.asPath === link.href
|
||||
return (
|
||||
<Link
|
||||
key={`${link.to}`}
|
||||
title={link.to}
|
||||
href={link.to}
|
||||
className={'py-1.5 px-5 text-base justify-between hover:bg-indigo-400 hover:text-white hover:shadow-lg cursor-pointer font-light flex flex-nowrap items-center ' +
|
||||
(selected ? 'bg-gray-200 text-black' : ' ')}>
|
||||
|
||||
key={`${link.href}`}
|
||||
title={link.href}
|
||||
href={link.href}
|
||||
className={
|
||||
'py-1.5 px-5 text-base justify-between hover:bg-indigo-400 hover:text-white hover:shadow-lg cursor-pointer font-light flex flex-nowrap items-center ' +
|
||||
(selected ? 'bg-gray-200 text-black' : ' ')
|
||||
}>
|
||||
<div className='my-auto items-center justify-center flex '>
|
||||
<i className={`${link.icon} w-4 text-center`} />
|
||||
<div className={'ml-4'}>{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
|
||||
</Link>
|
||||
)
|
||||
} else {
|
||||
|
||||
@@ -1,17 +1,37 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
|
||||
export const MenuListSide = (props) => {
|
||||
export const MenuListSide = props => {
|
||||
const { customNav, customMenu } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
let links = [
|
||||
{ icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', show: siteConfig('MATERY_MENU_ARCHIVE', null, CONFIG) },
|
||||
{ icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: siteConfig('MATERY_MENU_SEARCH', null, CONFIG) },
|
||||
{ icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('MATERY_MENU_CATEGORY', null, CONFIG) },
|
||||
{ icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('MATERY_MENU_TAG', null, CONFIG) }
|
||||
{
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: siteConfig('MATERY_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
href: '/search',
|
||||
show: siteConfig('MATERY_MENU_SEARCH', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-folder',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
href: '/category',
|
||||
show: siteConfig('MATERY_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
href: '/tag',
|
||||
show: siteConfig('MATERY_MENU_TAG', null, CONFIG)
|
||||
}
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
@@ -28,8 +48,10 @@ export const MenuListSide = (props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<nav>
|
||||
{links?.map((link, index) => <MenuItemCollapse key={index} link={link} />)}
|
||||
</nav>
|
||||
<nav>
|
||||
{links?.map((link, index) => (
|
||||
<MenuItemCollapse key={index} link={link} />
|
||||
))}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,25 +16,25 @@ export const MenuListTop = props => {
|
||||
{
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
to: '/archive',
|
||||
href: '/archive',
|
||||
show: siteConfig('MATERY_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
to: '/search',
|
||||
href: '/search',
|
||||
show: siteConfig('MATERY_MENU_SEARCH', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-folder',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
to: '/category',
|
||||
href: '/category',
|
||||
show: siteConfig('MATERY_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
to: '/tag',
|
||||
href: '/tag',
|
||||
show: siteConfig('MATERY_MENU_TAG', null, CONFIG)
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import Link from 'next/link'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* 归档分组
|
||||
@@ -9,31 +7,30 @@ import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
*/
|
||||
export default function BlogArchiveItem({ archiveTitle, archivePosts }) {
|
||||
return (
|
||||
<div key={archiveTitle}>
|
||||
<div id={archiveTitle} className="pt-16 pb-4 text-3xl dark:text-gray-300" >
|
||||
{archiveTitle}
|
||||
</div>
|
||||
<ul>
|
||||
{archivePosts[archiveTitle]?.map(post => {
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return <li key={post.id}
|
||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
||||
>
|
||||
<div id={post?.publishDay}>
|
||||
<span className="text-gray-400">
|
||||
{post.date?.start_date}
|
||||
</span>{' '}
|
||||
|
||||
|
||||
<Link passHref href={url}
|
||||
className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||
{post.title}
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<div key={archiveTitle}>
|
||||
<div id={archiveTitle} className='pt-16 pb-4 text-3xl dark:text-gray-300'>
|
||||
{archiveTitle}
|
||||
</div>
|
||||
<ul>
|
||||
{archivePosts[archiveTitle]?.map(post => {
|
||||
return (
|
||||
<li
|
||||
key={post.id}
|
||||
className='border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500'>
|
||||
<div id={post?.publishDay}>
|
||||
<span className='text-gray-400'>{post.date?.start_date}</span>{' '}
|
||||
|
||||
<Link
|
||||
passHref
|
||||
href={post?.href}
|
||||
className='dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600'>
|
||||
{post.title}
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,84 +1,91 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
import CategoryItem from './CategoryItem'
|
||||
import TagItemMini from './TagItemMini'
|
||||
import TwikooCommentCount from '@/components/TwikooCommentCount'
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
|
||||
const BlogPostCard = ({ post, showSummary }) => {
|
||||
const showPreview = siteConfig('MEDIUM_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
const showPreview =
|
||||
siteConfig('MEDIUM_POST_LIST_PREVIEW', null, CONFIG) && post.blockMap
|
||||
const { locale } = useGlobal()
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
return (
|
||||
<div
|
||||
key={post.id}
|
||||
data-aos='fade-up'
|
||||
data-aos-duration='300'
|
||||
data-aos-once='false'
|
||||
data-aos-anchor-placement='top-bottom'
|
||||
className='mb-6 max-w-7xl border-b dark:border-gray-800 '>
|
||||
<header className='lg:py-8 py-4 flex flex-col w-full'>
|
||||
<Link
|
||||
href={post?.href}
|
||||
passHref
|
||||
className={
|
||||
'cursor-pointer font-bold hover:underline text-3xl leading-tight text-gray-700 dark:text-gray-300 hover:text-green-500 dark:hover:text-green-400'
|
||||
}>
|
||||
<h2>
|
||||
{siteConfig('MEDIUM_POST_LIST_COVER', null, CONFIG) && (
|
||||
<div className='w-full max-h-96 object-cover overflow-hidden mb-2'>
|
||||
<LazyImage
|
||||
src={post.pageCoverThumbnail}
|
||||
style={post.pageCoverThumbnail ? {} : { height: '0px' }}
|
||||
className='w-full max-h-96 object-cover hover:scale-125 duration-150'
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{siteConfig('POST_TITLE_ICON') && (
|
||||
<NotionIcon icon={post.pageIcon} />
|
||||
)}
|
||||
{post.title}
|
||||
</h2>
|
||||
</Link>
|
||||
|
||||
<div
|
||||
key={post.id}
|
||||
data-aos="fade-up"
|
||||
data-aos-duration="300"
|
||||
data-aos-once="false"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className="mb-6 max-w-7xl border-b dark:border-gray-800 "
|
||||
>
|
||||
|
||||
<header className="lg:py-8 py-4 flex flex-col w-full">
|
||||
<Link
|
||||
href={url}
|
||||
passHref
|
||||
className={
|
||||
'cursor-pointer font-bold hover:underline text-3xl leading-tight text-gray-700 dark:text-gray-300 hover:text-green-500 dark:hover:text-green-400'
|
||||
}>
|
||||
<h2>
|
||||
{siteConfig('MEDIUM_POST_LIST_COVER', null, CONFIG) && <div className='w-full max-h-96 object-cover overflow-hidden mb-2'>
|
||||
<LazyImage src={post.pageCoverThumbnail} style={post.pageCoverThumbnail ? {} : { height: '0px' }} className='w-full max-h-96 object-cover hover:scale-125 duration-150' />
|
||||
</div>}
|
||||
{siteConfig('POST_TITLE_ICON') && <NotionIcon icon={post.pageIcon} />}{post.title}
|
||||
</h2>
|
||||
|
||||
</Link>
|
||||
|
||||
<div
|
||||
className={
|
||||
'flex mt-2 items-center justify-start flex-wrap space-x-3 text-gray-400'
|
||||
}
|
||||
>
|
||||
<div className="text-sm py-1">{post.date?.start_date}</div>
|
||||
{siteConfig('MEDIUM_POST_LIST_CATEGORY', null, CONFIG) && <CategoryItem category={post.category} />}
|
||||
{siteConfig('MEDIUM_POST_LIST_TAG', null, CONFIG) && post?.tagItems?.map(tag => <TagItemMini key={tag.name} tag={tag} />)}
|
||||
<TwikooCommentCount post={post} className='hover:underline'/>
|
||||
</div>
|
||||
|
||||
<div className="flex"></div>
|
||||
|
||||
{(!showPreview || showSummary) && (
|
||||
<main className="my-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{post.summary}
|
||||
</main>
|
||||
)}
|
||||
|
||||
{showPreview && (
|
||||
<div className="overflow-ellipsis truncate">
|
||||
<NotionPage post={post} />
|
||||
<div className="pointer-events-none border-t pt-8 border-dashed">
|
||||
<div className="w-full justify-start flex">
|
||||
<Link
|
||||
href={url}
|
||||
passHref
|
||||
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}
|
||||
<i className="ml-1 fas fa-angle-right" />
|
||||
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
className={
|
||||
'flex mt-2 items-center justify-start flex-wrap space-x-3 text-gray-400'
|
||||
}>
|
||||
<div className='text-sm py-1'>{post.date?.start_date}</div>
|
||||
{siteConfig('MEDIUM_POST_LIST_CATEGORY', null, CONFIG) && (
|
||||
<CategoryItem category={post.category} />
|
||||
)}
|
||||
{siteConfig('MEDIUM_POST_LIST_TAG', null, CONFIG) &&
|
||||
post?.tagItems?.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
<TwikooCommentCount post={post} className='hover:underline' />
|
||||
</div>
|
||||
|
||||
<div className='flex'></div>
|
||||
|
||||
{(!showPreview || showSummary) && (
|
||||
<main className='my-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7'>
|
||||
{post.summary}
|
||||
</main>
|
||||
)}
|
||||
|
||||
{showPreview && (
|
||||
<div className='overflow-ellipsis truncate'>
|
||||
<NotionPage post={post} />
|
||||
<div className='pointer-events-none border-t pt-8 border-dashed'>
|
||||
<div className='w-full justify-start flex'>
|
||||
<Link
|
||||
href={post?.href}
|
||||
passHref
|
||||
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}
|
||||
<i className='ml-1 fas fa-angle-right' />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
|
||||
export const MenuBarMobile = (props) => {
|
||||
export const MenuBarMobile = props => {
|
||||
const { customMenu, customNav } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
let links = [
|
||||
// { name: locale.NAV.INDEX, to: '/' || '/', show: true },
|
||||
{ name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('MEDIUM_MENU_CATEGORY', null, CONFIG) },
|
||||
{ name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('MEDIUM_MENU_TAG', null, CONFIG) },
|
||||
{ name: locale.NAV.ARCHIVE, to: '/archive', show: siteConfig('MEDIUM_MENU_ARCHIVE', null, CONFIG) }
|
||||
// { name: locale.NAV.SEARCH, to: '/search', show: siteConfig('MENU_SEARCH', null, CONFIG) }
|
||||
// { name: locale.NAV.INDEX, href: '/' || '/', show: true },
|
||||
{
|
||||
name: locale.COMMON.CATEGORY,
|
||||
href: '/category',
|
||||
show: siteConfig('MEDIUM_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.COMMON.TAGS,
|
||||
href: '/tag',
|
||||
show: siteConfig('MEDIUM_MENU_TAG', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: siteConfig('MEDIUM_MENU_ARCHIVE', null, CONFIG)
|
||||
}
|
||||
// { name: locale.NAV.SEARCH, href: '/search', show: siteConfig('MENU_SEARCH', null, CONFIG) }
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
@@ -30,8 +42,13 @@ export const MenuBarMobile = (props) => {
|
||||
|
||||
return (
|
||||
<nav id='nav' className=' text-md'>
|
||||
{links?.map((link, index) => <MenuItemCollapse onHeightChange={props.onHeightChange} key={index} link={link}/>)}
|
||||
|
||||
{links?.map((link, index) => (
|
||||
<MenuItemCollapse
|
||||
onHeightChange={props.onHeightChange}
|
||||
key={index}
|
||||
link={link}
|
||||
/>
|
||||
))}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export const MenuItemCollapse = props => {
|
||||
return null
|
||||
}
|
||||
|
||||
const selected = router.pathname === link.to || router.asPath === link.to
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
|
||||
const toggleShow = () => {
|
||||
changeShow(!show)
|
||||
@@ -43,7 +43,7 @@ export const MenuItemCollapse = props => {
|
||||
onClick={toggleShow}>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className='py-2 w-full my-auto items-center justify-between flex '>
|
||||
<div>
|
||||
@@ -79,7 +79,7 @@ export const MenuItemCollapse = props => {
|
||||
className='
|
||||
not:last-child:border-b-0 border-b dark:border-gray-800 py-2 px-14 cursor-pointer hover:bg-gray-100 dark:text-gray-200
|
||||
font-extralight dark:bg-black text-left justify-start text-gray-600 bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<div>
|
||||
<div
|
||||
className={`${sLink.icon} text-center w-3 mr-3 text-xs`}
|
||||
|
||||
@@ -12,7 +12,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
return null
|
||||
}
|
||||
const hasSubMenu = link?.subMenus?.length > 0
|
||||
const selected = router.pathname === link.to || router.asPath === link.to
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
|
||||
return (
|
||||
<li
|
||||
@@ -45,7 +45,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
? 'bg-green-600 text-white hover:text-white'
|
||||
: 'hover:text-green-600')
|
||||
}>
|
||||
<Link href={link?.to} target={link?.target}>
|
||||
<Link href={link?.href} target={link?.target}>
|
||||
{link?.icon && <i className={link?.icon} />} {link?.name}
|
||||
</Link>
|
||||
</div>
|
||||
@@ -60,7 +60,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
<li
|
||||
key={sLink.id}
|
||||
className='not:last-child:border-b-0 border-b text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 py-3 pr-6 pl-3'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-xs font-extralight'>
|
||||
{link?.icon && <i className={sLink?.icon}> </i>}
|
||||
{sLink.title}
|
||||
|
||||
@@ -9,19 +9,21 @@ export const NormalMenu = props => {
|
||||
return null
|
||||
}
|
||||
|
||||
const selected = (router.pathname === link.to) || (router.asPath === link.to)
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
|
||||
return <Link
|
||||
key={`${link.to}`}
|
||||
title={link.to}
|
||||
href={link.to}
|
||||
className={'py-0.5 duration-500 justify-between text-gray-500 dark:text-gray-300 hover:text-black hover:underline cursor-pointer flex flex-nowrap items-center ' +
|
||||
(selected ? 'text-black' : ' ')}>
|
||||
|
||||
<div className='my-auto items-center justify-center flex '>
|
||||
<div className={ 'hover:text-black'}>{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
|
||||
</Link>
|
||||
return (
|
||||
<Link
|
||||
key={`${link.href}`}
|
||||
title={link.href}
|
||||
href={link.href}
|
||||
className={
|
||||
'py-0.5 duration-500 justify-between text-gray-500 dark:text-gray-300 hover:text-black hover:underline cursor-pointer flex flex-nowrap items-center ' +
|
||||
(selected ? 'text-black' : ' ')
|
||||
}>
|
||||
<div className='my-auto items-center justify-center flex '>
|
||||
<div className={'hover:text-black'}>{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,21 +4,27 @@ import { useRouter } from 'next/router'
|
||||
export const MenuItemPCNormal = props => {
|
||||
const { link } = props
|
||||
const router = useRouter()
|
||||
const selected = (router.pathname === link.to) || (router.asPath === link.to)
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
if (!link || !link.show) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <Link
|
||||
key={`${link.id}-${link.to}`}
|
||||
title={link.to}
|
||||
href={link.to}
|
||||
className={'px-2 duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
|
||||
(selected ? 'bg-green-600 text-white hover:text-white' : 'hover:text-green-600')}>
|
||||
<div className='items-center justify-center flex '>
|
||||
<i className={link.icon} />
|
||||
<div className='ml-2 whitespace-nowrap'>{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
return (
|
||||
<Link
|
||||
key={`${link.id}-${link.href}`}
|
||||
title={link.href}
|
||||
href={link.href}
|
||||
className={
|
||||
'px-2 duration-300 text-sm justify-between dark:text-gray-300 cursor-pointer flex flex-nowrap items-center ' +
|
||||
(selected
|
||||
? 'bg-green-600 text-white hover:text-white'
|
||||
: 'hover:text-green-600')
|
||||
}>
|
||||
<div className='items-center justify-center flex '>
|
||||
<i className={link.icon} />
|
||||
<div className='ml-2 whitespace-nowrap'>{link.name}</div>
|
||||
</div>
|
||||
{link.slot}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import LogoBar from './LogoBar'
|
||||
import { useRef, useState } from 'react'
|
||||
import Collapse from '@/components/Collapse'
|
||||
import { MenuBarMobile } from './MenuBarMobile'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useRef, useState } from 'react'
|
||||
import CONFIG from '../config'
|
||||
import LogoBar from './LogoBar'
|
||||
import { MenuBarMobile } from './MenuBarMobile'
|
||||
import { MenuItemDrop } from './MenuItemDrop'
|
||||
|
||||
/**
|
||||
@@ -20,10 +20,30 @@ export default function TopNavBar(props) {
|
||||
const { locale } = useGlobal()
|
||||
|
||||
const defaultLinks = [
|
||||
{ icon: 'fas fa-th', name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('MEDIUM_MENU_CATEGORY', null, CONFIG) },
|
||||
{ icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('MEDIUM_MENU_TAG', null, CONFIG) },
|
||||
{ icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', show: siteConfig('MEDIUM_MENU_ARCHIVE', null, CONFIG) },
|
||||
{ icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: siteConfig('MEDIUM_MENU_SEARCH', null, CONFIG) }
|
||||
{
|
||||
icon: 'fas fa-th',
|
||||
name: locale.COMMON.CATEGORY,
|
||||
href: '/category',
|
||||
show: siteConfig('MEDIUM_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tag',
|
||||
name: locale.COMMON.TAGS,
|
||||
href: '/tag',
|
||||
show: siteConfig('MEDIUM_MENU_TAG', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: siteConfig('MEDIUM_MENU_ARCHIVE', null, CONFIG)
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
href: '/search',
|
||||
show: siteConfig('MEDIUM_MENU_SEARCH', null, CONFIG)
|
||||
}
|
||||
]
|
||||
|
||||
let links = defaultLinks.concat(customNav)
|
||||
@@ -42,33 +62,49 @@ export default function TopNavBar(props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div id='top-nav' className={'sticky top-0 lg:relative w-full z-40 ' + className}>
|
||||
|
||||
{/* 移动端折叠菜单 */}
|
||||
<Collapse type='vertical' collapseRef={collapseRef} isOpen={isOpen} className='md:hidden'>
|
||||
<div className='bg-white dark:bg-hexo-black-gray pt-1 py-2 lg:hidden '>
|
||||
<MenuBarMobile {...props} onHeightChange={(param) => collapseRef.current?.updateCollapseHeight(param)} />
|
||||
</div>
|
||||
</Collapse>
|
||||
|
||||
{/* 导航栏菜单 */}
|
||||
<div className='flex w-full h-12 shadow bg-white dark:bg-hexo-black-gray px-7 items-between'>
|
||||
|
||||
{/* 左侧图标Logo */}
|
||||
<LogoBar {...props} />
|
||||
|
||||
{/* 折叠按钮、仅移动端显示 */}
|
||||
<div className='mr-1 flex md:hidden justify-end items-center text-sm space-x-4 font-serif dark:text-gray-200'>
|
||||
<div onClick={toggleMenuOpen} className='cursor-pointer'>
|
||||
{isOpen ? <i className='fas fa-times' /> : <i className='fas fa-bars' />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 桌面端顶部菜单 */}
|
||||
<div className='hidden md:flex'>
|
||||
{links && links?.map((link, index) => <MenuItemDrop key={index} link={link}/>)}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id='top-nav'
|
||||
className={'sticky top-0 lg:relative w-full z-40 ' + className}>
|
||||
{/* 移动端折叠菜单 */}
|
||||
<Collapse
|
||||
type='vertical'
|
||||
collapseRef={collapseRef}
|
||||
isOpen={isOpen}
|
||||
className='md:hidden'>
|
||||
<div className='bg-white dark:bg-hexo-black-gray pt-1 py-2 lg:hidden '>
|
||||
<MenuBarMobile
|
||||
{...props}
|
||||
onHeightChange={param =>
|
||||
collapseRef.current?.updateCollapseHeight(param)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Collapse>
|
||||
|
||||
{/* 导航栏菜单 */}
|
||||
<div className='flex w-full h-12 shadow bg-white dark:bg-hexo-black-gray px-7 items-between'>
|
||||
{/* 左侧图标Logo */}
|
||||
<LogoBar {...props} />
|
||||
|
||||
{/* 折叠按钮、仅移动端显示 */}
|
||||
<div className='mr-1 flex md:hidden justify-end items-center text-sm space-x-4 font-serif dark:text-gray-200'>
|
||||
<div onClick={toggleMenuOpen} className='cursor-pointer'>
|
||||
{isOpen ? (
|
||||
<i className='fas fa-times' />
|
||||
) : (
|
||||
<i className='fas fa-bars' />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 桌面端顶部菜单 */}
|
||||
<div className='hidden md:flex'>
|
||||
{links &&
|
||||
links?.map((link, index) => (
|
||||
<MenuItemDrop key={index} link={link} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
|
||||
/**
|
||||
@@ -9,30 +7,30 @@ import Link from 'next/link'
|
||||
* @returns
|
||||
*/
|
||||
export default function BlogListGroupByDate({ archiveTitle, archivePosts }) {
|
||||
return <div key={archiveTitle}>
|
||||
<div id={archiveTitle} className="pt-16 pb-4 text-3xl dark:text-gray-300" >
|
||||
return (
|
||||
<div key={archiveTitle}>
|
||||
<div id={archiveTitle} className='pt-16 pb-4 text-3xl dark:text-gray-300'>
|
||||
{archiveTitle}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<ul>
|
||||
{archivePosts[archiveTitle].map(post => {
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return <li
|
||||
key={post.id}
|
||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
||||
>
|
||||
<div id={post?.publishDay}>
|
||||
<span className="text-gray-400">
|
||||
{post?.publishDay}
|
||||
</span>{' '}
|
||||
|
||||
<Link href={url} className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||
{post.title}
|
||||
</Link>
|
||||
</div>
|
||||
return (
|
||||
<li
|
||||
key={post.id}
|
||||
className='border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500'>
|
||||
<div id={post?.publishDay}>
|
||||
<span className='text-gray-400'>{post?.publishDay}</span>
|
||||
<Link
|
||||
href={post?.href}
|
||||
className='dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600'>
|
||||
{post.title}
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import TagItemMini from './TagItemMini'
|
||||
|
||||
const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
// 主题默认强制显示图片
|
||||
if (post && !post.pageCoverThumbnail) {
|
||||
post.pageCoverThumbnail = siteInfo?.pageCover || siteConfig('RANDOM_IMAGE_URL')
|
||||
post.pageCoverThumbnail =
|
||||
siteInfo?.pageCover || siteConfig('RANDOM_IMAGE_URL')
|
||||
}
|
||||
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return (
|
||||
<article data-wow-delay=".2s" className='wow fadeInUp w-full mb-4 cursor-pointer overflow-hidden shadow-movie dark:bg-hexo-black-gray'>
|
||||
<Link href={url} passHref legacyBehavior>
|
||||
<article
|
||||
data-wow-delay='.2s'
|
||||
className='wow fadeInUp w-full mb-4 cursor-pointer overflow-hidden shadow-movie dark:bg-hexo-black-gray'>
|
||||
<Link href={post?.href} passHref legacyBehavior>
|
||||
{/* 固定高度 ,空白用图片拉升填充 */}
|
||||
<div className='group flex flex-col aspect-[2/3] justify-between relative'>
|
||||
{/* 图片 填充卡片 */}
|
||||
@@ -40,7 +40,9 @@ const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
</div>
|
||||
{/* 阴影遮罩 */}
|
||||
<h2 className='absolute bottom-10 px-6 transition-all duration-200 text-white text-2xl font-semibold break-words shadow-text z-20'>
|
||||
{siteConfig('POST_TITLE_ICON') && <NotionIcon icon={post.pageIcon} />}
|
||||
{siteConfig('POST_TITLE_ICON') && (
|
||||
<NotionIcon icon={post.pageIcon} />
|
||||
)}
|
||||
{post.title}
|
||||
</h2>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import LazyImage from '@/components/LazyImage'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import CONFIG from '../config'
|
||||
|
||||
@@ -13,7 +12,11 @@ import CONFIG from '../config'
|
||||
export default function BlogRecommend(props) {
|
||||
const { recommendPosts, siteInfo } = props
|
||||
const { locale } = useGlobal()
|
||||
if (!siteConfig('MOVIE_ARTICLE_RECOMMEND', null, CONFIG) || !recommendPosts || recommendPosts.length === 0) {
|
||||
if (
|
||||
!siteConfig('MOVIE_ARTICLE_RECOMMEND', null, CONFIG) ||
|
||||
!recommendPosts ||
|
||||
recommendPosts.length === 0
|
||||
) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
@@ -27,21 +30,22 @@ export default function BlogRecommend(props) {
|
||||
</div>
|
||||
<div className='flex flex-nowrap gap-4'>
|
||||
{recommendPosts.map(post => {
|
||||
const headerImage = post?.pageCoverThumbnail ? post.pageCoverThumbnail : siteInfo?.pageCover
|
||||
const url = checkContainHttp(post.slug)
|
||||
? sliceUrlFromHttp(post.slug)
|
||||
: `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
const headerImage = post?.pageCoverThumbnail
|
||||
? post.pageCoverThumbnail
|
||||
: siteInfo?.pageCover
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={post.id}
|
||||
title={post.title}
|
||||
href={url}
|
||||
href={post?.href}
|
||||
passHref
|
||||
className='flex rounded-lg h-60 w-48 cursor-pointer overflow-hidden'>
|
||||
<div className='h-full w-full relative group shadow-movie'>
|
||||
<div className='absolute bottom-4 w-full z-20 duration-300 '>
|
||||
<div className='z-10 text-lg px-4 font-bold text-white shadow-text select-none'>{post.title}</div>
|
||||
<div className='z-10 text-lg px-4 font-bold text-white shadow-text select-none'>
|
||||
{post.title}
|
||||
</div>
|
||||
</div>
|
||||
{/* 卡片的阴影遮罩,为了凸显图片上的文字 */}
|
||||
<div className='h-3/4 w-full absolute left-0 bottom-0 z-10'>
|
||||
|
||||
@@ -28,25 +28,25 @@ export const Header = props => {
|
||||
id: 1,
|
||||
icon: 'fa-solid fa-house',
|
||||
name: locale.NAV.INDEX,
|
||||
to: '/',
|
||||
href: '/',
|
||||
show: siteConfig('MOVIE_MENU_INDEX', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: 'fas fa-search',
|
||||
name: locale.NAV.SEARCH,
|
||||
to: '/search',
|
||||
href: '/search',
|
||||
show: siteConfig('MOVIE_MENU_SEARCH', null, CONFIG)
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: 'fas fa-archive',
|
||||
name: locale.NAV.ARCHIVE,
|
||||
to: '/archive',
|
||||
href: '/archive',
|
||||
show: siteConfig('MOVIE_MENU_ARCHIVE', null, CONFIG)
|
||||
}
|
||||
// { icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('MENU_CATEGORY', null, CONFIG) },
|
||||
// { icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('MENU_TAG', null, CONFIG) }
|
||||
// { icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, href: '/category', show: siteConfig('MENU_CATEGORY', null, CONFIG) },
|
||||
// { icon: 'fas fa-tag', name: locale.COMMON.TAGS, href: '/tag', show: siteConfig('MENU_TAG', null, CONFIG) }
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
@@ -109,11 +109,18 @@ export const Header = props => {
|
||||
<div className='md:w-auto text-center flex'>
|
||||
{/* 右侧菜单 */}
|
||||
<>
|
||||
<nav id='nav-mobile' className='leading-8 justify-center w-full hidden md:flex'>
|
||||
{links?.map((link, index) => link && link.show && <MenuItemDrop key={index} link={link} />)}
|
||||
<nav
|
||||
id='nav-mobile'
|
||||
className='leading-8 justify-center w-full hidden md:flex'>
|
||||
{links?.map(
|
||||
(link, index) =>
|
||||
link && link.show && <MenuItemDrop key={index} link={link} />
|
||||
)}
|
||||
</nav>
|
||||
|
||||
<div onClick={toggleShowSearchInput} className='flex items-center cursor-pointer'>
|
||||
<div
|
||||
onClick={toggleShowSearchInput}
|
||||
className='flex items-center cursor-pointer'>
|
||||
<i className='fas fa-search dark:text-white'></i>
|
||||
</div>
|
||||
<div
|
||||
@@ -130,7 +137,9 @@ export const Header = props => {
|
||||
autoComplete='off'
|
||||
placeholder='Type then hit enter to search...'
|
||||
/>
|
||||
<button onClick={handleSearch} className='w-full bg-[#383838] rounded py-2'>
|
||||
<button
|
||||
onClick={handleSearch}
|
||||
className='w-full bg-[#383838] rounded py-2'>
|
||||
{locale.COMMON.SEARCH} 搜索
|
||||
</button>
|
||||
</div>
|
||||
@@ -138,7 +147,11 @@ export const Header = props => {
|
||||
{/* 移动端按钮 */}
|
||||
<div className='md:hidden'>
|
||||
<div onClick={toggleMenuOpen} className='w-8 cursor-pointer'>
|
||||
{isOpen ? <i className='fas fa-times' /> : <i className='fas fa-bars' />}
|
||||
{isOpen ? (
|
||||
<i className='fas fa-times' />
|
||||
) : (
|
||||
<i className='fas fa-bars' />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
@@ -147,13 +160,17 @@ export const Header = props => {
|
||||
|
||||
<Collapse collapseRef={collapseRef} type='vertical' isOpen={isOpen}>
|
||||
{/* 移动端菜单 */}
|
||||
<menu id='nav-menu-mobile' className='block md:hidden my-auto justify-start'>
|
||||
<menu
|
||||
id='nav-menu-mobile'
|
||||
className='block md:hidden my-auto justify-start'>
|
||||
{links?.map(
|
||||
(link, index) =>
|
||||
link &&
|
||||
link.show && (
|
||||
<MenuItemCollapse
|
||||
onHeightChange={param => collapseRef.current?.updateCollapseHeight(param)}
|
||||
onHeightChange={param =>
|
||||
collapseRef.current?.updateCollapseHeight(param)
|
||||
}
|
||||
key={index}
|
||||
link={link}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,6 @@ import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* 最新文章列表
|
||||
@@ -21,9 +20,9 @@ const LatestPostsGroup = ({ latestPosts }) => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="pb-1 px-2 flex flex-nowrap justify-between">
|
||||
<div className="text-2xl text-gray-600 dark:text-gray-200">
|
||||
<i className="mr-2 fas fa-history" />
|
||||
<div className='pb-1 px-2 flex flex-nowrap justify-between'>
|
||||
<div className='text-2xl text-gray-600 dark:text-gray-200'>
|
||||
<i className='mr-2 fas fa-history' />
|
||||
{locale.COMMON.LATEST_POSTS}
|
||||
</div>
|
||||
</div>
|
||||
@@ -31,17 +30,14 @@ const LatestPostsGroup = ({ latestPosts }) => {
|
||||
{latestPosts.map(post => {
|
||||
const selected =
|
||||
currentPath === `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
const url = checkContainHttp(post.slug)
|
||||
? sliceUrlFromHttp(post.slug)
|
||||
: `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={post.id}
|
||||
title={post.title}
|
||||
href={url}
|
||||
href={post?.href}
|
||||
passHref
|
||||
className={'my-1 flex'}
|
||||
>
|
||||
className={'my-1 flex'}>
|
||||
<div
|
||||
className={
|
||||
(selected
|
||||
@@ -49,9 +45,8 @@ const LatestPostsGroup = ({ latestPosts }) => {
|
||||
: 'text-gray-500 dark:text-green-400 ') +
|
||||
' py-1 flex hover:bg-gray-500 px-2 duration-200 w-full ' +
|
||||
'hover:text-white dark:hover:text-white cursor-pointer'
|
||||
}
|
||||
>
|
||||
<li className="line-clamp-2">{post.title}</li>
|
||||
}>
|
||||
<li className='line-clamp-2'>{post.title}</li>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
|
||||
@@ -33,7 +33,7 @@ export const MenuItemCollapse = props => {
|
||||
onClick={toggleShow}>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className='flex justify-between pl-2 pr-4 dark:text-gray-200 no-underline tracking-widest'>
|
||||
<span className=' transition-all items-center duration-200'>
|
||||
@@ -67,7 +67,7 @@ export const MenuItemCollapse = props => {
|
||||
<div
|
||||
key={index}
|
||||
className='dark:text-gray-200 text-left px-3 justify-start tracking-widest transition-all duration-200 py-3 pr-6'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-sm ml-4 whitespace-nowrap'>
|
||||
{link?.icon && <i className={sLink.icon + ' mr-2'} />}{' '}
|
||||
{sLink.title}
|
||||
|
||||
@@ -15,7 +15,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
onMouseOut={() => changeShow(false)}>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className='select-none menu-link pl-2 pr-4 no-underline tracking-widest pb-1'>
|
||||
{link?.icon && <i className={link?.icon} />} {link?.name}
|
||||
@@ -43,7 +43,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
<li
|
||||
key={index}
|
||||
className='cursor-pointer text-start dark:bg-hexo-black-gray dark:hover:bg-gray-300 hover:bg-gray-300 hover:text-black tracking-widest transition-all duration-200 dark:border-gray-800 py-1 pr-6 pl-3'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-sm'>
|
||||
{link?.icon && <i className={sLink?.icon}> </i>}
|
||||
{sLink.title}
|
||||
|
||||
@@ -5,10 +5,16 @@ import Link from 'next/link'
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
export const NormalMenuItem = (props) => {
|
||||
export const NormalMenuItem = props => {
|
||||
const { link } = props
|
||||
return link?.show && <Link href={link.to} key={link.to}
|
||||
className="px-2 md:pl-0 md:mr-3 my-4 md:pr-3 text-gray-700 dark:text-gray-200 no-underline md:border-r border-gray-light">
|
||||
return (
|
||||
link?.show && (
|
||||
<Link
|
||||
href={link.href}
|
||||
key={link.href}
|
||||
className='px-2 md:pl-0 md:mr-3 my-4 md:pr-3 text-gray-700 dark:text-gray-200 no-underline md:border-r border-gray-light'>
|
||||
{link.name}
|
||||
</Link>
|
||||
</Link>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,27 +5,25 @@ import Link from 'next/link'
|
||||
* @param {prev,next} param0
|
||||
* @returns
|
||||
*/
|
||||
export default function ArticleAround ({ prev, next }) {
|
||||
export default function ArticleAround({ prev, next }) {
|
||||
if (!prev || !next) {
|
||||
return <></>
|
||||
}
|
||||
return (
|
||||
<section className='text-gray-800 dark:text-gray-400 h-12 flex items-center justify-between space-x-5 my-4'>
|
||||
<Link
|
||||
href={`/${prev.slug}`}
|
||||
href={prev.href}
|
||||
passHref
|
||||
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}
|
||||
</Link>
|
||||
<Link
|
||||
href={`/${next.slug}`}
|
||||
href={next.href}
|
||||
passHref
|
||||
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' />
|
||||
|
||||
</Link>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import Link from 'next/link'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* 归档分组
|
||||
@@ -9,31 +7,30 @@ import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
*/
|
||||
export default function BlogArchiveItem({ archiveTitle, archivePosts }) {
|
||||
return (
|
||||
<div key={archiveTitle}>
|
||||
<div id={archiveTitle} className="pt-16 pb-4 text-3xl dark:text-gray-300" >
|
||||
{archiveTitle}
|
||||
</div>
|
||||
<ul>
|
||||
{archivePosts[archiveTitle]?.map(post => {
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
return <li key={post.id}
|
||||
className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500"
|
||||
>
|
||||
<div id={post?.publishDay}>
|
||||
<span className="text-gray-400">
|
||||
{post.date?.start_date}
|
||||
</span>{' '}
|
||||
|
||||
|
||||
<Link passHref href={url}
|
||||
className="dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600">
|
||||
{post.title}
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<div key={archiveTitle}>
|
||||
<div id={archiveTitle} className='pt-16 pb-4 text-3xl dark:text-gray-300'>
|
||||
{archiveTitle}
|
||||
</div>
|
||||
<ul>
|
||||
{archivePosts[archiveTitle]?.map(post => {
|
||||
return (
|
||||
<li
|
||||
key={post.id}
|
||||
className='border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500'>
|
||||
<div id={post?.publishDay}>
|
||||
<span className='text-gray-400'>{post.date?.start_date}</span>{' '}
|
||||
|
||||
<Link
|
||||
passHref
|
||||
href={post?.href}
|
||||
className='dark:text-gray-400 dark:hover:text-gray-300 overflow-x-hidden hover:underline cursor-pointer text-gray-600'>
|
||||
{post.title}
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,26 +1,49 @@
|
||||
import Link from 'next/link'
|
||||
import NotionIcon from './NotionIcon'
|
||||
import { useRouter } from 'next/router'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import { checkContainHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import NotionIcon from './NotionIcon'
|
||||
|
||||
/**
|
||||
* 博客卡牌
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
const BlogPostCard = ({ post, className }) => {
|
||||
const router = useRouter()
|
||||
const currentSelected = router.asPath.split('?')[0] === '/' + post.slug
|
||||
let pageIcon = post.pageIcon !== '' ? post.pageIcon : siteConfig('IMG_LAZY_LOAD_PLACEHOLDER')
|
||||
pageIcon = post.pageIcon.indexOf('amazonaws.com') !== -1 ? post.pageIcon + '&width=88' : post.pageIcon
|
||||
const url = checkContainHttp(post.slug) ? sliceUrlFromHttp(post.slug) : `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
let pageIcon =
|
||||
post.pageIcon !== ''
|
||||
? post.pageIcon
|
||||
: siteConfig('IMG_LAZY_LOAD_PLACEHOLDER')
|
||||
pageIcon =
|
||||
post.pageIcon.indexOf('amazonaws.com') !== -1
|
||||
? post.pageIcon + '&width=88'
|
||||
: post.pageIcon
|
||||
return (
|
||||
<Link href={`${url}`} target={(checkContainHttp(post.slug) ? '_blank' : '_self')} passHref>
|
||||
<div key={post.id} className={`${className} h-full rounded-2xl p-4 dark:bg-neutral-800 cursor-pointer bg-white hover:bg-white dark:hover:bg-gray-800 ${currentSelected ? 'bg-green-50 text-green-500' : ''}`}>
|
||||
<div className="stack-entry w-full flex space-x-3 select-none dark:text-neutral-200">
|
||||
{siteConfig('POST_TITLE_ICON') && <NotionIcon icon={pageIcon} size='10' className='text-6xl w-11 h-11 mx-1 my-0 flex-none' />}
|
||||
<div className="stack-comment flex-auto">
|
||||
<p className="title font-bold">{post.title}</p>
|
||||
<p className="description font-normal">{post.summary ? post.summary : '暂无简介'}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Link
|
||||
href={post?.href}
|
||||
target={checkContainHttp(post.slug) ? '_blank' : '_self'}
|
||||
passHref>
|
||||
<div
|
||||
key={post.id}
|
||||
className={`${className} h-full rounded-2xl p-4 dark:bg-neutral-800 cursor-pointer bg-white hover:bg-white dark:hover:bg-gray-800 ${currentSelected ? 'bg-green-50 text-green-500' : ''}`}>
|
||||
<div className='stack-entry w-full flex space-x-3 select-none dark:text-neutral-200'>
|
||||
{siteConfig('POST_TITLE_ICON') && (
|
||||
<NotionIcon
|
||||
icon={pageIcon}
|
||||
size='10'
|
||||
className='text-6xl w-11 h-11 mx-1 my-0 flex-none'
|
||||
/>
|
||||
)}
|
||||
<div className='stack-comment flex-auto'>
|
||||
<p className='title font-bold'>{post.title}</p>
|
||||
<p className='description font-normal'>
|
||||
{post.summary ? post.summary : '暂无简介'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import CONFIG from '../config'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
|
||||
/**
|
||||
@@ -8,16 +8,28 @@ import { MenuItemCollapse } from './MenuItemCollapse'
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
export const MenuBarMobile = (props) => {
|
||||
export const MenuBarMobile = props => {
|
||||
const { customMenu, customNav } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
let links = [
|
||||
// { name: locale.NAV.INDEX, to: '/' || '/', show: true },
|
||||
{ name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('NAV_MENU_CATEGORY', null, CONFIG) },
|
||||
{ name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('NAV_MENU_TAG', null, CONFIG) },
|
||||
{ name: locale.NAV.ARCHIVE, to: '/archive', show: siteConfig('NAV_MENU_ARCHIVE', null, CONFIG) }
|
||||
// { name: locale.NAV.SEARCH, to: '/search', show: siteConfig('MENU_SEARCH', null, CONFIG) }
|
||||
// { name: locale.NAV.INDEX, href: '/' || '/', show: true },
|
||||
{
|
||||
name: locale.COMMON.CATEGORY,
|
||||
href: '/category',
|
||||
show: siteConfig('NAV_MENU_CATEGORY', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.COMMON.TAGS,
|
||||
href: '/tag',
|
||||
show: siteConfig('NAV_MENU_TAG', null, CONFIG)
|
||||
},
|
||||
{
|
||||
name: locale.NAV.ARCHIVE,
|
||||
href: '/archive',
|
||||
show: siteConfig('NAV_MENU_ARCHIVE', null, CONFIG)
|
||||
}
|
||||
// { name: locale.NAV.SEARCH, href: '/search', show: siteConfig('MENU_SEARCH', null, CONFIG) }
|
||||
]
|
||||
|
||||
if (customNav) {
|
||||
@@ -35,7 +47,13 @@ export const MenuBarMobile = (props) => {
|
||||
|
||||
return (
|
||||
<nav id='nav' className=' text-md'>
|
||||
{links?.map((link, index) => <MenuItemCollapse onHeightChange={props.onHeightChange} key={index} link={link}/>)}
|
||||
{links?.map((link, index) => (
|
||||
<MenuItemCollapse
|
||||
onHeightChange={props.onHeightChange}
|
||||
key={index}
|
||||
link={link}
|
||||
/>
|
||||
))}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -21,52 +21,61 @@ export const MenuItem = ({ link }) => {
|
||||
}
|
||||
|
||||
// #号加标题 快速跳转到指定锚点
|
||||
const isAnchor = link?.to === '#'
|
||||
const url = isAnchor ? `#${link.name}` : link.to
|
||||
const isAnchor = link?.href === '#'
|
||||
const url = isAnchor ? `#${link.name}` : link.href
|
||||
|
||||
return <>
|
||||
{/* 菜单 */}
|
||||
<div
|
||||
onClick={toggleOpenSubMenu}
|
||||
className='nav-menu dark:text-neutral-400 text-gray-500 hover:text-black dark:hover:text-white text-sm text-gray w-full items-center duration-300 pt-2 font-light select-none flex justify-between cursor-pointer' key={link?.to}>
|
||||
|
||||
{link?.subMenus
|
||||
? (<>
|
||||
<span className='dark:text-neutral-400 dark:hover:text-white font-bold w-full display-block'>
|
||||
<i className={`text-base ${link?.icon ? link?.icon : ''} mr-1`} />{link?.title}
|
||||
</span>
|
||||
<div className='inline-flex items-center select-none pointer-events-none '>
|
||||
<i className={`${isOpen ? '-rotate-90' : ''} text-xs dark:text-neutral-500 text-gray-300 hover:text-black dark:hover:text-white-400 px-2 fas fa-chevron-left transition-all duration-200`}></i>
|
||||
</div>
|
||||
</>)
|
||||
: (
|
||||
<Link href={url} className='dark:text-neutral-400 dark:hover:text-white font-bold w-full display-block'>
|
||||
<i className={`text-base ${link?.icon ? link?.icon : (isAnchor ? 'fas fa-hashtag' : '')} mr-1`} />{link?.title}
|
||||
</Link>
|
||||
)
|
||||
|
||||
}
|
||||
</div>
|
||||
|
||||
{/* 子菜单按钮 */}
|
||||
{link?.subMenus && (
|
||||
<Collapse isOpen={isOpen} key='collapse'>
|
||||
{
|
||||
link?.subMenus?.map((sLink, index) => {
|
||||
// #号加标题 快速跳转到指定锚点
|
||||
const sIsAnchor = sLink?.to === '#'
|
||||
const sUrl = sIsAnchor ? `#${sLink.name}` : sLink.to
|
||||
return <div key={index} className='nav-submenu'>
|
||||
<Link href={sUrl}>
|
||||
<span className='dark:text-neutral-400 text-gray-500 hover:text-black dark:hover:text-white text-xs font-bold'>
|
||||
<i className={`text-xs mr-1 ${sLink?.icon ? sLink?.icon : (sIsAnchor ? 'fas fa-hashtag' : '')}`} />{sLink.title}</span>
|
||||
</Link>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
|
||||
</Collapse>
|
||||
return (
|
||||
<>
|
||||
{/* 菜单 */}
|
||||
<div
|
||||
onClick={toggleOpenSubMenu}
|
||||
className='nav-menu dark:text-neutral-400 text-gray-500 hover:text-black dark:hover:text-white text-sm text-gray w-full items-center duration-300 pt-2 font-light select-none flex justify-between cursor-pointer'
|
||||
key={link?.href}>
|
||||
{link?.subMenus ? (
|
||||
<>
|
||||
<span className='dark:text-neutral-400 dark:hover:text-white font-bold w-full display-block'>
|
||||
<i className={`text-base ${link?.icon ? link?.icon : ''} mr-1`} />
|
||||
{link?.title}
|
||||
</span>
|
||||
<div className='inline-flex items-center select-none pointer-events-none '>
|
||||
<i
|
||||
className={`${isOpen ? '-rotate-90' : ''} text-xs dark:text-neutral-500 text-gray-300 hover:text-black dark:hover:text-white-400 px-2 fas fa-chevron-left transition-all duration-200`}></i>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<Link
|
||||
href={url}
|
||||
className='dark:text-neutral-400 dark:hover:text-white font-bold w-full display-block'>
|
||||
<i
|
||||
className={`text-base ${link?.icon ? link?.icon : isAnchor ? 'fas fa-hashtag' : ''} mr-1`}
|
||||
/>
|
||||
{link?.title}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 子菜单按钮 */}
|
||||
{link?.subMenus && (
|
||||
<Collapse isOpen={isOpen} key='collapse'>
|
||||
{link?.subMenus?.map((sLink, index) => {
|
||||
// #号加标题 快速跳转到指定锚点
|
||||
const sIsAnchor = sLink?.href === '#'
|
||||
const sUrl = sIsAnchor ? `#${sLink.name}` : sLink.href
|
||||
return (
|
||||
<div key={index} className='nav-submenu'>
|
||||
<Link href={sUrl}>
|
||||
<span className='dark:text-neutral-400 text-gray-500 hover:text-black dark:hover:text-white text-xs font-bold'>
|
||||
<i
|
||||
className={`text-xs mr-1 ${sLink?.icon ? sLink?.icon : sIsAnchor ? 'fas fa-hashtag' : ''}`}
|
||||
/>
|
||||
{sLink.title}
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</Collapse>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export const MenuItemCollapse = props => {
|
||||
return null
|
||||
}
|
||||
|
||||
const selected = router.pathname === link.to || router.asPath === link.to
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
|
||||
const toggleShow = () => {
|
||||
changeShow(!show)
|
||||
@@ -42,7 +42,7 @@ export const MenuItemCollapse = props => {
|
||||
onClick={toggleShow}>
|
||||
{!hasSubMenu && (
|
||||
<Link
|
||||
href={link?.to}
|
||||
href={link?.href}
|
||||
target={link?.target}
|
||||
className='py-2 w-full my-auto items-center justify-between flex '>
|
||||
<div>
|
||||
@@ -78,7 +78,7 @@ export const MenuItemCollapse = props => {
|
||||
className='
|
||||
py-2 px-14 cursor-pointer hover:bg-gray-100 dark:text-gray-400 dark:hover:text-white font-bold
|
||||
dark:bg-black text-left justify-start text-gray-600 bg-gray-50 bg-opacity-20 dark:hover:bg-gray-600 tracking-widest transition-all duration-200'>
|
||||
{/* <Link href={sLink.to} target={link?.target}> */}
|
||||
{/* <Link href={sLink.href} target={link?.target}> */}
|
||||
<a href={`/#${sLink.title}`} target={'_self'}>
|
||||
<div>
|
||||
<div
|
||||
|
||||
@@ -12,7 +12,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
return null
|
||||
}
|
||||
const hasSubMenu = link?.subMenus?.length > 0
|
||||
const selected = router.pathname === link.to || router.asPath === link.to
|
||||
const selected = router.pathname === link.href || router.asPath === link.href
|
||||
|
||||
return (
|
||||
<li
|
||||
@@ -45,7 +45,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
? 'bg-green-600 text-white hover:text-white'
|
||||
: 'hover:text-green-600')
|
||||
}>
|
||||
<Link href={link?.to} target={link?.target}>
|
||||
<Link href={link?.href} target={link?.target}>
|
||||
{link?.icon && <i className={link?.icon} />} {link?.name}
|
||||
</Link>
|
||||
</div>
|
||||
@@ -60,7 +60,7 @@ export const MenuItemDrop = ({ link }) => {
|
||||
<li
|
||||
key={index}
|
||||
className='not:last-child:border-b-0 border-b text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-900 tracking-widest transition-all duration-200 dark:border-gray-800 py-3 pr-6 pl-3'>
|
||||
<Link href={sLink.to} target={link?.target}>
|
||||
<Link href={sLink.href} target={link?.target}>
|
||||
<span className='text-xs font-extralight'>
|
||||
{link?.icon && <i className={sLink?.icon}> </i>}
|
||||
{sLink.title}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user