diff --git a/frontend/src/pages/DataAnnotation/Annotate/LabelStudioTextEditor.tsx b/frontend/src/pages/DataAnnotation/Annotate/LabelStudioTextEditor.tsx index 18b2a21..5f3192c 100644 --- a/frontend/src/pages/DataAnnotation/Annotate/LabelStudioTextEditor.tsx +++ b/frontend/src/pages/DataAnnotation/Annotate/LabelStudioTextEditor.tsx @@ -8,6 +8,9 @@ import { getEditorTaskUsingGet, listEditorTasksUsingGet, upsertEditorAnnotationUsingPut, + checkFileVersionUsingGet, + useNewVersionUsingPost, + type FileVersionCheckResponse, } from "../annotation.api"; import { AnnotationResultStatus } from "../annotation.model"; @@ -269,6 +272,11 @@ export default function LabelStudioTextEditor() { return Array.from({ length: segmentTotal }, (_, index) => index); }, [segmentTotal]); + // 文件版本相关状态 + const [fileVersionInfo, setFileVersionInfo] = useState(null); + const [checkingFileVersion, setCheckingFileVersion] = useState(false); + const [usingNewVersion, setUsingNewVersion] = useState(false); + const focusIframe = useCallback(() => { const iframe = iframeRef.current; if (!iframe) return; @@ -548,6 +556,77 @@ export default function LabelStudioTextEditor() { } }, [iframeReady, message, postToIframe, project, projectId]); + const checkFileVersion = useCallback(async (fileId: string) => { + if (!projectId || !fileId) return; + setCheckingFileVersion(true); + try { + const resp = (await checkFileVersionUsingGet(projectId, fileId)) as ApiResponse; + const data = resp?.data; + if (data) { + setFileVersionInfo(data); + if (data.hasNewVersion) { + modal.warning({ + title: "文件有新版本", + content: ( +
+ + 文件已更新到新版本(当前版本: {data.currentFileVersion},标注版本: {data.annotationFileVersion})。 + + + 点击"使用新版本"可清空当前标注并使用最新版本的文件内容。 + +
+ ), + okText: "我知道了", + }); + } + } + } catch (e) { + console.error("检查文件版本失败", e); + } finally { + setCheckingFileVersion(false); + } + }, [modal, message, projectId]); + + const handleUseNewVersion = useCallback(async () => { + if (!selectedFileId) return; + + modal.confirm({ + title: "确认使用新版本", + content: ( +
+ + 确认使用新版本?这将清空当前标注并使用最新版本的文件内容。 + + {fileVersionInfo && ( + + 当前标注版本: {fileVersionInfo.annotationFileVersion},最新文件版本: {fileVersionInfo.currentFileVersion} + + )} +
+ ), + okText: "确认", + okType: "danger", + cancelText: "取消", + onOk: async () => { + if (!projectId || !selectedFileId) return; + setUsingNewVersion(true); + try { + await useNewVersionUsingPost(projectId, selectedFileId); + message.success("已使用新版本并清空标注"); + setFileVersionInfo(null); + await loadTasks({ mode: "reset" }); + await initEditorForFile(selectedFileId); + } catch (e) { + console.error("使用新版本失败", e); + message.error("使用新版本失败"); + } finally { + setUsingNewVersion(false); + } + }, + }); + }, [modal, message, projectId, selectedFileId, fileVersionInfo, loadTasks, initEditorForFile]); + const advanceAfterSave = useCallback(async (fileId: string, segmentIndex?: number) => { if (!fileId) return; if (segmented && segmentTotal > 0) { @@ -815,6 +894,13 @@ export default function LabelStudioTextEditor() { return () => window.removeEventListener("message", handler); }, [message, origin, saveFromExport]); + useEffect(() => { + if (selectedFileId && project?.supported) { + setFileVersionInfo(null); + checkFileVersion(selectedFileId); + } + }, [selectedFileId, project?.supported, checkFileVersion]); + const canLoadMore = taskTotalPages > 0 && taskPage + 1 < taskTotalPages; const saveDisabled = !iframeReady || !selectedFileId || saving || loadingTaskDetail; @@ -896,6 +982,22 @@ export default function LabelStudioTextEditor() {
+ {fileVersionInfo?.hasNewVersion && ( +
+ + ⚠ 文件有新版本({fileVersionInfo.currentFileVersion} > {fileVersionInfo.annotationFileVersion}) + + +
+ )}