"use client"; import { useState, useEffect, useCallback, useRef } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { FavoriteButton } from "@/components/common/FavoriteButton"; import { PRICING_LABEL, PRICING_COLOR } from "@/lib/constants"; interface Category { id: number; name: string; slug: string; children: Category[]; } interface Tool { id: number; name: string; slug: string; description: string; logoUrl: string | null; websiteUrl: string; pricingModel: string; viewCount: number; favorited?: boolean; category: { id: number; name: string; slug: string } | null; } const pricingDot: Record = { free: "bg-emerald-400", freemium: "bg-blue-400", paid: "bg-amber-400", subscription: "bg-purple-400", }; const pricingOptions = [ { value: "", label: "全部" }, { value: "free", label: "免费" }, { value: "freemium", label: "免费增值" }, { value: "paid", label: "付费" }, { value: "subscription", label: "订阅" }, ]; const categoryIcons: Record = { "文本生成": "M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7", "图像生成": "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z", "视频生成": "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z", "音频处理": "M9 19V6l12-3v13M9 19c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zm12-3c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2z", "代码助手": "M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4", "办公效率": "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2", "AI 角色": "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z", "设计工具": "M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01", "数据分析": "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z", "内容检测": "M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z", }; interface ToolsContentProps { initialCategories: Category[]; initialTools: Tool[]; initialTotal: number; } export default function ToolsContent({ initialCategories, initialTools, initialTotal }: ToolsContentProps) { const router = useRouter(); const searchParams = useSearchParams(); const [categories] = useState(initialCategories); const [tools, setTools] = useState(initialTools); const [total, setTotal] = useState(initialTotal); const [loading, setLoading] = useState(false); const [mobileFilterOpen, setMobileFilterOpen] = useState(false); const page = parseInt(searchParams.get("page") || "1"); const search = searchParams.get("search") || ""; const categoryId = searchParams.get("categoryId") || ""; const pricingModel = searchParams.get("pricingModel") || ""; const pageSize = 12; const [searchInput, setSearchInput] = useState(search); const isFirstRender = useRef(true); const fetchTools = useCallback(async () => { setLoading(true); try { const params = new URLSearchParams({ page: page.toString(), pageSize: pageSize.toString(), }); if (search) params.set("search", search); if (categoryId) params.set("categoryId", categoryId); if (pricingModel) params.set("pricingModel", pricingModel); const res = await fetch(`/api/tools?${params}`); const data = await res.json(); setTools(data.tools || []); setTotal(data.total || 0); } catch { setTools([]); } finally { setLoading(false); } }, [page, search, categoryId, pricingModel]); useEffect(() => { if (isFirstRender.current) { isFirstRender.current = false; return; } fetchTools(); }, [fetchTools]); const updateParams = (updates: Record) => { const params = new URLSearchParams(searchParams.toString()); for (const [key, value] of Object.entries(updates)) { if (value) { params.set(key, value); } else { params.delete(key); } } params.delete("page"); router.push(`/tools?${params.toString()}`); }; const totalPages = Math.ceil(total / pageSize); const activeCategoryName = categoryId ? categories.find((c) => c.id.toString() === categoryId)?.name || "" : "全部分类"; const activePricingName = pricingModel ? pricingOptions.find((p) => p.value === pricingModel)?.label || "" : ""; const Sidebar = () => (

分类筛选

定价模型

💡 点击分类或定价模型进行筛选,支持组合筛选

); return (
全部工具

探索AI工具

发现最适合你的AI工具,提升工作效率与创造力

setSearchInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && updateParams({ search: searchInput }) } placeholder="搜索AI工具..." className="h-11 w-full rounded-xl border border-border/50 bg-muted/50 pl-10 pr-4 text-sm text-foreground placeholder:text-muted-foreground/50 focus:border-primary/40 focus:bg-white focus:outline-none focus:ring-2 focus:ring-primary/15 transition-all duration-200" /> {searchInput && ( )}
{mobileFilterOpen && (
)}
{search ? `搜索 "${search}"` : activeCategoryName} {activePricingName && ( <> · {activePricingName} )} · {total} 个工具
{(categoryId || pricingModel || search) && ( )}
{loading ? (
{Array.from({ length: 6 }).map((_, i) => (
))}
) : tools.length === 0 ? (

暂无匹配的工具

试试其他关键词或筛选条件

) : ( <>
{tools.map((tool, i) => ( router.push(`/tools/${tool.slug}`)} >
e.stopPropagation()}>
{tool.logoUrl ? ( ) : (
{tool.name.charAt(0)}
)}

{tool.name}

{PRICING_LABEL[tool.pricingModel] || tool.pricingModel} {tool.category && ( {tool.category.name} )}

{tool.description || "暂无简介"}

{tool.viewCount}
))}
{totalPages > 1 && (
{Array.from({ length: Math.min(totalPages, 5) }, (_, i) => { let pageNum: number; if (totalPages <= 5) { pageNum = i + 1; } else if (page <= 3) { pageNum = i + 1; } else if (page >= totalPages - 2) { pageNum = totalPages - 4 + i; } else { pageNum = page - 2 + i; } return ( ); })}
)} )}
); }