auto refresh workspace preview

This commit is contained in:
2026-05-03 00:03:56 +08:00
parent 89e6fef7f9
commit e6f9873036

View File

@@ -30,7 +30,6 @@ import {
FolderOpen, FolderOpen,
Server, Server,
AlertCircle, AlertCircle,
Eye,
Info, Info,
X X
} from 'lucide-react'; } from 'lucide-react';
@@ -464,27 +463,42 @@ export default function App() {
showToast('密码更新成功'); showToast('密码更新成功');
}; };
const handleUpdatePreview = async () => { useEffect(() => {
if (currentPage !== 'workspace' || !selectedInputDir) return;
const controller = new AbortController();
const timer = window.setTimeout(async () => {
setIsPreviewLoading(true); setIsPreviewLoading(true);
try { try {
const data = await apiRequest('/api/preview', { const response = await fetch(`${API_BASE}/api/preview`, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
inputDir: selectedInputDir, inputDir: selectedInputDir,
angleDegrees: cervicalRotation angleDegrees: cervicalRotation
}) }),
signal: controller.signal
}); });
const data = await response.json();
if (!response.ok) throw new Error(data.error || '预览生成失败');
setPreviewImage(data.image); setPreviewImage(data.image);
setBackendOnline(true); setBackendOnline(true);
setBackendMessage('预览已由 head_extension_app.py 生成'); setBackendMessage('预览已自动更新');
showToast('预览已更新');
} catch (error) { } catch (error) {
if ((error as Error).name !== 'AbortError') {
setBackendOnline(false); setBackendOnline(false);
showToast((error as Error).message); setBackendMessage((error as Error).message);
} finally {
setIsPreviewLoading(false);
} }
} finally {
if (!controller.signal.aborted) setIsPreviewLoading(false);
}
}, 300);
return () => {
controller.abort();
window.clearTimeout(timer);
}; };
}, [currentPage, selectedInputDir, cervicalRotation]);
const handleRunSimulation = async () => { const handleRunSimulation = async () => {
if (isSimulating) return; if (isSimulating) return;
@@ -694,11 +708,8 @@ export default function App() {
</div> </div>
</div> </div>
<div className="grid grid-cols-2 gap-3"> <div>
<button onClick={handleUpdatePreview} disabled={isPreviewLoading || !selectedInputDir} className="py-3 bg-slate-900 text-white rounded-xl text-xs font-bold hover:bg-slate-700 transition-all flex items-center justify-center gap-2 active:scale-95 disabled:opacity-50"> <button onClick={handleRunSimulation} disabled={isSimulating || !selectedInputDir} className="w-full py-3 bg-blue-600 text-white rounded-xl text-xs font-bold hover:bg-blue-700 transition-all flex items-center justify-center gap-2 active:scale-95 disabled:opacity-50">
<Eye size={14} /> {isPreviewLoading ? '预览中' : '更新预览'}
</button>
<button onClick={handleRunSimulation} disabled={isSimulating || !selectedInputDir} className="py-3 bg-blue-600 text-white rounded-xl text-xs font-bold hover:bg-blue-700 transition-all flex items-center justify-center gap-2 active:scale-95 disabled:opacity-50">
<PlayIcon className="fill-white" size={14} /> {isSimulating ? '生成中' : '四状态输出'} <PlayIcon className="fill-white" size={14} /> {isSimulating ? '生成中' : '四状态输出'}
</button> </button>
</div> </div>
@@ -774,7 +785,10 @@ export default function App() {
<h4 className="font-black text-slate-800"> 2D </h4> <h4 className="font-black text-slate-800"> 2D </h4>
<p className="text-[10px] text-slate-400 font-bold mt-1"> head_extension_app.py preview_deform_2d</p> <p className="text-[10px] text-slate-400 font-bold mt-1"> head_extension_app.py preview_deform_2d</p>
</div> </div>
<div className="text-right">
<span className="text-[10px] font-mono text-slate-400">{cervicalRotation.toFixed(1)} DEG</span> <span className="text-[10px] font-mono text-slate-400">{cervicalRotation.toFixed(1)} DEG</span>
{isPreviewLoading && <p className="text-[9px] font-bold text-blue-500 mt-1">...</p>}
</div>
</div> </div>
<div className="h-[360px] bg-slate-950 flex items-center justify-center"> <div className="h-[360px] bg-slate-950 flex items-center justify-center">
{previewImage ? ( {previewImage ? (
@@ -782,7 +796,7 @@ export default function App() {
) : ( ) : (
<div className="text-center text-slate-500"> <div className="text-center text-slate-500">
<ImageIcon size={42} className="mx-auto mb-3 opacity-40" /> <ImageIcon size={42} className="mx-auto mb-3 opacity-40" />
<p className="text-xs font-bold"> DICOM</p> <p className="text-xs font-bold">{isPreviewLoading ? '正在自动生成预览...' : '选择影像库数据后自动生成预览'}</p>
</div> </div>
)} )}
</div> </div>