Merge branch 'main' into uat_us
commit
a3e79db023
File diff suppressed because it is too large
Load Diff
|
|
@ -15,11 +15,12 @@
|
|||
"i18n:en": "node i18nGenerate.js lang=en keyCol=5 valCol=7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.726.1",
|
||||
"@cornerstonejs/adapters": "4.19.2",
|
||||
"@aws-sdk/client-s3": "3.726.1",
|
||||
"@cornerstonejs/adapters": "^4.19.2",
|
||||
"@cornerstonejs/calculate-suv": "^1.1.0",
|
||||
"@cornerstonejs/core": "^4.19.2",
|
||||
"@cornerstonejs/dicom-image-loader": "^4.19.2",
|
||||
"@cornerstonejs/polymorphic-segmentation": "^4.19.2",
|
||||
"@cornerstonejs/tools": "^4.19.2",
|
||||
"@fingerprintjs/fingerprintjs": "^4.6.2",
|
||||
"@icr/polyseg-wasm": "^0.4.0",
|
||||
|
|
@ -38,7 +39,7 @@
|
|||
"dcmjs": "^0.29.8",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"dicomedit": "^0.1.0",
|
||||
"echarts": "^4.8.0",
|
||||
"echarts": "^6.0.0",
|
||||
"element-ui": "^2.15.14",
|
||||
"exceljs": "^4.4.0",
|
||||
"file-saver": "^2.0.5",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function addOrUpdateFileUploadRecord(params) {
|
||||
return request({
|
||||
url: `/FileUploadRecord/addOrUpdateFileUploadRecord`,
|
||||
method: 'post',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
|
||||
export function batchAddSyncFileTask(params) {
|
||||
return request({
|
||||
url: `/FileUploadRecord/batchAddSyncFileTask`,
|
||||
method: 'post',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
|
||||
export function getSubjectUploadRecordList(params) {
|
||||
return request({
|
||||
url: `/FileUploadRecord/getSubjectUploadRecordList`,
|
||||
method: 'post',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
|
||||
export function getFileUploadRecordList(params) {
|
||||
return request({
|
||||
url: `/FileUploadRecord/getFileUploadRecordList`,
|
||||
method: 'post',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
|
||||
export function getUploadFileSyncRecordList(params) {
|
||||
return request({
|
||||
url: `/FileUploadRecord/getUploadFileSyncRecordList`,
|
||||
method: 'post',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
|
|
@ -8,20 +8,49 @@
|
|||
|
||||
<script>
|
||||
import { getReportsChartData } from "@/api/reading"
|
||||
let echarts = require('echarts/lib/echarts');
|
||||
import * as echarts from 'echarts/core';
|
||||
import { LineChart } from 'echarts/charts';
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DataZoomComponent,
|
||||
LegendComponent,
|
||||
DatasetComponent,
|
||||
// 内置数据转换器组件 (filter, sort)
|
||||
TransformComponent
|
||||
} from 'echarts/components';
|
||||
// 标签自动布局、全局过渡动画等特性
|
||||
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
||||
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
echarts.use([
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DatasetComponent,
|
||||
TransformComponent,
|
||||
DataZoomComponent,
|
||||
LegendComponent,
|
||||
LineChart,
|
||||
LabelLayout,
|
||||
UniversalTransition,
|
||||
CanvasRenderer
|
||||
]);
|
||||
// let echarts = require('echarts/lib/echarts');
|
||||
|
||||
// 按需引入图表
|
||||
// require('echarts/lib/chart/bar');
|
||||
require('echarts/lib/chart/line');
|
||||
// require('echarts/lib/chart/line');
|
||||
// require('echarts/lib/chart/pie');
|
||||
// require('echarts/lib/chart/scatter');
|
||||
|
||||
// 按需引入组件
|
||||
require('echarts/lib/component/tooltip');
|
||||
require('echarts/lib/component/title');
|
||||
require('echarts/lib/component/legend');
|
||||
require('echarts/lib/component/grid');
|
||||
require('echarts/lib/component/dataZoom');
|
||||
// require('echarts/lib/component/tooltip');
|
||||
// require('echarts/lib/component/title');
|
||||
// require('echarts/lib/component/legend');
|
||||
// require('echarts/lib/component/grid');
|
||||
// require('echarts/lib/component/dataZoom');
|
||||
export default {
|
||||
name: "readingChart",
|
||||
props: {
|
||||
|
|
@ -151,7 +180,13 @@ export default {
|
|||
text: obj.title,
|
||||
textStyle: {
|
||||
color: "#fff"
|
||||
}
|
||||
},
|
||||
left: 0,
|
||||
top: 0
|
||||
},
|
||||
grid: {
|
||||
left: 50,
|
||||
bottom: 30
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
|
|
@ -193,6 +228,7 @@ export default {
|
|||
}
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#fff',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1174,6 +1174,7 @@ export default {
|
|||
},
|
||||
}
|
||||
let arr = []
|
||||
let uploadBatchId = scope.$guid()
|
||||
for (let i = 0; i < seriesList.length; i++) {
|
||||
let v = seriesList[i]
|
||||
let instanceList = []
|
||||
|
|
@ -1266,6 +1267,16 @@ export default {
|
|||
) {
|
||||
dicomInfo.uploadFileSize = dicomInfo.fileSize
|
||||
}
|
||||
},
|
||||
{
|
||||
fileName: o.file.name,
|
||||
fileSize: o.file.size,
|
||||
fileType: 'application/dicom',
|
||||
uploadBatchId: uploadBatchId,
|
||||
batchDataType: 5,
|
||||
trialId: params.trialId,
|
||||
subjectId: params.subjectId,
|
||||
subjectVisitId: params.subjectVisitId
|
||||
}
|
||||
)
|
||||
if (!res || !res.url) {
|
||||
|
|
@ -1289,7 +1300,17 @@ export default {
|
|||
let OSSclient = scope.OSSclient
|
||||
let seriesRes = await OSSclient.put(
|
||||
thumbnailPath,
|
||||
blob
|
||||
blob,
|
||||
{
|
||||
fileName: `${v.seriesUid}.jpg`,
|
||||
fileSize: blob.size,
|
||||
fileType: 'image/jpeg',
|
||||
uploadBatchId: uploadBatchId,
|
||||
batchDataType: 6,
|
||||
trialId: params.trialId,
|
||||
subjectId: params.subjectId,
|
||||
subjectVisitId: params.subjectVisitId
|
||||
}
|
||||
)
|
||||
if (seriesRes && seriesRes.url) {
|
||||
ImageResizePath = scope.$getObjectName(
|
||||
|
|
@ -1430,7 +1451,20 @@ export default {
|
|||
}
|
||||
let OSSclient = scope.OSSclient
|
||||
try {
|
||||
let seriesRes = await OSSclient.put(thumbnailPath, blob)
|
||||
let seriesRes = await OSSclient.put(
|
||||
thumbnailPath,
|
||||
blob,
|
||||
{
|
||||
fileName: `${v.seriesUid}.jpg`,
|
||||
fileSize: blob.size,
|
||||
fileType: 'image/jpeg',
|
||||
uploadBatchId: uploadBatchId,
|
||||
batchDataType: 6,
|
||||
trialId: params.trialId,
|
||||
subjectId: params.subjectId,
|
||||
subjectVisitId: params.subjectVisitId
|
||||
}
|
||||
)
|
||||
if (seriesRes && seriesRes.url) {
|
||||
o.ImageResizePath = scope.$getObjectName(seriesRes.url)
|
||||
}
|
||||
|
|
@ -1448,6 +1482,7 @@ export default {
|
|||
if (scope.IsImageSegment) {
|
||||
params.IsImageSegmentLabel = true
|
||||
}
|
||||
params.UploadBatchId = uploadBatchId
|
||||
addOrUpdateArchiveTaskStudy(params)
|
||||
.then((res) => {
|
||||
if (dicomInfo.failedFileCount === dicomInfo.fileCount) {
|
||||
|
|
|
|||
|
|
@ -548,8 +548,9 @@ export default {
|
|||
})
|
||||
if (res.IsSuccess) {
|
||||
this.studyMonitorId = res.Result
|
||||
let uploadBatchId = this.$guid()
|
||||
for (let i = 0; i < num; i++) {
|
||||
funArr.push(this.handleUploadTask(this.selectArr, i))
|
||||
funArr.push(this.handleUploadTask(this.selectArr, i, uploadBatchId))
|
||||
}
|
||||
if (funArr.length > 0) {
|
||||
let res = await Promise.all(funArr)
|
||||
|
|
@ -560,7 +561,7 @@ export default {
|
|||
}
|
||||
},
|
||||
// 并发上传
|
||||
async handleUploadTask(arr, index) {
|
||||
async handleUploadTask(arr, index, uploadBatchId) {
|
||||
if (!this.uploadVisible) return
|
||||
let file = this.fileList.filter((item) => item.id === arr[index].id)[0]
|
||||
file.status = 1
|
||||
|
|
@ -576,7 +577,7 @@ export default {
|
|||
}
|
||||
file.curPath = path
|
||||
const fileData = await this.fileToBlob(file.file)
|
||||
let res = await this.fileToOss(path, fileData, file)
|
||||
let res = await this.fileToOss(path, fileData, file, uploadBatchId)
|
||||
if (res) {
|
||||
file.status = 2
|
||||
this.successFileList.push({
|
||||
|
|
@ -610,13 +611,13 @@ export default {
|
|||
}
|
||||
let ind = arr.findIndex((item) => item.status === 0)
|
||||
if (ind >= 0) {
|
||||
return this.handleUploadTask(arr, ind)
|
||||
return this.handleUploadTask(arr, ind, uploadBatchId)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
// file上传到oss
|
||||
async fileToOss(path, file, item) {
|
||||
async fileToOss(path, file, item, uploadBatchId) {
|
||||
try {
|
||||
let res = await this.OSSclient.multipartUpload(
|
||||
{
|
||||
|
|
@ -629,6 +630,17 @@ export default {
|
|||
if (item.uploadFileSize > file.fileSize) {
|
||||
item.uploadFileSize = file.fileSize > 0 ? file.fileSize : 1
|
||||
}
|
||||
},
|
||||
{
|
||||
fileName: item.name,
|
||||
fileSize: item.size,
|
||||
fileType: item.fileType,
|
||||
uploadBatchId: uploadBatchId,
|
||||
batchDataType: 7,
|
||||
trialId: this.$route.query.trialId,
|
||||
subjectId: this.currentRow.SubjectId,
|
||||
subjectVisitId: this.currentRow.SourceSubjectVisitId,
|
||||
studyCode: this.SubjectCode
|
||||
}
|
||||
)
|
||||
if (res) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
import resize from './resize'
|
||||
|
||||
const install = function (Vue) {
|
||||
// 绑定v-adaptive指令
|
||||
Vue.directive('resize', resize)
|
||||
}
|
||||
|
||||
// if (window.Vue) {
|
||||
// window['adaptive'] = adaptive
|
||||
// // eslint-disable-next-line no-undef
|
||||
// Vue.use(install)
|
||||
// }
|
||||
|
||||
resize.install = install
|
||||
|
||||
export default resize
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
const map = new WeakMap()
|
||||
const ob = new ResizeObserver(entries => {
|
||||
for (const entry of entries) {
|
||||
const handler = map.get(entry.target)
|
||||
if (handler) {
|
||||
const { inlineSize, blockSize } = entry.contentBoxSize[0]
|
||||
handler({ width: inlineSize, height: blockSize })
|
||||
}
|
||||
}
|
||||
})
|
||||
export default {
|
||||
bind(el, binding) {
|
||||
map.set(el, binding.value)
|
||||
ob.observe(el)
|
||||
},
|
||||
unbind(el) {
|
||||
ob.unobserve(el)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1775115523115" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5347" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M458.105263 970.105263h-215.578947V264.084211h215.578947V970.105263z m-161.68421-53.894737h107.789473V317.978947h-107.789473V916.210526z" fill="#ffffff" p-id="5348"></path><path d="M296.421053 970.105263h-215.578948V479.663158h215.578948V970.105263z m-161.684211-53.894737h107.789474V533.557895h-107.789474V916.210526zM943.157895 970.105263h-215.578948V479.663158h215.578948V970.105263z m-161.684211-53.894737h107.789474V533.557895h-107.789474V916.210526zM619.789474 970.105263h-215.578948V53.894737h215.578948v916.210526z m-161.684211-53.894737h107.789474V107.789474h-107.789474v808.421052z" fill="#ffffff" p-id="5349"></path><path d="M781.473684 970.105263h-215.578947V264.084211h215.578947V970.105263z m-161.68421-53.894737h107.789473V317.978947h-107.789473V916.210526z" fill="#ffffff" p-id="5350"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -70,6 +70,9 @@ import FB from '@/components/feedBack/index'
|
|||
Vue.use(FB)
|
||||
import FBT from '@/components/feedBackTable/index'
|
||||
Vue.use(FBT)
|
||||
import resize from '@/directive/resize/index'
|
||||
// 表格自适应指令
|
||||
Vue.use(resize)
|
||||
import adaptive from '@/directive/adaptive/index'
|
||||
// 表格自适应指令
|
||||
Vue.use(adaptive)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import Vue from 'vue'
|
||||
import { anonymization } from './anonymization'
|
||||
export const dcmUpload = async function (data, config, progressFn) {
|
||||
export const dcmUpload = async function (data, config, progressFn, fileInfo) {
|
||||
return new Promise(async resolve => {
|
||||
try {
|
||||
// let blob = await encoder(file, config)
|
||||
|
|
@ -8,7 +8,8 @@ export const dcmUpload = async function (data, config, progressFn) {
|
|||
if (config) {
|
||||
blob = await anonymization(data.file, config)
|
||||
}
|
||||
let res = await Vue.prototype.OSSclient.multipartUpload(Object.assign(data, { file: blob.blob }), progressFn)
|
||||
|
||||
let res = await Vue.prototype.OSSclient.multipartUpload(Object.assign(data, { file: blob.blob }), progressFn, fileInfo)
|
||||
resolve({
|
||||
...res,
|
||||
image: blob.pixelDataElement
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const stream = require('stream')
|
|||
import Vue from 'vue'
|
||||
import { customerHttp, OSSclose } from "@/utils/multipartUpload/oss"
|
||||
import { exist, AWSclose } from "@/utils/multipartUpload/aws"
|
||||
import { addOrUpdateFileUploadRecord } from '@/api/file'
|
||||
const { GetObjectStoreToken } = require('../api/user.js')
|
||||
const {
|
||||
S3Client,
|
||||
|
|
@ -22,7 +23,6 @@ async function ossGenerateSTS() {
|
|||
res = await GetObjectStoreToken()
|
||||
localStorage.setItem('stsToken', JSON.stringify(res))
|
||||
}
|
||||
|
||||
// res.Result.ObjectStoreUse = 'AWS';
|
||||
Vue.prototype.OSSclientConfig = { ...res.Result[res.Result.ObjectStoreUse] }
|
||||
Vue.prototype.OSSclientConfig.ObjectStoreUse = res.Result.ObjectStoreUse;
|
||||
|
|
@ -34,7 +34,8 @@ async function ossGenerateSTS() {
|
|||
Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
|
||||
let OSSclient = new OSS(Vue.prototype.OSSclientConfig)
|
||||
Vue.prototype.OSSclient = {
|
||||
put: async function (objectName, object) {
|
||||
put: async function (objectName, object, fileInfo = {}) {
|
||||
|
||||
OSSclient = await RefreshClient(OSSclient)
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
|
|
@ -52,6 +53,15 @@ async function ossGenerateSTS() {
|
|||
}
|
||||
let res = await OSSclient.put(objectName, object)
|
||||
if (res && res.url) {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const trialId = urlParams.get('trialId')
|
||||
if (Object.keys(fileInfo).length !== 0) {
|
||||
let params = Object.assign({path: objectName}, fileInfo)
|
||||
addOrUpdateFileUploadRecord(params)
|
||||
} else if (trialId) {
|
||||
let params = { trialId }
|
||||
addOrUpdateFileUploadRecord(params)
|
||||
}
|
||||
resolve({
|
||||
name: objectName,
|
||||
url: res.url
|
||||
|
|
@ -65,7 +75,7 @@ async function ossGenerateSTS() {
|
|||
}
|
||||
})
|
||||
},
|
||||
multipartUpload: async (data, progress) => {
|
||||
multipartUpload: async (data, progress, fileInfo = {}) => {
|
||||
OSSclient = await RefreshClient(OSSclient)
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
|
|
@ -95,6 +105,16 @@ async function ossGenerateSTS() {
|
|||
}
|
||||
let res = await customerHttp(OSSclient, data, progress);
|
||||
if (res) {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const trialId = urlParams.get('trialId')
|
||||
if (Object.keys(fileInfo).length !== 0) {
|
||||
let params = Object.assign({path: data.path}, fileInfo)
|
||||
addOrUpdateFileUploadRecord(params)
|
||||
} else if (trialId) {
|
||||
let params = { trialId }
|
||||
addOrUpdateFileUploadRecord(params)
|
||||
}
|
||||
|
||||
resolve({
|
||||
name: data.path,
|
||||
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(res.name)
|
||||
|
|
@ -169,17 +189,17 @@ async function ossGenerateSTS() {
|
|||
}
|
||||
});
|
||||
Vue.prototype.OSSclient = {
|
||||
put: async function (objectName, object) {
|
||||
put: async function (objectName, object, fileInfo = {}) {
|
||||
let data = {
|
||||
file: object,
|
||||
path: objectName
|
||||
}
|
||||
aws = await RefreshClient(aws);
|
||||
return uploadAWS(aws, data, () => { });
|
||||
return uploadAWS(aws, data, () => { }, fileInfo);
|
||||
},
|
||||
multipartUpload: async (data, progress) => {
|
||||
multipartUpload: async (data, progress, fileInfo = {}) => {
|
||||
aws = await RefreshClient(aws);
|
||||
return uploadAWS(aws, data, progress);
|
||||
return uploadAWS(aws, data, progress, fileInfo);
|
||||
},
|
||||
close: () => {
|
||||
AWSclose();
|
||||
|
|
@ -189,7 +209,7 @@ async function ossGenerateSTS() {
|
|||
return
|
||||
}
|
||||
// AWS上传函数
|
||||
function uploadAWS(aws, data, progress) {
|
||||
function uploadAWS(aws, data, progress, fileInfo) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const { file, path } = data;
|
||||
|
|
@ -211,6 +231,16 @@ function uploadAWS(aws, data, progress) {
|
|||
data.path = data.path.replace(`/${bucketName}/`, '');
|
||||
await exist(aws, bucketName, data, progress, (path, status) => {
|
||||
if (status === 'success') {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
|
||||
const trialId = urlParams.get('trialId')
|
||||
if (Object.keys(fileInfo).length !== 0) {
|
||||
let params = Object.assign({path: decodeUtf8(curPath)}, fileInfo)
|
||||
addOrUpdateFileUploadRecord(params)
|
||||
} else if (trialId) {
|
||||
let params = { trialId }
|
||||
addOrUpdateFileUploadRecord(params)
|
||||
}
|
||||
resolve({
|
||||
name: decodeUtf8(curPath),
|
||||
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(curPath)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
<div ref="mapbox" class="map-wrapper" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import 'modules/echarts/map/js/world.js'
|
||||
import * as echarts from 'echarts'
|
||||
// import 'modules/echarts/map/js/world.js'
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
export default {
|
||||
props: {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div ref="trials_stats" style="width:100%;height:100%;" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import { getTrialCountRank } from '@/api/dashboard'
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
const Count = 5
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div ref="enroll_stats" style="width:100%;height:100%;" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import { getEnrollDataByQuarter } from '@/api/dashboard'
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
const Count = 6
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div ref="reviewers_stats" style="width:100%;height:100%;padding:5px;" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import { getReviewersByRank } from '@/api/dashboard'
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
export default {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div ref="monthly_reading_stats" style="width:100%;height:100%;" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import { getReadingDataByMonth } from '@/api/dashboard'
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
const Count = 6
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div ref="reading_rank" class="reading-rank" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import { getReadingDataRank } from '@/api/dashboard'
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
const Count = 5
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div ref="reading_stats" style="width:100%;height:100%;padding-top:5px;" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import { getReadingDataByType } from '@/api/dashboard'
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
export default {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
<template>
|
||||
<div class="adReview_wrapper">
|
||||
<el-card
|
||||
v-if="isReadingShowSubjectInfo"
|
||||
shadow="never"
|
||||
:body-style="{ padding: '10px' }"
|
||||
style="margin-bottom: 10px"
|
||||
>
|
||||
<el-card v-if="isReadingShowSubjectInfo" shadow="never" :body-style="{ padding: '10px' }"
|
||||
style="margin-bottom: 10px">
|
||||
<h4>
|
||||
<!-- 受试者: -->
|
||||
{{ $t("trials:adReview:title:subject") }}
|
||||
|
|
@ -17,14 +13,11 @@
|
|||
<!-- <div slot="header" class="clearfix">
|
||||
<span style="font-weight: bold;">评估结果</span>
|
||||
</div> -->
|
||||
<div
|
||||
slot="header"
|
||||
style="
|
||||
<div slot="header" style="
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
"
|
||||
>
|
||||
">
|
||||
<!-- 评估结果 -->
|
||||
<div style="font-weight: bold">
|
||||
{{ $t("trials:adReview:title:result") }}
|
||||
|
|
@ -39,46 +32,33 @@
|
|||
|
||||
<el-table :data="adInfo.VisitInfoList" style="width: 100%">
|
||||
<!-- 访视名称 -->
|
||||
<el-table-column
|
||||
prop="VisitName"
|
||||
:label="$t('trials:adReview:table:visitName')"
|
||||
show-overflow-tooltip
|
||||
width="150"
|
||||
/>
|
||||
<el-table-column prop="VisitName" :label="$t('trials:adReview:table:visitName')" show-overflow-tooltip
|
||||
width="150" />
|
||||
|
||||
<!-- 评估结果 -->
|
||||
<el-table-column
|
||||
v-for="j in judgeQuestion"
|
||||
:key="j.armEnum"
|
||||
:label="
|
||||
j.armEnum === 1
|
||||
<el-table-column v-for="j in judgeQuestion" :key="j.armEnum" :label="j.armEnum === 1
|
||||
? $t('trials:adReview:table:viewR1')
|
||||
: j.armEnum === 2
|
||||
? $t('trials:adReview:table:viewR2')
|
||||
: $fd('ArmEnum', j.armEnum)
|
||||
"
|
||||
align="center"
|
||||
prop=""
|
||||
>
|
||||
" align="center" prop="">
|
||||
<template>
|
||||
<el-table-column
|
||||
v-for="(qs, i) in j.judgeQuestionList"
|
||||
:key="i"
|
||||
prop=""
|
||||
:label="qs"
|
||||
show-overflow-tooltip
|
||||
width="150"
|
||||
>
|
||||
<el-table-column v-for="(qs, i) in j.judgeQuestionList" :key="i" prop="" :label="qs" show-overflow-tooltip
|
||||
width="150">
|
||||
<template slot-scope="scope">
|
||||
<div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 1">
|
||||
<span v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode">
|
||||
{{ $fd(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode,parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))
|
||||
{{
|
||||
$fd(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode,
|
||||
parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))
|
||||
}}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer }}
|
||||
</span>
|
||||
<span v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit && !isNaN(parseFloat(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))">{{ $fd('ValueUnit', parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit)) }}</span>
|
||||
<span
|
||||
v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit && !isNaN(parseFloat(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))">{{
|
||||
$fd('ValueUnit', parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit)) }}</span>
|
||||
</div>
|
||||
<div v-else-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 2">
|
||||
<div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer">
|
||||
|
|
@ -86,15 +66,11 @@
|
|||
{{ $fd("YesOrNo", scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer) }}
|
||||
</span>
|
||||
<!-- 查看详情 -->
|
||||
<el-button
|
||||
type="text"
|
||||
style="margin-left: 5px"
|
||||
@click="
|
||||
<el-button type="text" style="margin-left: 5px" @click="
|
||||
handleViewDetail(
|
||||
scope.row.VisitTaskInfoList[j.index].GlobalVisitTaskId
|
||||
)
|
||||
"
|
||||
>
|
||||
">
|
||||
{{ $t("trials:adReview:table:view") }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
|
@ -119,60 +95,34 @@
|
|||
</el-table-column>
|
||||
|
||||
<!-- 查看详情 -->
|
||||
<el-table-column
|
||||
:label="
|
||||
criterionType === 10
|
||||
<el-table-column :label="criterionType === 10
|
||||
? $t('trials:adReview:table:visitInfoview')
|
||||
: $t('trials:adReview:table:view')
|
||||
"
|
||||
width="200"
|
||||
:fixed="isFixed ? 'right' : false"
|
||||
>
|
||||
" width="200" :fixed="isFixed ? 'right' : false">
|
||||
<template slot-scope="scope">
|
||||
<!-- 查看R1详情 -->
|
||||
<el-button
|
||||
type="text"
|
||||
:title="$t('trials:adReview:table:viewR1')"
|
||||
@click="handleView(scope.row, 1)"
|
||||
>
|
||||
<el-button type="text" :title="$t('trials:adReview:table:viewR1')" @click="handleView(scope.row, 1)">
|
||||
R1
|
||||
</el-button>
|
||||
<!-- 查看R2详情 -->
|
||||
<el-button
|
||||
type="text"
|
||||
:title="$t('trials:adReview:table:viewR2')"
|
||||
@click="handleView(scope.row, 2)"
|
||||
>
|
||||
<el-button type="text" :title="$t('trials:adReview:table:viewR2')" @click="handleView(scope.row, 2)">
|
||||
R2
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 全局详情 PCWG -->
|
||||
<el-table-column
|
||||
v-if="criterionType === 10"
|
||||
prop="VisitName"
|
||||
:label="$t('trials:adReview:table:glInfo')"
|
||||
show-overflow-tooltip
|
||||
width="150"
|
||||
>
|
||||
<el-table-column v-if="criterionType === 10" prop="VisitName" :label="$t('trials:adReview:table:glInfo')"
|
||||
show-overflow-tooltip width="150">
|
||||
<template slot-scope="scope">
|
||||
<!-- 查看R1详情 -->
|
||||
<el-button
|
||||
v-if="scope.$index === adInfo.VisitInfoList.length - 1"
|
||||
type="text"
|
||||
:title="$t('trials:adReview:table:viewR1')"
|
||||
@click="handleViewGl(scope.row, 1)"
|
||||
>
|
||||
<el-button v-if="scope.$index === adInfo.VisitInfoList.length - 1" type="text"
|
||||
:title="$t('trials:adReview:table:viewR1')" @click="handleViewGl(scope.row, 1)">
|
||||
R1
|
||||
</el-button>
|
||||
<!-- 查看R2详情 -->
|
||||
<el-button
|
||||
v-if="scope.$index === adInfo.VisitInfoList.length - 1"
|
||||
type="text"
|
||||
:title="$t('trials:adReview:table:viewR2')"
|
||||
@click="handleViewGl(scope.row, 2)"
|
||||
>
|
||||
<el-button v-if="scope.$index === adInfo.VisitInfoList.length - 1" type="text"
|
||||
:title="$t('trials:adReview:table:viewR2')" @click="handleViewGl(scope.row, 2)">
|
||||
R2
|
||||
</el-button>
|
||||
</template>
|
||||
|
|
@ -186,120 +136,64 @@
|
|||
$t("trials:adReview:title:adResult")
|
||||
}}</span>
|
||||
</div>
|
||||
<el-form
|
||||
ref="adForm"
|
||||
v-loading="loading"
|
||||
:model="adForm"
|
||||
style="width: 800px"
|
||||
label-width="100"
|
||||
>
|
||||
<el-form ref="adForm" v-loading="loading" :model="adForm" style="width: 800px" label-width="100">
|
||||
<!-- 选择阅片人 -->
|
||||
<el-form-item
|
||||
:label="$t('trials:adReview:title:choseReader')"
|
||||
prop="judgeResultTaskId"
|
||||
:rules="[
|
||||
<el-form-item :label="$t('trials:adReview:title:choseReader')" prop="judgeResultTaskId" :rules="[
|
||||
{ required: true, message: this.$t('common:ruleMessage:select') },
|
||||
]"
|
||||
>
|
||||
<el-radio-group
|
||||
v-model="adForm.judgeResultTaskId"
|
||||
:disabled="adInfo.ReadingTaskState >= 2"
|
||||
>
|
||||
<el-radio
|
||||
v-for="t in visitTaskArmList"
|
||||
:key="t.VisitTaskId"
|
||||
:label="t.VisitTaskId"
|
||||
@change="handleVisitTaskArmChange"
|
||||
>
|
||||
]">
|
||||
<el-radio-group v-model="adForm.judgeResultTaskId" :disabled="adInfo.ReadingTaskState >= 2">
|
||||
<el-radio v-for="t in visitTaskArmList" :key="t.VisitTaskId" :label="t.VisitTaskId"
|
||||
@change="handleVisitTaskArmChange">
|
||||
{{ $fd("ArmEnum", t.ArmEnum) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- 裁判原因 -->
|
||||
<el-form-item
|
||||
:label="$t('trials:adReview:title:adReason')"
|
||||
prop="judgeResultRemark"
|
||||
:rules="[
|
||||
<el-form-item :label="$t('trials:adReview:title:adReason')" prop="judgeResultRemark" :rules="[
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify') },
|
||||
{
|
||||
max: 500,
|
||||
message: `${this.$t('common:ruleMessage:maxLength')} 500`,
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
]"
|
||||
>
|
||||
]">
|
||||
<div style="position: relative">
|
||||
<div
|
||||
style="
|
||||
<div style="
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 30px;
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
"
|
||||
>
|
||||
">
|
||||
{{ remark }}
|
||||
</div>
|
||||
<el-input
|
||||
v-model="adForm.judgeResultRemark"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 4, maxRows: 6 }"
|
||||
:disabled="adInfo.ReadingTaskState >= 2"
|
||||
style="margin-top: 25px"
|
||||
/>
|
||||
<el-input v-model="adForm.judgeResultRemark" type="textarea" :autosize="{ minRows: 4, maxRows: 6 }"
|
||||
:disabled="adInfo.ReadingTaskState >= 2" style="margin-top: 25px" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<!-- 截图说明 -->
|
||||
<el-form-item :label="$t('trials:adReview:title:screenShot')">
|
||||
<el-upload
|
||||
action
|
||||
:accept="accept"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:http-request="uploadScreenshot"
|
||||
list-type="picture-card"
|
||||
:on-remove="handleRemove"
|
||||
:file-list="fileList"
|
||||
:class="{ disabled: adInfo.ReadingTaskState >= 2 }"
|
||||
:disabled="adInfo.ReadingTaskState >= 2"
|
||||
>
|
||||
<el-upload action :accept="accept" :on-preview="handlePictureCardPreview" :before-upload="handleBeforeUpload"
|
||||
:http-request="uploadScreenshot" list-type="picture-card" :on-remove="handleRemove" :file-list="fileList"
|
||||
:class="{ disabled: adInfo.ReadingTaskState >= 2 }" :disabled="adInfo.ReadingTaskState >= 2">
|
||||
<i slot="default" class="el-icon-plus" />
|
||||
<div
|
||||
slot="file"
|
||||
slot-scope="{ file }"
|
||||
style="width: 100%; height: 100%"
|
||||
>
|
||||
<viewer
|
||||
:ref="file.url"
|
||||
:images="images"
|
||||
style="
|
||||
<div slot="file" slot-scope="{ file }" style="width: 100%; height: 100%">
|
||||
<viewer :ref="file.url" :images="images" 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"
|
||||
alt=""
|
||||
crossorigin="anonymous"
|
||||
style="max-width: 100%; max-height: 100%"
|
||||
>
|
||||
">
|
||||
<img class="el-upload-list__item-thumbnail" :src="OSSclientConfig.basePath + file.url" alt=""
|
||||
crossorigin="anonymous" style="max-width: 100%; max-height: 100%">
|
||||
<span class="el-upload-list__item-actions">
|
||||
<span
|
||||
class="el-upload-list__item-preview"
|
||||
@click="handlePictureCardPreview(file)"
|
||||
>
|
||||
<span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
|
||||
<i class="el-icon-zoom-in" />
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-if="adInfo.ReadingTaskState < 2"
|
||||
class="el-upload-list__item-delete"
|
||||
@click="handleRemove(file)"
|
||||
>
|
||||
<span v-if="adInfo.ReadingTaskState < 2" class="el-upload-list__item-delete"
|
||||
@click="handleRemove(file)">
|
||||
<i class="el-icon-delete" />
|
||||
</span>
|
||||
</span>
|
||||
|
|
@ -324,10 +218,7 @@
|
|||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card
|
||||
v-if="isReadingShowPreviousResults"
|
||||
:body-style="{ padding: '10px' }"
|
||||
>
|
||||
<el-card v-if="isReadingShowPreviousResults" :body-style="{ padding: '10px' }">
|
||||
<div slot="header" class="clearfix">
|
||||
<!-- 既往裁判评估 -->
|
||||
<span style="font-weight: bold">{{
|
||||
|
|
@ -335,24 +226,12 @@
|
|||
}}</span>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="priorLoading"
|
||||
:data="priorADList"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table v-loading="priorLoading" :data="priorADList" style="width: 100%">
|
||||
<!-- 裁判阅片 -->
|
||||
<el-table-column
|
||||
prop="TaskBlindName"
|
||||
:label="$t('trials:adReview:table:adReading')"
|
||||
show-overflow-tooltip
|
||||
width="200"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="JudgeResultArm"
|
||||
:label="$t('trials:adReview:table:adResult')"
|
||||
show-overflow-tooltip
|
||||
width="200"
|
||||
>
|
||||
<el-table-column prop="TaskBlindName" :label="$t('trials:adReview:table:adReading')" show-overflow-tooltip
|
||||
width="200" />
|
||||
<el-table-column prop="JudgeResultArm" :label="$t('trials:adReview:table:adResult')" show-overflow-tooltip
|
||||
width="200">
|
||||
<template slot-scope="scope">
|
||||
{{ $fd("ArmEnum", scope.row.JudgeResultArm) }}
|
||||
</template>
|
||||
|
|
@ -360,35 +239,22 @@
|
|||
<el-table-column :label="$t('common:action:action')" width="200">
|
||||
<template slot-scope="scope">
|
||||
<!-- 查看详情 -->
|
||||
<el-button
|
||||
circle
|
||||
:title="$t('trials:adReview:table:view')"
|
||||
icon="el-icon-view"
|
||||
@click="handleViewDetail(scope.row.VisitTaskId)"
|
||||
/>
|
||||
<el-button circle :title="$t('trials:adReview:table:view')" icon="el-icon-view"
|
||||
@click="handleViewDetail(scope.row.VisitTaskId)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<!-- 签名框 -->
|
||||
<el-dialog
|
||||
v-if="signVisible"
|
||||
:visible.sync="signVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="600px"
|
||||
custom-class="base-dialog-wrapper"
|
||||
>
|
||||
<el-dialog v-if="signVisible" :visible.sync="signVisible" :close-on-click-modal="false" width="600px"
|
||||
custom-class="base-dialog-wrapper">
|
||||
<div slot="title">
|
||||
<span style="font-size: 18px">{{ $t("common:dialogTitle:sign") }}</span>
|
||||
<span style="font-size: 12px; margin-left: 5px">{{
|
||||
`(${$t("common:label:sign")}${currentUser})`
|
||||
}}</span>
|
||||
</div>
|
||||
<SignForm
|
||||
ref="signForm"
|
||||
:sign-code-enum="signCode"
|
||||
@closeDialog="closeSignDialog"
|
||||
/>
|
||||
<SignForm ref="signForm" :sign-code-enum="signCode" @closeDialog="closeSignDialog" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -729,17 +595,13 @@ export default {
|
|||
this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2 ) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
|
||||
this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${
|
||||
this.subjectId
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
|
||||
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
|
||||
this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${
|
||||
this.subjectId
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
|
||||
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
}
|
||||
var routeData = this.$router.resolve({ path })
|
||||
|
|
@ -766,21 +628,15 @@ export default {
|
|||
this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2 ) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
|
||||
this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${
|
||||
this.subjectId
|
||||
}&visitTaskId=${
|
||||
task.VisitTaskId
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
|
||||
}&visitTaskId=${task.VisitTaskId
|
||||
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
|
||||
this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${
|
||||
this.subjectId
|
||||
}&visitTaskId=${
|
||||
task.VisitTaskId
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
|
||||
}&visitTaskId=${task.VisitTaskId
|
||||
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
}
|
||||
var routeData = this.$router.resolve({ path })
|
||||
|
|
@ -801,21 +657,15 @@ export default {
|
|||
this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2 ) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
|
||||
this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${
|
||||
this.subjectId
|
||||
}&visitTaskId=${
|
||||
task.GlobalVisitTaskId
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
|
||||
}&visitTaskId=${task.GlobalVisitTaskId
|
||||
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
|
||||
this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${
|
||||
this.subjectId
|
||||
}&visitTaskId=${
|
||||
task.GlobalVisitTaskId
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
|
||||
}&visitTaskId=${task.GlobalVisitTaskId
|
||||
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
}
|
||||
var routeData = this.$router.resolve({ path })
|
||||
|
|
@ -932,17 +782,21 @@ export default {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
.box-mr {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
::v-deep .el-upload--picture-card {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-upload-list__item {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
::v-deep .el-upload-list__item-thumbnail {
|
||||
/* 图片在方框内显示长边 */
|
||||
object-fit: scale-down !important;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
<template>
|
||||
<div class="ContourViewport" ref="ContourViewport" id="ContourViewport"></div>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
getRenderingEngine,
|
||||
CONSTANTS,
|
||||
setVolumesForViewports,
|
||||
eventTarget,
|
||||
Enums,
|
||||
utilities,
|
||||
} from '@cornerstonejs/core'
|
||||
import * as cornerstoneTools from '@cornerstonejs/tools'
|
||||
import setCtTransferFunctionForVolumeActor from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setCtTransferFunctionForVolumeActor'
|
||||
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
|
||||
const {
|
||||
Enums: csToolsEnums,
|
||||
segmentation,
|
||||
TrackballRotateTool,
|
||||
ToolGroupManager
|
||||
} = cornerstoneTools
|
||||
const { MouseBindings, Events: toolsEvents } = csToolsEnums
|
||||
export default {
|
||||
name: "ContourViewport",
|
||||
props: {
|
||||
renderingEngineId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
viewportId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
visitInfo: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// visible: {
|
||||
// type: Boolean,
|
||||
// default: false
|
||||
// },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
volumeId: null,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
eventTarget.addEventListener(Enums.Events.WEB_WORKER_PROGRESS, (evt) => {
|
||||
const { progress } = evt.detail;
|
||||
console.log(progress, 'countour_progress')
|
||||
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
async setSeriesInfo(obj) {
|
||||
try {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
let { volumeId, segmentationId } = obj
|
||||
this.volumeId = volumeId
|
||||
await setVolumesForViewports(
|
||||
renderingEngine,
|
||||
[{ volumeId, callback: setCtTransferFunctionForVolumeActor }],
|
||||
[this.viewportId]
|
||||
);
|
||||
// const volumeActor = viewport.getDefaultActor()
|
||||
// .actor;
|
||||
// utilities.applyPreset(
|
||||
// volumeActor,
|
||||
// CONSTANTS.VIEWPORT_PRESETS.find((preset) => preset.name === 'CT-Bone')
|
||||
// );
|
||||
// volumeActor.setVisibility(false);
|
||||
viewport.render();
|
||||
const toolGroup = ToolGroupManager.getToolGroup(this.viewportId)
|
||||
toolGroup.setToolActive(TrackballRotateTool.toolName, {
|
||||
bindings: [
|
||||
{
|
||||
mouseButton: MouseBindings.Primary,
|
||||
},
|
||||
],
|
||||
});
|
||||
let s = segmentation.getActiveSegmentation(this.viewportId)
|
||||
if (s) {
|
||||
await segmentation.removeSegmentationRepresentation(this.viewportId, {
|
||||
segmentationId: s.segmentationId,
|
||||
type: csToolsEnums.SegmentationRepresentations.Surface,
|
||||
})
|
||||
}
|
||||
console.log("ContourLoading...")
|
||||
await segmentation.addSegmentationRepresentations(this.viewportId, [
|
||||
{
|
||||
segmentationId,
|
||||
type: csToolsEnums.SegmentationRepresentations.Contour,
|
||||
},
|
||||
]);
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
#ContourViewport {
|
||||
width: 450px;
|
||||
height: 300px;
|
||||
position: fixed;
|
||||
top: 100px;
|
||||
z-index: -9999;
|
||||
left: 100px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -76,9 +76,24 @@ import * as cornerstoneTools from '@cornerstonejs/tools'
|
|||
import { createImageIdsAndCacheMetaData } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/createImageIdsAndCacheMetaData'
|
||||
import setCtTransferFunctionForVolumeActor from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setCtTransferFunctionForVolumeActor'
|
||||
import { setCtMappingRange } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setCtTransferFunctionForVolumeActor'
|
||||
import { setPetColorMapTransferFunctionForVolumeActor } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setPetColorMapTransferFunctionForVolumeActor'
|
||||
import {
|
||||
setPetTransferFunctionForVolumeActor
|
||||
} from './helpers/index.js'
|
||||
import { vec3, mat4 } from 'gl-matrix'
|
||||
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
|
||||
import {
|
||||
renderSegmentation,
|
||||
readingSegmentByConfig,
|
||||
selectSegmentation,
|
||||
selectSegment,
|
||||
createSegmentationRepresentation,
|
||||
viewSegmentation,
|
||||
viewSegment,
|
||||
jumpBidirectional,
|
||||
viewBidirectional,
|
||||
changeColor,
|
||||
resetViewport
|
||||
} from "./helpers/segmentations"
|
||||
export default {
|
||||
name: 'MPRViewport',
|
||||
props: {
|
||||
|
|
@ -99,6 +114,36 @@ export default {
|
|||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
histogramVisible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
actionConfiguration: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
SegmentConfig: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
curSegSeries: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
segmentIndex: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
segmentationId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
|
@ -140,6 +185,7 @@ export default {
|
|||
rotateAngle: 0,
|
||||
rotateBarLeft: 0,
|
||||
loading: false,
|
||||
toggleClipPlayTimer: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -149,6 +195,39 @@ export default {
|
|||
this.$nextTick(() => {
|
||||
this.initViewport()
|
||||
})
|
||||
DicomEvent.$on('createSegmentationRepresentation', (segmentationId) => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
createSegmentationRepresentation(this.viewportId, segmentationId)
|
||||
})
|
||||
DicomEvent.$on('viewSegmentation', (obj) => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
viewSegmentation(obj, this.viewportId)
|
||||
})
|
||||
DicomEvent.$on('viewSegment', (obj) => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
viewSegment(obj, this.viewportId)
|
||||
})
|
||||
DicomEvent.$on('jumpBidirectional', (obj) => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
jumpBidirectional(obj, this.viewportId, this.volumeId)
|
||||
})
|
||||
DicomEvent.$on('viewBidirectional', (obj) => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
viewBidirectional(obj, this.viewportId)
|
||||
})
|
||||
DicomEvent.$on('changeColor', (obj) => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
changeColor(obj, this.viewportId)
|
||||
})
|
||||
DicomEvent.$on('resetViewport', () => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
resetViewport(this.viewportId)
|
||||
})
|
||||
DicomEvent.$on('renderSegmentation', async (viewportId) => {
|
||||
// if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
|
||||
if (this.viewportId !== viewportId) return false
|
||||
await renderSegmentation(this.series, this.series.TaskInfo, this.viewportId, this.SegmentConfig, this.renderingEngineId, null, this.actionConfiguration)
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
MPRInfo: {
|
||||
|
|
@ -167,6 +246,25 @@ export default {
|
|||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
SegmentConfig: {
|
||||
handler() {
|
||||
if (!this.segmentationId) return false
|
||||
if (!this.series.TaskInfo) return false
|
||||
readingSegmentByConfig(this.series, this.series.TaskInfo, this.viewportId, this.segmentationId, this.SegmentConfig)
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
segmentIndex() {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
|
||||
if (this.segmentIndex <= 0) return false
|
||||
selectSegment(this.viewportId, this.segmentationId, this.segmentIndex)
|
||||
},
|
||||
segmentationId() {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
|
||||
if (!this.segmentationId) return false
|
||||
selectSegmentation(this.viewportId, this.segmentationId)
|
||||
readingSegmentByConfig(this.series, this.series.TaskInfo, this.viewportId, this.segmentationId, this.SegmentConfig)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -183,6 +281,7 @@ export default {
|
|||
this.element.addEventListener("CORNERSTONE_VOLUME_NEW_IMAGE", this.stackNewImage)
|
||||
this.element.addEventListener('CORNERSTONE_VOI_MODIFIED', this.voiModified)
|
||||
this.element.addEventListener('wheel', (e) => {
|
||||
// if (this.histogramVisible) return false
|
||||
// console.log('CORNERSTONE_STACK_VIEWPORT_SCROLL')
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
|
|
@ -285,6 +384,7 @@ export default {
|
|||
this.imageInfo.sliceThickness = type === this.series.orientation ? spacing[2] : spacing[0]
|
||||
this.imageInfo.total = detail.numberOfSlices
|
||||
this.getOrientationMarker()
|
||||
this.$emit("resetHistogram")
|
||||
let properties = viewport.getProperties(this.volumeId)
|
||||
if (properties && properties.voiRange) {
|
||||
var { lower, upper } = properties.voiRange
|
||||
|
|
@ -392,11 +492,20 @@ export default {
|
|||
this.playClipState = isPlay
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
|
||||
if (isPlay) {
|
||||
cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: true })
|
||||
this.toggleClipPlayTimer = setInterval(() => {
|
||||
let index = this.series.SliceIndex + 1;
|
||||
if (index > this.imageInfo.total - 1) index = 0
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: index });
|
||||
|
||||
}, framesPerSecond)
|
||||
// cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: true })
|
||||
} else {
|
||||
cornerstoneTools.utilities.cine.stopClip(viewport.element)
|
||||
if (this.toggleClipPlayTimer) {
|
||||
clearInterval(this.toggleClipPlayTimer)
|
||||
this.toggleClipPlayTimer = null
|
||||
}
|
||||
// cornerstoneTools.utilities.cine.stopClip(viewport.element)
|
||||
}
|
||||
},
|
||||
scrollPage(type) {
|
||||
|
|
@ -490,7 +599,7 @@ export default {
|
|||
.setVolumes([{
|
||||
volumeId: this.volumeId, callback: (r) => {
|
||||
if (this.series.Modality === 'PT') {
|
||||
setPetColorMapTransferFunctionForVolumeActor(r, true)
|
||||
setPetTransferFunctionForVolumeActor(r)
|
||||
} else {
|
||||
let volume = cache.getVolume(this.volumeId)
|
||||
const voi = metaData.get('voiLutModule', volume._imageIds[Math.ceil((volume._imageIds.length - 1) / 2)])
|
||||
|
|
@ -498,7 +607,6 @@ export default {
|
|||
setCtTransferFunctionForVolumeActor(r)
|
||||
}
|
||||
console.log("渲染成功")
|
||||
DicomEvent.$emit("isloaded", { isChange: false })
|
||||
}
|
||||
}]).then(r => {
|
||||
if (data.isLocation || !this.imageInfo.zoom) {
|
||||
|
|
@ -506,6 +614,22 @@ export default {
|
|||
}
|
||||
})
|
||||
viewport.render()
|
||||
if (this.series.Modality === 'PT') {
|
||||
setTimeout(() => {
|
||||
viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true })
|
||||
viewport.resetProperties()
|
||||
viewport.setProperties({ voiRange: { upper: 5, lower: 0 } })
|
||||
viewport.render()
|
||||
renderingEngine.render()
|
||||
}, 100)
|
||||
}
|
||||
await renderSegmentation(this.series, this.series.TaskInfo, this.viewportId, this.SegmentConfig, this.renderingEngineId, null, this.actionConfiguration)
|
||||
DicomEvent.$emit('SegmentationLoading', this.viewportId)
|
||||
let volume = cache.getVolume(this.volumeId)
|
||||
// console.log(volume, 'volume')
|
||||
if (this.series.orientation === 'AXIAL' && this.series.curIndex) return this.setFullScreen(this.series.curIndex)
|
||||
let index = this.series.orientation === 'AXIAL' ? Math.ceil((volume._imageIds.length - 1) / 2) - 1 : Math.ceil((volume.dimensions[0]) / 2) - 1
|
||||
this.setFullScreen(index)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
|
@ -680,6 +804,12 @@ export default {
|
|||
return `NS: ${this.$store.state.trials.downloadTip}`
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
if (this.toggleClipPlayTimer) {
|
||||
clearInterval(this.toggleClipPlayTimer)
|
||||
this.toggleClipPlayTimer = null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -123,6 +123,10 @@ export default {
|
|||
type: Number,
|
||||
required: true
|
||||
},
|
||||
activeTool: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -254,10 +258,15 @@ export default {
|
|||
this.defaultWindowLevel.windowCenter = windowCenter
|
||||
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
|
||||
}
|
||||
const toolGroupId = this.viewportId
|
||||
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId)
|
||||
const toolGroup =
|
||||
cornerstoneTools.ToolGroupManager.getToolGroupForViewport(
|
||||
this.viewportId,
|
||||
this.renderingEngineId
|
||||
) || cornerstoneTools.ToolGroupManager.getToolGroup(this.viewportId)
|
||||
if (toolGroup) {
|
||||
toolGroup.setToolEnabled('ScaleOverlay')
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
setFullScreen(index) {
|
||||
|
|
@ -829,6 +838,7 @@ export default {
|
|||
this.sliderInfo.isMove = false
|
||||
},
|
||||
handletoolsMouseWheel(e) {
|
||||
if (this.activeTool === 'Crosshairs') return
|
||||
const { viewportId, wheel } = e.detail
|
||||
if (this.isMip) {
|
||||
const container = document.getElementById('rotateBar')
|
||||
|
|
@ -852,6 +862,7 @@ export default {
|
|||
this.rotateBarInfo.isMove = false
|
||||
},
|
||||
rotateBarMousemove(e) {
|
||||
if (this.activeTool === 'Crosshairs') return
|
||||
// 滚动旋转
|
||||
if (!this.rotateBarInfo.isMove) return
|
||||
const container = document.getElementById('rotateBar')
|
||||
|
|
@ -867,6 +878,7 @@ export default {
|
|||
this.rotateBarLeft = x
|
||||
},
|
||||
rotateBarMousedown(e) {
|
||||
if (this.activeTool === 'Crosshairs') return
|
||||
this.rotateBarInfo.initLeft = e.srcElement.offsetLeft
|
||||
this.rotateBarInfo.initX = e.clientX
|
||||
this.rotateBarInfo.isMove = true
|
||||
|
|
@ -905,6 +917,7 @@ export default {
|
|||
viewport.render()
|
||||
},
|
||||
clickRotate(e) {
|
||||
if (this.activeTool === 'Crosshairs') return
|
||||
// console.log('clickRotate')
|
||||
const container = document.getElementById('rotateBar')
|
||||
const containerWidth = container.offsetWidth
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@
|
|||
<div v-for="s in visitTaskList" v-show="activeTaskId === s.VisitTaskId" :key="s.VisitTaskId"
|
||||
style="height:100%;">
|
||||
<study-list v-if="selectArr.includes(s.VisitTaskId) && s.StudyList.length > 0" :ref="s.VisitTaskId"
|
||||
:visit-task-info="s" :marked-series-ids="markedSeriesIds" @activeSeries="activeSeries"
|
||||
@showMultiFrame="showMultiFrame" />
|
||||
:visit-task-info="s" :marked-series-ids="markedSeriesIds" :readingTool="readingTool"
|
||||
@activeSeries="activeSeries" @showMultiFrame="showMultiFrame" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -165,6 +165,11 @@
|
|||
v-if="(criterionType === 0 && readingTool === 0) || this.readingTool === 3">
|
||||
<svg-icon icon-class="mpr" class="svg-icon" style="transform: rotate(180deg);" />
|
||||
</div>
|
||||
<!-- 直方图 -->
|
||||
<div class="tool-item" :title="`${$t('trials:reading:button:histogram')}`" @click.prevent="openHistogram"
|
||||
v-if="this.readingTool === 3">
|
||||
<svg-icon icon-class="histogram" class="svg-icon" />
|
||||
</div>
|
||||
<!-- 十字准星 -->
|
||||
<div :class="['tool-item', activeTool === 'Crosshairs' ? 'tool-item-active' : '']" v-if="isMPR"
|
||||
:title="$t('trials:reading:button:crosshairs')" @click.prevent="setToolActive('Crosshairs')">
|
||||
|
|
@ -175,6 +180,11 @@
|
|||
@click.prevent="openFusion">
|
||||
<svg-icon icon-class="fusion" class="svg-icon" />
|
||||
</div>
|
||||
<div :class="['tool-item', activeTool === 'Crosshairs' ? 'tool-item-active' : '']"
|
||||
v-if="readingTool === 2 && isFusion" :title="$t('trials:reading:button:crosshairs')"
|
||||
@click.prevent="setToolActive('Crosshairs')">
|
||||
<svg-icon icon-class="crosshairs" class="svg-icon" />
|
||||
</div>
|
||||
<div v-for="tool in tools" :key="tool.toolName"
|
||||
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === tool.toolName ? 'tool-item-active' : '']"
|
||||
:style="{ cursor: tool.isDisabled ? 'not-allowed' : 'pointer' }"
|
||||
|
|
@ -308,8 +318,13 @@
|
|||
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
|
||||
<VolumeViewport :ref="`viewport-${index}`" :data-viewport-uid="`viewport-${index}`"
|
||||
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index"
|
||||
@activeViewport="activeViewport" @toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
|
||||
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" v-if="readingTool === 3" />
|
||||
:histogramVisible="histogramVisible" :actionConfiguration="actionConfiguration"
|
||||
:SegmentConfig="SegmentConfig" :segmentationId.sync="segId" :segmentIndex.sync="segIndex"
|
||||
:curSegSeries.sync="curSegSeries" @activeViewport="activeViewport"
|
||||
@toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
|
||||
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup"
|
||||
@resetViewport="resetViewport" @resetHistogram="resetHistogram" v-if="readingTool === 3"
|
||||
v-resize="(e) => handleSizeChange(e, `viewport-${index}`)" />
|
||||
<Viewport :ref="`viewport-${index}`" :data-viewport-uid="`viewport-${index}`"
|
||||
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index"
|
||||
@activeViewport="activeViewport" @toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
|
||||
|
|
@ -325,9 +340,13 @@
|
|||
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
|
||||
<MPRViewport :ref="`viewport-MPR-${index}`" :data-viewport-uid="`viewport-MPR-${index}`"
|
||||
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-MPR-${index}`"
|
||||
:viewport-index="index" :MPRInfo="MPRInfo" @activeViewport="activeViewport" @setMPRInfo="setMPRInfo"
|
||||
:viewport-index="index" :histogramVisible="histogramVisible"
|
||||
:actionConfiguration="actionConfiguration" :SegmentConfig="SegmentConfig"
|
||||
:segmentationId.sync="segId" :segmentIndex.sync="segIndex" :curSegSeries.sync="curSegSeries"
|
||||
:MPRInfo="MPRInfo" @activeViewport="activeViewport" @setMPRInfo="setMPRInfo"
|
||||
@toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
|
||||
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" />
|
||||
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup"
|
||||
@resetHistogram="resetHistogram" v-resize="(e) => handleSizeChange(e, `viewport-MPR-${index}`)" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="readingTool === 2"
|
||||
|
|
@ -335,15 +354,19 @@
|
|||
:style="gridStyle">
|
||||
<div v-for="(v, index) in cellsMax" v-show="index < cells.length" :key="`viewport-fusion-${index}`"
|
||||
:class="['grid-cell', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
||||
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
|
||||
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)"
|
||||
@mouseenter="hoverFusionViewport(index)" @mouseleave="hoverFusionViewport(-1)">
|
||||
<PetCtViewport :ref="`viewport-fusion-${index}`" :data-viewport-uid="`viewport-fusion-${index}`"
|
||||
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-fusion-${index}`"
|
||||
:viewport-index="index" @activeViewport="activeViewport"
|
||||
:viewport-index="index" :active-tool="activeTool" @activeViewport="activeViewport"
|
||||
@toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
|
||||
@renderAnnotations="renderAnnotations" @upperRangeChange="upperRangeChange"
|
||||
@contentMouseup="contentMouseup" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="readingTool === 2" class="fusion-hidden-viewports">
|
||||
<div ref="viewport-fusion-hidden-sag" class="fusion-hidden-viewport" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 表单 -->
|
||||
|
|
@ -352,9 +375,11 @@
|
|||
<el-tab-pane :label="$t('trials:reading:dicom3D:tabs:segment')" name="segment">
|
||||
<Segmentations ref="Segmentations" :visitInfo="taskInfo" :isMPR="isMPR"
|
||||
:volumeToolGroupId="volumeToolGroupId" :viewportKey="viewportKey" :global-loading.sync="loading"
|
||||
:loadingText.sync="loadingText" :rendering-engine-id="renderingEngineId"
|
||||
:activeViewportIndex="activeViewportIndex" :activeTool.sync="activeTool"
|
||||
:actionConfiguration="actionConfiguration" @setToolsPassive="setToolsPassive"
|
||||
:trialCriterion="trialCriterion" :loadingText.sync="loadingText"
|
||||
:rendering-engine-id="renderingEngineId" :SegmentConfig="SegmentConfig" :segId.sync="segId"
|
||||
:segIndex.sync="segIndex" :curSegSeries.sync="curSegSeries" :activeViewportIndex="activeViewportIndex"
|
||||
:activeTool.sync="activeTool" :actionConfiguration="actionConfiguration"
|
||||
:histogramVisible="histogramVisible" @setToolsPassive="setToolsPassive"
|
||||
@resetQuestion="resetQuestion" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('trials:reading:dicom3D:tabs:ecrf')" name="ecrf">
|
||||
|
|
@ -486,6 +511,14 @@
|
|||
<SegmentForm ref="SegmentForm" v-if="segmentVisible" :visible.sync="segmentVisible" :visitInfo="segmentVisitInfo"
|
||||
@handleSegmentSave="handleSegmentSave" />
|
||||
</el-dialog>
|
||||
<!--直方图-->
|
||||
<histogram ref="histogram" v-if="readingTool === 3" :visible.sync="histogramVisible" :activeTool.sync="activeTool"
|
||||
:viewportKey="viewportKey" :rendering-engine-id="renderingEngineId" :activeViewportIndex="activeViewportIndex" />
|
||||
<!--分割可视化窗口-->
|
||||
<!-- <SurfaceViewport ref="surfaceViewport" viewportId="surfaceViewport" v-if="readingTool === 3"
|
||||
:visible.sync="surfaceVisible" :renderingEngineId="renderingEngineId" :visitInfo="taskInfo" />
|
||||
<ContourViewport ref="contourViewport" viewportId="contourViewport" v-if="readingTool === 3"
|
||||
:renderingEngineId="renderingEngineId" :visitInfo="taskInfo" /> -->
|
||||
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :subject-id="uploadSubjectId"
|
||||
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible"
|
||||
:visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" />
|
||||
|
|
@ -504,6 +537,7 @@ import {
|
|||
RenderingEngine,
|
||||
Enums,
|
||||
// imageLoader,
|
||||
// CONSTANTS,
|
||||
metaData,
|
||||
volumeLoader,
|
||||
getRenderingEngine,
|
||||
|
|
@ -524,6 +558,9 @@ import PetCtViewport from './PetCtViewport'
|
|||
import MPRViewport from './MPRViewport'
|
||||
import VolumeViewport from './VolumeViewport'
|
||||
import Segmentations from './Segmentations'
|
||||
import histogram from "./histogram"
|
||||
// import SurfaceViewport from "./SurfaceViewport"
|
||||
// import ContourViewport from "./ContourViewport"
|
||||
import mRecisit from './mRecist/QuestionList'
|
||||
import recisit from './Recist/QuestionList'
|
||||
import customizeQuestionList from './customize/QuestionList'
|
||||
|
|
@ -554,6 +591,9 @@ const {
|
|||
ToolGroupManager,
|
||||
Enums: csToolsEnums,
|
||||
StackScrollTool,
|
||||
TrackballRotateTool,
|
||||
PlanarFreehandContourSegmentationTool,
|
||||
SplineContourSegmentationTool,
|
||||
// ScaleOverlayTool,
|
||||
PanTool,
|
||||
ZoomTool,
|
||||
|
|
@ -615,6 +655,9 @@ export default {
|
|||
MPRViewport,
|
||||
VolumeViewport,
|
||||
Segmentations,
|
||||
histogram,
|
||||
// SurfaceViewport,
|
||||
// ContourViewport,
|
||||
mRecisit,
|
||||
recisit,
|
||||
customizeQuestionList,
|
||||
|
|
@ -648,7 +691,7 @@ export default {
|
|||
activeTaskIndex: -1,
|
||||
activeStudyIndex: -1,
|
||||
activeSeriesIndex: -1,
|
||||
currentVisitInfo: null,
|
||||
currentVisitInfo: {},
|
||||
layout: 1,
|
||||
cellsMax: 4,
|
||||
rows: 1,
|
||||
|
|
@ -729,6 +772,7 @@ export default {
|
|||
|
||||
isMPR: false,
|
||||
volumeToolGroupId: "share-viewport-volume",
|
||||
fusionToolGroupId: "share-viewport-fusion",
|
||||
MPRInfo: {
|
||||
AXIAL: {
|
||||
imageNum: 0
|
||||
|
|
@ -758,12 +802,28 @@ export default {
|
|||
},
|
||||
},
|
||||
},
|
||||
SegmentConfig: {
|
||||
renderOutline: true,
|
||||
renderFill: true,
|
||||
fillAlpha: 0.5,
|
||||
outlineWidth: 1,
|
||||
InactiveSegmentations: {
|
||||
show: true,
|
||||
fillAlpha: 0.3,
|
||||
}
|
||||
},
|
||||
segId: null,
|
||||
segIndex: null,
|
||||
curSegSeries: {},
|
||||
fusionOverlayModality: null,
|
||||
lastUpper: null,
|
||||
hasFusionUpperInitialized: false,
|
||||
timer: null,
|
||||
timer: {},
|
||||
FullTimerOut: null,
|
||||
isDelay: false
|
||||
isDelay: false,
|
||||
|
||||
histogramVisible: false,
|
||||
// surfaceVisible: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -856,7 +916,38 @@ export default {
|
|||
this.setToolsPassive()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// histogramVisible: {
|
||||
// handler() {
|
||||
// if (this.readingTool !== 3) return false
|
||||
// this.setToolsPassive()
|
||||
// let viewportIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3', this.volumeToolGroupId]
|
||||
// // if (this.isMPR) {
|
||||
// // viewportIds = [this.volumeToolGroupId]
|
||||
// // }
|
||||
// viewportIds.forEach(id => {
|
||||
// const toolGroup = ToolGroupManager.getToolGroup(id)
|
||||
// if (this.histogramVisible) {
|
||||
// toolGroup.setToolEnabled(StackScrollTool.toolName)
|
||||
// } else {
|
||||
// toolGroup.setToolActive(StackScrollTool.toolName, {
|
||||
// bindings: [{ mouseButton: MouseBindings.Wheel }]
|
||||
// })
|
||||
// let annotations = annotation.state.getAllAnnotations().filter(item => item.metadata.toolName.includes('histogram_'));
|
||||
// annotations.forEach(item => {
|
||||
// annotation.state.removeAnnotation(item.annotationUID)
|
||||
// })
|
||||
// for (let i = 0; i < this.cells.length; i++) {
|
||||
// const viewportId = `${this.viewportKey}-${i}`
|
||||
// let renderingEngine = getRenderingEngine(renderingEngineId)
|
||||
// const viewport = renderingEngine.getViewport(viewportId)
|
||||
// viewport.render()
|
||||
// }
|
||||
// }
|
||||
|
||||
// })
|
||||
// }
|
||||
// },
|
||||
},
|
||||
mounted() {
|
||||
this.taskInfo = JSON.parse(sessionStorage.getItem('taskInfo'))
|
||||
|
|
@ -904,6 +995,36 @@ export default {
|
|||
this.getSystemInfoReading();
|
||||
},
|
||||
methods: {
|
||||
resetHistogram() {
|
||||
if (!this.histogramVisible) return false
|
||||
if (this.timer['histogram']) {
|
||||
clearTimeout(this.timer['histogram'])
|
||||
this.timer['histogram'] = null
|
||||
}
|
||||
this.timer['histogram'] = setTimeout(() => {
|
||||
if (this.$refs.histogram && this.histogramVisible) {
|
||||
this.$refs.histogram.init()
|
||||
}
|
||||
clearTimeout(this.timer['histogram'])
|
||||
this.timer['histogram'] = null
|
||||
}, 500)
|
||||
|
||||
},
|
||||
showSurface(obj) {
|
||||
// this.surfaceVisible = true
|
||||
// this.$refs.contourViewport.setSeriesInfo(obj)
|
||||
// this.$refs.surfaceViewport.setSeriesInfo(obj)
|
||||
},
|
||||
async openHistogram() {
|
||||
this.histogramVisible = true
|
||||
this.setToolsPassive()
|
||||
this.$refs.histogram.init()
|
||||
|
||||
},
|
||||
handleSizeChange(e, viewportId) {
|
||||
let index = this.$refs[viewportId][0].series.SliceIndex
|
||||
this.resetRenderingEngine(viewportId, index)
|
||||
},
|
||||
resetQuestion() {
|
||||
this.$refs[`ecrf_${this.lastViewportTaskId}`][0].getQuestions(false)
|
||||
this.$refs[`ecrf_${this.lastViewportTaskId}`][0].initSegmentBinding()
|
||||
|
|
@ -1240,6 +1361,27 @@ export default {
|
|||
}
|
||||
}
|
||||
]
|
||||
// let element5 = this.$refs.surfaceViewport.$el
|
||||
// let element6 = this.$refs.contourViewport.$el
|
||||
// viewportInputArray.push({
|
||||
// viewportId: 'surfaceViewport',
|
||||
// type: ViewportType.VOLUME_3D,
|
||||
// element: element5,
|
||||
// defaultOptions: {
|
||||
// orientation: Enums.OrientationAxis.CORONAL,
|
||||
// background: [1, 1, 1]
|
||||
// }
|
||||
// })
|
||||
// viewportInputArray.push({
|
||||
// viewportId: 'contourViewport',
|
||||
// type: ViewportType.ORTHOGRAPHIC,
|
||||
// element: element6,
|
||||
// defaultOptions: {
|
||||
// orientation: Enums.OrientationAxis.AXIAL
|
||||
// }
|
||||
// })
|
||||
// viewportIds.push('surfaceViewport')
|
||||
// viewportIds.push('contourViewport')
|
||||
}
|
||||
if ((this.criterionType === 0 && this.readingTool === 0) || this.readingTool === 3) {
|
||||
const volumeElement1 = this.$refs['viewport-MPR-0'][0].$el
|
||||
|
|
@ -1279,6 +1421,7 @@ export default {
|
|||
const fusionElement2 = this.$refs['viewport-fusion-1'][0].$el
|
||||
const fusionElement3 = this.$refs['viewport-fusion-2'][0].$el
|
||||
const fusionElement4 = this.$refs['viewport-fusion-3'][0].$el
|
||||
const fusionHiddenSag = this.$refs['viewport-fusion-hidden-sag']
|
||||
const arr = [
|
||||
{
|
||||
viewportId: 'viewport-fusion-0',
|
||||
|
|
@ -1313,13 +1456,23 @@ export default {
|
|||
orientation: Enums.OrientationAxis.CORONAL,
|
||||
background: [1, 1, 1]
|
||||
}
|
||||
},
|
||||
{
|
||||
viewportId: 'viewport-fusion-hidden-sag',
|
||||
type: ViewportType.ORTHOGRAPHIC,
|
||||
element: fusionHiddenSag,
|
||||
defaultOptions: {
|
||||
orientation: Enums.OrientationAxis.SAGITTAL,
|
||||
background: [1, 1, 1]
|
||||
}
|
||||
}
|
||||
]
|
||||
viewportInputArray = [...viewportInputArray, ...arr]
|
||||
viewportIds = viewportIds.concat(fusionViewportIds)
|
||||
viewportIds = viewportIds.concat(fusionViewportIds, ['viewport-fusion-hidden-sag'])
|
||||
}
|
||||
renderingEngine.setViewports(viewportInputArray)
|
||||
this.addAnnotationListeners()
|
||||
// cornerstoneTools.addTool(TrackballRotateTool)
|
||||
cornerstoneTools.addTool(StackScrollTool)
|
||||
cornerstoneTools.addTool(PanTool)
|
||||
cornerstoneTools.addTool(ZoomTool)
|
||||
|
|
@ -1344,24 +1497,63 @@ export default {
|
|||
cornerstoneTools.addTool(LabelMapEditWithContourTool)
|
||||
cornerstoneTools.addTool(BrushTool)
|
||||
cornerstoneTools.addTool(SegmentBidirectionalTool)
|
||||
// cornerstoneTools.addTool(PlanarFreehandContourSegmentationTool);
|
||||
// cornerstoneTools.addTool(SplineContourSegmentationTool);
|
||||
viewportIds.forEach((viewportId, i) => {
|
||||
// const toolGroupId = `viewport-${i}`
|
||||
let toolGroupId = viewportId
|
||||
if (volumeViewportIds.includes(viewportId)) {
|
||||
toolGroupId = this.volumeToolGroupId
|
||||
} else if (viewportId.startsWith('viewport-fusion-')) {
|
||||
toolGroupId = this.fusionToolGroupId
|
||||
}
|
||||
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId) ? ToolGroupManager.getToolGroup(toolGroupId) : ToolGroupManager.createToolGroup(toolGroupId)
|
||||
toolGroup.addViewport(viewportId, renderingEngineId)
|
||||
if (toolGroupId.includes('surface')) {
|
||||
// toolGroup.addTool(TrackballRotateTool.toolName, {
|
||||
// rotateSampleDistanceFactor: 0,
|
||||
// });
|
||||
// toolGroup.setToolActive(TrackballRotateTool.toolName, {
|
||||
// bindings: [
|
||||
// {
|
||||
// mouseButton: MouseBindings.Primary,
|
||||
// },
|
||||
// ],
|
||||
// });
|
||||
} else if (toolGroupId.includes('contour')) {
|
||||
// toolGroup.addTool(PlanarFreehandContourSegmentationTool.toolName);
|
||||
// toolGroup.addTool(SplineContourSegmentationTool.toolName);
|
||||
// toolGroup.setToolActive(PlanarFreehandContourSegmentationTool.toolName, {
|
||||
// bindings: [
|
||||
// {
|
||||
// mouseButton: MouseBindings.Primary, // Middle Click
|
||||
// },
|
||||
// ],
|
||||
// });
|
||||
} else {
|
||||
toolGroup.addTool(StackScrollTool.toolName, {
|
||||
loop: true, // 启用循环滚动
|
||||
})
|
||||
|
||||
toolGroup.addTool(ScaleOverlayTool.toolName)
|
||||
|
||||
toolGroup.addTool(PanTool.toolName)
|
||||
toolGroup.addTool(ZoomTool.toolName)
|
||||
toolGroup.addTool(BrushTool.toolName)
|
||||
if (this.readingTool === 3 || toolGroupId === this.volumeToolGroupId) {
|
||||
toolGroup.addToolInstance(
|
||||
'histogram_RectangleROI',
|
||||
RectangleROITool.toolName,
|
||||
);
|
||||
toolGroup.addToolInstance(
|
||||
'histogram_CircleROI',
|
||||
CircleROITool.toolName,
|
||||
);
|
||||
toolGroup.addToolInstance(
|
||||
'histogram_PlanarFreehandROI',
|
||||
PlanarFreehandROITool.toolName,
|
||||
);
|
||||
toolGroup.addToolInstance(
|
||||
'CircularBrush',
|
||||
BrushTool.toolName,
|
||||
|
|
@ -1409,6 +1601,11 @@ export default {
|
|||
toolGroup.addTool(CrosshairsTool.toolName, {
|
||||
getReferenceLineColor: this.setCrosshairsToolLineColor
|
||||
});
|
||||
} else if (toolGroupId === this.fusionToolGroupId) {
|
||||
toolGroup.addTool(CrosshairsTool.toolName, {
|
||||
getReferenceLineColor: this.setCrosshairsToolLineColor,
|
||||
getReferenceLineSlabThicknessControlsOn: (otherViewportId) => otherViewportId !== 'viewport-fusion-3'
|
||||
});
|
||||
} else {
|
||||
toolGroup.addTool(WindowLevelTool.toolName)
|
||||
}
|
||||
|
|
@ -1450,26 +1647,20 @@ export default {
|
|||
toolGroup.addTool(EllipticalROITool.toolName, {
|
||||
getTextLines: this.getEllipticalROIToolTextLines
|
||||
})
|
||||
toolGroup.addTool(FixedRadiusCircleROITool.toolName), {
|
||||
toolGroup.addTool(FixedRadiusCircleROITool.toolName, {
|
||||
radius: Number.isFinite(this.taskInfo.CircleRadius) ? this.taskInfo.CircleRadius : 6,
|
||||
getTextLines: this.getCircleROIToolTextLines
|
||||
}
|
||||
})
|
||||
toolGroup.addTool(AngleTool.toolName, {
|
||||
getTextLines: this.getAngleToolTextLines
|
||||
})
|
||||
toolGroup.addTool(CobbAngleTool.toolName, {
|
||||
getTextLines: this.getCobbAngleToolTextLines
|
||||
})
|
||||
if (toolGroupId === 'viewport-fusion-3') {
|
||||
if (viewportId === 'viewport-fusion-3') {
|
||||
toolGroup.addTool(VolumeRotateTool.toolName)
|
||||
toolGroup.setToolActive(VolumeRotateTool.toolName, {
|
||||
bindings: [
|
||||
{
|
||||
mouseButton: MouseBindings.Wheel // mouse wheel
|
||||
}
|
||||
]
|
||||
})
|
||||
toolGroup.addTool(MIPJumpToClickTool.toolName, {
|
||||
targetViewportIds: fusionViewportIds.filter((id) => id !== toolGroupId)
|
||||
targetViewportIds: fusionViewportIds.filter((id) => id !== viewportId)
|
||||
})
|
||||
|
||||
// Set the initial state of the tools, here we set one tool active on left click.
|
||||
|
|
@ -1482,6 +1673,7 @@ export default {
|
|||
]
|
||||
})
|
||||
}
|
||||
|
||||
toolGroup.setToolConfiguration(
|
||||
ScaleOverlayTool.toolName,
|
||||
{
|
||||
|
|
@ -1501,7 +1693,6 @@ export default {
|
|||
toolGroup.setToolPassive(WindowLevelTool.toolName)
|
||||
toolGroup.setToolPassive(WindowLevelRegionTool.toolName)
|
||||
toolGroup.setToolPassive(PlanarRotateTool.toolName)
|
||||
|
||||
if (this.readingTaskState < 2) {
|
||||
toolGroup.setToolPassive(ArrowAnnotateTool.toolName)
|
||||
toolGroup.setToolPassive(RectangleROITool.toolName)
|
||||
|
|
@ -1528,9 +1719,14 @@ export default {
|
|||
toolGroup.setToolEnabled(FixedRadiusCircleROITool.toolName)
|
||||
toolGroup.setToolEnabled(AngleTool.toolName)
|
||||
toolGroup.setToolEnabled(CobbAngleTool.toolName)
|
||||
if (this.readingTool === 3) toolGroup.setToolEnabled(LabelMapEditWithContourTool.toolName)
|
||||
if (this.readingTool === 3) {
|
||||
toolGroup.setToolEnabled(LabelMapEditWithContourTool.toolName)
|
||||
toolGroup.setToolEnabled(SegmentBidirectionalTool.toolName);
|
||||
}
|
||||
}
|
||||
toolGroup.setToolPassive(EraserTool.toolName)
|
||||
}
|
||||
|
||||
})
|
||||
eventTarget.addEventListener('cornerstoneimageloadprogress', this.imageLoadProgress)
|
||||
// console.log(Events, toolsEvents)
|
||||
|
|
@ -1570,6 +1766,10 @@ export default {
|
|||
setNetWorkSpeedSizeAll(percentComplete, detail.total, imageId)
|
||||
}
|
||||
}
|
||||
if (this.readingTool === 3) {
|
||||
getNetWorkSpeed()
|
||||
setNetWorkSpeedSizeAll(percentComplete, detail.total, imageId)
|
||||
}
|
||||
if (percentComplete === 100) {
|
||||
workSpeedclose()
|
||||
}
|
||||
|
|
@ -1674,9 +1874,10 @@ export default {
|
|||
|
||||
annotationCompletedListener(e) {
|
||||
console.log('Completed')
|
||||
if (this.readingTaskState === 2) return
|
||||
const { annotation } = e.detail
|
||||
if (!annotation) return
|
||||
if (annotation.metadata.toolName.includes('histogram_')) return this.$refs.histogram.initToolValue(annotation)
|
||||
if (this.readingTaskState === 2) return
|
||||
if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
|
||||
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
|
||||
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
|
||||
|
|
@ -1699,10 +1900,11 @@ export default {
|
|||
},
|
||||
annotationModifiedListener(e) {
|
||||
console.log('Modified')
|
||||
if (this.readingTaskState === 2) return
|
||||
const { annotation } = e.detail
|
||||
if (!annotation.highlighted) return
|
||||
if (!annotation) return
|
||||
if (annotation.metadata.toolName.includes('histogram_')) return this.$refs.histogram.initToolValue(annotation)
|
||||
if (this.readingTaskState === 2) return
|
||||
if (!annotation.highlighted) return
|
||||
if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
|
||||
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
|
||||
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
|
||||
|
|
@ -1742,9 +1944,10 @@ export default {
|
|||
}
|
||||
},
|
||||
async customAnnotationCompletedListener(e) {
|
||||
if (this.readingTaskState === 2) return
|
||||
const { annotation } = e.detail
|
||||
if (!annotation) return
|
||||
if (annotation.metadata.toolName.includes('histogram_')) return this.$refs.histogram.initToolValue(annotation)
|
||||
if (this.readingTaskState === 2) return
|
||||
const i = this.tools.findIndex(i => i.toolName === annotation.metadata.toolName)
|
||||
if (i === -1) {
|
||||
if (annotation.metadata.toolName !== LabelMapEditWithContourTool.toolName) this.setToolsPassive()
|
||||
|
|
@ -1829,10 +2032,11 @@ export default {
|
|||
}
|
||||
},
|
||||
customAnnotationModifiedListener(e) {
|
||||
if (this.readingTaskState === 2) return
|
||||
const { annotation } = e.detail
|
||||
if (!annotation.highlighted) return
|
||||
if (!annotation) return
|
||||
if (annotation.metadata.toolName.includes('histogram_')) return this.$refs.histogram.initToolValue(annotation)
|
||||
if (this.readingTaskState === 2) return
|
||||
if (!annotation.highlighted) return
|
||||
const i = this.tools.findIndex(i => i.toolName === annotation.metadata.toolName)
|
||||
if (i === -1) {
|
||||
if (annotation.metadata.toolName !== LabelMapEditWithContourTool.toolName) this.setToolsPassive()
|
||||
|
|
@ -1864,8 +2068,14 @@ export default {
|
|||
const errorMsg = { message: 'annotation Not allowed to operate' }
|
||||
throw errorMsg
|
||||
}
|
||||
if (this.activeTool !== 'Eraser') return false
|
||||
const i = this.tools.findIndex(i => i.toolName === annotation.metadata.toolName)
|
||||
if (i === -1) {
|
||||
if (annotation.metadata.toolName === SegmentBidirectionalTool.toolName) {
|
||||
this.setToolsPassive()
|
||||
const errorMsg = { message: 'SegmentBidirectionalTool Not remove' }
|
||||
throw errorMsg
|
||||
}
|
||||
// 临时标记
|
||||
return
|
||||
}
|
||||
|
|
@ -2315,21 +2525,61 @@ export default {
|
|||
const factor = 10 ** precision
|
||||
return (Math.round(num * factor + 0.0000001) / factor).toFixed(precision)
|
||||
},
|
||||
getActiveToolGroupId() {
|
||||
if (this.isMPR) return this.volumeToolGroupId
|
||||
if (this.isFusion) return this.fusionToolGroupId
|
||||
return `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
},
|
||||
getCurrentToolGroupIds() {
|
||||
if (this.isMPR) return [this.volumeToolGroupId]
|
||||
if (this.isFusion) return [this.fusionToolGroupId]
|
||||
return [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
|
||||
},
|
||||
setFusionMipJumpEnabled(enabled) {
|
||||
if (!this.isFusion) return
|
||||
const toolGroup = ToolGroupManager.getToolGroup(this.fusionToolGroupId)
|
||||
if (!toolGroup || !toolGroup.hasTool(MIPJumpToClickTool.toolName)) return
|
||||
if (enabled) {
|
||||
toolGroup.setToolActive(MIPJumpToClickTool.toolName, {
|
||||
bindings: [{ mouseButton: MouseBindings.Primary }]
|
||||
})
|
||||
} else {
|
||||
toolGroup.setToolDisabled(MIPJumpToClickTool.toolName)
|
||||
}
|
||||
},
|
||||
setFusionMipRotateEnabled(enabled) {
|
||||
if (!this.isFusion) return
|
||||
const toolGroup = ToolGroupManager.getToolGroup(this.fusionToolGroupId)
|
||||
if (!toolGroup || !toolGroup.hasTool(VolumeRotateTool.toolName)) return
|
||||
if (enabled) {
|
||||
toolGroup.setToolActive(VolumeRotateTool.toolName, {
|
||||
bindings: [{ mouseButton: MouseBindings.Wheel }]
|
||||
})
|
||||
} else {
|
||||
toolGroup.setToolDisabled(VolumeRotateTool.toolName)
|
||||
}
|
||||
},
|
||||
// 激活工具
|
||||
setToolActive(toolName) {
|
||||
const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
if (this.histogramVisible) return false
|
||||
const toolGroupId = this.getActiveToolGroupId()
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup) return
|
||||
if (this.activeTool === toolName) {
|
||||
if (toolName === CrosshairsTool.toolName) {
|
||||
toolGroup.setToolDisabled(this.activeTool)
|
||||
this.setFusionMipJumpEnabled(true)
|
||||
this.setFusionMipRotateEnabled(true)
|
||||
} else {
|
||||
toolGroup.setToolPassive(this.activeTool)
|
||||
}
|
||||
this.activeTool = ''
|
||||
} else {
|
||||
if (this.activeTool) {
|
||||
if (toolName === CrosshairsTool.toolName) {
|
||||
if (this.activeTool === CrosshairsTool.toolName) {
|
||||
toolGroup.setToolDisabled(this.activeTool)
|
||||
this.setFusionMipJumpEnabled(true)
|
||||
this.setFusionMipRotateEnabled(true)
|
||||
} else {
|
||||
toolGroup.setToolPassive(this.activeTool)
|
||||
}
|
||||
|
|
@ -2337,18 +2587,67 @@ export default {
|
|||
toolGroup.setToolActive(toolName, {
|
||||
bindings: [{ mouseButton: MouseBindings.Primary }]
|
||||
})
|
||||
if (toolName === CrosshairsTool.toolName) {
|
||||
if (this.isFusion) {
|
||||
const instance = toolGroup.getToolInstance?.(CrosshairsTool.toolName)
|
||||
if (instance && !instance.__fusionSameForPatched) {
|
||||
instance.__fusionSameForPatched = true
|
||||
const original = instance._checkIfViewportsRenderingSameScene?.bind(instance)
|
||||
instance._checkIfViewportsRenderingSameScene = (viewport, otherViewport) => {
|
||||
try {
|
||||
const a = viewport?.getFrameOfReferenceUID?.()
|
||||
const b = otherViewport?.getFrameOfReferenceUID?.()
|
||||
if (a && b && a === b) return true
|
||||
} catch (e) { }
|
||||
return original ? original(viewport, otherViewport) : true
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setFusionMipJumpEnabled(false)
|
||||
this.setFusionMipRotateEnabled(false)
|
||||
}
|
||||
this.activeTool = toolName
|
||||
}
|
||||
},
|
||||
hoverFusionViewport(index) {
|
||||
if (!this.isFusion) return
|
||||
if (this.activeTool === CrosshairsTool.toolName) return
|
||||
const toolGroup = ToolGroupManager.getToolGroup(this.fusionToolGroupId)
|
||||
if (!toolGroup) return
|
||||
|
||||
const isMip = index === 3
|
||||
this.setFusionMipJumpEnabled(isMip)
|
||||
if (isMip) {
|
||||
if (toolGroup.hasTool(StackScrollTool.toolName)) {
|
||||
toolGroup.setToolDisabled(StackScrollTool.toolName)
|
||||
}
|
||||
if (toolGroup.hasTool(VolumeRotateTool.toolName)) {
|
||||
toolGroup.setToolActive(VolumeRotateTool.toolName, {
|
||||
bindings: [{ mouseButton: MouseBindings.Wheel }]
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (toolGroup.hasTool(VolumeRotateTool.toolName)) {
|
||||
toolGroup.setToolDisabled(VolumeRotateTool.toolName)
|
||||
}
|
||||
if (toolGroup.hasTool(StackScrollTool.toolName)) {
|
||||
toolGroup.setToolActive(StackScrollTool.toolName, {
|
||||
bindings: [{ mouseButton: MouseBindings.Wheel }]
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
// 激活标注工具
|
||||
setAnnotateToolActive(toolName) {
|
||||
// if (this.readingTaskState === 2) return
|
||||
if (this.histogramVisible) return false
|
||||
const toolObj = this.tools.find(i => i.toolName === toolName)
|
||||
if (!toolObj || toolObj.isDisabled) return
|
||||
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
|
||||
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
|
||||
const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
const toolGroupId = this.getActiveToolGroupId()
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup) return
|
||||
if (this.activeTool === toolName) {
|
||||
if (toolName === CrosshairsTool.toolName) {
|
||||
toolGroup.setToolDisabled(this.activeTool)
|
||||
|
|
@ -2379,8 +2678,9 @@ export default {
|
|||
if (this.activeTool === toolName) return
|
||||
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
|
||||
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
|
||||
const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
const toolGroupId = this.getActiveToolGroupId()
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup) return
|
||||
if (this.activeTool) {
|
||||
if (this.activeTool === CrosshairsTool.toolName) {
|
||||
toolGroup.setToolDisabled(this.activeTool)
|
||||
|
|
@ -2399,8 +2699,9 @@ export default {
|
|||
if (this.activeTool && this.toolNames.includes(this.activeTool)) {
|
||||
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
|
||||
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
|
||||
const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
const toolGroupId = this.getActiveToolGroupId()
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup) return
|
||||
if (this.activeTool === CrosshairsTool.toolName) {
|
||||
toolGroup.setToolDisabled(this.activeTool)
|
||||
} else {
|
||||
|
|
@ -2412,11 +2713,13 @@ export default {
|
|||
},
|
||||
setMoreToolActive(toolName) {
|
||||
// if (this.readingTaskState === 2) return
|
||||
if (this.histogramVisible) return false
|
||||
this.setToolsPassive()
|
||||
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
|
||||
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
|
||||
const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
const toolGroupId = this.getActiveToolGroupId()
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup) return
|
||||
toolGroup.setToolActive(toolName, {
|
||||
bindings: [{ mouseButton: MouseBindings.Primary }]
|
||||
})
|
||||
|
|
@ -2425,13 +2728,10 @@ export default {
|
|||
},
|
||||
setToolsPassive() {
|
||||
if (!this.activeTool) return
|
||||
let toolGroupIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
|
||||
if (this.isMPR) {
|
||||
// toolGroupIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`]
|
||||
toolGroupIds = [this.volumeToolGroupId]
|
||||
}
|
||||
const toolGroupIds = this.getCurrentToolGroupIds()
|
||||
toolGroupIds.forEach(toolGroupId => {
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup) return
|
||||
if (this.activeTool === CrosshairsTool.toolName) {
|
||||
toolGroup.setToolDisabled(this.activeTool)
|
||||
} else {
|
||||
|
|
@ -2442,13 +2742,10 @@ export default {
|
|||
},
|
||||
setToolEnabled() {
|
||||
if (!this.activeTool) return
|
||||
let toolGroupIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
|
||||
if (this.isMPR) {
|
||||
// toolGroupIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`]
|
||||
toolGroupIds = [this.volumeToolGroupId]
|
||||
}
|
||||
const toolGroupIds = this.getCurrentToolGroupIds()
|
||||
toolGroupIds.forEach(toolGroupId => {
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup) return
|
||||
toolGroup.setToolEnabled(this.activeTool)
|
||||
})
|
||||
this.activeTool = ''
|
||||
|
|
@ -2531,20 +2828,28 @@ export default {
|
|||
const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
const viewport = renderingEngine.getViewport(viewportId)
|
||||
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].resetOrientationMarkers()
|
||||
let index = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series.SliceIndex
|
||||
if (this.readingTool !== 3) {
|
||||
viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true })
|
||||
}
|
||||
viewport.resetProperties()
|
||||
if (this.isMPR) {
|
||||
let volume = cache.getVolume(this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].volumeId)
|
||||
const voi = metaData.get('voiLutModule', volume._imageIds[Math.ceil((volume._imageIds.length - 1) / 2)])
|
||||
const lower = voi.windowCenter[0] - voi.windowWidth[0] / 2
|
||||
const upper = voi.windowCenter[0] + voi.windowWidth[0] / 2 - 1
|
||||
console.log(lower, upper)
|
||||
viewport.setProperties({ voiRange: { upper: upper, lower: lower } })
|
||||
}
|
||||
if (this.readingTool === 3 && this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series.Modality === 'PT') {
|
||||
viewport.setProperties({ voiRange: { upper: 5, lower: 0 } })
|
||||
}
|
||||
viewport.render()
|
||||
renderingEngine.render()
|
||||
if (this.readingTool === 3) {
|
||||
DicomEvent.$emit('isloaded', { isChange: false })
|
||||
DicomEvent.$emit('isloaded', { isChange: false, viewportId })
|
||||
}
|
||||
if (this.readingTool === 3 || this.isMPR) {
|
||||
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setFullScreen(index)
|
||||
}
|
||||
},
|
||||
// 更改视图布局
|
||||
|
|
@ -2553,6 +2858,7 @@ export default {
|
|||
this.fullScreenIndex = null
|
||||
this.layout = v
|
||||
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
|
||||
let index = series.SliceIndex
|
||||
const seriesArr = []
|
||||
if (v === 1) {
|
||||
this.rows = 1
|
||||
|
|
@ -2565,6 +2871,7 @@ export default {
|
|||
this.rows = 1
|
||||
this.cols = 2
|
||||
this.activeViewportIndex = 1
|
||||
series.curIndex = index
|
||||
if (typeof series === 'object') {
|
||||
seriesArr.push(series)
|
||||
seriesArr.push(series)
|
||||
|
|
@ -2614,6 +2921,7 @@ export default {
|
|||
} else if (v === 4) {
|
||||
this.rows = 2
|
||||
this.cols = 2
|
||||
series.curIndex = index
|
||||
if (typeof series === 'object') {
|
||||
seriesArr.push(series)
|
||||
seriesArr.push(series)
|
||||
|
|
@ -2676,19 +2984,22 @@ export default {
|
|||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(`${this.viewportKey}-${this.activeViewportIndex}`)
|
||||
const { invert } = viewport.getProperties()
|
||||
if (this.isFusion) {
|
||||
if (this.isFusion || this.readingTool === 3) {
|
||||
viewport.setProperties({ invert: !invert }, this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].volumeId)
|
||||
}
|
||||
} else {
|
||||
viewport.setProperties({ invert: !invert })
|
||||
}
|
||||
viewport.render()
|
||||
},
|
||||
// 翻页
|
||||
scrollPage(type) {
|
||||
// if (this.histogramVisible) return false
|
||||
this.clipPlaying = false
|
||||
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].scrollPage(type)
|
||||
},
|
||||
// 播放
|
||||
toggleClipPlay(isPlay) {
|
||||
// if (this.histogramVisible) return false
|
||||
this.clipPlaying = !this.clipPlaying
|
||||
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].toggleClipPlay(isPlay, this.fps)
|
||||
},
|
||||
|
|
@ -2821,20 +3132,26 @@ export default {
|
|||
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].resize(forceFitToWindow)
|
||||
},
|
||||
// 重置视口
|
||||
resetRenderingEngine() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer)
|
||||
this.timer = null
|
||||
resetRenderingEngine(viewportId = null, i) {
|
||||
if (this.timer[viewportId]) {
|
||||
clearInterval(this.timer[viewportId])
|
||||
this.timer[viewportId] = null
|
||||
}
|
||||
let index = null
|
||||
this.timer = setTimeout(() => {
|
||||
index = index || index === 0 ? index : this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series.SliceIndex
|
||||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(viewportId)
|
||||
if (!viewport) return false
|
||||
if (viewport.volumeIds.size <= 0) return false
|
||||
let index = null
|
||||
this.timer[viewportId] = setTimeout(() => {
|
||||
index = i || i === 0 ? i : this.$refs[viewportId ? viewportId : `${this.viewportKey}-${this.activeViewportIndex}`][0].series.SliceIndex
|
||||
renderingEngine.resize(true, false)
|
||||
renderingEngine.render()
|
||||
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setFullScreen(index)
|
||||
clearTimeout(this.timer)
|
||||
this.timer = null
|
||||
this.$refs[viewportId ? viewportId : `${this.viewportKey}-${this.activeViewportIndex}`][0].setFullScreen(index)
|
||||
clearTimeout(this.timer[viewportId])
|
||||
this.timer[viewportId] = null
|
||||
if (this.readingTool === 3) {
|
||||
DicomEvent.$emit('isloaded', { isChange: false, viewportId })
|
||||
}
|
||||
}, 100)
|
||||
},
|
||||
setDelay(time) {
|
||||
|
|
@ -2854,12 +3171,12 @@ export default {
|
|||
if (this.readingTool === 3 || this.isMPR) {
|
||||
// this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series)
|
||||
this.$nextTick(() => {
|
||||
this.resetRenderingEngine()
|
||||
// this.resetRenderingEngine(`${this.viewportKey}-${index}`)
|
||||
this.isDelay = true
|
||||
this.setDelay(2000)
|
||||
if (this.readingTool === 3) {
|
||||
DicomEvent.$emit('isloaded', { isChange: false })
|
||||
}
|
||||
// if (this.readingTool === 3) {
|
||||
// DicomEvent.$emit('isloaded', { isChange: false })
|
||||
// }
|
||||
})
|
||||
|
||||
}
|
||||
|
|
@ -2958,6 +3275,7 @@ export default {
|
|||
const confirm = await this.$confirm(this.$t('trials:reading:confirm:changeStack'))
|
||||
if (!confirm) return false
|
||||
}
|
||||
if (this.histogramVisible) this.$refs.histogram.close()
|
||||
this.isFusion = false
|
||||
this.setToolsPassive()
|
||||
this.rows = 1
|
||||
|
|
@ -2966,8 +3284,10 @@ export default {
|
|||
this.fullScreenIndex = null
|
||||
this.isMPR = false
|
||||
obj.isChange = false
|
||||
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(obj)
|
||||
return this.$nextTick(() => {
|
||||
DicomEvent.$emit('activeSeries', obj)
|
||||
DicomEvent.$emit('changeMPR')
|
||||
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(obj)
|
||||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||||
renderingEngine.resize(true, false)
|
||||
renderingEngine.render()
|
||||
|
|
@ -2977,6 +3297,7 @@ export default {
|
|||
})
|
||||
|
||||
}
|
||||
if (this.histogramVisible) this.$refs.histogram.close()
|
||||
if (!obj.IsDicom) {
|
||||
return this.previewNoneDicoms(obj)
|
||||
}
|
||||
|
|
@ -3015,7 +3336,14 @@ export default {
|
|||
this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
|
||||
}
|
||||
}
|
||||
if (this.readingTool === 3) {
|
||||
this.$nextTick(() => {
|
||||
DicomEvent.$emit('SegmentationLoading', `${this.viewportKey}-${this.activeViewportIndex}`)
|
||||
})
|
||||
}
|
||||
if (this.activeTool !== CrosshairsTool.toolName) {
|
||||
this.setToolsPassive()
|
||||
}
|
||||
},
|
||||
getRelatedSeries(visitTaskInfo, baselineSeries) {
|
||||
let obj = {}
|
||||
|
|
@ -3139,13 +3467,15 @@ export default {
|
|||
if (i === -1) return
|
||||
const studyList = this.visitTaskList[i].StudyList
|
||||
let series = null
|
||||
let curSeriesId = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series.Id
|
||||
if (obj.segment) {
|
||||
let study = studyList.find(item => item.StudyId === obj.segmentGroup.StudyId)
|
||||
series = study.SeriesList.find(item => item.Id === obj.segmentGroup.SeriesId)
|
||||
series.SliceIndex = 0
|
||||
series.segment = obj.segment
|
||||
let Series = study.SeriesList.find(item => item.Id === obj.segmentGroup.SeriesId)
|
||||
Series.SliceIndex = 0
|
||||
series = Object.assign(Series, { segment: obj.segment })
|
||||
} else {
|
||||
series = this.getMarkedSeries(studyList, obj.annotation, true)
|
||||
delete series.segment
|
||||
}
|
||||
|
||||
if (series) {
|
||||
|
|
@ -3188,7 +3518,7 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(series, obj.segment ? false : true)
|
||||
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(series, series.segment || curSeriesId !== series.Id ? false : true)
|
||||
this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
|
||||
}
|
||||
},
|
||||
|
|
@ -3380,8 +3710,9 @@ export default {
|
|||
this.setToolsPassive()
|
||||
|
||||
}
|
||||
const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
const toolGroupId = this.getActiveToolGroupId()
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup) return
|
||||
toolGroup.setToolActive(toolName, {
|
||||
bindings: [{ mouseButton: MouseBindings.Primary }]
|
||||
})
|
||||
|
|
@ -3656,6 +3987,8 @@ export default {
|
|||
if (series.ImageIds.length <= 5) return this.$confirm(this.$t('trials:reading:confirm:smallNumberOfimage'), this.$t('system:menu:confirm:title:warning'), {
|
||||
type: 'warning'
|
||||
})
|
||||
series.curIndex = series.SliceIndex
|
||||
DicomEvent.$emit('changeMPR')
|
||||
if (series.ImageIds.length > 500) {
|
||||
let res = await this.getSystemInfo()
|
||||
if (!res) return false
|
||||
|
|
@ -3740,6 +4073,7 @@ export default {
|
|||
this.$refs[`viewport-fusion-1`][0].setSeriesInfo(ptData, false, { colorMap: false })
|
||||
this.$refs[`viewport-fusion-2`][0].setSeriesInfo(fusionData, false, { isFusion: true, colorMap: true })
|
||||
this.$refs[`viewport-fusion-3`][0].setSeriesInfo(ptData, false, { isMip: true, colorMap: false })
|
||||
await this.initFusionHiddenSagViewport(pt)
|
||||
// this.resetAnnotation = false
|
||||
this.$nextTick(() => {
|
||||
this.$refs[`colorMap`].init()
|
||||
|
|
@ -3770,6 +4104,18 @@ export default {
|
|||
}
|
||||
return false
|
||||
},
|
||||
async initFusionHiddenSagViewport(pt) {
|
||||
const ptVolumeId = pt?.SeriesInstanceUid
|
||||
if (!ptVolumeId || !cache.getVolume(ptVolumeId)) return
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const sagViewport = renderingEngine?.getViewport?.('viewport-fusion-hidden-sag')
|
||||
if (!sagViewport) return
|
||||
|
||||
await sagViewport.setVolumes([{ volumeId: ptVolumeId }])
|
||||
const midIndex = Math.max(0, Math.floor((pt.ImageIds?.length || 1) / 2))
|
||||
await csUtils.jumpToSlice(sagViewport.element, { imageIndex: midIndex })
|
||||
sagViewport.render()
|
||||
},
|
||||
async getVolume(serie, isFusion = false) {
|
||||
return new Promise(async res => {
|
||||
let volumeId = `${isFusion ? 'fusion_' : ''}` + serie.SeriesInstanceUid;
|
||||
|
|
@ -3954,8 +4300,14 @@ export default {
|
|||
this.saveCustomAnnotationTimer = null
|
||||
}
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer)
|
||||
this.timer = null
|
||||
Object.keys(this.timer).forEach(key => {
|
||||
if (this.timer[key]) {
|
||||
clearInterval(this.timer[key])
|
||||
clearTimeout(this.timer[key])
|
||||
this.timer[key] = null
|
||||
}
|
||||
})
|
||||
this.timer = {}
|
||||
}
|
||||
if (this.FullTimerOut) {
|
||||
clearTimeout(this.FullTimerOut)
|
||||
|
|
@ -4255,6 +4607,22 @@ export default {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.fusion-hidden-viewports {
|
||||
position: absolute;
|
||||
left: -100000px;
|
||||
top: -100000px;
|
||||
width: 512px;
|
||||
height: 512px;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.fusion-hidden-viewport {
|
||||
width: 512px;
|
||||
height: 512px;
|
||||
}
|
||||
|
||||
.viewports-box {
|
||||
display: grid;
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -2,21 +2,24 @@
|
|||
<el-form ref="segmentForm" :model="form" label-width="120px" label-position="left" :rules="rules">
|
||||
<!-- 检查名称 -->
|
||||
<el-form-item :label="$t('segment:form:label:studyName')" prop="taskBlindName">
|
||||
<el-select v-model="form.studyId" clearable @change="(e) => handleChange(e, 'study')">
|
||||
<el-select v-model="form.studyId" clearable @change="(e) => handleChange(e, 'study')"
|
||||
@clear="(e) => handleClear(e, 'study')">
|
||||
<el-option v-for="item in studyList" :key="item.StudyId" :label="item.StudyCode"
|
||||
:value="item.StudyId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 序列名称 -->
|
||||
<el-form-item :label="$t('segment:form:label:seriesName')" prop="taskBlindName">
|
||||
<el-select v-model="form.seriesId" clearable @change="(e) => handleChange(e, 'series')">
|
||||
<el-select v-model="form.seriesId" clearable @change="(e) => handleChange(e, 'series')"
|
||||
@clear="(e) => handleClear(e, 'series')">
|
||||
<el-option v-for="item in seriesList" :key="item.Id"
|
||||
:label="`#${item.SeriesNumber}/${item.Modality}/${item.Description}`" :value="item.Id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 分割组名称 -->
|
||||
<el-form-item :label="$t('segment:form:label:segmentGroupName')" prop="segmentGroupId">
|
||||
<el-select v-model="form.segmentGroupId" clearable @change="(e) => handleChange(e, 'segmentGroup')">
|
||||
<el-select v-model="form.segmentGroupId" clearable @change="(e) => handleChange(e, 'segmentGroup')"
|
||||
@clear="(e) => handleClear(e, 'segmentGroup')">
|
||||
<el-option v-for="item in segmentGroupList" :key="item.Id" :label="item.SegmentationName"
|
||||
:value="item.Id" />
|
||||
</el-select>
|
||||
|
|
@ -106,11 +109,22 @@ export default {
|
|||
})
|
||||
this.studyList = studyList
|
||||
if (this.visitInfo.operateStateEnum === 21) {
|
||||
let find = studyList.some(item => item.StudyId === this.series.StudyId)
|
||||
if (find) {
|
||||
this.form.studyId = this.series.StudyId
|
||||
this.handleChange(null, 'study')
|
||||
if (find.SeriesArr && find.SeriesArr.length > 0) {
|
||||
let findSeries = find.SeriesArr.some(item => item.Id === this.form.seriesId)
|
||||
if (findSeries) {
|
||||
this.form.seriesId = this.series.Id
|
||||
this.handleChange(null, 'series')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (this.visitInfo.operateStateEnum === 22) {
|
||||
let o = {}
|
||||
if (this.isTableQuestion) {
|
||||
|
|
@ -132,6 +146,7 @@ export default {
|
|||
},
|
||||
async handleChange(e, key) {
|
||||
if (key === 'study') {
|
||||
console.log(this.studyList, 'this.studyList')
|
||||
this.seriesList = this.studyList.find(item => item.StudyId === this.form.studyId).SeriesArr
|
||||
}
|
||||
if (key === 'series') {
|
||||
|
|
@ -142,6 +157,20 @@ export default {
|
|||
this.segmentList = list.filter(item => item.SegmentJson)
|
||||
}
|
||||
},
|
||||
handleClear(e, key) {
|
||||
if (key === 'study') {
|
||||
this.form.seriesId = null
|
||||
this.form.segmentGroupId = null
|
||||
this.form.segmentId = null
|
||||
}
|
||||
if (key === 'series') {
|
||||
this.form.segmentGroupId = null
|
||||
this.form.segmentId = null
|
||||
}
|
||||
if (key === 'segmentGroup') {
|
||||
this.form.segmentId = null
|
||||
}
|
||||
},
|
||||
handleCancel() {
|
||||
this.$emit("update:visible", false)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,25 +8,26 @@
|
|||
<div class="tool-frame">
|
||||
<div :title="$t('trials:Segmentations:tools:contour')"
|
||||
:class="['tool-item', activeTool === 'LabelMapEditWithContour' && segmentList.length > 0 ? 'tool-item-active' : '']"
|
||||
:style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) ? 'not-allowed' : 'pointer' }"
|
||||
:style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) || ['viewport-MPR-1', 'viewport-MPR-2'].includes(`${viewportKey}-${activeViewportIndex}`) ? 'not-allowed' : 'pointer' }"
|
||||
@click.prevent="setToolActive('LabelMapEditWithContour')">
|
||||
<svg-icon icon-class="contour" class="svg-icon" />
|
||||
</div>
|
||||
<div :title="$t('trials:Segmentations:tools:thresholecircle')"
|
||||
:class="['tool-item', ThresholdTools.includes(activeTool) && segmentList.length > 0 ? 'tool-item-active' : '']"
|
||||
:style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) ? 'not-allowed' : 'pointer' }"
|
||||
:style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) || ['viewport-MPR-1', 'viewport-MPR-2'].includes(`${viewportKey}-${activeViewportIndex}`) ? 'not-allowed' : 'pointer' }"
|
||||
@click.prevent="initThreshold">
|
||||
<svg-icon icon-class="thresholecircle" class="svg-icon" />
|
||||
</div>
|
||||
<div :title="$t('trials:Segmentations:tools:circularbrush')"
|
||||
:class="['tool-item', activeTool === 'CircularBrush' && segmentList.length > 0 ? 'tool-item-active' : '']"
|
||||
:style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) ? 'not-allowed' : 'pointer' }"
|
||||
:style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) || ['viewport-MPR-1', 'viewport-MPR-2'].includes(`${viewportKey}-${activeViewportIndex}`) ? 'not-allowed' : 'pointer' }"
|
||||
@click.prevent="setToolActive('CircularBrush')">
|
||||
<svg-icon icon-class="circularbrush" class="svg-icon" />
|
||||
</div>
|
||||
<div :class="['tool-item', activeTool === 'CircularEraser' && segmentList.length > 0 ? 'tool-item-active' : '']"
|
||||
:style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) ? 'not-allowed' : 'pointer' }"
|
||||
:title="$t('trials:Segmentations:tools:Eraser')" @click.prevent="setToolActive('CircularEraser')">
|
||||
:style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) || ['viewport-MPR-1', 'viewport-MPR-2'].includes(`${viewportKey}-${activeViewportIndex}`) ? 'not-allowed' : 'pointer' }"
|
||||
:title="$t('trials:Segmentations:tools:Eraser')"
|
||||
@click.prevent="setToolActive('CircularEraser')">
|
||||
<svg-icon icon-class="clear" class="svg-icon" />
|
||||
</div>
|
||||
<!-- <div :class="['tool-item']">
|
||||
|
|
@ -79,7 +80,8 @@
|
|||
@click.stop="changeShowSegmentConfig" />
|
||||
</div>
|
||||
</template>
|
||||
<div class="addSegmentBox viewHover" @click.stop="addSegment" v-if="segmentList.length <= 0">
|
||||
<div class="addSegmentBox viewHover" @click.stop="addSegment"
|
||||
v-if="segmentList.length <= 0 && readingTaskState < 2">
|
||||
<span><i class="el-icon-plus"></i>
|
||||
{{ $t('trials:reading:Segmentations:button:addSegmention') }}
|
||||
</span>
|
||||
|
|
@ -123,16 +125,11 @@
|
|||
$t('trials:reading:Segmentations:title:InactiveSegmentationsShow')
|
||||
}}</span>
|
||||
</div>
|
||||
<!-- <div class="SegmentConfig" v-if="SegmentConfig.InactiveSegmentations.show">
|
||||
<span>{{ $t('trials:reading:Segmentations:title:Opacity') }}</span>
|
||||
<el-slider v-model="SegmentConfig.InactiveSegmentations.fillAlpha" show-input :step="0.1"
|
||||
:max="1" input-size="mini" :show-input-controls="false" />
|
||||
</div> -->
|
||||
</div>
|
||||
<template v-if="segmentList.length > 0">
|
||||
<div class="SegmentGroupBox">
|
||||
<div style="display: flex;align-items: center;">
|
||||
<el-popover placement="left" width="40" trigger="click">
|
||||
<el-popover placement="left" width="40" trigger="click" v-if="readingTaskState < 2">
|
||||
<div class="SegmentGroupBtnBox">
|
||||
<div class="SegmentGroupBtn" @click.stop="addSegmentGroup">
|
||||
{{ $t('trials:reading:Segmentations:button:addSegmentGroup') }}
|
||||
|
|
@ -149,13 +146,14 @@
|
|||
</div>
|
||||
<i slot="reference" class="el-icon-more" style="cursor: pointer;color:#fff" />
|
||||
</el-popover>
|
||||
<el-select v-model="segmentationId" placeholder="" @change="selectSegmentGroup()">
|
||||
<el-select v-model="segmentationId" placeholder="" @change="selectSegmentGroup()"
|
||||
:disabled="saveLoading">
|
||||
<el-option v-for="item in segmentList" :key="`${item.segmentationId}`"
|
||||
:label="item.name" :value="item.segmentationId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div style="display: flex;align-items: center;">
|
||||
<div style="display: flex;align-items: center;" v-if="readingTaskState < 2">
|
||||
<i class="el-icon-warning-outline" style="color:red;margin-right: 5px;"
|
||||
:title="$t('trials:reading:Segmentations:tip:segmentationIsNotSave')"
|
||||
v-if="!curSegmentGroup.isSaved"></i>
|
||||
|
|
@ -166,7 +164,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="addSegmentBox" @click.stop="addSegment"
|
||||
style="display: flex;align-items: center;justify-content: space-between;">
|
||||
style="display: flex;align-items: center;justify-content: space-between;"
|
||||
v-if="readingTaskState < 2">
|
||||
<span><i class="el-icon-plus"></i>
|
||||
{{ $t('trials:reading:Segmentations:button:addSegment') }}
|
||||
</span>
|
||||
|
|
@ -219,7 +218,7 @@
|
|||
<i class="el-icon-lock" v-if="item.lock" @click.stop="lockSegment(item, false)"></i>
|
||||
<el-popover placement="bottom" width="40" trigger="click" class="docShow"
|
||||
:value="popoverId === `popover-${item.segmentationId}_${item.segmentIndex}`"
|
||||
@show="handleClickPopover(item)">
|
||||
@show="handleClickPopover(item)" v-if="readingTaskState < 2">
|
||||
<div class="SegmentGroupBtnBox">
|
||||
<div class="SegmentGroupBtn" @click.stop="rename('segment', item)">
|
||||
{{ $t('trials:reading:Segmentations:button:renameSegmentGroup') }}
|
||||
|
|
@ -244,7 +243,7 @@
|
|||
</template>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<div class="saveBtnBox">
|
||||
<div class="saveBtnBox" v-if="readingTaskState < 2">
|
||||
<el-button type="success" size="small" :disabled="saveLoading" @click="saveSegmentGroup()">
|
||||
{{ $t("trials:reading:Segmentations:button:saveAll") }}
|
||||
</el-button>
|
||||
|
|
@ -260,6 +259,8 @@ import * as cornerstoneAdapters from "@cornerstonejs/adapters";
|
|||
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'
|
||||
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
|
||||
import { getCustomizeStandardsSegmentDicomTools } from './toolConfig'
|
||||
import * as polySeg from '@cornerstonejs/polymorphic-segmentation'
|
||||
cornerstoneTools.init({ addons: { polySeg } })
|
||||
const {
|
||||
ToolGroupManager,
|
||||
Enums: csToolsEnums,
|
||||
|
|
@ -270,6 +271,7 @@ const {
|
|||
CrosshairsTool,
|
||||
utilities: CStUtils,
|
||||
} = cornerstoneTools;
|
||||
|
||||
const { MouseBindings, Events: toolsEvents } = csToolsEnums
|
||||
const { segmentation: segmentationUtils } = CStUtils;
|
||||
const { cache, getRenderingEngine, imageLoader, eventTarget, metaData, utilities: csUtils, volumeLoader } = cornerstone;
|
||||
|
|
@ -304,12 +306,38 @@ export default {
|
|||
return {}
|
||||
}
|
||||
},
|
||||
SegmentConfig: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
actionConfiguration: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
curSegSeries: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
trialCriterion: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
segId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
segIndex: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
renderingEngineId: {
|
||||
type: String,
|
||||
required: true
|
||||
|
|
@ -321,6 +349,10 @@ export default {
|
|||
globalLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
histogramVisible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
|
@ -338,16 +370,6 @@ export default {
|
|||
ThresholdTools: ['ThresholdCircle', 'ThresholdSphere'],
|
||||
thresholdType: null,
|
||||
showSegmentConfig: false,
|
||||
SegmentConfig: {
|
||||
renderOutline: true,
|
||||
renderFill: true,
|
||||
fillAlpha: 0.5,
|
||||
outlineWidth: 1,
|
||||
InactiveSegmentations: {
|
||||
show: true,
|
||||
fillAlpha: 0.3,
|
||||
}
|
||||
},
|
||||
segmentList: [],
|
||||
segmentationId: "",
|
||||
segmentIndex: null,
|
||||
|
|
@ -363,7 +385,7 @@ export default {
|
|||
'#ff994d',
|
||||
'#fb628b',
|
||||
],
|
||||
viewprotIds: ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3', 'viewport-MPR-0', 'viewport-MPR-1', 'viewport-MPR-2'], //
|
||||
viewportIds: [], //
|
||||
statsKey: [],
|
||||
drawing: false, // 是否正在分割
|
||||
// isDel: false,
|
||||
|
|
@ -383,14 +405,30 @@ export default {
|
|||
this.segmentationModifiedCallback
|
||||
);
|
||||
DicomEvent.$on('activeSeries', (series) => {
|
||||
console.log(series, 'series')
|
||||
let { TaskInfo = {}, Id } = series
|
||||
if (this.isMPR) return false
|
||||
if (Id === this.series.Id && TaskInfo.VisitTaskId === this.visitInfo.VisitTaskId) return false
|
||||
this.series = series
|
||||
this.$emit("update:curSegSeries", Object.assign(series, {}))
|
||||
this.getSegmentationList()
|
||||
})
|
||||
DicomEvent.$on('isloaded', (data) => {
|
||||
if (this.isloaded) return false
|
||||
this.isloaded = true
|
||||
let { segment, isChange = true } = data
|
||||
this.delAllSegment(isChange)
|
||||
this.getSegmentationList(segment)
|
||||
let { segment, isChange = true, viewportId, series } = data
|
||||
DicomEvent.$emit('renderSegmentation', viewportId)
|
||||
})
|
||||
DicomEvent.$on('SegmentationLoading', (viewportId) => {
|
||||
if (viewportId !== `${this.viewportKey}-${this.activeViewportIndex}`) return false
|
||||
this.loading = false
|
||||
})
|
||||
DicomEvent.$on('changeMPR', () => {
|
||||
if (this.loading) return false
|
||||
// if (viewportId !== `${this.viewportKey}-${this.activeViewportIndex}`) return false
|
||||
if (this.segmentList && this.segmentList.length > 0) {
|
||||
this.segmentationId = this.segmentList[0].segmentationId
|
||||
this.segmentIndex = this.segmentList[0] ? this.segmentList[0].segments[0].segmentIndex : null
|
||||
}
|
||||
|
||||
})
|
||||
const digitPlaces = Number(localStorage.getItem('digitPlaces'))
|
||||
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
|
||||
|
|
@ -409,12 +447,15 @@ export default {
|
|||
s = this.curSegmentGroup.segments.find(item => item.segmentIndex === this.segmentIndex)
|
||||
}
|
||||
return s
|
||||
},
|
||||
readingTaskState() {
|
||||
return this.series.TaskInfo ? this.series.TaskInfo.ReadingTaskState : 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
SegmentConfig: {
|
||||
handler() {
|
||||
this.readingSegmentByConfig()
|
||||
// this.readingSegmentByConfig()
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
|
|
@ -429,9 +470,23 @@ export default {
|
|||
this.setBrushThreshold()
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
segmentIndex() {
|
||||
this.$emit('update:segIndex', this.segmentIndex)
|
||||
},
|
||||
segmentationId() {
|
||||
this.$emit('update:segId', this.segmentationId)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showSurface(item) {
|
||||
this.$emit("showSurface", {
|
||||
segmentationId: item.segmentationId,
|
||||
segmentIndex: item.segmentIndex,
|
||||
volumeId: this.series.SeriesInstanceUid,
|
||||
segmentations: this.curSegmentGroup
|
||||
})
|
||||
},
|
||||
handleClickPopover(item) {
|
||||
this.popoverId = `popover-${item.segmentationId}_${item.segmentIndex}`
|
||||
},
|
||||
|
|
@ -499,7 +554,6 @@ export default {
|
|||
segmentationId: list[0].segmentationId,
|
||||
segmentIndices: list.map(item => item.segmentIndex),
|
||||
});
|
||||
console.log(bidirectionalData, list[0].segmentationId, 'bidirectionalData')
|
||||
if (bidirectionalData.length <= 0) {
|
||||
list.forEach(item => {
|
||||
let annotations = annotation.state.getAllAnnotations().filter(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex);
|
||||
|
|
@ -558,8 +612,11 @@ export default {
|
|||
|
||||
},
|
||||
setToolActive(toolName) {
|
||||
// if (!this.series.TaskInfo || this.series.TaskInfo.VisitTaskId !== this.visitInfo.VisitTaskId) return false
|
||||
if (this.segmentList.length <= 0) return false
|
||||
if (this.curSegment.lock) return false
|
||||
if (this.histogramVisible && !this.ThresholdTools.includes(toolName)) return false
|
||||
if (['viewport-MPR-1', 'viewport-MPR-2'].includes(`${this.viewportKey}-${this.activeViewportIndex}`)) return false
|
||||
const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (this.activeTool === toolName) {
|
||||
|
|
@ -596,71 +653,33 @@ export default {
|
|||
viewBidirectional(arr, view) {
|
||||
for (let j = 0; j < arr.length; j++) {
|
||||
let item = arr[j]
|
||||
let bidirectional = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
|
||||
item.bidirectionalView = view
|
||||
if (!bidirectional) continue
|
||||
annotation.visibility.setAnnotationVisibility(bidirectional.annotationUID, view)
|
||||
// let bidirectional = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
|
||||
// item.bidirectionalView = view
|
||||
// if (!bidirectional) continue
|
||||
// annotation.visibility.setAnnotationVisibility(bidirectional.annotationUID, view)
|
||||
}
|
||||
this.resetViewport()
|
||||
DicomEvent.$emit('viewBidirectional', arr)
|
||||
// this.resetViewport()
|
||||
},
|
||||
async jumpBidirectional(item) {
|
||||
if (item.bidirectional) {
|
||||
let an = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
|
||||
console.log(an, 'an')
|
||||
if (!an) return false
|
||||
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
const viewport = renderingEngine.getViewport(viewportId)
|
||||
let key = Object.keys(an.data.cachedStats)[0]; // referencedImageId
|
||||
if (key) {
|
||||
let sliceIndex = key.split("?")[1].split("&")[0].split("=")[1]
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIndex });
|
||||
} else {
|
||||
const points = an.data.handles.points;
|
||||
const worldPoint = points[0]; // 取一个点
|
||||
let volume = cache.getVolume(this.series.SeriesInstanceUid)
|
||||
let { imageData, numFrames } = volume
|
||||
const ijk = imageData.worldToIndex(worldPoint);
|
||||
const sliceIndex = Math.abs(Math.round(ijk[2]));
|
||||
// console.log(sliceIndex, 'sliceIndex')
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: numFrames - sliceIndex - 1 });
|
||||
}
|
||||
|
||||
}
|
||||
DicomEvent.$emit('jumpBidirectional', item)
|
||||
},
|
||||
viewSegmentGroup(item) {
|
||||
let view = !item.view
|
||||
this.viewprotIds.forEach(id => {
|
||||
segmentation.config.visibility.setSegmentationRepresentationVisibility(
|
||||
id,
|
||||
{
|
||||
segmentationId: item.segmentationId,
|
||||
type: csToolsEnums.SegmentationRepresentations.Labelmap,
|
||||
},
|
||||
view
|
||||
);
|
||||
})
|
||||
item.view = view
|
||||
|
||||
item.view = !item.view
|
||||
item.segments.forEach(i => {
|
||||
i.view = view
|
||||
// this.viewBidirectional(i, view)
|
||||
i.view = item.view
|
||||
})
|
||||
this.viewBidirectional(item.segments, view)
|
||||
DicomEvent.$emit('viewSegmentation', item)
|
||||
// this.viewBidirectional(item.segments, view)
|
||||
},
|
||||
viewSegment(item, view) {
|
||||
this.viewprotIds.forEach(id => {
|
||||
segmentation.config.visibility.setSegmentIndexVisibility(id, {
|
||||
segmentationId: item.segmentationId,
|
||||
type: csToolsEnums.SegmentationRepresentations.Labelmap,
|
||||
}, item.segmentIndex, view)
|
||||
})
|
||||
item.view = view
|
||||
this.viewBidirectional([item], view)
|
||||
this.$emit('setToolsPassive')
|
||||
DicomEvent.$emit('viewSegment', item)
|
||||
},
|
||||
lockSegment(item, lock) {
|
||||
if (this.readingTaskState >= 2) return false
|
||||
segmentation.segmentLocking.setSegmentIndexLocked(item.segmentationId, item.segmentIndex, lock)
|
||||
item.lock = lock
|
||||
if (!lock) this.changeSegmentationSavedStatus(item.segmentationId, lock)
|
||||
|
|
@ -670,7 +689,7 @@ export default {
|
|||
this.segmentationId = item.segmentationId;
|
||||
this.segmentIndex = item.segmentIndex;
|
||||
}
|
||||
segmentation.segmentIndex.setActiveSegmentIndex(item.segmentationId, item.segmentIndex);
|
||||
// segmentation.segmentIndex.setActiveSegmentIndex(item.segmentationId, item.segmentIndex);
|
||||
if (isChange) { this.jumpBidirectional(item) }
|
||||
|
||||
if (item.lock) {
|
||||
|
|
@ -679,30 +698,57 @@ export default {
|
|||
// this.resetViewport()
|
||||
},
|
||||
selectSegmentGroup(s) {
|
||||
this.viewprotIds.forEach(id => {
|
||||
segmentation.activeSegmentation.setActiveSegmentation(id, this.segmentationId)
|
||||
})
|
||||
// segmentation.activeSegmentation.setActiveSegmentation(`${this.viewportKey}-${this.activeViewportIndex}`, this.segmentationId)
|
||||
this.$emit('setToolsPassive')
|
||||
this.segmentIndex = null;
|
||||
let segment = s ? s : this.segmentList.find(item => item.segmentationId === this.segmentationId).segments[0]
|
||||
this.selectSegment(segment, s ? false : true)
|
||||
this.readingSegmentByConfig()
|
||||
this.$nextTick(() => {
|
||||
this.selectSegment(segment)
|
||||
})
|
||||
// this.segmentIndex = segment.segmentIndex
|
||||
// this.selectSegment(segment, s ? false : true)
|
||||
// this.readingSegmentByConfig()
|
||||
},
|
||||
getSegmentationName(num = 1) {
|
||||
let defaultSegmentationName = this.trialCriterion.DefaultSegmentName.SegmentationName
|
||||
let name = defaultSegmentationName
|
||||
let has = this.segmentList.find(item => item.name === name)
|
||||
if (has) {
|
||||
name = defaultSegmentationName + num
|
||||
has = this.segmentList.find(item => item.name === name)
|
||||
num++
|
||||
if (has) name = this.getSegmentationName(num)
|
||||
}
|
||||
return name
|
||||
},
|
||||
getSegmentName(arr, num = 1) {
|
||||
let defaultSegmentName = arr[0].SegmentLabel
|
||||
let name = defaultSegmentName + num
|
||||
let has = arr.find(item => item.SegmentLabel === name)
|
||||
num++
|
||||
if (has) name = this.getSegmentName(arr, num)
|
||||
return name
|
||||
},
|
||||
async addSegmentGroup() {
|
||||
let viewprotIds = this.viewprotIds
|
||||
// let segmentationId = this.$guid();
|
||||
let obj = {
|
||||
name: this.$t('trials:reading:Segmentations:name:SegmentGroup') + (this.segmentList.length + 1),
|
||||
name: this.getSegmentationName(),
|
||||
view: true,
|
||||
segmentationId: null,
|
||||
isSaved: false,
|
||||
segments: []
|
||||
}
|
||||
this.segmentIndex = null
|
||||
let segmentationId = await this.addOrUpdateSegmentation({ name: obj.name })
|
||||
obj.segmentationId = segmentationId
|
||||
await this.createSegmentation(obj.segmentationId)
|
||||
this.createSegmentationRepresentation(obj.segmentationId)
|
||||
this.trialCriterion.DefaultSegmentName.SegmentNameList.forEach(async (SegmentName, index) => {
|
||||
let o = {
|
||||
segmentIndex: 1,
|
||||
segmentIndex: index + 1,
|
||||
segmentationId,
|
||||
SegmentLabel: 'Segment 1',
|
||||
color: this.colors[0],
|
||||
SegmentLabel: SegmentName,
|
||||
color: this.colors[index],
|
||||
stats: null,
|
||||
bidirectional: null,
|
||||
bidirectionalView: true,
|
||||
|
|
@ -711,33 +757,42 @@ export default {
|
|||
}
|
||||
let id = await this.addOrUpdateSegment({ name: o.SegmentLabel, color: o.color, segmentIndex: o.segmentIndex, segmentationId: o.segmentationId })
|
||||
o.id = id
|
||||
obj.segments.push(o);
|
||||
obj.segments.splice(index, 0, o);
|
||||
segmentation.segmentIndex.setActiveSegmentIndex(obj.segmentationId, index + 1);
|
||||
this.changeColor(this.colors[index], { segmentationId: obj.segmentationId, segmentIndex: index + 1, color: this.colors[index] })
|
||||
if (index === this.trialCriterion.DefaultSegmentName.SegmentNameList.length - 1) {
|
||||
segmentation.segmentIndex.setActiveSegmentIndex(obj.segmentationId, 1);
|
||||
}
|
||||
})
|
||||
this.segmentList.push(obj);
|
||||
this.segmentationId = obj.segmentationId;
|
||||
await this.createSegmentation(obj.segmentationId)
|
||||
this.createSegmentationRepresentation(obj.segmentationId)
|
||||
this.segmentIndex = 1
|
||||
viewprotIds.forEach(id => {
|
||||
segmentation.config.color.setSegmentIndexColor(id, obj.segmentationId, 1, this.hex2Rgb(this.colors[0]))
|
||||
})
|
||||
this.selectSegmentGroup()
|
||||
// segmentation.segmentIndex.setActiveSegmentIndex(this.segmentationId, 1);
|
||||
// viewportIds.forEach(id => {
|
||||
// segmentation.config.color.setSegmentIndexColor(id, obj.segmentationId, 1, this.hex2Rgb(this.colors[0]))
|
||||
// })
|
||||
// this.selectSegmentGroup()
|
||||
},
|
||||
async addSegment() {
|
||||
let viewprotIds = this.viewprotIds;
|
||||
if (this.saveLoading) return false
|
||||
if (this.segmentList.length <= 0) {
|
||||
let obj = {
|
||||
name: this.$t('trials:reading:Segmentations:name:SegmentGroup') + 1,
|
||||
name: this.getSegmentationName(),
|
||||
view: true,
|
||||
isSaved: false,
|
||||
segments: []
|
||||
}
|
||||
this.segmentIndex = null
|
||||
let segmentationId = await this.addOrUpdateSegmentation({ name: obj.name })
|
||||
obj.segmentationId = segmentationId
|
||||
await this.createSegmentation(segmentationId)
|
||||
this.createSegmentationRepresentation(segmentationId)
|
||||
this.trialCriterion.DefaultSegmentName.SegmentNameList.forEach(async (SegmentName, index) => {
|
||||
let o = {
|
||||
segmentIndex: 1,
|
||||
segmentIndex: index + 1,
|
||||
segmentationId,
|
||||
SegmentLabel: 'Segment 1',
|
||||
color: this.colors[0],
|
||||
SegmentLabel: SegmentName,
|
||||
color: this.colors[index],
|
||||
stats: null,
|
||||
bidirectional: null,
|
||||
bidirectionalView: true,
|
||||
|
|
@ -746,17 +801,22 @@ export default {
|
|||
}
|
||||
let id = await this.addOrUpdateSegment({ name: o.SegmentLabel, color: o.color, segmentIndex: o.segmentIndex, segmentationId: o.segmentationId })
|
||||
o.id = id
|
||||
obj.segments.push(o);
|
||||
this.segmentList.push(obj);
|
||||
this.segmentationId = this.segmentList[0].segmentationId;
|
||||
await this.createSegmentation(this.segmentationId)
|
||||
this.createSegmentationRepresentation(this.segmentationId)
|
||||
this.segmentIndex = 1
|
||||
segmentation.segmentIndex.setActiveSegmentIndex(this.segmentList[0].segmentationId, 1);
|
||||
viewprotIds.forEach(id => {
|
||||
segmentation.config.color.setSegmentIndexColor(id, this.segmentList[0].segmentationId, 1, this.hex2Rgb(this.colors[0]))
|
||||
obj.segments.splice(index, 0, o);
|
||||
segmentation.segmentIndex.setActiveSegmentIndex(obj.segmentationId, index + 1);
|
||||
this.changeColor(this.colors[index], { segmentationId: obj.segmentationId, segmentIndex: index + 1, color: this.colors[index] })
|
||||
if (index === this.trialCriterion.DefaultSegmentName.SegmentNameList.length - 1) {
|
||||
segmentation.segmentIndex.setActiveSegmentIndex(obj.segmentationId, 1);
|
||||
}
|
||||
})
|
||||
this.readingSegmentByConfig()
|
||||
this.segmentList.push(obj);
|
||||
this.segmentationId = segmentationId;
|
||||
this.segmentIndex = 1
|
||||
// segmentation.segmentIndex.setActiveSegmentIndex(this.segmentList[0].segmentationId, 1);
|
||||
// this.changeColor(this.colors[0], { segmentationId: this.segmentList[0].segmentationId, segmentIndex: 1, color: this.colors[0] })
|
||||
// viewportIds.forEach(id => {
|
||||
// segmentation.config.color.setSegmentIndexColor(id, this.segmentList[0].segmentationId, 1, this.hex2Rgb(this.colors[0]))
|
||||
// })
|
||||
// this.readingSegmentByConfig()
|
||||
|
||||
} else {
|
||||
let item = this.segmentList.find(i => i.segmentationId === this.segmentationId)
|
||||
|
|
@ -765,7 +825,7 @@ export default {
|
|||
let obj = {
|
||||
segmentIndex: segmentIndex,
|
||||
segmentationId: this.segmentationId,
|
||||
SegmentLabel: `Segment ${segmentIndex}`,
|
||||
SegmentLabel: this.getSegmentName(item.segments),
|
||||
color: item.segments.length >= this.colors.length ? this.colors[0] : this.colors[item.segments.length],
|
||||
stats: null,
|
||||
bidirectional: null,
|
||||
|
|
@ -781,19 +841,22 @@ export default {
|
|||
let id = await this.addOrUpdateSegment({ name: obj.SegmentLabel, color: obj.color, segmentIndex: obj.segmentIndex, segmentationId: obj.segmentationId })
|
||||
obj.id = id
|
||||
item.segments.push(obj)
|
||||
segmentation.segmentIndex.setActiveSegmentIndex(obj.segmentationId, obj.segmentIndex);
|
||||
viewprotIds.forEach(id => {
|
||||
segmentation.config.color.setSegmentIndexColor(id, obj.segmentationId, obj.segmentIndex, this.hex2Rgb(obj.color))
|
||||
})
|
||||
this.segmentIndex = obj.segmentIndex
|
||||
// segmentation.segmentIndex.setActiveSegmentIndex(obj.segmentationId, obj.segmentIndex);
|
||||
this.changeColor(obj.color, { segmentationId: obj.segmentationId, segmentIndex: obj.segmentIndex, color: obj.color })
|
||||
// viewportIds.forEach(id => {
|
||||
// segmentation.config.color.setSegmentIndexColor(id, obj.segmentationId, obj.segmentIndex, this.hex2Rgb(obj.color))
|
||||
// })
|
||||
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
changeColor(e, item) {
|
||||
this.viewprotIds.forEach(id => {
|
||||
segmentation.config.color.setSegmentIndexColor(id, item.segmentationId, item.segmentIndex, this.hex2Rgb(e))
|
||||
})
|
||||
DicomEvent.$emit('changeColor', item)
|
||||
// this.viewportIds.forEach(id => {
|
||||
// segmentation.config.color.setSegmentIndexColor(id, item.segmentationId, item.segmentIndex, this.hex2Rgb(e))
|
||||
// })
|
||||
},
|
||||
// 清空所有分割
|
||||
delAllSegment(isChange) {
|
||||
|
|
@ -830,12 +893,13 @@ export default {
|
|||
} else {
|
||||
this.segmentationId = ''
|
||||
}
|
||||
this.readingSegmentByConfig()
|
||||
// this.readingSegmentByConfig()
|
||||
this.resetViewport()
|
||||
this.$emit('resetQuestion')
|
||||
},
|
||||
// 删除分割片段
|
||||
async delSegment(data) {
|
||||
this.popoverId = null;
|
||||
let segmentIndex = data.segmentIndex
|
||||
let confirm = await this.$confirm(this.$t('trials:reading:Segmentations:confirm:delSegment'))
|
||||
if (!confirm) return false
|
||||
|
|
@ -862,15 +926,18 @@ export default {
|
|||
|
||||
},
|
||||
resetViewport(passive = true) {
|
||||
let renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
this.viewprotIds.forEach(id => {
|
||||
const viewport = renderingEngine.getViewport(id)
|
||||
viewport.render()
|
||||
})
|
||||
DicomEvent.$emit('resetViewport')
|
||||
if (passive) this.$emit('setToolsPassive')
|
||||
},
|
||||
async rename(key, item) {
|
||||
let name = await this.customPrompt()
|
||||
let value = null
|
||||
if (key === 'segmentGroup') {
|
||||
let group = this.segmentList.find(i => i.segmentationId === this.segmentationId)
|
||||
value = group.name
|
||||
} else {
|
||||
value = item.SegmentLabel
|
||||
}
|
||||
let name = await this.customPrompt(value)
|
||||
if (!name) return false
|
||||
if (key === 'segmentGroup') {
|
||||
let group = this.segmentList.find(i => i.segmentationId === this.segmentationId)
|
||||
|
|
@ -881,7 +948,7 @@ export default {
|
|||
this.addOrUpdateSegment({ name: item.SegmentLabel, color: item.color, segmentIndex: item.segmentIndex, segmentationId: item.segmentationId, segmentJson: JSON.stringify({ stats: item.stats, bidirectional: item.bidirectional }), id: item.id })
|
||||
}
|
||||
},
|
||||
async customPrompt() {
|
||||
async customPrompt(name) {
|
||||
try {
|
||||
const that = this
|
||||
// 请输入标记名称
|
||||
|
|
@ -893,6 +960,7 @@ export default {
|
|||
showCancelButton: true,
|
||||
closeOnClickModal: false,
|
||||
closeOnPressEscape: false,
|
||||
inputValue: name,
|
||||
beforeClose: (action, instance, done) => {
|
||||
if (action === 'confirm') {
|
||||
// const value = instance.inputValue
|
||||
|
|
@ -911,7 +979,7 @@ export default {
|
|||
// 切换非当前分组分割标记显示
|
||||
changeInactiveSegmentShow() {
|
||||
let segmentList = this.segmentList.filter(item => item.segmentationId !== this.segmentationId)
|
||||
this.viewprotIds.forEach(id => {
|
||||
this.viewportIds.forEach(id => {
|
||||
segmentation.config.visibility.setSegmentationRepresentationVisibility(
|
||||
id,
|
||||
{
|
||||
|
|
@ -953,6 +1021,7 @@ export default {
|
|||
this.exportSegmentation(this.segmentationId, group, true)
|
||||
},
|
||||
exportSegmentation(segmentationId, group, isFile = false) {
|
||||
try {
|
||||
const segmentationIds = segmentation.state
|
||||
.getSegmentations()
|
||||
.map(x => x.segmentationId);
|
||||
|
|
@ -1060,6 +1129,10 @@ export default {
|
|||
} else {
|
||||
this.downloadDICOMData(generatedSegmentation.dataset, `${group.name}.dcm`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
},
|
||||
downloadDICOMData(bufferOrDataset, filename) {
|
||||
let blob;
|
||||
|
|
@ -1109,7 +1182,7 @@ export default {
|
|||
|
||||
const arrayBuffer = image.data.byteArray.buffer;
|
||||
await this.loadSegmentation(arrayBuffer, obj.segmentationId);
|
||||
this.createSegmentationRepresentation(obj.segmentationId);
|
||||
// this.createSegmentationRepresentation(obj.segmentationId);
|
||||
},
|
||||
async loadSegmentation(arrayBuffer, segmentationId) {
|
||||
const generateToolState =
|
||||
|
|
@ -1292,17 +1365,11 @@ export default {
|
|||
}
|
||||
},
|
||||
createSegmentationRepresentation(segmentationId) {
|
||||
this.viewprotIds.forEach(id => {
|
||||
segmentation.addSegmentationRepresentations(id, [
|
||||
{
|
||||
segmentationId,
|
||||
type: csToolsEnums.SegmentationRepresentations.Labelmap,
|
||||
},
|
||||
])
|
||||
})
|
||||
DicomEvent.$emit('createSegmentationRepresentation', segmentationId)
|
||||
this.$emit('setToolsPassive')
|
||||
},
|
||||
contentMouseup() {
|
||||
try {
|
||||
// console.log("segment contentMouseup")
|
||||
if (!this.drawing) return false
|
||||
if (this.timeoutId) {
|
||||
|
|
@ -1331,12 +1398,16 @@ export default {
|
|||
|
||||
}
|
||||
}, 500);
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
},
|
||||
// 获取当前任务分割标记与问题绑定关系
|
||||
async getSegmentBindingList(param = {}) {
|
||||
try {
|
||||
let data = {
|
||||
VisitTaskId: this.visitInfo.VisitTaskId,
|
||||
VisitTaskId: this.series.TaskInfo.VisitTaskId,
|
||||
PageSize: 9999,
|
||||
PageIndex: 1,
|
||||
}
|
||||
|
|
@ -1353,7 +1424,7 @@ export default {
|
|||
async saveSegmentBindingAndAnswer(list) {
|
||||
try {
|
||||
let data = {
|
||||
VisitTaskId: this.visitInfo.VisitTaskId,
|
||||
VisitTaskId: this.series.TaskInfo.VisitTaskId,
|
||||
BindingList: list
|
||||
}
|
||||
let res = await saveSegmentBindingAndAnswer(data)
|
||||
|
|
@ -1406,6 +1477,8 @@ export default {
|
|||
}/${this.series.Id}/${segmentGroup.name}.dcm`
|
||||
const result = await this.OSSclient.put(path, blob)
|
||||
segmentGroup.segUrl = this.$getObjectName(result.url)
|
||||
} else {
|
||||
segmentGroup.segUrl = null
|
||||
}
|
||||
|
||||
this.addOrUpdateSegmentation({ name: segmentGroup.name, id: segmentGroup.segmentationId, url: segmentGroup.segUrl })
|
||||
|
|
@ -1443,7 +1516,6 @@ export default {
|
|||
list.forEach(item => {
|
||||
this.createSegmentConfiguration(item.segmentIndex, item.segmentationId);
|
||||
})
|
||||
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
const viewport = renderingEngine.getViewport(viewportId);
|
||||
|
|
@ -1468,7 +1540,7 @@ export default {
|
|||
|
||||
let bidirectional = bidirectionalData[0]
|
||||
const { segmentIndex } = bidirectional;
|
||||
const { majorAxis, minorAxis, maxMajor, maxMinor } = bidirectional;
|
||||
const { majorAxis, minorAxis } = bidirectional;
|
||||
let item = list.find(i => i.segmentIndex === segmentIndex)
|
||||
SegmentBidirectionalTool.hydrate(viewportId, [majorAxis, minorAxis], {
|
||||
segmentIndex,
|
||||
|
|
@ -1477,7 +1549,7 @@ export default {
|
|||
let an = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === bidirectional.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
|
||||
if (an) {
|
||||
annotation.locking.setAnnotationLocked(an.annotationUID, true)
|
||||
annotation.visibility.setAnnotationVisibility(an.annotationUID, item.bidirectionalView)
|
||||
annotation.visibility.setAnnotationVisibility(an.annotationUID, true)
|
||||
}
|
||||
item.bidirectional = bidirectional
|
||||
reslove(true)
|
||||
|
|
@ -1516,13 +1588,13 @@ export default {
|
|||
answer = segment.stats && segment.stats[imageToolAttribute] ? Number((segment.stats[imageToolAttribute]).value).toFixed(this.digitPlaces) : ''
|
||||
}
|
||||
let o = {
|
||||
Answer: answer,
|
||||
Answer: answer === '-Infinity' || answer === 'NaN' ? null : answer,
|
||||
QuestionId: item.QuestionId,
|
||||
RowId: item.RowId,
|
||||
SegmentId: item.SegmentId,
|
||||
SegmentationId: item.SegmentationId,
|
||||
TableQuestionId: item.TableQuestionId,
|
||||
VisitTaskId: this.visitInfo.VisitTaskId,
|
||||
VisitTaskId: this.series.TaskInfo.VisitTaskId,
|
||||
}
|
||||
bindingList.push(o)
|
||||
}
|
||||
|
|
@ -1540,7 +1612,7 @@ export default {
|
|||
SubjectId: this.visitInfo.SubjectId,
|
||||
SubjectVisitId: this.visitInfo.VisistId,
|
||||
TrialId: this.$route.query.trialId,
|
||||
VisitTaskId: this.visitInfo.VisitTaskId,
|
||||
VisitTaskId: this.series.TaskInfo.VisitTaskId,
|
||||
}
|
||||
if (url) data.SegUrl = url;
|
||||
if (id) data.Id = id;
|
||||
|
|
@ -1558,17 +1630,20 @@ export default {
|
|||
// 获取分割组
|
||||
async getSegmentationList(SEGMENT = null) {
|
||||
try {
|
||||
this.$emit('setToolsPassive')
|
||||
let data = {
|
||||
SeriesId: this.series.Id,
|
||||
VisitTaskId: this.visitInfo.VisitTaskId,
|
||||
VisitTaskId: this.series.TaskInfo.VisitTaskId,
|
||||
PageSize: 9999,
|
||||
PageIndex: 1,
|
||||
}
|
||||
this.loading = true;
|
||||
let res = await getSegmentationList(data);
|
||||
this.loading = false;
|
||||
// this.loading = false;
|
||||
if (res.IsSuccess) {
|
||||
this.segmentList = []
|
||||
this.segmentationId = null;
|
||||
this.segmentIndex = null;
|
||||
let list = res.Result.CurrentPageData;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i]
|
||||
|
|
@ -1583,12 +1658,6 @@ export default {
|
|||
segments: []
|
||||
}
|
||||
this.segmentList.push(obj)
|
||||
if (item.SEGUrl) {
|
||||
await this.readSegmentation(obj)
|
||||
} else {
|
||||
await this.createSegmentation(obj.segmentationId)
|
||||
this.createSegmentationRepresentation(obj.segmentationId)
|
||||
}
|
||||
}
|
||||
if (!this.segmentationId) {
|
||||
this.segmentationId = obj.segmentationId
|
||||
|
|
@ -1607,48 +1676,20 @@ export default {
|
|||
bidirectional: SegmentJson.bidirectional,
|
||||
bidirectionalView: true,
|
||||
view: true,
|
||||
lock: item.locked,
|
||||
lock: true,
|
||||
id: s.Id
|
||||
}
|
||||
obj.segments.push(o)
|
||||
this.selectSegment(o, false)
|
||||
this.changeColor(s.ColorRgb, o)
|
||||
this.lockSegment(o, true)
|
||||
}
|
||||
if (!this.segmentIndex) {
|
||||
this.segmentIndex = s.SegmentNumber
|
||||
}
|
||||
})
|
||||
this.$nextTick(() => {
|
||||
if (SEGMENT) {
|
||||
// console.log(SEGMENT, 'SEGMENT')
|
||||
return this.getBidirectional(obj.segments, SEGMENT)
|
||||
}
|
||||
this.getBidirectional(obj.segments, null, false)
|
||||
})
|
||||
}
|
||||
if (this.segmentationId && this.segmentIndex && this.segmentList && this.segmentList.length > 0) {
|
||||
let o = this.segmentList.find(item => item.segmentationId === this.segmentationId)
|
||||
if (o) {
|
||||
let s = o.segments.find(item => item.segmentIndex === this.segmentIndex)
|
||||
this.selectSegmentGroup(s)
|
||||
} else {
|
||||
this.segmentationId = this.segmentList[0].segmentationId
|
||||
this.segmentIndex = this.segmentationId ? this.segmentList[0].segments[0].segmentIndex : null
|
||||
if (this.segmentationId && this.segmentIndex) {
|
||||
this.selectSegmentGroup(this.segmentList[0].segments[0])
|
||||
}
|
||||
|
||||
}
|
||||
// console.log(segment, 'segment')
|
||||
|
||||
// this.selectSegment(segment)
|
||||
}
|
||||
this.isloaded = false
|
||||
this.readingSegmentByConfig()
|
||||
}
|
||||
} catch (err) {
|
||||
this.loading = false
|
||||
// this.loading = false
|
||||
console.log(err)
|
||||
}
|
||||
},
|
||||
|
|
@ -1674,7 +1715,7 @@ export default {
|
|||
SegmentName: name,
|
||||
SegmentNumber: segmentIndex,
|
||||
SegmentationId: segmentationId,
|
||||
VisitTaskId: this.visitInfo.VisitTaskId,
|
||||
VisitTaskId: this.series.TaskInfo.VisitTaskId,
|
||||
SegmentJson: segmentJson
|
||||
}
|
||||
if (id) data.Id = id
|
||||
|
|
@ -1697,14 +1738,14 @@ export default {
|
|||
PageSize: 9999,
|
||||
PageIndex: 1,
|
||||
}
|
||||
this.loading = true;
|
||||
// this.loading = true;
|
||||
let res = await getSegmentList(data)
|
||||
this.loading = false;
|
||||
// this.loading = false;
|
||||
if (res.IsSuccess) {
|
||||
return res.Result.CurrentPageData
|
||||
}
|
||||
} catch (err) {
|
||||
this.loading = false
|
||||
// this.loading = false
|
||||
console.log(err)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@
|
|||
</div>
|
||||
<div class="ps">
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse-item v-for="(study, index) in studyList" :key="`${study.StudyId}`" :name="`${study.StudyId}`">
|
||||
<template v-for="(study, index) in studyList">
|
||||
<el-collapse-item :key="`${study.StudyId}`" :name="`${study.StudyId}`" v-if="!study.IsCriticalSequence">
|
||||
<template slot="title">
|
||||
<div v-if="!study.IsCriticalSequence" class="dicom-desc">
|
||||
<div v-if="readingTool !== 3 || !study.IsCriticalSequence" class="dicom-desc">
|
||||
<template v-if="taskInfo && taskInfo.IsShowStudyName">
|
||||
<div style="text-overflow: ellipsis;overflow: hidden;">
|
||||
<span :title="study.StudyCode">{{ study.StudyCode }}</span>
|
||||
|
|
@ -53,7 +54,8 @@
|
|||
style="position: absolute;right: 0;top: 0;">
|
||||
<el-popover placement="right" trigger="hover" popper-class="instance_frame_wrapper">
|
||||
<div class="frame_list">
|
||||
<div v-for="(instance, idx) in series.InstanceInfoList" :key="instance.Id" class="frame_content"
|
||||
<div v-for="(instance, idx) in series.InstanceInfoList" :key="instance.Id"
|
||||
class="frame_content"
|
||||
:style="{ 'margin-bottom': idx < series.InstanceInfoList.length - 1 ? '5px' : '0px' }"
|
||||
@click.stop="showMultiFrames(index, series, i, instance)">
|
||||
<div>
|
||||
|
|
@ -99,6 +101,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</template>
|
||||
|
||||
</el-collapse>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -119,6 +123,10 @@ export default {
|
|||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
readingTool: {
|
||||
type: Number,
|
||||
default: 2
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
|
@ -248,6 +256,7 @@ export default {
|
|||
background-color: #607d8b !important;
|
||||
border: 1px solid #607d8b !important;
|
||||
}
|
||||
|
||||
::v-deep .el-progress__text {
|
||||
color: #ccc;
|
||||
font-size: 12px;
|
||||
|
|
@ -299,6 +308,7 @@ export default {
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-collapse {
|
||||
border: none;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,144 @@
|
|||
<template>
|
||||
<div class="SurfaceViewport" ref="SurfaceViewport" v-show="visible" id="SurfaceViewport"></div>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
getRenderingEngine,
|
||||
CONSTANTS,
|
||||
setVolumesForViewports,
|
||||
eventTarget,
|
||||
Enums,
|
||||
utilities,
|
||||
} from '@cornerstonejs/core'
|
||||
import * as cornerstoneTools from '@cornerstonejs/tools'
|
||||
import setCtTransferFunctionForVolumeActor from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setCtTransferFunctionForVolumeActor'
|
||||
const {
|
||||
Enums: csToolsEnums,
|
||||
segmentation,
|
||||
TrackballRotateTool,
|
||||
ToolGroupManager
|
||||
} = cornerstoneTools
|
||||
const { MouseBindings, Events: toolsEvents } = csToolsEnums
|
||||
export default {
|
||||
name: "SurfaceViewport",
|
||||
props: {
|
||||
renderingEngineId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
viewportId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
visitInfo: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
volumeId: null,
|
||||
info: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
eventTarget.addEventListener(Enums.Events.WEB_WORKER_PROGRESS, (evt) => {
|
||||
const { progress } = evt.detail;
|
||||
console.log(progress, 'progress')
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
async setSeriesInfo(obj) {
|
||||
try {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
let { volumeId } = obj
|
||||
this.info = obj
|
||||
this.volumeId = volumeId
|
||||
await setVolumesForViewports(
|
||||
renderingEngine,
|
||||
[{ volumeId, callback: setCtTransferFunctionForVolumeActor }],
|
||||
[this.viewportId]
|
||||
);
|
||||
const volumeActor = viewport.getDefaultActor()
|
||||
.actor;
|
||||
utilities.applyPreset(
|
||||
volumeActor,
|
||||
CONSTANTS.VIEWPORT_PRESETS.find((preset) => preset.name === 'CT-Bone')
|
||||
);
|
||||
volumeActor.setVisibility(false);
|
||||
viewport.render();
|
||||
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
},
|
||||
async setSurface() {
|
||||
let { segmentationId, segmentations } = this.info
|
||||
const toolGroup = ToolGroupManager.getToolGroup(this.viewportId)
|
||||
toolGroup.setToolActive(TrackballRotateTool.toolName, {
|
||||
bindings: [
|
||||
{
|
||||
mouseButton: MouseBindings.Primary,
|
||||
},
|
||||
],
|
||||
});
|
||||
let s = segmentation.getActiveSegmentation(this.viewportId)
|
||||
console.log(s)
|
||||
if (s) {
|
||||
await segmentation.removeSegmentationRepresentation(this.viewportId, {
|
||||
segmentationId: s.segmentationId,
|
||||
type: csToolsEnums.SegmentationRepresentations.Surface,
|
||||
})
|
||||
}
|
||||
await segmentation.addSegmentationRepresentations(this.viewportId, [
|
||||
{
|
||||
segmentationId,
|
||||
type: csToolsEnums.SegmentationRepresentations.Contour,
|
||||
},
|
||||
]);
|
||||
console.log("loading...")
|
||||
this.$nextTick(async () => {
|
||||
await segmentation.addSegmentationRepresentations(this.viewportId, [
|
||||
{
|
||||
segmentationId,
|
||||
type: csToolsEnums.SegmentationRepresentations.Surface,
|
||||
},
|
||||
]);
|
||||
segmentations.segments.forEach(item => {
|
||||
segmentation.config.color.setSegmentIndexColor(this.viewportId, item.segmentationId, item.segmentIndex, this.hex2Rgb(item.color))
|
||||
})
|
||||
})
|
||||
},
|
||||
hex2Rgb(hexValue, alpha = 1) {
|
||||
const rgx = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
||||
const hex = hexValue.replace(rgx, (m, r, g, b) => r + r + g + g + b + b);
|
||||
const rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
if (!rgb) {
|
||||
return hexValue;
|
||||
}
|
||||
const r = parseInt(rgb[1], 16),
|
||||
g = parseInt(rgb[2], 16),
|
||||
b = parseInt(rgb[3], 16);
|
||||
return [r, g, b, alpha * 255];
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
#SurfaceViewport {
|
||||
width: 450px;
|
||||
height: 300px;
|
||||
position: fixed;
|
||||
top: 100px;
|
||||
z-index: 9999;
|
||||
left: 100px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -14,6 +14,25 @@
|
|||
<div v-if="imageInfo.total">Image: #{{ `${series.SliceIndex + 1}/${imageInfo.total}` }}</div>
|
||||
<div>{{ series.Modality }}</div>
|
||||
</div>
|
||||
<div v-if="series && taskInfo && taskInfo.IsReadingTaskViewInOrder === 1" class="top-center-tool">
|
||||
<div class="toggle-visit-container">
|
||||
<div class="arrw_icon"
|
||||
:style="{ cursor: series.TaskInfo && series.TaskInfo.VisitTaskNum !== 0 ? 'pointer' : 'not-allowed', color: series.TaskInfo && series.TaskInfo.VisitTaskNum !== 0 ? '#fff' : '#6b6b6b' }"
|
||||
@click.stop.prevent="toggleTask($event, series.TaskInfo.VisitTaskNum, -1)"
|
||||
@dblclick.stop="preventDefault($event)">
|
||||
<i class="el-icon-caret-left" />
|
||||
</div>
|
||||
<div class="arrow_text">
|
||||
{{ series.TaskInfo ? series.TaskInfo.TaskBlindName : '' }}
|
||||
</div>
|
||||
<div class="arrw_icon"
|
||||
:style="{ cursor: series.TaskInfo && series.TaskInfo.VisitTaskNum < taskInfo.VisitNum ? 'pointer' : 'not-allowed', color: series.TaskInfo && series.TaskInfo.VisitTaskNum < taskInfo.VisitNum ? '#fff' : '#6b6b6b' }"
|
||||
@click.stop.prevent="toggleTask($event, series.TaskInfo.VisitTaskNum, 1)"
|
||||
@dblclick.stop="preventDefault($event)">
|
||||
<i class="el-icon-caret-right" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="series" class="right-top-text">
|
||||
<div>{{ series.Description }}</div>
|
||||
</div>
|
||||
|
|
@ -78,9 +97,24 @@ import * as cornerstoneTools from '@cornerstonejs/tools'
|
|||
import { createImageIdsAndCacheMetaData } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/createImageIdsAndCacheMetaData'
|
||||
import setCtTransferFunctionForVolumeActor from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setCtTransferFunctionForVolumeActor'
|
||||
import { setCtMappingRange } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setCtTransferFunctionForVolumeActor'
|
||||
import { setPetColorMapTransferFunctionForVolumeActor } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setPetColorMapTransferFunctionForVolumeActor'
|
||||
import { vec3, mat4 } from 'gl-matrix'
|
||||
import {
|
||||
setPetTransferFunctionForVolumeActor
|
||||
} from './helpers/index.js'
|
||||
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
|
||||
import {
|
||||
renderSegmentation,
|
||||
readingSegmentByConfig,
|
||||
selectSegmentation,
|
||||
selectSegment,
|
||||
createSegmentationRepresentation,
|
||||
viewSegmentation,
|
||||
viewSegment,
|
||||
jumpBidirectional,
|
||||
viewBidirectional,
|
||||
changeColor,
|
||||
resetViewport
|
||||
} from "./helpers/segmentations"
|
||||
export default {
|
||||
name: 'MPRViewport',
|
||||
props: {
|
||||
|
|
@ -96,6 +130,36 @@ export default {
|
|||
type: Number,
|
||||
required: true
|
||||
},
|
||||
histogramVisible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
actionConfiguration: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
SegmentConfig: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
curSegSeries: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
segmentIndex: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
segmentationId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -139,6 +203,7 @@ export default {
|
|||
rotateAngle: 0,
|
||||
rotateBarLeft: 0,
|
||||
loading: false,
|
||||
toggleClipPlayTimer: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -148,6 +213,39 @@ export default {
|
|||
this.$nextTick(() => {
|
||||
this.initViewport()
|
||||
})
|
||||
DicomEvent.$on('createSegmentationRepresentation', (segmentationId) => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
createSegmentationRepresentation(this.viewportId, segmentationId)
|
||||
})
|
||||
DicomEvent.$on('viewSegmentation', (obj) => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
viewSegmentation(obj, this.viewportId)
|
||||
})
|
||||
DicomEvent.$on('viewSegment', (obj) => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
viewSegment(obj, this.viewportId)
|
||||
})
|
||||
DicomEvent.$on('jumpBidirectional', (obj) => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
jumpBidirectional(obj, this.viewportId, this.volumeId)
|
||||
})
|
||||
DicomEvent.$on('viewBidirectional', (obj) => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
viewBidirectional(obj, this.viewportId)
|
||||
})
|
||||
DicomEvent.$on('changeColor', (obj) => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
changeColor(obj, this.viewportId)
|
||||
})
|
||||
DicomEvent.$on('resetViewport', () => {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.TaskInfo.VisitTaskId !== this.series.TaskInfo.VisitTaskId) return false
|
||||
resetViewport(this.viewportId)
|
||||
})
|
||||
DicomEvent.$on('renderSegmentation', async (viewportId) => {
|
||||
// if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
|
||||
if (this.viewportId !== viewportId) return false
|
||||
await renderSegmentation(this.series, this.series.TaskInfo, this.viewportId, this.SegmentConfig, this.renderingEngineId, null, this.actionConfiguration)
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
MPRInfo: {
|
||||
|
|
@ -166,6 +264,25 @@ export default {
|
|||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
SegmentConfig: {
|
||||
handler() {
|
||||
if (!this.segmentationId) return false
|
||||
if (!this.series.TaskInfo) return false
|
||||
readingSegmentByConfig(this.series, this.series.TaskInfo, this.viewportId, this.segmentationId, this.SegmentConfig)
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
segmentIndex() {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
|
||||
if (this.segmentIndex <= 0) return false
|
||||
selectSegment(this.viewportId, this.segmentationId, this.segmentIndex)
|
||||
},
|
||||
segmentationId() {
|
||||
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
|
||||
if (!this.segmentationId) return false
|
||||
selectSegmentation(this.viewportId, this.segmentationId)
|
||||
readingSegmentByConfig(this.series, this.series.TaskInfo, this.viewportId, this.segmentationId, this.SegmentConfig)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -183,6 +300,7 @@ export default {
|
|||
this.element.addEventListener('CORNERSTONE_VOI_MODIFIED', this.voiModified)
|
||||
this.element.addEventListener('CORNERSTONE_IMAGE_RENDERED', this.imageRendered)
|
||||
this.element.addEventListener('wheel', (e) => {
|
||||
// if (this.histogramVisible) return false
|
||||
console.log('CORNERSTONE_STACK_VIEWPORT_SCROLL')
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
|
|
@ -266,6 +384,7 @@ export default {
|
|||
},
|
||||
stackNewImage(e) {
|
||||
const { detail } = e
|
||||
|
||||
this.series.SliceIndex = detail.imageIndex
|
||||
this.sliderInfo.height = detail.imageIndex * 100 / detail.numberOfSlices
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
|
|
@ -286,13 +405,14 @@ export default {
|
|||
let spacing = volume ? volume.spacing : []
|
||||
this.imageInfo.sliceThickness = type === 'AXIAL' ? spacing[2] : spacing[0]
|
||||
this.getOrientationMarker()
|
||||
if (this.series && this.series.Id) {
|
||||
let annotations = cornerstoneTools.annotation.state.getAllAnnotations().filter(item => item.metadata.toolName !== 'ScaleOverlay' && item.metadata.volumeId !== this.volumeId && !item.metadata.segmentationId && item.seriesId !== this.series.Id)
|
||||
annotations.forEach(item => {
|
||||
cornerstoneTools.annotation.state.removeAnnotation(item.annotationUID)
|
||||
})
|
||||
}
|
||||
// if (this.series && this.series.Id) {
|
||||
// let annotations = cornerstoneTools.annotation.state.getAllAnnotations().filter(item => item.metadata.toolName !== 'ScaleOverlay' && item.metadata.volumeId !== this.volumeId && !item.metadata.segmentationId && item.seriesId !== this.series.Id)
|
||||
// annotations.forEach(item => {
|
||||
// cornerstoneTools.annotation.state.removeAnnotation(item.annotationUID)
|
||||
// })
|
||||
// }
|
||||
this.$emit('renderAnnotations', this.series)
|
||||
this.$emit("resetHistogram")
|
||||
let properties = viewport.getProperties()
|
||||
if (this.isFusion) {
|
||||
properties = viewport.getProperties(this.ptVolumeId)
|
||||
|
|
@ -418,11 +538,20 @@ export default {
|
|||
this.playClipState = isPlay
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
|
||||
if (isPlay) {
|
||||
cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: true })
|
||||
this.toggleClipPlayTimer = setInterval(() => {
|
||||
let index = this.series.SliceIndex + 1;
|
||||
if (index > this.imageInfo.total - 1) index = 0
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: index });
|
||||
|
||||
}, framesPerSecond)
|
||||
// cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: true })
|
||||
} else {
|
||||
cornerstoneTools.utilities.cine.stopClip(viewport.element)
|
||||
if (this.toggleClipPlayTimer) {
|
||||
clearInterval(this.toggleClipPlayTimer)
|
||||
this.toggleClipPlayTimer = null
|
||||
}
|
||||
// cornerstoneTools.utilities.cine.stopClip(viewport.element)
|
||||
}
|
||||
},
|
||||
scrollPage(type) {
|
||||
|
|
@ -520,7 +649,6 @@ export default {
|
|||
if (this.series && data.Id === this.series.Id && data.Description === this.series.Description && !isLocate && !data.isLocation) {
|
||||
data.SliceIndex = this.series.SliceIndex
|
||||
}
|
||||
// console.log(data)
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
if (isLocate) return csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex });
|
||||
|
|
@ -532,7 +660,8 @@ export default {
|
|||
.setVolumes([{
|
||||
volumeId: this.volumeId, callback: (r) => {
|
||||
if (this.series.Modality === 'PT' || this.series.Modality === 'NM') {
|
||||
setPetColorMapTransferFunctionForVolumeActor(r, true)
|
||||
setPetTransferFunctionForVolumeActor(r)
|
||||
// viewport.setProperties({ voiRange: { upper: 5, lower: 0 } })
|
||||
} else {
|
||||
const voi = metaData.get('voiLutModule', res.volume._imageIds[Math.ceil((res.volume._imageIds.length - 1) / 2)])
|
||||
setCtMappingRange(voi.windowWidth[0], voi.windowCenter[0])
|
||||
|
|
@ -541,19 +670,28 @@ export default {
|
|||
console.log("渲染成功")
|
||||
}
|
||||
}]).then(r => {
|
||||
if (data.segment) {
|
||||
return DicomEvent.$emit("isloaded", { segment: data.segment, isChange: data.isChange })
|
||||
}
|
||||
if (data.isLocation) {
|
||||
setTimeout(() => { csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex }); })
|
||||
}
|
||||
DicomEvent.$emit("isloaded", { isChange: data.isChange })
|
||||
})
|
||||
res.volume.dimensionGroupNumber = 2;
|
||||
viewport.render()
|
||||
if (this.series.Modality === 'PT' || this.series.Modality === 'NM') {
|
||||
setTimeout(() => {
|
||||
viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true })
|
||||
viewport.resetProperties()
|
||||
viewport.setProperties({ voiRange: { upper: 5, lower: 0 } })
|
||||
viewport.render()
|
||||
renderingEngine.render()
|
||||
}, 100)
|
||||
}
|
||||
await renderSegmentation(this.series, this.series.TaskInfo, this.viewportId, this.SegmentConfig, this.renderingEngineId, data.segment, this.actionConfiguration)
|
||||
DicomEvent.$emit('SegmentationLoading', this.viewportId)
|
||||
if (this.series.hasOwnProperty('curIndex')) return this.setFullScreen(this.series.curIndex)
|
||||
this.setFullScreen(Math.ceil((res.volume._imageIds.length - 1) / 2) - 1)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
},
|
||||
cornerstoneToolsMouseMove(e) {
|
||||
const { currentPoints } = e.detail
|
||||
|
|
@ -723,6 +861,12 @@ export default {
|
|||
return `NS: ${this.$store.state.trials.downloadTip}`
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
if (this.toggleClipPlayTimer) {
|
||||
clearInterval(this.toggleClipPlayTimer)
|
||||
this.toggleClipPlayTimer = null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -1357,6 +1357,7 @@ export default {
|
|||
await this.getQuestionCalculateRelation()
|
||||
await this.getQuestions(true)
|
||||
this.$emit('resetAnnotations', this.visitTaskId)
|
||||
this.initSegmentBinding()
|
||||
this.$nextTick(() => {
|
||||
this.rerender = true
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,436 @@
|
|||
import * as cornerstoneTools from '@cornerstonejs/tools';
|
||||
import * as cornerstone from "@cornerstonejs/core";
|
||||
import dcmjs from '@/utils/dcmUpload/dcmjs'
|
||||
import * as cornerstoneAdapters from "@cornerstonejs/adapters";
|
||||
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'
|
||||
import * as polySeg from '@cornerstonejs/polymorphic-segmentation'
|
||||
cornerstoneTools.init({ addons: { polySeg } })
|
||||
import { getSegmentationList, getSegmentList } from '@/api/reading'
|
||||
import Vue from 'vue'
|
||||
const {
|
||||
ToolGroupManager,
|
||||
Enums: csToolsEnums,
|
||||
segmentation,
|
||||
annotation,
|
||||
LabelMapEditWithContourTool,
|
||||
SegmentBidirectionalTool,
|
||||
CrosshairsTool,
|
||||
utilities: CStUtils,
|
||||
} = cornerstoneTools;
|
||||
const { MouseBindings, Events: toolsEvents } = csToolsEnums
|
||||
const { segmentation: segmentationUtils } = CStUtils;
|
||||
const { cache, getRenderingEngine, imageLoader, eventTarget, metaData, utilities: csUtils, volumeLoader } = cornerstone;
|
||||
const { Cornerstone3D } = cornerstoneAdapters.adaptersSEG;
|
||||
let viewportInfo = {}
|
||||
let renderingEngineId = null
|
||||
async function createSegmentation(toolGroupId, volumeId, segmentationId) {
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId) || ToolGroupManager.getToolGroup('share-viewport-volume')
|
||||
toolGroup.setToolActive(
|
||||
LabelMapEditWithContourTool.toolName,
|
||||
);
|
||||
if (!cache.getVolume(segmentationId)) {
|
||||
await volumeLoader.createAndCacheDerivedLabelmapVolume(
|
||||
volumeId,
|
||||
{
|
||||
volumeId: segmentationId
|
||||
}
|
||||
)
|
||||
}
|
||||
if (!segmentation.state.getSegmentation(segmentationId)) {
|
||||
segmentation.addSegmentations([
|
||||
{
|
||||
segmentationId,
|
||||
representation: {
|
||||
type: cornerstoneTools.Enums.SegmentationRepresentations
|
||||
.Labelmap,
|
||||
data: {
|
||||
volumeId: segmentationId
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
async function createSegmentationRepresentation(viewportId, segmentationId) {
|
||||
segmentation.addSegmentationRepresentations(viewportId, [
|
||||
{
|
||||
segmentationId,
|
||||
type: csToolsEnums.SegmentationRepresentations.Labelmap,
|
||||
},
|
||||
])
|
||||
}
|
||||
async function readSegmentation(obj, series, segmentationId, isFile = false) {
|
||||
let imageId = null
|
||||
if (isFile) {
|
||||
imageId = cornerstoneDICOMImageLoader.wadouri.fileManager.add(obj);
|
||||
} else {
|
||||
const imageIdObj = await cornerstoneDICOMImageLoader.wadouri.loadImage(`wadouri:${Vue.prototype.OSSclientConfig.basePath}${obj}`).promise
|
||||
imageId = imageIdObj.imageId
|
||||
}
|
||||
const image = await imageLoader.loadAndCacheImage(imageId);
|
||||
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
const instance = metaData.get("instance", imageId);
|
||||
|
||||
if (instance.Modality !== "SEG") {
|
||||
console.error("This is not segmentation: " + file.name);
|
||||
return;
|
||||
}
|
||||
|
||||
const arrayBuffer = image.data.byteArray.buffer;
|
||||
await loadSegmentation(arrayBuffer, series, segmentationId);
|
||||
}
|
||||
async function loadSegmentation(arrayBuffer, series, segmentationId) {
|
||||
const generateToolState =
|
||||
await Cornerstone3D.Segmentation.generateToolState(
|
||||
series.ImageIds,
|
||||
arrayBuffer,
|
||||
metaData,
|
||||
);
|
||||
if (generateToolState.labelmapBufferArray.length !== 1) {
|
||||
alert(
|
||||
"Overlapping segments in your segmentation are not supported yet. You can turn on the skipOverlapping option but it will override the overlapping segments."
|
||||
);
|
||||
return;
|
||||
}
|
||||
// await createSegmentation(segmentationId);
|
||||
let arr = []
|
||||
generateToolState.segMetadata.data.forEach(item => {
|
||||
if (item) {
|
||||
let Target = JSON.parse(JSON.stringify(item))
|
||||
arr.push(Target)
|
||||
}
|
||||
})
|
||||
|
||||
let mapping = {}
|
||||
|
||||
arr.forEach((item, index) => {
|
||||
mapping[index + 1] = Number(item.SegmentNumber)
|
||||
})
|
||||
const megmentGroup =
|
||||
segmentation.state.getSegmentation(segmentationId);
|
||||
const { imageIds } = megmentGroup.representationData.Labelmap;
|
||||
const derivedSegmentationImages = imageIds.map(imageId =>
|
||||
cache.getImage(imageId)
|
||||
);
|
||||
|
||||
const volumeScalarData = new Uint8Array(
|
||||
generateToolState.labelmapBufferArray[0]
|
||||
);
|
||||
const remappedData = new Uint8Array(volumeScalarData.length);
|
||||
for (let i = 0; i < volumeScalarData.length; i++) {
|
||||
const value = volumeScalarData[i];
|
||||
remappedData[i] = value === 0 ? 0 : (mapping[value] ? mapping[value] : value);
|
||||
}
|
||||
for (let i = 0; i < derivedSegmentationImages.length; i++) {
|
||||
const voxelManager = derivedSegmentationImages[i].voxelManager;
|
||||
const scalarData = voxelManager.getScalarData();
|
||||
scalarData.set(
|
||||
remappedData.slice(
|
||||
i * scalarData.length,
|
||||
(i + 1) * scalarData.length
|
||||
)
|
||||
);
|
||||
voxelManager.setScalarData(scalarData);
|
||||
}
|
||||
}
|
||||
function hex2Rgb(hexValue, alpha = 1) {
|
||||
const rgx = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
||||
const hex = hexValue.replace(rgx, (m, r, g, b) => r + r + g + g + b + b);
|
||||
const rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
if (!rgb) {
|
||||
return hexValue;
|
||||
}
|
||||
const r = parseInt(rgb[1], 16),
|
||||
g = parseInt(rgb[2], 16),
|
||||
b = parseInt(rgb[3], 16);
|
||||
return [r, g, b, alpha * 255];
|
||||
}
|
||||
function removeSegmentFromViewport(viewportId) {
|
||||
return new Promise(resolve => {
|
||||
if (viewportInfo[viewportId] && viewportInfo[viewportId].length > 0) {
|
||||
viewportInfo[viewportId].forEach(async segmentationId => {
|
||||
segmentation.removeSegmentation(segmentationId)
|
||||
segmentation.state.removeSegmentation(segmentationId)
|
||||
let annotations = annotation.state.getAllAnnotations().filter(item => item.metadata.segmentationId && segmentationId === item.metadata.segmentationId && item.metadata.toolName === "SegmentBidirectional");
|
||||
annotations.forEach(item => {
|
||||
annotation.state.removeAnnotation(item.annotationUID)
|
||||
})
|
||||
})
|
||||
resetViewport(viewportId)
|
||||
}
|
||||
viewportInfo[viewportId] = []
|
||||
resolve(true)
|
||||
})
|
||||
}
|
||||
function createSegmentConfiguration(segmentIndex, segmentationId, viewportId, actionConfiguration, otherSegments) {
|
||||
const containedSegmentIndices = otherSegments
|
||||
? { has: (segmentIndex) => otherSegments.indexOf(segmentIndex) !== -1 }
|
||||
: undefined;
|
||||
const colorConfig = segmentation.config.color.getSegmentIndexColor(
|
||||
viewportId,
|
||||
segmentationId,
|
||||
segmentIndex
|
||||
);
|
||||
// Allow null style to skip style set
|
||||
let color, activeColor;
|
||||
if (colorConfig?.length) {
|
||||
color = `rgb(${colorConfig.join(',')})`;
|
||||
activeColor = color;
|
||||
}
|
||||
const style = {
|
||||
color,
|
||||
colorHighlightedActive: activeColor,
|
||||
colorActive: activeColor,
|
||||
textBoxColor: color,
|
||||
textBoxColorActive: activeColor,
|
||||
textBoxColorHighlightedActive: activeColor,
|
||||
};
|
||||
const label = otherSegments
|
||||
? `Combined ${segmentIndex} with ${otherSegments.join(', ')}`
|
||||
: `Segment ${segmentIndex}`;
|
||||
|
||||
actionConfiguration.contourBidirectional.data.segmentData.set(segmentIndex, {
|
||||
containedSegmentIndices,
|
||||
label,
|
||||
style,
|
||||
});
|
||||
actionConfiguration.contourBidirectional.data.segmentationId = segmentationId
|
||||
actionConfiguration.contourBidirectional.data.segmentIndex = segmentIndex
|
||||
}
|
||||
async function readingSegmentByConfig(series, visitInfo, viewportId, segmentationId, SegmentConfig) {
|
||||
let data = {
|
||||
SeriesId: series.Id,
|
||||
VisitTaskId: visitInfo.VisitTaskId,
|
||||
PageSize: 9999,
|
||||
PageIndex: 1,
|
||||
}
|
||||
let res = await getSegmentationList(data);
|
||||
if (res.IsSuccess) {
|
||||
let list = res.Result.CurrentPageData;
|
||||
changeInactiveSegmentShow(list, viewportId, segmentationId, SegmentConfig)
|
||||
}
|
||||
segmentation.config.style.setStyle(
|
||||
{
|
||||
type: csToolsEnums.SegmentationRepresentations.Labelmap,
|
||||
},
|
||||
{
|
||||
renderFill: SegmentConfig.renderFill,
|
||||
renderOutline: SegmentConfig.renderOutline,
|
||||
outlineWidth: Number(SegmentConfig.outlineWidth),
|
||||
fillAlpha: Number(SegmentConfig.fillAlpha),
|
||||
}
|
||||
)
|
||||
}
|
||||
function selectSegmentation(viewportId, segmentationId) {
|
||||
segmentation.activeSegmentation.setActiveSegmentation(viewportId, segmentationId)
|
||||
}
|
||||
function selectSegment(viewportId, segmentationId, segmentIndex) {
|
||||
selectSegmentation(viewportId, segmentationId)
|
||||
segmentation.segmentIndex.setActiveSegmentIndex(segmentationId, segmentIndex);
|
||||
}
|
||||
async function changeInactiveSegmentShow(list, viewportId, segmentationId, SegmentConfig) {
|
||||
let segmentList = list
|
||||
segmentList.forEach(segment => {
|
||||
segmentation.config.visibility.setSegmentationRepresentationVisibility(
|
||||
viewportId,
|
||||
{
|
||||
segmentationId: segment.Id,
|
||||
type: csToolsEnums.SegmentationRepresentations.Labelmap,
|
||||
},
|
||||
SegmentConfig.InactiveSegmentations.show
|
||||
);
|
||||
})
|
||||
segmentation.config.visibility.setSegmentationRepresentationVisibility(
|
||||
viewportId,
|
||||
{
|
||||
segmentationId: segmentationId,
|
||||
type: csToolsEnums.SegmentationRepresentations.Labelmap,
|
||||
},
|
||||
true
|
||||
);
|
||||
let arr = []
|
||||
for (let i = 0; i < segmentList.length; i++) {
|
||||
let item = segmentList[i]
|
||||
let params = {
|
||||
SegmentationId: item.Id,
|
||||
PageSize: 9999,
|
||||
PageIndex: 1,
|
||||
}
|
||||
let r = await getSegmentList(params)
|
||||
if (r.IsSuccess) {
|
||||
let segments = r.Result.CurrentPageData
|
||||
segments.forEach(s => {
|
||||
let obj = {
|
||||
segmentationId: item.Id,
|
||||
segmentIndex: s.SegmentNumber,
|
||||
view: SegmentConfig.InactiveSegmentations.show
|
||||
}
|
||||
if (item.Id === segmentationId) {
|
||||
obj.view = true
|
||||
}
|
||||
arr.push(obj)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
viewBidirectional(arr, viewportId)
|
||||
}
|
||||
function viewSegmentation(item, viewportId) {
|
||||
segmentation.config.visibility.setSegmentationRepresentationVisibility(
|
||||
viewportId,
|
||||
{
|
||||
segmentationId: item.segmentationId,
|
||||
type: csToolsEnums.SegmentationRepresentations.Labelmap,
|
||||
},
|
||||
item.view
|
||||
);
|
||||
viewBidirectional(item.segments, viewportId)
|
||||
}
|
||||
async function jumpBidirectional(item, viewportId, volumeId) {
|
||||
// DicomEvent.$emit('jumpBidirectional', item)
|
||||
if (item.bidirectional) {
|
||||
let an = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
|
||||
console.log(an, 'an')
|
||||
if (!an) return false
|
||||
if (['viewport-MPR-1', 'viewport-MPR-2'].includes(viewportId)) return false
|
||||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(viewportId)
|
||||
let key = Object.keys(an.data.cachedStats)[0]; // referencedImageId
|
||||
if (key) {
|
||||
let sliceIndex = key.split("?")[1].split("&")[0].split("=")[1]
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIndex });
|
||||
} else {
|
||||
const points = an.data.handles.points;
|
||||
const worldPoint = points[0]; // 取一个点
|
||||
let volume = cache.getVolume(volumeId)
|
||||
let { imageData, numFrames } = volume
|
||||
const ijk = imageData.worldToIndex(worldPoint);
|
||||
const sliceIndex = Math.abs(Math.round(ijk[2]));
|
||||
// console.log(sliceIndex, 'sliceIndex')
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: numFrames - sliceIndex - 1 });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
function viewSegment(item, viewportId) {
|
||||
segmentation.config.visibility.setSegmentIndexVisibility(viewportId, {
|
||||
segmentationId: item.segmentationId,
|
||||
type: csToolsEnums.SegmentationRepresentations.Labelmap,
|
||||
}, item.segmentIndex, item.view)
|
||||
viewBidirectional([item], viewportId)
|
||||
}
|
||||
function viewBidirectional(arr, viewportId) {
|
||||
for (let j = 0; j < arr.length; j++) {
|
||||
let item = arr[j]
|
||||
let bidirectional = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
|
||||
// item.bidirectionalView = view
|
||||
if (!bidirectional) continue
|
||||
let view = item.view
|
||||
if (item.hasOwnProperty('bidirectionalView')) view = item.bidirectionalView
|
||||
annotation.visibility.setAnnotationVisibility(bidirectional.annotationUID, view)
|
||||
}
|
||||
resetViewport(viewportId)
|
||||
}
|
||||
function resetViewport(viewportId) {
|
||||
let renderingEngine = getRenderingEngine(renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(viewportId)
|
||||
viewport.render()
|
||||
}
|
||||
function changeColor(item, viewportId) {
|
||||
segmentation.config.color.setSegmentIndexColor(viewportId, item.segmentationId, item.segmentIndex, hex2Rgb(item.color))
|
||||
}
|
||||
async function renderSegmentation(series, visitInfo, viewportId, SegmentConfig, RenderingEngineId, Segment = null, actionConfiguration) {
|
||||
try {
|
||||
// console.log(segmentation, 'segmentation')
|
||||
renderingEngineId = RenderingEngineId
|
||||
await removeSegmentFromViewport(viewportId)
|
||||
let data = {
|
||||
SeriesId: series.Id,
|
||||
VisitTaskId: visitInfo.VisitTaskId,
|
||||
PageSize: 9999,
|
||||
PageIndex: 1,
|
||||
}
|
||||
let segmentationId = null;
|
||||
let segmentIndex = null;
|
||||
let res = await getSegmentationList(data);
|
||||
if (res.IsSuccess) {
|
||||
let list = res.Result.CurrentPageData;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i]
|
||||
if (!segmentationId) segmentationId = item.Id
|
||||
await createSegmentation(viewportId, series.SeriesInstanceUid, item.Id)
|
||||
if (item.SEGUrl) await readSegmentation(item.SEGUrl, series, item.Id)
|
||||
createSegmentationRepresentation(viewportId, item.Id)
|
||||
if (!viewportInfo[viewportId]) {
|
||||
viewportInfo[viewportId] = [item.Id]
|
||||
} else {
|
||||
viewportInfo[viewportId].push(item.Id)
|
||||
}
|
||||
let params = {
|
||||
SegmentationId: item.Id,
|
||||
PageSize: 9999,
|
||||
PageIndex: 1,
|
||||
}
|
||||
let r = await getSegmentList(params)
|
||||
if (r.IsSuccess) {
|
||||
let segments = r.Result.CurrentPageData
|
||||
segments.forEach(s => {
|
||||
if (!segmentIndex) segmentIndex = s.SegmentNumber
|
||||
let SegmentJson = s.SegmentJson ? JSON.parse(s.SegmentJson) : {};
|
||||
segmentation.segmentIndex.setActiveSegmentIndex(s.SegmentationId, s.SegmentNumber);
|
||||
segmentation.config.color.setSegmentIndexColor(viewportId, s.SegmentationId, s.SegmentNumber, hex2Rgb(s.ColorRgb))
|
||||
segmentation.segmentLocking.setSegmentIndexLocked(s.SegmentationId, s.SegmentNumber, true)
|
||||
if (SegmentJson.bidirectional) {
|
||||
let an = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === s.SegmentationId && i.metadata.segmentIndex === SegmentJson.bidirectional.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
|
||||
if (!an) {
|
||||
let { majorAxis, minorAxis } = SegmentJson.bidirectional
|
||||
// createSegmentConfiguration(s.SegmentNumber, s.SegmentationId, viewportId, actionConfiguration)
|
||||
SegmentBidirectionalTool.hydrate(viewportId, [majorAxis, minorAxis], {
|
||||
segmentIndex: s.SegmentNumber,
|
||||
segmentationId: s.SegmentationId,
|
||||
})
|
||||
}
|
||||
an = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === s.SegmentationId && i.metadata.segmentIndex === SegmentJson.bidirectional.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
|
||||
if (an) {
|
||||
annotation.locking.setAnnotationLocked(an.annotationUID, true)
|
||||
annotation.visibility.setAnnotationVisibility(an.annotationUID, true)
|
||||
}
|
||||
console.log(an, 'an')
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
if (segmentationId && segmentIndex) {
|
||||
selectSegment(viewportId, segmentationId, segmentIndex)
|
||||
}
|
||||
|
||||
if (Segment) {
|
||||
jumpBidirectional(Segment, viewportId, series.SeriesInstanceUid)
|
||||
}
|
||||
}
|
||||
}
|
||||
readingSegmentByConfig(series, visitInfo, viewportId, segmentationId, SegmentConfig)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
createSegmentation,
|
||||
createSegmentationRepresentation,
|
||||
readSegmentation,
|
||||
renderSegmentation,
|
||||
readingSegmentByConfig,
|
||||
selectSegment,
|
||||
selectSegmentation,
|
||||
viewSegmentation,
|
||||
viewSegment,
|
||||
jumpBidirectional,
|
||||
viewBidirectional,
|
||||
changeColor,
|
||||
resetViewport
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import vtkColorTransferFunction from "@kitware/vtk.js/Rendering/Core/ColorTransferFunction";
|
||||
import vtkPiecewiseFunction from "@kitware/vtk.js/Common/DataModel/PiecewiseFunction";
|
||||
import { Scale } from "@kitware/vtk.js/Rendering/Core/ColorTransferFunction/Constants";
|
||||
import { cache, metaData, utilities } from "@cornerstonejs/core";
|
||||
|
||||
const { getColormap } = utilities.colormap;
|
||||
|
|
@ -14,7 +15,7 @@ function getWindowCenterFromVolumeId(volumeId) {
|
|||
? voiLutModule.windowCenter[0]
|
||||
: voiLutModule?.windowCenter;
|
||||
const center = Number(rawCenter);
|
||||
return Number.isFinite(center) ? center : null;
|
||||
return center;
|
||||
}
|
||||
|
||||
export default function setPetColorMapTransferFunctionForVolumeActor({
|
||||
|
|
@ -33,7 +34,25 @@ export default function setPetColorMapTransferFunctionForVolumeActor({
|
|||
|
||||
const center = getWindowCenterFromVolumeId(volumeId);
|
||||
const upper = center > 1 ? center : 5;
|
||||
cfun.setMappingRange(1, upper);
|
||||
const safeUpper = Number.isFinite(upper) && upper > 0 ? upper : 5;
|
||||
const rangeMin = 0;
|
||||
const rangeMax = safeUpper;
|
||||
cfun.setScale(Scale.LOG10);
|
||||
cfun.setMappingRange(rangeMin, rangeMax);
|
||||
|
||||
volumeActor.getProperty().setRGBTransferFunction(0, cfun);
|
||||
//低信号更明显,系数可以更小
|
||||
const thresholdValue0 = Math.max(rangeMin, rangeMax * 0.002);
|
||||
const thresholdValue1 = Math.max(thresholdValue0, rangeMax * 0.02);
|
||||
const delta = Math.abs(rangeMax - rangeMin) * 0.001;
|
||||
const threshold0MinusDelta = Math.max(rangeMin, thresholdValue0 - delta);
|
||||
|
||||
const ofun = vtkPiecewiseFunction.newInstance();
|
||||
ofun.addPoint(rangeMin, 0.0);
|
||||
ofun.addPoint(threshold0MinusDelta, 0.0);
|
||||
//低信号更明显,系数可以更小
|
||||
ofun.addPoint(thresholdValue0, 0.08);
|
||||
ofun.addPoint(thresholdValue1, 0.9);
|
||||
ofun.addPoint(rangeMax, 1.0);
|
||||
volumeActor.getProperty().setScalarOpacity(0, ofun);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -311,6 +311,15 @@ const config = {
|
|||
'isDisabled': false,
|
||||
'disabledReason': ''
|
||||
},
|
||||
{
|
||||
'name': '定圆工具',
|
||||
'icon': 'oval',
|
||||
'toolName': 'FixedRadiusCircleROI',
|
||||
'props': ['radius', 'area', 'mean', 'max', 'stdDev'],
|
||||
'i18nKey': 'trials:reading:button:fixedCircle',
|
||||
'isDisabled': false,
|
||||
'disabledReason': ''
|
||||
}
|
||||
],
|
||||
'customizeStandardsNoneDicom': [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -28,10 +28,12 @@ class FixedRadiusCircleROITool extends cornerstoneTools.CircleROITool {
|
|||
preventHandleOutsideImage: false,
|
||||
storePointData: false,
|
||||
centerPointRadius: 0,
|
||||
radius: 10, // Default radius in mm
|
||||
calculateStats: true,
|
||||
radius: 6, // Default radius in mm
|
||||
radiusUnit: 'mm',
|
||||
statsCalculator: BasicStatsCalculator,
|
||||
getTextLines: defaultGetTextLines,
|
||||
simplified: true,
|
||||
},
|
||||
}
|
||||
) {
|
||||
|
|
@ -67,9 +69,12 @@ class FixedRadiusCircleROITool extends cornerstoneTools.CircleROITool {
|
|||
);
|
||||
|
||||
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
||||
const targetId = this.getTargetId(viewport);
|
||||
|
||||
// Calculate end point based on fixed radius
|
||||
const radius = this.configuration.radius || 10;
|
||||
const radius = Number.isFinite(this.configuration.radius)
|
||||
? this.configuration.radius
|
||||
: 10;
|
||||
// viewUp is a normalized vector.
|
||||
// We want a point 'radius' distance away from center.
|
||||
// We can use viewUp or any vector in the plane.
|
||||
|
|
@ -106,7 +111,20 @@ class FixedRadiusCircleROITool extends cornerstoneTools.CircleROITool {
|
|||
points: [[...worldPos], [...endPos]],
|
||||
activeHandleIndex: null,
|
||||
},
|
||||
cachedStats: {},
|
||||
cachedStats: {
|
||||
[targetId]: {
|
||||
Modality: null,
|
||||
radius: null,
|
||||
radiusUnit: null,
|
||||
area: null,
|
||||
mean: null,
|
||||
stdDev: null,
|
||||
max: null,
|
||||
isEmptyArea: null,
|
||||
areaUnit: null,
|
||||
modalityUnit: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -180,9 +198,11 @@ class FixedRadiusCircleROITool extends cornerstoneTools.CircleROITool {
|
|||
};
|
||||
}
|
||||
|
||||
export default FixedRadiusCircleROITool;
|
||||
function defaultGetTextLines(data, targetId) {
|
||||
const cachedVolumeStats = data.cachedStats[targetId];
|
||||
const cachedVolumeStats = data?.cachedStats?.[targetId];
|
||||
if (!cachedVolumeStats) {
|
||||
return [];
|
||||
}
|
||||
const {
|
||||
radius,
|
||||
radiusUnit,
|
||||
|
|
@ -195,32 +215,33 @@ function defaultGetTextLines(data, targetId) {
|
|||
modalityUnit,
|
||||
} = cachedVolumeStats;
|
||||
const textLines = [];
|
||||
|
||||
if (radius) {
|
||||
if (csUtils.isNumber(radius)) {
|
||||
const radiusLine = isEmptyArea
|
||||
? `Radius: Oblique not supported`
|
||||
: `Radius: ${csUtils.roundNumber(radius)} ${radiusUnit}`;
|
||||
textLines.push(radiusLine);
|
||||
}
|
||||
|
||||
if (area) {
|
||||
if (csUtils.isNumber(area)) {
|
||||
const areaLine = isEmptyArea
|
||||
? `Area: Oblique not supported`
|
||||
: `Area: ${csUtils.roundNumber(area)} ${areaUnit}`;
|
||||
textLines.push(areaLine);
|
||||
}
|
||||
|
||||
if (mean) {
|
||||
if (csUtils.isNumber(mean)) {
|
||||
textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`);
|
||||
}
|
||||
|
||||
if (max) {
|
||||
if (csUtils.isNumber(max)) {
|
||||
textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
|
||||
}
|
||||
|
||||
if (stdDev) {
|
||||
if (csUtils.isNumber(stdDev)) {
|
||||
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
|
||||
}
|
||||
|
||||
return textLines;
|
||||
}
|
||||
|
||||
export default FixedRadiusCircleROITool;
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ export default {
|
|||
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
|
||||
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2 ) {
|
||||
if (readingTool === 0 || readingTool === 2|| readingTool === 3 ) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
|
|
|
|||
|
|
@ -358,7 +358,7 @@ export default {
|
|||
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
|
||||
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2 ) {
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ export default {
|
|||
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
|
||||
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2) {
|
||||
if (readingTool === 0 || readingTool === 2|| readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ export default {
|
|||
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
|
||||
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2) {
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
|
|
|
|||
|
|
@ -1,31 +1,14 @@
|
|||
<template>
|
||||
<el-form v-if="globalForm.taskList.length > 0" ref="globalRuleForm" :model="globalForm" class="global-form">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="globalForm.taskList"
|
||||
>
|
||||
<el-table v-loading="loading" :data="globalForm.taskList">
|
||||
<!-- 访视名称 -->
|
||||
<el-table-column
|
||||
prop="BlindName"
|
||||
:label="$t('trials:globalReview:table:visitName')"
|
||||
show-overflow-tooltip
|
||||
width="150"
|
||||
/>
|
||||
<el-table-column prop="BlindName" :label="$t('trials:globalReview:table:visitName')" show-overflow-tooltip
|
||||
width="150" />
|
||||
<!-- 评估结果 -->
|
||||
<el-table-column
|
||||
:label="$t('trials:globalReview:table:evaluationRes')"
|
||||
align="center"
|
||||
prop=""
|
||||
>
|
||||
<el-table-column :label="$t('trials:globalReview:table:evaluationRes')" align="center" prop="">
|
||||
<template>
|
||||
<el-table-column
|
||||
v-for="(qs,index) in globalInfo.evaluationQsList"
|
||||
:key="index"
|
||||
prop=""
|
||||
:label="qs"
|
||||
show-overflow-tooltip
|
||||
width="150"
|
||||
>
|
||||
<el-table-column v-for="(qs, index) in globalInfo.evaluationQsList" :key="index" prop="" :label="qs"
|
||||
show-overflow-tooltip width="150">
|
||||
<!-- <template slot-scope="scope">
|
||||
<div v-if="scope.row.BeforeQuestionList.length>index && scope.row.BeforeQuestionList[index].Answer" :style="{color: scope.row.BeforeQuestionList[index].IsGlobalAnswer ? '#f66' : null}">
|
||||
<span v-if="scope.row.BeforeQuestionList[index].DictionaryCode">
|
||||
|
|
@ -35,10 +18,14 @@
|
|||
</div>
|
||||
</template> -->
|
||||
<template slot-scope="scope">
|
||||
<template v-if="(scope.row.IsBaseLine && (scope.row.BeforeQuestionList[index].GlobalReadingShowType ===1 || scope.row.BeforeQuestionList[index].GlobalReadingShowType ===5)) || (!scope.row.IsBaseLine && (scope.row.BeforeQuestionList[index].GlobalReadingShowType ===2 || scope.row.BeforeQuestionList[index].GlobalReadingShowType ===6)) || (scope.row.BeforeQuestionList[index].GlobalReadingShowType ===0 || scope.row.BeforeQuestionList[index].GlobalReadingShowType ===4)">
|
||||
<div v-if="scope.row.BeforeQuestionList.length>index && scope.row.BeforeQuestionList[index].Answer" :style="{color: scope.row.BeforeQuestionList[index].IsGlobalAnswer ? '#f66' : null}">
|
||||
<template
|
||||
v-if="(scope.row.IsBaseLine && (scope.row.BeforeQuestionList[index].GlobalReadingShowType === 1 || scope.row.BeforeQuestionList[index].GlobalReadingShowType === 5)) || (!scope.row.IsBaseLine && (scope.row.BeforeQuestionList[index].GlobalReadingShowType === 2 || scope.row.BeforeQuestionList[index].GlobalReadingShowType === 6)) || (scope.row.BeforeQuestionList[index].GlobalReadingShowType === 0 || scope.row.BeforeQuestionList[index].GlobalReadingShowType === 4)">
|
||||
<div v-if="scope.row.BeforeQuestionList.length > index && scope.row.BeforeQuestionList[index].Answer"
|
||||
:style="{ color: scope.row.BeforeQuestionList[index].IsGlobalAnswer ? '#f66' : null }">
|
||||
<span v-if="scope.row.BeforeQuestionList[index].DictionaryCode">
|
||||
{{ $fd(scope.row.BeforeQuestionList[index].DictionaryCode,parseInt(scope.row.BeforeQuestionList[index].Answer)) }}
|
||||
{{
|
||||
$fd(scope.row.BeforeQuestionList[index].DictionaryCode, parseInt(scope.row.BeforeQuestionList[index].Answer))
|
||||
}}
|
||||
</span>
|
||||
<span v-else>{{ scope.row.BeforeQuestionList[index].Answer }}</span>
|
||||
</div>
|
||||
|
|
@ -49,211 +36,145 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
<!-- 是否同意访视结果 -->
|
||||
<el-table-column
|
||||
prop=""
|
||||
:label="$t('trials:globalReview:table:isAgreeEvaluationRes')"
|
||||
show-overflow-tooltip
|
||||
width="170"
|
||||
>
|
||||
<el-table-column prop="" :label="$t('trials:globalReview:table:isAgreeEvaluationRes')" show-overflow-tooltip
|
||||
width="170">
|
||||
<template slot-scope="scope">
|
||||
<el-form-item
|
||||
v-if="readingTaskState<2"
|
||||
:prop="`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`"
|
||||
label=""
|
||||
:rules="[
|
||||
<el-form-item v-if="readingTaskState < 2" :prop="`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`"
|
||||
label="" :rules="[
|
||||
{ required: true, message: $t('common:ruleMessage:select'), trigger: ['change', 'blur'] },
|
||||
]"
|
||||
class="form-item"
|
||||
>
|
||||
<el-radio-group
|
||||
v-model="globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]"
|
||||
@change="handleAgreeOrNotChange(scope.$index,scope.row.AgreeOrNot[0].GlobalAnswerType)"
|
||||
>
|
||||
<el-radio
|
||||
v-for="item of $d.ReadingYesOrNo"
|
||||
:key="'AgreeOrNot' + item.value"
|
||||
:label="String(item.value)"
|
||||
>
|
||||
]" class="form-item">
|
||||
<el-radio-group v-model="globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]"
|
||||
@change="handleAgreeOrNotChange(scope.$index, scope.row.AgreeOrNot[0].GlobalAnswerType)">
|
||||
<el-radio v-for="item of $d.ReadingYesOrNo" :key="'AgreeOrNot' + item.value" :label="String(item.value)">
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-tag v-else-if="scope.row.AgreeOrNot.length > 0 && parseInt(scope.row.AgreeOrNot[0].Answer) === 1" type="primary">
|
||||
<el-tag v-else-if="scope.row.AgreeOrNot.length > 0 && parseInt(scope.row.AgreeOrNot[0].Answer) === 1"
|
||||
type="primary">
|
||||
{{ $fd('ReadingYesOrNo', parseInt(scope.row.AgreeOrNot[0].Answer)) }}
|
||||
</el-tag>
|
||||
<el-tag v-else-if="scope.row.AgreeOrNot.length > 0 && parseInt(scope.row.AgreeOrNot[0].Answer) === 0" type="danger"> {{ $fd('ReadingYesOrNo',parseInt(scope.row.AgreeOrNot[0].Answer)) }}</el-tag>
|
||||
<el-tag v-else-if="scope.row.AgreeOrNot.length > 0 && parseInt(scope.row.AgreeOrNot[0].Answer) === 0"
|
||||
type="danger"> {{ $fd('ReadingYesOrNo', parseInt(scope.row.AgreeOrNot[0].Answer)) }}</el-tag>
|
||||
<span v-else />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 调整后结果 -->
|
||||
<el-table-column
|
||||
:label="$t('trials:globalReview:table:adjustedRes')"
|
||||
align="center"
|
||||
prop=""
|
||||
>
|
||||
<el-table-column :label="$t('trials:globalReview:table:adjustedRes')" align="center" prop="">
|
||||
<template v-for="(qs, index) in globalInfo.adjustedQsList">
|
||||
<el-table-column
|
||||
v-if="qs.isShow"
|
||||
:key="index"
|
||||
prop=""
|
||||
:label="qs.questionName"
|
||||
show-overflow-tooltip
|
||||
:min-width="index === 3 ? '200' : '200'"
|
||||
>
|
||||
<el-table-column v-if="qs.isShow" :key="index" prop="" :label="qs.questionName" show-overflow-tooltip
|
||||
:min-width="index === 3 ? '200' : '200'">
|
||||
<template slot-scope="scope">
|
||||
<div v-if="readingTaskState<2 && (scope.row.AfterQuestionList[index].GlobalReadingShowType === 0 || (scope.row.IsBaseLine && scope.row.AfterQuestionList[index].GlobalReadingShowType === 1) || (!scope.row.IsBaseLine && scope.row.AfterQuestionList[index].GlobalReadingShowType === 2))">
|
||||
<div
|
||||
v-if="readingTaskState < 2 && (scope.row.AfterQuestionList[index].GlobalReadingShowType === 0 || (scope.row.IsBaseLine && scope.row.AfterQuestionList[index].GlobalReadingShowType === 1) || (!scope.row.IsBaseLine && scope.row.AfterQuestionList[index].GlobalReadingShowType === 2))">
|
||||
<!-- <span v-if="(scope.row.IsBaseLine && scope.row.AfterQuestionList[index].LimitEdit === 2) || (!scope.row.IsBaseLine && scope.row.AfterQuestionList[index].LimitEdit === 1)">
|
||||
{{ $fd(scope.row.AfterQuestionList[index].DictionaryCode, parseInt(scope.row.AfterQuestionList[index].VisitAnswer)) }}
|
||||
</span> -->
|
||||
<el-form-item
|
||||
style="margin-bottom: 0;"
|
||||
<el-form-item style="margin-bottom: 0;"
|
||||
:prop="`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId ? scope.row.AfterQuestionList[index].QuestionId : scope.row.AfterQuestionList[index].GlobalAnswerType}`"
|
||||
label=""
|
||||
:rules="[
|
||||
label="" :rules="[
|
||||
{ required: parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) === 0, message: $t('common:ruleMessage:specify'), trigger: ['change', 'blur'] },
|
||||
]"
|
||||
>
|
||||
<label v-if="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) === 0" />
|
||||
]">
|
||||
<label
|
||||
v-if="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) === 0" />
|
||||
<!-- 裁判问题 -->
|
||||
<template v-if="scope.row.AfterQuestionList[index].GlobalAnswerType === 0">
|
||||
<el-tooltip v-if="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId?scope.row.AfterQuestionList[index].QuestionId:String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`], scope.row.AfterQuestionList[index])" class="item" effect="dark" :content="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId?scope.row.AfterQuestionList[index].QuestionId:String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`], scope.row.AfterQuestionList[index])" placement="top-start">
|
||||
<el-tooltip
|
||||
v-if="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId ? scope.row.AfterQuestionList[index].QuestionId : String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`], scope.row.AfterQuestionList[index])"
|
||||
class="item" effect="dark"
|
||||
:content="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId ? scope.row.AfterQuestionList[index].QuestionId : String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`], scope.row.AfterQuestionList[index])"
|
||||
placement="top-start">
|
||||
<el-select
|
||||
v-model="globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId ? scope.row.AfterQuestionList[index].QuestionId : String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`]"
|
||||
style="width:90%;"
|
||||
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0"
|
||||
>
|
||||
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0">
|
||||
<template v-if="scope.row.AfterQuestionList[index].TypeValue">
|
||||
<el-option
|
||||
v-for="val in scope.row.AfterQuestionList[index].TypeValue.split('|')"
|
||||
:key="val"
|
||||
:label="val"
|
||||
:value="val"
|
||||
/>
|
||||
<el-option v-for="val in scope.row.AfterQuestionList[index].TypeValue.split('|')" :key="val"
|
||||
:label="val" :value="val" />
|
||||
</template>
|
||||
<template v-else-if="scope.row.AfterQuestionList[index].DictionaryCode && scope.row.AfterQuestionList[index].QuestionType === 13">
|
||||
<el-option
|
||||
v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
|
||||
v-show="item.value!==-1"
|
||||
:key="item.id"
|
||||
:value="String(item.value)"
|
||||
:label="item.label"
|
||||
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName,scope.row)) === String(item.value)"
|
||||
/>
|
||||
<template
|
||||
v-else-if="scope.row.AfterQuestionList[index].DictionaryCode && scope.row.AfterQuestionList[index].QuestionType === 13">
|
||||
<el-option v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
|
||||
v-show="item.value !== -1" :key="item.id" :value="String(item.value)" :label="item.label"
|
||||
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName, scope.row)) === String(item.value)" />
|
||||
</template>
|
||||
<template v-else-if="scope.row.AfterQuestionList[index].DictionaryCode">
|
||||
<el-option
|
||||
v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
|
||||
:key="item.id"
|
||||
:value="String(item.value)"
|
||||
<el-option v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
|
||||
:key="item.id" :value="String(item.value)"
|
||||
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName, scope.row)) === String(item.value)"
|
||||
:label="item.label"
|
||||
/>
|
||||
:label="item.label" />
|
||||
</template>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
<el-select
|
||||
v-else
|
||||
<el-select v-else
|
||||
v-model="globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId ? scope.row.AfterQuestionList[index].QuestionId : String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`]"
|
||||
style="width:90%;"
|
||||
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0"
|
||||
>
|
||||
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0">
|
||||
<template v-if="scope.row.AfterQuestionList[index].TypeValue">
|
||||
<el-option
|
||||
v-for="val in scope.row.AfterQuestionList[index].TypeValue.split('|')"
|
||||
:key="val"
|
||||
:label="val"
|
||||
:value="val"
|
||||
/>
|
||||
<el-option v-for="val in scope.row.AfterQuestionList[index].TypeValue.split('|')" :key="val"
|
||||
:label="val" :value="val" />
|
||||
</template>
|
||||
<template v-else-if="scope.row.AfterQuestionList[index].DictionaryCode && scope.row.AfterQuestionList[index].QuestionType === 13">
|
||||
<template
|
||||
v-else-if="scope.row.AfterQuestionList[index].DictionaryCode && scope.row.AfterQuestionList[index].QuestionType === 13">
|
||||
<template v-if="getLesionCount(scope.row.LesionCountList, 0)">
|
||||
<el-option
|
||||
v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
|
||||
v-show="item.value!==-1 && item.value !== 1 && item.value !== 3"
|
||||
:key="item.id"
|
||||
:value="String(item.value)"
|
||||
:label="item.label"
|
||||
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName,scope.row)) === String(item.value)"
|
||||
/>
|
||||
<el-option v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
|
||||
v-show="item.value !== -1 && item.value !== 1 && item.value !== 3" :key="item.id"
|
||||
:value="String(item.value)" :label="item.label"
|
||||
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName, scope.row)) === String(item.value)" />
|
||||
</template>
|
||||
<template v-else-if="getLesionCount(scope.row.LesionCountList, 1)">
|
||||
<el-option
|
||||
v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
|
||||
v-show="item.value!==-1 && item.value !== 1 && item.value !== 6"
|
||||
:key="item.id"
|
||||
:value="String(item.value)"
|
||||
:label="item.label"
|
||||
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName,scope.row)) === String(item.value)"
|
||||
/>
|
||||
<el-option v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
|
||||
v-show="item.value !== -1 && item.value !== 1 && item.value !== 6" :key="item.id"
|
||||
:value="String(item.value)" :label="item.label"
|
||||
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName, scope.row)) === String(item.value)" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-option
|
||||
v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
|
||||
v-show="item.value === 1 || item.value === 2 || item.value === 4"
|
||||
:key="item.id"
|
||||
:value="String(item.value)"
|
||||
:label="item.label"
|
||||
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName,scope.row)) === String(item.value)"
|
||||
/>
|
||||
<el-option v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
|
||||
v-show="item.value === 1 || item.value === 2 || item.value === 4" :key="item.id"
|
||||
:value="String(item.value)" :label="item.label"
|
||||
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName, scope.row)) === String(item.value)" />
|
||||
</template>
|
||||
|
||||
</template>
|
||||
<template v-else-if="scope.row.AfterQuestionList[index].DictionaryCode">
|
||||
<el-option
|
||||
v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
|
||||
v-show="item.value !== 6"
|
||||
:key="item.id"
|
||||
:value="String(item.value)"
|
||||
<el-option v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
|
||||
v-show="item.value !== 6" :key="item.id" :value="String(item.value)"
|
||||
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName, scope.row)) === String(item.value)"
|
||||
:label="item.label"
|
||||
/>
|
||||
:label="item.label" />
|
||||
</template>
|
||||
</el-select>
|
||||
</template>
|
||||
<!-- 评估更新类型 GlobalAnswerType:3 -->
|
||||
<template v-if="scope.row.AfterQuestionList[index].GlobalAnswerType === 3">
|
||||
<el-tooltip v-if="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].GlobalAnswerType}`], scope.row.AfterQuestionList[index], scope.row.AfterQuestionList[index].GlobalAnswerType)" class="item" effect="dark" :content="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].GlobalAnswerType}`], scope.row.AfterQuestionList[index], scope.row.AfterQuestionList[index].GlobalAnswerType)" placement="top-start">
|
||||
<el-tooltip
|
||||
v-if="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].GlobalAnswerType}`], scope.row.AfterQuestionList[index], scope.row.AfterQuestionList[index].GlobalAnswerType)"
|
||||
class="item" effect="dark"
|
||||
:content="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].GlobalAnswerType}`], scope.row.AfterQuestionList[index], scope.row.AfterQuestionList[index].GlobalAnswerType)"
|
||||
placement="top-start">
|
||||
<el-select
|
||||
v-model="globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].GlobalAnswerType}`]"
|
||||
style="width:90%;"
|
||||
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0"
|
||||
>
|
||||
<el-option
|
||||
v-for="val in globalInfo.assessTypeList"
|
||||
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0">
|
||||
<el-option v-for="val in globalInfo.assessTypeList"
|
||||
v-show="(scope.row.IsBaseLine && val.IsBaseLineUse) || (!scope.row.IsBaseLine && val.IsFollowVisitUse)"
|
||||
:key="val.Code"
|
||||
:label="language === 'en'?val.Value:val.ValueCN"
|
||||
:value="val.Code"
|
||||
/>
|
||||
:key="val.Code" :label="language === 'en' ? val.Value : val.ValueCN" :value="val.Code" />
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
<el-select
|
||||
v-else
|
||||
<el-select v-else
|
||||
v-model="globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].GlobalAnswerType}`]"
|
||||
style="width:90%;"
|
||||
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0"
|
||||
>
|
||||
<el-option
|
||||
v-for="val in globalInfo.assessTypeList"
|
||||
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0">
|
||||
<el-option v-for="val in globalInfo.assessTypeList"
|
||||
v-show="(scope.row.IsBaseLine && val.IsBaseLineUse) || (!scope.row.IsBaseLine && val.IsFollowVisitUse)"
|
||||
:key="val.Code"
|
||||
:label="language === 'en'?val.Value:val.ValueCN"
|
||||
:value="val.Code"
|
||||
/>
|
||||
:key="val.Code" :label="language === 'en' ? val.Value : val.ValueCN" :value="val.Code" />
|
||||
</el-select>
|
||||
</template>
|
||||
<el-input
|
||||
v-if="scope.row.AfterQuestionList[index].GlobalAnswerType === 1"
|
||||
<el-input v-if="scope.row.AfterQuestionList[index].GlobalAnswerType === 1"
|
||||
v-model="globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId ? scope.row.AfterQuestionList[index].QuestionId : String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`]"
|
||||
type="textarea"
|
||||
maxlength="100"
|
||||
show-word-limit
|
||||
style="width:90%;"
|
||||
:autosize="{ minRows: 2 }"
|
||||
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0"
|
||||
/>
|
||||
type="textarea" maxlength="100" show-word-limit style="width:90%;" :autosize="{ minRows: 2 }"
|
||||
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-else>
|
||||
|
|
@ -262,7 +183,9 @@
|
|||
{{ getAssessType(scope.row.AfterQuestionList[index].Answer) }}
|
||||
</span>
|
||||
<span v-else-if="scope.row.AfterQuestionList[index].DictionaryCode">
|
||||
{{ $fd(scope.row.AfterQuestionList[index].DictionaryCode,parseInt(scope.row.AfterQuestionList[index].Answer)) }}
|
||||
{{
|
||||
$fd(scope.row.AfterQuestionList[index].DictionaryCode, parseInt(scope.row.AfterQuestionList[index].Answer))
|
||||
}}
|
||||
</span>
|
||||
<span v-else>{{ scope.row.AfterQuestionList[index].Answer }}</span>
|
||||
</div>
|
||||
|
|
@ -270,17 +193,10 @@
|
|||
</el-table-column>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('common:action:action')"
|
||||
width="100"
|
||||
>
|
||||
<el-table-column :label="$t('common:action:action')" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
circle
|
||||
:title="$t('trials:globalReview:table:view')"
|
||||
icon="el-icon-view"
|
||||
@click="handleView(scope.row)"
|
||||
/>
|
||||
<el-button circle :title="$t('trials:globalReview:table:view')" icon="el-icon-view"
|
||||
@click="handleView(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
|
@ -348,7 +264,7 @@ export default {
|
|||
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
|
||||
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2 ) {
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
|
|
@ -383,8 +299,10 @@ export default {
|
|||
}
|
||||
answerList.push(obj)
|
||||
})
|
||||
answerList.push({ questionId: '', globalAnswerType: item.AgreeOrNot[0].GlobalAnswerType,
|
||||
answer: this.globalForm[ `${index}${item.AgreeOrNot[0].GlobalAnswerType}`] })
|
||||
answerList.push({
|
||||
questionId: '', globalAnswerType: item.AgreeOrNot[0].GlobalAnswerType,
|
||||
answer: this.globalForm[`${index}${item.AgreeOrNot[0].GlobalAnswerType}`]
|
||||
})
|
||||
|
||||
visitTaskAnswerList.push({ visitTaskId: item.VisitTaskId, answerList: answerList })
|
||||
})
|
||||
|
|
@ -462,17 +380,21 @@ export default {
|
|||
color: #F56C6C;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
::v-deep .el-textarea .el-input__count {
|
||||
background: rgba(0, 0, 0, 0);
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
::v-deep .el-form-item {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.global-form {
|
||||
::v-deep .el-form-item__content {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
::v-deep .form-item .el-form-item__error {
|
||||
top: 60%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ export default {
|
|||
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
|
||||
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2 ) {
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ export default {
|
|||
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
|
||||
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2 ) {
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
|
|
|
|||
|
|
@ -404,7 +404,7 @@ export default {
|
|||
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool)
|
||||
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2) {
|
||||
if (readingTool === 0 || readingTool === 2|| readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ export default {
|
|||
var trialReadingCriterionId = this.rowData.TrialReadingCriterionId;
|
||||
|
||||
var path = "";
|
||||
if (readingTool === 0 || readingTool === 2) {
|
||||
if (readingTool === 0 || readingTool === 2|| readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${trialId}&subjectCode=${this.rowData.SubjectCode
|
||||
}&subjectId=${this.rowData.SubjectId
|
||||
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`;
|
||||
|
|
@ -273,7 +273,7 @@ export default {
|
|||
var trialReadingCriterionId = this.rowData.TrialReadingCriterionId;
|
||||
|
||||
var path = "";
|
||||
if (readingTool === 0 || readingTool === 2) {
|
||||
if (readingTool === 0 || readingTool === 2|| readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${trialId}&subjectCode=${this.rowData.SubjectCode
|
||||
}&subjectId=${this.rowData.SubjectId}&visitTaskId=${task.VisitTaskId
|
||||
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`;
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ export default {
|
|||
var isReadingTaskViewInOrder = this.rowData.IsReadingTaskViewInOrder
|
||||
var trialReadingCriterionId = this.rowData.TrialReadingCriterionId
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2) {
|
||||
if (readingTool === 0 || readingTool === 2|| readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${trialId}&subjectCode=${this.rowData.SubjectCode}&subjectId=${this.rowData.SubjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${trialId}&subjectCode=${this.rowData.SubjectCode}&subjectId=${this.rowData.SubjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ export default {
|
|||
var trialReadingCriterionId = this.rowData.TrialReadingCriterionId
|
||||
var path = ''
|
||||
|
||||
if (readingTool === 0 || readingTool === 2) {
|
||||
if (readingTool === 0 || readingTool === 2|| readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${trialId}&subjectCode=${this.rowData.SubjectCode}&subjectId=${this.rowData.SubjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${trialId}&subjectCode=${this.rowData.SubjectCode}&subjectId=${this.rowData.SubjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ export default {
|
|||
var trialReadingCriterionId = this.rowData.TrialReadingCriterionId
|
||||
var path = ''
|
||||
|
||||
if (readingTool === 0 || readingTool === 2) {
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${trialId}&subjectCode=${this.rowData.SubjectCode}&subjectId=${this.rowData.SubjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${trialId}&subjectCode=${this.rowData.SubjectCode}&subjectId=${this.rowData.SubjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
|
|
|
|||
|
|
@ -795,7 +795,7 @@ export default {
|
|||
var trialReadingCriterionId = row.TrialReadingCriterionId
|
||||
var path = ''
|
||||
|
||||
if (readingTool === 0 || readingTool === 2 ) {
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
|
|
|
|||
|
|
@ -452,7 +452,7 @@ export default {
|
|||
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
|
||||
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2) {
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
|
||||
|
|
|
|||
|
|
@ -953,7 +953,7 @@ export default {
|
|||
lookReadingResults(row) {
|
||||
var token = getToken()
|
||||
var path
|
||||
if (row.ReadingTool === 0 || row.ReadingTool === 2) {
|
||||
if (row.ReadingTool === 0 || row.ReadingTool === 2|| row.ReadingTool === 3) {
|
||||
path = `/readingDicoms?trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&visitTaskId=${row.Id}&isReadingTaskViewInOrder=${row.IsReadingTaskViewInOrder}&criterionType=${row.CriterionType}&readingTool=${row.ReadingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&visitTaskId=${row.Id}&isReadingTaskViewInOrder=${row.IsReadingTaskViewInOrder}&criterionType=${row.CriterionType}&readingTool=${row.ReadingTool}&TokenKey=${token}`
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ export default {
|
|||
window.localStorage.setItem('TrialReadingCriterionId', this.TrialReadingCriterionId)
|
||||
var token = getToken()
|
||||
var path = ''
|
||||
if (row.ReadingTool === 0 || row.ReadingTool === 2) {
|
||||
if (row.ReadingTool === 0 || row.ReadingTool === 2 || row.ReadingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${row.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&visitTaskId=${row.Id}&isReadingTaskViewInOrder=${row.IsReadingTaskViewInOrder}&criterionType=${row.CriterionType}&readingTool=${row.ReadingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${row.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&visitTaskId=${row.Id}&isReadingTaskViewInOrder=${row.IsReadingTaskViewInOrder}&criterionType=${row.CriterionType}&readingTool=${row.ReadingTool}&TokenKey=${token}`
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export default {
|
|||
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
|
||||
var criterionType = this.$router.currentRoute.query.criterionType
|
||||
var path = ''
|
||||
if (this.readingTool === 0 || this.readingTool === 2 ) {
|
||||
if (this.readingTool === 0 || this.readingTool === 2 || this.readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${TrialReadingCriterionId}&trialId=${trialId}&subjectCode=${subjectCode}&subjectId=${subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${this.readingTool}&TokenKey=${this.token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${TrialReadingCriterionId}&trialId=${trialId}&subjectCode=${subjectCode}&subjectId=${subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${this.readingTool}&TokenKey=${this.token}`
|
||||
|
|
|
|||
|
|
@ -368,7 +368,7 @@ export default {
|
|||
)
|
||||
var token = getToken()
|
||||
var path = ''
|
||||
if (this.readingTool === 0 || this.readingTool === 2) {
|
||||
if (this.readingTool === 0 || this.readingTool === 2|| this.readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${this.TrialReadingCriterionId}&trialId=${this.trialId}&isReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}&criterionType=${this.criterionType}&readingTool=${this.readingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${this.TrialReadingCriterionId}&trialId=${this.trialId}&isReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}&criterionType=${this.criterionType}&readingTool=${this.readingTool}&TokenKey=${token}`
|
||||
|
|
|
|||
|
|
@ -614,7 +614,7 @@ export default {
|
|||
}
|
||||
var token = getToken()
|
||||
var path
|
||||
if (row.ReadingTool === 0 || row.ReadingTool === 2) {
|
||||
if (row.ReadingTool === 0 || row.ReadingTool === 2|| row.ReadingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${row.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&visitTaskId=${row.Id}&isReadingTaskViewInOrder=${row.IsReadingTaskViewInOrder}&criterionType=${row.CriterionType}&readingTool=${row.ReadingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${row.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&visitTaskId=${row.Id}&isReadingTaskViewInOrder=${row.IsReadingTaskViewInOrder}&criterionType=${row.CriterionType}&readingTool=${row.ReadingTool}&TokenKey=${token}`
|
||||
|
|
|
|||
|
|
@ -1202,7 +1202,7 @@ export default {
|
|||
}
|
||||
var token = getToken()
|
||||
var path
|
||||
if (row.ReadingTool === 0 || row.ReadingTool === 2) {
|
||||
if (row.ReadingTool === 0 || row.ReadingTool === 2 || row.ReadingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${row.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&visitTaskId=${row.Id}&isReadingTaskViewInOrder=${row.IsReadingTaskViewInOrder}&criterionType=${row.CriterionType}&readingTool=${row.ReadingTool}&TokenKey=${token}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${row.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&visitTaskId=${row.Id}&isReadingTaskViewInOrder=${row.IsReadingTaskViewInOrder}&criterionType=${row.CriterionType}&readingTool=${row.ReadingTool}&TokenKey=${token}`
|
||||
|
|
|
|||
|
|
@ -4,123 +4,63 @@
|
|||
<template>
|
||||
<el-form v-loading="loading">
|
||||
<!-- 仲裁对象 -->
|
||||
<el-form-item
|
||||
:label="$t('trials:adRules:title:arbitrationRule')"
|
||||
:label-width="'180px'"
|
||||
>
|
||||
<el-radio-group
|
||||
v-model="JudgyInfo.ArbitrationRule"
|
||||
:disabled="OtherInfo.IsSign"
|
||||
>
|
||||
<el-radio
|
||||
v-for="item of $d.ArbitrationRule"
|
||||
:key="item.id"
|
||||
:disabled="!JudgyInfo.IsReadingTaskViewInOrder || true"
|
||||
:label="item.value"
|
||||
@change="changeArbitrationRule"
|
||||
>
|
||||
<el-form-item :label="$t('trials:adRules:title:arbitrationRule')" :label-width="'180px'">
|
||||
<el-radio-group v-model="JudgyInfo.ArbitrationRule" :disabled="OtherInfo.IsSign">
|
||||
<el-radio v-for="item of $d.ArbitrationRule" :key="item.id"
|
||||
:disabled="!JudgyInfo.IsReadingTaskViewInOrder || true" :label="item.value" @change="changeArbitrationRule">
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-collapse v-model="activeNames" style="border-top:none;">
|
||||
<el-collapse-item
|
||||
v-for="(item, index) of QuestionList"
|
||||
:key="item.ReadingQuestionTrialId"
|
||||
style="position: relative;padding:0 10px;"
|
||||
:name="item.ReadingQuestionTrialId"
|
||||
>
|
||||
<el-collapse-item v-for="(item, index) of QuestionList" :key="item.ReadingQuestionTrialId"
|
||||
style="position: relative;padding:0 10px;" :name="item.ReadingQuestionTrialId">
|
||||
<div slot="title">
|
||||
{{ item.PageName }}Q{{ index + 1 }}:{{ item.QuestionName }}
|
||||
</div>
|
||||
<div style="position: relative">
|
||||
<el-button
|
||||
v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
|
||||
style="position: absolute; right: 0; top: 0; z-index: 10"
|
||||
size="mini"
|
||||
type="primary"
|
||||
:disabled="OtherInfo.IsSign"
|
||||
@click="apply(item.ReadingQuestionTrialId, index)"
|
||||
>{{ $t('common:button:save') }}</el-button
|
||||
>
|
||||
<el-button
|
||||
v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
|
||||
style="position: absolute; right: 60px; top: 0; z-index: 10"
|
||||
size="mini"
|
||||
type="primary"
|
||||
:disabled="OtherInfo.IsSign"
|
||||
@click="reset(item.ReadingQuestionTrialId, index)"
|
||||
>{{ $t('common:button:reset') }}</el-button
|
||||
>
|
||||
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
|
||||
style="position: absolute; right: 0; top: 0; z-index: 10" size="mini" type="primary"
|
||||
:disabled="OtherInfo.IsSign" @click="apply(item.ReadingQuestionTrialId, index)">{{ $t('common:button:save')
|
||||
}}</el-button>
|
||||
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
|
||||
style="position: absolute; right: 60px; top: 0; z-index: 10" size="mini" type="primary"
|
||||
:disabled="OtherInfo.IsSign" @click="reset(item.ReadingQuestionTrialId, index)">{{ $t('common:button:reset')
|
||||
}}</el-button>
|
||||
<!-- 产生裁判阅片的条件 -->
|
||||
<el-form-item
|
||||
:label-width="'280px'"
|
||||
:label="$t('trials:adRules:title:judgeTypeEnum')"
|
||||
style="width: 95%"
|
||||
>
|
||||
<el-radio-group
|
||||
v-model="QuestionList[index].JudgeType"
|
||||
@input="(val) => JudgeTypeChange(val, index)"
|
||||
>
|
||||
<el-form-item :label-width="'280px'" :label="$t('trials:adRules:title:judgeTypeEnum')" style="width: 95%">
|
||||
<el-radio-group v-model="QuestionList[index].JudgeType" @input="(val) => JudgeTypeChange(val, index)">
|
||||
<template v-for="item of $d.JudgeTypeEnum">
|
||||
<el-radio
|
||||
style="margin-bottom: 5px"
|
||||
:key="item.id"
|
||||
:disabled="
|
||||
OtherInfo.IsSign ||
|
||||
<el-radio style="margin-bottom: 5px" :key="item.id" :disabled="OtherInfo.IsSign ||
|
||||
!hasPermi(['trials:trials-panel:setting:reading-unit:edit'])
|
||||
"
|
||||
:label="item.value"
|
||||
v-if="
|
||||
" :label="item.value" v-if="
|
||||
item.value < 4 ||
|
||||
(item.value > 3 &&
|
||||
(QuestionList[index].Type === 'number' ||
|
||||
QuestionList[index].Type === 'calculation'))
|
||||
"
|
||||
>
|
||||
">
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div
|
||||
v-if="QuestionList[index].JudgeType === 3"
|
||||
style="text-align: right; margin: 10px 0"
|
||||
>
|
||||
<div v-if="QuestionList[index].JudgeType === 3" style="text-align: right; margin: 10px 0">
|
||||
<!-- 添加规则 -->
|
||||
<el-button
|
||||
v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
|
||||
size="mini"
|
||||
type="primary"
|
||||
:disabled="OtherInfo.IsSign"
|
||||
@click="addGroup(index, null)"
|
||||
>{{ $t('trials:adRules:button:addRule') }}</el-button
|
||||
>
|
||||
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']" size="mini" type="primary"
|
||||
:disabled="OtherInfo.IsSign" @click="addGroup(index, null)">{{ $t('trials:adRules:button:addRule')
|
||||
}}</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-if="QuestionList[index].JudgeType === 3"
|
||||
v-loading="loading"
|
||||
:data="QuestionList[index].AnswerGroupList"
|
||||
border
|
||||
stripe
|
||||
>
|
||||
<el-table v-if="QuestionList[index].JudgeType === 3" v-loading="loading"
|
||||
:data="QuestionList[index].AnswerGroupList" border stripe>
|
||||
<!-- 序号 -->
|
||||
<el-table-column
|
||||
prop="AnswerGroupA"
|
||||
:label="$t('trials:adRules:title:order')"
|
||||
width="160"
|
||||
>
|
||||
<el-table-column prop="AnswerGroupA" :label="$t('trials:adRules:title:order')" width="160">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.$index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 阅片人A -->
|
||||
<el-table-column
|
||||
prop="AnswerGroupA"
|
||||
:label="$t('trials:adRules:title:answerGroupA')"
|
||||
min-width="100"
|
||||
>
|
||||
<el-table-column prop="AnswerGroupA" :label="$t('trials:adRules:title:answerGroupA')" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
{{
|
||||
QuestionList[index].QuestionGenre === 3
|
||||
|
|
@ -132,11 +72,7 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
<!-- 阅片人B -->
|
||||
<el-table-column
|
||||
prop="AnswerGroupB"
|
||||
:label="$t('trials:adRules:title:answerGroupB')"
|
||||
min-width="100"
|
||||
>
|
||||
<el-table-column prop="AnswerGroupB" :label="$t('trials:adRules:title:answerGroupB')" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
{{
|
||||
QuestionList[index].QuestionGenre === 3
|
||||
|
|
@ -147,83 +83,48 @@
|
|||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="AnswerGroupB"
|
||||
:label="$t('common:action:action')"
|
||||
min-width="80"
|
||||
>
|
||||
<el-table-column prop="AnswerGroupB" :label="$t('common:action:action')" min-width="80">
|
||||
<template slot-scope="scope">
|
||||
<!-- 编辑 -->
|
||||
<el-button
|
||||
icon="el-icon-edit"
|
||||
circle
|
||||
:title="$t('common:button:edit')"
|
||||
size="mini"
|
||||
:disabled="OtherInfo.IsSign"
|
||||
@click="addGroup(index, scope.$index)"
|
||||
/>
|
||||
<el-button icon="el-icon-edit" circle :title="$t('common:button:edit')" size="mini"
|
||||
:disabled="OtherInfo.IsSign" @click="addGroup(index, scope.$index)" />
|
||||
<!-- 删除 -->
|
||||
<el-button
|
||||
icon="el-icon-delete"
|
||||
circle
|
||||
:title="$t('common:button:delete')"
|
||||
size="mini"
|
||||
:disabled="OtherInfo.IsSign"
|
||||
@click="tagClose(index, scope.$index)"
|
||||
/>
|
||||
<el-button icon="el-icon-delete" circle :title="$t('common:button:delete')" size="mini"
|
||||
:disabled="OtherInfo.IsSign" @click="tagClose(index, scope.$index)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="QuestionList[index].JudgeType === 2">
|
||||
<div
|
||||
style="
|
||||
<div style="
|
||||
background: #f6f6f6;
|
||||
border-radius: 20px;
|
||||
padding: 15px 20px;
|
||||
margin-top: 10px;
|
||||
"
|
||||
>
|
||||
<el-checkbox-group
|
||||
v-if="QuestionList[index].QuestionGenre === 3"
|
||||
v-model="QuestionList[index].grouping"
|
||||
>
|
||||
">
|
||||
<el-checkbox-group v-if="QuestionList[index].QuestionGenre === 3" v-model="QuestionList[index].grouping">
|
||||
<template v-for="item of $d[QuestionList[index].DictionaryCode]">
|
||||
<el-checkbox
|
||||
v-if="item.value !== -1"
|
||||
:key="item.id"
|
||||
:disabled="OtherInfo.IsSign"
|
||||
:label="item.value"
|
||||
>{{ item.label }}</el-checkbox
|
||||
>
|
||||
<el-checkbox v-if="item.value !== -1" :key="item.id" :disabled="OtherInfo.IsSign" :label="item.value">{{
|
||||
item.label }}</el-checkbox>
|
||||
</template>
|
||||
</el-checkbox-group>
|
||||
<el-checkbox-group v-else v-model="QuestionList[index].grouping">
|
||||
<el-checkbox
|
||||
v-for="item of item.TypeValue.split('|')"
|
||||
:key="item"
|
||||
:disabled="OtherInfo.IsSign"
|
||||
:label="item"
|
||||
>{{ item }}</el-checkbox
|
||||
>
|
||||
<el-checkbox v-for="item of item.TypeValue.split('|')" :key="item" :disabled="OtherInfo.IsSign"
|
||||
:label="item">{{
|
||||
item }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
<div style="margin-top: 20px">
|
||||
<!-- 当前选择答案分组: -->
|
||||
{{ $t('trials:adRules:title:selectAnswerGroup') }}
|
||||
<el-tag
|
||||
v-if="
|
||||
<el-tag v-if="
|
||||
QuestionList[index].grouping.length > 0 &&
|
||||
QuestionList[index].QuestionGenre !== 3
|
||||
"
|
||||
>{{
|
||||
">{{
|
||||
QuestionList[index].grouping.toString().replaceAll(',', '|')
|
||||
}}</el-tag
|
||||
>
|
||||
<el-tag
|
||||
v-if="
|
||||
}}</el-tag>
|
||||
<el-tag v-if="
|
||||
QuestionList[index].grouping.length > 0 &&
|
||||
QuestionList[index].QuestionGenre === 3
|
||||
"
|
||||
>{{
|
||||
">{{
|
||||
QuestionList[index].grouping
|
||||
.map(
|
||||
(v) =>
|
||||
|
|
@ -233,54 +134,31 @@
|
|||
)
|
||||
.toString()
|
||||
.replaceAll(',', '|')
|
||||
}}</el-tag
|
||||
>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="primary"
|
||||
:disabled="OtherInfo.IsSign"
|
||||
@click="addGroup2(index)"
|
||||
>
|
||||
}}</el-tag>
|
||||
<el-button size="mini" type="primary" :disabled="OtherInfo.IsSign" @click="addGroup2(index)">
|
||||
<!-- 添加分组 -->
|
||||
{{ $t('trials:adRules:title:addGroup') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="QuestionList[index].QuestionGenre !== 3"
|
||||
style="margin-top: 20px"
|
||||
>
|
||||
<div v-if="QuestionList[index].QuestionGenre !== 3" style="margin-top: 20px">
|
||||
{{ $t('trials:adRules:title:group') }}
|
||||
<el-tag
|
||||
v-for="itemA of QuestionList[index].AnswerGroup2List"
|
||||
:key="itemA"
|
||||
closable
|
||||
style="margin-right: 10px"
|
||||
@close="
|
||||
<el-tag v-for="itemA of QuestionList[index].AnswerGroup2List" :key="itemA" closable
|
||||
style="margin-right: 10px" @close="
|
||||
() => {
|
||||
return tagClose2(index, indexA)
|
||||
}
|
||||
"
|
||||
>{{ itemA }}</el-tag
|
||||
>
|
||||
">{{ itemA }}</el-tag>
|
||||
</div>
|
||||
<div
|
||||
v-if="QuestionList[index].QuestionGenre === 3"
|
||||
style="margin-top: 20px"
|
||||
>
|
||||
<div v-if="QuestionList[index].QuestionGenre === 3" style="margin-top: 20px">
|
||||
<!-- 分组: -->
|
||||
{{ $t('trials:adRules:title:group') }}
|
||||
<el-tag
|
||||
v-for="itemA of QuestionList[index].AnswerGroup2List"
|
||||
:key="itemA"
|
||||
closable
|
||||
style="margin-right: 10px"
|
||||
@close="
|
||||
<el-tag v-for="itemA of QuestionList[index].AnswerGroup2List" :key="itemA" closable
|
||||
style="margin-right: 10px" @close="
|
||||
() => {
|
||||
return tagClose2(index, indexA)
|
||||
}
|
||||
"
|
||||
>
|
||||
">
|
||||
{{
|
||||
itemA
|
||||
.split('|')
|
||||
|
|
@ -292,58 +170,31 @@
|
|||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
<div v-if="
|
||||
QuestionList[index].JudgeType === 4 ||
|
||||
QuestionList[index].JudgeType === 5
|
||||
"
|
||||
>
|
||||
<el-form
|
||||
:inline="true"
|
||||
:model="QuestionList[index]"
|
||||
class="demo-form-inline"
|
||||
:ref="
|
||||
'JudgeDifferenceValue' + QuestionList[index].JudgeType + index
|
||||
"
|
||||
:rules="JudgeDifferenceValueQRules"
|
||||
>
|
||||
<el-form-item
|
||||
:label="
|
||||
$t(
|
||||
">
|
||||
<el-form :inline="true" :model="QuestionList[index]" class="demo-form-inline" :ref="'JudgeDifferenceValue' + QuestionList[index].JudgeType + index
|
||||
" :rules="JudgeDifferenceValueQRules">
|
||||
<el-form-item :label="$t(
|
||||
`trials:trials-panel:setting:reading-unit:JudgeDifferenceType`
|
||||
)
|
||||
"
|
||||
prop="JudgeDifferenceType"
|
||||
>
|
||||
<el-select
|
||||
v-model="QuestionList[index].JudgeDifferenceType"
|
||||
placeholder="请选择"
|
||||
:disabled="OtherInfo.IsSign"
|
||||
>
|
||||
<el-option
|
||||
v-for="item of $d.JudgeDifferenceType"
|
||||
:key="item.id"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
" prop="JudgeDifferenceType">
|
||||
<el-select v-model="QuestionList[index].JudgeDifferenceType" placeholder="请选择"
|
||||
:disabled="OtherInfo.IsSign">
|
||||
<el-option v-for="item of $d.JudgeDifferenceType" :key="item.id" :label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="
|
||||
$t(
|
||||
<el-form-item :label="$t(
|
||||
`trials:trials-panel:setting:reading-unit:JudgeDifferenceValue${QuestionList[index].JudgeType}`
|
||||
)
|
||||
"
|
||||
prop="JudgeDifferenceValue"
|
||||
>
|
||||
" prop="JudgeDifferenceValue">
|
||||
<div style="display: flex">
|
||||
<el-input
|
||||
v-model="QuestionList[index].JudgeDifferenceValue"
|
||||
clearable
|
||||
:disabled="OtherInfo.IsSign"
|
||||
></el-input>
|
||||
<span style="margin-left: 10px">{{
|
||||
<el-input v-model="QuestionList[index].JudgeDifferenceValue" clearable
|
||||
:disabled="OtherInfo.IsSign"></el-input>
|
||||
<span style="margin-left: 10px" v-if="QuestionList[index].JudgeType !== 5">{{
|
||||
$fd('ValueUnit', QuestionList[index].Unit)
|
||||
}}</span>
|
||||
</div>
|
||||
|
|
@ -355,124 +206,62 @@
|
|||
<!-- </el-tab-pane>-->
|
||||
<!-- </el-tabs>-->
|
||||
<!-- 选择答案 -->
|
||||
<el-dialog
|
||||
v-if="QuestionVisible"
|
||||
:title="$t('trials:adRules:title:selectAnswer')"
|
||||
:visible.sync="QuestionVisible"
|
||||
width="800px"
|
||||
:close-on-click-modal="false"
|
||||
custom-class="base-dialog-wrapper"
|
||||
append-to-body
|
||||
>
|
||||
<el-dialog v-if="QuestionVisible" :title="$t('trials:adRules:title:selectAnswer')" :visible.sync="QuestionVisible"
|
||||
width="800px" :close-on-click-modal="false" custom-class="base-dialog-wrapper" append-to-body>
|
||||
<div class="base-dialog-body">
|
||||
<!-- 阅片人A -->
|
||||
<el-form-item
|
||||
label-width="110px"
|
||||
:label="$t('trials:adRules:title:answerGroupA')"
|
||||
>
|
||||
<el-form-item label-width="110px" :label="$t('trials:adRules:title:answerGroupA')">
|
||||
<el-checkbox-group v-model="QuestionList[selectIndex].groupingA">
|
||||
<template
|
||||
v-for="item of QuestionList[selectIndex].TypeValue.split('|')"
|
||||
>
|
||||
<el-checkbox
|
||||
v-if="QuestionList[selectIndex].QuestionGenre !== 3"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:disabled="
|
||||
QuestionList[selectIndex].groupingB.length
|
||||
<template v-for="item of QuestionList[selectIndex].TypeValue.split('|')">
|
||||
<el-checkbox v-if="QuestionList[selectIndex].QuestionGenre !== 3" :key="item" :label="item" :disabled="QuestionList[selectIndex].groupingB.length
|
||||
? ~QuestionList[selectIndex].groupingB.indexOf(item)
|
||||
: false
|
||||
"
|
||||
>{{ item }}</el-checkbox
|
||||
>
|
||||
">{{ item }}</el-checkbox>
|
||||
</template>
|
||||
<template
|
||||
v-for="item of $d[QuestionList[selectIndex].DictionaryCode]"
|
||||
>
|
||||
<el-checkbox
|
||||
v-if="
|
||||
<template v-for="item of $d[QuestionList[selectIndex].DictionaryCode]">
|
||||
<el-checkbox v-if="
|
||||
QuestionList[selectIndex].QuestionGenre === 3 &&
|
||||
item.value !== -1
|
||||
"
|
||||
:key="item.id"
|
||||
:label="item.value"
|
||||
:disabled="
|
||||
QuestionList[selectIndex].groupingB.length
|
||||
" :key="item.id" :label="item.value" :disabled="QuestionList[selectIndex].groupingB.length
|
||||
? ~QuestionList[selectIndex].groupingB.indexOf(item.value)
|
||||
: false
|
||||
"
|
||||
>{{ item.label }}</el-checkbox
|
||||
>
|
||||
">{{ item.label }}</el-checkbox>
|
||||
</template>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<!-- 阅片人B -->
|
||||
<el-form-item
|
||||
label-width="110px"
|
||||
:label="$t('trials:adRules:title:answerGroupB')"
|
||||
>
|
||||
<el-form-item label-width="110px" :label="$t('trials:adRules:title:answerGroupB')">
|
||||
<el-checkbox-group v-model="QuestionList[selectIndex].groupingB">
|
||||
<template
|
||||
v-for="item of QuestionList[selectIndex].TypeValue.split('|')"
|
||||
>
|
||||
<el-checkbox
|
||||
v-if="QuestionList[selectIndex].QuestionGenre !== 3"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:disabled="
|
||||
QuestionList[selectIndex].groupingA.length
|
||||
<template v-for="item of QuestionList[selectIndex].TypeValue.split('|')">
|
||||
<el-checkbox v-if="QuestionList[selectIndex].QuestionGenre !== 3" :key="item" :label="item" :disabled="QuestionList[selectIndex].groupingA.length
|
||||
? ~QuestionList[selectIndex].groupingA.indexOf(item)
|
||||
: false
|
||||
"
|
||||
>{{ item }}</el-checkbox
|
||||
>
|
||||
">{{ item }}</el-checkbox>
|
||||
</template>
|
||||
<template
|
||||
v-for="item of $d[QuestionList[selectIndex].DictionaryCode]"
|
||||
>
|
||||
<el-checkbox
|
||||
v-if="
|
||||
<template v-for="item of $d[QuestionList[selectIndex].DictionaryCode]">
|
||||
<el-checkbox v-if="
|
||||
QuestionList[selectIndex].QuestionGenre === 3 &&
|
||||
item.value !== -1
|
||||
"
|
||||
:key="item.id"
|
||||
:label="item.value"
|
||||
:disabled="
|
||||
QuestionList[selectIndex].groupingA.length
|
||||
" :key="item.id" :label="item.value" :disabled="QuestionList[selectIndex].groupingA.length
|
||||
? ~QuestionList[selectIndex].groupingA.indexOf(item.value)
|
||||
: false
|
||||
"
|
||||
>{{ item.label }}</el-checkbox
|
||||
>
|
||||
">{{ item.label }}</el-checkbox>
|
||||
</template>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div
|
||||
class="base-dialog-footer"
|
||||
style="text-align: right; margin-top: 10px"
|
||||
>
|
||||
<div class="base-dialog-footer" style="text-align: right; margin-top: 10px">
|
||||
<!-- 取消 -->
|
||||
<el-button
|
||||
v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
|
||||
:disabled="btnLoading"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="
|
||||
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']" :disabled="btnLoading" size="small"
|
||||
type="primary" @click="
|
||||
QuestionVisible = false
|
||||
$set(QuestionList[selectIndex], 'groupingA', [])
|
||||
$set(QuestionList[selectIndex], 'groupingB', [])
|
||||
"
|
||||
>
|
||||
">
|
||||
{{ $t('common:button:cancel') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
|
||||
:disabled="btnLoading"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="save"
|
||||
>
|
||||
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']" :disabled="btnLoading" size="small"
|
||||
type="primary" @click="save">
|
||||
{{ $t('common:button:save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -34,13 +34,22 @@
|
|||
<el-form-item v-if="CriterionType === 0 && form.ReadingVersionEnum === 1"
|
||||
:label="$t('trials:readingUnit:readingRules:title:measureTool')">
|
||||
<el-checkbox-group v-model="form.ReadingToolList" :disabled="isConfirm ||
|
||||
!hasPermi(['trials:trials-panel:setting:reading-unit:edit'])
|
||||
">
|
||||
!hasPermi(['trials:trials-panel:setting:reading-unit:edit'])" @change="handleReadingToolListChange">
|
||||
<el-checkbox v-for="tool in tools" :key="tool.toolName" :label="tool.toolName" name="ReadingToolList">
|
||||
{{ $t(`${tool.i18nKey}`) }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<!-- 定圆工具半径 -->
|
||||
<el-form-item v-if="form.ReadingToolList.includes('FixedRadiusCircleROI')"
|
||||
:label="$t('trials:readingUnit:readingRules:title:circleRadius')" prop="CircleRadius">
|
||||
<el-input v-model="form.CircleRadius" @input="(val) => handleNumberInput(val, 'CircleRadius')" clearable
|
||||
:disabled="isConfirm ||
|
||||
!hasPermi(['trials:trials-panel:setting:reading-unit:edit'])
|
||||
">
|
||||
<template slot="append">mm</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!--分割工具 && (form.ReadingTool === 0 || form.ReadingTool === 1 || form.ReadingTool === 2)-->
|
||||
<el-form-item v-if="CriterionType === 0 && form.ReadingVersionEnum === 1 && form.ReadingTool === 3"
|
||||
:label="$t('trials:readingUnit:readingRules:title:segmentTool')">
|
||||
|
|
@ -52,6 +61,22 @@
|
|||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<!-- 分割组名 -->
|
||||
<el-form-item v-if="CriterionType === 0 && form.ReadingVersionEnum === 1 && form.ReadingTool === 3"
|
||||
:label="$t('trials:readingUnit:readingRules:title:segmentations')" prop="DefaultSegmentName.SegmentationName">
|
||||
<el-input v-model="form.DefaultSegmentName.SegmentationName" :disabled="isConfirm ||
|
||||
!hasPermi(['trials:trials-panel:setting:reading-unit:edit'])" style="width: 80%">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!-- 分割片段 -->
|
||||
<el-form-item v-if="CriterionType === 0 && form.ReadingVersionEnum === 1 && form.ReadingTool === 3"
|
||||
:label="$t('trials:readingUnit:readingRules:title:segment')" prop="DefaultSegmentName.SegmentNameList">
|
||||
<el-input v-model="form.SegmentNameListStr" style="width: 80%" disabled>
|
||||
</el-input>
|
||||
<el-button :disabled="isConfirm ||
|
||||
!hasPermi(['trials:trials-panel:setting:reading-unit:edit'])
|
||||
" icon="el-icon-plus" circle @click="segmentVisible = true" />
|
||||
</el-form-item>
|
||||
<!--检查类型筛选-->
|
||||
<el-form-item :label="$t('trials:processCfg:form:IsImageFilter')" prop="IsImageFilter">
|
||||
<el-radio-group v-model="form.IsImageFilter" @input="IsImageFilterChange" :disabled="isConfirm ||
|
||||
|
|
@ -392,6 +417,35 @@
|
|||
</el-dialog>
|
||||
<attachmentPreview :visible.sync="perview_visible" :isView="true" :isExternal="true" :ExternalList="ExternalList"
|
||||
v-if="perview_visible" />
|
||||
<!-- 分割段编辑 -->
|
||||
<el-dialog v-if="segmentVisible" :visible.sync="segmentVisible" :close-on-click-modal="false" width="600px"
|
||||
:title="$t('trials:trialCfg:dialogTitle:addSegment')" custom-class="base-dialog-wrapper">
|
||||
<div class="base-dialog-body">
|
||||
<el-button @click.stop="addSegment" style="margin-bottom: 5px;" type="primary" size="small">{{
|
||||
$t('common:button:new') }}</el-button>
|
||||
<el-table :data="SegmentNameList" border style="width: 100%" size="small">
|
||||
<el-table-column type="index" width="50" />
|
||||
<el-table-column prop="name" :label="$t('trials:trialCfg:table:segmentName')" show-overflow-tooltip />
|
||||
<el-table-column :label="$t('common:action:action')" align="left" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button circle icon="el-icon-top" :title="$t('dictionary:template:keyDocList:button:up')"
|
||||
@click.stop="sortSegment(scope, 'up')" :disabled="scope.$index === 0" />
|
||||
<el-button circle icon="el-icon-bottom" :title="$t('dictionary:template:keyDocList:button:down')"
|
||||
@click.stop="sortSegment(scope, 'down')" :disabled="scope.$index === SegmentNameList.length - 1" />
|
||||
<el-button circle icon="el-icon-edit-outline" :title="$t('dictionary:template:keyDocList:button:update')"
|
||||
@click.stop="updateSegment(scope)" />
|
||||
<el-button circle icon="el-icon-delete" :title="$t('dictionary:template:keyDocList:button:del')"
|
||||
@click.stop="delSegment(scope)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="base-dialog-footer" style="text-align: right; margin-top: 10px">
|
||||
<el-button size="small" type="primary" @click="segmentVisible = false">
|
||||
{{ $t('trials:trialCfg:button:confirmCfg') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
|
@ -420,6 +474,7 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
segmentVisible: false,
|
||||
perview_visible: false,
|
||||
ExternalList: [],
|
||||
keyDocVisible: false,
|
||||
|
|
@ -432,6 +487,11 @@ export default {
|
|||
ReadingVersionEnum: null,
|
||||
ReadingToolList: [],
|
||||
SegmentToolList: [],
|
||||
DefaultSegmentName: {
|
||||
SegmentationName: null,
|
||||
SegmentNameList: []
|
||||
},
|
||||
SegmentNameListStr: null,
|
||||
ReadingTaskViewEnum: null,
|
||||
IsImageLabeled: null,
|
||||
IsReadingShowSubjectInfo: null,
|
||||
|
|
@ -460,7 +520,8 @@ export default {
|
|||
ImageUploadEnum: null,
|
||||
IsImageFilter: false,
|
||||
KeyFileListStr: '',
|
||||
KeyFileList: []
|
||||
KeyFileList: [],
|
||||
CircleRadius: null
|
||||
},
|
||||
rules: {
|
||||
IsAutoCreate: [
|
||||
|
|
@ -596,6 +657,21 @@ export default {
|
|||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
'DefaultSegmentName.SegmentationName': [
|
||||
{
|
||||
required: true,
|
||||
message: this.$t('common:ruleMessage:specify'),
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
'DefaultSegmentName.SegmentNameList': [
|
||||
{
|
||||
required: true,
|
||||
type: 'array',
|
||||
message: this.$t('common:ruleMessage:select'),
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
CriterionModalitys: [
|
||||
{
|
||||
required: true,
|
||||
|
|
@ -613,6 +689,10 @@ export default {
|
|||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
CircleRadius: [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'], },
|
||||
{ pattern: /^(0|[1-9]\d*)(\.\d{1,2})?$/, message: '请输入正确的数值格式,最多两位小数', trigger: 'blur' }
|
||||
]
|
||||
// IsReadingTaskViewInOrder: [
|
||||
// { required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
|
||||
// ]
|
||||
|
|
@ -642,6 +722,16 @@ export default {
|
|||
isUSA() {
|
||||
return process.env.NODE_ENV === 'usa'
|
||||
},
|
||||
SegmentNameList() {
|
||||
let arr = []
|
||||
this.form.DefaultSegmentName.SegmentNameList.forEach(item => {
|
||||
let obj = {
|
||||
name: item
|
||||
}
|
||||
arr.push(obj)
|
||||
})
|
||||
return arr
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
CriterionModalitys: {
|
||||
|
|
@ -652,6 +742,76 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
sortSegment(row, key) {
|
||||
let index = row.$index
|
||||
let name = row.row.name
|
||||
this.form.DefaultSegmentName.SegmentNameList.splice(index, 1)
|
||||
if (key === 'up') {
|
||||
|
||||
|
||||
this.form.DefaultSegmentName.SegmentNameList.splice(index - 1, 0, name)
|
||||
} else {
|
||||
this.form.DefaultSegmentName.SegmentNameList.splice(index + 1, 0, name)
|
||||
}
|
||||
},
|
||||
async updateSegment(row) {
|
||||
let name = await this.customPrompt(row.row.name)
|
||||
if (!name) return false
|
||||
let index = row.$index
|
||||
this.form.DefaultSegmentName.SegmentNameList.splice(index, 1, name)
|
||||
this.form.SegmentNameListStr = this.form.DefaultSegmentName.SegmentNameList.join("|")
|
||||
},
|
||||
delSegment(row) {
|
||||
console.log(row)
|
||||
let index = row.$index
|
||||
this.form.DefaultSegmentName.SegmentNameList.splice(index, 1)
|
||||
this.form.SegmentNameListStr = this.form.DefaultSegmentName.SegmentNameList.join("|")
|
||||
},
|
||||
async addSegment() {
|
||||
let name = await this.customPrompt()
|
||||
if (!name) return false
|
||||
this.form.DefaultSegmentName.SegmentNameList.push(name)
|
||||
this.form.SegmentNameListStr = this.form.DefaultSegmentName.SegmentNameList.join("|")
|
||||
},
|
||||
async customPrompt(name = null) {
|
||||
try {
|
||||
const that = this
|
||||
// 请输入标记名称
|
||||
let message = !name ? this.$t('trials:reading:Segmentations:message:add') : this.$t('trials:reading:Segmentations:message:upate')
|
||||
const { value } = await this.$prompt(message, '', {
|
||||
showClose: false,
|
||||
cancelButtonText: this.$t('common:button:cancel'),
|
||||
confirmButtonText: this.$t('common:button:save'),
|
||||
showCancelButton: true,
|
||||
closeOnClickModal: false,
|
||||
closeOnPressEscape: false,
|
||||
inputValue: name,
|
||||
inputValidator: (value) => {
|
||||
if (!value) {
|
||||
return that.$t("trials:reading:Segmentations:message:notName")
|
||||
} else if (value === name) {
|
||||
return true
|
||||
} else if (that.form.DefaultSegmentName.SegmentNameList.includes(value)) {
|
||||
return that.$t("trials:reading:Segmentations:message:nameIsHas")
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
},
|
||||
beforeClose: (action, instance, done) => {
|
||||
if (action === 'confirm') {
|
||||
// const value = instance.inputValue
|
||||
done()
|
||||
} else {
|
||||
done()
|
||||
}
|
||||
}
|
||||
})
|
||||
return value
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
return null
|
||||
}
|
||||
},
|
||||
async viewManual() {
|
||||
try {
|
||||
if (this.form.KeyFileList.length <= 0) return this.$message.warning(this.$t("trials:researchRecord:ImageManual:message:noImageManual"))
|
||||
|
|
@ -814,6 +974,7 @@ export default {
|
|||
if (this.form.ReadingTool === 3) {
|
||||
this.segmentTools = [...config.customizeStandardsSegmentDicom]
|
||||
}
|
||||
this.form.SegmentNameListStr = res.Result.DefaultSegmentName.SegmentNameList.join(",")
|
||||
}
|
||||
this.CriterionModalitys = this.form.CriterionModalitys
|
||||
? this.form.CriterionModalitys.split('|')
|
||||
|
|
@ -900,6 +1061,37 @@ export default {
|
|||
})
|
||||
})
|
||||
},
|
||||
handleReadingToolListChange(v) {
|
||||
if (!v.includes('FixedRadiusCircleROI')) {
|
||||
this.form.CircleRadius = null
|
||||
}
|
||||
},
|
||||
handleNumberInput(value, field) {
|
||||
if (!value) {
|
||||
this.form[field] = ''
|
||||
return
|
||||
}
|
||||
|
||||
let val = value.toString()
|
||||
|
||||
// 1. 清除“数字”和“.”以外的字符
|
||||
val = val.replace(/[^\d.]/g, '')
|
||||
// 2. 保证第一个字符不能是“.”
|
||||
val = val.replace(/^\./g, '')
|
||||
// 3. 保证不能连续输入多个“.”,只保留第一个
|
||||
val = val.replace(/\.{2,}/g, '.')
|
||||
val = val.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.')
|
||||
// 4. 限制只能输入两位小数
|
||||
val = val.replace(/^(\-)*(\d+)\.(\d{2}).*$/, '$1$2.$3')
|
||||
// 5. 处理前导零(例如输入 01 自动变成 1)
|
||||
if (val.length > 1 && val[0] === '0' && val[1] !== '.') {
|
||||
val = val.replace(/^0+/, '')
|
||||
if (val === '') val = '0'
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.ruleForm[field] = val
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,24 @@
|
|||
<!-- 配置信息表单 -->
|
||||
<el-form ref="processConfigForm" v-loading="loading" :model="form" label-width="300px" style="width: 800px"
|
||||
:rules="rules" size="small">
|
||||
<!-- 数据存储 -->
|
||||
<el-form-item
|
||||
:label="$t('trials:processCfg:form:trialDataStoreType')"
|
||||
prop="TrialDataStoreType"
|
||||
>
|
||||
<el-radio-group
|
||||
v-model="form.TrialDataStoreType"
|
||||
:disabled="form.IsTrialProcessConfirmed && !isEdit"
|
||||
>
|
||||
<el-radio
|
||||
v-for="item of $d.TrialDataStoreType"
|
||||
:key="item.id"
|
||||
:label="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- 阅片方式 -->
|
||||
<el-form-item :label="$t('trials:processCfg:form:readingMode')" prop="ReadingMode">
|
||||
<el-radio-group v-model="form.ReadingMode" :disabled="form.IsTrialProcessConfirmed && !isEdit">
|
||||
|
|
@ -78,6 +96,7 @@
|
|||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 修约小数位数 -->
|
||||
<!-- <el-form-item
|
||||
:label="$t('trials:processCfg:form:digitPlaces')"
|
||||
|
|
@ -724,6 +743,7 @@ export default {
|
|||
VUE_IS_TEST: process.env.VUE_APP_IS_TEST,
|
||||
form: {
|
||||
TrialId: '',
|
||||
TrialDataStoreType: 0,
|
||||
ClinicalInformationTransmissionEnum: 1,
|
||||
ClinicalDataSetNames: [],
|
||||
ClinicalDataTrialSetIds: [],
|
||||
|
|
@ -750,6 +770,13 @@ export default {
|
|||
TrialCriterionIdEnums: [],
|
||||
},
|
||||
rules: {
|
||||
TrialDataStoreType: [
|
||||
{
|
||||
required: true,
|
||||
message: this.$t('common:ruleMessage:select'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
ClinicalDataSetNamesStr: [
|
||||
{
|
||||
required: true,
|
||||
|
|
@ -1005,6 +1032,11 @@ export default {
|
|||
// initialCriterions.push(this.$fd('ReadingStandard', id, 'id'))
|
||||
// })
|
||||
this.confirmData = [
|
||||
{
|
||||
Name: this.$t('trials:processCfg:form:trialDataStoreType'), // 数据存储
|
||||
NewVal: this.$fd('TrialDataStoreType', this.form.TrialDataStoreType),
|
||||
OldVal: this.$fd('TrialDataStoreType', this.initialForm.TrialDataStoreType),
|
||||
},
|
||||
{
|
||||
Name: this.$t('trials:processCfg:form:readingMode'), // 阅片方式
|
||||
NewVal: this.$fd('ReadingMode', this.form.ReadingMode),
|
||||
|
|
|
|||
|
|
@ -261,12 +261,23 @@ export default {
|
|||
uploadFilesAndSave() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.form.AddFileList = []
|
||||
let uploadBatchId = this.$guid()
|
||||
for (var i = 0; i < this.pendingUploadList.length; ++i) {
|
||||
// const file = await this.convertBase64ToBlob(this.pendingUploadList[i])
|
||||
const file = await this.fileToBlob(this.pendingUploadList[i])
|
||||
const res = await this.OSSclient.put(
|
||||
`/${this.data.TrialId}/ClinicalData/${this.pendingUploadList[i].name}`,
|
||||
file
|
||||
file,
|
||||
{
|
||||
fileName: this.pendingUploadList[i].name,
|
||||
fileSize: this.pendingUploadList[i].size,
|
||||
fileType: this.pendingUploadList[i].type,
|
||||
uploadBatchId: uploadBatchId,
|
||||
batchDataType: 4,
|
||||
trialId: this.data.TrialId,
|
||||
subjectId: this.data.SubjectId,
|
||||
subjectVisitId: this.data.SubjectVisitId
|
||||
}
|
||||
)
|
||||
this.form.AddFileList.push({
|
||||
fileName: this.pendingUploadList[i].name,
|
||||
|
|
|
|||
|
|
@ -551,6 +551,7 @@ export default {
|
|||
IsVisit: this.data.IsVisit,
|
||||
SubjectId: this.data.SubjectId,
|
||||
IsBaseLine: this.data.IsBaseLine,
|
||||
SubjectVisitId: this.data.SubjectVisitId
|
||||
}
|
||||
)
|
||||
this.addOrUpdateCD.title = this.$t('common:button:new')
|
||||
|
|
@ -565,6 +566,7 @@ export default {
|
|||
}
|
||||
this.currentData.IsVisit = this.data.IsVisit
|
||||
this.currentData.IsBaseLine = this.data.IsBaseLine
|
||||
this.currentData.SubjectVisitId = this.data.SubjectVisitId
|
||||
this.addOrUpdateCD.title = this.$t('common:button:edit')
|
||||
this.currentOption = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,20 +46,49 @@ import {
|
|||
getTrialCriterionList,
|
||||
getReportsChartSummary
|
||||
} from '@/api/trials'
|
||||
let echarts = require('echarts/lib/echarts');
|
||||
import * as echarts from 'echarts/core';
|
||||
import { LineChart } from 'echarts/charts';
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DataZoomComponent,
|
||||
LegendComponent,
|
||||
DatasetComponent,
|
||||
// 内置数据转换器组件 (filter, sort)
|
||||
TransformComponent
|
||||
} from 'echarts/components';
|
||||
// 标签自动布局、全局过渡动画等特性
|
||||
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
||||
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
echarts.use([
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DatasetComponent,
|
||||
TransformComponent,
|
||||
DataZoomComponent,
|
||||
LegendComponent,
|
||||
LineChart,
|
||||
LabelLayout,
|
||||
UniversalTransition,
|
||||
CanvasRenderer
|
||||
]);
|
||||
// let echarts = require('echarts/lib/echarts');
|
||||
|
||||
// 按需引入图表
|
||||
// require('echarts/lib/chart/bar');
|
||||
require('echarts/lib/chart/line');
|
||||
// require('echarts/lib/chart/line');
|
||||
// require('echarts/lib/chart/pie');
|
||||
// require('echarts/lib/chart/scatter');
|
||||
|
||||
// 按需引入组件
|
||||
require('echarts/lib/component/tooltip');
|
||||
require('echarts/lib/component/title');
|
||||
require('echarts/lib/component/legend');
|
||||
require('echarts/lib/component/grid');
|
||||
require('echarts/lib/component/dataZoom');
|
||||
// require('echarts/lib/component/tooltip');
|
||||
// require('echarts/lib/component/title');
|
||||
// require('echarts/lib/component/legend');
|
||||
// require('echarts/lib/component/grid');
|
||||
// require('echarts/lib/component/dataZoom');
|
||||
export default {
|
||||
name: "SubjectChart",
|
||||
props: {
|
||||
|
|
@ -259,12 +288,19 @@ export default {
|
|||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
grid: {
|
||||
left: 30,
|
||||
bottom: 30
|
||||
},
|
||||
xAxis: {
|
||||
data: obj.visitName,
|
||||
},
|
||||
yAxis: {
|
||||
name: obj.unit,
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
series: obj.series
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,442 @@
|
|||
<template>
|
||||
<BaseContainer>
|
||||
<template slot="search-container">
|
||||
<el-form :inline="true">
|
||||
<!-- 文件名称 -->
|
||||
<el-form-item label="文件名称" prop="FileName">
|
||||
<el-input v-model="searchData.FileName" size="small" clearable style="width: 120px" />
|
||||
</el-form-item>
|
||||
<!-- 文件类型 -->
|
||||
<el-form-item label="文件类型" prop="FileType">
|
||||
<el-input v-model="searchData.FileType" size="small" clearable style="width: 120px" />
|
||||
</el-form-item>
|
||||
<!-- 源区域 -->
|
||||
<el-form-item label="源区域" prop="UploadRegion">
|
||||
<el-select v-model="searchData.UploadRegion" style="width: 120px">
|
||||
<el-option
|
||||
v-for="item in regionOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 源可用时间 -->
|
||||
<el-form-item label="源可用时间">
|
||||
<el-date-picker
|
||||
v-model="uploadTimeRange"
|
||||
:default-time="['00:00:00', '23:59:59']"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
@change="handleUploadtimeChange"
|
||||
style="width: 300px"
|
||||
type="datetimerange">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<!-- 目标区域 -->
|
||||
<el-form-item label="目标区域" prop="TargetRegion">
|
||||
<el-select v-model="searchData.TargetRegion" style="width: 120px">
|
||||
<el-option
|
||||
v-for="item in regionOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 目标可用时间 -->
|
||||
<el-form-item label="目标可用时间">
|
||||
<el-date-picker
|
||||
v-model="SyncTimeRange"
|
||||
:default-time="['00:00:00', '23:59:59']"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
@change="handleSynctimeChange"
|
||||
style="width: 300px"
|
||||
type="datetimerange">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<!-- 优先级 -->
|
||||
<el-form-item label="优先级">
|
||||
<el-input v-model="searchData.Priority" clearable style="width: 120px"></el-input>
|
||||
</el-form-item>
|
||||
<!-- 是否同步完成 -->
|
||||
<el-form-item label="是否同步完成" prop="IsSync">
|
||||
<el-select v-model="searchData.IsSync" clearable style="width: 120px">
|
||||
<el-option
|
||||
v-for="item of $d.YesOrNo"
|
||||
:key="'IsSync' + item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</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-button type="primary" icon="el-icon-edit" :disabled="selectedRows.length === 0" @click="handleBatchEdit">
|
||||
批量编辑
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<template slot="main-container">
|
||||
<el-table v-loading="loading" v-adaptive="{ bottomOffset: 70 }" height="100" :data="list"
|
||||
class="table" @sort-change="handleSortByColumn" :default-sort="{ prop: 'CreateTime', order: 'descending' }" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="index" width="50" />
|
||||
<el-table-column type="selection" width="50" :selectable="selectableRow"/>
|
||||
<el-table-column label="文件名称" prop="FileName" min-width="90" show-overflow-tooltip sortable="custom">
|
||||
</el-table-column>
|
||||
<el-table-column label="文件大小" prop="FileSize" min-width="90" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
{{ fileSizeFormatter(scope.row.FileSize) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件类型" prop="FileType" min-width="90" show-overflow-tooltip sortable="custom">
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="源区域" prop="UploadRegion" min-width="60" show-overflow-tooltip sortable="custom" />
|
||||
|
||||
<el-table-column label="源可用时间" prop="CreateTime" min-width="90" show-overflow-tooltip sortable="custom" />
|
||||
<el-table-column label="路径" prop="Path" min-width="90" show-overflow-tooltip sortable="custom" />
|
||||
<el-table-column label="是否需要同步" prop="IsNeedSync" min-width="90" show-overflow-tooltip sortable="custom">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.IsNeedSync" type="success">
|
||||
{{ $fd('YesOrNo', scope.row.IsNeedSync) }}
|
||||
</el-tag>
|
||||
<el-tag v-else type="danger">
|
||||
{{ $fd('YesOrNo', scope.row.IsNeedSync) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="目标区域" prop="TargetRegion" min-width="80" show-overflow-tooltip sortable="custom" />
|
||||
<el-table-column label="目标可用时间" prop="SyncFinishedTime" min-width="90" show-overflow-tooltip sortable="custom" />
|
||||
<el-table-column label="优先级" prop="Priority" min-width="60" show-overflow-tooltip sortable="custom" />
|
||||
<el-table-column label="是否同步完成" prop="IsSync" min-width="90" show-overflow-tooltip sortable="custom">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.IsSync" type="success">
|
||||
{{ $fd('YesOrNo', scope.row.IsSync) }}
|
||||
</el-tag>
|
||||
<el-tag v-else type="danger">
|
||||
{{ $fd('YesOrNo', scope.row.IsSync) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="更新时间" prop="UpdateTime" min-width="90" show-overflow-tooltip sortable="custom" />
|
||||
<el-table-column label="操作" width="240" show-overflow-tooltip fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="primary" size="mini" @click="handleOpenTaskTable(scope.row)">
|
||||
详情
|
||||
</el-button>
|
||||
<el-button type="primary" size="mini" :disabled="!scope.row.IsNeedSync || !scope.row.IsSync" @click="execute(scope.row)">
|
||||
重新同步
|
||||
</el-button>
|
||||
<el-button type="primary" size="mini" :disabled="scope.row.IsSync" @click="handleEdit(scope.row)">
|
||||
编辑
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
|
||||
@pagination="getList" />
|
||||
</template>
|
||||
<el-dialog v-if="editDialogVisible" title="编辑" :visible.sync="editDialogVisible" width="600px" :close-on-click-modal="false" append-to-body>
|
||||
<el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="110px" v-loading="formLoading">
|
||||
<el-form-item label="文件名称">
|
||||
<el-input v-model="editForm.FileName" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="文件大小">
|
||||
<el-input :value="fileSizeFormatter(editForm.FileSize)" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="文件类型">
|
||||
<el-input v-model="editForm.FileType" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="源区域">
|
||||
<el-input v-model="editForm.UploadRegion" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标区域">
|
||||
<el-input v-model="editForm.TargetRegion" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否需要同步" prop="IsNeedSync">
|
||||
<el-select v-model="editForm.IsNeedSync" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item of $d.YesOrNo"
|
||||
:key="'IsNeedSync' + item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="更新时间">
|
||||
<el-input v-model="editForm.UpdateTime" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="路径">
|
||||
<el-input v-model="editForm.Path" type="textarea" :rows="2" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="优先级" prop="Priority">
|
||||
<el-input-number v-model="editForm.Priority" :min="0" :controls="true" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否同步完成" prop="IsSync">
|
||||
<el-select v-model="editForm.IsSync" style="width: 100%" disabled>
|
||||
<el-option
|
||||
v-for="item of $d.YesOrNo"
|
||||
:key="'IsSync' + item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="editDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSaveEdit">保存</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog v-if="batchEditDialogVisible" title="批量编辑" :visible.sync="batchEditDialogVisible" width="300px" :close-on-click-modal="false" append-to-body>
|
||||
<el-form ref="batchEditFormRef" :model="batchEditForm" :rules="batchEditRules" label-width="80px" v-loading="formLoading">
|
||||
<el-form-item label="优先级" prop="Priority">
|
||||
<el-input-number v-model="batchEditForm.Priority" :min="0" :controls="true" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="batchEditDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSaveBatchEdit">保存</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</BaseContainer>
|
||||
</template>
|
||||
<script>
|
||||
import { getFileUploadRecordList, addOrUpdateFileUploadRecord, batchAddSyncFileTask } from '@/api/file'
|
||||
import BaseContainer from '@/components/BaseContainer'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import moment from 'moment'
|
||||
const searchDataDefault = () => {
|
||||
return {
|
||||
TrialId: '',
|
||||
DataFileType: null,
|
||||
SubjectCode: '',
|
||||
VisitName: '',
|
||||
StudyCode: '',
|
||||
FileName: '',
|
||||
UploadRegion: null,
|
||||
TargetRegion: null,
|
||||
IsSync: null,
|
||||
UploadStartTime: null,
|
||||
UploadEndTime: null,
|
||||
SyncFinishedStartTime: null,
|
||||
SyncFinishedEndTime: null,
|
||||
Priority: null,
|
||||
Asc: false,
|
||||
SortField: 'UpdateTime',
|
||||
PageIndex: 1,
|
||||
PageSize: 20,
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: 'FileList',
|
||||
components: { BaseContainer, Pagination },
|
||||
props: {
|
||||
rowInfo: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
dataFileType: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
moment,
|
||||
searchData: searchDataDefault(),
|
||||
list: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
uploadTimeRange: null,
|
||||
SyncTimeRange: null,
|
||||
regionOptions: [
|
||||
{
|
||||
value: 'CN',
|
||||
label: 'CN'
|
||||
}, {
|
||||
value: 'US',
|
||||
label: 'US'
|
||||
}
|
||||
],
|
||||
editDialogVisible: false,
|
||||
editForm: {
|
||||
// Priority: null,
|
||||
// IsSync: null,
|
||||
},
|
||||
editRules: {
|
||||
Priority: [{ required: true, message: '请输入', trigger: 'change' }],
|
||||
IsSync: [{ required: true, message: '请选择', trigger: 'change' }],
|
||||
},
|
||||
formLoading: false,
|
||||
selectedRows: [],
|
||||
batchEditDialogVisible: false,
|
||||
batchEditForm: {
|
||||
Priority: null,
|
||||
},
|
||||
batchEditRules: {
|
||||
Priority: [{ required: true, message: '请输入', trigger: 'change' }],
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
async getList() {
|
||||
try {
|
||||
this.loading = true
|
||||
this.searchData.TrialId = this.$route.query.trialId
|
||||
this.searchData.SubjectCode = this.rowInfo.SubjectCode
|
||||
this.searchData.VisitName = this.rowInfo.VisitName
|
||||
this.searchData.StudyCode = this.rowInfo.StudyCode
|
||||
this.searchData.DataFileType = this.dataFileType
|
||||
let res = await getFileUploadRecordList(this.searchData)
|
||||
this.loading = false
|
||||
this.list = res.Result.CurrentPageData
|
||||
this.total = res.Result.TotalCount
|
||||
} catch(e) {
|
||||
this.loading = false
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
handleOpenTaskTable(row) {
|
||||
this.$emit('openTaskTable', row)
|
||||
},
|
||||
handleEdit(row) {
|
||||
this.editForm = {...row}
|
||||
this.editDialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.editFormRef) this.$refs.editFormRef.clearValidate()
|
||||
})
|
||||
},
|
||||
async handleSaveEdit() {
|
||||
try {
|
||||
let valid = await this.$refs.editFormRef.validate()
|
||||
if (!valid) return
|
||||
this.formLoading = true
|
||||
let res = await addOrUpdateFileUploadRecord(this.editForm)
|
||||
this.formLoading = false
|
||||
if (res.IsSuccess) {
|
||||
this.editDialogVisible = false
|
||||
this.getList()
|
||||
}
|
||||
} catch(e) {
|
||||
console.log(e)
|
||||
this.formLoading = false
|
||||
}
|
||||
},
|
||||
async execute(row) {
|
||||
try {
|
||||
this.loading = true
|
||||
let params = {
|
||||
fileUploadRecordIdList: [row.Id]
|
||||
}
|
||||
let res = await batchAddSyncFileTask(params)
|
||||
if (res.IsSuccess) {
|
||||
this.$message.success('执行成功!')
|
||||
}
|
||||
this.loading = false
|
||||
this.getList()
|
||||
} catch(e) {
|
||||
this.loading = false
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
},
|
||||
handleSelectionChange(rows) {
|
||||
this.selectedRows = rows
|
||||
},
|
||||
selectableRow(row) {
|
||||
return !row.IsSync
|
||||
},
|
||||
handleBatchEdit() {
|
||||
if (!this.selectedRows.length) return
|
||||
this.batchEditForm = {
|
||||
Priority: null,
|
||||
}
|
||||
this.batchEditDialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.batchEditFormRef) this.$refs.batchEditFormRef.clearValidate()
|
||||
})
|
||||
},
|
||||
async handleSaveBatchEdit() {
|
||||
try {
|
||||
let valid = await this.$refs.batchEditFormRef.validate()
|
||||
if (!valid) return
|
||||
this.formLoading = true
|
||||
let params = {
|
||||
fileUploadRecordIdList: this.selectedRows,
|
||||
priority: this.editForm.Priority
|
||||
}
|
||||
let res = await batchAddSyncFileTask(params)
|
||||
this.formLoading = false
|
||||
if (res.IsSuccess) {
|
||||
this.batchEditDialogVisible = false
|
||||
this.getList()
|
||||
}
|
||||
} catch(e) {
|
||||
console.log(e)
|
||||
this.formLoading = false
|
||||
}
|
||||
},
|
||||
fileSizeFormatter(size) {
|
||||
if (!size) return
|
||||
return (size / Math.pow(1024, 2)).toFixed(3) + 'MB'
|
||||
},
|
||||
handleUploadtimeChange(val) {
|
||||
if (val) {
|
||||
this.searchData.UploadStartTime = val[0]
|
||||
this.searchData.UploadEndTime = val[1]
|
||||
} else {
|
||||
this.searchData.UploadStartTime = ''
|
||||
this.searchData.UploadEndTime = ''
|
||||
}
|
||||
},
|
||||
handleSynctimeChange(val) {
|
||||
if (val) {
|
||||
this.searchData.SyncFinishedStartTime = val[0]
|
||||
this.searchData.SyncFinishedEndTime = val[1]
|
||||
} else {
|
||||
this.searchData.SyncFinishedStartTime = ''
|
||||
this.searchData.SyncFinishedEndTime = ''
|
||||
}
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchData.PageIndex = 1
|
||||
this.getList()
|
||||
},
|
||||
// 重置列表查询
|
||||
handleReset() {
|
||||
this.uploadTimeRange = null
|
||||
this.handleUploadtimeChange()
|
||||
this.SyncTimeRange = null
|
||||
this.handleSynctimeChange()
|
||||
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()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,351 @@
|
|||
<template>
|
||||
<BaseContainer>
|
||||
<template slot="search-container">
|
||||
<el-form :inline="true">
|
||||
<!-- 受试者编号 -->
|
||||
<el-form-item label="受试者编号" prop="SubjectCode">
|
||||
<el-input v-model="searchData.SubjectCode" size="small" clearable style="width: 120px" />
|
||||
</el-form-item>
|
||||
<!-- 访视名称 -->
|
||||
<el-form-item label="访视名称">
|
||||
<el-select v-model="searchData.VisitName" style="width: 140px" clearable>
|
||||
<el-option v-for="(item, index) of visitPlanOptions" :key="index" :label="item.VisitName"
|
||||
:value="item.VisitNum">
|
||||
<span style="float: left">{{ item.VisitName }}</span>
|
||||
</el-option>
|
||||
<!-- <el-option key="Other" label="Out of Plan" value="1.11" /> -->
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 检查编号 -->
|
||||
<el-form-item label="检查编号" prop="StudyCode">
|
||||
<el-input v-model="searchData.StudyCode" size="small" clearable style="width: 120px" />
|
||||
</el-form-item>
|
||||
<!-- 源区域 -->
|
||||
<el-form-item label="源区域" prop="UploadRegion">
|
||||
<el-select v-model="searchData.UploadRegion" style="width: 120px">
|
||||
<el-option
|
||||
v-for="item in regionOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 源可用时间 -->
|
||||
<!-- <el-form-item label="源可用时间">
|
||||
<el-date-picker
|
||||
v-model="uploadTimeRange"
|
||||
:default-time="['00:00:00', '23:59:59']"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
@change="handleUploadtimeChange"
|
||||
style="width: 250px"
|
||||
type="datetimerange">
|
||||
</el-date-picker>
|
||||
</el-form-item> -->
|
||||
<!-- 目标区域 -->
|
||||
<el-form-item label="目标区域" prop="TargetRegion">
|
||||
<el-select v-model="searchData.TargetRegion" style="width: 120px">
|
||||
<el-option
|
||||
v-for="item in regionOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 目标可用时间 -->
|
||||
<!-- <el-form-item label="目标可用时间">
|
||||
<el-date-picker
|
||||
v-model="SyncTimeRange"
|
||||
:default-time="['00:00:00', '23:59:59']"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
@change="handleSynctimeChange"
|
||||
style="width: 250px"
|
||||
type="datetimerange">
|
||||
</el-date-picker>
|
||||
</el-form-item> -->
|
||||
<!-- 是否同步完成 -->
|
||||
<el-form-item label="是否同步完成" prop="IsSync">
|
||||
<el-select v-model="searchData.IsSync" clearable style="width: 120px">
|
||||
<el-option
|
||||
v-for="item of $d.YesOrNo"
|
||||
:key="'IsSync' + item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</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>
|
||||
</template>
|
||||
<template slot="main-container">
|
||||
<el-table v-loading="loading" v-adaptive="{ bottomOffset: 60 }" height="100" :data="list"
|
||||
class="table" @sort-change="handleSortByColumn" :default-sort="{ prop: 'CreateTime', order: 'descending' }">
|
||||
<el-table-column type="index" width="50" />
|
||||
<el-table-column label="受试者编号" prop="SubjectCode" min-width="90" show-overflow-tooltip sortable="custom"/>
|
||||
<el-table-column label="访视名称" prop="VisitName" min-width="90" show-overflow-tooltip sortable="custom"/>
|
||||
<el-table-column label="检查编号" prop="StudyCode" min-width="90" show-overflow-tooltip sortable="custom"/>
|
||||
<el-table-column label="文件数" prop="FileCount" min-width="90" show-overflow-tooltip/>
|
||||
<el-table-column label="源区域" prop="UploadRegion" min-width="60" show-overflow-tooltip />
|
||||
<el-table-column label="源可用时间" prop="CreateTime" min-width="90" show-overflow-tooltip />
|
||||
<el-table-column label="目标区域" prop="TargetRegion" min-width="60" show-overflow-tooltip />
|
||||
<el-table-column label="目标可用时间" prop="SyncFinishedTime" min-width="90" show-overflow-tooltip/>
|
||||
<el-table-column label="是否同步完成" prop="IsSync" min-width="90" show-overflow-tooltip sortable="custom">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.IsSync" type="success">
|
||||
{{ $fd('YesOrNo', scope.row.IsSync) }}
|
||||
</el-tag>
|
||||
<el-tag v-else type="danger">
|
||||
{{ $fd('YesOrNo', scope.row.IsSync) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="80" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<el-button type="primary" size="small" @click="openDetailTable(scope.row)">
|
||||
详情
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
|
||||
@pagination="getList" />
|
||||
</template>
|
||||
<el-dialog
|
||||
v-if="detailDialog.visible"
|
||||
:visible.sync="detailDialog.visible"
|
||||
fullscreen
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
class="detail-dialog"
|
||||
>
|
||||
<span slot="title">{{ detailDialog.title }}</span>
|
||||
<span v-if="detailDialog.currentRow">{{`${detailDialog.currentRow.SubjectCode} / ${detailDialog.currentRow.VisitName} ${detailDialog.currentRow.StudyCode ? ' / ' + detailDialog.currentRow.StudyCode : ''} (源:${detailDialog.currentRow.UploadRegion} -> 目标:${detailDialog.currentRow.TargetRegion})`}}</span>
|
||||
<el-tabs class="detail-tabs" v-model="detailDialog.activeTab" @tab-click="handleDetailTabClick">
|
||||
<el-tab-pane label="文件级别" name="file">
|
||||
<FileList
|
||||
v-if="detailDialog.activeTab === 'file'"
|
||||
:dataFileType="1"
|
||||
:rowInfo="detailDialog.currentRow"
|
||||
@openTaskTable="openTaskTable"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="任务级别" name="task">
|
||||
<TaskList
|
||||
v-if="detailDialog.activeTab === 'task'"
|
||||
:rowInfo="detailDialog.currentRow"
|
||||
:fileUploadRecordId="fileUploadRecordId"
|
||||
:path="path"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-dialog>
|
||||
</BaseContainer>
|
||||
</template>
|
||||
<script>
|
||||
import { getSubjectUploadRecordList } from '@/api/file'
|
||||
import { getTrialVisitStageSelect } from '@/api/trials'
|
||||
import BaseContainer from '@/components/BaseContainer'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import FileList from './FileList'
|
||||
import TaskList from './TaskList'
|
||||
import moment from 'moment'
|
||||
const searchDataDefault = () => {
|
||||
return {
|
||||
TrialId: '',
|
||||
SubjectCode: '',
|
||||
VisitName: null,
|
||||
StudyCode: '',
|
||||
UploadRegion: '',
|
||||
TargetRegion: '',
|
||||
IsSync: null,
|
||||
// UploadStartTime: null,
|
||||
// UploadEndTime: null,
|
||||
// SyncFinishedStartTime: null,
|
||||
// SyncFinishedEndTime: null,
|
||||
PageIndex: 1,
|
||||
PageSize: 20,
|
||||
Asc: true,
|
||||
SortField: 'StudyCode'
|
||||
}
|
||||
}
|
||||
export default {
|
||||
components: { BaseContainer, Pagination, FileList, TaskList },
|
||||
data() {
|
||||
return {
|
||||
trialId: '',
|
||||
moment,
|
||||
searchData: searchDataDefault(),
|
||||
list: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
visitPlanOptions: [],
|
||||
detailDialog: {
|
||||
visible: false,
|
||||
title: '详情',
|
||||
activeTab: 'file',
|
||||
currentRow: null
|
||||
},
|
||||
regionOptions: [
|
||||
{
|
||||
value: 'CN',
|
||||
label: 'CN'
|
||||
}, {
|
||||
value: 'US',
|
||||
label: 'US'
|
||||
}
|
||||
],
|
||||
fileUploadRecordId: '',
|
||||
path: '',
|
||||
uploadTimeRange: null,
|
||||
SyncTimeRange: null,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.trialId = this.$route.query.trialId
|
||||
this.getList()
|
||||
this.getVisitPlanOptions()
|
||||
},
|
||||
methods: {
|
||||
async getList() {
|
||||
try {
|
||||
this.loading = true
|
||||
this.searchData.TrialId = this.trialId
|
||||
let res = await getSubjectUploadRecordList(this.searchData)
|
||||
this.loading = false
|
||||
this.list = res.Result.CurrentPageData
|
||||
this.total = res.Result.TotalCount
|
||||
} catch(e) {
|
||||
this.loading = false
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
openDetailTable(row) {
|
||||
this.detailDialog.currentRow = row || null
|
||||
this.detailDialog.activeTab = 'file'
|
||||
this.detailDialog.visible = true
|
||||
},
|
||||
openTaskTable(row) {
|
||||
this.fileUploadRecordId = row.Id
|
||||
this.path = row.Path
|
||||
this.detailDialog.activeTab = 'task'
|
||||
|
||||
},
|
||||
handleDetailTabClick(tab) {
|
||||
const name = tab.name
|
||||
if (name === 'file') {
|
||||
this.fileUploadRecordId = ''
|
||||
this.path = ''
|
||||
}
|
||||
// if (name !== 'upload' && name !== 'sync') return
|
||||
// if (this.detailDialog[name].list && this.detailDialog[name].list.length) return
|
||||
// this.fetchDetailList(name)
|
||||
},
|
||||
// 获取访视下拉框数据
|
||||
async getVisitPlanOptions() {
|
||||
try {
|
||||
let res = await getTrialVisitStageSelect(this.trialId)
|
||||
this.visitPlanOptions = res.Result
|
||||
} catch(e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
handleUploadtimeChange(val) {
|
||||
if (val) {
|
||||
this.searchData.UploadStartTime = val[0]
|
||||
this.searchData.UploadEndTime = val[1]
|
||||
} else {
|
||||
this.searchData.UploadStartTime = ''
|
||||
this.searchData.UploadEndTime = ''
|
||||
}
|
||||
},
|
||||
handleSynctimeChange(val) {
|
||||
if (val) {
|
||||
this.searchData.SyncFinishedStartTime = val[0]
|
||||
this.searchData.SyncFinishedEndTime = val[1]
|
||||
} else {
|
||||
this.searchData.SyncFinishedStartTime = ''
|
||||
this.searchData.SyncFinishedEndTime = ''
|
||||
}
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchData.PageIndex = 1
|
||||
this.getList()
|
||||
},
|
||||
// 重置列表查询
|
||||
handleReset() {
|
||||
// this.uploadTimeRange = null
|
||||
// this.handleUploadtimeChange()
|
||||
// this.SyncTimeRange = null
|
||||
// this.handleSynctimeChange()
|
||||
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()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.detail-dialog {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.el-dialog__header {
|
||||
flex: 0 0 auto;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
padding-top: 10px;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.is-fullscreen {
|
||||
.el-dialog__body {
|
||||
margin-top: 10px;
|
||||
height: calc(100% - 80px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-tabs {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.el-tabs__header {
|
||||
flex: 0 0 auto;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
.el-tabs__content {
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
.el-tab-pane {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
<template>
|
||||
<BaseContainer>
|
||||
<template slot="search-container">
|
||||
<el-form :inline="true">
|
||||
<!-- 文件名称 -->
|
||||
<el-form-item label="文件名称" prop="FileName">
|
||||
<el-input v-model="searchData.FileName" size="small" clearable style="width: 120px" />
|
||||
</el-form-item>
|
||||
<!-- 文件路径 -->
|
||||
<el-form-item label="文件路径" prop="Path">
|
||||
<el-input v-model="searchData.Path" size="small" clearable />
|
||||
</el-form-item>
|
||||
<!-- 任务状态 -->
|
||||
<el-form-item label="任务状态" prop="JobState">
|
||||
<el-select v-model="searchData.JobState" clearable style="width: 120px">
|
||||
<el-option
|
||||
v-for="item of $d.JobState"
|
||||
:key="'JobState' + item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 任务开始日期 -->
|
||||
<el-form-item label="任务开始日期">
|
||||
<el-date-picker
|
||||
v-model="searchData.StartTime"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
format="yyyy-MM-dd"
|
||||
:picker-options="beginPickerOption"
|
||||
style="width:140px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 任务结束日期 -->
|
||||
<el-form-item label="任务结束日期">
|
||||
<el-date-picker
|
||||
v-model="searchData.EndTime"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
format="yyyy-MM-dd"
|
||||
:picker-options="endpickerOption"
|
||||
style="width:140px;"
|
||||
/>
|
||||
</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>
|
||||
</template>
|
||||
<template slot="main-container">
|
||||
<el-table v-loading="loading" v-adaptive="{ bottomOffset: 70 }" height="100" :data="list"
|
||||
class="table" @sort-change="handleSortByColumn" :default-sort="{ prop: 'CreateTime', order: 'descending' }">
|
||||
<el-table-column type="index" width="50" />
|
||||
<el-table-column label="Job编号" prop="Id" min-width="90" show-overflow-tooltip/>
|
||||
<el-table-column label="文件名称" prop="FileName" min-width="90" show-overflow-tooltip sortable="custom">
|
||||
</el-table-column>
|
||||
<el-table-column label="路径" prop="Path" min-width="90" show-overflow-tooltip/>
|
||||
<el-table-column label="任务状态" prop="JobState" min-width="90" show-overflow-tooltip sortable="custom">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.JobState === 1" type="infoinf0">
|
||||
{{ $fd('JobState', scope.row.JobState) }}
|
||||
</el-tag>
|
||||
<el-tag v-else-if="scope.row.JobState === 1" type="warning">
|
||||
{{ $fd('JobState', scope.row.JobState) }}
|
||||
</el-tag>
|
||||
<el-tag v-else-if="scope.row.JobState === 2" type="success">
|
||||
{{ $fd('JobState', scope.row.JobState) }}
|
||||
</el-tag>
|
||||
<el-tag v-else-if="scope.row.JobState === 3" type="danger">
|
||||
{{ $fd('JobState', scope.row.JobState) }}
|
||||
</el-tag>
|
||||
<el-tag v-else>{{ $fd('JobState', scope.row.JobState) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任务开始时间" prop="StartTime" min-width="90" show-overflow-tooltip sortable="custom" />
|
||||
<el-table-column label="任务结束时间" prop="EndTime" min-width="90" show-overflow-tooltip sortable="custom" />
|
||||
<el-table-column label="创建时间" prop="CreateTime" min-width="90" show-overflow-tooltip sortable="custom" />
|
||||
|
||||
|
||||
<el-table-column label="操作" min-width="80" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<el-button type="primary" size="mini" :disabled="scope.row.JobState !== 3" @click="execute(scope.row)">
|
||||
再次执行
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
|
||||
@pagination="getList" />
|
||||
</template>
|
||||
</BaseContainer>
|
||||
</template>
|
||||
<script>
|
||||
import { getUploadFileSyncRecordList, batchAddSyncFileTask } from '@/api/file'
|
||||
import BaseContainer from '@/components/BaseContainer'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import moment from 'moment'
|
||||
const searchDataDefault = () => {
|
||||
return {
|
||||
TrialId: '',
|
||||
StudyCode: '',
|
||||
SubjectCode: '',
|
||||
VisitName: '',
|
||||
FileUploadRecordId: '',
|
||||
JobState: null,
|
||||
FileName: '',
|
||||
Path: '',
|
||||
StartTime: '',
|
||||
EndTime: '',
|
||||
Asc: false,
|
||||
SortField: '',
|
||||
PageIndex: 1,
|
||||
PageSize: 20,
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: 'TaskList',
|
||||
components: { BaseContainer, Pagination },
|
||||
props: {
|
||||
fileUploadRecordId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
rowInfo: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
moment,
|
||||
searchData: searchDataDefault(),
|
||||
list: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
datetimerange: [],
|
||||
beginPickerOption: {
|
||||
disabledDate: time => {
|
||||
if (this.searchData.EndTime) {
|
||||
return time.getTime() >= new Date(this.searchData.EndTime).getTime()
|
||||
} else {
|
||||
return time.getTime() > Date.now()
|
||||
}
|
||||
}
|
||||
},
|
||||
endpickerOption: {
|
||||
disabledDate: time => {
|
||||
if (this.searchData.StartTime) {
|
||||
return time.getTime() > Date.now() || time.getTime() <= new Date(this.searchData.StartTime).getTime() - 86400000
|
||||
} else {
|
||||
return time.getTime() > Date.now()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(()=>{
|
||||
this.searchData.Path = this.path
|
||||
this.searchData.FileUploadRecordId = this.fileUploadRecordId
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
async getList() {
|
||||
try {
|
||||
this.loading = true
|
||||
this.searchData.TrialId = this.$route.query.trialId
|
||||
this.searchData.StudyCode = this.rowInfo.StudyCode
|
||||
this.searchData.SubjectCode = this.rowInfo.SubjectCode
|
||||
this.searchData.VisitName = this.rowInfo.VisitName
|
||||
|
||||
let res = await getUploadFileSyncRecordList(this.searchData)
|
||||
this.loading = false
|
||||
this.list = res.Result.CurrentPageData
|
||||
this.total = res.Result.TotalCount
|
||||
} catch(e) {
|
||||
this.loading = false
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
async execute(row) {
|
||||
try {
|
||||
this.loading = true
|
||||
let params = {
|
||||
fileUploadRecordIdList: [row.Id]
|
||||
}
|
||||
let res = await batchAddSyncFileTask(params)
|
||||
if (res.IsSuccess) {
|
||||
this.$message.success('执行成功!')
|
||||
}
|
||||
|
||||
this.loading = false
|
||||
this.getList()
|
||||
} catch(e) {
|
||||
this.loading = false
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
},
|
||||
fileSizeFormatter(size) {
|
||||
if (!size) return
|
||||
return (size / Math.pow(1024, 2)).toFixed(3) + 'MB'
|
||||
},
|
||||
handleDatetimeChange(val) {
|
||||
if (val) {
|
||||
this.searchData.BeginDate = val[0]
|
||||
this.searchData.EndDate = val[1]
|
||||
} else {
|
||||
this.searchData.BeginDate = ''
|
||||
this.searchData.EndDate = ''
|
||||
}
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchData.PageIndex = 1
|
||||
this.getList()
|
||||
},
|
||||
// 重置列表查询
|
||||
handleReset() {
|
||||
this.datetimerange = null
|
||||
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()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<el-tabs class="data-sync-tabs" type="border-card" tab-position="left" v-model="activeTab" >
|
||||
<el-tab-pane label="检查列表" name="study">
|
||||
<StudyList />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="其他" name="other">其他</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
<script>
|
||||
import StudyList from './components/StudyList'
|
||||
export default {
|
||||
name: 'DataSync',
|
||||
components: {
|
||||
StudyList
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
activeTab: 'study'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.data-sync-tabs{
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
.el-tabs__header {
|
||||
height: 100%;
|
||||
}
|
||||
.el-tabs__content {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
.el-tab-pane {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -36,17 +36,49 @@
|
|||
</template>
|
||||
<script>
|
||||
import BaseContainer from '@/components/BaseContainer'
|
||||
let echarts = require('echarts/lib/echarts');
|
||||
import * as echarts from 'echarts/core';
|
||||
import { BarChart, FunnelChart } from 'echarts/charts';
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DataZoomComponent,
|
||||
LegendComponent,
|
||||
MarkLineComponent,
|
||||
DatasetComponent,
|
||||
// 内置数据转换器组件 (filter, sort)
|
||||
TransformComponent
|
||||
} from 'echarts/components';
|
||||
// 标签自动布局、全局过渡动画等特性
|
||||
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
||||
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
echarts.use([
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DatasetComponent,
|
||||
TransformComponent,
|
||||
DataZoomComponent,
|
||||
MarkLineComponent,
|
||||
LegendComponent,
|
||||
BarChart,
|
||||
FunnelChart,
|
||||
LabelLayout,
|
||||
UniversalTransition,
|
||||
CanvasRenderer
|
||||
]);
|
||||
// let echarts = require('echarts/lib/echarts');
|
||||
|
||||
require('echarts/lib/component/markLine');
|
||||
require('echarts/lib/chart/funnel');
|
||||
require('echarts/lib/chart/bar');
|
||||
// require('echarts/lib/component/markLine');
|
||||
// require('echarts/lib/chart/funnel');
|
||||
// require('echarts/lib/chart/bar');
|
||||
// 按需引入组件
|
||||
require('echarts/lib/component/tooltip');
|
||||
require('echarts/lib/component/title');
|
||||
require('echarts/lib/component/legend');
|
||||
require('echarts/lib/component/grid');
|
||||
require('echarts/lib/component/dataZoom');
|
||||
// require('echarts/lib/component/tooltip');
|
||||
// require('echarts/lib/component/title');
|
||||
// require('echarts/lib/component/legend');
|
||||
// require('echarts/lib/component/grid');
|
||||
// require('echarts/lib/component/dataZoom');
|
||||
import {
|
||||
getTrialCriterionList,
|
||||
getTrialVisitFinishedStatList,
|
||||
|
|
@ -178,8 +210,11 @@ export default {
|
|||
this.chart_left = echarts.init(this.$refs.chartContainer_left);
|
||||
// ...图表配置
|
||||
const option = {
|
||||
richInheritPlainLabel: false,
|
||||
title: {
|
||||
text: obj.titleText
|
||||
text: obj.titleText,
|
||||
left: 0,
|
||||
top: 0
|
||||
},
|
||||
color: this.color,
|
||||
tooltip: {
|
||||
|
|
@ -316,7 +351,13 @@ export default {
|
|||
// ...图表配置
|
||||
const option = {
|
||||
title: {
|
||||
text: obj.titleText
|
||||
text: obj.titleText,
|
||||
left: 0,
|
||||
top: 0
|
||||
},
|
||||
grid: {
|
||||
left: 80,
|
||||
bottom: 50
|
||||
},
|
||||
toolbox: {
|
||||
show: false
|
||||
|
|
@ -339,6 +380,9 @@ export default {
|
|||
],
|
||||
yAxis: [
|
||||
{
|
||||
axisLine: {
|
||||
show: true
|
||||
},
|
||||
name: obj.unit,
|
||||
type: 'value'
|
||||
}
|
||||
|
|
@ -424,12 +468,18 @@ export default {
|
|||
// ...图表配置
|
||||
const option = {
|
||||
title: {
|
||||
text: obj.titleText
|
||||
text: obj.titleText,
|
||||
left: 0,
|
||||
top: 0
|
||||
},
|
||||
toolbox: {
|
||||
show: false
|
||||
},
|
||||
color: this.color,
|
||||
grid: {
|
||||
left: 80,
|
||||
bottom: 50
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
|
|
@ -441,6 +491,9 @@ export default {
|
|||
data: obj.xAxisData
|
||||
},
|
||||
yAxis: {
|
||||
axisLine: {
|
||||
show: true
|
||||
},
|
||||
name: obj.unit,
|
||||
type: 'value',
|
||||
minInterval: 1
|
||||
|
|
@ -474,11 +527,17 @@ export default {
|
|||
// ...图表配置
|
||||
const option = {
|
||||
title: {
|
||||
text: obj.titleText
|
||||
text: obj.titleText,
|
||||
left: 0,
|
||||
top: 0
|
||||
},
|
||||
toolbox: {
|
||||
show: false
|
||||
},
|
||||
grid: {
|
||||
left: 80,
|
||||
bottom: 50
|
||||
},
|
||||
color: this.color,
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
|
|
@ -498,8 +557,11 @@ export default {
|
|||
yAxis: {
|
||||
name: obj.unit,
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
show: true
|
||||
},
|
||||
axisLabel: {
|
||||
formatter: '{value}%'
|
||||
formatter: '{value}%',
|
||||
}
|
||||
},
|
||||
series: {
|
||||
|
|
@ -531,7 +593,13 @@ export default {
|
|||
// ...图表配置
|
||||
const option = {
|
||||
title: {
|
||||
text: obj.titleText
|
||||
text: obj.titleText,
|
||||
left: 0,
|
||||
top: 0
|
||||
},
|
||||
grid: {
|
||||
left: 80,
|
||||
bottom: 50
|
||||
},
|
||||
toolbox: {
|
||||
show: false
|
||||
|
|
@ -550,7 +618,10 @@ export default {
|
|||
},
|
||||
yAxis: {
|
||||
name: obj.unit,
|
||||
type: 'value'
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
series: {
|
||||
markLine: {
|
||||
|
|
|
|||
|
|
@ -382,6 +382,7 @@ export default {
|
|||
size: files[i].size,
|
||||
type: extendName.split('.')[1],
|
||||
file: files[i],
|
||||
fileType: files[i].type
|
||||
}
|
||||
this.fileList.push(obj);
|
||||
}
|
||||
|
|
@ -401,7 +402,17 @@ export default {
|
|||
var timestamp = Date.now();
|
||||
const res = await this.OSSclient.put(
|
||||
`/${this.trialId}/ClinicalData/${timestamp}_${this.fileList[i].file.name}`,
|
||||
file
|
||||
file,
|
||||
{
|
||||
fileName: `${this.fileList[i].file.name}`,
|
||||
fileSize: file.size,
|
||||
fileType: this.fileList[i].fileType,
|
||||
uploadBatchId: this.$guid(),
|
||||
batchDataType: 4,
|
||||
trialId: this.trialId,
|
||||
subjectId: this.data.SubjectId,
|
||||
subjectVisitId: this.subjectVisitId
|
||||
}
|
||||
);
|
||||
this.addFileList.push({
|
||||
fileName: this.fileList[i].file.name,
|
||||
|
|
|
|||
|
|
@ -1520,6 +1520,7 @@ export default {
|
|||
},
|
||||
}
|
||||
let arr = []
|
||||
let uploadBatchId = scope.$guid()
|
||||
for (let i = 0; i < seriesList.length; i++) {
|
||||
let v = seriesList[i]
|
||||
let instanceList = []
|
||||
|
|
@ -1587,6 +1588,7 @@ export default {
|
|||
params.trialId
|
||||
)}`
|
||||
if (scope.isClose) return
|
||||
console.log(o.file)
|
||||
let res = await dcmUpload(
|
||||
{
|
||||
path: path,
|
||||
|
|
@ -1607,6 +1609,16 @@ export default {
|
|||
) {
|
||||
dicomInfo.uploadFileSize = dicomInfo.fileSize
|
||||
}
|
||||
},
|
||||
{
|
||||
fileName: o.file.name,
|
||||
fileSize: o.file.size,
|
||||
fileType: 'application/dicom',
|
||||
uploadBatchId: uploadBatchId,
|
||||
batchDataType: 1,
|
||||
trialId: params.trialId,
|
||||
subjectId: params.subjectId,
|
||||
subjectVisitId: params.subjectVisitId
|
||||
}
|
||||
)
|
||||
if (!res || !res.url) {
|
||||
|
|
@ -1623,11 +1635,22 @@ export default {
|
|||
o.imageColumns,
|
||||
o.imageRows
|
||||
)
|
||||
|
||||
let thumbnailPath = `/${params.trialId}/Image/${params.subjectId}/${params.subjectVisitId}/${dicomInfo.studyUid}/${v.seriesUid}.jpg`
|
||||
let OSSclient = scope.OSSclient
|
||||
let seriesRes = await OSSclient.put(
|
||||
thumbnailPath,
|
||||
blob
|
||||
blob,
|
||||
{
|
||||
fileName: `${v.seriesUid}.jpg`,
|
||||
fileSize: blob.size,
|
||||
fileType: 'image/jpeg',
|
||||
uploadBatchId: uploadBatchId,
|
||||
batchDataType: 2,
|
||||
trialId: params.trialId,
|
||||
subjectId: params.subjectId,
|
||||
subjectVisitId: params.subjectVisitId
|
||||
}
|
||||
)
|
||||
if (seriesRes && seriesRes.url) {
|
||||
ImageResizePath = scope.$getObjectName(
|
||||
|
|
@ -1762,7 +1785,20 @@ export default {
|
|||
let thumbnailPath = `/${params.trialId}/Image/${params.trialSiteId}/${params.subjectId}/${params.subjectVisitId}/${dicomInfo.studyUid}/${v.seriesUid}.jpg`
|
||||
let OSSclient = scope.OSSclient
|
||||
try {
|
||||
let seriesRes = await OSSclient.put(thumbnailPath, blob)
|
||||
let seriesRes = await OSSclient.put(
|
||||
thumbnailPath,
|
||||
blob,
|
||||
{
|
||||
fileName: `${v.seriesUid}.jpg`,
|
||||
fileSize: blob.size,
|
||||
fileType: 'image/jpeg',
|
||||
uploadBatchId: uploadBatchId,
|
||||
batchDataType: 2,
|
||||
trialId: params.trialId,
|
||||
subjectId: params.subjectId,
|
||||
subjectVisitId: params.subjectVisitId
|
||||
}
|
||||
)
|
||||
if (seriesRes && seriesRes.url) {
|
||||
o.ImageResizePath = scope.$getObjectName(seriesRes.url)
|
||||
}
|
||||
|
|
@ -1775,7 +1811,8 @@ export default {
|
|||
params.study.instanceCount = dicomInfo.failedFileCount
|
||||
params.RecordPath = scope.$getObjectName(logRes.url)
|
||||
if (scope.isClose) return false
|
||||
console.log(params)
|
||||
|
||||
params.UploadBatchId = uploadBatchId
|
||||
addOrUpdateArchiveStudy(params)
|
||||
.then((res) => {
|
||||
if (dicomInfo.failedFileCount === dicomInfo.fileCount) {
|
||||
|
|
|
|||
|
|
@ -756,6 +756,7 @@ export default {
|
|||
.substring(fileName.lastIndexOf('.'))
|
||||
.toLocaleLowerCase()
|
||||
if (this.faccept.indexOf(extendName) !== -1) {
|
||||
files[i].fileName = files[i].name
|
||||
files[i].id = `${files[i].lastModified}${files[i].name}`
|
||||
this.fileList.push(files[i])
|
||||
}
|
||||
|
|
@ -809,8 +810,9 @@ export default {
|
|||
})
|
||||
if (res.IsSuccess) {
|
||||
this.studyMonitorId = res.Result
|
||||
let uploadBatchId = this.$guid()
|
||||
for (let i = 0; i < num; i++) {
|
||||
funArr.push(this.handleUploadTask(this.selectArr, i))
|
||||
funArr.push(this.handleUploadTask(this.selectArr, i, uploadBatchId))
|
||||
}
|
||||
if (funArr.length > 0) {
|
||||
let res = await Promise.all(funArr)
|
||||
|
|
@ -821,17 +823,16 @@ export default {
|
|||
}
|
||||
},
|
||||
// 并发上传
|
||||
async handleUploadTask(arr, index) {
|
||||
async handleUploadTask(arr, index, uploadBatchId) {
|
||||
if (!this.uploadVisible) return
|
||||
let file = this.fileList.filter((item) => item.id === arr[index].id)[0]
|
||||
file.status = 1
|
||||
let fileName = `${this.$guid()}${file.name.substring(file.name.lastIndexOf('.')).toLocaleLowerCase()}`
|
||||
let path = `/${this.trialId}/Image/${this.data.SubjectId}/${this.data.Id
|
||||
}/${this.$guid()}${file.name
|
||||
.substring(file.name.lastIndexOf('.'))
|
||||
.toLocaleLowerCase()}`
|
||||
}/${fileName}`
|
||||
file.curPath = path
|
||||
const fileData = await this.fileToBlob(file.file)
|
||||
let res = await this.fileToOss(path, fileData, file)
|
||||
let res = await this.fileToOss(path, fileData, file, uploadBatchId)
|
||||
if (res) {
|
||||
file.status = 2
|
||||
this.successFileList.push({
|
||||
|
|
@ -865,13 +866,13 @@ export default {
|
|||
}
|
||||
let ind = arr.findIndex((item) => item.status === 0)
|
||||
if (ind >= 0) {
|
||||
return this.handleUploadTask(arr, ind)
|
||||
return this.handleUploadTask(arr, ind, uploadBatchId)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
// file上传到oss
|
||||
async fileToOss(path, file, item) {
|
||||
async fileToOss(path, file, item, uploadBatchId) {
|
||||
try {
|
||||
let res = await this.OSSclient.multipartUpload(
|
||||
{
|
||||
|
|
@ -884,6 +885,18 @@ export default {
|
|||
if (item.uploadFileSize > file.fileSize) {
|
||||
item.uploadFileSize = file.fileSize > 0 ? file.fileSize : 1
|
||||
}
|
||||
},
|
||||
{
|
||||
fileName: item.name,
|
||||
fileSize: item.size,
|
||||
fileType: item.fileType,
|
||||
uploadBatchId: uploadBatchId,
|
||||
batchDataType: 3,
|
||||
trialId: this.trialId,
|
||||
subjectId: this.data.SubjectId,
|
||||
subjectVisitId: this.subjectVisitId,
|
||||
studyCode: this.currentRow.CodeView
|
||||
|
||||
}
|
||||
)
|
||||
if (res) {
|
||||
|
|
|
|||
|
|
@ -361,6 +361,7 @@ export default {
|
|||
size: files[i].size,
|
||||
type: extendName.split('.')[1],
|
||||
file: files[i],
|
||||
fileType: files[i].type
|
||||
}
|
||||
this.fileList.push(obj)
|
||||
}
|
||||
|
|
@ -380,7 +381,17 @@ export default {
|
|||
var timestamp = Date.now()
|
||||
const res = await this.OSSclient.put(
|
||||
`/${this.trialId}/ClinicalData/${timestamp}_${this.fileList[i].file.name}`,
|
||||
file
|
||||
file,
|
||||
{
|
||||
fileName: `${this.fileList[i].file.name}`,
|
||||
fileSize: this.fileList[i].size,
|
||||
fileType: this.fileList[i].fileType,
|
||||
uploadBatchId: this.$guid(),
|
||||
batchDataType: 4,
|
||||
trialId: this.trialId,
|
||||
subjectId: this.data.SubjectId,
|
||||
subjectVisitId: this.subjectVisitId
|
||||
}
|
||||
)
|
||||
this.addFileList.push({
|
||||
fileName: this.fileList[i].file.name,
|
||||
|
|
|
|||
Loading…
Reference in New Issue