2026-05-25-14-00-24 修正项目导出结构与新增批处理API
This commit is contained in:
@@ -2825,6 +2825,7 @@ export default function ReverseWorkspace({
|
||||
const [fusionError, setFusionError] = useState('');
|
||||
const [saveStatus, setSaveStatus] = useState('');
|
||||
const [exporting, setExporting] = useState(false);
|
||||
const [exportProgress, setExportProgress] = useState({ active: false, percent: 0, phase: '' });
|
||||
const [stretchingAxis, setStretchingAxis] = useState<AxisKey | null>(null);
|
||||
const modelBoundsCacheRef = useRef(new Map<string, { min: THREE.Vector3; max: THREE.Vector3 }>());
|
||||
const [workspaceLoadState, setWorkspaceLoadState] = useState<WorkspaceLoadState>({
|
||||
@@ -2840,9 +2841,17 @@ export default function ReverseWorkspace({
|
||||
const poseImportInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const visualToolbarScrollRef = useRef<HTMLDivElement | null>(null);
|
||||
const saveToastTimerRef = useRef<number | null>(null);
|
||||
const exportProgressTimerRef = useRef<number | null>(null);
|
||||
const savedWorkspaceSnapshotRef = useRef('');
|
||||
const initialZStretchRef = useRef<{ projectId: string; pending: boolean }>({ projectId: '', pending: false });
|
||||
|
||||
const clearExportProgressTimer = () => {
|
||||
if (exportProgressTimerRef.current !== null) {
|
||||
window.clearInterval(exportProgressTimerRef.current);
|
||||
exportProgressTimerRef.current = null;
|
||||
}
|
||||
};
|
||||
|
||||
const handleExportSelected = async () => {
|
||||
const selectedItems = exportOptions
|
||||
.filter((option) => exportSelection[option.id])
|
||||
@@ -2854,18 +2863,46 @@ export default function ReverseWorkspace({
|
||||
|
||||
setExporting(true);
|
||||
setFusionError('');
|
||||
clearExportProgressTimer();
|
||||
setExportProgress({ active: true, percent: 8, phase: '准备导出压缩包' });
|
||||
exportProgressTimerRef.current = window.setInterval(() => {
|
||||
setExportProgress((current) => {
|
||||
if (!current.active || current.percent >= 88) {
|
||||
return current;
|
||||
}
|
||||
const step = current.percent < 50 ? 5 : 2;
|
||||
return { ...current, percent: Math.min(88, current.percent + step), phase: '正在生成导出内容' };
|
||||
});
|
||||
}, 450);
|
||||
try {
|
||||
await downloadProjectExportBundle(projectId, selectedItems, 'nii.gz', {
|
||||
pose: modelPose,
|
||||
segmentationScope: segmentationExportScope,
|
||||
segmentationExportMode,
|
||||
moduleStyles,
|
||||
}, (progress) => {
|
||||
if (progress.percent <= 0) {
|
||||
return;
|
||||
}
|
||||
setExportProgress((current) => ({
|
||||
active: true,
|
||||
percent: Math.max(current.percent, Math.min(99, progress.percent)),
|
||||
phase: '正在下载压缩包',
|
||||
}));
|
||||
});
|
||||
window.setTimeout(() => setExporting(false), 900);
|
||||
clearExportProgressTimer();
|
||||
setExportProgress({ active: true, percent: 100, phase: '导出完成' });
|
||||
window.setTimeout(() => {
|
||||
setExporting(false);
|
||||
setExportProgress({ active: false, percent: 0, phase: '' });
|
||||
}, 900);
|
||||
setShowExportMenu(false);
|
||||
} catch (error) {
|
||||
clearExportProgressTimer();
|
||||
setFusionError(error instanceof Error ? error.message : '导出失败');
|
||||
setExporting(false);
|
||||
setExportProgress({ active: true, percent: 100, phase: '导出失败' });
|
||||
window.setTimeout(() => setExportProgress({ active: false, percent: 0, phase: '' }), 1200);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2893,6 +2930,13 @@ export default function ReverseWorkspace({
|
||||
cutEnabled,
|
||||
]);
|
||||
|
||||
useEffect(() => () => {
|
||||
if (exportProgressTimerRef.current !== null) {
|
||||
window.clearInterval(exportProgressTimerRef.current);
|
||||
exportProgressTimerRef.current = null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleSaveSegmentationResult = useCallback(async (options: { showToast?: boolean } = {}) => {
|
||||
if (!project) {
|
||||
return false;
|
||||
@@ -3716,6 +3760,19 @@ export default function ReverseWorkspace({
|
||||
|
||||
return (
|
||||
<div className="h-full min-h-0 overflow-y-auto pr-2 flex flex-col gap-6">
|
||||
{exportProgress.active && (
|
||||
<div className="pointer-events-none fixed inset-x-0 top-0 z-[80]">
|
||||
<div className="h-1 w-full bg-slate-200/70">
|
||||
<div
|
||||
className={`h-full transition-all duration-300 ${exportProgress.phase === '导出失败' ? 'bg-rose-500' : 'bg-emerald-500'}`}
|
||||
style={{ width: `${Math.max(3, Math.min(100, exportProgress.percent))}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute right-6 top-2 rounded-full border border-emerald-100 bg-white/95 px-3 py-1 text-[11px] font-bold text-emerald-700 shadow-lg shadow-slate-950/10">
|
||||
{exportProgress.phase} {Math.round(exportProgress.percent)}%
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{saveStatus && (
|
||||
<>
|
||||
<style>
|
||||
|
||||
Reference in New Issue
Block a user