mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
@@ -29,7 +29,10 @@ export default {
|
||||
VIEWS: 'Views',
|
||||
COPYRIGHT_NOTICE: 'All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!',
|
||||
RESULT_OF_SEARCH: 'Results Found',
|
||||
ARTICLE_DETAIL: 'Article Details'
|
||||
ARTICLE_DETAIL: 'Article Details',
|
||||
PASSWORD_ERROR: 'Password Error!',
|
||||
ARTICLE_LOCK_TIPS: 'Please Enter the password:',
|
||||
SUBMIT: 'Submit'
|
||||
},
|
||||
PAGINATION: {
|
||||
PREV: 'Prev',
|
||||
|
||||
@@ -31,7 +31,10 @@ export default {
|
||||
VIEWS: '次查看',
|
||||
COPYRIGHT_NOTICE: '本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。',
|
||||
RESULT_OF_SEARCH: '篇搜索到的结果',
|
||||
ARTICLE_DETAIL: '文章详情'
|
||||
ARTICLE_DETAIL: '文章详情',
|
||||
PASSWORD_ERROR: '密码错误!',
|
||||
ARTICLE_LOCK_TIPS: '文章已上锁,请输入访问密码',
|
||||
SUBMIT: '提交'
|
||||
},
|
||||
PAGINATION: {
|
||||
PREV: '上一页',
|
||||
|
||||
@@ -31,7 +31,10 @@ export default {
|
||||
VIEWS: '次查看',
|
||||
COPYRIGHT_NOTICE: '本文採用 CC BY-NC-SA 4.0 許可協議,轉載請註明出處。',
|
||||
RESULT_OF_SEARCH: '篇搜尋到的结果',
|
||||
ARTICLE_DETAIL: '完整文章'
|
||||
ARTICLE_DETAIL: '完整文章',
|
||||
PASSWORD_ERROR: '密碼錯誤!',
|
||||
ARTICLE_LOCK_TIPS: '文章已上鎖,請輸入訪問密碼',
|
||||
SUBMIT: '提交'
|
||||
},
|
||||
PAGINATION: {
|
||||
PREV: '上一頁',
|
||||
|
||||
@@ -14,6 +14,9 @@ import {
|
||||
NotionRenderer
|
||||
} from 'react-notion-x'
|
||||
import LayoutBase from './LayoutBase'
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
import mediumZoom from 'medium-zoom'
|
||||
|
||||
const mapPageUrl = id => {
|
||||
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
||||
@@ -27,18 +30,46 @@ export const LayoutSlug = props => {
|
||||
type: 'article',
|
||||
tags: post.tags
|
||||
}
|
||||
// 文章加锁
|
||||
const articleLock = post.password && post.password !== ''
|
||||
const [lock, setLock] = useState(articleLock)
|
||||
const validPassword = result => {
|
||||
if (result) {
|
||||
setLock(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (post?.blockMap?.block) {
|
||||
if (!lock && post?.blockMap?.block) {
|
||||
post.content = Object.keys(post.blockMap.block)
|
||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||
}
|
||||
|
||||
const zoom =
|
||||
typeof window !== 'undefined' &&
|
||||
mediumZoom({
|
||||
container: '.notion-viewport',
|
||||
background: 'rgba(0, 0, 0, 0.2)',
|
||||
margin: getMediumZoomMargin()
|
||||
})
|
||||
const zoomRef = useRef(zoom ? zoom.clone() : null)
|
||||
useEffect(() => {
|
||||
// 将所有container下的所有图片添加medium-zoom
|
||||
const container = document.getElementById('notion-article')
|
||||
const imgList = container?.getElementsByTagName('img')
|
||||
if (imgList && zoomRef.current) {
|
||||
for (let i = 0; i < imgList.length; i++) {
|
||||
zoomRef.current.attach(imgList[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
return (
|
||||
<LayoutBase {...props} meta={meta}>
|
||||
<div>
|
||||
<h2>{post?.title}</h2>
|
||||
<p>
|
||||
<section id="notion-article" className="px-1">
|
||||
|
||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||
|
||||
{!lock && <section id="notion-article" className="px-1">
|
||||
{post.blockMap && (
|
||||
<NotionRenderer
|
||||
recordMap={post.blockMap}
|
||||
@@ -51,9 +82,27 @@ export const LayoutSlug = props => {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
</p>
|
||||
</section>}
|
||||
|
||||
</div>
|
||||
</LayoutBase>
|
||||
)
|
||||
}
|
||||
|
||||
function getMediumZoomMargin () {
|
||||
const width = window.innerWidth
|
||||
|
||||
if (width < 500) {
|
||||
return 8
|
||||
} else if (width < 800) {
|
||||
return 20
|
||||
} else if (width < 1280) {
|
||||
return 30
|
||||
} else if (width < 1600) {
|
||||
return 40
|
||||
} else if (width < 1920) {
|
||||
return 48
|
||||
} else {
|
||||
return 72
|
||||
}
|
||||
}
|
||||
|
||||
40
themes/empty/components/ArticleLock.js
Normal file
40
themes/empty/components/ArticleLock.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 加密文章校验组件
|
||||
* @param {password, validPassword} props
|
||||
* @param password 正确的密码
|
||||
* @param validPassword(bool) 回调函数,校验正确回调入参为true
|
||||
* @returns
|
||||
*/
|
||||
export const ArticleLock = props => {
|
||||
const { password, validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
const submitPassword = () => {
|
||||
const p = document.getElementById('password')
|
||||
if (p && p.value && p.value === password) {
|
||||
validPassword(true)
|
||||
} else {
|
||||
const tips = document.getElementById('tips')
|
||||
if (tips) {
|
||||
tips.innerHTML = ''
|
||||
tips.innerHTML = `<div class='text-red-500 animate__shakeX animate__animated'>${locale.COMMON.PASSWORD_ERROR}</div>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return <div className='w-full flex justify-center items-center h-96 font-sans'>
|
||||
<div className='text-center space-y-3'>
|
||||
<div className='font-bold'>{locale.COMMON.ARTICLE_LOCK_TIPS}</div>
|
||||
<div className='flex'>
|
||||
<input id="password" type='password' className='w-full text-sm pl-5 rounded-l transition focus:shadow-lgfont-light leading-10 text-black bg-white dark:bg-gray-500'></input>
|
||||
<div onClick={submitPassword} className="px-3 whitespace-nowrap cursor-pointer items-center justify-center py-2 rounded-r duration-300 bg-gray-300" >
|
||||
<i className={'duration-200 cursor-pointer fas fa-key'} > {locale.COMMON.SUBMIT}</i>
|
||||
</div>
|
||||
</div>
|
||||
<div id='tips'>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import 'prismjs/components/prism-python'
|
||||
import 'prismjs/components/prism-typescript'
|
||||
import ArticleDetail from './components/ArticleDetail'
|
||||
import LayoutBase from './LayoutBase'
|
||||
import { useState } from 'react'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
|
||||
export const LayoutSlug = (props) => {
|
||||
const { post } = props
|
||||
@@ -18,6 +20,15 @@ export const LayoutSlug = (props) => {
|
||||
tags: post.tags
|
||||
}
|
||||
|
||||
// 文章加锁
|
||||
const articleLock = post.password && post.password !== ''
|
||||
const [lock, setLock] = useState(articleLock)
|
||||
const validPassword = result => {
|
||||
if (result) {
|
||||
setLock(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (post?.blockMap?.block) {
|
||||
post.content = Object.keys(post.blockMap.block)
|
||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||
@@ -25,7 +36,8 @@ export const LayoutSlug = (props) => {
|
||||
|
||||
return (
|
||||
<LayoutBase meta={meta} {...props} >
|
||||
<ArticleDetail {...props} />
|
||||
</LayoutBase>
|
||||
{!lock && <ArticleDetail {...props} />}
|
||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||
</LayoutBase>
|
||||
)
|
||||
}
|
||||
|
||||
49
themes/fukasawa/components/ArticleLock.js
Normal file
49
themes/fukasawa/components/ArticleLock.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 加密文章校验组件
|
||||
* @param {password, validPassword} props
|
||||
* @param password 正确的密码
|
||||
* @param validPassword(bool) 回调函数,校验正确回调入参为true
|
||||
* @returns
|
||||
*/
|
||||
export const ArticleLock = props => {
|
||||
const { password, validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
const submitPassword = () => {
|
||||
const p = document.getElementById('password')
|
||||
if (p && p.value && p.value === password) {
|
||||
validPassword(true)
|
||||
} else {
|
||||
const tips = document.getElementById('tips')
|
||||
if (tips) {
|
||||
tips.innerHTML = ''
|
||||
tips.innerHTML = `<div class='text-red-500 animate__shakeX animate__animated'>${locale.COMMON.PASSWORD_ERROR}</div>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (<div className="flex justify-center">
|
||||
<div className="shadow md:hover:shadow-2xl overflow-x-auto max-w-5xl 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 subpixel-antialiased">
|
||||
<div className="w-full flex justify-center items-center h-96 font-sans">
|
||||
<div className="text-center space-y-3">
|
||||
<div className='font-bold'>{locale.COMMON.ARTICLE_LOCK_TIPS}</div>
|
||||
<div className="flex">
|
||||
<input
|
||||
id="password" type='password'
|
||||
className="w-full text-sm pl-5 rounded-l transition focus:shadow-lg dark:text-gray-300 font-light leading-10 text-black bg-gray-100 dark:bg-gray-500"
|
||||
></input>
|
||||
<div
|
||||
onClick={submitPassword}
|
||||
className="px-3 whitespace-nowrap cursor-pointer items-center justify-center py-2 bg-gray-700 hover:bg-gray-400 text-white rounded-r duration-300"
|
||||
>
|
||||
<i className={'duration-200 cursor-pointer fas fa-key'} > {locale.COMMON.SUBMIT}</i>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tips"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
@@ -7,8 +7,9 @@ import 'prismjs/components/prism-javascript'
|
||||
import 'prismjs/components/prism-markup'
|
||||
import 'prismjs/components/prism-python'
|
||||
import 'prismjs/components/prism-typescript'
|
||||
import { useRef } from 'react'
|
||||
import { useRef, useState } from 'react'
|
||||
import ArticleDetail from './components/ArticleDetail'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
import HeaderArticle from './components/HeaderArticle'
|
||||
import JumpToCommentButton from './components/JumpToCommentButton'
|
||||
import TocDrawer from './components/TocDrawer'
|
||||
@@ -24,7 +25,16 @@ export const LayoutSlug = props => {
|
||||
tags: post.tags
|
||||
}
|
||||
|
||||
if (post?.blockMap?.block) {
|
||||
// 文章加锁
|
||||
const articleLock = post.password && post.password !== ''
|
||||
const [lock, setLock] = useState(articleLock)
|
||||
const validPassword = result => {
|
||||
if (result) {
|
||||
setLock(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (!lock && post?.blockMap?.block) {
|
||||
post.content = Object.keys(post.blockMap.block)
|
||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||
}
|
||||
@@ -53,7 +63,8 @@ export const LayoutSlug = props => {
|
||||
floatSlot={floatSlot}
|
||||
>
|
||||
<div className="w-full dark:border-gray-600 lg:shadow-md lg:hover:shadow-xl lg:border lg:border-gray-100 lg:rounded-xl lg:px-2 lg:py-4 lg:bg-white lg:dark:bg-gray-800">
|
||||
<ArticleDetail {...props} />
|
||||
{!lock && <ArticleDetail {...props} />}
|
||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||
</div>
|
||||
|
||||
<div className='block lg:hidden'>
|
||||
|
||||
39
themes/hexo/components/ArticleLock.js
Normal file
39
themes/hexo/components/ArticleLock.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 加密文章校验组件
|
||||
* @param {password, validPassword} props
|
||||
* @param password 正确的密码
|
||||
* @param validPassword(bool) 回调函数,校验正确回调入参为true
|
||||
* @returns
|
||||
*/
|
||||
export const ArticleLock = props => {
|
||||
const { password, validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
const submitPassword = () => {
|
||||
const p = document.getElementById('password')
|
||||
if (p && p.value && p.value === password) {
|
||||
validPassword(true)
|
||||
} else {
|
||||
const tips = document.getElementById('tips')
|
||||
if (tips) {
|
||||
tips.innerHTML = ''
|
||||
tips.innerHTML = `<div class='text-red-500 animate__shakeX animate__animated'>${locale.COMMON.PASSWORD_ERROR}</div>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return <div className='w-full flex justify-center items-center h-96 font-sans'>
|
||||
<div className='text-center space-y-3'>
|
||||
<div className='font-bold'>{locale.COMMON.ARTICLE_LOCK_TIPS}</div>
|
||||
<div className='flex'>
|
||||
<input id="password" type='password' className='w-full text-sm pl-5 rounded-l transition focus:shadow-lg dark:text-gray-300 font-light leading-10 text-black bg-gray-100 dark:bg-gray-500'></input>
|
||||
<div onClick={submitPassword} className="px-3 whitespace-nowrap cursor-pointer items-center justify-center py-2 bg-blue-500 hover:bg-blue-400 text-white rounded-r duration-300" >
|
||||
<i className={'duration-200 cursor-pointer fas fa-key'} > {locale.COMMON.SUBMIT}</i>
|
||||
</div>
|
||||
</div>
|
||||
<div id='tips'>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -1,38 +1,16 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { getPageTableOfContents } from 'notion-utils'
|
||||
import 'prismjs'
|
||||
import 'prismjs/components/prism-bash'
|
||||
import 'prismjs/components/prism-javascript'
|
||||
import 'prismjs/components/prism-markup'
|
||||
import 'prismjs/components/prism-python'
|
||||
import 'prismjs/components/prism-typescript'
|
||||
import {
|
||||
Code,
|
||||
Collection,
|
||||
CollectionRow,
|
||||
Equation,
|
||||
NotionRenderer
|
||||
} from 'react-notion-x'
|
||||
import LayoutBase from './LayoutBase'
|
||||
import Comment from '@/components/Comment'
|
||||
import Image from 'next/image'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import formatDate from '@/lib/formatDate'
|
||||
import Link from 'next/link'
|
||||
import mediumZoom from 'medium-zoom'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import ArticleAround from './components/ArticleAround'
|
||||
import Catalog from './components/Catalog'
|
||||
import CategoryItem from './components/CategoryItem'
|
||||
import TagItemMini from './components/TagItemMini'
|
||||
import CONFIG_MEDIUM from './config_medium'
|
||||
|
||||
const mapPageUrl = id => {
|
||||
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
||||
}
|
||||
import LayoutBase from './LayoutBase'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import mediumZoom from 'medium-zoom'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Catalog from './components/Catalog'
|
||||
import { ArticleDetail } from './components/ArticleDetail'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
|
||||
export const LayoutSlug = props => {
|
||||
const { post, prev, next } = props
|
||||
const { post } = props
|
||||
const meta = {
|
||||
title: `${post.title} | ${BLOG.TITLE}`,
|
||||
description: post.summary,
|
||||
@@ -40,15 +18,20 @@ export const LayoutSlug = props => {
|
||||
tags: post.tags
|
||||
}
|
||||
|
||||
if (post?.blockMap?.block) {
|
||||
// 文章加锁
|
||||
const articleLock = post.password && post.password !== ''
|
||||
const [lock, setLock] = useState(articleLock)
|
||||
const validPassword = result => {
|
||||
if (result) {
|
||||
setLock(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (!lock && post?.blockMap?.block) {
|
||||
post.content = Object.keys(post.blockMap.block)
|
||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||
}
|
||||
const { locale } = useGlobal()
|
||||
const date = formatDate(
|
||||
post?.date?.start_date || post.createdTime,
|
||||
locale.LOCALE
|
||||
)
|
||||
|
||||
const zoom =
|
||||
typeof window !== 'undefined' &&
|
||||
@@ -83,68 +66,10 @@ export const LayoutSlug = props => {
|
||||
showInfoCard={true}
|
||||
slotRight={slotRight}
|
||||
>
|
||||
<h1 className="text-4xl pt-12 font-sans dark:text-gray-100">{post?.title}</h1>
|
||||
<section className="flex py-4 items-center font-sans px-1">
|
||||
<Link href="/about" passHref>
|
||||
<>
|
||||
<Image
|
||||
alt={BLOG.AUTHOR}
|
||||
width={25}
|
||||
height={25}
|
||||
loading="lazy"
|
||||
src="/avatar.jpg"
|
||||
className="rounded-full cursor-pointer"
|
||||
/>
|
||||
<div className="mr-3 ml-1 text-green-500 cursor-pointer">
|
||||
{BLOG.AUTHOR}
|
||||
</div>
|
||||
</>
|
||||
</Link>
|
||||
<div className="text-gray-500">{date}</div>
|
||||
<div className="hidden busuanzi_container_page_pv text-gray-500 font-light mr-2">
|
||||
<i className="ml-3 mr-0.5 fas fa-eye" />
|
||||
|
||||
<span className="mr-2 busuanzi_value_page_pv" />
|
||||
</div>
|
||||
</section>
|
||||
{/* Notion文章主体 */}
|
||||
<section id="notion-article" className="px-1 max-w-5xl">
|
||||
{post.blockMap && (
|
||||
<NotionRenderer
|
||||
recordMap={post.blockMap}
|
||||
mapPageUrl={mapPageUrl}
|
||||
components={{
|
||||
equation: Equation,
|
||||
code: Code,
|
||||
collectionRow: CollectionRow,
|
||||
collection: Collection
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">
|
||||
{/* 文章内嵌广告 */}
|
||||
<ins
|
||||
className="adsbygoogle"
|
||||
style={{ display: 'block', textAlign: 'center' }}
|
||||
data-adtest="on"
|
||||
data-ad-layout="in-article"
|
||||
data-ad-format="fluid"
|
||||
data-ad-client="ca-pub-2708419466378217"
|
||||
data-ad-slot="3806269138"
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<div className='flex justify-between'>
|
||||
{ CONFIG_MEDIUM.POST_DETAIL_CATEGORY && post.category && <CategoryItem category={post.category}/>}
|
||||
<div>
|
||||
{ CONFIG_MEDIUM.POST_DETAIL_TAG && post?.tagItems?.map(tag => <TagItemMini key={tag.name} tag={tag} />)}
|
||||
</div>
|
||||
</div>
|
||||
<ArticleAround prev={prev} next={next} />
|
||||
<Comment frontMatter={post} />
|
||||
</section>
|
||||
{!lock && <ArticleDetail {...props} />}
|
||||
|
||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||
</LayoutBase>
|
||||
)
|
||||
}
|
||||
|
||||
102
themes/medium/components/ArticleDetail.js
Normal file
102
themes/medium/components/ArticleDetail.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import {
|
||||
Code,
|
||||
Collection,
|
||||
CollectionRow,
|
||||
Equation,
|
||||
NotionRenderer
|
||||
} from 'react-notion-x'
|
||||
import Comment from '@/components/Comment'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import ArticleAround from './ArticleAround'
|
||||
import CategoryItem from './CategoryItem'
|
||||
import TagItemMini from './TagItemMini'
|
||||
import CONFIG_MEDIUM from '../config_medium'
|
||||
import formatDate from '@/lib/formatDate'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
import 'prismjs'
|
||||
import 'prismjs/components/prism-bash'
|
||||
import 'prismjs/components/prism-javascript'
|
||||
import 'prismjs/components/prism-markup'
|
||||
import 'prismjs/components/prism-python'
|
||||
import 'prismjs/components/prism-typescript'
|
||||
import BLOG from '@/blog.config'
|
||||
|
||||
const mapPageUrl = id => {
|
||||
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
||||
}
|
||||
|
||||
export const ArticleDetail = props => {
|
||||
const { post, prev, next } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
const date = formatDate(
|
||||
post?.date?.start_date || post.createdTime,
|
||||
locale.LOCALE
|
||||
)
|
||||
return <>
|
||||
<h1 className="text-4xl pt-12 font-sans dark:text-gray-100">{post?.title}</h1>
|
||||
<section className="flex py-4 items-center font-sans px-1">
|
||||
<Link href="/about" passHref>
|
||||
<>
|
||||
<Image
|
||||
alt={BLOG.AUTHOR}
|
||||
width={25}
|
||||
height={25}
|
||||
loading="lazy"
|
||||
src="/avatar.jpg"
|
||||
className="rounded-full cursor-pointer"
|
||||
/>
|
||||
<div className="mr-3 ml-1 text-green-500 cursor-pointer">
|
||||
{BLOG.AUTHOR}
|
||||
</div>
|
||||
</>
|
||||
</Link>
|
||||
<div className="text-gray-500">{date}</div>
|
||||
<div className="hidden busuanzi_container_page_pv text-gray-500 font-light mr-2">
|
||||
<i className="ml-3 mr-0.5 fas fa-eye" />
|
||||
|
||||
<span className="mr-2 busuanzi_value_page_pv" />
|
||||
</div>
|
||||
</section>
|
||||
{/* Notion文章主体 */}
|
||||
<section id="notion-article" className="px-1 max-w-5xl">
|
||||
{post.blockMap && (
|
||||
<NotionRenderer
|
||||
recordMap={post.blockMap}
|
||||
mapPageUrl={mapPageUrl}
|
||||
components={{
|
||||
equation: Equation,
|
||||
code: Code,
|
||||
collectionRow: CollectionRow,
|
||||
collection: Collection
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">
|
||||
{/* 文章内嵌广告 */}
|
||||
<ins
|
||||
className="adsbygoogle"
|
||||
style={{ display: 'block', textAlign: 'center' }}
|
||||
data-adtest="on"
|
||||
data-ad-layout="in-article"
|
||||
data-ad-format="fluid"
|
||||
data-ad-client="ca-pub-2708419466378217"
|
||||
data-ad-slot="3806269138"
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<div className='flex justify-between'>
|
||||
{ CONFIG_MEDIUM.POST_DETAIL_CATEGORY && post.category && <CategoryItem category={post.category}/>}
|
||||
<div>
|
||||
{ CONFIG_MEDIUM.POST_DETAIL_TAG && post?.tagItems?.map(tag => <TagItemMini key={tag.name} tag={tag} />)}
|
||||
</div>
|
||||
</div>
|
||||
<ArticleAround prev={prev} next={next} />
|
||||
<Comment frontMatter={post} />
|
||||
</section>
|
||||
</>
|
||||
}
|
||||
40
themes/medium/components/ArticleLock.js
Normal file
40
themes/medium/components/ArticleLock.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 加密文章校验组件
|
||||
* @param {password, validPassword} props
|
||||
* @param password 正确的密码
|
||||
* @param validPassword(bool) 回调函数,校验正确回调入参为true
|
||||
* @returns
|
||||
*/
|
||||
export const ArticleLock = props => {
|
||||
const { password, validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
const submitPassword = () => {
|
||||
const p = document.getElementById('password')
|
||||
if (p && p.value && p.value === password) {
|
||||
validPassword(true)
|
||||
} else {
|
||||
const tips = document.getElementById('tips')
|
||||
if (tips) {
|
||||
tips.innerHTML = ''
|
||||
tips.innerHTML = `<div class='text-red-500 animate__shakeX animate__animated'>${locale.COMMON.PASSWORD_ERROR}</div>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return <div className='w-full flex justify-center items-center h-96 font-sans'>
|
||||
<div className='text-center space-y-3'>
|
||||
<div className='font-bold'>{locale.COMMON.ARTICLE_LOCK_TIPS}</div>
|
||||
<div className='flex'>
|
||||
<input id="password" type='password' className='w-full text-sm pl-5 rounded-l transition focus:shadow-lg dark:text-gray-300 font-light leading-10 text-black bg-gray-100 dark:bg-gray-500'></input>
|
||||
<div onClick={submitPassword} className="px-3 whitespace-nowrap cursor-pointer items-center justify-center py-2 bg-green-500 hover:bg-green-400 text-white rounded-r duration-300" >
|
||||
<i className={'duration-200 cursor-pointer fas fa-key'} > {locale.COMMON.SUBMIT}</i>
|
||||
</div>
|
||||
</div>
|
||||
<div id='tips'>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -6,11 +6,12 @@ import Card from './components/Card'
|
||||
import LatestPostsGroup from './components/LatestPostsGroup'
|
||||
import ArticleDetail from './components/ArticleDetail'
|
||||
import TocDrawer from './components/TocDrawer'
|
||||
import { useRef } from 'react'
|
||||
import { useRef, useState } from 'react'
|
||||
import CONFIG_NEXT from './config_next'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
|
||||
export const LayoutSlug = (props) => {
|
||||
const { post, prev, next, recommendPosts, latestPosts, showArticleInfo } = props
|
||||
const { post, latestPosts } = props
|
||||
const meta = {
|
||||
title: `${post.title} | ${BLOG.TITLE}`,
|
||||
description: post.summary,
|
||||
@@ -18,12 +19,22 @@ export const LayoutSlug = (props) => {
|
||||
tags: post.tags
|
||||
}
|
||||
|
||||
const drawerRight = useRef(null)
|
||||
const targetRef = typeof window !== 'undefined' ? document.getElementById('container') : null
|
||||
if (post?.blockMap?.block) {
|
||||
// 文章加锁
|
||||
const articleLock = post.password && post.password !== ''
|
||||
const [lock, setLock] = useState(articleLock)
|
||||
const validPassword = result => {
|
||||
if (result) {
|
||||
setLock(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (!lock && post?.blockMap?.block) {
|
||||
post.content = Object.keys(post.blockMap.block)
|
||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||
}
|
||||
|
||||
const drawerRight = useRef(null)
|
||||
const targetRef = typeof window !== 'undefined' ? document.getElementById('container') : null
|
||||
const floatSlot = post?.toc?.length > 1
|
||||
? <div className='block lg:hidden'><TocDrawerButton onClick={() => {
|
||||
drawerRight?.current?.handleSwitchVisible()
|
||||
@@ -39,13 +50,10 @@ export const LayoutSlug = (props) => {
|
||||
CONFIG_NEXT.RIGHT_LATEST_POSTS && <Card><LatestPostsGroup posts={latestPosts} /></Card>
|
||||
}
|
||||
>
|
||||
<ArticleDetail
|
||||
post={post}
|
||||
prev={prev}
|
||||
next={next}
|
||||
recommendPosts={recommendPosts}
|
||||
showArticleInfo={showArticleInfo}
|
||||
/>
|
||||
|
||||
{!lock && <ArticleDetail {...props} />}
|
||||
|
||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||
|
||||
{/* 悬浮目录按钮 */}
|
||||
<div className='block lg:hidden'>
|
||||
|
||||
49
themes/next/components/ArticleLock.js
Normal file
49
themes/next/components/ArticleLock.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 加密文章校验组件
|
||||
* @param {password, validPassword} props
|
||||
* @param password 正确的密码
|
||||
* @param validPassword(bool) 回调函数,校验正确回调入参为true
|
||||
* @returns
|
||||
*/
|
||||
export const ArticleLock = props => {
|
||||
const { password, validPassword } = props
|
||||
const { locale } = useGlobal()
|
||||
|
||||
const submitPassword = () => {
|
||||
const p = document.getElementById('password')
|
||||
if (p && p.value && p.value === password) {
|
||||
validPassword(true)
|
||||
} else {
|
||||
const tips = document.getElementById('tips')
|
||||
if (tips) {
|
||||
tips.innerHTML = ''
|
||||
tips.innerHTML = `<div class='text-red-500 animate__shakeX animate__animated'>${locale.COMMON.PASSWORD_ERROR}</div>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div 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="text-center space-y-3">
|
||||
<div className='font-bold'>{locale.COMMON.ARTICLE_LOCK_TIPS}</div>
|
||||
<div className="flex">
|
||||
<input
|
||||
id="password" type='password'
|
||||
className="w-full text-sm pl-5 transition focus:shadow-lg dark:text-gray-300 font-light leading-10 text-black bg-gray-100 dark:bg-gray-500"
|
||||
></input>
|
||||
<div
|
||||
onClick={submitPassword}
|
||||
className="px-3 whitespace-nowrap cursor-pointer items-center justify-center py-2 bg-gray-700 hover:bg-gray-400 text-white duration-300"
|
||||
>
|
||||
<i className={'duration-200 cursor-pointer fas fa-key'} > {locale.COMMON.SUBMIT}</i>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tips"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user