diff --git a/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx b/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx index 63f72cf..184ab89 100644 --- a/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx +++ b/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx @@ -12,6 +12,7 @@ import DatasetFileTransfer from "@/components/business/DatasetFileTransfer"; import { DatasetType, type Dataset, type DatasetFile } from "@/pages/DataManagement/dataset.model"; import type { AnnotationTemplate } from "../../annotation.model"; import LabelStudioEmbed from "@/components/business/LabelStudioEmbed"; +import TemplateConfigurationForm from "../../components/TemplateConfigurationForm"; const { Option } = Select; @@ -119,6 +120,7 @@ export default function CreateAnnotationTask({ // Custom template state const [customXml, setCustomXml] = useState(""); const [showPreview, setShowPreview] = useState(false); + const [configMode, setConfigMode] = useState<"template" | "custom">("template"); const [selectAllClasses, setSelectAllClasses] = useState(true); const [selectedFilesMap, setSelectedFilesMap] = useState>({}); @@ -171,6 +173,7 @@ export default function CreateAnnotationTask({ setImageFileCount(0); setCustomXml(""); setShowPreview(false); + setConfigMode("template"); } }, [open, manualForm, autoForm]); @@ -183,13 +186,69 @@ export default function CreateAnnotationTask({ setImageFileCount(count); }, [selectedFilesMap]); + const generateXmlFromConfig = (objects: any[], labels: any[]) => { + let xml = '\n'; + + // Objects + if (objects) { + objects.forEach((obj: any) => { + xml += ` <${obj.type} name="${obj.name}" value="${obj.value}" />\n`; + }); + } + + // Controls + if (labels) { + labels.forEach((lbl: any) => { + let attrs = `name="${lbl.fromName}" toName="${lbl.toName}"`; + if (lbl.required) attrs += ' required="true"'; + + xml += ` <${lbl.type} ${attrs}>\n`; + + const options = lbl.type === 'Choices' ? lbl.options : lbl.labels; + if (options && options.length) { + options.forEach((opt: string) => { + if (lbl.type === 'Choices') { + xml += ` \n`; + } else { + xml += ` '; + return xml; + }; + const handleManualSubmit = async () => { try { const values = await manualForm.validateFields(); - if (!customXml.trim()) { - message.error("请配置标注模板或选择一个现有模板"); - return; + let finalLabelConfig = ""; + + if (configMode === "template") { + if (!customXml.trim()) { + message.error("请配置标注模板或选择一个现有模板"); + return; + } + finalLabelConfig = customXml; + } else { + // Custom mode + const objects = values.objects; + const labels = values.labels; + + if (!objects || objects.length === 0) { + message.error("请至少配置一个数据对象"); + return; + } + if (!labels || labels.length === 0) { + message.error("请至少配置一个标签控件"); + return; + } + + finalLabelConfig = generateXmlFromConfig(objects, labels); } setSubmitting(true); @@ -197,8 +256,8 @@ export default function CreateAnnotationTask({ name: values.name, description: values.description, datasetId: values.datasetId, - templateId: values.templateId, // Can be null/undefined if user just typed XML - labelConfig: customXml, // Pass the custom XML + templateId: configMode === 'template' ? values.templateId : undefined, + labelConfig: finalLabelConfig, }; await createAnnotationTaskUsingPost(requestData); message?.success?.("创建标注任务成功"); @@ -266,6 +325,21 @@ export default function CreateAnnotationTask({ } }; + const handleConfigModeChange = (e: any) => { + const mode = e.target.value; + setConfigMode(mode); + if (mode === "custom") { + // Set default values for custom configuration if empty + const currentObjects = manualForm.getFieldValue("objects"); + if (!currentObjects || currentObjects.length === 0) { + manualForm.setFieldsValue({ + objects: [{ name: "image", type: "Image", value: "$image" }], + labels: [], + }); + } + } + }; + return ( <> 标注配置 - + +
+ + 现有模板 + 自定义配置 + + + {configMode === 'template' && ( + + )} +
-
- - ({ + label: template.name, + value: template.id, + title: template.description, + config: template.labelConfig, + }))} + onChange={(value, option: any) => { + if (option && option.config) { + setCustomXml(option.config); + } + }} + optionRender={(option) => ( +
+
{option.label}
+ {option.data.title && ( +
+ {option.data.title} +
+ )} +
+ )} + /> +
- -