+
+
+
+
+
+
+
+
+
+ {{ `${taskInfo.SubjectCode} ${v.taskInfo.TaskBlindName} ` }}
+
+
{{ v.currentFileName }}
+
+
+
+
+
+
+
+
+
@@ -946,6 +988,12 @@ export default {
// })
// })
},
+ getCellStyle(index) {
+ return {
+ ...this.cellStyle,
+ display: index < this.cells.length ? this.cellStyle.display : 'none'
+ }
+ },
// 切换全屏
toggleFullScreen(e, index) {
const i = this.viewportInfos.findIndex(i => i.index === index)
@@ -1763,16 +1811,86 @@ export default {
viewport.render()
},
async saveImage() {
- const divForDownloadViewport = document.querySelector(
- `div[data-viewport-uid="canvas-${this.activeCanvasIndex}"]`
- )
+ const viewportIndex = this.viewportInfos.findIndex(v => v.index === this.activeCanvasIndex)
+ if (viewportIndex === -1) return
+ const currentViewport = this.viewportInfos[viewportIndex]
+
+ let divForDownloadViewport = null
+ let tempImg = null
+
+ if (currentViewport.fileType && currentViewport.fileType.includes('mp4')) {
+ divForDownloadViewport = this.$refs[`grid-cell-${this.activeCanvasIndex}`][0]
+ const videoEl = this.$refs[`videovideo-${this.activeCanvasIndex}`][0]
+
+ if (videoEl) {
+ // 将视频帧画到 canvas 上生成图片
+ const tempCanvas = document.createElement('canvas')
+ // 设置 canvas 尺寸为容器实际尺寸
+ const containerWidth = videoEl.clientWidth
+ const containerHeight = videoEl.clientHeight
+ tempCanvas.width = containerWidth
+ tempCanvas.height = containerHeight
+
+ const ctx = tempCanvas.getContext('2d')
+ // 填充黑色背景
+ ctx.fillStyle = '#000'
+ ctx.fillRect(0, 0, containerWidth, containerHeight)
+
+ // 计算视频保持比例的缩放尺寸和位置 (等同于 object-fit: contain)
+ const videoWidth = videoEl.videoWidth || containerWidth
+ const videoHeight = videoEl.videoHeight || containerHeight
+
+ const scale = Math.min(containerWidth / videoWidth, containerHeight / videoHeight)
+ const drawWidth = videoWidth * scale
+ const drawHeight = videoHeight * scale
+ const offsetX = (containerWidth - drawWidth) / 2
+ const offsetY = (containerHeight - drawHeight) / 2
+
+ // 将视频帧居中绘制
+ ctx.drawImage(videoEl, offsetX, offsetY, drawWidth, drawHeight)
+
+ const frameBase64 = tempCanvas.toDataURL('image/png')
+
+ // 创建一个 img 标签遮盖在 video 上
+ tempImg = document.createElement('img')
+ tempImg.src = frameBase64
+ tempImg.style.position = 'absolute'
+ tempImg.style.top = '0'
+ tempImg.style.left = '0'
+ tempImg.style.width = '100%'
+ tempImg.style.height = '100%'
+ tempImg.style.zIndex = '99'
+
+ const videoContainer = this.$refs[`video-content-main-${this.activeCanvasIndex}`][0]
+ videoContainer.style.position = 'relative'
+ videoContainer.appendChild(tempImg)
+ }
+ } else {
+ divForDownloadViewport = document.querySelector(
+ `div[data-viewport-uid="canvas-${this.activeCanvasIndex}"]`
+ )
+ }
+
if (!divForDownloadViewport) return
- const canvas = await html2canvas(divForDownloadViewport)
+
+ // 等待 DOM 更新
+ await this.$nextTick()
+
+ const canvas = await html2canvas(divForDownloadViewport, {
+ useCORS: true,
+ backgroundColor: '#000000' // 指定截图背景为黑色
+ })
+
+ // 截图后恢复
+ if (tempImg) {
+ tempImg.remove()
+ }
+
const base64Str = canvas.toDataURL('image/png', 1)
- const i = this.viewportInfos.findIndex(v => v.index === this.activeCanvasIndex)
- const name = (i > -1 ? this.viewportInfos[i].currentFileName : '') || `screenshot-${Date.now()}`
+ const name = currentViewport.currentFileName || `screenshot-${Date.now()}`
const safeName = String(name).replace(/[\\/:*?"<>|]/g, '_')
const downloadName = safeName.toLowerCase().endsWith('.png') ? safeName : `${safeName}.png`
+
const a = document.createElement('a')
a.href = base64Str
a.download = downloadName
@@ -2188,10 +2306,13 @@ export default {
.grid-cell {
border: 1px dashed #ccc;
- ;
display: flex;
align-items: center;
justify-content: center;
+ overflow: hidden;
+ min-width: 0;
+ min-height: 0;
+ z-index: 1;
}
.cell_active {
@@ -2294,6 +2415,21 @@ export default {
background: #9e9e9e;
cursor: move
}
+
+ .content-main {
+ position: relative;
+ overflow: hidden;
+ background: #1e1e1e;
+ }
+
+ video {
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ object-position: center center;
+ background: transparent;
+ }
}
}
diff --git a/src/views/trials/trials-panel/reading/visit-review/components/StudyList.vue b/src/views/trials/trials-panel/reading/visit-review/components/StudyList.vue
index 9cda8fc5..27bc7441 100644
--- a/src/views/trials/trials-panel/reading/visit-review/components/StudyList.vue
+++ b/src/views/trials/trials-panel/reading/visit-review/components/StudyList.vue
@@ -144,11 +144,11 @@ export default {
},
// 切换文件
selectFile(study, studyIndex, fileIndex, item) {
- if (item.FileType.includes('mp4')) return this.$preview({
- path: item.Path || item.FilePath,
- type: 'mp4',
- title: item.FileName,
- })
+ // if (item.FileType.includes('mp4')) return this.$preview({
+ // path: item.Path || item.FilePath,
+ // type: 'mp4',
+ // title: item.FileName,
+ // })
this.activeStudyIndex = studyIndex
this.activeFileIndex = fileIndex
const fileList = study.NoneDicomStudyFileList