From bdff6b52318f1fcb2e298e501238008623dbba47 Mon Sep 17 00:00:00 2001 From: caiyiling <1321909229@qq.com> Date: Wed, 26 Aug 2026 12:15:04 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E8=BF=9B=E5=BA=A6=E6=9D=A1?= =?UTF-8?q?=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reading/dicoms3D/components/ReadPage.vue | 36 +++++++-- .../reading/dicoms3D/components/Viewport.vue | 73 ++++++++++++++----- 2 files changed, 84 insertions(+), 25 deletions(-) diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue index cc6e4022..6e3fb370 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue @@ -1331,6 +1331,7 @@ export default { series.SliceIndex = 0 series.LoadedImageCount = 0 series.LoadedImageProgress = 0 + series.ImageLoadState = {} series.TaskInfo = Object.assign({}, taskInfo) series.StudyIndex = studyIndex series.SeriesIndex = seriesIndex @@ -1951,15 +1952,38 @@ export default { } }, // 设置图像下载进度信息 + // setImageLoadedProgress(series, percentComplete, instanceId) { + // let loadedImageCount = series.LoadedImageCount + // if (percentComplete === 100) { + // ++loadedImageCount + // this.$set(series, 'LoadedImageCount', loadedImageCount) + // } + // const newLoadedImageProgress = series.LoadedImageProgress - this.instanceInfo[instanceId].percentComplete + percentComplete + // this.instanceInfo[instanceId].percentComplete = percentComplete + // this.$set(series, 'LoadedImageProgress', newLoadedImageProgress) + // }, + // 设置图像下载进度信息 setImageLoadedProgress(series, percentComplete, instanceId) { - let loadedImageCount = series.LoadedImageCount - if (percentComplete === 100) { - ++loadedImageCount - this.$set(series, 'LoadedImageCount', loadedImageCount) + if (!series || !instanceId) return + if (!series.ImageLoadState || typeof series.ImageLoadState !== 'object') { + this.$set(series, 'ImageLoadState', {}) } - const newLoadedImageProgress = series.LoadedImageProgress - this.instanceInfo[instanceId].percentComplete + percentComplete - this.instanceInfo[instanceId].percentComplete = percentComplete + const imageLoadState = series.ImageLoadState + const prevPercentComplete = Number(imageLoadState[instanceId]?.percentComplete || 0) + const normalizedPercentComplete = Math.max(prevPercentComplete, Number(percentComplete || 0)) + const wasLoaded = Boolean(imageLoadState[instanceId]?.loaded) + const isLoaded = wasLoaded || normalizedPercentComplete === 100 + + this.$set(imageLoadState, instanceId, { + percentComplete: normalizedPercentComplete, + loaded: isLoaded + }) + + const newLoadedImageProgress = series.LoadedImageProgress - prevPercentComplete + normalizedPercentComplete this.$set(series, 'LoadedImageProgress', newLoadedImageProgress) + if (!wasLoaded && isLoaded) { + this.$set(series, 'LoadedImageCount', series.LoadedImageCount + 1) + } }, addHistoryAnnotations(taskId) { // if (this.renderedTaskIds.includes(taskId)) return diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue index 03fe3a19..100a6a90 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue @@ -515,30 +515,65 @@ export default { getImageFileSizeByImageId(imageId) { const instanceId = this.getImageInstanceId(imageId) if (!instanceId) return 0 - const fileSize = Number(this.instanceFileSizeMap[String(instanceId)] || 0) - return Number.isFinite(fileSize) && fileSize > 0 ? fileSize : 0 + return Number(this.instanceFileSizeMap[instanceId] || 0) }, + // async prefetchMetadataInformation(imageIdsToPrefetch, modality, initialLoadedBytes = 0, enableInitPrefetchLimit = false) { + // let totalLoadedBytes = initialLoadedBytes + // let taskPromises = [] + // for (let i = 0; i < imageIdsToPrefetch.length; i++) { + // const imageId = imageIdsToPrefetch[i] + // const nextImageBytes = this.getImageFileSizeByImageId(imageId) + // // 初始化预取累计超过 2G 后停止,剩余切片在用户滚动时按需加载 + // if (enableInitPrefetchLimit && (totalLoadedBytes >= InitPrefetchMaxBytes || (nextImageBytes > 0 && totalLoadedBytes + nextImageBytes > InitPrefetchMaxBytes))) { + // console.info(`[Viewport] 初始化预取累计下载达到 ${this.formatBytes(totalLoadedBytes)},停止继续预取。`) + // break + // } + // taskPromises.push(cornerstoneDICOMImageLoader.wadouri.loadImage(imageIdsToPrefetch[i]).promise) + // totalLoadedBytes += nextImageBytes + // if (taskPromises.length >= 6 || i === imageIdsToPrefetch.length - 1) { + // let res = await Promise.all(taskPromises) + // if (modality === 'PT') { + // this.cachePTMetadata(res) + // } + // taskPromises = [] + // } + // } + // }, async prefetchMetadataInformation(imageIdsToPrefetch, modality, initialLoadedBytes = 0, enableInitPrefetchLimit = false) { - let totalLoadedBytes = Number.isFinite(initialLoadedBytes) && initialLoadedBytes > 0 ? initialLoadedBytes : 0 - let taskPromises = [] - for (let i = 0; i < imageIdsToPrefetch.length; i++) { - const imageId = imageIdsToPrefetch[i] - const nextImageBytes = this.getImageFileSizeByImageId(imageId) - // 初始化预取累计超过 2G 后停止,剩余切片在用户滚动时按需加载 - if (enableInitPrefetchLimit && (totalLoadedBytes >= InitPrefetchMaxBytes || (nextImageBytes > 0 && totalLoadedBytes + nextImageBytes > InitPrefetchMaxBytes))) { - console.info(`[Viewport] 初始化预取累计下载达到 ${this.formatBytes(totalLoadedBytes)},停止继续预取。`) - break - } - taskPromises.push(cornerstoneDICOMImageLoader.wadouri.loadImage(imageIdsToPrefetch[i]).promise) - totalLoadedBytes += nextImageBytes - if (taskPromises.length >= 6 || i === imageIdsToPrefetch.length - 1) { - let res = await Promise.all(taskPromises) - if (modality === 'PT') { - this.cachePTMetadata(res) + if (!imageIdsToPrefetch?.length) return + + const CONCURRENT_LIMIT = 6 // 最大并发数 + let totalLoadedBytes = initialLoadedBytes + let currentIndex = 0 + // 单个任务:不断取下一个切片加载,直到全部完成或中止 + const runWorker = async () => { + while (currentIndex < imageIdsToPrefetch.length) { + const index = currentIndex++ + const imageId = imageIdsToPrefetch[index] + const nextImageBytes = this.getImageFileSizeByImageId(imageId) + + // 大小限制判断 + if (enableInitPrefetchLimit && (totalLoadedBytes >= InitPrefetchMaxBytes || (nextImageBytes > 0 && totalLoadedBytes + nextImageBytes > InitPrefetchMaxBytes))) { + console.info(`[Viewport] 初始化预取累计下载达到 ${this.formatBytes(totalLoadedBytes)},停止继续预取。`) + return + } + totalLoadedBytes += nextImageBytes + + try { + const image = await cornerstoneDICOMImageLoader.wadouri.loadImage(imageId).promise + // PT模态缓存元数据(也可批量收集后统一缓存) + if (modality === 'PT') { + this.cachePTMetadata([image]) + } + } catch (err) { + console.warn(`[Viewport] 预加载失败: ${imageId}`, err) } - taskPromises = [] } } + + // 启动 N 个并发,形成并发池 + const workers = Array.from({ length: CONCURRENT_LIMIT }, () => runWorker()) + await Promise.all(workers) }, cachePTMetadata(images) { images.map(i => {