2026-05-20-21-25-19 项目库结果视图与加载缓存优化
This commit is contained in:
@@ -26,6 +26,9 @@ import { api, downloadDicomArchive, downloadProjectExportBundle, ProjectExportTa
|
||||
import {
|
||||
FusionThreeView,
|
||||
VoxelizationMappingView,
|
||||
getCachedDicomFusionVolume,
|
||||
getCachedDicomPreview,
|
||||
getCachedModelPreview,
|
||||
dicomOpacityOptions as reverseDicomOpacityOptions,
|
||||
displayOptions as reverseDisplayOptions,
|
||||
} from './ReverseWorkspace';
|
||||
@@ -433,11 +436,7 @@ function NativeStlViewer({
|
||||
|
||||
Promise.allSettled(
|
||||
visibleFiles.map((fileName) =>
|
||||
fetch(`/api/projects/${projectId}/models/${encodeURIComponent(fileName)}/preview?limit=3500`)
|
||||
.then((response) => {
|
||||
if (!response.ok) throw new Error('模型预览数据加载失败');
|
||||
return response.json() as Promise<ModelPreviewPayload>;
|
||||
})
|
||||
getCachedModelPreview(projectId, fileName, 3500)
|
||||
.then((payload) => ({
|
||||
payload,
|
||||
style: styles[fileName] ?? { color: '#3b82f6', opacity: 0.72, visible: true, partId: 1 },
|
||||
@@ -490,13 +489,7 @@ function NativeStlViewer({
|
||||
const loadedBounds: Array<{ min: THREE.Vector3; max: THREE.Vector3 }> = [];
|
||||
|
||||
visibleFiles.forEach((fileName) => {
|
||||
fetch(`/api/projects/${projectId}/models/${encodeURIComponent(fileName)}/preview?limit=${detailLimit}`)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error('模型预览数据加载失败');
|
||||
}
|
||||
return response.json() as Promise<ModelPreviewPayload>;
|
||||
})
|
||||
getCachedModelPreview(projectId, fileName, detailLimit)
|
||||
.then((payload) => {
|
||||
if (disposed) return;
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
@@ -697,11 +690,11 @@ export default function ProjectLibrary({
|
||||
preloadedProjectIdsRef.current.add(project.id);
|
||||
const maxSlice = Math.max((project.dicomCount || 1) - 1, 0);
|
||||
if (project.dicomCount > 0) {
|
||||
void api.getDicomPreview(project.id, maxSlice, 'axial', 'default').catch(() => undefined);
|
||||
void api.getDicomFusionVolume(project.id, maxSlice, maxSlice, 'soft').catch(() => undefined);
|
||||
void getCachedDicomPreview(project.id, maxSlice, 'axial', 'default').catch(() => undefined);
|
||||
void getCachedDicomFusionVolume(project.id, maxSlice, maxSlice, 'soft').catch(() => undefined);
|
||||
}
|
||||
(project.stlFiles ?? []).slice(0, 3).forEach((fileName) => {
|
||||
void fetch(`/api/projects/${project.id}/models/${encodeURIComponent(fileName)}/preview?limit=3500`).catch(() => undefined);
|
||||
void getCachedModelPreview(project.id, fileName, 3500).catch(() => undefined);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -860,7 +853,7 @@ export default function ProjectLibrary({
|
||||
dicomRequestRef.current = requestId;
|
||||
setDicomError('');
|
||||
setIsSliceChanging(true);
|
||||
api.getDicomPreview(selectedProject.id, sliceIndex, plane, displayMode)
|
||||
getCachedDicomPreview(selectedProject.id, sliceIndex, plane, displayMode)
|
||||
.then((preview) => {
|
||||
if (!cancelled && requestId === dicomRequestRef.current) {
|
||||
setDicomPreview(preview);
|
||||
@@ -891,7 +884,7 @@ export default function ProjectLibrary({
|
||||
const start = Math.min(resultCutStart, resultCutEnd);
|
||||
const end = Math.max(resultCutStart, resultCutEnd);
|
||||
setResultFusionError('');
|
||||
api.getDicomFusionVolume(selectedProject.id, start, end, 'soft')
|
||||
getCachedDicomFusionVolume(selectedProject.id, start, end, 'soft')
|
||||
.then((volume) => {
|
||||
if (!cancelled) {
|
||||
setResultFusionVolume(volume);
|
||||
@@ -1566,8 +1559,8 @@ export default function ProjectLibrary({
|
||||
)}
|
||||
|
||||
{viewMode === 'mask' && (
|
||||
<div className="h-full grid grid-cols-1 gap-6 2xl:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_340px]">
|
||||
<div className="min-h-[560px]">
|
||||
<div className="grid h-full min-h-0 grid-cols-1 items-stretch gap-6 2xl:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_340px]">
|
||||
<div className="flex min-h-[620px] min-w-0 flex-col">
|
||||
{latestSegmentationResult ? (
|
||||
<FusionThreeView
|
||||
project={selectedProject}
|
||||
@@ -1581,9 +1574,10 @@ export default function ProjectLibrary({
|
||||
cutEnabled={latestSegmentationResult.cutEnabled ?? false}
|
||||
cutStart={resultCutStart}
|
||||
cutEnd={resultCutEnd}
|
||||
viewPreset="libraryResult"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-full min-h-[560px] items-center justify-center rounded-3xl border border-dashed border-slate-200 bg-slate-950 px-8 text-center text-sm font-bold text-white/35">
|
||||
<div className="flex h-full min-h-[620px] items-center justify-center rounded-3xl border border-dashed border-slate-200 bg-slate-950 px-8 text-center text-sm font-bold text-white/35">
|
||||
暂无保存结果,请在逆向工作区保存当前映射。
|
||||
</div>
|
||||
)}
|
||||
@@ -1594,42 +1588,7 @@ export default function ProjectLibrary({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="min-h-[560px]">
|
||||
<div className="mb-3 flex flex-wrap items-center justify-between gap-2">
|
||||
<h3 className="flex items-center gap-2 font-bold text-slate-700">
|
||||
<Layers size={18} className="text-cyan-500" />
|
||||
逆向分割映射视图
|
||||
</h3>
|
||||
<div className="flex flex-wrap items-center justify-end gap-1.5">
|
||||
<div className="flex rounded-xl bg-slate-100 p-1">
|
||||
{displayModes.map((mode) => (
|
||||
<button
|
||||
key={mode.id}
|
||||
onClick={() => setResultDisplayMode(mode.id)}
|
||||
className={`rounded-lg px-2 py-1 text-[10px] font-bold transition ${
|
||||
resultDisplayMode === mode.id ? 'bg-white text-cyan-600 shadow-sm' : 'text-slate-500 hover:text-slate-700'
|
||||
}`}
|
||||
>
|
||||
{mode.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setResultRotation((value) => (value + 270) % 360)}
|
||||
className="flex h-7 w-7 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-500 hover:text-cyan-600"
|
||||
title="左转 90°"
|
||||
>
|
||||
<RotateCcw size={14} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setResultRotation((value) => (value + 90) % 360)}
|
||||
className="flex h-7 w-7 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-500 hover:text-cyan-600"
|
||||
title="右转 90°"
|
||||
>
|
||||
<RotateCw size={14} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-h-[620px] min-w-0 flex-col">
|
||||
{latestSegmentationResult ? (
|
||||
<VoxelizationMappingView
|
||||
project={selectedProject}
|
||||
@@ -1641,9 +1600,41 @@ export default function ProjectLibrary({
|
||||
onSliceChange={setResultPreviewSlice}
|
||||
displayMode={resultDisplayMode}
|
||||
rotation={resultRotation}
|
||||
variant="library"
|
||||
toolbar={(
|
||||
<>
|
||||
<div className="flex rounded-xl bg-white/10 p-1">
|
||||
{displayModes.map((mode) => (
|
||||
<button
|
||||
key={mode.id}
|
||||
onClick={() => setResultDisplayMode(mode.id)}
|
||||
className={`rounded-lg px-2 py-1 text-[10px] font-bold transition ${
|
||||
resultDisplayMode === mode.id ? 'bg-white text-cyan-700 shadow-sm' : 'text-white/55 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
{mode.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setResultRotation((value) => (value + 270) % 360)}
|
||||
className="flex h-8 w-8 items-center justify-center rounded-xl border border-white/10 bg-black/60 text-white/65 hover:border-cyan-300/30 hover:text-cyan-100"
|
||||
title="左转 90°"
|
||||
>
|
||||
<RotateCcw size={14} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setResultRotation((value) => (value + 90) % 360)}
|
||||
className="flex h-8 w-8 items-center justify-center rounded-xl border border-white/10 bg-black/60 text-white/65 hover:border-cyan-300/30 hover:text-cyan-100"
|
||||
title="右转 90°"
|
||||
>
|
||||
<RotateCw size={14} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-full min-h-[520px] items-center justify-center rounded-3xl border border-dashed border-slate-200 bg-slate-950 px-8 text-center text-sm font-bold text-white/35">
|
||||
<div className="flex h-full min-h-[620px] items-center justify-center rounded-3xl border border-dashed border-slate-200 bg-slate-950 px-8 text-center text-sm font-bold text-white/35">
|
||||
暂无逆向分割映射视图。
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user