feat(annotation): 支持音频和视频数据类型的标注任务

- 添加了音频和视频数据类型常量定义
- 实现了音频和视频标注模板的内置配置
- 扩展前端组件以支持按数据类型过滤标注模板
- 重构后端编辑器服务以处理音频和视频任务构建
- 更新数据库初始化脚本包含音频和视频标注模板
- 添加音频和视频数据类型的预览URL映射逻辑
This commit is contained in:
2026-01-26 23:54:40 +08:00
parent 47295e8cdf
commit 977a930c97
5 changed files with 461 additions and 59 deletions

View File

@@ -13,7 +13,7 @@ import {
queryAnnotationTemplatesUsingGet,
} from "../../annotation.api";
import { DatasetType, type Dataset } from "@/pages/DataManagement/dataset.model";
import type { AnnotationTemplate, AnnotationTask } from "../../annotation.model";
import { DataType, type AnnotationTemplate, type AnnotationTask } from "../../annotation.model";
import LabelStudioEmbed from "@/components/business/LabelStudioEmbed";
import TemplateConfigurationTreeEditor from "../../components/TemplateConfigurationTreeEditor";
import { useTagConfig } from "@/hooks/useTagConfig";
@@ -57,6 +57,22 @@ const SEGMENTATION_OPTIONS = [
{ label: "需要切片段", value: true },
{ label: "不需要切片段", value: false },
];
const resolveTemplateDataType = (datasetType?: DatasetType) => {
switch (datasetType) {
case DatasetType.TEXT:
return DataType.TEXT;
case DatasetType.IMAGE:
return DataType.IMAGE;
case DatasetType.AUDIO:
return DataType.AUDIO;
case DatasetType.VIDEO:
return DataType.VIDEO;
default:
return undefined;
}
};
const resolveDefaultTemplate = (items: AnnotationTemplate[]) =>
items.find((template) => template.builtIn) || items[0];
export default function CreateAnnotationTask({
open,
@@ -112,19 +128,6 @@ export default function CreateAnnotationTask({
});
setDatasets(datasetData.content.map(mapDataset) || []);
// Fetch templates
const templateResponse = await queryAnnotationTemplatesUsingGet({
page: 1,
size: 100,
});
if (templateResponse.code === 200 && templateResponse.data) {
const fetchedTemplates = templateResponse.data.content || [];
setTemplates(fetchedTemplates);
} else {
console.error("Failed to fetch templates:", templateResponse);
setTemplates([]);
}
} catch (error) {
console.error("Error fetching data:", error);
setTemplates([]);
@@ -133,6 +136,66 @@ export default function CreateAnnotationTask({
fetchData();
}, [open]);
const fetchTemplates = async (dataType?: string) => {
if (!dataType) {
setTemplates([]);
return;
}
try {
const templateResponse = await queryAnnotationTemplatesUsingGet({
page: 1,
size: 200,
dataType,
});
if (templateResponse.code === 200 && templateResponse.data) {
const fetchedTemplates = templateResponse.data.content || [];
setTemplates(fetchedTemplates);
} else {
console.error("Failed to fetch templates:", templateResponse);
setTemplates([]);
}
} catch (error) {
console.error("Error fetching templates:", error);
setTemplates([]);
}
};
useEffect(() => {
if (!open || isEditMode) {
return;
}
if (!selectedDataset) {
setTemplates([]);
manualForm.setFieldsValue({ templateId: undefined });
setLabelConfig("");
return;
}
const dataType = resolveTemplateDataType(selectedDataset.datasetType);
fetchTemplates(dataType);
}, [isEditMode, manualForm, open, selectedDataset]);
useEffect(() => {
if (!open || isEditMode || configMode !== "template" || !selectedDataset) {
return;
}
if (templates.length === 0) {
manualForm.setFieldsValue({ templateId: undefined });
setLabelConfig("");
return;
}
const currentTemplateId = manualForm.getFieldValue("templateId");
const currentTemplate = templates.find((template) => template.id === currentTemplateId);
if (currentTemplate) {
return;
}
const defaultTemplate = resolveDefaultTemplate(templates);
if (defaultTemplate) {
manualForm.setFieldsValue({ templateId: defaultTemplate.id });
setLabelConfig(defaultTemplate.labelConfig || "");
}
}, [configMode, isEditMode, manualForm, open, selectedDataset, templates]);
// Reset form and manual-edit flag when modal opens, or load task data in edit mode
useEffect(() => {
if (open) {
@@ -587,6 +650,10 @@ export default function CreateAnnotationTask({
})}
onChange={(value) => {
setSelectedDatasetId(value);
if (!isEditMode) {
manualForm.setFieldsValue({ templateId: undefined });
setLabelConfig("");
}
const dataset = datasets.find((item) => item.id === value);
if (dataset?.datasetType === DatasetType.TEXT) {
const currentValue = manualForm.getFieldValue("segmentationEnabled");