mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-13 23:16:47 +00:00
Algolia搜索
This commit is contained in:
@@ -134,10 +134,11 @@ const BLOG = {
|
||||
POSTS_PER_PAGE: 12, // post counts per page
|
||||
POSTS_SORT_BY: process.env.NEXT_PUBLIC_POST_SORT_BY || 'notion', // 排序方式 'date'按时间,'notion'由notion控制
|
||||
|
||||
ALGOLIA_APP_ID: process.env.ALGOLIA_APP_ID || null, // 在这里查看 https://dashboard.algolia.com/account/api-keys/
|
||||
ALGOLIA_APP_KEY: process.env.ALGOLIA_APP_KEY || null, // 管理后台的KEY,不会暴露给前端,在这里查看 https://dashboard.algolia.com/account/api-keys/
|
||||
ALGOLIA_APP_SEARCH_KEY: process.env.ALGOLIA_APP_SEARCH_KEY || null, // 客户端搜索用的KEY
|
||||
ALGOLIA_INDEX: process.env.ALGOLIA_INDEX || null, // 在Algolia中创建一个index用作数据库
|
||||
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
|
||||
ALGOLIA_INDEX: process.env.NEXT_PUBLIC_ALGOLIA_INDEX || null, // 在Algolia中创建一个index用作数据库
|
||||
ALGOLIA_RECREATE_DATA: process.env.ALGOLIA_RECREATE_DATA || process.env.npm_lifecycle_event === 'build', // 为true时重新构建索引数据; 默认在build时会构建
|
||||
|
||||
PREVIEW_CATEGORY_COUNT: 16, // 首页最多展示的分类数量,0为不限制
|
||||
PREVIEW_TAG_COUNT: 16, // 首页最多展示的标签数量,0为不限制
|
||||
|
||||
92
components/AlgoliaSearchModal.js
Normal file
92
components/AlgoliaSearchModal.js
Normal file
@@ -0,0 +1,92 @@
|
||||
import { useState, useImperativeHandle } from 'react'
|
||||
import BLOG from '@/blog.config'
|
||||
import algoliasearch from 'algoliasearch'
|
||||
import replaceSearchResult from '@/components/Mark'
|
||||
|
||||
/**
|
||||
* 结合 Algolia 实现的弹出式搜索框
|
||||
* 打开方式 cRef.current.openSearch()
|
||||
* https://www.algolia.com/doc/api-reference/search-api-parameters/
|
||||
*/
|
||||
export default function AlgoliaSearchModal({ cRef }) {
|
||||
const [searchResults, setSearchResults] = useState([])
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
/**
|
||||
* 对外暴露方法
|
||||
*/
|
||||
useImperativeHandle(cRef, () => {
|
||||
return {
|
||||
openSearch: () => {
|
||||
setIsModalOpen(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!BLOG.ALGOLIA_APP_ID) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_SEARCH_ONLY_APP_KEY)
|
||||
const index = client.initIndex(BLOG.ALGOLIA_INDEX)
|
||||
|
||||
const handleSearch = async (query) => {
|
||||
try {
|
||||
const res = await index.search(query)
|
||||
console.log(res)
|
||||
const { hits } = res
|
||||
setSearchResults(hits)
|
||||
const doms = document.getElementById('search-wrapper').getElementsByClassName('replace')
|
||||
replaceSearchResult({
|
||||
doms,
|
||||
search: query,
|
||||
target: {
|
||||
element: 'span',
|
||||
className: 'text-blue-600 border-b border-dashed'
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Algolia search error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div id='search-wrapper' className={`${isModalOpen ? 'opacity-100' : 'invisible opacity-0 pointer-events-none'} fixed h-screen w-screen left-0 top-0 flex items-center justify-center`}>
|
||||
{/* 内容 */}
|
||||
<div className={`${isModalOpen ? 'opacity-100' : 'invisible opacity-0 translate-y-10'} flex flex-col justify-between w-full min-h-[10rem] max-w-xl dark:bg-hexo-black-gray dark:border-gray-800 bg-white dark:bg- p-5 rounded-lg z-50 shadow border hover:border-blue-600 duration-300 transition-all `}>
|
||||
|
||||
<div className='flex justify-between items-center'>
|
||||
<div className='text-2xl text-blue-600 font-bold'>搜索</div>
|
||||
<div><i class="text-gray-600 fa-solid fa-xmark p-1 cursor-pointer hover:text-blue-600" onClick={closeModal} ></i></div>
|
||||
</div>
|
||||
|
||||
<input type="text" placeholder="在这里输入搜索关键词..." onChange={(e) => handleSearch(e.target.value)}
|
||||
className="bg-gray-50 dark:bg-gray-600 outline-blue-500 w-full px-4 my-2 py-1 mb-4 border rounded-md" />
|
||||
|
||||
{/* 标签组 */}
|
||||
<div>
|
||||
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
{searchResults.map((result) => (
|
||||
<li key={result.objectID} className="replace my-2">
|
||||
<a href={`${BLOG.SUB_PATH}/${result.slug}`} className="font-bold hover:text-blue-600 ">
|
||||
{result.title}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div className='text-gray-600'><i class="fa-brands fa-algolia"></i> Algolia 提供搜索服务</div>
|
||||
</div>
|
||||
|
||||
{/* 遮罩 */}
|
||||
<div onClick={closeModal} className="z-30 fixed top-0 left-0 w-full h-full flex items-center justify-center glassmorphism" />
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -2,12 +2,25 @@ import BLOG from '@/blog.config'
|
||||
import { getPageContentText } from '@/pages/search/[keyword]'
|
||||
import algoliasearch from 'algoliasearch'
|
||||
|
||||
/**
|
||||
* 生成全文索引
|
||||
* @param {*} allPages
|
||||
*/
|
||||
const generateAlgoliaSearch = async({ allPages, force = false }) => {
|
||||
allPages?.forEach(p => {
|
||||
// 判断这篇文章是否需要重新创建索引
|
||||
if (p && !p.password) {
|
||||
uploadDataToAlgolia(p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传数据
|
||||
*/
|
||||
const uploadDataToAlgolia = (post) => {
|
||||
// Connect and authenticate with your Algolia app
|
||||
const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_APP_KEY)
|
||||
const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_ADMIN_APP_KEY)
|
||||
|
||||
// Create a new index and add a record
|
||||
const index = client.initIndex(BLOG.ALGOLIA_INDEX)
|
||||
@@ -26,4 +39,4 @@ const uploadDataToAlgolia = (post) => {
|
||||
})
|
||||
}
|
||||
|
||||
export { uploadDataToAlgolia }
|
||||
export { uploadDataToAlgolia, generateAlgoliaSearch }
|
||||
|
||||
@@ -38,6 +38,12 @@ export async function generateSitemapXml({ allPages }) {
|
||||
console.warn('无法写入文件', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成站点地图
|
||||
* @param {*} urls
|
||||
* @returns
|
||||
*/
|
||||
function createSitemapXml(urls) {
|
||||
let urlsXml = ''
|
||||
urls.forEach(u => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { generateRobotsTxt } from '@/lib/robots.txt'
|
||||
|
||||
import { useRouter } from 'next/router'
|
||||
import { getLayoutByTheme } from '@/themes/theme'
|
||||
import { generateAlgoliaSearch } from '@/lib/algolia'
|
||||
|
||||
/**
|
||||
* 首页布局
|
||||
@@ -62,6 +63,11 @@ export async function getStaticProps() {
|
||||
generateRss(props?.latestPosts || [])
|
||||
}
|
||||
|
||||
// 生成全文索引 - 仅在 yarn build 时执行 && process.env.npm_lifecycle_event === 'build'
|
||||
if (BLOG.ALGOLIA_APP_ID && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA)) {
|
||||
generateAlgoliaSearch({ allPages: props.allPages })
|
||||
}
|
||||
|
||||
delete props.allPages
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import AlgoliaSearchModal from '@/components/AlgoliaSearchModal'
|
||||
import { useRef } from 'react'
|
||||
|
||||
/**
|
||||
* 搜索按钮
|
||||
* @returns
|
||||
*/
|
||||
export default function SearchButton() {
|
||||
const { locale } = useGlobal()
|
||||
return <Link href="/search" title={locale.NAV.SEARCH} alt={locale.NAV.SEARCH} className='cursor-pointer hover:bg-black hover:bg-opacity-10 rounded-full w-10 h-10 flex justify-center items-center duration-200 transition-all'>
|
||||
<i title={locale.NAV.SEARCH} className="fa-solid fa-magnifying-glass"/>
|
||||
</Link>
|
||||
const router = useRouter()
|
||||
const searchModal = useRef(null)
|
||||
|
||||
function handleSearch() {
|
||||
if (BLOG.ALGOLIA_APP_ID) {
|
||||
searchModal.current.openSearch()
|
||||
} else {
|
||||
router.push('/search')
|
||||
}
|
||||
}
|
||||
|
||||
return <>
|
||||
<div onClick={handleSearch} title={locale.NAV.SEARCH} alt={locale.NAV.SEARCH} className='cursor-pointer hover:bg-black hover:bg-opacity-10 rounded-full w-10 h-10 flex justify-center items-center duration-200 transition-all'>
|
||||
<i title={locale.NAV.SEARCH} className="fa-solid fa-magnifying-glass" />
|
||||
</div>
|
||||
<AlgoliaSearchModal cRef={searchModal} />
|
||||
</>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user