Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running
Details
continuous-integration/drone/push Build is running
Details
commit
4c7c0cce47
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue