From 08336e2a13a02feec662d020281f59e71b5ff203 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Fri, 9 Jan 2026 13:05:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(annotation):=20=E6=B7=BB=E5=8A=A0=E6=A0=87?= =?UTF-8?q?=E6=B3=A8=E6=A8=A1=E6=9D=BF=E9=85=8D=E7=BD=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在schema中新增choice和show_inline字段支持选择模式配置 - 为编辑器服务添加空标注创建逻辑避免前端异常 - 实现标签类型的标准化处理和大小写兼容 - 支持Choices标签的单选/多选和行内显示配置 - 优化前端界面滚动条显示控制样式 --- frontend/public/lsf/lsf.html | 2 ++ .../app/module/annotation/schema/template.py | 2 ++ .../app/module/annotation/service/editor.py | 12 ++++++++++ .../app/module/annotation/service/template.py | 23 +++++++++++++++---- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/frontend/public/lsf/lsf.html b/frontend/public/lsf/lsf.html index b7f3d2d..5d7f846 100644 --- a/frontend/public/lsf/lsf.html +++ b/frontend/public/lsf/lsf.html @@ -9,9 +9,11 @@ body { height: 100%; margin: 0; + overflow: hidden; } #label-studio { height: 100vh; + overflow: auto; } diff --git a/runtime/datamate-python/app/module/annotation/schema/template.py b/runtime/datamate-python/app/module/annotation/schema/template.py index 27bd770..e82fec0 100644 --- a/runtime/datamate-python/app/module/annotation/schema/template.py +++ b/runtime/datamate-python/app/module/annotation/schema/template.py @@ -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) diff --git a/runtime/datamate-python/app/module/annotation/service/editor.py b/runtime/datamate-python/app/module/annotation/service/editor.py index 749ea71..ac04257 100644 --- a/runtime/datamate-python/app/module/annotation/service/editor.py +++ b/runtime/datamate-python/app/module/annotation/service/editor.py @@ -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, diff --git a/runtime/datamate-python/app/module/annotation/service/template.py b/runtime/datamate-python/app/module/annotation/service/template.py index d2fa52d..61abeb5 100644 --- a/runtime/datamate-python/app/module/annotation/service/template.py +++ b/runtime/datamate-python/app/module/annotation/service/template.py @@ -35,8 +35,20 @@ class AnnotationTemplateService: Label Studio XML字符串 """ tag_config = LabelStudioTagConfig() + control_types = tag_config.get_control_types() xml_parts = [''] + 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: