irc_web/src/views/trials/trials-panel/visit/qc-check/components/qualityAssurance.vue

2703 lines
88 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div v-loading="loading" class="qa-wrapper">
<div class="qa-content" style="position: relative">
<div
v-if="existsManual"
style="position: absolute; right: 20px; top: 10px; z-index: 9"
>
<el-button type="text" @click="openManuals">{{
$t('trials:reading:button:handbooks')
}}</el-button>
</div>
<!-- 所有检查信息 -->
<el-card shadow="hover" style="min-height: 600px">
<el-tabs v-model="activeName" type="card">
<!-- DICOM影像 -->
<el-tab-pane
v-if="data.DicomStudyCount > 0"
:label="$t('trials:audit:tab:dicoms')"
name="dicom"
>
<el-row>
<el-col :span="12">
<!-- 检查信息 -->
<h3>{{ $t('trials:audit:title:dicoms') }}</h3>
</el-col>
<el-col :span="12">
<div
v-show="isHaveFirstGiveMedicineDate"
style="text-align: right"
>
<!-- 访视窗口期 -->
<h4>
{{ $t('trials:audit:tip:visitWindow') }}:
<span v-if="!!baseDate" style="font-weight: normal">
{{ `${upperLimit}~${lowerLimit}` }}
</span>
<!-- 访视基准日期或窗口未设置 -->
<span v-else>{{
$t('trials:audit:tip:visitWindowNotSet')
}}</span>
</h4>
</div>
</el-col>
</el-row>
<div style="text-align: right; position: relative">
<!-- <div
class="uploadTip"
style="position: absolute; top: 0; left: 10px"
>
{{ $store.state.trials.uploadTip }}
</div> -->
<el-tooltip
v-if="
$store.state.trials.config.IsSupportQCDownloadImage &&
!hasPermi(['role:spm'])
"
:content="$t('trials:download:tip:message')"
placement="top-end"
effect="light"
>
<i class="el-icon-warning-outline" style="font-size: 18px"></i>
</el-tooltip>
<!-- 下载所有影像 -->
<el-button
v-if="
$store.state.trials.config.IsSupportQCDownloadImage &&
!hasPermi(['role:spm'])
"
:loading="downloading"
:disabled="selectTableDicom.length <= 0"
size="small"
type="primary"
style="margin-left: 10px"
@click="getCRCUploadedStudyInfo('dicom')"
>
{{ $t('trials:audit:button:downLoadAllDiocms') }}
</el-button>
<!-- 预览所有影像 -->
<el-button
size="small"
type="primary"
style="margin-left: 10px"
@click="handleViewImages"
>
{{ $t('trials:audit:button:previewAllDiocms') }}
</el-button>
<!-- 预览阅片影像 -->
<el-button
size="small"
type="primary"
style="margin-left: 10px"
@click="handleViewReadingImages"
>
{{ $t('trials:audit:button:previewReadingDiocms') }}
</el-button>
</div>
<el-table
:data="studyList"
:row-class-name="tableRowClassName"
@selection-change="handleSelectionChangeDicom"
:default-sort="{ prop: 'UploadedTime', order: 'ascending' }"
>
<el-table-column type="selection" width="55"> </el-table-column>
<!-- 检查编号 -->
<el-table-column
prop="StudyCode"
:label="$t('trials:audit:table:studyId')"
sortable
/>
<!-- 检查名称 -->
<el-table-column
prop="StudyName"
v-if="relationInfo.IsShowStudyName"
:label="$t('trials:audit:table:StudyName')"
sortable
>
<template slot-scope="scope">
<el-tooltip
class="item"
effect="dark"
:content="$t('trials:audit:message:noStudyName')"
placement="bottom"
v-if="!scope.row.StudyName"
>
<i
class="el-icon-warning"
style="color: #f44336; font-size: 16px"
/>
</el-tooltip>
<span v-else>{{ scope.row.StudyName }}</span>
</template>
</el-table-column>
<!-- 检查类型 -->
<el-table-column
prop="ModalityForEdit"
:label="$t('trials:audit:table:modality')"
sortable
/>
<!-- 检查类型 -->
<el-table-column
prop="Modalities"
:label="$t('trials:audit:table:modality1')"
sortable
/>
<!-- 检查部位 -->
<el-table-column
prop="BodyPartForEdit"
:label="$t('trials:audit:table:bodyPart')"
sortable
>
<template slot-scope="scope">
<el-tooltip
class="item"
effect="dark"
:content="$t('trials:audit:message:noBodyPartForEdit')"
placement="bottom"
v-if="!scope.row.BodyPartForEdit"
>
<i
class="el-icon-warning"
style="color: #f44336; font-size: 16px"
/>
</el-tooltip>
<span v-else>{{
getBodyPart(scope.row.BodyPartForEdit)
}}</span>
</template>
</el-table-column>
<!-- 序列数量 -->
<el-table-column
prop="SeriesCount"
:label="$t('trials:audit:table:seriesCount')"
sortable
/>
<!-- 图像数量 -->
<el-table-column
prop="InstanceCount"
:label="$t('trials:audit:table:instanceCount')"
sortable
/>
<!-- 检查日期 -->
<el-table-column
prop="StudyTime"
:label="$t('trials:audit:table:studyDate')"
sortable
>
<template slot-scope="scope">
<el-tooltip
class="item"
effect="dark"
:content="$t('trials:audit:message:qsOverWindow')"
placement="bottom"
>
<i
v-if="
isHaveFirstGiveMedicineDate &&
!!baseDate &&
scope.row.StudyTime &&
(moment(scope.row.StudyTime).format('YYYY-MM-DD') >
lowerLimit ||
moment(scope.row.StudyTime).format('YYYY-MM-DD') <
upperLimit)
"
class="el-icon-warning"
style="color: #f44336; font-size: 16px"
/>
</el-tooltip>
<span>{{
scope.row.StudyTime
? moment(scope.row.StudyTime).format('YYYY-MM-DD')
: ''
}}</span>
</template>
</el-table-column>
<!-- 更新时间 -->
<el-table-column
prop="UpdateTime"
:label="$t('trials:audit:table:seriesOfUpdateTime')"
min-width="120"
sortable
show-overflow-tooltip
/>
<!-- 上传时间 -->
<el-table-column
prop="UploadedTime"
:label="$t('trials:audit:table:seriesOfUploadedDate')"
min-width="120"
sortable
show-overflow-tooltip
/>
<el-table-column :label="$t('common:action:action')">
<template slot-scope="scope">
<!-- 预览 -->
<el-button
icon="el-icon-view"
:title="$t('trials:audit:action:preview')"
circle
:disabled="scope.row.IsDeleted"
@click="handlePreviewStudy(scope.row)"
/>
<!-- 编辑 -->
<el-button
icon="el-icon-edit-outline"
:title="$t('trials:audit:action:edit')"
circle
:disabled="isAudit || scope.row.IsDeleted"
@click="handleEditStudy(scope.row)"
/>
<!-- 预览PET-CT数据 -->
<el-button
type="primary"
icon="el-icon-document tip-i"
:title="$t('trials:audit:tab:clinicalData')"
v-if="
['PT、CT', 'CT、PT', 'PET-CT'].includes(
scope.row.Modalities
) && IsHaveStudyClinicalData
"
circle
:disabled="scope.row.IsDeleted"
@click="handlePreviewClinicalData(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<!-- 序列信息 -->
<h3>{{ $t('trials:audit:title:series') }}</h3>
<el-table
:data="seriesList"
:row-class-name="tableRowClassName"
:default-sort="{ prop: 'CreateTime', order: 'ascending' }"
>
<!-- 序列号 -->
<el-table-column
prop="SeriesNumber"
:label="$t('trials:audit:table:seriesId')"
sortable
/>
<!-- 检查编号 -->
<el-table-column
prop="StudyCode"
:label="$t('trials:audit:table:seriesOfStudyId')"
sortable
/>
<!-- 检查类型 -->
<el-table-column
prop="Modality"
:label="$t('trials:audit:table:seriesModality')"
sortable
show-overflow-tooltip
/>
<!-- 图像数量 -->
<el-table-column
prop="InstanceCount"
:label="$t('trials:audit:table:seriesOfInstanceCount')"
sortable
/>
<!-- 检查日期 -->
<el-table-column
prop="SeriesTime"
:label="$t('trials:audit:table:seriesOfStudyDate')"
sortable
show-overflow-tooltip
>
<template slot-scope="scope">
{{
scope.row.StudyTime
? moment(scope.row.StudyTime).format('YYYY-MM-DD')
: ''
}}
</template>
</el-table-column>
<!-- 更新时间 -->
<el-table-column
prop="UpdateTime"
:label="$t('trials:audit:table:seriesOfUpdateTime')"
sortable
show-overflow-tooltip
/>
<!-- 上传时间 -->
<el-table-column
prop="CreateTime"
:label="$t('trials:audit:table:seriesOfUploadedDate')"
sortable
show-overflow-tooltip
/>
<!-- 是否阅片 -->
<el-table-column
prop="IsReading"
:label="$t('trials:audit:table:isReading')"
sortable
>
<template slot-scope="scope">
<el-switch
v-model="scope.row.IsReading"
:disabled="scope.row.IsDeleted || isAudit"
@change="changeReadingStatus($event, scope.row)"
:active-text="$fd('YesOrNo', true)"
:inactive-text="$fd('YesOrNo', false)"
/>
</template>
</el-table-column>
<!-- 是否删除 复审时删除的序列不可再次编辑 -->
<el-table-column
prop="IsDeleted"
:label="$t('trials:audit:table:isDelete')"
sortable
>
<template slot-scope="scope">
<el-switch
v-model="scope.row.IsDeleted"
:disabled="isAudit"
@change="changeDeleteStatus($event, scope.row)"
:active-text="$fd('YesOrNo', true)"
:inactive-text="$fd('YesOrNo', false)"
/>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')">
<template slot-scope="scope">
<!-- 预览 -->
<el-button
icon="el-icon-view"
:title="$t('trials:audit:action:seriesPreview')"
circle
:disabled="scope.row.IsDeleted"
@click="handlePreviewInstance(scope.row)"
/>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 非DICOM影像 -->
<el-tab-pane
v-if="data.NoneDicomStudyCount > 0"
:label="$t('trials:audit:tab:nonDicoms')"
name="none-dicom"
>
<el-row>
<!-- 检查信息 -->
<el-col :span="12"
><h3>{{ $t('trials:audit:title:nonDicomsTitle') }}</h3></el-col
>
<el-col :span="12">
<div
v-show="isHaveFirstGiveMedicineDate"
style="text-align: right"
>
<!-- 访视窗口期 -->
<h4>
{{ $t('trials:audit:tip:nonDicomsVisitWindow') }}:
<span v-if="!!baseDate" style="font-weight: normal">
{{ `${upperLimit}~${lowerLimit}` }}
</span>
<!-- 访视基准日期或窗口未设置 -->
<span v-else>{{
$t('trials:audit:tip:visitWindowNotSet')
}}</span>
</h4>
</div>
</el-col>
</el-row>
<div style="text-align: right; position: relative">
<!-- <div
class="uploadTip"
style="position: absolute; top: 0; left: 10px"
>
{{ $store.state.trials.uploadTip }}
</div> -->
<el-tooltip
v-if="
$store.state.trials.config.IsSupportQCDownloadImage &&
!hasPermi(['role:spm'])
"
:content="$t('trials:download:tip:message')"
placement="top-end"
effect="light"
>
<i class="el-icon-warning-outline" style="font-size: 18px"></i>
</el-tooltip>
<!-- 下载所有影像 -->
<el-button
v-if="
$store.state.trials.config.IsSupportQCDownloadImage &&
!hasPermi(['role:spm'])
"
:loading="downloading"
:disabled="selectTableNonedicom.length <= 0"
size="small"
type="primary"
style="margin-left: 10px"
@click="getCRCUploadedStudyInfo('noneDicom')"
>
{{ $t('trials:audit:button:downLoadAllNonDiocms') }}
</el-button>
<!-- 预览 -->
<el-button
size="small"
:disabled="noneDicomStudyList.length === 0"
type="primary"
style="margin-left: 10px"
@click="handleViewAllNoneDicoms"
>
{{ $t('trials:audit:tip:nonDicomsPreviewAll') }}
</el-button>
</div>
<el-table
:data="noneDicomStudyList"
@selection-change="handleSelectionChangeNonedicom"
:default-sort="{ prop: 'CreateTime', order: 'ascending' }"
>
<el-table-column type="selection" width="55"> </el-table-column>
<!-- 检查编号 -->
<el-table-column
prop="CodeView"
:label="$t('trials:audit:table:nonDicomsStudyId')"
sortable
/>
<!-- 检查名称 -->
<el-table-column
prop="StudyName"
v-if="relationInfo.IsShowStudyName"
:label="$t('trials:audit:table:StudyName')"
sortable
/>
<!-- 检查类型 -->
<el-table-column
prop="Modality"
:label="$t('trials:audit:table:nonDicomsModality')"
sortable
/>
<!-- 检查部位 -->
<el-table-column
prop="BodyPart"
:label="$t('trials:audit:table:nonDicomsBodypart')"
sortable
>
<template slot-scope="scope">
{{ getBodyPart(scope.row.BodyPart) }}
</template>
</el-table-column>
<!-- 文件 -->
<el-table-column
prop="FileCount"
:label="$t('trials:audit:table:nonDicomsFileCount')"
sortable
>
<template slot-scope="scope">
<el-popover
v-if="scope.row.FileCount"
trigger="click"
placement="bottom"
>
<el-table
:data="scope.row.NoneDicomStudyFileList"
height="300"
size="small"
>
<!-- 文件名称 -->
<el-table-column
prop="FileName"
:label="$t('trials:audit:table:nonDicomsFileName')"
width="200"
/>
<el-table-column
prop="FileSize"
:label="$t('trials:audit:table:nonDicomsFileSize')"
width="100"
>
<template slot-scope="scope">
<span>{{
scope.row.FileSize && scope.row.FileSize > 0
? `${(scope.row.FileSize / 1024 / 1024).toFixed(
2
)}MB`
: 0
}}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('common:action:action')"
width="120"
>
<template slot-scope="files">
<!-- 预览 -->
<viewer
:ref="files.row.FullFilePath"
style="margin: 0 10px"
:images="[
`${OSSclientConfig.basePath}${files.row.FullFilePath}`,
]"
>
<el-button
type="text"
:disabled="
files.row.FileType &&
files.row.FileType.indexOf('zip') >= 0
"
@click.native.prevent="previewFile(files.row)"
>
{{ $t('trials:audit:button:nonDicomsPreview') }}
</el-button>
<img
v-show="false"
crossorigin="anonymous"
:src="`${OSSclientConfig.basePath}${files.row.FullFilePath}`"
alt="Image"
/>
</viewer>
</template>
</el-table-column>
</el-table>
<div slot="reference" class="name-wrapper">
<el-button type="text">
{{ scope.row.FileCount }}
</el-button>
</div>
</el-popover>
<span v-else>{{ scope.row.FileCount }}</span>
</template>
</el-table-column>
<!-- 检查日期 -->
<el-table-column
prop="ImageDate"
:label="$t('trials:audit:table:nonDicomsStudyDate')"
sortable
>
<template slot-scope="scope">
<i
v-if="
isHaveFirstGiveMedicineDate &&
!!baseDate &&
scope.row.ImageDate &&
(moment(scope.row.ImageDate).format('YYYY-MM-DD') >
lowerLimit ||
moment(scope.row.ImageDate).format('YYYY-MM-DD') <
upperLimit)
"
class="el-icon-warning"
style="color: #f44336; font-size: 16px"
/>
<span>{{
moment(scope.row.ImageDate).format('YYYY-MM-DD')
}}</span>
</template>
</el-table-column>
<!-- 更新时间 -->
<el-table-column
prop="UpdateTime"
:label="$t('trials:audit:table:nonDicomsUpdateTime')"
sortable
/>
<!-- 创建时间 -->
<el-table-column
prop="CreateTime"
:label="$t('trials:audit:table:nonDicomsCreateTime')"
sortable
/>
<el-table-column :label="$t('common:action:action')">
<template slot-scope="scope">
<!-- 预览 -->
<el-button
icon="el-icon-view"
:title="$t('trials:audit:button:nonDicomsPreview')"
circle
:disabled="scope.row.FileCount === 0"
@click.native.prevent="
handlePreviewNoneDicomFiles(scope.row)
"
/>
<!-- 编辑 -->
<el-button
icon="el-icon-edit-outline"
:title="$t('trials:audit:button:nonDicomsEdit')"
circle
:disabled="isAudit"
@click.native.prevent="handleEditNoneDicomInfo(scope.row)"
/>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 临床数据 -->
<!-- data.IsBaseLine && relationInfo.TrialClinicalInformationTransmissionEnum*1>0 && data.IsHaveClinicalData -->
<el-tab-pane
v-if="data.IsHaveClinicalData"
:label="$t('trials:audit:tab:clinicalData')"
name="clinical-data"
>
<ClinicalData
v-if="Object.keys(subjectClinicalData).length > 0"
:data="data"
style="margin-top: 5px"
:subject-clinical-data="subjectClinicalData"
:enum-type="
relationInfo.TrialClinicalInformationTransmissionEnum * 1
"
:subject-visit-id="data.Id"
:is-audit="isAudit"
:subject-id="data.SubjectId"
/>
</el-tab-pane>
</el-tabs>
</el-card>
<!-- 审核问题 -->
<!-- select/input/textarea/radio -->
<el-card shadow="hover" style="margin-top: 10px">
<!-- 审核问题 -->
<h3>{{ $t('trials:audit:title:questions') }}</h3>
<div v-if="qCQuestionAnswerList.length > 0" style="overflow-y: auto">
<QuestionForm
ref="questions"
:trial-id="trialId"
:subject-visit-id="data.Id"
:qc-process-enum="data.QCProcessEnum"
:current-q-c-enum="currentQCType"
:answers="qCQuestionAnswerList"
:is-audit="isAudit"
/>
</div>
<!-- 暂无数据 -->
<h3 v-else>{{ $t('trials:audit:message:noData') }}</h3>
</el-card>
<!-- 发起质疑 -->
<el-dialog
v-if="qcVisible"
:title="$t('trials:audit:dialogTitle:question')"
:visible.sync="qcVisible"
:close-on-click-modal="false"
append-to-body
custom-class="base-dialog-wrapper"
width="1000px"
>
<div
style="
padding: 10px;
border: 1px solid #e0e0e0;
max-height: 650px;
overflow-y: auto;
"
>
<el-form
v-if="qcVisible"
ref="qcForm"
:model="qcForm"
label-width="180px"
size="small"
>
<el-form-item :label="$t('trials:audit:form:questionContent')">
<!-- :header-row-style="{display:'none'}" -->
<el-table :data="qcForm.qcContent" style="width: 100%">
<el-table-column prop="isSelect" label="" width="40">
<template slot-scope="scope">
<el-checkbox v-model="scope.row.isSelect" />
</template>
</el-table-column>
<el-table-column prop="keyName" label="" width="180">
<template slot-scope="scope">
<span v-html="scope.row.keyName"></span>
</template>
</el-table-column>
<!-- 问题总结 -->
<el-table-column
prop="keyValue"
:label="$t('trials:audit:form:questionSummary')"
>
<template slot-scope="scope">
<el-input
v-show="scope.row.keyValue || scope.row.isSelect"
v-model="scope.row.keyValue"
type="textarea"
:rows="4"
size="mini"
/>
</template>
</el-table-column>
<!-- 行动事项 -->
<el-table-column
prop="ActionContent"
:label="$t('trials:audit:form:actionMatters')"
>
<template slot-scope="scope">
<el-input
v-show="scope.row.actionContent || scope.row.isSelect"
v-model="scope.row.actionContent"
type="textarea"
:rows="4"
size="mini"
/>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item :label="$t('trials:audit:form:questionDeadline')">
<el-date-picker
v-model="qcForm.deadlineTime"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetime"
size="small"
clearable
default-time="23:59:59"
style="width: 100%"
/>
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<!-- 取消 -->
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="qcVisible = false"
>
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button
size="small"
type="primary"
:loading="btnLoading"
@click="handleSaveQC"
>
{{ $t('common:button:save') }}
</el-button>
</div>
</el-dialog>
<!-- 历史质疑 -->
<el-dialog
v-if="historyVisible"
:visible.sync="historyVisible"
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
append-to-body
width="80%"
:title="$t('trials:audit:dialogTitle:historicalQuestions')"
>
<div style="text-align: right">
<el-button
icon="el-icon-refresh-left"
circle
:title="$t('trials:audit:button:refreshHistoryQs')"
@click="getHistoryInfo"
/>
</div>
<el-table
v-loading="historyLoading"
:data="historyList"
style="width: 100%"
height="500"
>
<!-- 质疑编号 -->
<el-table-column
prop="ChallengeCode"
:label="$t('trials:qcQuality:table:challengeCode')"
width="100"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-button
:style="{ color: scope.row.IsClosed ? '' : 'red' }"
type="text"
@click="handleReply(scope.row)"
>{{ scope.row.ChallengeCode }}</el-button
>
</template>
</el-table-column>
<!-- 质疑状态 -->
<el-table-column
prop="IsClosed"
:label="$t('trials:qcQuality:table:isClosed')"
width="80"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('IsClosed', scope.row.IsClosed) }}
</template>
</el-table-column>
<!-- 创建时间 -->
<el-table-column
prop="CreateTime"
:label="$t('trials:qcQuality:table:createTime')"
width="150"
show-overflow-tooltip
/>
<!-- 创建人 -->
<el-table-column
prop="CreateUserName"
:label="$t('trials:qcQuality:table:organizer')"
width="100"
show-overflow-tooltip
/>
<!-- 领取人 -->
<el-table-column
prop="CurrentActionUserName"
:label="$t('trials:qcCheck:table:crrentUser')"
width="100"
show-overflow-tooltip
/>
<!-- 最新消息时间 -->
<el-table-column
prop="LatestMsgTime"
:label="$t('trials:qcQuality:table:latestMsgTime')"
width="200"
show-overflow-tooltip
/>
<!-- 重传状态 -->
<el-table-column
prop="ReuploadEnum"
:label="$t('trials:qcQuality:table:reUploadStatus')"
show-overflow-tooltip
width="180"
sortable="custom"
>
<template slot-scope="scope">
<span v-if="scope.row.ReuploadEnum === 0">--</span>
<el-tag v-else-if="scope.row.ReuploadEnum === 1" type="danger">{{
$fd('ReuploadEnum', scope.row.ReuploadEnum)
}}</el-tag>
<el-tag v-else-if="scope.row.ReuploadEnum === 2" type="warning">{{
$fd('ReuploadEnum', scope.row.ReuploadEnum)
}}</el-tag>
<el-tag v-else-if="scope.row.ReuploadEnum === 3" type="primary">{{
$fd('ReuploadEnum', scope.row.ReuploadEnum)
}}</el-tag>
</template>
</el-table-column>
<!-- 重传完成时间 -->
<el-table-column
prop="ReUploadedTime"
:label="$t('trials:qcQuality:table:reUploadTime')"
show-overflow-tooltip
width="260"
sortable="custom"
/>
<!-- 处理截止日期 -->
<el-table-column
prop="DeadlineTime"
:label="$t('trials:qcQuality:table:deadline')"
width="160"
show-overflow-tooltip
/>
<!-- 是否超限 -->
<el-table-column
prop="IsOverTime"
:label="$t('trials:qcQuality:table:isOverTime')"
width="160"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsOverTime) }}
</template>
</el-table-column>
<!-- 关闭时间 -->
<el-table-column
prop="ClosedTime"
:label="$t('trials:qcQuality:table:closingTime')"
min-width="150"
show-overflow-tooltip
/>
<el-table-column
:label="$t('common:action:action')"
fixed="right"
min-width="160"
>
<template slot-scope="scope">
<!-- 回复 -->
<el-button
type="text"
:disabled="scope.row.IsClosed || isAudit"
@click="handleReply(scope.row)"
>
{{ $t('trials:qcQuality:action:reply') }}
</el-button>
<!-- 关闭 -->
<el-button
type="text"
:disabled="scope.row.IsClosed || isAudit"
@click="handleCloseQC(scope.row)"
>
{{ $t('trials:qcQuality:action:close') }}
</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
<!-- 质疑记录 -->
<el-dialog
v-if="chatVisible"
:visible.sync="chatVisible"
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
append-to-body
width="800px"
:title="
$t('trials:qcQuality:dialogTitle:reply') +
`(${currentQCRow.SubjectCode} ${currentQCRow.VisitName})`
"
>
<chat-form
:data="currentQCRow"
:sign-text="relationInfo.TrialImageQCSignText"
:clinical-enum="relationInfo.ClinicalInformationTransmissionEnum"
@close="chatVisible = false"
@getDialogList="getDialogList"
/>
</el-dialog>
<!-- 更新检查拍片部位信息 -->
<el-dialog
v-if="editStudyInfoVisible"
:title="$t('trials:audit:action:edit')"
:visible.sync="editStudyInfoVisible"
:close-on-click-modal="false"
append-to-body
custom-class="base-dialog-wrapper"
width="600px"
>
<div
style="
padding: 10px;
border: 1px solid #e0e0e0;
max-height: 650px;
overflow-y: auto;
"
>
<el-form ref="studyForm" :model="studyForm" label-width="100px">
<!-- 检查编号 -->
<el-form-item :label="$t('trials:audit:table:studyId')">
<el-input v-model="studyForm.StudyCode" disabled />
</el-form-item>
<!-- 检查名称 -->
<el-form-item
v-if="relationInfo.IsShowStudyName"
:label="$t('trials:audit:table:StudyName')"
prop="StudyName"
:rules="[
{
required: true,
message: $t('common:ruleMessage:specify'),
trigger: 'blur',
},
]"
>
<el-radio-group v-model="studyForm.StudyName">
<template v-for="m in relationInfo.StudyNameList">
<el-radio
v-if="m.IsChoose"
:key="m.Name"
:label="isEN ? m.EnName : m.Name"
style="line-height: 40px"
/>
</template>
</el-radio-group>
</el-form-item>
<!-- 检查类型 -->
<el-form-item
v-if="studyForm.IsDicomData"
:label="$t('trials:audit:table:modality')"
>
<el-input v-model="studyForm.Modalities" disabled />
</el-form-item>
<!-- 检查类型 -->
<el-form-item
v-else
:label="$t('trials:audit:table:modality')"
prop="Modalities"
:rules="[
{
required: true,
message: $t('common:ruleMessage:specify'),
trigger: 'blur',
},
]"
>
<el-radio-group v-model="studyForm.Modality">
<el-radio
v-for="m in trialModalitys"
v-show="m !== ''"
:key="m"
:label="m"
style="margin-bottom: 15px"
/>
</el-radio-group>
</el-form-item>
<!-- 检查部位 -->
<el-form-item
:label="$t('trials:audit:table:bodyPart')"
prop="BodyPartForEdit"
:rules="[
{
required: true,
message: $t('common:ruleMessage:specify'),
trigger: 'blur',
},
]"
>
<el-checkbox-group v-model="studyForm.BodyPartForEdit">
<el-checkbox
v-for="bodyPart in trialBodyPartTypes"
:key="bodyPart"
:label="bodyPart"
>{{
$fd('Bodypart', bodyPart, 'Code', BodyPart, 'Name')
}}</el-checkbox
>
</el-checkbox-group>
</el-form-item>
<!-- 序列数量 -->
<el-form-item :label="$t('trials:audit:table:seriesCount')">
<el-input v-model="studyForm.SeriesCount" disabled />
</el-form-item>
<!-- 图像数量 -->
<el-form-item
v-if="studyForm.InstanceCount"
:label="$t('trials:audit:table:instanceCount')"
>
<el-input v-model="studyForm.InstanceCount" disabled />
</el-form-item>
<!-- 检查日期 -->
<el-form-item :label="$t('trials:audit:table:studyDate')">
<el-date-picker
v-model="studyForm.StudyTime"
disabled
type="date"
value-format="yyyy-MM-dd"
format="yyyy-MM-dd"
style="width: 100%"
/>
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="editStudyInfoVisible = false"
>
{{ $t('common:button:cancel') }}
</el-button>
<el-button
:loading="btnLoading"
size="small"
type="primary"
@click="handleUpdateStudyInfo"
>
{{ $t('common:button:save') }}
</el-button>
</div>
</el-dialog>
<!-- 审核签名 -->
<el-dialog
v-if="signVisible"
:visible.sync="signVisible"
:close-on-click-modal="false"
width="600px"
append-to-body
custom-class="base-dialog-wrapper"
>
<div slot="title">
<span style="font-size: 18px">{{
$t('common:dialogTitle:sign')
}}</span>
<span style="font-size: 12px; margin-left: 5px">{{
`(${$t('common:label:sign')}${currentUser})`
}}</span>
</div>
<SignForm
ref="signForm"
:sign-code-enum="signCode"
:subject-visit-id="data.Id"
@closeDialog="closeSignDialog"
/>
</el-dialog>
<!-- 编辑非Dicom文件 -->
<el-dialog
v-if="editNoneDicomVisible"
:title="$t('trials:audit:button:nonDicomsEdit')"
:visible.sync="editNoneDicomVisible"
:close-on-click-modal="false"
append-to-body
custom-class="base-dialog-wrapper"
width="600px"
>
<div
style="
padding: 10px;
border: 1px solid #e0e0e0;
max-height: 650px;
overflow-y: auto;
"
>
<el-form
ref="noneDicomForm"
:model="noneDicomForm"
label-width="100px"
>
<!-- 检查编号 -->
<el-form-item :label="$t('trials:audit:table:nonDicomsStudyId')">
<el-input v-model="noneDicomForm.CodeView" disabled />
</el-form-item>
<!-- 检查名称 -->
<el-form-item
v-if="relationInfo.IsShowStudyName"
:label="$t('trials:audit:table:StudyName')"
prop="StudyName"
:rules="[
{
required: true,
message: $t('common:ruleMessage:specify'),
trigger: 'blur',
},
]"
>
<el-radio-group v-model="noneDicomForm.StudyName">
<template v-for="m in relationInfo.StudyNameList">
<el-radio
v-if="m.IsChoose"
:key="m.Name"
:label="isEN ? m.EnName : m.Name"
style="line-height: 40px"
/>
</template>
</el-radio-group>
</el-form-item>
<!-- 检查类型 -->
<el-form-item
:label="$t('trials:audit:table:nonDicomsModality')"
prop="Modality"
:rules="[
{
required: true,
message: $t('common:ruleMessage:specify'),
trigger: 'blur',
},
]"
>
<el-radio-group v-model="noneDicomForm.Modality">
<el-radio
v-for="m in trialModalitys"
v-show="m !== ''"
:key="m"
:label="m"
style="margin-bottom: 15px"
/>
</el-radio-group>
</el-form-item>
<!-- 检查部位 -->
<el-form-item
:label="$t('trials:audit:table:nonDicomsBodypart')"
prop="BodyParts"
:rules="[
{
required: true,
message: $t('common:ruleMessage:specify'),
trigger: 'blur',
},
]"
>
<el-checkbox-group v-model="noneDicomForm.BodyParts">
<el-checkbox
v-for="bodyPart in trialBodyPartTypes"
:key="bodyPart"
:label="bodyPart"
>{{
$fd('Bodypart', bodyPart, 'Code', BodyPart, 'Name')
}}</el-checkbox
>
</el-checkbox-group>
</el-form-item>
<!-- 检查日期 -->
<el-form-item
:label="$t('trials:audit:table:nonDicomsStudyDate')"
prop="ImageDate"
>
<el-date-picker
v-model="noneDicomForm.ImageDate"
disabled
type="date"
:picker-options="pickerOption"
value-format="yyyy-MM-dd"
format="yyyy-MM-dd"
style="width: 100%"
/>
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="editNoneDicomVisible = false"
>
{{ $t('common:button:cancel') }}
</el-button>
<el-button
:loading="btnLoading"
size="small"
type="primary"
@click="handleUpdateNoneDicomInfo"
>
{{ $t('common:button:save') }}
</el-button>
</div>
</el-dialog>
<!-- 关闭质疑 -->
<el-dialog
v-if="closeQuestionVisible"
:visible.sync="closeQuestionVisible"
:close-on-click-modal="false"
append-to-body
custom-class="base-dialog-wrapper"
width="600px"
:title="$t('trials:qcQuality:dialogTitle:closeQuestion')"
>
<div
style="
padding: 10px;
border: 1px solid #e0e0e0;
max-height: 650px;
overflow-y: auto;
"
>
<el-form
ref="closeQuestionForm"
:model="closeQuestionForm"
size="small"
label-width="120px"
>
<el-form-item
:label="$t('trials:qcQuality:label:closeReason')"
prop="Type"
:rules="[
{ required: true, message: $t('common:ruleMessage:select') },
]"
>
<el-radio-group v-model="closeQuestionForm.Type">
<!-- 问题已解决 -->
<el-radio :label="1">{{
$t('trials:qcQuality:radio:reason1')
}}</el-radio>
<!-- 问题无法解决强制关闭质疑 -->
<el-radio :label="2">{{
$t('trials:qcQuality:radio:reason2')
}}</el-radio>
<!-- &lt;!&ndash; 其他 &ndash;&gt;-->
<!-- <el-radio :label="3">{{ $t('trials:qcQuality:radio:reason3') }}</el-radio>-->
</el-radio-group>
</el-form-item>
<el-form-item
v-if="closeQuestionForm.Type === 2"
:label="$t('trials:consistencyCheck:label:closereason')"
prop="Remake"
:rules="[
{ required: true, message: $t('common:ruleMessage:specify') },
]"
>
<el-input
v-model="closeQuestionForm.Remake"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
/>
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<el-button
:disabled="closeBtnLoading"
size="small"
type="primary"
@click="closeQuestionVisible = false"
>
{{ $t('common:button:cancel') }}
</el-button>
<el-button
:loading="closeBtnLoading"
size="small"
type="primary"
@click="handleCloseQuestion"
>
{{ $t('common:button:save') }}
</el-button>
</div>
</el-dialog>
<!-- 预览单个图像 -->
<el-dialog
v-if="imgObj.visible"
:visible.sync="imgObj.visible"
:title="$t('trials:uploadNonDicoms:dialogTitle:preview')"
append-to-body
width="565px"
>
<div
v-loading="imgObj.loading"
class="base-modal-body"
style="border: 2px solid #ccc; padding: 10px"
>
<el-image
:src="`${OSSclientConfig.basePath}${imgObj.url}`"
crossorigin="anonymous"
fit="fit"
style="height: 500px; width: 500px"
@error="imgObj.loading = false"
@load="imgObj.loading = false"
/>
</div>
</el-dialog>
</div>
<div class="function-wrapper">
<!-- 保存 -->
<el-button
:disabled="isAudit"
size="small"
type="primary"
round
@click="handleSave"
>
{{ $t('trials:audit:button:save') }}
</el-button>
<!-- 发质疑 -->
<el-button
:disabled="isAudit"
size="small"
type="primary"
round
@click="handleQC"
>
{{ $t('trials:audit:button:question') }}
</el-button>
<!-- 历史质疑 -->
<el-button size="small" type="primary" round @click="handleHistoryQC">
{{
`${$t('trials:audit:button:historicalQuestions')}(${
relationInfo.TotalChallengeCount !== undefined
? relationInfo.TotalChallengeCount
: 0
}, ${
relationInfo.NotClosedChallengeCount !== undefined
? relationInfo.NotClosedChallengeCount
: 0
})`
}}
</el-button>
<!-- 审核通过 -->
<el-button
:disabled="isAudit"
size="small"
type="primary"
round
@click="handleQCState(8)"
>
{{ $t('trials:audit:button:auditPassed') }}
</el-button>
<!-- 审核终止 -->
<!-- <el-button :disabled="isAudit" size="small" type="primary" round @click="handleQCState(7)">-->
<!-- {{ $t('trials:audit:button:auditFailed') }}-->
<!-- </el-button>-->
</div>
<!--pet-ct临床数据预览-->
<el-dialog
v-if="petVisible"
:show-close="true"
:visible.sync="petVisible"
append-to-body
>
<uploadPetClinicalData
:subject-visit-id="data.Id"
:data="data"
:studyData="rowData"
:allow-add-or-edit="false"
/>
</el-dialog>
</div>
</template>
<script>
import {
collectNextIQCQuality,
getNextIQCQuality,
getVisitQCInfo,
getQCQuestionAnswerList,
getHistoryChallengeList,
getVisitQCStudyAndSeriesList,
getNoneDicomStudyList,
verifyCanQCPassedOrFailed,
verifyQCCanAddChallenge,
setSeriesStatus,
addOrUpdateNoneDicomStudy,
addOrUpdateQCQuestionAnswerList,
addOrUpdateQCChallenge,
closeQCChallenge,
updateModality,
getQCChallengeDialogList,
} from '@/api/trials'
import { qCPassedOrFailed } from '@/api/trials/visit'
import { getBasicDataSelects } from '@/api/dictionary/dictionary'
import ChatForm from './chatForm'
import QuestionForm from './questions'
import moment from 'moment'
import ClinicalData from './clinicalData'
import SignForm from '@/views/trials/components/newSignForm'
import { getToken } from '@/utils/auth'
import const_ from '@/const/sign-code'
import uploadPetClinicalData from '@/views/trials/trials-panel/visit/crc-upload/components/uploadPetClinicalData.vue'
import { downLoadFile } from '@/utils/stream.js'
import { getCRCUploadedStudyInfo, downloadImageSuccess } from '@/api/load.js'
import store from '@/store'
export default {
name: 'QualityAssurance',
components: {
ChatForm,
ClinicalData,
QuestionForm,
SignForm,
uploadPetClinicalData,
},
props: {
data: {
type: Object,
default() {
return {}
},
},
disabled: {
type: Boolean,
default: false,
},
qType: {
type: Number,
default: 1,
},
},
computed: {
isEN() {
return this.$i18n.locale !== 'zh'
},
},
data() {
return {
activeName: this.data.DicomStudyCount > 0 ? 'dicom' : 'none-dicom',
questionForm: {},
qCQuestionAnswerList: [],
studyList: [],
seriesList: [],
relationInfo: {},
noneDicomStudyList: [],
qcForm: {
subjectVisitId: '',
challengeType: '',
content: '',
actionContent: '',
deadlineTime: '',
qcContent: '',
},
qcVisible: false,
chatVisible: false,
loading: false,
btnLoading: false,
historyVisible: false,
historyList: [],
historyLoading: false,
editStudyInfoVisible: false,
studyForm: {
StudyCode: '',
IsDicomData: true,
Modalities: '',
BodyPartForEdit: [],
SeriesCount: null,
StudyTime: '',
},
currentQCRow: {},
isAudit: false, // 审核过之后功能按钮禁用标识
qcType: '',
trialBodyPartTypes: [],
trialModalitys: [],
signVisible: false,
signCode: '',
auditState: null,
currentNoneDicomId: '',
editNoneDicomVisible: false,
noneDicomForm: {
Id: '',
CodeView: '',
BodyParts: [],
Modality: '',
ImageDate: '',
StudyName: '',
},
subjectClinicalData: {},
moment,
currentQCType: null,
currentUser: zzSessionStorage.getItem('userName'),
pickerOption: {
disabledDate: (time) => {
return time.getTime() > Date.now()
},
},
isHaveFirstGiveMedicineDate: false,
baseDate: '',
upperLimit: '', // 超期上限
lowerLimit: '', // 超期下限
closeQuestionForm: {
Id: '',
SubjectVisitId: '',
Type: null,
Reason: '',
Remake: '',
},
closeBtnLoading: false,
closeQuestionVisible: false,
trialId: '',
dictionaryList: {},
currentSeriesIsReading: false,
currentSeriesIsDeleted: false,
imgObj: { url: '', visible: false, loading: false },
open: null,
existsManual: false,
// pet-ct临床数据
petVisible: false,
rowData: {},
IsHaveStudyClinicalData: false,
BodyPart: {},
// 下载相关
downloading: false,
selectTableDicom: [], // 选中的dicom数据
selectTableNonedicom: [], // 选中的非dicom数据
downloadId: null,
}
},
async mounted() {
this.BodyPart.Bodypart = await this.$getBodyPart(this.$route.query.trialId)
if (this.disabled) {
this.isAudit = true
this.currentQCType = this.qType
} else {
this.currentQCType = this.data.AuditState < 6 ? 1 : 2
}
window.addEventListener('message', this.receiveMsg)
this.trialId = this.$route.query.trialId
this.getQCInfo()
this.getDicData()
// this.handleViewImages()
// this.handleViewAllNoneDicoms()
},
beforeDestroy() {
if (this.open) {
this.open.close()
}
store.dispatch('trials/setUnLock', false)
},
methods: {
// 选中dicom
handleSelectionChangeDicom(val) {
this.selectTableDicom = val
},
// 选中非dicom
handleSelectionChangeNonedicom(val) {
this.selectTableNonedicom = val
},
// 获取下载文件信息
async getCRCUploadedStudyInfo(type) {
if (this.downloading) return
try {
let data = {
SubjectVisitId: this.data.Id,
NoneDicomStudyIdList: [],
DicomStudyIdList: [],
}
if (type === 'dicom') {
data.DicomStudyIdList = this.selectTableDicom.map(
(item) => item.StudyId
)
}
if (type === 'noneDicom') {
console.log(this.selectTableNonedicom)
data.NoneDicomStudyIdList = this.selectTableNonedicom.map(
(item) => item.Id
)
}
this.downloading = true
let res = await getCRCUploadedStudyInfo(data)
this.downloading = false
if (res.IsSuccess) {
this.downloadId = res.OtherInfo
this.downloadImage(res.Result, type)
}
} catch (err) {
this.downloading = false
console.log(err)
}
},
// 打包下载
async downloadImage(data, type) {
try {
let { files, name, fileType } = this.formatDownloadFile(data, type)
let res = null
if (fileType === 'list') {
res = await downLoadFile(files, name, 'zip')
} else {
res = await downLoadFile(files, name)
}
if (res && this.downloadId) {
this.downloadImageSuccess()
}
} catch (err) {
console.log(err)
}
},
// 格式化下载文件路径
formatDownloadFile(data, type) {
let files = [],
name = null
if (type === 'dicom') {
name = `${data.SubjectCode}_${data.VisitName}_${this.$fd(
'IsDicom',
true
)}.zip`
let StudyList = data.StudyList
StudyList.forEach((study) => {
if (study.SeriesList.length > 0) {
study.SeriesList.forEach((series) => {
if (series.InstanceList.length > 0) {
series.InstanceList.forEach((instance) => {
let fileName = instance.Path.split('/').pop()
let obj = {
name: `${data.SubjectCode}_${data.VisitName}_${this.$fd(
'IsDicom',
true
)}/${study.StudyCode}_${study.StudyTime.split(' ')[0]}_${
series.Modality
}/${fileName}`,
url: this.OSSclientConfig.basePath + instance.Path,
}
files.push(obj)
})
}
})
}
})
}
if (type === 'noneDicom') {
name = `${data.SubjectCode}_${data.VisitName}_${this.$fd(
'IsDicom',
false
)}.zip`
let NoneDicomStudyList = data.NoneDicomStudyList
// 单个zip包
if (
NoneDicomStudyList.length === 1 &&
NoneDicomStudyList[0].FileList.length === 1 &&
NoneDicomStudyList[0].FileList[0].FileType.includes('zip')
) {
files =
this.OSSclientConfig.basePath +
NoneDicomStudyList[0].FileList[0].Path
return { files, name, fileType: 'one' }
}
// 多文件
NoneDicomStudyList.forEach((study) => {
if (study.FileList.length > 0) {
study.FileList.forEach((item) => {
let obj = {
name: `${data.SubjectCode}_${data.VisitName}_${this.$fd(
'IsDicom',
true
)}/${study.StudyCode}_${study.ImageDate.split(' ')[0]}_${
study.Modality
}/${item.FileName}`,
url: this.OSSclientConfig.basePath + item.Path,
}
files.push(obj)
})
}
})
}
return { files, name, fileType: 'list' }
},
// 影像下载成功确认
async downloadImageSuccess() {
try {
let params = {
TrialImageDownloadId: this.downloadId,
}
await downloadImageSuccess(params)
} catch (err) {
console.log(err)
}
},
// 获取QC界面基本信息
getQCInfo() {
this.loading = true
getVisitQCInfo(this.data.Id, this.data.QCProcessEnum, this.currentQCType)
.then((res) => {
this.existsManual = res.Result.ExistsManual
this.qCQuestionAnswerList = res.Result.QCQuestionAnswerList
this.IsHaveStudyClinicalData = res.Result.IsHaveStudyClinicalData
this.studyList = res.Result.StudyList
this.seriesList = res.Result.SeriesList
this.noneDicomStudyList = res.Result.NoneDicomStudyList
this.relationInfo = res.Result.RelationInfo
this.trialBodyPartTypes = this.relationInfo.TrialBodyPartTypes
? this.relationInfo.TrialBodyPartTypes.trim().split('|')
: []
this.trialModalitys = this.relationInfo.TrialModalitys
? this.relationInfo.TrialModalitys.trim().split('|')
: []
this.subjectClinicalData = res.Result.SubjectClinicalData
this.baseDate = res.Result.RelationInfo.SubjectFirstGiveMedicineTime
if (this.baseDate) {
const uDay =
res.Result.RelationInfo.VisitDay * 1 +
res.Result.RelationInfo.VisitWindowLeft * 1
this.upperLimit = moment(this.baseDate, 'YYYY-MM-DD')
.add(uDay, 'days')
.format('YYYY-MM-DD')
const lDay =
res.Result.RelationInfo.VisitDay * 1 +
res.Result.RelationInfo.VisitWindowRight * 1
this.lowerLimit = moment(this.baseDate, 'YYYY-MM-DD')
.add(lDay, 'days')
.format('YYYY-MM-DD')
}
this.isHaveFirstGiveMedicineDate =
res.Result.RelationInfo.IsHaveFirstGiveMedicineDate
this.loading = false
})
.catch(() => {
this.loading = false
})
},
// 刷新审核问题列表
getCheckList() {
this.loading = true
getQCQuestionAnswerList(
this.trialId,
this.data.Id,
this.data.QCProcessEnum,
this.currentQCType
)
.then((res) => {
this.loading = false
res.Result.forEach((element, index) => {
// this.qCQuestionAnswerList
this.$set(this.qCQuestionAnswerList[index], 'Id', element.Id)
})
})
.catch(() => {
this.loading = false
})
},
// 保存审核问题
handleSave(isMessage) {
return new Promise((resolve) => {
this.$refs['questions']
.submit()
.then((res) => {
var answerList = []
res.forEach((item) => {
var index = this.qCQuestionAnswerList.findIndex(
(v) => v.TrialQCQuestionConfigureId === item.Id
)
if (index > -1) {
answerList.push({
id: this.qCQuestionAnswerList[index].Id,
answer: item.answer,
trialQCQuestionConfigureId: item.Id,
})
}
})
this.loading = true
addOrUpdateQCQuestionAnswerList(
this.trialId,
this.data.Id,
this.data.QCProcessEnum,
this.currentQCType,
answerList
)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
if (isMessage !== true) {
this.$message.success(
this.$t('common:message:savedSuccessfully')
)
}
this.getCheckList()
resolve(true)
}
})
.catch(() => {
this.loading = false
resolve(false)
})
})
.catch(() => {
this.loading = false
resolve(false)
})
})
},
// 保存质疑
handleSaveQC() {
this.$refs['qcForm'].validate((valid) => {
if (!valid) return
this.btnLoading = true
this.qcForm.subjectVisitId = this.data.Id
var contents = []
var types = []
var actions = []
this.qcForm.qcContent.forEach((item) => {
if (item.isSelect) {
types.push(`${item.keyName}`)
var str = `${item.keyName}<br>`
if (item.keyValue) {
// 问题总结
str = `${str}${this.$t(
'trials:audit:form:questionSummary'
)}<br>${item.keyValue}<br>`
}
if (item.actionContent) {
// 行动事项
actions.push(`${item.actionContent}`)
str = `${str}${this.$t(
'trials:audit:form:actionMatters'
)}<br><div style='margin-left:20px'>${item.actionContent}</div>`
}
contents.push(str)
}
})
if (contents.length === 0) {
// 质疑内容不能为空!
this.$alert(this.$t('trials:audit:message:qsContentIsNull'))
this.btnLoading = false
return
} else {
this.qcForm.content = contents.join('|')
this.qcForm.challengeType = types.join('|')
this.qcForm.actionContent = actions.join('|')
}
var params = {
challengeType: this.qcForm.challengeType,
subjectVisitId: this.qcForm.subjectVisitId,
content: this.qcForm.content,
actionContent: this.qcForm.actionContent,
reuploadEnum: 0,
deadlineTime: this.qcForm.deadlineTime,
}
addOrUpdateQCChallenge(
this.trialId,
this.data.QCProcessEnum,
this.currentQCType,
params
)
.then((res) => {
this.btnLoading = false
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.qcVisible = false
this.relationInfo.TotalChallengeCount++
this.relationInfo.NotClosedChallengeCount++
}
})
.catch(() => {
this.btnLoading = false
})
})
},
// 打开发起质疑弹窗
handleQC() {
this.loading = true
// 检验qc审核问题是否保存否则不允许发质疑
verifyQCCanAddChallenge(this.trialId, this.data.Id, this.currentQCType)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
this.renderQCForm()
}
})
.catch(() => {
this.loading = false
})
},
handleQCVerify() {
this.loading = true
return new Promise((resolve) => {
verifyQCCanAddChallenge(this.trialId, this.data.Id, this.currentQCType)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
resolve(true)
} else {
resolve(false)
}
})
.catch(() => {
this.loading = false
resolve(false)
})
})
},
// 渲染发质疑窗口
renderQCForm() {
var contents = []
const challengeType = this.dictionaryList['ChallengeType']
challengeType.forEach((i) => {
contents.push({ keyName: i.Value, keyValue: '', isSelect: false })
})
var isHyperwindow = false
if (this.isHaveFirstGiveMedicineDate && !!this.baseDate) {
isHyperwindow = this.isHyperwindow()
}
if (isHyperwindow) {
contents.push({
keyName: this.$t('trials:audit:message:qsOverWindow'),
keyValue: `${this.$t('trials:audit:message:qsOverWindow1')}${
this.upperLimit
}~${this.lowerLimit}${this.$t(
'trials:audit:message:qsOverWindow2'
)}${moment(this.relationInfo.EarliestScanDate).format(
'YYYY-MM-DD'
)}~${moment(this.relationInfo.LatestScanDate).format(
'YYYY-MM-DD'
)},${this.$t('trials:audit:message:qsOverWindow3')}`,
isSelect: false,
actionContent: this.$t('trials:audit:message:qsOverWindow4'),
})
}
this.qcForm.qcContent = contents
const day = this.relationInfo.TrialChangeDefalutDays
this.qcForm.deadlineTime = moment()
.add(day * 1, 'days')
.format('YYYY-MM-DD HH:mm:ss')
this.qcVisible = true
},
isHyperwindow() {
var isHyperwindow = false
if (
this.relationInfo.EarliestScanDate < this.upperLimit ||
this.relationInfo.LatestScanDate > this.lowerLimit
) {
isHyperwindow = true
}
return isHyperwindow
},
// 获取字典数据
getDicData() {
getBasicDataSelects(['ChallengeType']).then((res) => {
this.dictionaryList = { ...res.Result }
})
},
// 打开历史质疑弹窗
handleHistoryQC() {
this.historyVisible = true
this.getHistoryInfo()
},
// 获取历史质疑记录
getHistoryInfo() {
this.historyLoading = true
getHistoryChallengeList(
this.data.Id,
this.data.QCProcessEnum,
this.currentQCType
)
.then((res) => {
this.historyList = res.Result
this.historyLoading = false
})
.catch(() => {
this.historyLoading = false
})
},
// 回复质疑
handleReply(row) {
this.historyLoading = true
getQCChallengeDialogList(row.Id)
.then((res) => {
this.historyLoading = false
if (res.IsSuccess) {
if (res.Result.length > 0) {
Object.assign(row, res.Result[0])
}
this.currentQCRow = { ...row }
this.chatVisible = true
}
})
.catch(() => {
this.historyLoading = false
})
},
getDialogList() {
this.historyLoading = true
getQCChallengeDialogList(this.currentQCRow.Id)
.then((res) => {
this.historyLoading = false
if (res.IsSuccess && res.Result.length > 0) {
var i = this.historyList.findIndex(
(item) => item.Id === this.currentQCRow.Id
)
if (i > -1) {
this.currentQCRow = Object.assign(
this.historyList[i],
res.Result[0]
)
}
}
})
.catch(() => {
this.historyLoading = false
})
},
// 关闭质疑
handleCloseQuestion() {
this.$refs['closeQuestionForm'].validate((valid) => {
if (!valid) return
this.closeBtnLoading = true
if (this.closeQuestionForm.Type === 1) {
// 问题已解决
this.closeQuestionForm.Reason = this.$t(
'trials:qcQuality:message:problemSolved'
)
} else if (this.closeQuestionForm.Type === 2) {
// 问题无法解决强制关闭质疑,已提醒中心下次注意
this.closeQuestionForm.Reason = `${this.$t(
'trials:qcQuality:message:problemNotSolved'
)}<br/><br/>${this.$t('trials:consistencyCheck:label:closereason')}:${
this.closeQuestionForm.Remake
}`
}
var params = {
TrialId: this.data.TrialId,
QcChallengeId: this.closeQuestionForm.Id,
SubjectVisitId: this.data.Id,
CloseEnum: this.closeQuestionForm.Type,
CloseReason: this.closeQuestionForm.Reason,
}
closeQCChallenge(params)
.then((res) => {
this.closeBtnLoading = false
if (res.IsSuccess) {
this.getHistoryInfo()
this.$message.success(
this.$t('trials:qcQuality:message:closedSuccessfully')
)
this.closeQuestionVisible = false
this.relationInfo.NotClosedChallengeCount--
}
})
.catch(() => {
this.closeBtnLoading = false
})
})
},
// 打开关闭质疑框并初始化
handleCloseQC(row) {
this.closeQuestionForm = Object.assign(this.closeQuestionForm, row)
this.closeQuestionForm.CurrentQCType = this.currentQCType
this.closeQuestionForm.AuditState = this.data.AuditState
this.closeQuestionForm.TrialQCProcess = this.data.QCProcessEnum
this.closeQuestionForm.Type = null
this.closeQuestionForm.Reason = ''
this.closeQuestionForm.Remake = ''
this.closeQuestionVisible = true
},
// 打开质疑记录弹窗
handleQCInfo(row) {
this.currentQCRow = { ...row }
this.chatVisible = true
},
changeReadingStatus(callback, row) {
let statusStr = ''
if (callback) {
statusStr = this.$t('trials:audit:label:setSeriesReading')
row.IsReading = false
} else {
statusStr = this.$t('trials:audit:label:setSeriesNotReading')
row.IsReading = true
}
var message = this.$t('trials:audit:message:changeSeriesStatus').replace(
'xxx',
statusStr
)
message = message.replace('yyy', this.$fd('YesOrNo', !row.IsReading))
this.$confirm(message, {
distinguishCancelAndClose: true,
type: 'warning',
})
.then(() => {
const state = row.IsReading ? 1 : 2
this.loading = true
setSeriesStatus(
this.trialId,
this.data.Id,
row.StudyId,
row.Id,
state
)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
this.$message.success(
this.$t('common:message:savedSuccessfully')
)
this.getStudyInfo()
}
})
.catch(() => {
this.loading = false
})
})
.catch(() => {})
},
changeDeleteStatus(callback, row) {
let statusStr = ''
if (callback) {
statusStr = this.$t('trials:audit:label:setSeriesDeleted')
row.IsDeleted = false
} else {
statusStr = this.$t('trials:audit:label:setSeriesNotDelete')
row.IsDeleted = true
}
var message = this.$t('trials:audit:message:changeSeriesStatus').replace(
'xxx',
statusStr
)
message = message.replace('yyy', this.$fd('YesOrNo', !row.IsDeleted))
this.$confirm(message, {
distinguishCancelAndClose: true,
type: 'warning',
})
.then(() => {
const state = row.IsDeleted ? 5 : 4
this.loading = true
setSeriesStatus(
this.trialId,
this.data.Id,
row.StudyId,
row.Id,
state
)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
this.$message.success(
this.$t('common:message:savedSuccessfully')
)
this.getStudyInfo()
}
})
.catch(() => {
this.loading = false
})
})
.catch(() => {})
},
receiveMsg(event) {
if (event.data.type === 'refreshSeriesList') {
this.getStudyInfo()
}
},
// 刷新检查新
getStudyInfo() {
this.loading = true
getVisitQCStudyAndSeriesList(this.data.Id)
.then((res) => {
this.loading = false
this.studyList = res.Result.StudyList
this.seriesList = res.Result.SeriesList
})
.catch(() => {
this.loading = false
})
},
// 设置qc通过/不通过
async handleQCState(auditState) {
if (auditState === 7) {
this.signCode = this.getSignCode(auditState)
this.signVisible = true
this.auditState = auditState
return
}
if (auditState === 8) {
var isgo = true
var hasStudyName = true,
hasStudyNameList = []
var isgoList = []
this.studyList.forEach((v) => {
if (!v.BodyPartForEdit) {
isgo = false
isgoList.push(v.StudyCode)
}
if (this.relationInfo.IsShowStudyName && !v.StudyName) {
hasStudyName = false
hasStudyNameList.push(v.StudyCode)
}
})
if (!hasStudyName) {
// `请补充检查${isgoList.toString()}的检查名称!`
this.$confirm(
this.$t('trials:qcQuality:title:noStudyName').replace(
'xxx',
hasStudyNameList.join('、 ')
),
'',
{
showCancelButton: false,
}
)
return
}
if (!isgo) {
// `请补充检查${isgoList.toString()}的检查部位!`
this.$confirm(
this.$t('trials:qcQuality:title:title1').replace(
'xxx',
isgoList.join('、 ')
),
'',
{
showCancelButton: false,
}
)
return
}
}
this.loading = true
var isVerify = await this.handleSave(true)
if (!isVerify) {
return
}
// 验证是否关闭所有质疑
verifyCanQCPassedOrFailed(this.trialId, this.data.Id)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
this.signCode = this.getSignCode(auditState)
this.signVisible = true
this.auditState = auditState
}
})
.catch(() => {
this.loading = false
})
},
getSignCode(auditState) {
if (this.relationInfo.TrialQCProcessEnum === 1 && auditState === 8) {
// 单审通过
const { QCSingleReviewPassed } = const_.processSignature
return QCSingleReviewPassed
} else if (
this.relationInfo.TrialQCProcessEnum === 1 &&
auditState === 7
) {
// 单审不通过
const { QCSingleReviewFailed } = const_.processSignature
return QCSingleReviewFailed
} else if (
this.relationInfo.TrialQCProcessEnum === 2 &&
auditState === 8 &&
this.currentQCType === 1
) {
// 初审通过
const { PreliminaryReviewOfQCPassed } = const_.processSignature
return PreliminaryReviewOfQCPassed
} else if (
this.relationInfo.TrialQCProcessEnum === 2 &&
auditState === 7 &&
this.currentQCType === 1
) {
// 初审不通过
const { PreliminaryReviewOfQCFailed } = const_.processSignature
return PreliminaryReviewOfQCFailed
} else if (
this.relationInfo.TrialQCProcessEnum === 2 &&
auditState === 8 &&
this.currentQCType === 2
) {
// 复审通过
const { SecondaryReviewOfQCPassed } = const_.processSignature
return SecondaryReviewOfQCPassed
} else if (
this.relationInfo.TrialQCProcessEnum === 2 &&
auditState === 7 &&
this.currentQCType === 2
) {
// 复审不通过
const { SecondaryReviewOfQCFailed } = const_.processSignature
return SecondaryReviewOfQCFailed
}
},
// 关闭签名框
closeSignDialog(isSign, signInfo) {
if (isSign) {
this.setQCStatus(signInfo)
} else {
this.signVisible = false
}
},
// 设置qc审核状态
async setQCStatus(signInfo) {
this.loading = true
var params = {
data: {
TrialId: this.trialId,
SubjectVisitId: this.data.Id,
AuditState: this.auditState,
},
signInfo: signInfo,
}
qCPassedOrFailed(params)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
this.$refs['signForm'].btnLoading = false
this.signVisible = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
// this.getQCInfo()
this.isAudit = true
this.$forceUpdate()
getNextIQCQuality({
trialId: this.trialId,
SubjectId: this.data.SubjectId,
}).then((res) => {
if (res.Result && res.Result.VisitId) {
this.$confirm(
this.$t('trials:qcQuality:title:title2', '', {
showCancelButton: false,
})
)
.then(() => {
collectNextIQCQuality({
trialId: this.trialId,
SubjectId: this.data.SubjectId,
}).then((res) => {
this.$emit('getList')
this.$emit('nextTask', res.Result.VisitId)
})
})
.catch(() => {
this.$emit('getList')
})
} else {
// 没有后续质控任务
this.$emit('getList')
this.$confirm(this.$t('trials:qcQuality:title:closeQCDialog'))
.then(() => {
this.$emit('close')
})
.catch(() => {})
}
})
}
})
.catch(() => {
this.loading = false
this.$refs['signForm'].btnLoading = false
})
},
getNextQCInfo() {
// '是否确认进入下一个质控任务?'
var message = this.$t('trials:qcQuality:title:title2')
this.$confirm(message, {
type: 'warning',
distinguishCancelAndClose: true,
})
.then(() => {
this.loading = true
this.$emit('nextTask')
})
.catch((action) => {
this.loading = false
})
},
// 设置已删除序列行样式
tableRowClassName({ row, rowIndex }) {
if (row.IsDeleted) {
return 'delete-row'
} else {
return ''
}
},
// 打开检查信息编辑框
handleEditStudy(row) {
this.editStudyInfoVisible = true
this.studyForm = { ...row }
var bodyPart = []
if (this.studyForm.BodyPartForEdit.indexOf('|') !== -1) {
bodyPart = this.studyForm.BodyPartForEdit.split('|')
} else if (this.studyForm.BodyPartForEdit !== '') {
bodyPart.push(this.studyForm.BodyPartForEdit)
}
this.$set(this.studyForm, 'Modality', row.ModalityForEdit)
// this.studyForm.Modality = row.ModalityForEdit
this.studyForm.BodyPartForEdit = bodyPart
},
// 更新拍片部位/拍片类型信息
handleUpdateStudyInfo() {
this.$refs['studyForm'].validate((valid) => {
if (!valid) return
this.btnLoading = true
this.studyForm.BodyPart = this.studyForm.BodyPartForEdit.join('|')
// this.studyForm.Modality = this.studyForm.Modalities
var params = {
id: this.studyForm.StudyId,
subjectVisitId: this.data.Id,
type: 1,
modality: this.studyForm.Modality,
bodyPart: this.studyForm.BodyPart,
StudyName: this.studyForm.StudyName,
}
updateModality(this.data.TrialId, params)
.then((res) => {
this.btnLoading = false
if (res.IsSuccess) {
this.getStudyInfo()
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.editStudyInfoVisible = false
}
})
.catch(() => {
this.btnLoading = false
})
})
},
// 设置重传时刷新历史质疑列表及影响指控列表
refreshList() {
// 如果是复审时候设置重传,需要将当前审核状态设置为初审,且刷新页面数据并关闭历史质疑和质疑记录窗口
if (this.currentQCType === 2) {
this.currentQCType = 1
// this.getQCInfo()
this.getHistoryInfo()
} else {
// 如果是初审时候设置重传,不影响
this.getHistoryInfo()
}
this.$emit('getList')
},
// 打开非Dicom信息编辑框
handleEditNoneDicomInfo(row) {
const { CodeView, Id, BodyPart, Modality, ImageDate, StudyName } = {
...row,
}
this.noneDicomForm.StudyName = StudyName
this.noneDicomForm.CodeView = CodeView
this.noneDicomForm.Id = Id
this.noneDicomForm.BodyPart = BodyPart
this.noneDicomForm.Modality = Modality
this.noneDicomForm.ImageDate = ImageDate
this.noneDicomForm.BodyParts = BodyPart.split(', ')
this.editNoneDicomVisible = true
},
// 更新非Dicom部位/拍片类型信息
handleUpdateNoneDicomInfo() {
this.$refs.noneDicomForm.validate((valid) => {
if (!valid) return
this.btnLoading = true
this.noneDicomForm.BodyPart = this.noneDicomForm.BodyParts.join(', ')
this.noneDicomForm.TrialId = this.trialId
this.noneDicomForm.TrialSiteId = this.data.TrialSiteId
this.noneDicomForm.SubjectId = this.data.SubjectId
this.noneDicomForm.SubjectVisitId = this.data.Id
addOrUpdateNoneDicomStudy(this.noneDicomForm)
.then((res) => {
this.btnLoading = false
if (res.IsSuccess) {
this.getNoneDicomList()
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.editNoneDicomVisible = false
}
})
.catch(() => {
this.btnLoading = false
})
})
},
// 获取非Dicom检查信息
getNoneDicomList() {
this.loading = true
getNoneDicomStudyList(this.data.Id)
.then((res) => {
this.noneDicomStudyList = res.Result
this.loading = false
})
.catch(() => {
this.loading = false
})
},
// 预览文件
previewFile(row) {
if (!!~row.FileType.indexOf('pdf')) {
return this.$preview({
path: row.Path || row.fullPath,
type: 'pdf',
title: row.FileName,
})
}
// window.open(row.FullFilePath, '_blank')
// this.imgObj.url = row.FullFilePath
// this.imgObj.loading = true
// this.imgObj.visible = true
this.$refs[row.FullFilePath].$viewer.show()
},
// 预览所有影像
handleViewImages() {
if (this.open) {
this.open.close()
}
var token = getToken()
const routeData = this.$router.resolve({
path: `/showvisitdicoms?trialId=${this.data.TrialId}&visitInfo=${
this.data.VisitName
}(${this.data.VisitNum})&subjectVisitId=${this.data.Id}&showDelete=${
this.isAudit ? 0 : 1
}&TokenKey=${token}`,
})
this.open = window.open(routeData.href, '_blank')
},
// 预览阅片影像
handleViewReadingImages() {
if (this.open) {
this.open.close()
}
var token = getToken()
const routeData = this.$router.resolve({
path: `/showvisitdicoms?trialId=${this.data.TrialId}&visitInfo=${this.data.VisitName}(${this.data.VisitNum})&subjectVisitId=${this.data.Id}&isReading=1&TokenKey=${token}`,
})
this.open = window.open(routeData.href, '_blank')
},
// 预览临床数据
handlePreviewClinicalData(row) {
this.rowData = row
this.petVisible = true
},
// 预览某个检查
handlePreviewStudy(row) {
if (this.open) {
this.open.close()
}
var token = getToken()
const routeData = this.$router.resolve({
path: `/showdicom?trialId=${this.trialId}&subjectVisitId=${
this.data.Id
}&studyId=${row.StudyId}&showDelete=${
this.isAudit ? 0 : 1
}&TokenKey=${token}&type=Study`,
})
this.open = window.open(routeData.href, '_blank')
},
// 预览某个序列
handlePreviewInstance(row) {
try {
if (this.open) {
this.open.close()
}
var token = getToken()
const routeData = this.$router.resolve({
path: `/showdicom?trialId=${this.trialId}&studyId=${
row.StudyId
}&studyCode=${row.StudyCode}&modality=${row.Modality}&seriesId=${
row.Id
}&seriesNumber=${row.SeriesNumber}&showDelete=${
this.isAudit ? 0 : 1
}&subjectVisitId=${
this.data.Id
}&type=Series&TokenKey=${token}&description=${row.Description}`,
})
this.open = window.open(routeData.href, '_blank')
} catch (e) {
console.log(e)
}
},
// 预览所有检查下非Dicom文件
handleViewAllNoneDicoms() {
if (this.open) {
this.open.close()
}
// this.previewAllNoneDicomVisible = true
let trialId = this.$route.query.trialId
var token = getToken()
const routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${this.data.Id}&TokenKey=${token}`,
})
this.open = window.open(routeData.href, '_blank')
},
// 预览单个检查下非Dicom文件
handlePreviewNoneDicomFiles(row) {
if (this.open) {
this.open.close()
}
let trialId = this.$route.query.trialId
var token = getToken()
const routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${this.data.Id}&studyId=${row.Id}&TokenKey=${token}`,
})
this.open = window.open(routeData.href, '_blank')
},
getBodyPart(bodyPart) {
if (!bodyPart) return ''
var separator = ','
if (bodyPart.indexOf('|') > -1) {
separator = '|'
} else if (bodyPart.indexOf(',') > -1) {
separator = ','
} else if (bodyPart.indexOf('') > -1) {
separator = ''
}
var arr = bodyPart.split(separator)
var newArr = arr.map((i) => {
return this.$fd('Bodypart', i.trim(), 'Code', this.BodyPart, 'Name')
})
return newArr.join(' | ')
},
openManuals() {
this.$emit('openManuals')
},
},
}
</script>
<style lang="scss" scoped>
.qa-wrapper {
display: flex;
flex-flow: column;
height: 100%;
border: 1px solid #fff;
padding: 10px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
.function-wrapper {
text-align: right;
}
.qa-content {
flex: 1;
margin-top: 10px;
overflow-y: auto;
}
::v-deep .delete-row {
text-decoration-line: line-through;
color: #c0c4cc;
}
::v-deep .el-card {
padding: 10px;
}
}
::v-deep .tip-i {
&::before {
color: #fff !important;
}
}
</style>