|
|
@@ -31,6 +31,12 @@ import { EVENTS } from '@cornerstonejs/core';
|
|
|
import { useSelector, useDispatch } from 'react-redux';
|
|
|
import { selectOverlayEnabled } from '@/states/view/dicomOverlaySlice';
|
|
|
import { initPlayback, setFrameDetectionResult } from '@/states/view/playbackSlice';
|
|
|
+import {
|
|
|
+ startLoading,
|
|
|
+ updateLoadingProgress,
|
|
|
+ completeLoading,
|
|
|
+ selectViewportLoadingInfo
|
|
|
+} from '@/states/view/imageLoadingSlice';
|
|
|
import PolygonLengthMeasurementTool from '@/components/measures/PolygonLengthMeasurementTool';
|
|
|
import PolylineLengthMeasurementTool from '@/components/measures/PolylineLengthMeasurementTool';
|
|
|
import { PlaybackController } from '@/pages/view/components/playback/PlaybackController';
|
|
|
@@ -54,7 +60,7 @@ const {
|
|
|
|
|
|
// 导入 cursors 用于光标管理
|
|
|
import { cursors } from '@cornerstonejs/tools';
|
|
|
-import { message } from 'antd';
|
|
|
+import { message, Progress, Spin } from 'antd';
|
|
|
const { MouseBindings } = csToolsEnums;
|
|
|
// 全局工具状态变化监听器注册标志
|
|
|
let cursorResetListenerRegistered = false;
|
|
|
@@ -1729,6 +1735,8 @@ const StackViewer = ({
|
|
|
const [renderError, setRenderError] = React.useState<Error | null>(null);
|
|
|
// 获取overlay启用状态
|
|
|
const overlayEnabled = useSelector(selectOverlayEnabled);
|
|
|
+ // 从 Redux 获取当前视口的加载状态
|
|
|
+ const loadingInfo = useSelector(selectViewportLoadingInfo(viewportId));
|
|
|
// 获取 dispatch
|
|
|
const dispatch = useDispatch();
|
|
|
|
|
|
@@ -1871,6 +1879,13 @@ const StackViewer = ({
|
|
|
// 3. 预检查图像可访问性(可选)
|
|
|
// await validateImageUrls(imageUrls);
|
|
|
|
|
|
+ // 🎯 立即显示进度条 - 准备阶段
|
|
|
+ dispatch(startLoading({
|
|
|
+ viewportId,
|
|
|
+ stage: 'preparing',
|
|
|
+ message: '正在分析图像...'
|
|
|
+ }));
|
|
|
+
|
|
|
// 4. 多帧DICOM预处理:检测并扩展imageIds
|
|
|
let finalImageUrls = imageUrls;
|
|
|
let detectedFrameCount = 1;
|
|
|
@@ -1910,8 +1925,41 @@ const StackViewer = ({
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 5. 设置图像栈,使用扩展后的imageUrls
|
|
|
- await safeSetStack(viewport, finalImageUrls, imageIndex);
|
|
|
+ // 5. 🎯 切换到加载阶段
|
|
|
+ dispatch(updateLoadingProgress({
|
|
|
+ viewportId,
|
|
|
+ stage: 'loading',
|
|
|
+ message: '加载中...',
|
|
|
+ totalCount: finalImageUrls.length
|
|
|
+ }));
|
|
|
+
|
|
|
+ // 添加图像加载事件监听器来更新进度
|
|
|
+ let loadedCount = 0;
|
|
|
+ const imageLoadedHandler = (evt: any) => {
|
|
|
+ loadedCount++;
|
|
|
+ const newProgress = Math.round((loadedCount / finalImageUrls.length) * 100);
|
|
|
+ console.log(`[StackViewer] 图像加载进度: ${loadedCount}/${finalImageUrls.length} (${newProgress}%)`);
|
|
|
+
|
|
|
+ dispatch(updateLoadingProgress({
|
|
|
+ viewportId,
|
|
|
+ loadedCount,
|
|
|
+ progress: newProgress
|
|
|
+ }));
|
|
|
+ };
|
|
|
+
|
|
|
+ // 注册事件监听器
|
|
|
+ eventTarget.addEventListener(EVENTS.IMAGE_LOADED, imageLoadedHandler);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 设置图像栈,使用扩展后的imageUrls
|
|
|
+ await safeSetStack(viewport, finalImageUrls, imageIndex);
|
|
|
+
|
|
|
+ // 加载完成 - 隐藏进度条
|
|
|
+ dispatch(completeLoading(viewportId));
|
|
|
+ } finally {
|
|
|
+ // 清理事件监听器
|
|
|
+ eventTarget.removeEventListener(EVENTS.IMAGE_LOADED, imageLoadedHandler);
|
|
|
+ }
|
|
|
|
|
|
// 5. 初始化播放状态(用于多帧播放控制)
|
|
|
// 使用 DicomMetadataAnalyzer 进行深度分析,从 DICOM 元数据读取帧数
|
|
|
@@ -2020,6 +2068,8 @@ const StackViewer = ({
|
|
|
console.log(`重新加载图像----结束`);
|
|
|
|
|
|
} catch (error) {
|
|
|
+ // 加载失败时,隐藏进度条
|
|
|
+ dispatch(completeLoading(viewportId));
|
|
|
|
|
|
// 不直接抛出错误,而是保存到状态中
|
|
|
// 在下次渲染时抛出,这样 Error Boundary 才能捕获
|
|
|
@@ -2102,6 +2152,36 @@ const StackViewer = ({
|
|
|
position: 'relative', // 让浮动控制条能够正确定位
|
|
|
}}
|
|
|
>
|
|
|
+ {/* 图像加载进度条 */}
|
|
|
+ {loadingInfo?.isLoading && (
|
|
|
+ <div
|
|
|
+ style={{
|
|
|
+ position: 'absolute',
|
|
|
+ top: '50%',
|
|
|
+ left: '50%',
|
|
|
+ transform: 'translate(-50%, -50%)',
|
|
|
+ zIndex: 1000,
|
|
|
+ textAlign: 'center',
|
|
|
+ backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
|
+ padding: '20px',
|
|
|
+ borderRadius: '8px',
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Progress
|
|
|
+ type="circle"
|
|
|
+ percent={undefined}
|
|
|
+ strokeColor="#1890ff"
|
|
|
+ trailColor="#333"
|
|
|
+ size={80}
|
|
|
+ strokeWidth={6}
|
|
|
+ format={() => ''}//暂时不显示百分比数字,目前不容易得到下载进度,后续再改进
|
|
|
+ />
|
|
|
+ <div style={{ marginTop: 8, color: '#fff', fontSize: 14 }}>
|
|
|
+ {loadingInfo.stage === 'preparing' ? '正在分析图像...' : '加载中...'}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
{/* 播放控制器(逻辑组件,不渲染UI) */}
|
|
|
<PlaybackController
|
|
|
viewportId={viewportId}
|