搜索增加简繁转换器

This commit is contained in:
mtvpls
2026-01-12 23:28:48 +08:00
parent fd9d911583
commit 3f610124e7
5 changed files with 155 additions and 8 deletions

View File

@@ -36,6 +36,10 @@ function SearchPageClient() {
const [triggerAcgSearch, setTriggerAcgSearch] = useState(false);
// 用户权限
const [userRole, setUserRole] = useState<'owner' | 'admin' | 'user' | null>(null);
// 繁体转简体转换器
const converterRef = useRef<((text: string) => string) | null>(null);
// 转换器是否已初始化
const [converterReady, setConverterReady] = useState(false);
const router = useRouter();
const searchParams = useSearchParams();
@@ -538,6 +542,26 @@ function SearchPageClient() {
const authInfo = getAuthInfoFromBrowserCookie();
setUserRole(authInfo?.role || null);
// 初始化繁体转简体转换器
if (typeof window !== 'undefined') {
import('opencc-js').then((module) => {
try {
const OpenCC = module.default || module;
const converter = OpenCC.Converter({ from: 'hk', to: 'cn' });
converterRef.current = converter;
setConverterReady(true);
} catch (error) {
console.error('初始化繁体转简体转换器失败:', error);
setConverterReady(true); // 即使失败也设置为 true避免阻塞
}
}).catch((error) => {
console.error('加载 opencc-js 失败:', error);
setConverterReady(true); // 即使失败也设置为 true避免阻塞
});
} else {
setConverterReady(true);
}
// 初始加载搜索历史
getSearchHistory().then(setSearchHistory);
@@ -600,13 +624,41 @@ function SearchPageClient() {
}, []);
useEffect(() => {
// 等待转换器初始化完成
if (!converterReady) {
return;
}
// 当搜索参数变化时更新搜索状态
const query = searchParams.get('q') || '';
let query = searchParams.get('q') || '';
// 如果开启了繁体转简体,进行转换
if (query && typeof window !== 'undefined') {
const searchTraditionalToSimplified = localStorage.getItem('searchTraditionalToSimplified');
if (searchTraditionalToSimplified === 'true' && converterRef.current) {
try {
const originalQuery = query;
query = converterRef.current(query);
// 如果转换后的文本与原文本不同,更新 URL
if (originalQuery !== query) {
const trimmedConverted = query.trim();
// 使用 replace 而不是 push避免在历史记录中留下繁体版本
router.replace(`/search?q=${encodeURIComponent(trimmedConverted)}${searchParams.get('type') ? `&type=${searchParams.get('type')}` : ''}`);
return; // 等待 URL 更新后重新触发此 effect
}
} catch (error) {
console.error('[URL参数监听] 繁体转简体转换失败:', error);
}
}
}
currentQueryRef.current = query.trim();
if (query) {
setSearchQuery(query);
const trimmed = query.trim();
// 检查是否有缓存且不是强制刷新
@@ -799,7 +851,7 @@ function SearchPageClient() {
setShowResults(false);
setShowSuggestions(false);
}
}, [searchParams, forceRefresh]);
}, [searchParams, forceRefresh, converterReady]);
// 组件卸载时,关闭可能存在的连接
useEffect(() => {
@@ -838,9 +890,21 @@ function SearchPageClient() {
// 搜索表单提交时触发,处理搜索逻辑
const handleSearch = (e: React.FormEvent) => {
e.preventDefault();
const trimmed = searchQuery.trim().replace(/\s+/g, ' ');
let trimmed = searchQuery.trim().replace(/\s+/g, ' ');
if (!trimmed) return;
// 如果开启了繁体转简体,进行转换
if (typeof window !== 'undefined') {
const searchTraditionalToSimplified = localStorage.getItem('searchTraditionalToSimplified');
if (searchTraditionalToSimplified === 'true' && converterRef.current) {
try {
trimmed = converterRef.current(trimmed);
} catch (error) {
console.error('繁体转简体转换失败:', error);
}
}
}
// 回显搜索框
setSearchQuery(trimmed);
setShowResults(true);
@@ -863,7 +927,21 @@ function SearchPageClient() {
};
const handleSuggestionSelect = (suggestion: string) => {
setSearchQuery(suggestion);
let processedSuggestion = suggestion;
// 如果开启了繁体转简体,进行转换
if (typeof window !== 'undefined') {
const searchTraditionalToSimplified = localStorage.getItem('searchTraditionalToSimplified');
if (searchTraditionalToSimplified === 'true' && converterRef.current) {
try {
processedSuggestion = converterRef.current(suggestion);
} catch (error) {
console.error('繁体转简体转换失败:', error);
}
}
}
setSearchQuery(processedSuggestion);
setShowSuggestions(false);
// 自动执行搜索
@@ -872,15 +950,15 @@ function SearchPageClient() {
// 根据当前选项卡执行不同的搜索
if (activeTab === 'video') {
// 影视搜索
router.push(`/search?q=${encodeURIComponent(suggestion)}&type=video`);
router.push(`/search?q=${encodeURIComponent(processedSuggestion)}&type=video`);
// 其余由 searchParams 变化的 effect 处理
} else if (activeTab === 'pansou') {
// 网盘搜索 - 触发搜索
router.push(`/search?q=${encodeURIComponent(suggestion)}&type=pansou`);
router.push(`/search?q=${encodeURIComponent(processedSuggestion)}&type=pansou`);
setTriggerPansouSearch(prev => !prev);
} else if (activeTab === 'acg') {
// ACG 磁力搜索 - 触发搜索
router.push(`/search?q=${encodeURIComponent(suggestion)}&type=acg`);
router.push(`/search?q=${encodeURIComponent(processedSuggestion)}&type=acg`);
setTriggerAcgSearch(prev => !prev);
}
};

View File

@@ -106,6 +106,7 @@ export const UserMenu: React.FC = () => {
const [bufferStrategy, setBufferStrategy] = useState('medium');
const [nextEpisodePreCache, setNextEpisodePreCache] = useState(true);
const [isBufferStrategyDropdownOpen, setIsBufferStrategyDropdownOpen] = useState(false);
const [searchTraditionalToSimplified, setSearchTraditionalToSimplified] = useState(false);
// 折叠面板状态
const [isDoubanSectionOpen, setIsDoubanSectionOpen] = useState(true);
@@ -378,6 +379,12 @@ export const UserMenu: React.FC = () => {
console.error('解析首页模块配置失败:', error);
}
}
// 加载搜索繁体转简体设置
const savedSearchTraditionalToSimplified = localStorage.getItem('searchTraditionalToSimplified');
if (savedSearchTraditionalToSimplified !== null) {
setSearchTraditionalToSimplified(savedSearchTraditionalToSimplified === 'true');
}
}
}, []);
@@ -641,6 +648,13 @@ export const UserMenu: React.FC = () => {
}
};
const handleSearchTraditionalToSimplifiedToggle = (value: boolean) => {
setSearchTraditionalToSimplified(value);
if (typeof window !== 'undefined') {
localStorage.setItem('searchTraditionalToSimplified', String(value));
}
};
// 首页模块配置处理函数
const handleHomeModuleToggle = (id: string, enabled: boolean) => {
const updatedModules = homeModules.map(module =>
@@ -732,6 +746,7 @@ export const UserMenu: React.FC = () => {
setBufferStrategy('medium');
setNextEpisodePreCache(true);
setHomeModules(defaultHomeModules);
setSearchTraditionalToSimplified(false);
if (typeof window !== 'undefined') {
localStorage.setItem('defaultAggregateSearch', JSON.stringify(true));
@@ -747,6 +762,7 @@ export const UserMenu: React.FC = () => {
localStorage.setItem('bufferStrategy', 'medium');
localStorage.setItem('nextEpisodePreCache', 'true');
localStorage.setItem('homeModules', JSON.stringify(defaultHomeModules));
localStorage.setItem('searchTraditionalToSimplified', 'false');
window.dispatchEvent(new CustomEvent('homeModulesUpdated'));
}
};
@@ -1423,6 +1439,30 @@ export const UserMenu: React.FC = () => {
</div>
</label>
</div>
{/* 搜索繁体转简体 */}
<div className='flex items-center justify-between'>
<div>
<h4 className='text-sm font-medium text-gray-700 dark:text-gray-300'>
</h4>
<p className='text-xs text-gray-500 dark:text-gray-400 mt-1'>
</p>
</div>
<label className='flex items-center cursor-pointer'>
<div className='relative'>
<input
type='checkbox'
className='sr-only peer'
checked={searchTraditionalToSimplified}
onChange={(e) => handleSearchTraditionalToSimplifiedToggle(e.target.checked)}
/>
<div className='w-11 h-6 bg-gray-300 rounded-full peer-checked:bg-green-500 transition-colors dark:bg-gray-600'></div>
<div className='absolute top-0.5 left-0.5 w-5 h-5 bg-white rounded-full transition-transform peer-checked:translate-x-5'></div>
</div>
</label>
</div>
</div>
)}
</div>

8
src/types/opencc-js.d.ts vendored Normal file
View File

@@ -0,0 +1,8 @@
declare module 'opencc-js' {
interface ConverterOptions {
from: string;
to: string;
}
export function Converter(options: ConverterOptions): (text: string) => string;
}