auto refresh workspace preview
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user