feat(annotation): 添加标注模板配置功能

- 在schema中新增choice和show_inline字段支持选择模式配置
- 为编辑器服务添加空标注创建逻辑避免前端异常
- 实现标签类型的标准化处理和大小写兼容
- 支持Choices标签的单选/多选和行内显示配置
- 优化前端界面滚动条显示控制样式
This commit is contained in:
2026-01-09 13:05:09 +08:00
parent a82f4f1bc3
commit 08336e2a13
4 changed files with 34 additions and 5 deletions

View File

@@ -14,6 +14,8 @@ class LabelDefinition(BaseModel):
options: Optional[List[str]] = Field(None, description="选项列表(用于choices类型)")
labels: Optional[List[str]] = Field(None, description="标签列表(用于rectanglelabels等类型)")
required: bool = Field(False, description="是否必填")
choice: Optional[str] = Field(None, description="Choices 选择模式: single/multiple")
show_inline: Optional[bool] = Field(None, alias="showInline", description="Choices 是否行内显示")
description: Optional[str] = Field(None, description="标签描述")
model_config = ConfigDict(populate_by_name=True)

View File

@@ -251,6 +251,18 @@ class AnnotationEditorService:
if not isinstance(stored.get("id"), int):
stored["id"] = self._make_ls_annotation_id(project_id, file_id)
task["annotations"] = [stored]
else:
# 提供一个空 annotation,避免前端在没有选中 annotation 时无法产生 result
empty_ann_id = self._make_ls_annotation_id(project_id, file_id)
task["annotations"] = [
{
"id": empty_ann_id,
"task": ls_task_id,
"result": [],
"created_at": datetime.utcnow().isoformat() + "Z",
"updated_at": datetime.utcnow().isoformat() + "Z",
}
]
return EditorTaskResponse(
task=task,

View File

@@ -35,8 +35,20 @@ class AnnotationTemplateService:
Label Studio XML字符串
"""
tag_config = LabelStudioTagConfig()
control_types = tag_config.get_control_types()
xml_parts = ['<View>']
def normalize_control_type(raw: Optional[str]) -> str:
if not raw:
return "Choices"
if raw in control_types:
return raw
raw_lower = raw.lower()
for ct in control_types:
if ct.lower() == raw_lower:
return ct
return raw
# 生成对象定义
for obj in config.objects:
obj_attrs = [
@@ -47,16 +59,17 @@ class AnnotationTemplateService:
# 生成标签定义
for label in config.labels:
label_attrs = [
f'name="{label.from_name}"',
f'toName="{label.to_name}"'
]
label_attrs = [f'name="{label.from_name}"', f'toName="{label.to_name}"']
# 添加可选属性
if label.required:
label_attrs.append('required="true"')
tag_type = label.type.capitalize() if label.type else "Choices"
tag_type = normalize_control_type(label.type)
if tag_type == "Choices" and label.choice:
label_attrs.append(f'choice="{label.choice}"')
if tag_type == "Choices" and label.show_inline is not None:
label_attrs.append(f'showInline="{"true" if label.show_inline else "false"}"')
# 检查是否需要子元素
if label.options or label.labels: