多语言配置,starter主题微调、兼容

This commit is contained in:
tangly1024.com
2024-04-12 12:44:02 +08:00
parent 16779a0614
commit aefca7171e
10 changed files with 597 additions and 500 deletions

View File

@@ -52,7 +52,7 @@ export const siteConfig = (key, defaultVal = null, extendConfig = null) => {
}
}
// 其次 有传入的配置参考,则尝试读取
// 其次 有传入的extendConfig,则尝试读取
if (!val && extendConfig) {
val = extendConfig[key]
}
@@ -64,24 +64,37 @@ export const siteConfig = (key, defaultVal = null, extendConfig = null) => {
if (!val) {
return defaultVal
} else {
if (typeof val === 'string') {
if (val === 'true' || val === 'false') {
return JSON.parse(val)
}
if (/^\d+$/.test(val)) {
// 如果是数字使用parseFloat或者parseInt将字符串转换为数字
return parseInt(val)
}
return val
} else {
try {
return JSON.parse(val)
} catch (error) {
// 如果值是一个字符串但不是有效的 JSON 格式,直接返回字符串
return val
}
}
// 从Notion_CONFIG读取的配置通常都是字符串适当转义
if (typeof val === 'string') {
// 解析布尔
if (val === 'true' || val === 'false') {
return JSON.parse(val)
}
// 解析数字parseInt将字符串转换为数字
if (/^\d+$/.test(val)) {
return parseInt(val)
}
// 转移 [] , {} 这种json串为json对象
try {
const parsedJson = JSON.parse(val)
// 检查解析后的结果是否是对象或数组
if (typeof parsedJson === 'object' && parsedJson !== null) {
return parsedJson
}
} catch (error) {
// JSON 解析失败,返回原始字符串值
return val
}
}
try {
return JSON.parse(val)
} catch (error) {
// 如果值是一个字符串但不是有效的 JSON 格式,直接返回字符串
return val
}
}

View File

@@ -7,6 +7,7 @@
*
*/
import { getDateValue, getTextContent } from 'notion-utils'
import { deepClone } from '../utils'
import getAllPageIds from './getAllPageIds'
import { getPostBlocks } from './getPostBlocks'
@@ -162,9 +163,12 @@ export async function getConfigMapFromConfigPage(allPages) {
}
// 最后检查Notion_Config页面的INLINE_CONFIG是否是一个js对象
const inlineConfigs = parseConfig(configPage?.INLINE_CONFIG)
return Object.assign({}, notionConfig, inlineConfigs)
const combine = Object.assign(
{},
deepClone(notionConfig),
parseConfig(notionConfig?.INLINE_CONFIG)
)
return combine
}
/**
@@ -176,19 +180,13 @@ export function parseConfig(configString) {
if (!configString) {
return {}
}
// 解析对象
try {
// 尝试解析为 JSON 对象
const jsonConfig = JSON.parse(configString)
return jsonConfig
} catch (error) {
// 如果解析失败,则尝试使用 eval()
try {
// eslint-disable-next-line no-eval
const config = eval('(' + configString + ')')
return config
} catch (evalError) {
console.error('解析 INLINE_CONFIG 配置时出错:', evalError)
return {}
}
// eslint-disable-next-line no-eval
const config = eval('(' + configString + ')')
return config
} catch (evalError) {
console.error('解析 eval(INLINE_CONFIG) 配置时出错:', evalError)
return {}
}
}

View File

@@ -55,13 +55,14 @@ export async function getStaticProps({
locale
}) {
let fullSlug = prefix + '/' + slug + '/' + suffix.join('/')
if (JSON.parse(BLOG.PSEUDO_STATIC)) {
const from = `slug-props-${fullSlug}`
const props = await getGlobalData({ from, locale })
if (siteConfig('PSEUDO_STATIC', BLOG.PSEUDO_STATIC, props.NOTION_CONFIG)) {
if (!fullSlug.endsWith('.html')) {
fullSlug += '.html'
}
}
const from = `slug-props-${fullSlug}`
const props = await getGlobalData({ from, locale })
// 在列表内查找文章
props.post = props?.allPages?.find(p => {
return (

View File

@@ -39,13 +39,14 @@ export async function getStaticPaths() {
export async function getStaticProps({ params: { prefix, slug }, locale }) {
let fullSlug = prefix + '/' + slug
if (JSON.parse(BLOG.PSEUDO_STATIC)) {
const from = `slug-props-${fullSlug}`
const props = await getGlobalData({ from, locale })
if (siteConfig('PSEUDO_STATIC', BLOG.PSEUDO_STATIC, props.NOTION_CONFIG)) {
if (!fullSlug.endsWith('.html')) {
fullSlug += '.html'
}
}
const from = `slug-props-${fullSlug}`
const props = await getGlobalData({ from, locale })
// 在列表内查找文章
props.post = props?.allPages?.find(p => {
return (

View File

@@ -81,13 +81,14 @@ export async function getStaticPaths() {
export async function getStaticProps({ params: { prefix }, locale }) {
let fullSlug = prefix
if (JSON.parse(BLOG.PSEUDO_STATIC)) {
const from = `slug-props-${fullSlug}`
const props = await getGlobalData({ from, locale })
if (siteConfig('PSEUDO_STATIC', BLOG.PSEUDO_STATIC, props.NOTION_CONFIG)) {
if (!fullSlug.endsWith('.html')) {
fullSlug += '.html'
}
}
const from = `slug-props-${fullSlug}`
const props = await getGlobalData({ from, locale })
// 在列表内查找文章
props.post = props?.allPages?.find(p => {
return (

View File

@@ -1,6 +1,6 @@
// eslint-disable-next-line @next/next/no-document-import-in-page
import Document, { Html, Head, Main, NextScript } from 'next/document'
import BLOG from '@/blog.config'
import Document, { Head, Html, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
@@ -10,29 +10,52 @@ class MyDocument extends Document {
render() {
return (
<Html lang={BLOG.LANG}>
<Head>
<link rel='icon' href= {`${BLOG.BLOG_FAVICON}`} />
{/* 预加载字体 */}
{BLOG.FONT_AWESOME && <>
<link rel='preload' href={BLOG.FONT_AWESOME} as="style" crossOrigin="anonymous" />
<link rel="stylesheet" href={BLOG.FONT_AWESOME} crossOrigin="anonymous" referrerPolicy="no-referrer" />
</>}
<Html lang={BLOG.LANG}>
<Head>
<link rel='icon' href={`${BLOG.BLOG_FAVICON}`} />
{/* 预加载字体 */}
{BLOG.FONT_AWESOME && (
<>
<link
rel='preload'
href={BLOG.FONT_AWESOME}
as='style'
crossOrigin='anonymous'
/>
<link
rel='stylesheet'
href={BLOG.FONT_AWESOME}
crossOrigin='anonymous'
referrerPolicy='no-referrer'
/>
</>
)}
{BLOG.FONT_URL?.map((fontUrl, index) => {
if (fontUrl.endsWith('.css') || fontUrl.includes('googleapis.com/css')) {
return <link key={index} rel="stylesheet" href={fontUrl} />
} else {
return <link key={index} rel="preload" href={fontUrl} as="font" type="font/woff2" />
}
})}
</Head>
{BLOG.FONT_URL?.map((fontUrl, index) => {
if (
fontUrl.endsWith('.css') ||
fontUrl.includes('googleapis.com/css')
) {
return <link key={index} rel='stylesheet' href={fontUrl} />
} else {
return (
<link
key={index}
rel='preload'
href={fontUrl}
as='font'
type='font/woff2'
/>
)
}
})}
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}

View File

@@ -1,164 +1,187 @@
import { siteConfig } from '@/lib/config'
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
import SocialButton from '@/themes/fukasawa/components/SocialButton'
import CONFIG from '../config'
import { Logo } from './Logo'
import SocialButton from '@/themes/fukasawa/components/SocialButton'
import { SVGFooterCircleBG } from './svg/SVGFooterCircleBG'
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
/* eslint-disable @next/next/no-img-element */
export const Footer = (props) => {
export const Footer = props => {
const latestPosts = props?.latestPosts ? props?.latestPosts.slice(0, 2) : []
return <>
{/* <!-- ====== Footer Section Start --> */}
<footer
className="wow fadeInUp relative z-10 bg-[#090E34] pt-20 lg:pt-[100px]"
data-wow-delay=".15s"
>
<div className="container">
<div className="-mx-4 flex flex-wrap">
<div className="w-full px-4 sm:w-1/2 md:w-1/2 lg:w-4/12 xl:w-3/12">
<div className="mb-10 w-full">
<a
className="-mx-4 mb-6 inline-block max-w-[160px]"
>
<Logo white={true}/>
</a>
<p className="mb-8 max-w-[270px] text-base text-gray-7">
{siteConfig('STARTER_FOOTER_SLOGAN', null, CONFIG)}
</p>
<div className="-mx-3 flex items-center">
<div className='mx-3'><SocialButton/></div>
</div>
</div>
</div>
{/* 中间三列菜单组 */}
{CONFIG.STARTER_FOOTER_LINK_GROUP?.map((item, index) => {
return <div key={index} className="w-full px-4 sm:w-1/2 md:w-1/2 lg:w-2/12 xl:w-2/12">
<div className="mb-10 w-full">
<h4 className="mb-9 text-lg font-semibold text-white">
{item.TITLE}
</h4>
<ul>
{item?.LINK_GROUP?.map((l, i) => {
return <li key={i}>
<a href={l.URL}
className="mb-3 inline-block text-base text-gray-7 hover:text-primary"
>
{l.TITLE}
</a>
</li>
})}
</ul>
</div>
</div>
})}
{/* 页脚右侧最新博文 */}
<div className="w-full px-4 md:w-2/3 lg:w-6/12 xl:w-3/12">
<div className="mb-10 w-full">
<h4 className="mb-9 text-lg font-semibold text-white">
{siteConfig('STARTER_FOOTER_BLOG_LATEST_TITLE', null, CONFIG)}
</h4>
{/* 展示两条最新博客文章 */}
<div className="flex flex-col gap-8">
{latestPosts?.map((item, index) => {
const url = checkContainHttp(item.slug) ? sliceUrlFromHttp(item.slug) : `${siteConfig('SUB_PATH', '')}/${item.slug}`
return <a key={index}
href={url}
className="group flex items-center gap-[22px]"
>
<div className="overflow-hidden rounded w-20 h-12">
<img
src={item.pageCoverThumbnail}
alt={item.title}
/>
</div>
<span
className="line-clamp-2 max-w-[180px] text-base text-gray-7 group-hover:text-white"
>
{item.title}
</span>
const STARTER_FOOTER_LINK_GROUP = siteConfig(
'STARTER_FOOTER_LINK_GROUP',
[],
CONFIG
)
return (
<>
{/* <!-- ====== Footer Section Start --> */}
<footer
className='wow fadeInUp relative z-10 bg-[#090E34] pt-20 lg:pt-[100px]'
data-wow-delay='.15s'>
<div className='container'>
<div className='-mx-4 flex flex-wrap'>
<div className='w-full px-4 sm:w-1/2 md:w-1/2 lg:w-4/12 xl:w-3/12'>
<div className='mb-10 w-full'>
<a className='-mx-4 mb-6 inline-block max-w-[160px]'>
<Logo white={true} />
</a>
})}
</div>
</div>
</div>
</div>
</div>
{/* 底部版权信息相关 */}
<div
className="mt-12 border-t border-[#8890A4] border-opacity-40 py-8 lg:mt-[60px]"
>
<div className="container">
<div className="-mx-4 flex flex-wrap">
<div className="w-full px-4 md:w-2/3 lg:w-1/2">
<div className="my-1">
<div
className="-mx-3 flex items-center justify-center md:justify-start"
>
<a
href= {siteConfig('STARTER_FOOTER_PRIVACY_POLICY_URL', null, CONFIG)}
className="px-3 text-base text-gray-7 hover:text-white hover:underline"
>
{siteConfig('STARTER_FOOTER_PRIVACY_POLICY_TEXT', null, CONFIG)}
</a>
<a
href= {siteConfig('STARTER_FOOTER_PRIVACY_LEGAL_NOTICE_URL', null, CONFIG)}
className="px-3 text-base text-gray-7 hover:text-white hover:underline"
>
{siteConfig('STARTER_FOOTER_PRIVACY_LEGAL_NOTICE_TEXT', null, CONFIG)}
</a>
<a
href= {siteConfig('STARTER_FOOTER_PRIVACY_TERMS_OF_SERVICE_URL', null, CONFIG)}
className="px-3 text-base text-gray-7 hover:text-white hover:underline"
>
{siteConfig('STARTER_FOOTER_PRIVACY_TERMS_OF_SERVICE_TEXT', null, CONFIG)}
</a>
</div>
</div>
</div>
<div className="w-full px-4 md:w-1/3 lg:w-1/2">
<div className="my-1 flex justify-center md:justify-end">
<p className="text-base text-gray-7">
Designed and Developed by
<a
href="https://github.com/tangly1024/NotionNext"
rel="nofollow noopner noreferrer"
target="_blank"
className="px-1 text-gray-1 hover:underline"
>
NotionNext {siteConfig('VERSION')}
</a>
<p className='mb-8 max-w-[270px] text-base text-gray-7'>
{siteConfig('STARTER_FOOTER_SLOGAN', null, CONFIG)}
</p>
<div className='-mx-3 flex items-center'>
<div className='mx-3'>
<SocialButton />
</div>
</div>
</div>
</div>
{/* 中间三列菜单组 */}
{STARTER_FOOTER_LINK_GROUP?.map((item, index) => {
return (
<div
key={index}
className='w-full px-4 sm:w-1/2 md:w-1/2 lg:w-2/12 xl:w-2/12'>
<div className='mb-10 w-full'>
<h4 className='mb-9 text-lg font-semibold text-white'>
{item.TITLE}
</h4>
<ul>
{item?.LINK_GROUP?.map((l, i) => {
return (
<li key={i}>
<a
href={l.URL}
className='mb-3 inline-block text-base text-gray-7 hover:text-primary'>
{l.TITLE}
</a>
</li>
)
})}
</ul>
</div>
</div>
)
})}
{/* 页脚右侧最新博文 */}
<div className='w-full px-4 md:w-2/3 lg:w-6/12 xl:w-3/12'>
<div className='mb-10 w-full'>
<h4 className='mb-9 text-lg font-semibold text-white'>
{siteConfig('STARTER_FOOTER_BLOG_LATEST_TITLE', null, CONFIG)}
</h4>
{/* 展示两条最新博客文章 */}
<div className='flex flex-col gap-8'>
{latestPosts?.map((item, index) => {
const url = checkContainHttp(item.slug)
? sliceUrlFromHttp(item.slug)
: `${siteConfig('SUB_PATH', '')}/${item.slug}`
return (
<a
key={index}
href={url}
className='group flex items-center gap-[22px]'>
<div className='overflow-hidden rounded w-20 h-12'>
<img src={item.pageCoverThumbnail} alt={item.title} />
</div>
<span className='line-clamp-2 max-w-[180px] text-base text-gray-7 group-hover:text-white'>
{item.title}
</span>
</a>
)
})}
</div>
</div>
</div>
</div>
</div>
</div>
{/* Footer 背景 */}
<div>
<span className="absolute left-0 top-0 z-[-1]">
<img src="/images/starter/footer/shape-1.svg" alt="" />
</span>
{/* 底部版权信息相关 */}
<span className="absolute bottom-0 right-0 z-[-1]">
<img src="/images/starter/footer/shape-3.svg" alt="" />
</span>
<div className='mt-12 border-t border-[#8890A4] border-opacity-40 py-8 lg:mt-[60px]'>
<div className='container'>
<div className='-mx-4 flex flex-wrap'>
<div className='w-full px-4 md:w-2/3 lg:w-1/2'>
<div className='my-1'>
<div className='-mx-3 flex items-center justify-center md:justify-start'>
<a
href={siteConfig(
'STARTER_FOOTER_PRIVACY_POLICY_URL',
null,
CONFIG
)}
className='px-3 text-base text-gray-7 hover:text-white hover:underline'>
{siteConfig(
'STARTER_FOOTER_PRIVACY_POLICY_TEXT',
null,
CONFIG
)}
</a>
<a
href={siteConfig(
'STARTER_FOOTER_PRIVACY_LEGAL_NOTICE_URL',
null,
CONFIG
)}
className='px-3 text-base text-gray-7 hover:text-white hover:underline'>
{siteConfig(
'STARTER_FOOTER_PRIVACY_LEGAL_NOTICE_TEXT',
null,
CONFIG
)}
</a>
<a
href={siteConfig(
'STARTER_FOOTER_PRIVACY_TERMS_OF_SERVICE_URL',
null,
CONFIG
)}
className='px-3 text-base text-gray-7 hover:text-white hover:underline'>
{siteConfig(
'STARTER_FOOTER_PRIVACY_TERMS_OF_SERVICE_TEXT',
null,
CONFIG
)}
</a>
</div>
</div>
</div>
<div className='w-full px-4 md:w-1/3 lg:w-1/2'>
<div className='my-1 flex justify-center md:justify-end'>
<p className='text-base text-gray-7'>
Designed and Developed by
<a
href='https://github.com/tangly1024/NotionNext'
rel='nofollow noopner noreferrer'
target='_blank'
className='px-1 text-gray-1 hover:underline'>
NotionNext {siteConfig('VERSION')}
</a>
</p>
</div>
</div>
</div>
</div>
</div>
<span className="absolute right-0 top-0 z-[-1]">
<SVGFooterCircleBG/>
</span>
</div>
</footer>
{/* <!-- ====== Footer Section End --> */}
{/* Footer 背景 */}
<div>
<span className='absolute left-0 top-0 z-[-1]'>
<img src='/images/starter/footer/shape-1.svg' alt='' />
</span>
<span className='absolute bottom-0 right-0 z-[-1]'>
<img src='/images/starter/footer/shape-3.svg' alt='' />
</span>
<span className='absolute right-0 top-0 z-[-1]'>
<SVGFooterCircleBG />
</span>
</div>
</footer>
{/* <!-- ====== Footer Section End --> */}
</>
)
}

View File

@@ -2,94 +2,85 @@
import { siteConfig } from '@/lib/config'
import CONFIG from '../config'
import { SVGAvatarBG } from './svg/SVGAvatarBG'
import { SVGFacebook } from './svg/SVGFacebook'
import { SVGTwitter } from './svg/SVGTwitter'
import { SVGInstagram } from './svg/SVGInstagram'
export const Team = () => {
return <>
{/* <!-- ====== Team Section Start --> */}
<section
id="team"
className="overflow-hidden bg-gray-1 pb-12 pt-20 dark:bg-dark-2 lg:pb-[90px] lg:pt-[120px]"
>
<div className="container mx-auto">
<div className="-mx-4 flex flex-wrap">
<div className="w-full px-4">
<div className="mx-auto mb-[60px] max-w-[485px] text-center">
<span className="mb-2 block text-lg font-semibold text-primary">
{siteConfig('STARTER_TEAM_TITLE', null, CONFIG)}
</span>
<h2
className="mb-3 text-3xl font-bold leading-[1.2] text-dark dark:text-white sm:text-4xl md:text-[40px]"
>
{siteConfig('STARTER_TEAM_TEXT_1', null, CONFIG)}
</h2>
<p dangerouslySetInnerHTML={
{ __html: siteConfig('STARTER_TEAM_TEXT_2', null, CONFIG) }
} className="text-base text-body-color dark:text-dark-6">
</p>
return (
<>
{/* <!-- ====== Team Section Start --> */}
<section
id='team'
className='overflow-hidden bg-gray-1 pb-12 pt-20 dark:bg-dark-2 lg:pb-[90px] lg:pt-[120px]'>
<div className='container mx-auto'>
<div className='-mx-4 flex flex-wrap'>
<div className='w-full px-4'>
<div className='mx-auto mb-[60px] max-w-[485px] text-center'>
<span className='mb-2 block text-lg font-semibold text-primary'>
{siteConfig('STARTER_TEAM_TITLE', null, CONFIG)}
</span>
<h2 className='mb-3 text-3xl font-bold leading-[1.2] text-dark dark:text-white sm:text-4xl md:text-[40px]'>
{siteConfig('STARTER_TEAM_TEXT_1', null, CONFIG)}
</h2>
<p
dangerouslySetInnerHTML={{
__html: siteConfig('STARTER_TEAM_TEXT_2', null, CONFIG)
}}
className='text-base text-body-color dark:text-dark-6'></p>
</div>
</div>
</div>
</div>
{/* 团队成员排列矩阵 */}
<div className="-mx-4 flex flex-wrap justify-center">
{/* 团队成员排列矩阵 */}
<div className='-mx-4 flex flex-wrap justify-center'>
{CONFIG.STARTER_TEAM_ITEMS.map((item, index) => {
return <div key={index} className="w-full px-4 sm:w-1/2 lg:w-1/4 xl:w-1/4">
return (
<div
className="group mb-8 rounded-xl bg-white px-5 pb-10 pt-12 shadow-testimonial dark:bg-dark dark:shadow-none"
>
{/* 头像 */}
<div className="relative z-10 mx-auto mb-5 h-[120px] w-[120px]">
<img
src={item.STARTER_TEAM_ITEM_AVATAR}
alt="team image"
className="h-[120px] w-[120px] rounded-full"
/>
<span
className="absolute bottom-0 left-0 -z-10 h-10 w-10 rounded-full bg-secondary opacity-0 transition-all group-hover:opacity-100"
></span>
<span
className="absolute right-0 top-0 -z-10 opacity-0 transition-all group-hover:opacity-100"
>
<SVGAvatarBG/>
</span>
</div>
key={index}
className='w-full px-4 sm:w-1/2 lg:w-1/4 xl:w-1/4'>
<div className='group mb-8 rounded-xl bg-white px-5 pb-10 pt-12 shadow-testimonial dark:bg-dark dark:shadow-none'>
{/* 头像 */}
<div className='relative z-10 mx-auto mb-5 h-[120px] w-[120px]'>
<img
src={item.STARTER_TEAM_ITEM_AVATAR}
alt='team image'
className='h-[120px] w-[120px] rounded-full'
/>
<span className='absolute bottom-0 left-0 -z-10 h-10 w-10 rounded-full bg-secondary opacity-0 transition-all group-hover:opacity-100'></span>
<span className='absolute right-0 top-0 -z-10 opacity-0 transition-all group-hover:opacity-100'>
<SVGAvatarBG />
</span>
</div>
{/* 文字介绍 */}
<div className="text-center">
{/* 文字介绍 */}
<div className='text-center'>
<h4 className='mb-1 text-lg font-semibold text-dark dark:text-white'>
{item.STARTER_TEAM_ITEM_NICKNAME}
</h4>
<h4
className="mb-1 text-lg font-semibold text-dark dark:text-white"
>
{item.STARTER_TEAM_ITEM_NICKNAME}
</h4>
<p className='mb-5 text-sm text-body-color dark:text-dark-6'>
{item.STARTER_TEAM_ITEM_DESCRIPTION}
</p>
<p className="mb-5 text-sm text-body-color dark:text-dark-6">
{item.STARTER_TEAM_ITEM_DESCRIPTION}
</p>
{/* 社交链接 */}
<div className="flex items-center justify-center gap-5">
<a className="text-dark-6 hover:text-primary" >
<SVGFacebook/>
</a>
<a className="text-dark-6 hover:text-primary" >
<SVGTwitter/>
</a>
<a className="text-dark-6 hover:text-primary" >
<SVGInstagram/>
</a>
{/* 社交链接 */}
{/* <div className='flex items-center justify-center gap-5'>
<a className='text-dark-6 hover:text-primary'>
<SVGFacebook className='fill-current' />
</a>
<a className='text-dark-6 hover:text-primary'>
<SVGTwitter className='fill-current' />
</a>
<a className='text-dark-6 hover:text-primary'>
<SVGInstagram className='fill-current' />
</a>
</div> */}
</div>
</div>
</div>
</div>
)
})}
</div>
</div>
</div>
</section>
{/* <!-- ====== Team Section End --> */}
</section>
{/* <!-- ====== Team Section End --> */}
</>
)
}

View File

@@ -1,19 +1,25 @@
/* eslint-disable react/no-unescaped-entities */
/* eslint-disable @next/next/no-img-element */
import { siteConfig } from '@/lib/config';
import { loadExternalResource } from '@/lib/utils';
import { useEffect } from 'react';
import CONFIG from '../config';
import { SVGLeftArrow } from './svg/SVGLeftArrow';
import { SVGRightArrow } from './svg/SVGRightArrow';
import { siteConfig } from '@/lib/config'
import { loadExternalResource } from '@/lib/utils'
import { useEffect } from 'react'
import CONFIG from '../config'
import { SVGLeftArrow } from './svg/SVGLeftArrow'
import { SVGRightArrow } from './svg/SVGRightArrow'
/**
* 一些外部js
*/
const loadExternal = async () => {
await loadExternalResource('https://cdnjs.cloudflare.com/ajax/libs/Swiper/11.0.5/swiper-bundle.css', 'css');
await loadExternalResource('https://cdnjs.cloudflare.com/ajax/libs/Swiper/11.0.5/swiper-bundle.min.js', 'js');
await loadExternalResource(
'https://cdnjs.cloudflare.com/ajax/libs/Swiper/11.0.5/swiper-bundle.css',
'css'
)
await loadExternalResource(
'https://cdnjs.cloudflare.com/ajax/libs/Swiper/11.0.5/swiper-bundle.min.js',
'js'
)
const Swiper = window.Swiper
if (!Swiper) {
@@ -45,101 +51,109 @@ const loadExternal = async () => {
spaceBetween: 30
}
}
});
};
})
}
export const Testimonials = () => {
useEffect(() => {
loadExternal()
}, [])
// 用户评分
const ratings = [1, 2, 3, 4, 5];
return <>
{/* <!-- ====== Testimonial Section Start --> */}
<section
id="testimonials"
className="overflow-hidden bg-gray-1 py-20 dark:bg-dark-2 md:py-[120px]"
>
<div className="container mx-auto">
<div className="-mx-4 flex flex-wrap justify-center">
<div className="w-full px-4">
<div className="mx-auto mb-[60px] max-w-[485px] text-center">
<span className="mb-2 block text-lg font-semibold text-primary">
{siteConfig('STARTER_TESTIMONIALS_TITLE', null, CONFIG)}
</span>
<h2
className="mb-3 text-3xl font-bold leading-[1.2] text-dark dark:text-white sm:text-4xl md:text-[40px]"
>
{siteConfig('STARTER_TESTIMONIALS_TEXT_1', null, CONFIG)}
</h2>
<p className="text-base text-body-color dark:text-dark-6">
{siteConfig('STARTER_TESTIMONIALS_TEXT_2', null, CONFIG)}
</p>
const ratings = [1, 2, 3, 4, 5]
const STARTER_TESTIMONIALS_ITEMS = siteConfig(
'STARTER_TESTIMONIALS_ITEMS',
[],
CONFIG
)
return (
<>
{/* <!-- ====== Testimonial Section Start --> */}
<section
id='testimonials'
className='overflow-hidden bg-gray-1 py-20 dark:bg-dark-2 md:py-[120px]'>
<div className='container mx-auto'>
<div className='-mx-4 flex flex-wrap justify-center'>
<div className='w-full px-4'>
<div className='mx-auto mb-[60px] max-w-[485px] text-center'>
<span className='mb-2 block text-lg font-semibold text-primary'>
{siteConfig('STARTER_TESTIMONIALS_TITLE', null, CONFIG)}
</span>
<h2 className='mb-3 text-3xl font-bold leading-[1.2] text-dark dark:text-white sm:text-4xl md:text-[40px]'>
{siteConfig('STARTER_TESTIMONIALS_TEXT_1', null, CONFIG)}
</h2>
<p className='text-base text-body-color dark:text-dark-6'>
{siteConfig('STARTER_TESTIMONIALS_TEXT_2', null, CONFIG)}
</p>
</div>
</div>
</div>
</div>
<div className="-m-5">
<div className="swiper testimonial-carousel common-carousel p-5">
<div className="swiper-wrapper">
{/* 用户评价卡牌 */}
{CONFIG.STARTER_TESTIMONIALS_ITEMS.map((item, index) => {
return <div key={index} className="swiper-slide">
<div
className="rounded-xl bg-white px-4 py-[30px] shadow-testimonial dark:bg-dark sm:px-[30px]"
>
<div className="mb-[18px] flex items-center gap-[2px]">
{ratings.map((rating, index) => (
<img key={index} alt="star icon"// 为每个图片设置唯一的 key 属性
src={siteConfig('STARTER_TESTIMONIALS_STAR_ICON', null, CONFIG)}/>
))}
<div className='-m-5'>
<div className='swiper testimonial-carousel common-carousel p-5'>
<div className='swiper-wrapper'>
{/* 用户评价卡牌 */}
{STARTER_TESTIMONIALS_ITEMS.map((item, index) => {
return (
<div key={index} className='swiper-slide'>
<div className='rounded-xl bg-white px-4 py-[30px] shadow-testimonial dark:bg-dark sm:px-[30px]'>
<div className='mb-[18px] flex items-center gap-[2px]'>
{ratings.map((rating, index) => (
<img
key={index}
alt='star icon' // 为每个图片设置唯一的 key 属性
src={siteConfig(
'STARTER_TESTIMONIALS_STAR_ICON',
null,
CONFIG
)}
/>
))}
</div>
<p className="mb-6 text-base text-body-color dark:text-dark-6">
{item.STARTER_TESTIMONIALS_ITEM_TEXT}
<p className='mb-6 text-base text-body-color dark:text-dark-6'>
{item.STARTER_TESTIMONIALS_ITEM_TEXT}
</p>
<a href={item.STARTER_TESTIMONIALS_ITEM_URL} className="flex items-center gap-4">
<div className="h-[50px] w-[50px] overflow-hidden rounded-full">
<img
src={item.STARTER_TESTIMONIALS_ITEM_AVATAR}
alt="author"
className="h-[50px] w-[50px] overflow-hidden rounded-full object-cover"
/>
</div>
<a
href={item.STARTER_TESTIMONIALS_ITEM_URL}
className='flex items-center gap-4'>
<div className='h-[50px] w-[50px] overflow-hidden rounded-full'>
<img
src={item.STARTER_TESTIMONIALS_ITEM_AVATAR}
alt='author'
className='h-[50px] w-[50px] overflow-hidden rounded-full object-cover'
/>
</div>
<div>
<h3
className="text-sm font-semibold text-dark dark:text-white"
>
{item.STARTER_TESTIMONIALS_ITEM_NICKNAME}
</h3>
<p className="text-xs text-body-secondary">
{item.STARTER_TESTIMONIALS_ITEM_DESCRIPTION}
</p>
</div>
<div>
<h3 className='text-sm font-semibold text-dark dark:text-white'>
{item.STARTER_TESTIMONIALS_ITEM_NICKNAME}
</h3>
<p className='text-xs text-body-secondary'>
{item.STARTER_TESTIMONIALS_ITEM_DESCRIPTION}
</p>
</div>
</a>
</div>
</div>
)
})}
</div>
{/* 切换按钮 */}
<div className='mt-[60px] flex items-center justify-center gap-1'>
<div className='swiper-button-prev'>
<SVGLeftArrow />
</div>
<div className='swiper-button-next'>
<SVGRightArrow />
</div>
})}
</div>
{/* 切换按钮 */}
<div className="mt-[60px] flex items-center justify-center gap-1">
<div className="swiper-button-prev">
<SVGLeftArrow/>
</div>
<div className="swiper-button-next">
<SVGRightArrow/>
</div>
</div>
</div>
</div>
</div>
</section>
{/* <!-- ====== Testimonial Section End --> */}
</section>
{/* <!-- ====== Testimonial Section End --> */}
</>
)
}

View File

@@ -9,34 +9,35 @@
* 2. 内容大部分是在此文件中写死notion数据从props参数中传进来
* 3. 您可在此网站找到更多喜欢的组件 https://www.tailwind-kit.com/
*/
import { useRouter } from 'next/router'
import { isBrowser } from '@/lib/utils'
import { siteConfig } from '@/lib/config'
import CONFIG from './config'
import NotionPage from '@/components/NotionPage'
import Loading from '@/components/Loading'
import NotionPage from '@/components/NotionPage'
import { siteConfig } from '@/lib/config'
import { isBrowser } from '@/lib/utils'
import { useRouter } from 'next/router'
import { useEffect } from 'react'
import { Style } from './style'
import { NavBar } from './components/NavBar'
import { Hero } from './components/Hero'
import { Features } from './components/Features'
import { About } from './components/About'
import { Pricing } from './components/Pricing'
import { Testimonials } from './components/Testimonials'
import { FAQ } from './components/FAQ'
import { Team } from './components/Team'
import { Blog } from './components/Blog'
import { Contact } from './components/Contact'
import { Brand } from './components/Brand'
import { Footer } from './components/Footer'
import { BackToTopButton } from './components/BackToTopButton'
import { Blog } from './components/Blog'
import { Brand } from './components/Brand'
import { Contact } from './components/Contact'
import { FAQ } from './components/FAQ'
import { Features } from './components/Features'
import { Footer } from './components/Footer'
import { Hero } from './components/Hero'
import { NavBar } from './components/NavBar'
import { Pricing } from './components/Pricing'
import { Team } from './components/Team'
import { Testimonials } from './components/Testimonials'
import CONFIG from './config'
import { Style } from './style'
// import { MadeWithButton } from './components/MadeWithButton'
import { SVG404 } from './components/svg/SVG404'
import BLOG from '@/blog.config'
import { loadWowJS } from '@/lib/plugins/wow'
import Link from 'next/link'
import { Banner } from './components/Banner'
import { SignInForm } from './components/SignInForm'
import { SignUpForm } from './components/SignUpForm'
import Link from 'next/link'
import { loadWowJS } from '@/lib/plugins/wow'
import { SVG404 } from './components/svg/SVG404'
/**
* 布局框架
@@ -46,7 +47,7 @@ import { loadWowJS } from '@/lib/plugins/wow'
* @param {*} props
* @returns
*/
const LayoutBase = (props) => {
const LayoutBase = props => {
const { children } = props
// 加载wow动画
@@ -54,18 +55,22 @@ const LayoutBase = (props) => {
loadWowJS()
}, [])
return <div id='theme-starter' className={`${siteConfig('FONT_STYLE')} min-h-screen flex flex-col dark:bg-[#212b36] scroll-smooth`}>
<Style/>
<NavBar {...props}/>
return (
<div
id='theme-starter'
className={`${siteConfig('FONT_STYLE', BLOG.FONT_STYLE, props.NOTION_CONFIG)} min-h-screen flex flex-col dark:bg-[#212b36] scroll-smooth`}>
<Style />
<NavBar {...props} />
{children}
{children}
<Footer {...props}/>
<Footer {...props} />
{/* 悬浮按钮 */}
<BackToTopButton/>
{/* <MadeWithButton/> */}
</div>
{/* 悬浮按钮 */}
<BackToTopButton />
{/* <MadeWithButton/> */}
</div>
)
}
/**
@@ -73,34 +78,36 @@ const LayoutBase = (props) => {
* @param {*} props
* @returns
*/
const LayoutIndex = (props) => {
const LayoutIndex = props => {
const count = siteConfig('STARTER_BLOG_COUNT', 3, CONFIG)
const posts = props?.allNavPages
? props.allNavPages.slice(0, count)
: []
const posts = props?.allNavPages ? props.allNavPages.slice(0, count) : []
return (
<>
{/* 英雄区 */}
{siteConfig('STARTER_HERO_ENABLE', null, CONFIG) && <Hero/>}
{/* 产品特性 */}
{siteConfig('STARTER_FEATURE_ENABLE', null, CONFIG) && <Features/>}
{/* 关于 */}
{siteConfig('STARTER_ABOUT_ENABLE', null, CONFIG) && <About/>}
{/* 价格 */}
{siteConfig('STARTER_PRICING_ENABLE', null, CONFIG) && <Pricing/>}
{/* 评价展示 */}
{siteConfig('STARTER_TESTIMONIALS_ENABLE', null, CONFIG) && <Testimonials/>}
{/* 常见问题 */}
{siteConfig('STARTER_FAQ_ENABLE', null, CONFIG) && <FAQ/>}
{/* 团队介绍 */}
{siteConfig('团队成员区块', null, CONFIG) && <Team/>}
{/* 博文列表 */}
{siteConfig('STARTER_BLOG_ENABLE', null, CONFIG) && <Blog posts={posts}/>}
{/* 联系方式 */}
{siteConfig('STARTER_CONTACT_ENABLE', null, CONFIG) && <Contact/>}
{/* 合作伙伴 */}
{siteConfig('STARTER_BRANDS_ENABLE', null, CONFIG) && <Brand/>}
</>
<>
{/* 英雄区 */}
{siteConfig('STARTER_HERO_ENABLE', null, CONFIG) && <Hero />}
{/* 产品特性 */}
{siteConfig('STARTER_FEATURE_ENABLE', null, CONFIG) && <Features />}
{/* 关于 */}
{siteConfig('STARTER_ABOUT_ENABLE', null, CONFIG) && <About />}
{/* 价格 */}
{siteConfig('STARTER_PRICING_ENABLE', null, CONFIG) && <Pricing />}
{/* 评价展示 */}
{siteConfig('STARTER_TESTIMONIALS_ENABLE', null, CONFIG) && (
<Testimonials />
)}
{/* 常见问题 */}
{siteConfig('STARTER_FAQ_ENABLE', null, CONFIG) && <FAQ />}
{/* 团队介绍 */}
{siteConfig('STARTER_TEAM_ENABLE', null, CONFIG) && <Team />}
{/* 博文列表 */}
{siteConfig('STARTER_BLOG_ENABLE', null, CONFIG) && (
<Blog posts={posts} />
)}
{/* 联系方式 */}
{siteConfig('STARTER_CONTACT_ENABLE', null, CONFIG) && <Contact />}
{/* 合作伙伴 */}
{siteConfig('STARTER_BRANDS_ENABLE', null, CONFIG) && <Brand />}
</>
)
}
@@ -109,106 +116,126 @@ const LayoutIndex = (props) => {
* @param {*} props
* @returns
*/
const LayoutSlug = (props) => {
const LayoutSlug = props => {
const { post } = props
// 如果 是 /article/[slug] 的文章路径则視情況进行重定向到另一个域名
const router = useRouter()
if (!post && siteConfig('STARTER_POST_REDIRECT_ENABLE', null, CONFIG) && isBrowser && router.route === '/[prefix]/[slug]') {
const redirectUrl = siteConfig('STARTER_POST_REDIRECT_URL', null, CONFIG) + router.asPath.replace('?theme=landing', '')
if (
!post &&
siteConfig('STARTER_POST_REDIRECT_ENABLE', null, CONFIG) &&
isBrowser &&
router.route === '/[prefix]/[slug]'
) {
const redirectUrl =
siteConfig('STARTER_POST_REDIRECT_URL', null, CONFIG) +
router.asPath.replace('?theme=landing', '')
router.push(redirectUrl)
return <div id='theme-starter'><Loading /></div>
return (
<div id='theme-starter'>
<Loading />
</div>
)
}
return <>
<Banner title={post?.title} description={post?.summary}/>
<div className="container grow">
<div className="flex flex-wrap justify-center -mx-4">
<div className="w-full p-4">
<div id='container-inner' className='mx-auto'>
<NotionPage {...props} />
</div>
</div>
return (
<>
<Banner title={post?.title} description={post?.summary} />
<div className='container grow'>
<div className='flex flex-wrap justify-center -mx-4'>
<div className='w-full p-4'>
<div id='container-inner' className='mx-auto'>
<NotionPage {...props} />
</div>
</div>
</div>
</div>
</>
)
}
const LayoutSearch = (props) => <></>
const LayoutSearch = props => <></>
/**
* 文章归档
* @param {*} props
* @returns
*/
const LayoutArchive = (props) => <>
{/* 博文列表 */}
<Blog {...props}/>
</>
const LayoutArchive = props => (
<>
{/* 博文列表 */}
<Blog {...props} />
</>
)
/**
* 404页面
* @param {*} props
* @returns
*/
const Layout404 = (props) => {
return <>
{/* <!-- ====== 404 Section Start --> */}
<section className="bg-white py-20 dark:bg-dark-2 lg:py-[110px]">
<div className="container mx-auto">
<div className="flex flex-wrap items-center -mx-4">
<div className="w-full px-4 md:w-5/12 lg:w-6/12">
<div className="text-center">
<img
src="/images/starter/404.svg"
alt="image"
className="max-w-full mx-auto"
/>
</div>
const Layout404 = props => {
return (
<>
{/* <!-- ====== 404 Section Start --> */}
<section className='bg-white py-20 dark:bg-dark-2 lg:py-[110px]'>
<div className='container mx-auto'>
<div className='flex flex-wrap items-center -mx-4'>
<div className='w-full px-4 md:w-5/12 lg:w-6/12'>
<div className='text-center'>
<img
src='/images/starter/404.svg'
alt='image'
className='max-w-full mx-auto'
/>
</div>
<div className="w-full px-4 md:w-7/12 lg:w-6/12 xl:w-5/12">
<div>
<div className="mb-8">
<SVG404/>
</div>
<h3 className="mb-5 text-2xl font-semibold text-dark dark:text-white">
{siteConfig('STARTER_404_TITLE', null, CONFIG)}
</h3>
<p className="mb-8 text-base text-body-color dark:text-dark-6">
{siteConfig('STARTER_404_TEXT', null, CONFIG)}
</p>
<Link href='/'
className="py-3 text-base font-medium text-white transition rounded-md bg-dark px-7 hover:bg-primary"
>
{siteConfig('STARTER_404_BACK', null, CONFIG)}
</Link>
</div>
<div className='w-full px-4 md:w-7/12 lg:w-6/12 xl:w-5/12'>
<div>
<div className='mb-8'>
<SVG404 />
</div>
<h3 className='mb-5 text-2xl font-semibold text-dark dark:text-white'>
{siteConfig('STARTER_404_TITLE', null, CONFIG)}
</h3>
<p className='mb-8 text-base text-body-color dark:text-dark-6'>
{siteConfig('STARTER_404_TEXT', null, CONFIG)}
</p>
<Link
href='/'
className='py-3 text-base font-medium text-white transition rounded-md bg-dark px-7 hover:bg-primary'>
{siteConfig('STARTER_404_BACK', null, CONFIG)}
</Link>
</div>
</div>
</div>
</section>
{/* <!-- ====== 404 Section End --> */}
</div>
</section>
{/* <!-- ====== 404 Section End --> */}
</>
)
}
const LayoutCategoryIndex = (props) => <></>
const LayoutPostList = (props) => <></>
const LayoutTagIndex = (props) => <></>
const LayoutCategoryIndex = props => <></>
const LayoutPostList = props => <></>
const LayoutTagIndex = props => <></>
/**
* 登录页面
* @param {*} props
* @returns
*/
const LayoutSignIn = (props) => {
return <>
<div className='grow mt-20'>
<Banner title='登录' description='这里是演示页面NotionNext目前不提供会员登录功能'/>
<SignInForm/>
</div>
const LayoutSignIn = props => {
return (
<>
<div className='grow mt-20'>
<Banner
title='登录'
description='这里是演示页面NotionNext目前不提供会员登录功能'
/>
<SignInForm />
</div>
</>
)
}
/**
@@ -216,24 +243,29 @@ const LayoutSignIn = (props) => {
* @param {*} props
* @returns
*/
const LayoutSignUp = (props) => <>
<div className='grow mt-20'>
<Banner title='注册' description='这里是演示页面NotionNext目前不提供会员注册功能'/>
<SignUpForm/>
</div>
</>
const LayoutSignUp = props => (
<>
<div className='grow mt-20'>
<Banner
title='注册'
description='这里是演示页面NotionNext目前不提供会员注册功能'
/>
<SignUpForm />
</div>
</>
)
export {
CONFIG as THEME_CONFIG,
LayoutBase,
LayoutIndex,
LayoutSearch,
LayoutArchive,
LayoutSlug,
Layout404,
LayoutPostList,
LayoutArchive,
LayoutBase,
LayoutCategoryIndex,
LayoutTagIndex,
LayoutIndex,
LayoutPostList,
LayoutSearch,
LayoutSignIn,
LayoutSignUp
LayoutSignUp,
LayoutSlug,
LayoutTagIndex,
CONFIG as THEME_CONFIG
}