47 Commits

Author SHA1 Message Date
wangxiaoshuang 7fcbd7f983 生产环境配置修改
continuous-integration/drone/push Build encountered an error
2025-03-24 13:59:21 +08:00
wangxiaoshuang 779badb041 项目文档系统数据中英文转换
continuous-integration/drone/push Build is passing
2025-03-24 09:31:49 +08:00
caiyiling ef5e5527fd 标注更改及检查名称更改
continuous-integration/drone/push Build is passing
2025-03-21 16:29:47 +08:00
caiyiling 52ac12d6b6 阅片页面显示检查名称
continuous-integration/drone/push Build is passing
2025-03-21 15:36:23 +08:00
caiyiling 272c28a988 自定义阅片更改及非dicom阅片添加个性化配置
continuous-integration/drone/push Build is passing
2025-03-21 15:03:21 +08:00
caiyiling 6b10d07e2a 自定义阅片预览paf更改
continuous-integration/drone/push Build is passing
2025-03-21 14:03:53 +08:00
caiyiling 032e710890 阅片工具及报告页查看pdf更改
continuous-integration/drone/push Build is passing
2025-03-21 13:09:36 +08:00
caiyiling 032150904e 非dicom阅片更改
continuous-integration/drone/push Build is passing
2025-03-20 19:23:20 +08:00
wangxiaoshuang d91161a139 网速监控改为三位小数
continuous-integration/drone/push Build is passing
2025-03-19 15:53:41 +08:00
wangxiaoshuang bba2237337 文件大小改为保留3位小数
continuous-integration/drone/push Build is passing
2025-03-19 15:36:53 +08:00
wangxiaoshuang 707ca5602c Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2025-03-18 17:59:20 +08:00
wangxiaoshuang 3b03d6ba76 项目文档搜索栏样式问题 2025-03-18 17:59:10 +08:00
caiyiling 9277d48c85 1
continuous-integration/drone/push Build is passing
2025-03-18 17:55:22 +08:00
caiyiling d753fbc2ab 非dicom阅片测量工具更改
continuous-integration/drone/push Build is passing
2025-03-18 16:53:03 +08:00
caiyiling 5678e3302b 国际化补充
continuous-integration/drone/push Build is passing
2025-03-18 15:34:12 +08:00
caiyiling c25cb097ab 1
continuous-integration/drone/push Build is passing
2025-03-18 15:28:13 +08:00
caiyiling 070db5158d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2025-03-18 15:27:16 +08:00
caiyiling 121bf497bb 非dicom阅片更改 2025-03-18 15:26:51 +08:00
wangxiaoshuang e2ffd76504 阅片人筛选机构城市国家中英文显示
continuous-integration/drone/push Build is passing
2025-03-18 15:18:26 +08:00
wangxiaoshuang cfbe3b4839 项目文档培训记录培训人数校验修改
continuous-integration/drone/push Build is running
2025-03-18 15:16:31 +08:00
wangxiaoshuang 81bef8abe0 项目文档培训记录培训人数添加校验
continuous-integration/drone/push Build is passing
2025-03-18 14:15:31 +08:00
wangxiaoshuang 84b1263340 浏览器页签标题修改
continuous-integration/drone/push Build is passing
2025-03-18 13:57:07 +08:00
wangxiaoshuang 4016e71c37 项目文档阅片人问题修复
continuous-integration/drone/push Build is passing
2025-03-18 13:51:25 +08:00
wangxiaoshuang 3b0afd3913 1
continuous-integration/drone/push Build is passing
2025-03-18 13:45:13 +08:00
wangxiaoshuang 3672892451 项目文档阅片人问题修复
continuous-integration/drone/push Build is passing
2025-03-18 09:54:33 +08:00
wangxiaoshuang c46ca18a42 项目文档菜单与列表是否适用字段联动
continuous-integration/drone/push Build is passing
2025-03-17 16:38:14 +08:00
wangxiaoshuang 0e0b5c2c4c 项目文档同意入项记录上传方式修改
continuous-integration/drone/push Build is passing
2025-03-17 14:34:24 +08:00
wangxiaoshuang 5339e2df63 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2025-03-17 13:23:25 +08:00
wangxiaoshuang 66d5591d50 项目文档同意入项记录上传更改为弹框 2025-03-17 13:23:21 +08:00
caiyiling bfe2f1dbfb Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2025-03-17 11:37:30 +08:00
caiyiling 24806f57ed 自定义阅片bug修复 2025-03-17 11:37:09 +08:00
wangxiaoshuang b0ff8e7163 项目文档培训记录效果改为可输入所有字符
continuous-integration/drone/push Build is passing
2025-03-17 11:34:03 +08:00
caiyiling c9ef6cd19f Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2025-03-17 11:24:01 +08:00
caiyiling 00cc5d5d27 非dicom阅片ecrf更改 2025-03-17 11:23:24 +08:00
wangxiaoshuang 25ddd422a3 1
continuous-integration/drone/push Build is passing
2025-03-17 09:29:56 +08:00
wangxiaoshuang a060c1745d 项目文档资质材料
continuous-integration/drone/push Build is passing
2025-03-14 17:09:38 +08:00
wangxiaoshuang 1a4bfe64f9 项目文档上传文件路径更改
continuous-integration/drone/push Build is passing
2025-03-14 13:15:44 +08:00
caiyiling 2fd6e1893b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2025-03-14 10:35:46 +08:00
caiyiling dc0229810d 国际化补充 2025-03-14 10:35:20 +08:00
wangxiaoshuang 08c911d4a0 项目文档申报方同意入项记录审核人角色字段更改
continuous-integration/drone/push Build is passing
2025-03-14 10:25:35 +08:00
wangxiaoshuang 08cdcffd82 项目文档申报方同意入项记录
continuous-integration/drone/push Build is passing
2025-03-13 17:10:42 +08:00
wangxiaoshuang a5f16c866d 检查名称配置更改
continuous-integration/drone/push Build is passing
2025-03-13 16:37:08 +08:00
caiyiling 3167c2bea7 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2025-03-13 15:01:16 +08:00
caiyiling 26cca57f75 系统标准国际化补充 2025-03-13 15:00:50 +08:00
wangxiaoshuang ff88cbd6e2 1
continuous-integration/drone/push Build is passing
2025-03-13 13:23:56 +08:00
wangxiaoshuang c4a498d953 检查被IQC标记删除:在crc查看时,该行记录增加删除线
continuous-integration/drone/push Build is passing
2025-03-13 11:14:34 +08:00
wangxiaoshuang f58e2ff82f 影像重传,确认完成时,签名中XXX未替换 2025-03-13 11:14:00 +08:00
61 changed files with 3785 additions and 1585 deletions
+3
View File
@@ -2,6 +2,9 @@
ENV = 'prop'
NODE_ENV = 'prop'
# base public path
VUE_APP_BASE_PATH = '/'
# 是否开启登陆限制 true:是 false:否
VUE_APP_LOGIN_FOR_PERMISSION = true
+197
View File
File diff suppressed because one or more lines are too long
+72
View File
@@ -1249,3 +1249,75 @@ export function getTrialDoctorList(data) {
data
})
}
// 项目文档-上传同意入项记录
export function uploadTrialFileTypeFile(data) {
return request({
url: `/TrialFileType/uploadTrialFileTypeFile`,
method: 'post',
data
})
}
// 项目文档-获取上传同意入项记录
export function getTrialFileTypeFile(data) {
return request({
url: `/TrialFileType/getTrialFileTypeFile`,
method: 'post',
data
})
}
// 项目文档-删除上传同意入项记录
export function deleteTrialTypeFile(params) {
return request({
url: `/TrialFileType/deleteTrialTypeFile`,
method: 'post',
params
})
}
// 项目文档-获取资质材料列表
export function getTrialAttachments(data) {
return request({
url: `/Attachment/getTrialAttachments`,
method: 'post',
data
})
}
// 项目文档-修改资质材料稽查状态
export function setAuthorizedViewC(data) {
return request({
url: `/Attachment/setAuthorizedView`,
method: 'post',
data
})
}
// 项目文档-上传资质材料
export function saveTrialAttachments(data) {
return request({
url: `/Attachment/saveTrialAttachments`,
method: 'post',
data
})
}
// 项目文档-资质材料(获取医生列表)
export function getTrialDoctorListC(data) {
return request({
url: `/Attachment/getTrialDoctorList`,
method: 'post',
data
})
}
// 项目文档-修改资质材料
export function updateTrialAttachments(data) {
return request({
url: `/Attachment/updateTrialAttachments`,
method: 'post',
data
})
}
// 项目文档-删除资质材料
export function deleteAttachment(data) {
return request({
url: `/Attachment/deleteAttachment`,
method: 'post',
data
})
}
+8
View File
@@ -110,4 +110,12 @@ export function addOrUpdateTrialBodyPart(data) {
data
})
}
// 修改检查名称列表
export function updateTrialStudyNameList(data) {
return request({
url: `/TrialConfig/updateTrialStudyNameList`,
method: 'post',
data
})
}
@@ -316,9 +316,9 @@
scope.row.dicomInfo.fileCount
}}
({{
(scope.row.dicomInfo.uploadFileSize / 1024 / 1024).toFixed(2)
(scope.row.dicomInfo.uploadFileSize / 1024 / 1024).toFixed(3)
}}MB/{{
(scope.row.dicomInfo.fileSize / 1024 / 1024).toFixed(2)
(scope.row.dicomInfo.fileSize / 1024 / 1024).toFixed(3)
}}MB)
</span>
</template>
@@ -66,7 +66,7 @@
<template slot-scope="scope">
<span>{{
scope.row.FileSize && scope.row.FileSize > 0
? `${(scope.row.FileSize / 1024 / 1024).toFixed(2)}MB`
? `${(scope.row.FileSize / 1024 / 1024).toFixed(3)}MB`
: ''
}}</span>
</template>
@@ -138,7 +138,7 @@
<template slot-scope="scope">
<span>{{
scope.row.FileSize && scope.row.FileSize > 0
? `${(scope.row.FileSize / 1024 / 1024).toFixed(2)}MB`
? `${(scope.row.FileSize / 1024 / 1024).toFixed(3)}MB`
: ''
}}</span>
</template>
@@ -320,7 +320,7 @@
<template slot-scope="scope">
<span>{{
scope.row.size && scope.row.size > 0
? `${(scope.row.size / 1024 / 1024).toFixed(2)}MB`
? `${(scope.row.size / 1024 / 1024).toFixed(3)}MB`
: ''
}}</span>
</template>
+1 -1
View File
@@ -1,7 +1,7 @@
// eslint-disable-next-line no-undef
module.exports = {
title: 'IRCIS',
title: 'IRC Imaging System',
/**
* @type {boolean} true | false
+1
View File
@@ -862,6 +862,7 @@ const actions = {
const data = {}
data.StudyId = study.StudyId
data.StudyCode = study.StudyCode
data.StudyName = study.StudyName
data.Modalities = study.Modalities
data.SeriesCount = study.SeriesCount
data.InstanceCount = study.InstanceCount
+1 -1
View File
@@ -1,6 +1,6 @@
import defaultSettings from '@/settings'
const title = defaultSettings.title || 'IRCIS'
const title = defaultSettings.title || 'IRC Imaging System'
export default function getPageTitle(pageTitle) {
if (pageTitle) {
+1 -1
View File
@@ -445,7 +445,7 @@ function setTimer() {
totalBytes = totalBytes / 1024;
unit = "MB/s";
}
store.state.trials.uploadTip = totalBytes.toFixed(2) + unit;
store.state.trials.uploadTip = totalBytes.toFixed(3) + unit;
}
if (timeList.length >= 5) {
delete bytesReceivedPerSecond[timeList[0]]
+1 -1
View File
@@ -150,7 +150,7 @@ function setTimer() {
totalBytes = totalBytes / 1024;
unit = "MB/s";
}
store.state.trials.uploadTip = totalBytes.toFixed(2) + unit;
store.state.trials.uploadTip = totalBytes.toFixed(3) + unit;
}
if (timeList.length >= 5) {
delete bytesReceivedPerSecond[timeList[0]]
+1
View File
@@ -9,6 +9,7 @@ const ROUTER = require('@/router');
axios.defaults.withCredentials = false
const service = axios.create({
baseURL: '/api',
// baseURL: process.env.NODE_ENV === 'prod' ? "https://api.irc.extimaging.com" : '/api',
timeout: 2 * 360000, // request timeout
withCredentials: false
})
@@ -107,7 +107,7 @@
@click="handleSave"
v-loading="loading"
>
保存
{{ $t('common:button:save')}}
</el-button>
</div>
<el-table
@@ -226,7 +226,8 @@ export default {
})
},
handleAdd() {
this.drawer_cfg2 = { drawerChild: true, title: '选择标准字典' }
// 选择标准字典
this.drawer_cfg2 = { drawerChild: true, title: this.$t('template:criterionDictionary:title:selectDictionary') }
this.config.visible = true
this.$nextTick(() => {
var a = this.dicList.filter(v => {
@@ -261,9 +262,10 @@ export default {
},
handleDelete(row) {
this.loading = true
this.$confirm('确定要删除该系统标准字典吗?').then(() => {
// 是否确认删除
this.$confirm(this.$t('template:criterionDictionary:message:msg1')).then(() => {
deleteSystemCriterionDictionary({Id: row.Id}).then(res => {
this.$message.success('删除成功')
this.$message.success(this.$t('common:message:deletedSuccessfully'))
this.loading = false
this.getList()
}).catch(() => { this.loading = false })
@@ -10,7 +10,7 @@
@click="handleAdd"
style="margin-right: 10px;"
>
配置
{{ $t('dictionary:template:criterionDictionary:button:config') }}
</el-button>
</div>
</div>
@@ -24,39 +24,44 @@
>
<el-table-column
prop=""
label="序号"
label=""
width="50"
>
<template slot-scope="scope">
{{scope.$index + 1}}
</template>
</el-table-column>
<!-- 键值 -->
<el-table-column
prop="Code"
label="键值"
:label="$t('dictionary:template:criterionDictionary:table:code')"
show-overflow-tooltip
/>
<!-- 中文值 -->
<el-table-column
prop="ValueCN"
label="中文值"
:label="$t('dictionary:template:criterionDictionary:table:valueCN')"
show-overflow-tooltip
>
</el-table-column>
<!-- 英文值 -->
<el-table-column
prop="Value"
label="英文值"
:label="$t('dictionary:template:criterionDictionary:table:value')"
show-overflow-tooltip
>
</el-table-column>
<!-- 描述 -->
<el-table-column
prop="Description"
label="描述"
:label="$t('dictionary:template:criterionDictionary:table:description')"
show-overflow-tooltip
>
</el-table-column>
<!-- 分组 -->
<el-table-column
prop="Description"
label="分组"
:label="$t('dictionary:template:criterionDictionary:table:group')"
show-overflow-tooltip
>
<template slot-scope="scope">
@@ -84,7 +89,7 @@
@click="handleSave"
v-loading="loading"
>
保存
{{ $t('common:button:save')}}
</el-button>
</div>
<el-table
@@ -100,27 +105,31 @@
type="selection"
width="55">
</el-table-column>
<!-- 键值 -->
<el-table-column
prop="raw.Code"
label="键值"
:label="$t('dictionary:template:criterionDictionary:table:code')"
show-overflow-tooltip
>
</el-table-column>
<!-- 中文值 -->
<el-table-column
prop="raw.ValueCN"
label="中文值"
:label="$t('dictionary:template:criterionDictionary:table:valueCN')"
show-overflow-tooltip
>
</el-table-column>
<!-- 英文值 -->
<el-table-column
prop="raw.Value"
label="英文值"
:label="$t('dictionary:template:criterionDictionary:table:value')"
show-overflow-tooltip
>
</el-table-column>
<!-- 描述 -->
<el-table-column
prop="raw.Description"
label="描述"
:label="$t('dictionary:template:criterionDictionary:table:description')"
show-overflow-tooltip
>
</el-table-column>
@@ -152,7 +161,7 @@ export default {
activeName: '0',
addOrEdit: { visible: false, title: '' },
preview: { visible: false, title: 'eCRF预览' },
config: { visible: false, title: '标准字典值' },
config: { visible: false, title: this.$t('template:criterionDictionary:message:msg2') }, //标准字典值
selectedList: []
}
},
@@ -167,7 +176,7 @@ export default {
CrterionDictionaryGroup: v
}).then(res => {
this.loading = false
this.$message.success('设置成功')
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.getList()
}).catch(() => {this.loading = false})
},
+1 -1
View File
@@ -241,7 +241,7 @@ const searchDataDefault = () => {
PageIndex: 1,
PageSize: 20,
asc: false,
sortField: 'CreateTime',
sortField: '',
}
}
export default {
@@ -425,6 +425,8 @@ export default {
this.loading = true
if (this.viewStatus) {
this.searchData.IsConfirmed = true
}
if (this.isDoc) {
let IR = this.userTypeOptions.find((item) => item.UserTypeEnum == 13)
if (IR) {
this.searchData.UserTypeId = IR.Id
@@ -291,21 +291,33 @@
:label="$t('trials:seletctedReviews:table:institution')"
show-overflow-tooltip
width="150"
/>
>
<template slot-scope="scope">
<span>{{ isEN?scope.row.HospitalName:scope.row.HospitalNameCN }}</span>
</template>
</el-table-column>
<!-- City -->
<el-table-column
prop="City"
:label="$t('trials:seletctedReviews:table:city')"
show-overflow-tooltip
width="150"
/>
>
<template slot-scope="scope">
<span>{{ isEN?scope.row.City:scope.row.CityCN }}</span>
</template>
</el-table-column>
<!-- Country -->
<el-table-column
prop="Country"
:label="$t('trials:seletctedReviews:table:country')"
show-overflow-tooltip
width="150"
/>
>
<template slot-scope="scope">
<span>{{ isEN?scope.row.Country:scope.row.CountryCN }}</span>
</template>
</el-table-column>
<!-- Selector -->
<el-table-column
prop="OptUserName"
@@ -18,7 +18,6 @@
</div>
</div>
<el-form-item
v-for="qs in questions"
v-show="qs.ShowQuestion!==2"
@@ -51,35 +50,35 @@
type="text"
@click="addAnnotation(qs)"
>
{{$t('trials:MRIPDFF:button:measure')}}
{{ $t('trials:MRIPDFF:button:measure') }}
</el-button>
<!-- 清除标记 -->
<el-button
v-if="getAnnotationStatus(qs) && readingTaskState!== 2"
size="mini"
type="text"
@click="removeAnnotation(qs)"
style="margin-left: 0px"
@click="removeAnnotation(qs)"
>
{{$t('trials:MRIPDFF:button:clear')}}
{{ $t('trials:MRIPDFF:button:clear') }}
</el-button>
<!-- 返回 -->
<el-button
v-if="questionForm[qs.Id]"
size="mini"
type="text"
@click="locateAnnotation(qs)"
style="margin-left: 0px"
@click="locateAnnotation(qs)"
>
{{$t('trials:MRIPDFF:button:return')}}
{{ $t('trials:MRIPDFF:button:return') }}
</el-button>
<!-- 保存 -->
<el-button
v-if="questionForm[isMeasurableId] && parseInt(questionForm[isMeasurableId]) === 1 && questionForm[qs.Id] && readingTaskState!== 2"
size="mini"
type="text"
@click="saveAnnotation(qs)"
style="margin-left: 0px"
@click="saveAnnotation(qs)"
>
<!-- 未保存 -->
<el-tooltip v-if="getAnnotationSaveEnum(qs) === 0" class="item" effect="dark" :content="$t('trials:reading:button:unsaved')" placement="bottom">
@@ -279,7 +278,7 @@ export default {
},
mounted() {
this.trialId = this.$route.query.trialId
let digitPlaces = Number(localStorage.getItem('digitPlaces'))
const digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
this.initForm()
DicomEvent.$on('handleImageQualityAbnormal', () => {
@@ -295,26 +294,29 @@ export default {
this.isMeasurableId = this.getQuestionId(1105)
// const loading = this.$loading({ fullscreen: true })
this.questions.forEach(item => {
var val = this.answers[item.Id]
if (item.DictionaryCode) {
val = isNaN(parseInt(this.answers[item.Id])) ? this.answers[item.Id] : parseInt(this.answers[item.Id])
if (this.answers.hasOwnProperty(item.Id)) {
let val = this.answers[item.Id]
if (item.DictionaryCode) {
val = isNaN(parseInt(this.answers[item.Id])) ? this.answers[item.Id] : parseInt(this.answers[item.Id])
}
this.$set(this.questionForm, item.Id, val)
} else {
this.$set(this.questionForm, item.Id, '')
}
this.$set(this.questionForm, item.Id, val)
})
this.$set(this.questionForm, 'MeasureData', this.answers.MeasureData ? JSON.parse(this.answers.MeasureData) : '')
this.$set(this.questionForm, 'RowIndex', this.answers.RowIndex ? this.answers.RowIndex : '')
this.$set(this.questionForm, 'RowId', this.answers.RowId ? this.answers.RowId : '')
// 如果存在标记且是否可测量为否,则将是否可测量更改为是
if (this.isCurrentTask && this.readingTaskState < 2) {
let arr = JSON.parse(this.answers.TableQuestionMarkList)
let isExitsMarks = arr.findIndex(i=>i.MeasureData) > -1
const arr = JSON.parse(this.answers.TableQuestionMarkList)
const isExitsMarks = arr.findIndex(i => i.MeasureData) > -1
if (isExitsMarks && parseInt(this.questionForm[this.isMeasurableId]) === 0) {
this.$set(this.questionForm, this.isMeasurableId, 1)
}
}
// saveTypeEnum 0:未保存过(新建病灶);1:已保存,信息不完整(随访初始化病灶/分裂病灶,通过状态判断);2:已保存,信息完整
let isMeasurable = this.getQuestionVal(1105)
const isMeasurable = this.getQuestionVal(1105)
const mean = this.getQuestionVal(1104)
if (this.questionForm.saveTypeEnum !== 1 && this.isCurrentTask && this.readingTaskState < 2) {
this.$set(this.questionForm, 'saveTypeEnum', parseInt(isMeasurable) === 1 && isNaN(parseFloat(mean)) ? 1 : 2)
@@ -325,11 +327,11 @@ export default {
this.markList = []
this.isExitsMarks = false
this.isDisabledMeasurableRadio = false
let seg = this.getQuestionVal(1106)
const seg = this.getQuestionVal(1106)
this.liverSeg = this.$fd('LiverSegmentation', seg)
if (this.answers.TableQuestionMarkList) {
let arr = JSON.parse(this.answers.TableQuestionMarkList)
arr.map(i=>{
const arr = JSON.parse(this.answers.TableQuestionMarkList)
arr.map(i => {
if (i.MeasureData) {
this.isExitsMarks = true
if (!isNaN(parseInt(isMeasurable)) && parseInt(isMeasurable) === 1 && this.isCurrentTask && this.readingTaskState < 2) {
@@ -337,14 +339,14 @@ export default {
}
i.MeasureData = JSON.parse(i.MeasureData)
}
this.markList.push({tableQuestionId: i.TableQuestionId, measureData: i, saveEnum: 1})
this.markList.push({ tableQuestionId: i.TableQuestionId, measureData: i, saveEnum: 1 })
})
}
let newMean = this.getMean()
const newMean = this.getMean()
if (newMean !== mean) {
let meanId = this.getQuestionId(1104)
this.$set(this.questionForm, meanId, newMean ? newMean : '')
const meanId = this.getQuestionId(1104)
this.$set(this.questionForm, meanId, newMean || '')
}
if (this.questionForm.saveTypeEnum === 1 && this.isCurrentTask && this.readingTaskState < 2) {
this.setQuestions()
@@ -357,13 +359,13 @@ export default {
// I II III IV V VI VII VIII
// L-I-01 L-I-02 L-I-03
// L-II-01 L-II-02 L-II-03
let segArr = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII']
const segArr = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII']
let lessionName = ''
let segmentId = this.getQuestionId(1106)
const segmentId = this.getQuestionId(1106)
let segmentVal = this.answers[segmentId]
segmentVal = segmentVal ? parseInt(segmentVal) : null
if (segmentVal) {
let i = questionMark === 1101 ? '01' : questionMark === 1102 ? '02' : questionMark === 1103 ? '03' : ''
const i = questionMark === 1101 ? '01' : questionMark === 1102 ? '02' : questionMark === 1103 ? '03' : ''
lessionName = `${orderMark}-${segArr[segmentVal - 1]}-${i}`
}
return lessionName
@@ -397,18 +399,18 @@ export default {
this.$set(this.questionForm, 'saveTypeEnum', 1)
const mean = this.getQuestionVal(1104)
if (qs.QuestionMark === 1101 || qs.QuestionMark === 1102 || qs.QuestionMark === 1103) {
let newMean = this.getMean()
const newMean = this.getMean()
if (newMean !== mean) {
let meanId = this.getQuestionId(1104)
this.$set(this.questionForm, meanId, newMean ? newMean : '')
const meanId = this.getQuestionId(1104)
this.$set(this.questionForm, meanId, newMean || '')
}
} else if (qs.QuestionMark === 1105) {
if (!v) {
let meanId = this.getQuestionId(1104)
const meanId = this.getQuestionId(1104)
this.$set(this.questionForm, meanId, 'NE')
} else {
let mean = this.getMean()
let meanId = this.getQuestionId(1104)
const mean = this.getMean()
const meanId = this.getQuestionId(1104)
this.$set(this.questionForm, meanId, mean)
}
}
@@ -418,10 +420,10 @@ export default {
let mean = null
let isMeasurable = this.getQuestionVal(1105)
isMeasurable = !isNaN(parseInt(isMeasurable)) ? parseInt(isMeasurable) : null
let l1 = this.getQuestionVal(1101)
let l2 = this.getQuestionVal(1102)
let l3 = this.getQuestionVal(1103)
if ( isMeasurable && !isNaN(parseFloat(l1)) && !isNaN(parseFloat(l2)) && !isNaN(parseFloat(l3))) {
const l1 = this.getQuestionVal(1101)
const l2 = this.getQuestionVal(1102)
const l3 = this.getQuestionVal(1103)
if (isMeasurable && !isNaN(parseFloat(l1)) && !isNaN(parseFloat(l2)) && !isNaN(parseFloat(l3))) {
const sum = l1 + l2 + l3
mean = sum / 3
return parseFloat(mean.toFixed(this.digitPlaces))
@@ -442,7 +444,7 @@ export default {
// 维护标记信息
measureData.data.remark = this.getLesionName(this.orderMark, this.activeQuestionMark)
}
let val = measureData.data.cachedStats.mean / 10
const val = measureData.data.cachedStats.mean / 10
this.$set(this.questionForm, measureData.tableQuestionId, val.toFixed(this.digitPlaces))
data = {
Id: '',
@@ -461,17 +463,17 @@ export default {
}
store.dispatch('reading/addMeasuredData', { visitTaskId: this.visitTaskId, data: data })
let mean = this.getQuestionVal(1104)
let newMean = this.getMean()
const newMean = this.getMean()
if (newMean !== mean) {
mean = newMean
let meanId = this.getQuestionId(1104)
this.$set(this.questionForm, meanId, newMean ? newMean : '')
const meanId = this.getQuestionId(1104)
this.$set(this.questionForm, meanId, newMean || '')
}
const isMeasurable = this.getQuestionVal(1105)
DicomEvent.$emit('refreshStudyListMeasureData')
let i = this.markList.findIndex(i=>i.tableQuestionId === measureData.tableQuestionId)
const i = this.markList.findIndex(i => i.tableQuestionId === measureData.tableQuestionId)
if (i === -1) {
this.markList.push({tableQuestionId: measureData.tableQuestionId, measureData: data, saveEnum: 0})
this.markList.push({ tableQuestionId: measureData.tableQuestionId, measureData: data, saveEnum: 0 })
} else {
this.markList[i].saveEnum = 0
this.markList[i].measureData = data
@@ -486,19 +488,19 @@ export default {
},
addAnnotation(qs) {
// 判断是否有测量数据未保存
let i = this.markList.findIndex(i=>i.saveEnum === 0)
const i = this.markList.findIndex(i => i.saveEnum === 0)
if (i > -1 && this.markList[i].measureData && this.markList[i].measureData.MeasureData) {
this.$alert(this.$t('trials:MRIPDFF:message:message3'))
// this.$message.warning(this.$t('trials:MRIPDFF:message:message3'))
return
}
let orderMarkName = this.getLesionName(this.orderMark, qs.QuestionMark)
const orderMarkName = this.getLesionName(this.orderMark, qs.QuestionMark)
this.activeQuestionId = qs.Id
this.activeQuestionMark= qs.QuestionMark
DicomEvent.$emit('addAnnotation', {question: qs, locateInfo: { questionId: this.parentQsId, rowIndex: this.questionForm.RowIndex, visitTaskId: this.visitTaskId, lesionName: orderMarkName, lesionType: null, markTool: 'Probe', readingTaskState: this.readingTaskState, isMarked: true }})
this.activeQuestionMark = qs.QuestionMark
DicomEvent.$emit('addAnnotation', { question: qs, locateInfo: { questionId: this.parentQsId, rowIndex: this.questionForm.RowIndex, visitTaskId: this.visitTaskId, lesionName: orderMarkName, lesionType: null, markTool: 'Probe', readingTaskState: this.readingTaskState, isMarked: true }})
},
getAnnotationSaveEnum(qs) {
let i = this.markList.findIndex(i=>i.tableQuestionId === qs.Id)
const i = this.markList.findIndex(i => i.tableQuestionId === qs.Id)
if (i > -1) {
return this.markList[i].saveEnum
} else {
@@ -506,7 +508,7 @@ export default {
}
},
getAnnotationStatus(qs) {
let i = this.markList.findIndex(i=>i.tableQuestionId === qs.Id)
const i = this.markList.findIndex(i => i.tableQuestionId === qs.Id)
if (i > -1 && this.markList[i].measureData && this.markList[i].measureData.MeasureData) {
return true
} else {
@@ -516,14 +518,13 @@ export default {
getIsExitsMarks() {
const isMeasurable = this.getQuestionVal(1105)
if (!isNaN(parseInt(isMeasurable)) && parseInt(isMeasurable) === 1) {
return this.markList.findIndex(i=>i.measureData && i.measureData.MeasureData) > -1 ? true : false
return this.markList.findIndex(i => i.measureData && i.measureData.MeasureData) > -1
} else {
return false
}
},
async removeAnnotation(qs) {
let i = this.markList.findIndex(i=>i.tableQuestionId === qs.Id)
const i = this.markList.findIndex(i => i.tableQuestionId === qs.Id)
DicomEvent.$emit('imageLocation', { questionId: this.parentQsId, rowIndex: this.questionForm.RowIndex, visitTaskId: this.visitTaskId, lesionName: this.markList[i].measureData.OrderMarkName, lesionType: null, markTool: 'Probe', readingTaskState: this.readingTaskState, isMarked: true })
// 是否确认清除标记?
const confirm = await this.$confirm(
@@ -535,26 +536,26 @@ export default {
)
if (confirm !== 'confirm') return
let measureData = Object.assign({}, this.markList[i].measureData)
const measureData = Object.assign({}, this.markList[i].measureData)
if (measureData.Id) {
await deleteSingleTableQuestionMark({Id: measureData.Id}, 11)
await deleteSingleTableQuestionMark({ Id: measureData.Id }, 11)
}
// 移除缓存中的measureData
await store.dispatch('reading/removeMeasuredData', { visitTaskId: this.visitTaskId, measureData: measureData, questionId: this.parentQsId, rowIndex: this.questionForm.RowIndex, orderMarkName: measureData.OrderMarkName})
await store.dispatch('reading/removeMeasuredData', { visitTaskId: this.visitTaskId, measureData: measureData, questionId: this.parentQsId, rowIndex: this.questionForm.RowIndex, orderMarkName: measureData.OrderMarkName })
DicomEvent.$emit('getMeasureData')
this.markList[i].measureData = null
this.markList[i].saveEnum = 0
// 清除测量值、清除平均值
this.$set(this.questionForm, this.markList[i].tableQuestionId, '')
let meanId = this.getQuestionId(1104)
const meanId = this.getQuestionId(1104)
this.$set(this.questionForm, meanId, '')
this.isDisabledMeasurableRadio = this.getIsExitsMarks()
this.$set(this.questionForm, 'saveTypeEnum', 1)
this.setQuestions()
},
locateAnnotation(qs) {
let i = this.markList.findIndex(i=>i.tableQuestionId === qs.Id)
let measureData = this.markList[i].measureData
const i = this.markList.findIndex(i => i.tableQuestionId === qs.Id)
const measureData = this.markList[i].measureData
// 定位
var markTool = 'Probe'
var readingTaskState = this.readingTaskState
@@ -569,18 +570,18 @@ export default {
async saveAnnotation(qs) {
const loading = this.$loading({ fullscreen: true })
try {
let i = this.markList.findIndex(i=>i.tableQuestionId === qs.Id)
const i = this.markList.findIndex(i => i.tableQuestionId === qs.Id)
let params = {}
if (i > -1 && this.markList[i].measureData && this.markList[i].measureData.MeasureData) {
let measureData = this.markList[i].measureData.MeasureData
const measureData = this.markList[i].measureData.MeasureData
// 上传截图
DicomEvent.$emit('getScreenshots', { questionId: this.parentQsId, rowIndex: this.questionForm.RowIndex, visitTaskId: this.visitTaskId, lesionName: measureData.OrderMarkName, lesionType: null, isMarked: !!measureData }, async val => {
params = Object.assign({}, this.markList[i].measureData)
if (val) {
let pictureObj = await this.uploadScreenshots(`${new Date().getTime()}`, val)
const pictureObj = await this.uploadScreenshots(`${new Date().getTime()}`, val)
params.PicturePath = pictureObj.isSuccess ? this.$getObjectName(pictureObj.result.url) : ''
}
let tableQuestionId = this.markList[i].tableQuestionId
const tableQuestionId = this.markList[i].tableQuestionId
params.Answer = this.questionForm[tableQuestionId]
params.MeasureData = JSON.stringify(this.markList[i].measureData.MeasureData)
loading.close()
@@ -588,13 +589,13 @@ export default {
})
} else {
params = {
Answer: "",
Answer: '',
VisitTaskId: this.visitTaskId,
QuestionId: this.parentQsId,
InstanceId: '',
SeriesId: '',
StudyId: '',
MarkTool:'',
MarkTool: '',
PicturePath: '',
NumberOfFrames: 0,
MeasureData: '',
@@ -607,9 +608,7 @@ export default {
loading.close()
this.saveTableQuestionInfo(params, qs)
}
} catch(e) {
} catch (e) {
console.log(e)
loading.close()
}
@@ -617,16 +616,16 @@ export default {
async saveTableQuestionInfo(params, qs) {
const loading = this.$loading({ fullscreen: true })
try {
let res = await saveTableQuestionMark(params, 11)
const res = await saveTableQuestionMark(params, 11)
if (res.IsSuccess) {
// 保存后设置保存状态
let i = this.markList.findIndex(i=>i.tableQuestionId === qs.Id)
const i = this.markList.findIndex(i => i.tableQuestionId === qs.Id)
this.markList[i].saveEnum = 1
// 保存病灶
let j = this.markList.findIndex(i=>!(i.saveEnum === 1 && i.measureData && i.measureData.MeasureData))
const j = this.markList.findIndex(i => !(i.saveEnum === 1 && i.measureData && i.measureData.MeasureData))
if (j === -1) {
let answers = []
let 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}$/)
const answers = []
const 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.questionForm) {
if (reg.test(k)) {
if (answers.findIndex(i => i.tableQuestionId === k) === -1) {
@@ -634,7 +633,7 @@ export default {
}
}
}
let params = {
const params = {
questionId: this.parentQsId,
rowId: this.questionForm.RowId,
rowIndex: this.answers.RowIndex,
@@ -659,7 +658,7 @@ export default {
DicomEvent.$emit('setMeasuredToolsPassive')
loading.close()
}
} catch(e) {
} catch (e) {
console.log(e)
loading.close()
}
@@ -735,43 +734,43 @@ export default {
async handleSave() {
try {
const valid = await this.$refs.measurementForm.validate()
if (!valid) return
if (parseInt(this.questionForm[this.isMeasurableId]) === 1) {
if (!valid) return
if (parseInt(this.questionForm[this.isMeasurableId]) === 1) {
// 检验是否有标记为保存
let i = this.markList.findIndex(i=>i.saveEnum === 0)
if (i > -1) {
const i = this.markList.findIndex(i => i.saveEnum === 0)
if (i > -1) {
// 请先保存标注信息!
this.$alert(this.$t('trials:MRIPDFF:message:message1'))
// this.$message.warning(this.$t('trials:MRIPDFF:message:message1'))
return
}
} else {
this.$alert(this.$t('trials:MRIPDFF:message:message1'))
// this.$message.warning(this.$t('trials:MRIPDFF:message:message1'))
return
}
} else {
// 不可测量时,清空测量值,平均值
// '是否确认不可测量?'
const confirm = await this.$confirm(
this.$t('trials:MRIPDFF:message:message2'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
let l1Id = this.getQuestionId(1101)
this.$set(this.questionForm, l1Id, '')
let l2Id = this.getQuestionId(1102)
this.$set(this.questionForm, l2Id, '')
let l3Id = this.getQuestionId(1103)
this.$set(this.questionForm, l3Id, '')
let meanId = this.getQuestionId(1104)
this.$set(this.questionForm, meanId, 'NE')
}
const confirm = await this.$confirm(
this.$t('trials:MRIPDFF:message:message2'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
const l1Id = this.getQuestionId(1101)
this.$set(this.questionForm, l1Id, '')
const l2Id = this.getQuestionId(1102)
this.$set(this.questionForm, l2Id, '')
const l3Id = this.getQuestionId(1103)
this.$set(this.questionForm, l3Id, '')
const meanId = this.getQuestionId(1104)
this.$set(this.questionForm, meanId, 'NE')
}
const loading = this.$loading({ fullscreen: true })
const loading = this.$loading({ fullscreen: true })
try {
try {
// let isResetMarks = this.markList.findIndex(i=>i.measureData && i.measureData.MeasureData) > -1 ? true : false
if (parseInt(this.questionForm[this.isMeasurableId]) === 0 && this.isExitsMarks) {
await deleteTableQuestionMark({rowId: this.questionForm.RowId}, 11)
await deleteTableQuestionMark({ rowId: this.questionForm.RowId }, 11)
this.markList.forEach(i => {
if (i.measureData && i.measureData.MeasureData) {
i.measureData = ''
@@ -19,12 +19,21 @@
class="dicom-desc"
style="width: 150px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"
>
<el-tooltip class="item" effect="dark" :content="`${study.StudyCode} ${study.Description?study.Description:''} ${study.Modalities} (${study.SeriesCount})`" placement="right">
<div>
<span>{{ study.StudyCode }} {{ study.Description }}</span>
<span> {{ study.Modalities }} ({{ study.SeriesCount }})</span>
<div style="text-overflow: ellipsis;overflow: hidden;" v-if="!study.StudyName">
<span :title="study.StudyCode">{{ study.StudyCode }}</span>
<span style="margin-left: 5px;">{{ study.Modalities }} ({{ study.SeriesCount }})</span>
</div>
<div style="text-overflow: ellipsis;overflow: hidden;" v-else>
<span :title="study.StudyCode">{{ study.StudyCode }}</span>
<span v-if="taskInfo && taskInfo.IsShowStudyName" :title="study.StudyName" style="margin: 0 5px">
{{study.StudyName}}
</span>
<div>{{ study.Modalities }} ({{ study.SeriesCount }})</div>
</div>
<div style="text-overflow: ellipsis;overflow: hidden;" :title="study.Description">{{ study.Description }}</div>
</div>
</el-tooltip>
</div>
</template>
@@ -205,7 +214,8 @@ export default {
srDialogVisible: false,
srInfo: {},
digitPlaces: 2,
visitTaskIdx: -1
visitTaskIdx: -1,
taskInfo: null
}
},
@@ -243,6 +253,7 @@ export default {
this.subjectCode = localStorage.getItem('subjectCode')
var digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
DicomEvent.$on('refreshStudyListMeasureData', () => {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
this.measureData = this.visitTaskList[idx].MeasureData
@@ -76,7 +76,7 @@
<el-input
v-if="question.Type==='input'"
v-model="questionForm[question.Id]"
:disabled="question.TableQuestionType === 2"
:disabled="question.TableQuestionType === 2 || readingTaskState === 2"
/>
<!-- 多行文本输入框 -->
<el-input
@@ -84,13 +84,14 @@
v-model="questionForm[question.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:disabled="readingTaskState === 2"
/>
<!-- 下拉框 -->
<el-select
v-if="question.Type==='select'"
v-model="questionForm[question.Id]"
clearable
:disabled="(question.TableQuestionType === 2 || question.QuestionGenre === 2) && !!question.DictionaryCode"
:disabled="(question.TableQuestionType === 2 || question.QuestionGenre === 2) && !!question.DictionaryCode || readingTaskState === 2"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.TableQuestionType === 1">
@@ -132,6 +133,7 @@
v-if="question.Type==='radio'"
v-model="questionForm[question.Id]"
@change="((val)=>{formItemChange(val, question)})"
:disabled="readingTaskState === 2"
>
<el-radio
v-for="val in question.TypeValue.split('|')"
@@ -145,6 +147,7 @@
<el-checkbox-group
v-if="question.Type==='checkbox'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState === 2"
>
<el-checkbox
v-for="val in question.TypeValue.split('|')"
@@ -158,12 +161,12 @@
<el-input
v-if="question.Type === 'class' && question.ClassifyShowType === 1"
v-model="questionForm[question.Id]"
:disabled="!question.ClassifyEditType"
:disabled="!question.ClassifyEditType || readingTaskState === 2"
/>
<el-select
v-if="question.Type === 'class' && question.ClassifyShowType === 2"
v-model="questionForm[question.Id]"
:disabled="!question.ClassifyEditType"
:disabled="!question.ClassifyEditType || readingTaskState === 2"
@change="(val) => { formItemChange(val, question) }"
>
<el-option
@@ -176,7 +179,7 @@
<el-radio-group
v-if="question.Type === 'class' && question.ClassifyShowType === 3"
v-model="questionForm[question.Id]"
:disabled="!question.ClassifyEditType"
:disabled="!question.ClassifyEditType || readingTaskState === 2"
@change="(val) => { formItemChange(val, question) }"
>
<el-radio
@@ -190,7 +193,7 @@
<el-input
v-if="question.Type === 'class' && question.ClassifyShowType === 4"
type="number"
:disabled="!question.ClassifyEditType"
:disabled="!question.ClassifyEditType || readingTaskState === 2"
v-model="questionForm[question.Id]"
@change="(val) => { formItemNumberChange(val, question) }"
/>
@@ -215,6 +218,7 @@
v-model="questionForm[question.Id]"
clearable
@change="(val) => { formItemNumberChange(val, question) }"
:disabled="readingTaskState === 2"
>
<el-option
v-for="val in question.TypeValue.split('|')"
@@ -229,8 +233,9 @@
@change="(val) => { formItemNumberChange(val, question) }"
@blur="handleBlur(questionForm[question.Id], questionForm, question.Id)"
v-model="questionForm[question.Id]"
:disabled="readingTaskState === 2"
>
<template slot="append">1</template>
<!-- <template slot="append">1</template> -->
<template slot="append" v-if="question.Unit !== 0">{{question.Unit !== 4 ? $fd('ValueUnit', question.Unit) : question.CustomUnit}}</template>
<template slot="append" v-else-if="question.ValueType === 2">%</template>
</el-input>
@@ -238,16 +243,17 @@
type="number"
v-if="question.Type === 'number' && !question.TypeValue && question.DataSource === 1"
@blur="handleBlur(questionForm[question.Id], questionForm, question.Id)"
:disabled="question.DataSource === 1"
:disabled="question.DataSource === 1 || readingTaskState === 2"
v-model="questionForm[question.Id]"
>
<template slot="append">2</template>
<!-- <template slot="append">2</template> -->
<template slot="append" v-if="question.Unit !== 0">{{question.Unit !== 4 ? $fd('ValueUnit', question.Unit) : question.CustomUnit}}</template>
<template slot="append" v-else-if="question.ValueType === 2">%</template>
</el-input>
<!-- 上传图像 -->
<el-upload
v-if="question.Type==='upload'"
:disabled="readingTaskState === 2"
action
:accept="question.FileType"
:limit="question.ImageCount === 0 ? 100 : question.ImageCount"
@@ -258,7 +264,7 @@
:file-list="fileList"
:class="{disabled:question.ImageCount === 0 ? false : fileList.length >= question.ImageCount}"
>
<el-button slot="default" class="el-icon-plus">
<el-button slot="default" class="el-icon-plus" v-if="readingTaskState < 2">
{{this.$t('common:button:upload')}}
</el-button>
</el-upload>
@@ -337,16 +343,37 @@
</el-button>
</template>
</base-model>
<!-- 预览文件 -->
<el-dialog
v-if="previewVisible"
:visible.sync="previewVisible"
:title="$t('common:button:preview')"
:fullscreen="true"
append-to-body
custom-class="base-dialog-wrapper"
>
<div
class="base-modal-body"
style="border: 2px solid #ccc; padding: 10px"
>
<PreviewFile
v-if="previewVisible"
:file-path="currentPath"
:file-type="currentType"
/>
</div>
</el-dialog>
</div>
</template>
<script>
import { uploadReadingAnswerImage, getTrialOrganList, deleteReadingRowAnswer, getCustomTableQuestionPreview, getQuestionCalculateRelation, submitTableQuestion } from '@/api/trials'
import QuestionTableFormItem from './CustomizeQuestionTableFormItem'
import BaseModel from '@/components/BaseModel'
import PreviewFile from '@/components/PreviewFile/index'
import DicomEvent from './../components/DicomEvent'
export default {
name: 'CustomizeQuestionFormItem',
components: { QuestionTableFormItem, BaseModel },
components: { QuestionTableFormItem, BaseModel, PreviewFile },
props: {
IsBaseline: {
type: Boolean,
@@ -398,7 +425,10 @@ export default {
RowId: null,
digitPlaces: 2,
CalculationTabelList: [],
classArr: []
classArr: [],
previewVisible: false,
currentPath: '',
currentType: ''
}
},
watch: {
@@ -422,6 +452,13 @@ export default {
this.formItemNumberChange(this.question.Id, false)
}
},
readingTaskState: {
deep: true,
immediate: true,
handler(v, oldv) {
console.log(v)
}
}
},
mounted() {
var digitPlaces = Number(localStorage.getItem('digitPlaces'))
@@ -547,6 +584,7 @@ export default {
this.$set(this.QuestionsForm, obj.key, null)
},
handleSave() {
console.log('tableQsForm')
this.$refs.tableQsForm.validate(valid => {
if (!valid) return
const loading = this.$loading({ fullscreen: true })
@@ -612,6 +650,7 @@ export default {
var index = this.AnswersList.findIndex(v => v.RowId === this.QuestionsForm.RowId)
this.AnswersList.splice(index, 1, this.QuestionsForm)
}
console.log({key: this.question.Id, val: this.AnswersList, question: this.question})
this.$emit('setFormItemData', {key: this.question.Id, val: this.AnswersList, question: this.question})
this.formItemNumberChange(this.question.Id, true)
this.addOrEdit.visible = false
@@ -935,7 +974,8 @@ export default {
let res = await this.OSSclient.put(`/${this.$route.query.trialId}/Customize/${this.visitTaskId}/${fileName}`, file)
this.fileList.push({ name: `${this.$t('trials:emailManageCfg:title:fileName')}${this.fileList.length + 1}`, url: this.$getObjectName(res.url) })
this.urls.push(this.$getObjectName(res.url))
this.$emit('setFormItemData', { key: this.question.Id, val: this.urls.length > 0 ? this.urls.join('|') : '', question: this.question })
this.$emit("setFormItemData", { key: this.question.Id, val: this.urls.length > 0 ? this.urls.join('|') : '', question: this.question })
this.$set(this.QuestionsForm, this.question.Id, this.urls.length > 0 ? this.urls.join('|') : '')
loading.close()
// uploadReadingAnswerImage(this.$route.query.trialId, this.visitTaskId, formData).then(res => {
// if (res.IsSuccess) {
@@ -972,7 +1012,10 @@ export default {
var suffix = file.url.substring(file.url.lastIndexOf(".")+1)
suffix = suffix ? suffix.toLowerCase() : ''
if (suffix === 'doc' || suffix === 'docx' || suffix === 'pdf'){
window.open(this.OSSclientConfig.basePath + file.url,'_blank')
// window.open(this.OSSclientConfig.basePath + file.url,'_blank')
this.currentPath = file.url
this.currentType = suffix
this.previewVisible = true
}else{
this.imageUrl = this.OSSclientConfig.basePath + file.url
this.imgVisible = true
@@ -986,8 +1029,12 @@ export default {
if (file && file.status === "success") {
this.imageUrl = ''
this.fileList.splice(this.fileList.findIndex(f => f.url === file.url), 1)
this.fileList.forEach((i,index)=>{
i.name = `${this.$t('trials:emailManageCfg:title:fileName')}${index+ 1}`
})
this.urls.splice(this.fileList.findIndex(f => f === file.url), 1)
this.$emit('setFormItemData', { key: this.question.Id, val: this.urls.length > 0 ? this.urls.join('|') : '', question: this.question })
this.$set(this.QuestionsForm, this.question.Id, this.urls.length > 0 ? this.urls.join('|') : '')
}
}
}
@@ -20,7 +20,7 @@
:prop="question.Id"
:rules="[
{ required: (question.IsRequired === 0 || (question.IsRequired ===1 && question.RelevanceId && (questionForm[question.RelevanceId] === question.RelevanceValue))) && question.Type!=='group' && question.Type!=='summary',
message: '请注明', trigger: ['blur', 'change']},
message: $t('common:ruleMessage:specify'), trigger: ['blur', 'change']},
]"
:class="[question.Type==='group'?'mb':question.Type==='upload'?'uploadWrapper':'']"
>
@@ -77,6 +77,7 @@ export default {
methods: {
handleSave(isMsg) {
return new Promise(resolve => {
console.log('handleSave')
this.$refs['questions'].validate((valid) => {
if (!valid) {
resolve(false)
@@ -232,7 +233,9 @@ export default {
this.questionForm[v] = ''
},
setFormItemData(obj) {
console.log('setFormItemData', obj)
this.$set(this.questionForm, obj.key, JSON.parse(JSON.stringify(obj.val)))
console.log(this.questionForm)
this.classArr.map(i=>{
if (i.triggerId === obj.key) {
let answer = null
@@ -194,7 +194,7 @@
</template>
</template>
<template v-else-if="task.VisitTaskId === visitTaskId && scope.row.Type === 'upload'">
<CustomizeReportPageUpload
<customize-report-page-upload
v-if="scope.row.Type==='upload' && (scope.row.xfIndex || scope.row.xfIndex === 0)"
:visitTaskId="visitTaskId"
:question="scope.row"
@@ -202,8 +202,8 @@
:readingTaskState="readingTaskState"
:initUrl="questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]"
@setImageUrl="(url) => {setImageUrl(scope.row.QuestionId, scope.row.xfIndex, scope.row.TableQuestionId, url, scope.row.RowId)}"
></CustomizeReportPageUpload>
<CustomizeReportPageUpload
></customize-report-page-upload>
<customize-report-page-upload
v-else-if="scope.row.Type==='upload'"
:visitTaskId="visitTaskId"
:question="scope.row"
@@ -211,25 +211,25 @@
:readingTaskState="readingTaskState"
:initUrl="questionForm[scope.row.QuestionId]"
@setImageUrl="(url) => {setImageUrl(scope.row.QuestionId, scope.row.xfIndex, scope.row.TableQuestionId, url)}"
></CustomizeReportPageUpload>
></customize-report-page-upload>
</template>
<template v-else-if="scope.row.Type === 'upload'">
<CustomizeReportPageUpload
<customize-report-page-upload
v-if="scope.row.Type==='upload' && (scope.row.xfIndex || scope.row.xfIndex === 0)"
:visitTaskId="visitTaskId"
:question="scope.row"
:task="task"
:readingTaskState="readingTaskState"
:initUrl="scope.row.Answers[task.VisitTaskId]"
></CustomizeReportPageUpload>
<CustomizeReportPageUpload
></customize-report-page-upload>
<customize-report-page-upload
v-else-if="scope.row.Type==='upload'"
:visitTaskId="visitTaskId"
:question="scope.row"
:task="task"
:readingTaskState="readingTaskState"
:initUrl="scope.row.Answers[task.VisitTaskId]"
></CustomizeReportPageUpload>
></customize-report-page-upload>
</template>
<template v-else-if="scope.row.QuestionType=== 22">
{{ scope.row.Answers[task.VisitTaskId] === '-1' ? '未知' : scope.row.Answers[task.VisitTaskId] }}
@@ -276,6 +276,7 @@
<script>
import { changeCalculationAnswer, getReadingReportEvaluation, changeDicomReadingQuestionAnswer, submitDicomVisitTask, verifyVisitTaskQuestions, getQuestionCalculateRelation } from '@/api/trials'
import { setSkipReadingCache } from '@/api/reading'
import { getAutoCutNextTask } from '@/api/user'
import DicomEvent from './../components/DicomEvent'
import CustomizeReportPageUpload from './CustomizeReportPageUpload'
import const_ from '@/const/sign-code'
@@ -736,15 +737,16 @@ export default {
}
},
//
signConfirm(signInfo) {
async signConfirm(signInfo) {
this.loading = true
var params = {
data: {
visitTaskId: this.visitTaskId
},
signInfo: signInfo
}
submitDicomVisitTask(params).then(res => {
try {
var params = {
data: {
visitTaskId: this.visitTaskId
},
signInfo: signInfo
}
let res = await submitDicomVisitTask(params)
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
if (this.$refs['signForm']) {
@@ -752,41 +754,36 @@ export default {
}
this.signVisible = false
// window.location.reload()
// window.opener.postMessage('refreshTaskList', window.location)
//
this.readingTaskState = 2
store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
DicomEvent.$emit('setReadingState', 2)
window.opener.postMessage('refreshTaskList', window.location)
this.$confirm(this.$t('trials:oncologyReview:title:msg2'), {
type: 'warning',
distinguishCancelAndClose: true
})
const res = await getAutoCutNextTask()
let isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()
} else {
this.$confirm(this.$t('trials:oncologyReview:title:msg2'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
// var token = getToken()
// var subjectCode = this.$router.currentRoute.query.subjectCode
// var subjectId = this.$router.currentRoute.query.subjectId
// var trialId = this.$router.currentRoute.query.trialId
// this.$router.push({
// path: `/readingPage?subjectCode=${subjectCode}&subjectId=${subjectId}&trialId=${trialId}&TokenKey=${token}`
// })
// DicomEvent.$emit('getNextTask')
window.location.reload()
})
.catch(action => {
})
}
}
this.loading = false
}).catch(() => {
} catch(e) {
this.loading = false
if (this.$refs['signForm'] && this.$refs['signForm'].btnLoading) {
this.$refs['signForm'].btnLoading = false
}
})
}
},
previewDicoms(task) {
var token = getToken()
@@ -19,12 +19,20 @@
class="dicom-desc"
style="width: 150px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"
>
<el-tooltip class="item" effect="dark" :content="`${study.StudyCode} ${study.Description?study.Description:''} ${study.Modalities} (${study.SeriesCount})`" placement="right">
<div>
<span>{{ study.StudyCode }} {{ study.Description }}</span>
<span> {{ study.Modalities }} ({{ study.SeriesCount }})</span>
<div>
<div style="text-overflow: ellipsis;overflow: hidden;" v-if="!study.StudyName">
<span :title="study.StudyCode">{{ study.StudyCode }}</span>
<span>{{ study.Modalities }} ({{ study.SeriesCount }})</span>
</div>
</el-tooltip>
<div style="text-overflow: ellipsis;overflow: hidden;" v-else>
<span :title="study.StudyCode">{{ study.StudyCode }}</span>
<span v-if="taskInfo && taskInfo.IsShowStudyName" :title="study.StudyName">
{{study.StudyName}}
</span>
<div>{{ study.Modalities }} ({{ study.SeriesCount }})</div>
</div>
<div style="text-overflow: ellipsis;overflow: hidden;" :title="study.Description">{{ study.Description }}</div>
</div>
</div>
</template>
@@ -212,7 +220,8 @@ export default {
srDialogVisible: false,
srInfo: {},
digitPlaces: 2,
visitTaskIdx: -1
visitTaskIdx: -1,
taskInfo: null
}
},
@@ -250,6 +259,7 @@ export default {
this.subjectCode = localStorage.getItem('subjectCode')
var digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
DicomEvent.$on('refreshStudyListMeasureData', () => {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
this.measureData = this.visitTaskList[idx].MeasureData
@@ -339,6 +339,7 @@ export default {
localStorage.setItem('CriterionType', res.Result.CriterionType)
localStorage.setItem('digitPlaces', res.Result.DigitPlaces)
localStorage.setItem('IsExistUnprocessedFeedback', res.Result.IsExistUnprocessedFeedback)
localStorage.setItem('taskInfo', JSON.stringify(res.Result))
this.readingCategory = res.Result.ReadingCategory
this.questionFormChangeState = false
this.questionFormChangeNum = 0
@@ -66,7 +66,8 @@ export default function(evt) {
draw(context, context => {
// Configurable shadow
setShadow(context, this.configuration)
// Draw perpendicular line
const strokeWidth = lineWidth
const {
start,
end,
@@ -86,9 +87,6 @@ export default function(evt) {
// Draw the measurement line
drawLine(context, element, start, end, lineOptions)
// Draw perpendicular line
const strokeWidth = lineWidth
updatePerpendicularLineHandles(eventData, data)
drawLine(
@@ -10,9 +10,13 @@ const getNewContext = cornerstoneTools.import('drawing/getNewContext')
const draw = cornerstoneTools.import('drawing/draw')
const drawHandles = cornerstoneTools.import('drawing/drawHandles')
const drawTextBox = cornerstoneTools.import('drawing/drawTextBox')
const drawLink = cornerstoneTools.import('drawing/drawLink')
const drawLinkedTextBox = cornerstoneTools.import('drawing/drawLinkedTextBox')
// Utilities
const getRGBPixels = cornerstoneTools.import('util/getRGBPixels')
const calculateSUV = cornerstoneTools.import('util/calculateSUV')
const getROITextBoxCoords = cornerstoneTools.import('util/getROITextBoxCoords')
// import { probeCursor } from '../cursors/index.js';
// import { getLogger } from '../../util/logger.js';
const throttle = cornerstoneTools.import('util/throttle')
@@ -20,6 +24,8 @@ const getModule = cornerstoneTools.getModule
const getPixelSpacing = cornerstoneTools.import('util/getPixelSpacing')
// import numbersWithCommas from './../../util/numbersWithCommas.js';
const numbersWithCommas = cornerstoneTools.import('util/numbersWithCommas')
const clipBoxToDisplayedArea = cornerstoneTools.import('util/clip')
// const logger = getLogger('tools:annotation:ProbeTool');
import calculateEllipseStatistics from './calculateEllipseStatistics'
import getCircleCoords from './getCircleCoords'
@@ -31,6 +37,19 @@ import getCircleCoords from './getCircleCoords'
* desired position.
* @extends Tools.Base.BaseAnnotationTool
*/
const getHandle = (x, y, index, extraAttributes = {}) =>
Object.assign(
{
x,
y,
index,
drawnIndependently: false,
allowedOutsideImage: false,
highlight: true,
active: false
},
extraAttributes
)
export default class ProbeTool extends cornerstoneTools.ProbeTool {
constructor(props = {}) {
const defaultProps = {
@@ -64,6 +83,7 @@ export default class ProbeTool extends cornerstoneTools.ProbeTool {
return;
}
const { x, y } = eventData.currentPoints.image
return {
visible: true,
@@ -84,6 +104,23 @@ export default class ProbeTool extends cornerstoneTools.ProbeTool {
active: true,
radius: 0
},
// textBox: {
// active: false,
// hasMoved: false,
// movesIndependently: false,
// drawnIndependently: true,
// allowedOutsideImage: true,
// hasBoundingBox: true,
// }
textBox: getHandle(x, y - 30, null, {
highlight: false,
hasMoved: true,
active: false,
movesIndependently: false,
drawnIndependently: true,
allowedOutsideImage: true,
hasBoundingBox: true
})
},
};
}
@@ -226,7 +263,7 @@ export default class ProbeTool extends cornerstoneTools.ProbeTool {
draw(context, context => {
const color = toolColors.getColorIfActive(data);
const lineWidth = toolStyle.getToolWidth()
if (this.configuration.drawHandles) {
// Draw the handles
let radius = getCanvasRadius(data.handles, fixedRadius, element, pixelSpacing)
@@ -273,28 +310,133 @@ export default class ProbeTool extends cornerstoneTools.ProbeTool {
}
let r = getPixelRadius(fixedRadius, pixelSpacing)
// Coords for text
const coords = {
// Translate the x/y away from the cursor
x: data.handles.end.x + r,
y: data.handles.end.y - r,
};
const textCoords = external.cornerstone.pixelToCanvas(
eventData.element,
coords
);
drawTextBox(
if (!data.handles.hasOwnProperty('textBox')) {
const coords = {
// Translate the x/y away from the cursor
x: data.handles.end.x + r,
y: data.handles.end.y - r,
};
const textCoords = external.cornerstone.pixelToCanvas(
eventData.element,
coords
);
drawTextBox(
context,
textLines,
textCoords.x,
textCoords.y ,
color
);
return
}
// const handles = {
// start: {
// x: data.handles.end.x,
// y: data.handles.end.y
// },
// end: {
// x: data.handles.end.x + r,
// y: data.handles.end.y
// }
// }
let xOffset = 0
if (data.remark && !isNaN(parseInt(data.remark.slice(-1)))) {
let i = parseInt(data.remark.slice(-1))
if (i === 1) {
xOffset = -60
} else if (i === 2) {
xOffset = 0
} else if (i=== 3) {
xOffset = 0
}
}
// const xOffset = -30
const textBoxAnchorPoints = handles => [
handles.end
]
_drawLinkedTextBox(
context,
element,
data.handles.textBox,
textLines,
textCoords.x,
textCoords.y ,
color
);
// drawTextBox(context, '', textCoords.x, textCoords.y, color);
data.handles,
color,
lineWidth,
xOffset,
false
)
}
});
}
}
}
function _drawLinkedTextBox(
context,
element,
textBox,
text,
handles,
color,
lineWidth,
xOffset,
yCenter) {
const { pixelToCanvas } = external.cornerstone;
// Convert the textbox Image coordinates into Canvas coordinates
const textCoords = pixelToCanvas(element, textBox);
if (xOffset) {
textCoords.x += xOffset;
}
const options = {
centering: {
x: false,
y: yCenter,
},
};
options.displacer = box => clipBoxToDisplayedArea(element, box);
// Draw the text box
textBox.boundingBox = drawTextBox(
context,
text,
textCoords.x,
textCoords.y,
color,
options
);
if (textBox.hasMoved) {
// Identify the possible anchor points for the tool -> text line
let arr = [
{
x: handles.end.x,
y: handles.end.y
},
{
x: handles.end.x,
y: handles.end.y
}
]
const linkAnchorPoints = arr.map(h =>
pixelToCanvas(element, h)
);
// Draw dashed link line between tool and text
drawLink(
linkAnchorPoints,
textCoords,
textBox.boundingBox,
context,
color,
lineWidth
);
}
}
function _getUnit(modality, showHounsfieldUnits) {
return modality === 'CT' && showHounsfieldUnits !== false ? 'HU' : '';
}
@@ -5,28 +5,28 @@
<!-- 全局阅片 -->
<global-review
v-else-if="taskInfo && taskInfo.ReadingCategory=== 2"
:trialId="trialId"
:subjectId="taskInfo.SubjectId"
:visitTaskId="taskInfo.VisitTaskId"
:readingCategory="taskInfo.ReadingCategory"
:subjectCode="taskInfo.SubjectCode"
:taskBlindName="taskInfo.TaskBlindName"
:isReadingShowSubjectInfo="taskInfo.IsReadingShowSubjectInfo"
:isReadingShowPreviousResults="taskInfo.IsReadingShowPreviousResults"
:isExistsClinicalData="taskInfo.IsExistsClinicalData"
:trial-id="trialId"
:subject-id="taskInfo.SubjectId"
:visit-task-id="taskInfo.VisitTaskId"
:reading-category="taskInfo.ReadingCategory"
:subject-code="taskInfo.SubjectCode"
:task-blind-name="taskInfo.TaskBlindName"
:is-reading-show-subject-info="taskInfo.IsReadingShowSubjectInfo"
:is-reading-show-previous-results="taskInfo.IsReadingShowPreviousResults"
:is-exists-clinical-data="taskInfo.IsExistsClinicalData"
/>
<!-- 裁判阅片 -->
<ad-review
v-else-if="taskInfo && taskInfo.ReadingCategory=== 4"
:trialId="trialId"
:subjectId="taskInfo.SubjectId"
:visitTaskId="taskInfo.VisitTaskId"
:readingCategory="taskInfo.ReadingCategory"
:subjectCode="taskInfo.SubjectCode"
:taskBlindName="taskInfo.TaskBlindName"
:isReadingShowSubjectInfo="taskInfo.IsReadingShowSubjectInfo"
:isReadingShowPreviousResults="taskInfo.IsReadingShowPreviousResults"
:isExistsClinicalData="taskInfo.IsExistsClinicalData"
:trial-id="trialId"
:subject-id="taskInfo.SubjectId"
:visit-task-id="taskInfo.VisitTaskId"
:reading-category="taskInfo.ReadingCategory"
:subject-code="taskInfo.SubjectCode"
:task-blind-name="taskInfo.TaskBlindName"
:is-reading-show-subject-info="taskInfo.IsReadingShowSubjectInfo"
:is-reading-show-previous-results="taskInfo.IsReadingShowPreviousResults"
:is-exists-clinical-data="taskInfo.IsExistsClinicalData"
/>
<!-- 肿瘤学阅片 -->
<!-- <oncology-review v-else-if="taskInfo && taskInfo.ReadingCategory=== 5" /> -->
@@ -108,6 +108,7 @@ export default {
const res = await getNextTask(params)
this.taskInfo = res.Result
localStorage.setItem('taskInfo', JSON.stringify(res.Result))
localStorage.setItem('digitPlaces', JSON.stringify(res.Result.DigitPlaces))
this.loading = false
this.$nextTick(() => {
if (this.taskInfo.IsExistsClinicalData && this.taskInfo.IsNeedReadClinicalData && !this.taskInfo.IsReadClinicalData) {
@@ -133,7 +134,7 @@ export default {
} catch (e) {
this.loading = false
}
},
}
}
}
</script>
@@ -147,7 +148,7 @@ export default {
height:80%;
}
::v-deep .el-dialog__body{
padding: 20px 20px 0 20px;
padding: 10px;
height: calc(100% - 70px);
}
.el-dialog__header{
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,173 @@
<template>
<div>
<el-upload
action
:accept="question.FileType"
:limit="question.ImageCount === 0 ? 100 : question.ImageCount"
:on-preview="handlePictureCardPreview"
:before-upload="handleBeforeUpload"
:http-request="uploadScreenshot"
list-type="picture-card"
:on-remove="handleRemove"
:file-list="fileList"
:class="{disabled:readingTaskState >= 2 || (question.ImageCount === 0 ? false : fileList.length >= question.ImageCount) || (task.VisitTaskId !== visitTaskId) || question.IsShowInDicom || ((task.IsBaseLine && question.LimitEdit === 2) || (!task.IsBaseLine && question.LimitEdit === 1))}"
:disabled="readingTaskState >= 2 || task.VisitTaskId !== visitTaskId || question.IsShowInDicom || ((task.IsBaseLine && question.LimitEdit === 2) || (!task.IsBaseLine && question.LimitEdit === 1))"
>
<i slot="default" class="el-icon-plus" />
<div slot="file" slot-scope="{file}">
<viewer
:ref="file.url"
:images="[imageUrl]"
style="
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
"
>
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
crossOrigin="anonymous"
alt=""
style="max-width: 100%; max-height: 100%"
/>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</viewer>
</div>
</el-upload>
</div>
</template>
<script>
import { uploadReadingAnswerImage, getTrialOrganList, getCustomTableQuestionPreview } from '@/api/trials'
export default {
name: "CustomizeReportPageUpload",
props: {
task: {
Type: Object,
required: true
},
question: {
Type: Object,
required: true
},
visitTaskId: {
type: String,
required: true
},
readingTaskState: {
type: Number,
required: true
},
initUrl: {
type: String,
default: ''
}
},
data () {
return {
imgVisible: false,
imageUrl: null,
accept: '.png,.jpg,.jpeg',
fileList: [],
}
},
mounted() {
this.urls = this.initUrl === '' ? [] : this.initUrl.split('|')
console.log(this.visitTaskId, this.urls)
this.fileList = []
this.urls.map(url => {
this.fileList.push({ name: '', url: `${url}` })
})
// console.log(this.fileList)
},
methods: {
checkFileSuffix(fileName) {
var index = fileName.lastIndexOf('.')
var suffix = fileName.substring(index + 1, fileName.length)
if (this.question.FileType.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1) {
return false
} else {
return true
}
},
async uploadScreenshot(param) {
if (!this.visitTaskId) return
const loading = this.$loading({
target: document.querySelector('.ecrf-wrapper'),
fullscreen: false,
lock: true,
text: 'Loading',
spinner: 'el-icon-loading'
})
var trialId = this.$route.query.trialId
var subjectId = this.$route.query.trialId
var file = await this.fileToBlob(param.file)
const res = await this.OSSclient.put(`/${trialId}/Read/${subjectId}/Visit/${param.file.name}`, file)
console.log(res)
this.fileList.push({ name: param.file.name, path: this.$getObjectName(res.url), url: this.$getObjectName(res.url)})
this.urls.push(this.$getObjectName(res.url))
this.$emit('setImageUrl', this.urls.length > 0 ? this.urls.join('|') : '')
loading.close()
},
handleBeforeUpload(file) {
//
if (this.checkFileSuffix(file.name)) {
// this.fileList = []
return true
} else {
// this.$alert(` ${this.accept} `)
let msg = this.$t(
"trials:readingUnit:qsList:message:imageFormat"
).replace("xxx", this.question.FileType)
this.$alert(msg)
return false
}
},
//
handlePictureCardPreview(file) {
var suffix = file.url.substring(file.url.lastIndexOf(".")+1)
suffix = suffix ? suffix.toLowerCase() : ''
if (suffix === 'doc' || suffix === 'docx' || suffix === 'pdf'){
window.open(this.OSSclientConfig.basePath + file.url,'_blank')
}else{
this.imageUrl = this.OSSclientConfig.basePath + file.url
// this.imgVisible = true
this.$refs[file.url].$viewer.show()
}
},
//
handleRemove(file, fileList) {
this.imageUrl = ''
this.fileList.splice(this.fileList.findIndex(f => f.url === file.url), 1)
this.urls.splice(this.fileList.findIndex(f => f === file.url), 1)
this.$emit('setFormItemData', { key: this.question.Id, val: this.urls.length > 0 ? this.urls.join('|') : '' })
},
}
}
</script>
<style lang="scss" scoped>
.disabled{
::v-deep .el-upload--picture-card {
display: none;
}
}
</style>
@@ -1,12 +1,13 @@
<template>
<div v-loading="loading" class="ecrf-list-container">
<el-form
v-if="taskInfo"
ref="questions"
size="small"
:model="questionForm"
class="ecrf-form"
>
<FormItem
<!-- <FormItem
v-for="question of questions"
:key="question.Id"
:question="question"
@@ -16,6 +17,19 @@
:calculation-list="calculationList"
@setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData"
/> -->
<QuestionFormItem
v-for="question of questions"
:key="question.Id"
:visit-task-id="visitTaskId"
:question="question"
:question-form="questionForm"
:reading-task-state="readingTaskState"
:criterion-id="criterionId"
:calculation-list="calculationList"
:is-baseline="isBaseline"
@resetFormItemData="resetFormItemData"
@setFormItemData="setFormItemData"
/>
<el-form-item v-if="readingTaskState < 2">
@@ -53,17 +67,18 @@
<script>
import { getTrialReadingQuestion, saveVisitTaskQuestions, submitVisitTaskQuestionsInDto, getQuestionCalculateRelation } from '@/api/trials'
import { getCustomTableQuestionAnswer, changeDicomReadingQuestionAnswer, submitVisitTaskQuestionsInDto, getQuestionCalculateRelation } from '@/api/trials'
import { setSkipReadingCache } from '@/api/reading'
import const_ from '@/const/sign-code'
import store from '@/store'
import { mapGetters } from 'vuex'
import FormItem from './FormItem'
// import FormItem from './FormItem'
import QuestionFormItem from '@/views/trials/trials-panel/reading/dicoms/customize/CustomizeQuestionFormItem'
import SignForm from '@/views/trials/components/newSignForm'
export default {
name: 'EcrfList',
components: {
FormItem,
QuestionFormItem,
SignForm
},
props: {
@@ -88,7 +103,8 @@ export default {
activeName: 0,
classArr: [],
calculationList: [],
taskInfo: null
taskInfo: null,
isBaseline: false
}
},
computed: {
@@ -109,6 +125,7 @@ export default {
currentTaskState: {
immediate: true,
handler(state) {
console.log(state)
if (state === 2) {
this.readingTaskState = 2
}
@@ -117,45 +134,65 @@ export default {
},
mounted() {
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
this.isBaseline = this.taskInfo.IsBaseLine
},
methods: {
async getQuestions() {
async getQuestions(visitTaskId) {
this.loading = true
try {
const param = {
readingQuestionCriterionTrialId: this.criterionId,
visitTaskId: this.visitTaskId
}
const res = await getTrialReadingQuestion(param)
const res = await getCustomTableQuestionAnswer(param)
if (res.IsSuccess) {
this.readingTaskState = res.OtherInfo.readingTaskState
this.readingTaskState = res.OtherInfo.ReadingTaskState
res.Result.SinglePage.map((v) => {
if (v.Type === 'group' && v.Childrens.length === 0) return
if (!v.IsPage && v.Type !== 'group' && v.Type !== 'summary') {
this.$set(this.questionForm, v.Id, v.Answer ? v.Answer : null)
if (!v.IsPage && v.Type !== 'group' && v.Type !== 'summary' && v.Type !== 'table' && v.Type !== 'basicTable' && v.Type !== 'number') {
this.$set(this.questionForm, v.Id, v.Answer)
}
if (v.Type === 'table' || v.Type === 'basicTable') {
this.$set(this.questionForm, v.Id, v.TableQuestions.Answers)
}
if (v.Type === 'class') {
this.classArr.push({ triggerId: v.ClassifyQuestionId, classId: v.Id, classifyAlgorithms: v.ClassifyAlgorithms, classifyType: v.ClassifyType })
}
if (v.Type === 'number') {
this.$set(this.questionForm, v.Id, v.Answer === '' ? '' : parseFloat(v.Answer).toFixed(this.digitPlaces))
}
if (v.Childrens.length > 0) {
this.setChild(v.Childrens)
}
})
this.questions = res.Result.SinglePage
this.loading = false
}
this.loading = false
} catch (e) {
console.log(e)
this.loading = false
}
},
setChild(obj) {
obj.forEach(i => {
if (i.Type !== 'group' && i.Type !== 'summary' && i.Id) {
this.$set(this.questionForm, i.Id, i.Answer ? i.Answer : null)
if (i.Type !== 'group' && i.Type !== 'summary' && i.Id && i.Type !== 'table' && i.Type !== 'basicTable') {
this.$set(this.questionForm, i.Id, i.Answer)
}
if (i.Type === 'table' || i.Type === 'basicTable') {
i.TableQuestions.Questions.forEach(o => {
if (o.Type === 'number') {
i.TableQuestions.Answers.forEach((ite, index) => {
this.$set(i.TableQuestions.Answers[index], o.Id, i.TableQuestions.Answers[index][o.Id] ? parseFloat(i.TableQuestions.Answers[index][o.Id]).toFixed(this.digitPlaces) : null)
})
}
})
this.$set(this.questionForm, i.Id, i.TableQuestions.Answers)
}
if (i.Type === 'class') {
this.classArr.push({ triggerId: i.ClassifyQuestionId, classId: i.Id, classifyAlgorithms: i.ClassifyAlgorithms, classifyType: i.ClassifyType })
}
if (i.Type === 'number') {
this.$set(this.questionForm, i.Id, i.Answer === '' ? '' : parseFloat(i.Answer).toFixed(this.digitPlaces))
}
if (i.Childrens && i.Childrens.length > 0) {
this.setChild(i.Childrens)
}
@@ -169,26 +206,28 @@ export default {
console.log(e)
}
},
async handleSave() {
async handleSave(isMsg) {
const valid = await this.$refs['questions'].validate()
if (!valid) return
this.loading = true
const answers = []
for (const k in this.questionForm) {
answers.push({ readingQuestionTrialId: k, answer: this.questionForm[k] })
}
const params = {
trialId: this.trialId,
visitTaskId: this.visitTaskId,
readingQuestionCriterionTrialId: this.criterionId,
answerList: answers
}
try {
const res = await saveVisitTaskQuestions(params)
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
var answers = []
for (const k in this.questionForm) {
if (this.questionForm[k] instanceof Array) {} else {
answers.push({ id: k, answer: this.questionForm[k] })
}
}
var params = {
visitTaskId: this.visitTaskId,
answers: answers
}
const res = await changeDicomReadingQuestionAnswer(params)
if (res.IsSuccess) {
if (isMsg) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
this.loading = false
}
this.loading = false
} catch (e) {
this.loading = false
}
@@ -314,5 +353,50 @@ export default {
}
}
::v-deep .el-table,
.el-table__expanded-cell {
background-color: #000;
color: #fff;
border-color: #444444;
}
::v-deep .el-table th,
.el-table tr {
background-color: #000;
color: #fff;
border-color: #444444;
}
::v-deep .el-table__body tr > td {
background-color: #000 !important;
color: #fff;
border-color: #444444;
}
::v-deep .el-table__body tr:hover > td {
background-color: #858282 !important;
color: #fff;
border-color: #444444;
}
::v-deep .el-table--border th.gutter:last-of-type {
border: none;
}
::v-deep .el-dialog{
background: #1e1e1e;
border: 1px solid #ddd;
color: #ddd;
.el-dialog__title{
color:#fff;
}
.el-input .el-input__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
.el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
.el-form-item__label{
color: #dfdfdf
}
}
}
</style>
@@ -2,89 +2,110 @@
<div class="none-dicom-viewer">
<!-- tools -->
<div class="tools-wrapper">
<!-- 布局 -->
<div class="tool-item" :title="$t('trials:reading:button:layout')">
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
<svg-icon icon-class="layout" class="svg-icon" /><i class="el-icon-arrow-down el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="1*1">1*1</el-dropdown-item>
<el-dropdown-item command="1*2">1*2</el-dropdown-item>
<el-dropdown-item command="2*2">2*2</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<div class="tools-left">
<!-- 布局 -->
<div class="tool-item" :title="$t('trials:reading:button:layout')">
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
<svg-icon icon-class="layout" class="svg-icon" /><i class="el-icon-arrow-down el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="1*1">1*1</el-dropdown-item>
<el-dropdown-item command="1*2">1*2</el-dropdown-item>
<el-dropdown-item command="2*2">2*2</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<!-- 缩放 -->
<div
:class="['tool-item', activeTool === 'Zoom' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:zoom')"
@click.prevent="setToolActive('Zoom')"
>
<svg-icon icon-class="magnifier" class="svg-icon" />
</div>
<!-- 移动 -->
<div
:class="['tool-item', activeTool === 'Pan' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:move')"
@click.prevent="setToolActive('Pan')"
>
<svg-icon icon-class="move" class="svg-icon" />
</div>
<!-- 旋转 -->
<div
:class="['tool-item', activeTool === 'PlanarRotate' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:rotate')"
@click.prevent="setToolActive('PlanarRotate')"
>
<svg-icon icon-class="rotate" class="svg-icon" />
</div>
<!-- 箭头工具 -->
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'ArrowAnnotate' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:arrowAnnotate')"
@click.prevent="setAnnotateToolActive('ArrowAnnotate')"
>
<svg-icon icon-class="arrow" class="svg-icon" />
</div>
<!-- 矩形工具 -->
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'RectangleROI' ? 'tool-item-active' : '']"
:title="$t('trials:dicom-show:RectangleRoi')"
@click.prevent="setAnnotateToolActive('RectangleROI')"
>
<svg-icon icon-class="rectangle" class="svg-icon" />
</div>
<!-- 自由曲线 -->
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'PlanarFreehandROI' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:planarFreehandROI')"
@click.prevent="setAnnotateToolActive('PlanarFreehandROI')"
>
<svg-icon icon-class="polygon" class="svg-icon" />
</div>
<!-- <div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'SplineROITool' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:SplineROITool')"
@click.prevent="setAnnotateToolActive('SplineROITool')"
>
<svg-icon icon-class="polygon" class="svg-icon" />
</div> -->
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'Eraser' ? 'tool-item-active' : '']"
:title="$t('trials:dicom-show:Eraser')"
@click.prevent="setAnnotateToolActive('Eraser')"
>
<svg-icon icon-class="clear" class="svg-icon" />
</div>
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'Length' ? 'tool-item-active' : '']"
:title="$t('trials:nondicom-show:scale')"
@click.prevent="setAnnotateToolActive('Length')"
>
<svg-icon icon-class="length" class="svg-icon" />
</div>
<!-- 截图 -->
<!-- <div
class="tool-item"
:title="$t('trials:reading:button:screenShot')"
@click.prevent="saveImage"
>
<svg-icon icon-class="image" class="svg-icon" />
</div> -->
<!-- 重置 -->
<div
class="tool-item"
:title="$t('trials:reading:button:reset')"
@click.prevent="resetViewport"
>
<svg-icon icon-class="refresh" class="svg-icon" />
</div>
</div>
<!-- 缩放 -->
<div
:class="['tool-item', activeTool === 'Zoom' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:zoom')"
@click.prevent="setToolActive('Zoom')"
>
<svg-icon icon-class="magnifier" class="svg-icon" />
</div>
<!-- 移动 -->
<div
:class="['tool-item', activeTool === 'Pan' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:move')"
@click.prevent="setToolActive('Pan')"
>
<svg-icon icon-class="move" class="svg-icon" />
</div>
<!-- 旋转 -->
<div
:class="['tool-item', activeTool === 'PlanarRotate' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:rotate')"
@click.prevent="setToolActive('PlanarRotate')"
>
<svg-icon icon-class="rotate" class="svg-icon" />
</div>
<!-- 箭头工具 -->
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'ArrowAnnotate' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:arrowAnnotate')"
@click.prevent="setAnnotateToolActive('ArrowAnnotate')"
>
<svg-icon icon-class="arrow" class="svg-icon" />
</div>
<!-- 矩形工具RectangleROITool -->
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'RectangleROI' ? 'tool-item-active' : '']"
:title="$t('trials:dicom-show:RectangleRoi')"
@click.prevent="setAnnotateToolActive('RectangleROI')"
>
<svg-icon icon-class="rectangle" class="svg-icon" />
</div>
<!-- 自由曲线 -->
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'PlanarFreehandROI' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:planarFreehandROI')"
@click.prevent="setAnnotateToolActive('PlanarFreehandROI')"
>
<svg-icon icon-class="polygon" class="svg-icon" />
</div>
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'Eraser' ? 'tool-item-active' : '']"
:title="$t('trials:dicom-show:Eraser')"
@click.prevent="setAnnotateToolActive('Eraser')"
>
<svg-icon icon-class="clear" class="svg-icon" />
</div>
<!-- 截图 -->
<!-- <div
class="tool-item"
:title="$t('trials:reading:button:screenShot')"
@click.prevent="saveImage"
>
<svg-icon icon-class="image" class="svg-icon" />
</div> -->
<!-- 重置 -->
<div
class="tool-item"
:title="$t('trials:reading:button:reset')"
@click.prevent="resetViewport"
>
<svg-icon icon-class="refresh" class="svg-icon" />
<div>
<el-button type="text" @click="previewConfig">{{ $t('trials:reading:button:customCfg') }}</el-button>
</div>
</div>
<!-- viewports -->
@@ -146,7 +167,7 @@
</div>
</div>
<div :ref="`sliderBox-${index}`" class="right-slider-box" @click.stop="clickSlider($event, index)">
<div :style="{top: v.height + '%'}" class="slider" @click.stop.prevent="() => {return}" @mousedown.stop="sliderMousedown($event, index)"/>
<div :style="{top: v.height + '%'}" class="slider" @click.stop.prevent="() => {return}" @mousedown.stop="sliderMousedown($event, index)" />
</div>
</div>
<div v-show="v.fileType === 'application/pdf' && fullScreenIndex === null " class="content flex_col">
@@ -202,6 +223,36 @@
</div>
</div>
</div>
<el-dialog
:title="$t('trials:noneDicom:message:msg2')"
:visible.sync="dialogVisible"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
width="400px"
>
<el-form ref="lengthForm" :model="form" :rules="rules">
<el-form-item label="" prop="length">
<el-input v-model="form.length" type="number">
<template slot="append">mm</template>
</el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="saveForm">{{ $t('common:button:save') }}</el-button>
</span>
</el-dialog>
<el-dialog
v-if="personalConfigDialog.visible"
:visible.sync="personalConfigDialog.visible"
:close-on-click-modal="false"
:title="personalConfigDialog.title"
width="600px"
>
<Others />
</el-dialog>
</div>
</template>
<script>
@@ -212,7 +263,8 @@ import {
imageLoader,
metaData,
getRenderingEngine,
eventTarget
eventTarget,
utilities as csUtils
// getEnabledElementByIds
} from '@cornerstonejs/core'
@@ -222,6 +274,7 @@ import hardcodedMetaDataProvider from './../js/hardcodedMetaDataProvider'
import registerWebImageLoader from './../js/registerWebImageLoader'
import { mapGetters } from 'vuex'
import store from '@/store'
import Others from '@/views/trials/trials-panel/reading/dicoms/components/Others'
const { ViewportType } = Enums
const renderingEngineId = 'myRenderingEngine'
const {
@@ -234,18 +287,27 @@ const {
ArrowAnnotateTool,
RectangleROITool,
PlanarFreehandROITool,
EraserTool
SplineROITool,
EraserTool,
LengthTool
// cursors
} = cornerstoneTools
const { MouseBindings, Events: toolsEvents } = csToolsEnums
export default {
name: 'ImageViewer',
components: { Others },
props: {
relatedStudyInfo: {
type: Object,
default() {
return {}
}
},
psArr: {
type: Array,
default() {
return []
}
}
},
data() {
@@ -263,7 +325,20 @@ export default {
activeTool: '',
readingTaskState: 2,
renderHistoryAnnotationTaskIds: [],
imageType: ['image/jpeg', 'image/jpg', 'image/bmp', 'image/png']
imageType: ['image/jpeg', 'image/jpg', 'image/bmp', 'image/png'],
digitPlaces: 2,
dialogVisible: false,
personalConfigDialog: { visible: false, title: this.$t('trials:reading:button:customCfg') }, //
form: {
length: null,
annotationObj: {}
},
rules: {
length: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ pattern: /^\d+$/, message: this.$t('trials:noneDicom:message:msg3'), trigger: ['blur', 'change'] }
]
}
}
},
computed: {
@@ -335,6 +410,8 @@ export default {
isMove: false,
height: 0
}))
const digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
this.initLoader()
window.addEventListener('message', this.handleIframeMessage)
},
@@ -404,7 +481,9 @@ export default {
cornerstoneTools.addTool(ArrowAnnotateTool)
cornerstoneTools.addTool(RectangleROITool)
cornerstoneTools.addTool(PlanarFreehandROITool)
cornerstoneTools.addTool(SplineROITool)
cornerstoneTools.addTool(EraserTool)
cornerstoneTools.addTool(LengthTool)
viewportIds.forEach((viewportId, i) => {
const toolGroupId = `canvas-${i}`
@@ -423,12 +502,27 @@ export default {
return doneChangingTextCallback(await this.customPrompt())
}
})
toolGroup.addTool(RectangleROITool.toolName)
toolGroup.addTool(PlanarFreehandROITool.toolName, {
allowOpenContours: false
// cachedStats: false
toolGroup.addTool(RectangleROITool.toolName, {
cachedStats: false,
getTextLines: this.getRectangleROIToolTextLines
})
toolGroup.addTool(PlanarFreehandROITool.toolName, {
allowOpenContours: false,
cachedStats: false,
getTextLines: this.getPlanarFreehandROIToolTextLines
})
// const splineConfig = toolGroup.getToolConfiguration(
// splineToolName,
// 'spline'
// )
toolGroup.addTool(SplineROITool.toolName)
toolGroup.addTool(EraserTool.toolName)
toolGroup.addTool(LengthTool.toolName, {
getTextLines: this.getLengthToolTextLines,
cachedStats: false
})
toolGroup.setToolActive(StackScrollTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Wheel }]
})
@@ -439,10 +533,14 @@ export default {
toolGroup.setToolPassive(ArrowAnnotateTool.toolName)
toolGroup.setToolPassive(RectangleROITool.toolName)
toolGroup.setToolPassive(PlanarFreehandROITool.toolName)
toolGroup.setToolPassive(SplineROITool.toolName)
toolGroup.setToolPassive(LengthTool.toolName)
} else {
toolGroup.setToolEnabled(ArrowAnnotateTool.toolName)
toolGroup.setToolEnabled(RectangleROITool.toolName)
toolGroup.setToolEnabled(PlanarFreehandROITool.toolName)
toolGroup.setToolEnabled(SplineROITool.toolName)
toolGroup.setToolEnabled(LengthTool.toolName)
}
toolGroup.setToolPassive(EraserTool.toolName)
})
@@ -465,16 +563,13 @@ export default {
renderHistoryAnnotations(obj) {
if (this.renderHistoryAnnotationTaskIds.includes(obj.VisitTaskId)) return
this.renderHistoryAnnotationTaskIds.push(obj.VisitTaskId)
const arr = []
// let arr = []
obj.Annotations.map(i => {
if (typeof i.MeasureData === 'string') {
const annotation = JSON.parse(i.MeasureData)
annotation.annotationId = i.Id
arr.push(annotation)
cornerstoneTools.annotation.state.addAnnotation(annotation)
if (obj.ReadingTaskState === 2) {
cornerstoneTools.annotation.locking.setAnnotationLocked(annotation.annotationUID)
}
const annotation = i.MeasureData
annotation.annotationId = i.Id
cornerstoneTools.annotation.state.addAnnotation(annotation)
if (obj.ReadingTaskState === 2) {
cornerstoneTools.annotation.locking.setAnnotationLocked(annotation.annotationUID)
}
})
},
@@ -571,7 +666,7 @@ export default {
this.rows = parseInt(command.split('*')[0])
this.cols = parseInt(command.split('*')[1])
if (this.rows === 1 && this.cols === 1) {
this.$nextTick(()=>{
this.$nextTick(() => {
this.activeCanvas(0)
})
}
@@ -709,6 +804,19 @@ export default {
if (this.activeTool) {
toolGroup.setToolPassive(this.activeTool)
}
if (toolName === 'Length') {
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(`canvas-${this.activeCanvasIndex}`)
const imageId = viewport.csImage.imageId
const annotations = cornerstoneTools.annotation.state.getAllAnnotations()
const idx = annotations.findIndex(i => i.metadata.referencedImageId === imageId && i.metadata.toolName === 'Length')
if (idx > -1) {
this.activeTool = ''
//
this.$message.warning(this.$t('trials:noneDicom:message:msg4'))
return
}
}
toolGroup.setToolActive(toolName, {
bindings: [{ mouseButton: MouseBindings.Primary }]
})
@@ -753,12 +861,22 @@ export default {
if (this.readingTaskState === 2) return
const { annotation } = e.detail
if (!annotation) return
if (!annotation.annotationId) return
await deleteTrialFileType(annotation.annotationId)
if (annotation.annotationId) {
await deleteTrialFileType(annotation.annotationId)
} else {
cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID)
}
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(`canvas-${this.activeCanvasIndex}`)
viewport.render()
//
const i = this.viewportInfos.findIndex(i => i.index === this.activeCanvasIndex)
const imageId = annotation.metadata.referencedImageId
const path = imageId.split(`web:${this.OSSclientConfig.basePath}`)[1]
const fileList = this.viewportInfos[i].fileList
const fileIndex = fileList.findIndex(f => f.Path === path)
if (annotation.metadata.toolName === 'Length') {
this.$emit('setPS', { NoneDicomFileId: fileList[fileIndex].Id, Path: fileList[fileIndex].Path, PS: null })
}
},
async annotationModifiedListener(e) {
console.log('Modified')
@@ -775,6 +893,16 @@ export default {
const fileList = this.viewportInfos[i].fileList
const fileIndex = fileList.findIndex(f => f.Path === path)
if (fileIndex === -1) return
if (annotation.metadata.toolName === 'Length') {
const value = annotation.data.l
if (value) {
const cachedStats = Object.keys(annotation.data.cachedStats)
const ps = value / annotation.data.cachedStats[cachedStats[0]].length
annotation.data.ps = ps
this.$emit('setPS', { NoneDicomFileId: fileList[fileIndex].Id, Path: fileList[fileIndex].Path, PS: ps })
}
this.setToolsPassive()
}
const params = {
id: annotation.annotationId,
visitTaskId: this.viewportInfos[i].taskInfo.VisitTaskId,
@@ -801,6 +929,23 @@ export default {
const fileList = this.viewportInfos[i].fileList
const fileIndex = fileList.findIndex(f => f.Path === path)
if (fileIndex === -1) return
if (annotation.metadata.toolName === 'Length') {
this.form.annotationObj = {
id: '',
visitTaskId: this.viewportInfos[i].taskInfo.VisitTaskId,
studyId: this.viewportInfos[i].studyId,
noneDicomFileId: fileList[fileIndex].Id,
path: fileList[fileIndex].Path,
annotation,
fileList,
fileIndex
}
this.form.length = null
this.dialogVisible = true
this.setToolsPassive()
return
}
const params = {
id: '',
visitTaskId: this.viewportInfos[i].taskInfo.VisitTaskId,
@@ -811,9 +956,145 @@ export default {
}
const res = await addNoneDicomMark(params)
annotation.annotationId = res.Result
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(`canvas-${this.activeCanvasIndex}`)
viewport.render()
//
// this.$message.success(this.$t('common:message:savedSuccessfully'))
},
async saveForm() {
const validate = await this.$refs.lengthForm.validate()
if (!validate) return
const value = this.form.length
const annotation = this.form.annotationObj.annotation
const fileList = this.form.annotationObj.fileList
const fileIndex = this.form.annotationObj.fileIndex
if (value) {
annotation.data.l = parseFloat(value)
const cachedStats = Object.keys(annotation.data.cachedStats)
const ps = parseFloat(value) / annotation.data.cachedStats[cachedStats[0]].length
annotation.data.ps = ps
this.$emit('setPS', { NoneDicomFileId: fileList[fileIndex].Id, Path: fileList[fileIndex].Path, PS: ps })
} else {
cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID)
return
}
const params = {
id: '',
visitTaskId: this.form.annotationObj.visitTaskId,
studyId: this.form.annotationObj.studyId,
noneDicomFileId: this.form.annotationObj.noneDicomFileId,
path: this.form.annotationObj.path,
measureData: JSON.stringify(annotation)
}
const res = await addNoneDicomMark(params)
annotation.annotationId = res.Result
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(`canvas-${this.activeCanvasIndex}`)
viewport.render()
this.dialogVisible = false
},
getLengthToolTextLines(data, targetId) {
const cachedVolumeStats = data.cachedStats[targetId]
const { length, unit } = cachedVolumeStats
if (length === undefined || length === null || isNaN(length)) {
return
}
if (data.l === undefined || data.l === null || isNaN(data.l)) {
return
}
const textLines = []
textLines.push(`P: ${parseFloat(length).toFixed(this.digitPlaces)} ${unit}`)
if (data.l) {
textLines.push(`L: ${parseFloat(data.l).toFixed(this.digitPlaces)} mm`)
textLines.push(`PS: ${parseFloat(data.l / length).toFixed(3)} mm/px`)
}
return textLines
},
getPlanarFreehandROIToolTextLines(data, targetId) {
const cachedVolumeStats = data.cachedStats[targetId]
const {
area,
mean,
stdDev,
// length,
perimeter,
max,
isEmptyArea,
unit,
areaUnit,
modalityUnit
} = cachedVolumeStats || {}
const textLines = []
let ps = null
const path = targetId.split(`web:${this.OSSclientConfig.basePath}`)[1]
const i = this.psArr.findIndex(i => i.Path === path)
if (i > -1 && this.psArr[i].PS) {
ps = parseFloat(this.psArr[i].PS).toFixed(3)
}
if (area) {
const areaLine = isEmptyArea
? `Area: Oblique not supported`
: `Area: ${ps ? parseFloat(area * ps * ps).toFixed(this.digitPlaces) : parseFloat(area).toFixed(this.digitPlaces)} ${ps ? 'mm' + '\xb2' : areaUnit}`
textLines.push(areaLine)
}
if (mean) {
textLines.push(`Mean: ${parseFloat(mean).toFixed(this.digitPlaces)} ${modalityUnit}`)
}
if (Number.isFinite(max)) {
textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`)
}
if (stdDev) {
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`)
}
if (perimeter) {
if (ps) {
textLines.push(`Perimeter: ${parseFloat(perimeter * ps).toFixed(this.digitPlaces)} mm`)
} else {
textLines.push(`Perimeter: ${parseFloat(perimeter).toFixed(this.digitPlaces)} ${unit}`)
}
}
// if (length) {
// // No need to show length prefix as there is just the single value
// textLines.push(`${csUtils.roundNumber(length)} ${unit}`);
// }
return textLines
},
getRectangleROIToolTextLines(data, targetId) {
const cachedVolumeStats = data.cachedStats[targetId]
const { area, mean, max, stdDev, areaUnit, modalityUnit } = cachedVolumeStats
if (mean === undefined) {
return
}
const textLines = []
let ps = null
const path = targetId.split(`web:${this.OSSclientConfig.basePath}`)[1]
const i = this.psArr.findIndex(i => i.Path === path)
if (i > -1 && this.psArr[i].PS) {
ps = parseFloat(this.psArr[i].PS).toFixed(3)
}
if (ps) {
textLines.push(`Area: ${parseFloat(area * ps * ps).toFixed(this.digitPlaces)} ${'mm' + '\xb2'}`)
} else {
textLines.push(`Area: ${parseFloat(area).toFixed(this.digitPlaces)} ${areaUnit}`)
}
textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`)
textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`)
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`)
return textLines
},
debounce(callback, delay) {
let timerId
return function() {
@@ -835,7 +1116,8 @@ export default {
//
async customPrompt() {
try {
const { value } = await this.$prompt('请输入标记名称')
//
const { value } = await this.$prompt(this.$t('trials:noneDicom:message:msg1'))
return value
} catch {
return null
@@ -882,8 +1164,7 @@ export default {
viewport.setImageIdIndex(sliceIdx)
viewport.render()
// }
// this.$emit('toggleImage', { taskId: this.viewportInfos[i].taskInfo.VisitTaskId, studyId: this.viewportInfos[i].studyId, imageIndex: sliceIdx })
// this.$emit('toggleImage', { taskId: this.viewportInfos[i].taskInfo.VisitTaskId, studyId: this.viewportInfos[i].studyId, imageIndex: sliceIdx })
},
sliderMouseleave(e, index) {
const i = this.viewportInfos.findIndex(i => i.index === index)
@@ -918,6 +1199,9 @@ export default {
//
viewCD(id) {
this.$emit('previewCD', id)
},
previewConfig() {
this.personalConfigDialog.visible = true
}
}
}
@@ -932,11 +1216,17 @@ export default {
.tools-wrapper {
height: 50px;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
border-bottom: 1px solid #727272;
color: #ddd;
padding: 0 5px;
.tools-left {
flex: 1;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.tool-item {
padding: 5px;
margin: 0 5px;
@@ -1055,5 +1345,24 @@ export default {
}
}
}
::v-deep .el-dialog{
background: #1e1e1e;
border: 1px solid #ddd;
color: #ddd;
.el-dialog__title{
color:#fff;
}
.el-input .el-input__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
.el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
.el-form-item__label{
color: #dfdfdf
}
}
}
</style>
@@ -127,6 +127,7 @@
v-model="questionForm[question.Id]"
clearable
@change="(val) => { formItemNumberChange(val, question) }"
:disabled="readingTaskState >= 2"
>
<el-option
v-for="val in question.TypeValue.split('|')"
@@ -139,9 +140,9 @@
v-else-if="question.DataSource !== 1"
v-model="questionForm[question.Id]"
type="number"
onblur="value=parseFloat(value).toFixed(parseInt(localStorage.getItem('digitPlaces')));"
@blur="handleBlur(questionForm[question.Id], questionForm, question.Id)"
@change="(val) => { formItemNumberChange(val, question) }"
@input="limitInput($event, questionForm, question.Id)"
:disabled="readingTaskState >= 2"
>
<template v-if="question.Unit !== 0" slot="append">{{ question.Unit !== 4 ? $fd('ValueUnit', question.Unit) : question.CustomUnit }}</template>
<template v-else-if="question.ValueType === 2" slot="append">%</template>
@@ -150,9 +151,8 @@
v-else-if="question.DataSource === 1"
v-model="questionForm[question.Id]"
type="number"
onblur="value=parseFloat(value).toFixed(parseInt(localStorage.getItem('digitPlaces')));"
:disabled="question.DataSource === 1"
@input="limitInput($event, questionForm, question.Id)"
@blur="handleBlur(questionForm[question.Id], questionForm, question.Id)"
:disabled="question.DataSource === 1 || readingTaskState >= 2"
>
<template v-if="question.Unit !== 0" slot="append">{{ question.Unit !== 4 ? $fd('ValueUnit', question.Unit) : question.CustomUnit }}</template>
<template v-else-if="question.ValueType === 2" slot="append">%</template>
@@ -305,7 +305,7 @@ export default {
imgVisible: false,
imageUrl: '',
urls: [],
digitPlaces: null
digitPlaces: 2
}
},
watch: {
@@ -336,7 +336,8 @@ export default {
})
}
}
this.digitPlaces = localStorage.getItem('digitPlaces') ? parseInt(localStorage.getItem('digitPlaces')) : 0
let digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
},
methods: {
limitInput(value, a, b) {
@@ -346,6 +347,9 @@ export default {
}
}
},
handleBlur(value, a, b) {
this.$set(a, b, parseFloat(value).toFixed(this.digitPlaces))
},
logic(rules, num = 0) {
try {
if (rules.CalculateQuestionList.length === 0) {
@@ -461,14 +465,13 @@ export default {
} catch (e) {
console.log(e)
}
var digitPlaces = parseInt(localStorage.getItem('digitPlaces'))
if (rules.ValueType === 2) {
num = num * 100
}
if (rules.CustomCalculateMark === 13 || rules.CustomCalculateMark === 14) {
return num
} else {
return num.toFixed(digitPlaces)
return num.toFixed(this.digitPlaces)
}
},
formItemNumberChange(questionId, isTable) {
@@ -51,10 +51,12 @@
<file-viewer
ref="fileViewer"
:related-study-info="relatedStudyInfo"
:ps-arr="psArr"
@toggleTaskByViewer="toggleTaskByViewer"
@toggleTask="toggleTask"
@toggleImage="toggleImage"
@previewCD="previewCD"
@setPS="setPS"
/>
</div>
<!-- 表单 -->
@@ -152,7 +154,8 @@ export default {
clinicalDataVisible: false,
isClinicalDataFullscreen: false,
trialId: '',
cdVisitTaskId: ''
cdVisitTaskId: '',
psArr: []
}
},
computed: {
@@ -175,7 +178,6 @@ export default {
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
this.trialId = this.$route.query.trialId
this.getRelatedTask()
},
methods: {
//
@@ -249,7 +251,16 @@ export default {
visitTaskId: visitTaskId
}
const res = await getNoneDicomMarkListOutDto(params)
this.$set(this.visitTaskList[visitTaskIdx], 'Annotations', res.Result.NoneDicomMarkList)
const arr = res.Result.NoneDicomMarkList.map(i => {
if (typeof i.MeasureData === 'string') {
i.MeasureData = JSON.parse(i.MeasureData)
}
if (i.MeasureData.metadata.toolName === 'Length' && this.psArr.findIndex(p => p.NoneDicomFileId === i.NoneDicomFileId) === -1) {
this.psArr.push({ NoneDicomFileId: i.NoneDicomFileId, Path: i.Path, PS: i.MeasureData.data.ps })
}
return i
})
this.$set(this.visitTaskList[visitTaskIdx], 'Annotations', arr)
this.loading = false
resolve()
} catch (e) {
@@ -259,6 +270,14 @@ export default {
}
})
},
setPS(obj) {
const i = this.psArr.findIndex(p => p.NoneDicomFileId === obj.NoneDicomFileId)
if (i > -1) {
this.psArr[i].PS = obj.PS
} else {
this.psArr.push(obj)
}
},
//
toggleTask(taskInfo) {
this.setActiveTaskVisitId(taskInfo.VisitTaskId)
@@ -170,6 +170,13 @@
</span>
</div>
</template>
<template v-else-if="scope.row.Type==='upload' && scope.row.Answers[task.VisitTaskId]">
<span v-for="(url,index) in scope.row.Answers[task.VisitTaskId].split('|')" :key="url" style="margin-left: 5px;">
<el-button v-if="scope.row.Answers[task.VisitTaskId]" type="text" @click="preview(url)">
{{ `${$t('trials:noneDicom:title:attachment')}${index + 1}` }}
</el-button>
</span>
</template>
<template v-else-if="scope.row.DictionaryCode">
{{ $fd(scope.row.DictionaryCode, scope.row.Answers[task.VisitTaskId]) }}
</template>
@@ -206,19 +213,41 @@
</div>
<SignForm ref="signForm" :sign-code-enum="signCode" @closeDialog="closeSignDialog" />
</el-dialog>
<!-- 预览文件 -->
<el-dialog
v-if="previewVisible"
:visible.sync="previewVisible"
:title="$t('common:button:preview')"
:fullscreen="true"
append-to-body
custom-class="base-dialog-wrapper"
>
<div
class="base-modal-body"
style="border: 2px solid #ccc; padding: 10px"
>
<PreviewFile
v-if="previewVisible"
:file-path="currentPath"
:file-type="currentType"
/>
</div>
</el-dialog>
</div>
</template>
<script>
import { changeCalculationAnswer, getReadingReportEvaluation, submitDicomVisitTask, verifyVisitTaskQuestions, getQuestionCalculateRelation } from '@/api/trials'
import { setSkipReadingCache } from '@/api/reading'
// import UploadFile from './UploadFile'
import { getAutoCutNextTask } from '@/api/user'
import const_ from '@/const/sign-code'
import SignForm from '@/views/trials/components/newSignForm'
import PreviewFile from '@/components/PreviewFile/index'
import { getToken } from '@/utils/auth'
import store from '@/store'
export default {
name: 'ReportPage',
components: { SignForm },
name: 'CustomizeReportPage',
components: { SignForm, PreviewFile },
data() {
return {
trialId: '',
@@ -242,7 +271,10 @@ export default {
tableAnswers: {},
questionForm: {},
questionId: null,
taskInfo: null
taskInfo: null,
previewVisible: false,
currentPath: '',
currentType: ''
}
},
watch: {
@@ -264,7 +296,8 @@ export default {
this.visitTaskId = this.taskInfo.VisitTaskId
this.subjectId = this.taskInfo.SubjectId
this.criterionType = this.taskInfo.CriterionType
this.digitPlaces = this.taskInfo.DigitPlaces
var digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
this.TrialReadingCriterionId = this.taskInfo.TrialReadingCriterionId
window.addEventListener('resize', () => {
this.handleResize()
@@ -351,11 +384,11 @@ export default {
},
InitVisitTaskQuestionForm() {
this.taskQuestions.map((v, i) => {
if (v.Type === 'group' && v.Childrens.length === 0 && v.Type !== 'table') return
if (!v.IsPage && v.Type !== 'group' && v.Type !== 'summary' && v.Type !== 'table' && v.Type !== 'number') {
if (v.Type === 'group' && v.Childrens.length === 0 && v.Type !== 'table' && v.Type !== 'basicTable') return
if (!v.IsPage && v.Type !== 'group' && v.Type !== 'summary' && v.Type !== 'table' && v.Type !== 'basicTable' && v.Type !== 'number') {
this.$set(this.questionForm, v.QuestionId, v.Answers[this.visitTaskId])
}
if (v.Type === 'table') {
if (v.Type === 'table' || v.Type === 'basicTable') {
var tableAnswers = this.getTableAnswers(v.QuestionId, v.Childrens, i)
this.$set(this.questionForm, v.QuestionId, tableAnswers)
// this.$set(v, 'xfIndex', i)
@@ -379,10 +412,10 @@ export default {
},
setChild(obj) {
obj.forEach((i, index) => {
if (i.Type !== 'group' && i.Type !== 'summary' && i.Id && i.Type !== 'table') {
if (i.Type !== 'group' && i.Type !== 'summary' && i.Id && i.Type !== 'table' && i.Type !== 'basicTable') {
this.$set(this.questionForm, i.QuestionId, i.Answers[this.visitTaskId])
}
if (i.Type === 'table') {
if (i.Type === 'table' || i.Type === 'basicTable') {
var tableAnswers = this.getTableAnswers(i.QuestionId, i.Childrens, index)
this.$set(this.questionForm, i.QuestionId, tableAnswers)
}
@@ -397,7 +430,7 @@ export default {
}
this.$set(this.questionForm, i.QuestionId, val)
}
if (i.Childrens && i.Childrens.length > 0 && i.Type !== 'table') {
if (i.Childrens && i.Childrens.length > 0 && i.Type !== 'table' && i.Type !== 'basicTable') {
this.setChild(i.Childrens)
}
})
@@ -450,19 +483,18 @@ export default {
num = this.questionForm[o.QuestionId].length === 0 ? 0 : num / this.questionForm[o.QuestionId].length
break
case 8:
var arr = []
const arr = []
this.questionForm[o.QuestionId].forEach(q => {
arr.push(q[o.TableQuestionId])
})
num = arr.length === 0 ? 0 : Math.max(...arr)
break
case 9:
// eslint-disable-next-line no-redeclare
var arr = []
const arr1 = []
this.questionForm[o.QuestionId].forEach(q => {
arr.push(q[o.TableQuestionId])
arr1.push(q[o.TableQuestionId])
})
num = arr.length === 0 ? 0 : Math.min(...arr)
num = arr1.length === 0 ? 0 : Math.min(...arr1)
break
}
} else {
@@ -489,11 +521,10 @@ export default {
} catch (e) {
console.log(e)
}
var digitPlaces = parseInt(localStorage.getItem('digitPlaces'))
if (rules.ValueType === 2) {
num = num * 100
}
return isNaN(num) ? '' : isFinite(num) ? num.toFixed(digitPlaces) : '∞'
return isNaN(num) ? '' : isFinite(num) ? num.toFixed(this.digitPlaces) : '∞'
},
getReportInfo() {
this.loading = true
@@ -522,8 +553,6 @@ export default {
questions.forEach((item) => {
const obj = item
this.$set(obj, 'Answers', {})
var digitPlaces = parseInt(localStorage.getItem('digitPlaces')) || 0
item.Answer.forEach(i => {
if (item.DictionaryCode) {
this.$set(obj.Answers, i.VisitTaskId, i.Answer ? parseInt(i.Answer) : null)
@@ -536,7 +565,7 @@ export default {
} else if (item.ValueType === 3) {
val = i.Answer
} else {
val = isNaN(parseFloat(i.Answer)) ? i.Answer : parseFloat(i.Answer).toFixed(digitPlaces)
val = isNaN(parseFloat(i.Answer)) ? i.Answer : parseFloat(i.Answer).toFixed(this.digitPlaces)
}
this.$set(obj.Answers, i.VisitTaskId, val)
} else {
@@ -590,15 +619,16 @@ export default {
}
},
//
signConfirm(signInfo) {
async signConfirm(signInfo) {
this.loading = true
var params = {
data: {
visitTaskId: this.visitTaskId
},
signInfo: signInfo
}
submitDicomVisitTask(params).then(res => {
try {
var params = {
data: {
visitTaskId: this.visitTaskId
},
signInfo: signInfo
}
const res = await submitDicomVisitTask(params)
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
if (this.$refs['signForm']) {
@@ -606,43 +636,39 @@ export default {
}
this.signVisible = false
// window.location.reload()
// window.opener.postMessage('refreshTaskList', window.location)
//
this.readingTaskState = 2
this.taskInfo.ReadingTaskState = 2
localStorage.setItem('taskInfo', JSON.stringify(this.taskInfo))
store.dispatch('noneDicomReview/setCurrentTaskState', 2)
// DicomEvent.$emit('setReadingState', 2)
window.opener.postMessage('refreshTaskList', window.location)
this.$confirm(this.$t('trials:oncologyReview:title:msg2'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
// var token = getToken()
// var subjectCode = this.$router.currentRoute.query.subjectCode
// var subjectId = this.$router.currentRoute.query.subjectId
// var trialId = this.$router.currentRoute.query.trialId
// this.$router.push({
// path: `/readingPage?subjectCode=${subjectCode}&subjectId=${subjectId}&trialId=${trialId}&TokenKey=${token}`
// })
// DicomEvent.$emit('getNextTask')
window.location.reload()
})
.catch(action => {
const res = await getAutoCutNextTask()
const isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()
} else {
// ''
this.$confirm(this.$t('trials:readingReport:message:msg4'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
window.location.reload()
})
.catch(action => {
// changeURLStatic('visitTaskId', this.visitTaskId)
})
}
}
this.loading = false
}).catch(() => {
} catch (e) {
console.log(e)
this.loading = false
if (this.$refs['signForm'] && this.$refs['signForm'].btnLoading) {
this.$refs['signForm'].btnLoading = false
}
})
}
},
previewDicoms(task) {
var token = getToken()
@@ -721,6 +747,13 @@ export default {
this.loading = false
console.log(e)
}
},
//
preview(path) {
this.currentPath = path
const arr = path.split('.')
this.currentType = arr[arr.length - 1]
this.previewVisible = true
}
}
}
@@ -787,6 +820,28 @@ export default {
height: 30px;
line-height: 40px;
}
::v-deep .el-dialog{
background: #1e1e1e;
border: 1px solid #ddd;
color: #ddd;
.el-dialog__title{
color:#fff;
}
.el-input .el-input__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
.el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
.el-form-item__label{
color: #dfdfdf
}
}
}
::v-deep .el-switch__label{
color:#fff;
}
::v-deep .el-switch__label.is-active{
color: #428bca;
@@ -22,15 +22,19 @@
v-if="!study.IsCriticalSequence"
class="dicom-desc"
>
<div>{{ study.CodeView }}</div>
<div>
<!-- <div v-if="taskInfo && taskInfo.IsShowStudyName">{{ study.StudyName }}</div> -->
<div style="text-overflow: ellipsis;overflow: hidden;">
<span :title="study.CodeView">{{ study.CodeView }}</span>
<span v-if="taskInfo && taskInfo.IsShowStudyName" :title="study.StudyName" style="margin-left: 5px;">{{ study.StudyName }}</span>
</div>
<div style="text-overflow: ellipsis;overflow: hidden;">
<span :title="study.BodyPart">{{ study.BodyPart }}</span>
<span style="margin-left: 5px;" :title="study.Modality">{{ study.Modality }}</span>
</div>
</div>
<div v-else>
<!-- 关键序列 -->
关键文件
<!-- 关键文件 -->
{{ $t('trials:noneDicom:title:keyFiles') }}
</div>
</template>
<div class="file-list-container">
@@ -44,7 +48,7 @@
:class="{'file-active': index === activeStudyIndex && i === activeFileIndex}"
class="file-wrapper"
>
<div class="file-image">
<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%;"
@@ -164,6 +168,9 @@ export default {
text-align: left;
color: #d0d0d0;
padding: 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ps {
@@ -912,10 +912,19 @@
</el-form>
</template>
<template slot="dialog-footer">
<el-button type="primary" @click="addOrUpdateTrialStudyName">
<el-button
type="primary"
@click="addOrUpdateTrialStudyName"
:loading="studyNameLoading"
>
{{ $t('common:button:confirm') }}
</el-button>
<el-button @click="addStudyName_model.visible = false">
<el-button
@click="
;(addStudyName_model.visible = false),
(addStudyNameForm.Name = null)
"
>
{{ $t('common:button:cancel') }}
</el-button>
</template>
@@ -933,6 +942,7 @@ import {
ConfigTrialBasicInfoConfirm,
getTrialBodyPartList,
addOrUpdateTrialBodyPart,
updateTrialStudyNameList,
} from '@/api/trials/setting'
import SignForm from '@/views/trials/components/newSignForm'
import ClinicalDataForm from './clinicalDataForm'
@@ -1094,6 +1104,7 @@ export default {
width: '500px',
appendToBody: true,
},
studyNameLoading: false,
addBodyPartForm: {
bodyPartStr: null,
Id: null,
@@ -1328,13 +1339,51 @@ export default {
if (!validate) return
this.addStudyNameForm.EnName = this.addStudyNameForm.Name
let data = { ...this.addStudyNameForm }
this.trialStudyNameList.push(data)
let arr = []
this.trialStudyNameList.forEach((item) => {
let obj = {
EnName: item.EnName,
Name: item.EnName,
IsChoose: false,
}
arr.push(obj)
})
arr.push(data)
this.studyNameLoading = true
let res = await this.updateStudyNameList(arr)
this.studyNameLoading = false
if (!res)
return this.$message.warning(
this.$t('trials:seeting:message:addStudyNameFail')
)
this.$message.success(
this.$t('trials:seeting:message:addStudyNameSucccess')
)
this.addStudyNameForm = {
EnName: null,
Name: null,
IsChoose: false,
}
this.addStudyName_model.visible = false
// this.getStudyNameList()
this.trialStudyNameList.push(data)
},
async updateStudyNameList(StudyNameList) {
try {
let data = {
TrialId: this.$route.query.trialId,
StudyNameList,
}
let res = await updateTrialStudyNameList(data)
if (res.IsSuccess) {
return true
}
return false
} catch (err) {
console.log(err)
return false
}
},
//
handleEditBodyPart(item) {
@@ -1344,11 +1393,33 @@ export default {
this.addBodyPart_model.title = this.$t('trials:setting:button:edit')
this.addBodyPart_model.visible = true
},
handleDelStudyName(item) {
async handleDelStudyName(item) {
let index = this.trialStudyNameList.findIndex(
(data) => item.Name === data.Name
)
let arr = []
this.trialStudyNameList.forEach((item, i) => {
if (index !== i) {
let obj = {
EnName: item.EnName,
Name: item.EnName,
IsChoose: false,
}
arr.push(obj)
}
})
this.listLoading = true
let res = await this.updateStudyNameList(arr)
this.listLoading = false
if (!res)
return this.$message.warning(
this.$t('trials:seeting:message:delStudyNameFail')
)
this.$message.success(
this.$t('trials:seeting:message:delStudyNameSucccess')
)
this.trialStudyNameList.splice(index, 1)
// this.getStudyNameList()
},
handleSetBodyPart() {
this.bodyPartListVisible = true
@@ -1835,7 +1906,9 @@ export default {
this.form.ClinicalDataSetNames.join(', ')
}
this.getClinicalDataList(true)
this.$message.success('删除成功!')
this.$message.success(
this.$t('common:message:deletedSuccessfully')
)
}
})
.catch(() => {
@@ -99,9 +99,9 @@
<template slot-scope="scope">
<span>{{
scope.row.size && scope.row.size > 0
? `${(scope.row.size / 1024 / 1024).toFixed(2)}MB`
? `${(scope.row.size / 1024 / 1024).toFixed(3)}MB`
: scope.row.Size && scope.row.Size > 0
? `${(scope.row.Size / 1024 / 1024).toFixed(2)}MB`
? `${(scope.row.Size / 1024 / 1024).toFixed(3)}MB`
: ''
}}</span>
</template>
@@ -324,7 +324,7 @@ export default {
},
fileSizeFormatter(size) {
if (!size) return
return (size / Math.pow(1024, 2)).toFixed(3) + 'M'
return (size / Math.pow(1024, 2)).toFixed(3) + 'MB'
},
handleReset() {
this.searchData = searchDataDefault()
@@ -164,7 +164,7 @@
<template slot-scope="scope">
<span>{{
scope.row.FileSize && scope.row.FileSize > 0
? `${(scope.row.FileSize / 1024 / 1024).toFixed(2)}MB`
? `${(scope.row.FileSize / 1024 / 1024).toFixed(3)}MB`
: ''
}}</span>
</template>
@@ -278,6 +278,7 @@
>
<template slot-scope="scope">
<el-switch
:disabled="!scope.row.TrialFileRecord"
v-if="isManage && !viewStatus && hasEdit"
v-model="scope.row.IsAuthorizedView"
@change="(val) => auth(false, scope.row, val)"
@@ -747,7 +748,7 @@ export default {
},
fileSizeFormatter(size) {
if (!size) return
return (size / Math.pow(1024, 2)).toFixed(3) + 'M'
return (size / Math.pow(1024, 2)).toFixed(3) + 'MB'
},
openFile(isFolder = false) {
this.selectData = {}
@@ -769,7 +770,7 @@ export default {
},
immediate: true,
},
rowData: {
'rowData.IsEnable': {
handler() {
this.DATA = Object.assign({}, this.rowData)
},
@@ -780,7 +781,6 @@ export default {
let typeArr = ['', 'Report', 'Doc', 'Record', 'Reviewer', 'Template']
let types = typeArr[this.ArchiveTypeEnum]
this.uploadPath = `/${this.$route.query.trialId}/Document/${types}`
console.log(this.uploadPath, this.ArchiveTypeEnum)
},
computed: {
isEN() {
@@ -273,6 +273,12 @@ export default {
this.loading = true
let res = await setAuthorizedView(data)
this.loading = false
if (res.IsSuccess) {
this.$emit('update:rowData', {})
this.$nextTick(() => {
this.$emit('update:rowData', item)
})
}
} catch (err) {
this.loading = false
item.IsEnable = !item.IsEnable
@@ -361,11 +361,11 @@ export default {
var file = await this.fileToBlob(param.file)
let typeArr = ['', 'Report', 'Doc', 'Record', 'Reviewer', 'Template']
let types = typeArr[this.ArchiveTypeEnum]
let fileNameNoType = param.file.name
.substring(0, param.file.name.lastIndexOf('.'))
.toLocaleLowerCase()
// let fileNameNoType = param.file.name
// .substring(0, param.file.name.lastIndexOf('.'))
// .toLocaleLowerCase()
const res = await this.OSSclient.put(
`/${trialId}/Document/${types}/${fileNameNoType}${extendName}`,
`/${trialId}/Document/${types}/${this.$guid()}${extendName}`,
file
)
this.form[`${key}FileRecord`] = {
@@ -766,7 +766,7 @@ export default {
},
immediate: true,
},
rowData: {
'rowData.IsEnable': {
handler() {
this.DATA = Object.assign({}, this.rowData)
},
@@ -690,7 +690,7 @@ export default {
},
fileSizeFormatter(size) {
if (!size) return
return (size / Math.pow(1024, 2)).toFixed(3) + 'M'
return (size / Math.pow(1024, 2)).toFixed(3) + 'MB'
},
openFile(isFolder = false) {
this.selectData = {}
@@ -722,7 +722,7 @@ export default {
},
immediate: true,
},
rowData: {
'rowData.IsEnable': {
handler() {
this.DATA = Object.assign({}, this.rowData)
},
@@ -733,7 +733,6 @@ export default {
let typeArr = ['', 'Report', 'Doc', 'Record', 'Reviewer', 'Template']
let types = typeArr[this.ArchiveTypeEnum]
this.uploadPath = `/${this.$route.query.trialId}/Document/${types}`
console.log(this.uploadPath, this.ArchiveTypeEnum)
},
computed: {
isEN() {
@@ -1,101 +1,45 @@
<template>
<base-model :config="config">
<div slot="dialog-body">
<el-form
ref="trainRecordForm"
:model="form"
label-width="140px"
size="small"
:rules="rules"
>
<el-form ref="trainRecordForm" :model="form" label-width="140px" size="small" :rules="rules">
<div class="base-dialog-body">
<el-form-item
v-if="!config.upload"
:label="$t('trials:trialDocument:trainRecord:form:TrianingDate')"
prop="TrianingDate"
>
<el-date-picker
v-model="form.TrianingDate"
type="date"
placeholder=""
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
style="width: 100%"
>
<el-form-item v-if="!config.upload" :label="$t('trials:trialDocument:trainRecord:form:TrianingDate')"
prop="TrianingDate">
<el-date-picker v-model="form.TrianingDate" type="date" placeholder="" format="yyyy-MM-dd"
value-format="yyyy-MM-dd" style="width: 100%">
</el-date-picker>
</el-form-item>
<el-form-item
v-if="!config.upload"
:label="$t('trials:trialDocument:trainRecord:form:TrianingCount')"
prop="TrianingCount"
>
<el-form-item v-if="!config.upload" :label="$t('trials:trialDocument:trainRecord:form:TrianingCount')"
prop="TrianingCount">
<el-input v-model.number="form.TrianingCount" type="number" />
</el-form-item>
<el-form-item
v-if="!config.upload"
:label="$t('trials:trialDocument:trainRecord:form:TrianingState')"
prop="TrianingState"
>
<el-input v-model="form.TrianingState" type="number" />
<el-form-item v-if="!config.upload" :label="$t('trials:trialDocument:trainRecord:form:TrianingState')"
prop="TrianingState">
<el-input v-model="form.TrianingState" />
</el-form-item>
<el-form-item
v-if="!config.upload"
:label="$t('trials:trialDocument:trainRecord:form:Note')"
prop="Note"
>
<el-form-item v-if="!config.upload" :label="$t('trials:trialDocument:trainRecord:form:Note')" prop="Note">
<el-input v-model="form.Note" />
</el-form-item>
<el-form-item
v-if="!config.upload || config.upload === 'Trial'"
:label="$t('trials:trialDocument:trainRecord:form:TrialFileRecord')"
prop="TrialFileRecord"
>
<el-upload
class="upload-demo"
action
:before-upload="(param) => beforeUpload(param, 'Trial', '.pdf')"
:http-request="(param) => handleUploadFile(param, 'Trial')"
:on-remove="() => handleRemoveFile('Trial')"
:limit="1"
accept=".pdf"
:file-list="TrialFile"
>
<el-button
size="small"
type="primary"
:disabled="
!!form.TrialFileRecord && !!form.TrialFileRecord.FilePath
"
>{{ $t('common:button:upload') }}
<el-form-item v-if="!config.upload || config.upload === 'Trial'"
:label="$t('trials:trialDocument:trainRecord:form:TrialFileRecord')" prop="TrialFileRecord">
<el-upload class="upload-demo" action :before-upload="(param) => beforeUpload(param, 'Trial', '.pdf')"
:http-request="(param) => handleUploadFile(param, 'Trial')" :on-remove="() => handleRemoveFile('Trial')"
:limit="1" accept=".pdf" :file-list="TrialFile">
<el-button size="small" type="primary" :disabled="!!form.TrialFileRecord && !!form.TrialFileRecord.FilePath
">{{ $t('common:button:upload') }}
</el-button>
<span slot="tip" class="el-upload__tip">
{{ $t('trials:trialDocument:trainRecord:rule:mustPDF') }}
</span>
</el-upload>
</el-form-item>
<el-form-item
v-if="!config.upload || config.upload === 'History'"
:label="
$t('trials:trialDocument:trainRecord:form:historyFileRecord')
"
>
<el-upload
class="upload-demo"
action
:before-upload="(param) => beforeUpload(param, 'History', '.zip')"
<el-form-item v-if="!config.upload || config.upload === 'History'" :label="$t('trials:trialDocument:trainRecord:form:historyFileRecord')
">
<el-upload class="upload-demo" action :before-upload="(param) => beforeUpload(param, 'History', '.zip')"
:http-request="(param) => handleUploadFile(param, 'History')"
:on-remove="() => handleRemoveFile('History')"
:limit="1"
accept=".zip"
:file-list="HistoryFile"
>
<el-button
size="small"
type="primary"
:disabled="
!!form.HistoryFileRecord && !!form.HistoryFileRecord.FilePath
"
>{{ $t('common:button:upload') }}
:on-remove="() => handleRemoveFile('History')" :limit="1" accept=".zip" :file-list="HistoryFile">
<el-button size="small" type="primary" :disabled="!!form.HistoryFileRecord && !!form.HistoryFileRecord.FilePath
">{{ $t('common:button:upload') }}
</el-button>
<span slot="tip" class="el-upload__tip">
{{ $t('trials:trialDocument:trainRecord:rule:mustZIP') }}
@@ -115,7 +59,7 @@
</div>
</base-model>
</template>
<script>
<script>
import baseModel from '@/components/BaseModel'
import { addOrUpdateTrialTrianingRecord } from '@/api/dictionary'
export default {
@@ -184,7 +128,7 @@ export default {
callback(
new Error(
this.$t(
'trials:trialDocument:trainRecord:ruleMessage:TrianingCountPatternS'
'trials:trialDocument:trainRecord:ruleMessage:TrianingCountPattern'
)
)
)
@@ -194,7 +138,7 @@ export default {
},
trigger: 'blur',
},
],
]
},
loading: false,
TrialFile: [],
@@ -279,11 +223,11 @@ export default {
var file = await this.fileToBlob(param.file)
let typeArr = ['', 'Report', 'Doc', 'Record', 'Reviewer', 'Template']
let types = typeArr[this.ArchiveTypeEnum]
let fileNameNoType = param.file.name
.substring(0, param.file.name.lastIndexOf('.'))
.toLocaleLowerCase()
// let fileNameNoType = param.file.name
// .substring(0, param.file.name.lastIndexOf('.'))
// .toLocaleLowerCase()
const res = await this.OSSclient.put(
`/${trialId}/Document/${types}/${fileNameNoType}${extendName}`,
`/${trialId}/Document/${types}/${this.$guid()}${extendName}`,
file
)
this.form[`${key}FileRecord`] = {
@@ -376,7 +376,7 @@
v-if="hasDownLoad && isManage && !viewStatus"
icon="el-icon-download"
:title="
$t('trials:trialDocument:trainRecord:button:downLoadFileFileFile')
$t('trials:trialDocument:trainRecord:button:downLoadFile')
"
circle
:disabled="
@@ -843,7 +843,7 @@ export default {
},
fileSizeFormatter(size) {
if (!size) return
return (size / Math.pow(1024, 2)).toFixed(3) + 'M'
return (size / Math.pow(1024, 2)).toFixed(3) + 'MB'
},
openFile(isFolder = false) {
this.selectData = {}
@@ -865,7 +865,7 @@ export default {
},
immediate: true,
},
rowData: {
'rowData.IsEnable': {
handler() {
this.DATA = Object.assign({}, this.rowData)
},
@@ -81,7 +81,7 @@
<template slot-scope="scope">
<span>{{
scope.row.size && scope.row.size > 0
? `${(scope.row.size / 1024 / 1024).toFixed(2)}MB`
? `${(scope.row.size / 1024 / 1024).toFixed(3)}MB`
: ''
}}</span>
</template>
@@ -335,10 +335,10 @@ export default {
let fileType = file.name
.substring(file.name.lastIndexOf('.'))
.toLocaleLowerCase()
let fileName = file.name
.substring(0, file.name.lastIndexOf('.'))
.toLocaleLowerCase()
let path = `${this.uploadPath}/${fileName}${fileType}`
// let fileName = file.name
// .substring(0, file.name.lastIndexOf('.'))
// .toLocaleLowerCase()
let path = `${this.uploadPath}/${this.$guid()}${fileType}`
file.curPath = path
const fileData = await this.fileToBlob(file.file)
let res = await this.fileToOss(path, fileData, file)
@@ -0,0 +1,253 @@
<template>
<base-model :config="config">
<div slot="dialog-body">
<el-form
ref="certificateForm"
:model="form"
label-width="140px"
size="small"
:rules="rules"
>
<div class="base-dialog-body">
<el-form-item
:label="$t('trials:trialDocument:certificate:form:fileType')"
prop="Type"
>
<el-select
v-model="form.Type"
placeholder=""
clearable
:disabled="config.status === 'edit' && !!this.data.Type"
>
<el-option
v-for="item in $d.CertificateFileType"
:key="item.id"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item
:label="$t('trials:trialDocument:certificate:form:DoctorId')"
prop="DoctorId"
>
<el-select
v-model="form.DoctorId"
placeholder=""
clearable
:disabled="config.status === 'edit' && !!this.data.DoctorId"
>
<el-option
v-for="item in doctorList"
:key="item.Id"
:label="item.FullName"
:value="item.Id"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item
:label="$t('trials:trialDocument:certificate:form:pdfFileRecord')"
prop="Path"
>
<el-upload
class="upload-demo"
action
:before-upload="(param) => beforeUpload(param, '.pdf')"
:http-request="handleUploadFile"
:on-remove="handleRemoveFile"
:limit="1"
accept=".pdf"
:file-list="PDFFile"
>
<el-button size="small" type="primary" :disabled="!!form.Path"
>{{ $t('common:button:upload') }}
</el-button>
<span slot="tip" class="el-upload__tip">
{{ $t('trials:trialDocument:certificate:rule:mustPDF') }}
</span>
</el-upload>
</el-form-item>
</div>
</el-form>
</div>
<div slot="dialog-footer">
<el-button size="small" @click="canel">
{{ $t('trials:trialDocument:certificate:button:canel') }}
</el-button>
<el-button size="small" type="primary" :loading="loading" @click="save">
{{ $t('trials:trialDocument:certificate:button:save') }}
</el-button>
</div>
</base-model>
</template>
<script>
import baseModel from '@/components/BaseModel'
import { saveTrialAttachments, updateTrialAttachments } from '@/api/dictionary'
export default {
props: {
config: {
required: true,
default: () => {
return {}
},
},
data: {
required: true,
default: () => {
return {}
},
},
ArchiveTypeEnum: {
type: Number,
default: 0,
},
doctorList: {
type: Array,
default: () => {
return []
},
},
},
components: {
'base-model': baseModel,
},
data() {
return {
form: {
Type: null,
DoctorId: null,
FileName: null,
Path: null,
FullPath: null,
},
rules: {
Type: [
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: ['blur', 'change'],
},
],
DoctorId: [
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: ['blur', 'change'],
},
],
Path: [
{
required: true,
validator: (rule, value, callback) => {
if (!this.form.Path) {
callback(
new Error(
this.$t('trials:trialDocument:certificate:msg:noFile')
)
)
} else {
callback()
}
},
// message: this.$t('common:ruleMessage:specify'),
trigger: ['blur', 'change'],
},
],
},
loading: false,
PDFFile: [],
}
},
created() {
if (this.config.status === 'edit') {
Object.keys(this.form).forEach((key) => {
this.form[key] = this.data[key]
})
if (this.form.Path) {
this.PDFFile = []
this.PDFFile.push({
name: this.form.FileName,
url: this.form.Path,
})
}
}
},
methods: {
async save() {
try {
let validate = await this.$refs.certificateForm.validate()
if (!validate) return false
this.loading = true
this.form.TrialId = this.$route.query.trialId
let res = null
if (this.config.status === 'edit') {
this.form.Id = this.data.Id
res = await updateTrialAttachments(this.form)
} else {
res = await saveTrialAttachments([this.form])
}
if (res.IsSuccess) {
this.$emit('close')
this.$emit('getList')
}
} catch (err) {
console.log(err)
this.loading = false
}
},
canel() {
this.$emit('close')
},
handleRemoveFile() {
this.form.Path = null
this.form.FileName = null
this.form.FullPath = null
this.PDFFile = []
},
beforeUpload(param, accept) {
if (this.PDFFile && this.PDFFile[0]) {
this.$alert(
this.$t('trials:trialDocument:certificate:msg:hasFile')
).catch()
return
}
let extendName = param.name
.substring(param.name.lastIndexOf('.'))
.toLocaleLowerCase()
if (accept !== extendName) {
this.$alert(
this.$t('trials:trialDocument:certificate:msg:typeErr')
).catch()
return
}
},
async handleUploadFile(param) {
let trialId = this.$route.query.trialId
let extendName = param.file.name
.substring(param.file.name.lastIndexOf('.'))
.toLocaleLowerCase()
if (!trialId)
return this.$alert(
this.$t('trials:trialDocument:certificate:msg:noTrialId')
).catch()
this.loading = true
var file = await this.fileToBlob(param.file)
let typeArr = ['', 'Report', 'Doc', 'Record', 'Reviewer', 'Template']
let types = typeArr[this.ArchiveTypeEnum]
// let fileNameNoType = param.file.name
// .substring(0, param.file.name.lastIndexOf('.'))
// .toLocaleLowerCase()
const res = await this.OSSclient.put(
`/${trialId}/Document/${types}/${this.$guid()}${extendName}`,
file
)
this.form.Path = this.$getObjectName(res.url)
this.form.FullPath = this.$getObjectName(res.url)
this.form.FileName = param.file.name
this.loading = false
},
},
}
</script>
@@ -0,0 +1,720 @@
<template>
<box-content>
<div class="title">
{{ TITLE }}
</div>
<!-- 搜索框 -->
<div class="search" style="position: relative">
<el-form :inline="true" class="base-search-form">
<!--文件类型-->
<el-form-item
:label="$t('trials:trialDocument:certificate:search:fileType')"
>
<el-select
v-model="searchData.Type"
style="width: 100px"
placeholder=""
clearable
>
<el-option
v-for="item in $d.CertificateFileType"
:key="item.id"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
<!--文件名称-->
<el-form-item
:label="$t('trials:trialDocument:certificate:search:name')"
>
<el-input
v-model="searchData.FileName"
style="width: 100px"
clearable
/>
</el-form-item>
<!--阅片人-->
<el-form-item
:label="$t('trials:trialDocument:certificate:search:viewer')"
>
<el-select
v-model="searchData.DoctorId"
style="width: 100px"
placeholder=""
clearable
>
<el-option
v-for="item in doctorList"
:key="item.Id"
:label="item.FullName"
:value="item.Id"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item
:label="
$t('trials:trialDocument:certificate:search:isAuthorizedView')
"
v-if="isManage && !viewStatus"
>
<el-select
v-if="!viewStatus"
v-model="searchData.IsAuthorizedView"
style="width: 100px"
placeholder=""
clearable
>
<el-option
v-for="item in $d.YesOrNo"
:key="item.id"
:label="item.label"
:value="item.value"
>
</el-option>
</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-button
type="primary"
icon="el-icon-refresh-left"
@click="handleReset"
>
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
<el-form-item v-if="isManage && !viewStatus">
<el-button
type="primary"
:disabled="selectTable.length <= 0"
v-if="hasAccredit && isManage && !viewStatus"
@click.stop="auth"
>
{{ $t('trials:trialDocument:certificate:button:accredit') }}
</el-button>
<el-button
type="primary"
v-if="hasAdd && isManage && !viewStatus"
@click.stop="handleAdd"
>
{{ $t('trials:trialDocument:certificate:button:add') }}
</el-button>
<el-button
type="primary"
v-if="hasEdit && isManage && !viewStatus"
@click.stop="openFile(false)"
>
{{ $t('trials:trialDocument:certificate:button:uploadFile') }}
</el-button>
<el-button
type="primary"
v-if="hasEdit && isManage && !viewStatus"
@click.stop="openFile(true)"
>
{{ $t('trials:trialDocument:certificate:button:uploadFolder') }}
</el-button>
<el-button
type="primary"
icon="el-icon-bottom"
:disabled="selectTable.length <= 0"
v-if="hasDownLoad && isManage && !viewStatus"
@click.stop="downLoad"
>
{{ $t('trials:trialDocument:certificate:button:downLoadFile') }}
</el-button>
<el-button
type="primary"
:disabled="selectTable.length <= 0"
v-if="hasDel && isManage && !viewStatus"
@click.stop="handleDel"
>
{{ $t('trials:trialDocument:certificate:button:del') }}
</el-button>
</el-form-item>
</el-form>
</div>
<el-table
v-loading="loading"
v-adaptive="{ bottomOffset: 75 }"
:data="list"
stripe
height="100"
style="width: 100%"
@sort-change="handleSortByColumn"
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
width="55"
v-if="isManage && !viewStatus"
/>
<el-table-column type="index" v-else />
<!--文件类型-->
<el-table-column
prop="type"
:label="$t('trials:trialDocument:certificate:table:fileType')"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
<span>{{ $fd('CertificateFileType', scope.row.Type) }}</span>
</template>
</el-table-column>
<!--文件名称-->
<el-table-column
prop="FileName"
:label="$t('trials:trialDocument:certificate:table:FileName')"
show-overflow-tooltip
sortable="custom"
/>
<!--阅片人-->
<el-table-column
prop="UserName"
:label="$t('trials:trialDocument:certificate:table:viewer')"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
<span>{{ scope.row.UserName }} ({{ scope.row.FullName }})</span>
</template>
</el-table-column>
<el-table-column
prop="IsAuthorizedView"
:label="$t('trials:trialDocument:certificate:table:isAuthorizedView')"
show-overflow-tooltip
sortable="custom"
v-if="isManage && !viewStatus"
>
<template slot-scope="scope">
<el-switch
:disabled="!scope.row.DoctorId"
v-if="isManage && !viewStatus && hasEdit"
v-model="scope.row.IsAuthorizedView"
@change="(val) => auth(false, scope.row, val)"
:active-value="true"
:inactive-value="false"
:active-text="$fd('YesOrNo', true)"
:inactive-text="$fd('YesOrNo', false)"
>
</el-switch>
<span v-else>{{ $fd('YesOrNo', scope.row.IsAuthorizedView) }}</span>
</template>
</el-table-column>
<el-table-column
prop="UpdateTime"
:label="$t('trials:trialDocument:certificate:table:updateTime')"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="CreateTime"
:label="$t('trials:trialDocument:certificate:table:createTime')"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column :label="$t('common:action:action')" width="200">
<template slot-scope="scope">
<el-button
icon="el-icon-view"
:title="$t('common:button:view')"
circle
@click.stop="preview(scope.row)"
/>
<el-button
v-if="hasEdit && isManage && !viewStatus"
icon="el-icon-edit-outline"
:title="$t('common:button:edit')"
circle
@click.stop="handleEdit(scope.row)"
/>
<el-button
v-if="hasDownLoad && isManage && !viewStatus"
icon="el-icon-download"
:title="$t('trials:trialDocument:certificate:button:downLoadFile')"
circle
@click.stop="downLoad(scope.row, 'file')"
/>
<el-button
v-if="hasDel && isManage && !viewStatus"
icon="el-icon-delete"
:title="$t('trials:trialDocument:certificate:button:delete')"
circle
@click.stop="handleDel(scope.row, false)"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
class="page"
:total="total"
:page.sync="searchData.PageIndex"
:limit.sync="searchData.PageSize"
@pagination="getList"
/>
<upload-files
:config="config"
:faccept="faccept"
:uploadPath="uploadPath"
:limitLength="limitLength"
v-if="config.visible"
@close="close"
@uplaodFile="uplaodFile"
/>
<certificate-form
:ArchiveTypeEnum="ArchiveTypeEnum"
:config="update_config"
:data="selectData"
:doctorList="doctorList"
v-if="update_config.visible"
@close="updateClose"
@getList="getList"
/>
</box-content>
</template>
<script>
import {
getTrialAttachments,
setAuthorizedViewC,
saveTrialAttachments,
getTrialDoctorListC,
deleteAttachment,
} from '@/api/dictionary'
import { downLoadFile } from '@/utils/stream.js'
import { deepClone } from '@/utils/index.js'
import Pagination from '@/components/Pagination'
import BoxContent from '@/components/BoxContent'
import uploadFiles from '../../uploadFiles.vue'
import certificateForm from './form.vue'
const searchDataDefault = () => {
return {
IsAuthorizedView: null,
FileName: null,
DoctorId: null,
Type: null,
PageIndex: 1,
PageSize: 20,
Asc: false,
SortField: 'UserName',
}
}
export default {
name: 'certificate',
components: { BoxContent, Pagination, uploadFiles, certificateForm },
props: {
viewStatus: {
type: Boolean,
default: true,
},
ArchiveTypeEnum: {
type: Number,
default: 0,
},
Id: {
type: String,
default: '',
},
rowData: {
type: Object,
default: () => {
return {}
},
},
},
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
selectTable: [],
config: {
visible: false,
showClose: true,
width: '800px',
title: '',
appendToBody: false,
isFolder: false,
},
selectData: {},
uploadPath: null,
faccept: ['.pdf'],
limitLength: 0,
update_config: {
visible: false,
showClose: true,
width: '600px',
title: '',
appendToBody: false,
status: 'add',
upload: null,
},
DATA: {},
doctorList: [],
}
},
methods: {
//
async uplaodFile(list) {
console.log(list, 'list')
try {
if (!list || list.length <= 0) return false
let arr = []
list.forEach((item) => {
let obj = {
TrialId: this.$route.query.trialId,
FileName: item.FileName,
Path: item.FilePath,
FullPath: item.FilePath,
type: '',
DoctorId: null,
}
arr.push(obj)
})
this.loading = true
let res = await saveTrialAttachments(arr)
this.loading = false
if (res.IsSuccess) {
this.getList()
}
} catch (err) {
this.loading = false
console.log(err)
}
},
//
handleAdd() {
this.selectData = {}
this.update_config.title = `${this.$t(
'trials:trialDocument:certificate:form:title:add'
)}
-
${this.isEN ? this.rowData.Name : this.rowData.NameCN}`
this.update_config.status = 'add'
this.update_config.upload = null
this.update_config.visible = true
},
//
handleEdit(row) {
this.selectData = deepClone(row)
this.update_config.title = `${this.$t(
'trials:trialDocument:certificate:form:title:edit'
)}
-
${this.isEN ? this.rowData.Name : this.rowData.NameCN}`
this.update_config.status = 'edit'
this.update_config.upload = null
this.update_config.visible = true
},
//
async downLoad(row, type = 'zip') {
try {
if (type === 'file') {
return await downLoadFile(
this.OSSclientConfig.basePath + row.Path,
row.FileName
)
}
let { files, name } = this.formatDownloadFile()
let res = await downLoadFile(files, name, type)
// if (res && this.downloadId) {
// this.downloadImageSuccess()
// }
} catch (err) {
console.log(err)
}
},
//
formatDownloadFile() {
let files = [],
name = `${this.TITLE}_${new Date().getTime()}.zip`
this.selectTable.forEach((item) => {
let obj = {
name: `${item.UserName} (${item.LastName}_${
item.FirstName
})/${this.$fd(
'CertificateFileType',
item.Type
)}_${item.FileName.substring(
0,
item.FileName.lastIndexOf('.')
)}__${new Date().getTime()}${item.FileName.substring(
item.FileName.lastIndexOf('.')
).toLocaleLowerCase()}`,
url: this.OSSclientConfig.basePath + item.Path,
}
files.push(obj)
})
return { files, name }
},
//
async auth(isArray = true, row, IsAuthorizedView) {
try {
let data = {}
if (isArray) {
let flag = this.selectTable.some((item) => !item.DoctorId)
if (flag)
return this.$message.warning(
this.$t('trials:trialDocument:certificate:message:hsaNoDoctorId')
)
data = {
Ids: this.selectTable.map((item) => item.Id),
IsAuthorizedView: true,
}
} else {
data = {
Ids: [row.Id],
IsAuthorizedView,
}
}
this.loading = true
let res = await setAuthorizedViewC(data)
this.loading = false
if (res.IsSuccess) {
this.$t('trials:trialDocument:certificate:message:authSuccessfully')
if (isArray) {
this.getList()
}
}
} catch (err) {
this.loading = false
if (!isArray) {
row.IsAuthorizedView = !row.IsAuthorizedView
}
console.log(err)
}
},
//
handleDel(row, isArray = true) {
this.$confirm(
this.$t('trials:trialDocument:certificate:message:deleteMessage'),
{
type: 'warning',
distinguishCancelAndClose: true,
}
)
.then(() => {
this.loading = true
let data = {}
if (isArray) {
data.Ids = this.selectTable.map((item) => item.Id)
} else {
data.Ids = [row.Id]
}
deleteAttachment(data)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
this.getList()
this.$message.success(
this.$t('common:message:deletedSuccessfully')
)
}
})
.catch((err) => {
this.loading = false
console.log(err)
})
})
.catch((err) => {
this.loading = false
console.log(err)
})
},
//
async getTrialDoctorList() {
try {
let data = {
TrialId: this.$route.query.trialId,
}
let res = await getTrialDoctorListC(data)
if (res.IsSuccess) {
this.doctorList = res.Result
}
} catch (err) {
console.log(err)
}
},
async getList() {
try {
if (!this.Id) return false
// this.searchData.TrialFileTypeId = this.Id
this.searchData.TrialId = this.$route.query.trialId
if (this.viewStatus) {
this.searchData.IsAuthorizedView = true
}
this.loading = true
let res = await getTrialAttachments(this.searchData)
this.loading = false
if (res.IsSuccess) {
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
}
} catch (err) {
this.loading = false
console.log(err)
}
},
//
handleSearch() {
this.searchData.PageIndex = 1
this.getList()
},
//
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
//
handleSortByColumn(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()
},
handleSelectionChange(val) {
this.selectTable = val
},
//
preview(row) {
if (!row.Path) return false
this.$preview({
path: row.FilePath || row.fullPath || row.Path,
type: 'pdf',
title: row.FileName,
})
},
close() {
this.config.visible = false
// this.selectData = {}
// this.selectKey = null
this.faccept = ['.pdf']
this.limitLength = 0
},
updateClose() {
this.update_config.visible = false
},
fileSizeFormatter(size) {
if (!size) return
return (size / Math.pow(1024, 2)).toFixed(3) + 'MB'
},
openFile(isFolder = false) {
this.selectData = {}
this.faccept = ['.pdf']
this.limitLength = 0
this.config.title = this.$t(
'trials:trialDocument:certificate:form:title:uploadFile'
)
this.config.visible = true
this.config.isFolder = isFolder
},
},
watch: {
Id: {
handler() {
this.getList()
},
immediate: true,
},
rowData: {
handler() {
this.DATA = Object.assign({}, this.rowData)
},
immediate: true,
},
},
created() {
let typeArr = ['', 'Report', 'Doc', 'Record', 'Reviewer', 'Template']
let types = typeArr[this.ArchiveTypeEnum]
this.uploadPath = `/${this.$route.query.trialId}/Document/${types}`
this.getTrialDoctorList()
},
computed: {
isEN() {
return this.$i18n.locale !== 'zh'
},
isInspect() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:inspect',
])
},
isManage() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:manage',
])
},
hasAdd() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:add',
])
},
hasEdit() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:edit',
])
},
hasDel() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:del',
])
},
hasDownLoad() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:downLoad',
])
},
hasAccredit() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:accredit',
])
},
TITLE() {
return `${this.$fd('ArchiveType', this.ArchiveTypeEnum)}${
this.isEN ? this.rowData.Name : this.rowData.NameCN
}`
},
},
}
</script>
<style lang="scss" scoped>
.title {
line-height: 40px;
font-weight: bold;
margin-bottom: 10px;
}
.fileName {
display: inline-block;
max-width: calc(100% - 60px);
white-space: nowrap; /* 文本不换行 */
overflow: hidden; /* 超出部分隐藏 */
text-overflow: ellipsis;
}
.fileBtnBox {
display: inline-block;
width: 50px;
i {
cursor: pointer;
color: #409eff;
margin-right: 3px;
&:last-child {
margin: 0;
}
}
}
</style>
@@ -7,123 +7,67 @@
<div class="search" style="position: relative">
<el-form :inline="true" class="base-search-form">
<!--专业-->
<el-form-item
:label="$t('trials:trialDocument:curriculumVitae:search:Speciality')"
>
<el-select
v-model="searchData.SpecialityId"
style="width: 200px"
placeholder=""
clearable
>
<el-option
v-for="item in $d.Department"
:key="item.id"
:label="item.label"
:value="item.value"
>
<el-form-item :label="$t('trials:trialDocument:curriculumVitae:search:Speciality')">
<el-select v-model="searchData.SpecialityId" style="width: 200px" placeholder="" clearable>
<el-option v-for="item in $d.Department" :key="item.id" :label="item.label" :value="item.id">
</el-option>
</el-select>
</el-form-item>
<!--亚专业-->
<el-form-item
:label="
$t('trials:trialDocument:curriculumVitae:search:Subspeciality')
"
>
<el-select
v-model="searchData.SubspecialityId"
style="width: 200px"
placeholder=""
clearable
>
<el-option
v-for="item in $d.Subspeciality"
:key="item.id"
:label="item.label"
:value="item.value"
>
<el-form-item :label="$t('trials:trialDocument:curriculumVitae:search:Subspeciality')
">
<el-select v-model="searchData.SubspecialityId" style="width: 200px" placeholder="" clearable>
<el-option v-for="item in $d.Subspeciality" :key="item.id" :label="item.label" :value="item.id">
</el-option>
</el-select>
</el-form-item>
<!--机构-->
<el-form-item
:label="
$t('trials:trialDocument:curriculumVitae:search:HospitalName')
"
>
<el-select
v-model="searchData.HospitalId"
style="width: 200px"
placeholder=""
clearable
>
<el-option
v-for="(item, index) in hospitalList"
:key="index"
:label="isEN ? item.HospitalName : item.HospitalNameCN"
:value="item.Id"
/>
<el-form-item :label="$t('trials:trialDocument:curriculumVitae:search:HospitalName')
">
<el-select v-model="searchData.HospitalId" style="width: 200px" placeholder="" clearable>
<el-option v-for="(item, index) in hospitalList" :key="index"
:label="isEN ? item.HospitalName : item.HospitalNameCN" :value="item.Id" />
</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-button
type="primary"
icon="el-icon-refresh-left"
@click="handleReset"
>
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
</el-form>
</div>
<el-table
v-loading="loading"
v-adaptive="{ bottomOffset: 75 }"
:data="list"
stripe
height="100"
style="width: 100%"
>
<el-table v-loading="loading" v-adaptive="{ bottomOffset: 75 }" :data="list" stripe height="100"
style="width: 100%">
<el-table-column type="index" />
<!--姓名-->
<el-table-column
prop="BlindName"
:label="$t('trials:trialDocument:curriculumVitae:table:name')"
show-overflow-tooltip
sortable="custom"
>
<el-table-column prop="BlindName" :label="$t('trials:trialDocument:curriculumVitae:table:name')"
show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
<span>{{ isEN ? scope.row.BlindName : scope.row.BlindNameCN }}</span>
</template>
</el-table-column>
<el-table-column
prop="Speciality"
:label="$t('trials:trialDocument:curriculumVitae:table:Speciality')"
show-overflow-tooltip
sortable="custom"
>
<el-table-column prop="Code" :label="$t('trials:trialDocument:curriculumVitae:table:UserName')"
show-overflow-tooltip sortable="custom" />
<el-table-column prop="Speciality" :label="$t('trials:trialDocument:curriculumVitae:table:Speciality')"
show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
<span>
{{
scope.row.SpecialityId === otherId
? scope.row.SpecialityOther
: isEN
? scope.row.Speciality
: scope.row.SpecialityCN
? scope.row.Speciality
: scope.row.SpecialityCN
}}
</span>
</template>
</el-table-column>
<el-table-column
prop="Subspeciality"
:label="$t('trials:trialDocument:curriculumVitae:table:Subspeciality')"
show-overflow-tooltip
>
<el-table-column prop="Subspeciality" :label="$t('trials:trialDocument:curriculumVitae:table:Subspeciality')"
show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="isEN">
{{
@@ -141,62 +85,41 @@
</span>
</template>
</el-table-column>
<el-table-column
prop="HospitalName"
:label="$t('trials:trialDocument:curriculumVitae:table:HospitalName')"
show-overflow-tooltip
sortable="custom"
>
<el-table-column prop="HospitalName" :label="$t('trials:trialDocument:curriculumVitae:table:HospitalName')"
show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
<span>
{{ isEN ? scope.row.HospitalName : scope.row.HospitalNameCN }}
</span>
</template>
</el-table-column>
<el-table-column
prop="SubmmitTime"
:label="$t('trials:trialDocument:curriculumVitae:table:submmitTime')"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column prop="OptTimeStr" :label="$t('trials:trialDocument:curriculumVitae:table:submmitTime')"
show-overflow-tooltip sortable="custom" />
<el-table-column :label="$t('common:action:action')" width="200">
<template slot-scope="scope">
<el-button
icon="el-icon-view"
:title="$t('common:button:view')"
circle
@click.stop="preview(scope.row)"
/>
<el-button icon="el-icon-view" :title="$t('common:button:view')" circle @click.stop="preview(scope.row)" />
</template>
</el-table-column>
</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" />
<el-dialog :visible.sync="preview_visible" fullscreen>
<div style="height: 100%; overflow: auto">
<preview
:isEN="isEN"
:isAll="isAll"
:reviewerId.sync="reviewerId"
v-if="preview_visible"
/>
<preview :isEN="isEN" :isAll="isAll" :reviewerId.sync="reviewerId" v-if="preview_visible" />
</div>
</el-dialog>
</box-content>
</template>
<script>
const otherId = 'ef84e9cb-f1a6-49d7-b6da-34be2c12abd5'
import { getTrialDoctorList } from '@/api/dictionary'
import Pagination from '@/components/Pagination'
import BoxContent from '@/components/BoxContent'
import preview from '@/views/reviewers/curriculumVitae/preview.vue'
import { mapGetters } from 'vuex'
import store from '@/store'
const searchDataDefault = () => {
return {
SubspecialityId: null,
@@ -232,6 +155,7 @@ export default {
reviewerId: null,
isAll: false,
preview_visible: false,
otherId,
}
},
methods: {
@@ -287,6 +211,9 @@ export default {
immediate: true,
},
},
mounted() {
store.dispatch('global/getHospital')
},
computed: {
...mapGetters(['hospitalList']),
isEN() {
@@ -328,38 +255,44 @@ export default {
])
},
TITLE() {
return `${this.$fd('ArchiveType', this.ArchiveTypeEnum)}${
this.isEN ? this.rowData.Name : this.rowData.NameCN
}`
return `${this.$fd('ArchiveType', this.ArchiveTypeEnum)}${this.isEN ? this.rowData.Name : this.rowData.NameCN
}`
},
},
}
</script>
<style lang="scss" scoped>
<style lang="scss" scoped>
.title {
line-height: 40px;
font-weight: bold;
margin-bottom: 10px;
}
.fileName {
display: inline-block;
max-width: 100%;
white-space: nowrap; /* 文本不换行 */
overflow: hidden; /* 超出部分隐藏 */
white-space: nowrap;
/* 文本不换行 */
overflow: hidden;
/* 超出部分隐藏 */
text-overflow: ellipsis;
}
.fileBtnBox {
display: inline-block;
width: 50px;
i {
cursor: pointer;
color: #409eff;
margin-right: 3px;
&:last-child {
margin: 0;
}
}
}
.topForm {
.el-form-item {
margin-right: 30px;
@@ -0,0 +1,631 @@
<template>
<box-content>
<div class="title">
{{ TITLE }}
</div>
<!-- 搜索框 -->
<div class="search" style="position: relative">
<el-form :inline="true" class="base-search-form">
<!--阅片人-->
<el-form-item
:label="$t('trials:trialDocument:entryRecord:search:viewer')"
>
<el-input
v-model="searchData.Code"
style="width: 100px"
placeholder=""
clearable
/>
</el-form-item>
<!--审核人-->
<el-form-item
:label="$t('trials:trialDocument:entryRecord:search:auditor')"
>
<el-input
v-model="searchData.OptUserName"
style="width: 100px"
placeholder=""
clearable
/>
</el-form-item>
<!--审核人角色-->
<el-form-item
:label="$t('trials:trialDocument:entryRecord:search:auditorRole')"
>
<el-select
v-model="searchData.UserTypeEnum"
style="width: 100px"
placeholder=""
clearable
>
<el-option
v-for="item in $d.UserType"
:key="item.id"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item
:label="$t('trials:trialDocument:entryRecord:search:confirmTime')"
>
<el-date-picker
v-model="timeList"
@change="changeTimeList"
value-format="yyyy-MM-dd HH:mm:ss"
:default-time="['00:00:00', '23:59:59']"
type="datetimerange"
>
</el-date-picker>
</el-form-item>
<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-form-item>
<el-form-item>
<el-button type="primary" @click.stop="openUpload">
{{ $t('trials:trialDocument:entryRecord:title:uploadFile') }}
</el-button>
</el-form-item>
</el-form>
</div>
<el-table
v-loading="loading"
v-adaptive="{ bottomOffset: 75 }"
:data="list"
stripe
height="100"
style="width: 100%"
>
<el-table-column type="index" />
<!--姓名-->
<el-table-column
prop="BlindName"
:label="$t('trials:trialDocument:entryRecord:table:name')"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
<span>{{ isEN ? scope.row.BlindName : scope.row.BlindNameCN }}</span>
</template>
</el-table-column>
<el-table-column
prop="Code"
:label="$t('trials:trialDocument:entryRecord:table:userName')"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="Speciality"
:label="$t('trials:trialDocument:entryRecord:table:status')"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.DoctorTrialState === 10" type="primary">{{
$fd('DoctorTrialState', scope.row.DoctorTrialState)
}}</el-tag>
<el-tag v-if="scope.row.DoctorTrialState === 9" type="warning">{{
$fd('DoctorTrialState', scope.row.DoctorTrialState)
}}</el-tag>
<el-tag v-if="scope.row.DoctorTrialState === 8" type="danger">{{
$fd('DoctorTrialState', scope.row.DoctorTrialState)
}}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="OptUserName"
:label="$t('trials:trialDocument:entryRecord:table:auditor')"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="UserTypeEnum"
:label="$t('trials:trialDocument:entryRecord:table:auditorRole')"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
<span>{{ $fd('UserType', scope.row.UserTypeEnum) }}</span>
</template>
</el-table-column>
<el-table-column
prop="OptTimeStr"
:label="$t('trials:trialDocument:entryRecord:table:submmitTime')"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column :label="$t('common:action:action')" width="200">
<template slot-scope="scope">
<el-button
icon="el-icon-view"
:title="$t('common:button:view')"
circle
@click.stop="preview(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
class="page"
:total="total"
:page.sync="searchData.PageIndex"
:limit.sync="searchData.PageSize"
@pagination="getList"
/>
<el-dialog :visible.sync="preview_visible" fullscreen>
<div style="height: 100%; overflow: auto">
<preview
:isEN="isEN"
:isAll="isAll"
:reviewerId.sync="reviewerId"
v-if="preview_visible"
/>
</div>
</el-dialog>
<!-- <upload-files
:config="config"
:faccept="faccept"
:uploadPath="uploadPath"
:limitLength="limitLength"
v-if="config.visible"
@close="close"
@uplaodFile="uplaodFile"
/> -->
<base-model :config="upload_config">
<div slot="dialog-body">
<div class="file_title">
{{ $t('trials:trialDocument:entryRecord:title:uploadFile') }}
</div>
<div class="file" v-if="file.FileName">
<div class="name">{{ file.FileName }}</div>
<i
v-if="hasDownLoad && (isManage || isInspect)"
class="el-icon-download"
:title="$t('common:button:download')"
@click.stop="downLoad(file)"
/>
<i
v-if="hasDel && isManage && !viewStatus"
class="el-icon-delete"
:title="$t('common:button:delete')"
@click.stop="handleRemoveFile"
/>
</div>
<div class="btnBox">
<!-- <el-button
type="primary"
v-if="hasEdit && isManage && !viewStatus"
@click.stop="openFile(false)"
>
{{ $t('trials:trialDocument:entryRecord:button:uploadFile') }}
</el-button> -->
<el-upload
v-if="hasEdit && isManage && !viewStatus"
class="upload-demo"
action
:before-upload="(param) => beforeUpload(param, '.zip')"
:show-file-list="false"
:http-request="handleUploadFile"
:limit="1"
accept=".zip"
>
<el-button size="small" type="primary" :disabled="!!file.FileName">
{{ $t('trials:trialDocument:entryRecord:button:uploadFile') }}
</el-button>
</el-upload>
</div>
</div>
</base-model>
</box-content>
</template>
<script>
import { getConfirmationReviewerList } from '@/api/trials'
import {
uploadTrialFileTypeFile,
getTrialFileTypeFile,
deleteTrialTypeFile,
} from '@/api/dictionary'
import Pagination from '@/components/Pagination'
import BoxContent from '@/components/BoxContent'
import preview from '@/views/reviewers/curriculumVitae/preview.vue'
import baseModel from '@/components/BaseModel'
import { mapGetters } from 'vuex'
// import uploadFiles from '../../uploadFiles.vue'
import { downLoadFile } from '@/utils/stream.js'
const searchDataDefault = () => {
return {
Code: null,
UserTypeEnum: null,
OptUserName: null,
OptStartTime: null,
OptEndTime: null,
PageIndex: 1,
PageSize: 20,
Asc: false,
SortField: 'BlindName',
}
}
export default {
name: 'entryRecord',
components: {
BoxContent,
Pagination,
preview,
// uploadFiles,
baseModel,
},
props: {
ArchiveTypeEnum: {
type: Number,
default: 0,
},
rowData: {
type: Object,
default: () => {
return {}
},
},
viewStatus: {
type: Boolean,
default: false,
},
Id: {
type: String,
default: '',
},
},
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
reviewerId: null,
isAll: false,
preview_visible: false,
timeList: [],
config: {
visible: false,
showClose: true,
width: '800px',
title: '',
appendToBody: false,
isFolder: false,
},
upload_config: {
visible: false,
showClose: true,
width: '800px',
title: '',
appendToBody: false,
isFolder: false,
},
faccept: ['.zip'],
limitLength: 1,
uploadPath: null,
file: {},
}
},
methods: {
handleRemoveFile() {
this.$confirm(
this.$t('trials:trialDocument:entryRecord:message:deleteMes'),
{
type: 'warning',
distinguishCancelAndClose: true,
}
)
.then(() => {
deleteTrialTypeFile({
TrialFileTypeId: this.file.TrialFileTypeId,
}).then((res) => {
if (res.IsSuccess) {
this.getFile()
this.$message({
message: this.$t('common:message:deletedSuccessfully'),
type: 'success',
})
}
})
})
.catch((action) => {})
},
async handleUploadFile(param) {
try {
console.log(param)
let trialId = this.$route.query.trialId
let extendName = param.file.name
.substring(param.file.name.lastIndexOf('.'))
.toLocaleLowerCase()
if (!trialId)
return this.$alert(
this.$t('trials:trialDocument:entryRecord:msg:noTrialId')
).catch()
this.loading = true
var file = await this.fileToBlob(param.file)
let typeArr = ['', 'Report', 'Doc', 'Record', 'Reviewer', 'Template']
let types = typeArr[this.ArchiveTypeEnum]
// let fileNameNoType = param.file.name
// .substring(0, param.file.name.lastIndexOf('.'))
// .toLocaleLowerCase()
const res = await this.OSSclient.put(
`/${trialId}/Document/${types}/${this.$guid()}${extendName}`,
file
)
this.file = {
FileName: param.file.name,
FilePath: this.$getObjectName(res.url),
FileSize: param.file.size,
FileFormat: extendName,
}
this.loading = false
this.uplaodFile([this.file])
} catch (err) {
console.log(err)
}
},
beforeUpload(param, accept) {
if (this.file && !!this.file.FileName) {
this.$alert(
this.$t('trials:trialDocument:entryRecord:msg:hasFile')
).catch()
return
}
let extendName = param.name
.substring(param.name.lastIndexOf('.'))
.toLocaleLowerCase()
if (accept !== extendName) {
this.$alert(
this.$t('trials:trialDocument:entryRecord:msg:typeErr')
).catch()
return
}
},
openUpload() {
this.upload_config.title = `${this.$t(
'trials:trialDocument:entryRecord:title:uploadFile'
)}
-
${this.isEN ? this.rowData.Name : this.rowData.NameCN}`
this.upload_config.visible = true
},
//
async downLoad(row) {
try {
return await downLoadFile(
this.OSSclientConfig.basePath + row.FilePath,
row.FileName
)
} catch (err) {
console.log(err)
}
},
//
async getFile() {
try {
if (!this.Id) return false
let data = {
TrialFileTypeId: this.Id,
}
let res = await getTrialFileTypeFile(data)
if (res.IsSuccess) {
this.file = res.Result
}
} catch (err) {
console.log(err)
}
},
async uplaodFile(list) {
console.log(list, 'list')
try {
let data = {
TrialFileTypeId: this.Id,
FileRecord: list[0],
}
let res = await uploadTrialFileTypeFile(data)
if (res.IsSuccess) {
this.getFile()
}
} catch (err) {
console.log(err)
}
},
openFile(isFolder = false) {
this.config.title = this.$t(
'trials:trialDocument:entryRecord:form:title:uploadFile'
)
this.config.visible = true
this.config.isFolder = isFolder
},
async getList() {
try {
this.searchData.TrialId = this.$route.query.trialId
this.loading = true
let res = await getConfirmationReviewerList(this.searchData)
this.loading = false
if (res.IsSuccess) {
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
}
} catch (err) {
this.loading = false
console.log(err)
}
},
//
handleSearch() {
this.searchData.PageIndex = 1
this.getList()
},
//
handleReset() {
this.searchData = searchDataDefault()
this.timeList = []
this.getList()
},
//
handleSortByColumn(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()
},
//
preview(row) {
this.reviewerId = row.Id
this.isAll = false
this.preview_visible = true
},
changeTimeList() {
if (this.timeList) {
this.searchData.OptStartTime = this.timeList[0]
this.searchData.OptEndTime = this.timeList[1]
} else {
this.searchData.OptStartTime = null
this.searchData.OptEndTime = null
}
},
close() {
this.config.visible = false
},
},
created() {
let typeArr = ['', 'Report', 'Doc', 'Record', 'Reviewer', 'Template']
let types = typeArr[this.ArchiveTypeEnum]
this.uploadPath = `/${this.$route.query.trialId}/Document/${types}`
},
watch: {
Id: {
handler() {
this.rowBtnStatus = 'edit'
this.getList()
this.getFile()
},
immediate: true,
},
},
computed: {
...mapGetters(['hospitalList']),
isEN() {
return this.$i18n.locale !== 'zh'
},
isInspect() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:inspect',
])
},
isManage() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:manage',
])
},
hasAdd() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:add',
])
},
hasEdit() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:edit',
])
},
hasDel() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:del',
])
},
hasDownLoad() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:downLoad',
])
},
hasAccredit() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:accredit',
])
},
TITLE() {
return `${this.$fd('ArchiveType', this.ArchiveTypeEnum)}${
this.isEN ? this.rowData.Name : this.rowData.NameCN
}`
},
},
}
</script>
<style lang="scss" scoped>
.title {
line-height: 40px;
font-weight: bold;
margin-bottom: 10px;
}
.fileName {
display: inline-block;
max-width: 100%;
white-space: nowrap; /* 文本不换行 */
overflow: hidden; /* 超出部分隐藏 */
text-overflow: ellipsis;
}
.fileBtnBox {
display: inline-block;
width: 50px;
i {
cursor: pointer;
color: #409eff;
margin-right: 3px;
&:last-child {
margin: 0;
}
}
}
.topForm {
.el-form-item {
margin-right: 30px;
}
}
.file_title {
line-height: 40px;
font-weight: bold;
}
.file {
width: 100%;
display: flex;
margin-bottom: 10px;
.name {
max-width: 90%;
white-space: nowrap; /* 文本不会换行,会在同一行内继续,直到遇到<br>标签为止 */
text-overflow: ellipsis; /* 当文本溢出包含它的容器时,显示省略号(...)来表示被截断的文本 */
overflow: hidden; /* 隐藏溢出容器的文本 */
}
i {
cursor: pointer;
margin: 3px;
color: #409eff;
}
.disable {
cursor: not-allowed;
color: #909399;
}
}
.btnBox {
margin-top: 10px;
display: flex;
align-items: center;
justify-content: flex-end;
}
</style>
@@ -74,13 +74,13 @@
:rowData="rowData"
/>
<!--阅片人资质材料-->
<!-- <certificate
<certificate
v-if="[8].includes(SubIdentificationEnum)"
:ArchiveTypeEnum="ArchiveTypeEnum"
:rowData="rowData"
:Id="Id"
:viewStatus="viewStatus"
/> -->
/>
<!--医学审核-->
<medical-feedback
v-if="[9].includes(SubIdentificationEnum)"
@@ -96,13 +96,13 @@
:viewStatus="viewStatus"
/>
<!--申报方同意入项记录-->
<!-- <entry-record
<entry-record
v-if="[12].includes(SubIdentificationEnum)"
:ArchiveTypeEnum="ArchiveTypeEnum"
:rowData="rowData"
:Id="Id"
:viewStatus="viewStatus"
/> -->
/>
<!--培训记录/共享资料-->
<Attachments-management
v-if="[13].includes(SubIdentificationEnum)"
@@ -123,8 +123,8 @@ import temp from './components/template/index.vue'
//
import curriculumVitae from './components/viewer/curriculumVitae/index.vue'
// import certificate from './components/viewer/certificate/index.vue'
// import entryRecord from './components/viewer/entryRecord/index.vue'
import certificate from './components/viewer/certificate/index.vue'
import entryRecord from './components/viewer/entryRecord/index.vue'
import readingTracking from '@/views/trials/trials-panel/reading/reading-tracking/index.vue'
import medicalFeedback from '@/views/trials/trials-panel/reading/pm-medical-feedback/index.vue'
import AttachmentsManagement from '@/views/trials/trials-panel/attachments/attachment-management/index.vue'
@@ -138,8 +138,8 @@ export default {
trainRecord,
temp,
// entryRecord,
// certificate,
entryRecord,
certificate,
curriculumVitae,
readingTracking,
AttachmentsManagement,
@@ -155,6 +155,9 @@ export default {
}
},
computed: {
isEN() {
return this.$i18n.locale !== 'zh'
},
isInspect() {
return this.hasPermi([
'trials:trials-panel:trial-summary:trial-document:inspect',
@@ -247,4 +250,7 @@ export default {
margin: 0 0 12px 0;
border-top: 1px solid #ebeef5;
}
::v-deep .box-body .search .base-search-form .el-form-item {
margin-bottom: 15px;
}
</style>
@@ -459,7 +459,7 @@ export default {
},
fileSizeFormatter(size) {
if (!size) return
return (size / Math.pow(1024, 2)).toFixed(3) + 'M'
return (size / Math.pow(1024, 2)).toFixed(3) + 'MB'
},
handleReset() {
this.searchData = searchDataDefault()
@@ -29,7 +29,9 @@
/>
<div class="info">
<p class="user-info">
<span style="font-weight: 700">{{ record.CreateUserFullName || record.CreateUserName }} </span>
<span style="font-weight: 700"
>{{ record.CreateUserFullName || record.CreateUserName }}
</span>
<span>({{ record.CreateTime }}) </span>
</p>
<div
@@ -42,7 +44,9 @@
<div v-else class="word-my">
<div class="info">
<p class="user-info">
<span style="font-weight: 700">{{ record.CreateUserFullName || record.CreateUserName }} </span>
<span style="font-weight: 700"
>{{ record.CreateUserFullName || record.CreateUserName }}
</span>
<span>({{ record.CreateTime }}) </span>
</p>
<div
@@ -239,6 +243,7 @@
ref="signForm"
:sign-code-enum="signCode"
:subject-visit-id="data.SubjectVisitId"
:sign-replace-text="signReplaceText"
@closeDialog="closeReuploadFinishedDialog"
/>
</el-dialog>
@@ -250,6 +255,7 @@ import {
addQCChallengeReply,
cRCRequestReUpload,
verifyReuploadIsCanJump,
getVisitClinicalDataName,
} from '@/api/trials'
import { setReuploadFinished } from '@/api/trials/visit'
import adminAvatar from '@/assets/Admin.png'
@@ -322,6 +328,7 @@ export default {
ReuploadEnum: 0,
trialId: '',
uploadBtnLoading: false,
signReplaceText: null,
}
},
mounted() {
@@ -544,9 +551,16 @@ export default {
handleSetUploadFinished() {
//
if (this.clinicalEnum > 0 && this.data.IsBaseLine) {
const { ClinicalDataConfirmation } = const_.processSignature
this.signCode = ClinicalDataConfirmation
this.signVisible = true
getVisitClinicalDataName({ id: this.data.SubjectVisitId })
.then((res) => {
this.signReplaceText = res.Result.ClinicalDataName
const { ClinicalDataConfirmation } = const_.processSignature
this.signCode = ClinicalDataConfirmation
this.signVisible = true
})
.catch((err) => {
console.log(err)
})
} else {
this.$confirm(this.$t('trials:crcQuestion:message:reuploadConfirm'), {
type: 'warning',
@@ -127,7 +127,6 @@
<el-table-column
:label="$t('common:action:action')"
min-width="100"
fixed="right"
>
<template slot-scope="scope">
<!-- 预览 -->
@@ -323,7 +323,7 @@
<template slot-scope="scope">
<span>{{
scope.row.Size && scope.row.Size > 0
? `${(scope.row.Size / 1024 / 1024).toFixed(2)}MB`
? `${(scope.row.Size / 1024 / 1024).toFixed(3)}MB`
: ''
}}</span>
</template>
@@ -434,9 +434,9 @@
scope.row.dicomInfo.fileCount
}}
({{
(scope.row.dicomInfo.uploadFileSize / 1024 / 1024).toFixed(2)
(scope.row.dicomInfo.uploadFileSize / 1024 / 1024).toFixed(3)
}}MB/{{
(scope.row.dicomInfo.fileSize / 1024 / 1024).toFixed(2)
(scope.row.dicomInfo.fileSize / 1024 / 1024).toFixed(3)
}}MB)
</span>
</template>
@@ -27,6 +27,7 @@
:data="nonDicomStudyList"
style="width: 100%"
:default-sort="{ prop: 'CreateTime', order: 'ascending' }"
:row-class-name="tableRowClassName"
>
<!-- 检查编号 -->
<el-table-column
@@ -68,12 +69,14 @@
v-if="scope.row.FileCount"
trigger="click"
placement="bottom"
popper-class="imgTable"
>
<el-table
v-loading="filesLoading"
:data="nonDicomfilesList"
height="300"
size="small"
:row-class-name="tableRowClassName"
>
<!-- 文件名称 -->
<el-table-column
@@ -89,7 +92,7 @@
<template slot-scope="scope">
<span>{{
scope.row.FileSize && scope.row.FileSize > 0
? `${(scope.row.FileSize / 1024 / 1024).toFixed(2)}MB`
? `${(scope.row.FileSize / 1024 / 1024).toFixed(3)}MB`
: ''
}}</span>
</template>
@@ -207,6 +210,7 @@
v-if="allowAddOrEdit"
icon="el-icon-upload2"
circle
:disabled="scope.row.IsDeleted"
:title="$t('trials:uploadNonDicoms:action:upload')"
@click.native.prevent="handleUpload(scope.row)"
/>
@@ -458,7 +462,7 @@
<template slot-scope="scope">
<span>{{
scope.row.size && scope.row.size > 0
? `${(scope.row.size / 1024 / 1024).toFixed(2)}MB`
? `${(scope.row.size / 1024 / 1024).toFixed(3)}MB`
: ''
}}</span>
</template>
@@ -1308,6 +1312,14 @@ export default {
})
return newArr.join(' | ')
},
//
tableRowClassName({ row, rowIndex }) {
if (row.IsDeleted) {
return 'delete-row'
} else {
return ''
}
},
},
}
</script>
@@ -1351,4 +1363,16 @@ export default {
pointer-events: none; //pointer-events:none穿
}
}
.non-dicom-wrapper {
::v-deep .delete-row {
text-decoration-line: line-through;
color: #c0c4cc;
}
}
</style>
<style>
.imgTable .delete-row {
text-decoration-line: line-through;
color: #c0c4cc;
}
</style>
@@ -328,7 +328,7 @@
<template slot-scope="scope">
<span>{{
scope.row.Size && scope.row.Size > 0
? `${(scope.row.Size / 1024 / 1024).toFixed(2)}MB`
? `${(scope.row.Size / 1024 / 1024).toFixed(3)}MB`
: ''
}}</span>
</template>
+4 -4
View File
@@ -11,13 +11,13 @@ const webpack = require('webpack')
const defaultSettings = require('./src/settings.js')
const moment = require('moment')
var distDate = moment(new Date()).format('YYYY-MM-DD')
const name = process.env.NODE_ENV === 'usa' ? 'LILI' : defaultSettings.title || 'IRCIS' // page title
const name = process.env.NODE_ENV === 'usa' ? 'Imaging Trial Management System' : defaultSettings.title || 'IRC Imaging System' // page title
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
// eslint-disable-next-line no-undef
module.exports = defineConfig({
// lintOnSave: false,
transpileDependencies: false,
publicPath: process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'production' || process.env.VUE_APP_OSS_CONFIG_BUCKET === 'zyypacs-usa' ? process.env.VUE_APP_BASE_PATH : `${process.env.VUE_FILE_PATH}${process.env.VUE_APP_OSS_PATH}${distDate}/`,
publicPath: process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'prod' || process.env.NODE_ENV === 'production' || process.env.VUE_APP_OSS_CONFIG_BUCKET === 'zyypacs-usa' ? process.env.VUE_APP_BASE_PATH : `${process.env.VUE_FILE_PATH}${process.env.VUE_APP_OSS_PATH}${distDate}/`,
// publicPath: '/',
outputDir: 'dist',
assetsDir: 'static',
@@ -97,7 +97,7 @@ module.exports = defineConfig({
]
}),
// new BundleAnalyzerPlugin(),
process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'production' || process.env.VUE_APP_OSS_CONFIG_BUCKET === 'zyypacs-usa' ? function() { }
process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'prod' || process.env.NODE_ENV === 'production' || process.env.VUE_APP_OSS_CONFIG_BUCKET === 'zyypacs-usa' ? function () { }
: new WebpackAliyunOss({
from: ['./dist/**'],
dist: process.env.VUE_APP_OSS_PATH + distDate,
@@ -184,7 +184,7 @@ module.exports = defineConfig({
// 生成文件的最大体积
maxAssetSize: 3000000000,
// 只给出js的性能提示
assetFilter: function(assetFileName) {
assetFilter: function (assetFileName) {
return assetFileName.endsWith('.js')
}
}