mirror of
https://github.com/MoonTechLab/LunaTV.git
synced 2026-02-22 18:44:44 +08:00
feat: add search history
This commit is contained in:
@@ -5,12 +5,18 @@ import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { Suspense } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import {
|
||||
addSearchHistory,
|
||||
clearSearchHistory,
|
||||
getSearchHistory,
|
||||
} from '@/lib/db.client';
|
||||
|
||||
import PageLayout from '@/components/PageLayout';
|
||||
import VideoCard from '@/components/VideoCard';
|
||||
|
||||
function SearchPageClient() {
|
||||
// 模拟搜索历史数据
|
||||
const mockSearchHistory = ['流浪地球', '三体', '狂飙', '满江红'];
|
||||
// 搜索历史
|
||||
const [searchHistory, setSearchHistory] = useState<string[]>([]);
|
||||
|
||||
// 定义搜索结果类型
|
||||
type SearchResult = {
|
||||
@@ -33,6 +39,12 @@ function SearchPageClient() {
|
||||
useEffect(() => {
|
||||
// 自动聚焦搜索框
|
||||
searchInputRef.current?.focus();
|
||||
|
||||
// 加载搜索历史
|
||||
(async () => {
|
||||
const history = await getSearchHistory();
|
||||
setSearchHistory(history);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -41,6 +53,12 @@ function SearchPageClient() {
|
||||
if (query) {
|
||||
setSearchQuery(query);
|
||||
fetchSearchResults(query);
|
||||
|
||||
// 保存到搜索历史
|
||||
addSearchHistory(query).then(async () => {
|
||||
const history = await getSearchHistory();
|
||||
setSearchHistory(history);
|
||||
});
|
||||
} else {
|
||||
setShowResults(false);
|
||||
}
|
||||
@@ -68,10 +86,15 @@ function SearchPageClient() {
|
||||
|
||||
setIsLoading(true);
|
||||
setShowResults(true);
|
||||
// 模拟搜索延迟
|
||||
setTimeout(() => {
|
||||
fetchSearchResults(searchQuery);
|
||||
}, 1000);
|
||||
|
||||
// 直接发请求
|
||||
fetchSearchResults(searchQuery);
|
||||
|
||||
// 保存到搜索历史
|
||||
addSearchHistory(searchQuery).then(async () => {
|
||||
const history = await getSearchHistory();
|
||||
setSearchHistory(history);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -114,21 +137,32 @@ function SearchPageClient() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : mockSearchHistory.length > 0 ? (
|
||||
) : searchHistory.length > 0 ? (
|
||||
// 搜索历史
|
||||
<section className='mb-12'>
|
||||
<h2 className='mb-4 text-xl font-bold text-gray-800 text-left'>
|
||||
搜索历史
|
||||
{searchHistory.length > 0 && (
|
||||
<button
|
||||
onClick={async () => {
|
||||
await clearSearchHistory();
|
||||
setSearchHistory([]);
|
||||
}}
|
||||
className='ml-3 text-sm text-gray-500 hover:text-red-500 transition-colors'
|
||||
>
|
||||
清空
|
||||
</button>
|
||||
)}
|
||||
</h2>
|
||||
<div className='flex flex-wrap gap-2'>
|
||||
{mockSearchHistory.map((item, index) => (
|
||||
{searchHistory.map((item, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => {
|
||||
setSearchQuery(item);
|
||||
router.push(`/search?q=${encodeURIComponent(item)}`);
|
||||
}}
|
||||
className='px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-full text-sm text-gray-700 transition-colors duration-200'
|
||||
className='px-4 py-2 bg-gray-500/10 hover:bg-gray-300 rounded-full text-sm text-gray-700 transition-colors duration-200'
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
|
||||
@@ -36,6 +36,12 @@ const STORAGE_TYPE =
|
||||
| 'database'
|
||||
| undefined) || 'localstorage';
|
||||
|
||||
// ---------------- 搜索历史相关常量 ----------------
|
||||
const SEARCH_HISTORY_KEY = 'moontv_search_history';
|
||||
|
||||
// 搜索历史最大保存条数
|
||||
const SEARCH_HISTORY_LIMIT = 20;
|
||||
|
||||
// ---- 工具函数 ----
|
||||
async function fetchFromApi<T>(path: string): Promise<T> {
|
||||
const res = await fetch(path);
|
||||
@@ -164,3 +170,96 @@ export async function deletePlayRecord(
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------- 搜索历史相关 API ---------------- */
|
||||
|
||||
/**
|
||||
* 获取搜索历史
|
||||
*/
|
||||
export async function getSearchHistory(): Promise<string[]> {
|
||||
// 如果配置为使用数据库,则从后端 API 获取
|
||||
if (STORAGE_TYPE === 'database') {
|
||||
try {
|
||||
return fetchFromApi<string[]>('/api/searchhistory');
|
||||
} catch (err) {
|
||||
console.error('获取搜索历史失败:', err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// 默认从 localStorage 读取
|
||||
if (typeof window === 'undefined') {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const raw = localStorage.getItem(SEARCH_HISTORY_KEY);
|
||||
if (!raw) return [];
|
||||
const arr = JSON.parse(raw) as string[];
|
||||
// 仅返回字符串数组
|
||||
return Array.isArray(arr) ? arr : [];
|
||||
} catch (err) {
|
||||
console.error('读取搜索历史失败:', err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将关键字添加到搜索历史
|
||||
*/
|
||||
export async function addSearchHistory(keyword: string): Promise<void> {
|
||||
const trimmed = keyword.trim();
|
||||
if (!trimmed) return;
|
||||
|
||||
// 数据库模式
|
||||
if (STORAGE_TYPE === 'database') {
|
||||
try {
|
||||
await fetch('/api/searchhistory', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ keyword: trimmed }),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('保存搜索历史失败:', err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// localStorage 模式
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
try {
|
||||
const history = await getSearchHistory();
|
||||
const newHistory = [trimmed, ...history.filter((k) => k !== trimmed)];
|
||||
// 限制长度
|
||||
if (newHistory.length > SEARCH_HISTORY_LIMIT) {
|
||||
newHistory.length = SEARCH_HISTORY_LIMIT;
|
||||
}
|
||||
localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(newHistory));
|
||||
} catch (err) {
|
||||
console.error('保存搜索历史失败:', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空搜索历史
|
||||
*/
|
||||
export async function clearSearchHistory(): Promise<void> {
|
||||
// 数据库模式
|
||||
if (STORAGE_TYPE === 'database') {
|
||||
try {
|
||||
await fetch('/api/searchhistory', {
|
||||
method: 'DELETE',
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('清空搜索历史失败:', err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// localStorage 模式
|
||||
if (typeof window === 'undefined') return;
|
||||
localStorage.removeItem(SEARCH_HISTORY_KEY);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user