项目阅片关键点添加、首次进入阅片弹框提示阅片关键点
continuous-integration/drone/push Build is passing Details

main
wangxiaoshuang 2025-11-14 16:03:32 +08:00
parent cf1af92c2f
commit b55245a83a
14 changed files with 550 additions and 118 deletions

View File

@ -4366,4 +4366,20 @@ export function updateTrialExtralConfig(params, data) {
params,
data
})
}
// 获取项目标准阅片关键点列表
export function getTrialCriterionKeyFileList(data) {
return request({
url: `/TrialCriterionKeyFile/getTrialCriterionKeyFileList`,
method: 'post',
data
})
}
// 设置已阅读关键文件
export function setReadKeyFile(data) {
return request({
url: `/ReadingImageTask/setReadKeyFile`,
method: 'post',
data
})
}

View File

@ -60,6 +60,7 @@ import { Upload } from '@/api/dictionary'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import { getSystemCriterionKeyFileList, addOrUpdateSystemCriterionKeyFile, deleteSystemCriterionKeyFile } from '@/api/dictionary'
import { readEntry } from '@/utils/index'
const searchDataDefault = () => {
return {
FileName: '',

View File

@ -399,9 +399,9 @@
<div style="padding:5px;display: flex;">
<!-- 手册 -->
<el-tooltip class="item" effect="dark" :content="$t('trials:reading:button:handbooks')" placement="bottom"
v-if="isExistsManual">
v-if="isExistsManual || isHaveKeyFile">
<div class="tool-wrapper">
<div class="icon" @click.prevent="previewManuals">
<div class="icon" @click.prevent="previewManuals(false)">
<svg-icon icon-class="constitution" class="svg-icon" />
</div>
<div class="text">{{ $t('trials:reading:button:handbooks') }}</div>
@ -548,8 +548,14 @@
</span>
</span>
<div style="height: 100%;margin:0;">
<Manuals :trial-id="trialId" />
<Manuals :trial-id="trialId" :justKeyDoc="manualsDialog.justKeyDoc" />
<div slot="footer" style="text-align:right;" v-if="openManuals">
<!-- 确认 -->
<el-button type="primary" size="mini" @click="handleSubmitKeyDoc">
{{ $t('common:button:confirm') }}</el-button>
</div>
</div>
</el-dialog>
<el-dialog v-if="fusionSeries.visible" :visible.sync="fusionSeries.visible" :close-on-click-modal="false"
@ -683,7 +689,7 @@ import html2canvas from 'html2canvas'
import uploadDicomAndNonedicom from '@/components/uploadDicomAndNonedicom'
import downloadDicomAndNonedicom from '@/components/downloadDicomAndNonedicom'
import SignForm from '@/views/trials/components/newSignForm'
import { getCriterionReadingInfo, verifyVisitTaskQuestions, submitDicomVisitTask } from '@/api/trials'
import { getCriterionReadingInfo, verifyVisitTaskQuestions, submitDicomVisitTask, setReadKeyFile } from '@/api/trials'
import { setSkipReadingCache } from '@/api/reading'
import { getAutoCutNextTask } from '@/api/user'
import const_ from '@/const/sign-code'
@ -736,6 +742,14 @@ export default {
type: Boolean,
required: true
},
isReadKeyFile: {
type: Boolean,
required: true
},
isHaveKeyFile: {
type: Boolean,
required: false
},
iseCRFShowInDicomReading: {
type: Boolean,
required: true
@ -838,7 +852,7 @@ export default {
activeName: '1',
customWwcTpl: [],
isFullscreen: false,
manualsDialog: { visible: false },
manualsDialog: { visible: false, justKeyDoc: false },
petct: { visible: false },
fusionSeries: { visible: false },
fusionForm: {
@ -886,9 +900,18 @@ export default {
},
computed: {
...mapGetters(['visitTaskList', 'currentReadingTaskState', 'lastCanvasTaskId', 'imageQualityIssues'])
...mapGetters(['visitTaskList', 'currentReadingTaskState', 'lastCanvasTaskId', 'imageQualityIssues']),
openManuals() {
return !this.isReadKeyFile && this.isHaveKeyFile
}
},
watch: {
openManuals: {
handler() {
if (this.openManuals) this.previewManuals(true)
},
immediate: true
},
currentReadingTaskState: {
immediate: true,
handler(val) {
@ -1126,6 +1149,19 @@ export default {
})
},
methods: {
async handleSubmitKeyDoc() {
try {
let data = {
TrialCriterionId: this.$router.currentRoute.query.TrialReadingCriterionId,
}
let res = await setReadKeyFile(data)
if (res.IsSuccess) {
this.manualsDialog.visible = false
}
} catch (err) {
console.log(err)
}
},
showPanel(e, toolName) {
if (toolName === 'layout') return false
e.currentTarget.firstChild.lastChild.style.display = 'block'
@ -2206,9 +2242,10 @@ export default {
previewNoneDicoms() {
this.$emit('previewNoneDicoms')
},
previewManuals() {
previewManuals(justKeyDoc = false) {
this.isFullscreen = false
this.manualsDialog.visible = true
this.manualsDialog.justKeyDoc = justKeyDoc
},
async skipTask() {
//

View File

@ -1,14 +1,9 @@
<template>
<div v-loading="loading" class="manuals-wrapper">
<div class="left-wrapper">
<div v-if="fileList.length>0" class="basic-content">
<div
v-for="file in fileList"
:key="file.Id"
class="file-item"
:class="{activeItem:file.Id === selected.id}"
@click.prevent="preview(file)"
>
<div v-if="fileList.length > 0" class="basic-content">
<div v-for="file in fileList" :key="file.Id" class="file-item" :class="{ activeItem: file.Id === selected.id }"
@click.prevent="preview(file)">
<!-- {{ file.Name }} -->
<el-tooltip class="item" :content="file.Name" placement="bottom">
<span>{{ file.Name }} </span>
@ -21,19 +16,25 @@
</div>
<div class="right-wrapper">
<div class="right-content">
<iframe v-if="selected.filePath" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}&COMPANY=${COMPANY}`" width="100%" height="100%" frameborder="0" />
<iframe v-if="selected.filePath"
:src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}&COMPANY=${COMPANY}`"
width="100%" height="100%" frameborder="0" />
</div>
</div>
</div>
</template>
<script>
import { getManualList } from '@/api/trials'
import { getManualList, getTrialCriterionKeyFileList } from '@/api/trials'
export default {
name: 'Manuals',
props: {
trialId: {
type: String,
required: true
},
justKeyDoc: {
type: Boolean,
default: false
}
},
data() {
@ -45,7 +46,7 @@ export default {
fileList: [],
loading: false,
currentUser: zzSessionStorage.getItem('userName'),
COMPANY:process.env.VUE_APP_COMPANY_NAME
COMPANY: process.env.VUE_APP_COMPANY_NAME
}
},
mounted() {
@ -54,16 +55,35 @@ export default {
methods: {
async getList() {
this.loading = true
this.fileList = []
try {
var param = {
trialId: this.trialId
TrialCriterionId: this.$router.currentRoute.query.TrialReadingCriterionId,
PageIndex: 1,
PageSize: 1000
}
const res = await getManualList(param)
const res = await getTrialCriterionKeyFileList(param)
if (res.IsSuccess) {
this.fileList = res.Result
if (this.fileList.length > 0) {
this.preview(this.fileList[0])
let list = res.Result.CurrentPageData.map(item => {
return {
Id: item.Id,
Name: item.FileName,
Path: item.FilePath
}
})
this.fileList = [...this.fileList, ...list]
}
if (!this.justKeyDoc) {
var param = {
trialId: this.trialId
}
const res = await getManualList(param)
if (res.IsSuccess) {
this.fileList = [...this.fileList, ...res.Result]
}
}
if (this.fileList.length > 0) {
this.preview(this.fileList[0])
}
this.loading = false
} catch (e) {
@ -77,38 +97,43 @@ export default {
}
}
</script>
<style lang="scss">
.manuals-wrapper{
<style lang="scss" scoped>
.manuals-wrapper {
display: flex;
flex-direction: row !important;
width: 100%;
height: 100%;
height: 95%;
padding: 5px;
overflow: hidden;
::-webkit-scrollbar {
width: 7px;
height: 7px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.left-wrapper{
.left-wrapper {
box-sizing: border-box;
margin-right: 10px;
height: 100%;
width: 300px;
border: 1px solid #ddd;
.basic-content{
.basic-content {
height: 100%;
overflow: auto;
}
.basic-content-empty{
.basic-content-empty {
padding: 5px;
font-size: 16px;
}
.file-item{
.file-item {
box-sizing: border-box;
border-bottom: 1px solid #f3f3f3;
height: 50px;
@ -121,18 +146,21 @@ export default {
text-overflow: ellipsis;
width: 100%;
}
.activeItem{
.activeItem {
color: #428bca !important;
border-bottom: 1px solid #f3f3f3 !important;
}
}
.right-wrapper{
.right-wrapper {
flex: 1;
height: 100%;
border: 1px solid #ddd;
}
.right-content{
height:100%;
.right-content {
height: 100%;
}
}

View File

@ -43,6 +43,7 @@
:is-exists-clinical-data="isExistsClinicalData" :is-exists-no-dicom-file="isExistsNoDicomFile"
:is-reading-show-subject-info="isReadingShowSubjectInfo" :studyShow="studyShow"
:is-reading-task-view-in-order="isReadingTaskViewInOrder" :is-exists-manual="isExistsManual"
:isReadKeyFile="isReadKeyFile" :isHaveKeyFile="isHaveKeyFile"
:iseCRFShowInDicomReading="iseCRFShowInDicomReading" @previewCD="previewCD" />
</div>
@ -124,6 +125,14 @@ export default {
type: Boolean,
required: true
},
isReadKeyFile: {
type: Boolean,
required: true
},
isHaveKeyFile: {
type: Boolean,
required: false
},
iseCRFShowInDicomReading: {
type: Boolean,
required: true

View File

@ -340,9 +340,9 @@
<div style="margin-left: auto">
<div style="padding: 5px">
<el-tooltip class="item" effect="dark" :content="$t('trials:reading:button:handbooks')" placement="bottom"
v-if="isExistsManual">
v-if="isExistsManual || isHaveKeyFile">
<div class="tool-wrapper">
<div class="icon" @click.prevent="previewManuals">
<div class="icon" @click.prevent="previewManuals()">
<svg-icon icon-class="constitution" class="svg-icon" />
</div>
<div class="text">{{ $t('trials:reading:button:handbooks') }}</div>
@ -428,7 +428,12 @@
</span>
</span>
<div style="height: 100%; margin: 0">
<Manuals :trial-id="trialId" />
<Manuals :trial-id="trialId" :justKeyDoc="manualsDialog.justKeyDoc" />
<div slot="footer" style="text-align:right;" v-if="openManuals">
<!-- 确认 -->
<el-button type="primary" size="mini" @click="handleSubmitKeyDoc">
{{ $t('common:button:confirm') }}</el-button>
</div>
</div>
</el-dialog>
<!-- <button :title="$t('trials:dicom-show:Eraser')" class="btn-link" data-tool="Eraser" @click="setToolActive($event,'Eraser')">-->
@ -472,7 +477,7 @@ import store from '@/store'
import { getDoctorShortcutKey, getUserWLTemplateList } from '@/api/user'
import uploadDicomAndNonedicom from '@/components/uploadDicomAndNonedicom'
import downloadDicomAndNonedicom from '@/components/downloadDicomAndNonedicom'
import { getCriterionReadingInfo } from '@/api/trials'
import { getCriterionReadingInfo, setReadKeyFile } from '@/api/trials'
export default {
name: 'DicomViewer',
components: {
@ -499,6 +504,14 @@ export default {
type: Boolean,
required: true,
},
isReadKeyFile: {
type: Boolean,
required: true
},
isHaveKeyFile: {
type: Boolean,
required: false
},
isReadingShowSubjectInfo: {
type: Boolean,
required: true,
@ -654,7 +667,7 @@ export default {
activeName: '1',
customWwcTpl: [],
isFullscreen: false,
manualsDialog: { visible: false },
manualsDialog: { visible: false, justKeyDoc: false },
clipPlaying: false,
fps: 15,
trialCriterion: {},
@ -674,8 +687,17 @@ export default {
computed: {
...mapGetters(['visitTaskList', 'currentReadingTaskState']),
openManuals() {
return !this.isReadKeyFile && this.isHaveKeyFile
}
},
watch: {
openManuals: {
handler() {
if (this.openManuals) this.previewManuals(true)
},
immediate: true
},
currentReadingTaskState: {
immediate: true,
handler(val) {
@ -842,6 +864,19 @@ export default {
DicomEvent.$off('loadLinkedImageStack')
},
methods: {
async handleSubmitKeyDoc() {
try {
let data = {
TrialCriterionId: this.$router.currentRoute.query.TrialReadingCriterionId,
}
let res = await setReadKeyFile(data)
if (res.IsSuccess) {
this.manualsDialog.visible = false
}
} catch (err) {
console.log(err)
}
},
getTrialCriterion() {
getCriterionReadingInfo({
TrialId: this.trialId,
@ -2113,9 +2148,10 @@ export default {
previewNoneDicoms() {
this.$emit('previewNoneDicoms')
},
previewManuals() {
previewManuals(justKeyDoc = false) {
this.isFullscreen = false
this.manualsDialog.visible = true
this.manualsDialog.justKeyDoc = justKeyDoc
},
},
}

View File

@ -33,6 +33,7 @@
:is-exists-clinical-data="isExistsClinicalData" :is-exists-no-dicom-file="isExistsNoDicomFile"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-task-view-in-order="isReadingTaskViewInOrder" :isExistsManual="isExistsManual"
:isReadKeyFile="isReadKeyFile" :isHaveKeyFile="isHaveKeyFile"
:IseCRFShowInDicomReading="IseCRFShowInDicomReading" @previewCD="previewCD" />
</div>
</div>
@ -108,6 +109,14 @@ export default {
type: Boolean,
required: true,
},
isReadKeyFile: {
type: Boolean,
required: true
},
isHaveKeyFile: {
type: Boolean,
required: false
},
isExistsNoDicomFile: {
type: Boolean,
required: true,
@ -656,7 +665,7 @@ export default {
},
cornerstoneimageloadprogress(e) {
const imageId = e.detail.imageId
console.log(imageId,'imageId')
console.log(imageId, 'imageId')
const params = {}
const searchParams = new URLSearchParams(imageId.split('?')[1])
for (const [key, value] of searchParams.entries()) {

View File

@ -1,14 +1,9 @@
<template>
<div v-loading="loading" class="manuals-wrapper">
<div class="left-wrapper">
<div v-if="fileList.length>0" class="basic-content">
<div
v-for="file in fileList"
:key="file.Id"
class="file-item"
:class="{activeItem:file.Id === selected.id}"
@click.prevent="preview(file)"
>
<div v-if="fileList.length > 0" class="basic-content">
<div v-for="file in fileList" :key="file.Id" class="file-item" :class="{ activeItem: file.Id === selected.id }"
@click.prevent="preview(file)">
<!-- {{ file.Name }} -->
<el-tooltip class="item" :content="file.Name" placement="bottom">
<span>{{ file.Name }} </span>
@ -21,19 +16,25 @@
</div>
<div class="right-wrapper">
<div class="right-content">
<iframe v-if="selected.filePath" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}&COMPANY=${COMPANY}`" width="100%" height="100%" frameborder="0" />
<iframe v-if="selected.filePath"
:src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}&COMPANY=${COMPANY}`"
width="100%" height="100%" frameborder="0" />
</div>
</div>
</div>
</template>
<script>
import { getManualList } from '@/api/trials'
import { getManualList, getTrialCriterionKeyFileList } from '@/api/trials'
export default {
name: 'Manuals',
props: {
trialId: {
type: String,
required: true
},
justKeyDoc: {
type: Boolean,
default: false
}
},
data() {
@ -45,7 +46,7 @@ export default {
fileList: [],
loading: false,
currentUser: zzSessionStorage.getItem('userName'),
COMPANY:process.env.VUE_APP_COMPANY_NAME
COMPANY: process.env.VUE_APP_COMPANY_NAME
}
},
mounted() {
@ -54,18 +55,38 @@ export default {
methods: {
async getList() {
this.loading = true
var param = {
trialId: this.trialId
}
try{
let res = await getManualList(param)
this.fileList = res.Result
this.fileList = []
try {
var param = {
TrialCriterionId: this.$router.currentRoute.query.TrialReadingCriterionId,
PageIndex: 1,
PageSize: 1000
}
const res = await getTrialCriterionKeyFileList(param)
if (res.IsSuccess) {
let list = res.Result.CurrentPageData.map(item => {
return {
Id: item.Id,
Name: item.FileName,
Path: item.FilePath
}
})
this.fileList = [...this.fileList, ...list]
}
if (!this.justKeyDoc) {
var param = {
trialId: this.trialId
}
const res = await getManualList(param)
if (res.IsSuccess) {
this.fileList = [...this.fileList, ...res.Result]
}
}
if (this.fileList.length > 0) {
this.preview(this.fileList[0])
}
this.loading = false
}catch(e){
} catch (e) {
this.loading = false
}
},
@ -76,38 +97,43 @@ export default {
}
}
</script>
<style lang="scss">
.manuals-wrapper{
<style lang="scss" scoped>
.manuals-wrapper {
display: flex;
flex-direction: row !important;
width: 100%;
height: 100%;
height: 95%;
padding: 5px;
overflow: hidden;
::-webkit-scrollbar {
width: 7px;
height: 7px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.left-wrapper{
.left-wrapper {
box-sizing: border-box;
margin-right: 10px;
height: 100%;
width: 300px;
border: 1px solid #ddd;
.basic-content{
.basic-content {
height: 100%;
overflow: auto;
}
.basic-content-empty{
.basic-content-empty {
padding: 5px;
font-size: 16px;
}
.file-item{
.file-item {
box-sizing: border-box;
border-bottom: 1px solid #f3f3f3;
height: 50px;
@ -120,18 +146,21 @@ export default {
text-overflow: ellipsis;
width: 100%;
}
.activeItem{
.activeItem {
color: #428bca !important;
border-bottom: 1px solid #f3f3f3 !important;
}
}
.right-wrapper{
.right-wrapper {
flex: 1;
height: 100%;
border: 1px solid #ddd;
}
.right-content{
height:100%;
.right-content {
height: 100%;
}
}

View File

@ -18,6 +18,7 @@
:is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-task-view-in-order="isReadingTaskViewInOrder" :is-exists-clinical-data="isExistsClinicalData"
:is-exists-no-dicom-file="isExistsNoDicomFile" :is-exists-manual="isExistsManual"
:isReadKeyFile="isReadKeyFile" :isHaveKeyFile="isHaveKeyFile"
:ise-c-r-f-show-in-dicom-reading="IseCRFShowInDicomReading" @previewCD="previewCD" />
</el-tab-pane>
<!-- 报告 -->
@ -38,7 +39,8 @@
:subject-code="subjectCode" :is-show="isShow" :is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-task-view-in-order="isReadingTaskViewInOrder"
:ise-c-r-f-show-in-dicom-reading="IseCRFShowInDicomReading" :is-exists-clinical-data="isExistsClinicalData"
:is-exists-no-dicom-file="isExistsNoDicomFile" :is-exists-manual="isExistsManual" @previewCD="previewCD" />
:is-exists-no-dicom-file="isExistsNoDicomFile" :is-exists-manual="isExistsManual"
:isReadKeyFile="isReadKeyFile" :isHaveKeyFile="isHaveKeyFile" @previewCD="previewCD" />
</el-tab-pane>
<!-- 报告 -->
<el-tab-pane v-if="!IseCRFShowInDicomReading" :label="$t('trials:reading:tabTitle:report')" name="report">
@ -219,7 +221,9 @@ export default {
closeCDVisible: false,
cdVisitTaskId: '',
readingVersionEnum: null,
dialogBodyPartVisible: false
dialogBodyPartVisible: false,
isReadKeyFile: true,
isHaveKeyFile: false
}
},
computed: {
@ -327,6 +331,8 @@ export default {
this.isExistsNoDicomFile = res.Result.IsExistsNoDicomFile
this.isExistsClinicalData = res.Result.IsExistsClinicalData
this.isExistsManual = res.Result.ExistsManual
this.isReadKeyFile = res.Result.IsReadKeyFile
this.isHaveKeyFile = res.Result.IsHaveKeyFile
this.isReadClinicalData = res.Result.IsReadClinicalData
this.IsViewStudyPart = res.Result.IsViewStudyPart
this.isNeedReadClinicalData = res.Result.IsNeedReadClinicalData

View File

@ -270,7 +270,7 @@
<div style="display: flex;">
<!-- 手册 -->
<div class="tool-item" :title="$t('trials:reading:button:handbooks')"
v-if="taskInfo && taskInfo.ExistsManual" @click.prevent="previewManuals">
v-if="taskInfo && (taskInfo.ExistsManual || taskInfo.IsHaveKeyFile)" @click.prevent="previewManuals()">
<svg-icon icon-class="constitution" class="svg-icon" />
</div>
<!-- 临床数据 -->
@ -370,7 +370,12 @@
</span>
</span>
<div style="height: 100%;margin:0;">
<Manuals :trial-id="trialId" />
<Manuals :trial-id="trialId" :justKeyDoc="manualsDialog.justKeyDoc" />
<div slot="footer" style="text-align:right;" v-if="openManuals">
<!-- 确认 -->
<el-button type="primary" size="mini" @click="handleSubmitKeyDoc">
{{ $t('common:button:confirm') }}</el-button>
</div>
</div>
</el-dialog>
<!-- 个性化配置 -->
@ -425,7 +430,7 @@
</div>
</template>
<script>
import { getRelatedVisitTask, getReadingVisitStudyList, getTableAnswerRowInfoList, deleteCustomTag, getCriterionReadingInfo } from '@/api/trials'
import { getRelatedVisitTask, getReadingVisitStudyList, getTableAnswerRowInfoList, deleteCustomTag, getCriterionReadingInfo, setReadKeyFile } from '@/api/trials'
import { getDoctorShortcutKey, getUserWLTemplateList } from '@/api/user'
import { getCustomTag, submitCustomTag } from '@/api/reading'
import { getToken } from '@/utils/auth'
@ -596,7 +601,7 @@ export default {
activeViewportWC: null,
clipPlaying: false,
fps: 15,
manualsDialog: { visible: false, isFullscreen: false },
manualsDialog: { visible: false, isFullscreen: false, justKeyDoc: false },
hotKeyList: [],
forceFitToWindow: false,
isShowAnnotations: true,
@ -661,8 +666,17 @@ export default {
viewportKey() {
return this.isFusion ? 'viewport-fusion' : 'viewport'
},
openManuals() {
return !this.taskInfo.IsReadKeyFile && this.taskInfo.IsHaveKeyFile
}
},
watch: {
openManuals: {
handler() {
if (this.openManuals) this.previewManuals(true)
},
immediate: true
},
currentReadingTaskState() { console.log(this.currentReadingTaskState, 'currentReadingTaskState') },
activeTaskId: {
immediate: true,
@ -751,6 +765,19 @@ export default {
})
},
methods: {
async handleSubmitKeyDoc() {
try {
let data = {
TrialCriterionId: this.$router.currentRoute.query.TrialReadingCriterionId,
}
let res = await setReadKeyFile(data)
if (res.IsSuccess) {
this.manualsDialog.visible = false
}
} catch (err) {
console.log(err)
}
},
//
async loadRelatedTasks() {
this.loading = true
@ -2792,9 +2819,10 @@ export default {
return a < b ? (a < c ? a : c) : (b < c ? b : c)
},
//
previewManuals() {
previewManuals(justKeyDoc = false) {
this.manualsDialog.isFullscreen = false
this.manualsDialog.visible = true
this.manualsDialog.justKeyDoc = justKeyDoc
},
//
previewCD(taskId) {

View File

@ -116,8 +116,8 @@
<div style="display: flex;">
<!-- 手册 -->
<div class="tool-item" :title="$t('trials:reading:button:handbooks')" v-if="taskInfo && taskInfo.ExistsManual"
@click.prevent="previewManuals">
<div class="tool-item" :title="$t('trials:reading:button:handbooks')"
v-if="taskInfo && (taskInfo.ExistsManual || taskInfo.IsHaveKeyFile)" @click.prevent="previewManuals">
<svg-icon icon-class="constitution" class="svg-icon" />
</div>
<!-- 个性化配置 -->
@ -254,7 +254,12 @@
</span>
</span>
<div style="height: 100%;margin:0;">
<Manuals :trial-id="trialId" />
<Manuals :trial-id="trialId" :justKeyDoc="manualsDialog.justKeyDoc" />
<div slot="footer" style="text-align:right;" v-if="openManuals">
<!-- 确认 -->
<el-button type="primary" size="mini" @click="handleSubmitKeyDoc">
{{ $t('common:button:confirm') }}</el-button>
</div>
</div>
</el-dialog>
</div>
@ -410,9 +415,18 @@ export default {
cells() {
return Array(this.rows * this.cols).fill(0)
},
...mapGetters(['lastViewportTaskId', 'currentTaskState'])
...mapGetters(['lastViewportTaskId', 'currentTaskState']),
openManuals() {
return !this.taskInfo.IsReadKeyFile && this.taskInfo.IsHaveKeyFile
}
},
watch: {
openManuals: {
handler() {
if (this.openManuals) this.previewManuals(true)
},
immediate: true
},
relatedStudyInfo: {
immediate: true,
handler(obj) {
@ -477,10 +491,24 @@ export default {
window.removeEventListener('message', this.handleIframeMessage)
},
methods: {
async handleSubmitKeyDoc() {
try {
let data = {
TrialCriterionId: this.$router.currentRoute.query.TrialReadingCriterionId,
}
let res = await setReadKeyFile(data)
if (res.IsSuccess) {
this.manualsDialog.visible = false
}
} catch (err) {
console.log(err)
}
},
//
previewManuals() {
previewManuals(justKeyDoc = false) {
this.manualsDialog.isFullscreen = false
this.manualsDialog.visible = true
this.manualsDialog.justKeyDoc = justKeyDoc
},
getTrialCriterion() {
getCriterionReadingInfo({

View File

@ -288,6 +288,15 @@
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 阅片关键点 -->
<el-form-item :label="$t('trials:readingUnit:readingRules:title:keyDoc')
" prop="KeyFileList">
<el-input v-model="form.KeyFileListStr" type="textarea" :autosize="{ minRows: 1, maxRows: 3 }"
style="width: 80%" disabled />
<el-button :disabled="isConfirm ||
!hasPermi(['trials:trials-panel:setting:reading-unit:edit'])
" icon="el-icon-plus" circle @click="handleSetKeyFileList" />
</el-form-item>
<el-form-item :label="$t(
'trials:trials-panel:setting:reading-unit:TrialCriterionAdditionalAssessmentTypeList'
)
@ -332,11 +341,45 @@
</el-button>
</el-form-item>
</el-form>
<!-- 配置信息确认框 -->
<el-dialog v-if="keyDocVisible" :visible.sync="keyDocVisible" :close-on-click-modal="false" width="600px"
:title="$t('trials:reading-unit:dialogTitle:keyDoc')" custom-class="base-dialog-wrapper">
<div class="base-dialog-body">
<div class="upload">
<input directory accept=".pdf" type="file" name="uploadFolder" class="select-file" title=""
@change="beginScanFiles($event)" />
<div class="btn-select">
{{ $t('dictionary:template:basicData:button:selectFile') }}
</div>
</div>
<div class="drag" ref="drag" @dragover="handleDragover" @drop="handleDrop">
<el-table :data="confirmData" border style="width: 100%" size="small">
<el-table-column prop="FileName" :label="$t('trials:reading-unit:table:FileName')" show-overflow-tooltip />
<el-table-column :label="$t('common:action:action')" align="left" fixed="right">
<template slot-scope="scope">
<el-button circle icon="el-icon-view" :title="$t('dictionary:template:keyDocList:button:view')"
@click.stop="view(scope.row)" />
<el-button circle icon="el-icon-delete" :title="$t('dictionary:template:keyDocList:button:del')"
@click.stop="del(scope.$index)" />
</template>
</el-table-column>
</el-table>
</div>
</div>
<div class="base-dialog-footer" style="text-align: right; margin-top: 10px">
<!-- 确认 -->
<el-button size="small" type="primary" @click="handleConfirm">
{{ $t('trials:reading-unit:button:confirmCfg') }}
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getCriterionReadingInfo, setCriterionReadingInfo } from '@/api/trials'
import { config } from '@/views/trials/trials-panel/reading/dicoms3D/components/toolConfig'
import { readEntry } from '@/utils/index'
export default {
name: 'ReadingRules',
props: {
@ -350,9 +393,15 @@ export default {
type: Number,
default: 0,
},
TrialReadingCriterionName: {
type: String,
default: ''
}
},
data() {
return {
keyDocVisible: false,
confirmData: [],
additionalAssessmentOptionList: [],
form: {
TrialId: '',
@ -387,6 +436,8 @@ export default {
ImageDownloadEnum: null,
ImageUploadEnum: null,
IsImageFilter: false,
KeyFileListStr: '',
KeyFileList: []
},
rules: {
IsAutoCreate: [
@ -577,6 +628,83 @@ export default {
},
},
methods: {
handleDragover(e) {
e.stopPropagation();
e.preventDefault();
},
handleDrop(e) {
e.stopPropagation();
e.preventDefault();
this.beginScanFiles(e, true)
},
view(row) {
this.$preview({
path: row.FilePath,
type: 'pdf',
title: row.FileName,
})
},
del(index) {
this.confirmData.splice(index, 1)
},
handleSetKeyFileList() {
this.confirmData = [...this.form.KeyFileList]
this.keyDocVisible = true
},
handleConfirm() {
this.form.KeyFileList = [...this.confirmData]
this.form.KeyFileListStr = this.form.KeyFileList.map(item => item.FileName).join(',')
this.keyDocVisible = false
},
async beginScanFiles(e, isDrop = false) {
try {
let files = []
if (isDrop) {
const items = e.dataTransfer.items;
const allFiles = []; //
//
for (const item of items) {
const entry = item.webkitGetAsEntry(); //
if (entry) {
const files = await readEntry(entry); //
allFiles.push(...files);
}
}
files = allFiles
} else {
files = [...e.target.files]
}
for (let i = 0; i < files.length; i++) {
let file = files[i]
let name = file.name
if (!this.checkFileSuffix(name)) continue
file = await this.fileToBlob(file)
let scope = this
let res = await this.OSSclient.put(
`/${this.$route.query.trialId}/ReadingModule/${this.TrialReadingCriterionName}/${scope.getGuid(Date.now() + '_' + name.split('.')[0])}.pdf`,
file
)
let data = {
FilePath: this.$getObjectName(res.url),
FileName: name
}
this.confirmData.push(data)
}
} catch (err) {
console.log(err)
this.loading = false;
}
},
checkFileSuffix(fileName) {
var index = fileName.lastIndexOf('.')
var suffix = fileName.substring(index + 1, fileName.length)
if ('.pdf'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1) {
return false
} else {
return true
}
},
//
handleReadingToolInput(val) {
this.form.ReadingToolList = []
@ -625,6 +753,9 @@ export default {
this.form[k] = res.Result[k]
}
}
if (Array.isArray(this.form.KeyFileList) && this.form.KeyFileList.length > 0) {
this.form.KeyFileListStr = this.form.KeyFileList.map(item => item.FileName).join(',')
}
if (this.CriterionType === 0) {
if (this.form.ReadingTool === 0 || this.form.ReadingTool === 2) {
this.tools = [...config.customizeStandards]
@ -727,4 +858,46 @@ export default {
color: red;
}
}
.upload {
display: inline-block;
height: 30px;
width: 90px;
padding: 2px 10px;
line-height: 23px;
position: relative;
text-decoration: none;
border-radius: 3px;
overflow: hidden;
text-align: center;
background: #428bca;
border-color: #428bca;
color: #fff;
.select-file {
height: 30px;
width: 90px;
position: absolute;
overflow: hidden;
left: 0;
top: 0;
opacity: 0;
font-size: 0;
}
.btn-select {
//
width: 90px;
height: 30px;
line-height: 30px;
text-align: center;
cursor: pointer;
border-radius: 24px;
overflow: hidden;
position: absolute;
top: 0;
left: 0;
pointer-events: none; //pointer-events:none穿
}
}
</style>

View File

@ -19,11 +19,11 @@
<el-collapse-item :title="$t('trials:readingUnit:readingRules')" name="1">
<ReadingRules :ref="`readingRules${item.TrialReadingCriterionId}`"
:trial-reading-criterion-id="TrialReadingCriterionId" :CriterionType="item.CriterionType"
@setConfirm="setConfirm" @reloadArbitrationRules="reloadArbitrationRules"
@setArbitrationReading="setArbitrationReading" @setAdditionalAssessment="setAdditionalAssessment"
@setIsClinicalReading="setIsClinicalReading" @setGlobalReading="setGlobalReading"
@setOncologyReading="setOncologyReading" @setDigitPlaces="setDigitPlaces"
@setReadingTools="setReadingTools" />
:TrialReadingCriterionName="item.TrialReadingCriterionName" @setConfirm="setConfirm"
@reloadArbitrationRules="reloadArbitrationRules" @setArbitrationReading="setArbitrationReading"
@setAdditionalAssessment="setAdditionalAssessment" @setIsClinicalReading="setIsClinicalReading"
@setGlobalReading="setGlobalReading" @setOncologyReading="setOncologyReading"
@setDigitPlaces="setDigitPlaces" @setReadingTools="setReadingTools" />
</el-collapse-item>
<!-- 阅片标准 -->
<el-collapse-item :title="$t('trials:readingUnit:readingCriterion')" name="2">

View File

@ -1,14 +1,9 @@
<template>
<div v-loading="loading" class="manuals-wrapper">
<div class="left-wrapper">
<div v-if="fileList.length>0" class="basic-content">
<div
v-for="file in fileList"
:key="file.Id"
class="file-item"
:class="{activeItem:file.Id === selected.id}"
@click.prevent="preview(file)"
>
<div v-if="fileList.length > 0" class="basic-content">
<div v-for="file in fileList" :key="file.Id" class="file-item" :class="{ activeItem: file.Id === selected.id }"
@click.prevent="preview(file)">
<!-- {{ file.Name }} -->
<el-tooltip class="item" :content="file.Name" placement="bottom">
<span>{{ file.Name }} </span>
@ -21,19 +16,25 @@
</div>
<div class="right-wrapper">
<div class="right-content">
<iframe v-if="selected.filePath" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}&COMPANY=${COMPANY}`" width="100%" height="100%" frameborder="0" />
<iframe v-if="selected.filePath"
:src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}&COMPANY=${COMPANY}`"
width="100%" height="100%" frameborder="0" />
</div>
</div>
</div>
</template>
<script>
import { getManualList } from '@/api/trials'
import { getManualList, getTrialCriterionKeyFileList } from '@/api/trials'
export default {
name: 'Manuals',
props: {
trialId: {
type: String,
required: true
},
justKeyDoc: {
type: Boolean,
default: false
}
},
data() {
@ -45,26 +46,49 @@ export default {
fileList: [],
loading: false,
currentUser: zzSessionStorage.getItem('userName'),
COMPANY:process.env.VUE_APP_COMPANY_NAME
COMPANY: process.env.VUE_APP_COMPANY_NAME
}
},
mounted() {
this.getList()
},
methods: {
getList() {
async getList() {
this.loading = true
var param = {
trialId: this.trialId
}
getManualList(param).then(res => {
this.fileList = res.Result
this.fileList = []
try {
var param = {
TrialCriterionId: this.$router.currentRoute.query.TrialReadingCriterionId,
PageIndex: 1,
PageSize: 1000
}
const res = await getTrialCriterionKeyFileList(param)
if (res.IsSuccess) {
let list = res.Result.CurrentPageData.map(item => {
return {
Id: item.Id,
Name: item.FileName,
Path: item.FilePath
}
})
this.fileList = [...this.fileList, ...list]
}
if (!this.justKeyDoc) {
var param = {
trialId: this.trialId
}
const res = await getManualList(param)
if (res.IsSuccess) {
this.fileList = [...this.fileList, ...res.Result]
}
}
if (this.fileList.length > 0) {
this.preview(this.fileList[0])
}
this.loading = false
}).catch(() => { this.loading = false })
} catch (e) {
this.loading = false
}
},
preview(file) {
this.$set(this.selected, 'filePath', file.Path)
@ -73,38 +97,43 @@ export default {
}
}
</script>
<style lang="scss">
.manuals-wrapper{
<style lang="scss" scoped>
.manuals-wrapper {
display: flex;
flex-direction: row !important;
width: 100%;
height: 100%;
height: 95%;
padding: 5px;
overflow: hidden;
::-webkit-scrollbar {
width: 7px;
height: 7px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.left-wrapper{
.left-wrapper {
box-sizing: border-box;
margin-right: 10px;
height: 100%;
width: 300px;
border: 1px solid #ddd;
.basic-content{
.basic-content {
height: 100%;
overflow: auto;
}
.basic-content-empty{
.basic-content-empty {
padding: 5px;
font-size: 16px;
}
.file-item{
.file-item {
box-sizing: border-box;
border-bottom: 1px solid #f3f3f3;
height: 50px;
@ -117,18 +146,21 @@ export default {
text-overflow: ellipsis;
width: 100%;
}
.activeItem{
.activeItem {
color: #428bca !important;
border-bottom: 1px solid #f3f3f3 !important;
}
}
.right-wrapper{
.right-wrapper {
flex: 1;
height: 100%;
border: 1px solid #ddd;
}
.right-content{
height:100%;
.right-content {
height: 100%;
}
}