diff --git a/deployment/docker/label-studio/docker-compose.yml b/deployment/docker/label-studio/docker-compose.yml index 4026b6a..b45eeaf 100644 --- a/deployment/docker/label-studio/docker-compose.yml +++ b/deployment/docker/label-studio/docker-compose.yml @@ -52,8 +52,10 @@ volumes: label-studio-db: dataset_volume: name: datamate-dataset-volume + external: true networks: datamate: driver: bridge - name: datamate-network \ No newline at end of file + name: datamate-network + external: true \ No newline at end of file diff --git a/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx b/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx new file mode 100644 index 0000000..9f9ddbe --- /dev/null +++ b/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx @@ -0,0 +1,195 @@ +import { queryDatasetsUsingGet } from "@/pages/DataManagement/dataset.api"; +import { mapDataset } from "@/pages/DataManagement/dataset.const"; +import { Button, Form, Input, Modal, Select, message } from "antd"; +import TextArea from "antd/es/input/TextArea"; +import { useEffect, useState } from "react"; +import { createAnnotationTaskUsingPost, queryAnnotationTemplatesUsingGet } from "../../annotation.api"; +import { Dataset } from "@/pages/DataManagement/dataset.model"; +import type { AnnotationTemplate } from "../../annotation.model"; + +export default function CreateAnnotationTask({ + open, + onClose, + onRefresh, +}: { + open: boolean; + onClose: () => void; + onRefresh: () => void; +}) { + const [form] = Form.useForm(); + const [datasets, setDatasets] = useState([]); + const [templates, setTemplates] = useState([]); + const [submitting, setSubmitting] = useState(false); + const [nameManuallyEdited, setNameManuallyEdited] = useState(false); + + useEffect(() => { + if (!open) return; + const fetchData = async () => { + try { + // Fetch datasets + const { data: datasetData } = await queryDatasetsUsingGet({ + page: 0, + size: 1000, + }); + setDatasets(datasetData.content.map(mapDataset) || []); + + // Fetch templates + const templateResponse = await queryAnnotationTemplatesUsingGet({ + page: 1, + size: 100, // Backend max is 100 + }); + + // The API returns: {code, message, data: {content, total, page, ...}} + if (templateResponse.code === 200 && templateResponse.data) { + const fetchedTemplates = templateResponse.data.content || []; + console.log("Fetched templates:", fetchedTemplates); + setTemplates(fetchedTemplates); + } else { + console.error("Failed to fetch templates:", templateResponse); + setTemplates([]); + } + } catch (error) { + console.error("Error fetching data:", error); + setTemplates([]); + } + }; + fetchData(); + }, [open]); + + // Reset form and manual-edit flag when modal opens + useEffect(() => { + if (open) { + form.resetFields(); + setNameManuallyEdited(false); + } + }, [open, form]); + + const handleSubmit = async () => { + try { + const values = await form.validateFields(); + setSubmitting(true); + + // Send templateId instead of labelingConfig + const requestData = { + name: values.name, + description: values.description, + datasetId: values.datasetId, + templateId: values.templateId, + }; + + await createAnnotationTaskUsingPost(requestData); + message?.success?.("创建标注任务成功"); + onClose(); + onRefresh(); + } catch (err: any) { + console.error("Create annotation task failed", err); + const msg = err?.message || err?.data?.message || "创建失败,请稍后重试"; + (message as any)?.error?.(msg); + } finally { + setSubmitting(false); + } + }; + + return ( + + + + + } + width={800} + > +
+ {/* 数据集 与 标注工程名称 并排显示(数据集在左) */} +
+ + setNameManuallyEdited(true)} + /> + +
+ + {/* 描述变为可选 */} + +