From 2229eb218de30601945a7766fd4b8181b23302cf Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Mon, 19 Jan 2026 20:25:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(annotation):=20=E6=B7=BB=E5=8A=A0=E6=A0=87?= =?UTF-8?q?=E6=B3=A8=E4=BB=BB=E5=8A=A1=E7=BC=96=E8=BE=91=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增编辑模式支持,通过 editTask 属性控制 - 添加 getAnnotationTaskByIdUsingGet 和 updateAnnotationTaskByIdUsingPut API 接口 - 实现编辑模式下的任务详情加载和表单填充 - 编辑模式下禁用数据集修改和配置模式切换 - 更新模态框标题为动态显示(创建/编辑) - 在任务列表操作菜单中添加编辑按钮 - 编辑模式下只允许修改标签取值,限制模板结构调整 - 添加任务详情加载状态显示 --- .../components/CreateAnnotationTaskDialog.tsx | 132 +++++++++++++++--- .../DataAnnotation/Home/DataAnnotation.tsx | 20 ++- .../pages/DataAnnotation/annotation.api.ts | 8 ++ 3 files changed, 137 insertions(+), 23 deletions(-) diff --git a/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx b/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx index 840ebd1..8ae25dc 100644 --- a/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx +++ b/frontend/src/pages/DataAnnotation/Create/components/CreateAnnotationTaskDialog.tsx @@ -6,22 +6,30 @@ import { useEffect, useState } from "react"; import { Eye } from "lucide-react"; import { createAnnotationTaskUsingPost, + getAnnotationTaskByIdUsingGet, + updateAnnotationTaskByIdUsingPut, queryAnnotationTemplatesUsingGet, } from "../../annotation.api"; import { type Dataset } from "@/pages/DataManagement/dataset.model"; -import type { AnnotationTemplate } from "../../annotation.model"; +import type { AnnotationTemplate, AnnotationTask } from "../../annotation.model"; import LabelStudioEmbed from "@/components/business/LabelStudioEmbed"; import TemplateConfigurationForm from "../../components/TemplateConfigurationForm"; +interface AnnotationTaskDialogProps { + open: boolean; + onClose: () => void; + onRefresh: () => void; + /** 编辑模式:传入要编辑的任务数据 */ + editTask?: AnnotationTask | null; +} + export default function CreateAnnotationTask({ open, onClose, onRefresh, -}: { - open: boolean; - onClose: () => void; - onRefresh: () => void; -}) { + editTask, +}: AnnotationTaskDialogProps) { + const isEditMode = !!editTask; const { message } = App.useApp(); const [manualForm] = Form.useForm(); const [datasets, setDatasets] = useState([]); @@ -34,6 +42,8 @@ export default function CreateAnnotationTask({ const [showPreview, setShowPreview] = useState(false); const [previewTaskData, setPreviewTaskData] = useState>({}); const [configMode, setConfigMode] = useState<"template" | "custom">("template"); + // 模板编辑模式切换(可视化 vs XML) + const [templateEditTab, setTemplateEditTab] = useState<"visual" | "xml">("visual"); // 是否已选择模板(用于启用受限编辑模式) const [hasSelectedTemplate, setHasSelectedTemplate] = useState(false); @@ -51,6 +61,9 @@ export default function CreateAnnotationTask({ const [previewFileType, setPreviewFileType] = useState<"text" | "image" | "video" | "audio">("text"); const [previewMediaUrl, setPreviewMediaUrl] = useState(""); + // 任务详情加载状态(编辑模式) + const [taskDetailLoading, setTaskDetailLoading] = useState(false); + useEffect(() => { if (!open) return; const fetchData = async () => { @@ -83,7 +96,7 @@ export default function CreateAnnotationTask({ fetchData(); }, [open]); - // Reset form and manual-edit flag when modal opens + // Reset form and manual-edit flag when modal opens, or load task data in edit mode useEffect(() => { if (open) { manualForm.resetFields(); @@ -91,12 +104,58 @@ export default function CreateAnnotationTask({ setCustomXml(""); setShowPreview(false); setPreviewTaskData({}); - setConfigMode("template"); - setHasSelectedTemplate(false); - setSelectedDatasetId(null); setDatasetPreviewData([]); + + if (isEditMode && editTask) { + // 编辑模式:加载任务详情 + setTaskDetailLoading(true); + getAnnotationTaskByIdUsingGet(editTask.id) + .then((res: any) => { + if (res.code === 200 && res.data) { + const taskDetail = res.data; + // 填充基本信息 + manualForm.setFieldsValue({ + name: taskDetail.name, + description: taskDetail.description, + datasetId: taskDetail.datasetId, + }); + setSelectedDatasetId(taskDetail.datasetId); + + // 填充模板配置 + if (taskDetail.configuration) { + const { objects, labels } = taskDetail.configuration; + manualForm.setFieldsValue({ + objects: objects || [], + labels: labels || [], + }); + } + + // 设置 XML 配置用于预览 + if (taskDetail.labelConfig) { + setCustomXml(taskDetail.labelConfig); + } + + // 编辑模式始终使用 custom 配置模式(不改变结构,只改标签) + setConfigMode("custom"); + // 编辑模式下启用受限编辑 + setHasSelectedTemplate(true); + } + }) + .catch((err) => { + console.error("Failed to load task detail:", err); + message.error("加载任务详情失败"); + }) + .finally(() => { + setTaskDetailLoading(false); + }); + } else { + // 创建模式:重置为默认状态 + setConfigMode("template"); + setHasSelectedTemplate(false); + setSelectedDatasetId(null); + } } - }, [open, manualForm]); + }, [open, manualForm, isEditMode, editTask, message]); // 预览数据集 const handlePreviewDataset = async () => { @@ -342,14 +401,27 @@ export default function CreateAnnotationTask({ datasetId: values.datasetId, templateId: configMode === 'template' ? values.templateId : undefined, labelConfig: finalLabelConfig, + // 编辑模式需要传递配置结构,用于后端保存 + configuration: { + objects: objects || [], + labels: labels || [], + }, }; - await createAnnotationTaskUsingPost(requestData); - message.success("创建标注任务成功"); + + if (isEditMode && editTask) { + // 编辑模式:调用更新接口 + await updateAnnotationTaskByIdUsingPut(editTask.id, requestData); + message.success("更新标注任务成功"); + } else { + // 创建模式:调用创建接口 + await createAnnotationTaskUsingPost(requestData); + message.success("创建标注任务成功"); + } onClose(); onRefresh(); } catch (err: any) { - console.error("Create annotation task failed", err); - const msg = err?.message || err?.data?.message || "创建失败,请稍后重试"; + console.error(isEditMode ? "Update annotation task failed" : "Create annotation task failed", err); + const msg = err?.message || err?.data?.message || (isEditMode ? "更新失败,请稍后重试" : "创建失败,请稍后重试"); message.error(msg); } finally { setSubmitting(false); @@ -378,7 +450,8 @@ export default function CreateAnnotationTask({