Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running Details

main
wangxiaoshuang 2026-06-09 14:57:41 +08:00
commit 4c7c0cce47
2 changed files with 152 additions and 16 deletions

View File

@ -57,13 +57,13 @@
<svg-icon icon-class="lengthscale" class="svg-icon" />
</div>
<!-- 截图 -->
<!-- <div
<div
class="tool-item"
:title="$t('trials:reading:button:screenShot')"
@click.prevent="saveImage"
>
<svg-icon icon-class="image" class="svg-icon" />
</div> -->
</div>
<!-- 重置 -->
<div class="tool-item" :title="$t('trials:reading:button:reset')" @click.prevent="resetViewport">
<svg-icon icon-class="refresh" class="svg-icon" />
@ -130,7 +130,7 @@
<!-- viewports -->
<div class="viewports-wrapper" v-loading="loading" ref="viewports-wrapper">
<div ref="container" class="grid-container" :style="gridStyle">
<div v-for="(v, index) in viewportInfos" v-show="index < cells.length" :key="index" :style="cellStyle"
<div v-for="(v, index) in viewportInfos" :ref="`grid-cell-${index}`" :key="index" :style="getCellStyle(index)"
:class="['grid-cell', index === activeCanvasIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
@dblclick="toggleFullScreen($event, index)" @click="activeCanvas(index)"
@mouseup="sliderMouseup($event, index)" @mousemove="sliderMousemove($event, index)"
@ -172,7 +172,7 @@
@mousedown.stop="sliderMousedown($event, index)" />
</div>
</div>
<div v-show="v.fileType === 'application/pdf' && fullScreenIndex === null" class="content flex_col">
<div v-if="v.fileType === 'application/pdf' && fullScreenIndex === null" class="content flex_col">
<div class="content-top" style="height: 50px;">
<div class="left-top-text">
<div v-if="v.taskInfo.IsExistsClinicalData" class="cd-info"
@ -212,6 +212,48 @@
</div>
</div>
<div v-if="v.fileType.includes('mp4')" class="content flex_col">
<div class="content-top" style="height: 50px;">
<div class="left-top-text">
<div v-if="v.taskInfo.IsExistsClinicalData" class="cd-info"
:title="$t('trials:reading:button:clinicalData')">
<svg-icon style="cursor: pointer;" icon-class="documentation" class="svg-icon"
@click.stop="viewCD(v.taskInfo.VisitTaskId)" />
</div>
<h2 v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo && v.taskInfo" class="subject-info">
{{ `${taskInfo.SubjectCode} ${v.taskInfo.TaskBlindName} ` }}
</h2>
<div v-if="v.currentFileName">{{ v.currentFileName }}</div>
</div>
<div v-if="taskInfo && taskInfo.IsReadingTaskViewInOrder === 1 && v.taskInfo" class="top-center-tool">
<div class="toggle-visit-container">
<div class="arrw_icon"
:style="{ cursor: v.taskInfo.VisitTaskNum !== 0 ? 'pointer' : 'not-allowed', color: v.taskInfo.VisitTaskNum !== 0 ? '#fff' : '#6b6b6b' }"
@click.stop.prevent="toggleTask($event, v.taskInfo.VisitTaskNum, -1, v.index)"
@dblclick.stop="preventDefault($event)">
<i class="el-icon-caret-left" />
</div>
<div class="arrow_text">
{{ v.taskInfo.TaskBlindName }}
</div>
<div class="arrw_icon"
:style="{ cursor: v.taskInfo.VisitTaskNum < taskInfo.VisitNum ? 'pointer' : 'not-allowed', color: v.taskInfo.VisitTaskNum < taskInfo.VisitNum ? '#fff' : '#6b6b6b' }"
@click.stop.prevent="toggleTask($event, v.taskInfo.VisitTaskNum, 1, v.index)"
@dblclick.stop="preventDefault($event)">
<i class="el-icon-caret-right" />
</div>
</div>
</div>
</div>
<div class="content-main" :ref="`video-content-main-${index}`" style="flex: 1;">
<video v-if="v.currentFilePath" :ref="`videovideo-${index}`"
:src="`${OSSclientConfig.basePath}${v.currentFilePath}`" crossorigin="anonymous"
width="100%" height="100%" autoplay controls controlsList="nodownload"></video>
</div>
</div>
</div>
</div>
</div>
@ -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;
}
}
}

View File

@ -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