mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
magzine 微调
This commit is contained in:
@@ -7,9 +7,9 @@ export default function CategoryItem({ selected, category, categoryCount }) {
|
||||
passHref
|
||||
className={
|
||||
(selected
|
||||
? ' bg-gray-600 text-white '
|
||||
? 'bg-gray-600 text-white '
|
||||
: 'dark:text-gray-400 text-gray-900 ') +
|
||||
'text-sm hover:underline flex text-md items-center duration-300 cursor-pointer py-1 whitespace-nowrap'
|
||||
'text-sm font-semibold hover:underline flex text-md items-center duration-300 cursor-pointer py-1 whitespace-nowrap'
|
||||
}>
|
||||
<div>
|
||||
{category} {categoryCount && `(${categoryCount})`}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useRouter } from 'next/router'
|
||||
* @constructor
|
||||
*/
|
||||
const PostGroupLatest = props => {
|
||||
const { latestPosts } = props
|
||||
const { latestPosts, vertical } = props
|
||||
// 获取当前路径
|
||||
const currentPath = useRouter().asPath
|
||||
const { locale, siteInfo } = useGlobal()
|
||||
@@ -23,12 +23,12 @@ const PostGroupLatest = props => {
|
||||
return (
|
||||
<>
|
||||
{/* 标题 */}
|
||||
<div className=' mb-2 px-1 flex flex-nowrap justify-between'>
|
||||
<div className='mb-2 px-1 flex flex-nowrap justify-between'>
|
||||
<div className='font-bold text-lg'>{locale.COMMON.LATEST_POSTS}</div>
|
||||
</div>
|
||||
|
||||
{/* 文章列表 */}
|
||||
<div className='grid grid-cols-1 lg:grid-cols-4'>
|
||||
<div className={`grid grid-cols-1 ${!vertical ? 'lg:grid-cols-4' : ''}`}>
|
||||
{latestPosts.map(post => {
|
||||
const selected =
|
||||
currentPath === `${siteConfig('SUB_PATH', '')}/${post.slug}`
|
||||
|
||||
@@ -2,6 +2,7 @@ import LazyImage from '@/components/LazyImage'
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { formatDateFmt } from '@/lib/utils/formatDate'
|
||||
import Link from 'next/link'
|
||||
import CategoryItem from './CategoryItem'
|
||||
|
||||
@@ -14,19 +15,19 @@ const PostItemCard = ({ post }) => {
|
||||
const cover = post?.pageCoverThumbnail || siteInfo?.pageCover
|
||||
return (
|
||||
<div key={post.id} className='mb-6 max-w-screen-2xl'>
|
||||
<div className='flex flex-col'>
|
||||
<div className='flex flex-col space-y-3'>
|
||||
{siteConfig('MAGZINE_POST_LIST_COVER') && (
|
||||
<Link
|
||||
href={post?.href}
|
||||
passHref
|
||||
className={
|
||||
'cursor-pointer hover:underline leading-tight text-gray-700 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-400'
|
||||
'cursor-pointer hover:underline leading-tight text-gray-700 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-400'
|
||||
}>
|
||||
<div className='w-full h-40 aspect-video overflow-hidden mb-2'>
|
||||
<LazyImage
|
||||
src={cover}
|
||||
style={cover ? {} : { height: '0px' }}
|
||||
className='w-full h-40 aspect-video object-cover hover:scale-125 duration-150'
|
||||
className='w-full h-40 aspect-video object-cover'
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
@@ -39,7 +40,7 @@ const PostItemCard = ({ post }) => {
|
||||
href={post?.href}
|
||||
passHref
|
||||
className={
|
||||
'text-lg cursor-pointer hover:underline leading-tight text-gray-700 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-400'
|
||||
'text-xl cursor-pointer hover:underline leading-tight text-gray-700 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-400'
|
||||
}>
|
||||
<h2>
|
||||
{siteConfig('POST_TITLE_ICON') && (
|
||||
@@ -49,7 +50,9 @@ const PostItemCard = ({ post }) => {
|
||||
</h2>
|
||||
</Link>
|
||||
|
||||
<div className='text-sm py-1'>{post.date?.start_date}</div>
|
||||
<div className='text-sm'>
|
||||
{formatDateFmt(post.publishDate, 'yyyy-MM')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import NotionIcon from '@/components/NotionIcon'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import CategoryItem from './CategoryItem'
|
||||
|
||||
@@ -10,12 +9,10 @@ import CategoryItem from './CategoryItem'
|
||||
* @returns
|
||||
*/
|
||||
const PostItemCardSimple = ({ post, showSummary }) => {
|
||||
const showPreview = siteConfig('MAGZINE_POST_LIST_PREVIEW') && post.blockMap
|
||||
const { locale } = useGlobal()
|
||||
return (
|
||||
<div
|
||||
key={post.id}
|
||||
className='lg:mb-6 max-w-screen-2xl border-t mr-8 py-2 gap-y-4 flex flex-col dark:border-gray-800 '>
|
||||
className='lg:mb-6 max-w-screen-2xl border-t border-gray-300 mr-8 py-2 gap-y-3 flex flex-col dark:border-gray-800 '>
|
||||
<div className='flex mr-2 items-center'>
|
||||
{siteConfig('MAGZINE_POST_LIST_CATEGORY') && (
|
||||
<CategoryItem category={post.category} />
|
||||
@@ -35,7 +32,7 @@ const PostItemCardSimple = ({ post, showSummary }) => {
|
||||
</h2>
|
||||
</Link>
|
||||
|
||||
<div className='text-sm py-2 text-gray-700'>{post.date?.start_date}</div>
|
||||
<div className='text-sm text-gray-700'>{post.date?.start_date}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Link from 'next/link'
|
||||
import PostItemCard from './PostItemCard'
|
||||
import PostListEmpty from './PostListEmpty'
|
||||
import Swiper from './Swiper'
|
||||
|
||||
/**
|
||||
* 博文水平列表
|
||||
@@ -15,22 +16,34 @@ const PostListHorizontal = ({ title, href, posts, hasBg }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`w-full py-10 px-2 lg:px-0 ${hasBg ? 'bg-[#F6F6F1] dark:bg-black' : ''}`}>
|
||||
<div
|
||||
className={`w-full py-10 px-2 lg:px-0 ${hasBg ? 'bg-[#F6F6F1] dark:bg-black' : ''}`}>
|
||||
<div className='max-w-screen-2xl w-full mx-auto'>
|
||||
{/* 标题 */}
|
||||
<div className='flex justify-between items-center py-6'>
|
||||
<h3 className='text-2xl'>{title}</h3>
|
||||
<Link className='text-lg underline' href={href}>
|
||||
<span>查看全部</span>
|
||||
<i className='ml-2 fas fa-arrow-right' />
|
||||
</Link>
|
||||
{href && (
|
||||
<Link className='hidden lg:block text-lg underline' href={href}>
|
||||
<span>查看全部</span>
|
||||
<i className='ml-2 fas fa-arrow-right' />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
{/* 列表 */}
|
||||
<ul className='grid grid-cols-1 lg:grid-cols-4 gap-4'>
|
||||
<ul className='hidden lg:grid grid-cols-1 lg:grid-cols-4 gap-4'>
|
||||
{posts?.map((p, index) => {
|
||||
return <PostItemCard key={index} post={p} />
|
||||
})}
|
||||
</ul>
|
||||
<div className='block lg:hidden px-2'>
|
||||
<Swiper posts={posts} />
|
||||
{href && (
|
||||
<Link className='lg:hidden block text-lg underline' href={href}>
|
||||
<span>查看全部</span>
|
||||
<i className='ml-2 fas fa-arrow-right' />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import PostItemCard from './PostItemCard'
|
||||
import PostListEmpty from './PostListEmpty'
|
||||
import Swiper from './Swiper'
|
||||
|
||||
/**
|
||||
* 博文水平列表
|
||||
@@ -25,11 +26,14 @@ const PostListRecommend = ({ latestPosts, allNavPages }) => {
|
||||
<h3 className='text-4xl font-bold'>{title}</h3>
|
||||
</div>
|
||||
{/* 列表 */}
|
||||
<ul className='flex flex-col lg:flex-row gap-4 lg:overflow-x-scroll'>
|
||||
{recommendPosts?.map(p => {
|
||||
return <PostItemCard key={p.id} post={p} />
|
||||
<ul className='hidden lg:grid grid-cols-1 lg:grid-cols-4 gap-4'>
|
||||
{recommendPosts?.map((p, index) => {
|
||||
return <PostItemCard key={index} post={p} />
|
||||
})}
|
||||
</ul>
|
||||
<div className='block lg:hidden px-2'>
|
||||
<Swiper posts={recommendPosts} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
137
themes/magzine/components/Swiper.js
Normal file
137
themes/magzine/components/Swiper.js
Normal file
@@ -0,0 +1,137 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import PostItemCard from './PostItemCard'
|
||||
|
||||
const Swiper = ({ posts }) => {
|
||||
const [currentIndex, setCurrentIndex] = useState(0)
|
||||
const containerRef = useRef(null)
|
||||
|
||||
// 用于记录触摸开始和结束的水平位置
|
||||
const touchStartPos = useRef({ x: 0, y: 0 })
|
||||
const touchEndPos = useRef({ x: 0, y: 0 })
|
||||
const isHorizontalSwipe = useRef(false)
|
||||
|
||||
const handleTouchStart = e => {
|
||||
// 记录初始触摸位置
|
||||
touchStartPos.current = {
|
||||
x: e.touches[0].clientX,
|
||||
y: e.touches[0].clientY
|
||||
}
|
||||
isHorizontalSwipe.current = false // 重置滑动方向标志
|
||||
}
|
||||
|
||||
const handleTouchMove = e => {
|
||||
const touch = e.touches[0]
|
||||
const deltaX = touch.clientX - touchStartPos.current.x
|
||||
const deltaY = touch.clientY - touchStartPos.current.y
|
||||
|
||||
// 判断是否为水平滑动(避免垂直滑动干扰)
|
||||
if (!isHorizontalSwipe.current) {
|
||||
isHorizontalSwipe.current = Math.abs(deltaX) > Math.abs(deltaY)
|
||||
}
|
||||
|
||||
// 如果是水平滑动,阻止垂直滚动
|
||||
if (isHorizontalSwipe.current) {
|
||||
e.preventDefault() // 阻止垂直方向的默认滚动行为
|
||||
}
|
||||
}
|
||||
|
||||
const handleTouchEnd = e => {
|
||||
if (isHorizontalSwipe.current) {
|
||||
// 记录触摸结束位置
|
||||
touchEndPos.current = {
|
||||
x: e.changedTouches[0].clientX,
|
||||
y: e.changedTouches[0].clientY
|
||||
}
|
||||
|
||||
// 计算滑动距离
|
||||
const deltaX = touchEndPos.current.x - touchStartPos.current.x
|
||||
|
||||
// 如果滑动距离足够大,则决定滑动到下一张或上一张卡片
|
||||
const swipeThreshold = 50 // 设置滑动的阈值
|
||||
if (deltaX > swipeThreshold) {
|
||||
goToPrevious() // 向右滑动,上一张
|
||||
} else if (deltaX < -swipeThreshold) {
|
||||
goToNext() // 向左滑动,下一张
|
||||
} else {
|
||||
// 滑动距离不够,回到当前卡片
|
||||
scrollToCard(currentIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const goToPrevious = () => {
|
||||
const newIndex = currentIndex === 0 ? posts.length - 1 : currentIndex - 1
|
||||
setCurrentIndex(newIndex)
|
||||
scrollToCard(newIndex)
|
||||
}
|
||||
|
||||
const goToNext = () => {
|
||||
const newIndex = currentIndex === posts.length - 1 ? 0 : currentIndex + 1
|
||||
setCurrentIndex(newIndex)
|
||||
scrollToCard(newIndex)
|
||||
}
|
||||
|
||||
const scrollToCard = index => {
|
||||
const container = containerRef.current
|
||||
if (!container) return
|
||||
|
||||
const cardWidth = container.scrollWidth / posts.length
|
||||
container.scrollTo({
|
||||
left: index * cardWidth,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
|
||||
const handleIndicatorClick = index => {
|
||||
setCurrentIndex(index)
|
||||
scrollToCard(index)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='relative w-full mx-auto'>
|
||||
{/* 左侧点击区域 */}
|
||||
<div
|
||||
className='absolute inset-y-0 left-0 w-1/5 z-10'
|
||||
onClick={goToPrevious}></div>
|
||||
|
||||
{/* 右侧点击区域 */}
|
||||
<div
|
||||
className='absolute inset-y-0 right-0 w-1/5 z-10'
|
||||
onClick={goToNext}></div>
|
||||
|
||||
{/* Swiper Container */}
|
||||
<div
|
||||
ref={containerRef}
|
||||
className='relative w-full overflow-x-scroll scroll-smooth py-4'
|
||||
onTouchStart={handleTouchStart}
|
||||
onTouchMove={handleTouchMove}
|
||||
onTouchEnd={handleTouchEnd}
|
||||
style={{ WebkitOverflowScrolling: 'touch' }} // iOS自然滚动支持
|
||||
>
|
||||
<div className='flex gap-x-4'>
|
||||
{posts.map((item, index) => (
|
||||
<div key={index} className='w-5/6 flex-shrink-0'>
|
||||
<PostItemCard key={index} post={item} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Indicator Dots */}
|
||||
<div className='absolute bottom-0 left-0 right-0 flex justify-center space-x-2'>
|
||||
{posts.map((_, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => handleIndicatorClick(index)}
|
||||
className={`w-3 h-3 rounded-full ${
|
||||
currentIndex === index
|
||||
? 'bg-black dark:bg-white'
|
||||
: 'bg-gray-300 dark:bg-gray-700'
|
||||
}`}></button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Swiper
|
||||
@@ -178,7 +178,7 @@ const LayoutSlug = props => {
|
||||
|
||||
{/* 文章区块分为三列 */}
|
||||
<div className='grid grid-cols-1 lg:grid-cols-5 gap-8 py-12'>
|
||||
<div className='h-full lg:col-span-1 hidden lg:contents'>
|
||||
<div className='h-full lg:col-span-1 hidden lg:block'>
|
||||
<Catalog toc={post?.toc} className='sticky top-20' />
|
||||
</div>
|
||||
|
||||
@@ -231,7 +231,7 @@ const LayoutSlug = props => {
|
||||
|
||||
{/* 最新文章区块 */}
|
||||
<div>
|
||||
<PostGroupLatest {...props} />
|
||||
<PostGroupLatest {...props} vertical={true} />
|
||||
</div>
|
||||
|
||||
{/* 文章分类区块 */}
|
||||
|
||||
Reference in New Issue
Block a user