You've already forked DataMate
feat(annotation): 添加切换段落时自动保存功能
- 在 LabelStudioTextEditor 组件中新增 Switch 组件用于控制自动保存 - 添加 autoSaveOnSwitch 状态管理自动保存开关 - 修改 confirmSaveBeforeSwitch 函数支持保存、放弃、取消三种决策 - 实现自动保存逻辑,当开关开启时直接保存而不弹出确认对话框 - 在段落导航栏添加自动保存开关和标签显示 - 更新切换段落时的未保存更改处理逻辑
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { App, Button, Card, List, Spin, Typography, Tag } from "antd";
|
||||
import { App, Button, Card, List, Spin, Typography, Tag, Switch } from "antd";
|
||||
import { LeftOutlined, ReloadOutlined, SaveOutlined, MenuFoldOutlined, MenuUnfoldOutlined, CheckOutlined } from "@ant-design/icons";
|
||||
import { useNavigate, useParams } from "react-router";
|
||||
|
||||
@@ -71,6 +71,8 @@ type ExportPayload = {
|
||||
requestId?: string | null;
|
||||
};
|
||||
|
||||
type SwitchDecision = "save" | "discard" | "cancel";
|
||||
|
||||
const LSF_IFRAME_SRC = "/lsf/lsf.html";
|
||||
|
||||
const resolveSegmentIndex = (value: unknown) => {
|
||||
@@ -160,6 +162,7 @@ export default function LabelStudioTextEditor() {
|
||||
const [tasks, setTasks] = useState<EditorTaskListItem[]>([]);
|
||||
const [selectedFileId, setSelectedFileId] = useState<string>("");
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||
const [autoSaveOnSwitch, setAutoSaveOnSwitch] = useState(false);
|
||||
|
||||
// 分段相关状态
|
||||
const [segmented, setSegmented] = useState(false);
|
||||
@@ -402,14 +405,32 @@ export default function LabelStudioTextEditor() {
|
||||
}, [iframeReady, lsReady, postToIframe]);
|
||||
|
||||
const confirmSaveBeforeSwitch = useCallback(() => {
|
||||
return new Promise<boolean>((resolve) => {
|
||||
modal.confirm({
|
||||
return new Promise<SwitchDecision>((resolve) => {
|
||||
let resolved = false;
|
||||
let modalInstance: { destroy: () => void } | null = null;
|
||||
const settle = (decision: SwitchDecision) => {
|
||||
if (resolved) return;
|
||||
resolved = true;
|
||||
resolve(decision);
|
||||
};
|
||||
const handleDiscard = () => {
|
||||
if (modalInstance) modalInstance.destroy();
|
||||
settle("discard");
|
||||
};
|
||||
modalInstance = modal.confirm({
|
||||
title: "当前段落有未保存标注",
|
||||
content: "切换段落前请先保存当前标注。",
|
||||
content: (
|
||||
<div className="flex flex-col gap-2">
|
||||
<Typography.Text>切换段落前请先保存当前标注。</Typography.Text>
|
||||
<Button type="link" danger style={{ padding: 0, height: "auto" }} onClick={handleDiscard}>
|
||||
放弃未保存并切换
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
okText: "保存并切换",
|
||||
cancelText: "取消",
|
||||
onOk: () => resolve(true),
|
||||
onCancel: () => resolve(false),
|
||||
onOk: () => settle("save"),
|
||||
onCancel: () => settle("cancel"),
|
||||
});
|
||||
});
|
||||
}, [modal]);
|
||||
@@ -464,10 +485,17 @@ export default function LabelStudioTextEditor() {
|
||||
const hasUnsavedChange = snapshotKey !== undefined && lastSnapshot !== undefined && latestSnapshot !== lastSnapshot;
|
||||
|
||||
if (hasUnsavedChange) {
|
||||
const shouldSave = await confirmSaveBeforeSwitch();
|
||||
if (!shouldSave) return;
|
||||
const saved = await saveFromExport(payload);
|
||||
if (!saved) return;
|
||||
if (autoSaveOnSwitch) {
|
||||
const saved = await saveFromExport(payload);
|
||||
if (!saved) return;
|
||||
} else {
|
||||
const decision = await confirmSaveBeforeSwitch();
|
||||
if (decision === "cancel") return;
|
||||
if (decision === "save") {
|
||||
const saved = await saveFromExport(payload);
|
||||
if (!saved) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await initEditorForFile(selectedFileId, newIndex);
|
||||
@@ -707,9 +735,16 @@ export default function LabelStudioTextEditor() {
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<Tag color="blue" style={{ marginLeft: 8 }}>
|
||||
{currentSegmentIndex + 1} / {segments.length}
|
||||
</Tag>
|
||||
<div className="flex items-center gap-2 ml-auto">
|
||||
<Tag color="blue">{currentSegmentIndex + 1} / {segments.length}</Tag>
|
||||
<Typography.Text style={{ fontSize: 12 }}>切段自动保存</Typography.Text>
|
||||
<Switch
|
||||
size="small"
|
||||
checked={autoSaveOnSwitch}
|
||||
onChange={(checked) => setAutoSaveOnSwitch(checked)}
|
||||
disabled={segmentSwitching || saving || loadingTaskDetail || !lsReady}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user