You've already forked DataMate
fix(annotation): 标注配置可视化编辑器根据父节点类型限制子标签选项
根据选中节点类型动态过滤"添加子节点"和"添加同级节点"下拉选项: - 标注控件(如 Choices/RectangleLabels)仅允许添加对应的子标签(Choice/Label) - 无子节点的控件(如 TextArea/Rating)和数据对象标签禁用添加子节点 - Choice 节点允许嵌套 Choice(支持 Taxonomy 层级结构) - View 容器允许添加所有标签类型但排除裸子标签 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -566,6 +566,76 @@ const TemplateConfigurationTreeEditor = ({
|
||||
return options;
|
||||
}, [objectOptions, controlOptions]);
|
||||
|
||||
type TagOptionGroup = { label: string; options: { value: string; label: string }[] };
|
||||
|
||||
const getAllowedChildOptions = useCallback(
|
||||
(node: XmlNode): TagOptionGroup[] => {
|
||||
if (!config) return tagOptions;
|
||||
|
||||
const controlConfig = config.controls?.[node.tag];
|
||||
const objectConfig = config.objects?.[node.tag];
|
||||
|
||||
// Control with child_tag: only allow that specific child tag
|
||||
if (controlConfig?.requires_children && controlConfig.child_tag) {
|
||||
const childTag = controlConfig.child_tag;
|
||||
return [
|
||||
{
|
||||
label: "子标签",
|
||||
options: [
|
||||
{
|
||||
value: childTag,
|
||||
label: COMMON_TAG_DISPLAY_NAMES[childTag] || childTag,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// Control without requires_children: no children allowed
|
||||
if (controlConfig && !controlConfig.requires_children) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Object tag: no children
|
||||
if (objectConfig) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Child tag: Choice allows nested Choice (for Taxonomy), others don't
|
||||
if (CHILD_TAGS.includes(node.tag)) {
|
||||
if (node.tag === "Choice") {
|
||||
return [
|
||||
{
|
||||
label: "子标签",
|
||||
options: [
|
||||
{
|
||||
value: "Choice",
|
||||
label: COMMON_TAG_DISPLAY_NAMES.Choice || "Choice",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// View or other containers: show all except bare child tags
|
||||
return tagOptions.filter((group) => group.label !== "子标签");
|
||||
},
|
||||
[config, tagOptions]
|
||||
);
|
||||
|
||||
const childTagOptions = useMemo(
|
||||
() => getAllowedChildOptions(selectedNode),
|
||||
[getAllowedChildOptions, selectedNode]
|
||||
);
|
||||
|
||||
const siblingTagOptions = useMemo(() => {
|
||||
const { parent } = findNodeWithParent(tree, selectedNode.id);
|
||||
if (!parent) return [];
|
||||
return getAllowedChildOptions(parent);
|
||||
}, [tree, selectedNode, getAllowedChildOptions]);
|
||||
|
||||
const getTagDisplayName = useCallback(
|
||||
(tag: string) => {
|
||||
if (config?.objects?.[tag]) return getObjectDisplayName(tag);
|
||||
@@ -796,21 +866,21 @@ const TemplateConfigurationTreeEditor = ({
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Select
|
||||
placeholder="添加子节点"
|
||||
options={tagOptions}
|
||||
options={childTagOptions}
|
||||
value={null}
|
||||
onChange={(value) => {
|
||||
handleAddNode(value, "child");
|
||||
}}
|
||||
disabled={isStructureLocked}
|
||||
disabled={isStructureLocked || childTagOptions.length === 0}
|
||||
/>
|
||||
<Select
|
||||
placeholder="添加同级节点"
|
||||
options={tagOptions}
|
||||
options={siblingTagOptions}
|
||||
value={null}
|
||||
onChange={(value) => {
|
||||
handleAddNode(value, "sibling");
|
||||
}}
|
||||
disabled={isStructureLocked || selectedNode.id === tree.id}
|
||||
disabled={isStructureLocked || selectedNode.id === tree.id || siblingTagOptions.length === 0}
|
||||
/>
|
||||
<Button
|
||||
danger
|
||||
|
||||
Reference in New Issue
Block a user