diff --git a/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx b/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx index 184ab89..8d0d2de 100644 --- a/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx +++ b/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx @@ -121,6 +121,7 @@ export default function CreateAnnotationTask({ const [customXml, setCustomXml] = useState(""); const [showPreview, setShowPreview] = useState(false); const [configMode, setConfigMode] = useState<"template" | "custom">("template"); + const [templateEditTab, setTemplateEditTab] = useState<"visual" | "xml">("visual"); const [selectAllClasses, setSelectAllClasses] = useState(true); const [selectedFilesMap, setSelectedFilesMap] = useState>({}); @@ -174,6 +175,7 @@ export default function CreateAnnotationTask({ setCustomXml(""); setShowPreview(false); setConfigMode("template"); + setTemplateEditTab("visual"); } }, [open, manualForm, autoForm]); @@ -222,23 +224,59 @@ export default function CreateAnnotationTask({ return xml; }; + // 从表单值同步生成 XML + const syncFormToXml = () => { + const objects = manualForm.getFieldValue("objects"); + const labels = manualForm.getFieldValue("labels"); + if (objects && objects.length > 0) { + const xml = generateXmlFromConfig(objects, labels || []); + setCustomXml(xml); + } + }; + + // 当选择模板时,加载模板配置到表单 + const handleTemplateSelect = (value: string, option: any) => { + if (option && option.config) { + setCustomXml(option.config); + } + + // 从模板列表中找到完整的模板数据 + const selectedTemplate = templates.find(t => t.id === value); + if (selectedTemplate?.configuration) { + const { objects, labels } = selectedTemplate.configuration; + manualForm.setFieldsValue({ + objects: objects || [{ name: "image", type: "Image", value: "$image" }], + labels: labels || [], + }); + } else if (option && option.config) { + // 如果没有结构化配置,设置默认值 + manualForm.setFieldsValue({ + objects: [{ name: "image", type: "Image", value: "$image" }], + labels: [], + }); + } + }; + const handleManualSubmit = async () => { try { const values = await manualForm.validateFields(); - + let finalLabelConfig = ""; + const objects = values.objects; + const labels = values.labels; if (configMode === "template") { - if (!customXml.trim()) { - message.error("请配置标注模板或选择一个现有模板"); - return; + // 模板模式:优先使用可视化配置生成 XML,回退到直接使用 XML 编辑器内容 + if (templateEditTab === "visual" && objects && objects.length > 0) { + finalLabelConfig = generateXmlFromConfig(objects, labels || []); + } else if (customXml.trim()) { + finalLabelConfig = customXml; + } else { + message.error("请配置标注模板或选择一个现有模板"); + return; } - finalLabelConfig = customXml; } else { - // Custom mode - const objects = values.objects; - const labels = values.labels; - + // 自定义模式 if (!objects || objects.length === 0) { message.error("请至少配置一个数据对象"); return; @@ -247,7 +285,6 @@ export default function CreateAnnotationTask({ message.error("请至少配置一个标签控件"); return; } - finalLabelConfig = generateXmlFromConfig(objects, labels); } @@ -328,15 +365,17 @@ 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: [], - }); - } + // 两种模式都需要初始化默认值 + const currentObjects = manualForm.getFieldValue("objects"); + if (!currentObjects || currentObjects.length === 0) { + manualForm.setFieldsValue({ + objects: [{ name: "image", type: "Image", value: "$image" }], + labels: [], + }); + } + // 切换到模板模式时,重置 tab 到可视化 + if (mode === "template") { + setTemplateEditTab("visual"); } }; @@ -451,24 +490,40 @@ export default function CreateAnnotationTask({ 自定义配置 - {configMode === 'template' && ( - - )} + {configMode === 'template' ? (