You've already forked DataMate
feat(data-management): 添加数据集父子层级结构功能
- 在OpenAPI规范中新增parentDatasetId字段用于层级过滤 - 实现数据集父子关系的创建、更新和删除逻辑 - 添加数据集移动时的路径重命名和文件路径前缀更新 - 增加子数据集数量验证防止误删父数据集 - 更新前端界面支持选择父数据集和导航显示 - 优化Python后端自动标注任务的路径处理逻辑 - 修改数据库表结构添加外键约束确保数据一致性
This commit is contained in:
@@ -17,6 +17,7 @@ export default function DatasetCreate() {
|
||||
description: "",
|
||||
datasetType: DatasetType.TEXT,
|
||||
tags: [],
|
||||
parentDatasetId: "",
|
||||
});
|
||||
|
||||
const handleSubmit = async () => {
|
||||
|
||||
@@ -26,6 +26,7 @@ export default function EditDataset({
|
||||
description: "",
|
||||
datasetType: DatasetType.TEXT,
|
||||
tags: [],
|
||||
parentDatasetId: "",
|
||||
});
|
||||
const fetchDataset = async () => {
|
||||
if (!open) return;
|
||||
@@ -36,6 +37,7 @@ export default function EditDataset({
|
||||
...newData,
|
||||
type: newData.type,
|
||||
tags: newData.tags.map((tag) => tag.name) || [],
|
||||
parentDatasetId: newData.parentDatasetId || "",
|
||||
};
|
||||
setNewDataset(updatedDataset);
|
||||
form.setFieldsValue(updatedDataset);
|
||||
|
||||
@@ -2,7 +2,7 @@ import RadioCard from "@/components/RadioCard";
|
||||
import { Input, Select, Form } from "antd";
|
||||
import { datasetTypes } from "../../dataset.const";
|
||||
import { useEffect, useState } from "react";
|
||||
import { queryDatasetTagsUsingGet } from "../../dataset.api";
|
||||
import { queryDatasetTagsUsingGet, queryDatasetsUsingGet } from "../../dataset.api";
|
||||
import {queryTasksUsingGet} from "@/pages/DataCollection/collection.apis.ts";
|
||||
|
||||
export default function BasicInformation({
|
||||
@@ -22,6 +22,9 @@ export default function BasicInformation({
|
||||
}[]
|
||||
>([]);
|
||||
const [collectionOptions, setCollectionOptions] = useState([]);
|
||||
const [parentDatasetOptions, setParentDatasetOptions] = useState<
|
||||
{ label: string; value: string }[]
|
||||
>([]);
|
||||
|
||||
// 获取标签
|
||||
const fetchTags = async () => {
|
||||
@@ -52,10 +55,36 @@ export default function BasicInformation({
|
||||
}
|
||||
};
|
||||
|
||||
const fetchParentDatasets = async () => {
|
||||
if (hidden.includes("parentDatasetId")) return;
|
||||
try {
|
||||
const { data: resData } = await queryDatasetsUsingGet({
|
||||
parentDatasetId: "",
|
||||
page: 1,
|
||||
size: 1000,
|
||||
});
|
||||
const currentDatasetId = data?.id;
|
||||
const rootDatasets = resData?.content || [];
|
||||
const options = rootDatasets
|
||||
.filter((dataset) => dataset.id !== currentDatasetId)
|
||||
.map((dataset) => ({
|
||||
label: dataset.name,
|
||||
value: dataset.id,
|
||||
}));
|
||||
setParentDatasetOptions([
|
||||
{ label: "根数据集", value: "" },
|
||||
...options,
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error("Error fetching parent datasets:", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchTags();
|
||||
fetchCollectionTasks();
|
||||
}, []);
|
||||
fetchParentDatasets();
|
||||
}, [data?.id, hidden.join(",")]);
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
@@ -70,6 +99,15 @@ export default function BasicInformation({
|
||||
<Input.TextArea placeholder="描述数据集的用途和内容" rows={3} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{!hidden.includes("parentDatasetId") && (
|
||||
<Form.Item name="parentDatasetId" label="父数据集">
|
||||
<Select
|
||||
className="w-full"
|
||||
options={parentDatasetOptions}
|
||||
placeholder="选择父数据集(仅支持一层)"
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{/* 数据集类型选择 - 使用卡片形式 */}
|
||||
{!hidden.includes("datasetType") && (
|
||||
|
||||
@@ -37,29 +37,51 @@ const tabList = [
|
||||
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<Dataset>({} as Dataset);
|
||||
const filesOperation = useFilesOperation(dataset);
|
||||
const [activeTab, setActiveTab] = useState("overview");
|
||||
const { message } = App.useApp();
|
||||
const [showEditDialog, setShowEditDialog] = useState(false);
|
||||
|
||||
const [dataset, setDataset] = useState<Dataset>({} as Dataset);
|
||||
const [parentDataset, setParentDataset] = useState<Dataset | null>(null);
|
||||
const filesOperation = useFilesOperation(dataset);
|
||||
|
||||
const [showUploadDialog, setShowUploadDialog] = useState(false);
|
||||
const navigateItems = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: <Link to="/data/management">数据管理</Link>,
|
||||
},
|
||||
{
|
||||
title: dataset.name || "数据集详情",
|
||||
},
|
||||
],
|
||||
[dataset]
|
||||
);
|
||||
const fetchDataset = async () => {
|
||||
const { data } = await queryDatasetByIdUsingGet(id as unknown as number);
|
||||
setDataset(mapDataset(data));
|
||||
};
|
||||
const navigateItems = useMemo(() => {
|
||||
const items = [
|
||||
{
|
||||
title: <Link to="/data/management">数据管理</Link>,
|
||||
},
|
||||
];
|
||||
if (parentDataset) {
|
||||
items.push({
|
||||
title: (
|
||||
<Link to={`/data/management/detail/${parentDataset.id}`}>
|
||||
{parentDataset.name}
|
||||
</Link>
|
||||
),
|
||||
});
|
||||
}
|
||||
items.push({
|
||||
title: dataset.name || "数据集详情",
|
||||
});
|
||||
return items;
|
||||
}, [dataset, parentDataset]);
|
||||
const fetchDataset = async () => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
const { data } = await queryDatasetByIdUsingGet(id);
|
||||
const mapped = mapDataset(data);
|
||||
setDataset(mapped);
|
||||
if (data?.parentDatasetId) {
|
||||
const { data: parentData } = await queryDatasetByIdUsingGet(
|
||||
data.parentDatasetId
|
||||
);
|
||||
setParentDataset(mapDataset(parentData));
|
||||
} else {
|
||||
setParentDataset(null);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchDataset();
|
||||
|
||||
@@ -21,7 +21,7 @@ export function useFilesOperation(dataset: Dataset) {
|
||||
|
||||
// 文件相关状态
|
||||
const [fileList, setFileList] = useState<DatasetFile[]>([]);
|
||||
const [selectedFiles, setSelectedFiles] = useState<number[]>([]);
|
||||
const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
|
||||
const [pagination, setPagination] = useState<{
|
||||
current: number;
|
||||
pageSize: number;
|
||||
|
||||
@@ -135,9 +135,9 @@ export default function DatasetManagementPage() {
|
||||
message.success("数据集下载成功");
|
||||
};
|
||||
|
||||
const handleDeleteDataset = async (id: number) => {
|
||||
if (!id) return;
|
||||
await deleteDatasetByIdUsingDelete(id);
|
||||
const handleDeleteDataset = async (id: string) => {
|
||||
if (!id) return;
|
||||
await deleteDatasetByIdUsingDelete(id);
|
||||
fetchData({ pageOffset: 0 });
|
||||
message.success("数据删除成功");
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@ export enum DataSource {
|
||||
}
|
||||
|
||||
export interface DatasetFile {
|
||||
id: number;
|
||||
id: string;
|
||||
fileName: string;
|
||||
size: string;
|
||||
uploadDate: string;
|
||||
@@ -41,10 +41,10 @@ export interface DatasetFile {
|
||||
}
|
||||
|
||||
export interface Dataset {
|
||||
id: number;
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
parentId?: number;
|
||||
parentDatasetId?: string;
|
||||
datasetType: DatasetType;
|
||||
status: DatasetStatus;
|
||||
size?: string;
|
||||
@@ -77,7 +77,7 @@ export interface ScheduleConfig {
|
||||
}
|
||||
|
||||
export interface DatasetTask {
|
||||
id: number;
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
type: string;
|
||||
|
||||
Reference in New Issue
Block a user