"use client"; import { useState, useEffect, useCallback } from "react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Package, FileCode, Code, FileClock } from "lucide-react"; import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"; import { SearchInput } from "@/components/common/search-input"; import { ResponsiveTabs, type ResponsiveTabItem } from "@/components/common/responsive-tabs"; import { trpc } from "@/lib/trpc"; import { Skeleton } from "@/components/ui/skeleton"; import { PackageAnalyzeDialog, PackageAnalyzeTrigger } from "./components/PackageAnalyzeDialog"; import { PackageDetailSheet } from "./components/PackageDetailSheet"; import { toast } from 'sonner' import type { PackageData } from "@/server/routers/dev/arch"; import { formatDate } from "@/lib/format"; // 依赖包卡片组件 function PackageCard({ pkg, onClick }: { pkg: PackageData; onClick: () => void }) { return (
{pkg.homepage ? ( {pkg.name} ) : ( {pkg.name} )} {pkg.repositoryUrl && ( )} v{pkg.version}
{pkg.name} v{pkg.version}
更新于 {formatDate(pkg.modifiedAt)}
{pkg.description}
核心功能

{pkg.projectRoleSummary}

{pkg.relatedFileCount} 个文件
{formatDate(pkg.lastAnalyzedAt)}
最近分析时间
); } export default function ArchPackagePage() { const [searchQuery, setSearchQuery] = useState(''); // 用于刷新数据的 utils const utils = trpc.useUtils() // 获取所有包类型 const { data: pkgTypes, isLoading: isLoadingTypes } = trpc.devArch!.getAllPkgTypes.useQuery(); // 获取所有依赖包数据 const { data: packagesByType, isLoading: isLoadingPackages } = trpc.devArch!.getAllPackages.useQuery(); // 刷新依赖包列表 const handleRefreshPackages = useCallback(() => { utils.devArch!.getAllPackages.invalidate() utils.devArch!.getAllPkgTypes.invalidate() }, [utils]) // 使用第一个包类型作为默认激活标签 const [activeTab, setActiveTab] = useState(''); // 分析对话框状态 const [isAnalyzeDialogOpen, setIsAnalyzeDialogOpen] = useState(false) const [analyzeJobId, setAnalyzeJobId] = useState(null) // 详情Sheet状态 const [selectedPackage, setSelectedPackage] = useState(null) const [isDetailSheetOpen, setIsDetailSheetOpen] = useState(false) // 处理卡片点击 const handleCardClick = useCallback((pkg: PackageData) => { setSelectedPackage(pkg) setIsDetailSheetOpen(true) }, []) // 启动依赖包分析 mutation const analyzeMutation = trpc.devArch!.startAnalyzePackages.useMutation({ onSuccess: (data) => { // 打开进度对话框 setAnalyzeJobId(String(data.jobId)) setIsAnalyzeDialogOpen(true) }, onError: (error) => { toast.error(error.message || '启动依赖包分析失败') }, }) // 启动分析 const handleStartAnalyze = () => { analyzeMutation.mutate() } // 当包类型加载完成后,设置默认激活标签 useEffect(() => { if (pkgTypes && pkgTypes.length > 0 && !activeTab) { setActiveTab(pkgTypes[0].id); } }, [pkgTypes, activeTab]); const isLoading = isLoadingTypes || isLoadingPackages; // 按优先级搜索过滤:name > description > projectRoleSummary > primaryUsagePattern const getFilteredPackages = useCallback((typeId: string) => { const packages = packagesByType?.[typeId] || []; if (!searchQuery) return packages; const query = searchQuery.toLowerCase(); // 计算每个包的匹配优先级 const packagesWithPriority = packages.map((pkg) => { let priority = 0; if (pkg.name.toLowerCase().includes(query)) { priority = 4; } else if (pkg.description.toLowerCase().includes(query)) { priority = 3; } else if (pkg.projectRoleSummary.toLowerCase().includes(query)) { priority = 2; } else if (pkg.primaryUsagePattern.toLowerCase().includes(query)) { priority = 1; } return { pkg, priority }; }); // 过滤出有匹配的包,并按优先级排序 return packagesWithPriority .filter(({ priority }) => priority > 0) .sort((a, b) => b.priority - a.priority) .map(({ pkg }) => pkg); }, [packagesByType, searchQuery]); // 将包类型转换为标签项 const tabItems: ResponsiveTabItem[] = pkgTypes?.map((type) => ({ id: type.id, name: type.name, description: type.description, count: packagesByType?.[type.id]?.length || 0, })) || []; // 仅当pkgTypes完成加载且为空时才显示"暂无依赖包数据" if (!isLoading && (!pkgTypes || pkgTypes.length === 0)) { return (

暂无依赖包数据

); } return ( <> {isLoading ? (
{[1, 2, 3].map((i) => ( ))}
) : ( {tabItems.map((item) => { const filteredPackages = getFilteredPackages(item.id); return (
{/* 搜索栏和操作按钮 */}
{/* 包列表 */} {filteredPackages.length > 0 ? (
{filteredPackages.map((pkg) => ( handleCardClick(pkg)} /> ))}
) : (

{searchQuery ? '未找到匹配的依赖包' : '暂无依赖包数据'}

)}
); })} )} {/* 依赖包分析进度对话框 */} {/* 依赖包详情Sheet */} ); }