feat(annotation): 添加数据类型过滤功能到标签配置树编辑器

- 引入 DataType 枚举类型定义
- 根据数据类型动态过滤对象标签选项
- 在模板表单中添加数据类型监听
- 改进错误处理逻辑以提高类型安全性
- 集成数据类型参数到配置树编辑器组件
This commit is contained in:
2026-02-02 20:37:38 +08:00
parent daa63bdd13
commit b36fdd2438
2 changed files with 30 additions and 4 deletions

View File

@@ -36,6 +36,7 @@ const TemplateForm: React.FC<TemplateFormProps> = ({
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<TemplateFormProps> = ({
} 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<TemplateFormProps> = ({
value={labelConfig}
onChange={setLabelConfig}
height={420}
dataType={selectedDataType}
/>
</div>
</Form>

View File

@@ -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, string[]> = {
[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<XmlNode>(() => 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 {