mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-06-03 23:16:52 +00:00
MailChimp
This commit is contained in:
@@ -33,7 +33,7 @@ const BLOG = {
|
|||||||
NOTION_HOST: process.env.NEXT_PUBLIC_NOTION_HOST || 'https://www.notion.so', // Notion域名,您可以选择用自己的域名进行反向代理,如果不懂得什么是反向代理,请勿修改此项
|
NOTION_HOST: process.env.NEXT_PUBLIC_NOTION_HOST || 'https://www.notion.so', // Notion域名,您可以选择用自己的域名进行反向代理,如果不懂得什么是反向代理,请勿修改此项
|
||||||
|
|
||||||
// 网站字体
|
// 网站字体
|
||||||
FONT_STYLE: process.env.NEXT_PUBLIC_FONT_STYLE || 'font-serif', // ['font-serif','font-sans'] 两种可选,分别是衬线和无衬线: 参考 https://www.jianshu.com/p/55e410bd2115
|
FONT_STYLE: process.env.NEXT_PUBLIC_FONT_STYLE || 'font-sans', // ['font-serif','font-sans'] 两种可选,分别是衬线和无衬线: 参考 https://www.jianshu.com/p/55e410bd2115
|
||||||
FONT_URL: [
|
FONT_URL: [
|
||||||
// 字体CSS 例如 https://npm.elemecdn.com/lxgw-wenkai-webfont@1.6.0/style.css
|
// 字体CSS 例如 https://npm.elemecdn.com/lxgw-wenkai-webfont@1.6.0/style.css
|
||||||
'https://fonts.googleapis.com/css?family=Bitter&display=swap',
|
'https://fonts.googleapis.com/css?family=Bitter&display=swap',
|
||||||
@@ -319,8 +319,10 @@ const BLOG = {
|
|||||||
icon: process.env.NEXT_PUBLIC_NOTION_PROPERTY_ICON || 'icon'
|
icon: process.env.NEXT_PUBLIC_NOTION_PROPERTY_ICON || 'icon'
|
||||||
},
|
},
|
||||||
|
|
||||||
// RSS
|
// RSS订阅
|
||||||
ENABLE_RSS: process.env.NEXT_PUBLIC_ENABLE_RSS || true, // 是否开启RSS订阅功能
|
ENABLE_RSS: process.env.NEXT_PUBLIC_ENABLE_RSS || true, // 是否开启RSS订阅功能
|
||||||
|
MAILCHIMP_LIST_ID: process.env.MAILCHIMP_LIST_ID || null, // 开启mailichimp邮件订阅 客户列表ID ,具体使用方法参阅文档
|
||||||
|
MAILCHIMP_API_KEY: process.env.MAILCHIMP_API_KEY || null, // 开启mailichimp邮件订阅 APIkey
|
||||||
|
|
||||||
// 作废配置
|
// 作废配置
|
||||||
AVATAR: process.env.NEXT_PUBLIC_AVATAR || '/avatar.svg', // 作者头像,被notion中的ICON覆盖。若无ICON则取public目录下的avatar.png
|
AVATAR: process.env.NEXT_PUBLIC_AVATAR || '/avatar.svg', // 作者头像,被notion中的ICON覆盖。若无ICON则取public目录下的avatar.png
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ const NotionPage = ({ post, className }) => {
|
|||||||
return <>{post?.summary || ''}</>
|
return <>{post?.summary || ''}</>
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div id='container' className={`mx-auto ${className}`}>
|
return <div id='notion-article' className={`mx-auto ${className || ''}`}>
|
||||||
<NotionRenderer
|
<NotionRenderer
|
||||||
recordMap={post.blockMap}
|
recordMap={post.blockMap}
|
||||||
mapPageUrl={mapPageUrl}
|
mapPageUrl={mapPageUrl}
|
||||||
|
|||||||
49
lib/mailchimp.js
Normal file
49
lib/mailchimp.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import BLOG from '@/blog.config'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订阅邮件-服务端接口
|
||||||
|
* @param {*} email
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function subscribeToMailchimpApi({ email, first_name = '', last_name = '' }) {
|
||||||
|
const listId = BLOG.MAILCHIMP_LIST_ID // 替换为你的邮件列表 ID
|
||||||
|
const apiKey = BLOG.MAILCHIMP_API_KEY // 替换为你的 API KEY
|
||||||
|
if (!email || !listId || !apiKey) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
email_address: email,
|
||||||
|
status: 'subscribed',
|
||||||
|
merge_fields: {
|
||||||
|
FNAME: first_name,
|
||||||
|
LNAME: last_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fetch(`https://us18.api.mailchimp.com/3.0/lists/${listId}/members`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Authorization: `apikey ${apiKey}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端接口
|
||||||
|
* @param {*} email
|
||||||
|
* @param {*} firstName
|
||||||
|
* @param {*} lastName
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function subscribeToNewsletter(email, firstName, lastName) {
|
||||||
|
const response = await fetch('/api/subscribe', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ email, first_name: firstName, last_name: lastName })
|
||||||
|
})
|
||||||
|
const data = await response.json()
|
||||||
|
return data
|
||||||
|
}
|
||||||
@@ -48,7 +48,7 @@ const Slug = props => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 5 * 1000) // 404时长 8秒
|
}, 8 * 1000) // 404时长 8秒
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文章加密
|
// 文章加密
|
||||||
|
|||||||
22
pages/api/subscribe.js
Normal file
22
pages/api/subscribe.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import subscribeToMailchimpApi from '@/lib/mailchimp'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接受邮件订阅
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
*/
|
||||||
|
export default async function handler(req, res) {
|
||||||
|
if (req.method === 'POST') {
|
||||||
|
const { email, firstName, lastName } = req.body
|
||||||
|
try {
|
||||||
|
const response = await subscribeToMailchimpApi({ email, first_name: firstName, last_name: lastName })
|
||||||
|
const data = await response.json()
|
||||||
|
console.log('data', data)
|
||||||
|
res.status(200).json({ status: 'success', message: 'Subscription successful!' })
|
||||||
|
} catch (error) {
|
||||||
|
res.status(400).json({ status: 'error', message: 'Subscription failed!', error })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(405).json({ status: 'error', message: 'Method not allowed' })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2043,3 +2043,6 @@ code.language-mermaid {
|
|||||||
@apply dark:fill-gray-200 !important;
|
@apply dark:fill-gray-200 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notion-external-image {
|
||||||
|
@apply w-6 h-6 mx-3 my-2 !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const LayoutSearch = props => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const container = isBrowser() && document.getElementById('container')
|
const container = isBrowser() && document.getElementById('posts-wrapper')
|
||||||
if (container && container.innerHTML) {
|
if (container && container.innerHTML) {
|
||||||
const re = new RegExp(keyword, 'gim')
|
const re = new RegExp(keyword, 'gim')
|
||||||
const instance = new Mark(container)
|
const instance = new Mark(container)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const LayoutSlug = props => {
|
|||||||
|
|
||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
{!lock && <div id="notion-article" className="px-2">
|
{!lock && <div id="article-wrapper" className="px-2">
|
||||||
{post && <>
|
{post && <>
|
||||||
<ArticleInfo post={post} />
|
<ArticleInfo post={post} />
|
||||||
<NotionPage post={post} />
|
<NotionPage post={post} />
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const BlogListPage = props => {
|
|||||||
return (
|
return (
|
||||||
<div className={`w-full ${showPageCover ? 'md:pr-2' : 'md:pr-12'}} mb-12`}>
|
<div className={`w-full ${showPageCover ? 'md:pr-2' : 'md:pr-12'}} mb-12`}>
|
||||||
|
|
||||||
<div id="container">
|
<div id="posts-wrapper">
|
||||||
{posts?.map(post => (
|
{posts?.map(post => (
|
||||||
<BlogPostCard key={post.id} post = {post}/>
|
<BlogPostCard key={post.id} post = {post}/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const BlogListScroll = props => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div className={`w-full ${showPageCover ? 'md:pr-2' : 'md:pr-12'}} mb-12`} ref={targetRef}>
|
<div id='posts-wrapper' className={`w-full ${showPageCover ? 'md:pr-2' : 'md:pr-12'}} mb-12`} ref={targetRef}>
|
||||||
|
|
||||||
{postsToShow?.map(post => (
|
{postsToShow?.map(post => (
|
||||||
<BlogPostCard key={post.id} post={post} />
|
<BlogPostCard key={post.id} post={post} />
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const LayoutSearch = (props) => {
|
|||||||
const currentSearch = keyword || router?.query?.s
|
const currentSearch = keyword || router?.query?.s
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const container = isBrowser() && document.getElementById('container')
|
const container = isBrowser() && document.getElementById('posts-wrapper')
|
||||||
if (container && container.innerHTML) {
|
if (container && container.innerHTML) {
|
||||||
const re = new RegExp(currentSearch, 'gim')
|
const re = new RegExp(currentSearch, 'gim')
|
||||||
const instance = new Mark(container)
|
const instance = new Mark(container)
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export default function ArticleDetail(props) {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* Notion文章主体 */}
|
{/* Notion文章主体 */}
|
||||||
<section id='notion-article' className='px-1'>
|
<section id='article-wrapper' className='px-1'>
|
||||||
{post && <NotionPage post={post} />}
|
{post && <NotionPage post={post} />}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const BlogListPage = ({ page = 1, posts = [], postCount, siteInfo }) => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
<div id="container" className='grid-container'>
|
<div id="posts-wrapper" className='grid-container'>
|
||||||
{filterPosts?.map(post => (
|
{filterPosts?.map(post => (
|
||||||
<div key={post.id} className='grid-item justify-center flex' style={{ breakInside: 'avoid' }}>
|
<div key={post.id} className='grid-item justify-center flex' style={{ breakInside: 'avoid' }}>
|
||||||
<BlogCard index={posts.indexOf(post)} key={post.id} post={post} siteInfo={siteInfo} />
|
<BlogCard index={posts.indexOf(post)} key={post.id} post={post} siteInfo={siteInfo} />
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ const BlogListScroll = props => {
|
|||||||
return <BlogPostListEmpty />
|
return <BlogPostListEmpty />
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div id="container" ref={targetRef} className='grid-container' >
|
<div id="posts-wrapper" ref={targetRef} className='grid-container' >
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
{postsToShow?.map(post => (
|
{postsToShow?.map(post => (
|
||||||
<div key={post.id} className='grid-item justify-center flex' style={{ breakInside: 'avoid' }}>
|
<div key={post.id} className='grid-item justify-center flex' style={{ breakInside: 'avoid' }}>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const LayoutSearch = (props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const container = isBrowser() && document.getElementById('container')
|
const container = isBrowser() && document.getElementById('posts-wrapper')
|
||||||
if (container && container.innerHTML) {
|
if (container && container.innerHTML) {
|
||||||
const re = new RegExp(currentSearch, 'gim')
|
const re = new RegExp(currentSearch, 'gim')
|
||||||
const instance = new Mark(container)
|
const instance = new Mark(container)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export const LayoutSlug = (props) => {
|
|||||||
<h1 className="text-3xl pt-12 dark:text-gray-300">{post?.title}</h1>
|
<h1 className="text-3xl pt-12 dark:text-gray-300">{post?.title}</h1>
|
||||||
|
|
||||||
{/* Notion文章主体 */}
|
{/* Notion文章主体 */}
|
||||||
{post && (<section id="notion-article" className="px-1">
|
{post && (<section id="article-wrapper" className="px-1">
|
||||||
<NotionPage post={post} />
|
<NotionPage post={post} />
|
||||||
</section>)}
|
</section>)}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full justify-center'>
|
<div className='w-full justify-center'>
|
||||||
<div id='container'>
|
<div id='posts-wrapper'>
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
{posts?.map(post => (
|
{posts?.map(post => (
|
||||||
<BlogPostCard key={post.id} post={post} />
|
<BlogPostCard key={post.id} post={post} />
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const NavPostList = (props) => {
|
|||||||
if (!filteredPosts || filteredPosts.length === 0) {
|
if (!filteredPosts || filteredPosts.length === 0) {
|
||||||
return <NavPostListEmpty currentSearch={currentSearch} />
|
return <NavPostListEmpty currentSearch={currentSearch} />
|
||||||
} else {
|
} else {
|
||||||
return <div className='w-full'>
|
return <div id='posts-wrapper' className='w-full'>
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
{filteredPosts?.map((group, index) => <NavPostItem key={index} group={group} onHeightChange={props.onHeightChange}/>)}
|
{filteredPosts?.map((group, index) => <NavPostItem key={index} group={group} onHeightChange={props.onHeightChange}/>)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
|
|||||||
const currentRef = targetRef?.current || targetRef
|
const currentRef = targetRef?.current || targetRef
|
||||||
const [percent, changePercent] = useState(0)
|
const [percent, changePercent] = useState(0)
|
||||||
const scrollListener = () => {
|
const scrollListener = () => {
|
||||||
const target = currentRef || (isBrowser() && document.getElementById('container'))
|
const target = currentRef || (isBrowser() && document.getElementById('posts-wrapper'))
|
||||||
if (target) {
|
if (target) {
|
||||||
const clientHeight = target.clientHeight
|
const clientHeight = target.clientHeight
|
||||||
const scrollY = window.pageYOffset
|
const scrollY = window.pageYOffset
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const Layout404 = props => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 延时3秒如果加载失败就返回首页
|
// 延时3秒如果加载失败就返回首页
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const article = typeof document !== 'undefined' && document.getElementById('container')
|
const article = typeof document !== 'undefined' && document.getElementById('notion-article')
|
||||||
if (!article) {
|
if (!article) {
|
||||||
router.push('/').then(() => {
|
router.push('/').then(() => {
|
||||||
// console.log('找不到页面', router.asPath)
|
// console.log('找不到页面', router.asPath)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const LayoutSlug = props => {
|
|||||||
></LayoutBase>
|
></LayoutBase>
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetRef = isBrowser() ? document.getElementById('container') : null
|
const targetRef = isBrowser() ? document.getElementById('article-wrapper') : null
|
||||||
|
|
||||||
const floatSlot = <>
|
const floatSlot = <>
|
||||||
{post?.toc?.length > 1 && <div className="block lg:hidden">
|
{post?.toc?.length > 1 && <div className="block lg:hidden">
|
||||||
@@ -50,11 +50,11 @@ export const LayoutSlug = props => {
|
|||||||
<div className="w-full lg:hover:shadow lg:border rounded-t-xl lg:rounded-xl lg:px-2 lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black article">
|
<div className="w-full lg:hover:shadow lg:border rounded-t-xl lg:rounded-xl lg:px-2 lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black article">
|
||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
{!lock && <div id="container" className="overflow-x-auto flex-grow mx-auto md:w-full md:px-5 ">
|
{!lock && <div id="article-wrapper" className="overflow-x-auto flex-grow mx-auto md:w-full md:px-5 ">
|
||||||
|
|
||||||
<article itemScope itemType="https://schema.org/Movie" className="subpixel-antialiased overflow-y-hidden" >
|
<article itemScope itemType="https://schema.org/Movie" className="subpixel-antialiased overflow-y-hidden" >
|
||||||
{/* Notion文章主体 */}
|
{/* Notion文章主体 */}
|
||||||
<section id='notion-article' className='px-5 justify-center mx-auto max-w-2xl lg:max-w-full'>
|
<section className='px-5 justify-center mx-auto max-w-2xl lg:max-w-full'>
|
||||||
{post && <NotionPage post={post} />}
|
{post && <NotionPage post={post} />}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
|
|||||||
const currentRef = targetRef?.current || targetRef
|
const currentRef = targetRef?.current || targetRef
|
||||||
const [percent, changePercent] = useState(0)
|
const [percent, changePercent] = useState(0)
|
||||||
const scrollListener = () => {
|
const scrollListener = () => {
|
||||||
const target = currentRef || (isBrowser() && document.getElementById('container'))
|
const target = currentRef || (isBrowser() && document.getElementById('article-wrapper'))
|
||||||
if (target) {
|
if (target) {
|
||||||
const clientHeight = target.clientHeight
|
const clientHeight = target.clientHeight
|
||||||
const scrollY = window.pageYOffset
|
const scrollY = window.pageYOffset
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default function Features() {
|
|||||||
{/* Section header */}
|
{/* Section header */}
|
||||||
<div className="max-w-3xl mx-auto text-center pb-12 md:pb-16">
|
<div className="max-w-3xl mx-auto text-center pb-12 md:pb-16">
|
||||||
<h1 className="h2 mb-4">探索解决方案</h1>
|
<h1 className="h2 mb-4">探索解决方案</h1>
|
||||||
<p className="text-xl text-gray-600">个人或小企业有什么方法可以快速搭建自己的站点,塑造一个品牌展示中心?<br/>以前,它是<strong className='font-bold'>繁重</strong>的Wordpress,它是操作<strong className='font-bold'>复杂</strong>的Hexo,它是一个<strong className='font-bold'>昂贵</strong>的研发外包团队;但现在,您只要一个笔记软件、Notion就够了</p>
|
<p className="text-xl text-gray-600">如何搭建自己的门户网站,塑造一个品牌展示中心?<br/>以前,它是系统<strong className='font-bold'>繁重</strong>的Wordpress,它是操作<strong className='font-bold'>复杂</strong>的Hexo,它是一个<strong className='font-bold'>昂贵</strong>的研发外包团队;但现在,您只要一个笔记软件、Notion就够了</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Section content */}
|
{/* Section content */}
|
||||||
|
|||||||
@@ -1,149 +1,174 @@
|
|||||||
|
import { subscribeToNewsletter } from '@/lib/mailchimp'
|
||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import Logo from './Logo'
|
import Logo from './Logo'
|
||||||
|
|
||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
|
const formRef = useRef()
|
||||||
|
const [success, setSuccess] = useState(false)
|
||||||
|
useEffect(() => {
|
||||||
|
const form = formRef.current
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const email = document.querySelector('#newsletter').value
|
||||||
|
subscribeToNewsletter(email).then(response => {
|
||||||
|
console.log('Subscription succeeded:', response)
|
||||||
|
// 在此处添加成功订阅后的操作
|
||||||
|
setSuccess(true)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Subscription failed:', error)
|
||||||
|
// 在此处添加订阅失败后的操作
|
||||||
|
})
|
||||||
|
}
|
||||||
|
form.addEventListener('submit', handleSubmit)
|
||||||
|
return () => {
|
||||||
|
form.removeEventListener('submit', handleSubmit)
|
||||||
|
}
|
||||||
|
}, [subscribeToNewsletter])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer>
|
<footer>
|
||||||
<div className="max-w-6xl mx-auto px-4 sm:px-6">
|
<div className="max-w-6xl mx-auto px-4 sm:px-6">
|
||||||
|
|
||||||
{/* Top area: Blocks */}
|
{/* Top area: Blocks */}
|
||||||
<div className="grid sm:grid-cols-12 gap-8 py-8 md:py-12 border-t border-gray-200">
|
<div className="grid sm:grid-cols-12 gap-8 py-8 md:py-12 border-t border-gray-200">
|
||||||
|
|
||||||
{/* 1st block */}
|
{/* 1st block */}
|
||||||
<div className="sm:col-span-12 lg:col-span-3">
|
<div className="sm:col-span-12 lg:col-span-3">
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<Logo />
|
<Logo />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-600">
|
<div className="text-sm text-gray-600">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 hover:underline transition duration-150 ease-in-out">Terms</a> · <a href="#0" className="text-gray-600 hover:text-gray-900 hover:underline transition duration-150 ease-in-out">Privacy Policy</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 hover:underline transition duration-150 ease-in-out">服务条款</a> · <a href="#0" className="text-gray-600 hover:text-gray-900 hover:underline transition duration-150 ease-in-out">隐私政策</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 2nd block */}
|
{/* 2nd block */}
|
||||||
<div className="sm:col-span-6 md:col-span-3 lg:col-span-2">
|
<div className="sm:col-span-6 md:col-span-3 lg:col-span-2">
|
||||||
<h6 className="text-gray-800 font-medium mb-2">Products</h6>
|
<h6 className="text-gray-800 font-medium mb-2">产品</h6>
|
||||||
<ul className="text-sm">
|
<ul className="text-sm">
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Web Studio</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">NotionNext</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">DynamicBox Flex</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Vercel</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Programming Forms</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Github</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Integrations</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Notion</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Command-line</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">NextJs</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 3rd block */}
|
{/* 3rd block */}
|
||||||
<div className="sm:col-span-6 md:col-span-3 lg:col-span-2">
|
<div className="sm:col-span-6 md:col-span-3 lg:col-span-2">
|
||||||
<h6 className="text-gray-800 font-medium mb-2">Resources</h6>
|
<h6 className="text-gray-800 font-medium mb-2">资源</h6>
|
||||||
<ul className="text-sm">
|
<ul className="text-sm">
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Documentation</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">技术文档</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Tutorials & Guides</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">教程指南</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Blog</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">博客</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Support Center</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">支持中心</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Partners</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">合作方</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 4th block */}
|
{/* 4th block */}
|
||||||
<div className="sm:col-span-6 md:col-span-3 lg:col-span-2">
|
<div className="sm:col-span-6 md:col-span-3 lg:col-span-2">
|
||||||
<h6 className="text-gray-800 font-medium mb-2">Company</h6>
|
<h6 className="text-gray-800 font-medium mb-2">企业</h6>
|
||||||
<ul className="text-sm">
|
<ul className="text-sm">
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Home</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">主页</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">About us</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">关于我们</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Company values</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">公司价值观</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Pricing</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">价格</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="mb-2">
|
<li className="mb-2">
|
||||||
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">Privacy Policy</a>
|
<a href="#0" className="text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">隐私政策</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 5th block */}
|
||||||
|
<div className="sm:col-span-6 md:col-span-3 lg:col-span-3">
|
||||||
|
<h6 className="text-gray-800 font-medium mb-2">Subscribe</h6>
|
||||||
|
<p className="text-sm text-gray-600 mb-4">Get the latest news and articles to your inbox every month.</p>
|
||||||
|
<form ref={formRef}>
|
||||||
|
<div className="flex flex-wrap mb-4">
|
||||||
|
<div className="w-full">
|
||||||
|
<label className="block text-sm sr-only" htmlFor="newsletter">Email</label>
|
||||||
|
<div className="relative flex items-center max-w-xs">
|
||||||
|
<input disabled={success} id="newsletter" type="email" className="form-input w-full text-gray-800 px-3 py-2 pr-12 text-sm" placeholder="Your email" required />
|
||||||
|
<button disabled={success} type="submit" className="absolute inset-0 left-auto" aria-label="Subscribe">
|
||||||
|
<span className="absolute inset-0 right-auto w-px -ml-px my-2 bg-gray-300" aria-hidden="true"></span>
|
||||||
|
<svg className="w-3 h-3 fill-current text-blue-600 mx-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.707 5.293L7 .586 5.586 2l3 3H0v2h8.586l-3 3L7 11.414l4.707-4.707a1 1 0 000-1.414z" fillRule="nonzero" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/* Success message */}
|
||||||
|
{success && <p className="mt-2 text-green-600 text-sm">Thanks for subscribing!</p>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 5th block */}
|
|
||||||
<div className="sm:col-span-6 md:col-span-3 lg:col-span-3">
|
|
||||||
<h6 className="text-gray-800 font-medium mb-2">Subscribe</h6>
|
|
||||||
<p className="text-sm text-gray-600 mb-4">Get the latest news and articles to your inbox every month.</p>
|
|
||||||
<form>
|
|
||||||
<div className="flex flex-wrap mb-4">
|
|
||||||
<div className="w-full">
|
|
||||||
<label className="block text-sm sr-only" htmlFor="newsletter">Email</label>
|
|
||||||
<div className="relative flex items-center max-w-xs">
|
|
||||||
<input id="newsletter" type="email" className="form-input w-full text-gray-800 px-3 py-2 pr-12 text-sm" placeholder="Your email" required />
|
|
||||||
<button type="submit" className="absolute inset-0 left-auto" aria-label="Subscribe">
|
|
||||||
<span className="absolute inset-0 right-auto w-px -ml-px my-2 bg-gray-300" aria-hidden="true"></span>
|
|
||||||
<svg className="w-3 h-3 fill-current text-blue-600 mx-3 shrink-0" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M11.707 5.293L7 .586 5.586 2l3 3H0v2h8.586l-3 3L7 11.414l4.707-4.707a1 1 0 000-1.414z" fillRule="nonzero" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{/* Success message */}
|
|
||||||
{/* <p className="mt-2 text-green-600 text-sm">Thanks for subscribing!</p> */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
{/* Bottom area */}
|
||||||
|
<div className="md:flex md:items-center md:justify-between py-4 md:py-8 border-t border-gray-200">
|
||||||
|
|
||||||
{/* Bottom area */}
|
{/* Social as */}
|
||||||
<div className="md:flex md:items-center md:justify-between py-4 md:py-8 border-t border-gray-200">
|
<ul className="flex mb-4 md:order-1 md:ml-4 md:mb-0">
|
||||||
|
{/* <li>
|
||||||
{/* Social as */}
|
|
||||||
<ul className="flex mb-4 md:order-1 md:ml-4 md:mb-0">
|
|
||||||
<li>
|
|
||||||
<a href="#0" className="flex justify-center items-center text-gray-600 hover:text-gray-900 bg-white hover:bg-white-100 rounded-full shadow transition duration-150 ease-in-out" aria-label="Twitter">
|
<a href="#0" className="flex justify-center items-center text-gray-600 hover:text-gray-900 bg-white hover:bg-white-100 rounded-full shadow transition duration-150 ease-in-out" aria-label="Twitter">
|
||||||
<svg className="w-8 h-8 fill-current" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
<svg className="w-8 h-8 fill-current" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M24 11.5c-.6.3-1.2.4-1.9.5.7-.4 1.2-1 1.4-1.8-.6.4-1.3.6-2.1.8-.6-.6-1.5-1-2.4-1-1.7 0-3.2 1.5-3.2 3.3 0 .3 0 .5.1.7-2.7-.1-5.2-1.4-6.8-3.4-.3.5-.4 1-.4 1.7 0 1.1.6 2.1 1.5 2.7-.5 0-1-.2-1.5-.4 0 1.6 1.1 2.9 2.6 3.2-.3.1-.6.1-.9.1-.2 0-.4 0-.6-.1.4 1.3 1.6 2.3 3.1 2.3-1.1.9-2.5 1.4-4.1 1.4H8c1.5.9 3.2 1.5 5 1.5 6 0 9.3-5 9.3-9.3v-.4c.7-.5 1.3-1.1 1.7-1.8z" />
|
<path d="M24 11.5c-.6.3-1.2.4-1.9.5.7-.4 1.2-1 1.4-1.8-.6.4-1.3.6-2.1.8-.6-.6-1.5-1-2.4-1-1.7 0-3.2 1.5-3.2 3.3 0 .3 0 .5.1.7-2.7-.1-5.2-1.4-6.8-3.4-.3.5-.4 1-.4 1.7 0 1.1.6 2.1 1.5 2.7-.5 0-1-.2-1.5-.4 0 1.6 1.1 2.9 2.6 3.2-.3.1-.6.1-.9.1-.2 0-.4 0-.6-.1.4 1.3 1.6 2.3 3.1 2.3-1.1.9-2.5 1.4-4.1 1.4H8c1.5.9 3.2 1.5 5 1.5 6 0 9.3-5 9.3-9.3v-.4c.7-.5 1.3-1.1 1.7-1.8z" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li> */}
|
||||||
<li className="ml-4">
|
<li className="ml-4">
|
||||||
<a href="#0" className="flex justify-center items-center text-gray-600 hover:text-gray-900 bg-white hover:bg-white-100 rounded-full shadow transition duration-150 ease-in-out" aria-label="Github">
|
<a href="#0" className="flex justify-center items-center text-gray-600 hover:text-gray-900 bg-white hover:bg-white-100 rounded-full shadow transition duration-150 ease-in-out" aria-label="Github">
|
||||||
<svg className="w-8 h-8 fill-current" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
<svg className="w-8 h-8 fill-current" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M16 8.2c-4.4 0-8 3.6-8 8 0 3.5 2.3 6.5 5.5 7.6.4.1.5-.2.5-.4V22c-2.2.5-2.7-1-2.7-1-.4-.9-.9-1.2-.9-1.2-.7-.5.1-.5.1-.5.8.1 1.2.8 1.2.8.7 1.3 1.9.9 2.3.7.1-.5.3-.9.5-1.1-1.8-.2-3.6-.9-3.6-4 0-.9.3-1.6.8-2.1-.1-.2-.4-1 .1-2.1 0 0 .7-.2 2.2.8.6-.2 1.3-.3 2-.3s1.4.1 2 .3c1.5-1 2.2-.8 2.2-.8.4 1.1.2 1.9.1 2.1.5.6.8 1.3.8 2.1 0 3.1-1.9 3.7-3.7 3.9.3.4.6.9.6 1.6v2.2c0 .2.1.5.6.4 3.2-1.1 5.5-4.1 5.5-7.6-.1-4.4-3.7-8-8.1-8z" />
|
<path d="M16 8.2c-4.4 0-8 3.6-8 8 0 3.5 2.3 6.5 5.5 7.6.4.1.5-.2.5-.4V22c-2.2.5-2.7-1-2.7-1-.4-.9-.9-1.2-.9-1.2-.7-.5.1-.5.1-.5.8.1 1.2.8 1.2.8.7 1.3 1.9.9 2.3.7.1-.5.3-.9.5-1.1-1.8-.2-3.6-.9-3.6-4 0-.9.3-1.6.8-2.1-.1-.2-.4-1 .1-2.1 0 0 .7-.2 2.2.8.6-.2 1.3-.3 2-.3s1.4.1 2 .3c1.5-1 2.2-.8 2.2-.8.4 1.1.2 1.9.1 2.1.5.6.8 1.3.8 2.1 0 3.1-1.9 3.7-3.7 3.9.3.4.6.9.6 1.6v2.2c0 .2.1.5.6.4 3.2-1.1 5.5-4.1 5.5-7.6-.1-4.4-3.7-8-8.1-8z" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="ml-4">
|
{/* <li className="ml-4">
|
||||||
<a href="#0" className="flex justify-center items-center text-gray-600 hover:text-gray-900 bg-white hover:bg-white-100 rounded-full shadow transition duration-150 ease-in-out" aria-label="Facebook">
|
<a href="#0" className="flex justify-center items-center text-gray-600 hover:text-gray-900 bg-white hover:bg-white-100 rounded-full shadow transition duration-150 ease-in-out" aria-label="Facebook">
|
||||||
<svg className="w-8 h-8 fill-current" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
<svg className="w-8 h-8 fill-current" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M14.023 24L14 17h-3v-3h3v-2c0-2.7 1.672-4 4.08-4 1.153 0 2.144.086 2.433.124v2.821h-1.67c-1.31 0-1.563.623-1.563 1.536V14H21l-1 3h-2.72v7h-3.257z" />
|
<path d="M14.023 24L14 17h-3v-3h3v-2c0-2.7 1.672-4 4.08-4 1.153 0 2.144.086 2.433.124v2.821h-1.67c-1.31 0-1.563.623-1.563 1.536V14H21l-1 3h-2.72v7h-3.257z" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li> */}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{/* Copyrights note */}
|
{/* Copyrights note */}
|
||||||
<div className="text-sm text-gray-600 mr-4">© NotionNext. All rights reserved.</div>
|
<div className="text-sm text-gray-600 mr-4">© NotionNext. All rights reserved.</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ export default function Header() {
|
|||||||
{/* Desktop sign in links */}
|
{/* Desktop sign in links */}
|
||||||
<ul className="flex grow justify-end flex-wrap items-center">
|
<ul className="flex grow justify-end flex-wrap items-center">
|
||||||
<li>
|
<li>
|
||||||
<Link href="https://github.com/tangly1024/NotionNext" className="font-medium hover:font-bold text-gray-600 hover:text-gray-900 px-5 py-3 flex items-center transition duration-150 ease-in-out">
|
<Link href="https://github.com/tangly1024/NotionNext" target='_blank' className="font-medium hover:font-bold text-gray-600 hover:text-gray-900 px-5 py-3 flex items-center transition duration-150 ease-in-out">
|
||||||
<i className='fab fa-github mr-1'></i><div>Github</div>
|
<i className='fab fa-github mr-1'></i><div>Github开源</div>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="https://blog.tangly1024.com/" className="btn-sm text-gray-200 bg-gray-900 hover:bg-gray-800 ml-3">
|
<Link href="https://blog.tangly1024.com/" target='_blank' className="btn-sm text-gray-200 bg-gray-900 hover:bg-gray-800 ml-3">
|
||||||
<span>我的博客</span>
|
<span>我的博客</span>
|
||||||
<svg className="w-3 h-3 fill-current text-gray-400 shrink-0 ml-2 -mr-1" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
<svg className="w-3 h-3 fill-current text-gray-400 shrink-0 ml-2 -mr-1" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M11.707 5.293L7 .586 5.586 2l3 3H0v2h8.586l-3 3L7 11.414l4.707-4.707a1 1 0 000-1.414z" fillRule="nonzero" />
|
<path d="M11.707 5.293L7 .586 5.586 2l3 3H0v2h8.586l-3 3L7 11.414l4.707-4.707a1 1 0 000-1.414z" fillRule="nonzero" />
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default function Hero() {
|
|||||||
<div className="text-center pb-12 md:pb-16">
|
<div className="text-center pb-12 md:pb-16">
|
||||||
<h1 className="text-5xl md:text-6xl font-extrabold leading-tighter tracking-tighter mb-4" data-aos="zoom-y-out"><span className="bg-clip-text text-transparent bg-gradient-to-r from-blue-500 to-teal-400">NotionNext</span></h1>
|
<h1 className="text-5xl md:text-6xl font-extrabold leading-tighter tracking-tighter mb-4" data-aos="zoom-y-out"><span className="bg-clip-text text-transparent bg-gradient-to-r from-blue-500 to-teal-400">NotionNext</span></h1>
|
||||||
<div className="max-w-3xl mx-auto">
|
<div className="max-w-3xl mx-auto">
|
||||||
<p className="text-xl text-gray-600 mb-8" data-aos="zoom-y-out" data-aos-delay="150">只需一个Notion笔记就可以搭建各种网站,这也许是成本最低、最快的建站方案</p>
|
<p className="text-xl text-gray-600 mb-8" data-aos="zoom-y-out" data-aos-delay="150">一个笔记软件、一个脚本工具,建站从未如此简单!</p>
|
||||||
<div className="max-w-xs mx-auto sm:max-w-none sm:flex sm:justify-center" data-aos="zoom-y-out" data-aos-delay="300">
|
<div className="max-w-xs mx-auto sm:max-w-none sm:flex sm:justify-center" data-aos="zoom-y-out" data-aos-delay="300">
|
||||||
<div>
|
<div>
|
||||||
<a className="btn text-white bg-blue-600 hover:bg-blue-700 w-full mb-4 sm:w-auto sm:mb-0" href="https://docs.tangly1024.com/article/vercel-deploy-notion-next">开始体验</a>
|
<a className="btn text-white bg-blue-600 hover:bg-blue-700 w-full mb-4 sm:w-auto sm:mb-0" href="https://docs.tangly1024.com/article/vercel-deploy-notion-next">开始体验</a>
|
||||||
|
|||||||
@@ -1,63 +1,89 @@
|
|||||||
|
import { subscribeToNewsletter } from '@/lib/mailchimp'
|
||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
export default function Newsletter() {
|
export default function Newsletter() {
|
||||||
|
const formRef = useRef()
|
||||||
|
const [success, setSuccess] = useState(false)
|
||||||
|
useEffect(() => {
|
||||||
|
const form = formRef.current
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const email = document.querySelector('#newsletter').value
|
||||||
|
subscribeToNewsletter(email).then(response => {
|
||||||
|
console.log('Subscription succeeded:', response)
|
||||||
|
// 在此处添加成功订阅后的操作
|
||||||
|
setSuccess(true)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Subscription failed:', error)
|
||||||
|
// 在此处添加订阅失败后的操作
|
||||||
|
})
|
||||||
|
}
|
||||||
|
form.addEventListener('submit', handleSubmit)
|
||||||
|
return () => {
|
||||||
|
form.removeEventListener('submit', handleSubmit)
|
||||||
|
}
|
||||||
|
}, [subscribeToNewsletter])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<div className="max-w-6xl mx-auto px-4 sm:px-6">
|
<div className="max-w-6xl mx-auto px-4 sm:px-6">
|
||||||
<div className="pb-12 md:pb-20">
|
<div className="pb-12 md:pb-20">
|
||||||
|
|
||||||
{/* CTA box */}
|
{/* CTA box */}
|
||||||
<div className="relative bg-gray-900 rounded py-10 px-8 md:py-16 md:px-12 shadow-2xl overflow-hidden" data-aos="zoom-y-out">
|
<div className="relative bg-gray-900 rounded py-10 px-8 md:py-16 md:px-12 shadow-2xl overflow-hidden" data-aos="zoom-y-out">
|
||||||
|
|
||||||
{/* Background illustration */}
|
{/* Background illustration */}
|
||||||
<div className="absolute right-0 bottom-0 pointer-events-none hidden lg:block" aria-hidden="true">
|
<div className="absolute right-0 bottom-0 pointer-events-none hidden lg:block" aria-hidden="true">
|
||||||
<svg width="428" height="328" xmlns="http://www.w3.org/2000/svg">
|
<svg width="428" height="328" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
<radialGradient cx="35.542%" cy="34.553%" fx="35.542%" fy="34.553%" r="96.031%" id="ni-a">
|
<radialGradient cx="35.542%" cy="34.553%" fx="35.542%" fy="34.553%" r="96.031%" id="ni-a">
|
||||||
<stop stopColor="#DFDFDF" offset="0%" />
|
<stop stopColor="#DFDFDF" offset="0%" />
|
||||||
<stop stopColor="#4C4C4C" offset="44.317%" />
|
<stop stopColor="#4C4C4C" offset="44.317%" />
|
||||||
<stop stopColor="#333" offset="100%" />
|
<stop stopColor="#333" offset="100%" />
|
||||||
</radialGradient>
|
</radialGradient>
|
||||||
</defs>
|
</defs>
|
||||||
<g fill="none" fillRule="evenodd">
|
<g fill="none" fillRule="evenodd">
|
||||||
<g fill="#FFF">
|
<g fill="#FFF">
|
||||||
<ellipse fillOpacity=".04" cx="185" cy="15.576" rx="16" ry="15.576" />
|
<ellipse fillOpacity=".04" cx="185" cy="15.576" rx="16" ry="15.576" />
|
||||||
<ellipse fillOpacity=".24" cx="100" cy="68.402" rx="24" ry="23.364" />
|
<ellipse fillOpacity=".24" cx="100" cy="68.402" rx="24" ry="23.364" />
|
||||||
<ellipse fillOpacity=".12" cx="29" cy="251.231" rx="29" ry="28.231" />
|
<ellipse fillOpacity=".12" cx="29" cy="251.231" rx="29" ry="28.231" />
|
||||||
<ellipse fillOpacity=".64" cx="29" cy="251.231" rx="8" ry="7.788" />
|
<ellipse fillOpacity=".64" cx="29" cy="251.231" rx="8" ry="7.788" />
|
||||||
<ellipse fillOpacity=".12" cx="342" cy="31.303" rx="8" ry="7.788" />
|
<ellipse fillOpacity=".12" cx="342" cy="31.303" rx="8" ry="7.788" />
|
||||||
<ellipse fillOpacity=".48" cx="62" cy="126.811" rx="2" ry="1.947" />
|
<ellipse fillOpacity=".48" cx="62" cy="126.811" rx="2" ry="1.947" />
|
||||||
<ellipse fillOpacity=".12" cx="78" cy="7.072" rx="2" ry="1.947" />
|
<ellipse fillOpacity=".12" cx="78" cy="7.072" rx="2" ry="1.947" />
|
||||||
<ellipse fillOpacity=".64" cx="185" cy="15.576" rx="6" ry="5.841" />
|
<ellipse fillOpacity=".64" cx="185" cy="15.576" rx="6" ry="5.841" />
|
||||||
</g>
|
</g>
|
||||||
<circle fill="url(#ni-a)" cx="276" cy="237" r="200" />
|
<circle fill="url(#ni-a)" cx="276" cy="237" r="200" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative flex flex-col lg:flex-row justify-between items-center">
|
<div className="relative flex flex-col lg:flex-row justify-between items-center">
|
||||||
|
|
||||||
{/* CTA content */}
|
{/* CTA content */}
|
||||||
<div className="text-center lg:text-left lg:max-w-xl">
|
<div className="text-center lg:text-left lg:max-w-xl">
|
||||||
<h3 className="h3 text-white mb-2">需要更多的教程和帮助?</h3>
|
<h3 className="h3 text-white mb-2">需要更多的教程和帮助?</h3>
|
||||||
<p className="text-gray-300 text-lg mb-6">请留下您的电子邮件,我会第一时间与您取得联系</p>
|
<p className="text-gray-300 text-lg mb-6">请留下您的电子邮件,我会第一时间与您取得联系</p>
|
||||||
|
|
||||||
|
{/* CTA form */}
|
||||||
|
<form ref={formRef} className="w-full lg:w-auto">
|
||||||
|
<div className="flex flex-col sm:flex-row justify-center max-w-xs mx-auto sm:max-w-md lg:mx-0">
|
||||||
|
<input disabled={success} type="email" className="form-input w-full appearance-none bg-gray-800 border border-gray-700 focus:border-gray-600 rounded-sm px-4 py-3 mb-2 sm:mb-0 sm:mr-2 placeholder-gray-500" placeholder="Your email…" aria-label="Your email…" required />
|
||||||
|
<button disabled={success} type='submit' className={`btn text-white shadow ${success ? 'bg-green-600 hover:bg-green-700' : 'bg-blue-600 hover:bg-blue-700'}`} href="#0">{success ? 'Subscribed' : 'Subscribe'}</button>
|
||||||
|
</div>
|
||||||
|
{/* Success message */}
|
||||||
|
{success && <p className="text-sm text-gray-400 mt-3">感谢您的订阅!</p>}
|
||||||
|
{!success && <p className="text-sm text-gray-400 mt-3">没有垃圾邮件,您可以随时取消订阅</p>}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* CTA form */}
|
|
||||||
<form className="w-full lg:w-auto">
|
|
||||||
<div className="flex flex-col sm:flex-row justify-center max-w-xs mx-auto sm:max-w-md lg:mx-0">
|
|
||||||
<input type="email" className="form-input w-full appearance-none bg-gray-800 border border-gray-700 focus:border-gray-600 rounded-sm px-4 py-3 mb-2 sm:mb-0 sm:mr-2 text-white placeholder-gray-500" placeholder="Your email…" aria-label="Your email…" />
|
|
||||||
<a className="btn text-white bg-blue-600 hover:bg-blue-700 shadow" href="#0">Subscribe</a>
|
|
||||||
</div>
|
</div>
|
||||||
{/* Success message */}
|
|
||||||
{/* <p className="text-sm text-gray-400 mt-3">Thanks for subscribing!</p> */}
|
|
||||||
<p className="text-sm text-gray-400 mt-3">没有垃圾邮件,您可以随时取消订阅</p>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const THEME_CONFIG = { THEME: 'landing' }
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutBase = (props) => {
|
const LayoutBase = (props) => {
|
||||||
const { siteInfo, children } = props
|
const { children } = props
|
||||||
return <div id='theme-blank' className="flex flex-col justify-between bg-white">
|
return <div id='theme-blank' className="flex flex-col justify-between bg-white">
|
||||||
{/* 顶部导航栏 */}
|
{/* 顶部导航栏 */}
|
||||||
<Header />
|
<Header />
|
||||||
@@ -50,7 +50,6 @@ const LayoutBase = (props) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutIndex = (props) => {
|
const LayoutIndex = (props) => {
|
||||||
const { siteInfo } = props
|
|
||||||
return (
|
return (
|
||||||
<LayoutBase {...props}>
|
<LayoutBase {...props}>
|
||||||
<Hero />
|
<Hero />
|
||||||
@@ -68,7 +67,7 @@ const LayoutIndex = (props) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutSlug = (props) => <LayoutBase {...props}>
|
const LayoutSlug = (props) => <LayoutBase {...props}>
|
||||||
<div className='p-12'>
|
<div id='container-inner' className='mx-auto max-w-screen-lg p-12'>
|
||||||
<NotionPage {...props} />
|
<NotionPage {...props} />
|
||||||
</div>
|
</div>
|
||||||
</LayoutBase>
|
</LayoutBase>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const Layout404 = props => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 延时3秒如果加载失败就返回首页
|
// 延时3秒如果加载失败就返回首页
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const article = typeof document !== 'undefined' && document.getElementById('container')
|
const article = typeof document !== 'undefined' && document.getElementById('notion-article')
|
||||||
if (!article) {
|
if (!article) {
|
||||||
router.push('/').then(() => {
|
router.push('/').then(() => {
|
||||||
// console.log('找不到页面', router.asPath)
|
// console.log('找不到页面', router.asPath)
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const LayoutBase = props => {
|
|||||||
|
|
||||||
<main id="wrapper" className={`${CONFIG_MATERY.HOME_BANNER_ENABLE ? '' : 'pt-16'} flex-1 w-full py-8 md:px-8 lg:px-24 relative`}>
|
<main id="wrapper" className={`${CONFIG_MATERY.HOME_BANNER_ENABLE ? '' : 'pt-16'} flex-1 w-full py-8 md:px-8 lg:px-24 relative`}>
|
||||||
{/* 嵌入区域 */}
|
{/* 嵌入区域 */}
|
||||||
<div id="container-slot" className={`w-full max-w-6xl ${props?.post && ' lg:max-w-3xl 2xl:max-w-4xl '} mt-6 px-3 mx-auto lg:flex lg:space-x-4 justify-center relative z-10`}>
|
<div id="container-slot" className={`w-full max-w-6xl ${props?.post && ' lg:max-w-3xl 2xl:max-w-4xl '} mt-6 px-3 mx-auto lg:flex lg:space-x-4 justify-center relative z-10`}>
|
||||||
{props.containerSlot}
|
{props.containerSlot}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export const LayoutSearch = props => {
|
|||||||
</>}
|
</>}
|
||||||
|
|
||||||
{currentSearch && <>
|
{currentSearch && <>
|
||||||
<div id="container">
|
<div id="posts-wrapper">
|
||||||
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
|
||||||
</div>
|
</div>
|
||||||
</>}
|
</>}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export const LayoutSlug = props => {
|
|||||||
<div className="-mt-32 transition-all duration-300 rounded-md mx-3 lg:border lg:rounded-xl lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black">
|
<div className="-mt-32 transition-all duration-300 rounded-md mx-3 lg:border lg:rounded-xl lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black">
|
||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
{!lock && <div id="container" className="overflow-x-auto md:w-full px-3 ">
|
{!lock && <div id="article-wrapper" className="overflow-x-auto md:w-full px-3 ">
|
||||||
{post?.type && post?.type === 'Post' && <>
|
{post?.type && post?.type === 'Post' && <>
|
||||||
<div
|
<div
|
||||||
data-aos="fade-down"
|
data-aos="fade-down"
|
||||||
@@ -70,7 +70,7 @@ export const LayoutSlug = props => {
|
|||||||
<div className='lg:px-10 subpixel-antialiased'>
|
<div className='lg:px-10 subpixel-antialiased'>
|
||||||
<article itemScope >
|
<article itemScope >
|
||||||
{/* Notion文章主体 */}
|
{/* Notion文章主体 */}
|
||||||
<section id='notion-article' className='justify-center mx-auto max-w-2xl lg:max-w-full'>
|
<section className='justify-center mx-auto max-w-2xl lg:max-w-full'>
|
||||||
{post && <NotionPage post={post} />}
|
{post && <NotionPage post={post} />}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const NotionPage = dynamic(() => import('@/components/NotionPage'))
|
|||||||
|
|
||||||
const Announcement = ({ notice }) => {
|
const Announcement = ({ notice }) => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
if (!notice) {
|
if (!notice || Object.keys(notice).length === 0) {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
return <div className="px-3 w-full">
|
return <div className="px-3 w-full">
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const ArticleLock = props => {
|
|||||||
passwordInputRef.current.focus()
|
passwordInputRef.current.focus()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return <div id='container' className='w-full flex justify-center items-center h-96 '>
|
return <div id='article-wrapper' className='w-full flex justify-center items-center h-96 '>
|
||||||
<div className='text-center space-y-3 dark:text-gray-300 text-black'>
|
<div className='text-center space-y-3 dark:text-gray-300 text-black'>
|
||||||
<div className='font-bold'>{locale.COMMON.ARTICLE_LOCK_TIPS}</div>
|
<div className='font-bold'>{locale.COMMON.ARTICLE_LOCK_TIPS}</div>
|
||||||
<div className='flex mx-4'>
|
<div className='flex mx-4'>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount, siteInfo }) => {
|
|||||||
return <BlogPostListEmpty />
|
return <BlogPostListEmpty />
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div id="container" className='w-full'>
|
<div className='w-full'>
|
||||||
<div className='pt-6'></div>
|
<div className='pt-6'></div>
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
<div className="pt-4 flex flex-wrap pb-12" >
|
<div className="pt-4 flex flex-wrap pb-12" >
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
|
|||||||
const [percent, changePercent] = useState(0)
|
const [percent, changePercent] = useState(0)
|
||||||
const scrollListener = () => {
|
const scrollListener = () => {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const target = currentRef || (isBrowser() && document.getElementById('container'))
|
const target = currentRef || (isBrowser() && document.getElementById('article-wrapper'))
|
||||||
if (target) {
|
if (target) {
|
||||||
const clientHeight = target.clientHeight
|
const clientHeight = target.clientHeight
|
||||||
const scrollY = window.pageYOffset
|
const scrollY = window.pageYOffset
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const LayoutSearch = (props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const container = isBrowser() && document.getElementById('container')
|
const container = isBrowser() && document.getElementById('posts-wrapper')
|
||||||
if (container && container.innerHTML) {
|
if (container && container.innerHTML) {
|
||||||
const re = new RegExp(currentSearch, 'gim')
|
const re = new RegExp(currentSearch, 'gim')
|
||||||
const instance = new Mark(container)
|
const instance = new Mark(container)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export const LayoutSlug = props => {
|
|||||||
{/* 文章锁 */}
|
{/* 文章锁 */}
|
||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
{!lock && <div id='container'>
|
{!lock && <div id='article-wrapper'>
|
||||||
|
|
||||||
{/* title */}
|
{/* title */}
|
||||||
<h1 className="text-3xl pt-12 dark:text-gray-300">{post?.title}</h1>
|
<h1 className="text-3xl pt-12 dark:text-gray-300">{post?.title}</h1>
|
||||||
@@ -67,7 +67,7 @@ export const LayoutSlug = props => {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Notion文章主体 */}
|
{/* Notion文章主体 */}
|
||||||
<section id="notion-article" className="px-1 max-w-4xl">
|
<section className="px-1 max-w-4xl">
|
||||||
{post && (<NotionPage post={post} />)}
|
{post && (<NotionPage post={post} />)}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full justify-center'>
|
<div className='w-full justify-center'>
|
||||||
<div id='container'>
|
<div id='posts-wrapper'>
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
{posts?.map(post => (
|
{posts?.map(post => (
|
||||||
<BlogPostCard key={post.id} post={post} />
|
<BlogPostCard key={post.id} post={post} />
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ const BlogPostListScroll = ({ posts = [], currentSearch }) => {
|
|||||||
if (!postsToShow || postsToShow.length === 0) {
|
if (!postsToShow || postsToShow.length === 0) {
|
||||||
return <BlogPostListEmpty currentSearch={currentSearch} />
|
return <BlogPostListEmpty currentSearch={currentSearch} />
|
||||||
} else {
|
} else {
|
||||||
return <div id='container' ref={targetRef} className='w-full'>
|
return <div id='posts-wrapper' ref={targetRef} className='w-full'>
|
||||||
|
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
<div className='space-y-1 lg:space-y-4'>
|
<div className='space-y-1 lg:space-y-4'>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
|
|||||||
const currentRef = targetRef?.current || targetRef
|
const currentRef = targetRef?.current || targetRef
|
||||||
const [percent, changePercent] = useState(0)
|
const [percent, changePercent] = useState(0)
|
||||||
const scrollListener = () => {
|
const scrollListener = () => {
|
||||||
const target = currentRef || (isBrowser() && document.getElementById('container'))
|
const target = currentRef || (isBrowser() && document.getElementById('article-wrapper'))
|
||||||
if (target) {
|
if (target) {
|
||||||
const clientHeight = target.clientHeight
|
const clientHeight = target.clientHeight
|
||||||
const scrollY = window.pageYOffset
|
const scrollY = window.pageYOffset
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export const Layout404 = props => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 延时3秒如果加载失败就返回首页
|
// 延时3秒如果加载失败就返回首页
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const article = isBrowser() && document.getElementById('container')
|
const article = isBrowser() && document.getElementById('article-wrapper')
|
||||||
if (!article) {
|
if (!article) {
|
||||||
router.push('/').then(() => {
|
router.push('/').then(() => {
|
||||||
// console.log('找不到页面', router.asPath)
|
// console.log('找不到页面', router.asPath)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const LayoutSearch = (props) => {
|
|||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const { posts, keyword } = props
|
const { posts, keyword } = props
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const container = isBrowser() && document.getElementById('container')
|
const container = isBrowser() && document.getElementById('posts-wrapper')
|
||||||
if (container && container.innerHTML) {
|
if (container && container.innerHTML) {
|
||||||
const re = new RegExp(keyword, 'gim')
|
const re = new RegExp(keyword, 'gim')
|
||||||
const instance = new Mark(container)
|
const instance = new Mark(container)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { isBrowser } from '@/lib/utils'
|
|||||||
export const LayoutSlug = (props) => {
|
export const LayoutSlug = (props) => {
|
||||||
const { post, latestPosts, lock, validPassword } = props
|
const { post, latestPosts, lock, validPassword } = props
|
||||||
const drawerRight = useRef(null)
|
const drawerRight = useRef(null)
|
||||||
const targetRef = isBrowser() ? document.getElementById('container') : null
|
const targetRef = isBrowser() ? document.getElementById('article-wrapper') : null
|
||||||
const floatSlot = post?.toc?.length > 1
|
const floatSlot = post?.toc?.length > 1
|
||||||
? <div className='block lg:hidden'><TocDrawerButton onClick={() => {
|
? <div className='block lg:hidden'><TocDrawerButton onClick={() => {
|
||||||
drawerRight?.current?.handleSwitchVisible()
|
drawerRight?.current?.handleSwitchVisible()
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default function ArticleDetail(props) {
|
|||||||
const showArticleInfo = CONFIG_NEXT.ARTICLE_INFO
|
const showArticleInfo = CONFIG_NEXT.ARTICLE_INFO
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="container"
|
<div id="article-wrapper"
|
||||||
className="shadow md:hover:shadow-2xl overflow-x-auto flex-grow mx-auto w-screen md:w-full ">
|
className="shadow md:hover:shadow-2xl overflow-x-auto flex-grow mx-auto w-screen md:w-full ">
|
||||||
<div itemScope itemType="https://schema.org/Movie"
|
<div itemScope itemType="https://schema.org/Movie"
|
||||||
data-aos="fade-down"
|
data-aos="fade-down"
|
||||||
@@ -77,7 +77,7 @@ export default function ArticleDetail(props) {
|
|||||||
</header>}
|
</header>}
|
||||||
|
|
||||||
{/* Notion内容主体 */}
|
{/* Notion内容主体 */}
|
||||||
<article id='notion-article' className='px-1 max-w-3xl mx-auto'>
|
<article className='px-1 max-w-3xl mx-auto'>
|
||||||
{post && (<NotionPage post={post} />)}
|
{post && (<NotionPage post={post} />)}
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const ArticleLock = props => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id='container' className="shadow md:hover:shadow-2xl overflow-x-auto flex-grow mx-auto w-screen md:w-full py-10 px-5 lg:pt-24 md:px-24 min-h-screen dark:border-gray-700 bg-white dark:bg-gray-800 duration-200">
|
<div id='article-wrapper' className="shadow md:hover:shadow-2xl overflow-x-auto flex-grow mx-auto w-screen md:w-full py-10 px-5 lg:pt-24 md:px-24 min-h-screen dark:border-gray-700 bg-white dark:bg-gray-800 duration-200">
|
||||||
<div className="w-full flex justify-center items-center h-96 font-sans">
|
<div className="w-full flex justify-center items-center h-96 font-sans">
|
||||||
<div className="text-center space-y-3 dark:text-gray-300 text-black">
|
<div className="text-center space-y-3 dark:text-gray-300 text-black">
|
||||||
<div className='font-bold'>{locale.COMMON.ARTICLE_LOCK_TIPS}</div>
|
<div className='font-bold'>{locale.COMMON.ARTICLE_LOCK_TIPS}</div>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount }) => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
<div id="container" className="flex flex-wrap lg:space-y-4 space-y-1">
|
<div id="posts-wrapper" className="flex flex-wrap lg:space-y-4 space-y-1">
|
||||||
{posts?.map(post => (
|
{posts?.map(post => (
|
||||||
<BlogPostCard key={post.id} post={post} />
|
<BlogPostCard key={post.id} post={post} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_NE
|
|||||||
return <div ref={targetRef}>
|
return <div ref={targetRef}>
|
||||||
|
|
||||||
{/* 文章列表 */}
|
{/* 文章列表 */}
|
||||||
<div id='container' className='flex flex-wrap space-y-1 lg:space-y-4'>
|
<div id='posts-wrapper' className='flex flex-wrap space-y-1 lg:space-y-4'>
|
||||||
{postsToShow.map(post => (
|
{postsToShow.map(post => (
|
||||||
<BlogPostCard key={post.id} post={post} showSummary={showSummary} />
|
<BlogPostCard key={post.id} post={post} showSummary={showSummary} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const Progress = ({ targetRef, showPercent = true }) => {
|
|||||||
const currentRef = targetRef?.current || targetRef
|
const currentRef = targetRef?.current || targetRef
|
||||||
const [percent, changePercent] = useState(0)
|
const [percent, changePercent] = useState(0)
|
||||||
const scrollListener = () => {
|
const scrollListener = () => {
|
||||||
const target = currentRef || (isBrowser() && document.getElementById('container'))
|
const target = currentRef || (isBrowser() && document.getElementById('article-wrapper'))
|
||||||
if (target) {
|
if (target) {
|
||||||
const clientHeight = target.clientHeight
|
const clientHeight = target.clientHeight
|
||||||
const scrollY = window.pageYOffset
|
const scrollY = window.pageYOffset
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const LayoutSearch = props => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const container = isBrowser() && document.getElementById('container')
|
const container = isBrowser() && document.getElementById('posts-wrapper')
|
||||||
if (container && container.innerHTML) {
|
if (container && container.innerHTML) {
|
||||||
const re = new RegExp(keyword, 'gim')
|
const re = new RegExp(keyword, 'gim')
|
||||||
const instance = new Mark(container)
|
const instance = new Mark(container)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const LayoutSlug = props => {
|
|||||||
|
|
||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
{!lock && <div id="notion-article" className="px-2">
|
{!lock && <div id="article-wrapper" className="px-2">
|
||||||
<>
|
<>
|
||||||
<ArticleInfo post={post} />
|
<ArticleInfo post={post} />
|
||||||
<NotionPage post={post} />
|
<NotionPage post={post} />
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const BlogListPage = props => {
|
|||||||
return (
|
return (
|
||||||
<div className="w-full md:pr-12 mb-12">
|
<div className="w-full md:pr-12 mb-12">
|
||||||
|
|
||||||
<div id="container">
|
<div id="posts-wrapper">
|
||||||
{posts?.map(post => (
|
{posts?.map(post => (
|
||||||
<BlogPost key={post.id} post={post}/>
|
<BlogPost key={post.id} post={post}/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export const BlogListScroll = props => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="container" className="w-full md:pr-12 mb-12" ref={targetRef}>
|
<div id="posts-wrapper" className="w-full md:pr-12 mb-12" ref={targetRef}>
|
||||||
{postsToShow.map(p => (
|
{postsToShow.map(p => (
|
||||||
<article key={p.id} className="mb-12" >
|
<article key={p.id} className="mb-12" >
|
||||||
<h2 className="mb-4">
|
<h2 className="mb-4">
|
||||||
@@ -78,5 +78,5 @@ export const BlogListScroll = props => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const LayoutSearch = props => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const container = isBrowser() && document.getElementById('container')
|
const container = isBrowser() && document.getElementById('posts-wrapper')
|
||||||
if (container && container.innerHTML) {
|
if (container && container.innerHTML) {
|
||||||
const re = new RegExp(keyword, 'gim')
|
const re = new RegExp(keyword, 'gim')
|
||||||
const instance = new Mark(container)
|
const instance = new Mark(container)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const LayoutSlug = props => {
|
|||||||
|
|
||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
<div id="notion-article" className="px-2 xl:max-w-4xl 2xl:max-w-6xl ">
|
<div id="article-wrapper" className="px-2 xl:max-w-4xl 2xl:max-w-6xl ">
|
||||||
|
|
||||||
<ArticleInfo post={post} />
|
<ArticleInfo post={post} />
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const BlogListPage = props => {
|
|||||||
return (
|
return (
|
||||||
<div className="w-full md:pr-8 mb-12">
|
<div className="w-full md:pr-8 mb-12">
|
||||||
|
|
||||||
<div id="container">
|
<div id="posts-wrapper">
|
||||||
{posts?.map((p, index) => (<div key={p.id}>
|
{posts?.map((p, index) => (<div key={p.id}>
|
||||||
{(index + 1) % 3 === 0 && <AdSlot type='in-article' />}
|
{(index + 1) % 3 === 0 && <AdSlot type='in-article' />}
|
||||||
{ (index + 1) === 4 && <AdSlot type='flow'/>}
|
{ (index + 1) === 4 && <AdSlot type='flow'/>}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export const BlogListScroll = props => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="container" className="w-full md:pr-8 mb-12" ref={targetRef}>
|
<div id="posts-wrapper" className="w-full md:pr-8 mb-12" ref={targetRef}>
|
||||||
{postsToShow.map(p => (
|
{postsToShow.map(p => (
|
||||||
<BlogItem key={p.id} post={p}/>
|
<BlogItem key={p.id} post={p}/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user