diff --git a/blog.config.js b/blog.config.js index 6c22adf3..511376d0 100644 --- a/blog.config.js +++ b/blog.config.js @@ -14,6 +14,7 @@ const BLOG = { BEI_AN: process.env.NEXT_PUBLIC_BEI_AN || '', // 备案号 闽ICP备XXXXXXX APPEARANCE: 'auto', // ['light', 'dark', 'auto'], FONT: 'font-serif tracking-wider subpixel-antialiased', // 文章字体 ['font-sans', 'font-serif', 'font-mono'] @see https://www.tailwindcss.cn/docs/font-family + FONT_AWESOME_PATH: 'https://cdn.bootcdn.net/ajax/libs/font-awesome/5.15.4/css/all.min.css', // 图标库CDN ,国内推荐BootCDN,国外推荐 CloudFlare https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css BACKGROUND_LIGHT: '#eeeeee', // use hex value, don't forget '#' e.g #fffefc BACKGROUND_DARK: '#111827', // use hex value, don't forget '#' PATH: '', // leave this empty unless you want to deploy in a folder @@ -24,6 +25,9 @@ const BLOG = { POSTS_PER_PAGE: 6, // post counts per page POSTS_SORT_BY: 'notion', // 排序方式 'date'按时间,'notion'由notion控制 + PREVIEW_CATEGORY_COUNT: 16, // 首页最多展示的分类数量,0为不限制 + PREVIEW_TAG_COUNT: 16, // 首页最多展示的标签数量,0为不限制 + // 社交链接,不需要可留空白,例如 CONTACT_WEIBO:'' CONTACT_EMAIL: 'tlyong1992@hotmail.com', CONTACT_WEIBO: '', @@ -36,13 +40,14 @@ const BLOG = { COMMENT_CUSDIS_HOST: process.env.NEXT_PUBLIC_COMMENT_CUSDIS_HOST || 'https://cusdis.com', // data-host, change this if you're using self-hosted version COMMENT_CUSDIS_SCRIPT_SRC: process.env.NEXT_PUBLIC_COMMENT_CUSDIS_SCRIPT_SRC || 'https://cusdis.com/js/cusdis.es.js', // change this if you're using self-hosted version - COMMENT_UTTERRANCES_REPO: process.env.NEXT_PUBLIC_COMMENT_UTTERRANCES_REPO || '', // e.g 'tangly1024/NotionNext' see https://utteranc.es/ + COMMENT_UTTERRANCES_REPO: process.env.NEXT_PUBLIC_COMMENT_UTTERRANCES_REPO || '', // 你的代码仓库名, 例如我是 'tangly1024/NotionNext'; 更多文档参考 https://utteranc.es/ - COMMENT_GITALK_REPO: process.env.NEXT_PUBLIC_COMMENT_GITALK_REPO || '', // e.g NotionNext see https://gitalk.github.io/ - COMMENT_GITALK_OWNER: process.env.NEXT_PUBLIC_COMMENT_GITALK_OWNER || '', // e.g tangly1024 - COMMENT_GITALK_ADMIN: process.env.NEXT_PUBLIC_COMMENT_GITALK_ADMIN || '', // e.g 'tangly1024' - COMMENT_GITALK_CLIENT_ID: process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_ID || '', // e.g 20位ID - COMMENT_GITALK_CLIENT_SECRET: process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_SECRET || '', // e.g 40位ID + // gitalk评论插件 更多参考 https://gitalk.github.io/ + COMMENT_GITALK_REPO: process.env.NEXT_PUBLIC_COMMENT_GITALK_REPO || '', // 你的Github仓库名,例如 'NotionNext' + COMMENT_GITALK_OWNER: process.env.NEXT_PUBLIC_COMMENT_GITALK_OWNER || '', // 你的用户名 e.g tangly1024 + COMMENT_GITALK_ADMIN: process.env.NEXT_PUBLIC_COMMENT_GITALK_ADMIN || '', // 管理员用户名、一般是自己 e.g 'tangly1024' + COMMENT_GITALK_CLIENT_ID: process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_ID || '', // e.g 20位ID , 在gitalk后台获取 + COMMENT_GITALK_CLIENT_SECRET: process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_SECRET || '', // e.g 40位ID, 在gitalk后台获取 COMMENT_GITALK_DISTRACTION_FREE_MODE: false, COMMENT_GITTER_ROOM: process.env.NEXT_PUBLIC_COMMENT_GITTER_ROOM || '', // gitter聊天室 see https://gitter.im/ 不需要则留空 diff --git a/components/Comment.js b/components/Comment.js index 772a3776..075b6ba6 100644 --- a/components/Comment.js +++ b/components/Comment.js @@ -25,6 +25,10 @@ const Comment = ({ frontMatter }) => { return (
+ {BLOG.COMMENT_UTTERRANCES_REPO && (
+ +
)} + {BLOG.COMMENT_CUSDIS_APP_ID && (
{ />
)} - {BLOG.COMMENT_UTTERRANCES_REPO && (
- -
)} - {BLOG.COMMENT_GITALK_CLIENT_ID && (
{
{children.map((item, index) => { - return
- {item} + return
+ {currentTab === index && item}
})}
diff --git a/lib/notion/getAllCategories.js b/lib/notion/getAllCategories.js index 8e94e8ba..61aab49c 100644 --- a/lib/notion/getAllCategories.js +++ b/lib/notion/getAllCategories.js @@ -3,11 +3,11 @@ * @param allPosts * @returns {Promise<{}|*[]>} */ -export async function getAllCategories (allPosts) { - if (!allPosts) { +export async function getAllCategories ({ allPosts, categoryOptions, sliceCount = 0 }) { + if (!allPosts || !categoryOptions) { return [] } - + // 计数 let categories = allPosts.map(p => p.category) categories = [...categories.flat()] const categoryObj = {} @@ -18,5 +18,19 @@ export async function getAllCategories (allPosts) { categoryObj[category] = 1 } }) - return categoryObj + const list = [] + categoryOptions.forEach(c => { + const count = categoryObj[c.value] + if (count) { + list.push({ id: c.id, name: c.value, color: c.color, count }) + } + }) + + // 按照数量排序 + // list.sort((a, b) => b.count - a.count) + if (sliceCount && sliceCount > 0) { + return list.slice(0, sliceCount) + } else { + return list + } } diff --git a/lib/notion/getAllTags.js b/lib/notion/getAllTags.js index f1a9681b..3ac01f54 100644 --- a/lib/notion/getAllTags.js +++ b/lib/notion/getAllTags.js @@ -6,15 +6,13 @@ * @param tagOptions tags的下拉选项 * @returns {Promise<{}|*[]>} */ -export async function getAllTags ({ allPosts, sliceCount = 16, tagOptions }) { - if (!allPosts) { +export async function getAllTags ({ allPosts, sliceCount = 0, tagOptions }) { + if (!allPosts || !tagOptions) { return [] } - + // 计数 let tags = allPosts.map(p => p.tags) tags = [...tags.flat()] - - // 标签计数 const tagObj = {} tags.forEach(tag => { if (tag in tagObj) { @@ -23,13 +21,16 @@ export async function getAllTags ({ allPosts, sliceCount = 16, tagOptions }) { tagObj[tag] = 1 } }) - - // 按照标签数量排序 - const list = Object.keys(tagObj).map((tag) => { - const color = tagOptions.find(option => option.value === tag)?.color || 'gray' - return { name: tag, count: tagObj[tag], color } + const list = [] + tagOptions.forEach(c => { + const count = tagObj[c.value] + if (count) { + list.push({ id: c.id, name: c.value, color: c.color, count }) + } }) - list.sort((a, b) => b.count - a.count) + + // 按照数量排序 + // list.sort((a, b) => b.count - a.count) if (sliceCount && sliceCount > 0) { return list.slice(0, sliceCount) } else { diff --git a/lib/notion/getNotionData.js b/lib/notion/getNotionData.js index 4873cf9c..62691554 100644 --- a/lib/notion/getNotionData.js +++ b/lib/notion/getNotionData.js @@ -11,6 +11,7 @@ import { getAllTags } from './getAllTags' * @param {*} pageId * @param {*} from * @param latestPostCount 截取最新文章数量 + * @param categoryCount * @param tagsCount 截取标签数量 * @param pageType 过滤的文章类型,数组格式 ['Page','Post'] * @returns { @@ -27,17 +28,29 @@ export async function getGlobalNotionData ({ pageId = BLOG.NOTION_PAGE_ID, from, latestPostCount = 5, - tagsCount = 16, + categoryCount = BLOG.PREVIEW_CATEGORY_COUNT, + tagsCount = BLOG.PREVIEW_TAG_COUNT, pageType = ['Post'] }) { const notionPageData = await getNotionPageData({ pageId, from }) const tagOptions = notionPageData.tagOptions + const categoryOptions = notionPageData.categoryOptions const allPosts = await getAllPosts({ notionPageData, from, pageType }) const postCount = await getAllPostCount({ notionPageData, from }) const customNav = await getCustomNav({ notionPageData }) - const categories = await getAllCategories(allPosts) + const categories = await getAllCategories({ allPosts, categoryOptions, sliceCount: categoryCount }) const tags = await getAllTags({ allPosts, tagOptions, sliceCount: tagsCount }) - // 深拷贝 + const latestPosts = await getLatestPosts({ notionPageData, from, latestPostCount }) + return { allPosts, latestPosts, categories, postCount, customNav, tags } +} + +/** + * 获取最新文章 + * @param {*}} param0 + * @returns + */ +async function getLatestPosts ({ notionPageData, from, latestPostCount }) { + const allPosts = await getAllPosts({ notionPageData, from, pageType: ['Post'] }) let latestPosts = Object.create(allPosts) // 时间排序 latestPosts.sort((a, b) => { @@ -48,14 +61,7 @@ export async function getGlobalNotionData ({ // 只取前五 latestPosts = latestPosts.slice(0, latestPostCount) - return { - allPosts, - latestPosts, - categories, - postCount, - customNav, - tags - } + return latestPosts } /** @@ -103,10 +109,17 @@ async function getCustomNav ({ notionPageData }) { * @returns {undefined} */ function getTagOptions (schema) { + if (!schema) return {} const tagSchema = Object.values(schema).find(e => e.name === 'tags') return tagSchema?.options || {} } +function getCategoryOptions (schema) { + if (!schema) return {} + const categorySchema = Object.values(schema).find(e => e.name === 'category') + return categorySchema?.options || {} +} + /** * 调用NotionAPI获取Page数据 * @returns {Promise} @@ -116,7 +129,6 @@ async function getPageRecordMapByNotionAPI ({ pageId, from }) { if (!pageRecordMap) { return [] } - pageId = idToUuid(pageId) const collection = Object.values(pageRecordMap.collection)[0]?.value const collectionQuery = pageRecordMap.collection_query @@ -124,6 +136,7 @@ async function getPageRecordMapByNotionAPI ({ pageId, from }) { const schema = collection?.schema const rawMetadata = block[pageId].value const tagOptions = getTagOptions(schema) + const categoryOptions = getCategoryOptions(schema) // Check Type Page-Database和Inline-Database if ( @@ -140,6 +153,7 @@ async function getPageRecordMapByNotionAPI ({ pageId, from }) { block, schema, tagOptions, + categoryOptions, rawMetadata } } diff --git a/lib/notion/getPostBlocks.js b/lib/notion/getPostBlocks.js index b2ef8f19..ee4fae68 100644 --- a/lib/notion/getPostBlocks.js +++ b/lib/notion/getPostBlocks.js @@ -2,7 +2,7 @@ import BLOG from '@/blog.config' import { NotionAPI } from 'notion-client' import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager' -export async function getPostBlocks (id, from, slice) { +export async function getPostBlocks (id, from, slice, retryCount = 3) { const cacheKey = 'page_block_' + id let pageBlock = await getDataFromCache(cacheKey) if (pageBlock) { @@ -17,6 +17,10 @@ export async function getPostBlocks (id, from, slice) { console.log('[请求成功]', `from:${from}`, `id:${id}`) } catch (error) { console.error('[请求失败]', `from:${from}`, `id:${id}`, `error:${error}`) + if (retryCount && retryCount > 0) { // 重试 + console.error('[重试请求]', `from:${from}`, `id:${id}`, `剩余次数:${retryCount}`) + return getPostBlocks(id, from, slice, retryCount - 1) + } return null } diff --git a/pages/_app.js b/pages/_app.js index 98c5abc1..e5b63150 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -27,7 +27,8 @@ const MyApp = ({ Component, pageProps }) => { {BLOG.ANALYTICS_GOOGLE_ID && } {JSON.parse(BLOG.ANALYTICS_BUSUANZI_ENABLE) && } {BLOG.ADSENSE_GOOGLE_ID && } - + {/* FontawesomeCDN */} + ) diff --git a/pages/category/index.js b/pages/category/index.js index f9739597..7361a706 100644 --- a/pages/category/index.js +++ b/pages/category/index.js @@ -8,7 +8,7 @@ export default function Category (props) { export async function getStaticProps () { const from = 'category-index-props' - const { allPosts, categories, tags, postCount, latestPosts, customNav } = await getGlobalNotionData({ from }) + const { allPosts, categories, tags, postCount, latestPosts, customNav } = await getGlobalNotionData({ from, categoryCount: 0 }) return { props: { diff --git a/pages/index.js b/pages/index.js index be549212..599b3cf9 100644 --- a/pages/index.js +++ b/pages/index.js @@ -27,7 +27,6 @@ export async function getStaticProps () { BLOG.POSTS_PER_PAGE * page ) if (THEME_CONFIG.POST_LIST_PREVIEW || BLOG.POST_LIST_PREVIEW) { - console.log('加载预览') for (const i in postsToShow) { const post = postsToShow[i] const blockMap = await getPostBlocks(post.id, 'slug', BLOG.POST_PREVIEW_LINES) diff --git a/pages/page/[page].js b/pages/page/[page].js index f0f67312..211a2f92 100644 --- a/pages/page/[page].js +++ b/pages/page/[page].js @@ -45,7 +45,6 @@ export async function getStaticProps ({ params: { page } }) { ) // 加载预览 if (THEME_CONFIG.POST_LIST_PREVIEW || BLOG.POST_LIST_PREVIEW) { - console.log('加载预览') for (const i in postsToShow) { const post = postsToShow[i] const blockMap = await getPostBlocks(post.id, 'slug', BLOG.POST_PREVIEW_LINES) diff --git a/pages/search.js b/pages/search.js deleted file mode 100644 index 57bc57fb..00000000 --- a/pages/search.js +++ /dev/null @@ -1,30 +0,0 @@ -import { getGlobalNotionData } from '@/lib/notion/getNotionData' -import { LayoutSearch } from '@/themes' - -export async function getStaticProps () { - const { - allPosts, - categories, - tags, - postCount, - latestPosts, - customNav - } = await getGlobalNotionData({ from: 'search-props', pageType: ['Post'] }) - return { - props: { - posts: allPosts, - tags, - categories, - postCount, - latestPosts, - customNav - }, - revalidate: 1 - } -} - -const Search = (props) => { - return -} - -export default Search diff --git a/pages/search/[keyword].js b/pages/search/[keyword].js new file mode 100644 index 00000000..90a2bd62 --- /dev/null +++ b/pages/search/[keyword].js @@ -0,0 +1,135 @@ +import { getGlobalNotionData } from '@/lib/notion/getNotionData' +import { LayoutSearch } from '@/themes' +import BLOG from '@/blog.config' +import { useGlobal } from '@/lib/global' +import { getDataFromCache } from '@/lib/cache/cache_manager' + +export async function getServerSideProps ({ params: { keyword } }) { + const { + allPosts, + categories, + tags, + postCount, + latestPosts, + customNav + } = await getGlobalNotionData({ from: 'search-props', pageType: ['Post'] }) + + const filterPosts = await filterByMemCache(allPosts, keyword) + return { + props: { + posts: filterPosts, + tags, + categories, + postCount, + latestPosts, + customNav, + keyword + } + } +} + +const Index = (props) => { + const { keyword } = props + const { locale } = useGlobal() + const meta = { + title: `${keyword || ''} | ${locale.NAV.SEARCH} | ${BLOG.TITLE} `, + description: BLOG.DESCRIPTION, + type: 'website' + } + return +} + +/** + * 将对象的指定字段拼接到字符串 + * @param sourceTextArray + * @param targetObj + * @param key + * @returns {*} + */ +function appendText (sourceTextArray, targetObj, key) { + if (!targetObj) { + return sourceTextArray + } + const textArray = targetObj[key] + const text = textArray ? getTextContent(textArray) : '' + if (text && text !== 'Untitled') { + return sourceTextArray.concat(text) + } + return sourceTextArray +} + +/** + * 递归获取层层嵌套的数组 + * @param {*} textArray + * @returns + */ +function getTextContent (textArray) { + if (typeof textArray === 'object' && isIterable(textArray)) { + let result = '' + for (const textObj of textArray) { + result = result + getTextContent(textObj) + } + return result + } else if (typeof textArray === 'string') { + return textArray + } +} + +/** + * 对象是否可以遍历 + * @param {*} obj + * @returns + */ +const isIterable = obj => obj != null && typeof obj[Symbol.iterator] === 'function' + +/** + * 在内存缓存中进行全文索引 + * @param {*} allPosts + * @param keyword 关键词 + * @returns + */ +async function filterByMemCache (allPosts, keyword) { + const filterPosts = [] + for (const post of allPosts) { + const cacheKey = 'page_block_' + post.id + // const page = await getPostBlocks(post.id, 'search') + const page = await getDataFromCache(cacheKey) + const tagContent = post.tags ? post.tags.join(' ') : '' + const categoryContent = post.category ? post.category.join(' ') : '' + const articleInfo = post.title + post.summary + tagContent + categoryContent + let hit = articleInfo.indexOf(keyword) > -1 + let indexContent = [post.summary] + if (page && page.block) { + const contentIds = Object.keys(page.block) + contentIds.forEach(id => { + const properties = page?.block[id]?.value?.properties + indexContent = appendText(indexContent, properties, 'title') + indexContent = appendText(indexContent, properties, 'caption') + }) + } + // console.log('搜索是否命中缓存', page !== null, indexContent) + post.results = [] + let hitCount = 0 + const re = new RegExp(`${keyword}`, 'gim') + for (const i in indexContent) { + const c = indexContent[i] + const index = c.toLowerCase().indexOf(keyword.toLowerCase()) + const referText = c?.replace(re, `${keyword}`) + if (index > -1) { + hit = true + hitCount += 1 + post.results.push(`${referText}`) + } else { + if ((post.results.length - 1) / hitCount < 3 || i === 0) { + post.results.push(`${referText}`) + } + } + } + if (hit) { + filterPosts.push(post) + } + } + return filterPosts +} + +export default Index diff --git a/pages/search/index.js b/pages/search/index.js new file mode 100644 index 00000000..ef7cae89 --- /dev/null +++ b/pages/search/index.js @@ -0,0 +1,62 @@ +import { getGlobalNotionData } from '@/lib/notion/getNotionData' +import { LayoutSearch } from '@/themes' +import BLOG from '@/blog.config' +import { useGlobal } from '@/lib/global' +import { useRouter } from 'next/router' + +export async function getStaticProps () { + const { + allPosts, + categories, + tags, + postCount, + latestPosts, + customNav + } = await getGlobalNotionData({ from: 'search-props', pageType: ['Post'] }) + return { + props: { + posts: allPosts, + tags, + categories, + postCount, + latestPosts, + customNav + }, + revalidate: 1 + } +} + +const Search = (props) => { + const { posts } = props + let filteredPosts + const searchKey = getSearchKey() + // 静态过滤 + if (searchKey) { + filteredPosts = posts.filter(post => { + const tagContent = post.tags ? post.tags.join(' ') : '' + const categoryContent = post.category ? post.category.join(' ') : '' + const searchContent = post.title + post.summary + tagContent + categoryContent + return searchContent.toLowerCase().includes(searchKey.toLowerCase()) + }) + } else { + filteredPosts = posts + } + + const { locale } = useGlobal() + const meta = { + title: `${searchKey || ''} | ${locale.NAV.SEARCH} | ${BLOG.TITLE} `, + description: BLOG.DESCRIPTION, + type: 'website' + } + return +} + +function getSearchKey () { + const router = useRouter() + if (router.query && router.query.s) { + return router.query.s + } + return null +} + +export default Search diff --git a/themes/Empty/LayoutSearch.js b/themes/Empty/LayoutSearch.js index c7469a60..fb9244c3 100644 --- a/themes/Empty/LayoutSearch.js +++ b/themes/Empty/LayoutSearch.js @@ -1,31 +1,6 @@ -import { useRouter } from 'next/router' import LayoutBase from './LayoutBase' export const LayoutSearch = (props) => { - const { posts } = props - let filteredPosts - const searchKey = getSearchKey() - if (searchKey) { - filteredPosts = posts.filter(post => { - const tagContent = post.tags ? post.tags.join(' ') : '' - const searchContent = post.title + post.summary + tagContent - return searchContent.toLowerCase().includes(searchKey.toLowerCase()) - }) - } else { - filteredPosts = posts - } - - console.log(filteredPosts) - return - Search {searchKey} } - -function getSearchKey () { - const router = useRouter() - if (router.query && router.query.s) { - return router.query.s - } - return null -} diff --git a/themes/Fukasawa/LayoutCategoryIndex.js b/themes/Fukasawa/LayoutCategoryIndex.js index 0941d901..f7c8196e 100644 --- a/themes/Fukasawa/LayoutCategoryIndex.js +++ b/themes/Fukasawa/LayoutCategoryIndex.js @@ -17,11 +17,11 @@ export const LayoutCategoryIndex = (props) => { {locale.COMMON.CATEGORY}:
- {Object.keys(categories).map(category => { - return + {categories && categories.map(category => { + return
- {category}({categories[category]}) + {category.name}({category.count})
})} diff --git a/themes/Fukasawa/LayoutIndex.js b/themes/Fukasawa/LayoutIndex.js index 5a1209c5..0158a856 100644 --- a/themes/Fukasawa/LayoutIndex.js +++ b/themes/Fukasawa/LayoutIndex.js @@ -3,8 +3,6 @@ import LayoutBase from './LayoutBase' export const LayoutIndex = (props) => { return - - - + } diff --git a/themes/Fukasawa/LayoutSearch.js b/themes/Fukasawa/LayoutSearch.js index 1d77bf69..8112f850 100644 --- a/themes/Fukasawa/LayoutSearch.js +++ b/themes/Fukasawa/LayoutSearch.js @@ -1,30 +1,8 @@ -import { useRouter } from 'next/router' import LayoutBase from './LayoutBase' +import BlogListPage from './components/BlogListPage' export const LayoutSearch = (props) => { - let filteredPosts - const searchKey = getSearchKey() - if (searchKey) { - filteredPosts = props.posts.filter(post => { - const tagContent = post.tags ? post.tags.join(' ') : '' - const searchContent = post.title + post.summary + tagContent - return searchContent.toLowerCase().includes(searchKey.toLowerCase()) - }) - } else { - filteredPosts = props.posts - } - - console.log(filteredPosts) - return - Search {searchKey} + } - -function getSearchKey () { - const router = useRouter() - if (router.query && router.query.s) { - return router.query.s - } - return null -} diff --git a/themes/Fukasawa/components/AsideLeft.js b/themes/Fukasawa/components/AsideLeft.js index 2b46e616..a596ae27 100644 --- a/themes/Fukasawa/components/AsideLeft.js +++ b/themes/Fukasawa/components/AsideLeft.js @@ -9,8 +9,6 @@ import Catalog from './Catalog' function AsideLeft (props) { const { tags, currentTag, categories, currentCategory, post } = props - console.log(post) - return
diff --git a/themes/Fukasawa/components/BlogListPage.js b/themes/Fukasawa/components/BlogListPage.js index 4c6fc823..afc7f6f7 100644 --- a/themes/Fukasawa/components/BlogListPage.js +++ b/themes/Fukasawa/components/BlogListPage.js @@ -14,7 +14,7 @@ import PaginationSimple from './PaginationSimple' */ const BlogListPage = ({ page = 1, posts = [], postCount }) => { const totalPage = Math.ceil(postCount / BLOG.POSTS_PER_PAGE) - const showNext = page < totalPage && posts.length < postCount + const showNext = page < totalPage && posts.length <= BLOG.POSTS_PER_PAGE && posts.length < postCount const [colCount, changeCol] = useState(3) function updateCol () { diff --git a/themes/Fukasawa/components/GroupCategory.js b/themes/Fukasawa/components/GroupCategory.js index 3dd437d8..fabc2e57 100644 --- a/themes/Fukasawa/components/GroupCategory.js +++ b/themes/Fukasawa/components/GroupCategory.js @@ -8,14 +8,14 @@ function GroupCategory ({ currentCategory, categories }) { return <>
- {Object.keys(categories).map(category => { - const selected = currentCategory === category - return + {categories.map(category => { + const selected = currentCategory === category.name + return - {category}({categories[category]}) + {category.name}({category.count}) })} diff --git a/themes/Fukasawa/components/SearchInput.js b/themes/Fukasawa/components/SearchInput.js index 7a7c4aba..803ce49c 100644 --- a/themes/Fukasawa/components/SearchInput.js +++ b/themes/Fukasawa/components/SearchInput.js @@ -36,16 +36,28 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => { setSearchKey('') } + let lock = false const updateSearchKey = (val) => { - setSearchKey(val) + if (!lock) { + setSearchKey(val) + } + } + function lockSearchInput () { + lock = true } + function unLockSearchInput () { + lock = false + } return
updateSearchKey(e.target.value)} defaultValue={searchKey} /> diff --git a/themes/Hexo/LayoutBase.js b/themes/Hexo/LayoutBase.js index f1c9652e..7c25dbf7 100644 --- a/themes/Hexo/LayoutBase.js +++ b/themes/Hexo/LayoutBase.js @@ -46,14 +46,14 @@ const LayoutBase = (props) => {
-
+
{children}
- {/* 右下角悬浮 */} + {/* 右下角悬浮 */}
diff --git a/themes/Hexo/LayoutCategoryIndex.js b/themes/Hexo/LayoutCategoryIndex.js index 6585e290..1eadfc7d 100644 --- a/themes/Hexo/LayoutCategoryIndex.js +++ b/themes/Hexo/LayoutCategoryIndex.js @@ -20,16 +20,16 @@ export const LayoutCategoryIndex = props => { {locale.COMMON.CATEGORY}:
- {Object.keys(categories).map(category => { + {categories.map(category => { return ( - +
- {category}({categories[category]}) + {category.name}({category.count})
) diff --git a/themes/Hexo/LayoutSearch.js b/themes/Hexo/LayoutSearch.js index 77be5014..3b26ad57 100644 --- a/themes/Hexo/LayoutSearch.js +++ b/themes/Hexo/LayoutSearch.js @@ -1,38 +1,9 @@ -import BLOG from '@/blog.config' -import { useGlobal } from '@/lib/global' -import { useRouter } from 'next/router' + import BlogPostListPage from './components/BlogPostListPage' import LayoutBase from './LayoutBase' export const LayoutSearch = (props) => { - const { posts } = props - let filteredPosts - const searchKey = getSearchKey() - if (searchKey) { - filteredPosts = posts.filter(post => { - const tagContent = post.tags ? post.tags.join(' ') : '' - const searchContent = post.title + post.summary + tagContent - return searchContent.toLowerCase().includes(searchKey.toLowerCase()) - }) - } else { - filteredPosts = posts - } - - const { locale } = useGlobal() - const meta = { - title: `${searchKey || ''} | ${locale.NAV.SEARCH} | ${BLOG.TITLE} `, - description: BLOG.DESCRIPTION, - type: 'website' - } - return - + return + } - -function getSearchKey () { - const router = useRouter() - if (router.query && router.query.s) { - return router.query.s - } - return null -} diff --git a/themes/Hexo/LayoutSlug.js b/themes/Hexo/LayoutSlug.js index 87c69bee..ceb7322b 100644 --- a/themes/Hexo/LayoutSlug.js +++ b/themes/Hexo/LayoutSlug.js @@ -53,7 +53,7 @@ export const LayoutSlug = props => { showTag={false} floatSlot={floatSlot} > -
+
diff --git a/themes/Hexo/components/BlogPostCard.js b/themes/Hexo/components/BlogPostCard.js index 1e7a332b..6cba3568 100644 --- a/themes/Hexo/components/BlogPostCard.js +++ b/themes/Hexo/components/BlogPostCard.js @@ -9,7 +9,7 @@ import CONFIG_HEXO from '../config_hexo' const BlogPostCard = ({ post, showSummary }) => { const showPreview = CONFIG_HEXO.POST_LIST_PREVIEW && post.blockMap return ( -
+
diff --git a/themes/Hexo/components/BlogPostListPage.js b/themes/Hexo/components/BlogPostListPage.js index b08a0da6..a9ba1289 100644 --- a/themes/Hexo/components/BlogPostListPage.js +++ b/themes/Hexo/components/BlogPostListPage.js @@ -20,7 +20,7 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount }) => { return (
{/* 文章列表 */} -
+
{posts.map(post => ( ))} diff --git a/themes/Hexo/components/BlogPostListScroll.js b/themes/Hexo/components/BlogPostListScroll.js index 53a1d93f..ce23f44c 100644 --- a/themes/Hexo/components/BlogPostListScroll.js +++ b/themes/Hexo/components/BlogPostListScroll.js @@ -55,7 +55,7 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_HE return
{/* 文章列表 */} -
+
{postsToShow.map(post => ( ))} diff --git a/themes/Hexo/components/Card.js b/themes/Hexo/components/Card.js index 61feccfd..8cb8123c 100644 --- a/themes/Hexo/components/Card.js +++ b/themes/Hexo/components/Card.js @@ -1,7 +1,7 @@ const Card = ({ children, headerSlot, className }) => { return
<>{headerSlot} -
+
{children}
diff --git a/themes/Hexo/components/Catalog.js b/themes/Hexo/components/Catalog.js index 540c0d75..59942526 100644 --- a/themes/Hexo/components/Catalog.js +++ b/themes/Hexo/components/Catalog.js @@ -53,7 +53,7 @@ const Catalog = ({ toc }) => { return
目录
-
+
- +
} diff --git a/themes/Medium/components/CategoryGroup.js b/themes/Medium/components/CategoryGroup.js index cf6a7d38..797e3afa 100644 --- a/themes/Medium/components/CategoryGroup.js +++ b/themes/Medium/components/CategoryGroup.js @@ -8,10 +8,9 @@ const CategoryGroup = ({ currentCategory, categories }) => { return
分类
- {Object.keys(categories).map(category => { - const selected = currentCategory === category - const categoryCount = +categories[category] - return + {categories.map(category => { + const selected = currentCategory === category.name + return })}
diff --git a/themes/Medium/components/CategoryItem.js b/themes/Medium/components/CategoryItem.js index 86aef898..bb8a1841 100644 --- a/themes/Medium/components/CategoryItem.js +++ b/themes/Medium/components/CategoryItem.js @@ -6,7 +6,7 @@ export default function CategoryItem ({ selected, category, categoryCount }) { ? 'hover:text-white dark:hover:text-white bg-green-600 text-white ' : 'dark:text-green-400 text-gray-500 hover:text-white dark:hover:text-white hover:bg-green-600') + ' flex text-sm items-center duration-300 cursor-pointer py-1 font-light px-2 whitespace-nowrap'}> -
{category} {categoryCount && (categoryCount)} +
{category} {categoryCount && `(${categoryCount})`}
diff --git a/themes/Medium/components/SearchInput.js b/themes/Medium/components/SearchInput.js index bd704624..baf6f859 100644 --- a/themes/Medium/components/SearchInput.js +++ b/themes/Medium/components/SearchInput.js @@ -37,8 +37,18 @@ const SearchInput = ({ currentTag, currentSearch, cRef, className }) => { setSearchKey('') } + let lock = false const updateSearchKey = (val) => { - setSearchKey(val) + if (!lock) { + setSearchKey(val) + } + } + function lockSearchInput () { + lock = true + } + + function unLockSearchInput () { + lock = false } return
@@ -47,6 +57,9 @@ const SearchInput = ({ currentTag, currentSearch, cRef, className }) => { type='text' className={'w-full text-sm pl-2 transition focus:shadow-lg font-light leading-10 text-black bg-gray-100 dark:bg-gray-900 dark:text-white'} onKeyUp={handleKeyUp} + onCompositionStart={lockSearchInput} + onCompositionUpdate={lockSearchInput} + onCompositionEnd={unLockSearchInput} onChange={e => updateSearchKey(e.target.value)} defaultValue={searchKey} /> diff --git a/themes/NEXT/Layout404.js b/themes/NEXT/Layout404.js index 9c51b293..2fcfc7aa 100644 --- a/themes/NEXT/Layout404.js +++ b/themes/NEXT/Layout404.js @@ -23,7 +23,7 @@ export const Layout404 = (props) => {
-

404

+

404

页面无法加载,即将返回首页

diff --git a/themes/NEXT/LayoutCategoryIndex.js b/themes/NEXT/LayoutCategoryIndex.js index b4a5ef51..ac443a94 100644 --- a/themes/NEXT/LayoutCategoryIndex.js +++ b/themes/NEXT/LayoutCategoryIndex.js @@ -12,16 +12,16 @@ export const LayoutCategoryIndex = (props) => { type: 'website' } return -
+
{locale.COMMON.CATEGORY}:
- {Object.keys(categories).map(category => { - return + {categories.map(category => { + return
- {category}({categories[category]}) + {category.name}({category.count})
})} diff --git a/themes/NEXT/LayoutSearch.js b/themes/NEXT/LayoutSearch.js index 21f34de0..34a48e47 100644 --- a/themes/NEXT/LayoutSearch.js +++ b/themes/NEXT/LayoutSearch.js @@ -2,52 +2,22 @@ import LayoutBase from './LayoutBase' import StickyBar from './components/StickyBar' import BlogPostListScroll from './components/BlogPostListScroll' import { useGlobal } from '@/lib/global' -import BLOG from '@/blog.config' -import { useRouter } from 'next/router' export const LayoutSearch = (props) => { - const { posts, tags } = props - let filteredPosts - const searchKey = getSearchKey() - if (searchKey) { - filteredPosts = posts.filter(post => { - const tagContent = post.tags ? post.tags.join(' ') : '' - const searchContent = post.title + post.summary + tagContent - return searchContent.toLowerCase().includes(searchKey.toLowerCase()) - }) - } else { - filteredPosts = posts - } - const { locale } = useGlobal() - const meta = { - title: `${searchKey || ''} | ${locale.NAV.SEARCH} | ${BLOG.TITLE} `, - description: BLOG.DESCRIPTION, - type: 'website' - } + const { posts } = props + return ( - +
{' '} - {filteredPosts.length} {locale.COMMON.RESULT_OF_SEARCH} + {posts?.length} {locale.COMMON.RESULT_OF_SEARCH}
- +
) } - -function getSearchKey () { - const router = useRouter() - if (router.query && router.query.s) { - return router.query.s - } - return null -} diff --git a/themes/NEXT/LayoutTag.js b/themes/NEXT/LayoutTag.js index 47d0580b..2e7addfc 100644 --- a/themes/NEXT/LayoutTag.js +++ b/themes/NEXT/LayoutTag.js @@ -15,13 +15,9 @@ export const LayoutTag = (props) => { type: 'website' } - // 将当前选中的标签置顶🔝 - const currentTag = tags?.find(r => r?.name === tag) - const newTags = currentTag ? [currentTag].concat(tags.filter(r => r?.name !== tag)) : tags.filter(r => r?.name !== tag) - return - +
diff --git a/themes/NEXT/LayoutTagIndex.js b/themes/NEXT/LayoutTagIndex.js index 8ddd5bc4..b452874a 100644 --- a/themes/NEXT/LayoutTagIndex.js +++ b/themes/NEXT/LayoutTagIndex.js @@ -12,7 +12,7 @@ export const LayoutTagIndex = (props) => { type: 'website' } return -
+
{locale.COMMON.TAGS}:
{ tags.map(tag => { diff --git a/themes/NEXT/components/ArticleDetail.js b/themes/NEXT/components/ArticleDetail.js index ae430539..194020dd 100644 --- a/themes/NEXT/components/ArticleDetail.js +++ b/themes/NEXT/components/ArticleDetail.js @@ -91,7 +91,7 @@ export default function ArticleDetail (props) { )}
- +   | diff --git a/themes/NEXT/components/BlogPostCard.js b/themes/NEXT/components/BlogPostCard.js index 8fb3ce7e..2e104601 100644 --- a/themes/NEXT/components/BlogPostCard.js +++ b/themes/NEXT/components/BlogPostCard.js @@ -41,10 +41,16 @@ const BlogPostCard = ({ post, showSummary }) => {
- {(!showPreview || showSummary) &&

+ {(!showPreview || showSummary) && !post.results &&

{post.summary}

} + {/* 搜索结果 */} + {post.results &&

+ {post.results.map(r => ...)} +

+ } + {showPreview && post?.blockMap &&
{ />
} -
+
- + {locale.COMMON.ARTICLE_DETAIL} diff --git a/themes/NEXT/components/BlogPostListScroll.js b/themes/NEXT/components/BlogPostListScroll.js index 8e520392..a220c8ad 100644 --- a/themes/NEXT/components/BlogPostListScroll.js +++ b/themes/NEXT/components/BlogPostListScroll.js @@ -65,7 +65,7 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_NE
{ handleGetMore() }} - className='w-full my-4 py-4 text-center cursor-pointer glassmorphism shadow-xl rounded-xl dark:text-gray-200' + className='w-full my-4 py-4 text-center cursor-pointer glassmorphism shadow hover:shadow-xl duration-200 dark:text-gray-200' > {hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}
diff --git a/themes/NEXT/components/CategoryGroup.js b/themes/NEXT/components/CategoryGroup.js index a0c8eed0..1c22d04e 100644 --- a/themes/NEXT/components/CategoryGroup.js +++ b/themes/NEXT/components/CategoryGroup.js @@ -2,16 +2,17 @@ import Link from 'next/link' import React from 'react' const CategoryGroup = ({ currentCategory, categories }) => { + if (!categories) return <> return <>
- {Object.keys(categories).map(category => { - const selected = currentCategory === category - return + {categories.map(category => { + const selected = currentCategory === category.name + return - {category}({categories[category]}) + {category.name}({category.count}) })} diff --git a/themes/NEXT/components/CategoryList.js b/themes/NEXT/components/CategoryList.js index d7447424..5390a769 100644 --- a/themes/NEXT/components/CategoryList.js +++ b/themes/NEXT/components/CategoryList.js @@ -10,10 +10,10 @@ const CategoryList = ({ currentCategory, categories }) => { return
  • {locale.COMMON.CATEGORY}
  • - {Object.keys(categories).map(category => { - const selected = category === currentCategory + {categories.map(category => { + const selected = category.name === currentCategory return ( - +
  • { > - {`${category} `} + {`${category.name} (${category.count})`}
  • ) diff --git a/themes/NEXT/components/SearchInput.js b/themes/NEXT/components/SearchInput.js index 2c18732f..c3565c43 100644 --- a/themes/NEXT/components/SearchInput.js +++ b/themes/NEXT/components/SearchInput.js @@ -2,9 +2,11 @@ import { useRouter } from 'next/router' import { useGlobal } from '@/lib/global' import { useImperativeHandle, useRef, useState } from 'react' +let lock = false + const SearchInput = ({ currentTag, currentSearch, cRef }) => { const { locale } = useGlobal() - const [searchKey, setSearchKey] = useState(currentSearch || '') + // const [searchKey, setSearchKey] = useState(currentSearch || '') const [onLoading, setLoadingState] = useState(false) const router = useRouter() const searchInputRef = useRef() @@ -15,12 +17,14 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => { } } }) - const handleSearch = (key) => { + const handleSearch = () => { + const key = searchInputRef.current.value if (key && key !== '') { setLoadingState(true) - router.push({ pathname: '/search', query: { s: key } }).then(r => { - setLoadingState(false) - }) + // router.push({ pathname: '/search/' + key }).then(r => { + // setLoadingState(false) + // }) + location.href = '/search/' + key } else { router.push({ pathname: '/' }).then(r => { }) @@ -35,11 +39,26 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => { } const cleanSearch = () => { searchInputRef.current.value = '' - setSearchKey('') + setShowClean(false) + } + function lockSearchInput () { + lock = true } + function unLockSearchInput () { + lock = false + } + const [showClean, setShowClean] = useState(false) const updateSearchKey = (val) => { - setSearchKey(val) + if (lock) { + return + } + searchInputRef.current.value = val + if (val) { + setShowClean(true) + } else { + setShowClean(false) + } } return
    @@ -47,15 +66,18 @@ const SearchInput = ({ currentTag, currentSearch, cRef }) => { ref={searchInputRef} type='text' placeholder={currentTag ? `${locale.SEARCH.TAGS} #${currentTag}` : `${locale.SEARCH.ARTICLES}`} - className={'w-full text-sm pl-2 transition focus:shadow-lg font-light leading-10 border-gray-300 text-black bg-gray-100 dark:bg-gray-900 dark:text-white'} + className={'w-full text-sm pl-4 transition focus:shadow-lg font-light leading-10 border-gray-300 text-black bg-gray-100 dark:bg-gray-900 dark:text-white'} onKeyUp={handleKeyUp} + onCompositionStart={lockSearchInput} + onCompositionUpdate={lockSearchInput} + onCompositionEnd={unLockSearchInput} onChange={e => updateSearchKey(e.target.value)} - defaultValue={searchKey} + defaultValue={currentSearch} /> - {(searchKey && searchKey.length && )} + {(showClean && )}
    { handleSearch(searchKey) }}> + onClick={handleSearch}>
    diff --git a/themes/NEXT/components/SideAreaLeft.js b/themes/NEXT/components/SideAreaLeft.js index e59662be..9eb9a372 100644 --- a/themes/NEXT/components/SideAreaLeft.js +++ b/themes/NEXT/components/SideAreaLeft.js @@ -19,7 +19,7 @@ import CONFIG_NEXT from '../config_next' * @constructor */ const SideAreaLeft = (props) => { - const { currentTag, post, postCount, currentSearch } = props + const { post, postCount } = props const { locale } = useGlobal() const showToc = post && post.toc && post.toc.length > 1 return
{CONFIG_NEXT.MENU_SEARCH &&
- +
} diff --git a/themes/NEXT/components/SideAreaRight.js b/themes/NEXT/components/SideAreaRight.js index daaa62c2..b4f03826 100644 --- a/themes/NEXT/components/SideAreaRight.js +++ b/themes/NEXT/components/SideAreaRight.js @@ -5,6 +5,7 @@ import Card from './Card' import CategoryGroup from './CategoryGroup' import TagGroups from './TagGroups' import CONFIG_NEXT from '../config_next' +import { useRouter } from 'next/router' /** * 侧边平铺 @@ -16,18 +17,13 @@ import CONFIG_NEXT from '../config_next' * @returns {JSX.Element} * @constructor */ -const SideAreaRight = ({ - tags, - currentTag, - slot, - categories, - currentCategory -}) => { +const SideAreaRight = (props) => { + const { tags, currentTag, slot, categories, currentCategory } = props const { locale } = useGlobal() if (!CONFIG_NEXT.RIGHT_BAR) { return <> } - + const router = useRouter() return (