'use client'; import React, { useState, useEffect } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faStar, faSearch, faTimes, faChevronDown, faBook, faCode, faCloud, faBell, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'; import { faStar as farStar } from '@fortawesome/free-regular-svg-icons'; import { faGithub } from '@fortawesome/free-brands-svg-icons'; import categories from '@/config/categories'; import tools from '@/config/tools'; import { useRouter } from 'next/navigation'; import ThemeToggle from '@/components/ThemeToggle'; import LanguageToggle from '@/components/LanguageToggle'; import Link from 'next/link'; import { useLanguage } from '@/context/LanguageContext'; export default function Home() { const { t, language } = useLanguage(); const [activeCategory, setActiveCategory] = useState(() => { // 从 localStorage 获取上次选择的分类,如果没有则默认为 "all" if (typeof window !== 'undefined') { return localStorage.getItem('lastActiveCategory') || "all"; } return "all"; }); const [searchTerm, setSearchTerm] = useState(""); const [iconsLoaded, setIconsLoaded] = useState(false); const [favoriteTools, setFavoriteTools] = useState([]); const [showFavorites, setShowFavorites] = useState(false); const [showNotification, setShowNotification] = useState(false); const [firstFavoriteAdded, setFirstFavoriteAdded] = useState(false); const [prefetchedTools, setPrefetchedTools] = useState>(new Set()); const [loadingProgress, setLoadingProgress] = useState<{ current: number, total: number }>({ current: 0, total: 0 }); const [showProductsDropdown, setShowProductsDropdown] = useState(false); const router = useRouter(); // 产品推荐列表 const recommendedProducts = [ { title: "ShowDoc", url: "https://www.showdoc.com.cn/", description: "API文档、技术文档工具", icon: faBook }, { title: "RunApi", url: "https://www.runapi.com.cn/", description: "接口管理与测试平台", icon: faCode }, { title: "大风云", url: "https://www.dfyun.com.cn/", description: "性价比巨高的CDN服务", icon: faCloud }, { title: "Push", url: "https://push.showdoc.com.cn/", description: "消息推送服务", icon: faBell } ]; // 点击其他区域关闭产品推荐下拉菜单 useEffect(() => { const handleClickOutside = (event: MouseEvent) => { const target = event.target as Element; if (!target.closest('#products-dropdown-container')) { setShowProductsDropdown(false); } }; document.addEventListener('click', handleClickOutside); return () => { document.removeEventListener('click', handleClickOutside); }; }, []); // 切换产品推荐下拉菜单的显示状态 const toggleProductsDropdown = (e: React.MouseEvent) => { e.stopPropagation(); setShowProductsDropdown(prev => !prev); }; // 批量工具预加载管理 useEffect(() => { let isMounted = true; // 页面完全加载后开始批量预加载 const startBatchPrefetch = () => { // 获取收藏的工具列表 const savedFavorites = localStorage.getItem('favoriteTools'); const userFavorites = savedFavorites ? JSON.parse(savedFavorites) : []; // 按优先级排序工具:1. 收藏的工具 2. 常用工具 3. 其他工具 const favoriteTools = tools.filter(tool => userFavorites.includes(tool.code)); const commonTools = tools.filter(tool => tool.category.includes('common') && !userFavorites.includes(tool.code) ); const otherTools = tools.filter(tool => !tool.category.includes('common') && !userFavorites.includes(tool.code) ); // 按优先级顺序合并工具列表 const orderedTools = [...favoriteTools, ...commonTools, ...otherTools]; if (!isMounted) return; // 设置总数 setLoadingProgress({ current: 0, total: orderedTools.length }); // 创建预加载队列 const prefetchQueue = async () => { const newPrefetchedSet = new Set(prefetchedTools); for (let i = 0; i < orderedTools.length; i++) { if (!isMounted) return; const tool = orderedTools[i]; // 如果已经预加载过,跳过 if (newPrefetchedSet.has(tool.code)) { setLoadingProgress(prev => ({ ...prev, current: prev.current + 1 })); continue; } try { // 计算优先级:收藏 > 常用 > 其他 const isPriority = userFavorites.includes(tool.code) || (i < favoriteTools.length + commonTools.length); // 使用 fetch 预加载页面内容 const prefetchUrl = `/tools/${tool.code}`; const response = await fetch(prefetchUrl, { priority: isPriority ? 'high' : 'low', method: 'GET', cache: 'default' }); if (!isMounted) return; if (response.ok) { // 预解析响应以确保它被缓存 await response.text(); // 更新已预加载的工具集合 newPrefetchedSet.add(tool.code); // 更新进度 setLoadingProgress(prev => ({ ...prev, current: prev.current + 1 })); // 更新预加载状态 if (isMounted) { setPrefetchedTools(prev => { const updatedSet = new Set(prev); updatedSet.add(tool.code); return updatedSet; }); } } else { // 即使请求不成功也更新进度 setLoadingProgress(prev => ({ ...prev, current: prev.current + 1 })); } } catch (error) { console.warn(`批量预加载工具 ${tool.code} 失败:`, error); // 即使失败也更新进度 setLoadingProgress(prev => ({ ...prev, current: prev.current + 1 })); } // 调整不同优先级工具的预加载延迟 if (i < orderedTools.length - 1) { // 收藏与常用工具间隔短一些,其他工具间隔长一些 const delay = i < favoriteTools.length + commonTools.length ? 150 : 300; await new Promise(resolve => setTimeout(resolve, delay)); } } // 预加载完成后,在开发环境打印信息 if (process.env.NODE_ENV === 'development') { console.log(`预加载完成! 收藏工具: ${favoriteTools.length}, 常用工具: ${commonTools.length}, 其他工具: ${otherTools.length}`); } }; // 开始预加载队列 prefetchQueue(); }; // 等待页面完全加载后开始预加载 if (document.readyState === 'complete') { // 给页面内容完全渲染一些时间,然后开始批量预加载 setTimeout(startBatchPrefetch, 2000); } else { window.addEventListener('load', () => { // 页面加载完成后延迟一段时间再开始预加载 setTimeout(startBatchPrefetch, 2000); }); } // 清理函数 return () => { isMounted = false; }; }, []); // 添加预加载工具页面的函数 const prefetchTool = (toolCode: string) => { // 如果已经预加载过,则不再重复预加载 if (prefetchedTools.has(toolCode)) { return; } // 使用 fetch 预加载页面内容 const prefetchUrl = `/tools/${toolCode}`; fetch(prefetchUrl, { priority: 'low' }) .then(response => { if (!response.ok) { throw new Error(`预加载失败: ${response.status}`); } return response.text(); }) .then(() => { // 成功预加载后更新状态 setPrefetchedTools(prev => { const newSet = new Set(prev); newSet.add(toolCode); return newSet; }); }) .catch(error => { console.warn(`工具 ${toolCode} 预加载失败:`, error); }); }; // 处理工具导航并显示加载指示器 const navigateToTool = (toolCode: string) => { // 检查是否已预加载 - 使用最新状态重新检查一次 const isPrefetched = prefetchedTools.has(toolCode); // 检查是否是首次访问该工具 const isFirstVisit = !localStorage.getItem(`visited-${toolCode}`); // 记录已访问工具 localStorage.setItem(`visited-${toolCode}`, 'true'); // 打印调试信息 - 开发环境 if (process.env.NODE_ENV === 'development') { console.log(`导航到工具: ${toolCode}, 预加载状态: ${isPrefetched ? '已预加载' : '未预加载'}`); } // 如果已预加载,直接导航,不显示loading if (isPrefetched) { // 在导航前存储一个标志,表示这是从主页导航过来的 localStorage.setItem('from_homepage', 'true'); router.push(`/tools/${toolCode}`); return; } // 在导航前存储一个标志,表示这是从主页导航过来的 localStorage.setItem('from_homepage', 'true'); const loadingId = 'loading-indicator-' + Date.now(); const loadingEl = document.createElement('div'); loadingEl.id = loadingId; loadingEl.className = 'fixed inset-0 flex items-center justify-center bg-[rgba(0,0,0,0.3)] z-50 backdrop-blur-sm transition-opacity duration-300'; // 使用自适应主题的加载指示器 const bgColor = getComputedStyle(document.documentElement).getPropertyValue('--color-bg-card').trim(); const borderColor = getComputedStyle(document.documentElement).getPropertyValue('--color-primary').trim(); const textColor = getComputedStyle(document.documentElement).getPropertyValue('--color-text-primary').trim(); loadingEl.innerHTML = `
加载中...
`; document.body.appendChild(loadingEl); const removeLoadingIndicator = () => { const indicator = document.getElementById(loadingId); if (indicator) { indicator.classList.add('opacity-0'); setTimeout(() => { indicator.remove(); }, 200); } }; // 立即导航 router.push(`/tools/${toolCode}`); // 超短超时检测,针对缓存页面快速检测 setTimeout(() => { // 如果页面已变化并且不是首次访问,很可能是使用了缓存,可以快速移除loading if (window.location.pathname.includes(`/tools/${toolCode}`) && !isFirstVisit) { removeLoadingIndicator(); return; } // 否则继续进行DOM检测 startDomCheck(); }, 20); // 启动DOM监测 const startDomCheck = () => { let checkCount = 0; const maxChecks = isFirstVisit ? 20 : 10; // 首次访问检查更多次 const baseDelay = isFirstVisit ? 50 : 30; // 首次访问间隔更长 const checkForPageLoad = () => { // 检查是否已经导航到新页面 if (window.location.pathname.includes(`/tools/${toolCode}`)) { // 检查页面内容是否已经渲染 const content = document.querySelector('main') || document.querySelector('#tool-content'); if (content) { removeLoadingIndicator(); return; } } checkCount++; if (checkCount < maxChecks) { // 最开始快速检查,逐渐减慢检查频率 const delay = Math.min(baseDelay + checkCount * 5, isFirstVisit ? 100 : 60); setTimeout(checkForPageLoad, delay); } }; // 立即开始检查 checkForPageLoad(); }; // 兜底保障,确保加载指示器一定会被移除 // 对于首次访问等待更长时间,后续访问缩短时间 setTimeout(removeLoadingIndicator, isFirstVisit ? 800 : 300); }; // 图标加载处理 useEffect(() => { // 设置小延迟确保图标已加载 const timer = setTimeout(() => { setIconsLoaded(true); }, 200); return () => clearTimeout(timer); }, []); // 加载收藏工具和首次收藏状态 useEffect(() => { const savedFavorites = localStorage.getItem('favoriteTools'); const hasSeenFirstFavoriteNotification = localStorage.getItem('hasSeenFirstFavoriteNotification') === 'true'; setFirstFavoriteAdded(hasSeenFirstFavoriteNotification); if (savedFavorites) { setFavoriteTools(JSON.parse(savedFavorites)); setShowFavorites(true); } }, []); // 收藏/取消收藏工具 const toggleFavorite = (toolCode: string, e: React.MouseEvent) => { e.stopPropagation(); // 检查是否要添加到收藏 const isAdding = !favoriteTools.includes(toolCode); const updatedFavorites = isAdding ? [...favoriteTools, toolCode] : favoriteTools.filter(code => code !== toolCode); setFavoriteTools(updatedFavorites); localStorage.setItem('favoriteTools', JSON.stringify(updatedFavorites)); // 更新收藏分类的显示状态 setShowFavorites(updatedFavorites.length > 0); // 如果是添加收藏且是第一次收藏,显示通知 if (isAdding && !firstFavoriteAdded) { setShowNotification(true); setFirstFavoriteAdded(true); localStorage.setItem('hasSeenFirstFavoriteNotification', 'true'); // 5秒后自动关闭通知 setTimeout(() => { setShowNotification(false); }, 5000); } }; // 关闭通知 const closeNotification = () => { setShowNotification(false); }; // 切换到收藏分类 const viewFavorites = () => { setActiveCategory('favorites'); }; // 过滤工具列表 const filteredTools = () => tools.filter(tool => { // 根据搜索词过滤 if (searchTerm && !t(`tools.${tool.code}.title`).toLowerCase().includes(searchTerm.toLowerCase()) && !t(`tools.${tool.code}.description`).toLowerCase().includes(searchTerm.toLowerCase()) && !tool.keywords?.some(keyword => keyword.toLowerCase().includes(searchTerm.toLowerCase()))) { return false; } // 过滤收藏分类 if (activeCategory === 'favorites') { return favoriteTools.includes(tool.code); } // 根据类别过滤 if (activeCategory !== "all" && !tool.category.includes(activeCategory)) { return false; } return true; }).sort((a, b) => { // 搜索优先级排序 if (searchTerm) { // 计算匹配得分 (越高越靠前) const getScore = (tool: typeof tools[0]) => { let score = 0; const term = searchTerm.toLowerCase(); const title = t(`tools.${tool.code}.title`).toLowerCase(); const description = t(`tools.${tool.code}.description`).toLowerCase(); // 标题匹配权重最高 if (title.includes(term)) { score += 100; // 标题精确匹配给额外加分 if (title === term) { score += 50; } } // 关键词匹配权重次之 if (tool.keywords?.some(keyword => keyword.toLowerCase() === term)) { // 关键词精确匹配 score += 80; } else if (tool.keywords?.some(keyword => keyword.toLowerCase().includes(term))) { // 关键词部分匹配 score += 60; } // 描述匹配权重稍低 if (description.includes(term)) { score += 40; } return score; }; const scoreA = getScore(a); const scoreB = getScore(b); // 按分数降序排列 if (scoreA !== scoreB) { return scoreB - scoreA; } } // 在全部工具视图中,将常用工具排在前面 if (activeCategory === "all") { const aIsCommon = a.category.includes('common'); const bIsCommon = b.category.includes('common'); if (aIsCommon && !bIsCommon) return -1; if (!aIsCommon && bIsCommon) return 1; } return 0; }); // 构建分类列表(添加动态的"我的收藏"分类) const allCategories = [ ...categories.slice(0, 2), // 全部工具和常用工具 ...(showFavorites ? [{ code: "favorites", name: t('common.favorites'), active: false }] : []), ...categories.slice(2) // 其余分类 ]; // 更新分类选择处理函数 const handleCategoryChange = (categoryCode: string) => { setActiveCategory(categoryCode); // 保存到 localStorage localStorage.setItem('lastActiveCategory', categoryCode); }; return (
{/* 通知提示 */} {showNotification && (
工具已添加到收藏夹,点击导航栏中的"我的收藏"查看
)} {/* 添加预加载进度提示(仅在开发环境显示) */} {process.env.NODE_ENV === 'development' && loadingProgress.total > 0 && (
预加载: {loadingProgress.current}/{loadingProgress.total}
)} {/* 头部 */}

{t('common.siteName')}

|

{t('common.siteDesc')}

{language === 'zh' && ( <>
)}
setSearchTerm(e.target.value)} /> {searchTerm && ( )}
{/* GitHub链接按钮 */} GitHub {/* 产品推荐下拉菜单 - 仅在中文语言下显示 */} {language === 'zh' && (
{showProductsDropdown && (
{recommendedProducts.map((product, index) => (
{product.title}
{product.description}
))}
)}
)}
{/* 主内容区域 */}
{/* 分类导航 - 独立导航栏 */}
{allCategories.map((category, index) => ( ))}
{/* 工具列表 */}
{filteredTools().map((tool, index) => (
navigateToTool(tool.code)} onMouseEnter={() => prefetchTool(tool.code)} > {/* 添加不可见的 Link 组件用于 Next.js 原生预加载 */}

{t(`tools.${tool.code}.title`)}

{tool.category.map((catCode, catIndex) => ( {t(`categories.${catCode}`)} ))}

{t(`tools.${tool.code}.description`)}

))}
{/* 底部 */}
); }