import { useEffect, useMemo, useRef, useState } from "react"; import { Breadcrumb, App, Tabs, Table, Tag } from "antd"; import { ReloadOutlined, DownloadOutlined, EditOutlined, DeleteOutlined, PlusOutlined, } from "@ant-design/icons"; import DetailHeader from "@/components/DetailHeader"; import { mapDataset, datasetTypeMap } from "../dataset.const"; import type { Dataset } from "@/pages/DataManagement/dataset.model"; import { Link, useNavigate, useParams } from "react-router"; import { useFilesOperation } from "./useFilesOperation"; import { createDatasetTagUsingPost, deleteDatasetByIdUsingDelete, downloadDatasetUsingGet, queryDatasetByIdUsingGet, queryDatasetsUsingGet, queryDatasetTagsUsingGet, querySimilarDatasetsUsingGet, updateDatasetByIdUsingPut, } from "../dataset.api"; import DataQuality from "./components/DataQuality"; import DataLineageFlow from "./components/DataLineageFlow"; import Overview from "./components/Overview"; import { Activity, Clock, File, FileType } from "lucide-react"; import EditDataset from "../Create/EditDataset"; import ImportConfiguration from "./components/ImportConfiguration"; import CardView from "@/components/CardView"; const SIMILAR_DATASET_LIMIT = 4; const SIMILAR_TAGS_PREVIEW_LIMIT = 3; export default function DatasetDetail() { const { id } = useParams(); // 获取动态路由参数 const navigate = useNavigate(); const [activeTab, setActiveTab] = useState("overview"); const { message } = App.useApp(); const [showEditDialog, setShowEditDialog] = useState(false); const [dataset, setDataset] = useState({} as Dataset); const [parentDataset, setParentDataset] = useState(null); const [childDatasets, setChildDatasets] = useState([]); const [childDatasetsLoading, setChildDatasetsLoading] = useState(false); const [similarDatasets, setSimilarDatasets] = useState([]); const [similarDatasetsLoading, setSimilarDatasetsLoading] = useState(false); const [similarTagNames, setSimilarTagNames] = useState([]); const similarRequestRef = useRef(0); const filesOperation = useFilesOperation(dataset); const [showUploadDialog, setShowUploadDialog] = useState(false); const normalizeTagNames = ( tags?: Array ) => { if (!tags || tags.length === 0) { return []; } const names = tags .map((tag) => (typeof tag === "string" ? tag : tag?.name)) .filter((name): name is string => !!name && name.trim().length > 0) .map((name) => name.trim()); return Array.from(new Set(names)); }; const fetchSimilarDatasets = async (currentDataset: Dataset) => { const requestId = similarRequestRef.current + 1; similarRequestRef.current = requestId; if (!currentDataset?.id) { setSimilarDatasets([]); setSimilarTagNames([]); setSimilarDatasetsLoading(false); return; } const tagNames = normalizeTagNames( currentDataset.tags as Array ); setSimilarTagNames(tagNames); setSimilarDatasets([]); if (tagNames.length === 0) { setSimilarDatasetsLoading(false); return; } setSimilarDatasetsLoading(true); try { const { data } = await querySimilarDatasetsUsingGet(currentDataset.id, { limit: SIMILAR_DATASET_LIMIT, }); if (similarRequestRef.current !== requestId) { return; } const list = Array.isArray(data) ? data : []; setSimilarDatasets(list.map((item) => mapDataset(item))); } catch (error) { console.error("Failed to fetch similar datasets:", error); } finally { if (similarRequestRef.current === requestId) { setSimilarDatasetsLoading(false); } } }; const similarTagsSummary = useMemo(() => { if (!similarTagNames || similarTagNames.length === 0) { return ""; } const visibleTags = similarTagNames.slice(0, SIMILAR_TAGS_PREVIEW_LIMIT); const hiddenCount = similarTagNames.length - visibleTags.length; if (hiddenCount > 0) { return `${visibleTags.join("、")} 等 ${similarTagNames.length} 个`; } return visibleTags.join("、"); }, [similarTagNames]); const navigateItems = useMemo(() => { const items = [ { title: 数据管理, }, ]; if (parentDataset) { items.push({ title: ( {parentDataset.name} ), }); } items.push({ title: dataset.name || "数据集详情", }); return items; }, [dataset, parentDataset]); const tabList = useMemo(() => { const items = [ { key: "overview", label: "概览", }, ]; if (!dataset?.parentDatasetId) { items.push({ key: "children", label: "关联数据集", }); } return items; }, [dataset?.parentDatasetId]); const handleCreateChildDataset = () => { if (!dataset?.id) { return; } navigate("/data/management/create", { state: { parentDatasetId: dataset.id }, }); }; const fetchChildDatasets = async (parentId?: string) => { if (!parentId) { setChildDatasets([]); return; } setChildDatasetsLoading(true); try { const { data: res } = await queryDatasetsUsingGet({ parentDatasetId: parentId, page: 1, size: 1000, }); const list = res?.content || res?.data || []; setChildDatasets(list.map((item) => mapDataset(item))); } finally { setChildDatasetsLoading(false); } }; const fetchDataset = async () => { if (!id) { return; } const { data } = await queryDatasetByIdUsingGet(id); const mapped = mapDataset(data); setDataset(mapped); fetchSimilarDatasets(mapped); if (data?.parentDatasetId) { const { data: parentData } = await queryDatasetByIdUsingGet( data.parentDatasetId ); setParentDataset(mapDataset(parentData)); setChildDatasets([]); } else { setParentDataset(null); await fetchChildDatasets(data?.id); } }; useEffect(() => { if (!id) { return; } fetchDataset(); }, [id]); useEffect(() => { if (dataset?.id) { filesOperation.fetchFiles("", 1, 10); // 从根目录开始,第一页 } // eslint-disable-next-line react-hooks/exhaustive-deps }, [dataset?.id]); useEffect(() => { if (dataset?.parentDatasetId && activeTab === "children") { setActiveTab("overview"); } }, [activeTab, dataset?.parentDatasetId]); const handleRefresh = async (showMessage = true, prefixOverride?: string) => { fetchDataset(); // 刷新当前目录,保持在当前页 const targetPrefix = prefixOverride !== undefined ? prefixOverride : filesOperation.pagination.prefix; filesOperation.fetchFiles( targetPrefix, filesOperation.pagination.current, filesOperation.pagination.pageSize ); if (showMessage) message.success({ content: "数据刷新成功" }); }; const handleDownload = async () => { await downloadDatasetUsingGet(dataset.id); message.success("文件下载成功"); }; const handleDeleteDataset = async () => { await deleteDatasetByIdUsingDelete(dataset.id); navigate("/data/management"); message.success("数据集删除成功"); }; useEffect(() => { const refreshData = (e: Event) => { const custom = e as CustomEvent<{ prefix?: string }>; const prefixOverride = custom.detail?.prefix; handleRefresh(false, prefixOverride); }; window.addEventListener("update:dataset", refreshData as EventListener); return () => { window.removeEventListener( "update:dataset", refreshData as EventListener ); }; }, []); // 基本信息描述项 const statistics = [ { icon: , key: "file", value: dataset?.fileCount || 0, }, { icon: , key: "size", value: dataset?.size || "0 B", }, { icon: , key: "type", value: datasetTypeMap[dataset?.datasetType as keyof typeof datasetTypeMap] ?.label || dataset?.type || "未知", }, { icon: , key: "time", value: dataset?.updatedAt, }, ]; // 数据集操作列表 const operations = [ ...(dataset?.id && !dataset.parentDatasetId ? [ { key: "create-child", label: "创建关联数据集", icon: , onClick: handleCreateChildDataset, }, ] : []), { key: "edit", label: "编辑", icon: , onClick: () => { setShowEditDialog(true); }, }, { key: "export", label: "导出", icon: , // isDropdown: true, // items: [ // { key: "alpaca", label: "Alpaca 格式", icon: }, // { key: "jsonl", label: "JSONL 格式", icon: }, // { key: "csv", label: "CSV 格式", icon: }, // { key: "coco", label: "COCO 格式", icon: }, // ], onClick: () => handleDownload(), }, { key: "refresh", label: "刷新", icon: , onClick: handleRefresh, }, { key: "delete", label: "删除", danger: true, confirm: { title: "确认删除该数据集?", description: "删除后该数据集将无法恢复,请谨慎操作。", okText: "删除", cancelText: "取消", okType: "danger", }, icon: , onClick: handleDeleteDataset, }, ]; const childColumns = [ { title: "名称", dataIndex: "name", key: "name", render: (_: string, record: Dataset) => ( {record.name} ), }, { title: "类型", dataIndex: "datasetType", key: "datasetType", width: 120, render: (value: string) => datasetTypeMap[value]?.label || "未知", }, { title: "状态", dataIndex: "status", key: "status", width: 120, render: (status) => status ? {status.label} : "-", }, { title: "文件数", dataIndex: "fileCount", key: "fileCount", width: 120, render: (value?: number) => value ?? 0, }, { title: "大小", dataIndex: "size", key: "size", width: 140, render: (value?: string) => value || "0 B", }, { title: "更新时间", dataIndex: "updatedAt", key: "updatedAt", width: 180, }, ]; return (
{/* Header */} { const res = await queryDatasetTagsUsingGet({ page: 0, pageSize: 1000, }); return res.data || []; }, onCreateAndTag: async (tagName) => { const res = await createDatasetTagUsingPost({ name: tagName }); if (res.data) { await updateDatasetByIdUsingPut(dataset.id, { tags: [...dataset.tags.map((tag) => tag.name), res.data.name], }); handleRefresh(); } }, onAddTag: async (tag) => { const res = await updateDatasetByIdUsingPut(dataset.id, { tags: [...dataset.tags.map((tag) => tag.name), tag], }); if (res.data) { handleRefresh(); } }, }} />
{activeTab === "overview" && ( setShowUploadDialog(true)} /> )} {activeTab === "children" && (

关联数据集

共 {childDatasets.length} 个
)} {activeTab === "lineage" && } {activeTab === "quality" && } {/* 相似数据集 */}

相似数据集

{similarTagsSummary && ( 匹配标签:{similarTagsSummary} )}
{ navigate(`/data/management/detail/${item.id}`); }} />
setShowUploadDialog(false)} prefix={filesOperation.pagination.prefix} updateEvent="update:dataset" /> setShowEditDialog(false)} onRefresh={handleRefresh} /> ); }