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;
|
return options;
|
||||||
}, [objectOptions, controlOptions]);
|
}, [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(
|
const getTagDisplayName = useCallback(
|
||||||
(tag: string) => {
|
(tag: string) => {
|
||||||
if (config?.objects?.[tag]) return getObjectDisplayName(tag);
|
if (config?.objects?.[tag]) return getObjectDisplayName(tag);
|
||||||
@@ -796,21 +866,21 @@ const TemplateConfigurationTreeEditor = ({
|
|||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
<Select
|
<Select
|
||||||
placeholder="添加子节点"
|
placeholder="添加子节点"
|
||||||
options={tagOptions}
|
options={childTagOptions}
|
||||||
value={null}
|
value={null}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
handleAddNode(value, "child");
|
handleAddNode(value, "child");
|
||||||
}}
|
}}
|
||||||
disabled={isStructureLocked}
|
disabled={isStructureLocked || childTagOptions.length === 0}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
placeholder="添加同级节点"
|
placeholder="添加同级节点"
|
||||||
options={tagOptions}
|
options={siblingTagOptions}
|
||||||
value={null}
|
value={null}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
handleAddNode(value, "sibling");
|
handleAddNode(value, "sibling");
|
||||||
}}
|
}}
|
||||||
disabled={isStructureLocked || selectedNode.id === tree.id}
|
disabled={isStructureLocked || selectedNode.id === tree.id || siblingTagOptions.length === 0}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
danger
|
danger
|
||||||
|
|||||||
Reference in New Issue
Block a user