2026-05-03-23-22-10 修复阅览切换和下载触发
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user