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 {