2026-05-03-23-22-10 修复阅览切换和下载触发

This commit is contained in:
2026-05-03 23:26:36 +08:00
parent bff7eead08
commit 7b7c555321
6 changed files with 304 additions and 10 deletions

View File

@@ -346,6 +346,7 @@ function OverviewDicomThumbnail({ item }: { item: LibraryItem }) {
export default function App() {
const restoredDeformationJob = useRef(readStoredDeformationJob()).current;
const downloadedZipJobIds = useRef<Set<string>>(new Set());
// --- Authentication State ---
const [isLoggedIn, setIsLoggedIn] = useState(false);
@@ -381,6 +382,7 @@ export default function App() {
const [viewerPlane, setViewerPlane] = useState('coronal');
const [viewerWindow, setViewerWindow] = useState('default');
const [viewerSliceIndex, setViewerSliceIndex] = useState<number | 'middle'>('middle');
const [debouncedViewerSliceIndex, setDebouncedViewerSliceIndex] = useState<number | 'middle'>('middle');
const [viewerPreview, setViewerPreview] = useState<LibraryViewerPreview | null>(null);
const [isViewerLoading, setIsViewerLoading] = useState(false);
const [viewerError, setViewerError] = useState('');
@@ -462,10 +464,28 @@ export default function App() {
const fileUrl = (path?: string) => path ? `${API_BASE}/api/file?path=${encodeURIComponent(path)}` : '';
const triggerDownload = (path?: string, name?: string) => {
const triggerDownload = async (path?: string, name?: string) => {
if (!path) return;
const directUrl = fileUrl(path);
try {
const response = await fetch(directUrl);
if (!response.ok) throw new Error('下载文件读取失败');
const blob = await response.blob();
const objectUrl = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = objectUrl;
link.download = name || path.split('/').pop() || 'download';
document.body.appendChild(link);
link.click();
link.remove();
window.setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
return;
} catch {
showToast('浏览器安全策略阻止直接下载时,将尝试备用下载方式');
}
const link = document.createElement('a');
link.href = fileUrl(path);
link.href = directUrl;
if (name) link.download = name;
document.body.appendChild(link);
link.click();
@@ -739,8 +759,11 @@ export default function App() {
: job;
setZipJobs(current => ({ ...current, [target]: displayJob }));
if (job.status === 'completed') {
triggerDownload(job.result?.file?.path, job.result?.file?.name);
showToast('ZIP 打包完成,已开始下载');
if (!downloadedZipJobIds.current.has(job.id)) {
downloadedZipJobIds.current.add(job.id);
await triggerDownload(job.result?.file?.path, job.result?.file?.name);
showToast('ZIP 打包完成,已开始下载');
}
}
if (job.status === 'failed') {
showToast(job.error || 'ZIP 打包失败');
@@ -903,6 +926,7 @@ export default function App() {
setViewerPlane('coronal');
setViewerWindow('default');
setViewerSliceIndex('middle');
setDebouncedViewerSliceIndex('middle');
setViewerPreview(null);
setViewerError('');
};
@@ -914,6 +938,13 @@ export default function App() {
setIsViewerLoading(false);
};
useEffect(() => {
const timer = window.setTimeout(() => {
setDebouncedViewerSliceIndex(viewerSliceIndex);
}, typeof viewerSliceIndex === 'number' ? 180 : 0);
return () => window.clearTimeout(timer);
}, [viewerSliceIndex]);
useEffect(() => {
if (!libraryViewerItem) return;
@@ -921,7 +952,7 @@ export default function App() {
setIsViewerLoading(true);
setViewerError('');
fetch(
`${API_BASE}/api/library/reformat-preview?id=${encodeURIComponent(libraryViewerItem.id)}&plane=${encodeURIComponent(viewerPlane)}&index=${encodeURIComponent(String(viewerSliceIndex))}&window=${encodeURIComponent(viewerWindow)}`,
`${API_BASE}/api/library/reformat-preview?id=${encodeURIComponent(libraryViewerItem.id)}&plane=${encodeURIComponent(viewerPlane)}&index=${encodeURIComponent(String(debouncedViewerSliceIndex))}&window=${encodeURIComponent(viewerWindow)}`,
{ signal: controller.signal }
)
.then(async response => {
@@ -937,7 +968,7 @@ export default function App() {
});
return () => controller.abort();
}, [libraryViewerItem?.id, viewerPlane, viewerSliceIndex, viewerWindow]);
}, [libraryViewerItem?.id, viewerPlane, debouncedViewerSliceIndex, viewerWindow]);
const changePassword = (userId: string, newPass: string) => {
setUsers(users.map(u => u.id === userId ? { ...u, password: newPass } : u));
@@ -1953,6 +1984,7 @@ export default function App() {
onClick={() => {
setViewerPlane(option.key);
setViewerSliceIndex('middle');
setDebouncedViewerSliceIndex('middle');
}}
className={`py-3 rounded-xl text-xs font-black transition-all ${
viewerPlane === option.key