diff --git a/frontend/src/pages/DataManagement/Create/EditDataset.tsx b/frontend/src/pages/DataManagement/Create/EditDataset.tsx index 2189f8a..1dac677 100644 --- a/frontend/src/pages/DataManagement/Create/EditDataset.tsx +++ b/frontend/src/pages/DataManagement/Create/EditDataset.tsx @@ -3,7 +3,7 @@ import { queryDatasetByIdUsingGet, updateDatasetByIdUsingPut, } from "../dataset.api"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { Dataset, DatasetType } from "../dataset.model"; import { App, Button, Form, Modal } from "antd"; @@ -28,7 +28,7 @@ export default function EditDataset({ tags: [], parentDatasetId: "", }); - const fetchDataset = async () => { + const fetchDataset = useCallback(async () => { if (!open) return; // 如果有id,说明是编辑模式 if (data && data.id) { @@ -42,14 +42,14 @@ export default function EditDataset({ setNewDataset(updatedDataset); form.setFieldsValue(updatedDataset); } - }; + }, [data, form, open]); useEffect(() => { fetchDataset(); - }, [data]); + }, [fetchDataset]); const handleValuesChange = (_, allValues) => { - setNewDataset({ ...newDataset, ...allValues }); + setNewDataset((prev) => ({ ...prev, ...allValues })); }; const handleSubmit = async () => { diff --git a/frontend/src/pages/DataManagement/Create/components/BasicInformation.tsx b/frontend/src/pages/DataManagement/Create/components/BasicInformation.tsx index a7271db..290f8ac 100644 --- a/frontend/src/pages/DataManagement/Create/components/BasicInformation.tsx +++ b/frontend/src/pages/DataManagement/Create/components/BasicInformation.tsx @@ -1,37 +1,33 @@ import RadioCard from "@/components/RadioCard"; import { Input, Select, Form } from "antd"; import { datasetTypes } from "../../dataset.const"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; +import type { Dispatch, SetStateAction } from "react"; import { queryDatasetTagsUsingGet, queryDatasetsUsingGet } from "../../dataset.api"; -import {queryTasksUsingGet} from "@/pages/DataCollection/collection.apis.ts"; +import { queryTasksUsingGet } from "@/pages/DataCollection/collection.apis.ts"; +import type { Dataset, TagItem } from "../../dataset.model"; export default function BasicInformation({ data, setData, hidden = [], }: { - data: any; - setData: any; + data: DatasetFormData; + setData: Dispatch>; hidden?: string[]; }) { - const [tagOptions, setTagOptions] = useState< - { - label: JSX.Element; - title: string; - options: { label: JSX.Element; value: string }[]; - }[] - >([]); - const [collectionOptions, setCollectionOptions] = useState([]); + const [tagOptions, setTagOptions] = useState([]); + const [collectionOptions, setCollectionOptions] = useState([]); const [parentDatasetOptions, setParentDatasetOptions] = useState< { label: string; value: string }[] >([]); - // 获取标签 - const fetchTags = async () => { + const fetchTags = useCallback(async () => { if (hidden.includes("tags")) return; try { const { data } = await queryDatasetTagsUsingGet(); - const customTags = data.map((tag) => ({ + const tags = Array.isArray(data) ? (data as TagItem[]) : []; + const customTags = tags.map((tag) => ({ label: tag.name, value: tag.name, })); @@ -39,13 +35,16 @@ export default function BasicInformation({ } catch (error) { console.error("Error fetching tags: ", error); } - }; + }, [hidden]); // 获取归集任务 - const fetchCollectionTasks = async () => { + const fetchCollectionTasks = useCallback(async () => { try { const res = await queryTasksUsingGet({ page: 0, size: 100 }); - const options = res.data.content.map((task: any) => ({ + const tasks = Array.isArray(res?.data?.content) + ? (res.data.content as CollectionTask[]) + : []; + const options = tasks.map((task) => ({ label: task.name, value: task.id, })); @@ -53,9 +52,9 @@ export default function BasicInformation({ } catch (error) { console.error("Error fetching collection tasks:", error); } - }; + }, []); - const fetchParentDatasets = async () => { + const fetchParentDatasets = useCallback(async () => { if (hidden.includes("parentDatasetId")) return; try { const { data: resData } = await queryDatasetsUsingGet({ @@ -64,7 +63,9 @@ export default function BasicInformation({ size: 1000, }); const currentDatasetId = data?.id; - const rootDatasets = resData?.content || []; + const rootDatasets = Array.isArray(resData?.content) + ? (resData.content as DatasetSummary[]) + : []; const options = rootDatasets .filter((dataset) => dataset.id !== currentDatasetId) .map((dataset) => ({ @@ -78,13 +79,13 @@ export default function BasicInformation({ } catch (error) { console.error("Error fetching parent datasets:", error); } - }; + }, [data?.id, hidden]); useEffect(() => { fetchTags(); fetchCollectionTasks(); fetchParentDatasets(); - }, [data?.id, hidden.join(",")]); + }, [fetchTags, fetchCollectionTasks, fetchParentDatasets]); return ( <> ); } + +type DatasetFormData = Partial & { + type?: string; + parentDatasetId?: string; +}; + +type DatasetTagOption = { + label: string; + value: string; +}; + +type SelectOption = { + label: string; + value: string; +}; + +type CollectionTask = { + id: string; + name: string; +}; + +type DatasetSummary = { + id: string; + name: string; +}; diff --git a/frontend/src/pages/DataManagement/Detail/useFilesOperation.ts b/frontend/src/pages/DataManagement/Detail/useFilesOperation.ts index 0263d4a..4d316ce 100644 --- a/frontend/src/pages/DataManagement/Detail/useFilesOperation.ts +++ b/frontend/src/pages/DataManagement/Detail/useFilesOperation.ts @@ -3,7 +3,7 @@ import type { DatasetFile, } from "@/pages/DataManagement/dataset.model"; import { App } from "antd"; -import { useState } from "react"; +import { useState } from "react"; import { deleteDatasetFileUsingDelete, downloadFileByIdUsingGet, @@ -34,16 +34,20 @@ export function useFilesOperation(dataset: Dataset) { const [previewContent, setPreviewContent] = useState(""); const [previewFileName, setPreviewFileName] = useState(""); - const fetchFiles = async (prefix?: string, current?, pageSize?) => { - // 如果明确传了 prefix(包括空字符串),使用传入的值;否则使用当前 pagination.prefix - const targetPrefix = prefix !== undefined ? prefix : (pagination.prefix || ''); - - const params: any = { - page: current !== undefined ? current : pagination.current, - size: pageSize !== undefined ? pageSize : pagination.pageSize, - isWithDirectory: true, - prefix: targetPrefix, - }; + const fetchFiles = async ( + prefix?: string, + current?: number, + pageSize?: number + ) => { + // 如果明确传了 prefix(包括空字符串),使用传入的值;否则使用当前 pagination.prefix + const targetPrefix = prefix !== undefined ? prefix : (pagination.prefix || ''); + + const params: DatasetFilesQueryParams = { + page: current !== undefined ? current : pagination.current, + size: pageSize !== undefined ? pageSize : pagination.pageSize, + isWithDirectory: true, + prefix: targetPrefix, + }; const { data } = await queryDatasetFilesUsingGet(id!, params); setFileList(data.content || []); @@ -86,28 +90,28 @@ export function useFilesOperation(dataset: Dataset) { setSelectedFiles([]); // 清空选中状态 }; - const handleShowFile = (file: any) => async () => { - // 请求文件内容并弹窗预览 - try { - const res = await fetch(`/api/datasets/${dataset.id}/file/${file.id}`); - const data = await res.text(); - setPreviewFileName(file.fileName); - setPreviewContent(data); - setPreviewVisible(true); - } catch (err) { - message.error({ content: "文件预览失败" }); - } - }; - - const handleDeleteFile = async (file) => { - try { - await deleteDatasetFileUsingDelete(dataset.id, file.id); - fetchFiles(); // 刷新文件列表 - message.success({ content: `文件 ${file.fileName} 已删除` }); - } catch (error) { - message.error({ content: `文件 ${file.fileName} 删除失败` }); - } - }; + const handleShowFile = (file: DatasetFile) => async () => { + // 请求文件内容并弹窗预览 + try { + const res = await fetch(`/api/datasets/${dataset.id}/file/${file.id}`); + const data = await res.text(); + setPreviewFileName(file.fileName); + setPreviewContent(data); + setPreviewVisible(true); + } catch { + message.error({ content: "文件预览失败" }); + } + }; + + const handleDeleteFile = async (file: DatasetFile) => { + try { + await deleteDatasetFileUsingDelete(dataset.id, file.id); + fetchFiles(); // 刷新文件列表 + message.success({ content: `文件 ${file.fileName} 已删除` }); + } catch { + message.error({ content: `文件 ${file.fileName} 删除失败` }); + } + }; const handleBatchExport = () => { if (selectedFiles.length === 0) { @@ -158,29 +162,36 @@ export function useFilesOperation(dataset: Dataset) { // 创建成功后刷新当前目录,重置到第一页 await fetchFiles(currentPrefix, 1, pagination.pageSize); message.success({ content: `文件夹 ${directoryName} 创建成功` }); - } catch (error) { - message.error({ content: `文件夹 ${directoryName} 创建失败` }); - throw error; - } - }, - handleDownloadDirectory: async (directoryPath: string, directoryName: string) => { - try { - await downloadDirectoryUsingGet(dataset.id, directoryPath); - message.success({ content: `文件夹 ${directoryName} 下载成功` }); - } catch (error) { - message.error({ content: `文件夹 ${directoryName} 下载失败` }); - } - }, - handleDeleteDirectory: async (directoryPath: string, directoryName: string) => { - try { - await deleteDirectoryUsingDelete(dataset.id, directoryPath); - // 删除成功后刷新当前目录 - const currentPrefix = pagination.prefix || ""; - await fetchFiles(currentPrefix, 1, pagination.pageSize); - message.success({ content: `文件夹 ${directoryName} 已删除` }); - } catch (error) { - message.error({ content: `文件夹 ${directoryName} 删除失败` }); - } - }, - }; -} + } catch (caught) { + message.error({ content: `文件夹 ${directoryName} 创建失败` }); + throw caught; + } + }, + handleDownloadDirectory: async (directoryPath: string, directoryName: string) => { + try { + await downloadDirectoryUsingGet(dataset.id, directoryPath); + message.success({ content: `文件夹 ${directoryName} 下载成功` }); + } catch { + message.error({ content: `文件夹 ${directoryName} 下载失败` }); + } + }, + handleDeleteDirectory: async (directoryPath: string, directoryName: string) => { + try { + await deleteDirectoryUsingDelete(dataset.id, directoryPath); + // 删除成功后刷新当前目录 + const currentPrefix = pagination.prefix || ""; + await fetchFiles(currentPrefix, 1, pagination.pageSize); + message.success({ content: `文件夹 ${directoryName} 已删除` }); + } catch { + message.error({ content: `文件夹 ${directoryName} 删除失败` }); + } + }, + }; +} + +interface DatasetFilesQueryParams { + page: number; + size: number; + isWithDirectory: boolean; + prefix: string; +} diff --git a/frontend/src/pages/DataManagement/Home/DataManagement.tsx b/frontend/src/pages/DataManagement/Home/DataManagement.tsx index 49b693d..fa0aaea 100644 --- a/frontend/src/pages/DataManagement/Home/DataManagement.tsx +++ b/frontend/src/pages/DataManagement/Home/DataManagement.tsx @@ -8,7 +8,8 @@ import { } from "@ant-design/icons"; import TagManager from "@/components/business/TagManagement"; import { Link, useNavigate } from "react-router"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; +import type { ReactNode } from "react"; import { SearchControls } from "@/components/SearchControls"; import CardView from "@/components/CardView"; import type { Dataset } from "@/pages/DataManagement/dataset.model"; @@ -35,19 +36,19 @@ export default function DatasetManagementPage() { const [editDatasetOpen, setEditDatasetOpen] = useState(false); const [currentDataset, setCurrentDataset] = useState(null); const [showUploadDialog, setShowUploadDialog] = useState(false); - const [statisticsData, setStatisticsData] = useState({ - count: {}, - size: {}, - }); + const [statisticsData, setStatisticsData] = useState({ + count: [], + size: [], + }); async function fetchStatistics() { const { data } = await getDatasetStatisticsUsingGet(); - const statistics = { - size: [ - { - title: "数据集总数", - value: data?.totalDatasets || 0, + const statistics: StatisticsData = { + size: [ + { + title: "数据集总数", + value: data?.totalDatasets || 0, }, { title: "文件总数", @@ -75,10 +76,10 @@ export default function DatasetManagementPage() { title: "视频", value: data?.count?.video || 0, }, - ], - }; - setStatisticsData(statistics); - } + ], + }; + setStatisticsData(statistics); + } const [tags, setTags] = useState([]); @@ -222,12 +223,12 @@ export default function DatasetManagementPage() { title: "状态", dataIndex: "status", key: "status", - render: (status: any) => { - return ( - - {status?.label} - - ); + render: (status: DatasetStatusMeta) => { + return ( + + {status?.label} + + ); }, width: 120, }, @@ -273,10 +274,10 @@ export default function DatasetManagementPage() { key: "actions", width: 200, fixed: "right", - render: (_: any, record: Dataset) => ( -
- {operations.map((op) => ( - + render: (_: unknown, record: Dataset) => ( +
+ {operations.map((op) => ( +
@@ -395,5 +396,22 @@ export default function DatasetManagementPage() { updateEvent="update:datasets" />
- ); -} + ); +} + +type StatisticsItem = { + title: string; + value: number | string; +}; + +type StatisticsData = { + count: StatisticsItem[]; + size: StatisticsItem[]; +}; + +type DatasetStatusMeta = { + label: string; + value: string; + color: string; + icon: ReactNode; +}; diff --git a/frontend/src/pages/DataManagement/dataset.model.ts b/frontend/src/pages/DataManagement/dataset.model.ts index 92103f6..6e51fc2 100644 --- a/frontend/src/pages/DataManagement/dataset.model.ts +++ b/frontend/src/pages/DataManagement/dataset.model.ts @@ -58,6 +58,15 @@ export interface Dataset { distribution?: Record>; } +export interface DatasetImportConfig { + source?: DataSource | string; + target?: DataSource | string; + dataSource?: string; + splitByLine?: boolean; + hasArchive?: boolean; + [key: string]: string | number | boolean | null | undefined; +} + export interface TagItem { id: string; name: string; @@ -84,7 +93,7 @@ export interface DatasetTask { status: "importing" | "waiting" | "completed" | "failed"; progress: number; createdAt: string; - importConfig: any; + importConfig: DatasetImportConfig; scheduleConfig: ScheduleConfig; nextExecution?: string; lastExecution?: string;