Merge branch 'uat' into uat_us

# Conflicts:
#	src/views/trials/trials-layout/components/trialsNavbar.vue
#	src/views/trials/trials-panel/reading/read-task/index.vue
uat_us
wangxiaoshuang 2026-08-21 11:38:52 +08:00
commit b977f1ab1d
176 changed files with 13142 additions and 4104 deletions

10
package-lock.json generated
View File

@ -57,6 +57,7 @@
"sortablejs": "^1.15.5",
"streamsaver": "^2.0.6",
"svgo": "^1.2.2",
"unzipit": "^2.0.3",
"v-viewer": "^1.7.4",
"vcrontab": "^0.3.5",
"vue": "2.7.16",
@ -21835,6 +21836,15 @@
"node": ">=0.10.0"
}
},
"node_modules/unzipit": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/unzipit/-/unzipit-2.0.3.tgz",
"integrity": "sha512-e6+ftgSQRj9Hwf2mIWFwYIGqBoKV7AziG/d7pep2Uv3NCzHbhLO5d9dGRwpahVlnpQ7aQC+5kWk72dnWocdYyA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/unzipper": {
"version": "0.10.14",
"resolved": "https://registry.npmmirror.com/unzipper/-/unzipper-0.10.14.tgz",

View File

@ -64,6 +64,7 @@
"sortablejs": "^1.15.5",
"streamsaver": "^2.0.6",
"svgo": "^1.2.2",
"unzipit": "^2.0.3",
"v-viewer": "^1.7.4",
"vcrontab": "^0.3.5",
"vue": "2.7.16",

View File

@ -98,6 +98,7 @@ import timeTag from '@/components/timeTag'
import Vue from 'vue'
import i18n from './lang'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import WHITELIST from "./utils/whiteList"
export default {
name: 'App',
components: {
@ -120,10 +121,10 @@ export default {
mounted() {
this.show = process.env.VUE_APP_OSS_PATH === '/test/dist'
Vue.prototype.$openI18n = this.openI18n
// this.getIsLock()
// DicomEvent.$on("isLock", (isLock) => {
// this.isLock = isLock
// })
this.getIsLock()
DicomEvent.$on("isLock", (isLock) => {
this.isLock = isLock
})
},
// watch: {
// '$route.query': {
@ -156,7 +157,7 @@ export default {
// },
methods: {
getIsLock() {
if (zzSessionStorage.getItem('isLock') === 'true') {
if (zzSessionStorage.getItem('isLock') === 'true' && !WHITELIST.includes(this.$route.path)) {
this.isLock = true
} else {
this.isLock = false
@ -322,7 +323,7 @@ export default {
<style lang="scss">
.mask {
position: relative;
filter: blur(2px);
filter: blur(5px);
user-select: none;
}
@ -334,7 +335,7 @@ export default {
height: 100%;
content: '';
display: block;
background: rgba(255, 253, 253, 0.2);
background: rgba(255, 253, 253, 0.5);
}
$light_gray: #606266;

View File

@ -316,3 +316,30 @@ export function GetReadingMedicineSystemQuestionList_Export(data) {
data
})
}
// 导出中心调研
export function getTrialSiteSurveyList_Export(data) {
return requestDownload({
url: `/ExcelExport/getTrialSiteSurveyList_Export`,
responseType: 'blob',
method: 'post',
data
})
}
// 导出中心设备
export function getSiteSurveyEquipmentList_Export(data) {
return requestDownload({
url: `/ExcelExport/getSiteSurveyEquipmentList_Export`,
responseType: 'blob',
method: 'post',
data
})
}
// 导出中心调研基本信息
export function getSiteSurveyInfoList_Export(data) {
return requestDownload({
url: `/ExcelExport/getSiteSurveyInfoList_Export`,
responseType: 'blob',
method: 'post',
data
})
}

View File

@ -432,6 +432,22 @@ export function studyUndoMaskImage(data) {
data
})
}
// 非dicom图像数据匿名
export function noneDicomStudyMaskImage(data) {
return request({
url: `/NoneDicomStudy/noneDicomStudyMaskImage`,
method: 'post',
data
})
}
// 非dicom图像数据撤销匿名
export function noneDicomStudyUndoMaskImage(data) {
return request({
url: `/NoneDicomStudy/noneDicomStudyUndoMaskImage`,
method: 'post',
data
})
}
// 获取分割组历史版本
export function getSegmentationVersionList(data) {
return request({
@ -447,4 +463,28 @@ export function restoreSegmentationVersion(data) {
method: 'post',
data
})
}
}
// 获取标记信息
export function getMarkList(data) {
return request({
url: `/ReadingImageTask/getMarkList`,
method: 'post',
data
})
}
export function getNoneDicomMarkList(data) {
return request({
url: `/ReadingImageTask/getNoneDicomMarkList`,
method: 'post',
data
})
}
export function getFilterTableQuestion(data) {
return request({
url: `/ReadingImageTask/getFilterTableQuestion`,
method: 'post',
data
})
}

View File

@ -129,3 +129,31 @@ export function getUserTypeList(userTypeEnum) {
method: 'get'
})
}
export function getSiteSurveyLogList(param) {
return request({
url: `/TrialSiteSurvey/getSiteSurveyLogList`,
method: 'post',
data: param
})
}
export function getTrialIsSPMJoin(trialId) {
return request({
url: `/TrialSiteSurvey/getTrialIsSPMJoin?trialId=${trialId}`,
method: 'get'
})
}
export function getSiteSurveyEquipmentList(param) {
return request({
url: `/TrialSiteSurvey/getSiteSurveyEquipmentList`,
method: 'post',
data: param
})
}
export function getSiteSurveyInfoList(param) {
return request({
url: `/TrialSiteSurvey/getSiteSurveyInfoList`,
method: 'post',
data: param
})
}

View File

@ -357,10 +357,11 @@ export function getSubjectList(param) {
})
}
export function getTrialSiteSelect(trialId) {
export function getTrialSiteSelect(trialId, params) {
return request({
url: `/trialMaintenance/getTrialSiteSelect/${trialId}`,
method: 'get'
method: 'get',
params
})
}
@ -4474,4 +4475,51 @@ export function getTrialUnreadVisitList(params) {
method: 'get',
params,
})
}
export function getDoctorUserTrialReadingStat(data) {
return request({
url: `/DoctorWorkload/getDoctorUserTrialReadingStat`,
method: 'post',
data,
})
}
// 设置项目链接过期时间
export function setTrialLinkExpirationTime(data) {
return request({
url: `/TrialConfig/setTrialLinkExpirationTime`,
method: 'post',
data,
})
}
// 获取项目链接过期时间
export function getTrialLinkExpirationTime(data) {
return request({
url: `/TrialConfig/getTrialLinkExpirationTime`,
method: 'post',
data,
})
}
// 获取项目链接时间是否过期
export function getLinkLinkExpirationTime(data) {
return request({
url: `/TrialConfig/getLinkLinkExpirationTime`,
method: 'post',
data,
})
}
// 获取项目链接code是否有效
export function getLinkVerificationCodeIsEffective(data) {
return request({
url: `/TrialConfig/getLinkVerificationCodeIsEffective`,
method: 'post',
data,
})
}
// 上传通用文件记录
export function addOrUpdateCommonUploadRecord(data) {
return request({
url: `/FileUploadRecord/addOrUpdateCommonUploadRecord`,
method: 'post',
data,
})
}

View File

@ -488,3 +488,67 @@ export function setRandomTaskOrder(data) {
data
})
}
export function getJudgeForm(data) {
return request({
url: `/ReadingImageTask/getJudgeForm`,
method: 'post',
data
})
}
export function setJudgeForm(data) {
return request({
url: `/ReadingImageTask/setJudgeForm`,
method: 'post',
data
})
}
export function getSingleVisitJudgeAnswer(data) {
return request({
url: `/ReadingImageTask/getSingleVisitJudgeAnswer`,
method: 'post',
data
})
}
export function submitJudgeForm(data) {
return request({
url: `/Inspection/ReadingImageTask/SubmitJudgeForm`,
method: 'post',
data
})
}
export function getTrialQuestionVisitJudgeRule(data) {
return request({
url: `/ReadingQuestion/getTrialQuestionVisitJudgeRule`,
method: 'post',
data
})
}
export function setTrialQuestionVisitJudgeRule(data) {
return request({
url: `/ReadingQuestion/setTrialQuestionVisitJudgeRule`,
method: 'post',
data
})
}
export function copyTrialQuestionVisitJudgeRule(data) {
return request({
url: `/ReadingQuestion/copyTrialQuestionVisitJudgeRule`,
method: 'post',
data
})
}
export function applyBaseLineTrialQuestionVisitJudgeRule(data) {
return request({
url: `/ReadingQuestion/applyBaseLineTrialQuestionVisitJudgeRule`,
method: 'post',
data
})
}

View File

@ -94,6 +94,14 @@ export function getTrialJudgyInfo(params) {
data: params
})
}
export function setTrialJudgeRuleInfo(params) {
return request({
url: `/ReadingQuestion/setTrialJudgeRuleInfo`,
method: 'post',
data: params
})
}
// 获取检查部位列表
export function getTrialBodyPartList(params) {
return request({

View File

@ -34,8 +34,8 @@
</div>
<div class="info-subject">
<div v-if="series.subjectCode">{{ series.subjectCode }}</div>
<div v-if="series.visitName">{{ series.visitName }}</div>
<div v-if="series.subjectCode && IsReadingTaskViewInOrder !== 0">{{ series.subjectCode }}</div>
<div v-if="series.visitName && IsReadingTaskViewInOrder !== 0">{{ series.visitName }}</div>
<div>{{ stack.description }}</div>
<!-- <div>{{ dicomInfo.hospital }}</div> -->
<!-- <div v-show="dicomInfo.pid">{{ dicomInfo.pid }}</div> -->
@ -174,7 +174,8 @@ export default {
orientationMarkers: [],
originalMarkers: [],
dcmTag: { visible: false, title: this.$t('trials:dicom-tag:title') },
tip: ''
tip: '',
IsReadingTaskViewInOrder: 2
}
},
@ -182,6 +183,9 @@ export default {
this.type = this.$router.currentRoute.query.type
? this.$router.currentRoute.query.type
: ''
if (this.$router.currentRoute.query.IsReadingTaskViewInOrder) {
this.IsReadingTaskViewInOrder = Number(this.$router.currentRoute.query.IsReadingTaskViewInOrder)
}
this.canvas = this.$refs.canvas
this.canvas.addEventListener('cornerstonenewimage', this.onNewImage)
this.canvas.addEventListener(

View File

@ -56,7 +56,8 @@ export default {
TrialConfig: {},
TrialStatusStr: null,
trialId: null,
IsAdditionalAssessment: false
IsAdditionalAssessment: false,
isReload: false
}
},
computed: {
@ -71,7 +72,11 @@ export default {
if (~url.indexOf('?')) {
query = url.split('?')[1]
}
zzSessionStorage.setItem('lastWorkbench', `${v.path}${query ? '?' : ''}${query}`)
if (!this.isReload) {
zzSessionStorage.setItem('lastWorkbench', `${v.path}${query ? '?' : ''}${query}`)
} else {
this.isReload = false
}
var firstGoIn = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children[0]
if (this.trialsTab === '/trials/trials-panel') {
var targetPath = firstGoIn.path
@ -96,6 +101,9 @@ export default {
}
},
mounted() {
this.$EventBus.$on("reload", () => {
this.isReload = true
})
var query = ``
var url = window.location.href
if (~url.indexOf('?')) {

View File

@ -33,7 +33,7 @@
<el-table-column type="index" width="40" />
<!--受试者-->
<el-table-column :label="$t('download:table:subjectCode')" min-width="130" prop="SubjectCode"
show-overflow-tooltip />
show-overflow-tooltip v-if="isReadingTaskViewInOrder !== 0" />
<!--访视名称-->
<el-table-column prop="VisitName" :label="$t('download:table:VisitName')" sortable v-if="IsImageSegment" />
<!--任务名称-->
@ -78,7 +78,7 @@
</el-table>
<study-view v-if="model_cfg.visible" :model_cfg="model_cfg" :modelList="modelList" :bodyPart="bodyPart"
:subjectVisitId="modelSubjectVisitId" :IsDicom="IsDicom" :isDownload="true" :visitTaskId="modelTaskId"
:IsImageSegment="IsImageSegment" :Criterion="Criterion" />
:IsImageSegment="IsImageSegment" :Criterion="Criterion" :IsReadingTaskViewInOrder="isReadingTaskViewInOrder" />
</el-dialog>
</template>
<script>
@ -136,6 +136,14 @@ export default {
IsImageSegment: {
type: Boolean,
default: false,
},
isReadingTaskViewInOrder: {
type: Number,
default: 2,
},
isReading: {
type: Boolean,
default: false,
}
},
data() {
@ -170,6 +178,7 @@ export default {
this.getList()
this.title = `Download Images${this.SubjectCode}${this.Criterion.TrialReadingCriterionName}`
if (this.IsImageSegment) this.title = `Download Images${this.SubjectCode}`
if (this.isReadingTaskViewInOrder === 0) this.title = ''
},
beforeDestroy() {
store.dispatch('trials/setUnLock', false)
@ -327,6 +336,14 @@ export default {
if (data.StudyList && data.StudyList.length > 0) {
let StudyList = data.StudyList
StudyList.forEach((study) => {
let arr = []
study.SeriesList.forEach(item => {
if (!arr.includes(item.Modality)) {
arr.push(item.Modality)
}
})
let str = arr.join('_')
let time = study.StudyTime.split(' ')[0]
if (study.StudyDIRPath) {
let obj = {
name: `${data.SubjectCode}/${data.TaskBlindName
@ -338,7 +355,7 @@ export default {
obj = {
name: `${data.SubjectCode}/${data.VisitName
}/${this.$fd('IsDicom', true)}/${study.StudyCode
}/DICOMDIR`,
}_${time}_${str}/DICOMDIR`,
url: this.OSSclientConfig.basePath + study.StudyDIRPath,
}
} else if (this.IsReadingTaskViewInOrder === 0) {
@ -371,7 +388,7 @@ export default {
obj = {
name: `${data.SubjectCode}/${data.VisitName
}/${this.$fd('IsDicom', true)}/${study.StudyCode
}/IMAGE/${fileName}`,
}_${time}_${str}/IMAGE/${fileName}`,
url: this.OSSclientConfig.basePath + instance.Path,
IsEncapsulated: instance.IsEncapsulated
}
@ -437,6 +454,9 @@ export default {
let params = {
TrialImageDownloadId: this.downloadId,
}
if (this.isReading) {
params.VisitTaskId = this.TaskId
}
await downloadImageSuccess(params)
} catch (err) {
console.log(err)
@ -447,6 +467,9 @@ export default {
if (this.IsImageSegment) {
this.model_cfg.title = `${item.SubjectCode || ''} > ${item.VisitName}`
}
if (this.isReadingTaskViewInOrder === 0) {
this.model_cfg.title = ''
}
if (item.IsDicom) {
this.modelList = item.DicomStudyList
} else {
@ -488,11 +511,11 @@ export default {
let routeData = null
if (!this.IsImageSegment && (this.Criterion.CriterionType == 19 || this.Criterion.CriterionType == 20)) {
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&isImageSegmentLabel=${false}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}&isReading=true`,
path: `/showNoneDicoms?trialId=${trialId}&isImageSegmentLabel=${false}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}&isReading=true&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
} else {
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}&isReading=true`,
path: `/showNoneDicoms?trialId=${trialId}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}&isReading=true&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
}
this.open = window.open(routeData.href, '_blank')
@ -505,7 +528,7 @@ export default {
var token = getToken()
let trialId = this.$route.query.trialId
const routeData = this.$router.resolve({
path: `/showvisitdicoms?page=download&trialId=${trialId}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&isReading=1&TokenKey=${token}`,
path: `/showvisitdicoms?page=download&trialId=${trialId}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&isReading=1&TokenKey=${token}&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
this.open = window.open(routeData.href, '_blank')
},

View File

@ -10,7 +10,8 @@
<!--检查列表-->
<el-table :data="list" style="width: 100%" height="300" :loading="loading">
<!--受试者-->
<el-table-column prop="SubjectCode" :label="$t('upload:dicom:table:subjectCode')" sortable />
<el-table-column prop="SubjectCode" :label="$t('upload:dicom:table:subjectCode')" sortable
v-if="isReadingTaskViewInOrder !== 0" />
<!--访视名称-->
<el-table-column prop="VisitName" :label="$t('download:table:VisitName')" v-if="IsImageSegment" sortable />
<!--任务名称-->
@ -42,7 +43,8 @@
<template slot-scope="scope">
<div class="btnBox">
<!--上传--->
<form id="inputForm" :ref="`uploadForm_${scope.row.Id}`" enctype="multipart/form-data" v-if="!forbid">
<form id="inputForm" :ref="`uploadForm_${scope.row.Id}`" enctype="multipart/form-data"
v-if="!forbid && (!isReading || isDownloaded)">
<div class="form-group" style="margin-right: 10px">
<div :id="`directoryInputWrapper_${scope.row.Id}`" class="btn btn-link file-input">
<el-button circle icon="el-icon-upload2" :disabled="btnLoading" :loading="btnLoading"
@ -69,7 +71,7 @@
</el-table>
<div style="margin: 10px 0" class="top">
<span>{{ $t('upload:dicom:uploadTitle') }}</span>
<div class="btnBox" v-if="!forbid">
<div class="btnBox" v-if="!forbid && (!isReading || isDownloaded)">
<span style="margin-right: 10px">{{ $store.state.trials.uploadTip }}</span>
<form id="inputForm" ref="uploadForm" enctype="multipart/form-data">
<div class="form-group">
@ -304,7 +306,8 @@
</el-table>
<study-view v-if="model_cfg.visible" :model_cfg="model_cfg" :IsDicom="true" :bodyPart="bodyPart"
:subjectVisitId="openSubjectVisitId" :modelList="modelList" :isUpload="openIsUpload"
:visitTaskId="openVisitTaskId" :TrialModality="TrialModality" @getList="getList" />
:visitTaskId="openVisitTaskId" :TrialModality="TrialModality" :IsReadingTaskViewInOrder="IsReadingTaskViewInOrder"
@getList="getList" />
</div>
</template>
<script>
@ -377,6 +380,10 @@ export default {
type: Boolean,
default: false,
},
isReading: {
type: Boolean,
default: false,
}
},
components: {
'study-view': studyView,
@ -426,6 +433,8 @@ export default {
openSubjectVisitId: null,
openVisitTaskId: null,
TrialModality: [],
IsReadingTaskViewInOrder: 2,
isDownloaded: false
}
},
async mounted() {
@ -468,7 +477,12 @@ export default {
let res = await getSubjectImageUploadList(params)
this.loading = false
if (res.IsSuccess) {
this.isDownloaded = this.Criterion && this.Criterion.CriterionType && (
this.Criterion.CriterionType == 19 ||
this.Criterion.CriterionType == 20
) ? res.OtherInfo.IsIRImageDownloaded : true
this.TrialModality = res.OtherInfo.TrialModality.split('|')
this.IsReadingTaskViewInOrder = res.OtherInfo.IsReadingTaskViewInOrder || res.OtherInfo.IsReadingTaskViewInOrder === 0 ? res.OtherInfo.IsReadingTaskViewInOrder : 2
this.StudyInstanceUidList = []
this.SopInstanceUidList = []
this.UploadStudyList = []
@ -550,6 +564,7 @@ export default {
this.openVisitTaskId = item.VisitTaskId
this.model_cfg.title = `${item.SubjectCode || ''} > ${this.IsImageSegment ? item.VisitName : item.TaskBlindName
}`
if (this.isReadingTaskViewInOrder === 0) this.model_cfg.title = ''
this.modelList = item[list]
this.model_cfg.visible = true
},
@ -1639,7 +1654,7 @@ export default {
const routeData = this.$router.resolve({
path: `/showvisitdicoms?page=upload&trialId=${trialId}&visitTaskId=${this.IsImageSegment ? 'undefined' : row.VisitTaskId
}&subjectVisitId=${row.SourceSubjectVisitId
}&isReading=1&TokenKey=${token}`,
}&isReading=1&TokenKey=${token}&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
this.open = window.open(routeData.href, '_blank')
},

View File

@ -3,15 +3,16 @@
:append-to-body="true" class="uploadDicomAndNonedicom">
<span slot="title">{{ title }}</span>
<el-tabs type="border-card" v-model="activeName">
<el-tab-pane :label="$t('uploadDicomAndNonedicom:label:dicom')" name="dicom">
<el-tab-pane :label="$t('uploadDicomAndNonedicom:label:dicom')" name="dicom" v-if="!IsImageSegment">
<dicomFile v-if="activeName === 'dicom'" :SubjectId="SubjectId" :SubjectCode="SubjectCode"
:Criterion="Criterion" :TaskId="VisitTaskId" :isUpload.sync="isUpload"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :IsImageSegment="IsImageSegment" :forbid="forbid" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :IsImageSegment="IsImageSegment" :forbid="forbid"
:isReading="isReading" />
</el-tab-pane>
<el-tab-pane :label="$t('uploadDicomAndNonedicom:label:nonedicom')" name="nonedicom">
<nonedicomFile v-if="activeName === 'nonedicom'" :SubjectId="SubjectId" :SubjectCode="SubjectCode"
:Criterion="Criterion" :VisitTaskId="VisitTaskId" :isUpload.sync="isUpload" :IsImageSegment="IsImageSegment"
:forbid="forbid" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :forbid="forbid" :isReading="isReading" />
</el-tab-pane>
</el-tabs>
</el-dialog>
@ -58,6 +59,10 @@ export default {
IsImageSegment: {
type: Boolean,
default: false,
},
isReading: {
type: Boolean,
default: false,
}
},
data() {
@ -67,9 +72,16 @@ export default {
isUpload: false,
}
},
watch: {
IsImageSegment() {
if (this.IsImageSegment) this.activeName = 'nonedicom'
}
},
mounted() {
this.title = `Upload Images${this.SubjectCode}${this.Criterion.TrialReadingCriterionName}`
if (this.IsImageSegment) this.title = `Upload Images${this.SubjectCode}`
if (this.isReadingTaskViewInOrder === 0) this.title = ''
if (this.IsImageSegment) this.activeName = 'nonedicom'
store.dispatch('trials/setUnLock', true)
},
methods: {

View File

@ -14,7 +14,8 @@
<el-table :data="list" style="width: 100%" v-adaptive="{ bottomOffset: 60 }" :loading="loading"
:default-sort="{ prop: 'TaskBlindName', order: 'descending' }">
<!--受试者 sortable="custom"-->
<el-table-column prop="SubjectCode" :label="$t('upload:nonedicom:table:subject')" />
<el-table-column prop="SubjectCode" :label="$t('upload:nonedicom:table:subject')"
v-if="isReadingTaskViewInOrder !== 0" />
<!--访视名称-->
<el-table-column prop="VisitName" :label="$t('upload:nonedicom:table:VisitName')" sortable
v-if="IsImageSegment" />
@ -110,7 +111,7 @@
:title="$t('upload:nonedicom:button:preview')" @click.stop="handlePreviewNoneDicomFiles(scope.row)" />
<!--上传--->
<el-button circle icon="el-icon-upload2" :title="$t('upload:nonedicom:button:upload')" v-if="!forbid"
@click.native.prevent="handleUpload(scope.row)" />
@click.native.prevent="handleUpload(scope.row)" :disabled="isReading && !isDownloaded" />
<!--删除--->
<el-button :disabled="scope.row.UploadedFileCount <= 0 ||
scope.row.ReadingTaskState === 2
@ -223,6 +224,7 @@ import {
import { preArchiveStudy, uploadNoneDicomFile } from '@/api/trials'
import store from '@/store'
import { getToken } from '@/utils/auth'
import { unzipRaw } from 'unzipit';
let defaultSearchData = () => {
return {
SubjectId: null,
@ -265,6 +267,14 @@ export default {
type: Boolean,
default: false,
},
isReadingTaskViewInOrder: {
type: Number,
default: 0,
},
isReading: {
type: Boolean,
default: false,
}
},
data() {
return {
@ -286,7 +296,8 @@ export default {
BodyPart: {},
relationInfo: {
ImageFormatList: []
}
},
isDownloaded: false
}
},
watch: {
@ -322,6 +333,34 @@ export default {
store.dispatch('trials/setUnLock', false)
},
methods: {
async readZipFileListRaw(zipFile) {
try {
// 1. 使 unzipRaw
const { entries } = await unzipRaw(zipFile);
// 2. entries
const fileList = entries.map(entry => {
// 3. 使 TextDecoder
// ZIP 使 'gbk'
const decoder = new TextDecoder('gbk'); // 'big5'
const name = decoder.decode(entry.nameBytes);
return {
path: name,
name: name.split('/').pop(),
isDirectory: entry.isDirectory,
size: entry.size,
lastModified: entry.lastModDate
};
});
console.log('解码后的文件列表:', fileList);
return fileList;
} catch (error) {
console.error('读取 ZIP 失败:', error);
throw error;
}
},
async getList() {
try {
this.searchData.SubjectId = this.SubjectId
@ -343,6 +382,10 @@ export default {
if (res.IsSuccess) {
this.list = res.Result
this.relationInfo = res.OtherInfo
this.isDownloaded = this.Criterion && this.Criterion.CriterionType && (
this.Criterion.CriterionType == 19 ||
this.Criterion.CriterionType == 20
) ? res.OtherInfo.IsIRImageDownloaded : true
this.faccept = []
this.relationInfo.ImageFormatList.forEach((item) => {
this.faccept.push(`.${item}`)
@ -356,11 +399,15 @@ export default {
//
beginScanFiles(e) {
var files = [...e.target.files]
var sameFiles = []
var sameFiles = [], zipFiles = [], f = false
files.forEach((file) => {
var extendName = file.name
.substring(file.name.lastIndexOf('.'))
.toLocaleLowerCase()
if (extendName === '.zip' && this.IsImageSegment) {
if (file.name.indexOf('筛选期') > -1 || file.name.indexOf('研究结束') > -1) f = true
zipFiles.push(file)
}
if (
this.faccept.indexOf(extendName) !== -1 &&
this.fileList.findIndex((v) => v.name === file.name) > -1
@ -368,6 +415,16 @@ export default {
sameFiles.push(file.name)
}
})
if (zipFiles.length > 0) {
zipFiles.forEach(async file => {
let list = await this.readZipFileListRaw(file)
if (!f) f = list.some(i => i.path.indexOf('筛选期') > -1 || i.path.indexOf('研究结束') > -1)
list = null
})
}
if (f) return this.$confirm(this.$t("trials:uploadNonDicoms:confirm:zipNameNonCompliant"), '', {
type: "warning"
})
var scope = this
if (sameFiles.length > 0) {
const h = this.$createElement
@ -475,7 +532,7 @@ export default {
let trialId = this.$route.query.trialId
var token = getToken()
const routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${row.SourceSubjectVisitId}&studyId=${row.Id}&visitTaskId=${row.VisitTaskId}&TokenKey=${token}`,
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${row.SourceSubjectVisitId}&studyId=${row.Id}&visitTaskId=${row.VisitTaskId}&TokenKey=${token}&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
this.open = window.open(routeData.href, '_blank')
},

View File

@ -7,7 +7,8 @@
</el-button>
<el-table :data="modelList" style="width: 100%" height="300">
<!--检查编号-->
<el-table-column prop="StudyCode" :label="$t('trials:uploadImage:table:StudyCode')" />
<el-table-column prop="StudyCode" :label="$t('trials:uploadImage:table:StudyCode')"
v-if="IsReadingTaskViewInOrder !== 0" />
<!--检查类型-->
<el-table-column prop="ModalityForEdit" :label="$t('trials:uploadImage:table:ModalityForEdit')"
v-if="IsDicom" />
@ -139,6 +140,10 @@ export default {
return {}
},
},
IsReadingTaskViewInOrder: {
type: Number,
default: 2,
}
},
components: {
'base-model': baseModel,
@ -176,18 +181,18 @@ export default {
var token = getToken()
let trialId = this.$route.query.trialId
routeData = this.$router.resolve({
path: `/showvisitdicoms?trialId=${trialId}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&isReading=1&TokenKey=${token}`,
path: `/showvisitdicoms?trialId=${trialId}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&isReading=1&TokenKey=${token}&IsReadingTaskViewInOrder=${this.IsReadingTaskViewInOrder}`,
})
} else {
let trialId = this.$route.query.trialId
var token = getToken()
if (!this.IsImageSegment && (this.Criterion.CriterionType == 19 || this.Criterion.CriterionType == 20)) {
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&isImageSegmentLabel=${false}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&TokenKey=${token}&isReading=true`,
path: `/showNoneDicoms?trialId=${trialId}&isImageSegmentLabel=${false}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&TokenKey=${token}&isReading=true&IsReadingTaskViewInOrder=${this.IsReadingTaskViewInOrder}`,
})
} else {
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&TokenKey=${token}&isReading=true`,
path: `/showNoneDicoms?trialId=${trialId}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&TokenKey=${token}&isReading=true&IsReadingTaskViewInOrder=${this.IsReadingTaskViewInOrder}`,
})
}

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1782284218093" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4982" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 51.2a460.8 460.8 0 1 1 0 921.6 460.8 460.8 0 0 1 0-921.6z m0 51.2a409.6 409.6 0 1 0 0 819.2 409.6 409.6 0 0 0 0-819.2z" fill="#ffffff" p-id="4983"></path><path d="M512 358.4c70.4 0 137.1136 5.2736 196.7616 14.6432l27.0336 4.6592C877.1584 403.9168 972.8 454.2464 972.8 512c0 57.7536-95.6416 108.032-237.0048 134.2976l-27.0336 4.608A1275.1872 1275.1872 0 0 1 512 665.6c-70.4 0-137.1136-5.2736-196.7616-14.6432l-27.0336-4.6592C146.8416 620.0832 51.2 569.7536 51.2 512c0-57.7536 95.6416-108.032 237.0048-134.2976l27.0336-4.608c55.0912-8.704 116.1728-13.824 180.5824-14.592L512 358.4z m0 51.2c-31.5904 0-62.3104 1.1776-91.8528 3.328l-29.0816 2.5088-28.2112 3.072c-23.1424 2.7648-45.2096 6.144-66.048 9.984l-24.4224 4.7616-23.0912 5.12-21.76 5.5296C150.3744 464.5376 102.4 490.8544 102.4 512c0 21.1456 47.9744 47.4624 125.1328 68.096l21.76 5.4784 23.0912 5.1712 24.4224 4.7616c20.8384 3.84 42.9056 7.168 66.048 9.984l28.16 3.072 29.1328 2.5088a1261.4656 1261.4656 0 0 0 183.7056 0l29.0816-2.5088 28.2112-3.072c23.1424-2.7648 45.2096-6.144 66.048-9.984l24.4224-4.7616 23.0912-5.12 21.76-5.5296C873.6256 559.4624 921.6 533.1456 921.6 512c0-21.1456-47.9744-47.4624-125.1328-68.096l-21.76-5.4784-23.0912-5.1712-24.4224-4.7616c-20.8384-3.84-42.9056-7.168-66.048-9.984l-28.16-3.072-29.1328-2.5088c-24.576-1.792-50.0736-2.9184-76.1344-3.2256L512 409.6z" fill="#ffffff" p-id="4984"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -2,11 +2,7 @@
<div class="navbar navbar-flex-wrapper">
<div class="leftMenu">
<div class="navbar-flex-wrapper">
<hamburger
:is-active="sidebar.opened"
class="hamburger-container"
@toggleClick="toggleSideBar"
/>
<hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb class="breadcrumb-container" />
</div>
@ -51,12 +47,8 @@
<TopLang></TopLang>
</div>
</div>
<toggleRole
v-if="toggleRoleVisible"
:visible.sync="toggleRoleVisible"
:loading="toggleRoleLoading"
@save="loginByRole"
/>
<toggleRole v-if="toggleRoleVisible" :visible.sync="toggleRoleVisible" :loading="toggleRoleLoading"
@save="loginByRole" />
</div>
</template>
@ -192,12 +184,14 @@ export default {
flex-direction: row;
justify-content: space-between;
}
.navbar {
height: 50px;
overflow: hidden;
// position: relative;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.hamburger-container {
line-height: 46px;
height: 100%;
@ -219,6 +213,7 @@ export default {
// float: right;
height: 100%;
line-height: 65px;
// position: relative;
&:focus {
outline: none;
@ -245,6 +240,7 @@ export default {
.avatar-container {
display: inline-block;
margin-right: 200px;
.user-avatar {
margin-top: 5px;
width: 40px;
@ -252,16 +248,19 @@ export default {
border-radius: 10px;
}
}
.dropdown-container {
// margin-left: 30px;
// position: absolute;
// right: 20px;
// top: 0px;
cursor: pointer;
.el-icon-caret-bottom {
font-size: 12px;
}
}
.el-dropdown-link {
color: #999c9e;
cursor: pointer;

View File

@ -1,6 +1,6 @@
import Vue from 'vue'
// import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import 'normalize.css/normalize.css' // A modern alternative to CSS resets
// import { createVersionPolling } from "@/utils/version-polling.esm.js";
import ElementUI, { MessageBox } from 'element-ui'
@ -452,7 +452,7 @@ async function VueInit() {
isOpen = false
isLock = null
zzSessionStorage.removeItem('isLock')
// DicomEvent.$emit('isLock', false)
DicomEvent.$emit('isLock', false)
router.push("/login")
}).then(() => {
// _vm.$alert(lang === 'zh' ? '由于您长时间未操作,为保护您的数据安全已强制将您下线,如果需要继续操作请重新登陆!' : 'No operation for a long time non-operation, you have been forced logout to protect data security. If continue to operate, please login again!', {
@ -473,7 +473,7 @@ async function VueInit() {
if (_vm.$route.path === '/login') {
zzSessionStorage.removeItem('lastWorkbench')
zzSessionStorage.removeItem('isLock')
// DicomEvent.$emit('isLock', false)
DicomEvent.$emit('isLock', false)
isLock = null
}
return
@ -483,7 +483,7 @@ async function VueInit() {
}
isOpen = true
zzSessionStorage.setItem('isLock', 'true')
// DicomEvent.$emit('isLock', true)
DicomEvent.$emit('isLock', true)
_vm.$msgbox({
title: _vm.$t("env:lock:msgBox:title"),
confirmButtonText: _vm.$t("env:lock:msgBox:confirmButtonText"),
@ -516,7 +516,7 @@ async function VueInit() {
count = 0;
isLock = null
zzSessionStorage.removeItem('isLock')
// DicomEvent.$emit('isLock', false)
DicomEvent.$emit('isLock', false)
localStorage.setItem('count', '0')
document.querySelector('#my_username').value = null
document.querySelector('#my_password').value = null

View File

@ -15,7 +15,7 @@ NProgress.configure({ showSpinner: false })
const whiteList = WHITELIST
store.state.trials.whiteList = whiteList
router.beforeEach(async(to, from, next) => {
router.beforeEach(async (to, from, next) => {
NProgress.start()
// 设置页面标题
// document.title = getPageTitle(to.meta.title) store.state.trials.whiteList.indexOf(to.path) === -1
@ -60,7 +60,7 @@ router.beforeEach(async(to, from, next) => {
Vue.prototype.toPath = to.path
Vue.prototype.$path = []
if (hasToken) {
if (to.path === '/login' || to.path === '/recompose' || to.path === '/email-recompose' || to.path === '/error' || to.path === '/ReviewersResearchForm' || to.path === '/ReviewersResearch') {
if (to.path === '/login' || to.path === '/recompose' || to.path === '/email-recompose' || to.path === '/link_expired' || to.path === '/error' || to.path === '/ReviewersResearchForm' || to.path === '/ReviewersResearch') {
if (to.path === '/ReviewersResearch') {
await OSSclient()
await store.dispatch('user/logout')
@ -92,8 +92,13 @@ router.beforeEach(async(to, from, next) => {
await store.dispatch('user/getUserInfo')
const accessRoutes = await store.dispatch('permission/generateRoutes')
resetRouter()
router.addRoutes(accessRoutes)
next({ ...to, replace: true })
if (accessRoutes.length > 0) {
router.addRoutes(accessRoutes)
next({ ...to, replace: true })
} else {
next()
}
} catch (error) {
console.log(error)
// 删除token并进入登录页面以重新登录

View File

@ -128,6 +128,11 @@ export const constantRoutes = [
hidden: true,
component: () => import('@/views/none-dicom-show/index')
},
{
path: '/none_dicoms',
hidden: true,
component: () => import('@/views/none-dicoms/index')
},
{
path: '/noneDicomReading',
name: 'noneDicomReading',
@ -144,6 +149,11 @@ export const constantRoutes = [
hidden: true,
component: () => import('@/views/trials/trials-panel/reading/dicoms/none-dicoms')
},
{
path: '/ecrfList',
hidden: true,
component: () => import('@/views/trials/trials-panel/reading/dicoms/components/TableList')
},
{
path: '/readingPage',
name: 'readingPage',
@ -218,6 +228,12 @@ export const constantRoutes = [
component: () => import('@/views/research/form'),
hidden: true
},
{
path: '/researchLog',
name: 'researchLog',
component: () => import('@/views/research/form'),
hidden: true
},
{
path: '/researchLogin_m',
name: 'researchLogin_m',
@ -278,6 +294,11 @@ export const constantRoutes = [
component: () => import('@/views/error'),
hidden: true
},
{
path: '/link_expired',
component: () => import('@/views/linkExpired'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),

View File

@ -128,7 +128,14 @@ const actions = {
if (response.IsSuccess) {
var noticeList = []
response.Result.forEach(el => {
noticeList.push({ Id: el.Id, Content: el.NoticeContent, NoticeTypeEnum: el.NoticeTypeEnum })
noticeList.push({
Id: el.Id,
Content: el.NoticeContent,
NoticeTypeEnum: el.NoticeTypeEnum,
PublishedTime: el.PublishedTime,
FileName: el.FileName,
Path: el.Path
})
})
commit('SET_NOTICE', noticeList)

View File

@ -121,7 +121,7 @@ const actions = {
localStorage.setItem('CompanyInfo', JSON.stringify(response.Result.CompanyInfo))
const data = response.Result
commit('SET_ROLES', data.BasicInfo.AccountList)
if (data.BasicInfo.IsFirstAdd || data.BasicInfo.LoginState === 1) {
if (data.BasicInfo.IsFirstAdd || data.BasicInfo.LoginState === 1 || data.BasicInfo.IsNeedResetPwd) {
try {
zzSessionStorage.setItem('userId', data.BasicInfo.Id)
commit('SET_TOKEN', data.JWTStr)

View File

@ -376,4 +376,8 @@ body .el-table th.gutter {
height: 20px !important;
vertical-align: -0.4em !important;
cursor: pointer;
}
.mobile_confirm {
width: 200px;
}

View File

@ -21,6 +21,16 @@ var path
service.interceptors.request.use(
async config => {
if (config.url.includes("/Dictionary/getBasicDataSelect")) {
let query = router && router.app && router.app._route && router.app._route.query
if (query.trialId) {
let str = config.url.split("?")
config.url = `${str[0]}?TrialId=${query.trialId}`
if (str[1]) {
config.url += `&${str[1]}`
}
}
}
path = router && router.app && router.app._route && router.app._route.path
config.headers['Content-Type'] = 'application/json;charset=UTF-8'
// config.baseURL = process.env.NODE_ENV === 'prod' ? "https://api.irc.extimaging.com" : '/api'
@ -108,6 +118,9 @@ service.interceptors.response.use(
if (res.IsSuccess) {
return Promise.resolve(res)
} else if (res.IsSuccess === false) {
if (res.Code === -4) {
return Promise.resolve(res)
}
if (res.Code !== 5) {
MessageBox.confirm(res.ErrorMessage, {
type: 'warning',

View File

@ -11,10 +11,10 @@ function zipFiles(zipName, files) {
store.dispatch('trials/setUnLock', true)
files = formatFiles(files)
// 创建压缩文件输出流
const zipFileOutputStream = streamSaver.createWriteStream(zipName);
let zipFileOutputStream = streamSaver.createWriteStream(zipName);
// 创建下载文件流
const fileIterator = files.values();
const readableZipStream = new ZIP({
let readableZipStream = new ZIP({
async pull(ctrl) {
const fileInfo = fileIterator.next();
if (fileInfo.done) {//迭代终止
@ -41,9 +41,13 @@ function zipFiles(zipName, files) {
.then(() => {
console.log("同步下载打包结束时间:" + new Date());
store.dispatch('trials/setUnLock', false)
readableZipStream = null;
zipFileOutputStream = null;
resolve(true)
}).catch(err => {
console.log(err);
readableZipStream = null;
zipFileOutputStream = null;
resolve(false)
});
} else {
@ -61,14 +65,16 @@ async function updateFile(file, name) {
return new Promise(async resolve => {
try {
store.dispatch('trials/setUnLock', true)
const fileOutputStream = streamSaver.createWriteStream(name);
let fileOutputStream = streamSaver.createWriteStream(name);
// file = decodeUtf8(file);
let res = await fetch(file);
res.body.pipeTo(fileOutputStream).then(() => {
store.dispatch('trials/setUnLock', true)
fileOutputStream = null;
resolve(true)
}).catch(err => {
console.log(err)
fileOutputStream = null;
resolve(false)
});
} catch (err) {

View File

@ -1,2 +1,2 @@
const WHITELIST = ['/', "/curriculumVitae", 'researchDetail_m', '/researchForm', '/ReviewersResearch', '/login', '/error', '/resetpassword', '/recompose', '/email-recompose', '/trialStats', '/showdicom', '/imagesShare', '/audit', '/preview', '/researchLogin', '/researchLogin_m', '/blindResumeInfo', '/trialsResume', '/joinVerify', '/showNoneDicoms', '/noneDicomReading', '/clinicalData', '/readingDicoms', '/readingPage', '/visitDicomReview', '/visitNondicomReview', '/globalReview', '/adReview', '/oncologyReview', '/nonedicoms']
const WHITELIST = ['/', "/curriculumVitae", 'researchDetail_m', '/researchForm', '/ReviewersResearch', '/login', '/link_expired', '/error', '/resetpassword', '/recompose', '/email-recompose', '/trialStats', '/showdicom', '/imagesShare', '/audit', '/preview', '/researchLogin', '/researchLogin_m', '/blindResumeInfo', '/trialsResume', '/joinVerify', '/showNoneDicoms', '/noneDicomReading', '/clinicalData', '/readingDicoms', '/readingPage', '/visitDicomReview', '/visitNondicomReview', '/globalReview', '/adReview', '/oncologyReview', '/nonedicoms', '/ecrfList', '/researchLog']
export default WHITELIST

View File

@ -20,13 +20,13 @@
:name="`${study.StudyId}`">
<template slot="title">
<div class="collapse-title-wrapper">
<div class="text-desc">
<div class="text-desc" v-if="IsReadingTaskViewInOrder !== 0">
{{ study.StudyCode }}
</div>
<!-- <div v-show="study.Description" class="text-desc">
<!-- <div v-show="study.Description" class="text-desc">
{{ study.Description }}
</div> -->
<!-- <el-tooltip v-show="study.Description" class="item" effect="dark" :content="study.Description"
<!-- <el-tooltip v-show="study.Description" class="item" effect="dark" :content="study.Description"
placement="bottom">
<div v-if="study.Description"
style="width: 50px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;padding: 1x;">
@ -155,8 +155,8 @@
</div>
</div>
</el-tab-pane>
<el-tab-pane v-show="!visitTaskId" :label="$t('trials:dicom-show:relatedVisit')" name="relation-study"
class="pane-relation-wrapper">
<el-tab-pane v-show="!visitTaskId" v-if="IsReadingTaskViewInOrder >= 2"
:label="$t('trials:dicom-show:relatedVisit')" name="relation-study" class="pane-relation-wrapper">
<div class="viewerSidethumbinner">
<el-collapse v-model="relationActiveName" @change="handelRelationActiveChange">
@ -169,10 +169,10 @@
v-if="study.VisitName === item.VisitName">
<template slot="title">
<div class="collapse-title-wrapper">
<div class="text-desc">
<div class="text-desc" v-if="IsReadingTaskViewInOrder !== 0">
{{ study.StudyCode }}
</div>
<!-- <div v-show="study.Description" class="text-desc">
<!-- <div v-show="study.Description" class="text-desc">
{{ study.Description }}
</div> -->
@ -187,19 +187,18 @@
<div class="viewerSidethumbs ps" style="position: relative;">
<div class="viewerSidethumbinner">
<div v-for="(seriesItem, index) in study.seriesList" :key="seriesItem.seriesId">
<div class="viewernavigatorwrapper"
style="position: relative;border:1px solid #434343;" series-type="relation"
<div class="viewernavigatorwrapper" style="position: relative;border:1px solid #434343;"
series-type="relation"
@click="showRelationSeriesImage($event, seriesItem, studyIndex, index)">
<div class="imageBox" style="width: 72px;height:72px;">
<img class="image-preview" :src="seriesItem.previewImageUrl"
crossorigin="anonymous" alt="" style="width: 72px;height:72px;" fit="fill" />
<img class="image-preview" :src="seriesItem.previewImageUrl" crossorigin="anonymous"
alt="" style="width: 72px;height:72px;" fit="fill" />
<i class="el-icon-refresh" :title="$t('tip:refreshImage')"
@click.stop="refreshImage(item)"></i>
</div>
<div class="viewernavitextwrapper">
<div
style="padding: 1px 5px 1px 1px;display: flex;justify-content: space-between;">
<div style="padding: 1px 5px 1px 1px;display: flex;justify-content: space-between;">
<div v-if="seriesItem.keySeries" style="color:red">
Key Images
</div>
@ -332,7 +331,8 @@ export default {
visitTaskId: null,
page: '',
activeSeriesId: null,
activeInstanceId: null
activeInstanceId: null,
IsReadingTaskViewInOrder: 2
}
},
mounted() {
@ -351,6 +351,10 @@ export default {
this.isFromCRCUpload = !!this.$router.currentRoute.query.isFromCRCUpload
this.visitTaskId = this.$router.currentRoute.query.visitTaskId
this.page = this.$route.query.page
if (this.$router.currentRoute.query.IsReadingTaskViewInOrder) {
this.IsReadingTaskViewInOrder = Number(this.$router.currentRoute.query.IsReadingTaskViewInOrder)
}
console.log(this.IsReadingTaskViewInOrder)
this.beforeUnloadHandler = () => {
cornerstone.imageCache.purgeCache()
requestPoolManager.resetRequestPool()
@ -951,7 +955,7 @@ export default {
if (this.imageList.length > 0) {
requestPoolManager.startTaskTimer()
this.imageList.map(image => {
requestPoolManager.loadAndCacheImagePlus(image.imageId, image.seriesId, image.priority).catch(() => {})
requestPoolManager.loadAndCacheImagePlus(image.imageId, image.seriesId, image.priority).catch(() => { })
})
requestPoolManager.sortTaskPool()
this.imageList = []
@ -1338,6 +1342,7 @@ export default {
border-color: #213a54 !important;
background-color: #213a54;
}
.frame_content_active {
border-color: #213a54 !important;
background-color: #213a54;

View File

@ -26,11 +26,19 @@
<el-table v-if="refreshTable" v-adaptive="{ bottomOffset: 45 }" v-loading="loading" height="100" :data="menuList"
:default-expand-all="isExpandAll" row-key="Id" :tree-props="{ children: 'Children', hasChildren: 'hasChildren' }">
<!-- 模块 -->
<el-table-column prop="ModuleTypeValueCN" :label="$t('audit:label:moduleType')" show-overflow-tooltip align="left" min-width="230px" />
<el-table-column v-if="language === 'en'" prop="ModuleTypeValue" :label="$t('audit:label:moduleType')" show-overflow-tooltip align="left" min-width="230px">
</el-table-column>
<el-table-column v-else prop="ModuleTypeValueCN" :label="$t('audit:label:moduleType')" show-overflow-tooltip align="left" min-width="230px">
</el-table-column>
<!-- 操作名称 -->
<el-table-column prop="Description" :label="$t('audit:label:desc')" :show-overflow-tooltip="true" min-width="200px">
<el-table-column v-if="language === 'en'" prop="Description" :label="$t('audit:label:desc')" :show-overflow-tooltip="true" min-width="200px">
<template slot-scope="scope">
<span>{{ scope.row.DescriptionCN }}{{ scope.row.Description }}</span>
<span>{{ scope.row.Description }}</span>
</template>
</el-table-column>
<el-table-column v-else prop="DescriptionCN" :label="$t('audit:label:desc')" :show-overflow-tooltip="true" min-width="200px">
<template slot-scope="scope">
<span>{{ scope.row.DescriptionCN }}</span>
</template>
</el-table-column>
<!-- 其他 -->
@ -61,17 +69,20 @@
</template>
</el-table-column>
<!-- 标识 -->
<el-table-column prop="Identification" :label="$t('audit:label:identification')" :show-overflow-tooltip="true" min-width="200px" />
<el-table-column prop="Identification" :label="$t('audit:label:identification')" :show-overflow-tooltip="true" min-width="120px" />
<!-- 操作类型 -->
<el-table-column prop="OptTypeValueCN" :label="$t('audit:label:optType')" show-overflow-tooltip width="100px" />
<el-table-column v-if="language === 'en'" prop="OptTypeValue" :label="$t('audit:label:optType')" show-overflow-tooltip min-width="110px" />
<el-table-column v-else prop="OptTypeValueCN" :label="$t('audit:label:optType')" show-overflow-tooltip min-width="110px" />
<!-- 对象类型 -->
<el-table-column prop="ObjectTypeValueCN" :label="$t('audit:label:objectType')" show-overflow-tooltip min-width="100px" />
<el-table-column v-if="language === 'en'" prop="ObjectTypeValue" :label="$t('audit:label:objectType')" show-overflow-tooltip width="100px" />
<el-table-column v-else prop="ObjectTypeValueCN" :label="$t('audit:label:objectType')" show-overflow-tooltip min-width="100px" />
<!-- 数据类型 -->
<el-table-column prop="ChildrenTypeValueCN" :label="$t('audit:label:dataType')" show-overflow-tooltip min-width="100px" />
<el-table-column v-if="language === 'en'" prop="ChildrenTypeValue" :label="$t('audit:label:dataType')" show-overflow-tooltip min-width="100px" />
<el-table-column v-else prop="ChildrenTypeValueCN" :label="$t('audit:label:dataType')" show-overflow-tooltip min-width="100px" />
<!-- 显示顺序 -->
<el-table-column prop="Sort" :label="$t('audit:label:sort')" min-width="80" show-overflow-tooltip />
<el-table-column prop="Sort" :label="$t('audit:label:sort')" min-width="100" show-overflow-tooltip />
<!-- 启用 -->
<el-table-column :label="$t('audit:label:isEnable')" align="center" prop="CreateTime" min-width="80">
<el-table-column :label="$t('audit:label:isEnable')" align="center" prop="CreateTime" min-width="90">
<!-- 启用/禁用 -->
<template slot-scope="scope">
<!-- <el-tag v-if="scope.row.IsEnable" size="success"></el-tag>
@ -81,7 +92,7 @@
</el-tag>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" align="center" fixed="right" min-width="340" class-name="small-padding fixed-width">
<el-table-column :label="$t('common:action:action')" align="center" fixed="right" width="290" class-name="small-padding fixed-width">
<template slot-scope="scope">
<!-- 上移 -->
<el-button
@ -659,7 +670,7 @@ import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import Sortable from 'sortablejs'
import BaseModel from "@/components/BaseModel";
import { mapGetters } from 'vuex'
export default {
name: 'CheckConfig',
dicts: ['OptType', 'ObjectType', 'ModuleType', 'ChildrenType'],
@ -715,6 +726,9 @@ export default {
this.getList();
this.getDictionaryCodeList();
},
computed: {
...mapGetters(['language'])
},
methods: {
querySearch(queryString, cb) {
var DictionaryCodeList = this.DictionaryCodeList;

View File

@ -1,7 +1,7 @@
<template>
<box-content>
<div class="search">
<el-form :inline="true" class="base-search-form">
<el-form :inline="true" class="base-search-form" @submit.native.prevent="">
<!-- CRO Name -->
<el-form-item :label="$t('institutions:cros:label:croName')">
<el-input v-model="searchData.CROName" style="width: 100px" />

View File

@ -1,7 +1,7 @@
<template>
<box-content>
<div class="search">
<el-form :inline="true" class="base-search-form">
<el-form :inline="true" class="base-search-form" @submit.native.prevent="">
<!-- Sponsor Name -->
<el-form-item :label="$t('institutions:sponsors:label:sponsorName')">
<el-input v-model="searchData.SponsorName" style="width: 100px" />

276
src/views/linkExpired.vue Normal file
View File

@ -0,0 +1,276 @@
<template>
<div class="wscn-http404-container">
<div class="wscn-http404" style="display: flex;align-items: center;flex-wrap: wrap;justify-content: center;">
<div class="pic-404">
<!-- <img class="pic-404__parent" src="@/assets/login-bg.png" alt="404"> -->
<svg-icon icon-class="login-bg" style="width: 100%; height: 100%" />
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops" style="color: #428bca">{{ $t('trials:researchForm:title:IsExpired') }}!</div>
<!-- <div class="bullshit__headline">{{ $t('trials:researchForm:title:IsExpired') }}</div> -->
<!-- <div class="bullshit__return-home" style="background: #428bca" @click="$router.push({ path: '/login' })">{{
$t('common:title:goHome') }}</div> -->
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapMutations } from 'vuex'
export default {
computed: {
...mapGetters([
'asyncRoutes',
'routes',
'language'
]),
},
mounted() {
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
this.$updateDictionary()
},
methods: {
...mapMutations({ setLanguage: 'lang/setLanguage' }),
}
}
</script>
<style lang="scss" scoped>
.wscn-http404-container {
transform: translate(-50%, -50%);
position: absolute;
top: 40%;
left: 50%;
width: 100%;
}
.wscn-http404 {
position: relative;
width: 100%;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 100%;
max-width: 500px;
height: 300px;
overflow: hidden;
margin-right: 10px;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
text-align: center;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>

View File

@ -154,7 +154,7 @@
<!-- {{ $t('login:title:system_title_about') }} -->
<img src="@/assets/system.png" alt=""
:style="{ width: isEN ? '180px' : '200px', height: isEN ? '60px' : '65px' }" />
<p style="margin-bottom: 0px">{{ $t('login:title:system') }}</p>
<p style="margin-bottom: 0px">{{ $t('login:title:system') }}</p>
</div>
<p style="margin-bottom: 20px; margin-top: 0">
V{{ $version.IsEnv_US ? $version.Version_US : $version.Version }}
@ -372,6 +372,15 @@ export default {
})
}, 500)
return
} else if (res.BasicInfo.IsNeedResetPwd) {
// ,
this.$message.success(this.$t('login:message:login5'))
setTimeout(() => {
this.$router.push({
path: `/user-recompose?UserId=${res.BasicInfo.IdentityUserId}&Email=${res.BasicInfo.EMail}&UserName=${res.BasicInfo.UserName}&lang=${this.$i18n.locale}&access_token=${res.JWTStr}&isUpdate=1`,
})
}, 500)
return
} else if (res.BasicInfo.NeedChangePassWord) {
//
this.$alert(

View File

@ -153,7 +153,8 @@ export default {
isAudit: false,
activeNames: [],
Asc: false
Asc: false,
IsReadingTaskViewInOrder: 2
}
},
async created() {
@ -165,6 +166,9 @@ export default {
store.dispatch('user/setToken', this.$router.currentRoute.query.TokenKey)
changeURLStatic('TokenKey', '')
}
if (this.$router.currentRoute.query.IsReadingTaskViewInOrder) {
this.IsReadingTaskViewInOrder = Number(this.$router.currentRoute.query.IsReadingTaskViewInOrder)
}
this.subjectVisitId = this.$router.currentRoute.query.subjectVisitId
this.studyId = this.$router.currentRoute.query.studyId
this.getNoneDicomList()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,516 @@
<template>
<div v-loading="loading" class="none-dicom-study-wrapper">
<div class="study-info">
<div style="cursor: pointer;" @click.stop="sortFile">
<i :class="['el-icon-caret-top', asc === false ? '' : 'icon_check']"
style="display: block;margin-bottom: -5px;"></i>
<i :class="['el-icon-caret-bottom', asc === true ? '' : 'icon_check']"
style="display: block; margin-top: -5px;"></i>
</div>
<div>{{ $t('trials:none-dicom-show:fileList') }} </div>
</div>
<div class="ps" ref="studyBox_ps">
<el-collapse v-model="activeNames">
<el-collapse-item v-for="(study, index) in studyList" :key="`${study.Id}`" :name="`${study.Id}`">
<template slot="title">
<div class="dicom-desc">
<div style="text-overflow: ellipsis;overflow: hidden;">
<span :title="study.CodeView">{{ study.CodeView }}</span>
<span v-if="otherInfo && otherInfo.IsShowStudyName" :title="study.StudyName" style="margin-left: 5px;">
{{ study.StudyName }}
</span>
</div>
<div style="text-overflow: ellipsis;overflow: hidden;">
<span :title="study.BodyPart">{{ getBodyPart(study.BodyPart, study.BodyPartForEditOther) }}</span>
<span style="margin-left: 5px;" :title="study.Modality">{{ study.Modality }}</span>
</div>
</div>
</template>
<div class="file-list-container">
<div v-for="(k, i) in study.NoneDicomStudyFileList" :key="i" style="position:relative;margin-top:1px;"
@click="selectFile(study, index, i, k)" :ref="`noneDicomRef_${index}_${i}`">
<div :class="{ 'file-active': index === activeStudyIndex && i === activeFileIndex }" class="file-wrapper">
<div class="file-main">
<div class="file-image">
<el-image
v-if="k.FileType === 'image/jpeg' || k.FileType === 'image/jpg' || k.FileType === 'image/bmp' || k.FileType === 'image/png'"
style="width: 100%;height: 100%;"
:src="`${OSSclientConfig.basePath}${k.Path}?x-oss-process=image/resize,w_50,h_50/format,png`"
fit="contain" crossorigin="anonymous" />
<el-image v-else-if="k.FileType === 'application/pdf'" style="width: 100%;height: 100%;" :src="pdf"
fit="contain" crossorigin="anonymous" />
<el-image v-else-if="
!!~k.FileType.indexOf('mp4')
" style="width: 100%; height: 100%" :src="mp4" fit="contain" crossorigin="anonymous" />
<el-image v-else-if="
!!~k.FileType.indexOf('zip')
" style="width: 100%; height: 100%" :src="zipImg" fit="contain" crossorigin="anonymous" />
</div>
<div class="file-text" :title="k.FileName">
{{ k.FileName }}
</div>
</div>
<div v-if="isQcCheck" class="switchBox">
<div class="item">
<span>{{ $t('trials:audit:table:isReading') }}</span>
<el-switch v-model="k.IsReading" :disabled="k.IsDeleted || isAudit"
:active-text="$fd('YesOrNo', true)" :inactive-text="$fd('YesOrNo', false)"
@change="changeReadingStatus($event, study, k)" />
</div>
<div class="item">
<span>{{ $t('trials:audit:table:isDelete') }}</span>
<el-switch v-model="k.IsDeleted" :disabled="isAudit" :active-text="$fd('YesOrNo', true)"
:inactive-text="$fd('YesOrNo', false)" @change="changeDeleteStatus($event, study, k)" />
</div>
</div>
</div>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
</template>
<script>
import { getNoneDicomStudyList, setNodicomStudyState } from '@/api/trials'
import FileNameSorter from "@/utils/customSort"
import pdf from '@/assets/pdf.png'
import mp4 from '@/assets/mp4.png'
import zipImg from '@/assets/zip.jpg'
export default {
name: 'StudyList',
props: {
visitTaskInfo: {
type: Object,
default() {
return {}
}
},
isComparison: {
type: Boolean,
default: false
}
},
data() {
return {
loading: false,
activeNames: [],
activeStudyId: '',
activeFileId: '',
activeStudyIndex: -1,
activeFileIndex: -1,
studyList: [],
pdf,
mp4,
zipImg,
BodyPart: {},
subjectVisitId: '',
studyId: '',
otherInfo: null,
criterionType: null,
isQcCheck: false,
isAudit: false,
asc: false
}
},
async mounted() {
this.subjectVisitId = this.$route.query.subjectVisitId
this.studyId = this.$route.query.studyId
this.isQcCheck = !!this.$route.query.isQcCheck
this.getNoneDicoms()
// this.$nextTick(() => {
// this.activeStudy(this.studyList[0].Id)
// })
this.BodyPart.Bodypart = await this.$getBodyPart(this.$route.query.trialId)
},
methods: {
scrollActiveFileIntoView() {
this.$nextTick(() => {
const refKey = `noneDicomRef_${this.activeStudyIndex}_${this.activeFileIndex}`
const targetRef = this.$refs[refKey]
const element = Array.isArray(targetRef) ? targetRef[0] : targetRef
if (!element) return
this.scrollElementToVisible(element)
})
},
scrollElementToVisible(element, options = {}) {
const container = this.$refs['studyBox_ps']
if (!container || !element) return
const elementTop = element.offsetTop
const elementBottom = elementTop + element.offsetHeight
const visibleTop = container.scrollTop
const visibleBottom = visibleTop + container.clientHeight
if (elementTop >= visibleTop && elementBottom <= visibleBottom) return
const offset = options.offset != null
? options.offset
: Math.max((container.clientHeight - element.offsetHeight) / 2, 0)
const targetPosition = Math.max(elementTop - offset, 0)
container.scrollTo({
top: targetPosition,
behavior: options.behavior || 'smooth'
})
},
async getNoneDicoms() {
const loading = this.$loading({ fullscreen: true })
try {
let res = await getNoneDicomStudyList(
this.subjectVisitId,
this.studyId,
false,
this.$route.query.visitTaskId,
!!this.$route.query.isReading,
this.$route.query.isImageSegmentLabel,
)
this.studyList = res.Result
this.studyList.forEach(study => {
if (study.NoneDicomStudyFileList && study.NoneDicomStudyFileList.length > 0) {
study.NoneDicomStudyFileList.forEach(file => {
if (file.Path.includes('.MaskNoneDicom_')) {
file.IsBeMark = true
} else {
file.IsBeMark = false
}
})
}
})
this.otherInfo = res.OtherInfo
this.isAudit = res.OtherInfo.AuditState * 1 > 6
const studyIndex = this.studyList.findIndex((item) => {
return item.NoneDicomStudyFileList.length > 0
})
if (studyIndex > -1) {
this.selectFile(this.studyList[studyIndex], studyIndex, 0)
let studyId = this.studyList[studyIndex].Id
this.activeNames = [studyId]
}
if (this.criterionType === 19 || this.criterionType === 20) {
this.sortFile()
}
loading.close()
} catch (e) {
console.log(e)
loading.close()
}
},
selected() {
},
sortFile() {
this.asc = !this.asc
this.studyList.forEach(study => {
if (study.NoneDicomStudyFileList.length > 0) {
study.NoneDicomStudyFileList = FileNameSorter.sortFileNames(study.NoneDicomStudyFileList, {
direction: this.asc ? 'asc' : 'desc',
key: 'FileName'
})
}
})
this.activeStudyIndex = this.studyList.findIndex(item => item.Id === this.activeStudyId)
if (this.activeStudyIndex === -1) return
this.activeFileIndex = this.studyList[this.activeStudyIndex].NoneDicomStudyFileList.findIndex(item => item.Id === this.activeFileId)
},
changeReadingStatus(callback, row, file) {
let statusStr = ''
if (callback) {
statusStr = this.$t('trials:audit:label:setSeriesReading')
file.IsReading = false
} else {
statusStr = this.$t('trials:audit:label:setSeriesNotReading')
file.IsReading = true
}
let message = this.$t('trials:audit:message:changeSeriesStatus').replace('xxx', statusStr)
message = message.replace('yyy', this.$fd('YesOrNo', !file.IsReading))
this.$confirm(message, {
distinguishCancelAndClose: true,
type: 'warning',
})
.then(() => {
const state = file.IsReading ? 1 : 2
this.loading = true
const params = {
SubjectVisitId: row.SubjectVisitId,
NoneDicomStudyId: row.Id,
State: state,
NoneDicomStudyFileId: file.Id,
}
return setNodicomStudyState(params)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
file.IsReading = !file.IsReading
}
})
.catch(() => {
this.loading = false
})
})
.catch(() => { })
},
changeDeleteStatus(callback, row, file) {
let statusStr = ''
if (callback) {
statusStr = this.$t('trials:audit:label:setSeriesDeleted')
file.IsDeleted = false
} else {
statusStr = this.$t('trials:audit:label:setSeriesNotDelete')
file.IsDeleted = true
}
let message = this.$t('trials:audit:message:changeSeriesStatus').replace('xxx', statusStr)
message = message.replace('yyy', this.$fd('YesOrNo', !file.IsDeleted))
this.$confirm(message, {
distinguishCancelAndClose: true,
type: 'warning',
})
.then(() => {
const state = file.IsDeleted ? 5 : 4
this.loading = true
const params = {
SubjectVisitId: row.SubjectVisitId,
NoneDicomStudyId: row.Id,
State: state,
NoneDicomStudyFileId: file.Id,
}
return setNodicomStudyState(params)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
file.IsDeleted = !file.IsDeleted
}
})
.catch(() => {
this.loading = false
})
})
.catch(() => { })
},
getBodyPart(bodyPart, other) {
if (!bodyPart && !other) 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')
})
if (other) {
newArr.push(other)
}
newArr = newArr.filter(Boolean)
return newArr.join(' | ')
},
//
selectFile(study, studyIndex, fileIndex, isReset = false) {
if (this.isComparison) return false
this.activeStudyIndex = studyIndex
this.activeFileIndex = fileIndex
this.activeStudyId = study.Id
const fileList = study.NoneDicomStudyFileList
this.activeFileId = fileList[fileIndex].Id
this.$emit('selectFile', { fileInfo: fileList[fileIndex], fileList, visitTaskInfo: this.visitTaskInfo, studyId: study.Id, isReset })
this.scrollActiveFileIntoView()
},
activeImage(obj) {
if (!obj || this.studyList.length === 0) return
const study = this.studyList.find(i => i.Id === obj.studyId)
const studyIndex = this.studyList.findIndex(i => i.Id === obj.studyId)
if (studyIndex === -1) return
this.activeStudy(`${obj.studyId}`)
this.activeStudyIndex = studyIndex
this.activeStudyId = obj.studyId
const fileList = this.studyList[studyIndex].NoneDicomStudyFileList || []
const fileIndex = fileList.findIndex(f => f.Path === obj.path)
if (fileIndex === -1) return
this.activeFileIndex = fileIndex
this.activeFileId = fileList[fileIndex].Id
this.selectFile(study, studyIndex, fileIndex)
this.scrollActiveFileIntoView()
},
replaceImage(obj) {
if (!obj || this.studyList.length === 0) return
const study = this.studyList.find(i => i.Id === obj.studyId)
const studyIndex = this.studyList.findIndex(i => i.Id === obj.studyId)
if (studyIndex === -1) return
this.activeStudy(`${obj.studyId}`)
this.activeStudyIndex = studyIndex
this.activeStudyId = obj.studyId
const fileList = this.studyList[studyIndex].NoneDicomStudyFileList || []
const fileIndex = fileList.findIndex(f => f.Path === obj.path)
const file = fileList.find(f => f.Path === obj.path)
if (fileIndex === -1) return
this.activeFileIndex = fileIndex
this.activeFileId = fileList[fileIndex].Id
file.Path = obj.file.Path
file.FullFilePath = obj.file.Path
file.IsBeMark = obj.IsBeMark
this.selectFile(study, studyIndex, fileIndex, true)
},
activeStudy(id) {
const studyId = `${id}`
if (this.activeNames.indexOf(studyId) > -1) return
this.activeNames.push(studyId)
}
}
}
</script>
<style lang="scss" scoped>
.none-dicom-study-wrapper {
width: 100%;
height: 100%;
overflow-y: hidden;
overflow-x: hidden;
display: flex;
flex-direction: column;
.study-info {
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
font-weight: bold;
color: #ddd;
background-color: #4c4c4c;
line-height: 40px;
user-select: none;
}
.icon_check {
color: #409EFF;
}
.dicom-desc {
font-weight: bold;
font-size: 13px;
text-align: left;
color: #d0d0d0;
padding: 2px 8px 2px 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ps {
flex: 1;
overflow-anchor: none;
touch-action: auto;
overflow-y: auto;
}
.file-active {
// background: #16477b;
background-color: #183553 !important;
border: 1px solid #23527b !important;
}
::v-deep .el-progress__text {
color: #ccc;
font-size: 12px;
}
::v-deep .el-switch__label {
color: #ccc;
}
.file-list-container {
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
.file-wrapper {
width: 100%;
padding: 5px;
display: flex;
flex-direction: column;
align-items: stretch;
cursor: pointer;
background-color: #3a3a3a;
// color: #ddd;
// white-space: nowrap;
// overflow: hidden;
// text-overflow: ellipsis;
.file-main {
display: flex;
flex-direction: row;
align-items: center;
min-width: 0;
}
.el-progress__text {
display: none;
}
.el-progress-bar {
padding-right: 0px;
}
.file-image {
width: 50px;
height: 50px;
}
.file-text {
flex: 1;
padding-left: 5px;
color: #ddd;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.switchBox {
width: 100%;
margin-top: 6px;
color: #ddd;
.item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 5px;
}
.item:last-child {
margin-bottom: 0;
}
}
::v-deep .el-collapse {
border: none;
.el-collapse-item {
background-color: #000 !important;
color: #ddd;
}
.el-collapse-item__content {
padding-bottom: 0px;
background-color: #000 !important;
}
.el-collapse-item__header {
background-color: #353535 !important;
color: #ddd;
border-bottom-color: #5a5a5a;
padding-left: 5px;
height: 60px;
line-height: 20px;
}
}
::v-deep .el-progress-bar__inner {
transition: width 0s ease;
}
}
</style>

View File

@ -0,0 +1,140 @@
<template>
<div class="read-page-container">
<!-- 检查列表 -->
<div class="left-panel">
<div class="study-info">
<study-list ref="studyList" :isComparison="isComparison" @selectFile="selectFile" />
</div>
</div>
<!-- 图像 -->
<div class="middle-panel">
<file-viewer ref="fileViewer" :isComparison.sync="isComparison" @toggleImage="toggleImage"
@selectFile="selectFile" @replaceImage="replaceImage" />
</div>
</div>
</template>
<script>
import store from '@/store'
import { changeURLStatic } from '@/utils/history.js'
import StudyList from './components/study-list'
import FileViewer from './components/file-viewer'
export default {
name: 'NoneDicoms',
components: {
StudyList,
FileViewer
},
data() {
return {
isComparison: false
}
},
mounted() {
if (this.$route.query.TokenKey) {
store.dispatch('user/setToken', this.$route.query.TokenKey)
changeURLStatic('TokenKey', '')
}
},
methods: {
toggleImage(obj) {
this.$refs['studyList'].activeImage(obj)
},
selectFile(obj) {
if (obj.fileInfo && obj.fileList && obj.studyId) {
this.$refs['fileViewer'].setActiveCanvasImages(obj)
return
}
},
replaceImage(obj) {
this.$refs['studyList'].replaceImage(obj)
}
}
}
</script>
<style lang="scss" scoped>
.read-page-container {
box-sizing: border-box;
height: 100%;
display: flex;
background-color: #444;
padding: 5px;
::-webkit-scrollbar {
width: 5px;
height: 5px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.left-panel {
display: flex;
width: 230px;
border: 1px solid #727272;
color: #fff;
::-webkit-scrollbar {
width: 3px;
height: 3px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.study-info {
width: 100%;
border-left: 1px solid #727272;
}
}
.middle-panel {
flex: 1;
border: 1px solid #727272;
margin: 0 5px;
}
// .right-panel {
// width: 400px;
// border: 1px solid #727272;
// padding: 0 10px;
// overflow-y: auto;
// .text-info {
// display: flex;
// align-items: center;
// justify-content: space-between;
// color: #ddd;
// }
// }
::v-deep .dialog-container {
margin-top: 50px !important;
width: 75%;
height: 80%;
}
::v-deep .el-dialog__body {
padding: 20px 20px 0 20px;
height: calc(100% - 70px);
}
.el-dialog__header {
position: relative;
}
.full-dialog-container {
::v-deep .is-fullscreen .el-dialog__body {
height: calc(100% - 70px);
}
}
}
</style>

View File

@ -25,19 +25,10 @@
<div class="login_content">
<div class="form-label-width">
<el-form-item :label="$t('trials:researchForm:form:siteName')" prop="TrialSiteId">
<el-select
v-model="form.TrialSiteId"
filterable
style="width:100%;"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
@change="handleSiteChange"
>
<el-option
v-for="(item,index) of siteOptions"
:key="index"
:label="item.TrialSiteAliasName"
:value="item.TrialSiteId"
/>
<el-select v-model="form.TrialSiteId" filterable style="width:100%;"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" @change="handleSiteChange">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteAliasName"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<!-- 中心编号 -->
@ -49,74 +40,149 @@
<el-input v-model="form.UserName" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
</el-form-item>
<!-- 联系电话 -->
<el-form-item
:label="$t('trials:researchForm:form:contactorPhone')"
prop="Phone"
>
<el-form-item :label="$t('trials:researchForm:form:contactorPhone')" prop="Phone">
<el-input v-model="form.Phone" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
</el-form-item>
<!-- 联系邮箱 -->
<el-form-item :label="$t('trials:researchForm:form:contactorEmail')">
<el-form-item :label="$t('trials:researchForm:form:contactorEmail')" prop="Email">
<el-input v-model="form.Email" disabled />
</el-form-item>
<el-form-item :label="$t('trials:researchForm:form:CommonUploadRecordId')
" v-if="IsSupportUploadFile" class="file">
<el-input v-model="form.SiteSurveyFile.FileName" type="textarea" :autosize="{ minRows: 1, maxRows: 3 }"
style="margin-right: 5px;" disabled />
<div class="upload" v-if="!(!(state === 0 && userTypeEnumInt === 0) || isHistory)">
<input accept=".pdf,.docx,.doc" type="file" name="uploadFolder" class="select-file" title=""
@change="beginScanFiles($event)" />
<div class="btn-select">
{{ $t('dictionary:template:basicData:button:selectFile') }}
</div>
</div>
<!-- <el-button type="primary" @click="viewManualFile"
:disabled="!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path">{{
$t('trials:researchForm:form:preview') }}</el-button> -->
<el-button type="primary" @click="downLoad"
:disabled="!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path">{{
$t('trials:researchForm:form:download') }}</el-button>
</el-form-item>
<!-- 平均刻盘周期 -->
<el-form-item v-if="!notShowFieldList.includes('AverageEngravingCycle')" :label="$t('trials:researchForm:form:engravingCycle')">
<el-input-number v-model="form.AverageEngravingCycle" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" controls-position="right" :min="0" style="width:100%;" />
<el-form-item v-if="!notShowFieldList.includes('AverageEngravingCycle')"
:label="$t('trials:researchForm:form:engravingCycle')">
<el-input-number v-model="form.AverageEngravingCycle"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" controls-position="right" :min="0"
style="width:100%;" />
</el-form-item>
</div>
<!-- MRI-PDFF 是否为本中心该适应症的常规诊疗检查项目 -->
<el-form-item v-if="!notShowFieldList.includes('IsRoutineMRIPDEE')"
:label="$t('trials:researchForm:form:IsRoutineMRIPDEE')" prop="IsRoutineMRIPDEE">
<el-radio-group v-model="form.IsRoutineMRIPDEE"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)">
<el-radio v-for="item of $d.YesOrNo" :key="`IsRoutineMRIPDEE${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- MRI-PDFF 检查的检测周期含单次检查时长预约等待时长等 -->
<el-form-item
v-if="!notShowFieldList.includes('MRIPDFFScanTime') || !notShowFieldList.includes('MRIPDFFLeadTime') || !notShowFieldList.includes('MRIPDFFOther')"
:label="$t('trials:researchForm:form:IsMRIPDFF')">
</el-form-item>
<!-- 单次检查时长分钟 -->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFScanTime')"
:label="$t('trials:researchForm:form:MRIPDFFScanTime')">
<el-input-number v-model="form.MRIPDFFScanTime"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)" controls-position="right" :min="0" />
</el-form-item>
<!-- 平均预约等待时长 -->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFLeadTime')"
:label="$t('trials:researchForm:form:MRIPDFFLeadTime')">
<el-input-number v-model="form.MRIPDFFLeadTime"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)" controls-position="right" :min="0" />
</el-form-item>
<!-- 特殊情况备注-->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFOther')"
:label="$t('trials:researchForm:form:MRIPDFFOther')">
<el-input v-model="form.MRIPDFFOther" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)" />
</el-form-item>
<!-- 如已选择研究者评估项目是否会授权影像科老师参与本试验如不单独授权是否可在试验中保持 1-2 名固定技师操作-->
<el-form-item
v-if="!notShowFieldList.includes('IsAuthorizeRadiologistsParticipate') || !notShowFieldList.includes('AssignFixedTechnologists')"
:label="$t('trials:researchForm:form:IsAuthorize')" prop="IsAuthorize">
<el-radio-group v-model="form.IsAuthorize" :disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)"
@input="handleIsAuthorizeInput">
<el-radio label="IsAuthorizeRadiologistsParticipate">{{
$t('trials:researchForm:form:IsAuthorizeRadiologistsParticipate') }}</el-radio>
<el-radio label="AssignFixedTechnologists">{{
$t('trials:researchForm:form:AssignFixedTechnologists') }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 请确认参与本项目影像采集的影像技师具备对应的资质技师证对应设备的大型设备上岗证 -->
<el-form-item v-if="!notShowFieldList.includes('IsConfirmImagingTechnologist')" :label="$t('trials:researchForm:form:isQualified')">
<el-radio-group v-model="form.IsConfirmImagingTechnologist" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio
v-for="item of $d.YesOrNo"
:key="`IsConfirmImagingTechnologist${item.value}`"
:label="item.value"
>{{ item.label }}</el-radio>
<el-form-item v-if="!notShowFieldList.includes('IsConfirmImagingTechnologist')"
:label="$t('trials:researchForm:form:isQualified')" prop="IsConfirmImagingTechnologist">
<el-radio-group v-model="form.IsConfirmImagingTechnologist"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio v-for="item of $d.YesOrNo" :key="`IsConfirmImagingTechnologist${item.value}`"
:label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 原因 -->
<el-form-item
v-if="!notShowFieldList.includes('NotConfirmReson') && form.IsConfirmImagingTechnologist === false"
:label="$t('trials:researchForm:form:notQualifiedReason')"
>
<el-input
v-model="form.NotConfirmReson"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
/>
:label="$t('trials:researchForm:form:notQualifiedReason')" prop="NotConfirmReson">
<el-input v-model="form.NotConfirmReson" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
</el-form-item>
<!-- 研究单位疗效评估人员类型 -->
<el-form-item v-if="!notShowFieldList.includes('EfficacyEvaluatorType')" :label="$t('trials:researchForm:form:staffType')">
<el-radio-group v-model="form.EfficacyEvaluatorType" :disabled="!(state === 0 && userTypeEnumInt === 0)|| isHistory">
<el-radio v-for="item of $d.EfficacyEvaluatorType" :key="`EfficacyEvaluatorType${item.value}`" :label="item.value">{{ item.label }}</el-radio>
<el-form-item v-if="!notShowFieldList.includes('EfficacyEvaluatorType')"
:label="$t('trials:researchForm:form:staffType')">
<el-radio-group v-model="form.EfficacyEvaluatorType"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio v-for="item of $d.EfficacyEvaluatorType" :key="`EfficacyEvaluatorType${item.value}`"
:label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 是否严格按照研究单位影像手册参数完成图像采集 -->
<el-form-item v-if="!notShowFieldList.includes('IsFollowStudyParameters')">
<el-form-item v-if="!notShowFieldList.includes('IsFollowStudyParameters')" prop="IsFollowStudyParameters">
<span slot="label" v-html="$t('trials:researchForm:form:isFollowStudyParam')" />
<el-radio-group v-model="form.IsFollowStudyParameters" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio v-for="item of $d.YesOrNo" :key="`IsFollowStudyParameters${item.value}`" :label="item.value">{{ item.label }}</el-radio>
<el-radio-group v-model="form.IsFollowStudyParameters"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio v-for="item of $d.YesOrNo" :key="`IsFollowStudyParameters${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
<el-button type="primary" size="small" style="margin-left: 5px;" @click="viewManual">
{{ $t('trials:researchForm:button:viewManual') }}
</el-button>
</el-form-item>
<!-- 不能严格按照研究单位影像手册参数采集图像原因 -->
<el-form-item
v-if="!notShowFieldList.includes('NotFollowReson') && !form.IsFollowStudyParameters"
>
<el-form-item v-if="!notShowFieldList.includes('NotFollowReson') && !form.IsFollowStudyParameters"
prop="NotFollowReson">
<span slot="label" v-html="$t('trials:researchForm:form:notFollowStudyParam')" />
<el-input
v-model="form.NotFollowReson"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
/>
<el-input v-model="form.NotFollowReson" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
</el-form-item>
<!-- 是否严格按照影像手册参数完成刻盘 -->
<el-form-item v-if="!notShowFieldList.includes('ISStrictManualBurnFlag')" prop="ISStrictManualBurnFlag">
<span slot="label" v-html="$t('trials:researchForm:form:ISStrictManualBurnFlag')" />
<el-radio-group v-model="form.ISStrictManualBurnFlag"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)" style="margin-right: 10px;">
<el-radio v-for="item of $d.YesOrNo" :key="`ISStrictManualBurnFlag${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 不能严格按照影像手册参数完成刻盘原因 -->
<el-form-item v-if="!notShowFieldList.includes('NotStrictManualBurnFlagReason') && !form.ISStrictManualBurnFlag"
prop="NotStrictManualBurnFlagReason">
<span slot="label" v-html="$t('trials:researchForm:form:NotStrictManualBurnFlagReason')" />
<el-input v-model="form.NotStrictManualBurnFlagReason" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)" />
</el-form-item>
</div>
</el-form>
</div>
</template>
<script>
import { getTrialSiteSelect } from '@/api/trials'
import { getTrialSiteSelect, getTrialDocumentList, addOrUpdateCommonUploadRecord } from '@/api/trials'
import { addOrUpdateTrialSiteSurvey } from '@/api/research'
export default {
name: 'ResearchBasicInfo',
@ -124,7 +190,15 @@ export default {
isHistory: {
type: Boolean,
default: false
}
},
IsOnlyUploadFile: {
type: Boolean,
default: false
},
IsSupportUploadFile: {
type: Boolean,
default: false
},
},
data() {
var checkPhone = (rule, value, callback) => {
@ -156,16 +230,51 @@ export default {
Phone: '', //
Email: '', //
AverageEngravingCycle: '',
IsRoutineMRIPDEE: '',
MRIPDFFScanTime: '',
MRIPDFFLeadTime: '',
MRIPDFFOther: '',
IsAuthorize: '',
IsAuthorizeRadiologistsParticipate: '',
AssignFixedTechnologists: '',
ISStrictManualBurnFlag: '',
NotStrictManualBurnFlagReason: '',
IsConfirmImagingTechnologist: '',
NotConfirmReson: '',
EfficacyEvaluatorType: '',
IsFollowStudyParameters: '',
NotFollowReson: ''
NotFollowReson: '',
CommonUploadRecordId: null,
SiteSurveyFile: []
},
rules: {
TrialSiteId: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
],
IsRoutineMRIPDEE: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
IsAuthorize: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
ISStrictManualBurnFlag: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
IsFollowStudyParameters: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
IsConfirmImagingTechnologist: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
NotConfirmReson: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
],
NotFollowReson: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
],
NotStrictManualBurnFlagReason: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
],
UserName: [
{ required: true, validator: (rule, value, callback) => { !value ? callback(new Error(this.$t('trials:researchForm:formRule:specify'))) : callback() }, trigger: 'blur' },
{ min: 0, max: 50, message: this.$t('trials:researchForm:formRule:maxLength'), trigger: ['blur', 'change'] }
@ -186,14 +295,173 @@ export default {
state: null,
userTypeEnumInt: zzSessionStorage.getItem('userTypeEnumInt') * 1,
isShow: false,
notShowFieldList: []
notShowFieldList: [],
perview_visible: false,
ExternalList: []
}
},
methods: {
async beginScanFiles(e) {
try {
let file = e.target.files[0]
let name = file.name
if (!this.checkFileSuffix(name)) return this.$message.warning(this.$t("trials:researchRecord:ImageManual:message:checkFileSuffix"))
this.loading = true
file = await this.fileToBlob(file)
let scope = this
var index = name.lastIndexOf('.')
var type = name.substring(index + 1, name.length)
let res = await this.OSSclient.put(
`/${this.$route.query.trialId}/InspectionUpload/SiteSurvey/${this.form.TrialSiteId}/${scope.getGuid(Date.now() + '_' + name.split('.')[0])}.${type}`,
file
)
let data = {
Path: this.$getObjectName(res.url),
FileName: name,
FileSize: file.size,
FileType: type
}
res = await addOrUpdateCommonUploadRecord(data)
this.loading = false
if (res.IsSuccess) {
this.form.CommonUploadRecordId = res.Result
data.Id = res.Result
this.form.SiteSurveyFile = 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 && '.docx'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1 && '.doc'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1) {
return false
} else {
return true
}
},
handleIsAuthorizeInput(label) {
this.form.IsAuthorizeRadiologistsParticipate = false
this.form.AssignFixedTechnologists = false
this.form[label] = true
},
async viewManualFile() {
if (!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path) return this.$message.warning(this.$t("trials:researchForm:message:notFile"))
this.$preview({
path: this.form.SiteSurveyFile.Path,
type: this.form.SiteSurveyFile.FileType,
title: this.form.SiteSurveyFile.Path.FileName,
})
},
async downLoad() {
if (!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path) return this.$message.warning(this.$t("trials:researchForm:message:notFile"))
let link = document.createElement('a')
link.href = this.OSSclientConfig.basePath + this.form.SiteSurveyFile.Path;
link.target = '_blank'
link.download = this.form.SiteSurveyFile.FileName
link.style.display = 'none'
document.body.append(link)
link.click()
document.body.removeChild(link)
link = null
},
async viewManual() {
try {
let data = {
PageIndex: 1,
PageSize: 20,
TrialId: this.$route.query.trialId,
IsPublish: true,
FileTypeCode: 4,
IsDeleted: false
}
let res = await getTrialDocumentList(data)
if (res.IsSuccess) {
const { CurrentPageData } = res.Result
if (CurrentPageData.length <= 0) return this.$message.warning(this.$t("trials:researchRecord:ImageManual:message:noImageManual"))
this.ExternalList = []
CurrentPageData.forEach(item => {
let obj = {
FilePath: item.Path,
FileFormat: 'pdf',
Name: item.Name
}
this.ExternalList.push(obj)
});
// this.perview_visible = true
this.ExternalList.forEach(item => {
let link = document.createElement('a')
link.href = this.OSSclientConfig.basePath + item.FilePath;
link.target = '_blank'
link.download = item.Name
link.style.display = 'none'
document.body.append(link)
link.click()
document.body.removeChild(link)
link = null
})
}
} catch (err) {
console.log(err)
}
},
//
handleSave(isAutoCommit) {
return new Promise(async(resolve) => {
handleSave(isAutoCommit, isCheck = false) {
return new Promise(async (resolve) => {
isCheck = false
try {
if (this.IsOnlyUploadFile && !isCheck) {
if (!this.form.UserName) {
this.$message.warning(this.$t("trials:researchForm:message:notUserName"))
return resolve(false)
}
if (!this.form.Phone) {
this.$message.warning(this.$t("trials:researchForm:message:notPhone"))
return resolve(false)
}
if (!this.form.CommonUploadRecordId) {
this.$message.warning(this.$t("trials:researchForm:message:notCommonUploadRecordId"))
return resolve(false)
}
this.btnLoading = true
const param = {
id: this.form.Id,
trialId: this.$route.query.trialId,
trialSiteId: this.form.TrialSiteId,
userName: this.form.UserName,
phone: this.form.Phone,
email: this.form.Email,
averageEngravingCycle: this.form.AverageEngravingCycle,
IsRoutineMRIPDEE: this.form.IsRoutineMRIPDEE,
MRIPDFFScanTime: this.form.MRIPDFFScanTime,
MRIPDFFLeadTime: this.form.MRIPDFFLeadTime,
MRIPDFFOther: this.form.MRIPDFFOther,
IsAuthorizeRadiologistsParticipate: this.form.IsAuthorizeRadiologistsParticipate,
AssignFixedTechnologists: this.form.AssignFixedTechnologists,
ISStrictManualBurnFlag: this.form.ISStrictManualBurnFlag,
NotStrictManualBurnFlagReason: this.form.NotStrictManualBurnFlagReason,
isConfirmImagingTechnologist: this.form.IsConfirmImagingTechnologist,
notConfirmReson: this.form.NotConfirmReson,
efficacyEvaluatorType: this.form.EfficacyEvaluatorType,
isFollowStudyParameters: this.form.IsFollowStudyParameters,
notFollowReson: this.form.NotFollowReson,
CommonUploadRecordId: this.form.CommonUploadRecordId
}
addOrUpdateTrialSiteSurvey(param).then(res => {
this.btnLoading = false
if (res.IsSuccess && !isAutoCommit) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
resolve(true)
}).catch(() => {
this.btnLoading = false
reject(false)
})
return resolve(true)
}
const valid = await this.$refs['researchBSForm'].validate()
if (valid) {
this.loading = true
@ -205,11 +473,20 @@ export default {
phone: this.form.Phone,
email: this.form.Email,
averageEngravingCycle: this.form.AverageEngravingCycle,
IsRoutineMRIPDEE: this.form.IsRoutineMRIPDEE,
MRIPDFFScanTime: this.form.MRIPDFFScanTime,
MRIPDFFLeadTime: this.form.MRIPDFFLeadTime,
MRIPDFFOther: this.form.MRIPDFFOther,
IsAuthorizeRadiologistsParticipate: this.form.IsAuthorizeRadiologistsParticipate,
AssignFixedTechnologists: this.form.AssignFixedTechnologists,
ISStrictManualBurnFlag: this.form.ISStrictManualBurnFlag,
NotStrictManualBurnFlagReason: this.form.NotStrictManualBurnFlagReason,
isConfirmImagingTechnologist: this.form.IsConfirmImagingTechnologist,
notConfirmReson: this.form.NotConfirmReson,
efficacyEvaluatorType: this.form.EfficacyEvaluatorType,
isFollowStudyParameters: this.form.IsFollowStudyParameters,
notFollowReson: this.form.NotFollowReson
notFollowReson: this.form.NotFollowReson,
CommonUploadRecordId: this.form.CommonUploadRecordId
}
const res = await addOrUpdateTrialSiteSurvey(param)
this.loading = false
@ -232,7 +509,7 @@ export default {
//
async initForm(trialInfo, trialSiteSurvey, notShowFieldList) {
// site
const { Result } = await getTrialSiteSelect(this.$route.query.trialId)
const { Result } = await getTrialSiteSelect(this.$route.query.trialId, { IgnoreDisable: true })
this.siteOptions = Result
this.form.Id = trialSiteSurvey.Id
this.form.Sponsor = trialInfo.Sponsor //
@ -246,7 +523,20 @@ export default {
this.form.UserName = trialSiteSurvey.UserName //
this.form.Phone = trialSiteSurvey.Phone //
this.form.Email = trialSiteSurvey.Email //
this.form.CommonUploadRecordId = trialSiteSurvey.CommonUploadRecordId
this.form.SiteSurveyFile = trialSiteSurvey.SiteSurveyFile || {}
this.form.fileStr = this.form.SiteSurveyFile.FileName
this.form.AverageEngravingCycle = trialSiteSurvey.AverageEngravingCycle
this.form.IsRoutineMRIPDEE = trialSiteSurvey.IsRoutineMRIPDEE
this.form.MRIPDFFScanTime = trialSiteSurvey.MRIPDFFScanTime
this.form.MRIPDFFLeadTime = trialSiteSurvey.MRIPDFFLeadTime
this.form.MRIPDFFOther = trialSiteSurvey.MRIPDFFOther
this.form.IsAuthorizeRadiologistsParticipate = trialSiteSurvey.IsAuthorizeRadiologistsParticipate
this.form.AssignFixedTechnologists = trialSiteSurvey.AssignFixedTechnologists
if (this.form.IsAuthorizeRadiologistsParticipate) this.form.IsAuthorize = 'IsAuthorizeRadiologistsParticipate'
if (this.form.AssignFixedTechnologists) this.form.IsAuthorize = 'AssignFixedTechnologists'
this.form.ISStrictManualBurnFlag = trialSiteSurvey.ISStrictManualBurnFlag
this.form.NotStrictManualBurnFlagReason = trialSiteSurvey.NotStrictManualBurnFlagReason
this.form.IsConfirmImagingTechnologist = trialSiteSurvey.IsConfirmImagingTechnologist
this.form.NotConfirmReson = trialSiteSurvey.NotConfirmReson
this.form.EfficacyEvaluatorType = trialSiteSurvey.EfficacyEvaluatorType
@ -267,39 +557,46 @@ export default {
}
</script>
<style lang="scss" scoped>
.research_bs_content{
.research_bs_content {
.basic_content{
.basic_content {
padding: 0 20px;
background: #fff;
}
.login_content{
.login_content {
padding: 5px 20px;
margin-top: 5px;
background: #fff;
::v-deep .el-form-item {
padding-bottom: 20px;
}
}
.code_content{
display:flex;
.code_content {
display: flex;
flex-direction: row;
justify-content: flex-start;
.el-input{
.el-input {
margin-right: 10px;
}
}
.form-label-width{
::v-deep .el-form-item__label{
.form-label-width {
::v-deep .el-form-item__label {
width: 140px;
}
::v-deep .el-form-item__content{
::v-deep .el-form-item__content {
margin-left: 140px;
}
}
.submit_content{
.submit_content {
margin-top: 20px;
text-align: center;
}
@ -308,10 +605,61 @@ export default {
margin-bottom: 0px;
padding-top: 5px;
border-bottom: 1px solid #f5f7fa;
.el-form-item__content{
.el-form-item__content {
color: #82848a;
}
}
}
.upload {
display: inline-block;
height: 36px;
width: 90px;
padding: 0 10px;
line-height: 23px;
position: relative;
text-decoration: none;
border-radius: 3px;
overflow: hidden;
text-align: center;
background: #428bca;
border-color: #428bca;
color: #fff;
margin-right: 5px;
.select-file {
height: 36px;
width: 90px;
position: absolute;
overflow: hidden;
left: 0;
top: 0;
opacity: 0;
font-size: 0;
}
.btn-select {
//
// width: 70px;
height: 36px;
line-height: 36px;
text-align: center;
cursor: pointer;
border-radius: 24px;
overflow: hidden;
position: absolute;
top: 0;
left: 10px;
pointer-events: none; //pointer-events:none穿
}
}
.file {
::v-deep .el-form-item__content {
display: flex;
align-items: center;
}
}
</style>

View File

@ -1,59 +1,107 @@
<template>
<div
v-loading="loading"
class="equipment_form_content"
>
<el-form
ref="equipmentForm"
:model="form"
:rules="rules"
label-position="left"
>
<div v-loading="loading" class="equipment_form_content">
<el-form ref="equipmentForm" :model="form" :rules="rules" label-position="left">
<!-- 扫描设备 -->
<el-form-item :label="$t('trials:equiptResearch:form:equipment')" prop="EquipmentTypeId">
<el-select
v-model="form.EquipmentTypeId"
style="width:100%"
>
<el-option
v-for="item of $d.SiteSurvey_ScanEquipmentType"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
<el-form-item :label="$t('trials:equiptResearch:form:equipment')" prop="EquipmentTypeEnum"
v-if="EquipmentControlFieldList.includes('EquipmentTypeEnum')">
<div style="display: flex;align-items: center;">
<el-select v-model="form.EquipmentTypeEnum" style="width:100%" @change="form.OtherEquipmentType = null">
<el-option v-for="item of $d.SiteSurvey_ScanEquipmentType" :key="item.id" :label="item.label"
:value="item.value" />
</el-select>
<el-input placeholder="" v-model="form.OtherEquipmentType" style="margin-left: 10px;"
v-if="form.EquipmentTypeEnum == '-1'" clearable>
</el-input>
</div>
</el-form-item>
<!-- 扫描参数 -->
<el-form-item v-if="isShowParameters" :label="$t('trials:equiptResearch:form:param')">
<el-form-item :label="$t('trials:equiptResearch:form:param')"
v-if="EquipmentControlFieldList.includes('Parameters')" prop="Parameters">
<el-input v-model="form.Parameters" />
</el-form-item>
<!-- 扫描仪器制造商名称 -->
<el-form-item :label="$t('trials:equiptResearch:form:manufacturer')">
<el-input v-model="form.ManufacturerName" />
<el-form-item :label="$t('trials:equiptResearch:form:manufacturer')"
v-if="EquipmentControlFieldList.includes('ManufacturerType')" prop="ManufacturerType">
<div style="display: flex;align-items: center;">
<el-select v-model="form.ManufacturerType" style="width:100%" @change="form.ManufacturerName = null">
<el-option v-for="item of $d.ManufacturerType" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
<el-input placeholder="" v-model="form.ManufacturerName" style="margin-left: 10px;"
v-if="form.ManufacturerType == '-1'" clearable>
</el-input>
</div>
</el-form-item>
<!-- 扫描仪型号 -->
<el-form-item :label="$t('trials:equiptResearch:form:model')">
<el-form-item :label="$t('trials:equiptResearch:form:model')"
v-if="EquipmentControlFieldList.includes('ScannerType')" prop="ScannerType">
<el-input v-model="form.ScannerType" />
</el-form-item>
<!-- 磁场强度 -->
<el-form-item :label="$t('trials:equiptResearch:form:MagneticFieldStrengthType')"
v-if="EquipmentControlFieldList.includes('MagneticFieldStrengthType')" prop="MagneticFieldStrengthType">
<el-select v-model="form.MagneticFieldStrengthType" style="width:100%">
<el-option v-for="item of $d.MagneticFieldStrengthType" :key="item.id" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 体部线圈通道数 -->
<el-form-item :label="$t('trials:equiptResearch:form:BodyCoilChannelCount')"
v-if="EquipmentControlFieldList.includes('BodyCoilChannelCount')" prop="BodyCoilChannelCount">
<el-select v-model="form.BodyCoilChannelCount" style="width:100%">
<el-option v-for="item of $d.BodyCoilChannelCount" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 是否具备专用的PDFF脂肪定量序列CSE-MRI序列 -->
<el-form-item :label="$t('trials:equiptResearch:form:HasDedicatedPdfFatQuantificationSequence')"
v-if="EquipmentControlFieldList.includes('HasDedicatedPdfFatQuantificationSequence')"
prop="HasDedicatedPdfFatQuantificationSequence">
<el-select v-model="form.HasDedicatedPdfFatQuantificationSequence" style="width:100%"
@change="form.PdfFatQuantificationSequenceType = null, form.OtherSequenceSpecification = null">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- PDFF脂肪定量序列 -->
<el-form-item :label="$t('trials:equiptResearch:form:PdfFatQuantificationSequenceType')"
prop="PdfFatQuantificationSequenceType"
v-if="form.HasDedicatedPdfFatQuantificationSequence && EquipmentControlFieldList.includes('PdfFatQuantificationSequenceType')">
<div style="display: flex;align-items: center;">
<el-select v-model="form.PdfFatQuantificationSequenceType" style="width:100%"
@change="form.OtherSequenceSpecification = null">
<el-option v-for="item of $d.PdfFatQuantificationSequenceType" :key="item.id" :label="item.label"
:value="item.value" />
</el-select>
<el-input placeholder="" v-model="form.OtherSequenceSpecification" style="margin-left: 10px;"
v-if="form.PdfFatQuantificationSequenceType == '-1'" clearable>
</el-input>
</div>
</el-form-item>
<!-- 是否包含 T2/R2 校正用于铁沉积校正 -->
<el-form-item :label="$t('trials:equiptResearch:form:HasT2R2Correction')" prop="HasT2R2Correction"
v-if="EquipmentControlFieldList.includes('HasT2R2Correction')">
<el-select v-model="form.HasT2R2Correction" style="width:100%">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 是否可完整导出 PDFF 参数图及全部原始 DICOM 数据 -->
<el-form-item :label="$t('trials:equiptResearch:form:CanFullyExportPdfParameterMapsAndRawDicom')"
prop="CanFullyExportPdfParameterMapsAndRawDicom"
v-if="EquipmentControlFieldList.includes('CanFullyExportPdfParameterMapsAndRawDicom')">
<el-select v-model="form.CanFullyExportPdfParameterMapsAndRawDicom" style="width:100%">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 备注 -->
<el-form-item :label="$t('trials:equiptResearch:form:remark')">
<el-form-item :label="$t('trials:equiptResearch:form:remark')" prop="Note"
v-if="EquipmentControlFieldList.includes('Note')">
<el-input v-model="form.Note" />
</el-form-item>
<div style="text-align: center;padding:20px 0px;">
<!-- 取消 -->
<el-button
size="large"
type="primary"
@click="handleCancel"
>
<el-button size="large" type="primary" @click="handleCancel">
{{ $t("common:button:cancel") }}
</el-button>
<!-- 保存 -->
<el-button
size="large"
type="primary"
@click="handleSave"
>
<el-button size="large" type="primary" @click="handleSave">
{{ $t("common:button:save") }}
</el-button>
</div>
@ -76,29 +124,96 @@ export default {
type: String,
default: ''
},
isShowParameters: {
type: Boolean,
default: false
EquipmentControlFieldList: {
type: Array,
default: () => {
return []
}
}
},
data() {
return {
form: {
Id: '',
EquipmentTypeId: '',
Parameters: '',
ManufacturerName: '',
ScannerType: '',
Note: '',
EquipmentTypeEnum: null,
OtherEquipmentType: null,
Parameters: null,
Note: null,
ManufacturerType: null,
ManufacturerName: null,
ScannerType: null,
MagneticFieldStrengthType: null,
BodyCoilChannelCount: null,
HasDedicatedPdfFatQuantificationSequence: null,
PdfFatQuantificationSequenceType: null,
OtherSequenceSpecification: null,
HasT2R2Correction: null,
CanFullyExportPdfParameterMapsAndRawDicom: null,
TrialSiteSurveyId: ''
},
rules: {
EquipmentTypeId: [
EquipmentTypeEnum: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (this.form.EquipmentTypeEnum === -1 && !this.form.OtherEquipmentType) {
callback(this.$t('common:ruleMessage:specify'));
} else {
callback()
}
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
},
],
Parameters: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
],
// Note: [
// { required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
// ],
ManufacturerType: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (this.form.ManufacturerType === -1 && !this.form.ManufacturerName) {
callback(this.$t('common:ruleMessage:specify'));
} else {
callback()
}
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
},
],
ScannerType: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
],
MagneticFieldStrengthType: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
]
],
BodyCoilChannelCount: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
HasDedicatedPdfFatQuantificationSequence: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
PdfFatQuantificationSequenceType: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (this.form.PdfFatQuantificationSequenceType === -1 && !this.form.OtherSequenceSpecification) {
callback(this.$t('common:ruleMessage:specify'));
} else {
callback()
}
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
},
],
HasT2R2Correction: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
CanFullyExportPdfParameterMapsAndRawDicom: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
},
loading: false,
dictionaryList: {}
}
},
mounted() {
@ -154,13 +269,15 @@ export default {
}
</script>
<style lang="scss" scoped>
.equipment_form_content{
.equipment_form_content {
padding: 0 10px;
::v-deep .el-form-item {
margin-bottom: 0px;
padding: 5px 0 20px 0;
border-bottom: 1px solid #f5f7fa;
.el-form-item__content{
.el-form-item__content {
color: #82848a;
}
}

View File

@ -13,18 +13,22 @@
<i class="el-icon-receiving" />
</div>
<div class="p_info">
<div
class="p_info_basic"
:style="{maxWidth:w+'px','white-space': 'nowrap',
overflow: 'hidden',
'text-overflow': 'ellipsis'}"
>
<div class="p_text">{{ item.EquipmentType }}</div>
<div v-if="isShowParameters" class="p_text" style="margin-left:10px">{{ item.Parameters }}</div>
<div class="p_text" style="margin-left:10px">{{ item.ManufacturerName }}</div>
<div class="p_info_basic" :style="{
maxWidth: w + 'px', 'white-space': 'nowrap',
overflow: 'hidden',
'text-overflow': 'ellipsis'
}">
<div class="p_text" v-if="EquipmentControlFieldList.includes('EquipmentTypeEnum')">
{{ item.OtherEquipmentType ? item.OtherEquipmentType :
$fd('SiteSurvey_ScanEquipmentType', item.EquipmentTypeEnum) }}</div>
<div v-if="EquipmentControlFieldList.includes('Parameters')" class="p_text" style="margin-left:10px">{{
item.Parameters }}</div>
<div class="p_text" style="margin-left:10px" v-if="EquipmentControlFieldList.includes('ManufacturerType')">{{
item.ManufacturerName ? item.ManufacturerName : $fd('ManufacturerType',
item.ManufacturerType) }}</div>
</div>
<div class="p_text">{{ item.ScannerType }}</div>
<div class="p_text">{{ item.Note }}</div>
<div class="p_text" v-if="EquipmentControlFieldList.includes('ScannerType')">{{ item.ScannerType }}</div>
<div class="p_text" v-if="EquipmentControlFieldList.includes('Note')">{{ item.Note }}</div>
</div>
<div v-if="state === 0 && userTypeEnumInt === 0 && !isHistory" class="p_func">
<el-button type="text" @click="handleEdit(item)">
@ -36,19 +40,9 @@
</div>
</div>
<!-- 添加/编辑设备信息 -->
<el-drawer
:title="title"
:visible.sync="formVisible"
direction="btt"
size="70%"
>
<EquipmentForm
v-if="formVisible"
:equipment-info="equipmentInfo"
:is-show-parameters="isShowParameters"
@getList="getList"
@close="close"
/>
<el-drawer :title="title" :visible.sync="formVisible" direction="btt" size="70%">
<EquipmentForm v-if="formVisible" :equipment-info="equipmentInfo"
:EquipmentControlFieldList="EquipmentControlFieldList" @getList="getList" @close="close" />
</el-drawer>
</div>
</template>
@ -81,7 +75,7 @@ export default {
state: null,
trialSiteSurveyId: '',
trialId: '',
isShowParameters: false
EquipmentControlFieldList: []
}
},
mounted() {
@ -142,8 +136,11 @@ export default {
console.log(e)
}
},
initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, isShowParameters) {
this.isShowParameters = isShowParameters
initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, EquipmentControlFieldList) {
this.EquipmentControlFieldList = []
EquipmentControlFieldList.forEach(item => {
this.EquipmentControlFieldList.push(item.FiledName)
})
this.list = TrialSiteEquipmentSurveyList
this.state = trialSiteSurvey.State
this.$forceUpdate()
@ -157,63 +154,70 @@ export default {
}
</script>
<style lang="scss" scoped>
.equipments_content{
.equipments_content {
background: #fff;
padding: 10px;
.title{
.title {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.equipment_info{
.equipment_info {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
border-top: 1px solid #f5f7fa;
.p_icon{
.p_icon {
width: 70px;
font-size: 25px;
font-weight: bold;
text-align: center;
}
.p_info{
.p_info {
position: relative;
flex:1;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-evenly;
// border-right: 1px solid #f5f7fa;
font-size: 13px;
}
.p_info:after {
content: '';
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 0;
height: 50px;
width: 1px;
background-color: #f5f7fa;
content: '';
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 0;
height: 50px;
width: 1px;
background-color: #f5f7fa;
}
.p_info_basic{
.p_info_basic {
display: flex;
flex-direction: row;
align-items: center;
}
.p_text{
.p_text {
line-height: 25px;
color: #82848a;
}
.p_func{
.p_func {
width: 80px;
padding: 0 10px;
text-align: center;
}
}
}
</style>

View File

@ -4,30 +4,22 @@
<div class="d_content">
<!-- 项目基本信息 -->
<BasicInfo ref="basicInfo" />
<BasicInfo ref="basicInfo" :IsOnlyUploadFile="IsOnlyUploadFile" :IsSupportUploadFile="IsSupportUploadFile" />
<!-- 历史人员 -->
<HistoricalParticipants ref="historicalParticipant" class="mt5" />
<!-- 新增人员 -->
<Participants ref="participants" class="mt5" />
<!-- 设备调研 -->
<Equipments ref="equipments" class="mt5" />
<Equipments ref="equipments" class="mt5" v-if="!siteSurveyNoteInfo.IsCloseEquipmentSurvey" />
<!-- 备注 -->
<Notes ref="notes" class="mt5" />
</div>
<div class="d_footer">
<div class="d_func_row">
<div
v-if="state === 0 && userTypeEnumInt === 0"
class="d_func"
@click="handleSave"
>
<div v-if="state === 0 && userTypeEnumInt === 0" class="d_func" @click="handleSave">
{{ $t('common:button:save') }}
</div>
<div
v-if="(state === 0 && userTypeEnumInt === 0)"
class="d_func"
@click="handleSubmit('submit')"
>
<div v-if="(state === 0 && userTypeEnumInt === 0)" class="d_func" @click="handleSubmit('submit')">
{{ $t('trials:researchForm:button:submit') }}
</div>
</div>
@ -58,7 +50,9 @@ export default {
userTypeEnumInt: 0,
rejectVisible: false,
rejectForm: { reason: '' },
siteSurveyNoteInfo: null
siteSurveyNoteInfo: {},
IsSupportUploadFile: false,
IsOnlyUploadFile: false
}
},
mounted() {
@ -78,6 +72,9 @@ export default {
if (res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0) {
this.siteSurveyNoteInfo = res.Result.SiteSurveyFiledConfig.ModifyFiledList.find(i => i.NeedModifyFiled === 'SiteSurveyNote')
}
this.IsSupportUploadFile = res.Result.SiteSurveyFiledConfig.IsSupportUploadFile
this.IsOnlyUploadFile = res.Result.SiteSurveyFiledConfig.IsOnlyUploadFile
this.siteSurveyNoteInfo.IsCloseEquipmentSurvey = res.Result.SiteSurveyFiledConfig.IsCloseEquipmentSurvey
this.state = res.Result.TrialSiteSurvey.State
this.$refs['basicInfo'].initForm(res.Result.TrialInfo, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig ? res.Result.SiteSurveyFiledConfig.NotShowFieldList : null)
var historicalArr = []
@ -92,7 +89,7 @@ export default {
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
this.$refs['participants'].initList(newArr, res.Result.TrialSiteSurvey)
this.$refs['equipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, !(res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0))
this.$refs['equipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig.EquipmentControlFieldList)
this.$refs['notes'].initPage(this.siteSurveyNoteInfo)
}
this.loading = false
@ -129,7 +126,8 @@ export default {
this.userTypeEnumInt === 0 ? this.$t('trials:researchForm:message:submitWarning') : this.$t('trials:researchForm:message:submitWarning2'),
{
type: 'warning',
distinguishCancelAndClose: true
distinguishCancelAndClose: true,
customClass: "mobile_confirm"
}
)
if (confirm !== 'confirm') return
@ -160,9 +158,10 @@ export default {
}
</script>
<style lang="scss" scoped>
.research_detal_wrapper{
background-color:#f5f7fa;
.d_title{
.research_detal_wrapper {
background-color: #f5f7fa;
.d_title {
margin-bottom: 5px;
line-height: 80px;
font-size: 28px;
@ -173,10 +172,11 @@ export default {
top: 0;
z-index: 10;
}
// .d_content{
// }
.d_footer{
.d_footer {
position: sticky;
bottom: 0;
z-index: 10;
@ -185,12 +185,14 @@ export default {
text-align: center;
// padding: 20px;
}
.d_func_row{
.d_func_row {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.d_func{
.d_func {
flex: 1;
margin: 20px;
color: #fff;
@ -207,15 +209,16 @@ export default {
line-height: 1;
white-space: nowrap;
cursor: pointer;
&:hover{
&:hover {
background: #68a2d5;
border-color: #68a2d5;
color: #fff;
}
}
.mt5{
.mt5 {
margin-top: 5px
}
}
</style>

View File

@ -1,131 +1,88 @@
<template>
<div class="research_login_m_content">
<div class="title">{{ $t('trials:researchForm:title:question') }}</div>
<el-form
ref="loginForm"
v-loading="loading"
label-position="left"
label-width="120px"
:model="form"
:rules="rules"
>
<div class="basic_content">
<!-- 项目编号 -->
<el-form-item
:label="$t('trials:researchForm:form:trialId')"
>
<span>{{ form.TrialCode }}</span>
</el-form-item>
<!-- 试验方案号 -->
<el-form-item
:label="$t('trials:researchForm:form:researchNo')"
>
<span>{{ form.ResearchProgramNo }}</span>
</el-form-item>
<!-- 试验名称 -->
<el-form-item
:label="$t('trials:researchForm:form:researchName')"
>
<span>{{ form.ExperimentName }}</span>
</el-form-item>
<!-- 适应症类型 -->
<el-form-item
:label="$t('trials:researchForm:form:decleareType')"
>
<span>{{ form.IndicationType }}</span>
</el-form-item>
<div v-if="IsExpired"></div>
<div v-else-if="verify" class="LinkVerificationCode_content">
<el-form ref="codeForm" v-loading="loading" label-position="top" label-width="120px" :model="codeForm"
:rules="code_rules">
<div class="basic_content">
<!-- 项目编号 -->
<el-form-item :label="$t('trials:researchForm:message:LinkVerificationCode')" prop="LinkVerificationCode">
<el-input v-model="codeForm.LinkVerificationCode" autocomplete="new-password" />
</el-form-item>
</div>
</el-form>
<div class="submit_content">
<el-button size="large" type="primary" @click="getLinkVerificationCodeIsEffective">
{{ $t('common:button:submit') }}
</el-button>
</div>
<div class="login_content">
<el-form-item
:label="$t('trials:researchForm:form:siteName')"
prop="TrialSiteId"
>
<el-select
v-model="form.TrialSiteId"
filterable
style="width:100%;"
@change="handleSiteChange"
>
<el-option
v-for="(item,index) of siteOptions"
:key="index"
:label="item.TrialSiteAliasName"
:value="item.TrialSiteId"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.TrialSiteId && isHaveSiteSurveyRecord"
label=""
style="text-align:right;"
>
<!-- 更新调研表 -->
<el-link
v-if="!form.IsUpdate"
type="primary"
@click="form.IsUpdate = true"
>
{{ $t('trials:researchForm:button:updateQsForm') }}
</el-link>
<!-- 取消更新调研表 -->
<el-link
v-else
type="primary"
@click="form.IsUpdate = false;form.ReplaceUserEmailOrPhone=''"
>
{{ $t('trials:researchForm:button:cancelUpdateQsForm') }}
</el-link>
</el-form-item>
</div>
<template v-else>
<div class="title">{{ $t('trials:researchForm:title:question') }}</div>
<el-form ref="loginForm" v-loading="loading" label-position="left" label-width="120px" :model="form"
:rules="rules">
<div class="basic_content">
<!-- 项目编号 -->
<el-form-item :label="$t('trials:researchForm:form:trialId')">
<span>{{ form.TrialCode }}</span>
</el-form-item>
<!-- 试验方案号 -->
<el-form-item :label="$t('trials:researchForm:form:researchNo')">
<span>{{ form.ResearchProgramNo }}</span>
</el-form-item>
<!-- 试验名称 -->
<el-form-item :label="$t('trials:researchForm:form:researchName')">
<span>{{ form.ExperimentName }}</span>
</el-form-item>
<!-- 适应症类型 -->
<el-form-item :label="$t('trials:researchForm:form:decleareType')">
<span>{{ form.IndicationType }}</span>
</el-form-item>
</div>
<div class="login_content">
<el-form-item :label="$t('trials:researchForm:form:siteName')" prop="TrialSiteId">
<el-select v-model="form.TrialSiteId" filterable style="width:100%;" @change="handleSiteChange">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteAliasName"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<el-form-item v-if="form.TrialSiteId && isHaveSiteSurveyRecord" label="" style="text-align:right;">
<!-- 更新调研表 -->
<el-link v-if="!form.IsUpdate" type="primary" @click="form.IsUpdate = true">
{{ $t('trials:researchForm:button:updateQsForm') }}
</el-link>
<!-- 取消更新调研表 -->
<el-link v-else type="primary" @click="form.IsUpdate = false; form.ReplaceUserEmailOrPhone = ''">
{{ $t('trials:researchForm:button:cancelUpdateQsForm') }}
</el-link>
</el-form-item>
<!-- 原调研表填写人邮箱 -->
<el-form-item
v-if="form.IsUpdate"
:label="$t('trials:researchForm:form:originalEmail')"
prop="ReplaceUserEmailOrPhone"
>
<el-input
v-model="form.ReplaceUserEmailOrPhone"
autocomplete="new-password"
/>
</el-form-item>
<!-- 联系邮箱 -->
<el-form-item
:label="$t('trials:researchForm:form:contactorEmail')"
prop="EmailOrPhone"
>
<el-input
v-model="form.EmailOrPhone"
autocomplete="new-password"
/>
</el-form-item>
<el-form-item
:label="$t('trials:researchForm:form:verifyCode')"
>
<div class="code_content">
<el-input
v-model="form.VerificationCode"
autocomplete="new-password"
/>
<el-button
type="primary"
:disabled="sendDisabled || !form.EmailOrPhone || count > 0"
@click="handleSendCode"
>
{{ this.$t('trials:researchForm:button:send') }} {{ sendTitle ? `${sendTitle}` : null }}
<!-- 原调研表填写人邮箱 -->
<el-form-item v-if="form.IsUpdate" :label="$t('trials:researchForm:form:originalEmail')"
prop="ReplaceUserEmailOrPhone">
<el-input v-model="form.ReplaceUserEmailOrPhone" autocomplete="new-password" />
</el-form-item>
<!-- 联系邮箱 -->
<el-form-item :label="$t('trials:researchForm:form:contactorEmail')" prop="EmailOrPhone">
<el-input v-model="form.EmailOrPhone" autocomplete="new-password" />
</el-form-item>
<el-form-item :label="$t('trials:researchForm:form:verifyCode')">
<div class="code_content">
<el-input v-model="form.VerificationCode" autocomplete="new-password" />
<el-button type="primary" :disabled="sendDisabled || !form.EmailOrPhone || count > 0"
@click="handleSendCode">
{{ this.$t('trials:researchForm:button:send') }} {{ sendTitle ? `${sendTitle}` : null }}
</el-button>
</div>
</el-form-item>
<div class="submit_content">
<el-button size="large" type="primary" @click="onSubmit">
{{ $t('common:button:submit') }}
</el-button>
</div>
</el-form-item>
<div class="submit_content">
<el-button
size="large"
type="primary"
@click="onSubmit"
>
{{ $t('common:button:submit') }}
</el-button>
</div>
</div>
</el-form>
</el-form>
</template>
</div>
</template>
<script>
@ -133,6 +90,10 @@ import { sendVerifyCode, verifySendCode, getTrialSurveyInitInfo } from '@/api/re
import { getUserMenuTree, getUserPermissions } from '@/api/user'
import store from '@/store'
import { mapGetters, mapMutations } from 'vuex'
import {
getLinkLinkExpirationTime,
getLinkVerificationCodeIsEffective
} from '@/api/trials'
export default {
name: 'ResearchMobileLogin',
data() {
@ -185,6 +146,16 @@ export default {
}
return {
trialId: '',
IsExpired: true,
verify: true,
codeForm: {
LinkVerificationCode: null,
},
code_rules: {
LinkVerificationCode: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: ['blur'] }
],
},
form: {
Sponsor: null, //
ResearchProgramNo: null, //
@ -239,16 +210,119 @@ export default {
])
},
mounted() {
this.$i18n.locale = this.$route.query.lang
this.setLanguage(this.$route.query.lang)
this.$updateDictionary()
if (this.$route.query.trialId) {
this.trialId = this.$route.query.trialId
this.initPage()
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
this.$updateDictionary()
this.getLinkTimeIsExpired()
},
methods: {
...mapMutations({ setLanguage: 'lang/setLanguage' }),
async getLinkTimeIsExpired() {
try {
let data = {
TrialId: this.$route.query.trialId,
}
let res = await getLinkLinkExpirationTime(data)
if (res.IsSuccess) {
if (res.Result.IsExpired) {
return this.$router.replace('/link_expired')
// return this.$confirm(this.$t("trials:researchForm:confirm:linkIsExpired"), '', {
// type: 'warning'
// })
// return this.IsExpired = true
}
// this.customPrompt()
this.IsExpired = false
}
} catch (err) {
console.log(err)
}
},
async getLinkVerificationCodeIsEffective() {
try {
let validate = await this.$refs.codeForm.validate()
if (!validate) return false
let data = {
TrialId: this.$route.query.trialId,
LinkVerificationCode: this.codeForm.LinkVerificationCode
}
let res = await getLinkVerificationCodeIsEffective(data)
if (res.IsSuccess) {
if (!res.Result.IsEffective) {
return false
}
this.verify = false
if (this.$route.query.trialId) {
this.trialId = this.$route.query.trialId
this.initPage()
}
if (this.$route.query.isUpload) {
this.isUpload = true
this.form.IsUpdate = true
let { email, oldEMail, trialSiteId } = this.$route.query
if (trialSiteId) this.form.TrialSiteId = trialSiteId
if (oldEMail) this.form.ReplaceUserEmailOrPhone = oldEMail
if (email && email !== 'null') {
this.form.EmailOrPhone = email
} else {
this.form.EmailOrPhone = oldEMail
}
if ((email && email !== 'null') || (oldEMail && oldEMail !== 'null')) {
this.isNeedUpload = false
}
}
}
return true
} catch (err) {
return false
console.log(err)
}
},
async customPrompt(name) {
try {
const that = this
//
let message = this.$t('trials:researchForm:message:LinkVerificationCode')
const { value } = await this.$prompt(message, '', {
showClose: false,
cancelButtonText: this.$t('common:button:cancel'),
confirmButtonText: this.$t('trials:researchForm:button:saveLinkCode'),
showCancelButton: false,
closeOnClickModal: false,
closeOnPressEscape: false,
inputValue: name,
beforeClose: async (action, instance, done) => {
if (action === 'confirm') {
const value = instance.inputValue
if (!value) {
that.$message.error(this.$t('trials:researchForm:error:noValue'))
} else {
let flag = await this.getLinkVerificationCodeIsEffective(value)
if (!flag) {
that.$message.error(this.$t('trials:researchForm:confirm:isEffective'))
} else {
done()
}
}
} else {
done()
}
}
})
this.getLinkVerificationCodeIsEffective(value)
} catch (err) {
console.log(err)
return null
}
},
//
async initPage() {
try {
@ -292,8 +366,18 @@ export default {
if (res.IsSuccess) {
zzSessionStorage.clear()
this.$i18n.locale = this.$route.query.lang
this.setLanguage(this.$route.query.lang)
// this.$i18n.locale = this.$route.query.lang
// this.setLanguage(this.$route.query.lang)
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
store.dispatch('user/setToken', res.Result.Token)
zzSessionStorage.setItem('TokenKey', res.Result.Token)
var permissions = await getUserPermissions()
@ -363,9 +447,10 @@ export default {
}
</script>
<style lang="scss" scoped>
.research_login_m_content{
background-color:#f5f7fa;
.title{
.research_login_m_content {
background-color: #f5f7fa;
.title {
margin-bottom: 5px;
line-height: 80px;
font-size: 28px;
@ -373,27 +458,33 @@ export default {
text-align: center;
background: #fff;
}
.basic_content{
.basic_content {
padding: 0 20px;
background: #fff;
}
.login_content{
.login_content {
padding: 5px 20px;
margin-top: 5px;
background: #fff;
::v-deep .el-form-item {
padding-bottom: 20px;
}
}
.code_content{
display:flex;
.code_content {
display: flex;
flex-direction: row;
justify-content: flex-start;
.el-input{
.el-input {
margin-right: 10px;
}
}
.submit_content{
.submit_content {
margin-top: 20px;
text-align: center;
}
@ -402,11 +493,20 @@ export default {
margin-bottom: 0px;
padding-top: 5px;
border-bottom: 1px solid #f5f7fa;
.el-form-item__content{
.el-form-item__content {
color: #82848a;
}
}
}
.LinkVerificationCode_content {
position: fixed;
top: 40%;
bottom: 40%;
right: 0;
left: 0;
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<el-form v-if="isShow" ref="researchBSForm" size="small" :model="form" :rules="rules" style="width:80%"
label-position="left">
label-position="left" v-loading="loading">
<!-- 项目编号 -->
<el-form-item :label="$t('trials:researchForm:form:trialId')">
<el-input v-model="form.TrialCode" disabled />
@ -20,7 +20,7 @@
<!-- 中心名称 -->
<el-form-item :label="$t('trials:researchForm:form:siteName')" prop="TrialSiteId">
<el-select v-model="form.TrialSiteId" filterable style="width:100%;"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" @change="handleSiteChange">
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" @change="handleSiteChange">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteAliasName"
:value="item.TrialSiteId" />
</el-select>
@ -31,28 +31,47 @@
</el-form-item>
<!-- 联系人 -->
<el-form-item :label="$t('trials:researchForm:form:contactor')" prop="UserName">
<el-input v-model="form.UserName" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
<el-input v-model="form.UserName" :disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" />
</el-form-item>
<!-- 联系电话 -->
<el-form-item :label="$t('trials:researchForm:form:contactorPhone')" prop="Phone">
<el-input v-model="form.Phone" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
<el-input v-model="form.Phone" :disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" />
</el-form-item>
<!-- 联系邮箱 -->
<el-form-item :label="$t('trials:researchForm:form:contactorEmail')" prop="Email">
<el-input v-model="form.Email" disabled />
</el-form-item>
<el-form-item :label="$t('trials:researchForm:form:CommonUploadRecordId')
" v-if="IsSupportUploadFile" class="file">
<el-input v-model="form.SiteSurveyFile.FileName" type="textarea" :autosize="{ minRows: 1, maxRows: 3 }"
style="width: 30%;margin-right: 5px;" disabled />
<div class="upload" v-if="!(!(state === 0 && userTypeEnumInt === 0) || isHistory)">
<input accept=".pdf,.docx,.doc" type="file" name="uploadFolder" class="select-file" title=""
@change="beginScanFiles($event)" />
<div class="btn-select">
{{ $t('dictionary:template:basicData:button:selectFile') }}
</div>
</div>
<el-button type="primary" @click="viewManualFile"
:disabled="!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path">{{
$t('trials:researchForm:form:preview') }}</el-button>
<el-button type="primary" @click="downLoad"
:disabled="!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path">{{
$t('trials:researchForm:form:download') }}</el-button>
</el-form-item>
<!-- <el-divider /> -->
<!-- 平均刻盘周期 -->
<el-form-item v-if="!notShowFieldList.includes('AverageEngravingCycle')"
:label="$t('trials:researchForm:form:engravingCycle')">
<el-input-number v-model="form.AverageEngravingCycle"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" controls-position="right" :min="0" />
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" controls-position="right"
:min="0" />
</el-form-item>
<!-- MRI-PDFF 是否为本中心该适应症的常规诊疗检查项目 -->
<el-form-item v-if="!notShowFieldList.includes('IsRoutineMRIPDEE')"
:label="$t('trials:researchForm:form:IsRoutineMRIPDEE')" prop="IsRoutineMRIPDEE">
<el-radio-group v-model="form.IsRoutineMRIPDEE" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio-group v-model="form.IsRoutineMRIPDEE"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM">
<el-radio v-for="item of $d.YesOrNo" :key="`IsRoutineMRIPDEE${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
@ -65,27 +84,29 @@
<!-- 单次检查时长分钟 -->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFScanTime')"
:label="$t('trials:researchForm:form:MRIPDFFScanTime')">
<el-input-number v-model="form.MRIPDFFScanTime" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
controls-position="right" :min="0" />
<el-input-number v-model="form.MRIPDFFScanTime"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" controls-position="right"
:min="0" />
</el-form-item>
<!-- 平均预约等待时长 -->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFLeadTime')"
:label="$t('trials:researchForm:form:MRIPDFFLeadTime')">
<el-input-number v-model="form.MRIPDFFLeadTime" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
controls-position="right" :min="0" />
<el-input-number v-model="form.MRIPDFFLeadTime"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" controls-position="right"
:min="0" />
</el-form-item>
<!-- 特殊情况备注-->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFOther')"
:label="$t('trials:researchForm:form:MRIPDFFOther')">
<el-input v-model="form.MRIPDFFOther" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" />
</el-form-item>
<!-- 如已选择研究者评估项目是否会授权影像科老师参与本试验如不单独授权是否可在试验中保持 1-2 名固定技师操作-->
<el-form-item
v-if="!notShowFieldList.includes('IsAuthorizeRadiologistsParticipate') || !notShowFieldList.includes('AssignFixedTechnologists')"
:label="$t('trials:researchForm:form:IsAuthorize')" prop="IsAuthorize">
<el-radio-group v-model="form.IsAuthorize" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
@input="handleIsAuthorizeInput">
<el-radio-group v-model="form.IsAuthorize"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" @input="handleIsAuthorizeInput">
<el-radio label="IsAuthorizeRadiologistsParticipate">{{
$t('trials:researchForm:form:IsAuthorizeRadiologistsParticipate') }}</el-radio>
<el-radio label="AssignFixedTechnologists">{{
@ -96,7 +117,7 @@
<el-form-item v-if="!notShowFieldList.includes('IsConfirmImagingTechnologist')"
:label="$t('trials:researchForm:form:isQualified')" prop="IsConfirmImagingTechnologist">
<el-radio-group v-model="form.IsConfirmImagingTechnologist"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM">
<el-radio v-for="item of $d.YesOrNo" :key="`IsConfirmImagingTechnologist${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
@ -105,13 +126,13 @@
<el-form-item v-if="!notShowFieldList.includes('NotConfirmReson') && form.IsConfirmImagingTechnologist === false"
:label="$t('trials:researchForm:form:notQualifiedReason')" prop="NotConfirmReson">
<el-input v-model="form.NotConfirmReson" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" />
</el-form-item>
<!-- 研究单位疗效评估人员类型 -->
<el-form-item v-if="!notShowFieldList.includes('EfficacyEvaluatorType')"
:label="$t('trials:researchForm:form:staffType')">
<el-radio-group v-model="form.EfficacyEvaluatorType"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM">
<el-radio v-for="item of $d.EfficacyEvaluatorType" :key="`EfficacyEvaluatorType${item.value}`"
:label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
@ -120,7 +141,7 @@
<el-form-item v-if="!notShowFieldList.includes('IsFollowStudyParameters')" prop="IsFollowStudyParameters">
<span slot="label" v-html="$t('trials:researchForm:form:isFollowStudyParam')" />
<el-radio-group v-model="form.IsFollowStudyParameters"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" style="margin-right: 10px;">
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" style="margin-right: 10px;">
<el-radio v-for="item of $d.YesOrNo" :key="`IsFollowStudyParameters${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
@ -133,13 +154,13 @@
prop="NotFollowReson">
<span slot="label" v-html="$t('trials:researchForm:form:notFollowStudyParam')" />
<el-input v-model="form.NotFollowReson" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" />
</el-form-item>
<!-- 是否严格按照影像手册参数完成刻盘 -->
<el-form-item v-if="!notShowFieldList.includes('ISStrictManualBurnFlag')" prop="ISStrictManualBurnFlag">
<span slot="label" v-html="$t('trials:researchForm:form:ISStrictManualBurnFlag')" />
<el-radio-group v-model="form.ISStrictManualBurnFlag"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" style="margin-right: 10px;">
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" style="margin-right: 10px;">
<el-radio v-for="item of $d.YesOrNo" :key="`ISStrictManualBurnFlag${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
@ -149,7 +170,7 @@
prop="NotStrictManualBurnFlagReason">
<span slot="label" v-html="$t('trials:researchForm:form:NotStrictManualBurnFlagReason')" />
<el-input v-model="form.NotStrictManualBurnFlagReason" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" />
</el-form-item>
<el-form-item>
<!-- 保存 -->
@ -163,9 +184,10 @@
</el-form>
</template>
<script>
import { getTrialSiteSelect, getTrialDocumentList } from '@/api/trials'
import { getTrialSiteSelect, getTrialDocumentList, addOrUpdateCommonUploadRecord } from '@/api/trials'
import { addOrUpdateTrialSiteSurvey } from '@/api/research'
import attachmentPreview from '@/views/dictionary/attachment/components/SignatureTemplate/attachmentPreview'
import { downLoadFile } from '@/utils/stream.js'
export default {
name: 'QuestionForm',
components: { attachmentPreview },
@ -173,7 +195,19 @@ export default {
isHistory: {
type: Boolean,
default: false
}
},
IsOnlyUploadFile: {
type: Boolean,
default: false
},
IsSupportUploadFile: {
type: Boolean,
default: false
},
isPM: {
type: Boolean,
default: false
},
},
data() {
var checkPhone = (rule, value, callback) => {
@ -198,6 +232,7 @@ export default {
}
}
return {
loading: false,
form: {
Id: '',
Sponsor: '', //
@ -225,7 +260,9 @@ export default {
NotConfirmReson: '',
EfficacyEvaluatorType: '',
IsFollowStudyParameters: '',
NotFollowReson: ''
NotFollowReson: '',
CommonUploadRecordId: null,
SiteSurveyFile: []
},
rules: {
TrialSiteId: [
@ -281,11 +318,68 @@ export default {
}
},
methods: {
async beginScanFiles(e) {
try {
let file = e.target.files[0]
let name = file.name
if (!this.checkFileSuffix(name)) return this.$message.warning(this.$t("trials:researchRecord:ImageManual:message:checkFileSuffix"))
this.loading = true
file = await this.fileToBlob(file)
let scope = this
var index = name.lastIndexOf('.')
var type = name.substring(index + 1, name.length)
let res = await this.OSSclient.put(
`/${this.$route.query.trialId}/InspectionUpload/SiteSurvey/${this.form.TrialSiteId}/${scope.getGuid(Date.now() + '_' + name.split('.')[0])}.${type}`,
file
)
let data = {
Path: this.$getObjectName(res.url),
FileName: name,
FileSize: file.size,
FileType: type
}
res = await addOrUpdateCommonUploadRecord(data)
this.loading = false
if (res.IsSuccess) {
this.form.CommonUploadRecordId = res.Result
data.Id = res.Result
this.form.SiteSurveyFile = 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 && '.docx'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1 && '.doc'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1) {
return false
} else {
return true
}
},
handleIsAuthorizeInput(label) {
this.form.IsAuthorizeRadiologistsParticipate = false
this.form.AssignFixedTechnologists = false
this.form[label] = true
},
async viewManualFile() {
if (!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path) return this.$message.warning(this.$t("trials:researchForm:message:notFile"))
this.$preview({
path: this.form.SiteSurveyFile.Path,
type: this.form.SiteSurveyFile.FileType,
title: this.form.SiteSurveyFile.Path.FileName,
})
},
async downLoad() {
if (!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path) return this.$message.warning(this.$t("trials:researchForm:message:notFile"))
return await downLoadFile(
this.OSSclientConfig.basePath + this.form.SiteSurveyFile.Path,
this.form.SiteSurveyFile.FileName
)
},
async viewManual() {
try {
let data = {
@ -316,8 +410,58 @@ export default {
}
},
//
handleSave(isAutoCommit) {
handleSave(isAutoCommit, isCheck = false) {
return new Promise((resolve, reject) => {
isCheck = false
if (this.IsOnlyUploadFile && !isCheck) {
if (!this.form.UserName) {
this.$message.warning(this.$t("trials:researchForm:message:notUserName"))
return resolve(false)
}
if (!this.form.Phone) {
this.$message.warning(this.$t("trials:researchForm:message:notPhone"))
return resolve(false)
}
if (!this.form.CommonUploadRecordId) {
this.$message.warning(this.$t("trials:researchForm:message:notCommonUploadRecordId"))
return resolve(false)
}
this.btnLoading = true
const param = {
id: this.form.Id,
trialId: this.$route.query.trialId,
trialSiteId: this.form.TrialSiteId,
userName: this.form.UserName,
phone: this.form.Phone,
email: this.form.Email,
averageEngravingCycle: this.form.AverageEngravingCycle,
IsRoutineMRIPDEE: this.form.IsRoutineMRIPDEE,
MRIPDFFScanTime: this.form.MRIPDFFScanTime,
MRIPDFFLeadTime: this.form.MRIPDFFLeadTime,
MRIPDFFOther: this.form.MRIPDFFOther,
IsAuthorizeRadiologistsParticipate: this.form.IsAuthorizeRadiologistsParticipate,
AssignFixedTechnologists: this.form.AssignFixedTechnologists,
ISStrictManualBurnFlag: this.form.ISStrictManualBurnFlag,
NotStrictManualBurnFlagReason: this.form.NotStrictManualBurnFlagReason,
isConfirmImagingTechnologist: this.form.IsConfirmImagingTechnologist,
notConfirmReson: this.form.NotConfirmReson,
efficacyEvaluatorType: this.form.EfficacyEvaluatorType,
isFollowStudyParameters: this.form.IsFollowStudyParameters,
notFollowReson: this.form.NotFollowReson,
CommonUploadRecordId: this.form.CommonUploadRecordId
}
addOrUpdateTrialSiteSurvey(param).then(res => {
this.btnLoading = false
if (res.IsSuccess && !isAutoCommit) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
resolve(true)
}).catch(() => {
this.btnLoading = false
reject(false)
})
return resolve(true)
}
this.$refs['researchBSForm'].validate(valid => {
if (valid) {
this.btnLoading = true
@ -341,7 +485,8 @@ export default {
notConfirmReson: this.form.NotConfirmReson,
efficacyEvaluatorType: this.form.EfficacyEvaluatorType,
isFollowStudyParameters: this.form.IsFollowStudyParameters,
notFollowReson: this.form.NotFollowReson
notFollowReson: this.form.NotFollowReson,
CommonUploadRecordId: this.form.CommonUploadRecordId
}
addOrUpdateTrialSiteSurvey(param).then(res => {
this.btnLoading = false
@ -366,7 +511,7 @@ export default {
//
async initForm(trialInfo, trialSiteSurvey, notShowFieldList) {
// site
const { Result } = await getTrialSiteSelect(this.$route.query.trialId)
const { Result } = await getTrialSiteSelect(this.$route.query.trialId, { IgnoreDisable: true })
this.siteOptions = Result
this.form.Id = trialSiteSurvey.Id
this.form.Sponsor = trialInfo.Sponsor //
@ -380,6 +525,9 @@ export default {
this.form.UserName = trialSiteSurvey.UserName //
this.form.Phone = trialSiteSurvey.Phone //
this.form.Email = trialSiteSurvey.Email //
this.form.CommonUploadRecordId = trialSiteSurvey.CommonUploadRecordId
this.form.SiteSurveyFile = trialSiteSurvey.SiteSurveyFile || {}
this.form.fileStr = this.form.SiteSurveyFile.FileName
this.form.AverageEngravingCycle = trialSiteSurvey.AverageEngravingCycle
this.form.IsRoutineMRIPDEE = trialSiteSurvey.IsRoutineMRIPDEE
this.form.MRIPDFFScanTime = trialSiteSurvey.MRIPDFFScanTime
@ -410,3 +558,54 @@ export default {
}
}
</script>
<style lang="scss" scoped>
.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;
margin-right: 5px;
.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穿
}
}
.file {
::v-deep .el-form-item__content {
display: flex;
align-items: center;
}
}
</style>

View File

@ -132,7 +132,11 @@ export default {
default: () => {
return []
}
}
},
isPM: {
type: Boolean,
default: false
},
},
data() {
return {
@ -170,9 +174,9 @@ export default {
Parameters: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
],
Note: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
],
// Note: [
// { required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
// ],
ManufacturerType: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
{

View File

@ -2,7 +2,8 @@
<div class="equipment-wrapper">
<div class="header-wrapper">
<!-- 新增 -->
<el-button v-if="state === 0 && userTypeEnumInt === 0 && !isHistory && EquipmentControlFieldList.length > 0"
<el-button
v-if="((state === 0 && userTypeEnumInt === 0 && !isHistory) || isPM) && EquipmentControlFieldList.length > 0"
size="small" type="primary" @click="handleAdd">
{{ $t('common:button:add') }}
</el-button>
@ -82,7 +83,7 @@
<!-- 备注 -->
<el-table-column v-if="EquipmentControlFieldList.includes('Note')" min-width="120" prop="Note"
:label="$t('trials:equiptResearch:form:precautions')" show-overflow-tooltip />
<el-table-column v-if="state === 0 && userTypeEnumInt === 0 && !isHistory" fixed="right"
<el-table-column v-if="(state === 0 && userTypeEnumInt === 0 && !isHistory) || isPM" fixed="right"
:label="$t('common:action:action')" width="100">
<template slot-scope="scope">
<!-- 编辑 -->
@ -121,7 +122,11 @@ export default {
isHistory: {
type: Boolean,
default: false
}
},
isPM: {
type: Boolean,
default: false
},
},
data() {
return {
@ -186,7 +191,6 @@ export default {
EquipmentControlFieldList.forEach(item => {
this.EquipmentControlFieldList.push(item.FiledName)
})
console.log(this.EquipmentControlFieldList, 'this.EquipmentControlFieldList')
this.list = TrialSiteEquipmentSurveyList
this.state = trialSiteSurvey.State
this.$forceUpdate()

View File

@ -1,18 +1,11 @@
<template>
<div class="particiapant-wrapper">
<div class="header-wrapper" />
<el-table
v-loading="loading"
:data="list"
border
style="width: 100%"
>
<el-table-column
prop="date"
:label="$t('trials:staffResearch:form:name')"
>
<el-table v-loading="loading" :data="list" border style="width: 100%">
<el-table-column prop="date" :label="$t('trials:staffResearch:form:name')">
<template slot-scope="scope">
<el-tooltip v-if="scope.row.ErrorMessage" class="item" effect="dark" :content="scope.row.ErrorMessage" placement="bottom">
<el-tooltip v-if="scope.row.ErrorMessage" class="item" effect="dark" :content="scope.row.ErrorMessage"
placement="bottom">
<i class="el-icon-warning" style="color:red" />
</el-tooltip>
@ -29,20 +22,11 @@
</template>
</el-table-column> -->
<!-- 电话号码 -->
<el-table-column
prop="Phone"
:label="$t('trials:staffResearch:form:phone')"
/>
<el-table-column prop="Phone" :label="$t('trials:staffResearch:form:phone')" />
<!-- 邮箱 -->
<el-table-column
prop="Email"
:label="$t('trials:staffResearch:form:email')"
/>
<el-table-column prop="Email" :label="$t('trials:staffResearch:form:email')" />
<!-- 单位 -->
<el-table-column
prop="OrganizationName"
:label="$t('trials:staffResearch:form:organization')"
/>
<el-table-column prop="OrganizationName" :label="$t('trials:staffResearch:form:organization')" />
<!-- 是否生成账号 -->
<!-- <el-table-column
prop="IsGenerateAccount"
@ -53,27 +37,20 @@
</template>
</el-table-column> -->
<!-- 用户类型 -->
<el-table-column
prop="UserType"
:label="$t('trials:staffResearch:form:userType')"
/>
<el-table-column prop="UserType" :label="$t('trials:staffResearch:form:userType')" />
<!-- 原状态 -->
<el-table-column
prop="IsHistoryUserOriginDeleted"
:label="$t('trials:staffResearch:form:originalState')"
>
<el-table-column prop="IsHistoryUserOriginDeleted" :label="$t('trials:staffResearch:form:originalState')">
<template slot-scope="scope">
<el-tag v-if="scope.row.IsHistoryUserOriginDeleted" type="info">{{ $fd('IsUserExitTrial', scope.row.IsHistoryUserOriginDeleted) }}</el-tag>
<el-tag v-if="scope.row.IsHistoryUserOriginDeleted" type="info">{{ $fd('IsUserExitTrial',
scope.row.IsHistoryUserOriginDeleted) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('IsUserExitTrial', scope.row.IsHistoryUserOriginDeleted) }}</el-tag>
</template>
</el-table-column>
<!-- 更新状态 -->
<el-table-column
prop="IsHistoryUserDeleted"
:label="$t('trials:staffResearch:form:updateState')"
>
<el-table-column prop="IsHistoryUserDeleted" :label="$t('trials:staffResearch:form:updateState')">
<template slot-scope="scope">
<el-tag v-if="scope.row.IsHistoryUserDeleted" type="info">{{ $fd('IsUserExitTrial', scope.row.IsHistoryUserDeleted) }}</el-tag>
<el-tag v-if="scope.row.IsHistoryUserDeleted" type="info">{{ $fd('IsUserExitTrial',
scope.row.IsHistoryUserDeleted) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('IsUserExitTrial', scope.row.IsHistoryUserDeleted) }}</el-tag>
</template>
</el-table-column>
@ -88,14 +65,10 @@
<el-tag v-else type="danger">{{ $fd('IsJoin', scope.row.IsJoin) }}</el-tag>
</template>
</el-table-column> -->
<el-table-column
v-if="state === 0 && (userTypeEnumInt===0 ) && !isHistory"
fixed="right"
:label="$t('common:action:action')"
width="100"
>
<el-table-column v-if="(state === 0 && (userTypeEnumInt === 0) && !isHistory) || isPM" fixed="right"
:label="$t('common:action:action')" width="100">
<template slot-scope="scope">
<el-button v-if="userTypeEnumInt===0" type="text" size="small" @click="handleEdit(scope.row)">
<el-button v-if="userTypeEnumInt === 0 || isPM" type="text" size="small" @click="handleEdit(scope.row)">
{{ $t('common:button:edit') }}
</el-button>
</template>
@ -103,16 +76,9 @@
</el-table>
<!-- 新增/编辑人员信息 -->
<el-dialog
v-if="editVisible"
:visible.sync="editVisible"
:close-on-click-modal="false"
:title="title"
width="900px"
custom-class="base-dialog-wrapper"
:append-to-body="userTypeEnumInt !== 0"
>
<HistoricalParticipantForm :state="state" :data="rowData" @getList="getList" @close="closeDialog" />
<el-dialog v-if="editVisible" :visible.sync="editVisible" :close-on-click-modal="false" :title="title" width="900px"
custom-class="base-dialog-wrapper" :append-to-body="userTypeEnumInt !== 0">
<HistoricalParticipantForm :state="state" :isPM="isPM" :data="rowData" @getList="getList" @close="closeDialog" />
</el-dialog>
</div>
@ -128,7 +94,11 @@ export default {
isHistory: {
type: Boolean,
default: false
}
},
isPM: {
type: Boolean,
default: false
},
},
data() {
return {
@ -199,8 +169,8 @@ export default {
}
</script>
<style lang="scss" scoped>
.particiapant-wrapper{
.header-wrapper{
.particiapant-wrapper {
.header-wrapper {
text-align: right;
margin-bottom: 10px;
}

View File

@ -0,0 +1,93 @@
<template>
<div class="log-list">
<el-table v-loading="loading" :data="tableData" v-adaptive="{ bottomOffset: 40 }" height="100" style="width: 100%">
<el-table-column prop="CreateTime" :label="$t('trials:researchForm:logList:label:CreateTime')" min-width="120"
show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.CreateTime ? moment(scope.row.CreateTime).format('YYYY-MM-DD HH:mm:ss') : '' }}
</template>
</el-table-column>
<el-table-column prop="Name" :label="$t('trials:researchForm:logList:label:Name')" min-width="100"
show-overflow-tooltip>
</el-table-column>
<el-table-column prop="UserType" :label="$t('trials:researchForm:logList:label:UserType')" min-width="100"
show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.UserType && $fd('UserType', scope.row.UserType) ? $fd('UserType', scope.row.UserType) : '--' }}
</template>
</el-table-column>
<el-table-column prop="OptType" :label="$t('trials:researchForm:logList:label:OptType')" min-width="100"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('SiteSurveyOptTyp', scope.row.OptType) }}
</template>
</el-table-column>
<el-table-column prop="Json" :label="$t('trials:researchForm:logList:label:Json')" min-width="100"
show-overflow-tooltip>
<template slot-scope="scope">
<el-button v-if="scope.row.Json" type="text" @click="openLog(scope.row)">{{
$t('trials:researchForm:logList:label:SurveyForm') }}</el-button>
</template>
</el-table-column>
<el-table-column prop="Reason" :label="$t('trials:researchForm:logList:label:Reason')" min-width="140"
show-overflow-tooltip>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { getSiteSurveyLogList } from '@/api/research'
import moment from 'moment'
import { getToken } from '@/utils/auth'
export default {
name: 'LogList',
props: {
trialSiteSurveyId: {
type: String,
required: true
}
},
data() {
return {
loading: false,
tableData: [],
moment,
openWindow: null
}
},
mounted() {
this.getList()
},
methods: {
async getList() {
try {
this.loading = true
let res = await getSiteSurveyLogList({ trialSiteSurveyId: this.trialSiteSurveyId })
if (res.IsSuccess) {
this.tableData = res.Result
}
} catch (e) {
console.log(e)
} finally {
this.loading = false
}
},
openLog(row) {
if (this.openWindow) {
this.openWindow.close()
}
var token = getToken()
var path = `/researchLog?trialId=${this.$route.query.trialId}&trialSiteSurveyId=${this.trialSiteSurveyId}&logId=${row.Id}&TokenKey=${token}`
this.openWindow = window.open(path, '_blank')
}
}
}
</script>
<style lang="scss" scoped>
.log-list {
height: 100%;
min-height: 0;
overflow: hidden;
}
</style>

View File

@ -3,43 +3,32 @@
<div class="base-dialog-body">
<!-- -->
<el-form-item :label="$t('trials:staffResearch:form:lastName')" prop="LastName">
<el-input v-model="form.LastName" :disabled="!(state === 0 && userTypeEnumInt === 0)" style="width: 200px" />
<el-input v-model="form.LastName" :disabled="(!(state === 0 && userTypeEnumInt === 0))&&!isPM" style="width: 200px" />
</el-form-item>
<!-- -->
<el-form-item :label="$t('trials:staffResearch:form:firstName')" prop="FirstName">
<el-input v-model="form.FirstName" :disabled="!(state === 0 && userTypeEnumInt === 0)" style="width: 200px" />
<el-input v-model="form.FirstName" :disabled="(!(state === 0 && userTypeEnumInt === 0))&&!isPM" style="width: 200px" />
</el-form-item>
<!-- 角色 -->
<el-form-item :label="$t('trials:staffResearch:form:role')" prop="TrialRoleCode">
<el-select
v-model="form.TrialRoleCode"
style="width: 200px"
:disabled="!(state === 0 && userTypeEnumInt === 0)"
@change="handleTrialRoleChange"
>
<el-option
v-for="item of $d.SiteSurvey_UserRoles"
:key="item.id"
:label="item.label"
:value="parseInt(item.value)"
/>
<el-select v-model="form.TrialRoleCode" style="width: 200px" :disabled="(!(state === 0 && userTypeEnumInt === 0))&&!isPM"
@change="handleTrialRoleChange">
<el-option v-for="item of $d.SiteSurvey_UserRoles" :key="item.id" :label="item.label"
:value="parseInt(item.value)" />
</el-select>
</el-form-item>
<!-- 电话号码 -->
<el-form-item :label="$t('trials:staffResearch:form:phone')" prop="Phone">
<el-input v-model="form.Phone" :disabled="!(state === 0 && userTypeEnumInt === 0)" style="width: 200px" />
<el-input v-model="form.Phone" :disabled="(!(state === 0 && userTypeEnumInt === 0))&&!isPM" style="width: 200px" />
</el-form-item>
<!-- 邮箱 -->
<el-form-item :label="$t('trials:staffResearch:form:email')" prop="Email">
<el-input v-model="form.Email" :disabled="!(state === 0 && userTypeEnumInt === 0)" style="width: 200px" />
<el-input v-model="form.Email" :disabled="(!(state === 0 && userTypeEnumInt === 0))&&!isPM" style="width: 200px" />
</el-form-item>
<!-- 单位 -->
<el-form-item :label="$t('trials:staffResearch:form:organization')" prop="OrganizationName">
<el-input
v-model="form.OrganizationName"
:disabled="!(state === 0 && userTypeEnumInt === 0)"
style="width: 200px"
/>
<el-input v-model="form.OrganizationName" :disabled="(!(state === 0 && userTypeEnumInt === 0))&&!isPM"
style="width: 200px" />
</el-form-item>
<!-- <el-row>
&lt;!&ndash; 是否生成账号 &ndash;&gt;
@ -112,7 +101,11 @@ export default {
state: {
type: Number,
required: true
}
},
isPM: {
type: Boolean,
default: false
},
},
data() {
// var checkPhone = (rule, value, callback) => {

View File

@ -2,27 +2,16 @@
<div class="particiapant-wrapper">
<div class="header-wrapper">
<!-- 新增 -->
<el-button
v-if="state === 0 && userTypeEnumInt === 0 && !isHistory"
size="small"
type="primary"
@click="handleAdd"
>
<el-button v-if="(state === 0 && userTypeEnumInt === 0 && !isHistory) || isPM" size="small" type="primary"
@click="handleAdd">
{{ $t('common:button:add') }}
</el-button>
</div>
<el-table
v-loading="loading"
:data="list"
border
style="width: 100%"
>
<el-table-column
prop="date"
:label="$t('trials:staffResearch:form:name')"
>
<el-table v-loading="loading" :data="list" border style="width: 100%">
<el-table-column prop="date" :label="$t('trials:staffResearch:form:name')">
<template slot-scope="scope">
<el-tooltip v-if="scope.row.ErrorMessage" class="item" effect="dark" :content="scope.row.ErrorMessage" placement="bottom">
<el-tooltip v-if="scope.row.ErrorMessage" class="item" effect="dark" :content="scope.row.ErrorMessage"
placement="bottom">
<i class="el-icon-warning" style="color:red" />
</el-tooltip>
@ -30,29 +19,17 @@
</template>
</el-table-column>
<!-- 姓名 -->
<el-table-column
prop="TrialRoleCode"
:label="$t('trials:staffResearch:form:role')"
>
<el-table-column prop="TrialRoleCode" :label="$t('trials:staffResearch:form:role')">
<template slot-scope="scope">
{{ $fd('SiteSurvey_UserRoles',scope.row.TrialRoleCode) }}
{{ $fd('SiteSurvey_UserRoles', scope.row.TrialRoleCode) }}
</template>
</el-table-column>
<!-- 电话号码 -->
<el-table-column
prop="Phone"
:label="$t('trials:staffResearch:form:phone')"
/>
<el-table-column prop="Phone" :label="$t('trials:staffResearch:form:phone')" />
<!-- 邮箱 -->
<el-table-column
prop="Email"
:label="$t('trials:staffResearch:form:email')"
/>
<el-table-column prop="Email" :label="$t('trials:staffResearch:form:email')" />
<!-- 单位 -->
<el-table-column
prop="OrganizationName"
:label="$t('trials:staffResearch:form:organization')"
/>
<el-table-column prop="OrganizationName" :label="$t('trials:staffResearch:form:organization')" />
<!-- 是否生成账号 -->
<!-- <el-table-column
prop="IsGenerateAccount"
@ -68,33 +45,28 @@
:label="$t('trials:staffResearch:form:userType')"
/>-->
<!-- 状态 -->
<el-table-column
v-if="hasPermi(['role:pm','role:apm'])"
prop="State"
:label="$t('trials:staffResearch:form:status')"
>
<el-table-column v-if="hasPermi(['role:pm', 'role:apm'])" prop="State"
:label="$t('trials:staffResearch:form:status')">
<template slot-scope="scope">
<el-tag v-if="scope.row.IsJoin" type="info">{{ $fd('IsJoin', scope.row.IsJoin) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('IsJoin', scope.row.IsJoin) }}</el-tag>
</template>
</el-table-column>
<!-- v-if="state !== 3 && (userTypeEnumInt===0 || hasPermi(['role:pm','role:apm']))" -->
<el-table-column
v-if="state === 0 && (userTypeEnumInt===0 ) && !isHistory"
fixed="right"
:label="$t('common:action:action')"
width="100"
>
<el-table-column v-if="(state === 0 && (userTypeEnumInt === 0) && !isHistory) || isPM" fixed="right"
:label="$t('common:action:action')" width="100">
<template slot-scope="scope">
<!-- 编辑 -->
<!-- <el-button type="text" size="small" :disabled="scope.row.IsGenerateSuccess || (state !== 0 && userTypeEnumInt===0) || (state !== 2 &&hasPermi(['role:pm']))" @click="handleEdit(scope.row)">
{{ $t('common:button:edit') }}
</el-button> -->
<el-button v-if="userTypeEnumInt===0" type="text" size="small" :disabled="state!==0 || scope.row.IsGenerateSuccess" @click="handleEdit(scope.row)">
<el-button v-if="userTypeEnumInt === 0 || isPM" type="text" size="small"
:disabled="(state !== 0 || scope.row.IsGenerateSuccess) && !isPM" @click="handleEdit(scope.row)">
{{ $t('common:button:edit') }}
</el-button>
<!-- 删除 -->
<el-button v-if="userTypeEnumInt===0" type="text" size="small" :disabled="state!==0 || scope.row.IsGenerateSuccess" @click="handleDelete(scope.row)">
<el-button v-if="userTypeEnumInt === 0 || isPM" type="text" size="small"
:disabled="(state !== 0 || scope.row.IsGenerateSuccess) && !isPM" @click="handleDelete(scope.row)">
{{ $t('common:button:delete') }}
</el-button>
</template>
@ -102,16 +74,9 @@
</el-table>
<!-- 新增/编辑人员信息 -->
<el-dialog
v-if="editVisible"
:visible.sync="editVisible"
:close-on-click-modal="false"
:title="title"
width="900px"
custom-class="base-dialog-wrapper"
:append-to-body="userTypeEnumInt !== 0"
>
<ParticipantForm :state="state" :data="rowData" @getList="getList" @close="closeDialog" />
<el-dialog v-if="editVisible" :visible.sync="editVisible" :close-on-click-modal="false" :title="title" width="900px"
custom-class="base-dialog-wrapper" :append-to-body="userTypeEnumInt !== 0">
<ParticipantForm :state="state" :isPM="isPM" :data="rowData" @getList="getList" @close="closeDialog" />
</el-dialog>
</div>
@ -127,7 +92,11 @@ export default {
isHistory: {
type: Boolean,
default: false
}
},
isPM: {
type: Boolean,
default: false
},
},
data() {
return {
@ -197,7 +166,7 @@ export default {
this.$message.success(this.$t('trials:staffResearch:message:delSuccessfully'))
}
}).catch(() => { this.loading = false })
}).catch(() => {})
}).catch(() => { })
},
isExistIncorrect() {
return this.list.some(item => item.IsCorrect === false)
@ -224,8 +193,8 @@ export default {
}
</script>
<style lang="scss" scoped>
.particiapant-wrapper{
.header-wrapper{
.particiapant-wrapper {
.header-wrapper {
text-align: right;
margin-bottom: 10px;
}

View File

@ -13,7 +13,8 @@
<div class="center-wrapper">
<!-- 基本信息 -->
<el-card shadow="hover">
<BaseInfo ref="baseResearchInfo" :is-history="true" />
<BaseInfo ref="baseResearchInfo" :is-history="true" :IsOnlyUploadFile="IsOnlyUploadFile"
:IsSupportUploadFile="IsSupportUploadFile" />
</el-card>
<!-- 人员调查 -->
@ -125,7 +126,7 @@ export default {
type: String,
default: ''
}
},
},
data() {
return {
trialId: this.$route.query.trialId,
@ -139,7 +140,9 @@ export default {
btnLoading: false,
isFullscreen: false,
historyVisible: false,
siteSurveyNoteInfo: {}
siteSurveyNoteInfo: {},
IsSupportUploadFile: false,
IsOnlyUploadFile: false
}
},
mounted() {
@ -154,6 +157,8 @@ export default {
this.loading = true
getSiteSurveyInfo(this.trialId, this.trialSiteSurveyId).then(res => {
if (res.Result) {
this.IsSupportUploadFile = res.Result.SiteSurveyFiledConfig.IsSupportUploadFile
this.IsOnlyUploadFile = res.Result.SiteSurveyFiledConfig.IsOnlyUploadFile
// this.trialSiteSurveyEquipmentType = res.Result.TrialInfo.TrialSiteSurveyEquipmentType
// this.trialSiteSurveyUserRoles = res.Result.TrialInfo.TrialSiteSurveyUserRoles
if (res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0) {

View File

@ -8,7 +8,7 @@
<!-- <TopLang style="position: fixed;top: 40px;right: 40px" /> -->
<div style="display: flex;justify-content: space-between;" v-if="!isPreview">
<div>{{ $t('trials:researchForm:title:researchSurveyStatus') }} <el-tag>{{ $fd('ResearchRecord', state)
}}</el-tag></div>
}}</el-tag></div>
<div>
<!-- 提交 -->
<el-button v-if="(state === 0 && userTypeEnumInt === 0)" type="primary" size="small"
@ -35,6 +35,10 @@
<el-button v-if="userTypeEnumInt === 0" type="primary" size="small" @click="handleHistory">
{{ $t('trials:researchForm:button:historicalRecord') }}
</el-button>
<!-- 操作日志 -->
<el-button type="primary" size="small" v-if="!isLog" @click="viewLog">
{{ $t('trials:researchForm:button:log') }}
</el-button>
<!-- 退出 -->
<el-button v-if="userTypeEnumInt === 0" type="primary" size="small" @click="handleBack">
{{ $t('trials:researchForm:button:loginOut') }}
@ -45,26 +49,27 @@
<div class="center-wrapper">
<!-- 基本信息 -->
<el-card shadow="hover">
<BaseInfo ref="baseResearchInfo" :isPreview="isPreview" />
<BaseInfo ref="baseResearchInfo" :isPreview="isPreview" :IsOnlyUploadFile="IsOnlyUploadFile"
:IsSupportUploadFile="IsSupportUploadFile" :isPM="isPM" />
</el-card>
<!-- 人员调查 -->
<el-card shadow="hover" class="mt10">
<!-- 历史人员 -->
<h4>{{ $t('trials:staffResearch:title:historicalStaff') }}</h4>
<HistoricalParticipant ref="historicalParticipant" :isPreview="isPreview" @refreshPage="refreshPage"
@getList="initPage" />
<HistoricalParticipant ref="historicalParticipant" :isPreview="isPreview" :isPM="isPM"
@refreshPage="refreshPage" @getList="initPage" />
<!-- <h4>{{ $t('trials:staffResearch:title:staff') }}</h4> -->
<!-- 新增人员 -->
<h4>{{ $t('trials:staffResearch:title:newStaff') }}</h4>
<ParticipantList ref="researchParticipants" :isPreview="isPreview" @refreshPage="refreshPage"
<ParticipantList ref="researchParticipants" :isPreview="isPreview" :isPM="isPM" @refreshPage="refreshPage"
@getList="initPage" />
</el-card>
<!-- 设备调研 -->
<el-card shadow="hover" class="mt10" v-if="!siteSurveyNoteInfo.IsCloseEquipmentSurvey">
<h4>{{ $t('trials:equiptResearch:title:equiptResearch') }}</h4>
<EquipmentList ref="researchEquipments" :isPreview="isPreview" />
<EquipmentList ref="researchEquipments" :isPreview="isPreview" :isPM="isPM" />
</el-card>
<!-- 其他信息调研 -->
@ -132,26 +137,49 @@
</div>
</div>
</span>
<div style="height:100%;margin:0;">
<div class="history-dialog-content">
<HistoricalRecord :trial-id="trialId" :site-id="siteId" :trial-site-survey-id="trialSiteSurveyId" />
</div>
</el-dialog>
<el-dialog v-if="logVisible" :visible.sync="logVisible"
:custom-class="isFullscreen ? 'full-log-dialog-container' : 'log-dialog-container'" :close-on-click-modal="false"
:fullscreen="isFullscreen" :show-close="false" width="60%" append-to-body>
<span slot="title" class="dialog-footer">
<div style="display: flex;flex-direction: row;justify-content: space-between;">
<div>
{{ $t('trials:researchForm:button:log') }}
</div>
<div>
<svg-icon :icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'"
style="vertical-align: baseline;cursor: pointer;font-size: 20px;" @click="toggleLogFullscreen" />
<svg-icon icon-class="dClose" style="cursor: pointer;font-size: 25px;margin-left: 10px;"
@click="logVisible = false" />
</div>
</div>
</span>
<div class="log-dialog-content">
<LogList :trial-site-survey-id="trialSiteSurveyId" />
</div>
</el-dialog>
</div>
</template>
<script>
import { getSiteSurveyInfo, trialSurveySubmit, submissionRejection } from '@/api/research'
import { getQueryString } from '@/utils/history.js'
import { getSiteSurveyInfo, trialSurveySubmit, submissionRejection, getSiteSurveyLogList } from '@/api/research'
import { getQueryString, changeURLStatic } from '@/utils/history.js'
import BaseInfo from './components/BaseInfo'
import HistoricalParticipant from './components/HistoricalParticipant'
import ParticipantList from './components/ParticipantList'
import EquipmentList from './components/EquipmentList'
import { mapMutations } from 'vuex'
import TopLang from './topLang'
import HistoricalRecord from './components/HistoricalRecord'
import LogList from './components/LogList'
import store from '@/store'
export default {
name: 'QuestionForm',
components: { BaseInfo, HistoricalParticipant, ParticipantList, EquipmentList, TopLang, HistoricalRecord },
components: { BaseInfo, HistoricalParticipant, ParticipantList, EquipmentList, TopLang, HistoricalRecord, LogList },
props: {
isPreview: {
type: Boolean,
@ -172,54 +200,123 @@ export default {
btnLoading: false,
isFullscreen: false,
historyVisible: false,
siteSurveyNoteInfo: {}
siteSurveyNoteInfo: {},
IsSupportUploadFile: false,
IsOnlyUploadFile: false,
logVisible: false,
isLog: false,
logId: null
}
},
mounted() {
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
this.$updateDictionary()
this.trialSiteSurveyId = getQueryString('trialSiteSurveyId')
this.logId = getQueryString('logId')
if (this.logId) this.isLog = true
if (this.$router.currentRoute.query.TokenKey) {
store.dispatch('user/setToken', this.$router.currentRoute.query.TokenKey)
changeURLStatic('TokenKey', '')
}
this.initPage()
if (zzSessionStorage.getItem('userTypeEnumInt')) {
this.userTypeEnumInt = zzSessionStorage.getItem('userTypeEnumInt') * 1
}
},
computed: {
isPM() {
return this.hasPermi(['role:pm', 'role:spm', 'role:cpm', 'role:apm']) && this.state !== 3 && this.IsOnlyUploadFile
}
},
methods: {
...mapMutations({ setLanguage: 'lang/setLanguage' }),
//
initPage() {
this.loading = true
getSiteSurveyInfo(this.trialId, this.trialSiteSurveyId).then(res => {
if (res.Result) {
// this.trialSiteSurveyEquipmentType = res.Result.TrialInfo.TrialSiteSurveyEquipmentType
// this.trialSiteSurveyUserRoles = res.Result.TrialInfo.TrialSiteSurveyUserRoles
if (res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0) {
this.siteSurveyNoteInfo = res.Result.SiteSurveyFiledConfig.ModifyFiledList.find(i => i.NeedModifyFiled === 'SiteSurveyNote')
}
this.siteSurveyNoteInfo.IsCloseEquipmentSurvey = res.Result.SiteSurveyFiledConfig.IsCloseEquipmentSurvey
this.state = res.Result.TrialSiteSurvey.State
this.siteId = res.Result.TrialSiteSurvey.TrialSiteId
this.$refs['baseResearchInfo'].initForm(res.Result.TrialInfo, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig ? res.Result.SiteSurveyFiledConfig.NotShowFieldList : null)
var historicalArr = []
var newArr = []
res.Result.TrialSiteUserSurveyList.map(i => {
if (i.IsHistoryUser) {
historicalArr.push(i)
} else {
newArr.push(i)
if (this.isLog) {
getSiteSurveyLogList({ trialSiteSurveyId: this.trialSiteSurveyId, Id: this.logId }).then(r => {
if (r.Result && r.Result[0].Json) {
let res = {
Result: JSON.parse(r.Result[0].Json)
}
})
if (res.Result) {
// this.trialSiteSurveyEquipmentType = res.Result.TrialInfo.TrialSiteSurveyEquipmentType
// this.trialSiteSurveyUserRoles = res.Result.TrialInfo.TrialSiteSurveyUserRoles
if (res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0) {
this.siteSurveyNoteInfo = res.Result.SiteSurveyFiledConfig.ModifyFiledList.find(i => i.NeedModifyFiled === 'SiteSurveyNote')
}
this.IsSupportUploadFile = res.Result.SiteSurveyFiledConfig.IsSupportUploadFile
this.IsOnlyUploadFile = res.Result.SiteSurveyFiledConfig.IsOnlyUploadFile
this.siteSurveyNoteInfo.IsCloseEquipmentSurvey = res.Result.SiteSurveyFiledConfig.IsCloseEquipmentSurvey
this.state = 3
this.siteId = res.Result.TrialSiteSurvey.TrialSiteId
this.$refs['baseResearchInfo'].initForm(res.Result.TrialInfo, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig ? res.Result.SiteSurveyFiledConfig.NotShowFieldList : null)
var historicalArr = []
var newArr = []
res.Result.TrialSiteUserSurveyList.map(i => {
if (i.IsHistoryUser) {
historicalArr.push(i)
} else {
newArr.push(i)
}
})
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
this.$refs['researchParticipants'].initList(newArr, res.Result.TrialSiteSurvey)
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig.EquipmentControlFieldList)
this.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
}
this.loading = false
}
}).catch((err) => { console.log(err); this.loading = false })
} else {
getSiteSurveyInfo(this.trialId, this.trialSiteSurveyId).then(res => {
if (res.Result) {
// this.trialSiteSurveyEquipmentType = res.Result.TrialInfo.TrialSiteSurveyEquipmentType
// this.trialSiteSurveyUserRoles = res.Result.TrialInfo.TrialSiteSurveyUserRoles
if (res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0) {
this.siteSurveyNoteInfo = res.Result.SiteSurveyFiledConfig.ModifyFiledList.find(i => i.NeedModifyFiled === 'SiteSurveyNote')
}
this.IsSupportUploadFile = res.Result.SiteSurveyFiledConfig.IsSupportUploadFile
this.IsOnlyUploadFile = res.Result.SiteSurveyFiledConfig.IsOnlyUploadFile
this.siteSurveyNoteInfo.IsCloseEquipmentSurvey = res.Result.SiteSurveyFiledConfig.IsCloseEquipmentSurvey
this.state = res.Result.TrialSiteSurvey.State
this.siteId = res.Result.TrialSiteSurvey.TrialSiteId
this.$refs['baseResearchInfo'].initForm(res.Result.TrialInfo, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig ? res.Result.SiteSurveyFiledConfig.NotShowFieldList : null)
var historicalArr = []
var newArr = []
res.Result.TrialSiteUserSurveyList.map(i => {
if (i.IsHistoryUser) {
historicalArr.push(i)
} else {
newArr.push(i)
}
})
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
this.$refs['researchParticipants'].initList(newArr, res.Result.TrialSiteSurvey)
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig.EquipmentControlFieldList)
this.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
}
this.loading = false
}).catch(() => { this.loading = false })
}
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
this.$refs['researchParticipants'].initList(newArr, res.Result.TrialSiteSurvey)
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig.EquipmentControlFieldList)
this.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
}
this.loading = false
}).catch(() => { this.loading = false })
},
//
handleSubmit(type) {
if (this.userTypeEnumInt === 0) {
this.loading = true
this.$refs['baseResearchInfo'].handleSave(true).then(res => {
this.$refs['baseResearchInfo'].handleSave(true, type === 'approve').then(res => {
this.loading = false
if (res) {
this.submit(type)
@ -259,38 +356,33 @@ export default {
},
//
generateAccount() {
//
this.$confirm(this.$t('trials:researchForm:message:auditWarning'), {
type: 'warning',
distinguishCancelAndClose: true
}).then(() => {
// 1crc1SR
// var list = this.$refs['researchParticipants'].list
// var cIdx = list.findIndex(i => i.UserTypeEnum === 5)
// var sIdx = list.findIndex(i => i.UserTypeEnum === 9)
// if (cIdx === -1 || sIdx === -1) {
// this.$alert(this.$t('trials:researchForm:message:saveWarning2'))
// return
// }
this.loading = true
var param = {
TrialId: this.trialId,
TrialSiteSurveyId: this.trialSiteSurveyId,
LoginUrl: `${location.protocol}//${location.host}/login`,
BaseUrl: `${location.protocol}//${location.host}/login`,
RouteUrl: `${location.protocol}//${location.host}/email-recompose`
}
trialSurveySubmit(param).then((res) => {
this.loading = false
if (res.IsSuccess) {
this.initPage()
this.$emit('refreshPage')
this.$message.success(this.$t('common:message:approvedSuccessfully'))
this.$refs['baseResearchInfo'].handleSave(true, true).then(res => {
this.$confirm(this.$t('trials:researchForm:message:auditWarning'), {
type: 'warning',
distinguishCancelAndClose: true
}).then(() => {
if (res) {
var param = {
TrialId: this.trialId,
TrialSiteSurveyId: this.trialSiteSurveyId,
LoginUrl: `${location.protocol}//${location.host}/login`,
BaseUrl: `${location.protocol}//${location.host}/login`,
RouteUrl: `${location.protocol}//${location.host}/email-recompose`
}
this.loading = false
trialSurveySubmit(param).then((res) => {
this.loading = false
if (res.IsSuccess) {
this.initPage()
this.$emit('refreshPage')
this.$message.success(this.$t('common:message:approvedSuccessfully'))
}
}).catch(() => {
this.loading = false
})
}
}).catch(() => {
this.loading = false
})
}).catch(() => { })
})
},
//
handleReject() {
@ -330,7 +422,14 @@ export default {
handleHistory() {
this.isFullscreen = false
this.historyVisible = true
}
},
toggleLogFullscreen() {
this.isFullscreen = !this.isFullscreen
},
viewLog() {
this.isFullscreen = false
this.logVisible = true
},
}
}
</script>
@ -382,5 +481,45 @@ export default {
}
}
.history-dialog-content,
.log-dialog-content {
height: 100%;
margin: 0;
}
.log-dialog-content {
overflow: hidden;
}
}
</style>
<style lang="scss">
.full-log-dialog-container,
.log-dialog-container {
overflow: hidden;
}
.full-log-dialog-container {
display: flex;
flex-direction: column;
}
.full-log-dialog-container .el-dialog__body {
flex: 1;
min-height: 0;
padding: 10px;
overflow: hidden;
}
.log-dialog-container .el-dialog__body {
height: calc(100% - 80px);
padding: 10px;
overflow: hidden;
}
.full-log-dialog-container .log-dialog-content,
.log-dialog-container .log-dialog-content {
height: 100%;
overflow: hidden;
}
</style>

View File

@ -1,6 +1,7 @@
<template>
<div class="question-login-wrapper">
<div class="box-wrapper">
<div v-if="verify"></div>
<div class="box-wrapper" v-else>
<h2 style="text-align:center;">
<!-- 中心调研表 -->
{{ $t('trials:researchForm:title:question') }}
@ -91,7 +92,10 @@ import { getUserMenuTree, getUserPermissions } from '@/api/user'
import store from '@/store'
import TopLang from './topLang'
import { mapGetters, mapMutations } from 'vuex'
import {
getLinkLinkExpirationTime,
getLinkVerificationCodeIsEffective
} from '@/api/trials'
export default {
components: { TopLang },
data() {
@ -146,6 +150,7 @@ export default {
}
return {
trialId: '',
verify: true,
form: {
Sponsor: null, //
ResearchProgramNo: null, //
@ -202,31 +207,116 @@ export default {
])
},
mounted() {
this.$i18n.locale = this.$route.query.lang
this.setLanguage(this.$route.query.lang)
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
this.$updateDictionary()
if (this.$route.query.trialId) {
this.trialId = this.$route.query.trialId
this.initPage()
}
if (this.$route.query.isUpload) {
this.isUpload = true
this.form.IsUpdate = true
let { email, oldEMail, trialSiteId } = this.$route.query
if (trialSiteId) this.form.TrialSiteId = trialSiteId
if (oldEMail) this.form.ReplaceUserEmailOrPhone = oldEMail
if (email && email !== 'null') {
this.form.EmailOrPhone = email
} else {
this.form.EmailOrPhone = oldEMail
}
if ((email && email !== 'null') || (oldEMail && oldEMail !== 'null')) {
this.isNeedUpload = false
}
}
this.getLinkTimeIsExpired()
},
methods: {
...mapMutations({ setLanguage: 'lang/setLanguage' }),
async getLinkTimeIsExpired() {
try {
let data = {
TrialId: this.$route.query.trialId,
}
let res = await getLinkLinkExpirationTime(data)
if (res.IsSuccess) {
if (res.Result.IsExpired) {
return this.$router.replace('/link_expired')
// return this.$confirm(this.$t("trials:researchForm:confirm:linkIsExpired"), '', {
// type: 'warning'
// })
// return this.IsExpired = true
}
this.customPrompt()
}
} catch (err) {
console.log(err)
}
},
async getLinkVerificationCodeIsEffective(value) {
try {
let data = {
TrialId: this.$route.query.trialId,
LinkVerificationCode: value
}
let res = await getLinkVerificationCodeIsEffective(data)
if (res.IsSuccess) {
if (!res.Result.IsEffective) {
return false
}
this.verify = false
if (this.$route.query.trialId) {
this.trialId = this.$route.query.trialId
this.initPage()
}
if (this.$route.query.isUpload) {
this.isUpload = true
this.form.IsUpdate = true
let { email, oldEMail, trialSiteId } = this.$route.query
if (trialSiteId) this.form.TrialSiteId = trialSiteId
if (oldEMail) this.form.ReplaceUserEmailOrPhone = oldEMail
if (email && email !== 'null') {
this.form.EmailOrPhone = email
} else {
this.form.EmailOrPhone = oldEMail
}
if ((email && email !== 'null') || (oldEMail && oldEMail !== 'null')) {
this.isNeedUpload = false
}
}
}
return true
} catch (err) {
return false
console.log(err)
}
},
async customPrompt(name) {
try {
const that = this
//
let message = this.$t('trials:researchForm:message:LinkVerificationCode')
const { value } = await this.$prompt(message, '', {
showClose: false,
cancelButtonText: this.$t('common:button:cancel'),
confirmButtonText: this.$t('trials:researchForm:button:saveLinkCode'),
showCancelButton: false,
closeOnClickModal: false,
closeOnPressEscape: false,
inputValue: name,
beforeClose: async (action, instance, done) => {
if (action === 'confirm') {
const value = instance.inputValue
if (!value) {
that.$message.error(this.$t('trials:researchForm:error:noValue'))
} else {
let flag = await this.getLinkVerificationCodeIsEffective(value)
if (!flag) {
that.$message.error(this.$t('trials:researchForm:confirm:isEffective'))
} else {
done()
}
}
} else {
done()
}
}
})
this.getLinkVerificationCodeIsEffective(value)
} catch (err) {
console.log(err)
return null
}
},
//
async initPage() {
this.loading = true
@ -267,8 +357,18 @@ export default {
}
verifySendCode(param).then(async res => {
zzSessionStorage.clear()
this.$i18n.locale = this.$route.query.lang
this.setLanguage(this.$route.query.lang)
// this.$i18n.locale = this.$route.query.lang
// this.setLanguage(this.$route.query.lang)
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
store.dispatch('user/setToken', res.Result.Token)
zzSessionStorage.setItem('TokenKey', res.Result.Token)
zzSessionStorage.setItem('userId', res.Result.UserRoleId);

View File

@ -4,30 +4,38 @@
<el-form ref="NoticeForm" :model="form" :rules="rules" label-width="180px" size="small">
<el-form-item :label="$t('system:notice:label:NoticeLevel')" prop="NoticeLevelEnum">
<el-select v-model="form.NoticeLevelEnum" clearable size="small">
<el-option v-for="item of dict.type.NoteLevel" :key="item.value" :label="item.label"
<el-option v-for="item of $d.NoteLevel" :key="item.value" :label="item.label"
:value="item.raw.Code * 1" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:notice:label:NoticeType')" prop="NoticeTypeEnum">
<el-select v-model="form.NoticeTypeEnum" clearable size="small">
<el-option v-for="item of dict.type.NoteType" :key="item.value" :label="item.label"
<el-option v-for="item of $d.NoteType" :key="item.value" :label="item.label"
:value="item.raw.Code * 1" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:notice:label:ApplicableProject')" prop="ApplicableProjectEnum">
<el-select v-model="form.ApplicableProjectEnum" clearable size="small">
<el-option v-for="item of dict.type.NoticeApplicableTrial" :key="item.value" :label="item.label"
<el-option v-for="item of $d.NoticeApplicableTrial" :key="item.value" :label="item.label"
:value="item.raw.Code * 1" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:notice:label:NoticeUserTypeIdList')" prop="NoticeUserTypeIdList">
<el-select v-model="form.NoticeUserTypeIdList" multiple clearable size="small">
<el-option v-for="item of roleList" :key="item.Id" :label="item.UserTypeShortName" :value="item.Id" />
</el-select>
<div class="role-checkbox-group">
<div class="role-checkbox-actions">
<el-button size="small" type="text" @click="handleSelectAllRoles"></el-button>
<el-button size="small" type="text" @click="handleClearRoles"></el-button>
</div>
<el-checkbox-group v-model="form.NoticeUserTypeIdList" class="role-checkbox-list">
<el-checkbox v-for="item of roleList" :key="item.Id" :label="item.Id">
{{ item.UserTypeShortName }}
</el-checkbox>
</el-checkbox-group>
</div>
</el-form-item>
<el-form-item :label="$t('system:notice:label:NoticeMode')" prop="NoticeModeEnum">
<el-select v-model="form.NoticeModeEnum" clearable size="small">
<el-option v-for="item of dict.type.NoticeMode" :key="item.value" :label="item.label"
<el-option v-for="item of $d.NoticeMode" :key="item.value" :label="item.label"
:value="item.raw.Code * 1" />
</el-select>
</el-form-item>
@ -35,13 +43,14 @@
<el-date-picker v-model="form.StartDate" type="datetime" />
</el-form-item>
<el-form-item :label="$t('system:notice:label:EndDate')" prop="EndDate">
<el-date-picker v-model="form.EndDate" type="datetime" :default-time="'23:59:59'" />
<el-date-picker v-model="form.EndDate" type="datetime" :default-time="'23:59:59'"
@change="handleEndDateChange" />
</el-form-item>
<el-form-item v-if="!form.Id" :label="$t('system:notice:label:IsPush')" prop="IsPush">
<el-switch v-model="form.IsPush" :active-value="true" :inactive-value="false" />
</el-form-item>
<el-form-item :label="$t('system:notice:label:NoticeContent')" prop="NoticeContent">
<el-input v-model="form.NoticeContent" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }" />
<el-input v-model="form.NoticeContent" type="textarea" :autosize="{ minRows: 4, maxRows: 8 }" />
</el-form-item>
<el-form-item :label="$t('system:notice:label:file')">
<el-upload class="upload-demo" action :before-upload="beforeUpload" :http-request="handleUploadFile"
@ -106,12 +115,44 @@ export default {
NoticeUserTypeIdList: [{ required: true, message: 'Please specify', trigger: 'blur' }]
},
fileList: [],
model_cfg: { visible: false, showClose: true, width: '600px', title: '', appendToBody: true }
model_cfg: {
visible: false,
showClose: true,
width: '800px',
title: '',
appendToBody: true,
bodyStyle: {
maxHeight: '60vh',
overflowY: 'auto'
}
}
}
},
mounted() {
},
methods: {
handleSelectAllRoles() {
this.form.NoticeUserTypeIdList = this.roleList.map(item => item.Id)
},
handleClearRoles() {
this.form.NoticeUserTypeIdList = []
},
handleEndDateChange(value) {
this.form.EndDate = this.normalizeEndDate(value)
},
normalizeEndDate(value) {
if (!value) {
return value
}
const date = new Date(value)
if (Number.isNaN(date.getTime())) {
return value
}
if (date.getHours() === 0 && date.getMinutes() === 0 && date.getSeconds() === 0) {
date.setHours(23, 59, 59, 0)
}
return date
},
handleRemoveFile() {
this.form.FileName = null
this.form.Path = null
@ -147,6 +188,7 @@ export default {
this.fileList[0] = { name: this.form.FileName, path: this.form.FilePath, fullPath: this.form.FullFilePath }
}
this.$set(this.form, 'NoticeUserTypeIdList', this.form.NoticeUserTypeList.map(v => v.Id))
this.form.EndDate = this.normalizeEndDate(this.form.EndDate)
} else {
this.form = formDataDefault()
}
@ -177,3 +219,27 @@ export default {
}
}
</script>
<style scoped>
.role-checkbox-group {
display: flex;
flex-direction: column;
}
.role-checkbox-actions {
margin-bottom: 8px;
}
.role-checkbox-actions .el-button + .el-button {
margin-left: 12px;
}
.role-checkbox-list {
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
}
.role-checkbox-list .el-checkbox {
margin-right: 0;
}
</style>

View File

@ -57,14 +57,21 @@
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">{{ $t('common:button:add') }}</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" v-adaptive="{bottomOffset:45}" height="100" :data="list" class="table">
<el-table
v-loading="loading"
v-adaptive="{bottomOffset:45}"
height="100"
:data="list"
class="table"
@sort-change="handleSortChange"
>
<el-table-column type="index" width="50" />
<el-table-column :label="$t('system:notice:table:NoticeLevel')" prop="NoticeLevelEnum" min-width="120" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:NoticeLevel')" prop="NoticeLevelEnum" min-width="120" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{ $d.NoteLevel.find(v => {return v.raw.Code * 1 === scope.row.NoticeLevelEnum}) ? $d.NoteLevel.find(v => {return v.raw.Code * 1 === scope.row.NoticeLevelEnum}).label : '' }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:NoticeType')" prop="NoticeTypeEnum" min-width="100" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:NoticeType')" prop="NoticeTypeEnum" min-width="100" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{ $d.NoteType.find(v => {return v.raw.Code * 1 === scope.row.NoticeTypeEnum}) ? $d.NoteType.find(v => {return v.raw.Code * 1 === scope.row.NoticeTypeEnum}).label : '' }}
</template>
@ -74,46 +81,47 @@
{{ scope.row.NoticeContent }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:NoticeState')" prop="NoticeStateEnum" min-width="100" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:NoticeState')" prop="NoticeStateEnum" min-width="100" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
<el-tag :type="scope.row.ActualNoticeStateEnum === 0 ? 'info' : scope.row.ActualNoticeStateEnum === 1 ? 'success' : 'danger'">
{{ $d.NoticeState.find(v => {return v.raw.Code * 1 === scope.row.ActualNoticeStateEnum}) ? $d.NoticeState.find(v => {return v.raw.Code * 1 === scope.row.ActualNoticeStateEnum}).label : '' }}
</el-tag>
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:ApplicableProject')" prop="ApplicableProjectEnum" min-width="100" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:ApplicableProject')" prop="ApplicableProjectEnum" min-width="100" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{ $d.NoticeApplicableTrial.find(v => {return v.raw.Code * 1 === scope.row.ApplicableProjectEnum}) ? $d.NoticeApplicableTrial.find(v => {return v.raw.Code * 1 === scope.row.ApplicableProjectEnum}).label : '' }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:NoticeUserTypeList')" prop="NoticeUserTypeList" min-width="80" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:NoticeUserTypeList')" prop="NoticeUserTypeList" min-width="100" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.NoticeUserTypeList.map(v => v.UserTypeShortName).toString() }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:NoticeMode')" prop="NoticeModeEnum" min-width="100" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:NoticeMode')" prop="NoticeModeEnum" min-width="100" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{ dict.type.NoticeMode.find(v => {return v.raw.Code * 1 === scope.row.NoticeModeEnum}) ? $d.NoticeMode.find(v => {return v.raw.Code * 1 === scope.row.NoticeModeEnum}).label : '' }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:StartDate')" prop="StartDate" min-width="240" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:StartDate')" prop="StartDate" min-width="160" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{ scope.row.StartDate }}-{{ scope.row.EndDate }}
{{ scope.row.StartDate }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:PublishUserName')" prop="PublishUserName" min-width="100" show-overflow-tooltip />
<el-table-column :label="$t('system:notice:table:PublishedTime')" prop="PublishedTime" min-width="160" show-overflow-tooltip />
<el-table-column :label="$t('system:notice:table:UpdateTime')" prop="UpdateTime" min-width="160" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:UpdateTime')" prop="EndDate" min-width="160" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{ scope.row.StartDate ? scope.row.StartDate: scope.row.CreateTime }}
{{ scope.row.EndDate }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:FileName')" fixed="right" prop="FileName" min-width="120" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:PublishUserName')" prop="PublishUserName" min-width="100" show-overflow-tooltip sortable="custom"/>
<el-table-column :label="$t('system:notice:table:PublishedTime')" prop="PublishedTime" min-width="160" show-overflow-tooltip sortable="custom"/>
<el-table-column :label="$t('system:notice:table:FileName')" fixed="right" prop="FileName" min-width="120" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
<a :href="OSSclientConfig.basePath + scope.row.FullFilePath" target="_blank" style="color:#428bca">{{ scope.row.FileName }}</a>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" fixed="right" prop="UserTypeShortName" min-width="200" show-overflow-tooltip>
<el-table-column :label="$t('common:action:action')" fixed="right" prop="UserTypeShortName" min-width="120" show-overflow-tooltip>
<template slot-scope="scope">
<el-button
type="text"
@ -146,8 +154,8 @@ import NoticeForm from './components/from'
import Pagination from '@/components/Pagination'
const searchDataDefault = () => {
return {
Asc: true,
SortField: '',
Asc: false,
SortField: 'PublishedTime',
NoticeContent: null,
FileName: null,
NoticeTypeEnum: null,
@ -236,6 +244,17 @@ export default {
}).catch(() => {
this.loading = false
})
},
//
handleSortChange(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
}
}
}

View File

@ -16,7 +16,7 @@
<el-form-item label="Shortname: " prop="UserTypeShortName">
<el-input v-model="form.UserTypeShortName" />
</el-form-item>
<el-form-item
<!-- <el-form-item
:label="$t('system:role:form:isInternal')"
prop="IsInternal"
>
@ -28,7 +28,7 @@
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
</el-form-item> -->
<el-form-item :label="$t('system:role:form:IsEnable')" prop="IsEnable">
<el-switch
v-model="form.IsEnable"

View File

@ -178,7 +178,7 @@ export default {
updateUser(this.user)
.then((res) => {
this.isDisabled = false
this.$message.success('Updated successfully')
this.$message.success(this.$t("common:message:updatedSuccessfully"))
})
.catch(() => {
this.isDisabled = false
@ -190,7 +190,7 @@ export default {
this.user.Id = res.Result.Id
this.user.UserCode = res.Result.UserCode
this.$emit('getUserId', res.Result.Id)
this.$message.success('Added successfully')
this.$message.success(this.$t("common:message:addedSuccessfully"))
this.$router.push({ path: '/system/user/list' })
})
.catch(() => {
@ -308,63 +308,63 @@ export default {
UserWorkLanguage: [
{
required: true,
message: 'Please Select',
message: this.$t("common:ruleMessage:select"),
trigger: ['blur', 'change'],
},
],
Roles: [
{
required: true,
message: 'Please Select',
message: this.$t("common:ruleMessage:select"),
trigger: ['blur', 'change'],
},
],
IsZhiZhun: [
{
required: true,
message: 'Please Select',
message: this.$t("common:ruleMessage:select"),
trigger: ['blur', 'change'],
},
],
OrganizationName: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' },
],
LastName: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' },
{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' },
{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") },
],
FirstName: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' },
{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' },
{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") },
],
Phone: [
{
max: 20,
min: 7,
message: 'The length is 7 to 20',
message: this.$t("system:userInfo:ruleMessage:length7to20"),
trigger: ['blur'],
},
],
EMail: [
{
required: true,
message: 'Please input the email address',
message: this.$t("common:ruleMessage:specify"),
trigger: 'blur',
},
{
// type: 'email',
pattern: new RegExp(this.$reg().EmailRegexStr),
message: 'Please input the correct email address',
message: this.$t("trials:site:ruleMessage:correctEmail"),
trigger: ['blur'],
},
{ max: 50, message: 'The maximum length is 50' },
{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") },
],
Sex: [{ required: true, message: 'Please specify', trigger: 'blur' }],
Sex: [{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' }],
Status: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' },
],
DepartmentName: [{ max: 50, message: 'The maximum length is 50' }],
PositionName: [{ max: 50, message: 'The maximum length is 50' }],
DepartmentName: [{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") }],
PositionName: [{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") }],
},
userTypeOptions: [],
isDisabled: false,

View File

@ -0,0 +1,177 @@
<template>
<el-dialog
class="notice-detail-dialog"
:visible.sync="dialogVisible"
:title="title"
width="700px"
append-to-body
:close-on-click-modal="false"
@closed="handleClosed"
>
<div class="notice-detail-dialog__body">
<div v-if="publishedTime" class="notice-detail-dialog__meta">
<span class="notice-detail-dialog__time">{{ publishedTime }}</span>
</div>
<div class="notice-detail-dialog__content">
{{ content || '-' }}
</div>
<div v-if="filePath" class="notice-detail-dialog__attachment">
<div class="notice-detail-dialog__attachment-label">{{ $t('trials:notice:table:appendix') }}</div>
<div class="notice-detail-dialog__attachment-card">
<i class="el-icon-paperclip" />
<el-button size="small" type="text" @click="$emit('open-file', filePath)">
{{ fileName}}
</el-button>
</div>
</div>
</div>
<span v-if="showConfirmButton" slot="footer" class="notice-detail-dialog__footer">
<el-button size="small" @click="handleClose">{{ $t('common:button:close') }}</el-button>
<el-button
size="small"
type="primary"
:loading="confirmLoading"
@click="$emit('confirm')"
>
{{ $t('common:button:confirm') }}
</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
name: 'NoticeDetailDialog',
props: {
visible: {
type: Boolean,
default: false,
},
title: {
type: String,
default: '',
},
content: {
type: String,
default: '',
},
publishedTime: {
type: String,
default: '',
},
fileName: {
type: String,
default: '',
},
filePath: {
type: String,
default: '',
},
showConfirmButton: {
type: Boolean,
default: false,
},
confirmLoading: {
type: Boolean,
default: false,
},
},
computed: {
dialogVisible: {
get() {
return this.visible
},
set(value) {
this.$emit('update:visible', value)
},
},
},
methods: {
handleClose() {
this.dialogVisible = false
},
handleClosed() {
this.$emit('closed')
},
},
}
</script>
<style lang="scss" scoped>
.notice-detail-dialog {
::v-deep .el-dialog__header {
padding: 18px 20px 10px;
border-bottom: 1px solid #ebeef5;
}
::v-deep .el-dialog__body {
padding: 0;
}
::v-deep .el-dialog__footer {
padding: 12px 20px 18px;
border-top: 1px solid #ebeef5;
}
}
.notice-detail-dialog__body {
max-height: 70vh;
overflow-y: auto;
padding: 18px 20px;
}
.notice-detail-dialog__meta {
margin-bottom: 12px;
color: #909399;
font-size: 13px;
}
.notice-detail-dialog__content {
padding: 14px 16px;
border: 1px solid #ebeef5;
border-radius: 8px;
background-color: #f8f9fb;
white-space: pre-wrap;
word-break: break-word;
line-height: 1.7;
color: #303133;
}
.notice-detail-dialog__attachment {
margin-top: 16px;
}
.notice-detail-dialog__attachment-label {
margin-bottom: 8px;
color: #606266;
font-size: 13px;
}
.notice-detail-dialog__attachment-card {
display: flex;
align-items: center;
padding: 12px 14px;
border: 1px solid #ebeef5;
border-radius: 8px;
background-color: #fff;
}
.notice-detail-dialog__attachment-card .el-icon-paperclip {
margin-right: 8px;
color: #409eff;
font-size: 16px;
}
.notice-detail-dialog__attachment-name {
flex: 1;
min-width: 0;
margin-right: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #303133;
}
</style>

View File

@ -7,16 +7,45 @@
{{ item.Content }}
</span>
</marquee>
<notice-detail-dialog
:visible.sync="detailDialog.visible"
:title="detailDialog.title"
:content="detailDialog.content"
:published-time="detailDialog.publishedTime"
:file-name="detailDialog.fileName"
:file-path="detailDialog.filePath"
:show-confirm-button="detailDialog.showConfirmButton"
:confirm-loading="detailDialog.confirmLoading"
@confirm="handleDetailConfirm"
@open-file="downLoadFile"
@closed="handleDetailClosed"
/>
</div>
</template>
<script>
import { setSystemNoticeHaveRead } from '@/api/global'
import { getBasicDataSelects } from '@/api/dictionary/dictionary'
import NoticeDetailDialog from '@/views/trials/components/NoticeDetailDialog'
const detailDialogDefault = () => {
return {
visible: false,
title: '',
content: '',
publishedTime: '',
fileName: '',
filePath: '',
showConfirmButton: false,
confirmLoading: false,
noticeId: null
}
}
export default {
name: 'NoticeMarquee',
components: { NoticeDetailDialog },
data() {
return {
noteType: []
noteType: [],
detailDialog: detailDialogDefault()
}
},
computed: {
@ -40,25 +69,41 @@ export default {
if (i > -1) {
currentNoticeType = this.noteType[i].Value
}
const h = this.$createElement
this.$msgbox({
title: currentNoticeType,
message: h('span', null, item.Content),
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true
setSystemNoticeHaveRead(item.Id).then(async res => {
if (res.IsSuccess) {
await this.$store.dispatch('global/getNoticeList')
}
instance.confirmButtonLoading = false
done()
}).catch(() => { instance.confirmButtonLoading = false })
} else {
done()
}
}
})
this.detailDialog = {
visible: true,
title: currentNoticeType,
content: item.Content || '',
publishedTime: item.PublishedTime || '',
fileName: item.FileName || '',
filePath: item.Path || '',
showConfirmButton: true,
confirmLoading: false,
noticeId: item.Id
}
},
async handleDetailConfirm() {
if (!this.detailDialog.noticeId) {
return
}
this.detailDialog.confirmLoading = true
try {
const res = await setSystemNoticeHaveRead(this.detailDialog.noticeId)
if (res.IsSuccess) {
await this.$store.dispatch('global/getNoticeList')
this.detailDialog.visible = false
}
} catch (err) {
console.log(err)
} finally {
this.detailDialog.confirmLoading = false
}
},
handleDetailClosed() {
this.detailDialog = detailDialogDefault()
},
downLoadFile(filePath) {
if (!filePath) return
window.open(this.OSSclientConfig.basePath + filePath, '_blank')
},
getDicData() {
getBasicDataSelects(['NoteType']).then(res => {

View File

@ -10,53 +10,38 @@
<NoticeMarquee />
</div>
<div class="right-menu">
<el-menu
:default-active="activeIndex"
class="el-menu-demo"
mode="horizontal"
size="mini"
@select="handleSelect"
>
<el-menu-item
v-if="
!hasPermi([
'role:air',
'role:rpm',
'role:radmin',
'role:rcrc',
'role:rir',
])
"
index="1"
>
<el-menu :default-active="activeIndex" class="el-menu-demo" mode="horizontal" size="mini" @select="handleSelect">
<el-menu-item v-if="
!hasPermi([
'role:air',
'role:rpm',
'role:radmin',
'role:rcrc',
'role:rir',
])
" index="1">
<i class="el-icon-odometer" />
<!-- 工作台 -->
<span slot="title">{{ $t('trials:menuTitle:workbench') }}</span>
</el-menu-item>
<el-menu-item
v-if="!hasPermi(['role:zys', 'role:zyss', 'role:zybs'])"
index="2"
:disabled="TotalNeedSignSystemDocCount !== 0"
>
<el-menu-item v-if="!hasPermi(['role:zys', 'role:zyss', 'role:zybs'])" index="2"
:disabled="TotalNeedSignSystemDocCount !== 0">
<i class="el-icon-box" />
<!-- 我的项目 -->
<span slot="title">
{{ $t('trials:tab:trials') }}
</span>
</el-menu-item>
<el-menu-item
v-if="
!hasPermi([
'role:air',
'role:rpm',
'role:radmin',
'role:rcrc',
'role:rir',
'role:zys',
])
"
index="3"
>
<el-menu-item v-if="
!hasPermi([
'role:air',
'role:rpm',
'role:radmin',
'role:rcrc',
'role:rir',
'role:zys',
])
" index="3">
<i class="el-icon-chat-dot-square" />
<!-- 通知消息 -->
<span slot="title">{{ $t('trials:tab:notice') }}</span>
@ -74,11 +59,8 @@
$t('trials:trials-myinfo:title:accountInfo')
}}</el-menu-item>
<!-- 管理后台 -->
<el-menu-item
v-if="hasPermi(['role:dev', 'role:oa', 'role:admin'])"
index="4-4"
>{{ $t('trials:trials-myinfo:title:system') }}</el-menu-item
>
<el-menu-item v-if="hasPermi(['role:dev', 'role:oa', 'role:admin'])" index="4-4">{{
$t('trials:trials-myinfo:title:system') }}</el-menu-item>
<!-- 切换角色 -->
<el-menu-item index="4-5" v-if="hasRole">{{
$t('trials:trials-myinfo:title:toggleRole')
@ -89,18 +71,12 @@
}}</el-menu-item>
</el-submenu>
</el-menu>
<TopLang
v-if="
VUE_APP_OSS_CONFIG_REGION !== 'oss-us-west-1' && NODE_ENV !== 'usa'
"
/>
<TopLang v-if="
VUE_APP_OSS_CONFIG_REGION !== 'oss-us-west-1' && NODE_ENV !== 'usa'
" />
</div>
<toggleRole
v-if="toggleRoleVisible"
:visible.sync="toggleRoleVisible"
:loading="toggleRoleLoading"
@save="loginByRole"
/>
<toggleRole v-if="toggleRoleVisible" :visible.sync="toggleRoleVisible" :loading="toggleRoleLoading"
@save="loginByRole" />
</div>
</template>
@ -306,37 +282,45 @@ export default {
overflow: hidden;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.el-breadcrumb__item {
font-size: 16px;
}
.leftMenu {
display: flex;
align-items: flex-end;
padding-left: 20px;
padding-bottom: 20px;
img {
height: 28px;
margin-right: 10px;
}
.title-logo {
height: 36px;
}
span {
font-size: 20px;
position: relative;
bottom: -3px;
}
}
.right-menu,
.center-menu {
display: flex;
flex-direction: row;
justify-content: space-between;
height: 100%;
// line-height: 70px;
&:focus {
outline: none;
}
.right-menu-item {
display: inline-block;
padding: 0 8px;
@ -354,9 +338,11 @@ export default {
}
}
}
.avatar-container {
display: inline-block;
margin-right: 200px;
.user-avatar {
margin-top: 5px;
width: 40px;
@ -364,29 +350,34 @@ export default {
border-radius: 10px;
}
}
.dropdown-container {
cursor: pointer;
.el-icon-caret-bottom {
font-size: 12px;
}
}
.el-dropdown-link {
color: #999c9e;
cursor: pointer;
}
}
.right-menu {
.el-menu-item.is-active {
font-weight: bolder;
}
}
.center-menu {
width: calc(100% - 1200px);
}
}
</style>
<style>
.el-menu--horizontal > .my_info.el-submenu .el-submenu__icon-arrow {
.el-menu--horizontal>.my_info.el-submenu .el-submenu__icon-arrow {
margin-top: 0;
}
</style>

View File

@ -172,6 +172,19 @@
:limit.sync="searchData.PageSize"
@pagination="getList"
/>
<notice-detail-dialog
:visible.sync="detailDialog.visible"
:title="detailDialog.title"
:content="detailDialog.content"
:published-time="detailDialog.publishedTime"
:file-name="detailDialog.fileName"
:file-path="detailDialog.filePath"
:show-confirm-button="detailDialog.showConfirmButton"
:confirm-loading="detailDialog.confirmLoading"
@confirm="handleDetailConfirm"
@open-file="downLoadFile"
@closed="handleDetailClosed"
/>
</template>
</BaseContainer>
</template>
@ -179,6 +192,7 @@
import { getUserSystemNoticeList, setSystemNoticeHaveRead } from '@/api/global'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import NoticeDetailDialog from '@/views/trials/components/NoticeDetailDialog'
const searchDataDefault = () => {
return {
NoticeContent: '',
@ -193,9 +207,22 @@ const searchDataDefault = () => {
PageSize: 20,
}
}
const detailDialogDefault = () => {
return {
visible: false,
title: '',
content: '',
publishedTime: '',
fileName: '',
filePath: '',
showConfirmButton: false,
confirmLoading: false,
noticeId: null,
}
}
export default {
name: 'TrialsNotice',
components: { BaseContainer, Pagination },
components: { BaseContainer, Pagination, NoticeDetailDialog },
dicts: ['NoteType', 'NoticeState', 'NoticeMode'],
data() {
return {
@ -203,6 +230,7 @@ export default {
list: [],
total: 0,
loading: false,
detailDialog: detailDialogDefault(),
}
},
mounted() {
@ -222,38 +250,39 @@ export default {
})
},
showDetail(row) {
var currentNoticeType = this.$fd('NoteType', row.NoticeTypeEnum)
if (row.IsRead || row.ActualNoticeStateEnum !== 1) {
this.$alert(row.NoticeContent, currentNoticeType, {
showConfirmButton: false,
})
} else {
const h = this.$createElement
this.$msgbox({
title: currentNoticeType,
message: h('span', null, row.NoticeContent),
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true
setSystemNoticeHaveRead(row.Id)
.then(async (res) => {
if (res.IsSuccess) {
await this.$store.dispatch('global/getNoticeList')
this.getList()
}
instance.confirmButtonLoading = false
done()
})
.catch(() => {
instance.confirmButtonLoading = false
})
} else {
done()
}
},
})
this.detailDialog = {
visible: true,
title: this.$fd('NoteType', row.NoticeTypeEnum),
content: row.NoticeContent || '',
publishedTime: row.PublishedTime || '',
fileName: row.FileName || '',
filePath: row.Path || '',
showConfirmButton: !row.IsRead && row.ActualNoticeStateEnum === 1,
confirmLoading: false,
noticeId: row.Id
}
},
async handleDetailConfirm() {
if (!this.detailDialog.noticeId) {
return
}
this.detailDialog.confirmLoading = true
try {
const res = await setSystemNoticeHaveRead(this.detailDialog.noticeId)
if (res.IsSuccess) {
await this.$store.dispatch('global/getNoticeList')
this.detailDialog.visible = false
this.getList()
}
} catch (err) {
console.log(err)
} finally {
this.detailDialog.confirmLoading = false
}
},
handleDetailClosed() {
this.detailDialog = detailDialogDefault()
},
downLoadFile(filePath) {
if (!filePath) return
window.open(this.OSSclientConfig.basePath + filePath, '_blank')
@ -281,4 +310,9 @@ export default {
}
</script>
<style lang="scss" scoped>
.notice-content-text {
white-space: pre-wrap;
word-break: break-word;
line-height: 1.5;
}
</style>

View File

@ -482,7 +482,7 @@ export default {
}
},
handleSelectTable(row) {
return row.DoctorTrialState !== 1 && row.ResumeStatus === 1 && row.AcceptingNewTrial && !row.IsVirtual && !row.IsVacation && row.CooperateStatus === 1
return row.DoctorTrialState !== 1 && row.ResumeStatus === 1 && row.AcceptingNewTrial && ((!row.IsVirtual && this.$store.state.trials.config.TrialType !== 0) || (row.IsVirtual && this.$store.state.trials.config.TrialType === 0)) && !row.IsVacation && row.CooperateStatus === 1
},
handleDetail(row) {
const { href } = this.$router.resolve({

View File

@ -665,7 +665,7 @@ export default {
async audit(row) {
try {
this.rowData = Object.assign({}, row)
this.rowData.title = `${row.ApplicantName} (${this.$fd('RequestReReadingType', row.RequestReReadingType)}) ${this.$t('trials:spmAudit:title:imageBack').replace('xxx', row.OriginalReReadingTask.SubjectCode).replace('yyy', row.OriginalReReadingTask.TaskName)}`
this.rowData.title = `${row.ApplicantName} (${this.$fd('RequestReReadingType', row.RequestReReadingType)}) ${this.$t('trials:spmAudit:title:imageBack').replace('xxx', row.OriginalReReadingTask.SubjectCode).replace('yyy', row.OriginalReReadingTask.TaskBlindName)}`
Object.keys(this.form).forEach(key => {
this.form[key] = null
})

View File

@ -0,0 +1,311 @@
<template>
<BaseContainer>
<!-- 搜索框 -->
<template slot="search-container">
<el-form :inline="true">
<!-- 中心编号 -->
<el-form-item :label="$t('trials:researchStaff:table:siteId')">
<el-select v-model="searchData.TrialSiteId" clearable filterable style="width:120px;">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteCode"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<!-- 中心名称 -->
<el-form-item :label="$t('trials:researchStaff:table:siteName')">
<el-input v-model="searchData.TrialSiteName" class="mr" clearable style="width:120px;" />
</el-form-item>
<!-- 状态 -->
<el-form-item :label="$t('trials:researchRecord:table:status')">
<el-select v-model="searchData.State" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of $d.ResearchRecord" :key="index" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 是否废除 -->
<!-- <el-form-item :label="$t('trials:researchRecord:table:isDeleted')">
<el-select v-model="searchData.IsDeleted" clearable filterable style="width: 120px">
<el-option v-for="item of $d.YesOrNo" v-show="item.raw.ValueCN !== ''" :key="`IsDeleted${item.value}`"
:label="item.label" :value="item.value" />
</el-select>
</el-form-item> -->
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
</el-form-item>
<!-- 重置 -->
<el-form-item>
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
<!-- 导出 -->
<el-form-item>
<el-button type="primary" icon="el-icon-download" :disabled="list.length === 0" @click="handleExport">
{{ $t('common:button:export') }}
</el-button>
</el-form-item>
</el-form>
</template>
<template slot="main-container">
<el-table ref="equipmentsList" v-loading="loading" v-adaptive="{ bottomOffset: 55 }" :data="list" stripe
height="100" @sort-change="handleSortByColumn">
<el-table-column type="index" width="50" />
<!-- 中心编号 -->
<el-table-column prop="TrialSiteCode" :label="$t('trials:researchStaff:table:siteId')" show-overflow-tooltip
sortable="custom" min-width="100">
</el-table-column>
<!-- 中心名称 -->
<el-table-column prop="TrialSiteName" :label="$t('trials:researchStaff:table:siteName')" show-overflow-tooltip
sortable="custom" min-width="120">
</el-table-column>
<!-- 状态 -->
<el-table-column prop="State" :label="$t('trials:researchRecord:table:status')" min-width="150"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.State === 0" type="primary">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 1" type="info">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 2" type="warning">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 3" type="danger">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
</template>
</el-table-column>
<!-- 是否废除 -->
<!-- <el-table-column prop="IsDeleted" :label="$t('trials:researchRecord:table:isDeleted')" min-width="100"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted" type="danger">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
<el-tag v-else type="primary">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
</template>
</el-table-column> -->
<!-- 联系人 -->
<el-table-column prop="UserName" :label="$t('trials:researchForm:form:contactor')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 联系电话 -->
<el-table-column prop="Phone" :label="$t('trials:researchForm:form:contactorPhone')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 联系邮箱 -->
<el-table-column prop="Email" :label="$t('trials:researchForm:form:contactorEmail')" show-overflow-tooltip
sortable="custom" min-width="100" />
<template v-if="equipmentControlFieldList.includes('EquipmentTypeEnum')">
<!-- 扫描设备 -->
<el-table-column prop="EquipmentTypeEnum" :label="$t('trials:equiptResearch:form:equipment')" min-width="120"
show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.OtherEquipmentType ? scope.row.OtherEquipmentType :
$fd('SiteSurvey_ScanEquipmentType', scope.row.EquipmentTypeEnum) }}
</template>
</el-table-column>
</template>
<template v-if="equipmentControlFieldList.includes('Parameters')">
<!-- 扫描参数 -->
<el-table-column prop="Parameters" :label="$t('trials:equiptResearch:form:param')" min-width="100"
show-overflow-tooltip />
</template>
<template v-if="equipmentControlFieldList.includes('ManufacturerType')">
<!-- 扫描仪器制造商名称 -->
<el-table-column min-width="120" prop="ManufacturerType"
:label="$t('trials:equiptResearch:form:manufacturer')" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.ManufacturerName ? scope.row.ManufacturerName : $fd('ManufacturerType',
scope.row.ManufacturerType) }}
</template>
</el-table-column>
</template>
<template v-if="equipmentControlFieldList.includes('ScannerType')">
<!-- 扫描仪型号 -->
<el-table-column min-width="120" prop="ScannerType" :label="$t('trials:equiptResearch:form:model')"
show-overflow-tooltip />
</template>
<template v-if="equipmentControlFieldList.includes('MagneticFieldStrengthType')">
<!-- 磁场强度 -->
<el-table-column min-width="120" prop="MagneticFieldStrengthType"
:label="$t('trials:equiptResearch:form:MagneticFieldStrengthType')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('MagneticFieldStrengthType', scope.row.MagneticFieldStrengthType) }}
</template>
</el-table-column>
</template>
<template v-if="equipmentControlFieldList.includes('BodyCoilChannelCount')">
<!-- 体部线圈通道数 -->
<el-table-column min-width="120" prop="BodyCoilChannelCount"
:label="$t('trials:equiptResearch:form:BodyCoilChannelCount')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('BodyCoilChannelCount', scope.row.BodyCoilChannelCount) }}
</template>
</el-table-column>
</template>
<template v-if="equipmentControlFieldList.includes('HasDedicatedPdfFatQuantificationSequence')">
<!-- 是否具备专用的PDFF脂肪定量序列CSE-MRI序列 -->
<el-table-column min-width="120" prop="HasDedicatedPdfFatQuantificationSequence"
:label="$t('trials:equiptResearch:form:HasDedicatedPdfFatQuantificationSequence')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.HasDedicatedPdfFatQuantificationSequence) }}
</template>
</el-table-column>
</template>
<template v-if="equipmentControlFieldList.includes('PdfFatQuantificationSequenceType')">
<!-- 专用的PDFF脂肪定量序列类型 -->
<el-table-column min-width="120" prop="PdfFatQuantificationSequenceType"
:label="$t('trials:equiptResearch:form:PdfFatQuantificationSequenceType')" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.OtherSequenceSpecification ? scope.row.OtherSequenceSpecification :
$fd('PdfFatQuantificationSequenceType', scope.row.PdfFatQuantificationSequenceType) }}
</template>
</el-table-column>
</template>
<template v-if="equipmentControlFieldList.includes('HasT2R2Correction')">
<!-- 是否包含 T2/R2 校正用于铁沉积校正 -->
<el-table-column min-width="120" prop="HasT2R2Correction"
:label="$t('trials:equiptResearch:form:HasT2R2Correction')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.HasT2R2Correction) }}
</template>
</el-table-column>
</template>
<template v-if="equipmentControlFieldList.includes('CanFullyExportPdfParameterMapsAndRawDicom')">
<!-- 是否可完整导出 PDFF 参数图及全部原始 DICOM 数据 -->
<el-table-column prop="CanFullyExportPdfParameterMapsAndRawDicom"
:label="$t('trials:equiptResearch:form:CanFullyExportPdfParameterMapsAndRawDicom')" min-width="120"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.CanFullyExportPdfParameterMapsAndRawDicom) }}
</template>
</el-table-column>
</template>
<template v-if="equipmentControlFieldList.includes('Note')">
<!-- 备注 -->
<el-table-column min-width="120" prop="Note" :label="$t('trials:equiptResearch:form:remark')"
show-overflow-tooltip />
</template>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</template>
</BaseContainer>
</template>
<script>
import { getTrialSiteSelect } from '@/api/trials'
import { getSiteSurveyEquipmentList } from '@/api/research'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import { getSiteSurveyEquipmentList_Export } from '@/api/export'
const searchDataDefault = () => {
return {
SortField: '',
State: "",
Asc: true,
PageIndex: 1,
PageSize: 20,
TrialId: '',
TrialSiteId: '',
TrialSiteName: ''
}
}
export default {
name: 'Equipments',
components: { BaseContainer, Pagination },
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
trialId: '',
otherInfo: {},
siteOptions: [],
equipmentControlFieldList: []
}
},
mounted() {
this.trialId = this.$route.query.trialId
this.getList()
this.getSite()
},
methods: {
async handleExport() {
await getSiteSurveyEquipmentList_Export(this.searchData)
},
async getList() {
try {
this.loading = true
this.searchData.TrialId = this.trialId
let res = await getSiteSurveyEquipmentList(this.searchData)
if (res.IsSuccess) {
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.otherInfo = res.OtherInfo
const { EquipmentControlFieldList } = res.OtherInfo
this.equipmentControlFieldList = []
EquipmentControlFieldList.forEach(item => {
this.equipmentControlFieldList.push(item.FiledName)
})
this.$nextTick(() => {
this.$refs.equipmentsList.doLayout()
})
}
} catch (e) {
console.log(e)
} finally {
this.loading = false
}
},
// site
async getSite() {
try {
let res = await getTrialSiteSelect(this.trialId)
this.siteOptions = res.Result
} catch (e) {
console.log(e)
}
},
//
handleReset() {
this.searchData = searchDataDefault()
this.getList()
this.$nextTick(() => {
this.$refs.list.clearSort()
})
},
//
handleSearch() {
this.getList()
},
//
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.getList()
}
}
}
</script>

View File

@ -1,6 +1,25 @@
<template>
<div class="ImageManual">
<div class="config">
<div style="margin-bottom: 20px;font-weight: bold;">
{{ $t("trials:researchRecord:ImageManual:uploadFile") }}
</div>
<el-form size="small" :model="uploadForm" style="width:80%">
<el-form-item :label="$t('trials:researchRecord:ImageManual:IsSupportUploadFile')">
<el-radio-group v-model="uploadForm.IsSupportUploadFile"
@input="uploadForm.IsOnlyUploadFile = false">
<el-radio :label="item.value" v-for="item in $d.YesOrNo" :key="item.id">{{ item.label
}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('trials:researchRecord:ImageManual:IsOnlyUploadFile')"
v-if="uploadForm.IsSupportUploadFile">
<el-radio-group v-model="uploadForm.IsOnlyUploadFile">
<el-radio :label="item.value" v-for="item in $d.YesOrNo" :key="item.id">{{ item.label
}}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div style="margin-bottom: 20px;font-weight: bold;">
{{ $t("trials:researchRecord:ImageManual:BasicQuestion") }}
</div>
@ -56,11 +75,11 @@
<el-form-item :label="$t('trials:researchRecord:ImageManual:Equipment')">
<el-radio-group v-model="form.IsCloseEquipmentSurvey" @input="handleInput">
<el-radio :label="item.value" v-for="item in $d.YesOrNo" :key="item.id">{{ item.label
}}</el-radio>
}}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div style="margin-bottom: 20px;font-weight: bold;">
<div style="margin-bottom: 20px;font-weight: bold;" v-if="form.IsCloseEquipmentSurvey">
{{ $t("trials:researchRecord:ImageManual:EquipmentConfig") }}
</div>
<el-form size="small" :model="EquipmentForm" style="width:80%" v-if="form.IsCloseEquipmentSurvey">
@ -192,6 +211,10 @@ export default {
HasT2R2Correction: false,
CanFullyExportPdfParameterMapsAndRawDicom: false,
},
uploadForm: {
IsSupportUploadFile: false,
IsOnlyUploadFile: false
},
obj: {
AverageEngravingCycle: false,
IsConfirmImagingTechnologist: false,
@ -206,7 +229,7 @@ export default {
IsAuthorizeRadiologistsParticipate: false,
AssignFixedTechnologists: false,
ISStrictManualBurnFlag: false,
NotStrictManualBurnFlagReason: false,
NotStrictManualBurnFlagReason: false,
},
loading: false
}
@ -241,6 +264,8 @@ export default {
}
let res = await getTrialExtralConfig(param)
if (res.IsSuccess) {
this.uploadForm.IsOnlyUploadFile = res.Result.IsOnlyUploadFile
this.uploadForm.IsSupportUploadFile = res.Result.IsSupportUploadFile
Object.keys(this.form).forEach(key => {
this.form[key] = true
if (key === 'ReplaceContent' || key === 'ReplaceContentCN') this.form[key] = ''
@ -314,6 +339,8 @@ export default {
IsOpenLostVistRead: this.form.IsOpenLostVistRead,
IsSupportQCDownloadImage: this.form.IsSupportQCDownloadImage,
IsCloseEquipmentSurvey: !this.form.IsCloseEquipmentSurvey,
IsOnlyUploadFile: this.uploadForm.IsOnlyUploadFile,
IsSupportUploadFile: this.uploadForm.IsSupportUploadFile,
ModifyFiledList: [],
NotShowFieldList: [],
EquipmentControlFieldList: []

View File

@ -0,0 +1,356 @@
<template>
<BaseContainer>
<!-- 搜索框 -->
<template slot="search-container">
<el-form :inline="true">
<!-- 中心编号 -->
<el-form-item :label="$t('trials:researchStaff:table:siteId')">
<el-select v-model="searchData.TrialSiteId" clearable filterable style="width:120px;">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteCode"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<!-- 中心名称 -->
<el-form-item :label="$t('trials:researchStaff:table:siteName')">
<el-input v-model="searchData.TrialSiteName" class="mr" clearable style="width:120px;" />
</el-form-item>
<!-- 状态 -->
<el-form-item :label="$t('trials:researchRecord:table:status')">
<el-select v-model="searchData.State" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of $d.ResearchRecord" :key="index" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 是否废除 -->
<!-- <el-form-item :label="$t('trials:researchRecord:table:isDeleted')">
<el-select v-model="searchData.IsDeleted" clearable filterable style="width: 120px">
<el-option v-for="item of $d.YesOrNo" v-show="item.raw.ValueCN !== ''" :key="`IsDeleted${item.value}`"
:label="item.label" :value="item.value" />
</el-select>
</el-form-item> -->
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
</el-form-item>
<!-- 重置 -->
<el-form-item>
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
<!-- 导出 -->
<el-form-item>
<el-button type="primary" icon="el-icon-download" :disabled="list.length === 0" @click="handleExport">
{{ $t('common:button:export') }}
</el-button>
</el-form-item>
</el-form>
</template>
<template slot="main-container">
<el-table ref="questionsList" v-loading="loading" v-adaptive="{ bottomOffset: 55 }" :data="list" stripe
height="100" @sort-change="handleSortByColumn">
<el-table-column type="index" width="50" />
<!-- 项目编号 -->
<!-- <el-table-column
prop="TrialCode"
:label="$t('trials:researchForm:form:trialId')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/> -->
<!-- 试验方案号 -->
<!-- <el-table-column
prop="ResearchProgramNo"
:label="$t('trials:researchForm:form:researchNo')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/> -->
<!-- 试验名称 -->
<!-- <el-table-column
prop="ExperimentName"
:label="$t('trials:researchForm:form:researchName')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/> -->
<!-- 适应症类型 -->
<!-- <el-table-column
prop="IndicationType"
:label="$t('trials:researchForm:form:decleareType')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/> -->
<!-- 适应症类型 -->
<!-- <el-table-column
prop="IndicationType"
:label="$t('trials:researchForm:form:decleareType')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/> -->
<!-- 中心编号 -->
<el-table-column prop="TrialSiteCode" :label="$t('trials:researchStaff:table:siteId')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 中心名称 -->
<el-table-column prop="SiteName" :label="$t('trials:researchStaff:table:siteName')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 状态 -->
<el-table-column prop="State" :label="$t('trials:researchRecord:table:status')" min-width="150"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.State === 0" type="primary">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 1" type="info">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 2" type="warning">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 3" type="danger">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
</template>
</el-table-column>
<!-- 是否废除 -->
<!-- <el-table-column prop="IsDeleted" :label="$t('trials:researchRecord:table:isDeleted')" min-width="100"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted" type="danger">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
<el-tag v-else type="primary">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
</template>
</el-table-column> -->
<!-- 联系人 -->
<el-table-column prop="UserName" :label="$t('trials:researchForm:form:contactor')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 联系电话 -->
<el-table-column prop="Phone" :label="$t('trials:researchForm:form:contactorPhone')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 联系邮箱 -->
<el-table-column prop="Email" :label="$t('trials:researchForm:form:contactorEmail')" show-overflow-tooltip
sortable="custom" min-width="100" />
<el-table-column v-show="otherInfo.IsSupportUploadFile" prop="SiteSurveyFile.FileName"
:label="$t('trials:researchForm:form:SiteSurveyFile')" show-overflow-tooltip min-width="100">
<template slot-scope="scope">
<el-button v-if="scope.row.SiteSurveyFile && scope.row.SiteSurveyFile.FileName" type="text"
@click="viewFile(scope.row.SiteSurveyFile)">
{{ scope.row.SiteSurveyFile.FileName }}
</el-button>
</template>
</el-table-column>
<template v-if="!notShowFieldList.includes('AverageEngravingCycle')">
<!-- 平均刻盘周期 -->
<el-table-column prop="AverageEngravingCycle" :label="$t('trials:researchForm:form:engravingCycle')"
show-overflow-tooltip />
</template>
<template v-if="!notShowFieldList.includes('IsRoutineMRIPDEE')">
<!-- MRI-PDFF 是否为本中心该适应症的常规诊疗检查项目 -->
<el-table-column prop="IsRoutineMRIPDEE" :label="$t('trials:researchForm:form:IsRoutineMRIPDEE')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsRoutineMRIPDEE) }}
</template>
</el-table-column>
</template>
<template
v-if="!notShowFieldList.includes('MRIPDFFScanTime') || !notShowFieldList.includes('MRIPDFFLeadTime') || !notShowFieldList.includes('MRIPDFFOther')">
<!-- MRI-PDFF 检查的检测周期含单次检查时长预约等待时长等 -->
<!-- <el-table-column :label="$t('trials:researchForm:form:IsRoutineMRIPDEE')"> -->
<!-- 单次检查时长分钟 -->
<el-table-column v-show="!notShowFieldList.includes('MRIPDFFScanTime')" prop="MRIPDFFScanTime"
:label="$t('trials:researchForm:form:MRIPDFFScanTime')" show-overflow-tooltip />
<!-- 平均预约等待时长 -->
<el-table-column v-show="!notShowFieldList.includes('MRIPDFFLeadTime')" prop="MRIPDFFLeadTime"
:label="$t('trials:researchForm:form:MRIPDFFLeadTime')" show-overflow-tooltip />
<!-- 特殊情况备注-->
<el-table-column v-show="!notShowFieldList.includes('MRIPDFFOther')" prop="MRIPDFFOther"
:label="$t('trials:researchForm:form:MRIPDFFOther')" show-overflow-tooltip />
<!-- </el-table-column> -->
</template>
<template
v-if="!notShowFieldList.includes('IsAuthorizeRadiologistsParticipate') || !notShowFieldList.includes('AssignFixedTechnologists')">
<!-- 如已选择研究者评估项目是否会授权影像科老师参与本试验如不单独授权是否可在试验中保持 1-2 名固定技师操作 -->
<el-table-column prop="IsAuthorize" :label="$t('trials:researchForm:form:IsAuthorize')" show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="scope.row.IsAuthorizeRadiologistsParticipate">
{{ $t('trials:researchForm:form:IsAuthorizeRadiologistsParticipate') }}
</span>
<span v-else-if="scope.row.AssignFixedTechnologists">
{{ $t('trials:researchForm:form:AssignFixedTechnologists') }}
</span>
</template>
</el-table-column>
</template>
<template v-if="!notShowFieldList.includes('IsConfirmImagingTechnologist')">
<!-- 请确认参与本项目影像采集的影像技师具备对应的资质技师证对应设备的大型设备上岗证 -->
<el-table-column prop="IsConfirmImagingTechnologist" :label="$t('trials:researchForm:form:isQualified')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsConfirmImagingTechnologist) }}
</template>
</el-table-column>
</template>
<template v-if="!notShowFieldList.includes('NotConfirmReson')">
<!-- 原因 -->
<el-table-column prop="NotConfirmReson" :label="$t('trials:researchForm:form:notQualifiedReason')"
show-overflow-tooltip />
</template>
<template v-if="!notShowFieldList.includes('EfficacyEvaluatorType')">
<!-- 研究单位疗效评估人员类型 -->
<el-table-column prop="EfficacyEvaluatorType" :label="$t('trials:researchForm:form:staffType')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('EfficacyEvaluatorType', scope.row.EfficacyEvaluatorType) }}
</template>
</el-table-column>
</template>
<template v-if="!notShowFieldList.includes('IsFollowStudyParameters')">
<!-- 是否严格按照研究单位影像手册参数完成图像采集 -->
<el-table-column prop="IsFollowStudyParameters" :label="$t('trials:researchForm:form:isFollowStudyParam')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsFollowStudyParameters) }}
</template>
</el-table-column>
</template>
<template v-if="!notShowFieldList.includes('NotFollowReson')">
<!-- 不能严格按照研究单位影像手册参数采集图像原因 -->
<el-table-column prop="NotFollowReson" :label="$t('trials:researchForm:form:notFollowStudyParam')"
show-overflow-tooltip />
</template>
<template v-if="!notShowFieldList.includes('ISStrictManualBurnFlag')">
<!-- 是否严格按照影像手册参数完成刻盘 -->
<el-table-column prop="ISStrictManualBurnFlag" :label="$t('trials:researchForm:form:ISStrictManualBurnFlag')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.ISStrictManualBurnFlag) }}
</template>
</el-table-column>
</template>
<template v-if="!notShowFieldList.includes('NotStrictManualBurnFlagReason')">
<!-- 不能严格按照影像手册参数完成刻盘原因 -->
<el-table-column prop="NotStrictManualBurnFlagReason"
:label="$t('trials:researchForm:form:NotStrictManualBurnFlagReason')" show-overflow-tooltip />
</template>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</template>
</BaseContainer>
</template>
<script>
import { getTrialSiteSelect } from '@/api/trials'
import { getSiteSurveyInfoList } from '@/api/research'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import { getSiteSurveyInfoList_Export } from '@/api/export'
const searchDataDefault = () => {
return {
SortField: '',
Asc: true,
State: "",
PageIndex: 1,
PageSize: 20,
TrialId: '',
TrialSiteId: '',
TrialSiteName: ''
}
}
export default {
name: 'Equipments',
components: { BaseContainer, Pagination },
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
trialId: '',
otherInfo: {},
siteOptions: [],
notShowFieldList: []
}
},
mounted() {
this.trialId = this.$route.query.trialId
this.getList()
this.getSite()
},
methods: {
async handleExport() {
await getSiteSurveyInfoList_Export(this.searchData)
},
async getList() {
try {
this.loading = true
this.searchData.TrialId = this.trialId
let res = await getSiteSurveyInfoList(this.searchData)
if (res.IsSuccess) {
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.otherInfo = res.OtherInfo
this.notShowFieldList = res.OtherInfo.NotShowFieldList
this.$nextTick(() => {
this.$refs.questionsList.doLayout()
})
}
} catch (e) {
console.log(e)
} finally {
this.loading = false
}
},
// site
async getSite() {
try {
let res = await getTrialSiteSelect(this.trialId)
this.siteOptions = res.Result
} catch (e) {
console.log(e)
}
},
viewFile(siteSurveyFile) {
this.$preview({
path: siteSurveyFile.Path,
type: siteSurveyFile.FileType,
title: siteSurveyFile.FileName,
})
},
//
handleReset() {
this.searchData = searchDataDefault()
this.getList()
this.$nextTick(() => {
this.$refs.list.clearSort()
})
},
//
handleSearch() {
this.getList()
},
//
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.getList()
}
}
}
</script>

View File

@ -0,0 +1,733 @@
<template>
<BaseContainer>
<!-- 搜索框 -->
<template slot="search-container">
<el-form :inline="true">
<!-- 中心 -->
<el-form-item :label="$t('trials:researchRecord:table:siteId')">
<el-select v-model="searchData.TrialSiteId" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteCode"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<!-- 联系人 -->
<el-form-item :label="$t('trials:researchRecord:table:contactor')">
<el-input v-model="searchData.UserKeyInfo" class="mr" clearable :placeholder="`${$t(
'trials:researchRecord:placeholder:contactorInfo'
)}`" style="width: 140px" />
</el-form-item>
<!-- 初审人 -->
<el-form-item :label="$t('trials:researchRecord:table:preliminaryUser')">
<el-input v-model="searchData.PreliminaryUserName" class="mr" clearable style="width: 140px" />
</el-form-item>
<!-- 审核人 -->
<el-form-item :label="$t('trials:researchRecord:table:ReviewerUser')">
<el-input v-model="searchData.ReviewerUserName" class="mr" clearable style="width: 140px" />
</el-form-item>
<!-- 状态 -->
<!-- <el-form-item :label="$t('trials:researchRecord:table:status')">
<el-select v-model="searchData.State" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of $d.ResearchRecord" :key="index" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item> -->
<!-- 是否废除 -->
<el-form-item :label="$t('trials:researchRecord:table:isDeleted')">
<el-select v-model="searchData.IsDeleted" clearable filterable style="width: 120px">
<el-option v-for="item of $d.YesOrNo" v-show="item.raw.ValueCN !== ''" :key="`IsDeleted${item.value}`"
:label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 更新时间 -->
<el-form-item :label="$t('trials:researchRecord:table:updateTime')">
<el-date-picker v-model="searchData.DateRange" type="daterange" value-format="yyyy-MM-dd" format="yyyy-MM-dd"
style="width: 250px" />
</el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
<!-- 导出 -->
<el-button type="primary" @click="handleExport">
{{ $t('common:button:export') }}
</el-button>
<!-- 中心人员 -->
<el-button v-show="searchData.State === 3"
v-hasPermi="['trials:trials-panel:attachments:site-research:summary-record']" type="primary"
icon="el-icon-info" @click="showResearchUser">
{{ $t('trials:researchRecord:button:researchData') }}
</el-button>
<!-- 调查表链接 -->
<el-button v-show="searchData.State === 0"
v-hasPermi="['trials:trials-panel:attachments:site-research:questionnaire-link']" type="primary"
icon="el-icon-link" @click="showResearchLink">
{{ $t('trials:researchRecord:button:questionLink') }}
</el-button>
</el-form>
</template>
<template slot="main-container">
<!-- 系统文件列表 -->
<el-table ref="siteResearchList" v-loading="loading" v-adaptive="{ bottomOffset: 60 }" :data="list" stripe
height="100" @sort-change="handleSortByColumn">
<el-table-column type="index" width="50" />
<!-- 中心编号 -->
<el-table-column prop="TrialSiteCode" :label="$t('trials:researchRecord:table:siteId')" min-width="100"
sortable="custom" show-overflow-tooltip />
<!-- 中心名称 -->
<el-table-column prop="SiteName" :label="$t('trials:researchRecord:table:siteName')" min-width="100"
sortable="custom" show-overflow-tooltip />
<!-- 联系人 -->
<el-table-column prop="UserName" :label="$t('trials:researchRecord:table:contactor')" min-width="100"
sortable="custom" show-overflow-tooltip />
<!-- 联系电话 -->
<el-table-column prop="Phone" :label="$t('trials:researchRecord:table:contactorPhone')" min-width="100"
show-overflow-tooltip />
<!-- 联系邮箱 -->
<el-table-column prop="Email" :label="$t('trials:researchRecord:table:contactorEmail')" min-width="150"
show-overflow-tooltip />
<!-- 初审人 -->
<el-table-column prop="preliminaryUser" :label="$t('trials:researchRecord:table:preliminaryUser')"
min-width="150" show-overflow-tooltip>
<template slot-scope="scope">
{{
scope.row.PreliminaryUser
? scope.row.PreliminaryUser.RealName
: ''
}}
</template>
</el-table-column>
<!-- 审核人 -->
<el-table-column prop="ReviewerUser" :label="$t('trials:researchRecord:table:ReviewerUser')" min-width="150"
show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.ReviewerUser ? scope.row.ReviewerUser.RealName : '' }}
</template>
</el-table-column>
<!-- 状态 -->
<el-table-column prop="State" :label="$t('trials:researchRecord:table:status')" min-width="150"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.State === 0" type="primary">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 1" type="info">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 2" type="warning">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 3" type="danger">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
</template>
</el-table-column>
<!-- 是否废除 -->
<el-table-column prop="IsDeleted" :label="$t('trials:researchRecord:table:isDeleted')" min-width="100"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted" type="danger">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
<el-tag v-else type="primary">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
</template>
</el-table-column>
<!-- 更新时间 -->
<el-table-column prop="UpdateTime" :label="$t('trials:researchRecord:table:updateTime')" min-width="150"
show-overflow-tooltip sortable="custom" />
<el-table-column width="150">
<template slot-scope="scope">
<!-- 查看 -->
<el-button v-show="searchData.State === 3" :disabled="scope.row.State !== 3" circle
:title="$t('common:button:view')" icon="el-icon-view" @click="handleViewResearchList(scope.row)" />
<!-- 审批 -->
<el-button v-show="(searchData.State === 1 || searchData.State === 2) && isAudit"
v-hasPermi="['trials:trials-panel:attachments:site-research:auidt']"
:disabled="scope.row.State === 0 || scope.row.State === 3 || scope.row.IsDeleted" circle
:title="$t('trials:researchRecord:action:view')" icon="el-icon-s-check"
@click="handleViewResearchList(scope.row)" />
<!-- 日志 -->
<el-button circle :title="$t('trials:researchRecord:action:log')" icon="el-icon-tickets"
@click="viewLog(scope.row)" />
<!-- 废除 -->
<el-button v-show="searchData.State === 0" v-hasPermi="[
'trials:trials-panel:attachments:site-research:abolish',
]" :disabled="scope.row.State !== 0 || scope.row.IsDeleted" circle
:title="$t('trials:researchRecord:action:abolish')" icon="el-icon-delete"
@click="handleRepealResearch(scope.row)" />
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</template>
<!-- 中心数据 trials:researchRecord:button:researchData-->
<!-- 中心人员 trials:researchRecord:dialogTitle:questionStaff-->
<el-dialog v-if="researchUserVisible" :visible.sync="researchUserVisible"
:title="$t('trials:researchRecord:button:researchData')" custom-class="base-dialog-wrapper" :fullscreen="true">
<div class="base-modal-body" style="border: 1px solid #ccc; padding: 10px">
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('trials:researchRecord:dialogTitle:questionStaff')" name="first">
<Users v-if="activeName === 'first'" />
</el-tab-pane>
<el-tab-pane label="中心设备" name="second">
<Equipments v-if="activeName === 'second'" />
</el-tab-pane>
<el-tab-pane label="基本情况" name="third">
<Questions v-if="activeName === 'third'" />
</el-tab-pane>
</el-tabs>
</div>
</el-dialog>
<!-- 调查表 -->
<el-dialog v-if="researchInfoVisible" :visible.sync="researchInfoVisible" :fullscreen="true"
:close-on-click-modal="false">
<research-form v-if="researchInfoVisible" @refreshPage="getList" />
</el-dialog>
<!-- 调查表编辑 -->
<el-dialog v-if="ImageManualVisible" :visible.sync="ImageManualVisible" :fullscreen="true"
:close-on-click-modal="false" :title="$t('trials:researchRecord:dialogTitle:ImageManualEdit')">
<ImageManual v-if="ImageManualVisible" :trialSiteSurveyId="trialSiteSurveyId" @getList="getList" />
</el-dialog>
<!-- 调查表链接 -->
<base-model :config="share_model">
<template slot="dialog-body" v-loading="shareLoading">
<el-button size="small" type="primary" style="margin-bottom: 10px;" @click.stop="openImageManual">{{
$t('trials:researchRecord:label:edit')
}}</el-button>
<div style="width: 100%; display: flex">
<div class="date">
<el-form :model="shareForm" :rules="rules" ref="shareForm" label-width="100px">
<el-form-item :label="$t('trials:researchRecord:label:ExpirationDays')" prop="ExpirationDays">
<el-radio-group v-model="shareForm.ExpirationDays" @input="shareForm.OtherExpirationDays = null">
<el-radio :label="1">{{ $t('trials:researchRecord:label:day1') }}</el-radio>
<el-radio :label="7">{{ $t('trials:researchRecord:label:day7') }}</el-radio>
<el-radio :label="15">{{ $t('trials:researchRecord:label:day15') }}</el-radio>
<el-radio :label="`default`">{{ $t('trials:researchRecord:label:default') }}
<el-input placeholder="" type="number" @input="handleInput" v-model="shareForm.OtherExpirationDays"
clearable size="mini" style="width: 60px;" class="dayInput" />
<span style="margin-left: 10px;">{{ $t('trials:researchRecord:label:day') }}</span>
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('trials:researchRecord:label:LinkVerificationCode')" prop="LinkVerificationCode">
<el-radio-group v-model="shareForm.LinkVerificationCode"
@input="shareForm.OtherLinkVerificationCode = null">
<el-radio label="researchProgramNo">{{ $t('trials:researchRecord:label:researchProgramNo')
}}</el-radio>
<el-radio label="default">{{ $t('trials:researchRecord:label:default') }}
<el-input placeholder="" type="number" v-model="shareForm.OtherLinkVerificationCode" clearable
size="mini" style="width: 100px;" />
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<el-button type="primary" round @click="setLink" style="float: right;">
{{ $t('trials:researchRecord:button:setLink') }}
</el-button>
</div>
<div class="share">
<div class="shareLink">
<h3>{{ $t('trials:researchRecord:label:link') }}</h3>
<el-input ref="shareLink" v-model="shareLink" readonly type="textarea" autosize :rows="3" />
<div class="dateBox">
<span>{{ $t('trials:researchRecord:label:linkVerificationCode') }}</span>
<span>{{ LinkVerificationCode }}</span>
</div>
<div class="dateBox">
<span>{{ $t('trials:researchRecord:label:ValidityPeriod') }}</span>
<span>{{ validityPeriod }}</span>
<span style="color: red;" v-if="isExpired">({{ $t('trials:researchRecord:label:Expired') }})</span>
</div>
<el-button type="primary" round @click="copyLink" class="shareLinkBtn"
:disabled="!validityPeriod || isExpired">
{{ $t('trials:researchRecord:button:copyLink') }}
</el-button>
</div>
<div class="shareCode">
<h3>{{ $t('trials:researchRecord:label:shareCode') }}</h3>
<div style="display: flex;align-items: center;justify-content: space-between;">
<div class="qrCodeBox">
<div id="qrcode" ref="qrcode"></div>
</div>
<div class="codeBtnBox">
<el-button @click="handleCopyImg" type="primary" round :disabled="!validityPeriod || isExpired">{{
$t('trials:researchRecord:button:copyCode')
}}</el-button>
<el-button @click="savePic" round :disabled="!validityPeriod || isExpired">{{
$t('trials:researchRecord:button:savePic')
}}</el-button>
</div>
</div>
</div>
</div>
</div>
</template>
</base-model>
<el-dialog v-if="logVisible" :visible.sync="logVisible"
:custom-class="isFullscreen ? 'full-log-dialog-container' : 'log-dialog-container'" :close-on-click-modal="false"
:fullscreen="isFullscreen" :show-close="false" width="60%" append-to-body>
<span slot="title" class="dialog-footer">
<div style="display: flex;flex-direction: row;justify-content: space-between;">
<div>
{{ $t('trials:researchForm:button:log') }}
</div>
<div>
<svg-icon :icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'"
style="vertical-align: baseline;cursor: pointer;font-size: 20px;" @click="toggleLogFullscreen" />
<svg-icon icon-class="dClose" style="cursor: pointer;font-size: 25px;margin-left: 10px;"
@click="logVisible = false" />
</div>
</div>
</span>
<div class="log-dialog-content">
<LogList :trial-site-survey-id="trialSiteSurveyId" />
</div>
</el-dialog>
</BaseContainer>
</template>
<script>
import {
getTrialSiteSurveyList,
getTrialSiteSelect,
abandonSiteSurvey,
setTrialLinkExpirationTime,
getTrialLinkExpirationTime,
getLinkLinkExpirationTime,
} from '@/api/trials'
import { changeURLStatic } from '@/utils/history.js'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import Users from './users'
import Equipments from './Equipments'
import Questions from './Questions'
import ResearchForm from '@/views/research/form'
import BaseModel from '@/components/BaseModel'
import ImageManual from './ImageManual'
import QRCode from 'qrcodejs2'
import LogList from '@/views/research/components/LogList'
import { getTrialSiteSurveyList_Export } from '@/api/export'
const searchDataDefault = () => {
return {
SortField: '',
Asc: true,
PageIndex: 1,
PageSize: 20,
TrialSiteId: '',
UserKeyInfo: '',
State: null,
IsDeleted: '',
DateRange: [],
PreliminaryUserName: null,
ReviewerUserName: null,
}
}
export default {
name: 'ResearchList',
components: { BaseContainer, Pagination, Users, Equipments, Questions, ResearchForm, BaseModel, ImageManual, LogList },
props: {
isAudit: {
type: Boolean,
default: false
}
},
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
trialId: this.$route.query.trialId,
siteOptions: [],
researchUserVisible: false,
researchInfoVisible: false,
share_model: {
visible: false,
title: this.$t('trials:researchRecord:title:shark'),
width: '1000px',
},
shareLink: '',
researchState: this.$d.ResearchRecord,
qrcode: null,
validityPeriod: null,
LinkVerificationCode: null,
isExpired: false,
shareLoading: false,
shareForm: {
ExpirationDays: null,
OtherExpirationDays: null,
LinkVerificationCode: null,
OtherLinkVerificationCode: null,
},
rules: {
ExpirationDays: [
{ required: true, message: this.$t("common:ruleMessage:select"), trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value === 'default' && !this.shareForm.OtherExpirationDays) {
callback(new Error(this.$t("common:ruleMessage:specify")));
} else {
callback()
}
}, trigger: 'blur'
}
],
LinkVerificationCode: [
{ required: true, message: this.$t("common:ruleMessage:select"), trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value === 'default' && !this.shareForm.OtherLinkVerificationCode) {
callback(new Error(this.$t("common:ruleMessage:specify")));
} else {
callback()
}
}, trigger: 'blur'
}
],
},
ImageManualVisible: false,
trialSiteSurveyId: null,
activeName: 'first',
isFullscreen: false,
logVisible: false
}
},
mounted() {
// this.getList()
this.getSite()
},
methods: {
async handleExport() {
await getTrialSiteSurveyList_Export(this.searchData)
},
toggleLogFullscreen() {
this.isFullscreen = !this.isFullscreen
},
viewLog(row) {
this.trialSiteSurveyId = row.Id
this.isFullscreen = false
this.logVisible = true
},
async getLinkTimeIsExpired() {
try {
let data = {
TrialId: this.$route.query.trialId,
}
this.shareLoading = true
let res = await getLinkLinkExpirationTime(data)
this.shareLoading = false
if (res.IsSuccess) {
this.isExpired = res.Result.IsIsExpired
}
} catch (err) {
this.shareLoading = false
console.log(err)
}
},
async getLinkTime() {
try {
let data = {
TrialId: this.$route.query.trialId,
}
this.shareLoading = true
let res = await getTrialLinkExpirationTime(data)
this.shareLoading = false
if (res.IsSuccess) {
this.validityPeriod = res.Result.LinkExpirationTime
this.LinkVerificationCode = res.Result.LinkVerificationCode || res.Result.ResearchProgramNo
if (!this.validityPeriod) {
this.LinkVerificationCode = null
}
if (!this.validityPeriod) return false
this.getLinkTimeIsExpired()
this.shareLink = `${location.protocol}//${location.host}/researchLogin?trialId=${this.$route.query.trialId}`
this.$nextTick(() => {
this.creatQrCode()
})
}
} catch (err) {
this.shareLoading = false
console.log(err)
}
},
async setLink() {
try {
let validate = await this.$refs.shareForm.validate()
if (!validate) return false
let data = {
TrialId: this.$route.query.trialId,
ExpirationDays: this.shareForm.ExpirationDays,
LinkVerificationCode: this.shareForm.LinkVerificationCode
}
if (this.shareForm.LinkVerificationCode === 'researchProgramNo') {
data.LinkVerificationCode = null
}
if (this.shareForm.LinkVerificationCode === 'default') {
data.LinkVerificationCode = this.shareForm.OtherLinkVerificationCode
}
if (this.shareForm.ExpirationDays === 'default') {
data.ExpirationDays = this.shareForm.OtherExpirationDays
}
this.shareLoading = true
let res = await setTrialLinkExpirationTime(data)
this.shareLoading = false
if (res.IsSuccess) {
this.getLinkTime()
}
} catch (err) {
this.shareLoading = false
console.log(err)
}
},
handleInput(val) {
//
this.shareForm.OtherExpirationDays = val.replace(/[^\d]/g, '').replace(/^0+/, '')
},
openImageManual() {
// if (!this.trialSiteSurveyId) return false
this.ImageManualVisible = true
},
//
getList() {
this.loading = true
this.searchData.TrialId = this.trialId
if (this.searchData.DateRange && this.searchData.DateRange.length === 2) {
this.searchData.UpdateTimeBegin = this.searchData.DateRange[0]
this.searchData.updateTimeEnd = this.searchData.DateRange[1]
} else {
this.searchData.UpdateTimeBegin = ''
this.searchData.updateTimeEnd = ''
}
getTrialSiteSurveyList(this.searchData)
.then((res) => {
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
})
.catch(() => {
this.loading = false
})
},
//
handleViewResearchList(row) {
changeURLStatic('trialSiteSurveyId', row.Id)
this.researchInfoVisible = true
},
//
handleRepealResearch(row) {
//
this.$confirm(this.$t('trials:researchRecord:message:abolish'), {
type: 'warning',
distinguishCancelAndClose: true,
}).then(() => {
abandonSiteSurvey(this.trialId, row.Id).then((res) => {
if (res.IsSuccess) {
this.getList()
//
this.$message.success(
this.$t('trials:researchRecord:message:abolishSuccessfully')
)
}
})
})
},
//
showResearchUser() {
this.researchUserVisible = true
this.activeName = 'first'
},
//
copyLink() {
//
this.$copyText(
`${this.$t('trials:researchRecord:message:researchFormLink')}: ${this.shareLink
}\n${this.$t('trials:researchRecord:label:linkVerificationCode')}${this.LinkVerificationCode}\n${this.$t('trials:researchRecord:label:ValidityPeriod')}${this.validityPeriod}`
)
.then((res) => {
//
this.$message.success(
this.$t('trials:researchRecord:message:copySuccessfully')
)
})
.catch(() => {
//
this.$alert(this.$t('trials:researchRecord:message:copyFailed'))
})
},
//
creatQrCode() {
this.$refs.qrcode.innerHTML = '' //
let text = this.shareLink
this.qrcode = new QRCode(this.$refs.qrcode, {
text: text, // ,#
width: 200,
height: 200,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H,
})
// qrcode.clear(); //
},
//
savePic() {
let qrCodeCanvas = document
.getElementById('qrcode')
.getElementsByTagName('canvas')
let a = document.createElement('a')
a.href = qrCodeCanvas[0].toDataURL('image/url')
a.download = `${this.$route.query.researchProgramNo}${this.$t('trials:researchRecord:title:code')}.png`
a.click()
},
//
handleCopyImg() {
let qrCodeCanvas = document
.getElementById('qrcode')
.getElementsByTagName('canvas')
qrCodeCanvas[0].toBlob(async (blob) => {
console.log(blob)
const data = [
new ClipboardItem({
[blob.type]: blob,
}),
] // https://w3c.github.io/clipboard-apis/#dom-clipboard-write
await navigator.clipboard.write(data).then(
() => {
this.$message.success(
this.$t('trials:researchRecord:message:copySuccess')
)
},
() => {
this.$message.error(
this.$t('trials:researchRecord:message:UnableWrite')
)
}
)
})
},
// site
getSite() {
getTrialSiteSelect(this.trialId).then((res) => {
this.siteOptions = res.Result
})
},
//
showResearchLink() {
this.shareForm = {
ExpirationDays: null,
OtherExpirationDays: null,
LinkVerificationCode: null,
OtherLinkVerificationCode: null,
}
this.validityPeriod = null
this.isExpired = false
this.share_model.visible = true
this.getLinkTime()
// &lang=${this.$i18n.locale}
// this.trialSiteSurveyId = this.list[0].Id
},
//
handleReset() {
const state = this.searchData.State
this.searchData = searchDataDefault()
this.searchData.DateRange = []
if (this.searchData.DateRange && this.searchData.DateRange.length === 2) {
this.searchData.UpdateTimeBegin = this.searchData.DateRange[0]
this.searchData.updateTimeEnd = this.searchData.DateRange[1]
} else {
this.searchData.UpdateTimeBegin = ''
this.searchData.updateTimeEnd = ''
}
this.searchData.State = state
this.getList()
this.$nextTick(() => {
this.$refs.siteResearchList.clearSort()
})
},
//
handleSearch() {
this.getList()
},
//
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.getList()
},
},
beforeDestroy() {
if (this.qrcode) {
this.qrcode = null
}
},
}
</script>
<style lang="scss" scoped>
.date,
.share {
width: 55%;
height: 100%;
}
.dayInput {
::v-deep .el-input__inner {
padding-left: 5px;
}
}
.date {
padding-right: 10px;
}
.share {
width: 45%;
padding-left: 5%;
border-left: 1px solid #f0f0f0;
box-sizing: border-box;
}
.shareLinkBtn {
float: right;
}
.qrCodeBox {
width: 220px;
height: 220px;
display: flex;
border: 1px solid #c0c4cc;
border-radius: 5px;
box-shadow: 1px 1px 5px #c0c4cc;
align-items: center;
justify-content: center;
}
.dateBox {
margin: 10px;
}
.codeBtnBox {
::v-deep .el-button {
display: block;
margin: 10px auto;
}
}
</style>

View File

@ -6,31 +6,33 @@
<!-- 中心编号 -->
<el-form-item :label="$t('trials:researchStaff:table:siteId')">
<el-select v-model="searchData.TrialSiteId" clearable filterable style="width:120px;">
<el-option
v-for="(item,index) of siteOptions"
:key="index"
:label="item.TrialSiteCode"
:value="item.TrialSiteId"
/>
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteCode"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<!-- 状态 -->
<el-form-item :label="$t('trials:researchRecord:table:status')">
<el-select v-model="searchData.State" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of $d.ResearchRecord" :key="index" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 是否废除 -->
<!-- <el-form-item :label="$t('trials:researchRecord:table:isDeleted')">
<el-select v-model="searchData.IsDeleted" clearable filterable style="width: 120px">
<el-option v-for="item of $d.YesOrNo" v-show="item.raw.ValueCN !== ''" :key="`IsDeleted${item.value}`"
:label="item.label" :value="item.value" />
</el-select>
</el-form-item> -->
<!-- 姓名 -->
<el-form-item :label="$t('trials:researchStaff:table:Name')">
<el-input v-model="searchData.UserName" class="mr" clearable style="width:120px;" />
</el-form-item>
<!-- 角色 -->
<el-form-item :label="$t('trials:researchStaff:table:role')">
<el-select
v-model="searchData.TrialRoleCode"
style="width:120px;"
clearable
>
<el-option
v-for="item of dict.type.SiteSurvey_UserRoles"
:key="item.value"
:label="item.label"
:value="parseInt(item.raw.Code)"
/>
<el-select v-model="searchData.TrialRoleCode" style="width:120px;" clearable>
<el-option v-for="item of dict.type.SiteSurvey_UserRoles" :key="item.value" :label="item.label"
:value="parseInt(item.raw.Code)" />
</el-select>
</el-form-item>
<!-- 单位 -->
@ -39,42 +41,19 @@
</el-form-item>
<!-- 是否生成账号 -->
<el-form-item :label="$t('trials:researchStaff:table:isGenerateAccount')">
<el-select
v-model="searchData.IsGenerateAccount"
style="width:120px;"
clearable
>
<el-option v-for="item of $d.YesOrNo" :key="`IsGenerateAccount${item.value}`" :label="item.label" :value="item.value" />
<el-select v-model="searchData.IsGenerateAccount" style="width:120px;" clearable>
<el-option v-for="item of $d.YesOrNo" :key="`IsGenerateAccount${item.value}`" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 用户类型 -->
<el-form-item :label="$t('trials:researchStaff:table:userType')">
<el-select
v-model="searchData.UserTypeId"
style="width:120px;"
clearable
>
<el-option
v-for="item of userTypeOptions"
:key="item.Id"
:label="item.UserTypeShortName"
:value="item.Id"
>
<el-select v-model="searchData.UserTypeId" style="width:120px;" clearable>
<el-option v-for="item of userTypeOptions" :key="item.Id" :label="item.UserTypeShortName" :value="item.Id">
<span>{{ item.UserType }}</span>
</el-option>
</el-select>
</el-form-item>
<!-- 状态 -->
<!-- <el-form-item :label="$t('trials:researchStaff:table:status')">
<el-select v-model="searchData.State" clearable filterable style="width:120px;">
<el-option
v-for="(item,index) of researchUserState"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item> -->
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
@ -89,25 +68,15 @@
</el-form-item>
<!-- 发邮件 -->
<el-form-item>
<el-button
v-hasPermi="['trials:trials-panel:attachments:site-research:summary-record:sendEmail']"
type="primary"
icon="el-icon-message"
:loading="sendEmailLoading"
:disabled="selectArr.length === 0"
@click="handleSendEmail"
>
<el-button v-hasPermi="['trials:trials-panel:attachments:site-research:summary-record:sendEmail']"
type="primary" icon="el-icon-message" :loading="sendEmailLoading" :disabled="selectArr.length === 0"
@click="handleSendEmail">
{{ $t('trials:researchRecord:dialogButton:sendEmail') }}
</el-button>
</el-form-item>
<!-- 导出 -->
<el-form-item>
<el-button
type="primary"
icon="el-icon-download"
:disabled="list.length === 0"
@click="handleExport"
>
<el-button type="primary" icon="el-icon-download" :disabled="list.length === 0" @click="handleExport">
{{ $t('common:button:export') }}
</el-button>
</el-form-item>
@ -116,96 +85,74 @@
<template slot="main-container">
<!-- 系统文件列表 -->
<el-table
ref="researchUsers"
v-loading="loading"
v-adaptive="{bottomOffset:70}"
:data="list"
stripe
height="100"
@selection-change="handleSelectChange"
@sort-change="handleSortByColumn"
>
<el-table-column
type="selection"
width="50"
:selectable="handleSelectTable"
/>
<el-table ref="researchUsers" v-loading="loading" v-adaptive="{ bottomOffset: 55 }" :data="list" stripe
height="100" @selection-change="handleSelectChange" @sort-change="handleSortByColumn">
<el-table-column type="selection" width="50" :selectable="handleSelectTable" />
<el-table-column type="index" width="50" />
<!-- 中心编号 -->
<el-table-column
prop="TrialSiteSurvey.TrialSiteCode"
:label="$t('trials:researchStaff:table:siteId')"
show-overflow-tooltip
sortable="custom"
min-width="100"
>
<el-table-column prop="TrialSiteSurvey.TrialSiteCode" :label="$t('trials:researchStaff:table:siteId')"
show-overflow-tooltip sortable="custom" min-width="100">
<template slot-scope="scope">{{ `${scope.row.TrialSiteSurvey.TrialSiteCode}` }}</template>
</el-table-column>
<!-- 中心名称 -->
<el-table-column
prop="TrialSiteSurvey.SiteName"
:label="$t('trials:researchStaff:table:siteName')"
show-overflow-tooltip
sortable="custom"
min-width="120"
>
<el-table-column prop="TrialSiteSurvey.SiteName" :label="$t('trials:researchStaff:table:siteName')"
show-overflow-tooltip sortable="custom" min-width="120">
<template slot-scope="scope">{{ `${scope.row.TrialSiteSurvey.SiteName}` }}</template>
</el-table-column>
<!-- 状态 -->
<el-table-column prop="State" :label="$t('trials:researchRecord:table:status')" min-width="150"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.State === 0" type="primary">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 1" type="info">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 2" type="warning">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 3" type="danger">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
</template>
</el-table-column>
<!-- 是否废除 -->
<!-- <el-table-column prop="IsDeleted" :label="$t('trials:researchRecord:table:isDeleted')" min-width="100"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted" type="danger">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
<el-tag v-else type="primary">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
</template>
</el-table-column> -->
<!-- 姓名 -->
<el-table-column
prop="LastName"
:label="$t('trials:researchStaff:table:Name')"
show-overflow-tooltip
sortable="custom"
min-width="100"
>
<el-table-column prop="LastName" :label="$t('trials:researchStaff:table:Name')" show-overflow-tooltip
sortable="custom" min-width="100">
<template slot-scope="scope">{{ `${scope.row.LastName} / ${scope.row.FirstName}` }}</template>
</el-table-column>
<!-- 角色 -->
<el-table-column
prop="TrialRoleCode"
:label="$t('trials:researchStaff:table:role')"
show-overflow-tooltip
sortable="custom"
min-width="100"
>
<el-table-column prop="TrialRoleCode" :label="$t('trials:researchStaff:table:role')" show-overflow-tooltip
sortable="custom" min-width="100">
<template slot-scope="scope">
{{ $fd('SiteSurvey_UserRoles',scope.row.TrialRoleCode) }}
{{ $fd('SiteSurvey_UserRoles', scope.row.TrialRoleCode) }}
</template>
</el-table-column>
<!-- 电话号码 -->
<el-table-column
prop="Phone"
:label="$t('trials:researchStaff:table:phone')"
show-overflow-tooltip
sortable="custom"
min-width="120"
/>
<el-table-column prop="Phone" :label="$t('trials:researchStaff:table:phone')" show-overflow-tooltip
sortable="custom" min-width="120" />
<!-- 邮箱 -->
<el-table-column
prop="Email"
:label="$t('trials:researchStaff:table:email')"
show-overflow-tooltip
sortable="custom"
min-width="150"
/>
<el-table-column prop="Email" :label="$t('trials:researchStaff:table:email')" show-overflow-tooltip
sortable="custom" min-width="150" />
<!-- 单位 -->
<el-table-column
prop="OrganizationName"
:label="$t('trials:researchStaff:table:organization')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/>
<el-table-column prop="OrganizationName" :label="$t('trials:researchStaff:table:organization')"
show-overflow-tooltip sortable="custom" min-width="100" />
<!-- 是否生成账号 -->
<el-table-column
prop="IsGenerateAccount"
:label="$t('trials:researchStaff:table:isGenerateAccount')"
show-overflow-tooltip
sortable="custom"
min-width="130"
>
<el-table-column prop="IsGenerateAccount" :label="$t('trials:researchStaff:table:isGenerateAccount')"
show-overflow-tooltip sortable="custom" min-width="130">
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsGenerateAccount) }}
</template>
@ -223,21 +170,11 @@
<!-- </el-table-column>-->
<!-- 用户类型 -->
<el-table-column
prop="UserType"
:label="$t('trials:researchStaff:table:userType')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/>
<el-table-column prop="UserType" :label="$t('trials:researchStaff:table:userType')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 是否加入 -->
<el-table-column
prop="IsJoin"
:label="$t('trials:researchStaff:table:isJoin')"
show-overflow-tooltip
sortable="custom"
min-width="100"
>
<el-table-column prop="IsJoin" :label="$t('trials:researchStaff:table:isJoin')" show-overflow-tooltip
sortable="custom" min-width="100">
<template slot-scope="scope">
<el-tag v-if="scope.row.IsJoin" type="info">{{ $fd('IsJoin', scope.row.IsJoin) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('IsJoin', scope.row.IsJoin) }}</el-tag>
@ -246,7 +183,8 @@
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</template>
</BaseContainer>
</template>
@ -296,7 +234,6 @@ export default {
}
},
mounted() {
console.log(this.dict.type.SiteSurvey_UserRoles)
this.getList()
this.getSite()
this.getUserType()

View File

@ -1,502 +1,208 @@
<template>
<BaseContainer>
<!-- 搜索框 -->
<template slot="search-container">
<el-form :inline="true">
<!-- 中心 -->
<el-form-item :label="$t('trials:researchRecord:table:siteId')">
<el-select v-model="searchData.TrialSiteId" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteCode"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<!-- 联系人 -->
<el-form-item :label="$t('trials:researchRecord:table:contactor')">
<el-input v-model="searchData.UserKeyInfo" class="mr" clearable :placeholder="`${$t(
'trials:researchRecord:placeholder:contactorInfo'
)}`" style="width: 140px" />
</el-form-item>
<!-- 初审人 -->
<el-form-item :label="$t('trials:researchRecord:table:preliminaryUser')">
<el-input v-model="searchData.PreliminaryUserName" class="mr" clearable style="width: 140px" />
</el-form-item>
<!-- 审核人 -->
<el-form-item :label="$t('trials:researchRecord:table:ReviewerUser')">
<el-input v-model="searchData.ReviewerUserName" class="mr" clearable style="width: 140px" />
</el-form-item>
<!-- 状态 -->
<el-form-item :label="$t('trials:researchRecord:table:status')">
<el-select v-model="searchData.State" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of $d.ResearchRecord" :key="index" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 是否废除 -->
<el-form-item :label="$t('trials:researchRecord:table:isDeleted')">
<el-select v-model="searchData.IsDeleted" clearable filterable style="width: 120px">
<el-option v-for="item of $d.YesOrNo" v-show="item.raw.ValueCN !== ''" :key="`IsDeleted${item.value}`"
:label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 更新时间 -->
<el-form-item :label="$t('trials:researchRecord:table:updateTime')">
<el-date-picker v-model="searchData.DateRange" type="daterange" value-format="yyyy-MM-dd" format="yyyy-MM-dd"
style="width: 250px" />
</el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
<!-- 中心人员 -->
<el-button v-hasPermi="[
'trials:trials-panel:attachments:site-research:summary-record',
]" type="primary" icon="el-icon-info" @click="showResearchUser">
{{ $t('trials:researchRecord:button:questionStaffs') }}
</el-button>
<!-- 调查表链接 -->
<el-button v-hasPermi="[
'trials:trials-panel:attachments:site-research:questionnaire-link',
]" type="primary" icon="el-icon-link" @click="showResearchLink">
{{ $t('trials:researchRecord:button:questionLink') }}
</el-button>
</el-form>
</template>
<template slot="main-container">
<!-- 系统文件列表 -->
<el-table ref="siteResearchList" v-loading="loading" v-adaptive="{ bottomOffset: 60 }" :data="list" stripe
height="100" @sort-change="handleSortByColumn">
<el-table-column type="index" width="50" />
<!-- 中心编号 -->
<el-table-column prop="TrialSiteCode" :label="$t('trials:researchRecord:table:siteId')" min-width="100"
sortable="custom" show-overflow-tooltip />
<!-- 中心名称 -->
<el-table-column prop="SiteName" :label="$t('trials:researchRecord:table:siteName')" min-width="100"
sortable="custom" show-overflow-tooltip />
<!-- 联系人 -->
<el-table-column prop="UserName" :label="$t('trials:researchRecord:table:contactor')" min-width="100"
sortable="custom" show-overflow-tooltip />
<!-- 联系电话 -->
<el-table-column prop="Phone" :label="$t('trials:researchRecord:table:contactorPhone')" min-width="100"
show-overflow-tooltip />
<!-- 联系邮箱 -->
<el-table-column prop="Email" :label="$t('trials:researchRecord:table:contactorEmail')" min-width="150"
show-overflow-tooltip />
<!-- 初审人 -->
<el-table-column prop="preliminaryUser" :label="$t('trials:researchRecord:table:preliminaryUser')"
min-width="150" show-overflow-tooltip>
<template slot-scope="scope">
{{
scope.row.PreliminaryUser
? scope.row.PreliminaryUser.RealName
: ''
}}
</template>
</el-table-column>
<!-- 审核人 -->
<el-table-column prop="ReviewerUser" :label="$t('trials:researchRecord:table:ReviewerUser')" min-width="150"
show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.ReviewerUser ? scope.row.ReviewerUser.RealName : '' }}
</template>
</el-table-column>
<!-- 状态 -->
<el-table-column prop="State" :label="$t('trials:researchRecord:table:status')" min-width="150"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.State === 0" type="primary">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 1" type="info">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 2" type="warning">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 3" type="danger">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
</template>
</el-table-column>
<!-- 是否废除 -->
<el-table-column prop="IsDeleted" :label="$t('trials:researchRecord:table:isDeleted')" min-width="100"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted" type="danger">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
<el-tag v-else type="primary">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
</template>
</el-table-column>
<!-- 更新时间 -->
<el-table-column prop="UpdateTime" :label="$t('trials:researchRecord:table:updateTime')" min-width="150"
show-overflow-tooltip sortable="custom" />
<el-table-column width="150">
<template slot-scope="scope">
<!-- 查看 -->
<el-button :disabled="scope.row.State !== 3" circle :title="$t('common:button:view')" icon="el-icon-view"
@click="handleViewResearchList(scope.row)" />
<!-- 审批 -->
<el-button v-hasPermi="[
'trials:trials-panel:attachments:site-research:auidt',
]" :disabled="scope.row.State === 0 || scope.row.State === 3 || scope.row.IsDeleted" circle
:title="$t('trials:researchRecord:action:view')" icon="el-icon-s-check"
@click="handleViewResearchList(scope.row)" />
<!-- 废除 -->
<el-button v-hasPermi="[
'trials:trials-panel:attachments:site-research:abolish',
]" :disabled="scope.row.State !== 0 || scope.row.IsDeleted" circle
:title="$t('trials:researchRecord:action:abolish')" icon="el-icon-delete"
@click="handleRepealResearch(scope.row)" />
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</template>
<!-- 中心人员 -->
<el-dialog v-if="researchUserVisible" :visible.sync="researchUserVisible"
:title="$t('trials:researchRecord:dialogTitle:questionStaff')" custom-class="base-dialog-wrapper"
:fullscreen="true">
<div class="base-modal-body" style="border: 1px solid #ccc; padding: 10px">
<Users v-if="researchUserVisible" />
<div class="step-wrapper">
<el-steps :active="activeStatus" align-center :space="300">
<!-- 中心填写 -->
<el-step :title="$t('trials:siteResearch:timeline:step1')" class="click_cursor"
@click.native="handleClick(0)" />
<!-- SPM审核 -->
<el-step v-if="isSPMJoin" :title="$t('trials:siteResearch:timeline:step2')" class="click_cursor"
@click.native="handleClick(1)" />
<!-- PM审核 -->
<el-step :title="$t('trials:siteResearch:timeline:step3')" class="click_cursor"
@click.native="handleClick(2)" />
<!-- 审核完毕 -->
<el-step :title="$t('trials:siteResearch:timeline:step14')" class="click_cursor"
@click.native="handleClick(3)" />
</el-steps>
<div class="step-content">
<ResearchList ref="researchList" :isAudit="isAudit" />
</div>
</el-dialog>
<!-- 调查表 -->
<el-dialog v-if="researchInfoVisible" :visible.sync="researchInfoVisible" :fullscreen="true"
:close-on-click-modal="false">
<research-form v-if="researchInfoVisible" @refreshPage="getList" />
</el-dialog>
<!-- 调查表编辑 -->
<el-dialog v-if="ImageManualVisible" :visible.sync="ImageManualVisible" :fullscreen="true"
:close-on-click-modal="false" :title="$t('trials:researchRecord:dialogTitle:ImageManualEdit')">
<ImageManual v-if="ImageManualVisible" :trialSiteSurveyId="trialSiteSurveyId" @getList="getList" />
</el-dialog>
<!-- 调查表链接 -->
<base-model :config="share_model">
<template slot="dialog-body">
<el-button size="small" type="primary" style="margin-bottom: 10px;" @click.stop="openImageManual">{{
$t('trials:researchRecord:label:edit')
}}</el-button>
<div style="width: 100%; display: flex">
<div class="shareLink">
<!-- <div>
<i style="color: #428bca" class="el-icon-success" />
<span>{{
$t('trials:researchRecord:message:createLinkSuccessfully')
}}</span>
</div> -->
<div style="margin: 10px 0">
<!-- 链接 -->
{{ $t('trials:researchRecord:label:link') }}
<el-input ref="shareLink" v-model="shareLink" readonly type="textarea" autosize />
</div>
<div>
<!-- 复制链接 -->
<el-button type="primary" round @click="copyLink" class="shareLinkBtn">
{{ $t('trials:researchRecord:button:copyLink') }}
</el-button>
</div>
</div>
<div class="shareCode">
<div class="qrCode">
<div id="qrcode" ref="qrcode"></div>
</div>
<div class="codeBtnBox">
<el-button @click="handleCopyImg" type="primary" round>{{
$t('trials:researchRecord:button:copyCode')
}}</el-button>
<el-button @click="savePic" round>{{
$t('trials:researchRecord:button:savePic')
}}</el-button>
</div>
</div>
</div>
</template>
</base-model>
</div>
</BaseContainer>
</template>
<script>
import {
getTrialSiteSurveyList,
getTrialSiteSelect,
abandonSiteSurvey,
} from '@/api/trials'
import { changeURLStatic } from '@/utils/history.js'
import { getTrialIsSPMJoin } from '@/api/research'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import Users from './components/users'
import ResearchForm from '@/views/research/form'
import BaseModel from '@/components/BaseModel'
import ImageManual from './components/ImageManual'
import QRCode from 'qrcodejs2'
const searchDataDefault = () => {
return {
SortField: '',
Asc: true,
PageIndex: 1,
PageSize: 20,
TrialSiteId: '',
UserKeyInfo: '',
State: null,
IsDeleted: '',
DateRange: [],
PreliminaryUserName: null,
ReviewerUserName: null,
}
}
import ResearchList from './components/ResearchList'
export default {
name: 'SiteResearchList',
components: { BaseContainer, Pagination, Users, ResearchForm, BaseModel, ImageManual },
name: 'SiteResearch',
components: {
BaseContainer,
ResearchList
},
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
trialId: this.$route.query.trialId,
siteOptions: [],
researchUserVisible: false,
researchInfoVisible: false,
share_model: {
visible: false,
title: this.$t('trials:researchRecord:title:shark'),
width: '800px',
},
shareLink: '',
researchState: this.$d.ResearchRecord,
qrcode: null,
isSPMJoin: true,
activeStatus: 0
}
},
watch: {
activeStatus: {
deep: true,
immediate: true,
handler(v) {
this.$nextTick(() => {
if (!this.isSPMJoin && v > 0) {
this.$refs['researchList'].searchData.State = v + 1
} else {
this.$refs['researchList'].searchData.State = v
}
ImageManualVisible: false,
trialSiteSurveyId: null
this.$refs['researchList'].getList()
})
}
}
},
computed: {
isAudit() {
if (this.isSPMJoin) {
return (this.hasPermi(['role:spm', 'role:cpm', 'role:apm']) && this.activeStatus === 1) || (this.hasPermi(['role:pm']) && this.activeStatus === 2)
} else {
return this.hasPermi(['role:pm']) && this.activeStatus === 1
}
}
},
mounted() {
this.getList()
this.getSite()
this.getTrialIsSPMJoin()
},
methods: {
openImageManual() {
// if (!this.trialSiteSurveyId) return false
this.ImageManualVisible = true
},
//
getList() {
this.loading = true
this.searchData.TrialId = this.trialId
if (this.searchData.DateRange && this.searchData.DateRange.length === 2) {
this.searchData.UpdateTimeBegin = this.searchData.DateRange[0]
this.searchData.updateTimeEnd = this.searchData.DateRange[1]
} else {
this.searchData.UpdateTimeBegin = ''
this.searchData.updateTimeEnd = ''
async getTrialIsSPMJoin() {
try {
let trialId = this.$route.query.trialId
if (!trialId) return
let res = await getTrialIsSPMJoin(trialId)
this.isSPMJoin = res.Result
if (this.isSPMJoin) {
if (this.hasPermi(['role:pm'])) this.activeStatus = 2
if (this.hasPermi(['role:spm', 'role:cpm', 'role:apm'])) this.activeStatus = 1
} else {
if (this.hasPermi(['role:pm'])) this.activeStatus = 1
if (this.hasPermi(['role:spm', 'role:cpm', 'role:apm'])) this.activeStatus = 0
}
} catch (e) {
console.log(e)
}
getTrialSiteSurveyList(this.searchData)
.then((res) => {
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
})
.catch(() => {
this.loading = false
})
},
//
handleViewResearchList(row) {
changeURLStatic('trialSiteSurveyId', row.Id)
this.researchInfoVisible = true
handleClick(step) {
this.activeStatus = step
if (!this.isSPMJoin && step > 0) this.activeStatus = step - 1
},
//
handleRepealResearch(row) {
//
this.$confirm(this.$t('trials:researchRecord:message:abolish'), {
type: 'warning',
distinguishCancelAndClose: true,
}).then(() => {
abandonSiteSurvey(this.trialId, row.Id).then((res) => {
if (res.IsSuccess) {
this.getList()
//
this.$message.success(
this.$t('trials:researchRecord:message:abolishSuccessfully')
)
}
})
})
},
//
showResearchUser() {
this.researchUserVisible = true
},
//
copyLink() {
//
this.$copyText(
`${this.$t('trials:researchRecord:message:researchFormLink')}: ${this.shareLink
}`
)
.then((res) => {
//
this.$message.success(
this.$t('trials:researchRecord:message:copySuccessfully')
)
})
.catch(() => {
//
this.$alert(this.$t('trials:researchRecord:message:copyFailed'))
})
},
//
creatQrCode() {
this.$refs.qrcode.innerHTML = '' //
let text = this.shareLink
this.qrcode = new QRCode(this.$refs.qrcode, {
text: text, // ,#
width: 200,
height: 200,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H,
})
// qrcode.clear(); //
},
//
savePic() {
let qrCodeCanvas = document
.getElementById('qrcode')
.getElementsByTagName('canvas')
let a = document.createElement('a')
a.href = qrCodeCanvas[0].toDataURL('image/url')
a.download = `${this.$route.query.researchProgramNo}${this.$t('trials:researchRecord:title:code')}.png`
a.click()
},
//
handleCopyImg() {
let qrCodeCanvas = document
.getElementById('qrcode')
.getElementsByTagName('canvas')
qrCodeCanvas[0].toBlob(async (blob) => {
console.log(blob)
const data = [
new ClipboardItem({
[blob.type]: blob,
}),
] // https://w3c.github.io/clipboard-apis/#dom-clipboard-write
await navigator.clipboard.write(data).then(
() => {
this.$message.success(
this.$t('trials:researchRecord:message:copySuccess')
)
},
() => {
this.$message.error(
this.$t('trials:researchRecord:message:UnableWrite')
)
}
)
})
},
// site
getSite() {
getTrialSiteSelect(this.trialId).then((res) => {
this.siteOptions = res.Result
})
},
//
showResearchLink() {
const trialId = this.trialId
this.shareLink = `${location.protocol}//${location.host}/researchLogin?trialId=${trialId}&lang=${this.$i18n.locale}`
this.share_model.visible = true
// this.trialSiteSurveyId = this.list[0].Id
this.$nextTick(() => {
this.creatQrCode()
})
},
//
handleReset() {
this.searchData = searchDataDefault()
this.searchData.DateRange = []
if (this.searchData.DateRange && this.searchData.DateRange.length === 2) {
this.searchData.UpdateTimeBegin = this.searchData.DateRange[0]
this.searchData.updateTimeEnd = this.searchData.DateRange[1]
} else {
this.searchData.UpdateTimeBegin = ''
this.searchData.updateTimeEnd = ''
}
this.getList()
this.$nextTick(() => {
this.$refs.siteResearchList.clearSort()
})
},
//
handleSearch() {
this.getList()
},
//
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.getList()
},
},
beforeDestroy() {
if (this.qrcode) {
this.qrcode = null
}
},
}
}
</script>
<style lang="scss" scoped>
.shareLink {
padding-right: 20px;
width: 50%;
position: relative;
.step-wrapper {
height: 100%;
flex: 1;
display: flex;
flex-direction: column;
padding: 0;
margin: 0;
background-color: #fff;
}
.shareLinkBtn {
position: absolute;
bottom: 0px;
left: 0px;
.el-steps {
height: 80px;
justify-content: center;
}
.step-content {
flex: 1;
}
.title {
height: 40px;
line-height: 40px;
padding-left: 5px;
background: #e4ebf1;
border-left: 3px solid #0fc8e0;
font-size: 13px;
p {
padding: 0;
margin: 0;
}
}
.shareCode {
width: 50%;
border-left: 1px solid #eee;
display: flex;
justify-content: center;
flex-wrap: wrap;
.trial-wrapper {
height: 60px;
.qrCode {
width: 220px;
height: 220px;
.div-row {
display: flex;
border: 1px solid #c0c4cc;
border-radius: 5px;
box-shadow: 1px 1px 5px #c0c4cc;
align-items: center;
justify-content: center;
}
justify-content: space-between;
margin: 10px 20px !important;
font-size: 13px;
.codeBtnBox {
margin-top: 20px;
width: 100%;
display: flex;
justify-content: space-around;
.div-col {
flex: 1;
width: 100px;
white-space: nowrap !important;
text-overflow: ellipsis !important;
word-break: break-word !important;
overflow: hidden !important;
label {
font-weight: bold;
}
}
.div-textarea {
display: inline-block;
}
}
}
::v-deep .el-step__head.is-process {
color: #428bca;
border-color: #428bca;
}
::v-deep .el-step__title.is-process {
color: #428bca;
border-color: #428bca;
}
::v-deep .el-step__description.is-process {
color: #428bca;
border-color: #428bca;
}
::v-deep .el-step__head.is-process .el-step__line {
background-color: #428bca;
}
::v-deep .el-step__head.is-finish {
color: #303133;
border-color: #303133;
}
::v-deep .el-step__title.is-finish {
color: #303133;
border-color: #303133;
}
::v-deep .el-step__description.is-finish {
color: #303133;
border-color: #303133;
}
::v-deep .el-step__title.is-process {
text-decoration: none;
}
::v-deep .el-step__title.is-finish {
text-decoration: none;
}
::v-deep .el-step__head.is-finish .el-step__line {
background-color: #303133;
}
.click_cursor {
cursor: pointer;
}
</style>

View File

@ -217,6 +217,14 @@
)
}).TotalCount
}}
<el-button type="text" @click="
statistics(
scope.row,
ite.TrialReadingCriterionId
)
">
{{ $t('trials:enrolledReviews:button:Statistics') }}
</el-button>
</div>
</template>
</el-table-column>
@ -351,6 +359,7 @@ import {
enrollBackOrOut,
setEnrollReadingCategory,
batchSetCriterionJoinJoinAnalysis,
getDoctorUserTrialReadingStat
} from '@/api/trials'
import { addDoctorCriterionFile } from '@/api/reviewers'
import { uploadFile } from '@/api/attachment'
@ -470,6 +479,28 @@ export default {
this.initPage()
},
methods: {
async statistics(row, id) {
try {
let data = {
TrialId: this.$route.query.trialId,
TrialReadingCriterionId: id,
DoctoruserIdList: [row.DoctorUserId]
}
let res = await getDoctorUserTrialReadingStat(data)
if (res.IsSuccess && res.Result[0]) {
row.CriterionCategoryList.some((v) => {
if (v.TrialReadingCriterionId === id) {
v.PendingCount = res.Result[0].PendingCount
v.ComplectedCount = res.Result[0].ComplectedCount
v.TotalCount = res.Result[0].TotalCount
}
return v.TrialReadingCriterionId === id
})
}
} catch (err) {
console.log(err)
}
},
//
preview(row, TrialReadingCriterionId, key) {
let file = row.CriterionCategoryList.find((v) => {

File diff suppressed because it is too large Load Diff

View File

@ -315,7 +315,7 @@
<el-table-column
prop="SubjectCode"
:label="$t('trials:consistencyAnalysis:table:subjectCode')"
min-width="120"
min-width="150"
sortable="custom"
show-overflow-tooltip
>
@ -726,7 +726,7 @@
:label="
$t('trials:selftConsistencyAnalysis:table:intervalPeriod')
"
width="100"
width="120"
show-overflow-tooltip
v-if="IsSelfAnalysis"
>

View File

@ -47,7 +47,8 @@
<el-button size="small" @click="handleCancel">
{{ $t('common:button:cancel') }}
</el-button>
<el-button size="small" type="primary" v-if="readingTaskState < 2" @click="handleSave">
<el-button size="small" type="primary" v-if="readingTaskState < 2" :loading="saveLoading"
:disabled="saveLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
@ -84,7 +85,8 @@ export default {
data() {
return {
form: {},
isRender: false
isRender: false,
saveLoading: false
}
},
computed: {
@ -106,10 +108,13 @@ export default {
},
methods: {
async handleSave() {
const valid = await this.$refs.assessmentForm.validate()
if (!valid) return
const loading = this.$loading({ fullscreen: true })
if (this.saveLoading) return
this.saveLoading = true
let loading = null
try {
const valid = await this.$refs.assessmentForm.validate()
if (!valid) return
loading = this.$loading({ fullscreen: true })
var answers = []
for (const k in this.form) {
answers.push({ questionId: k, answer: this.form[k] })
@ -125,7 +130,9 @@ export default {
loading.close()
} catch (e) {
console.log(e)
loading.close()
} finally {
if (loading) loading.close()
this.saveLoading = false
}
},
handleCancel() {

View File

@ -113,4 +113,9 @@ export default {
.mb {
margin-bottom: 0px;
}
::v-deep .el-radio__input.is-disabled.is-checked .el-radio__inner {
background-color: #428bca;
border-color: #428bca;
}
</style>

View File

@ -219,6 +219,58 @@
</div>
</el-tooltip>
<el-popover v-model="keySeriesPopoverVisible" placement="bottom" trigger="click"
popper-class="key-series-popper" @show="showKeySeriesPanel">
<ul class="key-series-list">
<li v-if="keyStack && keyStack.taskBlindName" class="key-series-header">
<div class="key-series-header-top">
<div class="key-series-header-title">
<span class="key-series-header-dot"></span>
<div class="key-series-task-name" :title="keyStack.taskBlindName">
{{ keyStack.taskBlindName }}
</div>
</div>
</div>
</li>
<li v-if="keySeriesLoading" class="key-series-empty">
<i class="el-icon-loading"></i>
</li>
<li v-else-if="keySeries.length === 0" class="key-series-empty">
{{ $t('trials:reading:keySeries:noData') }}
</li>
<li v-for="(kf, idx) in keySeries" :key="idx" class="key-series-item"
@click.stop.prevent="jumpToKeySeries(kf)">
<div class="key-series-img">
<img v-if="kf.PicturePath" :src="`${OSSclientConfig.basePath}${kf.PicturePath}`" />
<svg-icon v-else icon-class="image" class="svg-icon default-icon" />
</div>
<div class="key-series-info">
<div v-if="kf.MarkName" class="key-series-meta">
<div class="key-series-label">{{ $t('trials:reading:keySeries:annotationName') }}</div>
<div class="key-series-uid" :title="kf.MarkName">
{{ kf.MarkName }}
</div>
</div>
<div class="key-series-meta">
<div class="key-series-label">{{ $t('trials:reading:keySeries:sopUid') }}</div>
<div class="key-series-uid" :title="kf.SopInstanceUid">
{{ kf.SopInstanceUid }}
</div>
</div>
</div>
<div class="key-series-star">
<svg-icon icon-class="star" class="svg-icon" />
</div>
</li>
</ul>
<div slot="reference" class="tool-wrapper">
<div class="icon" :title="$t('trials:reading:button:keySeries')">
<i class="el-icon-star-off svg-icon" />
</div>
<div class="text">{{ $t('trials:reading:button:keySeries') }}</div>
</div>
</el-popover>
<div v-for="tool in measuredTools" :key="tool.toolName">
<el-tooltip class="item" effect="dark" placement="bottom">
<div slot="content">
@ -504,13 +556,14 @@
<!-- 跳过 -->
{{ $t('trials:readingReport:button:skip') }}
</el-button>
<el-button type="primary" size="small" @click="submit">
<el-button type="primary" size="small" :loading="submitLoading" :disabled="submitLoading"
@click="submit">
<!-- 提交 -->
{{ $t('common:button:submit') }}
</el-button>
</div>
</div>
</transition>
@ -543,7 +596,7 @@
</el-tab-pane>
<!-- 其他 -->
<el-tab-pane :label="$t('trials:reading:tab:others')" name="3">
<Others v-if="activeName === '3'" :imageToolType="1"/>
<Others v-if="activeName === '3'" :imageToolType="1" />
</el-tab-pane>
</el-tabs>
@ -565,7 +618,8 @@
<Manuals :trial-id="trialId" :justKeyDoc="manualsDialog.justKeyDoc" />
<div slot="footer" style="text-align:right;" v-if="openManuals && !ManualsClose">
<!-- 确认 -->
<el-button type="primary" size="mini" @click="handleSubmitKeyDoc">
<el-button type="primary" size="mini" :loading="keyDocSubmitting" :disabled="keyDocSubmitting"
@click="handleSubmitKeyDoc">
{{ $t('common:button:confirm') }}</el-button>
</div>
</div>
@ -646,17 +700,18 @@
<!-- 取消 -->
<el-button size="mini" @click="fusionSeries.visible = false">{{ $t('common:button:cancel') }}</el-button>
<!-- 确认 -->
<el-button type="primary" size="mini" @click="handleSubmitFusionSeries">
<el-button type="primary" size="mini" :loading="fusionSubmitLoading" :disabled="fusionSubmitLoading"
@click="handleSubmitFusionSeries">
{{ $t('common:button:confirm') }}</el-button>
</el-form-item>
</el-form>
</el-dialog>
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible"
:visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" />
:visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" :isReading="true" />
<download-dicom-and-nonedicom v-if="downloadImageVisible" :subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :task-id="taskId"
:visible.sync="downloadImageVisible" />
:visible.sync="downloadImageVisible" :isReading="true" />
<!-- 签名框 -->
<el-dialog v-if="signVisible" :visible.sync="signVisible" :close-on-click-modal="false" width="600px"
custom-class="base-dialog-wrapper">
@ -706,6 +761,7 @@ import downloadDicomAndNonedicom from '@/components/downloadDicomAndNonedicom'
import SignForm from '@/views/trials/components/newSignForm'
import { getCriterionReadingInfo, verifyVisitTaskQuestions, submitDicomVisitTask, setReadKeyFile } from '@/api/trials'
import { setSkipReadingCache } from '@/api/reading'
import { getMarkList } from '@/api/reading'
import { getAutoCutNextTask } from '@/api/user'
import const_ from '@/const/sign-code'
import { changeURLStatic } from '@/utils/history.js'
@ -903,10 +959,17 @@ export default {
uploadTrialCriterion: {},
uploadStatus: 'upload',
taskId: '',
keySeries: [],
keyStack: null,
keySeriesPopoverVisible: false,
keySeriesLoading: false,
signVisible: false,
signCode: null,
currentUser: zzSessionStorage.getItem('userName'),
tmpData: null,
submitLoading: false,
fusionSubmitLoading: false,
keyDocSubmitting: false,
AspectRatio: 1,
listShow: true,
@ -1066,7 +1129,7 @@ export default {
DicomEvent.$on('imageLocation', async (measuredData) => {
return new Promise(async resolve => {
if (!measuredData) return
await this.imageLocation(measuredData)
resolve()
})
@ -1134,7 +1197,7 @@ export default {
} else if (this.CriterionType === 22) {
this.setToolActive('Probe', true, null, 'tableQuestion')
}
})
window.addEventListener('beforeunload', () => {
if (this.petctWindow) {
@ -1177,6 +1240,8 @@ export default {
this.$emit('handleReadingChart', e)
},
async handleSubmitKeyDoc() {
if (this.keyDocSubmitting) return
this.keyDocSubmitting = true
try {
let data = {
TrialCriterionId: this.$router.currentRoute.query.TrialReadingCriterionId,
@ -1188,6 +1253,8 @@ export default {
}
} catch (err) {
console.log(err)
} finally {
this.keyDocSubmitting = false
}
},
showPanel(e, toolName) {
@ -1349,6 +1416,45 @@ export default {
resetViewport() {
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].resetViewport()
},
getKeySeriesVisitTaskId() {
const currentCanvas = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`]
if (currentCanvas && currentCanvas[0] && currentCanvas[0].stack && currentCanvas[0].stack.visitTaskId) {
return currentCanvas[0].stack.visitTaskId
}
return this.activeSeries && this.activeSeries.visitTaskId ? this.activeSeries.visitTaskId : ''
},
async showKeySeriesPanel() {
this.keySeries = []
const currentCanvas = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`]
this.keyStack = currentCanvas && currentCanvas[0] ? currentCanvas[0].stack : null
const visitTaskId = this.keyStack?.visitTaskId
if (!visitTaskId) return
this.keySeriesLoading = true
try {
const res = await getMarkList({ visitTaskId })
if (res && res.Result) {
this.keySeries = res.Result
}
} catch (err) {
console.error(err)
} finally {
this.keySeriesLoading = false
}
},
jumpToKeySeries(kf) {
// this.keySeriesPopoverVisible = false
const visitTaskId = this.keyStack?.visitTaskId
if (!visitTaskId) return
// let annotation = {}
// try {
// annotation = kf.MeasureData ? JSON.parse(kf.MeasureData) : {}
// } catch (err) {
// console.error(err)
// }
const series = this.getSeriesInfoByMark(visitTaskId, { lesionName: kf.MarkName })
if (!series) return
this.loadImageStack(series)
},
previewConfig() {
this.activeName = '1'
this.personalConfigDialog.visible = true
@ -2339,6 +2445,8 @@ export default {
}
},
async submit() {
if (this.submitLoading) return
this.submitLoading = true
const loading = this.$loading({ fullscreen: true })
try {
const idx = this.visitTaskList.findIndex(i => i.IsCurrentTask)
@ -2353,6 +2461,8 @@ export default {
loading.close()
} catch (e) {
loading.close()
} finally {
this.submitLoading = false
}
},
//
@ -2386,7 +2496,7 @@ export default {
this.readingTaskState = 2
await store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
await store.dispatch('reading/setCurrentReadingTaskState', 2)
const res = await getAutoCutNextTask({imageToolType: 1})
const res = await getAutoCutNextTask({ imageToolType: 1 })
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()
@ -2488,15 +2598,18 @@ export default {
this.petSeriesInfo = {}
}
},
handleSubmitFusionSeries() {
this.$refs['fusionForm'].validate(async (valid) => {
async handleSubmitFusionSeries() {
if (this.fusionSubmitLoading) return
this.fusionSubmitLoading = true
try {
const valid = await this.$refs['fusionForm'].validate()
if (!valid) return
if (Object.keys(this.ctSeriesInfo).length === 0 || Object.keys(this.petSeriesInfo).length === 0) {
// ''
this.$confirm(this.$t('trials:lugano:fusionDialog:message:checkSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
await this.$confirm(this.$t('trials:lugano:fusionDialog:message:checkSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
showCancelButton: false,
type: 'warning'
}).then(() => { }).catch(() => { })
}).catch(() => { })
return
}
if (this.ctSeriesInfo.instanceCount > 400) {
@ -2506,15 +2619,15 @@ export default {
var count = Math.abs(this.ctSeriesInfo.instanceCount - this.petSeriesInfo.instanceCount)
if (count > 10) {
// ', ?'
this.$confirm(this.$t('trials:lugano:fusionDialog:message:validSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
const confirm = await this.$confirm(this.$t('trials:lugano:fusionDialog:message:validSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
type: 'warning'
}).then(() => {
this.fusion()
}).catch(() => { })
} else {
this.fusion()
}).catch(() => false)
if (confirm !== 'confirm') return
}
})
this.fusion()
} finally {
this.fusionSubmitLoading = false
}
},
fusion() {
// sessionStorage.setItem('ctSeriesInfo', JSON.stringify(this.ctSeriesInfo))
@ -3030,3 +3143,183 @@ export default {
flex-grow: 1;
}
</style>
<style lang="scss">
.key-series-popper {
padding: 8px !important;
background-color: #1e1e1e !important;
border: 1px solid #4a4a4a !important;
.key-series-list {
width: 340px;
max-height: 420px;
overflow-y: auto;
margin: 0;
padding: 0;
list-style: none;
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-thumb {
background: #4a4a4a;
border-radius: 4px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
}
.key-series-header {
padding: 5px 12px;
margin-bottom: 10px;
background: linear-gradient(180deg, rgba(64, 64, 64, 0.92) 0%, rgba(43, 43, 43, 0.98) 100%);
border: 1px solid #474747;
border-radius: 6px;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), 0 6px 18px rgba(0, 0, 0, 0.2);
}
.key-series-header-top {
display: flex;
align-items: center;
justify-content: space-between;
}
.key-series-header-title {
display: flex;
align-items: center;
min-width: 0;
}
.key-series-header-dot {
width: 8px;
height: 8px;
margin-right: 8px;
border-radius: 50%;
background: linear-gradient(180deg, #ffd76a 0%, #f39c12 100%);
box-shadow: 0 0 8px rgba(243, 156, 18, 0.45);
}
.key-series-task-name {
color: #fff;
font-size: 15px;
font-weight: 600;
line-height: 22px;
word-break: break-all;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.key-series-empty {
text-align: center;
color: #999;
padding: 10px;
}
.key-series-item {
text-align: left;
display: flex;
align-items: center;
padding: 10px;
cursor: pointer;
background: linear-gradient(180deg, rgba(46, 46, 46, 0.96) 0%, rgba(40, 40, 40, 0.98) 100%);
border-radius: 6px;
margin-bottom: 8px;
border: 1px solid #353535;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
// transition: all 0.2s ease;
&:last-child {
margin-bottom: 0;
}
&:hover {
background: linear-gradient(180deg, rgba(54, 54, 54, 0.98) 0%, rgba(46, 46, 46, 1) 100%);
border-color: #5a5a5a;
// transform: translateY(-1px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.18);
}
.key-series-img {
width: 60px;
height: 60px;
flex-shrink: 0;
background-color: #000;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
margin-right: 12px;
border-radius: 4px;
border: 1px solid #3a3a3a;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
.default-icon {
font-size: 24px;
color: #666;
}
}
.key-series-info {
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
padding-right: 5px;
.key-series-meta {
width: 100%;
overflow: hidden;
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 4px;
&:last-child {
margin-bottom: 0;
}
}
.key-series-label {
font-size: 13px;
color: #aaa;
margin-right: 4px;
white-space: nowrap;
}
.key-series-uid {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px;
color: #ddd;
line-height: 1.2;
}
}
.key-series-star {
margin-left: 10px;
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 50%;
background: rgba(243, 156, 18, 0.08);
border: 1px solid rgba(243, 156, 18, 0.16);
.svg-icon {
color: #f39c12;
font-size: 16px;
}
}
}
}
</style>

View File

@ -141,6 +141,8 @@
<el-button
type="primary"
size="mini"
:loading="submitLoading"
:disabled="submitLoading"
@click="handleSubmitFusionSeries"
>
{{ $t('common:button:confirm') }}</el-button>
@ -171,7 +173,8 @@ export default {
ctSeries: [],
petSeries: [],
petctWindow: null,
digitPlaces: 2
digitPlaces: 2,
submitLoading: false
}
},
mounted() {
@ -216,29 +219,32 @@ export default {
this.$refs.petSeries.toggleRowSelection(this.petSeries[ptSexiesIdx])
})
},
handleSubmitFusionSeries() {
this.$refs['fusionForm'].validate((valid) => {
async handleSubmitFusionSeries() {
if (this.submitLoading) return
this.submitLoading = true
try {
const valid = await this.$refs['fusionForm'].validate()
if (!valid) return
if (Object.keys(this.fusionForm.ctSeriesInfo).length === 0 || Object.keys(this.fusionForm.petSeriesInfo).length === 0) {
// ''
this.$confirm(this.$t('trials:lugano:fusionDialog:message:checkSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
await this.$confirm(this.$t('trials:lugano:fusionDialog:message:checkSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
showCancelButton: false,
type: 'warning'
}).then(() => {}).catch(() => {})
}).catch(() => {})
return
}
var count = Math.abs(this.fusionForm.ctSeriesInfo.instanceCount - this.fusionForm.petSeriesInfo.instanceCount)
if (count > 10) {
// ', ?'
this.$confirm(this.$t('trials:lugano:fusionDialog:message:validSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
const confirm = await this.$confirm(this.$t('trials:lugano:fusionDialog:message:validSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
type: 'warning'
}).then(() => {
this.fusion()
}).catch(() => {})
} else {
this.fusion()
}).catch(() => false)
if (confirm !== 'confirm') return
}
})
this.fusion()
} finally {
this.submitLoading = false
}
},
fusion() {
const token = getToken()

View File

@ -440,4 +440,9 @@ export default {
::v-deep .el-select.is-disabled .el-input__inner {
background-color: #646464a1;
}
::v-deep .el-radio__input.is-disabled.is-checked .el-radio__inner {
background-color: #428bca;
border-color: #428bca;
}
</style>

View File

@ -10,7 +10,7 @@
</template>
<el-form-item v-if="readingTaskState < 2">
<div style="text-align:right">
<el-button :disabled="!questionFormChangeState" :type="questionFormChangeState ? 'primary' : null" size="mini"
<el-button :loading="saveLoading" :disabled="saveLoading || !questionFormChangeState" :type="questionFormChangeState ? 'primary' : null" size="mini"
@click="handleSave">{{ $t('common:button:save') }}</el-button>
</div>
</el-form-item>
@ -56,7 +56,8 @@ export default {
focalFDGId: '',
baseLinePET5PS: null,
liverRender: true,
lungRender: true
lungRender: true,
saveLoading: false
}
},
computed: {
@ -94,7 +95,7 @@ export default {
this.resetSuvQuestions()
})
FusionEvent.$on('addAnnotation', obj => {
this.$set(obj, 'SaveEnum', 1)
// this.$set(obj, 'SaveEnum', 1)
this.addAnnotation(obj)
})
FusionEvent.$on('removeAnnotation', obj => {
@ -242,12 +243,18 @@ export default {
})
},
handleSave() {
if (this.saveLoading) return
this.saveLoading = true
this.$refs['questions'].validate((valid) => {
if (!valid) return
if (!valid) {
this.saveLoading = false
return
}
//
var existUnSave = this.checkAnnotationStatus(this.questions)
if (existUnSave) {
this.$alert(this.$t('trials:lugano:message:saveWarning1'), this.$t('trials:lugano:fusionDialog:warning'))
this.saveLoading = false
return
}
this.saveQuestionsForm()
@ -277,11 +284,13 @@ export default {
window.opener.postMessage({ type: 'petctLesionUpdate' }, window.location)
this.loading = false
this.questionFormChangeState = false
this.saveLoading = false
this.$message({ message: this.$t('common:message:savedSuccessfully'), type: 'success', duration: 2000 })
}
} catch (e) {
console.log(e)
this.loading = false
this.saveLoading = false
}
},
checkAnnotationStatus(obj) {

View File

@ -157,7 +157,7 @@
{{ $t('common:button:delete') }}
</el-button>
<!-- 保存 -->
<el-button size="mini" @click="handleSave">
<el-button size="mini" :loading="saveLoading" :disabled="saveLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</div>
@ -238,7 +238,8 @@ export default {
pictureBaseStr: '',
currentMarkTool: '',
firstRenderAnnotation: true,
isInsideVolume: true
isInsideVolume: true,
saveLoading: false
}
},
mounted() {
@ -738,8 +739,13 @@ export default {
return new Blob([bytesCode], { type: imgtype })
},
handleSave() {
if (this.saveLoading) return
this.saveLoading = true
this.$refs.measurementForm.validate(async valid => {
if (!valid) return
if (!valid) {
this.saveLoading = false
return
}
const loading = this.$loading({ fullscreen: true })
if (!this.isInsideVolume) {
// this.$alert('', '', {
@ -753,6 +759,7 @@ export default {
callback: action => { }
})
this.saveLoading = false
return
}
// SUV
@ -766,6 +773,7 @@ export default {
showCancelButton: false,
callback: action => { }
})
this.saveLoading = false
return
}
// suv0
@ -779,6 +787,7 @@ export default {
showCancelButton: false,
callback: action => { }
})
this.saveLoading = false
return
}
}
@ -856,9 +865,11 @@ export default {
this.$message({ message: this.$t('common:message:savedSuccessfully'), type: 'success', duration: 2000 })
}
loading.close()
this.saveLoading = false
} catch (e) {
console.log(e)
loading.close()
this.saveLoading = false
}
})
})
@ -1054,6 +1065,11 @@ export default {
background-color: #646464a1;
}
::v-deep .el-radio__input.is-disabled.is-checked .el-radio__inner {
background-color: #428bca;
border-color: #428bca;
}
::v-deep .el-button--mini,
.el-button--mini.is-round {
padding: 7px 10px;

View File

@ -33,17 +33,21 @@
<div v-for="item in qs.Childrens" :key="item.Id">
<div v-if="item.Type === 'basicTable'" class="flex-row" style="margin:3px 0;">
<div class="title">{{ item.QuestionName }}</div>
<div v-if="item.LesionType === 104 && readingTaskState < 2">
<div class="add-icon" @click.prevent="downloadTpl">
<div v-if="item.LesionType === 104">
<div
class="add-icon" @click.prevent="downloadTpl" v-if="readingTaskState < 2">
<i class="el-icon-download" />
</div>
<div class="add-icon" style="margin: 0 5px;"
@click.prevent="uploadTpl(item.LesionType, item.QuestionName)">
@click.prevent="uploadTpl(item.LesionType, item.QuestionName)" v-if="readingTaskState < 2">
<i class="el-icon-upload2" />
</div>
<div class="add-icon" @click.prevent="handleAddOrEdit('add', item)">
<div class="add-icon" @click.prevent="handleAddOrEdit('add', item)" v-if="readingTaskState < 2">
<i class="el-icon-plus" />
</div>
<div class="add-icon" @click.prevent="handleView(item)">
<i class="el-icon-view" />
</div>
</div>
</div>
@ -173,7 +177,8 @@
<div v-if="isCurrentTask && readingTaskState < 2 && (qs.GroupClassify === 5 || qs.GroupClassify === 6)"
class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<!-- 保存 -->
<el-button type="primary" size="mini" :disabled="!formChanged" @click="handleSave(index)">
<el-button type="primary" size="mini" :loading="!!rowSaveLoadingMap[index]"
:disabled="!!rowSaveLoadingMap[index] || !formChanged" @click="handleSave(index)">
{{ $t('common:button:save') }}
</el-button>
</div>
@ -184,11 +189,22 @@
</template>
<!-- 计算值 -->
<Questions ref="ecrf2" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
:group-classify="4" style="margin-top:20px" />
<Questions
ref="ecrf2"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="4"
:question-type="12"
style="margin-top:20px" />
<!-- 评估结果 -->
<Questions ref="ecrf3" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
<Questions
ref="ecrf3"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:question-type="12"
:group-classify="5" />
<!-- 新增编辑弹窗 -->
<el-dialog v-if="addOrEdit.visible" :visible.sync="addOrEdit.visible" :close-on-click-modal="false"
@ -202,7 +218,8 @@
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" class="my_upload_btn" @click="saveFormData">
<el-button size="small" class="my_upload_btn" :loading="dialogSaveLoading"
:disabled="dialogSaveLoading" @click="saveFormData">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
@ -220,6 +237,7 @@
<script>
import { saveTaskQuestion, submitTableQuestion, deleteReadingRowAnswer } from '@/api/trials'
import { resetReadingTask, getIVUSTemplate } from '@/api/reading'
import { getToken } from '@/utils/auth'
import DicomEvent from './../DicomEvent'
import store from '@/store'
import { mapGetters } from 'vuex'
@ -280,7 +298,10 @@ export default {
lumenId: '',
sRoiStartDistanceId: '',
sRoiEndDistanceId: '',
sRoiDistanceId: ''
sRoiDistanceId: '',
rowSaveLoadingMap: {},
dialogSaveLoading: false,
childWindows: []
}
},
computed: {
@ -320,11 +341,13 @@ export default {
DicomEvent.$on('refreshQuestions', _ => {
this.refreshQuestions()
})
window.addEventListener('beforeunload', this.closeAllChildWindows)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
DicomEvent.$off('getUnSaveTarget')
DicomEvent.$off('refreshQuestions')
window.removeEventListener('beforeunload', this.closeAllChildWindows)
},
methods: {
handleReadingChart(e) {
@ -610,29 +633,37 @@ export default {
return v
}
},
async handleSave(index) {
async handleSave(index, options = {}) {
if (this.rowSaveLoadingMap[index]) return
const refName = `questions${index}`
const valid = await this.$refs[refName][0].validate()
if (!valid) return
const loading = this.$loading({ fullscreen: true })
var answers = []
for (const k in this.questionForm) {
answers.push({ id: k, answer: Array.isArray(this.questionForm[k]) ? JSON.stringify(this.questionForm[k]) : this.questionForm[k] })
}
var params = {
visitTaskId: this.visitTaskId,
answers: answers
}
this.$set(this.rowSaveLoadingMap, index, true)
let loading = null
try {
const valid = await this.$refs[refName][0].validate()
if (!valid) return
loading = this.$loading({ fullscreen: true })
var answers = []
for (const k in this.questionForm) {
answers.push({ id: k, answer: Array.isArray(this.questionForm[k]) ? JSON.stringify(this.questionForm[k]) : this.questionForm[k] })
}
var params = {
visitTaskId: this.visitTaskId,
answers: answers
}
await saveTaskQuestion(12, params)
this.$message.success(this.$t('common:message:savedSuccessfully'))
loading.close()
if (!options.silentSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
DicomEvent.$emit('getReportInfo', true)
this.refreshQuestions()
if (!options.skipRefresh) {
await this.refreshQuestions()
}
this.formChanged = false
} catch (e) {
console.log(e)
loading.close()
} finally {
if (loading) loading.close()
this.$set(this.rowSaveLoadingMap, index, false)
}
},
getECRFQuestions(obj) {
@ -731,11 +762,32 @@ export default {
this.addOrEdit.visible = true
this.addOrEdit.lesionType = row.LesionType
},
handleView(item) {
localStorage.setItem('taskBlindName', this.taskBlindName)
localStorage.setItem('isReadingShowSubjectInfo', this.isReadingShowSubjectInfo)
let token = getToken()
let routeData = this.$router.resolve({ path: `/ecrfList?trialId=${this.$route.query.trialId}&visitTaskId=${this.visitTaskId}&questionId=${item.Id}&TokenKey=${token}` })
const win = window.open(routeData.href, '_blank')
if (win) {
this.childWindows.push(win)
}
},
closeAllChildWindows() {
this.childWindows.forEach(win => {
if (win && !win.closed) {
win.close()
}
})
this.childWindows = []
},
async saveFormData() {
const valid = await this.$refs.tableQsForm.validate()
if (!valid) return
const loading = this.$loading({ fullscreen: true })
if (this.dialogSaveLoading) return
this.dialogSaveLoading = true
let loading = null
try {
const valid = await this.$refs.tableQsForm.validate()
if (!valid) return
loading = this.$loading({ fullscreen: true })
var answers = []
var reg = new RegExp(/^[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}$/)
for (const k in this.qsForm) {
@ -765,10 +817,11 @@ export default {
this.addOrEdit.visible = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
loading.close()
} catch (e) {
console.log(e)
loading.close()
} finally {
if (loading) loading.close()
this.dialogSaveLoading = false
}
},
async handleDelete(row, index) {
@ -820,9 +873,29 @@ export default {
}
},
async uploadDlgClose() {
this.refreshQuestions()
DicomEvent.$emit('getReportInfo', true)
this.upload.visible = false
try {
await this.refreshQuestions()
const saveTasks = []
for (let index = 0; index < this.questions.length; index++) {
const qs = this.questions[index]
if (qs.GroupClassify === 6) {
saveTasks.push(() => this.handleSave(index, { silentSuccess: true, skipRefresh: true }))
}
}
saveTasks.push(() => this.$refs['ecrf2'] && this.$refs['ecrf2'].handleSave({ silentSuccess: true, skipRefresh: true }))
saveTasks.push(() => this.$refs['ecrf3'] && this.$refs['ecrf3'].handleSave({ silentSuccess: true, skipRefresh: true }))
for (const saveTask of saveTasks) {
try {
await saveTask()
} catch (e) {
console.log(e)
}
}
} catch (e) {
console.log(e)
} finally {
this.upload.visible = false
}
},
renderHeader(h, { column, $index }) {
const span = document.createElement('span')

View File

@ -249,4 +249,9 @@ export default {
::v-deep .el-select.is-disabled .el-input__inner {
background-color: #646464a1;
}
::v-deep .el-radio__input.is-disabled.is-checked .el-radio__inner {
background-color: #428bca;
border-color: #428bca;
}
</style>

View File

@ -1,4 +1,4 @@
<template>
<template>
<el-form v-if="isRender" ref="measurementForm" v-loading="loading" :model="questionForm" size="mini"
class="measurement-form">
<div class="base-dialog-body">
@ -192,7 +192,7 @@
{{ $t('common:button:delete') }}
</el-button>
<!-- 保存 -->
<el-button size="mini" @click="handleSave">
<el-button size="mini" :loading="saveLoading" :disabled="saveLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</div>
@ -276,7 +276,9 @@ export default {
stateDisabled: false,
splitLesionTargetPPD: null,
splitLesionTargetLDi: null,
splitLesionTargetSDi: null
splitLesionTargetSDi: null,
saveLoading: false,
validationDialogVisible: false
}
},
mounted() {
@ -299,7 +301,24 @@ export default {
handleReadingChart(e) {
this.$emit('handleReadingChart', e)
},
scrollHandle() {
async showValidationDialog(msg) {
if (this.validationDialogVisible) {
return false
}
this.validationDialogVisible = true
try {
await this.$confirm(msg, {
type: 'warning',
showCancelButton: false,
closeOnClickModal: false,
closeOnPressEscape: false
})
} catch (e) {
} finally {
this.validationDialogVisible = false
}
return false
}, scrollHandle() {
for (const k in this.$refs) {
if (k.indexOf('select_') === -1) continue
if (this.$refs[k] instanceof Array) {
@ -1039,43 +1058,41 @@ export default {
return new Blob([bytesCode], { type: imgtype })
},
async handleSave() {
const valid = await this.$refs.measurementForm.validate()
if (!valid) {
if (this.lesionType === 0) {
this.$refs['scrollPanel'].scrollTo({ top: 0, behavior: 'smooth' })
if (this.saveLoading) return
this.saveLoading = true
let loading = null
try {
const valid = await this.$refs.measurementForm.validate()
if (!valid) {
if (this.lesionType === 0) {
this.$refs['scrollPanel'].scrollTo({ top: 0, behavior: 'smooth' })
}
return
}
return
}
const loading = this.$loading({ fullscreen: true })
var measureData = this.questionForm.MeasureData
var lesionState = this.getQuestionVal(7)
var lesionLength = this.getQuestionVal(0)
var lesionShort = this.getQuestionVal(1)
var lymphNodes = this.getQuestionVal(2)
lymphNodes = (lymphNodes !== null && lymphNodes !== undefined) ? parseInt(lymphNodes) : null
if (this.isBaseLineTask) {
loading = this.$loading({ fullscreen: true })
var measureData = this.questionForm.MeasureData
var lesionState = this.getQuestionVal(7)
var lesionLength = this.getQuestionVal(0)
var lesionShort = this.getQuestionVal(1)
var lymphNodes = this.getQuestionVal(2)
lymphNodes = (lymphNodes !== null && lymphNodes !== undefined) ? parseInt(lymphNodes) : null
if (this.isBaseLineTask) {
// 线
// 0
if (this.lesionType === 0 && lesionState === 0) {
if (lymphNodes === 1) {
// 15 mm使
if (!(measureData && measureData.type === 'Bidirectional' && lesionLength > 15 && lesionLength >= 2 * measureData.thick)) {
this.$confirm(this.$t('trials:lugano:message:validState1'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState1'))
loading.close()
return
}
} else {
// 10 mm使
if (!(measureData && measureData.type === 'Bidirectional' && lesionLength > 10 && lesionLength >= 2 * measureData.thick)) {
this.$confirm(this.$t('trials:lugano:message:validState2'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState2'))
loading.close()
return
}
@ -1087,22 +1104,16 @@ export default {
if (lymphNodes === 1) {
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg21'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg21'))
loading.close()
return
}
} else {
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:lugano:message:validState12'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState12'))
loading.close()
return
}
@ -1115,11 +1126,8 @@ export default {
if (lymphNodes === 1) {
if (!(measureData && measureData.type === 'Bidirectional' && lesionLength > 15)) {
// 15 mm使
this.$confirm(this.$t('trials:lugano:message:validState3'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState3'))
loading.close()
return
}
@ -1127,11 +1135,8 @@ export default {
//
if (!(measureData && measureData.type === 'Bidirectional')) {
// 10 mm使
this.$confirm(this.$t('trials:lugano:message:validState4'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState4'))
loading.close()
return
}
@ -1143,11 +1148,8 @@ export default {
// 使5mm
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg25'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg25'))
loading.close()
return
} else if (measureData && measureData.type === 'ArrowAnnotate') {
@ -1162,11 +1164,8 @@ export default {
// 5mm
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg26'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg26'))
loading.close()
return
} else if (measureData && measureData.type === 'ArrowAnnotate') {
@ -1186,11 +1185,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg27'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg27'))
loading.close()
return
}
@ -1198,11 +1194,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg28'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg28'))
loading.close()
return
}
@ -1215,20 +1208,14 @@ export default {
// 使 measureData.type === 'ArrowAnnotate' ||
if (!(measureData && (measureData.type === 'Bidirectional'))) {
// 使
this.$confirm(this.$t('trials:lugano:message:validState5'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState5'))
loading.close()
return
} else if (measureData && measureData.type === 'Bidirectional' && lesionLength > 15) {
// ,15mm
this.$confirm(this.$t('trials:lugano:message:validState6'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState6'))
loading.close()
return
}
@ -1246,11 +1233,8 @@ export default {
//
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:lugano:warnning:validState7'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:warnning:validState7'))
loading.close()
return
}
@ -1275,11 +1259,8 @@ export default {
if (lymphNodes === 1) {
if (!(measureData && measureData.type === 'Bidirectional' && lesionLength > 15)) {
// 15 mm使
this.$confirm(this.$t('trials:lugano:message:validState7'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState7'))
loading.close()
return
}
@ -1287,11 +1268,8 @@ export default {
//
if (!(measureData && measureData.type === 'Bidirectional' && lesionLength > 10)) {
// 10 mm使
this.$confirm(this.$t('trials:lugano:message:validState8'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState8'))
loading.close()
return
}
@ -1304,11 +1282,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg31'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg31'))
loading.close()
return
}
@ -1316,11 +1291,8 @@ export default {
//
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:lugano:message:validState9'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState9'))
loading.close()
return
}
@ -1333,11 +1305,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// `使`
this.$confirm(this.$t('trials:reading:warnning:msg33'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg33'))
loading.close()
return
}
@ -1345,11 +1314,8 @@ export default {
//
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:lugano:message:validState10'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState10'))
loading.close()
return
}
@ -1362,11 +1328,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg35'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg35'))
loading.close()
return
}
@ -1374,11 +1337,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg36'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg36'))
loading.close()
return
}
@ -1391,11 +1351,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'ArrowAnnotate' || (measureData.type === 'Bidirectional' && lesionLength < 15)))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:lgmsg37'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:lgmsg37'))
loading.close()
return
}
@ -1403,11 +1360,8 @@ export default {
//
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg38'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg38'))
loading.close()
return
}
@ -1420,11 +1374,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg39'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg39'))
loading.close()
return
}
@ -1432,11 +1383,8 @@ export default {
//
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:lugano:message:validState11'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState11'))
loading.close()
return
}
@ -1478,11 +1426,8 @@ export default {
// 使
if (measureData) {
// ``
this.$confirm(this.$t('trials:reading:warnning:msg43'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg43'))
loading.close()
return
}
@ -1490,11 +1435,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg44'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg44'))
loading.close()
return
}
@ -1507,11 +1449,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'ArrowAnnotate' || (measureData.type === 'Bidirectional' && lesionLength < 15)))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:lgmsg45'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:lgmsg45'))
loading.close()
return
}
@ -1519,16 +1458,17 @@ export default {
//
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg46'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg46'))
loading.close()
return
}
}
}
}
} finally {
if (loading) loading.close()
this.saveLoading = false
}
DicomEvent.$emit('getScreenshots', { questionId: this.parentQsId, rowIndex: this.questionForm.RowIndex, visitTaskId: this.visitTaskId, lesionName: this.lesionMark, lesionType: this.lesionType, isMarked: !!measureData }, async val => {
// 0线
@ -1803,6 +1743,11 @@ export default {
background-color: #646464a1;
}
::v-deep .el-radio__input.is-disabled.is-checked .el-radio__inner {
background-color: #428bca;
border-color: #428bca;
}
::v-deep .el-button--mini,
.el-button--mini.is-round {
padding: 7px 10px;

View File

@ -1,4 +1,4 @@
<template>
<template>
<el-form v-if="isRender" ref="measurementForm" v-loading="loading" :model="questionForm" size="mini"
class="measurement-form">
<div class="base-dialog-body">
@ -192,7 +192,7 @@
{{ $t('common:button:delete') }}
</el-button>
<!-- 保存 -->
<el-button size="mini" @click="handleSave">
<el-button size="mini" :loading="saveLoading" :disabled="saveLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</div>
@ -276,7 +276,9 @@ export default {
stateDisabled: false,
splitLesionTargetPPD: null,
splitLesionTargetLDi: null,
splitLesionTargetSDi: null
splitLesionTargetSDi: null,
saveLoading: false,
validationDialogVisible: false
}
},
mounted() {
@ -299,7 +301,24 @@ export default {
handleReadingChart(e) {
this.$emit('handleReadingChart', e)
},
scrollHandle() {
async showValidationDialog(msg) {
if (this.validationDialogVisible) {
return false
}
this.validationDialogVisible = true
try {
await this.$confirm(msg, {
type: 'warning',
showCancelButton: false,
closeOnClickModal: false,
closeOnPressEscape: false
})
} catch (e) {
} finally {
this.validationDialogVisible = false
}
return false
}, scrollHandle() {
for (const k in this.$refs) {
if (k.indexOf('select_') === -1) continue
if (this.$refs[k] instanceof Array) {
@ -1039,43 +1058,41 @@ export default {
return new Blob([bytesCode], { type: imgtype })
},
async handleSave() {
const valid = await this.$refs.measurementForm.validate()
if (!valid) {
if (this.lesionType === 0) {
this.$refs['scrollPanel'].scrollTo({ top: 0, behavior: 'smooth' })
if (this.saveLoading) return
this.saveLoading = true
let loading = null
try {
const valid = await this.$refs.measurementForm.validate()
if (!valid) {
if (this.lesionType === 0) {
this.$refs['scrollPanel'].scrollTo({ top: 0, behavior: 'smooth' })
}
return
}
return
}
const loading = this.$loading({ fullscreen: true })
var measureData = this.questionForm.MeasureData
var lesionState = this.getQuestionVal(7)
var lesionLength = this.getQuestionVal(0)
var lesionShort = this.getQuestionVal(1)
var lymphNodes = this.getQuestionVal(2)
lymphNodes = (lymphNodes !== null && lymphNodes !== undefined) ? parseInt(lymphNodes) : null
if (this.isBaseLineTask) {
loading = this.$loading({ fullscreen: true })
var measureData = this.questionForm.MeasureData
var lesionState = this.getQuestionVal(7)
var lesionLength = this.getQuestionVal(0)
var lesionShort = this.getQuestionVal(1)
var lymphNodes = this.getQuestionVal(2)
lymphNodes = (lymphNodes !== null && lymphNodes !== undefined) ? parseInt(lymphNodes) : null
if (this.isBaseLineTask) {
// 线
// 0
if (this.lesionType === 0 && lesionState === 0) {
if (lymphNodes === 1) {
// 15 mm使
if (!(measureData && measureData.type === 'Bidirectional' && lesionLength > 15 && lesionLength >= 2 * measureData.thick)) {
this.$confirm(this.$t('trials:lugano:message:validState1'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState1'))
loading.close()
return
}
} else {
// 10 mm使
if (!(measureData && measureData.type === 'Bidirectional' && lesionLength > 10 && lesionLength >= 2 * measureData.thick)) {
this.$confirm(this.$t('trials:lugano:message:validState2'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState2'))
loading.close()
return
}
@ -1087,22 +1104,16 @@ export default {
if (lymphNodes === 1) {
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg21'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg21'))
loading.close()
return
}
} else {
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:lugano:message:validState12'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState12'))
loading.close()
return
}
@ -1115,11 +1126,8 @@ export default {
if (lymphNodes === 1) {
if (!(measureData && measureData.type === 'Bidirectional' && lesionLength > 15)) {
// 15 mm使
this.$confirm(this.$t('trials:lugano:message:validState3'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState3'))
loading.close()
return
}
@ -1127,11 +1135,8 @@ export default {
//
if (!(measureData && measureData.type === 'Bidirectional')) {
// 10 mm使
this.$confirm(this.$t('trials:lugano:message:validState4'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState4'))
loading.close()
return
}
@ -1143,11 +1148,8 @@ export default {
// 使5mm
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg25'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg25'))
loading.close()
return
} else if (measureData && measureData.type === 'ArrowAnnotate') {
@ -1162,11 +1164,8 @@ export default {
// 5mm
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg26'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg26'))
loading.close()
return
} else if (measureData && measureData.type === 'ArrowAnnotate') {
@ -1186,11 +1185,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg27'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg27'))
loading.close()
return
}
@ -1198,11 +1194,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg28'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg28'))
loading.close()
return
}
@ -1215,20 +1208,14 @@ export default {
// 使 measureData.type === 'ArrowAnnotate' ||
if (!(measureData && (measureData.type === 'Bidirectional'))) {
// 使
this.$confirm(this.$t('trials:lugano:message:validState5'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState5'))
loading.close()
return
} else if (measureData && measureData.type === 'Bidirectional' && lesionLength > 15) {
// ,15mm
this.$confirm(this.$t('trials:lugano:message:validState6'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState6'))
loading.close()
return
}
@ -1246,11 +1233,8 @@ export default {
//
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:lugano:warnning:validState7'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:warnning:validState7'))
loading.close()
return
}
@ -1275,11 +1259,8 @@ export default {
if (lymphNodes === 1) {
if (!(measureData && measureData.type === 'Bidirectional' && lesionLength > 15)) {
// 15 mm使
this.$confirm(this.$t('trials:lugano:message:validState7'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState7'))
loading.close()
return
}
@ -1287,11 +1268,8 @@ export default {
//
if (!(measureData && measureData.type === 'Bidirectional' && lesionLength > 10)) {
// 10 mm使
this.$confirm(this.$t('trials:lugano:message:validState8'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState8'))
loading.close()
return
}
@ -1304,11 +1282,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg31'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg31'))
loading.close()
return
}
@ -1316,11 +1291,8 @@ export default {
//
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:lugano:message:validState9'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState9'))
loading.close()
return
}
@ -1333,11 +1305,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// `使`
this.$confirm(this.$t('trials:reading:warnning:msg33'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg33'))
loading.close()
return
}
@ -1345,11 +1314,8 @@ export default {
//
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:lugano:message:validState10'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState10'))
loading.close()
return
}
@ -1362,11 +1328,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg35'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg35'))
loading.close()
return
}
@ -1374,11 +1337,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg36'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg36'))
loading.close()
return
}
@ -1391,11 +1351,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'ArrowAnnotate' || (measureData.type === 'Bidirectional' && lesionLength < 15)))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:lgmsg37'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:lgmsg37'))
loading.close()
return
}
@ -1403,11 +1360,8 @@ export default {
//
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg38'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg38'))
loading.close()
return
}
@ -1420,11 +1374,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg39'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg39'))
loading.close()
return
}
@ -1432,11 +1383,8 @@ export default {
//
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:lugano:message:validState11'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:lugano:message:validState11'))
loading.close()
return
}
@ -1478,11 +1426,8 @@ export default {
// 使
if (measureData) {
// ``
this.$confirm(this.$t('trials:reading:warnning:msg43'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg43'))
loading.close()
return
}
@ -1490,11 +1435,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg44'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg44'))
loading.close()
return
}
@ -1507,11 +1449,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'ArrowAnnotate' || (measureData.type === 'Bidirectional' && lesionLength < 15)))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:lgmsg45'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:lgmsg45'))
loading.close()
return
}
@ -1519,16 +1458,17 @@ export default {
//
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg46'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg46'))
loading.close()
return
}
}
}
}
} finally {
if (loading) loading.close()
this.saveLoading = false
}
DicomEvent.$emit('getScreenshots', { questionId: this.parentQsId, rowIndex: this.questionForm.RowIndex, visitTaskId: this.visitTaskId, lesionName: this.lesionMark, lesionType: this.lesionType, isMarked: !!measureData }, async val => {
// 0线
@ -1803,6 +1743,11 @@ export default {
background-color: #646464a1;
}
::v-deep .el-radio__input.is-disabled.is-checked .el-radio__inner {
background-color: #428bca;
border-color: #428bca;
}
::v-deep .el-button--mini,
.el-button--mini.is-round {
padding: 7px 10px;

View File

@ -139,7 +139,7 @@
<div v-if="isCurrentTask && readingTaskState < 2" class="base-dialog-footer"
style="text-align:right;margin-top:10px;">
<!-- 保存 -->
<el-button size="mini" @click="handleSave">
<el-button size="mini" :loading="saveLoading" :disabled="saveLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</div>
@ -219,7 +219,8 @@ export default {
isMeasurableId: '',
isExitsMarks: false,
isDisabledMeasurableRadio: false,
liverSeg: ''
liverSeg: '',
saveLoading: false
}
},
mounted() {
@ -698,9 +699,14 @@ export default {
return new Blob([bytesCode], { type: imgtype })
},
async handleSave() {
if (this.saveLoading) return
this.saveLoading = true
try {
const valid = await this.$refs.measurementForm.validate()
if (!valid) return
if (!valid) {
this.saveLoading = false
return
}
if (parseInt(this.questionForm[this.isMeasurableId]) === 1) {
//
const i = this.markList.findIndex(i => i.saveEnum === 0)
@ -708,6 +714,7 @@ export default {
//
this.$alert(this.$t('trials:MRIPDFF:message:message1'))
// this.$message.warning(this.$t('trials:MRIPDFF:message:message1'))
this.saveLoading = false
return
}
} else {
@ -720,7 +727,10 @@ export default {
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
if (confirm !== 'confirm') {
this.saveLoading = false
return
}
const l1Id = this.getQuestionId(1101)
this.$set(this.questionForm, l1Id, '')
const l2Id = this.getQuestionId(1102)
@ -779,12 +789,15 @@ export default {
this.$emit('getReadingQuestionAndAnswer')
}
loading.close()
this.saveLoading = false
} catch (e) {
console.log(e)
loading.close()
this.saveLoading = false
}
} catch (e) {
console.log(e)
this.saveLoading = false
}
},
async handleClose() {
@ -830,6 +843,11 @@ export default {
background-color: #646464a1;
}
::v-deep .el-radio__input.is-disabled.is-checked .el-radio__inner {
background-color: #428bca;
border-color: #428bca;
}
::v-deep .el-button--mini,
.el-button--mini.is-round {
padding: 7px 7px;

View File

@ -145,7 +145,7 @@
{{ $t('trials:reading:button:removeMark') }}
</el-button> -->
<!-- 保存 -->
<el-button size="mini" @click="handleSave">
<el-button size="mini" :loading="saveLoading" :disabled="saveLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</div>
@ -223,7 +223,8 @@ export default {
digitPlaces: 2,
isMeasurableId: '',
isDisabledMeasurableRadio: false,
liverSeg: ''
liverSeg: '',
saveLoading: false
}
},
mounted() {
@ -555,9 +556,14 @@ export default {
DicomEvent.$emit('refreshStudyListMeasureData')
},
async handleSave() {
if (this.saveLoading) return
this.saveLoading = true
try {
const valid = await this.$refs.measurementForm.validate()
if (!valid) return
if (!valid) {
this.saveLoading = false
return
}
const loading = this.$loading({ fullscreen: true })
let measureData = this.questionForm.MeasureData
if (parseInt(this.questionForm[this.isMeasurableId]) === 0 && measureData) {
@ -613,12 +619,15 @@ export default {
this.$emit('getReadingQuestionAndAnswer')
}
loading.close()
this.saveLoading = false
} catch(e) {
loading.close()
this.saveLoading = false
}
})
} catch (e) {
console.log(e)
this.saveLoading = false
}
},
async handleClose() {
@ -664,6 +673,11 @@ export default {
background-color: #646464a1;
}
::v-deep .el-radio__input.is-disabled.is-checked .el-radio__inner {
background-color: #428bca;
border-color: #428bca;
}
::v-deep .el-button--mini,
.el-button--mini.is-round {
padding: 7px 7px;

View File

@ -30,12 +30,12 @@
<div class="image-viewer__actions__inner">
<i class="el-icon-zoom-out" @click="handleActions('zoomOut')" />
<i class="el-icon-zoom-in" @click="handleActions('zoomIn')" />
<i class="el-image-viewer__actions__divider" />
<!-- <i class="el-image-viewer__actions__divider" /> -->
<i class="el-icon-c-scale-to-original" @click="toggleMode" />
<i class="el-image-viewer__actions__divider" />
<!-- <i class="el-image-viewer__actions__divider" /> -->
<i class="el-icon-refresh-left" @click="handleActions('anticlocelise')" />
<i class="el-icon-refresh-right" @click="handleActions('clocelise')" />
<i v-if="readingTaskState < 2" class="el-icon-bottom-right"
<i v-if="readingTaskState < 2 && taskInfo.CriterionType !== 19 && taskInfo.CriterionType !== 20" class="el-icon-bottom-right"
:style="{ cursor: isToolDisabled || readingTaskState >= 2 ? 'not-allowed' : 'pointer' }"
@click="handleDrawArrow" />
@ -44,7 +44,7 @@
:style="{cursor:isToolDisabled || readingTaskState >=2 ?'not-allowed':'pointer'}"
@click="handleDrawRectangle"
/> -->
<svg-icon v-if="readingTaskState < 2" icon-class="rectangle" class="svg-icon"
<svg-icon v-if="readingTaskState < 2 && taskInfo.CriterionType !== 19 && taskInfo.CriterionType !== 20" icon-class="rectangle" class="svg-icon"
:style="{ cursor: isToolDisabled || readingTaskState >= 2 ? 'not-allowed' : 'pointer' }"
@click="handleDrawRectangle" />
</div>
@ -109,6 +109,7 @@ export default {
}
},
data() {
const taskInfo = JSON.parse(localStorage.getItem('taskInfo') || '{}')
return {
loading: false,
index: this.initialIndex,
@ -137,11 +138,13 @@ export default {
currentDrawData: {},
canvasData: [],
imgZoom: 1,
isDragging: false,
isToolDisabled: false,
lesionName: '',
visitTaskId: '',
isInit: false,
imgSize: { height: '', width: '' } //
imgSize: { height: '', width: '' }, //
taskInfo
}
},
computed: {
@ -209,6 +212,10 @@ export default {
}
this.deviceSupportInstall()
},
beforeDestroy() {
window.removeEventListener('message', this.receiveMsg)
this.stopDragging()
},
methods: {
getCanvasData() {
return new Promise(async resolve => {
@ -337,25 +344,55 @@ export default {
//
initCanvas(img) {
this.loading = true
this.canvasSize.height = img.height
this.canvasSize.width = img.width
this.imgSize.height = img.height
this.imgSize.width = img.width
const canvasDiv = document.getElementsByClassName('image-viewer__canvas')[0]
const widthRatio = canvasDiv && canvasDiv.offsetWidth ? canvasDiv.offsetWidth / img.width : 1
const heightRatio = canvasDiv && canvasDiv.offsetHeight ? canvasDiv.offsetHeight / img.height : 1
this.imgZoom = Math.min(widthRatio, heightRatio, 1)
this.canvasSize.height = Math.round(img.height * this.imgZoom)
this.canvasSize.width = Math.round(img.width * this.imgZoom)
this.canvasPosition = {
x: canvasDiv.offsetWidth / 2 - img.width / 2,
y: canvasDiv.offsetHeight / 2 - img.height / 2 - 25
x: canvasDiv.offsetWidth / 2 - this.canvasSize.width / 2,
y: canvasDiv.offsetHeight / 2 - this.canvasSize.height / 2 - 25
} //
this.$nextTick(async () => {
ctx = document.getElementById('canvas').getContext('2d') //
await ctx.drawImage(img, 0, 0, img.width, img.height) // canvas()
await ctx.drawImage(img, 0, 0, this.canvasSize.width, this.canvasSize.height) // canvas()
this.drawToArr()
this.loading = false
})
},
scalePoint(point) {
return {
x: point.x * this.imgZoom,
y: point.y * this.imgZoom
}
},
startDragging(e) {
const { offsetX, offsetY } = this.transform
const startX = e.pageX
const startY = e.pageY
this.isDragging = true
this._dragHandler = rafThrottle((ev) => {
this.transform.offsetX = offsetX + ev.pageX - startX
this.transform.offsetY = offsetY + ev.pageY - startY
})
this._dragEndHandler = () => {
this.stopDragging()
}
on(document, 'mousemove', this._dragHandler)
on(document, 'mouseup', this._dragEndHandler)
},
stopDragging() {
off(document, 'mousemove', this._dragHandler)
off(document, 'mouseup', this._dragEndHandler)
this._dragHandler = null
this._dragEndHandler = null
this.isDragging = false
},
//
clearCanvas() {
@ -381,6 +418,11 @@ export default {
if (e.button !== 0) {
return
}
if (!this.model) {
this.startDragging(e)
e.preventDefault()
return
}
this.isMouseDown = true //
//
this.canvasMouseStart = {
@ -391,6 +433,9 @@ export default {
//
async handleMouseMove(e) {
if (!this.model || this.isDragging) {
return
}
// ()
this.mousePosition = {
x: e.pageX,
@ -437,10 +482,12 @@ export default {
var headlen = 10// 线
var theta = 45// 线线
var arrowX, arrowY// 线
var fromX = e.data.handles.start.x
var fromY = e.data.handles.start.y
var toX = e.data.handles.end.x
var toY = e.data.handles.end.y
var start = this.scalePoint(e.data.handles.start)
var end = this.scalePoint(e.data.handles.end)
var fromX = start.x
var fromY = start.y
var toX = end.x
var toY = end.y
//
var angle = Math.atan2(fromY - toY, fromX - toX) * 180 / Math.PI
var angle1 = (angle + theta) * Math.PI / 180
@ -453,8 +500,8 @@ export default {
// 线
ctx.lineWidth = this.lineWidth
ctx.beginPath()
ctx.moveTo(fromX * this.imgZoom, fromY * this.imgZoom)
ctx.lineTo(toX * this.imgZoom, toY * this.imgZoom)
ctx.moveTo(fromX, fromY)
ctx.lineTo(toX, toY)
arrowX = toX + topX
arrowY = toY + topY
@ -473,18 +520,16 @@ export default {
ctx.lineWidth = this.lineWidth
ctx.beginPath()
ctx.strokeStyle = this.color
const start = this.scalePoint(e.data.handles.start)
const end = this.scalePoint(e.data.handles.end)
//
ctx.strokeRect(
// this.canvasMouseStart.x,
// this.canvasMouseStart.y,
// e.offsetX - this.canvasMouseStart.x,
// e.offsetY - this.canvasMouseStart.y
e.data.handles.start.x,
e.data.handles.start.y,
e.data.handles.end.x - e.data.handles.start.x,
e.data.handles.end.y - e.data.handles.start.y
start.x,
start.y,
end.x - start.x,
end.y - start.y
)
this.drawText(ctx, { x: e.data.handles.start.x, y: e.data.handles.start.y }, { x: e.data.handles.end.x, y: e.data.handles.end.y }, e.data.remark, this.color)
this.drawText(ctx, start, end, e.data.remark, this.color)
}
}
})
@ -495,6 +540,10 @@ export default {
if (e.button !== 0) {
return //
}
if (this.isDragging) {
this.stopDragging()
return
}
//
if (this.model === 'ArrowAnnotate' || this.model === 'RectangleRoi') {

View File

@ -33,17 +33,20 @@
<div v-for="item in qs.Childrens" :key="item.Id">
<div v-if="item.Type === 'basicTable'" class="flex-row" style="margin:3px 0;">
<div class="title">{{ item.QuestionName }}</div>
<div v-if="(item.LesionType === 104) && readingTaskState < 2">
<div class="add-icon" @click.prevent="downloadTpl(item.LesionType)">
<div v-if="(item.LesionType === 104)">
<div class="add-icon" @click.prevent="downloadTpl(item.LesionType)" v-if="readingTaskState < 2">
<i class="el-icon-download" />
</div>
<div class="add-icon" style="margin: 0 5px;"
@click.prevent="uploadTpl(item.LesionType, item.QuestionName)">
@click.prevent="uploadTpl(item.LesionType, item.QuestionName)" v-if="readingTaskState < 2">
<i class="el-icon-upload2" />
</div>
<div class="add-icon" @click.prevent="handleAddOrEdit('add', item)">
<div class="add-icon" @click.prevent="handleAddOrEdit('add', item)" v-if="readingTaskState < 2">
<i class="el-icon-plus" />
</div>
<div class="add-icon" @click.prevent="handleView(item)">
<i class="el-icon-view" />
</div>
</div>
</div>
@ -174,7 +177,8 @@
<div v-if="isCurrentTask && readingTaskState < 2 && (qs.GroupClassify === 5 || qs.GroupClassify === 6)"
class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<!-- 保存 -->
<el-button type="primary" size="mini" :disabled="!formChanged" @click="handleSave(index)">
<el-button type="primary" size="mini" :loading="!!rowSaveLoadingMap[index]"
:disabled="!!rowSaveLoadingMap[index] || !formChanged" @click="handleSave(index)">
{{ $t('common:button:save') }}
</el-button>
</div>
@ -185,11 +189,22 @@
</template>
<!-- 计算值 -->
<Questions ref="ecrf2" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
:group-classify="4" style="margin-top:20px" />
<Questions
ref="ecrf2"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="4"
:question-type="12"
style="margin-top:20px" />
<!-- 评估结果 -->
<Questions ref="ecrf3" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
<Questions
ref="ecrf3"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:question-type="12"
:group-classify="5" />
<el-dialog v-if="addOrEdit.visible" :visible.sync="addOrEdit.visible" :close-on-click-modal="false"
:title="addOrEdit.title" width="500px">
@ -202,7 +217,8 @@
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" class="my_upload_btn" @click="saveFormData">
<el-button size="small" class="my_upload_btn" :loading="dialogSaveLoading"
:disabled="dialogSaveLoading" @click="saveFormData">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
@ -222,6 +238,7 @@ import { saveTaskQuestion, submitTableQuestion, deleteReadingRowAnswer } from '@
import { resetReadingTask, getOCTFCTTemplate, getOCTLipidAngleTemplate } from '@/api/reading'
import DicomEvent from './../DicomEvent'
import store from '@/store'
import { getToken } from '@/utils/auth'
import { mapGetters } from 'vuex'
import Questions from './../Questions'
import QuestionTableFormItem from './QuestionTableFormItem'
@ -281,7 +298,10 @@ export default {
avgId: '',
sRoiStartDistanceId: '',
sRoiEndDistanceId: '',
sRoiDistanceId: ''
sRoiDistanceId: '',
rowSaveLoadingMap: {},
dialogSaveLoading: false,
childWindows: []
}
},
computed: {
@ -321,11 +341,13 @@ export default {
DicomEvent.$on('refreshQuestions', _ => {
this.refreshQuestions()
})
window.addEventListener('beforeunload', this.closeAllChildWindows)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
DicomEvent.$off('getUnSaveTarget')
DicomEvent.$off('refreshQuestions')
window.removeEventListener('beforeunload', this.closeAllChildWindows)
},
methods: {
handleReadingChart(e) {
@ -606,29 +628,37 @@ export default {
return v
}
},
async handleSave(index) {
async handleSave(index, options = {}) {
if (this.rowSaveLoadingMap[index]) return
const refName = `questions${index}`
const valid = await this.$refs[refName][0].validate()
if (!valid) return
const loading = this.$loading({ fullscreen: true })
var answers = []
for (const k in this.questionForm) {
answers.push({ id: k, answer: Array.isArray(this.questionForm[k]) ? JSON.stringify(this.questionForm[k]) : this.questionForm[k] })
}
var params = {
visitTaskId: this.visitTaskId,
answers: answers
}
this.$set(this.rowSaveLoadingMap, index, true)
let loading = null
try {
const valid = await this.$refs[refName][0].validate()
if (!valid) return
loading = this.$loading({ fullscreen: true })
var answers = []
for (const k in this.questionForm) {
answers.push({ id: k, answer: Array.isArray(this.questionForm[k]) ? JSON.stringify(this.questionForm[k]) : this.questionForm[k] })
}
var params = {
visitTaskId: this.visitTaskId,
answers: answers
}
await saveTaskQuestion(12, params)
this.$message.success(this.$t('common:message:savedSuccessfully'))
loading.close()
if (!options.silentSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
DicomEvent.$emit('getReportInfo', true)
this.refreshQuestions()
if (!options.skipRefresh) {
await this.refreshQuestions()
}
this.formChanged = false
} catch (e) {
console.log(e)
loading.close()
} finally {
if (loading) loading.close()
this.$set(this.rowSaveLoadingMap, index, false)
}
},
getECRFQuestions(obj) {
@ -747,11 +777,35 @@ export default {
this.addOrEdit.visible = true
this.addOrEdit.lesionType = row.LesionType
},
handleView(item) {
if (this.openWindow) {
this.openWindow.close()
}
localStorage.setItem('taskBlindName', this.taskBlindName)
localStorage.setItem('isReadingShowSubjectInfo', this.isReadingShowSubjectInfo)
let token = getToken()
let routeData = this.$router.resolve({ path: `/ecrfList?trialId=${this.$route.query.trialId}&visitTaskId=${this.visitTaskId}&questionId=${item.Id}&TokenKey=${token}` })
const win = window.open(routeData.href, '_blank')
if (win) {
this.childWindows.push(win)
}
},
closeAllChildWindows() {
this.childWindows.forEach(win => {
if (win && !win.closed) {
win.close()
}
})
this.childWindows = []
},
async saveFormData() {
const valid = await this.$refs.tableQsForm.validate()
if (!valid) return
const loading = this.$loading({ fullscreen: true })
if (this.dialogSaveLoading) return
this.dialogSaveLoading = true
let loading = null
try {
const valid = await this.$refs.tableQsForm.validate()
if (!valid) return
loading = this.$loading({ fullscreen: true })
var answers = []
var reg = new RegExp(/^[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}$/)
for (const k in this.qsForm) {
@ -785,10 +839,11 @@ export default {
this.addOrEdit.visible = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
loading.close()
} catch (e) {
console.log(e)
loading.close()
} finally {
if (loading) loading.close()
this.dialogSaveLoading = false
}
},
async handleDelete(row, index) {
@ -844,9 +899,29 @@ export default {
}
},
async uploadDlgClose() {
this.refreshQuestions()
DicomEvent.$emit('getReportInfo', true)
this.upload.visible = false
try {
await this.refreshQuestions()
const saveTasks = []
for (let index = 0; index < this.questions.length; index++) {
const qs = this.questions[index]
if (qs.GroupClassify === 6) {
saveTasks.push(() => this.handleSave(index, { silentSuccess: true, skipRefresh: true }))
}
}
saveTasks.push(() => this.$refs['ecrf2'] && this.$refs['ecrf2'].handleSave({ silentSuccess: true, skipRefresh: true }))
saveTasks.push(() => this.$refs['ecrf3'] && this.$refs['ecrf3'].handleSave({ silentSuccess: true, skipRefresh: true }))
for (const saveTask of saveTasks) {
try {
await saveTask()
} catch (e) {
console.log(e)
}
}
} catch (e) {
console.log(e)
} finally {
this.upload.visible = false
}
},
renderHeader(h, { column, $index }) {
const span = document.createElement('span')

View File

@ -267,4 +267,9 @@ export default {
::v-deep .el-select.is-disabled .el-input__inner {
background-color: #646464a1;
}
::v-deep .el-radio__input.is-disabled.is-checked .el-radio__inner {
background-color: #428bca;
border-color: #428bca;
}
</style>

View File

@ -22,7 +22,7 @@
</el-form>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-button size="small" type="primary" @click="handleSave"> {{ $t('common:button:save') }}</el-button>
<el-button size="small" type="primary" :loading="loading" :disabled="loading" @click="handleSave"> {{ $t('common:button:save') }}</el-button>
</div>
</div>
@ -64,18 +64,19 @@ export default {
}
},
async handleSave() {
const valid = await this.$refs.otherForm.validate()
if (!valid) return
if (this.loading) return
this.loading = true
try {
const valid = await this.$refs.otherForm.validate()
if (!valid) return
let params = Object.assign(this.form, {imageToolType: this.imageToolType})
const res = await setAutoCutNextTask(params)
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
this.loading = false
} catch (e) {
this.loading = false
} finally {
this.loading = false
}
}
}

View File

@ -1,4 +1,4 @@
<template>
<template>
<el-form v-if="isRender" ref="measurementForm" v-loading="loading" :model="questionForm" size="mini"
class="measurement-form">
<div class="base-dialog-body">
@ -116,7 +116,7 @@
{{ $t('common:button:delete') }}
</el-button>
<!-- 保存 -->
<el-button size="mini" style="margin-left: 5px;padding:7px 10px" @click="handleSave">
<el-button size="mini" style="margin-left: 5px;padding:7px 10px" :loading="saveLoading" :disabled="saveLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</div>
@ -198,7 +198,9 @@ export default {
pictureBaseStr: '',
digitPlaces: 0,
focusQs: null,
currentMarkTool: ''
currentMarkTool: '',
saveLoading: false,
validationDialogVisible: false
}
},
@ -216,7 +218,24 @@ export default {
handleReadingChart(e) {
this.$emit('handleReadingChart', e)
},
validateLesionCount(rule, value, callback) {
async showValidationDialog(msg) {
if (this.validationDialogVisible) {
return false
}
this.validationDialogVisible = true
try {
await this.$confirm(msg, {
type: 'warning',
showCancelButton: false,
closeOnClickModal: false,
closeOnPressEscape: false
})
} catch (e) {
} finally {
this.validationDialogVisible = false
}
return false
}, validateLesionCount(rule, value, callback) {
// const reg = /^[1-9][0-9]?$|^100$/
// const reg = /^(0+)|[^\d]+/g
const reg = /^(?:[1-9]|[1-9][0-9])$/
@ -563,8 +582,20 @@ export default {
return new Blob([bytesCode], { type: imgtype })
},
async handleSave() {
const valid = await this.$refs.measurementForm.validate()
if (!valid) return
if (this.saveLoading) return
this.saveLoading = true
let valid = false
try {
valid = await this.$refs.measurementForm.validate()
} catch (e) {
this.saveLoading = false
console.log(e)
return
}
if (!valid) {
this.saveLoading = false
return
}
var lesionState = this.getQuestionVal(7)
var measureData = this.questionForm.MeasureData
// 线
@ -573,12 +604,9 @@ export default {
// 使2
if (this.lesionType === 2 && lesionState === 2) {
if (!(measureData && measureData.type === 'ArrowAnnotate')) {
this.$confirm(this.$t('trials:readingPGWC:warnning:msg2'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
loading.close()
await this.showValidationDialog(this.$t('trials:readingPGWC:warnning:msg2'))
this.saveLoading = false
return
}
}
@ -586,48 +614,36 @@ export default {
// 使2
if (this.lesionType === 3 && lesionState === 2) {
if (!(measureData && measureData.type === 'ArrowAnnotate')) {
this.$confirm(this.$t('trials:readingPGWC:warnning:msg3'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
loading.close()
await this.showValidationDialog(this.$t('trials:readingPGWC:warnning:msg3'))
this.saveLoading = false
return
}
}
// 使3
if (this.lesionType === 3 && lesionState === 3) {
if (!(measureData && measureData.type === 'ArrowAnnotate')) {
this.$confirm(this.$t('trials:readingPGWC:warnning:msg4'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
loading.close()
await this.showValidationDialog(this.$t('trials:readingPGWC:warnning:msg4'))
this.saveLoading = false
return
}
}
// 使1
if (this.lesionType === 3 && lesionState === 1) {
if (!(measureData && measureData.type === 'ArrowAnnotate')) {
this.$confirm(this.$t('trials:readingPGWC:warnning:msg5'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
loading.close()
await this.showValidationDialog(this.$t('trials:readingPGWC:warnning:msg5'))
this.saveLoading = false
return
}
}
// 4
if (this.lesionType === 3 && lesionState === 4) {
if (measureData) {
this.$confirm(this.$t('trials:readingPGWC:warnning:msg6'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
loading.close()
await this.showValidationDialog(this.$t('trials:readingPGWC:warnning:msg6'))
this.saveLoading = false
return
}
}
@ -690,10 +706,12 @@ export default {
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('setMeasuredToolsPassive')
loading.close()
this.saveLoading = false
}
} catch (e) {
console.log(e)
loading.close()
this.saveLoading = false
}
})
},
@ -858,6 +876,11 @@ export default {
background-color: #646464a1;
}
::v-deep .el-radio__input.is-disabled.is-checked .el-radio__inner {
background-color: #428bca;
border-color: #428bca;
}
.el-form-item__content .el-select {
width: 100%;
}

View File

@ -447,4 +447,9 @@ export default {
::v-deep .el-select.is-disabled .el-input__inner {
background-color: #646464a1;
}
::v-deep .el-radio__input.is-disabled.is-checked .el-radio__inner {
background-color: #428bca;
border-color: #428bca;
}
</style>

View File

@ -10,7 +10,8 @@
<div class="ecrf-footer">
<i v-if="groupClassify === null || groupClassify === 1" class="el-icon-warning feedback-icon"
@click="openFeedBackTable" :style="{ color: isExistUnprocessedFeedback ? '#ffeb3b' : '#fff' }" />
<el-button size="mini" :disabled="!questionFormChangeState || (!formChanged && groupClassify > 0)"
<el-button size="mini" :loading="saveLoading"
:disabled="saveLoading || !questionFormChangeState || (!formChanged && groupClassify > 0)"
:type="questionFormChangeState || (!formChanged && groupClassify > 0) ? 'primary' : null"
@click="handleSave">{{ $t('common:button:save') }}</el-button>
</div>
@ -80,7 +81,8 @@ export default {
calculateSpleenStatus: '',
formChanged: false,
digitPlaces: 2,
isExistUnprocessedFeedback: false
isExistUnprocessedFeedback: false,
saveLoading: false
}
},
computed: {
@ -115,7 +117,7 @@ export default {
})
if (this.groupClassify === 3) {
DicomEvent.$on('addAnnotation', obj => {
this.$set(obj, 'SaveEnum', 1)
// this.$set(obj, 'SaveEnum', 1)
this.addAnnotation(obj)
})
DicomEvent.$on('removeAnnotation', obj => {
@ -277,74 +279,86 @@ export default {
})
},
handleSave() {
this.$refs['questions'].validate(async (valid) => {
if (!valid) return
// lugano
if ((this.criterionType === 2 || this.criterionType === 18) && this.groupClassify === 3) {
//
var existUnSave = this.checkAnnotationStatus(this.questions)
if (existUnSave) {
this.$alert(this.$t('trials:lugano:message:saveWarning'), this.$t('trials:lugano:fusionDialog:warning'))
return
}
var currentSpleenStatus = this.questionForm[this.spleenStatusId]
var currentSpleenLength = this.questionForm[this.spleenLengthId]
currentSpleenStatus = isNaN(parseInt(currentSpleenStatus)) ? null : parseInt(currentSpleenStatus)
var stIdx = this.measurements.findIndex(i => i.QuestionType === 60)
var slIdx = this.measurements.findIndex(i => i.QuestionType === 61)
if (currentSpleenLength && currentSpleenStatus === 5) {
// '!'
this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
} else if (currentSpleenStatus === 5 && ((stIdx > -1 && this.measurements[stIdx].MeasureData) || (slIdx > -1 && this.measurements[slIdx].MeasureData))) {
//
this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
} else if (((stIdx > -1 && this.measurements[stIdx].MeasureData) || (slIdx > -1 && this.measurements[slIdx].MeasureData)) && !currentSpleenLength) {
this.$alert(this.$t('trials:lugano:message:validSpleen2'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
} else {
this.saveQuestionsList()
}
} else if ((this.criterionType === 2 || this.criterionType === 18) && this.groupClassify === 1 && this.questionForm[this.imageQualityIssuesId] && parseInt(this.questionForm[this.imageQualityIssuesId]) === 6) {
// PET-CT
let res = null
if (this.criterionType === 18) {
res = await getCanChooseNotMergeNoPet({ visitTaskId: this.visitTaskId })
} else {
res = await getCanChooseNotMerge({ visitTaskId: this.visitTaskId })
}
if (res.IsSuccess && !res.Result.IsCanChooseNotMerge) {
// this.$alert(this.$t('trials:lugano:saveQuestions:warning1'), this.$t('trials:lugano:fusionDialog:warning'), {showCancelButton: false})
this.$alert(this.$t('trials:lugano:saveQuestions:warning1'), this.$t('trials:lugano:fusionDialog:warning'), {
showCancelButton: false,
callback: action => { }
})
return
} else {
// PET-CTFDG-PETNE
this.$confirm(this.$t('trials:lugano:saveQuestions:warning2'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
this.saveQuestionsList()
})
}
} else {
this.saveQuestionsList()
}
// this.saveQuestionsList()
async handleSave(options = {}) {
if (this.saveLoading) return false
this.saveLoading = true
const valid = await new Promise(resolve => {
this.$refs['questions'].validate(v => resolve(v))
})
if (!valid) {
this.saveLoading = false
return false
}
// lugano
if ((this.criterionType === 2 || this.criterionType === 18) && this.groupClassify === 3) {
//
var existUnSave = this.checkAnnotationStatus(this.questions)
if (existUnSave) {
await this.$alert(this.$t('trials:lugano:message:saveWarning'), this.$t('trials:lugano:fusionDialog:warning'))
this.saveLoading = false
return false
}
var currentSpleenStatus = this.questionForm[this.spleenStatusId]
var currentSpleenLength = this.questionForm[this.spleenLengthId]
currentSpleenStatus = isNaN(parseInt(currentSpleenStatus)) ? null : parseInt(currentSpleenStatus)
var stIdx = this.measurements.findIndex(i => i.QuestionType === 60)
var slIdx = this.measurements.findIndex(i => i.QuestionType === 61)
if (currentSpleenLength && currentSpleenStatus === 5) {
// '!'
await this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
return false
} else if (currentSpleenStatus === 5 && ((stIdx > -1 && this.measurements[stIdx].MeasureData) || (slIdx > -1 && this.measurements[slIdx].MeasureData))) {
//
await this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
return false
} else if (((stIdx > -1 && this.measurements[stIdx].MeasureData) || (slIdx > -1 && this.measurements[slIdx].MeasureData)) && !currentSpleenLength) {
await this.$alert(this.$t('trials:lugano:message:validSpleen2'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
return false
}
return await this.saveQuestionsList(options)
} else if ((this.criterionType === 2 || this.criterionType === 18) && this.groupClassify === 1 && this.questionForm[this.imageQualityIssuesId] && parseInt(this.questionForm[this.imageQualityIssuesId]) === 6) {
// PET-CT
let res = null
if (this.criterionType === 18) {
res = await getCanChooseNotMergeNoPet({ visitTaskId: this.visitTaskId })
} else {
res = await getCanChooseNotMerge({ visitTaskId: this.visitTaskId })
}
if (res.IsSuccess && !res.Result.IsCanChooseNotMerge) {
await this.$alert(this.$t('trials:lugano:saveQuestions:warning1'), this.$t('trials:lugano:fusionDialog:warning'), {
showCancelButton: false,
callback: action => { }
})
this.saveLoading = false
return false
}
// PET-CTFDG-PETNE
const confirm = await this.$confirm(this.$t('trials:lugano:saveQuestions:warning2'), {
type: 'warning',
distinguishCancelAndClose: true
}).then(() => true).catch(() => false)
if (!confirm) {
this.saveLoading = false
return false
}
return await this.saveQuestionsList(options)
} else {
return await this.saveQuestionsList(options)
}
},
async saveQuestionsList() {
async saveQuestionsList(options = {}) {
this.loading = true
var answers = []
var imageQuality = null
@ -370,18 +384,22 @@ export default {
// })
// params.questionMarkInfoList = questionMarkInfoList
// }
saveTaskQuestion(this.questionType, params).then(async res => {
try {
const res = await saveTaskQuestion(this.questionType, params)
if (this.imageQualityIssuesId) {
store.dispatch('reading/setImageQualityIssues', this.questionForm[this.imageQualityIssuesId])
if (this.questionForm[this.imageQualityIssuesId] && parseInt(this.questionForm[this.imageQualityIssuesId]) === 6) {
DicomEvent.$emit('closePetct')
}
}
this.$message.success(this.$t('common:message:savedSuccessfully'))
var trialId = this.$route.query.trialId
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: trialId, visitTaskId: this.visitTaskId })
this.getQuestions(this.visitTaskId, true)
if (!options.silentSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
if (!options.skipRefresh) {
var trialId = this.$route.query.trialId
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: trialId, visitTaskId: this.visitTaskId })
this.getQuestions(this.visitTaskId, true)
}
this.loading = false
if (this.isQulityIssues) {
DicomEvent.$emit('questionFormChange', false)
@ -404,9 +422,13 @@ export default {
}
}
this.formChanged = false
}).catch(() => {
this.saveLoading = false
return res
} catch (e) {
this.loading = false
})
this.saveLoading = false
throw e
}
},
checkAnnotationStatus(obj) {
for (let i = 0; i < obj.length; i++) {

View File

@ -27,7 +27,7 @@
class="study-wrapper">
<StudyList v-if="selectArr.includes(s.VisitTaskId)" :ref="s.VisitTaskId" :visit-task-id="s.VisitTaskId"
:trial-id="trialId" :subject-visit-id="s.VisitId" :task-blind-name="s.TaskBlindName"
:is-reading-show-subject-info="isReadingShowSubjectInfo" @loadImageStack="loadImageStack"
:is-reading-show-subject-info="isReadingShowSubjectInfo" :is-reading-task-view-in-order="isReadingTaskViewInOrder" @loadImageStack="loadImageStack"
@previewNoneDicoms="previewNoneDicoms" />
</div>
</div>

View File

@ -1,4 +1,4 @@
<template>
<template>
<el-form v-if="isRender" ref="measurementForm" v-loading="loading" :model="questionForm" size="mini"
class="measurement-form">
<div class="base-dialog-body">
@ -139,7 +139,7 @@
{{ $t('common:button:delete') }}
</el-button>
<!-- 保存 -->
<el-button size="mini" @click="handleSave">
<el-button size="mini" :loading="saveLoading" :disabled="saveLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</div>
@ -217,7 +217,9 @@ export default {
lesionMark: '',
deleteInfo: null,
pictureBaseStr: '',
currentMarkTool: ''
currentMarkTool: '',
saveLoading: false,
validationDialogVisible: false
}
},
mounted() {
@ -234,8 +236,30 @@ export default {
handleReadingChart(e) {
this.$emit('handleReadingChart', e)
},
async initForm() {
async showValidationDialog(msg) {
if (this.validationDialogVisible) {
return false
}
this.validationDialogVisible = true
try {
await this.$confirm(msg, {
type: 'warning',
showCancelButton: false,
closeOnClickModal: false,
closeOnPressEscape: false
})
} catch (e) {
} finally {
this.validationDialogVisible = false
}
return false
}, async initForm() {
const loading = this.$loading({ fullscreen: true })
const closeLoading = loading.close.bind(loading)
loading.close = () => {
closeLoading()
this.saveLoading = false
}
this.questions.forEach(item => {
var val = this.answers[item.Id]
if (item.DictionaryCode) {
@ -763,8 +787,20 @@ export default {
return new Blob([bytesCode], { type: imgtype })
},
async handleSave() {
const valid = await this.$refs.measurementForm.validate()
if (!valid) return
if (this.saveLoading) return
this.saveLoading = true
let valid = false
try {
valid = await this.$refs.measurementForm.validate()
} catch (e) {
this.saveLoading = false
console.log(e)
return
}
if (!valid) {
this.saveLoading = false
return
}
const loading = this.$loading({ fullscreen: true })
var measureData = this.questionForm.MeasureData
var lesionState = this.getQuestionVal(7)
@ -779,11 +815,8 @@ export default {
// 使,15mm
if (!(measureData && measureData.type === 'Bidirectional' && lesionShort >= 15)) {
// 使15mm
this.$confirm(this.$t('trials:reading:warnning:msg19'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg19'))
loading.close()
return
}
@ -791,11 +824,8 @@ export default {
// 10mm2
if (!(measureData && measureData.type === 'Length' && lesionLength >= 10 && (measureData.thick && lesionLength >= 2 * measureData.thick || !measureData.thick))) {
// 使10mm2
this.$confirm(this.$t('trials:reading:warnning:msg20'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg20'))
loading.close()
return
}
@ -808,11 +838,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg21'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg21'))
loading.close()
return
}
@ -820,11 +847,8 @@ export default {
//
if (!(measureData && (measureData.type === 'Length' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg22'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg22'))
loading.close()
return
}
@ -838,11 +862,8 @@ export default {
// 使
if (!(measureData && measureData.type === 'Bidirectional' && lesionShort >= 10)) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg23'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg23'))
loading.close()
return
}
@ -850,11 +871,8 @@ export default {
//
if (!(measureData && measureData.type === 'Length')) {
// `使`
this.$confirm(this.$t('trials:reading:warnning:msg24'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg24'))
loading.close()
return
}
@ -866,11 +884,8 @@ export default {
// 使5mm
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg25'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg25'))
loading.close()
return
} else if (measureData && measureData.type === 'ArrowAnnotate') {
@ -882,11 +897,8 @@ export default {
// 5mm
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg26'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg26'))
loading.close()
return
} else if (measureData && measureData.type === 'ArrowAnnotate') {
@ -903,11 +915,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg27'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg27'))
loading.close()
return
}
@ -915,11 +924,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg28'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg28'))
loading.close()
return
}
@ -950,11 +956,8 @@ export default {
// }
if (!(measureData && (measureData.type === 'Bidirectional' && lesionShort < 10))) {
// `使,10mm`
this.$confirm(this.$t('trials:reading:warnning:msg29'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg29'))
loading.close()
return
}
@ -962,11 +965,8 @@ export default {
//
if (!(measureData && measureData.type === 'ArrowAnnotate')) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg30'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg30'))
loading.close()
return
} else if (measureData && measureData.type === 'ArrowAnnotate') {
@ -983,11 +983,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg31'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg31'))
loading.close()
return
}
@ -995,11 +992,8 @@ export default {
//
if (!(measureData && (measureData.type === 'Length' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg32'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg32'))
loading.close()
return
}
@ -1012,11 +1006,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'RectangleRoi'))) {
// `使`
this.$confirm(this.$t('trials:reading:warnning:msg33'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg33'))
loading.close()
return
}
@ -1024,11 +1015,8 @@ export default {
//
if (!(measureData && (measureData.type === 'Length' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg34'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg34'))
loading.close()
return
}
@ -1041,11 +1029,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg35'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg35'))
loading.close()
return
}
@ -1053,11 +1038,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg36'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg36'))
loading.close()
return
}
@ -1070,11 +1052,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'ArrowAnnotate' || (measureData.type === 'Bidirectional' && lesionShort < 10)))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg37'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg37'))
loading.close()
return
}
@ -1082,11 +1061,8 @@ export default {
//
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg38'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg38'))
loading.close()
return
}
@ -1099,11 +1075,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg39'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg39'))
loading.close()
return
}
@ -1111,11 +1084,8 @@ export default {
//
if (!(measureData && (measureData.type === 'Length' || measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg40'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg40'))
loading.close()
return
}
@ -1128,11 +1098,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'Bidirectional' || measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg41'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg41'))
loading.close()
return
}
@ -1140,11 +1107,8 @@ export default {
//
if (!(measureData && (measureData.type === 'Length' || measureData.type === 'ArrowAnnotate'))) {
// `使`
this.$confirm(this.$t('trials:reading:warnning:msg42'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg42'))
loading.close()
return
}
@ -1157,11 +1121,8 @@ export default {
// 使
if (measureData) {
// ``
this.$confirm(this.$t('trials:reading:warnning:msg43'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg43'))
loading.close()
return
}
@ -1169,11 +1130,8 @@ export default {
//
if (measureData) {
//
this.$confirm(this.$t('trials:reading:warnning:msg44'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg44'))
loading.close()
return
}
@ -1186,11 +1144,8 @@ export default {
// 使
if (!(measureData && (measureData.type === 'ArrowAnnotate' || (measureData.type === 'Bidirectional' && lesionShort < 10)))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg45'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg45'))
loading.close()
return
}
@ -1198,11 +1153,8 @@ export default {
//
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:reading:warnning:msg46'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:reading:warnning:msg46'))
loading.close()
return
}
@ -1277,9 +1229,11 @@ export default {
DicomEvent.$emit('setMeasuredToolsPassive')
}
loading.close()
this.saveLoading = false
} catch (e) {
console.log(e)
loading.close()
this.saveLoading = false
}
})
},
@ -1456,6 +1410,11 @@ export default {
background-color: #646464a1;
}
::v-deep .el-radio__input.is-disabled.is-checked .el-radio__inner {
background-color: #428bca;
border-color: #428bca;
}
::v-deep .el-button--mini,
.el-button--mini.is-round {
padding: 7px 10px;

View File

@ -1,4 +1,4 @@
<template>
<template>
<el-form v-if="isRender" ref="measurementForm" v-loading="loading" :model="questionForm" size="mini"
class="measurement-form">
<div class="base-dialog-body">
@ -130,7 +130,7 @@
{{ $t('common:button:delete') }}
</el-button>
<!-- 保存 -->
<el-button size="mini" @click="handleSave">
<el-button size="mini" :loading="saveLoading" :disabled="saveLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</div>
@ -208,7 +208,9 @@ export default {
lesionMark: '',
deleteInfo: null,
pictureBaseStr: '',
currentMarkTool: ''
currentMarkTool: '',
saveLoading: false,
validationDialogVisible: false
}
},
mounted() {
@ -225,8 +227,30 @@ export default {
handleReadingChart(e) {
this.$emit('handleReadingChart', e)
},
async initForm() {
async showValidationDialog(msg) {
if (this.validationDialogVisible) {
return false
}
this.validationDialogVisible = true
try {
await this.$confirm(msg, {
type: 'warning',
showCancelButton: false,
closeOnClickModal: false,
closeOnPressEscape: false
})
} catch (e) {
} finally {
this.validationDialogVisible = false
}
return false
}, async initForm() {
const loading = this.$loading({ fullscreen: true })
const closeLoading = loading.close.bind(loading)
loading.close = () => {
closeLoading()
this.saveLoading = false
}
this.questions.forEach(item => {
var val = this.answers[item.Id]
if (item.DictionaryCode) {
@ -690,8 +714,20 @@ export default {
return new Blob([bytesCode], { type: imgtype })
},
async handleSave() {
const valid = await this.$refs.measurementForm.validate()
if (!valid) return
if (this.saveLoading) return
this.saveLoading = true
let valid = false
try {
valid = await this.$refs.measurementForm.validate()
} catch (e) {
this.saveLoading = false
console.log(e)
return
}
if (!valid) {
this.saveLoading = false
return
}
const loading = this.$loading({ fullscreen: true })
var measureData = this.questionForm.MeasureData
var lesionState = this.getQuestionVal(7)
@ -704,11 +740,8 @@ export default {
// 10mm2
if (!(measureData && measureData.type === 'Length' && lesionLength >= 10 && (measureData.thick && lesionLength >= 2 * measureData.thick || !measureData.thick))) {
// 10mm2使
this.$confirm(this.$t('trials:readingBM:warnning:msg06'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg06'))
loading.close()
return
}
@ -718,11 +751,8 @@ export default {
if (this.lesionType === 1 && lesionState === 0) {
if (!(measureData && (measureData.type === 'Length' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:readingBM:warnning:msg07'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg07'))
loading.close()
return
}
@ -733,11 +763,8 @@ export default {
if (this.lesionType === 0 && lesionState === 0) {
if (!(measureData && measureData.type === 'Length')) {
// `使`
this.$confirm(this.$t('trials:readingBM:warnning:msg08'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg08'))
loading.close()
return
}
@ -746,11 +773,8 @@ export default {
if (this.lesionType === 0 && lesionState === 1) {
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:readingBM:warnning:msg09'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg09'))
loading.close()
return
} else if (measureData && measureData.type === 'ArrowAnnotate') {
@ -764,11 +788,8 @@ export default {
if (this.lesionType === 0 && lesionState === 2) {
if (measureData) {
//
this.$confirm(this.$t('trials:readingBM:warnning:msg10'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg10'))
loading.close()
return
}
@ -778,11 +799,8 @@ export default {
if (this.lesionType === 0 && lesionState === 3) {
if (!(measureData && measureData.type === 'ArrowAnnotate')) {
// 使
this.$confirm(this.$t('trials:readingBM:warnning:msg11'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg11'))
loading.close()
return
} else if (measureData && measureData.type === 'ArrowAnnotate') {
@ -796,11 +814,8 @@ export default {
if (this.lesionType === 1 && lesionState === 0) {
if (!(measureData && (measureData.type === 'Length' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:readingBM:warnning:msg12'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg12'))
loading.close()
return
}
@ -810,11 +825,8 @@ export default {
if (this.lesionType === 1 && lesionState === 1) {
if (!(measureData && (measureData.type === 'Length' || measureData.type === 'RectangleRoi'))) {
// 使
this.$confirm(this.$t('trials:readingBM:warnning:msg13'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg13'))
loading.close()
return
}
@ -824,11 +836,8 @@ export default {
if (this.lesionType === 1 && lesionState === 2) {
if (measureData) {
//
this.$confirm(this.$t('trials:readingBM:warnning:msg14'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg14'))
loading.close()
return
}
@ -838,11 +847,8 @@ export default {
if (this.lesionType === 1 && lesionState === 3) {
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:readingBM:warnning:msg15'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg15'))
loading.close()
return
}
@ -852,11 +858,8 @@ export default {
if (this.lesionType === 2 && lesionState === 0) {
if (!(measureData && (measureData.type === 'Length' || measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:readingBM:warnning:msg16'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg16'))
loading.close()
return
}
@ -866,11 +869,8 @@ export default {
if (this.lesionType === 2 && lesionState === 1) {
if (!(measureData && (measureData.type === 'Length' || measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:readingBM:warnning:msg17'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg17'))
loading.close()
return
}
@ -880,11 +880,8 @@ export default {
if (this.lesionType === 2 && lesionState === 2) {
if (measureData) {
//
this.$confirm(this.$t('trials:readingBM:warnning:msg18'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg18'))
loading.close()
return
}
@ -894,11 +891,8 @@ export default {
if (this.lesionType === 2 && lesionState === 3) {
if (!(measureData && (measureData.type === 'ArrowAnnotate'))) {
// 使
this.$confirm(this.$t('trials:readingBM:warnning:msg19'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
await this.showValidationDialog(this.$t('trials:readingBM:warnning:msg19'))
loading.close()
return
}
@ -966,9 +960,11 @@ export default {
DicomEvent.$emit('setMeasuredToolsPassive')
}
loading.close()
this.saveLoading = false
} catch (e) {
console.log(e)
loading.close()
this.saveLoading = false
}
})
},
@ -1139,6 +1135,11 @@ export default {
background-color: #646464a1;
}
::v-deep .el-radio__input.is-disabled.is-checked .el-radio__inner {
background-color: #428bca;
border-color: #428bca;
}
::v-deep .el-button--mini,
.el-button--mini.is-round {
padding: 7px 10px;

View File

@ -22,11 +22,13 @@
{{ $t('trials:readingReport:button:refresh') }}
</el-button>
<el-button v-if="readingTaskState < 2 && CriterionType !== 10" type="primary" size="small"
:loading="submitLockAction === 'save'" :disabled="!!submitLockAction"
@click="handleSave(true)">
<!-- 保存 -->
{{ $t('common:button:save') }}
</el-button>
<el-button v-if="readingTaskState < 2" type="primary" size="small" @click="beforeLeave">
<el-button v-if="readingTaskState < 2" type="primary" size="small"
:loading="submitLockAction === 'submit'" :disabled="!!submitLockAction" @click="beforeLeave">
<!-- 提交 -->
{{ $t('common:button:submit') }}
</el-button>
@ -126,7 +128,7 @@
:autosize="{ minRows: 2, maxRows: 4 }" size="mini" maxlength="500"
@change="evaluateReasonChange" />
<!-- 系统评估结果为xxx,与当前调整的结果不一致请填写调整原因 -->
<p v-if="currentEvaluateResult !== tumorEvaluate"
<p v-if="currentEvaluateResult !== null && currentEvaluateResult !== undefined && currentEvaluateResult !== tumorEvaluate"
style="width: 140px;padding:0 2px;white-space: normal;word-break: break-all;word-wrap: break-word;"
v-html="getWarningText()" />
<p v-else-if="currentExistDisease !== isExistDisease"
@ -279,7 +281,9 @@ export default {
tLesionCount: null,
ntLesionCount: null,
openWindow: null,
expandedRows: []
expandedRows: [],
submitLockAction: '',
validationDialogVisible: false
}
},
computed: {
@ -353,6 +357,24 @@ export default {
})
})
},
async showValidationDialog(msg) {
if (this.validationDialogVisible) {
return false
}
this.validationDialogVisible = true
try {
await this.$confirm(msg, {
type: 'warning',
showCancelButton: false,
closeOnClickModal: false,
closeOnPressEscape: false
})
} catch (e) {
} finally {
this.validationDialogVisible = false
}
return false
},
async beforeLeave() {
// if (this.questionFormChangeState && this.CriterionType !== 2) {
// var msg = this.$t('trials:readingReport:message:msg5')
@ -483,15 +505,23 @@ export default {
var idx = item.Childrens.findIndex(i => i.QuestionMark === 8)
var idxLoc = item.Childrens.findIndex(i => i.QuestionMark === 10)
var state = item.Childrens.findIndex(i => i.QuestionMark === 7)
if (this.CriterionType === 22) {
idx = item.Childrens.findIndex(i => i.QuestionMark === 1106)
}
if (idx > -1) {
if (item.Childrens[idx].Answer.length > 0) {
var k = item.Childrens[idx].Answer.findIndex(v => v.Answer !== '')
var part = ''
if (obj.IsCanEditPosition) {
part = `${item.Childrens[idx].Answer[k].Answer}--${item.Childrens[idxLoc].Answer[k].Answer}`
if (this.CriterionType === 22) {
part = item.Childrens[idx].Answer[k].Answer ? this.$fd('Liver4Segmentation', parseInt(item.Childrens[idx].Answer[k].Answer)) : ''
} else {
part = `${item.Childrens[idx].Answer[k].Answer}`
if (obj.IsCanEditPosition) {
part = `${item.Childrens[idx].Answer[k].Answer}--${item.Childrens[idxLoc].Answer[k].Answer}`
} else {
part = `${item.Childrens[idx].Answer[k].Answer}`
}
}
if (item.SplitOrMergeLesionName && k > -1) {
// obj.QuestionName = `${obj.QuestionName} --${part} (Split from ${item.SplitOrMergeLesionName})`
obj.QuestionName = `${obj.QuestionName} --${part}`
@ -509,6 +539,7 @@ export default {
this.$set(obj, 'Answers', Answers)
// obj.QuestionName = `${obj.QuestionName} `
} else if (!item.SplitOrMergeLesionName && k > -1) {
console.log(part)
obj.QuestionName = `${obj.QuestionName}--${part}`
const Answers = {}
if (state >= 0) {
@ -655,11 +686,12 @@ export default {
}
},
async handleConfirm() {
if (this.submitLockAction) return false
this.submitLockAction = 'submit'
this.loading = true
try {
// await this.handleSave(false)
let r = await this.handleSave(false)
if (!r) return
const r = await this.handleSave(false, true)
if (!r) return false
await verifyVisitTaskQuestions({ visitTaskId: this.visitTaskId })
var i = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
var isBaseline = this.visitTaskList[i].IsBaseLine
@ -680,9 +712,13 @@ export default {
this.signCode = ImageAssessmentReportConfirmation
this.signVisible = true
}
this.loading = false
return true
} catch (e) {
console.log(e)
return false
} finally {
this.loading = false
this.submitLockAction = ''
}
},
sign(type = 'sign') {
@ -800,15 +836,26 @@ export default {
}
// this.openWindow = window.open(routeData.href, '_blank')
},
handleSave(isPrompt) {
return new Promise(async (resolve, reject) => {
async handleSave(isPrompt, fromSubmit = false) {
if (this.submitLockAction && !fromSubmit) return false
if (!fromSubmit) {
this.submitLockAction = 'save'
}
const hasValue = (value) => value !== null && value !== undefined && `${value}`.trim() !== ''
const resetSaveState = () => {
this.loading = false
if (!fromSubmit) {
this.submitLockAction = ''
}
}
try {
var isBeill
var evaluateResult = ''
var evaluateAjustReason = ''
this.answers = []
var isExistEvaluateResult = false
this.answerArr.map(item => {
if (item.questionType === 13 || item.questionType === 42) {
if (item.questionType === 13 || item.questionType === 42 || (item.questionType === 39 && this.CriterionType === 18)) {
evaluateResult = item.answer
isExistEvaluateResult = true
}
@ -820,53 +867,36 @@ export default {
}
this.answers.push({ id: item.id, answer: item.answer })
})
console.log(this.currentExistDisease, this.isExistDisease, evaluateAjustReason, isBeill)
if (this.currentExistDisease !== this.isExistDisease && !evaluateAjustReason) {
this.$confirm(this.$t('trials:readingReport:message:msg2'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
reject()
return
if (this.currentExistDisease !== this.isExistDisease && !hasValue(evaluateAjustReason)) {
await this.showValidationDialog(this.$t('trials:readingReport:message:msg2'))
return false
}
if (isExistEvaluateResult && evaluateResult === null) {
if (isExistEvaluateResult && !hasValue(evaluateResult) && !this.isBaseline) {
//
this.$confirm(this.$t('trials:readingReport:message:msg2'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
reject()
return
await this.showValidationDialog(this.$t('trials:readingReport:message:msg2'))
return false
}
if (isExistEvaluateResult && (evaluateResult !== this.tumorEvaluate) && !evaluateAjustReason) {
if (isExistEvaluateResult && hasValue(evaluateResult) && (evaluateResult !== this.tumorEvaluate) && !hasValue(evaluateAjustReason)) {
//
this.$confirm(this.$t('trials:readingReport:message:msg3'), {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
reject()
return
await this.showValidationDialog(this.$t('trials:readingReport:message:msg3'))
return false
}
this.loading = true
try {
var params = {
visitTaskId: this.visitTaskId,
answers: this.answers
}
const res = await changeDicomReadingQuestionAnswer(params)
if (res.IsSuccess && isPrompt) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
this.loading = false
resolve(true)
} catch (e) {
this.loading = false
reject()
var params = {
visitTaskId: this.visitTaskId,
answers: this.answers
}
})
const res = await changeDicomReadingQuestionAnswer(params)
if (res.IsSuccess && isPrompt) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
return !!res.IsSuccess
} catch (e) {
console.log(e)
return false
} finally {
resetSaveState()
}
},
getWarningText() {
var sysRes = ''
@ -881,6 +911,9 @@ export default {
sysRes = this.$fd('OverallAssessment', this.tumorEvaluate)
curRes = this.$fd('OverallAssessment', this.currentEvaluateResult)
}
if (!curRes) {
return ''
}
const msg = this.$t('trials:readingReport:message:msg9').replace('xxx', '<font color="red">' + sysRes + '</font>').replace('yyy', '<font color="red">' + curRes + '</font>')
return msg
},

View File

@ -28,7 +28,7 @@
</div>
<div>
<span class="study-meta-line" :title="study.Modalities">
<span class="study-code" :title="study.StudyCode">{{ study.StudyCode }}</span>
<span v-if="isReadingTaskViewInOrder !== 0" class="study-code" :title="study.StudyCode">{{ study.StudyCode }}</span>
<span class="study-modality">{{ study.Modalities }}({{ study.SeriesCount }})</span>
<span class="patient-info" v-if="['PT、CT', 'CT、PT', 'PET-CT'].includes(study.Modalities)">
<el-popover placement="right-start" trigger="hover" popper-class="patient-info-popper">
@ -226,6 +226,10 @@ export default {
isReadingShowSubjectInfo: {
type: Boolean,
required: true
},
isReadingTaskViewInOrder: {
type: Number,
required: true
}
},
data() {
@ -340,7 +344,7 @@ export default {
}
})
var i = -1
var isReadingTaskViewInOrder = parseInt(this.$router.currentRoute.query.isReadingTaskViewInOrder)
var isReadingTaskViewInOrder = parseInt(this.isReadingTaskViewInOrder)
if (isReadingTaskViewInOrder === 2) {
//
i = res.length === 2 ? 1 : -1
@ -427,7 +431,7 @@ export default {
async getInitSeries() {
var seriesList = []
var isReadingTaskViewInOrder = parseInt(this.$router.currentRoute.query.isReadingTaskViewInOrder)
var isReadingTaskViewInOrder = parseInt(this.isReadingTaskViewInOrder)
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
this.studyList = this.visitTaskList[idx].StudyList
const studyList = this.visitTaskList[idx].StudyList.filter(i => i.IsDicom)

View File

@ -0,0 +1,115 @@
<template>
<div class="table-list-container" v-loading="loading">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
<span style="margin-left:5px;">{{ taskBlindName }}</span>
</h3>
<h3 v-if="tableQuestion && tableQuestion.GroupName">
{{ language === 'en' ? tableQuestion.GroupEnName : tableQuestion.GroupName }}
</h3>
<h4 v-if="tableQuestion && tableQuestion.QuestionName">
{{ tableQuestion.QuestionName }}
</h4>
<el-table
v-if="tableQuestion && tableQuestion.Type === 'basicTable' && tableQuestion.TableQuestions"
:ref="tableQuestion.Id"
:data="tableQuestion.TableQuestions.Answers"
height="100%"
>
<el-table-column
v-for="q of tableQuestion.TableQuestions.Questions"
:key="q.Id"
:prop="q.Id"
:label="q.QuestionName"
show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="q.Unit > 0 && !isNaN(parseFloat(scope.row[q.Id]))">
{{ `${scope.row[q.Id]} ${$fd('ValueUnit', parseInt(q.Unit))}` }}
</span>
<span v-else-if="q.DictionaryCode">
{{`${scope.row[q.Id] instanceof Array ? scope.row[q.Id].map(item => $fd(q.DictionaryCode,
parseInt(item))).join(',') : $fd(q.DictionaryCode, parseInt(scope.row[q.Id]))}`}}
</span>
<span v-else-if="q.OptionTypeEnum === 1">
{{ `${scope.row[q.Id] instanceof Array ? scope.row[q.Id].join(',') : scope.row[q.Id]}` }}
</span>
<span v-else>
{{ `${scope.row[q.Id]}` }}
</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { getFilterTableQuestion } from '@/api/reading'
import store from '@/store'
import { mapGetters } from 'vuex'
import { changeURLStatic } from '@/utils/history.js'
export default {
name: 'DicomTableList',
data() {
return {
isReadingShowSubjectInfo: false,
subjectCode: null,
taskBlindName: null,
questionId: null,
tableQuestion: null,
loading: false
}
},
computed: {
...mapGetters(['language'])
},
created() {
if (this.$route.query.TokenKey) {
store.dispatch('user/setToken', this.$route.query.TokenKey)
changeURLStatic('TokenKey', '')
}
this.subjectCode = localStorage.getItem('subjectCode')
this.taskBlindName = localStorage.getItem('taskBlindName')
this.isReadingShowSubjectInfo = localStorage.getItem('isReadingShowSubjectInfo')
this.getList()
},
methods: {
async getList() {
try {
this.loading = true
let params = {
trialId: this.$route.query.trialId,
visitTaskId: this.$route.query.visitTaskId,
questionId: this.$route.query.questionId
}
let res = await getFilterTableQuestion(params)
if (res.IsSuccess) {
this.tableQuestion = res.Result
}
} catch (e) {
console.log(e)
} finally {
this.loading = false
}
},
resetList() {
}
}
}
</script>
<style lang="scss" scoped>
.table-list-container {
height: 100%;
padding: 0 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
}
h3, h4 {
flex-shrink: 0;
margin: 10px 0 10px;
}
</style>

View File

@ -30,6 +30,8 @@
<!-- Save -->
<el-button
type="primary"
:loading="loading"
:disabled="loading"
@click="handleSave"
>
{{ $t('common:button:save') }}
@ -84,12 +86,12 @@ export default {
},
methods: {
async handleSave() {
const valid = await this.$refs.wlForm.validate()
if (!valid) return
if (this.loading) return
this.loading = true
try {
const valid = await this.$refs.wlForm.validate()
if (!valid) return
await addOrUpdateUserWLTemplate(this.form)
this.loading = false
this.$emit('getWL')
this.$emit('close')
if (this.form.Id) {
@ -98,7 +100,8 @@ export default {
this.$message.success(this.$t('common:message:addedSuccessfully'))
}
} catch (e) {
this.loading = false
} finally {
this.loading = false
}
},
handleCancel() {

View File

@ -338,7 +338,7 @@
</el-tooltip>
<div style="margin-left: auto">
<div style="padding: 5px">
<div style="display: flex;">
<el-tooltip class="item" effect="dark" :content="$t('trials:reading:button:handbooks')" placement="bottom"
v-if="(isExistsManual || isHaveKeyFile) && currentReadingTaskState < 2">
<div class="tool-wrapper">
@ -411,7 +411,7 @@
<WL v-if="activeName === '2'" @getWwcTpl="getWwcTpl" />
</el-tab-pane>
<el-tab-pane :label="$t('trials:reading:tab:others')" name="3">
<Others v-if="activeName === '3'" :imageToolType="1"/>
<Others v-if="activeName === '3'" :imageToolType="1" />
</el-tab-pane>
</el-tabs>
</el-dialog>
@ -448,10 +448,10 @@
/> -->
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :SubjectId="uploadSubjectId" :SubjectCode="uploadSubjectCode"
:Criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible" :VisitTaskId="taskId"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :isReading="true" />
<download-dicom-and-nonedicom v-if="downloadImageVisible" :SubjectId="uploadSubjectId"
:SubjectCode="uploadSubjectCode" :Criterion="uploadTrialCriterion" :TaskId="taskId"
:visible.sync="downloadImageVisible" />
:visible.sync="downloadImageVisible" :isReading="true"/>
</div>
</template>
<script>

Some files were not shown because too many files have changed in this diff Show More