-
{t('dataAnnotation.home.confirm.deleteTaskContent1')}
-
{t('dataAnnotation.home.confirm.deleteTaskContent2')}
-
- ),
- okText: t('dataAnnotation.home.confirm.deleteOkText'),
- okType: "danger",
- cancelText: t('dataAnnotation.home.confirm.deleteCancelText'),
- onOk: async () => {
- try {
- await deleteAnnotationTaskByIdUsingDelete(task.id);
- message.success(t('dataAnnotation.home.messages.deleteSuccess'));
- fetchData();
- // clear selection if deleted item was selected
- setSelectedRowKeys((keys) => keys.filter((k) => k !== task.id));
- setSelectedRows((rows) => rows.filter((r) => r.id !== task.id));
- } catch (e) {
- console.error(e);
- message.error(t('dataAnnotation.home.messages.deleteFailed'));
- }
- },
- });
- };
-
- // 自动标注删除函数已屏蔽(保留代码在注释中)
- // const handleDeleteAuto = (task: any) => {
- // Modal.confirm({
- // title: t('dataAnnotation.home.confirm.deleteAutoTaskTitle', { name: task.name }),
- // content:
-
{t('dataAnnotation.home.messages.syncToDbContent1')}
-
{t('dataAnnotation.home.messages.syncToDbContent2')}
-
- ),
- okText: t('dataAnnotation.home.actions.syncToDb'),
- cancelText: t('dataAnnotation.home.confirm.deleteCancelText'),
- onOk: async () => {
- const hide = message.loading(t('dataAnnotation.dialogs.syncToDb.loading'), 0);
- try {
- await syncManualAnnotationToDatabaseUsingPost(task.id as any);
- hide();
- message.success(t('dataAnnotation.dialogs.syncToDb.success'));
- } catch (e) {
- console.error(e);
- hide();
- message.error(t('dataAnnotation.dialogs.syncToDb.fail'));
- }
- },
- });
- };
-
- // 自动标注导入函数已屏蔽(保留代码在注释中)
- // const handleImportAutoFromLabelStudio = (row: any) => {
- // if (!row?.id) {
- // message.error(t('dataAnnotation.home.messages.autoTaskNotFound'));
- // return;
- // }
- // const full = autoTasks.find((t: any) => t.id === row.id);
- // if (!full) {
- // message.error(t('dataAnnotation.home.messages.autoTaskNotFound') + t('dataAnnotation.home.messages.deleteFailed').split(',')[1]);
- // return;
- // }
- // setImportingAutoTask(full);
- // setShowImportAutoDialog(true);
- // };
-
- // 自动标注同步到数据库函数已屏蔽(保留代码在注释中)
- // const handleSyncAutoToDatabase = (row: any) => {
- // if (!row?.id) {
- // message.error(t('dataAnnotation.home.messages.autoTaskNotFound'));
- // return;
- // }
- // Modal.confirm({
- // title: t('dataAnnotation.home.messages.syncToDbTitle', { name: row.name }),
- // content: (
- //
- //
{t('dataAnnotation.home.messages.syncToDbContent1')}
- //
{t('dataAnnotation.home.messages.syncToDbContent2')}
- //
- // ),
- // okText: t('dataAnnotation.home.actions.syncToDb'),
- // cancelText: t('dataAnnotation.home.confirm.deleteCancelText'),
- // onOk: async () => {
- // const hide = message.loading(t('dataAnnotation.dialogs.syncToDb.loading'), 0);
- // try {
- // await syncAutoAnnotationToDatabaseUsingPost(row.id);
- // hide();
- // message.success(t('dataAnnotation.dialogs.syncToDb.success'));
- // } catch (e) {
- // console.error(e);
- // hide();
- // message.error(t('dataAnnotation.dialogs.syncToDb.fail'));
- // }
- // },
- // });
- // };
-
- // 自动标注跳转标注函数已屏蔽(保留代码在注释中)
- // const handleAnnotateAuto = (task: any) => {
- // (async () => {
- // try {
- // if (!labelStudioBase) {
- // message.error(t('dataAnnotation.home.messages.cannotJumpNoBase'));
- // return;
- // }
- // let projId: string | undefined;
- // try {
- // const resp = await getAutoAnnotationLabelStudioProjectUsingGet(task.id);
- // const data = (resp as any)?.data ?? resp;
- // projId = data?.projectId || data?.labeling_project_id;
- // } catch (e) {
- // console.error("Failed to resolve LS project for auto task", e);
- // }
- // if (!projId) {
- // const datasetId = task.datasetId;
- // if (!datasetId) {
- // message.error(t('dataAnnotation.home.messages.cannotJumpAutoNoDataset'));
- // return;
- // }
- // projId = datasetProjectMap[String(datasetId)];
- // }
- // if (!projId) {
- // message.error(t('dataAnnotation.home.messages.cannotJumpAutoNoProject'));
- // return;
- // }
- // const target = `${labelStudioBase}/projects/${projId}/data`;
- // window.open(target, "_blank");
- // } catch (error) {
- // console.error(error);
- // message.error(t('dataAnnotation.home.messages.cannotJumpError'));
- // }
- // })();
- // };
-
- const handleSync = (task: AnnotationTask, batchSize: number = 50) => {
- Modal.confirm({
- title: t('dataAnnotation.home.confirm.syncTitle', { name: task.name }),
- content: (
-
-
{t('dataAnnotation.home.confirm.deleteTaskContent1')}
-
{t('dataAnnotation.home.confirm.deleteTaskContent2')}
-
- ),
- okText: t('dataAnnotation.home.confirm.deleteOkText'),
- okType: "danger",
- cancelText: t('dataAnnotation.home.confirm.deleteCancelText'),
- onOk: async () => {
- try {
- await Promise.all(
- [
- ...manualRows.map((r) => deleteAnnotationTaskByIdUsingDelete(r.id)),
- // 自动标注批量删除已屏蔽
- // ...autoRows.map((r) => deleteAutoAnnotationTaskByIdUsingDelete(r.id)),
- ]
- );
- message.success(t('dataAnnotation.home.messages.batchDeleteSuccess'));
- fetchData();
- setSelectedRowKeys([]);
- setSelectedRows([]);
- } catch (e) {
- console.error(e);
- message.error(t('dataAnnotation.home.messages.batchDeleteFailed'));
- }
- },
- });
- };
-
- const operations = [
- {
- key: "annotate",
- label: t('dataAnnotation.home.actions.annotate'),
- icon: (
- [] = [
- {
- title: t('dataAnnotation.home.columns.taskName'),
- dataIndex: "name",
- key: "name",
- fixed: "left" as const,
- },
- {
- title: t('dataAnnotation.home.columns.type'),
- key: "kind",
- width: 100,
- render: (_: any, record: any) =>
- record._kind === "auto" ? t('dataAnnotation.home.autoAnnotation') : t('dataAnnotation.home.manualAnnotation'),
- },
- {
- title: t('dataAnnotation.home.columns.taskId'),
- dataIndex: "id",
- key: "id",
- },
- {
- title: t('dataAnnotation.home.columns.dataset'),
- dataIndex: "datasetName",
- key: "datasetName",
- width: 180,
- render: (text: string, record: any) => {
- if (!text || text === "-") return "-";
- const datasetId = record.datasetId || record.dataset_id;
- if (!datasetId) return text;
-
- return (
- {
- e.preventDefault();
- e.stopPropagation();
- navigate(`/data/management/detail/${datasetId}`);
- }}
- style={{ cursor: 'pointer', color: '#1890ff' }}
- >
- {text}
-
- );
- },
- },
- // 自动标注相关列已屏蔽(保留代码在注释中)
- // {
- // title: t('dataAnnotation.home.columns.model'),
- // key: "modelSize",
- // width: 160,
- // render: (_: any, record: any) => {
- // if (record._kind !== "auto") return "-";
- // const size = record.autoConfig?.modelSize;
- // return t(`dataAnnotation.home.autoModelSizeLabels.${size}`) || size || "-";
- // },
- // },
- // {
- // title: t('dataAnnotation.home.columns.confidence'),
- // key: "confThreshold",
- // width: 120,
- // render: (_: any, record: any) => {
- // if (record._kind !== "auto") return "-";
- // const threshold = record.autoConfig?.confThreshold;
- // if (typeof threshold !== "number") return "-";
- // return `${(threshold * 100).toFixed(0)}%`;
- // },
- // },
- // {
- // title: t('dataAnnotation.home.columns.targetClasses'),
- // key: "targetClasses",
- // width: 160,
- // render: (_: any, record: any) => {
- // if (record._kind !== "auto") return "-";
- // const classes: number[] = record.autoConfig?.targetClasses || [];
- // if (!classes.length) return t('dataAnnotation.home.allCategories');
- // const text = classes.join(", ");
- // return (
- //
- // {t('dataAnnotation.home.categoriesCount', { count: classes.length })}
- //
- // );
- // },
- // },
- // {
- // title: t('dataAnnotation.home.columns.autoStatus'),
- // key: "autoStatus",
- // width: 130,
- // render: (_: any, record: any) => {
- // if (record._kind !== "auto") return "-";
- // const status = record.autoStatus as string;
- // const label = t(`dataAnnotation.home.autoStatusLabels.${status}`) || status || "-";
- // return {label};
- // },
- // },
- // {
- // title: t('dataAnnotation.home.columns.autoProgress'),
- // key: "autoProgress",
- // width: 200,
- // render: (_: any, record: any) => {
- // if (record._kind !== "auto") return "-";
- // const progress = typeof record.autoProgress === "number" ? record.autoProgress : 0;
- // const processed = record.autoProcessedImages ?? 0;
- // const total = record.autoTotalImages ?? 0;
- // return (
- //
- //
- //
- // {processed} / {total}
- //
- //
- // );
- // },
- // },
- // {
- // title: t('dataAnnotation.home.columns.detectedObjects'),
- // key: "detectedObjects",
- // width: 120,
- // render: (_: any, record: any) => {
- // if (record._kind !== "auto") return "-";
- // const count = record.autoDetectedObjects;
- // if (typeof count !== "number") return "-";
- // try {
- // return count.toLocaleString();
- // } catch {
- // return String(count);
- // }
- // },
- // },
- {
- title: t('dataAnnotation.home.columns.createdAt'),
- dataIndex: "createdAt",
- key: "createdAt",
- width: 180,
- },
- {
- title: t('dataAnnotation.home.columns.updatedAt'),
- dataIndex: "updatedAt",
- key: "updatedAt",
- width: 180,
- },
- {
- title: t('dataAnnotation.home.columns.actions'),
- key: "actions",
- fixed: "right" as const,
- width: 260,
- dataIndex: "actions",
- render: (_: any, task: any) => (
-
- {task._kind === "manual" && (
- <>
- }
- onClick={() => handleAnnotate(task)}
- title={t('dataAnnotation.home.actions.annotate')}
- >
- {t('dataAnnotation.home.actions.edit')}
-
- }
- onClick={() => handleSyncManualToDatabase(task)}
- title={t('dataAnnotation.home.actions.syncToDb')}
- >
- {t('dataAnnotation.home.actions.syncToDb')}
-
-
- ,
- onClick: () => handleImportManualFromLabelStudio(task),
- },
- {
- key: "edit-dataset",
- label: t('dataAnnotation.home.editDataset'),
- icon: ,
- onClick: () => handleEditManualTaskDataset(task),
- },
- {
- key: "delete",
- label: t('dataAnnotation.home.actions.delete'),
- icon: ,
- danger: true,
- onClick: () => handleDelete(task),
- },
- ],
- }}
- trigger={["click"]}
- >
- } title={t('dataAnnotation.home.actions.moreActions')} />
-
- >
- )}
- {/* 自动标注任务操作按钮已屏蔽(保留代码在注释中) */}
- {/* {task._kind === "auto" && (
- <>
- }
- onClick={() => handleAnnotateAuto(task)}
- title={t('dataAnnotation.home.actions.annotate')}
- >
- {t('dataAnnotation.home.actions.edit')}
-
-
- }
- onClick={() => handleSyncAutoToDatabase(task)}
- title={t('dataAnnotation.home.actions.syncToDb')}
- >
- {t('dataAnnotation.home.actions.syncToDb')}
-
-
- ,
- onClick: () => handleImportAutoFromLabelStudio(task),
- },
- {
- key: "edit-dataset",
- label: t('dataAnnotation.home.editDataset'),
- icon: ,
- onClick: () => handleEditAutoTaskDataset(task),
- },
- {
- key: "delete",
- label: t('dataAnnotation.home.actions.delete'),
- icon: ,
- danger: true,
- onClick: () => handleDeleteAuto(task),
- },
- ],
- }}
- trigger={["click"]}
- >
- }
- title={t('dataAnnotation.home.actions.moreActions')}
- />
-
- >
- )} */}
-
- ),
- },
- ];
-
- return (
-
- {/* Header */}
-
-
{t('dataAnnotation.home.title')}
-
-
- {/* Tabs */}
-
- {/* Search, Filters and Buttons in one row */}
-
- {/* Left side: Search and view controls */}
-
-
-
-
- {/* Right side: All action buttons */}
-
-
-
- }
- onClick={() => setShowCreateDialog(true)}
- >
- {t('dataAnnotation.home.createTask')}
-
-
-
-
- {/* Task List/Card */}
- {viewMode === "list" ? (
-
- {
- setSelectedRowKeys(keys as (string | number)[]);
- setSelectedRows(rows as any[]);
- },
- }}
- scroll={{ x: "max-content", y: "calc(100vh - 24rem)" }}
- />
-
- ) : (
-
- )}
-
- setShowCreateDialog(false)}
- onRefresh={() => {
- // 手动标注创建成功后刷新标注任务列表
- fetchData();
- // 自动标注创建成功后刷新已屏蔽
- // if (mode === "auto") {
- // refreshAutoTasks(true);
- // }
- }}
- />
-
- ),
- },
- {
- key: "templates",
- label: t('dataAnnotation.home.tabs.templates'),
- children: ,
- },
- ]}
- />
-
- {/* {editingAutoTask && (
- {
- setShowEditAutoDatasetDialog(false);
- setEditingAutoTask(null);
- }}
- onSuccess={() => {
- setShowEditAutoDatasetDialog(false);
- setEditingAutoTask(null);
- // refreshAutoTasks();
- }}
- />
- )} */}
-
- {editingManualTask && (
- {
- setShowEditManualDatasetDialog(false);
- setEditingManualTask(null);
- }}
- onSuccess={() => {
- setShowEditManualDatasetDialog(false);
- setEditingManualTask(null);
- }}
- />
- )}
-
- {importingManualTask && (
- {
- setShowImportManualDialog(false);
- setImportingManualTask(null);
- }}
- onSuccess={() => {
- setShowImportManualDialog(false);
- setImportingManualTask(null);
- }}
- />
- )}
-
- {/* 自动标注导入对话框已屏蔽(保留代码在注释中) */}
- {/* {importingAutoTask && (
- {
- setShowImportAutoDialog(false);
- setImportingAutoTask(null);
- }}
- onSuccess={() => {
- setShowImportAutoDialog(false);
- setImportingAutoTask(null);
- }}
- />
- )} */}
-
- );
-}
+import { useState, useEffect } from "react";
+import { useNavigate } from "react-router";
+import { Card, Button, Table, message, Modal, Tabs, Tag, Progress, Tooltip, Dropdown } from "antd";
+import {
+ PlusOutlined,
+ EditOutlined,
+ DeleteOutlined,
+ SyncOutlined,
+ MoreOutlined,
+ SettingOutlined,
+ ExportOutlined,
+ ImportOutlined,
+} from "@ant-design/icons";
+import { SearchControls } from "@/components/SearchControls";
+import CardView from "@/components/CardView";
+import type { AnnotationTask } from "../annotation.model";
+import useFetchData from "@/hooks/useFetchData";
+import {
+ deleteAnnotationTaskByIdUsingDelete,
+ queryAnnotationTasksUsingGet,
+ syncAnnotationTaskUsingPost,
+ // 自动标注API已屏蔽(保留代码在注释中)
+ // queryAutoAnnotationTasksUsingGet,
+ // deleteAutoAnnotationTaskByIdUsingDelete,
+ // getAutoAnnotationLabelStudioProjectUsingGet,
+ loginAnnotationUsingGet,
+ syncManualAnnotationToDatabaseUsingPost,
+ // syncAutoAnnotationToDatabaseUsingPost,
+} from "../annotation.api";
+import { mapAnnotationTask } from "../annotation.const";
+import CreateAnnotationTask from "../Create/components/CreateAnnotationTaskDialog";
+import { ColumnType } from "antd/es/table";
+import { TemplateList } from "../Template";
+// 自动标注组件导入已屏蔽(保留代码在注释中)
+// import EditAutoAnnotationDatasetDialog from "../AutoAnnotation/components/EditAutoAnnotationDatasetDialog";
+// import ImportFromLabelStudioDialog from "../AutoAnnotation/components/ImportFromLabelStudioDialog";
+import ManualImportFromLabelStudioDialog from "../ManualImportFromLabelStudioDialog";
+import EditManualAnnotationDatasetDialog from "../EditManualAnnotationDatasetDialog";
+import { useTranslation } from "react-i18next";
+// Note: DevelopmentInProgress intentionally not used here
+
+export default function DataAnnotation() {
+ const { t } = useTranslation();
+ const navigate = useNavigate();
+ // return ;
+ const [activeTab, setActiveTab] = useState("tasks");
+ const [viewMode, setViewMode] = useState<"list" | "card">("list");
+ const [showCreateDialog, setShowCreateDialog] = useState(false);
+ // 自动标注任务state已屏蔽(保留代码在注释中)
+ // const [autoTasks, setAutoTasks] = useState([]);
+
+ const {
+ loading,
+ tableData,
+ pagination,
+ searchParams,
+ fetchData,
+ handleFiltersChange,
+ handleKeywordChange,
+ } = useFetchData(queryAnnotationTasksUsingGet, (item) => mapAnnotationTask(item, t), 30000, true, [], 0);
+
+ const [labelStudioBase, setLabelStudioBase] = useState(null);
+ const [selectedRowKeys, setSelectedRowKeys] = useState<(string | number)[]>([]);
+ const [selectedRows, setSelectedRows] = useState([]);
+ const [datasetProjectMap, setDatasetProjectMap] = useState>({});
+ // 自动标注相关state已屏蔽(保留代码在注释中)
+ // const [editingAutoTask, setEditingAutoTask] = useState(null);
+ // const [showEditAutoDatasetDialog, setShowEditAutoDatasetDialog] = useState(false);
+ const [editingManualTask, setEditingManualTask] = useState(null);
+ const [showEditManualDatasetDialog, setShowEditManualDatasetDialog] = useState(false);
+ // 自动标注相关state已屏蔽(保留代码在注释中)
+ // const [importingAutoTask, setImportingAutoTask] = useState(null);
+ // const [showImportAutoDialog, setShowImportAutoDialog] = useState(false);
+ const [importingManualTask, setImportingManualTask] = useState(null);
+ const [showImportManualDialog, setShowImportManualDialog] = useState(false);
+
+ // 自动标注任务拉取函数已屏蔽(保留代码在注释中)
+ // const refreshAutoTasks = async (silent = false) => {
+ // try {
+ // const response = await queryAutoAnnotationTasksUsingGet();
+ // const tasks = (response as any)?.data || response || [];
+ // if (Array.isArray(tasks)) {
+ // setAutoTasks(tasks);
+ // }
+ // } catch (error) {
+ // console.error("Failed to fetch auto annotation tasks:", error);
+ // if (!silent) {
+ // message.error(t('dataAnnotation.home.messages.fetchAutoTasksFailed'));
+ // }
+ // }
+ // };
+
+ // prefetch config on mount so clicking annotate is fast and we know whether base URL exists
+ // useEffect ensures this runs once
+ useEffect(() => {
+ let mounted = true;
+ (async () => {
+ try {
+ const baseUrl = `http://${window.location.hostname}:${parseInt(window.location.port) + 1}`;
+ if (mounted) setLabelStudioBase(baseUrl);
+ } catch (e) {
+ if (mounted) setLabelStudioBase(null);
+ }
+ })();
+ return () => {
+ mounted = false;
+ };
+ }, []);
+
+ // 基于手动标注任务构建 datasetId -> Label Studio project 映射,供自动标注跳转使用
+ useEffect(() => {
+ const map: Record = {};
+ (tableData as any[]).forEach((task: any) => {
+ const datasetId = task.datasetId || task.dataset_id;
+ const projId = task.labelingProjId || task.projId || task.labeling_project_id;
+ if (datasetId && projId) {
+ map[String(datasetId)] = String(projId);
+ }
+ });
+ setDatasetProjectMap(map);
+ }, [tableData]);
+
+ // 自动标注任务轮询已屏蔽(保留代码在注释中)
+ // useEffect(() => {
+ // refreshAutoTasks();
+ // const timer = setInterval(() => refreshAutoTasks(true), 3000);
+ // return () => {
+ // clearInterval(timer);
+ // };
+ // }, []);
+
+ const handleAnnotate = (task: AnnotationTask) => {
+ // Open Label Studio project page in a new tab
+ (async () => {
+ try {
+ // `labelingProjId` = Label Studio project ID (integer), used for LS URL only
+ // `mappingId` = mapping UUID, used for backend API calls
+ let labelingProjId = (task as any).labelingProjId || (task as any).projId || undefined;
+ let mappingId = (task as any).id || undefined;
+
+ // no fallback external mapping lookup; rely on normalized fields from mapAnnotationTask
+
+ // use prefetched base if available
+ const base = labelStudioBase;
+
+ // no debug logging in production
+ await loginAnnotationUsingGet(mappingId)
+ if (labelingProjId) {
+ // only open external Label Studio when we have a configured base url
+ if (base) {
+ const target = `${base}/projects/${labelingProjId}/data`;
+ window.open(target, "_blank");
+ } else {
+ // no external Label Studio URL configured — do not perform internal redirect in this version
+ message.error(t('dataAnnotation.home.messages.cannotJumpNoBase'));
+ return;
+ }
+ } else {
+ // no labeling project id available — do not attempt internal redirect in this version
+ message.error(t('dataAnnotation.home.messages.cannotJumpNoMapping'));
+ return;
+ }
+ } catch (error) {
+ // on error, surface a user-friendly message instead of redirecting
+ message.error(t('dataAnnotation.home.messages.cannotJumpError'));
+ return;
+ }
+ })();
+ };
+
+ const handleDelete = (task: AnnotationTask) => {
+ Modal.confirm({
+ title: t('dataAnnotation.home.confirm.deleteTaskTitle', { name: task.name }),
+ content: (
+
+
{t('dataAnnotation.home.confirm.deleteTaskContent1')}
+
{t('dataAnnotation.home.confirm.deleteTaskContent2')}
+
+ ),
+ okText: t('dataAnnotation.home.confirm.deleteOkText'),
+ okType: "danger",
+ cancelText: t('dataAnnotation.home.confirm.deleteCancelText'),
+ onOk: async () => {
+ try {
+ await deleteAnnotationTaskByIdUsingDelete(task.id);
+ message.success(t('dataAnnotation.home.messages.deleteSuccess'));
+ fetchData();
+ // clear selection if deleted item was selected
+ setSelectedRowKeys((keys) => keys.filter((k) => k !== task.id));
+ setSelectedRows((rows) => rows.filter((r) => r.id !== task.id));
+ } catch (e) {
+ console.error(e);
+ message.error(t('dataAnnotation.home.messages.deleteFailed'));
+ }
+ },
+ });
+ };
+
+ // 自动标注删除函数已屏蔽(保留代码在注释中)
+ // const handleDeleteAuto = (task: any) => {
+ // Modal.confirm({
+ // title: t('dataAnnotation.home.confirm.deleteAutoTaskTitle', { name: task.name }),
+ // content: {t('dataAnnotation.home.confirm.deleteAutoTaskContent')}
,
+ // okText: t('dataAnnotation.home.confirm.deleteOkText'),
+ // okType: "danger",
+ // cancelText: t('dataAnnotation.home.confirm.deleteCancelText'),
+ // onOk: async () => {
+ // try {
+ // await deleteAutoAnnotationTaskByIdUsingDelete(task.id);
+ // message.success(t('dataAnnotation.home.messages.autoTaskDeleteSuccess'));
+ // setAutoTasks((prev) => prev.filter((t: any) => t.id !== task.id));
+ // setSelectedRowKeys((keys) => keys.filter((k) => k !== task.id));
+ // setSelectedRows((rows) => rows.filter((r) => r.id !== task.id));
+ // } catch (e) {
+ // console.error(e);
+ // message.error(t('dataAnnotation.home.messages.deleteFailed'));
+ // }
+ // },
+ // });
+ // };
+
+ // 自动标注编辑数据集函数已屏蔽(保留代码在注释中)
+ // const handleEditAutoTaskDataset = (row: any) => {
+ // if (!row?.id) {
+ // message.error(t('dataAnnotation.home.messages.autoTaskNotFound'));
+ // return;
+ // }
+ // const full = autoTasks.find((t: any) => t.id === row.id);
+ // if (!full) {
+ // message.error(t('dataAnnotation.home.messages.autoTaskNotFound') + t('dataAnnotation.home.messages.deleteFailed').split(',')[1]);
+ // return;
+ // }
+ // setEditingAutoTask(full);
+ // setShowEditAutoDatasetDialog(true);
+ // };
+
+ const handleEditManualTaskDataset = (task: AnnotationTask) => {
+ if (!task?.id) {
+ message.error(t('dataAnnotation.home.messages.taskNotFound'));
+ return;
+ }
+ setEditingManualTask(task);
+ setShowEditManualDatasetDialog(true);
+ };
+
+ const handleImportManualFromLabelStudio = (task: AnnotationTask) => {
+ if (!task?.id) {
+ message.error(t('dataAnnotation.home.messages.taskNotFound'));
+ return;
+ }
+
+ setImportingManualTask(task);
+ setShowImportManualDialog(true);
+ };
+
+ const handleSyncManualToDatabase = async (task: AnnotationTask) => {
+ if (!task?.id) {
+ message.error(t('dataAnnotation.home.messages.taskNotFound'));
+ return;
+ }
+
+ Modal.confirm({
+ title: t('dataAnnotation.home.messages.syncToDbTitle', { name: task.name }),
+ content: (
+
+
{t('dataAnnotation.home.messages.syncToDbContent1')}
+
{t('dataAnnotation.home.messages.syncToDbContent2')}
+
+ ),
+ okText: t('dataAnnotation.home.actions.syncToDb'),
+ cancelText: t('dataAnnotation.home.confirm.deleteCancelText'),
+ onOk: async () => {
+ const hide = message.loading(t('dataAnnotation.dialogs.syncToDb.loading'), 0);
+ try {
+ await syncManualAnnotationToDatabaseUsingPost(task.id as any);
+ hide();
+ message.success(t('dataAnnotation.dialogs.syncToDb.success'));
+ } catch (e) {
+ console.error(e);
+ hide();
+ message.error(t('dataAnnotation.dialogs.syncToDb.fail'));
+ }
+ },
+ });
+ };
+
+ // 自动标注导入函数已屏蔽(保留代码在注释中)
+ // const handleImportAutoFromLabelStudio = (row: any) => {
+ // if (!row?.id) {
+ // message.error(t('dataAnnotation.home.messages.autoTaskNotFound'));
+ // return;
+ // }
+ // const full = autoTasks.find((t: any) => t.id === row.id);
+ // if (!full) {
+ // message.error(t('dataAnnotation.home.messages.autoTaskNotFound') + t('dataAnnotation.home.messages.deleteFailed').split(',')[1]);
+ // return;
+ // }
+ // setImportingAutoTask(full);
+ // setShowImportAutoDialog(true);
+ // };
+
+ // 自动标注同步到数据库函数已屏蔽(保留代码在注释中)
+ // const handleSyncAutoToDatabase = (row: any) => {
+ // if (!row?.id) {
+ // message.error(t('dataAnnotation.home.messages.autoTaskNotFound'));
+ // return;
+ // }
+ // Modal.confirm({
+ // title: t('dataAnnotation.home.messages.syncToDbTitle', { name: row.name }),
+ // content: (
+ //
+ //
{t('dataAnnotation.home.messages.syncToDbContent1')}
+ //
{t('dataAnnotation.home.messages.syncToDbContent2')}
+ //
+ // ),
+ // okText: t('dataAnnotation.home.actions.syncToDb'),
+ // cancelText: t('dataAnnotation.home.confirm.deleteCancelText'),
+ // onOk: async () => {
+ // const hide = message.loading(t('dataAnnotation.dialogs.syncToDb.loading'), 0);
+ // try {
+ // await syncAutoAnnotationToDatabaseUsingPost(row.id);
+ // hide();
+ // message.success(t('dataAnnotation.dialogs.syncToDb.success'));
+ // } catch (e) {
+ // console.error(e);
+ // hide();
+ // message.error(t('dataAnnotation.dialogs.syncToDb.fail'));
+ // }
+ // },
+ // });
+ // };
+
+ // 自动标注跳转标注函数已屏蔽(保留代码在注释中)
+ // const handleAnnotateAuto = (task: any) => {
+ // (async () => {
+ // try {
+ // if (!labelStudioBase) {
+ // message.error(t('dataAnnotation.home.messages.cannotJumpNoBase'));
+ // return;
+ // }
+ // let projId: string | undefined;
+ // try {
+ // const resp = await getAutoAnnotationLabelStudioProjectUsingGet(task.id);
+ // const data = (resp as any)?.data ?? resp;
+ // projId = data?.projectId || data?.labeling_project_id;
+ // } catch (e) {
+ // console.error("Failed to resolve LS project for auto task", e);
+ // }
+ // if (!projId) {
+ // const datasetId = task.datasetId;
+ // if (!datasetId) {
+ // message.error(t('dataAnnotation.home.messages.cannotJumpAutoNoDataset'));
+ // return;
+ // }
+ // projId = datasetProjectMap[String(datasetId)];
+ // }
+ // if (!projId) {
+ // message.error(t('dataAnnotation.home.messages.cannotJumpAutoNoProject'));
+ // return;
+ // }
+ // const target = `${labelStudioBase}/projects/${projId}/data`;
+ // window.open(target, "_blank");
+ // } catch (error) {
+ // console.error(error);
+ // message.error(t('dataAnnotation.home.messages.cannotJumpError'));
+ // }
+ // })();
+ // };
+
+ const handleSync = (task: AnnotationTask, batchSize: number = 50) => {
+ Modal.confirm({
+ title: t('dataAnnotation.home.confirm.syncTitle', { name: task.name }),
+ content: (
+
+
{t('dataAnnotation.home.confirm.syncContent1')}
+
{t('dataAnnotation.home.confirm.syncContent2')}
+
+ ),
+ okText: t('dataAnnotation.home.confirm.syncOkText'),
+ cancelText: t('dataAnnotation.home.confirm.syncCancelText'),
+ onOk: async () => {
+ try {
+ await syncAnnotationTaskUsingPost({ id: task.id, batchSize });
+ message.success(t('dataAnnotation.home.messages.syncRequestSent'));
+ // optional: refresh list/status
+ fetchData();
+ // clear selection for the task
+ setSelectedRowKeys((keys) => keys.filter((k) => k !== task.id));
+ setSelectedRows((rows) => rows.filter((r) => r.id !== task.id));
+ } catch (e) {
+ console.error(e);
+ message.error(t('dataAnnotation.home.messages.syncFailed'));
+ }
+ },
+ });
+ };
+
+ const handleBatchSync = (batchSize: number = 50) => {
+ if (!selectedRows || selectedRows.length === 0) return;
+ const manualRows = selectedRows.filter((r) => r._kind !== "auto");
+ if (manualRows.length === 0) {
+ message.warning(t('dataAnnotation.home.messages.batchSyncManualOnly'));
+ return;
+ }
+ Modal.confirm({
+ title: t('dataAnnotation.home.confirm.batchSyncTitle', { count: manualRows.length }),
+ content: (
+
+
{t('dataAnnotation.home.confirm.syncContent1')}
+
{t('dataAnnotation.home.confirm.syncContent2')}
+
+ ),
+ okText: t('dataAnnotation.home.confirm.syncOkText'),
+ cancelText: t('dataAnnotation.home.confirm.syncCancelText'),
+ onOk: async () => {
+ try {
+ await Promise.all(
+ manualRows.map((r) => syncAnnotationTaskUsingPost({ id: r.id, batchSize }))
+ );
+ message.success(t('dataAnnotation.home.messages.batchSyncSuccess'));
+ fetchData();
+ setSelectedRowKeys([]);
+ setSelectedRows([]);
+ } catch (e) {
+ console.error(e);
+ message.error(t('dataAnnotation.home.messages.batchSyncFailed'));
+ }
+ },
+ });
+ };
+
+ const handleBatchDelete = () => {
+ if (!selectedRows || selectedRows.length === 0) return;
+ const manualRows = selectedRows.filter((r) => r._kind !== "auto");
+ // 自动标注批量删除已屏蔽
+ // const autoRows = selectedRows.filter((r) => r._kind === "auto");
+ Modal.confirm({
+ title: t('dataAnnotation.home.confirm.batchDeleteTitle', { count: selectedRows.length }),
+ content: (
+
+
{t('dataAnnotation.home.confirm.deleteTaskContent1')}
+
{t('dataAnnotation.home.confirm.deleteTaskContent2')}
+
+ ),
+ okText: t('dataAnnotation.home.confirm.deleteOkText'),
+ okType: "danger",
+ cancelText: t('dataAnnotation.home.confirm.deleteCancelText'),
+ onOk: async () => {
+ try {
+ await Promise.all(
+ [
+ ...manualRows.map((r) => deleteAnnotationTaskByIdUsingDelete(r.id)),
+ // 自动标注批量删除已屏蔽
+ // ...autoRows.map((r) => deleteAutoAnnotationTaskByIdUsingDelete(r.id)),
+ ]
+ );
+ message.success(t('dataAnnotation.home.messages.batchDeleteSuccess'));
+ fetchData();
+ setSelectedRowKeys([]);
+ setSelectedRows([]);
+ } catch (e) {
+ console.error(e);
+ message.error(t('dataAnnotation.home.messages.batchDeleteFailed'));
+ }
+ },
+ });
+ };
+
+ const operations = [
+ {
+ key: "annotate",
+ label: t('dataAnnotation.home.actions.annotate'),
+ icon: (
+
+ ),
+ onClick: handleAnnotate,
+ },
+ {
+ key: "sync-db",
+ label: t('dataAnnotation.home.actions.syncToDb'),
+ icon: ,
+ onClick: handleSyncManualToDatabase,
+ },
+ {
+ key: "export-result",
+ label: t('dataAnnotation.home.actions.exportResult'),
+ icon: , // 导出/下载类图标
+ onClick: handleImportManualFromLabelStudio,
+ },
+ {
+ key: "delete",
+ label: t('dataAnnotation.home.actions.delete'),
+ icon: ,
+ onClick: handleDelete,
+ },
+ ];
+ // 合并手动标注任务与自动标注任务
+ // 对于由自动标注逻辑内部创建的 Label Studio 映射(名称以 " - 自动标注" 结尾),
+ // 仅用于 datasetId -> projectId 映射,不在列表中单独展示,避免给人“多了一条手动任务”的感觉。
+ const manualVisibleTasks = tableData.filter((task: any) => {
+ const name = (task as any)?.name;
+ if (typeof name !== "string") return true;
+ return !name.endsWith(" - 自动标注");
+ });
+
+ // 自动标注功能已屏蔽:仅显示手动标注任务
+ // 自动标注任务合并逻辑已屏蔽(保留代码在注释中,需要恢复时可取消注释)
+ // ...autoTasks.map((task: any) => {
+ // const sourceList = Array.isArray(task.sourceDatasets)
+ // ? task.sourceDatasets
+ // : task.datasetName
+ // ? [task.datasetName]
+ // : [];
+ // const datasetName = sourceList.length > 0 ? sourceList.join(",") : "-";
+ // return {
+ // id: task.id,
+ // name: task.name,
+ // datasetId: task.datasetId || task.dataset_id,
+ // datasetName,
+ // createdAt: task.createdAt || "-",
+ // updatedAt: task.updatedAt || "-",
+ // _kind: "auto" as const,
+ // autoStatus: task.status,
+ // autoProgress: task.progress,
+ // autoProcessedImages: task.processedImages,
+ // autoTotalImages: task.totalImages,
+ // autoDetectedObjects: task.detectedObjects,
+ // autoConfig: task.config || {},
+ // };
+ // }),
+
+ const mergedTableData: any[] = manualVisibleTasks.map((task: any) => ({
+ ...task,
+ _kind: "manual" as const,
+ }));
+
+ const columns: ColumnType[] = [
+ {
+ title: t('dataAnnotation.home.columns.taskName'),
+ dataIndex: "name",
+ key: "name",
+ fixed: "left" as const,
+ },
+ {
+ title: t('dataAnnotation.home.columns.type'),
+ key: "kind",
+ width: 100,
+ render: (_: any, record: any) =>
+ record._kind === "auto" ? t('dataAnnotation.home.autoAnnotation') : t('dataAnnotation.home.manualAnnotation'),
+ },
+ {
+ title: t('dataAnnotation.home.columns.taskId'),
+ dataIndex: "id",
+ key: "id",
+ },
+ {
+ title: t('dataAnnotation.home.columns.dataset'),
+ dataIndex: "datasetName",
+ key: "datasetName",
+ width: 180,
+ render: (text: string, record: any) => {
+ if (!text || text === "-") return "-";
+ const datasetId = record.datasetId || record.dataset_id;
+ if (!datasetId) return text;
+
+ return (
+ {
+ e.preventDefault();
+ e.stopPropagation();
+ navigate(`/data/management/detail/${datasetId}`);
+ }}
+ style={{ cursor: 'pointer', color: '#1890ff' }}
+ >
+ {text}
+
+ );
+ },
+ },
+ // 自动标注相关列已屏蔽(保留代码在注释中)
+ // {
+ // title: t('dataAnnotation.home.columns.model'),
+ // key: "modelSize",
+ // width: 160,
+ // render: (_: any, record: any) => {
+ // if (record._kind !== "auto") return "-";
+ // const size = record.autoConfig?.modelSize;
+ // return t(`dataAnnotation.home.autoModelSizeLabels.${size}`) || size || "-";
+ // },
+ // },
+ // {
+ // title: t('dataAnnotation.home.columns.confidence'),
+ // key: "confThreshold",
+ // width: 120,
+ // render: (_: any, record: any) => {
+ // if (record._kind !== "auto") return "-";
+ // const threshold = record.autoConfig?.confThreshold;
+ // if (typeof threshold !== "number") return "-";
+ // return `${(threshold * 100).toFixed(0)}%`;
+ // },
+ // },
+ // {
+ // title: t('dataAnnotation.home.columns.targetClasses'),
+ // key: "targetClasses",
+ // width: 160,
+ // render: (_: any, record: any) => {
+ // if (record._kind !== "auto") return "-";
+ // const classes: number[] = record.autoConfig?.targetClasses || [];
+ // if (!classes.length) return t('dataAnnotation.home.allCategories');
+ // const text = classes.join(", ");
+ // return (
+ //
+ // {t('dataAnnotation.home.categoriesCount', { count: classes.length })}
+ //
+ // );
+ // },
+ // },
+ // {
+ // title: t('dataAnnotation.home.columns.autoStatus'),
+ // key: "autoStatus",
+ // width: 130,
+ // render: (_: any, record: any) => {
+ // if (record._kind !== "auto") return "-";
+ // const status = record.autoStatus as string;
+ // const label = t(`dataAnnotation.home.autoStatusLabels.${status}`) || status || "-";
+ // return {label};
+ // },
+ // },
+ // {
+ // title: t('dataAnnotation.home.columns.autoProgress'),
+ // key: "autoProgress",
+ // width: 200,
+ // render: (_: any, record: any) => {
+ // if (record._kind !== "auto") return "-";
+ // const progress = typeof record.autoProgress === "number" ? record.autoProgress : 0;
+ // const processed = record.autoProcessedImages ?? 0;
+ // const total = record.autoTotalImages ?? 0;
+ // return (
+ //
+ //
+ //
+ // {processed} / {total}
+ //
+ //
+ // );
+ // },
+ // },
+ // {
+ // title: t('dataAnnotation.home.columns.detectedObjects'),
+ // key: "detectedObjects",
+ // width: 120,
+ // render: (_: any, record: any) => {
+ // if (record._kind !== "auto") return "-";
+ // const count = record.autoDetectedObjects;
+ // if (typeof count !== "number") return "-";
+ // try {
+ // return count.toLocaleString();
+ // } catch {
+ // return String(count);
+ // }
+ // },
+ // },
+ {
+ title: t('dataAnnotation.home.columns.createdAt'),
+ dataIndex: "createdAt",
+ key: "createdAt",
+ width: 180,
+ },
+ {
+ title: t('dataAnnotation.home.columns.updatedAt'),
+ dataIndex: "updatedAt",
+ key: "updatedAt",
+ width: 180,
+ },
+ {
+ title: t('dataAnnotation.home.columns.actions'),
+ key: "actions",
+ fixed: "right" as const,
+ width: 260,
+ dataIndex: "actions",
+ render: (_: any, task: any) => (
+
+ {task._kind === "manual" && (
+ <>
+ }
+ onClick={() => handleAnnotate(task)}
+ title={t('dataAnnotation.home.actions.annotate')}
+ >
+ {t('dataAnnotation.home.actions.edit')}
+
+ }
+ onClick={() => handleSyncManualToDatabase(task)}
+ title={t('dataAnnotation.home.actions.syncToDb')}
+ >
+ {t('dataAnnotation.home.actions.syncToDb')}
+
+
+ ,
+ onClick: () => handleImportManualFromLabelStudio(task),
+ },
+ {
+ key: "edit-dataset",
+ label: t('dataAnnotation.home.editDataset'),
+ icon: ,
+ onClick: () => handleEditManualTaskDataset(task),
+ },
+ {
+ key: "delete",
+ label: t('dataAnnotation.home.actions.delete'),
+ icon: ,
+ danger: true,
+ onClick: () => handleDelete(task),
+ },
+ ],
+ }}
+ trigger={["click"]}
+ >
+ } title={t('dataAnnotation.home.actions.moreActions')} />
+
+ >
+ )}
+ {/* 自动标注任务操作按钮已屏蔽(保留代码在注释中) */}
+ {/* {task._kind === "auto" && (
+ <>
+ }
+ onClick={() => handleAnnotateAuto(task)}
+ title={t('dataAnnotation.home.actions.annotate')}
+ >
+ {t('dataAnnotation.home.actions.edit')}
+
+
+ }
+ onClick={() => handleSyncAutoToDatabase(task)}
+ title={t('dataAnnotation.home.actions.syncToDb')}
+ >
+ {t('dataAnnotation.home.actions.syncToDb')}
+
+
+ ,
+ onClick: () => handleImportAutoFromLabelStudio(task),
+ },
+ {
+ key: "edit-dataset",
+ label: t('dataAnnotation.home.editDataset'),
+ icon: ,
+ onClick: () => handleEditAutoTaskDataset(task),
+ },
+ {
+ key: "delete",
+ label: t('dataAnnotation.home.actions.delete'),
+ icon: ,
+ danger: true,
+ onClick: () => handleDeleteAuto(task),
+ },
+ ],
+ }}
+ trigger={["click"]}
+ >
+ }
+ title={t('dataAnnotation.home.actions.moreActions')}
+ />
+
+ >
+ )} */}
+
+ ),
+ },
+ ];
+
+ return (
+
+ {/* Header */}
+
+
{t('dataAnnotation.home.title')}
+
+
+ {/* Tabs */}
+
+ {/* Search, Filters and Buttons in one row */}
+
+ {/* Left side: Search and view controls */}
+
+
+
+
+ {/* Right side: All action buttons */}
+
+
+
+ }
+ onClick={() => setShowCreateDialog(true)}
+ >
+ {t('dataAnnotation.home.createTask')}
+
+
+
+
+ {/* Task List/Card */}
+ {viewMode === "list" ? (
+
+ {
+ setSelectedRowKeys(keys as (string | number)[]);
+ setSelectedRows(rows as any[]);
+ },
+ }}
+ scroll={{ x: "max-content", y: "calc(100vh - 24rem)" }}
+ />
+
+ ) : (
+
+ )}
+
+ setShowCreateDialog(false)}
+ onRefresh={() => {
+ // 手动标注创建成功后刷新标注任务列表
+ fetchData();
+ // 自动标注创建成功后刷新已屏蔽
+ // if (mode === "auto") {
+ // refreshAutoTasks(true);
+ // }
+ }}
+ />
+
+ ),
+ },
+ {
+ key: "templates",
+ label: t('dataAnnotation.home.tabs.templates'),
+ children: ,
+ },
+ ]}
+ />
+
+ {/* {editingAutoTask && (
+ {
+ setShowEditAutoDatasetDialog(false);
+ setEditingAutoTask(null);
+ }}
+ onSuccess={() => {
+ setShowEditAutoDatasetDialog(false);
+ setEditingAutoTask(null);
+ // refreshAutoTasks();
+ }}
+ />
+ )} */}
+
+ {editingManualTask && (
+ {
+ setShowEditManualDatasetDialog(false);
+ setEditingManualTask(null);
+ }}
+ onSuccess={() => {
+ setShowEditManualDatasetDialog(false);
+ setEditingManualTask(null);
+ }}
+ />
+ )}
+
+ {importingManualTask && (
+ {
+ setShowImportManualDialog(false);
+ setImportingManualTask(null);
+ }}
+ onSuccess={() => {
+ setShowImportManualDialog(false);
+ setImportingManualTask(null);
+ }}
+ />
+ )}
+
+ {/* 自动标注导入对话框已屏蔽(保留代码在注释中) */}
+ {/* {importingAutoTask && (
+ {
+ setShowImportAutoDialog(false);
+ setImportingAutoTask(null);
+ }}
+ onSuccess={() => {
+ setShowImportAutoDialog(false);
+ setImportingAutoTask(null);
+ }}
+ />
+ )} */}
+
+ );
+}