From b36fdd24383825c96c3c9b39b1e2a538ade58c01 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Mon, 2 Feb 2026 20:37:38 +0800 Subject: [PATCH] =?UTF-8?q?feat(annotation):=20=E6=B7=BB=E5=8A=A0=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=B1=BB=E5=9E=8B=E8=BF=87=E6=BB=A4=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=88=B0=E6=A0=87=E7=AD=BE=E9=85=8D=E7=BD=AE=E6=A0=91=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入 DataType 枚举类型定义 - 根据数据类型动态过滤对象标签选项 - 在模板表单中添加数据类型监听 - 改进错误处理逻辑以提高类型安全性 - 集成数据类型参数到配置树编辑器组件 --- .../DataAnnotation/Template/TemplateForm.tsx | 10 ++++++-- .../TemplateConfigurationTreeEditor.tsx | 24 +++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/DataAnnotation/Template/TemplateForm.tsx b/frontend/src/pages/DataAnnotation/Template/TemplateForm.tsx index fcf0b71..1e251ab 100644 --- a/frontend/src/pages/DataAnnotation/Template/TemplateForm.tsx +++ b/frontend/src/pages/DataAnnotation/Template/TemplateForm.tsx @@ -36,6 +36,7 @@ const TemplateForm: React.FC = ({ const [form] = Form.useForm(); const [loading, setLoading] = useState(false); const [labelConfig, setLabelConfig] = useState(""); + const selectedDataType = Form.useWatch("dataType", form); useEffect(() => { if (visible && template && mode === "edit") { @@ -96,8 +97,12 @@ const TemplateForm: React.FC = ({ } else { message.error(response.message || `模板${mode === "create" ? "创建" : "更新"}失败`); } - } catch (error: any) { - if (error.errorFields) { + } catch (error: unknown) { + const hasErrorFields = + typeof error === "object" && + error !== null && + "errorFields" in error; + if (hasErrorFields) { message.error("请填写所有必填字段"); } else { message.error(`模板${mode === "create" ? "创建" : "更新"}失败`); @@ -195,6 +200,7 @@ const TemplateForm: React.FC = ({ value={labelConfig} onChange={setLabelConfig} height={420} + dataType={selectedDataType} /> diff --git a/frontend/src/pages/DataAnnotation/components/TemplateConfigurationTreeEditor.tsx b/frontend/src/pages/DataAnnotation/components/TemplateConfigurationTreeEditor.tsx index 6b7f01f..9dffc18 100644 --- a/frontend/src/pages/DataAnnotation/components/TemplateConfigurationTreeEditor.tsx +++ b/frontend/src/pages/DataAnnotation/components/TemplateConfigurationTreeEditor.tsx @@ -22,6 +22,7 @@ import { getObjectDisplayName, type LabelStudioTagConfig, } from "../annotation.tagconfig"; +import { DataType } from "../annotation.model"; const { Text, Title } = Typography; @@ -44,10 +45,22 @@ interface TemplateConfigurationTreeEditorProps { readOnly?: boolean; readOnlyStructure?: boolean; height?: number | string; + dataType?: DataType; } const DEFAULT_ROOT_TAG = "View"; const CHILD_TAGS = ["Label", "Choice", "Relation", "Item", "Path", "Channel"]; +const OBJECT_TAGS_BY_DATA_TYPE: Record = { + [DataType.TEXT]: ["Text", "Paragraphs", "Markdown"], + [DataType.IMAGE]: ["Image", "Bitmask"], + [DataType.AUDIO]: ["Audio", "AudioPlus"], + [DataType.VIDEO]: ["Video"], + [DataType.PDF]: ["PDF"], + [DataType.TIMESERIES]: ["Timeseries", "TimeSeries", "Vector"], + [DataType.CHAT]: ["Chat"], + [DataType.HTML]: ["HyperText", "Markdown"], + [DataType.TABLE]: ["Table", "Vector"], +}; const createId = () => `node_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`; @@ -436,6 +449,7 @@ const TemplateConfigurationTreeEditor = ({ readOnly = false, readOnlyStructure = false, height = 420, + dataType, }: TemplateConfigurationTreeEditorProps) => { const { config } = useTagConfig(false); const [tree, setTree] = useState(() => createEmptyTree()); @@ -512,11 +526,17 @@ const TemplateConfigurationTreeEditor = ({ const objectOptions = useMemo(() => { if (!config?.objects) return []; - return Object.keys(config.objects).map((tag) => ({ + const options = Object.keys(config.objects).map((tag) => ({ value: tag, label: getObjectDisplayName(tag), })); - }, [config]); + if (!dataType) return options; + const allowedTags = OBJECT_TAGS_BY_DATA_TYPE[dataType]; + if (!allowedTags) return options; + const allowedSet = new Set(allowedTags); + const filtered = options.filter((option) => allowedSet.has(option.value)); + return filtered.length > 0 ? filtered : options; + }, [config, dataType]); const tagOptions = useMemo(() => { const options = [] as {