diff --git a/blog.config.js b/blog.config.js index 4677c555..b3c84657 100644 --- a/blog.config.js +++ b/blog.config.js @@ -91,6 +91,30 @@ const BLOG = { FONT_AWESOME: process.env.NEXT_PUBLIC_FONT_AWESOME_PATH || 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css', // font-awesome 字体图标地址; 可选 /css/all.min.css , https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/font-awesome/6.0.0/css/all.min.css // END ************网站字体***************** + + // 路径和组件映射,不同路径分别展示主题的什么组件 + LAYOUT_MAPPINGS: { + '-1': 'LayoutBase', + '/': 'LayoutIndex', + '/archive': 'LayoutArchive', + '/page/[page]': 'LayoutPostList', + '/category/[category]': 'LayoutPostList', + '/category/[category]/page/[page]': 'LayoutPostList', + '/tag/[tag]': 'LayoutPostList', + '/tag/[tag]/page/[page]': 'LayoutPostList', + '/search': 'LayoutSearch', + '/search/[keyword]': 'LayoutSearch', + '/search/[keyword]/page/[page]': 'LayoutSearch', + '/404': 'Layout404', + '/tag': 'LayoutTagIndex', + '/category': 'LayoutCategoryIndex', + '/[prefix]': 'LayoutSlug', + '/[prefix]/[slug]': 'LayoutSlug', + '/[prefix]/[slug]/[...suffix]': 'LayoutSlug', + '/signin': 'LayoutSignIn', + '/signup': 'LayoutSignUp' + }, + CAN_COPY: process.env.NEXT_PUBLIC_CAN_COPY || true, // 是否允许复制页面内容 默认允许,如果设置为false、则全栈禁止复制内容。 CUSTOM_RIGHT_CLICK_CONTEXT_MENU: process.env.NEXT_PUBLIC_CUSTOM_RIGHT_CLICK_CONTEXT_MENU || true, // 自定义右键菜单,覆盖系统菜单 CUSTOM_RIGHT_CLICK_CONTEXT_MENU_THEME_SWITCH: process.env.NEXT_PUBLIC_CUSTOM_RIGHT_CLICK_CONTEXT_MENU_THEME_SWITCH || true, @@ -154,6 +178,8 @@ const BLOG = { POSTS_PER_PAGE: 12, // post counts per page POSTS_SORT_BY: process.env.NEXT_PUBLIC_POST_SORT_BY || 'notion', // 排序方式 'date'按时间,'notion'由notion控制 + POST_WAITING_TIME_FOR_404: process.env.NEXT_PUBLIC_POST_WAITING_TIME_FOR_404 || '8', // 文章加载超时时间,单位秒;超时后跳转到404页面 + ALGOLIA_APP_ID: process.env.NEXT_PUBLIC_ALGOLIA_APP_ID || null, // 在这里查看 https://dashboard.algolia.com/account/api-keys/ ALGOLIA_ADMIN_APP_KEY: process.env.ALGOLIA_ADMIN_APP_KEY || null, // 管理后台的KEY,不要暴露在代码中,在这里查看 https://dashboard.algolia.com/account/api-keys/ ALGOLIA_SEARCH_ONLY_APP_KEY: process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_ONLY_APP_KEY || null, // 客户端搜索用的KEY diff --git a/components/CustomContextMenu.js b/components/CustomContextMenu.js index ff0dc313..4398c331 100644 --- a/components/CustomContextMenu.js +++ b/components/CustomContextMenu.js @@ -2,7 +2,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' import { useEffect, useState, useRef, useLayoutEffect } from 'react' import { useGlobal } from '@/lib/global' -import { saveDarkModeToCookies, THEMES } from '@/themes/theme' +import { saveDarkModeToLocalStorage, THEMES } from '@/themes/theme' import useWindowSize from '@/hooks/useWindowSize' import { siteConfig } from '@/lib/config' @@ -122,7 +122,7 @@ export default function CustomContextMenu(props) { function handleChangeDarkMode() { const newStatus = !isDarkMode - saveDarkModeToCookies(newStatus) + saveDarkModeToLocalStorage(newStatus) updateDarkMode(newStatus) const htmlElement = document.getElementsByTagName('html')[0] htmlElement.classList?.remove(newStatus ? 'light' : 'dark') diff --git a/components/ThemeSwitch.js b/components/ThemeSwitch.js index ba39b539..aeaeb3ab 100644 --- a/components/ThemeSwitch.js +++ b/components/ThemeSwitch.js @@ -38,7 +38,7 @@ const ThemeSwitch = () => { return (<> -
+
{/* 深色按钮 */}
diff --git a/lib/global.js b/lib/global.js index f1c84618..4eadfa19 100644 --- a/lib/global.js +++ b/lib/global.js @@ -1,7 +1,7 @@ -import { generateLocaleDict, initLocale, saveLangToCookies } from './lang' +import { generateLocaleDict, initLocale, saveLangToLocalStorage } from './lang' import { createContext, useContext, useEffect, useState } from 'react' import { useRouter } from 'next/router' -import { THEMES, initDarkMode, saveDarkModeToCookies } from '@/themes/theme' +import { THEMES, initDarkMode, saveDarkModeToLocalStorage } from '@/themes/theme' import { APPEARANCE, LANG, THEME } from 'blog.config' const GlobalContext = createContext() @@ -38,7 +38,7 @@ export function GlobalContextProvider(props) { // 切换深色模式 const toggleDarkMode = () => { const newStatus = !isDarkMode - saveDarkModeToCookies(newStatus) + saveDarkModeToLocalStorage(newStatus) updateDarkMode(newStatus) const htmlElement = document.getElementsByTagName('html')[0] htmlElement.classList?.remove(newStatus ? 'light' : 'dark') @@ -50,7 +50,7 @@ export function GlobalContextProvider(props) { */ function changeLang(lang) { if (lang) { - saveLangToCookies(lang) + saveLangToLocalStorage(lang) updateLang(lang) updateLocale(generateLocaleDict(lang)) } diff --git a/lib/lang.js b/lib/lang.js index 5f81af18..eb6a8890 100644 --- a/lib/lang.js +++ b/lib/lang.js @@ -5,7 +5,6 @@ import zhTW from './lang/zh-TW' import frFR from './lang/fr-FR' import trTR from './lang/tr-TR' import jaJP from './lang/ja-JP' -import cookie from 'react-cookies' import { getQueryVariable, isBrowser, mergeDeep } from './utils' /** @@ -65,14 +64,14 @@ export function generateLocaleDict(langString) { */ export function initLocale(lang, locale, changeLang, changeLocale) { if (isBrowser) { - const queryLang = getQueryVariable('lang') || loadLangFromCookies() + const queryLang = getQueryVariable('lang') || loadLangFromLocalStorage() let currentLang = lang if (queryLang && queryLang !== 'undefined' && queryLang !== lang) { currentLang = queryLang } changeLang(currentLang) - saveLangToCookies(currentLang) + saveLangToLocalStorage(currentLang) const targetLocale = generateLocaleDict(currentLang) if (JSON.stringify(locale) !== JSON.stringify(currentLang)) { @@ -84,14 +83,14 @@ export function initLocale(lang, locale, changeLang, changeLocale) { * 读取语言 * @returns {*} */ -export const loadLangFromCookies = () => { - return cookie.load('lang') +export const loadLangFromLocalStorage = () => { + return localStorage.getItem('lang') } /** * 保存语言 * @param newTheme */ -export const saveLangToCookies = (lang) => { - cookie.save('lang', lang, { path: '/' }) +export const saveLangToLocalStorage = (lang) => { + localStorage.setItem('lang', lang) } diff --git a/package.json b/package.json index 82efcf06..333f945f 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "preact": "^10.5.15", "prism-themes": "1.9.0", "react": "^18.2.0", - "react-cookies": "^0.1.1", "react-dom": "^18.2.0", "react-facebook": "^8.1.4", "react-notion-x": "6.16.0", @@ -75,4 +74,4 @@ "url": "https://github.com/tangly/NotionNext/issues", "email": "tlyong1992@hotmail.com" } -} \ No newline at end of file +} diff --git a/pages/[prefix]/index.js b/pages/[prefix]/index.js index 2a779790..91f1ec76 100644 --- a/pages/[prefix]/index.js +++ b/pages/[prefix]/index.js @@ -8,7 +8,7 @@ import { getNotion } from '@/lib/notion/getNotion' import { getPageTableOfContents } from '@/lib/notion/getPageTableOfContents' import { getLayoutByTheme } from '@/themes/theme' import md5 from 'js-md5' -import { checkContainHttp, isBrowser } from '@/lib/utils' +import { checkContainHttp } from '@/lib/utils' import { uploadDataToAlgolia } from '@/lib/algolia' import { siteConfig } from '@/lib/config' @@ -20,7 +20,6 @@ import { siteConfig } from '@/lib/config' */ const Slug = props => { const { post } = props - const router = useRouter() // 文章锁🔐 const [lock, setLock] = useState(post?.password && post?.password !== '') @@ -40,20 +39,6 @@ const Slug = props => { // 文章加载 useEffect(() => { - // 404 - if (!post) { - setTimeout(() => { - if (isBrowser) { - const article = document.getElementById('notion-article') - if (!article) { - router.push('/404').then(() => { - console.warn('找不到页面', router.asPath) - }) - } - } - }, 8 * 1000) // 404时长 8秒 - } - // 文章加密 if (post?.password && post?.password !== '') { setLock(true) diff --git a/pages/signin.js b/pages/signin.js new file mode 100644 index 00000000..9abfb1f8 --- /dev/null +++ b/pages/signin.js @@ -0,0 +1,29 @@ +import BLOG from '@/blog.config' +import { getGlobalData } from '@/lib/notion/getNotionData' +import { useRouter } from 'next/router' +import { getLayoutByTheme } from '@/themes/theme' +import { siteConfig } from '@/lib/config' + +/** + * 登录 + * @param {*} props + * @returns + */ +const SignIn = props => { + // 根据页面路径加载不同Layout文件 + const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() }) + return +} + +export async function getStaticProps() { + const from = 'SignIn' + const props = await getGlobalData({ from }) + + delete props.allPages + return { + props, + revalidate: parseInt(BLOG.NEXT_REVALIDATE_SECOND) + } +} + +export default SignIn diff --git a/pages/signup.js b/pages/signup.js new file mode 100644 index 00000000..d570d46b --- /dev/null +++ b/pages/signup.js @@ -0,0 +1,29 @@ +import BLOG from '@/blog.config' +import { getGlobalData } from '@/lib/notion/getNotionData' +import { useRouter } from 'next/router' +import { getLayoutByTheme } from '@/themes/theme' +import { siteConfig } from '@/lib/config' + +/** + * 注册 + * @param {*} props + * @returns + */ +const SignUp = props => { + // 根据页面路径加载不同Layout文件 + const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() }) + return +} + +export async function getStaticProps() { + const from = 'SignIn' + const props = await getGlobalData({ from }) + + delete props.allPages + return { + props, + revalidate: parseInt(BLOG.NEXT_REVALIDATE_SECOND) + } +} + +export default SignUp diff --git a/public/images/starter/404.svg b/public/images/starter/404.svg new file mode 100644 index 00000000..79e94624 --- /dev/null +++ b/public/images/starter/404.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/starter/about/about-image-01.jpg b/public/images/starter/about/about-image-01.jpg new file mode 100644 index 00000000..9cdc28a4 Binary files /dev/null and b/public/images/starter/about/about-image-01.jpg differ diff --git a/public/images/starter/about/about-image-02.jpg b/public/images/starter/about/about-image-02.jpg new file mode 100644 index 00000000..f8d10918 Binary files /dev/null and b/public/images/starter/about/about-image-02.jpg differ diff --git a/public/images/starter/blog/article-author-01.png b/public/images/starter/blog/article-author-01.png new file mode 100644 index 00000000..6d376483 Binary files /dev/null and b/public/images/starter/blog/article-author-01.png differ diff --git a/public/images/starter/blog/article-author-02.png b/public/images/starter/blog/article-author-02.png new file mode 100644 index 00000000..e0928f51 Binary files /dev/null and b/public/images/starter/blog/article-author-02.png differ diff --git a/public/images/starter/blog/article-author-03.png b/public/images/starter/blog/article-author-03.png new file mode 100644 index 00000000..1209a41a Binary files /dev/null and b/public/images/starter/blog/article-author-03.png differ diff --git a/public/images/starter/blog/article-author-04.png b/public/images/starter/blog/article-author-04.png new file mode 100644 index 00000000..b60009a5 Binary files /dev/null and b/public/images/starter/blog/article-author-04.png differ diff --git a/public/images/starter/blog/author-01.png b/public/images/starter/blog/author-01.png new file mode 100644 index 00000000..cfad7368 Binary files /dev/null and b/public/images/starter/blog/author-01.png differ diff --git a/public/images/starter/blog/bannder-ad.png b/public/images/starter/blog/bannder-ad.png new file mode 100644 index 00000000..72c54efe Binary files /dev/null and b/public/images/starter/blog/bannder-ad.png differ diff --git a/public/images/starter/blog/blog-01.jpg b/public/images/starter/blog/blog-01.jpg new file mode 100644 index 00000000..9c117386 Binary files /dev/null and b/public/images/starter/blog/blog-01.jpg differ diff --git a/public/images/starter/blog/blog-02.jpg b/public/images/starter/blog/blog-02.jpg new file mode 100644 index 00000000..8c7c5354 Binary files /dev/null and b/public/images/starter/blog/blog-02.jpg differ diff --git a/public/images/starter/blog/blog-03.jpg b/public/images/starter/blog/blog-03.jpg new file mode 100644 index 00000000..e735ad0b Binary files /dev/null and b/public/images/starter/blog/blog-03.jpg differ diff --git a/public/images/starter/blog/blog-details-01.jpg b/public/images/starter/blog/blog-details-01.jpg new file mode 100644 index 00000000..18405648 Binary files /dev/null and b/public/images/starter/blog/blog-details-01.jpg differ diff --git a/public/images/starter/blog/blog-footer-01.jpg b/public/images/starter/blog/blog-footer-01.jpg new file mode 100644 index 00000000..0b98efdf Binary files /dev/null and b/public/images/starter/blog/blog-footer-01.jpg differ diff --git a/public/images/starter/blog/blog-footer-02.jpg b/public/images/starter/blog/blog-footer-02.jpg new file mode 100644 index 00000000..63c95dbc Binary files /dev/null and b/public/images/starter/blog/blog-footer-02.jpg differ diff --git a/public/images/starter/blog/dotted-shape.svg b/public/images/starter/blog/dotted-shape.svg new file mode 100644 index 00000000..ebb755ab --- /dev/null +++ b/public/images/starter/blog/dotted-shape.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/starter/blog/quote-bg.svg b/public/images/starter/blog/quote-bg.svg new file mode 100644 index 00000000..0bfe5c30 --- /dev/null +++ b/public/images/starter/blog/quote-bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/starter/brands/ayroui-white.svg b/public/images/starter/brands/ayroui-white.svg new file mode 100644 index 00000000..cff071ae --- /dev/null +++ b/public/images/starter/brands/ayroui-white.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/starter/brands/ayroui.svg b/public/images/starter/brands/ayroui.svg new file mode 100644 index 00000000..07c9c65f --- /dev/null +++ b/public/images/starter/brands/ayroui.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/starter/brands/graygrids-white.svg b/public/images/starter/brands/graygrids-white.svg new file mode 100644 index 00000000..c67f2a78 --- /dev/null +++ b/public/images/starter/brands/graygrids-white.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/starter/brands/graygrids.svg b/public/images/starter/brands/graygrids.svg new file mode 100644 index 00000000..3e585981 --- /dev/null +++ b/public/images/starter/brands/graygrids.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/starter/brands/lineicons-white.svg b/public/images/starter/brands/lineicons-white.svg new file mode 100644 index 00000000..f87604f3 --- /dev/null +++ b/public/images/starter/brands/lineicons-white.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/starter/brands/lineicons.svg b/public/images/starter/brands/lineicons.svg new file mode 100644 index 00000000..32c4f6ed --- /dev/null +++ b/public/images/starter/brands/lineicons.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/starter/brands/tailgrids-white.svg b/public/images/starter/brands/tailgrids-white.svg new file mode 100644 index 00000000..00a3b911 --- /dev/null +++ b/public/images/starter/brands/tailgrids-white.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/starter/brands/tailgrids.svg b/public/images/starter/brands/tailgrids.svg new file mode 100644 index 00000000..72f1405b --- /dev/null +++ b/public/images/starter/brands/tailgrids.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/starter/brands/uideck-white.svg b/public/images/starter/brands/uideck-white.svg new file mode 100644 index 00000000..68c5bb3e --- /dev/null +++ b/public/images/starter/brands/uideck-white.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/images/starter/brands/uideck.svg b/public/images/starter/brands/uideck.svg new file mode 100644 index 00000000..712612fe --- /dev/null +++ b/public/images/starter/brands/uideck.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/images/starter/favicon.png b/public/images/starter/favicon.png new file mode 100644 index 00000000..565e7693 Binary files /dev/null and b/public/images/starter/favicon.png differ diff --git a/public/images/starter/footer/shape-1.svg b/public/images/starter/footer/shape-1.svg new file mode 100644 index 00000000..9077a676 --- /dev/null +++ b/public/images/starter/footer/shape-1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/starter/footer/shape-3.svg b/public/images/starter/footer/shape-3.svg new file mode 100644 index 00000000..caa49058 --- /dev/null +++ b/public/images/starter/footer/shape-3.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/starter/hero/brand.svg b/public/images/starter/hero/brand.svg new file mode 100644 index 00000000..badad418 --- /dev/null +++ b/public/images/starter/hero/brand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/starter/hero/hero-image.webp b/public/images/starter/hero/hero-image.webp new file mode 100644 index 00000000..9729bff6 Binary files /dev/null and b/public/images/starter/hero/hero-image.webp differ diff --git a/public/images/starter/logo/favicon.svg b/public/images/starter/logo/favicon.svg new file mode 100644 index 00000000..cdf1d91f --- /dev/null +++ b/public/images/starter/logo/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/starter/logo/logo-white.svg b/public/images/starter/logo/logo-white.svg new file mode 100644 index 00000000..60c45844 --- /dev/null +++ b/public/images/starter/logo/logo-white.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/starter/logo/logo.svg b/public/images/starter/logo/logo.svg new file mode 100644 index 00000000..74f74640 --- /dev/null +++ b/public/images/starter/logo/logo.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/starter/team/dotted-shape.svg b/public/images/starter/team/dotted-shape.svg new file mode 100644 index 00000000..4a60b6d2 --- /dev/null +++ b/public/images/starter/team/dotted-shape.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/starter/team/shape-2.svg b/public/images/starter/team/shape-2.svg new file mode 100644 index 00000000..a49060dd --- /dev/null +++ b/public/images/starter/team/shape-2.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/starter/team/team-01.png b/public/images/starter/team/team-01.png new file mode 100644 index 00000000..f12ff515 Binary files /dev/null and b/public/images/starter/team/team-01.png differ diff --git a/public/images/starter/team/team-02.png b/public/images/starter/team/team-02.png new file mode 100644 index 00000000..873da2b7 Binary files /dev/null and b/public/images/starter/team/team-02.png differ diff --git a/public/images/starter/team/team-03.png b/public/images/starter/team/team-03.png new file mode 100644 index 00000000..cc0026bf Binary files /dev/null and b/public/images/starter/team/team-03.png differ diff --git a/public/images/starter/team/team-04.png b/public/images/starter/team/team-04.png new file mode 100644 index 00000000..2eba5d6f Binary files /dev/null and b/public/images/starter/team/team-04.png differ diff --git a/public/images/starter/testimonials/author-01.jpg b/public/images/starter/testimonials/author-01.jpg new file mode 100644 index 00000000..5d8a66d3 Binary files /dev/null and b/public/images/starter/testimonials/author-01.jpg differ diff --git a/public/images/starter/testimonials/author-02.jpg b/public/images/starter/testimonials/author-02.jpg new file mode 100644 index 00000000..d240eb1c Binary files /dev/null and b/public/images/starter/testimonials/author-02.jpg differ diff --git a/public/images/starter/testimonials/author-03.jpg b/public/images/starter/testimonials/author-03.jpg new file mode 100644 index 00000000..e78dfafb Binary files /dev/null and b/public/images/starter/testimonials/author-03.jpg differ diff --git a/public/images/starter/testimonials/icon-star.svg b/public/images/starter/testimonials/icon-star.svg new file mode 100644 index 00000000..666ced1a --- /dev/null +++ b/public/images/starter/testimonials/icon-star.svg @@ -0,0 +1,3 @@ + + + diff --git a/styles/animate.css b/styles/animate.css index 43f253c1..c83e40e1 100644 --- a/styles/animate.css +++ b/styles/animate.css @@ -1,363 +1,13 @@ -@charset "UTF-8";/*! - * animate.css - https://animate.style/ - * Version - 4.1.1 +@charset "UTF-8"; + +/*! + * animate.css -https://daneden.github.io/animate.css/ + * Version - 3.7.2 * Licensed under the MIT license - http://opensource.org/licenses/MIT - * https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css - * 这里做了精减,后续不再使用animate.css,因为占用体积太大,不如手写动画 - * Copyright (c) 2020 Animate.css + * + * Copyright (c) 2019 Daniel Eden */ -:root { - --animate-duration: 1s; - --animate-delay: 1s; - --animate-repeat: 1; -} -.animate__animated { - -webkit-animation-duration: 1s; - animation-duration: 1s; - -webkit-animation-duration: var(--animate-duration); - animation-duration: var(--animate-duration); - -webkit-animation-fill-mode: both; - animation-fill-mode: both; -} - -.animate__animated.animate__faster { - -webkit-animation-duration: calc(1s / 2); - animation-duration: calc(1s / 2); - -webkit-animation-duration: calc(var(--animate-duration) / 2); - animation-duration: calc(var(--animate-duration) / 2); -} -.animate__animated.animate__fast { - -webkit-animation-duration: calc(1s * 0.8); - animation-duration: calc(1s * 0.8); - -webkit-animation-duration: calc(var(--animate-duration) * 0.8); - animation-duration: calc(var(--animate-duration) * 0.8); -} - - -@media print, (prefers-reduced-motion: reduce) { - .animate__animated { - -webkit-animation-duration: 1ms !important; - animation-duration: 1ms !important; - -webkit-transition-duration: 1ms !important; - transition-duration: 1ms !important; - -webkit-animation-iteration-count: 1 !important; - animation-iteration-count: 1 !important; - } - - .animate__animated[class*='Out'] { - opacity: 0; - } -} - - -@-webkit-keyframes shakeX { - from, - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - 10%, - 30%, - 50%, - 70%, - 90% { - -webkit-transform: translate3d(-10px, 0, 0); - transform: translate3d(-10px, 0, 0); - } - - 20%, - 40%, - 60%, - 80% { - -webkit-transform: translate3d(10px, 0, 0); - transform: translate3d(10px, 0, 0); - } -} -@keyframes shakeX { - from, - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - 10%, - 30%, - 50%, - 70%, - 90% { - -webkit-transform: translate3d(-10px, 0, 0); - transform: translate3d(-10px, 0, 0); - } - - 20%, - 40%, - 60%, - 80% { - -webkit-transform: translate3d(10px, 0, 0); - transform: translate3d(10px, 0, 0); - } -} -.animate__shakeX { - -webkit-animation-name: shakeX; - animation-name: shakeX; -} -@-webkit-keyframes shakeY { - from, - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - 10%, - 30%, - 50%, - 70%, - 90% { - -webkit-transform: translate3d(0, -10px, 0); - transform: translate3d(0, -10px, 0); - } - - 20%, - 40%, - 60%, - 80% { - -webkit-transform: translate3d(0, 10px, 0); - transform: translate3d(0, 10px, 0); - } -} -@keyframes shakeY { - from, - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - 10%, - 30%, - 50%, - 70%, - 90% { - -webkit-transform: translate3d(0, -10px, 0); - transform: translate3d(0, -10px, 0); - } - - 20%, - 40%, - 60%, - 80% { - -webkit-transform: translate3d(0, 10px, 0); - transform: translate3d(0, 10px, 0); - } -} -.animate__shakeY { - -webkit-animation-name: shakeY; - animation-name: shakeY; -} -@-webkit-keyframes headShake { - 0% { - -webkit-transform: translateX(0); - transform: translateX(0); - } - - 6.5% { - -webkit-transform: translateX(-6px) rotateY(-9deg); - transform: translateX(-6px) rotateY(-9deg); - } - - 18.5% { - -webkit-transform: translateX(5px) rotateY(7deg); - transform: translateX(5px) rotateY(7deg); - } - - 31.5% { - -webkit-transform: translateX(-3px) rotateY(-5deg); - transform: translateX(-3px) rotateY(-5deg); - } - - 43.5% { - -webkit-transform: translateX(2px) rotateY(3deg); - transform: translateX(2px) rotateY(3deg); - } - - 50% { - -webkit-transform: translateX(0); - transform: translateX(0); - } -} -@keyframes headShake { - 0% { - -webkit-transform: translateX(0); - transform: translateX(0); - } - - 6.5% { - -webkit-transform: translateX(-6px) rotateY(-9deg); - transform: translateX(-6px) rotateY(-9deg); - } - - 18.5% { - -webkit-transform: translateX(5px) rotateY(7deg); - transform: translateX(5px) rotateY(7deg); - } - - 31.5% { - -webkit-transform: translateX(-3px) rotateY(-5deg); - transform: translateX(-3px) rotateY(-5deg); - } - - 43.5% { - -webkit-transform: translateX(2px) rotateY(3deg); - transform: translateX(2px) rotateY(3deg); - } - - 50% { - -webkit-transform: translateX(0); - transform: translateX(0); - } -} -.animate__headShake { - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - -webkit-animation-name: headShake; - animation-name: headShake; -} - - -@keyframes jello { - from, - 11.1%, - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - 22.2% { - -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); - transform: skewX(-12.5deg) skewY(-12.5deg); - } - - 33.3% { - -webkit-transform: skewX(6.25deg) skewY(6.25deg); - transform: skewX(6.25deg) skewY(6.25deg); - } - - 44.4% { - -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); - transform: skewX(-3.125deg) skewY(-3.125deg); - } - - 55.5% { - -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); - transform: skewX(1.5625deg) skewY(1.5625deg); - } - - 66.6% { - -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); - transform: skewX(-0.78125deg) skewY(-0.78125deg); - } - - 77.7% { - -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); - transform: skewX(0.390625deg) skewY(0.390625deg); - } - - 88.8% { - -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); - transform: skewX(-0.1953125deg) skewY(-0.1953125deg); - } -} -.animate__jello { - -webkit-animation-name: jello; - animation-name: jello; - -webkit-transform-origin: center; - transform-origin: center; -} - - - -@-webkit-keyframes bounceInRight { - from, - 60%, - 75%, - 90%, - to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); - animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); - } - - from { - opacity: 0; - -webkit-transform: translate3d(3000px, 0, 0) scaleX(3); - transform: translate3d(3000px, 0, 0) scaleX(3); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(-25px, 0, 0) scaleX(1); - transform: translate3d(-25px, 0, 0) scaleX(1); - } - - 75% { - -webkit-transform: translate3d(10px, 0, 0) scaleX(0.98); - transform: translate3d(10px, 0, 0) scaleX(0.98); - } - - 90% { - -webkit-transform: translate3d(-5px, 0, 0) scaleX(0.995); - transform: translate3d(-5px, 0, 0) scaleX(0.995); - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} -@keyframes bounceInRight { - from, - 60%, - 75%, - 90%, - to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); - animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); - } - - from { - opacity: 0; - -webkit-transform: translate3d(3000px, 0, 0) scaleX(3); - transform: translate3d(3000px, 0, 0) scaleX(3); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(-25px, 0, 0) scaleX(1); - transform: translate3d(-25px, 0, 0) scaleX(1); - } - - 75% { - -webkit-transform: translate3d(10px, 0, 0) scaleX(0.98); - transform: translate3d(10px, 0, 0) scaleX(0.98); - } - - 90% { - -webkit-transform: translate3d(-5px, 0, 0) scaleX(0.995); - transform: translate3d(-5px, 0, 0) scaleX(0.995); - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} -.animate__bounceInRight { - -webkit-animation-name: bounceInRight; - animation-name: bounceInRight; -} - - -/* Fading entrances */ @-webkit-keyframes fadeIn { from { opacity: 0; @@ -367,6 +17,7 @@ opacity: 1; } } + @keyframes fadeIn { from { opacity: 0; @@ -376,128 +27,208 @@ opacity: 1; } } -.animate__fadeIn { + +.fadeIn { -webkit-animation-name: fadeIn; animation-name: fadeIn; } - -/* Fading exits */ -/* 删除 */ - -/* Flippers */ -/* 删除 */ - -/* Lightspeed */ -/* 删除 */ - -/* Rotating exits */ -/* 删除 */ - -/* Zooming entrances */ -/* 删除 */ - -/* Sliding entrances */ - -@-webkit-keyframes slideInLeft { +@-webkit-keyframes fadeInDown { from { - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - visibility: visible; + opacity: 0; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); } to { + opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } -@keyframes slideInLeft { + +@keyframes fadeInDown { from { - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - visibility: visible; + opacity: 0; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); } to { + opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } -.animate__slideInLeft { - -webkit-animation-name: slideInLeft; - animation-name: slideInLeft; + +.fadeInDown { + -webkit-animation-name: fadeInDown; + animation-name: fadeInDown; } -@-webkit-keyframes slideInRight { + +@-webkit-keyframes fadeInLeft { from { - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - visibility: visible; + opacity: 0; + -webkit-transform: translate3d(-20px, 0, 0); + transform: translate3d(-20px, 0, 0); } to { + opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } -@keyframes slideInRight { + +@keyframes fadeInLeft { from { - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - visibility: visible; + opacity: 0; + -webkit-transform: translate3d(-20px, 0, 0); + transform: translate3d(-20px, 0, 0); } to { + opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } -.animate__slideInRight { - -webkit-animation-name: slideInRight; - animation-name: slideInRight; + +.fadeInLeft { + -webkit-animation-name: fadeInLeft; + animation-name: fadeInLeft; } - -@keyframes slideOutLeft { +@-webkit-keyframes fadeInRight { from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); + opacity: 0; + -webkit-transform: translate3d(20px, 0, 0); + transform: translate3d(20px, 0, 0); } to { - visibility: hidden; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } -} -.animate__slideOutLeft { - -webkit-animation-name: slideOutLeft; - animation-name: slideOutLeft; -} -@-webkit-keyframes slideOutRight { - from { + opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } +} + +@keyframes fadeInRight { + from { + opacity: 0; + -webkit-transform: translate3d(20px, 0, 0); + transform: translate3d(20px, 0, 0); + } to { - visibility: hidden; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } -} -@keyframes slideOutRight { - from { + opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } +} + +.fadeInRight { + -webkit-animation-name: fadeInRight; + animation-name: fadeInRight; +} + +@-webkit-keyframes fadeInUp { + from { + opacity: 0; + -webkit-transform: translate3d(0, 20px, 0); + transform: translate3d(0, 20px, 0); + } to { - visibility: hidden; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); } } -.animate__slideOutRight { - -webkit-animation-name: slideOutRight; - animation-name: slideOutRight; + +@keyframes fadeInUp { + from { + opacity: 0; + -webkit-transform: translate3d(0, 20px, 0); + transform: translate3d(0, 20px, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.fadeInUp { + -webkit-animation-name: fadeInUp; + animation-name: fadeInUp; +} + +.animated { + -webkit-animation-duration: 1s; + animation-duration: 1s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; +} + +.animated.infinite { + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; +} + +.animated.delay-1s { + -webkit-animation-delay: 1s; + animation-delay: 1s; +} + +.animated.delay-2s { + -webkit-animation-delay: 2s; + animation-delay: 2s; +} + +.animated.delay-3s { + -webkit-animation-delay: 3s; + animation-delay: 3s; +} + +.animated.delay-4s { + -webkit-animation-delay: 4s; + animation-delay: 4s; +} + +.animated.delay-5s { + -webkit-animation-delay: 5s; + animation-delay: 5s; +} + +.animated.fast { + -webkit-animation-duration: 800ms; + animation-duration: 800ms; +} + +.animated.faster { + -webkit-animation-duration: 500ms; + animation-duration: 500ms; +} + +.animated.slow { + -webkit-animation-duration: 2s; + animation-duration: 2s; +} + +.animated.slower { + -webkit-animation-duration: 3s; + animation-duration: 3s; +} + +@media (print), (prefers-reduced-motion: reduce) { + .animated { + -webkit-animation-duration: 1ms !important; + animation-duration: 1ms !important; + -webkit-transition-duration: 1ms !important; + transition-duration: 1ms !important; + -webkit-animation-iteration-count: 1 !important; + animation-iteration-count: 1 !important; + } } diff --git a/tailwind.config.js b/tailwind.config.js index 76f8e710..f5780609 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -6,6 +6,26 @@ module.exports = { darkMode: BLOG.APPEARANCE === 'class' ? 'media' : 'class', // or 'media' or 'class' theme: { fontFamily: fontFamilies, + screens: { + sm: '540px', + // => @media (min-width: 576px) { ... } + + md: '720px', + // => @media (min-width: 768px) { ... } + + lg: '960px', + // => @media (min-width: 992px) { ... } + + xl: '1140px', + // => @media (min-width: 1200px) { ... } + + '2xl': '1320px' + // => @media (min-width: 1400px) { ... } + }, + container: { + center: true, + padding: '16px' + }, extend: { colors: { day: { @@ -18,11 +38,43 @@ module.exports = { 'background-gray': '#f5f5f5', 'black-gray': '#101414', 'light-gray': '#e5e5e5' - } + }, + black: '#212b36', + 'dark-700': '#090e34b3', + dark: { + DEFAULT: '#111928', + 2: '#1F2A37', + 3: '#374151', + 4: '#4B5563', + 5: '#6B7280', + 6: '#9CA3AF', + 7: '#D1D5DB', + 8: '#E5E7EB' + }, + primary: '#3758F9', + 'blue-dark': '#1B44C8', + secondary: '#13C296', + 'body-color': '#637381', + 'body-secondary': '#8899A8', + warning: '#FBBF24', + stroke: '#DFE4EA', + 'gray-1': '#F9FAFB', + 'gray-2': '#F3F4F6', + 'gray-7': '#CED4DA' }, maxWidth: { side: '14rem', '9/10': '90%' + }, + boxShadow: { + input: '0px 7px 20px rgba(0, 0, 0, 0.03)', + form: '0px 1px 55px -11px rgba(0, 0, 0, 0.01)', + pricing: '0px 0px 40px 0px rgba(0, 0, 0, 0.08)', + 'switch-1': '0px 0px 5px rgba(0, 0, 0, 0.15)', + testimonial: '0px 10px 20px 0px rgba(92, 115, 160, 0.07)', + 'testimonial-btn': '0px 8px 15px 0px rgba(72, 72, 138, 0.08)', + 1: '0px 1px 3px 0px rgba(166, 175, 195, 0.40)', + 2: '0px 5px 12px 0px rgba(0, 0, 0, 0.10)' } } }, diff --git a/themes/example/index.js b/themes/example/index.js index 54d88281..2bb24981 100644 --- a/themes/example/index.js +++ b/themes/example/index.js @@ -146,6 +146,22 @@ const LayoutPostList = props => { */ const LayoutSlug = props => { const { post, lock, validPassword } = props + const router = useRouter() + useEffect(() => { + // 404 + if (!post) { + setTimeout(() => { + if (isBrowser) { + const article = document.getElementById('notion-article') + if (!article) { + router.push('/404').then(() => { + console.warn('找不到页面', router.asPath) + }) + } + } + }, siteConfig('POST_WAITING_TIME_FOR_404') * 1000) + } + }, [post]) return ( <> {lock @@ -249,11 +265,11 @@ export { CONFIG as THEME_CONFIG, LayoutBase, LayoutIndex, - LayoutPostList, LayoutSearch, LayoutArchive, LayoutSlug, Layout404, + LayoutPostList, LayoutCategoryIndex, LayoutTagIndex } diff --git a/themes/fukasawa/index.js b/themes/fukasawa/index.js index 2713e4e6..70b367fb 100644 --- a/themes/fukasawa/index.js +++ b/themes/fukasawa/index.js @@ -118,7 +118,23 @@ const LayoutPostList = (props) => { * @returns */ const LayoutSlug = (props) => { - const { lock, validPassword } = props + const { post, lock, validPassword } = props + const router = useRouter() + useEffect(() => { + // 404 + if (!post) { + setTimeout(() => { + if (isBrowser) { + const article = document.getElementById('notion-article') + if (!article) { + router.push('/404').then(() => { + console.warn('找不到页面', router.asPath) + }) + } + } + }, siteConfig('POST_WAITING_TIME_FOR_404') * 1000) + } + }, [post]) return ( <> {lock ? : } diff --git a/themes/gitbook/index.js b/themes/gitbook/index.js index 075c3dc6..73ea61d8 100644 --- a/themes/gitbook/index.js +++ b/themes/gitbook/index.js @@ -215,7 +215,22 @@ const LayoutPostList = (props) => { */ const LayoutSlug = (props) => { const { post, prev, next, lock, validPassword } = props - + const router = useRouter() + useEffect(() => { + // 404 + if (!post) { + setTimeout(() => { + if (isBrowser) { + const article = document.getElementById('notion-article') + if (!article) { + router.push('/404').then(() => { + console.warn('找不到页面', router.asPath) + }) + } + } + }, siteConfig('POST_WAITING_TIME_FOR_404') * 1000) + } + }, [post]) return ( <> {/* 文章锁 */} @@ -354,7 +369,7 @@ export { LayoutArchive, LayoutSlug, Layout404, - LayoutCategoryIndex, LayoutPostList, + LayoutCategoryIndex, LayoutTagIndex } diff --git a/themes/heo/components/DarkModeButton.js b/themes/heo/components/DarkModeButton.js index 9b56e6da..bef62962 100644 --- a/themes/heo/components/DarkModeButton.js +++ b/themes/heo/components/DarkModeButton.js @@ -1,5 +1,5 @@ import { useGlobal } from '@/lib/global' -import { saveDarkModeToCookies } from '@/themes/theme' +import { saveDarkModeToLocalStorage } from '@/themes/theme' import { Moon, Sun } from '@/components/HeroIcons' import { useImperativeHandle } from 'react' @@ -24,7 +24,7 @@ const DarkModeButton = (props) => { // 用户手动设置主题 const handleChangeDarkMode = () => { const newStatus = !isDarkMode - saveDarkModeToCookies(newStatus) + saveDarkModeToLocalStorage(newStatus) updateDarkMode(newStatus) const htmlElement = document.getElementsByTagName('html')[0] htmlElement.classList?.remove(newStatus ? 'light' : 'dark') diff --git a/themes/heo/components/FloatDarkModeButton.js b/themes/heo/components/FloatDarkModeButton.js index ec480ee4..0918093b 100644 --- a/themes/heo/components/FloatDarkModeButton.js +++ b/themes/heo/components/FloatDarkModeButton.js @@ -1,5 +1,5 @@ import { useGlobal } from '@/lib/global' -import { saveDarkModeToCookies } from '@/themes/theme' +import { saveDarkModeToLocalStorage } from '@/themes/theme' import CONFIG from '../config' import { siteConfig } from '@/lib/config' @@ -13,7 +13,7 @@ export default function FloatDarkModeButton () { // 用户手动设置主题 const handleChangeDarkMode = () => { const newStatus = !isDarkMode - saveDarkModeToCookies(newStatus) + saveDarkModeToLocalStorage(newStatus) updateDarkMode(newStatus) const htmlElement = document.getElementsByTagName('html')[0] htmlElement.classList?.remove(newStatus ? 'light' : 'dark') diff --git a/themes/heo/components/MenuGroupCard.js b/themes/heo/components/MenuGroupCard.js index 53157875..e1de4792 100644 --- a/themes/heo/components/MenuGroupCard.js +++ b/themes/heo/components/MenuGroupCard.js @@ -1,6 +1,7 @@ import Link from 'next/link' import { useGlobal } from '@/lib/global' import CONFIG from '../config' +import { siteConfig } from '@/lib/config' const MenuGroupCard = (props) => { const { postCount, categoryOptions, tagOptions } = props diff --git a/themes/heo/index.js b/themes/heo/index.js index 6c87aab0..f10be1a2 100644 --- a/themes/heo/index.js +++ b/themes/heo/index.js @@ -39,6 +39,7 @@ import LazyImage from '@/components/LazyImage' import WWAds from '@/components/WWAds' import { AdSlot } from '@/components/GoogleAdsense' import { siteConfig } from '@/lib/config' +import { isBrowser } from '@/lib/utils' /** * 基础布局 采用上中下布局,移动端使用顶部侧边导航栏 @@ -266,6 +267,22 @@ const LayoutSlug = props => { siteConfig('COMMENT_GISCUS_REPO') || siteConfig('COMMENT_CUSDIS_APP_ID') || siteConfig('COMMENT_UTTERRANCES_REPO') || siteConfig('COMMENT_GITALK_CLIENT_ID') || siteConfig('COMMENT_WEBMENTION_ENABLE') + const router = useRouter() + useEffect(() => { + // 404 + if (!post) { + setTimeout(() => { + if (isBrowser) { + const article = document.getElementById('notion-article') + if (!article) { + router.push('/404').then(() => { + console.warn('找不到页面', router.asPath) + }) + } + } + }, siteConfig('POST_WAITING_TIME_FOR_404') * 1000) + } + }, [post]) return ( <>
@@ -489,7 +506,7 @@ export { LayoutArchive, LayoutSlug, Layout404, - LayoutCategoryIndex, LayoutPostList, + LayoutCategoryIndex, LayoutTagIndex } diff --git a/themes/hexo/components/FloatDarkModeButton.js b/themes/hexo/components/FloatDarkModeButton.js index d5848b06..6a7fb97e 100644 --- a/themes/hexo/components/FloatDarkModeButton.js +++ b/themes/hexo/components/FloatDarkModeButton.js @@ -1,5 +1,5 @@ import { useGlobal } from '@/lib/global' -import { saveDarkModeToCookies } from '@/themes/theme' +import { saveDarkModeToLocalStorage } from '@/themes/theme' import CONFIG from '../config' import { siteConfig } from '@/lib/config' @@ -13,7 +13,7 @@ export default function FloatDarkModeButton () { // 用户手动设置主题 const handleChangeDarkMode = () => { const newStatus = !isDarkMode - saveDarkModeToCookies(newStatus) + saveDarkModeToLocalStorage(newStatus) updateDarkMode(newStatus) const htmlElement = document.getElementsByTagName('html')[0] htmlElement.classList?.remove(newStatus ? 'light' : 'dark') diff --git a/themes/hexo/index.js b/themes/hexo/index.js index e0e50daf..add49e0a 100644 --- a/themes/hexo/index.js +++ b/themes/hexo/index.js @@ -222,7 +222,22 @@ const LayoutArchive = (props) => { */ const LayoutSlug = props => { const { post, lock, validPassword } = props - + const router = useRouter() + useEffect(() => { + // 404 + if (!post) { + setTimeout(() => { + if (isBrowser) { + const article = document.getElementById('notion-article') + if (!article) { + router.push('/404').then(() => { + console.warn('找不到页面', router.asPath) + }) + } + } + }, siteConfig('POST_WAITING_TIME_FOR_404') * 1000) + } + }, [post]) return ( <>
diff --git a/themes/landing/index.js b/themes/landing/index.js index c4fd203c..d7301d4c 100644 --- a/themes/landing/index.js +++ b/themes/landing/index.js @@ -21,6 +21,7 @@ import Loading from '@/components/Loading' import { isBrowser } from '@/lib/utils' import { siteConfig } from '@/lib/config' import { Pricing } from './components/Pricing' +import { useEffect } from 'react' /** * 布局框架 @@ -71,8 +72,26 @@ const LayoutIndex = (props) => { * @returns */ const LayoutSlug = (props) => { + const { post } = props + // 如果 是 /article/[slug] 的文章路径则进行重定向到另一个域名 const router = useRouter() + useEffect(() => { + // 404 + if (!post) { + setTimeout(() => { + if (isBrowser) { + const article = document.getElementById('notion-article') + if (!article) { + router.push('/404').then(() => { + console.warn('找不到页面', router.asPath) + }) + } + } + }, siteConfig('POST_WAITING_TIME_FOR_404') * 1000) + } + }, [post]) + if (JSON.parse(siteConfig('LANDING_POST_REDIRECT_ENABLE', null, CONFIG)) && isBrowser && router.route === '/[prefix]/[slug]') { const redirectUrl = siteConfig('LANDING_POST_REDIRECT_URL', null, CONFIG) + router.asPath.replace('?theme=landing', '') router.push(redirectUrl) diff --git a/themes/matery/components/FloatDarkModeButton.js b/themes/matery/components/FloatDarkModeButton.js index e42dac8d..5db024c1 100644 --- a/themes/matery/components/FloatDarkModeButton.js +++ b/themes/matery/components/FloatDarkModeButton.js @@ -1,5 +1,5 @@ import { useGlobal } from '@/lib/global' -import { saveDarkModeToCookies } from '@/themes/theme' +import { saveDarkModeToLocalStorage } from '@/themes/theme' import CONFIG from '../config' import { siteConfig } from '@/lib/config' @@ -13,7 +13,7 @@ export default function FloatDarkModeButton() { // 用户手动设置主题 const handleChangeDarkMode = () => { const newStatus = !isDarkMode - saveDarkModeToCookies(newStatus) + saveDarkModeToLocalStorage(newStatus) updateDarkMode(newStatus) const htmlElement = document.getElementsByTagName('html')[0] htmlElement.classList?.remove(newStatus ? 'light' : 'dark') diff --git a/themes/matery/index.js b/themes/matery/index.js index 76f3bdd5..6adc6e05 100644 --- a/themes/matery/index.js +++ b/themes/matery/index.js @@ -30,6 +30,7 @@ import { Transition } from '@headlessui/react' import { Style } from './style' import replaceSearchResult from '@/components/Mark' import { siteConfig } from '@/lib/config' +import { isBrowser } from '@/lib/utils' /** * 基础布局 @@ -197,7 +198,22 @@ const LayoutArchive = (props) => { const LayoutSlug = props => { const { post, lock, validPassword } = props const { fullWidth } = useGlobal() - + const router = useRouter() + useEffect(() => { + // 404 + if (!post) { + setTimeout(() => { + if (isBrowser) { + const article = document.getElementById('notion-article') + if (!article) { + router.push('/404').then(() => { + console.warn('找不到页面', router.asPath) + }) + } + } + }, siteConfig('POST_WAITING_TIME_FOR_404') * 1000) + } + }, [post]) return (<>
diff --git a/themes/medium/index.js b/themes/medium/index.js index 1dd51496..b4d56abd 100644 --- a/themes/medium/index.js +++ b/themes/medium/index.js @@ -155,6 +155,23 @@ const LayoutSlug = props => {
) + const router = useRouter() + useEffect(() => { + // 404 + if (!post) { + setTimeout(() => { + if (isBrowser) { + const article = document.getElementById('notion-article') + if (!article) { + router.push('/404').then(() => { + console.warn('找不到页面', router.asPath) + }) + } + } + }, siteConfig('POST_WAITING_TIME_FOR_404') * 1000) + } + }, [post]) + return (
{/* 文章锁 */} diff --git a/themes/nav/index.js b/themes/nav/index.js index 84663453..2b2e4973 100755 --- a/themes/nav/index.js +++ b/themes/nav/index.js @@ -34,6 +34,8 @@ import { siteConfig } from '@/lib/config' import Live2D from '@/components/Live2D' import BlogArchiveItem from './components/BlogArchiveItem' import NotionIcon from '@/components/NotionIcon' +import { useRouter } from 'next/router' +import { isBrowser } from '@/lib/utils' const WWAds = dynamic(() => import('@/components/WWAds'), { ssr: false }) @@ -214,6 +216,22 @@ const LayoutPostList = props => { */ const LayoutSlug = (props) => { const { post, lock, validPassword } = props + const router = useRouter() + useEffect(() => { + // 404 + if (!post) { + setTimeout(() => { + if (isBrowser) { + const article = document.getElementById('notion-article') + if (!article) { + router.push('/404').then(() => { + console.warn('找不到页面', router.asPath) + }) + } + } + }, siteConfig('POST_WAITING_TIME_FOR_404') * 1000) + } + }, [post]) return ( <> {/* 文章锁 */} diff --git a/themes/next/components/DarkModeButton.js b/themes/next/components/DarkModeButton.js index a1b5aea5..8858f6b7 100644 --- a/themes/next/components/DarkModeButton.js +++ b/themes/next/components/DarkModeButton.js @@ -1,12 +1,12 @@ import { useGlobal } from '@/lib/global' -import { saveDarkModeToCookies } from '@/themes/theme' +import { saveDarkModeToLocalStorage } from '@/themes/theme' const DarkModeButton = () => { const { isDarkMode, updateDarkMode } = useGlobal() // 用户手动设置主题 const handleChangeDarkMode = () => { const newStatus = !isDarkMode - saveDarkModeToCookies(newStatus) + saveDarkModeToLocalStorage(newStatus) updateDarkMode(newStatus) const htmlElement = document.getElementsByTagName('html')[0] htmlElement.classList?.remove(newStatus ? 'light' : 'dark') diff --git a/themes/next/components/FloatDarkModeButton.js b/themes/next/components/FloatDarkModeButton.js index 0cc5e5bf..3412f646 100644 --- a/themes/next/components/FloatDarkModeButton.js +++ b/themes/next/components/FloatDarkModeButton.js @@ -1,5 +1,5 @@ import { useGlobal } from '@/lib/global' -import { saveDarkModeToCookies } from '@/themes/theme' +import { saveDarkModeToLocalStorage } from '@/themes/theme' import CONFIG from '../config' import { siteConfig } from '@/lib/config' @@ -13,7 +13,7 @@ export default function FloatDarkModeButton () { // 用户手动设置主题 const handleChangeDarkMode = () => { const newStatus = !isDarkMode - saveDarkModeToCookies(newStatus) + saveDarkModeToLocalStorage(newStatus) updateDarkMode(newStatus) const htmlElement = document.getElementsByTagName('html')[0] htmlElement.classList?.remove(newStatus ? 'light' : 'dark') diff --git a/themes/next/index.js b/themes/next/index.js index 5808f208..e0ee29f7 100644 --- a/themes/next/index.js +++ b/themes/next/index.js @@ -276,6 +276,23 @@ const LayoutArchive = (props) => { */ const LayoutSlug = (props) => { const { post, lock, validPassword } = props + + const router = useRouter() + useEffect(() => { + // 404 + if (!post) { + setTimeout(() => { + if (isBrowser) { + const article = document.getElementById('notion-article') + if (!article) { + router.push('/404').then(() => { + console.warn('找不到页面', router.asPath) + }) + } + } + }, siteConfig('POST_WAITING_TIME_FOR_404') * 1000) + } + }, [post]) return ( <> diff --git a/themes/nobelium/index.js b/themes/nobelium/index.js index a7b87810..85e6faa5 100644 --- a/themes/nobelium/index.js +++ b/themes/nobelium/index.js @@ -24,6 +24,7 @@ import { Style } from './style' import replaceSearchResult from '@/components/Mark' import AlgoliaSearchModal from '@/components/AlgoliaSearchModal' import { siteConfig } from '@/lib/config' +import { useRouter } from 'next/router' // 主题全局状态 const ThemeGlobalNobelium = createContext() @@ -198,7 +199,22 @@ const LayoutArchive = props => { */ const LayoutSlug = props => { const { post, lock, validPassword } = props - + const router = useRouter() + useEffect(() => { + // 404 + if (!post) { + setTimeout(() => { + if (isBrowser) { + const article = document.getElementById('notion-article') + if (!article) { + router.push('/404').then(() => { + console.warn('找不到页面', router.asPath) + }) + } + } + }, siteConfig('POST_WAITING_TIME_FOR_404') * 1000) + } + }, [post]) return ( <> diff --git a/themes/plog/index.js b/themes/plog/index.js index b916a30e..2bb9b610 100644 --- a/themes/plog/index.js +++ b/themes/plog/index.js @@ -164,7 +164,22 @@ const LayoutArchive = props => { */ const LayoutSlug = props => { const { post, lock, validPassword } = props - + const router = useRouter() + useEffect(() => { + // 404 + if (!post) { + setTimeout(() => { + if (isBrowser) { + const article = document.getElementById('notion-article') + if (!article) { + router.push('/404').then(() => { + console.warn('找不到页面', router.asPath) + }) + } + } + }, siteConfig('POST_WAITING_TIME_FOR_404') * 1000) + } + }, [post]) return ( <> diff --git a/themes/simple/index.js b/themes/simple/index.js index ccc0ea95..cf15cb1c 100644 --- a/themes/simple/index.js +++ b/themes/simple/index.js @@ -11,6 +11,7 @@ import replaceSearchResult from '@/components/Mark' import dynamic from 'next/dynamic' import NotionPage from '@/components/NotionPage' import AlgoliaSearchModal from '@/components/AlgoliaSearchModal' +import { useRouter } from 'next/router' // 主题组件 const BlogListScroll = dynamic(() => import('./components/BlogListScroll'), { ssr: false }); @@ -216,6 +217,23 @@ const LayoutSlug = props => { * @returns */ const Layout404 = (props) => { + const { post } = props + const router = useRouter() + useEffect(() => { + // 404 + if (!post) { + setTimeout(() => { + if (isBrowser) { + const article = document.getElementById('notion-article') + if (!article) { + router.push('/404').then(() => { + console.warn('找不到页面', router.asPath) + }) + } + } + }, siteConfig('POST_WAITING_TIME_FOR_404') * 1000) + } + }, [post]) return <> 404 Not found. diff --git a/themes/starter/components/About.js b/themes/starter/components/About.js new file mode 100644 index 00000000..203205db --- /dev/null +++ b/themes/starter/components/About.js @@ -0,0 +1,204 @@ +/* eslint-disable @next/next/no-img-element */ +/* eslint-disable react/no-unescaped-entities */ +import { siteConfig } from '@/lib/config' +import CONFIG from '../config' + +/** + * 首页的关于模块 + */ +export const About = () => { + return <> + {/* */} +
+
+
+
+ + {/* 左侧的文字说明板块 */} +
+
+

+ {siteConfig('STARTER_ABOUT_TITLE', null, CONFIG)} +

+

+ + + {siteConfig('STARTER_ABOUT_BUTTON_TEXT', null, CONFIG)} + +
+
+ + {/* 右侧的图片海报 */} +
+
+
+
+ about image +
+
+ +
+
+ about image +
+ +
+
+ + {siteConfig('STARTER_ABOUT_TIPS_1', null, CONFIG)} + + + {siteConfig('STARTER_ABOUT_TIPS_2', null, CONFIG)} + + + {siteConfig('STARTER_ABOUT_TIPS_3', null, CONFIG)} + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+ {/* */} + +} diff --git a/themes/starter/components/BackToTopButton.js b/themes/starter/components/BackToTopButton.js new file mode 100644 index 00000000..1f201684 --- /dev/null +++ b/themes/starter/components/BackToTopButton.js @@ -0,0 +1,67 @@ +import throttle from 'lodash.throttle'; +import { useCallback, useEffect } from 'react' + +export const BackToTopButton = () => { + useEffect(() => { + // ====== scroll top js + function scrollTo(element, to = 0, duration = 500) { + const start = element.scrollTop; + const change = to - start; + const increment = 20; + let currentTime = 0; + + const animateScroll = () => { + currentTime += increment; + + const val = Math.easeInOutQuad(currentTime, start, change, duration); + + element.scrollTop = val; + + if (currentTime < duration) { + setTimeout(animateScroll, increment); + } + }; + + animateScroll(); + } + + Math.easeInOutQuad = function (t, b, c, d) { + t /= d / 2; + if (t < 1) return (c / 2) * t * t + b; + t--; + return (-c / 2) * (t * (t - 2) - 1) + b; + }; + const backToTop = document.querySelector('.back-to-top') + if (backToTop) { + backToTop.onclick = () => { + scrollTo(document.documentElement); + }; + } + + window.addEventListener('scroll', navBarScollListener) + return () => { + window.removeEventListener('scroll', navBarScollListener) + } + }, []) + + // 滚动监听 + const throttleMs = 200 + const navBarScollListener = useCallback( + throttle(() => { + const scrollY = window.scrollY; + // 显示或隐藏返回顶部按钮 + const backToTop = document.querySelector('.back-to-top'); + if (backToTop) { + backToTop.style.display = scrollY > 50 ? 'flex' : 'none'; + } + }, throttleMs) + ) + + return <> + {/* */} + + + + {/* */} + +} diff --git a/themes/starter/components/Banner.js b/themes/starter/components/Banner.js new file mode 100644 index 00000000..ab89d43c --- /dev/null +++ b/themes/starter/components/Banner.js @@ -0,0 +1,53 @@ +/** + * 详情页面顶部条 + * @returns + */ +export const Banner = ({ title, description }) => { + return <> + {/* */} +
+
+
+
+
+
+

+ {title} +

+

+ {description} +

+ + {/* */} +
+
+
+
+
+ {/* */} + +} diff --git a/themes/starter/components/Blog.js b/themes/starter/components/Blog.js new file mode 100644 index 00000000..be245d17 --- /dev/null +++ b/themes/starter/components/Blog.js @@ -0,0 +1,77 @@ +/* eslint-disable @next/next/no-img-element */ +import { siteConfig } from '@/lib/config' +import CONFIG from '../config' + +/** + * 博文列表 + * @param {*} param0 + * @returns + */ +export const Blog = ({ posts }) => { + return <> + {/* */} +
+
+ {/* 区块标题文字 */} +
+
+
+ + {siteConfig('STARTER_BLOG_TITLE', null, CONFIG)} + +

+ {siteConfig('STARTER_BLOG_TEXT_1', null, CONFIG)} +

+

+ +

+
+
+
+ {/* 博客列表 此处优先展示3片文章 */} +
+ {posts?.map((item, index) => { + return
+
+
+ + {item.title} + +
+
+ + {item.publishDay} + +

+ + {item.title} + +

+

+ {item.summary} +

+
+
+
+ }) } +
+
+
+ {/* */} + +} diff --git a/themes/starter/components/Brand.js b/themes/starter/components/Brand.js new file mode 100644 index 00000000..db51823e --- /dev/null +++ b/themes/starter/components/Brand.js @@ -0,0 +1,37 @@ +/* eslint-disable @next/next/no-img-element */ + +import CONFIG from '../config' + +/** + * 合作伙伴 + * @returns + */ +export const Brand = () => { + return <> + + {/* */} +
+
+
+ {CONFIG.STARTER_BRANDS?.map((item, index) => { + return + {item.TITLE} + {item.TITLE} + + })} +
+
+
+ {/* */} + +} diff --git a/themes/starter/components/CTA.js b/themes/starter/components/CTA.js new file mode 100644 index 00000000..756b1f26 --- /dev/null +++ b/themes/starter/components/CTA.js @@ -0,0 +1,106 @@ +export const CTA = () => { + return <> + + {/* */} +
+
+
+
+
+
+

+ What Are You Looking For? + + Get Started Now + +

+

+ There are many variations of passages of Lorem Ipsum but the + majority have suffered in some form. +

+ + Start using Play + +
+
+
+
+
+
+ + + + + + + + + + + + + + +
+
+ {/* */} + +} diff --git a/themes/starter/components/Contact.js b/themes/starter/components/Contact.js new file mode 100644 index 00000000..84fba5d2 --- /dev/null +++ b/themes/starter/components/Contact.js @@ -0,0 +1,90 @@ +import { siteConfig } from '@/lib/config' +import CONFIG from '../config' +import { SVGLocation } from './svg/SVGLocation' +import { SVGEmail } from './svg/SVGEmail' + +/* eslint-disable react/no-unescaped-entities */ +export const Contact = () => { + return <> + {/* */} +
+
+
+
+
+ + {/* 联系方式左侧文字 */} +
+
+
+ + {siteConfig('STARTER_CONTACT_TITLE', null, CONFIG)} + +

+ {siteConfig('STARTER_CONTACT_TEXT', null, CONFIG)} +

+
+
+
+
+ +
+
+
+ {siteConfig('STARTER_CONTACT_LOCATION_TITLE', null, CONFIG)} +
+

+ {siteConfig('STARTER_CONTACT_LOCATION_TEXT', null, CONFIG)} +

+
+
+
+
+ +
+
+
+ {siteConfig('STARTER_CONTACT_EMAIL_TITLE', null, CONFIG)} +
+

+ {siteConfig('STARTER_CONTACT_EMAIL_TEXT', null, CONFIG)} +

+
+
+
+
+
+ + {/* 联系方式右侧留言 */} +
+
+ {/* 自定义的留言表单 、 需要对接接口 */} + {/* */} + {/* 嵌入外部表单 */} + + +
+
+ +
+
+
+ {/* */} + + +} diff --git a/themes/starter/components/DarkModeButton.js b/themes/starter/components/DarkModeButton.js new file mode 100644 index 00000000..9d4ceef1 --- /dev/null +++ b/themes/starter/components/DarkModeButton.js @@ -0,0 +1,84 @@ +import { useGlobal } from '@/lib/global'; +import { useRouter } from 'next/router'; + +export const DarkModeButton = () => { + const { toggleDarkMode } = useGlobal() + const router = useRouter() + return <> + + +} diff --git a/themes/starter/components/FAQ.js b/themes/starter/components/FAQ.js new file mode 100644 index 00000000..daff2aaf --- /dev/null +++ b/themes/starter/components/FAQ.js @@ -0,0 +1,137 @@ +import { siteConfig } from '@/lib/config'; +import { useEffect } from 'react' +import CONFIG from '../config'; +import { SVGQuestion } from './svg/SVGQuestion'; +import { SVGCircleBG } from './svg/SVGCircleBG'; + +export const FAQ = () => { + useEffect(() => { + // ===== Faq accordion + const faqs = document.querySelectorAll('.single-faq'); + faqs.forEach((el) => { + el.querySelector('.faq-btn').addEventListener('click', () => { + el.querySelector('.icon').classList.toggle('rotate-180'); + el.querySelector('.faq-content').classList.toggle('hidden'); + }); + }); + }) + return <> + {/* */} +
+
+
+
+
+ + {siteConfig('STARTER_FAQ_TITLE', null, CONFIG)} + +

+ {siteConfig('STARTER_FAQ_TEXT_1', null, CONFIG)} +

+

+ {siteConfig('STARTER_FAQ_TEXT_2', null, CONFIG)} +

+
+
+
+ +
+
+
+
+ +
+
+

+ {siteConfig('STARTER_FAQ_1_QUESTION', null, CONFIG)} +

+

+

+
+
+
+
+ +
+
+

+ {siteConfig('STARTER_FAQ_2_QUESTION', null, CONFIG)} +

+

+

+
+
+
+ +
+
+
+ +
+
+

+ {siteConfig('STARTER_FAQ_3_QUESTION', null, CONFIG)} +

+

+

+
+
+
+
+ +
+
+

+ {siteConfig('STARTER_FAQ_4_QUESTION', null, CONFIG)} +

+

+

+
+
+
+
+
+ + {/* 背景图案 */} +
+ + + + + + +
+
+ {/* */} + +} diff --git a/themes/starter/components/Features.js b/themes/starter/components/Features.js new file mode 100644 index 00000000..c3306746 --- /dev/null +++ b/themes/starter/components/Features.js @@ -0,0 +1,135 @@ +import CONFIG from '../config' +import { siteConfig } from '@/lib/config'; +import { SVGGifts } from './svg/SVGGifts'; +import { SVGTemplate } from './svg/SVGTemplate'; +import { SVGDesign } from './svg/SVGDesign'; +import { SVGEssential } from './svg/SVGEssential'; +/** + * 产品特性相关,将显示在首页中 + * @returns + */ +export const Features = () => { + return <> + {/* */} +
+
+
+
+
+ + {siteConfig('STARTER_FEATURE_TITLE', null, CONFIG)} + +

+ {siteConfig('STARTER_FEATURE_TEXT_1', null, CONFIG)} +

+

+ {siteConfig('STARTER_FEATURE_TEXT_2', null, CONFIG)} +

+
+
+
+
+
+
+
+ + +
+

+ {siteConfig('STARTER_FEATURE_1_TITLE_1', null, CONFIG)} +

+

+ {siteConfig('STARTER_FEATURE_1_TEXT_1', null, CONFIG)} +

+ + {siteConfig('STARTER_FEATURE_1_BUTTON_TEXT', null, CONFIG)} + +
+
+
+
+
+ + +
+

+ {siteConfig('STARTER_FEATURE_2_TITLE_1', null, CONFIG)} +

+

+ {siteConfig('STARTER_FEATURE_2_TEXT_1', null, CONFIG)} +

+ + {siteConfig('STARTER_FEATURE_2_BUTTON_TEXT', null, CONFIG)} + +
+
+
+
+
+ + +
+

+ {siteConfig('STARTER_FEATURE_3_TITLE_1', null, CONFIG)} +

+

+ {siteConfig('STARTER_FEATURE_3_TEXT_1', null, CONFIG)} +

+ + {siteConfig('STARTER_FEATURE_3_BUTTON_TEXT', null, CONFIG)} + +
+
+
+
+
+ + +
+

+ {siteConfig('STARTER_FEATURE_4_TITLE_1', null, CONFIG)} +

+

+ {siteConfig('STARTER_FEATURE_4_TEXT_1', null, CONFIG)} +

+ + {siteConfig('STARTER_FEATURE_3_BUTTON_TEXT', null, CONFIG)} + +
+
+
+
+
+ {/* */} + +} diff --git a/themes/starter/components/Footer.js b/themes/starter/components/Footer.js new file mode 100644 index 00000000..46fcee62 --- /dev/null +++ b/themes/starter/components/Footer.js @@ -0,0 +1,164 @@ +import { siteConfig } from '@/lib/config' +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) => { + const latestPosts = props?.latestPosts ? props?.latestPosts.slice(0, 2) : [] + + return <> + {/* */} + + {/* */} + +} diff --git a/themes/starter/components/Hero.js b/themes/starter/components/Hero.js new file mode 100644 index 00000000..ad49776e --- /dev/null +++ b/themes/starter/components/Hero.js @@ -0,0 +1,1277 @@ +import { siteConfig } from '@/lib/config' +import CONFIG from '../config' +import SVGPlayBootstrap from './svg/SVGPlayBoostrap' +import SVGPlayTailwind from './svg/SVGPlayTailWind' +import SVGPlayReact from './svg/SVGPlayReact' +import SVGPlayAstro from './svg/SVGPlayAstro' +import SVGPlayNext from './svg/SVGPlayNext' + +export const Hero = () => { + return <> + {/* */} +
+
+
+
+
+ {/* 主标题 */} +

+ {siteConfig('STARTER_HERO_TITLE_1', null, CONFIG)} +

+ {/* 次标题 */} +

+ {siteConfig('STARTER_HERO_TITLE_2', null, CONFIG)} +

+ {/* 按钮组 */} + + + {/* 技术栈说明 */} +
+

+ {siteConfig('STARTER_HERO_TITLE_3', null, CONFIG)} +

+ + {/* 产品技术栈图标 */} +
+ + {/* 图标放这里 */} + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ + {/* 产品预览图片 */} +
+
+
+ {/* eslint-disable-next-line @next/next/no-img-element */} + hero +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
+ {/* */} + +} diff --git a/themes/starter/components/Logo.js b/themes/starter/components/Logo.js new file mode 100644 index 00000000..b6afeae1 --- /dev/null +++ b/themes/starter/components/Logo.js @@ -0,0 +1,61 @@ +/* eslint-disable @next/next/no-html-link-for-pages */ +import { siteConfig } from '@/lib/config'; +import { useGlobal } from '@/lib/global'; +import throttle from 'lodash.throttle'; +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; +import CONFIG from '../config'; + +/** + * 站点图标 + * @returns + */ +export const Logo = ({ white }) => { + const router = useRouter() + const { isDarkMode } = useGlobal() + const logoWhite = siteConfig('STARTER_LOGO_WHITE', null, CONFIG) + const [logo, setLogo] = useState(logoWhite) + const [logoTextColor, setLogoTextColor] = useState('text-white') + + useEffect(() => { + // 滚动监听 + const throttleMs = 200 + const navBarScrollListener = throttle(() => { + const scrollY = window.scrollY; + // 何时显示浅色或白底的logo + const homePageNavBar = router.route === '/' && scrollY < 10 // 在首页并且视窗在页面顶部 + console.log('白色', homePageNavBar, router.route, scrollY < 10) + if (white || isDarkMode || homePageNavBar) { + setLogo(siteConfig('STARTER_LOGO_WHITE', null, CONFIG)) + setLogoTextColor('text-white') + } else { + setLogo(siteConfig('STARTER_LOGO', null, CONFIG)) + setLogoTextColor('text-black') + } + }, throttleMs) + + navBarScrollListener() + window.addEventListener('scroll', navBarScrollListener) + return () => { + window.removeEventListener('scroll', navBarScrollListener) + } + }, [isDarkMode, router]) + + return
+
+ {/* eslint-disable-next-line @next/next/no-img-element */} + {logo && { + router.push('/') + }} + src={logo} + alt="logo" + className="header-logo w-full" + />} + {/* logo文字 */} + { router.push('/') }} className={`${logoTextColor} dark:text-white py-1.5 header-logo-text whitespace-nowrap text-2xl font-semibold`}> + {siteConfig('TITLE')} + +
+
+} diff --git a/themes/starter/components/MadeWithButton.js b/themes/starter/components/MadeWithButton.js new file mode 100644 index 00000000..a833f78b --- /dev/null +++ b/themes/starter/components/MadeWithButton.js @@ -0,0 +1,29 @@ +/* eslint-disable @next/next/no-img-element */ +export const MadeWithButton = () => { + return <> + {/* */} + + + Made with + + + + tailgrids + tailgrids + + + +} diff --git a/themes/starter/components/MenuItem.js b/themes/starter/components/MenuItem.js new file mode 100644 index 00000000..d5ce70a2 --- /dev/null +++ b/themes/starter/components/MenuItem.js @@ -0,0 +1,47 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' + +export const MenuItem = ({ link }) => { + const hasSubMenu = link?.subMenus?.length > 0 + const router = useRouter() + return <> + {/* MenuItem */} + {!hasSubMenu &&
  • + + {link?.icon && }{link?.name} + +
  • } + + {hasSubMenu &&
  • + + {/* 有子菜单的MenuItem */} + + {link?.icon && }{link?.name} + + + + + + +
    + {link.subMenus.map((sLink, index) => { + return + {/* 子菜单SubMenuItem */} + {link?.icon && } {sLink.title} + + })} +
    +
  • } + +} diff --git a/themes/starter/components/MenuList.js b/themes/starter/components/MenuList.js new file mode 100644 index 00000000..081e9933 --- /dev/null +++ b/themes/starter/components/MenuList.js @@ -0,0 +1,82 @@ +import { siteConfig } from '@/lib/config'; +import { useGlobal } from '@/lib/global'; +import { useEffect } from 'react'; +import CONFIG from '../config'; +import { MenuItem } from './MenuItem'; +/** + * 响应式 折叠菜单 + */ +export const MenuList = (props) => { + const { customNav, customMenu } = props + const { locale } = useGlobal() + + let links = [ + { icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', show: siteConfig('HEO_MENU_ARCHIVE', null, CONFIG) }, + { icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: siteConfig('HEO_MENU_SEARCH', null, CONFIG) }, + { icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('HEO_MENU_CATEGORY', null, CONFIG) }, + { icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('HEO_MENU_TAG', null, CONFIG) } + ] + + if (customNav) { + links = customNav.concat(links) + } + + useEffect(() => { + // ===== responsive navbar + const navbarToggler = document.querySelector('#navbarToggler'); + const navbarCollapse = document.querySelector('#navbarCollapse'); + + // 点击弹出菜单 + navbarToggler?.addEventListener('click', () => { + navbarToggler?.classList.toggle('navbarTogglerActive'); + navbarCollapse?.classList.toggle('hidden'); + }); + + //= ==== close navbar-collapse when a clicked + document + .querySelectorAll('#navbarCollapse ul li:not(.submenu-item) a') + .forEach((e) => + e.addEventListener('click', () => { + navbarToggler?.classList.remove('navbarTogglerActive'); + navbarCollapse?.classList.add('hidden'); + }) + ); + + // ===== Sub-menu + const submenuItems = document.querySelectorAll('.submenu-item'); + submenuItems.forEach((el) => { + el.querySelector('a')?.addEventListener('click', () => { + el.querySelector('.submenu')?.classList.toggle('hidden'); + }); + }); + }, []) + + // 如果 开启自定义菜单,则覆盖Page生成的菜单 + if (siteConfig('CUSTOM_MENU')) { + links = customMenu + } + + // if (!links || links.length === 0) { + // return null + // } + + return <> +
    + + {/* 移动端菜单切换按钮 */} + + + {/* 响应式菜单 */} + +
    + + +} diff --git a/themes/starter/components/MessageForm.js b/themes/starter/components/MessageForm.js new file mode 100644 index 00000000..25bce6eb --- /dev/null +++ b/themes/starter/components/MessageForm.js @@ -0,0 +1,132 @@ +import { siteConfig } from '@/lib/config' +import CONFIG from '../config' +import { useRef, useState } from 'react' + +/** + * 留言表单 + * @returns + */ +export const MessageForm = () => { + const formRef = useRef() + const [success] = useState(false) + const [formData, setFormData] = useState({ + fullName: '', + email: '', + phone: '', + message: '' + }) + + const handleChange = (e) => { + const { name, value } = e.target + setFormData(prevState => ({ + ...prevState, + [name]: value + })) + } + + // useEffect(() => { + // const form = formRef.current + // const handleSubmit = (e) => { + // e.preventDefault() + // submitComments(formData).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) + // } + // }, [submitComments]) + + return <> +

    + {siteConfig('STARTER_CONTACT_MSG_TITLE', null, CONFIG)} +

    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + {/* Success message */} + {success &&

    {siteConfig('STARTER_CONTACT_MSG_THANKS', null, CONFIG)}

    } + +
    +
    + +} diff --git a/themes/starter/components/NavBar.js b/themes/starter/components/NavBar.js new file mode 100644 index 00000000..2d4fda41 --- /dev/null +++ b/themes/starter/components/NavBar.js @@ -0,0 +1,91 @@ +/* eslint-disable no-unreachable */ +import throttle from 'lodash.throttle'; +import { useCallback, useEffect, useState } from 'react' +import { MenuList } from './MenuList'; +import { DarkModeButton } from './DarkModeButton'; +import { Logo } from './Logo'; +import { useRouter } from 'next/router'; +import { siteConfig } from '@/lib/config'; +import CONFIG from '../config'; +import { useGlobal } from '@/lib/global'; + +/** + * 顶部导航栏 + */ +export const NavBar = (props) => { + const router = useRouter() + const { isDarkMode } = useGlobal() + const [buttonTextColor, setColor] = useState(router.route === '/' ? 'text-white' : '') + useEffect(() => { + if (isDarkMode || router.route === '/') { + setColor('text-white') + } else { + setColor('') + } + // ======= Sticky + window.addEventListener('scroll', navBarScollListener) + return () => { + window.removeEventListener('scroll', navBarScollListener) + } + }, [[isDarkMode]]) + + // 滚动监听 + const throttleMs = 200 + const navBarScollListener = useCallback( + throttle(() => { + // eslint-disable-next-line camelcase + const ud_header = document.querySelector('.ud-header'); + const scrollY = window.scrollY; + // 控制台输出当前滚动位置和 sticky 值 + if (scrollY > 0) { + ud_header?.classList?.add('sticky'); + } else { + ud_header?.classList?.remove('sticky'); + } + }, throttleMs) + ) + + return <> + {/* */} +
    + +
    + +
    + + {/* Logo */} + + +
    + + {/* 中间菜单 */} + + + {/* 右侧功能 */} + + +
    +
    +
    +
    + {/* */} + +} diff --git a/themes/starter/components/Pricing.js b/themes/starter/components/Pricing.js new file mode 100644 index 00000000..466f74f7 --- /dev/null +++ b/themes/starter/components/Pricing.js @@ -0,0 +1,177 @@ +import { siteConfig } from '@/lib/config' +import CONFIG from '../config' + +/** + * 价格板块 + * @returns + */ +export const Pricing = () => { + return <> + {/* */} +
    +
    +
    +
    +
    + + {siteConfig('STARTER_PRICING_TITLE', null, CONFIG)} + +

    + {siteConfig('STARTER_PRICING_TEXT_1', null, CONFIG)} +

    +

    + {siteConfig('STARTER_PRICING_TEXT_2', null, CONFIG)} +

    +
    +
    +
    + + {/* 第一个付费计划 */} +
    +
    +
    + + {siteConfig('STARTER_PRICING_1_TITLE', null, CONFIG)} + +

    + {siteConfig('STARTER_PRICING_1_PRICE_CURRENCY', null, CONFIG)} + {siteConfig('STARTER_PRICING_1_PRICE', null, CONFIG)} + + {siteConfig('STARTER_PRICING_1_PRICE_PERIOD', null, CONFIG)} + +

    + +
    +
    + {siteConfig('STARTER_PRICING_1_HEADER', null, CONFIG)} +
    +
    + {siteConfig('STARTER_PRICING_1_FEATURES', null, CONFIG)?.split(',').map((feature, index) => { + return

    + {feature} +

    + })} + +
    +
    + + {siteConfig('STARTER_PRICING_1_BUTTON_TEXT', null, CONFIG)} + +
    +
    + + {/* 第二个付费计划 */} +
    +
    +

    + {siteConfig('STARTER_PRICING_2_TAG', null, CONFIG)} +

    + + {siteConfig('STARTER_PRICING_2_TITLE', null, CONFIG)} + +

    + {siteConfig('STARTER_PRICING_1_PRICE_CURRENCY', null, CONFIG)} + {siteConfig('STARTER_PRICING_1_PRICE', null, CONFIG)} + + {siteConfig('STARTER_PRICING_2_PRICE_PERIOD', null, CONFIG)} + +

    + +
    +
    + {siteConfig('STARTER_PRICING_2_HEADER', null, CONFIG)} +
    +
    + {siteConfig('STARTER_PRICING_2_FEATURES', null, CONFIG)?.split(',').map((feature, index) => { + return

    + {feature} +

    + })} + +
    +
    + + {siteConfig('STARTER_PRICING_2_BUTTON_TEXT', null, CONFIG)} + +
    +
    + + {/* 第三个付费计划 */} +
    +
    + + {siteConfig('STARTER_PRICING_3_TITLE', null, CONFIG)} + +

    + {siteConfig('STARTER_PRICING_3_PRICE_CURRENCY', null, CONFIG)} + {siteConfig('STARTER_PRICING_3_PRICE', null, CONFIG)} + + {siteConfig('STARTER_PRICING_3_PRICE_PERIOD', null, CONFIG)} + +

    + +
    +
    + {siteConfig('STARTER_PRICING_3_HEADER', null, CONFIG)} +
    +
    + {siteConfig('STARTER_PRICING_3_FEATURES', null, CONFIG)?.split(',').map((feature, index) => { + return

    + {feature} +

    + })} + +
    +
    + + {siteConfig('STARTER_PRICING_3_BUTTON_TEXT', null, CONFIG)} + +
    +
    +
    +
    +
    + {/* */} + + +} diff --git a/themes/starter/components/SignInForm.js b/themes/starter/components/SignInForm.js new file mode 100644 index 00000000..1b7024c3 --- /dev/null +++ b/themes/starter/components/SignInForm.js @@ -0,0 +1,97 @@ +/* eslint-disable @next/next/no-img-element */ + +import { Logo } from './Logo' +import { SVGCircleBg2 } from './svg/SVGCircleBG2' +import { SVGCircleBG3 } from './svg/SVGCircleBG3' +import { SVGFacebook } from './svg/SVGFacebook' +import { SVGGoogle } from './svg/SVGGoogle' +import { SVGTwitter } from './svg/SVGTwitter' + +/** + * 登录 + * @returns + */ +export const SignInForm = () => { + return <> + {/* */} +
    +
    +
    +
    +
    +
    +
    + +
    +
    + + {/* 表单内容 */} +
    +
    + +
    +
    + +
    +
    + +
    +
    + + + + Connect With + + + {/* 社交平台 */} + + + Forget Password? + +

    + Not a member yet? + + Sign Up + +

    + +
    + + + + + + +
    +
    +
    +
    +
    +
    + {/* */} + +} diff --git a/themes/starter/components/SignUpForm.js b/themes/starter/components/SignUpForm.js new file mode 100644 index 00000000..f6f36553 --- /dev/null +++ b/themes/starter/components/SignUpForm.js @@ -0,0 +1,160 @@ +import { Logo } from './Logo' + +/** + * 注册 + * @returns + */ +export const SignUpForm = () => { + return <> + {/* */} +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + + + Connect With + + + +

    + By creating an account you are agree with our + + Privacy + + and + + Policy + +

    + +

    + Already have an account? + + Sign In + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    +
    + {/* */} + +} diff --git a/themes/starter/components/SocialButton.js b/themes/starter/components/SocialButton.js new file mode 100644 index 00000000..b9def55b --- /dev/null +++ b/themes/starter/components/SocialButton.js @@ -0,0 +1,44 @@ +import { siteConfig } from '@/lib/config' + +/** + * 社交联系方式按钮组 + * @returns {JSX.Element} + * @constructor + */ +const SocialButton = () => { + return
    +
    + {siteConfig('CONTACT_GITHUB') && + + } + {siteConfig('CONTACT_TWITTER') && + + } + {siteConfig('CONTACT_TELEGRAM') && + + } + {siteConfig('CONTACT_LINKEDIN') && + + } + {siteConfig('CONTACT_WEIBO') && + + } + {siteConfig('CONTACT_INSTAGRAM') && + + } + {siteConfig('CONTACT_EMAIL') && + + } + {JSON.parse(siteConfig('ENABLE_RSS')) && + + } + {siteConfig('CONTACT_BILIBILI') && + + } + {siteConfig('CONTACT_YOUTUBE') && + + } +
    +
    +} +export default SocialButton diff --git a/themes/starter/components/Team.js b/themes/starter/components/Team.js new file mode 100644 index 00000000..5d24e18e --- /dev/null +++ b/themes/starter/components/Team.js @@ -0,0 +1,95 @@ +/* eslint-disable @next/next/no-img-element */ +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 <> + {/* */} +
    +
    +
    +
    +
    + + {siteConfig('STARTER_TEAM_TITLE', null, CONFIG)} + +

    + {siteConfig('STARTER_TEAM_TEXT_1', null, CONFIG)} +

    +

    +

    +
    +
    +
    + + {/* 团队成员排列矩阵 */} +
    + {CONFIG.STARTER_TEAM_ITEMS.map((item, index) => { + return
    +
    + {/* 头像 */} +
    + team image + + + + +
    + + {/* 文字介绍 */} +
    + +

    + {item.STARTER_TEAM_ITEM_NICKNAME} +

    + +

    + {item.STARTER_TEAM_ITEM_DESCRIPTION} +

    + + {/* 社交链接 */} + +
    +
    +
    + })} + +
    +
    +
    + {/* */} + +} diff --git a/themes/starter/components/Testimonials.js b/themes/starter/components/Testimonials.js new file mode 100644 index 00000000..6fdd98b6 --- /dev/null +++ b/themes/starter/components/Testimonials.js @@ -0,0 +1,145 @@ +/* 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'; + +/** + * 一些外部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'); + + const Swiper = window.Swiper + if (!Swiper) { + return + } + // Testimonial + // eslint-disable-next-line no-unused-vars + const testimonialSwiper = new Swiper('.testimonial-carousel', { + slidesPerView: 1, + spaceBetween: 30, + + // Navigation arrows + navigation: { + nextEl: '.swiper-button-next', + prevEl: '.swiper-button-prev' + }, + + breakpoints: { + 640: { + slidesPerView: 2, + spaceBetween: 30 + }, + 1024: { + slidesPerView: 3, + spaceBetween: 30 + }, + 1280: { + slidesPerView: 3, + spaceBetween: 30 + } + } + }); +}; + +export const Testimonials = () => { + useEffect(() => { + loadExternal() + }, []) + // 用户评分 + const ratings = [1, 2, 3, 4, 5]; + return <> + {/* */} +
    +
    +
    +
    +
    + + {siteConfig('STARTER_TESTIMONIALS_TITLE', null, CONFIG)} + +

    + {siteConfig('STARTER_TESTIMONIALS_TEXT_1', null, CONFIG)} +

    +

    + {siteConfig('STARTER_TESTIMONIALS_TEXT_2', null, CONFIG)} +

    +
    +
    +
    + +
    +
    +
    + + {/* 用户评价卡牌 */} + {CONFIG.STARTER_TESTIMONIALS_ITEMS.map((item, index) => { + return
    +
    +
    + {ratings.map((rating, index) => ( + star icon + ))} +
    + +

    + “{item.STARTER_TESTIMONIALS_ITEM_TEXT}” +

    + + +
    + author +
    + +
    +

    + {item.STARTER_TESTIMONIALS_ITEM_NICKNAME} +

    +

    + {item.STARTER_TESTIMONIALS_ITEM_DESCRIPTION} +

    +
    +
    +
    +
    + })} + +
    + + {/* 切换按钮 */} +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + {/* */} + +} diff --git a/themes/starter/components/svg/SVG404.js b/themes/starter/components/svg/SVG404.js new file mode 100644 index 00000000..dae4e7bc --- /dev/null +++ b/themes/starter/components/svg/SVG404.js @@ -0,0 +1,33 @@ +export const SVG404 = () => { + return + + + + + + + +} diff --git a/themes/starter/components/svg/SVGAvatarBG.js b/themes/starter/components/svg/SVGAvatarBG.js new file mode 100644 index 00000000..38f3ef75 --- /dev/null +++ b/themes/starter/components/svg/SVGAvatarBG.js @@ -0,0 +1,220 @@ +export const SVGAvatarBG = () => { + return + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} diff --git a/themes/starter/components/svg/SVGCircleBG.js b/themes/starter/components/svg/SVGCircleBG.js new file mode 100644 index 00000000..58d95909 --- /dev/null +++ b/themes/starter/components/svg/SVGCircleBG.js @@ -0,0 +1,290 @@ +export const SVGCircleBG = () => { + return + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} diff --git a/themes/starter/components/svg/SVGCircleBG2.js b/themes/starter/components/svg/SVGCircleBG2.js new file mode 100644 index 00000000..842d2a5e --- /dev/null +++ b/themes/starter/components/svg/SVGCircleBG2.js @@ -0,0 +1,35 @@ +/** + * 圆点背景图 + */ +export const SVGCircleBg2 = () => { + return + + + + + + + + + + + + + + + + + +} diff --git a/themes/starter/components/svg/SVGCircleBG3.js b/themes/starter/components/svg/SVGCircleBG3.js new file mode 100644 index 00000000..0390998d --- /dev/null +++ b/themes/starter/components/svg/SVGCircleBG3.js @@ -0,0 +1,24 @@ +export const SVGCircleBG3 = () => { + return + + + + + + + + + + + + + +} diff --git a/themes/starter/components/svg/SVGDesign.js b/themes/starter/components/svg/SVGDesign.js new file mode 100644 index 00000000..414e8fbe --- /dev/null +++ b/themes/starter/components/svg/SVGDesign.js @@ -0,0 +1,46 @@ +export const SVGDesign = () => { + return + + + + + + + + + + +} diff --git a/themes/starter/components/svg/SVGEmail.js b/themes/starter/components/svg/SVGEmail.js new file mode 100644 index 00000000..13f90a9b --- /dev/null +++ b/themes/starter/components/svg/SVGEmail.js @@ -0,0 +1,12 @@ +export const SVGEmail = () => { + return + + +} diff --git a/themes/starter/components/svg/SVGEssential.js b/themes/starter/components/svg/SVGEssential.js new file mode 100644 index 00000000..bf8527c1 --- /dev/null +++ b/themes/starter/components/svg/SVGEssential.js @@ -0,0 +1,26 @@ +export const SVGEssential = () => { + return + + + + + +} diff --git a/themes/starter/components/svg/SVGFacebook.js b/themes/starter/components/svg/SVGFacebook.js new file mode 100644 index 00000000..3d9836e3 --- /dev/null +++ b/themes/starter/components/svg/SVGFacebook.js @@ -0,0 +1,15 @@ +export const SVGFacebook = ({ className }) => { + return + + +} diff --git a/themes/starter/components/svg/SVGFooterCircleBG.js b/themes/starter/components/svg/SVGFooterCircleBG.js new file mode 100644 index 00000000..62280889 --- /dev/null +++ b/themes/starter/components/svg/SVGFooterCircleBG.js @@ -0,0 +1,255 @@ +export const SVGFooterCircleBG = () => { + return + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} diff --git a/themes/starter/components/svg/SVGGifts.js b/themes/starter/components/svg/SVGGifts.js new file mode 100644 index 00000000..6c0d78bd --- /dev/null +++ b/themes/starter/components/svg/SVGGifts.js @@ -0,0 +1,14 @@ +export const SVGGifts = () => { + return + + +} diff --git a/themes/starter/components/svg/SVGGoogle.js b/themes/starter/components/svg/SVGGoogle.js new file mode 100644 index 00000000..2773996a --- /dev/null +++ b/themes/starter/components/svg/SVGGoogle.js @@ -0,0 +1,14 @@ +export const SVGGoogle = ({ className }) => { + return + + +} diff --git a/themes/starter/components/svg/SVGInstagram.js b/themes/starter/components/svg/SVGInstagram.js new file mode 100644 index 00000000..f904ce7b --- /dev/null +++ b/themes/starter/components/svg/SVGInstagram.js @@ -0,0 +1,23 @@ +export const SVGInstagram = () => { + return + + + + +} diff --git a/themes/starter/components/svg/SVGLeftArrow.js b/themes/starter/components/svg/SVGLeftArrow.js new file mode 100644 index 00000000..693aafbd --- /dev/null +++ b/themes/starter/components/svg/SVGLeftArrow.js @@ -0,0 +1,14 @@ +export const SVGLeftArrow = () => { + return + + +} diff --git a/themes/starter/components/svg/SVGLocation.js b/themes/starter/components/svg/SVGLocation.js new file mode 100644 index 00000000..bfc8c18f --- /dev/null +++ b/themes/starter/components/svg/SVGLocation.js @@ -0,0 +1,15 @@ +export const SVGLocation = () => { + return + + + +} diff --git a/themes/starter/components/svg/SVGPlayAstro.js b/themes/starter/components/svg/SVGPlayAstro.js new file mode 100644 index 00000000..0af28259 --- /dev/null +++ b/themes/starter/components/svg/SVGPlayAstro.js @@ -0,0 +1,28 @@ +export default function SVGPlayAstro() { + return + + + + + + + + + + +} diff --git a/themes/starter/components/svg/SVGPlayBoostrap.js b/themes/starter/components/svg/SVGPlayBoostrap.js new file mode 100644 index 00000000..0f3aeca1 --- /dev/null +++ b/themes/starter/components/svg/SVGPlayBoostrap.js @@ -0,0 +1,31 @@ +export default function SVGPlayBootstrap() { + return + + + + + + + +} diff --git a/themes/starter/components/svg/SVGPlayNext.js b/themes/starter/components/svg/SVGPlayNext.js new file mode 100644 index 00000000..91a0a537 --- /dev/null +++ b/themes/starter/components/svg/SVGPlayNext.js @@ -0,0 +1,14 @@ +export default function SVGPlayNext() { + return + + +} diff --git a/themes/starter/components/svg/SVGPlayReact.js b/themes/starter/components/svg/SVGPlayReact.js new file mode 100644 index 00000000..2556599b --- /dev/null +++ b/themes/starter/components/svg/SVGPlayReact.js @@ -0,0 +1,17 @@ +export default function SVGPlayReact() { + return + + + +} diff --git a/themes/starter/components/svg/SVGPlayTailWind.js b/themes/starter/components/svg/SVGPlayTailWind.js new file mode 100644 index 00000000..c95fd149 --- /dev/null +++ b/themes/starter/components/svg/SVGPlayTailWind.js @@ -0,0 +1,29 @@ +export default function SVGPlayTailwind() { + return + + + + + + + +} diff --git a/themes/starter/components/svg/SVGQuestion.js b/themes/starter/components/svg/SVGQuestion.js new file mode 100644 index 00000000..817171a2 --- /dev/null +++ b/themes/starter/components/svg/SVGQuestion.js @@ -0,0 +1,18 @@ +export const SVGQuestion = () => { + return + + + + +} diff --git a/themes/starter/components/svg/SVGRightArrow.js b/themes/starter/components/svg/SVGRightArrow.js new file mode 100644 index 00000000..ba6338cb --- /dev/null +++ b/themes/starter/components/svg/SVGRightArrow.js @@ -0,0 +1,14 @@ +export const SVGRightArrow = () => { + return + + +} diff --git a/themes/starter/components/svg/SVGTemplate.js b/themes/starter/components/svg/SVGTemplate.js new file mode 100644 index 00000000..a618d0ba --- /dev/null +++ b/themes/starter/components/svg/SVGTemplate.js @@ -0,0 +1,14 @@ +export const SVGTemplate = () => { + return + + +} diff --git a/themes/starter/components/svg/SVGTwitter.js b/themes/starter/components/svg/SVGTwitter.js new file mode 100644 index 00000000..dcef95fc --- /dev/null +++ b/themes/starter/components/svg/SVGTwitter.js @@ -0,0 +1,15 @@ +export const SVGTwitter = ({ className }) => { + return + + +} diff --git a/themes/starter/config.js b/themes/starter/config.js new file mode 100644 index 00000000..e175a401 --- /dev/null +++ b/themes/starter/config.js @@ -0,0 +1,302 @@ +/** + * 另一个落地页主题 + */ +const CONFIG = { + + // 默认只展示Logo文字,如果设置了logo图片,会在文字左侧显示图标 + STARTER_LOGO: '', // 普通logo图片 示例:/images/starter/logo/logo.svg + STARTER_LOGO_WHITE: '', // 透明底浅色logo 示例: /images/starter/logo/logo-white.svg + + // MENU , 菜单部分不在此处配置,请在Notion数据库中添加MENU + + // 英雄区块导航 + STARTER_HERO_TITLE_1: '开源且免费的基于 Notion 笔记的网站构建工具', // 英雄区文字 + STARTER_HERO_TITLE_2: '通过笔记无感知地建站、成倍放大您的价值', // 英雄区文字 + STARTER_HERO_TITLE_3: '使用最新技术构建', // 英雄区文字 + STARTER_HERO_BUTTON_1_TEXT: 'Start Now', // 英雄区按钮 + STARTER_HERO_BUTTON_1_URL: 'https://docs.tangly1024.com/article/vercel-deploy-notion-next', // 英雄区按钮 + STARTER_HERO_BUTTON_2_TEXT: 'Star on Github', // 英雄区按钮 + STARTER_HERO_BUTTON_2_URL: 'https://github.com/tangly1024/NotionNext', // 英雄区按钮 + STARTER_HERO_PREVIEW_IMAGE: '/images/starter/hero/hero-image.webp', // 产品预览图 ,默认读取public目录下图片 + + // 顶部右侧导航暗流 + STARTER_NAV_BUTTON_1_TEXT: 'Sign In', + STARTER_NAV_BUTTON_1_URL: '/signin', + + STARTER_NAV_BUTTON_2_TEXT: 'Sign Up', + STARTER_NAV_BUTTON_2_URL: '/signup', + + // 特性区块 + STARTER_FEATURE_TITLE: '特性', // 特性 + STARTER_FEATURE_TEXT_1: 'NotionNext的主要特性', // 特性 + STARTER_FEATURE_TEXT_2: 'NotionNext的愿景是帮助您简单、无感知地稳定地搭建自己的网站,放大品牌的价值。 ', // 特性 + + STARTER_FEATURE_1_TITLE_1: '免费且开源', // 特性1 + STARTER_FEATURE_1_TEXT_1: '项目源码在Github上完全开放共享,遵循MIT协议', // 特性1 + STARTER_FEATURE_1_BUTTON_TEXT: '了解更多', // 特性1 + STARTER_FEATURE_1_BUTTON_URL: 'https://github.com/tangly1024/NotionNext', // 特性1 + + STARTER_FEATURE_2_TITLE_1: '多种主题定制', // 特性2 + STARTER_FEATURE_2_TEXT_1: '数十种主题,适用于不同场景,总有一款适合你', // 特性2 + STARTER_FEATURE_2_BUTTON_TEXT: '了解更多', // 特性2 + STARTER_FEATURE_2_BUTTON_URL: 'https://docs.tangly1024.com/article/notion-next-themes', // 特性2 + + STARTER_FEATURE_3_TITLE_1: '优秀的性能', // 特性3 + STARTER_FEATURE_3_TEXT_1: '基于NextJS开发,更快的相应速度,更好的SEO', // 特性3 + STARTER_FEATURE_3_BUTTON_TEXT: '了解更多', // 特性3 + STARTER_FEATURE_3_BUTTON_URL: 'https://docs.tangly1024.com/article/next-js', // 特性3 + + STARTER_FEATURE_4_TITLE_1: '便捷的写作体验', // 特性4 + STARTER_FEATURE_4_TEXT_1: '只需在Notion笔记中编修,自动同步到网站', // 特性4 + STARTER_FEATURE_4_BUTTON_TEXT: '了解更多', // 特性4 + STARTER_FEATURE_4_BUTTON_URL: 'https://docs.tangly1024.com/about', // 特性4 + + // 首页ABOUT区块 + STARTER_ABOUT_TITLE: '一套轻量实用的建站解决方案', + STARTER_ABOUT_TEXT: 'NotionNext的愿景是帮助非技术人员的小白,最低成本、最快速地搭建自己的网站,帮助您将自己的产品与故事高效地传达给世界。

    功能强大的Notion笔记,简单快速的Vercel托管平台,组成一个简单的网站', + STARTER_ABOUT_BUTTON_TEXT: '了解更多', + STARTER_ABOUT_BUTTON_URL: 'https://docs.tangly1024.com/about', + STARTER_ABOUT_IMAGE_1: '/images/starter/about/about-image-01.jpg', + STARTER_ABOUT_IMAGE_2: '/images/starter/about/about-image-02.jpg', + STARTER_ABOUT_TIPS_1: '7000+', + STARTER_ABOUT_TIPS_2: '博客站点', + STARTER_ABOUT_TIPS_3: '正在线上运行', + + // 首页价格区块 + STARTER_PRICING_TITLE: '价格表', + STARTER_PRICING_TEXT_1: '很棒的定价计划', + STARTER_PRICING_TEXT_2: '我们制定了灵活的付费模式,您可以按需选择。(NotionNext免费开源,这里仅演示产品订阅付费功能,请勿下单购买!)', + + STARTER_PRICING_1_TITLE: '入门版', + STARTER_PRICING_1_PRICE: '19.9', + STARTER_PRICING_1_PRICE_CURRENCY: '$', + STARTER_PRICING_1_PRICE_PERIOD: '每月', + STARTER_PRICING_1_HEADER: '功能点', + STARTER_PRICING_1_FEATURES: '所有的主题,免费更新,帮助手册', // 英文逗号隔开 + STARTER_PRICING_1_BUTTON_TEXT: '立即购买', + STARTER_PRICING_1_BUTTON_URL: 'https://tangly1024.lemonsqueezy.com/checkout/buy/c1a38a65-362e-44c5-8065-733fee39eb54', + + STARTER_PRICING_2_TAG: '推荐', + STARTER_PRICING_2_TITLE: '基础版', + STARTER_PRICING_2_PRICE: '39.9', + STARTER_PRICING_2_PRICE_CURRENCY: '$', + STARTER_PRICING_2_PRICE_PERIOD: '每月', + STARTER_PRICING_2_HEADER: '功能点', + STARTER_PRICING_2_FEATURES: '包含入门版,项目源码,内部社群,技术咨询,SEO优化', // 英文逗号隔开 + STARTER_PRICING_2_BUTTON_TEXT: '立即购买', + STARTER_PRICING_2_BUTTON_URL: 'https://tangly1024.lemonsqueezy.com/checkout/buy/590ad70a-c3b7-4caf-94ec-9ca27bde06d4', + + STARTER_PRICING_3_TITLE: '高级版', + STARTER_PRICING_3_PRICE: '59.9', + STARTER_PRICING_3_PRICE_CURRENCY: '$', + STARTER_PRICING_3_PRICE_PERIOD: '每月', + STARTER_PRICING_3_HEADER: '功能点', + STARTER_PRICING_3_FEATURES: '包含基础版,功能定制开发', // 英文逗号隔开 + STARTER_PRICING_3_BUTTON_TEXT: '立即购买', + STARTER_PRICING_3_BUTTON_URL: 'https://tangly1024.lemonsqueezy.com/checkout/buy/df924d66-09dc-42a4-a632-a6b0c5cc4f28', + + STARTER_TESTIMONIALS_TITLE: '用户反馈', + STARTER_TESTIMONIALS_TEXT_1: '我们的用户怎么说', + STARTER_TESTIMONIALS_TEXT_2: '数千位站长选择用NotionNext搭建他们的网站,通过帮助手册、交流社群以及技术咨询,大家成功上线了自己的网站', + STARTER_TESTIMONIALS_STAR_ICON: '/images/starter/testimonials/icon-star.svg', // 评分图标 + + // 这里不支持CONFIG和环境变量,需要一一修改此处代码。 + STARTER_TESTIMONIALS_ITEMS: [ + { + STARTER_TESTIMONIALS_ITEM_TEXT: '感谢大佬的方法。之前尝试过Super、Potion等国外的第三方平台,实现效果一般,个性化程度远不如这个方法,已经用起来了! ', + STARTER_TESTIMONIALS_ITEM_AVATAR: 'https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F22de3fcb-d90d-4271-bc01-f815f476122b%2F4FE0A0C0-E487-4C74-BF8E-6F01A27461B8-14186-000008094BC289A6.jpg?table=collection&id=a320a2cc-6ebe-4a8d-95cc-ea94e63bced9&width=200', + STARTER_TESTIMONIALS_ITEM_NICKNAME: 'Ryan_G', + STARTER_TESTIMONIALS_ITEM_DESCRIPTION: 'Ryan`Log 站长', + STARTER_TESTIMONIALS_ITEM_URL: 'https://blog.gaoran.xyz/' + }, + { + STARTER_TESTIMONIALS_ITEM_TEXT: '很喜欢这个主题,本代码小白用三天台风假期搭建出来了,还根据大佬的教程弄了自定义域名,十分感谢,已请喝咖啡~', + STARTER_TESTIMONIALS_ITEM_AVATAR: 'https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F0d33d169-f932-41ff-ac6b-88a923c08e02%2F%25E5%25A4%25B4%25E5%2583%258F.jfif?table=collection&id=7787658d-d5c0-4f34-8e32-60c523dfaba3&width=400', + STARTER_TESTIMONIALS_ITEM_NICKNAME: 'Asenkits', + STARTER_TESTIMONIALS_ITEM_DESCRIPTION: '阿森的百宝袋 站长', + STARTER_TESTIMONIALS_ITEM_URL: 'https://asenkits.top/' + }, + { + STARTER_TESTIMONIALS_ITEM_TEXT: '呜呜呜,经过一个下午的努力,终于把博客部署好啦,非常感谢Tangly1024大佬的框架和教程,这是我有生之年用过的最好用的博客框架┭┮﹏┭┮。从今之后,我就可以在自己的博客里bb啦,( •̀ ω •́ )y ', + STARTER_TESTIMONIALS_ITEM_AVATAR: 'https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F6c096b44-beb9-48ee-8f92-1efdde47f3a3%2F338962f1-d352-49c7-9a1b-746e35a7005c%2Fhf.png?table=block&id=ce5a48a9-d77a-4843-a3d9-a78cd4f794ce&spaceId=6c096b44-beb9-48ee-8f92-1efdde47f3a3&width=100&userId=27074aef-7216-41ed-baef-d9b53addd870&cache=v2', + STARTER_TESTIMONIALS_ITEM_NICKNAME: 'DWIND', + STARTER_TESTIMONIALS_ITEM_DESCRIPTION: '且听风吟 站长', + STARTER_TESTIMONIALS_ITEM_URL: 'https://www.dwind.top/' + }, + { + STARTER_TESTIMONIALS_ITEM_TEXT: '感谢提供这么好的项目哈哈 之前一直不知道怎么部署(别的项目好难好复杂)这个相对非常简单 新手非常友好哦', + STARTER_TESTIMONIALS_ITEM_AVATAR: 'https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fd52f6766-3e32-4c3d-8529-46e1f214360f%2Ffavicon.svg?table=collection&id=7d76aad5-a2c4-4d9a-887c-c7913fae4eed&width=400', + STARTER_TESTIMONIALS_ITEM_NICKNAME: '迪升disheng ', + STARTER_TESTIMONIALS_ITEM_DESCRIPTION: 'AI资源分享 Blog', + STARTER_TESTIMONIALS_ITEM_URL: 'https://blog.disheng.org/' + }, + { + STARTER_TESTIMONIALS_ITEM_TEXT: '灰常感谢大佬的博客项目,能将博客和notion结合起来,这一直是我挺期待的博客模式。', + STARTER_TESTIMONIALS_ITEM_AVATAR: 'https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fafb21381-f51b-4fd0-9998-800dbeb64dbe%2Favatar.png?table=block&id=195935d2-0d8d-49fc-bd81-1db42ee50840&spaceId=6c096b44-beb9-48ee-8f92-1efdde47f3a3&width=100&userId=27074aef-7216-41ed-baef-d9b53addd870&cache=v2', + STARTER_TESTIMONIALS_ITEM_NICKNAME: 'AnJhon', + STARTER_TESTIMONIALS_ITEM_DESCRIPTION: 'Anjhon`s Blog 站长', + STARTER_TESTIMONIALS_ITEM_URL: 'https://www.anjhon.top' + }, + { + STARTER_TESTIMONIALS_ITEM_TEXT: '用好久了,太感谢了', + STARTER_TESTIMONIALS_ITEM_AVATAR: 'https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fe4f391d7-7d65-4c05-a82c-c6e2c40f06e4%2Fa2a7641a26b367608c6ef28ce9b7e983_(2).png?table=block&id=a386eb0e-4c07-4b18-9ece-bba4e79ce21c&spaceId=6c096b44-beb9-48ee-8f92-1efdde47f3a3&width=100&userId=27074aef-7216-41ed-baef-d9b53addd870&cache=v2', + STARTER_TESTIMONIALS_ITEM_NICKNAME: 'LUCEN', + STARTER_TESTIMONIALS_ITEM_DESCRIPTION: 'LUCEN考验辅导 站长', + STARTER_TESTIMONIALS_ITEM_URL: 'https://www.lucenczz.top/' + } + ], + + // FAQ模块 + STARTER_FAQ_TITLE: '常见问题解答', + STARTER_FAQ_TEXT_1: '有任何问题吗?请看这里', + STARTER_FAQ_TEXT_2: '我们收集了常见的用户疑问', + + STARTER_FAQ_1_QUESTION: 'NotionNext有帮助文档吗?', + STARTER_FAQ_1_ANSWER: 'NotionNext提供了帮助文档,操作演示视频,以及交流社群来协助您完成网站的搭建部署', + + STARTER_FAQ_2_QUESTION: '部署后要如何编写文章?', + STARTER_FAQ_2_ANSWER: '您可以在Notion中之间添加或修改类型为Post的页面,内容将被实时同步在站点中,详情参考《帮助文档》', + + STARTER_FAQ_3_QUESTION: '站点部署失败,更新失败?', + STARTER_FAQ_3_ANSWER: '通常是配置修改错误导致,请检查配置或者重试操作步骤,或者通过Vercel后台的Deployments中找到错误日志,并向网友求助', + + STARTER_FAQ_4_QUESTION: '文章没有实时同步?', + STARTER_FAQ_4_ANSWER: '先检查Notion_Page_ID是否正确配置,其次由于博客的每个页面都有独立缓存,刷新网页后即可解决', + + STARTER_TEAM_TITLE: '团队成员', + STARTER_TEAM_TEXT_1: '我们的开发者团队', + STARTER_TEAM_TEXT_2: 'NotionNext 由众多开源技术爱好者们共同合作完成,感谢每一位贡献者', + + // 这里不支持CONFIG和环境变量,需要一一修改此处代码。 + STARTER_TEAM_ITEMS: [ + { + STARTER_TEAM_ITEM_AVATAR: 'https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fa06c61bb-980e-4180-bc18-c15f92c78bb4%2Ftangly1024.jpg?table=collection&id=8e7acf17-de09-4fa1-abde-b5b80ad4a813&t=8e7acf17-de09-4fa1-abde-b5b80ad4a813&width=100&cache=v2', + STARTER_TEAM_ITEM_NICKNAME: 'Tangly', + STARTER_TEAM_ITEM_DESCRIPTION: 'Developer' + }, + { + STARTER_TEAM_ITEM_AVATAR: '/images/starter/team/team-01.png', + STARTER_TEAM_ITEM_NICKNAME: 'Melissa Tatcher', + STARTER_TEAM_ITEM_DESCRIPTION: 'Marketing Expert' + }, + { + STARTER_TEAM_ITEM_AVATAR: '/images/starter/team/team-02.png', + STARTER_TEAM_ITEM_NICKNAME: 'Stuard Ferrel', + STARTER_TEAM_ITEM_DESCRIPTION: 'Digital Marketer' + }, + { + STARTER_TEAM_ITEM_AVATAR: '/images/starter/team/team-03.png', + STARTER_TEAM_ITEM_NICKNAME: 'Eva Hudson', + STARTER_TEAM_ITEM_DESCRIPTION: 'Creative Designer' + } + ], + + // 博客文章区块 + STARTER_BLOG_TITLE: '我们的博客', + STARTER_BLOG_COUNT: 3, // 首页博文区块默认展示前3篇文章 + STARTER_BLOG_TEXT_1: '最近的新闻', + STARTER_BLOG_TEXT_2: '这里会发布一些关于NotionNext的最新动态,包括新的动向、新的未来计划,以及新功能的特性', + + // 联系模块 + STARTER_CONTACT_TITLE: '联系我们', + STARTER_CONTACT_TEXT: '告诉我们您遇到的问题', + STARTER_CONTACT_LOCATION_TITLE: '我们的位置', + STARTER_CONTACT_LOCATION_TEXT: '中国,福建', + STARTER_CONTACT_EMAIL_TITLE: '我们如何帮助您?', + STARTER_CONTACT_EMAIL_TEXT: 'mail@tangly1024.com', + + // 嵌入外部表单 + STARTER_CONTACT_MSG_EXTERNAL_URL: 'https://noteforms.com/forms/yfctc7', // 基于NoteForm创建,将留言数据存在Notion中 + // 自定义留言表单 + // STARTER_CONTACT_MSG_TITLE: '向我们留言', + // STARTER_CONTACT_MSG_NAME: '姓名', + // STARTER_CONTACT_MSG_EMAIL: '邮箱地址', + // STARTER_CONTACT_MSG_PHONE: '联系电话', + // STARTER_CONTACT_MSG_TEXT: '消息内容', + // STARTER_CONTACT_MSG_SEND: '发送消息', + // STARTER_CONTACT_MSG_THANKS: '感谢您的留言', + + // 合作伙伴的图标 + STARTER_BRANDS: [ + { + IMAGE: '/images/starter/brands/graygrids.svg', + IMAGE_WHITE: '/images/starter/brands/graygrids-white.svg', + URL: 'https://graygrids.com/', + TITLE: 'graygrids' + }, + { + IMAGE: '/images/starter/brands/lineicons.svg', + IMAGE_WHITE: '/images/starter/brands/lineicons-white.svg', + URL: 'https://lineicons.com/', + TITLE: 'lineicons' + }, + { + IMAGE: '/images/starter/brands/uideck.svg', + IMAGE_WHITE: '/images/starter/brands/uideck-white.svg', + URL: 'https://uideck.com/', + TITLE: 'uideck' + }, + { + IMAGE: '/images/starter/brands/ayroui.svg', + IMAGE_WHITE: '/images/starter/brands/ayroui-white.svg', + URL: 'https://ayroui.com/', + TITLE: 'ayroui' + }, + { + IMAGE: '/images/starter/brands/tailgrids.svg', + IMAGE_WHITE: '/images/starter/brands/tailgrids-white.svg', + URL: '"https://tailgrids.com/', + TITLE: 'tailgrids' + } + ], + + STARTER_FOOTER_SLOGAN: '我们通过技术为品牌和公司创造数字体验。', + + // 页脚三列菜单组 + STARTER_FOOTER_LINK_GROUP: [ + { + TITLE: '关于我们', + LINK_GROUP: [ + { TITLE: '官方主页', URL: '/#home' }, + { TITLE: '操作文档', URL: 'https://docs.tangly1024.com/about' }, + { TITLE: '帮助支持', URL: 'https://docs.tangly1024.com/article/how-to-question' }, + { TITLE: '合作申请', URL: 'https://docs.tangly1024.com/my-service' } + ] + }, + { + TITLE: '功能特性', + LINK_GROUP: [ + { TITLE: '部署指南', URL: 'https://docs.tangly1024.com/article/vercel-deploy-notion-next' }, + { TITLE: '升级指南', URL: 'https://docs.tangly1024.com/article/how-to-update-notionnext' }, + { TITLE: '最新版本', URL: 'https://docs.tangly1024.com/article/latest' } + ] + }, + { + TITLE: 'Notion写作', + LINK_GROUP: [ + { TITLE: 'Notion开始写作', URL: 'https://docs.tangly1024.com/article/start-to-write' }, + { TITLE: '快捷键提升效率', URL: 'https://docs.tangly1024.com/article/notion-short-key' }, + { TITLE: '中国大陆使用Notion', URL: 'https://docs.tangly1024.com/article/notion-faster' } + ] + } + ], + + STARTER_FOOTER_BLOG_LATEST_TITLE: '最新文章', + + STARTER_FOOTER_PRIVACY_POLICY_TEXT: '隐私政策', + STARTER_FOOTER_PRIVACY_POLICY_URL: '/privacy-policy', + + STARTER_FOOTER_PRIVACY_LEGAL_NOTICE_TEXT: '法律声明', + STARTER_FOOTER_PRIVACY_LEGAL_NOTICE_URL: '/legacy-notice', + + STARTER_FOOTER_PRIVACY_TERMS_OF_SERVICE_TEXT: '服务协议', + STARTER_FOOTER_PRIVACY_TERMS_OF_SERVICE_URL: '/terms-of-use', + + // 404页面的提示语 + STARTER_404_TITLE: '我们似乎找不到您要找的页面。', + STARTER_404_TEXT: '抱歉!您要查找的页面不存在。可能已经移动或删除。', + STARTER_404_BACK: '回到主页', + + STARTER_NEWSLETTER: process.env.NEXT_PUBLIC_THEME_STARTER_NEWSLETTER || false // 是否开启邮件订阅 请先配置mailchimp功能 https://docs.tangly1024.com/article/notion-next-mailchimp +} +export default CONFIG diff --git a/themes/starter/index.js b/themes/starter/index.js new file mode 100644 index 00000000..bb8e7f6a --- /dev/null +++ b/themes/starter/index.js @@ -0,0 +1,249 @@ +/* eslint-disable react/no-unescaped-entities */ +/* eslint-disable @next/next/no-img-element */ + +'use client' + +/** + * 这是一个空白主题,方便您用作创建新主题时的模板,从而开发出您自己喜欢的主题 + * 1. 禁用了代码质量检查功能,提高了代码的宽容度;您可以使用标准的html写法 + * 2. 内容大部分是在此文件中写死,notion数据从props参数中传进来 + * 3. 您可在此网站找到更多喜欢的组件 https://www.tailwind-kit.com/ + */ +import { useRouter } from 'next/router' +import { isBrowser, loadExternalResource } from '@/lib/utils' +import { siteConfig } from '@/lib/config' +import CONFIG from './config' +import NotionPage from '@/components/NotionPage' +import Loading from '@/components/Loading' +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 { MadeWithButton } from './components/MadeWithButton' +import { SVG404 } from './components/svg/SVG404' +import { Banner } from './components/Banner' +import { SignInForm } from './components/SignInForm' +import { SignUpForm } from './components/SignUpForm' +import Link from 'next/link' + +/** + * 一些外部js + */ +const loadExternal = async () => { + await loadExternalResource('https://cdnjs.cloudflare.com/ajax/libs/wow/1.1.2/wow.min.js', 'js'); + // 配合animatecss 实现延时滚动动画,和AOS动画相似 + const WOW = window.WOW; + console.log('加载WOW动画', WOW) + if (WOW) { + new WOW().init(); + } +}; + +/** + * 布局框架 + * Landing-2 主题用作产品落地页展示 + * 结合Stripe或者lemonsqueezy插件可以成为saas支付订阅 + * https://play-tailwind.tailgrids.com/ + * @param {*} props + * @returns + */ +const LayoutBase = (props) => { + const { children } = props + + useEffect(() => { + loadExternal() + }, []) + + return
    + +} + +export { Style } diff --git a/themes/theme.js b/themes/theme.js index da9e1b66..a516ab66 100644 --- a/themes/theme.js +++ b/themes/theme.js @@ -1,24 +1,22 @@ -import cookie from 'react-cookies' -import BLOG from '@/blog.config' +import BLOG, { LAYOUT_MAPPINGS } from '@/blog.config' import { getQueryParam, getQueryVariable, isBrowser } from '../lib/utils' import dynamic from 'next/dynamic' import getConfig from 'next/config' import * as ThemeComponents from '@theme-components' -// 所有主题在next.config.js中扫描 + +// 在next.config.js中扫描所有主题 export const { THEMES = [] } = getConfig().publicRuntimeConfig /** * 加载全局布局 - * 如果是 * @param {*} themeQuery * @returns */ export const getGlobalLayoutByTheme = (themeQuery) => { - const layout = getLayoutNameByPath(-1) if (themeQuery !== BLOG.THEME) { - return dynamic(() => import(`@/themes/${themeQuery}`).then(m => m[layout]), { ssr: true }) + return dynamic(() => import(`@/themes/${themeQuery}`).then(m => m[getLayoutNameByPath(-1)]), { ssr: true }) } else { - return ThemeComponents[layout] + return ThemeComponents[getLayoutNameByPath('-1')] } } @@ -30,20 +28,32 @@ export const getGlobalLayoutByTheme = (themeQuery) => { */ export const getLayoutByTheme = ({ router, theme }) => { const themeQuery = getQueryParam(router.asPath, 'theme') || theme - const layoutName = getLayoutNameByPath(router.pathname) - if (themeQuery !== BLOG.THEME) { return dynamic(() => import(`@/themes/${themeQuery}`).then(m => { setTimeout(() => { checkThemeDOM() }, 500); - return m[layoutName] + return m[getLayoutNameByPath(router.pathname, router.asPath)] }), { ssr: true }) } else { setTimeout(() => { checkThemeDOM() }, 100); - return ThemeComponents[layoutName] + return ThemeComponents[getLayoutNameByPath(router.pathname, router.asPath)] + } +} + +/** + * 根据路径 获取对应的layout + * @param {*} path + * @returns + */ +const getLayoutNameByPath = (path) => { + if (LAYOUT_MAPPINGS[path]) { + return LAYOUT_MAPPINGS[path]; + } else { + // 没有特殊处理的路径返回默认layout名称 + return 'LayoutSlug'; } } @@ -63,40 +73,6 @@ const checkThemeDOM = () => { } } -/** - * 根据路径 获取对应的layout - * @param {*} path - * @returns - */ -export const getLayoutNameByPath = (path) => { - switch (path) { - case -1: - return 'LayoutBase' - case '/': - return 'LayoutIndex' - case '/archive': - return 'LayoutArchive' - case '/page/[page]': - case '/category/[category]': - case '/category/[category]/page/[page]': - case '/tag/[tag]': - case '/tag/[tag]/page/[page]': - return 'LayoutPostList' - case '/search': - case '/search/[keyword]': - case '/search/[keyword]/page/[page]': - return 'LayoutSearch' - case '/404': - return 'Layout404' - case '/tag': - return 'LayoutTagIndex' - case '/category': - return 'LayoutCategoryIndex' - default: - return 'LayoutSlug' - } -} - /** * 初始化主题 , 优先级 query > cookies > systemPrefer * @param isDarkMode @@ -108,7 +84,7 @@ export const initDarkMode = (updateDarkMode) => { let newDarkMode = isPreferDark() // 查看cookie中是否用户强制设置深色模式 - const cookieDarkMode = loadDarkModeFromCookies() + const cookieDarkMode = loadDarkModeFromLocalStorage() if (cookieDarkMode) { newDarkMode = JSON.parse(cookieDarkMode) } @@ -120,7 +96,7 @@ export const initDarkMode = (updateDarkMode) => { } updateDarkMode(newDarkMode) - saveDarkModeToCookies(newDarkMode) + saveDarkModeToLocalStorage(newDarkMode) document.getElementsByTagName('html')[0].setAttribute('class', newDarkMode ? 'dark' : 'light') } @@ -145,30 +121,14 @@ export function isPreferDark() { * 读取深色模式 * @returns {*} */ -export const loadDarkModeFromCookies = () => { - return cookie.load('darkMode') +export const loadDarkModeFromLocalStorage = () => { + return localStorage.getItem('darkMode') } /** * 保存深色模式 * @param newTheme */ -export const saveDarkModeToCookies = (newTheme) => { - cookie.save('darkMode', newTheme, { path: '/' }) -} - -/** - * 读取默认主题 - * @returns {*} - */ -export const loadThemeFromCookies = () => { - return cookie.load('theme') -} - -/** - * 保存默认主题 - * @param newTheme - */ -export const saveThemeToCookies = (newTheme) => { - cookie.save('theme', newTheme, { path: '/' }) +export const saveDarkModeToLocalStorage = (newTheme) => { + localStorage.setItem('darkMode', newTheme) } diff --git a/yarn.lock b/yarn.lock index 1d1a0511..bf13ffd0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2070,11 +2070,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -cookie@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" - integrity sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw== - copy-to-clipboard@^3.3.1: version "3.3.3" resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" @@ -4188,14 +4183,6 @@ quick-lru@^5.1.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== -react-cookies@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/react-cookies/-/react-cookies-0.1.1.tgz#2a35807e04f5a13f58ccd1a9fb66574506873c88" - integrity sha512-PP75kJ4vtoHuuTdq0TAD3RmlAv7vuDQh9fkC4oDlhntgs9vX1DmREomO0Y1mcQKR9nMZ6/zxoflaMJ3MAmF5KQ== - dependencies: - cookie "^0.3.1" - object-assign "^4.1.1" - react-dom@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"