17 Commits

Author SHA1 Message Date
caiyiling 2303c04857 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-09-03 15:32:41 +08:00
caiyiling beeadfbdeb 非dicom阅片添加报告页面 2024-09-03 15:32:00 +08:00
DESKTOP-6C3NK6N\WXS b866ffdd49 iqc下载文件
continuous-integration/drone/push Build is passing
2024-09-03 15:23:22 +08:00
DESKTOP-6C3NK6N\WXS 8ad50b5618 上传网速检测修改 2024-09-03 15:22:44 +08:00
DESKTOP-6C3NK6N\WXS e517556808 crc上传dicom添加FileSize
continuous-integration/drone/push Build is passing
2024-09-03 14:12:39 +08:00
DESKTOP-6C3NK6N\WXS 1a8653b47e crc上传下载
continuous-integration/drone/push Build is passing
2024-09-03 13:15:50 +08:00
caiyiling bfe86e6868 项目列表详情按钮添加权限控制
continuous-integration/drone/push Build is running
2024-09-03 13:11:44 +08:00
caiyiling f0eda2bc66 预览图片修改
continuous-integration/drone/push Build is passing
2024-09-02 14:12:26 +08:00
caiyiling 8f274aad89 获取tag属性乱码优化
continuous-integration/drone/push Build is passing
2024-08-30 11:10:19 +08:00
caiyiling 709063a26e 在影像上传预览和质控预览工具中,增加DICOM标签查看工具
continuous-integration/drone/push Build is passing
2024-08-30 10:25:32 +08:00
caiyiling 9e786c53b8 1
continuous-integration/drone/push Build is passing
2024-08-28 15:46:59 +08:00
caiyiling 37520fccb6 稽查更改
continuous-integration/drone/push Build is passing
2024-08-28 15:35:19 +08:00
caiyiling f3b36b9127 稽查修改
continuous-integration/drone/push Build is passing
2024-08-28 15:06:15 +08:00
caiyiling 870354b4b2 稽查配置更改
continuous-integration/drone/push Build is passing
2024-08-28 11:40:18 +08:00
caiyiling f838df51a5 阅片单元配置附加评估逻辑及阅片问题验证更改
continuous-integration/drone/push Build is passing
2024-08-28 10:21:21 +08:00
caiyiling 0a6a98c33c 稽查国际化更改
continuous-integration/drone/push Build is passing
2024-08-27 10:21:59 +08:00
caiyiling 28c357a702 稽查配置更改
continuous-integration/drone/push Build is passing
2024-08-27 10:11:00 +08:00
39 changed files with 8371 additions and 2089 deletions
+1
View File
@@ -27,6 +27,7 @@
"axios": "0.18.1",
"babel-eslint": "7.2.3",
"copy-webpack-plugin": "^4.5.2",
"@aws-sdk/client-s3": "^3.370.0",
"core-js": "^3.8.3",
"cornerstone-core": "^2.6.1",
"cornerstone-math": "^0.1.10",
+8
View File
@@ -39,4 +39,12 @@ export function deleteTaskStudy(params) {
method: 'delete',
params
})
}
// 获取iqc下载文件信息
export function getCRCUploadedStudyInfo(data) {
return request({
url: '/DownloadAndUpload/getCRCUploadedStudyInfo',
method: 'post',
data
})
}
+31 -11
View File
@@ -81,16 +81,25 @@
<!-- <div v-show="stack.firstImageLoading" class="load-indicator">
Loading Series #{{ stack.seriesNumber }}...
</div>-->
<el-dialog
v-if="dcmTag.visible"
:visible.sync="dcmTag.visible"
:close-on-click-modal="false"
:title="dcmTag.title"
width="1000px"
custom-class="base-dialog-wrapper"
append-to-body
>
<DicomTags :image-data="imageData" @close="dcmTag.visible = false" />
</el-dialog>
</div>
</template>
<script>
import Vue from 'vue'
import Contextmenu from 'vue-contextmenujs'
Vue.use(Contextmenu)
import * as cornerstone from 'cornerstone-core'
import * as cornerstoneMath from 'cornerstone-math'
import * as cornerstoneTools from 'cornerstone-tools'
const scroll = cornerstoneTools.import('util/scrollToIndex')
import Hammer from 'hammerjs'
import getOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/getOrientationString'
@@ -108,8 +117,10 @@ cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)')
// cornerstoneTools.init({ showSVGCursors: true })
cornerstoneTools.init()
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
import DicomTags from './DicomTags'
export default {
name: 'DicomCanvas',
components: { DicomTags },
data() {
return {
loading: false,
@@ -164,7 +175,9 @@ export default {
mousePosition: { x: '', y: '', mo: '' },
markers: { top: '', right: '', bottom: '', left: '' },
orientationMarkers: [],
originalMarkers: []
originalMarkers: [],
dcmTag: { visible: false, title: 'DICOM Tags' },
imageData: null
}
},
@@ -215,7 +228,7 @@ export default {
cornerstoneTools.stopClip(this.canvas)
this.toolState.clipPlaying = false
this.loading = true
cornerstone.loadAndCacheImage(this.stack.imageIds[this.stack.currentImageIdIndex])
.then(image => {
this.loading = false
@@ -364,8 +377,8 @@ export default {
if (this.dicomInfo.thick) {
this.dicomInfo.thick = this.dicomInfo.thick.toFixed(2)
}
let newImageIdIndex = this.stack.imageIds.findIndex(i=>i===e.detail.image.imageId)
if(newImageIdIndex === -1) return
const newImageIdIndex = this.stack.imageIds.findIndex(i => i === e.detail.image.imageId)
if (newImageIdIndex === -1) return
this.stack.currentImageIdIndex = newImageIdIndex
this.stack.imageIdIndex = newImageIdIndex
this.series.imageIdIndex = newImageIdIndex
@@ -441,7 +454,7 @@ export default {
if (!markers) {
return
}
this.orientationMarkers = [oppositeColumn, row, column, oppositeRow]
this.originalMarkers = [oppositeColumn, row, column, oppositeRow]
this.setMarkers()
@@ -636,7 +649,7 @@ export default {
enabledElement.renderingTools.renderCanvasData = renderCanvasData
},
scrollPage(offset) {
if(this.loading) return
if (this.loading) return
var index = this.stack.currentImageIdIndex + offset
if (index < 0) index = 0
else if (index >= this.stack.imageIds.length) {
@@ -648,7 +661,7 @@ export default {
},
toggleClipPlay() {
if(this.loading) return
if (this.loading) return
if (this.toolState.clipPlaying) {
cornerstoneTools.stopClip(this.canvas)
this.toolState.clipPlaying = false
@@ -707,7 +720,7 @@ export default {
this.orientationMarkers = [...this.originalMarkers]
this.setMarkers()
}
var viewport = cornerstone.getViewport(this.canvas)
viewport.hflip = false
viewport.vflip = false
@@ -747,6 +760,13 @@ export default {
var uid = cornerstone.getImage(this.canvas).data.string('x00080018')
cornerstoneTools.SaveAs(this.canvas, `${uid}.png`)
},
showTags() {
var image = cornerstone.getImage(this.canvas)
// var dataSet = dicomParser.parseDicom(image.data.byteArray)
// console.log('showTags', dataSet)
this.dcmTag.visible = true
this.imageData = image.data
},
fitToWindow() {
if (this.stack.seriesNumber) {
cornerstone.fitToWindow(this.canvas)
+261
View File
@@ -0,0 +1,261 @@
<template>
<div class="dcm-tag">
<el-input
v-model="search"
size="mini"
placeholder="输入关键字搜索"
style="width:200px"
/>
<el-table
:data="filterList(list)"
row-key="id"
default-expand-all
:tree-props="{children: 'child', hasChildren: 'hasChildren'}"
:default-sort="{prop: 'tagCode', order: 'ascending'}"
height="500"
>
<el-table-column
prop="tagCode"
label="Tag"
min-width="120"
sortable
/>
<el-table-column
prop="tagName"
label="Description"
min-width="150"
show-overflow-tooltip
sortable
/>
<el-table-column
prop="vr"
label="VR"
min-width="50"
show-overflow-tooltip
sortable
/>
<el-table-column
prop="tagLength"
label="Length"
min-width="80"
show-overflow-tooltip
sortable
/>
<el-table-column
prop="value"
label="Value"
min-width="200"
show-overflow-tooltip
sortable
/>
</el-table>
</div>
</template>
<script>
import TAG_DICT from './dataDictionary'
import dicomParser from 'dicom-parser'
export default {
name: 'DicomTags',
props: {
imageData: {
type: Object,
default() {
return []
}
}
},
data() {
return {
list: [],
idx: 0,
search: ''
}
},
mounted() {
var dataSet = dicomParser.parseDicom(this.imageData.byteArray)
var output = []
this.dumpDataSet(dataSet, output)
this.list = output
},
methods: {
filterList(list) {
if (list.length === 0) return []
if (!this.search) {
return list
} else {
return list.filter(data => data.tagCode.toLowerCase().includes(this.search.toLowerCase()) || data.tagName.toLowerCase().includes(this.search.toLowerCase()) || (data.value && data.value.toLowerCase().includes(this.search.toLowerCase())))
}
},
dumpDataSet(dataSet, output) {
try {
for (const propertyName in dataSet.elements) {
const elementObject = {}
const element = dataSet.elements[propertyName]
const tag = this.getTag(element.tag)
elementObject.id = `${this.idx++}${new Date().getTime()}`
elementObject.tagCode = element.tag
elementObject.tagName = tag.name
elementObject.tagLength = element.length
elementObject.value = ''
if (element.vr) {
elementObject.vr = element.vr
}
elementObject.child = []
if (element.items) {
element.items.forEach(item => {
const childOutput = []
this.dumpDataSet(item.dataSet, childOutput)
elementObject.child.push(...childOutput)
})
} else if (element.fragments) {
// 多帧处理
} else {
var vr
if (element.vr !== undefined) {
vr = element.vr
}
if (element.length < 128) {
// const str = dataSet.string(propertyName)
// if (elementObject.tagCode === 'x00280010') {
// console.log(str)
// }
// const stringIsAscii = this.isASCII(str)
// if (stringIsAscii && str !== undefined) {
// elementObject.value = str
// }
if (element.vr === undefined && tag === undefined) {
if (element.length === 2) {
elementObject.value = dataSet.uint16(propertyName)
} else if (element.length === 4) {
elementObject.value = dataSet.uint32(propertyName)
}
const str = dataSet.string(propertyName)
const stringIsAscii = this.isASCII(str)
if (stringIsAscii) {
if (str !== undefined) {
elementObject.value = str
}
} else {
if (element.length !== 2 && element.length !== 4) {
// elementObject.value = 'binary data'
}
}
} else {
if (this.isStringVr(vr)) {
const str = dataSet.string(propertyName)
const stringIsAscii = this.isASCII(str)
if (stringIsAscii) {
if (str !== undefined) {
elementObject.value = str
}
} else {
if (element.length !== 2 && element.length !== 4) {
// elementObject.value = 'binary data'
}
}
} else if (vr === 'US') {
let text = dataSet.uint16(propertyName)
for (let i = 1; i < dataSet.elements[propertyName].length / 2; i++) {
text += '\\' + dataSet.uint16(propertyName, i)
}
elementObject.value = text
} else if (vr === 'SS') {
let text = dataSet.int16(propertyName)
for (let i = 1; i < dataSet.elements[propertyName].length / 2; i++) {
text += '\\' + dataSet.int16(propertyName, i)
}
elementObject.value = text
} else if (vr === 'UL') {
let text = dataSet.uint32(propertyName)
for (let i = 1; i < dataSet.elements[propertyName].length / 4; i++) {
text += '\\' + dataSet.uint32(propertyName, i)
}
elementObject.value = text
} else if (vr === 'SL') {
let text = dataSet.int32(propertyName)
for (let i = 1; i < dataSet.elements[propertyName].length / 4; i++) {
text += '\\' + dataSet.int32(propertyName, i)
}
elementObject.value = text
} else if (vr === 'FD') {
let text = dataSet.double(propertyName)
for (let i = 1; i < dataSet.elements[propertyName].length / 8; i++) {
text += '\\' + dataSet.double(propertyName, i)
}
elementObject.value = text
} else if (vr === 'FL') {
let text = dataSet.float(propertyName)
for (let i = 1; i < dataSet.elements[propertyName].length / 4; i++) {
text += '\\' + dataSet.float(propertyName, i)
}
elementObject.value = text
} else if (vr === 'OB' || vr === 'OW' || vr === 'UN' || vr === 'OF' || vr === 'UT') {
if (element.length === 2) {
elementObject.value = dataSet.uint16(propertyName)
} else if (element.length === 4) {
elementObject.value = dataSet.uint32(propertyName)
} else {
}
} else if (vr === 'AT') {
// var group = dataSet.uint16(propertyName, 0);
// var groupHexStr = ("0000" + group.toString(16)).substr(-4);
// var element = dataSet.uint16(propertyName, 1);
// var elementHexStr = ("0000" + element.toString(16)).substr(-4);
// text += "x" + groupHexStr + elementHexStr;
} else if (vr === 'SQ') {
} else {
// no display code for VR yet, sorry!
}
}
}
}
output.push(elementObject)
}
} catch (err) {
const ex = {
exception: err,
output: output
}
throw ex
}
},
getTag(tag) {
var group = tag.substring(1, 5)
var element = tag.substring(5, 9)
var tagIndex = ('(' + group + ',' + element + ')').toUpperCase()
var attr = TAG_DICT[tagIndex]
return attr
},
isASCII(str) {
return /^[\x00-\x7F]*$/.test(str)
},
isStringVr(vr) {
if (vr === 'AT' || vr === 'FL' || vr === 'FD' || vr === 'OB' || vr === 'OF' || vr === 'OW' || vr === 'SI' || vr === 'SQ' || vr === 'SS' || vr === 'UL' || vr === 'US') {
return false
}
return true
}
}
}
</script>
<style lang="scss" scoped>
.dcm-tag{
// user-select: none;
::-webkit-scrollbar {
width: 8px;
height: 15px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
}
</style>
+7 -3
View File
@@ -182,7 +182,7 @@
<svg-icon icon-class="fitToImage" style="font-size:20px;" />
</button>
<!-- <button title="旋转" class="btn-link dropdown" data-tool="Rotate" @click="setToolActive($event,'Rotate')"> -->
</div>
</div>
<!-- 测量标注 -->
@@ -233,6 +233,10 @@
<button :title="$t('trials:dicom-show:image')" class="btn-link" @click="currentDicomCanvas.saveImage()">
<svg-icon icon-class="image" style="font-size:20px;" />
</button>
<!-- 标签 -->
<button :title="$t('trials:dicom-show:tags')" class="btn-link" @click="currentDicomCanvas.showTags()">
<svg-icon icon-class="dictionary" style="font-size:20px;" />
</button>
</div>
</div>
<div class="measureTool-wrapper">
@@ -405,7 +409,7 @@ export default {
loadImageStack(dicomSeries) {
this.currentDicomCanvas.toolState.clipPlaying = false
this.$nextTick(() => {
let series = Object.assign({}, dicomSeries)
const series = Object.assign({}, dicomSeries)
this.currentDicomCanvas.loadImageStack(series)
})
},
@@ -416,7 +420,7 @@ export default {
Array.from(elements).forEach((element, index) => {
const canvasIndex = element.getAttribute('data-index')
if (index < seriesList.length && element.style.display !== 'none') {
let series = Object.assign({}, seriesList[index])
const series = Object.assign({}, seriesList[index])
this.$refs[`dicomCanvas${canvasIndex}`].loadImageStack(series)
}
})
File diff suppressed because it is too large Load Diff
+13 -16
View File
@@ -28,25 +28,22 @@ Vue.use(permission)
import Viewer from 'v-viewer'
import './assets/css/viewer.css'
Vue.use(Viewer)
Viewer.setDefaults({
Options: {
'inline': true,
'button': true,
'navbar': true,
'title': true,
'toolbar': true,
'tooltip': true,
'movable': true,
'zoomable': true,
'rotatable': true,
'scalable': true,
'transition': true,
'keyboard': true,
'url': 'data-source'
// navbar: true, //底部缩略图
toolbar: {
zoomIn: true,
zoomOut: true,
reset: true,
prev: true,
next: true,
rotateLeft: true,
rotateRight: true,
flipHorizontal: true,
flipVertical: true,
}
})
Vue.use(Viewer)
import hasPermi from './directive/permission'
Vue.use(hasPermi)
+1 -1
View File
@@ -9,7 +9,7 @@ const getDefaultState = () => {
studyListQuery: null,
unlock: false,
config: {},
uploadTip: null,
uploadTip: '0.00kb/s',
timer: null,
whiteList: [],
checkTaskId: null
+5 -5
View File
@@ -1,14 +1,14 @@
import Vue from 'vue'
import { anonymization } from './anonymization'
export const dcmUpload = async function (name, file, config) {
export const dcmUpload = async function (data, config, progressFn) {
return new Promise(async resolve => {
try {
// let blob = await encoder(file, config)
let blob = await fileToBlob(file)
let blob = await fileToBlob(data.file)
if (config) {
blob = await anonymization(file, config)
blob = await anonymization(data.file, config)
}
let res = await Vue.prototype.OSSclient.put(name, blob.blob)
let res = await Vue.prototype.OSSclient.multipartUpload(Object.assign(data, { file: blob.blob }), progressFn)
resolve({
...res,
image: blob.pixelDataElement
@@ -19,7 +19,7 @@ export const dcmUpload = async function (name, file, config) {
} catch (e) {
console.log(file, 'warning')
console.log(data.file, 'warning')
resolve(false)
console.log(e)
}
+459
View File
@@ -0,0 +1,459 @@
const {
CreateMultipartUploadCommand,
UploadPartCommand,
CompleteMultipartUploadCommand,
ListMultipartUploadsCommand,//bucket中正在上传的文件列表
ListPartsCommand,//列出文件已上传的分片
GetObjectCommand,//获取文件
} = require("@aws-sdk/client-s3");
import SparkMD5 from "./spark-md5.min.js";
import store from "@/store";
let timer = null, // 网速定时器
bytesReceivedPerSecond = {}; // 时间节点上传文件总量
export function AWSclose() {
if (timer) {
clearInterval(timer);
timer = null;
store.state.trials.uploadTip = '0kb/s'
}
bytesReceivedPerSecond = {};
}
//查询文件是否存在于bucket或者正在上传
export async function exist(s3, bucket, fileInformation, progressFn, changeStatus) {
// 拿到上传到的file
const File = fileInformation.file;
// 拿到上传的size
const uploadFileSize = File.size; // 这里拿到的单位是字节(uploadFileSize/ 1024 / 1024
// = 多少兆)
// 设置每一片的大小,shardSize 指定上传的每个分片的大小,范围为100 KB~5 GB。
// 分片标准为5MB,文件总大小大于5GB分片为20MB
let shardSize = 5 * 1024 * 1024;
if (uploadFileSize < partSize) {
shardSize = uploadFileSize;
}
if (uploadFileSize > 5 * 1024 * 1024 * 1024) {
shardSize = 20 * 1024 * 1024;
}
fileInformation = Object.assign({
shardSize,
sharding: []
}, fileInformation)
if (fileInformation.speed) {
setTimer();
}
// 1、查询该文件是否已上传到bucket
//判断sharding里面是否有东西,有东西证明已经上传过分片了,不需要再进行检测
if (fileInformation.sharding.length === 0) {
let existBucket = await existInBucket({ s3, bucket, fileInformation: fileInformation });
console.log("existBucket", existBucket)
if (existBucket === 'true') {
changeStatus(fileInformation.path, 'success');//直接告诉前端,状态
return;
} else if (existBucket === 'same key') {
console.log(fileInformation.path + " bucket中存在同名不同内容的文件");
} else if (existBucket === 'not exist') {
console.log(fileInformation.path + " bucket中不存在该文件");
}
//2、查询该文件是否存在上传事件
let upload = await existUpload({ s3, bucket: bucket, fileInformation: fileInformation });
if (upload.code === 0) {
//存在该上传事件并且已经上传了多个分片
console.log(fileInformation.path + " 存在上传事件,并已经上传多个分片");
//将分片存入sharding
const uploadId = upload.uploadId;
let parts = upload.parts;
let SIZE = 0;
for (let i = 0; i < parts.length; i++) {
SIZE += parts[i].Size;
fileInformation.sharding.push({ ETag: parts[i].ETag, PartNumber: parts[i].PartNumber, Size: parts[i].Size, UploadId: uploadId });
}
progressFn(SIZE / uploadFileSize, { fileSize: uploadFileSize }, 0);//告诉前端,加入分片
//重新上传
await uploadFile({ fileInformation: fileInformation, uploadId: uploadId, bucket, changeStatus, getSuspend, progressFn });
} else if (upload.code === 1) {
// //重名但是不同文件
console.log('err 重名文件')
changeStatus(fileInformation.path, 'same key');
} else if (upload.code === 2) {
//没有上传事件
console.log(fileInformation.path + " 不存在上传事件");
//建立分段上传事件
const connect = await createMultipartUpload({ s3, bucket: bucket, key: fileInformation.path, type: fileInformation.file.type });
//上传整个文件
await uploadFile({ s3, fileInformation: fileInformation, uploadId: connect.UploadId, bucket: bucket, changeStatus, progressFn });
}
} else {
//分片组里面有东西
//重新上传
await uploadFile({ s3, fileInformation: fileInformation, uploadId: fileInformation.sharding[0].UploadId, bucket, changeStatus, progressFn });
}
}
//上传文件未上传的所有分片
async function uploadFile({ s3, fileInformation, uploadId, bucket, changeStatus, progressFn }) {// file:上传文件, uploadId parts:已上传的分片
const chunkCount = Math.ceil(fileInformation.file.size / fileInformation.shardSize)//总分片数
//循环切片并上传
for (let i = 0; i < chunkCount; i++) {
let start = i * fileInformation.shardSize;//文件分片开始位置
let end = Math.min(fileInformation.file.size, start + fileInformation.shardSize)//文件分片结束位置
let _chunkFile = fileInformation.file.slice(start, end);//切片文件 即 待上传文件分片
//判断parts中是否存在该分片
let res1 = fileInformation.sharding.filter((part) => {
return part.PartNumber === (i + 1);
});
if (res1.length === 0) {
//不包含该分片
const upload = await uploadPart({ s3, f: _chunkFile, uploadId: uploadId, key: fileInformation.path, bucket: bucket, num: i + 1 });//将分片上传
//判断sharding中是否存在该分片,如果不存在的话,才判错
let res2 = fileInformation.sharding.filter((part) => {
return part.PartNumber === (i + 1);
});
if (res2.length === 0) {
if (upload !== 'err') {//上传分片成功,并且没有暂停上传
//判断是否存在该分片
//判断parts中是否存在该分片
let res3 = fileInformation.sharding.filter((part) => {
return part.PartNumber === (i + 1);
});
if (res3.length === 0) {
let LASTSIZE = fileInformation.sharding.reduce((sum, item) => sum + item.Size, 0)
fileInformation.sharding.push({ ETag: upload.ETag, PartNumber: i + 1, Size: _chunkFile.size, UploadId: uploadId });//上传成功,存到sharding
let SIZE = fileInformation.sharding.reduce((sum, item) => sum + item.Size, 0)
let lastPercentage = LASTSIZE / fileInformation.file.size, percentage = SIZE / fileInformation.file.size;
progressFn(percentage, fileInformation.file, lastPercentage);
if (fileInformation.speed) {
let time = new Date().getTime();
let timeList = Object.keys(bytesReceivedPerSecond).sort((a, b) => a - b);
let bytesTime = timeList.find(item => time - item < 1000);
if (bytesTime) {
bytesReceivedPerSecond[bytesTime] += fileInformation.file.size * percentage;
} else {
console.log("未查询到时间")
if (timeList.length > 0) {
bytesReceivedPerSecond[timeList[timeList.length - 1]] += fileInformation.file.size * percentage;
} else {
bytesReceivedPerSecond[time] = fileInformation.file.size * percentage;
}
}
}
}
} else if (upload === 'err') {
changeStatus(fileInformation.path, 'err');
return;
}
}
}
}//for
if (fileInformation.sharding.length === chunkCount) {
//合并分片
const complete = await completeMultipartUpload({ s3, bucket: bucket, key: fileInformation.path, sharding: fileInformation.sharding, uploadId: uploadId });
if (complete !== 'err') {
changeStatus(fileInformation.path, 'success');//通知前端,上传成功
} else {
changeStatus(fileInformation.path, 'err');//通知前端,上传失败
}
}
}
// 判断该文件是否已经存在于bucket
// bucket file:上传文件
// 返回值 'same key':同名不同文件 'not exist':不存在该文件 'true':该文件已存在bucket中
async function existInBucket({ s3, bucket, fileInformation }) {
if (s3 === null) {
return console.log("未创建s3客户端,请先调用init事件");
}
//getObject 每次最多传回767448b的数据,所以要分段请求
let bucketFileUniArray = [];
// 分段
let count = Math.ceil(fileInformation.file.size / 767448);
if (count > 4) {
count = 4;
}
for (let i = 0; i < count; i++) {
const obj = await getObject({ s3, bucket: bucket, fileInformation: fileInformation, count: i });
if (obj !== 'err') {
//获取文件的文件体 计算某个分片的md5
const fileBody = obj.Body;
let fileUnitArray = await fileBody.transformToByteArray();
bucketFileUniArray = [...bucketFileUniArray, ...fileUnitArray];
} else {
return 'not exist';
}
}
let bucketFileBufferArray = new Uint8Array(bucketFileUniArray);
console.log("bucketFileBufferArray.buffer", bucketFileBufferArray.buffer)
// 将传入文件的fileReader 转成 arrayBuffer
let fileArrayBuff = null;
fileArrayBuff = await new Promise((resolve) => {
let fileReader = new FileReader();
fileReader.readAsArrayBuffer(fileInformation.file.slice(0, count * 767448));
fileReader.onload = (e) => {
resolve(e.target.result);
};
});
if (fileArrayBuff.byteLength > count * 767448) {
fileArrayBuff = fileArrayBuff.slice(0, count * 767448);
}
let bodyMD5 = await getMD5({ arrayBuffer: bucketFileBufferArray.buffer });
let fileMD5 = await getMD5({ arrayBuffer: fileArrayBuff });
if (bodyMD5 === fileMD5) {
//证明是同一个文件 秒传
return 'true';
} else {
return 'same key';
}
}
//判断该文件是否正在上传
// bucket:bucket file:上传文件
//返回值 'not exist upload':不存在上传事件 'same key':同名不同文件
async function existUpload({ s3, bucket, fileInformation }) {
//判断该文件是否有上传事件
const listUploads = await listMultipartUploadsCommand({ s3, bucket: bucket, key: fileInformation.path });
if (listUploads !== 'err') {
if (listUploads.Uploads !== undefined && listUploads.Uploads.length > 0) {
//存在上传事件 获取上传的第一个分片的eTag,计算传入文件md5,相比较是否相同
const uploads = listUploads.Uploads;
for (const one in uploads) {//可能存在多个连接
let uploadOne = uploads[one];
const uploadId = uploadOne.UploadId;//UploadId
const key = uploadOne.Key;//key
//查询该文件已上传分片
const listParts = await listPartsCommand({ s3, bucket: bucket, key: key, uploadId: uploadId });
if (listParts !== 'err') {
if (listParts.Parts !== undefined && listParts.Parts.length !== 0) {
//存在分片
let etag = listParts.Parts[0].ETag;
//计算文件的第一个分片的md5
let fileSlice = null;
if (fileInformation.file.size > fileInformation.shardSize) {
fileSlice = fileInformation.file.slice(0, fileInformation.shardSize);
} else {
fileSlice = fileInformation.file;
}
let fileMD5 = await new Promise((resolve) => {
const fileReader = new FileReader();
var spark = new SparkMD5.ArrayBuffer();
fileReader.readAsArrayBuffer(fileSlice);
fileReader.onload = (e) => {
spark.append(e.target.result);
var m = spark.end();
resolve(m);
};
});
if (etag.split('"')[1] === fileMD5) {
//是同一个文件上传
return {
code: 0,
message: 'true',
uploadId: uploadId,
key: key,
parts: listParts.Parts
}
} else {
//同名不同文件
continue;
}
} else {
//该文件有进行上传,但没有上传完成一个分片
continue;
}
} else {
//有连接,没上传分片
continue;
}
}//for
return {
code: 1,
message: 'same key'
}
} else {
//无连接
return {
code: 2,
message: 'not exist upload'
};
}
} else {
//无连接
return {
code: 2,
message: 'not exist upload'
};
}
}
//计算arrayBuffer的md5值
async function getMD5({ arrayBuffer }) {
console.log("arrayBuffer", arrayBuffer)
return await new Promise((resolve) => {
const spark = new SparkMD5.ArrayBuffer();
spark.append(arrayBuffer);
const m = spark.end();
resolve(m);
});
}
//建立文件上传事件
async function createMultipartUpload({ s3, bucket, key, type }) {//bucket:bucket key:文件名 type:文件类型
if (s3 === null) {
return console.log("未创建s3客户端,请先调用init事件");
}
const params = {
Bucket: bucket,
Key: key,
ContentType: type
};
const res = async () => {
try {
return await s3.send(new CreateMultipartUploadCommand(params));
} catch (err) {
console.log('建立上传事件失败:', err.message)
return 'err';
}
}
return res();
}
//上传一个分片
async function uploadPart({ s3, f, uploadId, key, bucket, num }) { //f:文件分片,num:分片标号
if (s3 === null) {
return console.log("未创建s3客户端,请先调用init事件");
}
const params = {
Bucket: bucket,
Key: key,
PartNumber: num,
UploadId: uploadId,
Body: f,
// ContentDisposition: "attachment; filename=hahaha.dcm"
};
const res = async () => {
try {
return await s3.send(new UploadPartCommand(params));
} catch (err) {
console.log('上传分片第 ' + num + ' 片错误信息', err.message)
return 'err';
}
}
return res();
}
//将分片合并
async function completeMultipartUpload({ s3, bucket, key, sharding, uploadId }) {
if (s3 === null) {
return console.log("未创建s3客户端,请先调用init事件");
}
let parts = [];
for (let i = 0; i < sharding.length; i++) {
parts.push({
"ETag": sharding[i].ETag,
"PartNumber": sharding[i].PartNumber,
})
}
const params = {
Bucket: bucket,
Key: key,
MultipartUpload: {
Parts: parts
},
UploadId: uploadId
};
const res = async () => {
try {
return await s3.send(new CompleteMultipartUploadCommand(params))
} catch (err) {
console.log("合并分片失败: ", err.message);
return 'err';
}
}
return res();
}
//查询某个文件已经上传的所有分片
async function listPartsCommand({ s3, bucket, key, uploadId }) {
if (s3 === null) {
return console.log("未创建s3客户端,请先调用init事件");
}
const params = {
Bucket: bucket,
Key: key,
UploadId: uploadId
};
const res = async () => {
try {
return await s3.send(new ListPartsCommand(params));
} catch (err) {
console.log("查询该文件已上传分片失败: " + err.message);
return 'err';
}
}
return res();
}
//查询该文件是否存在上传事件
async function listMultipartUploadsCommand({ s3, bucket, key }) {
if (s3 === null) {
return console.log("未创建s3客户端,请先调用init事件");
}
const params = {
Bucket: bucket,
Delimiter: '',
MaxUploads: 1000,
Prefix: key
};
const res = async () => {
try {
return await s3.send(new ListMultipartUploadsCommand(params));
} catch (err) {
console.log("查询 " + key + " 文件是否存在上传事件失败: " + err.message);
return 'err';
}
}
return res();
}
//获取文件
async function getObject({ s3, bucket, fileInformation, count }) {
//一次请求最多 767448
if (s3 === null) {
return console.log("未创建s3客户端,请先调用init事件");
}
let byte1 = ((count + 1) * 767448 - 1) > fileInformation.file.size ? fileInformation.file.size : ((count + 1) * 767448 - 1);
let byte2 = (count * 767448) > fileInformation.file.size ? fileInformation.file.size : (count * 767448);
let range = "bytes=" + byte2 + "-" + byte1;
const params = {
Bucket: bucket,
Key: fileInformation.path,
Range: range
};
const res = async () => {
try {
return await s3.send(new GetObjectCommand(params));
} catch (err) {
console.log('获取 ' + fileInformation.path + ' 文件失败:', err.message);
return 'err';
}
}
return res();
}
function setTimer() {
if (timer) return false;
timer = setInterval(() => {
let timeList = Object.keys(bytesReceivedPerSecond).sort((a, b) => a - b);
if (timeList.length > 0) {
let totalBytes = timeList.reduce((sum, bytes) => sum + bytesReceivedPerSecond[bytes], 0) / (5 * 1024);
let unit = 'kb/s';
if (totalBytes > 1024) {
totalBytes = totalBytes / 1024;
unit = "mb/s";
}
store.state.trials.uploadTip = totalBytes.toFixed(2) + unit;
}
if (timeList.length >= 5) {
delete bytesReceivedPerSecond[timeList[0]]
}
let time = new Date().getTime();
bytesReceivedPerSecond[time] = 0;
}, 1000)
}
+155
View File
@@ -0,0 +1,155 @@
import moment from "moment";
import store from "@/store";
let savaData = {},
checkData = {}, // 当前上传的节点文件和上一次提交进度
timer = null, // 网速定时器
bytesReceivedPerSecond = {}; // 时间节点上传文件总量
export function OSSclose() {
if (timer) {
clearInterval(timer);
timer = null;
store.state.trials.uploadTip = '0kb/s'
}
bytesReceivedPerSecond = {};
savaData = {};
checkData = {};
saveFinishedData(savaData);
}
export async function customerHttp(OSSclient, data, progressFn) {
// 拿到上传到的file
const uploadFile = data.file;
// 拿到上传的size
const uploadFileSize = uploadFile.size; // 这里拿到的单位是字节(uploadFileSize/ 1024 / 1024
// = 多少兆)
// 设置每一片的大小,partSize 指定上传的每个分片的大小,范围为100 KB~5 GB。
// 分片标准为5MB,文件总大小大于5GB分片为20MB
let partSize = 5 * 1024 * 1024;
if (uploadFileSize < partSize) {
partSize = uploadFileSize;
}
if (uploadFileSize > 5 * 1024 * 1024 * 1024) {
partSize = 20 * 1024 * 1024;
}
// 设置所有的文件上传所有的唯一的saveFileId
const saveFileId = `${uploadFileSize}_${data.path}`;
if (data.speed) {
setTimer();
}
initPage();
let res = await multipartUpload(OSSclient, partSize, saveFileId, uploadFile, data, progressFn);
return res;
}
async function multipartUpload(OSSclient, partSize, saveFileId, uploadFile, data, progressFn) {
try {
// object-name目前我是用的uploadFile.name,其实也是要根据你们的项目而定,
// 有没有具体的规定, 要不要加项目名, 要不要加对应的环境;
// 上传的参数
const uploadParams = {
partSize,
progress: (percentage, checkpoint) => {
savaData[saveFileId] = checkpoint;
if (!checkData[saveFileId]) {
checkData[saveFileId] = 0
}
if (data.speed) {
let time = new Date().getTime();
let timeList = Object.keys(bytesReceivedPerSecond).sort((a, b) => a - b);
let bytesTime = timeList.find(item => time - item < 1000);
if (bytesTime) {
bytesReceivedPerSecond[bytesTime] += data.file.size * percentage;
} else {
console.log("未查询到时间")
if (timeList.length > 0) {
bytesReceivedPerSecond[timeList[timeList.length - 1]] += data.file.size * percentage;
} else {
bytesReceivedPerSecond[time] = data.file.size * percentage;
}
}
}
savaData["lastSaveTime"] = new Date();
progressFn(percentage, data.file, checkData[saveFileId])
checkData[saveFileId] = percentage;
if (percentage === 1) {
delete checkData[saveFileId]
}
// 在上传过程中,把已经上传的数据存储下来
saveFinishedData(savaData);
},
// headers: {
// "Content-Disposition": `attachment; filename=hahaha.dcm`,
// "Cache-Control": "public, no-cache"
// }
};
// 断点续传
await resumeUpload(uploadParams, saveFileId);
const res = await OSSclient.multipartUpload(
data.path,
uploadFile,
uploadParams
);
if (res.res.status === 200) {
// 重新去掉某个缓存进行设置
delete savaData[saveFileId];
saveFinishedData(savaData);
}
return res;
} catch (e) {
console.log(e);
// 捕获超时异常。
if (e.code === "ConnectionTimeoutError") {
console.log("TimeoutError");
// do ConnectionTimeoutError operation
}
return false;
}
}
async function resumeUpload(uploadParams, saveFileId) {
if (localStorage.getItem("upload-function-name")) {
const obj = JSON.parse(localStorage.getItem("upload-function-name"));
if (Object.keys(obj).includes(saveFileId)) {
uploadParams.checkpoint = obj[saveFileId];
}
}
}
// 存储到内存
function saveFinishedData(finishedData) {
localStorage.setItem(
"upload-function-name",
JSON.stringify(finishedData)
);
}
function initPage() {
// 判断是不是有缓存
const localData = localStorage.getItem("upload-function-name");
if (!localData) return;
savaData = JSON.parse(localData);
// 当前时间 > 存储时间(1000 * 60 * 60表示1h,意思就是这些数据你要存多久,
// 可以是1h也可以是多少天,随意)
if (
moment(new Date()).diff(moment(savaData.lastSaveTime)) >
1000 * 60 * 60
) {
localStorage.removeItem("upload-function-name");
}
}
function setTimer() {
if (timer) return false;
timer = setInterval(() => {
let timeList = Object.keys(bytesReceivedPerSecond).sort((a, b) => a - b);
if (timeList.length > 0) {
let totalBytes = timeList.reduce((sum, bytes) => sum + bytesReceivedPerSecond[bytes], 0) / (5 * 1024);
let unit = 'kb/s';
if (totalBytes > 1024) {
totalBytes = totalBytes / 1024;
unit = "mb/s";
}
store.state.trials.uploadTip = totalBytes.toFixed(2) + unit;
}
if (timeList.length >= 5) {
delete bytesReceivedPerSecond[timeList[0]]
}
let time = new Date().getTime();
bytesReceivedPerSecond[time] = 0;
}, 1000)
}
File diff suppressed because one or more lines are too long
+140 -6
View File
@@ -3,17 +3,16 @@ const router = require('@/router');
const Minio = require('minio')
const stream = require('stream')
import Vue from 'vue'
import { customerHttp, OSSclose } from "@/utils/multipartUpload/oss"
import { exist, AWSclose } from "@/utils/multipartUpload/aws"
const { GetObjectStoreToken } = require('../api/user.js')
const {
S3Client,
} = require("@aws-sdk/client-s3");
Vue.prototype.OSSclientConfig = {
}
function blobToBuffer(blob, fileName) {
return new Promise((resolve, reject) => {
const file = new File([blob], fileName);
resolve(file)
})
}
async function ossGenerateSTS() {
let res = await GetObjectStoreToken()
@@ -23,12 +22,23 @@ async function ossGenerateSTS() {
switch (res.Result.ObjectStoreUse) {
case 'AliyunOSS':
Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
Vue.prototype.OSSclientConfig.stsToken = Vue.prototype.OSSclientConfig.securityToken
Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
let OSSclient = new OSS(Vue.prototype.OSSclientConfig)
Vue.prototype.OSSclient = {
put: function (objectName, object) {
return new Promise(async (resolve, reject) => {
try {
let config = await getSTSToken(Vue.prototype.OSSclientConfig.Expiration);
if (config) {
Vue.prototype.OSSclientConfig = { ...config.Result[res.Result.ObjectStoreUse] }
Vue.prototype.OSSclientConfig.ObjectStoreUse = config.Result.ObjectStoreUse;
Vue.prototype.OSSclientConfig.basePath = Vue.prototype.OSSclientConfig.viewEndpoint;
Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
Vue.prototype.OSSclientConfig.stsToken = Vue.prototype.OSSclientConfig.securityToken
Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
OSSclient = new OSS(Vue.prototype.OSSclientConfig);
}
let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
var objectItem = objectName.split('/')
@@ -49,6 +59,45 @@ async function ossGenerateSTS() {
reject()
}
})
},
multipartUpload: (data, progress) => {
return new Promise(async (resolve, reject) => {
try {
const { file, path } = data;
if (!file || !path) return reject('file and path be required');
let config = await getSTSToken(Vue.prototype.OSSclientConfig.Expiration);
if (config) {
Vue.prototype.OSSclientConfig = { ...config.Result[res.Result.ObjectStoreUse] }
Vue.prototype.OSSclientConfig.ObjectStoreUse = config.Result.ObjectStoreUse;
Vue.prototype.OSSclientConfig.basePath = Vue.prototype.OSSclientConfig.viewEndpoint;
Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
Vue.prototype.OSSclientConfig.stsToken = Vue.prototype.OSSclientConfig.securityToken
Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
OSSclient = new OSS(Vue.prototype.OSSclientConfig);
}
let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
var objectItem = data.path.split('/')
objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
data.path = objectItem.join('/')
}
let res = await customerHttp(OSSclient, data, progress);
if (res) {
resolve({
name: data.path,
url: Vue.prototype.OSSclientConfig.viewEndpoint + res.name
})
} else {
reject()
}
} catch (err) {
console.log(err)
reject(err)
}
})
},
close: () => {
OSSclose();
}
}
break
@@ -86,6 +135,9 @@ async function ossGenerateSTS() {
console.log(e)
}
})
},
close: () => {
return false
}
}
break
@@ -124,6 +176,47 @@ async function ossGenerateSTS() {
console.log(e)
}
})
},
multipartUpload: (data, progress) => {
return new Promise(async (resolve, reject) => {
try {
const { file, path } = data;
if (!file || !path) return reject('file and path be required');
let config = await getSTSToken(Vue.prototype.OSSclientConfig.Expiration);
if (config) {
Vue.prototype.OSSclientConfig = { ...config.Result[res.Result.ObjectStoreUse] }
Vue.prototype.OSSclientConfig.ObjectStoreUse = config.Result.ObjectStoreUse;
Vue.prototype.OSSclientConfig.basePath = Vue.prototype.OSSclientConfig.viewEndpoint;
Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
Vue.prototype.OSSclientConfig.stsToken = Vue.prototype.OSSclientConfig.securityToken
Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
OSSclient = new S3Client(Vue.prototype.OSSclientConfig);
}
let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
var objectItem = data.path.split('/')
objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
data.path = objectItem.join('/')
}
await exist(OSSclient, Vue.prototype.OSSclientConfig.bucket, data, progress, (res) => {
if (res) {
resolve({
name: data.path,
url: Vue.prototype.OSSclientConfig.viewEndpoint + res.name
})
} else {
reject()
}
});
} catch (err) {
console.log(err)
reject(err)
}
})
},
close: () => {
AWSclose();
}
}
}
@@ -138,6 +231,47 @@ function decodeUtf8(bytes) {
str2.pop();
return str2.join("/") + '/' + name;
}
const queue = []
let loading = false;
// 获取凭证
function getSTSToken(credentials) {
return new Promise(async (resolve, reject) => {
let isExpired = isCredentialsExpired(credentials);
if (!isExpired) {
if (loading) {
queue.push({ resolve, reject })
}
if (!loading) {
loading = true;
let res = await GetObjectStoreToken();
loading = false;
resolve(res)
let p = queue.shift();
while (p) {
p.resolve(res)
p = queue.shift();
}
}
} else {
console.log("凭证未过期");
resolve(false)
}
})
}
/**
* oss判断临时凭证是否到期
**/
function isCredentialsExpired(credentials) {
if (!credentials) {
return true;
}
const expireDate = new Date(credentials.Expiration);
const now = new Date();
// 如果有效期不足五分钟,视为过期。
return expireDate.getTime() - now.getTime() <= 300000;
}
export const OSSclient = ossGenerateSTS
+48
View File
@@ -0,0 +1,48 @@
import streamSaver from "streamsaver";
import "streamsaver/examples/zip-stream.js";
// 下载文件并压缩
function zipFiles(zipName, files) {
console.log("同步下载打包开始时间:" + new Date());
// 创建压缩文件输出流
const zipFileOutputStream = streamSaver.createWriteStream(zipName);
// 创建下载文件流
const fileIterator = files.values();
const readableZipStream = new ZIP({
async pull(ctrl) {
const fileInfo = fileIterator.next();
if (fileInfo.done) {//迭代终止
ctrl.close();
} else {
const { name, url } = fileInfo.value;
return fetch(url).then(res => {
ctrl.enqueue({
name,
stream: () => res.body
});
})
}
}
});
if (window.WritableStream && readableZipStream.pipeTo) {
// 开始下载
readableZipStream
.pipeTo(zipFileOutputStream)
.then(() => console.log("同步下载打包结束时间:" + new Date()));
}
}
// 下载文件并修改名称
async function updateFile(file, name) {
try {
const fileOutputStream = streamSaver.createWriteStream(name);
let res = await fetch(file);
res.body.pipeTo(fileOutputStream);
} catch (err) {
console.log(err)
}
}
export function downLoadFile(file, name, type = 'file') {
if (type === 'zip') return zipFiles(name, file);
return updateFile(file, name)
}
+50 -2
View File
@@ -49,7 +49,7 @@
<el-table-column prop="Identification" label="标识" :show-overflow-tooltip="true" min-width="200px" />
<el-table-column prop="OptTypeValueCN" label="操作类型" show-overflow-tooltip width="100px" />
<el-table-column prop="ObjectTypeValueCN" label="对象类型" show-overflow-tooltip min-width="100px" />
<el-table-column prop="ChildrenTypeValueCN" label="数据类型" show-overflow-tooltip min-width="100px" show-overflow-tooltip />
<el-table-column prop="ChildrenTypeValueCN" label="数据类型" show-overflow-tooltip min-width="100px"/>
<el-table-column
prop="Sort"
label="显示顺序"
@@ -110,7 +110,7 @@
<!-- 添加或修改菜单对话框 -->
<el-dialog :title="title" top="100px" :close-on-click-modal="false" id="check_config" :visible.sync="open" :width="form.DataType === 'Table' ? '1280px' : '680px'" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-row>
<el-col v-show="title !== '复制'" :span="24">
<el-divider content-position="left">基本信息</el-divider>
@@ -372,6 +372,16 @@
<el-input :disabled="!scope.row.IsFixedColumn" v-model="scope.row.FixedColumnName" placeholder="固定列名"/>
</template>
</el-table-column>
<el-table-column
prop="FixedColumnEnName"
min-width="120"
label="固定列名EN"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-input :disabled="!scope.row.IsFixedColumn" v-model="scope.row.FixedColumnEnName" placeholder="固定列名EN"/>
</template>
</el-table-column>
<el-table-column
prop="ColumnName"
min-width="120"
@@ -382,6 +392,16 @@
<el-input :disabled="scope.row.IsFixedColumn" v-model="scope.row.ColumnName" placeholder="列字段名"/>
</template>
</el-table-column>
<el-table-column
prop="ColumnEnName"
min-width="120"
label="列字段名En"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-input :disabled="scope.row.IsFixedColumn" v-model="scope.row.ColumnEnName" placeholder="列字段名En"/>
</template>
</el-table-column>
<el-table-column
prop="ColumnValue"
min-width="120"
@@ -415,6 +435,16 @@
<el-input :disabled="!scope.row.IsMerge" v-model="scope.row.MergeColumnName" placeholder="合并组"/>
</template>
</el-table-column>
<el-table-column
prop="MergeColumnEnName"
min-width="120"
label="合并组EN"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-input :disabled="!scope.row.IsMerge" v-model="scope.row.MergeColumnEnName" placeholder="合并组EN"/>
</template>
</el-table-column>
<el-table-column
prop="IsPicture"
min-width="120"
@@ -489,6 +519,14 @@
<el-input v-model="form.ChildDataLabel" placeholder="请输入子数据Lable" />
</el-form-item>
</el-col>
<el-col v-show="form.ConfigType === 'C' && title !== '复制' && form.DataType === 'Array'" :span="12">
<el-form-item>
<span slot="label">
子数据LableEN
</span>
<el-input v-model="form.ChildDataEnLabel" placeholder="请输入子数据LableEN" />
</el-form-item>
</el-col>
<el-col v-show="form.ConfigType === 'C' && title !== '复制' && form.DataType === 'Array'" :span="12">
<el-form-item>
<span slot="label">
@@ -559,6 +597,11 @@
<el-input v-model="form.ForeignKeyText" placeholder="请输入数据库字段to" />
</el-form-item>
</el-col>
<el-col v-show="form.ConfigType === 'C' && title !== '复制' && form.EnumType === 'Foreign'" :span="12">
<el-form-item label="字段toEN">
<el-input v-model="form.ForeignKeyEnText" placeholder="请输入数据库字段toEN" />
</el-form-item>
</el-col>
</el-row>
</el-col>
<el-col v-show="title !== '复制'" :span="24">
@@ -821,10 +864,13 @@ export default {
ListName: null,
IsFixedColumn: false,
FixedColumnName: null,
FixedColumnEnName: null,
ColumnName: null,
ColumnEnName: null,
ColumnValue: null,
IsMerge: false,
MergeColumnName: null,
MergeColumnEnName: null,
IsPicture: false,
IsDynamicTranslate: false,
IsNeedTransalate: false,
@@ -1111,6 +1157,7 @@ export default {
IsSpecialType: false,
DataType: '',
ChildDataLabel: null,
ChildDataEnLabel: null,
ChildDataValue: null,
DateType: null,
DictionaryCode: null,
@@ -1118,6 +1165,7 @@ export default {
ForeignKeyTableName: null,
ForeignKeyValue: null,
ForeignKeyText: null,
ForeignKeyEnText: null,
TableConfigList: [],
UrlConfig: {
RoutePath: null,
+140 -260
View File
@@ -41,8 +41,8 @@
</el-form-item>
<!-- 阅片标准 -->
<el-form-item
:label="$t('trials:trials-list:table:IR_ReadingCriterionList')"
v-if="hasPermi(['role:ir'])"
:label="$t('trials:trials-list:table:IR_ReadingCriterionList')"
>
<el-select
v-model="searchData.CriterionType"
@@ -59,8 +59,8 @@
</el-form-item>
<!-- 联系人 -->
<el-form-item
:label="$t('trials:trials-list:table:IR_PMEmailList')"
v-if="hasPermi(['role:ir'])"
:label="$t('trials:trials-list:table:IR_PMEmailList')"
>
<el-input
v-model="searchData.PM_EMail"
@@ -293,9 +293,10 @@
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSelectSearch"
>Search</el-button
>
<el-button
type="primary"
@click="handleSelectSearch"
>Search</el-button>
<el-button type="primary" @click="handleReset">Reset</el-button>
<el-button type="primary" @click="isShow = false">Back</el-button>
</el-form-item>
@@ -357,23 +358,19 @@
<el-tag
v-if="scope.row.TrialStatusStr === 'Initializing'"
type="info"
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag
>
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag>
<el-tag
v-if="scope.row.TrialStatusStr === 'Ongoing'"
type="primary"
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag
>
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag>
<el-tag
v-if="scope.row.TrialStatusStr === 'Completed'"
type="warning"
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag
>
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag>
<el-tag
v-if="scope.row.TrialStatusStr === 'Stopped'"
type="danger"
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag
>
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag>
</template>
</el-table-column>
<el-table-column
@@ -582,7 +579,7 @@
:disabled="
(scope.row.TrialStatusStr === 'Initializing' &&
!hasPermi(['role:pm'])) ||
scope.row.IsDeleted
scope.row.IsDeleted || ((scope.row.TrialStatusStr === 'Completed' || scope.row.TrialStatusStr === 'Stopped') && !(hasPermi(['role:qa']) || hasPermi(['role:ea']) || hasPermi(['role:pm'])))
"
:title="$t('trials:trials-list:action:panel')"
@click.stop="handleDetail(scope.row)"
@@ -612,7 +609,7 @@
icon="el-icon-delete"
:disabled="
scope.row.IsDeleted ||
scope.row.TrialStatusStr !== 'Initializing'
scope.row.TrialStatusStr !== 'Initializing'
"
:title="$t('trials:trials-list:action:abolition')"
@click.stop="handleAbandon(scope.row)"
@@ -693,54 +690,54 @@
import {
abandonTrial,
ifTrialCanOngoing,
getTrialToBeDoneList,
} from "@/api/trials";
import { getTrialList_Export } from "@/api/export";
import store from "@/store";
import { mapGetters } from "vuex";
import BaseContainer from "@/components/BaseContainer";
import Pagination from "@/components/Pagination";
import TrialForm from "./components/TrialForm";
import TrialStatusForm from "./components/TrialStatusForm";
import DoneList from "./components/DoneList";
getTrialToBeDoneList
} from '@/api/trials'
import { getTrialList_Export } from '@/api/export'
import store from '@/store'
import { mapGetters } from 'vuex'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import TrialForm from './components/TrialForm'
import TrialStatusForm from './components/TrialStatusForm'
import DoneList from './components/DoneList'
const searchDataDefault = () => {
return {
Code: "",
Code: '',
CriterionIds: [],
SponsorId: "",
SponsorId: '',
ReviewTypeIds: [],
CROId: "",
Expedited: "",
Indication: "",
Phase: "",
CROId: '',
Expedited: '',
Indication: '',
Phase: '',
ModalityIds: [],
BeginDate: "",
EndDate: "",
AttendedReviewerType: "",
ResearchProgramNo: "",
ExperimentName: "",
BeginDate: '',
EndDate: '',
AttendedReviewerType: '',
ResearchProgramNo: '',
ExperimentName: '',
PageIndex: 1,
PageSize: 20,
Asc: false,
SortField: "",
SortField: '',
CriterionType: null,
PM_EMail: null,
};
};
PM_EMail: null
}
}
export default {
name: "Trials",
name: 'Trials',
components: {
Pagination,
BaseContainer,
TrialForm,
TrialStatusForm,
DoneList,
DoneList
},
dicts: ["ReadingStandard", "ReviewType", "ReadingType"],
dicts: ['ReadingStandard', 'ReviewType', 'ReadingType'],
data() {
return {
exportLoading: false,
userTypeEnumInt: zzSessionStorage.getItem("userTypeEnumInt") * 1,
userTypeEnumInt: zzSessionStorage.getItem('userTypeEnumInt') * 1,
doneDialogVisible: false,
doneTitle: null,
selectArr: [],
@@ -750,26 +747,26 @@ export default {
total: 0,
isShow: false,
dialogVisible: false,
title: "",
currentId: "",
title: '',
currentId: '',
statusVisible: false,
currentRow: {},
currentUser: zzSessionStorage.getItem("userName"),
currentUser: zzSessionStorage.getItem('userName'),
phaseOptions: [
{ value: "I" },
{ value: "II" },
{ value: "III" },
{ value: "IV" },
{ value: 'I' },
{ value: 'II' },
{ value: 'III' },
{ value: 'IV' }
],
expeditedOption: this.$d.TrialExpeditedState,
beginPickerOption: {
disabledDate: (time) => {
if (this.searchData.EndDate) {
return time.getTime() > new Date(this.searchData.EndDate).getTime();
return time.getTime() > new Date(this.searchData.EndDate).getTime()
} else {
return time.getTime() > Date.now();
return time.getTime() > Date.now()
}
},
}
},
endpickerOption: {
disabledDate: (time) => {
@@ -777,306 +774,189 @@ export default {
return (
time.getTime() > Date.now() ||
time.getTime() <= new Date(this.searchData.BeginDate).getTime()
);
)
} else {
return time.getTime() > Date.now();
return time.getTime() > Date.now()
}
},
},
};
}
}
}
},
computed: {
...mapGetters(["sponsorList", "croList"]),
...mapGetters(['sponsorList', 'croList'])
},
created() {
this.initPage();
this.initPage()
},
methods: {
initPage() {
this.getList();
store.dispatch("global/getSponsorList");
store.dispatch("global/getCROList");
this.getList()
store.dispatch('global/getSponsorList')
store.dispatch('global/getCROList')
},
//
getList() {
this.listLoading = true;
this.listLoading = true
getTrialToBeDoneList(this.searchData)
.then((res) => {
this.list = res.Result.CurrentPageData;
this.total = res.Result.TotalCount;
this.listLoading = false;
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.listLoading = false
})
.catch(() => {
this.listLoading = false;
});
this.listLoading = false
})
},
//
handleSearch() {
this.searchData.PageIndex = 1;
this.getList();
this.searchData.PageIndex = 1
this.getList()
},
//
handleSelectSearch() {
this.searchData.PageIndex = 1;
this.getList();
this.isShow = false;
this.searchData.PageIndex = 1
this.getList()
this.isShow = false
},
//
handleReset() {
this.searchData = searchDataDefault();
this.getList();
this.searchData = searchDataDefault()
this.getList()
},
//
handleNew() {
// this.$router.push({ name: 'CreateTrial' })
this.title = this.$t("trials:trials-list:dialogTitle:new");
this.currentId = "";
this.dialogVisible = true;
this.title = this.$t('trials:trials-list:dialogTitle:new')
this.currentId = ''
this.dialogVisible = true
},
//
handleEdit(row) {
this.title = this.$t("trials:trials-list:dialogTitle:edit");
this.currentId = row.Id;
this.dialogVisible = true;
this.title = this.$t('trials:trials-list:dialogTitle:edit')
this.currentId = row.Id
this.dialogVisible = true
},
handleCommission(row) {
this.doneTitle = this.$t("trials:trials-list:dialogTitle:doneTitle");
this.currentId = row.Id;
this.doneDialogVisible = true;
this.doneTitle = this.$t('trials:trials-list:dialogTitle:doneTitle')
this.currentId = row.Id
this.doneDialogVisible = true
},
closeDialog() {
this.dialogVisible = false;
this.dialogVisible = false
},
//
handleStatus(row) {
if (row.TrialStatusStr === "Initializing") {
this.listLoading = true;
if (row.TrialStatusStr === 'Initializing') {
this.listLoading = true
ifTrialCanOngoing(row.Id)
.then((res) => {
this.listLoading = false;
this.listLoading = false
if (res.Result) {
this.currentRow = { ...row };
this.statusVisible = true;
this.currentRow = { ...row }
this.statusVisible = true
} else {
this.$confirm(res.ErrorMessage, {
type: "warning",
type: 'warning',
showCancelButton: false,
callback: (action) => {},
});
callback: (action) => {}
})
}
})
.catch(() => {
this.listLoading = false;
});
this.listLoading = false
})
} else {
this.currentRow = { ...row };
this.statusVisible = true;
this.currentRow = { ...row }
this.statusVisible = true
}
},
closeStatusDialog() {
this.statusVisible = false;
this.statusVisible = false
},
//
handleAbandon(row) {
this.$confirm(this.$t("trials:trials-list:message:abolition"), {
type: "warning",
distinguishCancelAndClose: true,
this.$confirm(this.$t('trials:trials-list:message:abolition'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
this.currentRow = { ...row };
this.abandonTrial();
this.currentRow = { ...row }
this.abandonTrial()
})
.catch(() => {});
.catch(() => {})
},
//
abandonTrial() {
this.listLoading = true;
this.listLoading = true
abandonTrial(this.currentRow.Id, true)
.then((res) => {
this.listLoading = false;
this.listLoading = false
if (res.IsSuccess) {
this.getList();
this.getList()
this.$message.success(
this.$t("trials:trials-list:message:abolitionSuccessfully")
);
this.$t('trials:trials-list:message:abolitionSuccessfully')
)
}
})
.catch(() => {
this.listLoading = false;
});
this.listLoading = false
})
},
rowClick(row, col) {
if (
(row.TrialStatusStr === "Initializing" &&
!this.hasPermi(["role:pm"])) ||
row.IsDeleted
)
return;
if ((row.TrialStatusStr === 'Initializing' && !this.hasPermi(['role:pm'])) || row.IsDeleted) {
return
} else if ((row.TrialStatusStr === 'Completed' || row.TrialStatusStr === 'Stopped') && !(this.hasPermi(['role:qa']) || this.hasPermi(['role:ea']) || this.hasPermi(['role:pm']))) {
return
}
this.$router.push({
path: `/trials/trials-panel?trialId=${row.Id}&trialCode=${row.TrialCode}&researchProgramNo=${row.ResearchProgramNo}`,
});
path: `/trials/trials-panel?trialId=${row.Id}&trialCode=${row.TrialCode}&researchProgramNo=${row.ResearchProgramNo}`
})
},
// panel
handleDetail(row) {
this.$router.push({
path: `/trials/trials-panel?trialId=${row.Id}&trialCode=${row.TrialCode}&researchProgramNo=${row.ResearchProgramNo}`,
});
path: `/trials/trials-panel?trialId=${row.Id}&trialCode=${row.TrialCode}&researchProgramNo=${row.ResearchProgramNo}`
})
},
//
handleSelectChange(val) {
const arr = [];
const arr = []
for (let index = 0; index < val.length; index++) {
arr.push(val[index]);
arr.push(val[index])
}
this.selectArr = arr;
this.selectArr = arr
},
//
handleSortChange(column) {
if (column.order === "ascending") {
this.searchData.Asc = true;
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false;
this.searchData.Asc = false
}
if (column.prop === "Criterion") {
this.searchData.SortField = "CriterionId";
if (column.prop === 'Criterion') {
this.searchData.SortField = 'CriterionId'
} else {
this.searchData.SortField = column.prop;
this.searchData.SortField = column.prop
}
this.searchData.PageIndex = 1;
this.getList();
this.searchData.PageIndex = 1
this.getList()
},
// Excel
handleExportTrial() {
this.exportLoading = true;
let data = {
...this.searchData,
};
data.TrialIdList = this.selectArr.map((item) => item.Id);
this.exportLoading = true
const data = {
...this.searchData
}
data.TrialIdList = this.selectArr.map((item) => item.Id)
return getTrialList_Export(data)
.then((res) => {
this.exportLoading = false;
this.exportLoading = false
})
.catch(() => {
this.exportLoading = false;
});
this.selectArr.forEach((element, index) => {
// element.ExpeditedStr = element.Expedited === 0 ? 'No' : element.Expedited === 1 ? '24H' : '48H'
// element.ModalityListStr = element.ModalityList.join(', ')
// element.CreateTimeStr = element.CreateTime
// element.Criterion = element.CriterionList.join(', ')
element.Deleted = element.IsDeleted ? "Yes" : "No";
element.Index = index + 1;
});
var workbook = new Excel.Workbook();
var sheet = workbook.addWorksheet("Trials");
sheet.properties.defaultRowHeight = 22;
// sheet.columns = [
// { key: 'Index', width: 5 },
// { key: 'Code', width: 15 },
// { key: 'ExpeditedStr', width: 13 },
// { key: 'TrialStatusStr', width: 10 },
// { key: 'Indication', width: 25 },
// { key: 'Phase', width: 10 },
// { key: 'ReviewType', width: 20 },
// { key: 'Criterion', width: 15 },
// { key: 'ModalityListStr', width: 30 },
// { key: 'CRO', width: 10 },
// { key: 'Sponsor', width: 20 },
// { key: 'CreateTimeStr', width: 18 }
// ]
sheet.columns = [
{ key: "Index", width: 5 },
{ key: "TrialCode", width: 25 },
{ key: "ExperimentName", width: 25 },
{ key: "ResearchProgramNo", width: 25 },
{ key: "Sponsor", width: 25 },
{ key: "Deleted", width: 10 },
{ key: "CreateTime", width: 25 },
];
//
sheet.mergeCells("A1", "G2");
sheet.getCell("A1").value = "Trials";
sheet.getCell("A1").alignment = {
vertical: "middle",
horizontal: "center",
};
sheet.getCell("A1").font = {
name: "SimSun",
family: 4,
size: 13,
bold: true,
};
sheet.mergeCells("A3", "G3");
var now = new Date();
sheet.getCell("A3").value = now.toLocaleDateString();
sheet.getCell("A3").alignment = {
vertical: "middle",
horizontal: "right",
};
sheet.getRow(4).values = [
"NO.",
"Trial ID",
"试验名称",
"研究方案号",
"申办方",
"是否废除",
"Date Created",
];
sheet.getRow(4).font = {
name: "SimSun",
family: 4,
size: 11,
bold: true,
};
sheet.getRow(4).alignment = { vertical: "middle", horizontal: "left" };
sheet.addRows(this.selectArr);
sheet.eachRow((row, number) => {
if (number > 3) {
row.eachCell((cell, rowNumber) => {
cell.alignment = { vertical: "center", horizontal: "left" };
cell.border = {
top: { style: "thin" },
left: { style: "thin" },
bottom: { style: "thin" },
right: { style: "thin" },
};
});
}
});
workbook.xlsx
.writeBuffer({
base64: true,
this.exportLoading = false
})
.then(function (xls64) {
var data = new Blob([xls64], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
if ("msSaveOrOpenBlob" in navigator) {
// ie使
window.navigator.msSaveOrOpenBlob(data, "Trials" + ".xlsx");
} else {
var a = document.createElement("a");
var url = URL.createObjectURL(data);
a.href = url;
a.download = "Trials" + ".xlsx";
document.body.appendChild(a);
a.click();
setTimeout(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
});
},
},
};
}
}
}
</script>
@@ -54,8 +54,8 @@
j.armEnum === 1
? $t('trials:adReview:table:viewR1')
: j.armEnum === 2
? $t('trials:adReview:table:viewR2')
: $fd('ArmEnum', j.armEnum)
? $t('trials:adReview:table:viewR2')
: $fd('ArmEnum', j.armEnum)
"
align="center"
prop=""
@@ -81,18 +81,17 @@
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>
<span v-else>{{
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i]
.Answer
@@ -321,7 +320,7 @@
alt=""
crossorigin="anonymous"
style="max-width: 100%; max-height: 100%"
/>
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@@ -435,88 +434,87 @@ import {
// uploadJudgeTaskImage,
saveJudgeVisitTaskResult,
submitJudgeVisitTaskResult,
getReadingPastResultList,
} from "@/api/trials";
import { getAutoCutNextTask } from "@/api/user";
import { setSkipReadingCache } from "@/api/reading";
import const_ from "@/const/sign-code";
import { getToken } from "@/utils/auth";
import SignForm from "@/views/trials/components/newSignForm";
import DicomEvent from "@/views/trials/trials-panel/reading/dicoms/components/DicomEvent";
getReadingPastResultList
} from '@/api/trials'
import { getAutoCutNextTask } from '@/api/user'
import { setSkipReadingCache } from '@/api/reading'
import const_ from '@/const/sign-code'
import { getToken } from '@/utils/auth'
import SignForm from '@/views/trials/components/newSignForm'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
// import store from '@/store'
import { changeURLStatic } from "@/utils/history.js";
import Viewer from "v-viewer";
import { changeURLStatic } from '@/utils/history.js'
export default {
name: "AdReview",
name: 'AdReview',
components: { SignForm },
props: {
trialId: {
type: String,
required: true,
required: true
},
subjectId: {
type: String,
required: true,
required: true
},
visitTaskId: {
type: String,
required: true,
required: true
},
readingCategory: {
type: Number,
required: true,
required: true
},
subjectCode: {
type: String,
required: true,
required: true
},
taskBlindName: {
type: String,
required: true,
required: true
},
isReadingShowSubjectInfo: {
type: Boolean,
required: true,
required: true
},
isReadingShowPreviousResults: {
type: Boolean,
required: true,
required: true
},
isExistsClinicalData: {
type: Boolean,
required: true,
},
required: true
}
},
data() {
return {
adInfo: {},
judgeQuestion: [],
adForm: {
visitTaskId: "",
judgeResultTaskId: "",
judgeResultRemark: "",
visitTaskId: '',
judgeResultTaskId: '',
judgeResultRemark: '',
// judgeResultImagePath: ''
judgeResultImagePathList: [],
judgeResultImagePathList: []
},
currentUser: zzSessionStorage.getItem("userName"),
currentUser: zzSessionStorage.getItem('userName'),
signVisible: false,
signCode: null,
accept: ".png,.jpg,.jpeg",
accept: '.png,.jpg,.jpeg',
imgVisible: false,
imageUrl: "",
imageUrl: '',
uploadDisabled: false,
fileList: [],
loading: false,
visitTaskArmList: [],
priorADList: [],
priorLoading: false,
judgeResultArmEnum: "",
judgeResultArmEnum: '',
criterionType: null,
openWindow: null,
isFixed: false,
images: [],
remark: "",
};
remark: ''
}
},
// watch: {
// visitTaskId: {
@@ -532,261 +530,260 @@ export default {
// }
// },
mounted() {
this.initializeViewer();
this.criterionType = parseInt(this.$route.query.criterionType);
this.getAdInfo();
this.criterionType = parseInt(this.$route.query.criterionType)
this.getAdInfo()
if (this.isReadingShowPreviousResults) {
this.getPriorAdList();
this.getPriorAdList()
}
DicomEvent.$on("resetOpenWindow", () => {
DicomEvent.$on('resetOpenWindow', () => {
if (this.openWindow) {
this.openWindow.close();
this.openWindow.close()
}
});
})
},
beforeDestroy() {
DicomEvent.$off("resetOpenWindow");
DicomEvent.$off('resetOpenWindow')
if (this.openWindow) {
this.openWindow.close();
this.openWindow.close()
}
},
methods: {
async getAdInfo() {
this.loading = true;
this.loading = true
try {
const res = await getJudgeReadingInfo({
visitTaskId: this.visitTaskId,
});
visitTaskId: this.visitTaskId
})
if (res.IsSuccess) {
var judgeQS = [];
var judgeQS = []
if (res.Result.VisitInfoList.length > 0) {
res.Result.VisitInfoList[0].VisitTaskInfoList.map((v, index) => {
var qsObj = {
armEnum: v.ArmEnum,
judgeQuestionList: [],
index: index,
};
index: index
}
v.JudgeQuestionList.map((q) => {
if (q.QuestionType === 1) {
qsObj.judgeQuestionList.push(q.QuestionName);
qsObj.judgeQuestionList.push(q.QuestionName)
} else if (q.QuestionType === 3 && this.criterionType === 10) {
qsObj.judgeQuestionList.push(
this.$t("trials:globalReview:table:visitRemark")
);
this.$t('trials:globalReview:table:visitRemark')
)
} else {
qsObj.judgeQuestionList.push(
this.$fd("JudgeReadingQuestionType", q.QuestionType)
);
this.$fd('JudgeReadingQuestionType', q.QuestionType)
)
}
});
judgeQS.push(qsObj);
});
})
judgeQS.push(qsObj)
})
}
this.judgeQuestion = judgeQS;
this.judgeQuestion = judgeQS
this.isFixed =
this.judgeQuestion.length > 0 &&
this.judgeQuestion[0].judgeQuestionList.length > 4;
this.adInfo = res.Result;
this.adForm.judgeResultTaskId = res.Result.JudgeResultTaskId;
this.judgeQuestion[0].judgeQuestionList.length > 4
this.adInfo = res.Result
this.adForm.judgeResultTaskId = res.Result.JudgeResultTaskId
this.fileList = [];
this.fileList = []
if (res.Result.JudgeResultImagePathList) {
res.Result.JudgeResultImagePathList.map((url) => {
if (url) {
this.fileList.push({ name: "", url: url });
this.fileList.push({ name: '', url: url })
}
});
})
this.adForm.judgeResultImagePathList =
res.Result.JudgeResultImagePathList;
res.Result.JudgeResultImagePathList
}
this.visitTaskArmList = res.Result.VisitTaskArmList;
this.visitTaskArmList = res.Result.VisitTaskArmList
var i = this.visitTaskArmList.findIndex(
(i) => i.VisitTaskId === this.adForm.judgeResultTaskId
);
)
if (i > -1) {
// R1
this.judgeResultArmEnum = this.visitTaskArmList[i].ArmEnum;
var msg = "";
this.judgeResultArmEnum = this.visitTaskArmList[i].ArmEnum
var msg = ''
if (this.judgeResultArmEnum === 1) {
msg = this.$t("trials:adReview:title:msg1");
msg = this.$t('trials:adReview:title:msg1')
} else if (this.judgeResultArmEnum === 2) {
msg = this.$t("trials:adReview:title:msg3");
msg = this.$t('trials:adReview:title:msg3')
}
this.remark = msg;
this.adForm.judgeResultRemark = res.Result.JudgeResultRemark;
this.remark = msg
this.adForm.judgeResultRemark = res.Result.JudgeResultRemark
}
}
this.loading = false;
this.loading = false
} catch (e) {
this.loading = false;
this.loading = false
}
},
async getPriorAdList() {
this.priorLoading = true;
this.priorLoading = true
try {
const res = await getReadingPastResultList({
visitTaskId: this.visitTaskId,
});
visitTaskId: this.visitTaskId
})
if (res.IsSuccess) {
this.priorADList = res.Result;
this.priorADList = res.Result
}
this.priorLoading = false;
this.priorLoading = false
} catch (e) {
this.priorLoading = false;
this.priorLoading = false
}
},
handleVisitTaskArmChange(v) {
var i = this.visitTaskArmList.findIndex((i) => i.VisitTaskId === v);
var i = this.visitTaskArmList.findIndex((i) => i.VisitTaskId === v)
if (i > -1) {
// R1
this.judgeResultArmEnum = this.visitTaskArmList[i].ArmEnum;
var msg = "";
this.judgeResultArmEnum = this.visitTaskArmList[i].ArmEnum
var msg = ''
if (this.judgeResultArmEnum === 1) {
msg = this.$t("trials:adReview:title:msg1");
msg = this.$t('trials:adReview:title:msg1')
} else {
msg = this.$t("trials:adReview:title:msg3");
msg = this.$t('trials:adReview:title:msg3')
}
// this.adForm.judgeResultRemark = `${this.$fd('ArmEnum', this.judgeResultArmEnum)}`
this.remark = msg;
this.adForm.judgeResultRemark = "";
this.remark = msg
this.adForm.judgeResultRemark = ''
} else {
this.judgeResultArmEnum = "";
this.remark = "";
this.adForm.judgeResultRemark = "";
this.judgeResultArmEnum = ''
this.remark = ''
this.adForm.judgeResultRemark = ''
}
},
previewCD() {
var token = getToken();
var token = getToken()
const routeData = this.$router.resolve({
path: `/clinicalData?subjectId=${this.subjectId}&trialId=${this.trialId}&visitTaskId=${this.visitTaskId}&TokenKey=${token}`,
});
window.open(routeData.href, "_blank");
path: `/clinicalData?subjectId=${this.subjectId}&trialId=${this.trialId}&visitTaskId=${this.visitTaskId}&TokenKey=${token}`
})
window.open(routeData.href, '_blank')
},
async handleSave() {
const valid = await this.$refs["adForm"].validate();
if (!valid) return;
this.loading = true;
var paths = [];
const valid = await this.$refs['adForm'].validate()
if (!valid) return
this.loading = true
var paths = []
this.fileList.map((file) => {
if (file.url) {
paths.push(file.url);
paths.push(file.url)
}
});
this.adForm.judgeResultImagePathList = paths;
this.adForm.visitTaskId = this.visitTaskId;
})
this.adForm.judgeResultImagePathList = paths
this.adForm.visitTaskId = this.visitTaskId
try {
await saveJudgeVisitTaskResult(this.adForm);
this.$message.success(this.$t("common:message:savedSuccessfully"));
this.loading = false;
await saveJudgeVisitTaskResult(this.adForm)
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.loading = false
} catch (e) {
this.loading = false;
this.loading = false
}
},
async handleSubmit() {
const valid = await this.$refs["adForm"].validate();
if (!valid) return;
const { ImageAssessmentReportConfirmation } = const_.processSignature;
this.signCode = ImageAssessmentReportConfirmation;
this.signVisible = true;
const valid = await this.$refs['adForm'].validate()
if (!valid) return
const { ImageAssessmentReportConfirmation } = const_.processSignature
this.signCode = ImageAssessmentReportConfirmation
this.signVisible = true
},
//
closeSignDialog(isSign, signInfo) {
if (isSign) {
this.signConfirm(signInfo);
this.signConfirm(signInfo)
} else {
this.signVisible = false;
this.signVisible = false
}
},
//
async signConfirm(signInfo) {
this.loading = true;
var paths = [];
this.loading = true
var paths = []
this.fileList.map((file) => {
paths.push(file.url);
});
paths.push(file.url)
})
var params = {
data: {
visitTaskId: this.visitTaskId,
judgeResultTaskId: this.adForm.judgeResultTaskId,
judgeResultRemark: this.adForm.judgeResultRemark,
judgeResultImagePathList: paths,
judgeResultImagePathList: paths
},
signInfo: signInfo,
};
signInfo: signInfo
}
try {
const res = await submitJudgeVisitTaskResult(params);
const res = await submitJudgeVisitTaskResult(params)
if (res.IsSuccess) {
this.$message.success(this.$t("common:message:savedSuccessfully"));
this.isEdit = false;
this.$refs["signForm"].btnLoading = false;
this.signVisible = false;
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.isEdit = false
this.$refs['signForm'].btnLoading = false
this.signVisible = false
// window.location.reload()
// window.opener.postMessage('refreshTaskList', window.location)
//
this.adInfo.ReadingTaskState = 2;
const res = await getAutoCutNextTask();
var isAutoTask = res.Result.AutoCutNextTask;
this.adInfo.ReadingTaskState = 2
const res = await getAutoCutNextTask()
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
// store.dispatch('reading/resetVisitTasks')
window.location.reload();
window.location.reload()
} else {
// ''
const confirm = await this.$confirm(
this.$t("trials:adReview:title:msg2"),
this.$t('trials:adReview:title:msg2'),
{
type: "warning",
distinguishCancelAndClose: true,
type: 'warning',
distinguishCancelAndClose: true
}
);
if (confirm === "confirm") {
)
if (confirm === 'confirm') {
// store.dispatch('reading/resetVisitTasks')
// DicomEvent.$emit('getNextTask')
window.location.reload();
window.location.reload()
} else {
changeURLStatic("visitTaskId", this.visitTaskId);
changeURLStatic('visitTaskId', this.visitTaskId)
}
}
window.opener.postMessage("refreshTaskList", window.location);
window.opener.postMessage('refreshTaskList', window.location)
}
this.loading = false;
this.loading = false
} catch (e) {
this.loading = false;
this.$refs["signForm"].btnLoading = false;
this.loading = false
this.$refs['signForm'].btnLoading = false
}
},
handleViewDetail(visitTaskId) {
if (this.openWindow) {
this.openWindow.close();
this.openWindow.close()
}
var token = getToken();
var criterionType = parseInt(localStorage.getItem("CriterionType"));
var readingTool = this.$router.currentRoute.query.readingTool;
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool);
var token = getToken()
var criterionType = parseInt(localStorage.getItem('CriterionType'))
var readingTool = this.$router.currentRoute.query.readingTool
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool)
var isReadingTaskViewInOrder =
this.$router.currentRoute.query.isReadingTaskViewInOrder;
this.$router.currentRoute.query.isReadingTaskViewInOrder
var trialReadingCriterionId =
this.$router.currentRoute.query.TrialReadingCriterionId;
this.$router.currentRoute.query.TrialReadingCriterionId
var path = "";
var path = ''
if (readingTool === 0) {
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()}`;
}&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
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`;
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
}
var routeData = this.$router.resolve({ path });
this.openWindow = window.open(routeData.href, "_blank");
var routeData = this.$router.resolve({ path })
this.openWindow = window.open(routeData.href, '_blank')
},
handleView(row, armEnum) {
if (this.openWindow) {
this.openWindow.close();
this.openWindow.close()
}
// var token = getToken()
// var task = row.VisitTaskInfoList.find(item => item.ArmEnum === armEnum)
@@ -794,17 +791,17 @@ export default {
// path: `/readingPage?trialId=${this.trialId}&visitTaskId=${task.VisitTaskId}&TokenKey=${token}&isReadingShowPreviousResults=false`
// })
// window.open(routeData.href, '_blank')
var token = getToken();
var task = row.VisitTaskInfoList.find((item) => item.ArmEnum === armEnum);
var criterionType = this.$router.currentRoute.query.criterionType;
var readingTool = this.$router.currentRoute.query.readingTool;
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool);
var token = getToken()
var task = row.VisitTaskInfoList.find((item) => item.ArmEnum === armEnum)
var criterionType = this.$router.currentRoute.query.criterionType
var readingTool = this.$router.currentRoute.query.readingTool
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool)
var isReadingTaskViewInOrder =
this.$router.currentRoute.query.isReadingTaskViewInOrder;
this.$router.currentRoute.query.isReadingTaskViewInOrder
var trialReadingCriterionId =
this.$router.currentRoute.query.TrialReadingCriterionId;
this.$router.currentRoute.query.TrialReadingCriterionId
var path = "";
var path = ''
if (readingTool === 0) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
@@ -812,7 +809,7 @@ export default {
this.subjectId
}&visitTaskId=${
task.VisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`;
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} else {
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
@@ -820,26 +817,26 @@ export default {
this.subjectId
}&visitTaskId=${
task.VisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`;
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
}
var routeData = this.$router.resolve({ path });
this.openWindow = window.open(routeData.href, "_blank");
var routeData = this.$router.resolve({ path })
this.openWindow = window.open(routeData.href, '_blank')
},
handleViewGl(row, armEnum) {
if (this.openWindow) {
this.openWindow.close();
this.openWindow.close()
}
var token = getToken();
var task = row.VisitTaskInfoList.find((item) => item.ArmEnum === armEnum);
var criterionType = this.$router.currentRoute.query.criterionType;
var readingTool = this.$router.currentRoute.query.readingTool;
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool);
var token = getToken()
var task = row.VisitTaskInfoList.find((item) => item.ArmEnum === armEnum)
var criterionType = this.$router.currentRoute.query.criterionType
var readingTool = this.$router.currentRoute.query.readingTool
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool)
var isReadingTaskViewInOrder =
this.$router.currentRoute.query.isReadingTaskViewInOrder;
this.$router.currentRoute.query.isReadingTaskViewInOrder
var trialReadingCriterionId =
this.$router.currentRoute.query.TrialReadingCriterionId;
this.$router.currentRoute.query.TrialReadingCriterionId
var path = "";
var path = ''
if (readingTool === 0) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
@@ -847,7 +844,7 @@ export default {
this.subjectId
}&visitTaskId=${
task.GlobalVisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`;
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} else {
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
@@ -855,10 +852,10 @@ export default {
this.subjectId
}&visitTaskId=${
task.GlobalVisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`;
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
}
var routeData = this.$router.resolve({ path });
this.openWindow = window.open(routeData.href, "_blank");
var routeData = this.$router.resolve({ path })
this.openWindow = window.open(routeData.href, '_blank')
},
// uploadScreenshot(param) {
// this.loading = true
@@ -876,105 +873,93 @@ export default {
// })
// },
async uploadScreenshot(param) {
this.loading = true;
this.uploadDisabled = false;
var trialId = this.$route.query.trialId;
var file = await this.fileToBlob(param.file);
this.loading = true
this.uploadDisabled = false
var trialId = this.$route.query.trialId
var file = await this.fileToBlob(param.file)
const res = await this.OSSclient.put(
`/${trialId}/Read/${this.subjectId}/visit/${param.file.name}`,
file
);
console.log(res);
)
console.log(res)
this.fileList.push({
name: param.file.name,
url: this.$getObjectName(res.url),
});
this.loading = false;
this.uploadDisabled = true;
url: this.$getObjectName(res.url)
})
this.loading = false
this.uploadDisabled = true
},
handleBeforeUpload(file) {
//
if (this.checkFileSuffix(file.name)) {
return true;
return true
} else {
const msg = this.$t("trials:adReview:title:msg4").replace(
"xxx",
const msg = this.$t('trials:adReview:title:msg4').replace(
'xxx',
this.accept
);
this.$alert(msg);
return false;
)
this.$alert(msg)
return false
}
},
checkFileSuffix(fileName) {
var index = fileName.lastIndexOf(".");
var suffix = fileName.substring(index + 1, fileName.length);
var index = fileName.lastIndexOf('.')
var suffix = fileName.substring(index + 1, fileName.length)
if (
this.accept.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) ===
-1
) {
return false;
return false
} else {
return true;
return true
}
},
//
removeImage() {
this.imageUrl = "";
this.fileList = [];
this.adForm.judgeResultImagePath = "";
this.imageUrl = ''
this.fileList = []
this.adForm.judgeResultImagePath = ''
},
//
handlePictureCardPreview(file) {
this.images = this.fileList.map(
(f) => this.OSSclientConfig.basePath + f.url
);
)
// this.imageUrl = this.OSSclientConfig.basePath + file.url
this.$refs[file.url].$viewer.show();
this.$refs[file.url].$viewer.show()
},
//
handleRemove(file, fileList) {
var idx = this.fileList.findIndex((i) => i.url === file.url);
if (idx === -1) return;
this.fileList.splice(idx, 1);
var idx = this.fileList.findIndex((i) => i.url === file.url)
if (idx === -1) return
this.fileList.splice(idx, 1)
},
async skipTask() {
try {
//
const confirm = await this.$confirm(
this.$t("trials:readingReport:message:skipConfirm"),
this.$t('trials:readingReport:message:skipConfirm'),
{
type: "warning",
distinguishCancelAndClose: true,
type: 'warning',
distinguishCancelAndClose: true
}
);
if (confirm !== "confirm") return;
this.loading = true;
)
if (confirm !== 'confirm') return
this.loading = true
const res = await setSkipReadingCache({
visitTaskId: this.visitTaskId,
});
this.loading = false;
visitTaskId: this.visitTaskId
})
this.loading = false
if (res.IsSuccess) {
window.location.reload();
window.location.reload()
}
} catch (e) {
this.loading = false;
console.log(e);
this.loading = false
console.log(e)
}
},
initializeViewer() {
Viewer.setDefaults({
toolbar: {
zoomIn: true,
zoomOut: true,
rotateLeft: true,
rotateRight: true,
flipHorizontal: true,
flipVertical: true,
},
});
},
},
};
}
}
}
</script>
<style lang="scss" scoped>
.adReview_wrapper {
@@ -276,7 +276,6 @@ import { submitTableQuestion, deleteReadingRowAnswer, getIsSuvMaxLesion } from '
// import { uploadPrintscreen } from '@/api/reading'
import DicomEvent from './../DicomEvent'
import store from '@/store'
import Viewer from 'v-viewer'
export default {
name: 'MeasurementForm',
props: {
@@ -353,7 +352,6 @@ export default {
}
},
mounted() {
this.initializeViewer()
this.trialId = this.$route.query.trialId
this.initForm()
DicomEvent.$on('handleImageQualityAbnormal', () => {
@@ -380,11 +378,6 @@ export default {
}
}
},
initializeViewer() {
Viewer.setDefaults({
toolbar: { zoomIn: true, zoomOut: true, rotateLeft: true, rotateRight: true, flipHorizontal: true, flipVertical: true }
})
},
previewImage() {
this.$refs.viewer[0].$viewer.show()
},
@@ -239,7 +239,7 @@
<!-- </span>-->
<!-- </div>-->
</el-upload>
<el-dialog
<!-- <el-dialog
v-if="question.Type==='upload'"
append-to-body
:visible.sync="imgVisible"
@@ -250,7 +250,20 @@
加载中<span class="dot">...</span>
</div>
</el-image>
</el-dialog>
</el-dialog> -->
<viewer
v-if="question.Type==='upload' && imgVisible"
:ref="imageUrl"
style="margin:0 10px;"
:images="[imageUrl]"
>
<img
v-show="false"
crossorigin="anonymous"
:src="imageUrl"
alt="Image"
>
</viewer>
</el-form-item>
</template>
@@ -801,6 +814,9 @@ export default {
}else{
this.imageUrl = this.OSSclientConfig.basePath + file.url
this.imgVisible = true
this.$nextTick(()=>{
this.$refs[this.imageUrl].$viewer.show()
})
}
},
//
@@ -138,27 +138,41 @@
>
<i slot="default" class="el-icon-plus" />
<div slot="file" slot-scope="{file}">
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
alt=""
<viewer
:ref="file.url"
:images="[imageUrl]"
style="
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
"
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
crossOrigin="anonymous"
alt=""
style="max-width: 100%; max-height: 100%"
/>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</span>
</viewer>
</div>
</el-upload>
<el-dialog
@@ -308,7 +322,7 @@ export default {
window.open(this.OSSclientConfig.basePath + file.url,'_blank')
}else{
this.imageUrl = this.OSSclientConfig.basePath + file.url
this.imgVisible = true
this.$refs[file.url].$viewer.show();
}
},
//
@@ -148,27 +148,41 @@
>
<i slot="default" class="el-icon-plus" />
<div slot="file" slot-scope="{file}">
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
alt=""
<viewer
:ref="file.url"
:images="[imageUrl]"
style="
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
"
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
crossOrigin="anonymous"
alt=""
style="max-width: 100%; max-height: 100%"
/>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</span>
</viewer>
</div>
</el-upload>
<el-dialog
@@ -492,7 +506,8 @@ export default {
window.open(this.OSSclientConfig.basePath + file.url,'_blank')
}else{
this.imageUrl = this.OSSclientConfig.basePath + file.url
this.imgVisible = true
// this.imgVisible = true
this.$refs[file.url].$viewer.show()
}
},
@@ -14,41 +14,43 @@
>
<i slot="default" class="el-icon-plus" />
<div slot="file" slot-scope="{file}">
<img
class="el-upload-list__item-thumbnail"
crossOrigin="Anonymous"
:src="OSSclientConfig.basePath + file.url"
alt=""
<viewer
:ref="file.url"
:images="[imageUrl]"
style="
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
"
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
crossOrigin="anonymous"
alt=""
style="max-width: 100%; max-height: 100%"
/>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</viewer>
</div>
</el-upload>
<el-dialog
append-to-body
:visible.sync="imgVisible"
width="600px"
>
<el-image :src="imageUrl" width="100%" crossOrigin="Anonymous">
<div slot="placeholder" class="image-slot">
加载中<span class="dot">...</span>
</div>
</el-image>
</el-dialog>
</div>
</template>
@@ -142,7 +144,8 @@ name: "CustomizeReportPageUpload",
window.open(this.OSSclientConfig.basePath + file.url,'_blank')
}else{
this.imageUrl = this.OSSclientConfig.basePath + file.url
this.imgVisible = true
// this.imgVisible = true
this.$refs[file.url].$viewer.show()
}
},
//
@@ -143,7 +143,6 @@ import { getMedicalReviewDialog } from '@/api/trials'
import FeedbackForm from './FeedbackForm'
import mimAvatar from '@/assets/MIM.png'
import irAvatar from '@/assets/IR.png'
import Viewer from 'v-viewer'
export default {
name: 'ChatForm',
components: { FeedbackForm },
@@ -177,7 +176,6 @@ export default {
}
},
mounted() {
this.initializeViewer()
this.getMessageList()
},
methods: {
@@ -213,11 +211,6 @@ export default {
this.imagePath = `${this.OSSclientConfig.basePath}${path}`
this.previewDialog = true
this.$refs[path][0].$viewer.show()
},
initializeViewer() {
Viewer.setDefaults({
toolbar: { zoomIn: true, zoomOut: true, rotateLeft: true, rotateRight: true, flipHorizontal: true, flipVertical: true }
})
}
}
@@ -171,7 +171,6 @@ import { getMedicalReviewDialog, sendMedicalReviewDialog } from '@/api/trials'
import FeedbackForm from '@/views/trials/trials-panel/reading/medical-feedback/components/FeedbackForm'
import mimAvatar from '@/assets/MIM.png'
import irAvatar from '@/assets/IR.png'
import Viewer from 'v-viewer'
export default {
name: 'ChatForm',
components: {
@@ -207,7 +206,6 @@ export default {
}
},
mounted() {
this.initializeViewer()
this.getMessageList()
},
methods: {
@@ -261,11 +259,6 @@ export default {
this.irFeedbackForm.title = this.$t('trials:medicalFeedback:title:feedback')
this.irFeedbackForm.visible = true
},
initializeViewer() {
Viewer.setDefaults({
toolbar: { zoomIn: true, zoomOut: true, rotateLeft: true, rotateRight: true, flipHorizontal: true, flipVertical: true }
})
}
}
@@ -197,7 +197,6 @@
import { saveMedicalReviewInfo } from "@/api/trials";
import ChatForm from "./ChatForm";
import CloseQC from "./CloseQC";
import Viewer from "v-viewer";
export default {
name: "AuditConclusions",
components: {
@@ -253,7 +252,6 @@ export default {
};
},
mounted() {
this.initializeViewer();
this.initForm();
},
methods: {
@@ -435,21 +433,9 @@ export default {
var idx = this.fileList.findIndex((i) => i.url === file.url);
if (idx === -1) return;
this.fileList.splice(idx, 1);
},
initializeViewer() {
Viewer.setDefaults({
toolbar: {
zoomIn: true,
zoomOut: true,
rotateLeft: true,
rotateRight: true,
flipHorizontal: true,
flipVertical: true,
},
});
},
},
};
}
}
}
</script>
<style lang="scss" scoped>
.conclusions {
@@ -1,17 +1,15 @@
<template>
<!-- <el-tabs v-model="activeName" v-loading="loading" style="min-height:500px">-->
<!-- <el-tab-pane-->
<!-- v-for="criterion in criterions"-->
<!-- :key="criterion.ReadingQuestionCriterionTrialId"-->
<!-- :label="criterion.ReadingQuestionCriterionTrialName"-->
<!-- :name="criterion.ReadingQuestionCriterionTrialId"-->
<!-- >-->
<div v-loading="loading" style="min-height:500px">
<h3 v-if="isReadingShowSubjectInfo" style="padding: 5px 0px;margin: 0;">
<span v-if="subjectCode">{{ subjectCode }} </span>
<span style="margin-left:5px;">{{ taskBlindName }}</span>
</h3>
<ECRF
:trial-id="trialId"
:subject-id="subjectId"
:criterion-id="criterionId"
:visit-task-id="visitTaskId"
:iseCRFShowInDicomReading="iseCRFShowInDicomReading"
/>
</div>
@@ -43,6 +41,22 @@ export default {
criterionId: {
type: String,
required: true
},
subjectCode: {
type: String,
required: true
},
taskBlindName: {
type: String,
required: true
},
isReadingShowSubjectInfo: {
type: Boolean,
required: true
},
iseCRFShowInDicomReading: {
type: Boolean,
required: true
}
},
data() {
@@ -77,13 +77,13 @@
<el-form-item v-if="readingTaskState < 2">
<div style="text-align:center;">
<el-button type="primary" @click="skipTask">
<el-button type="primary" @click="skipTask" v-if="iseCRFShowInDicomReading">
{{ $t('trials:readingReport:button:skip') }}
</el-button>
<el-button type="primary" @click="handleSave">
<el-button type="primary" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
<el-button type="primary" @click="handleSubmit">
<el-button type="primary" @click="handleSubmit" v-if="iseCRFShowInDicomReading">
{{ $t('common:button:submit') }}
</el-button>
</div>
@@ -116,7 +116,7 @@ import const_ from '@/const/sign-code'
import FormItem from './FormItem'
import SignForm from '@/views/trials/components/newSignForm'
// import { getToken } from '@/utils/auth'
// import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
export default {
name: 'ECRF',
components: {
@@ -139,6 +139,10 @@ export default {
visitTaskId: {
type: String,
required: true
},
iseCRFShowInDicomReading: {
type: Boolean,
required: true
}
},
data() {
@@ -158,6 +162,9 @@ export default {
},
mounted() {
this.getQuestions()
DicomEvent.$on('refreshQuestionAnswer', _ => {
this.getQuestions()
})
},
methods: {
async getQuestions() {
@@ -243,6 +250,7 @@ export default {
try {
const res = await saveVisitTaskQuestions(params)
if (res.IsSuccess) {
DicomEvent.$emit('getReportInfo', true)
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
this.loading = false
@@ -285,6 +293,7 @@ export default {
const res = await submitVisitTaskQuestionsInDto(params)
this.loading = false
if (res.IsSuccess) {
DicomEvent.$emit('getReportInfo', true)
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.isEdit = false
this.$refs['signForm'].btnLoading = false
@@ -0,0 +1,934 @@
<template>
<div class="report-wrapper">
<el-card v-loading="loading" shadow="never" style="display:flex;flex-direction: column;">
<div slot="header" class="clearfix report-header">
<h3 style="margin:0;padding:0;">{{ $t('trials:readingReport:title:eicrf') }}</h3>
<div style="margin-left:auto">
<!-- <el-switch
v-model="isShowDetail"
:active-text="$t('trials:readingReport:title:expandDetails')"
:inactive-text="$t('trials:readingReport:title:collapseDetails')"
style="margin-right:5px"
@change="handleShowDetail"
/> -->
<el-button
v-if="readingTaskState<2"
type="primary"
size="small"
@click="skipTask"
>
<!-- 跳过 -->
{{ $t('trials:readingReport:button:skip') }}
</el-button>
<el-button v-if="readingTaskState<2" type="primary" size="small" @click="getReportInfo">{{$t('trials:readingReport:button:refresh')}}</el-button>
<el-button v-if="readingTaskState<2" type="primary" size="small" @click="handleSave(true)">{{$t('common:button:save')}}</el-button>
<el-button v-if="readingTaskState<2" type="primary" size="small" @click="handleConfirm">{{$t('common:button:submit')}}</el-button>
</div>
</div>
<div style="flex: 1">
<el-table
ref="reportList"
v-adaptive="{bottomOffset:0}"
:data="taskQuestions"
v-if="taskQuestions.length > 0"
row-key="Id"
border
default-expand-all
height="100"
:tree-props="{children: 'Childrens', hasChildren: 'hasChildren'}"
size="mini"
>
<el-table-column
prop=""
label=""
show-overflow-tooltip
width="350px"
>
<template slot-scope="scope">
<span v-if="scope.row.QuestionName">{{ scope.row.BlindName ? scope.row.QuestionName : scope.row.QuestionName }}</span>
<span
v-else
style="font-weight: bold;font-size: 16px;color: #f44336;"
>
{{ scope.row.GroupName }}
</span>
</template>
</el-table-column>
<el-table-column
v-for="task in visitTaskList"
:key="task.VisitTaskId"
prop="date"
show-overflow-tooltip
width="200px"
>
<template slot="header">
<div v-if="task.IsCurrentTask">
{{ task.BlindName }}
</div>
<div v-else>
<div>
{{ task.BlindName }}
<el-button type="text" size="small" @click="previewDicoms(task)">
<span class="el-icon-view"></span>
</el-button>
</div>
<!-- <div v-if="task.LatestScanDate">-->
<!-- {{ task.LatestScanDate.split(' ')[0] }}-->
<!-- </div>-->
<!-- {{ `(影像点击跳转)` }} -->
<!-- {{ $t('trials:readingReport:button:jump') }}-->
</div>
</template>
<template slot-scope="scope">
<template v-if="readingTaskState<2 && task.VisitTaskId === visitTaskId && (scope.row.Type==='input' || scope.row.Type==='number' || scope.row.Type==='select' || scope.row.Type==='textarea' || scope.row.Type==='radio')">
<template>
<!-- 输入框 -->
<div>
<template v-if="!((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)">
</template>
<el-input
v-else-if="questionForm[scope.row.QuestionId] instanceof Array && (scope.row.Type==='input' || scope.row.Type==='textarea') && !scope.row.IsShowInDicom && ((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)"
v-model="questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]"
size="mini"
/>
<span v-else-if="questionForm[scope.row.QuestionId] instanceof Array && (scope.row.Type==='input' || scope.row.Type==='textarea')">
{{questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]}}
</span>
<el-input
v-else-if="(scope.row.Type==='input' || scope.row.Type==='textarea') && !scope.row.IsShowInDicom && ((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)"
v-model="questionForm[scope.row.QuestionId]"
size="mini"
/>
<span v-else-if="scope.row.Type==='input' || scope.row.Type==='textarea'">
{{questionForm[scope.row.QuestionId]}}
</span>
<el-select
v-else-if="questionForm[scope.row.QuestionId] instanceof Array && (scope.row.Type==='select' || scope.row.Type==='radio') && !scope.row.IsShowInDicom && ((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)"
v-model="questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]"
size="mini"
clearable
>
<template>
<el-option
v-for="val in scope.row.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<span v-else-if="questionForm[scope.row.QuestionId] instanceof Array && scope.row.Type==='select'">
{{questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]}}
</span>
<el-select
v-else-if="(scope.row.Type==='select' || scope.row.Type==='radio') && !scope.row.IsShowInDicom && ((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)"
v-model="questionForm[scope.row.QuestionId]"
size="mini"
clearable
>
<template>
<el-option
v-for="val in scope.row.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<span v-else-if="scope.row.Type==='select' || scope.row.Type==='radio'">
{{questionForm[scope.row.QuestionId]}}
</span>
<el-input
v-else-if="scope.row.DataSource !== 1 && questionForm[scope.row.QuestionId] instanceof Array && scope.row.Type==='number' && (scope.row.xfIndex || scope.row.xfIndex === 0) && !scope.row.IsShowInDicom && ((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)"
v-model="questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]"
:disabled="scope.row.DataSource === 1"
onblur="value=parseFloat(value).toFixed(parseInt(localStorage.getItem('digitPlaces')))"
@input="limitInput($event, questionForm[scope.row.QuestionId][scope.row.xfIndex], scope.row.TableQuestionId)"
size="mini"
@focus="() => {questionId = scope.row.QuestionId}"
>
<template slot="append" v-if="scope.row.Unit !== 0">{{scope.row.Unit !== 4 ? $fd('ValueUnit', scope.row.Unit) : scope.row.CustomUnit}}</template>
<template slot="append" v-else-if="scope.row.ValueType === 2">%</template>
</el-input>
<span v-else-if="questionForm[scope.row.QuestionId] instanceof Array && scope.row.Type==='number' && (scope.row.xfIndex || scope.row.xfIndex === 0)">
<template v-if="(scope.row.ValueType === 0 || scope.row.ValueType === 1) && scope.row.Unit">
{{ isNaN(parseInt(questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]))? questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]:`${questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]} ${scope.row.Unit !== 4 ? $fd('ValueUnit',scope.row.Unit) : scope.row.CustomUnit}` }}
</template>
<template v-else-if="scope.row.ValueType === 2">
{{ isNaN(parseInt(questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId])) ? questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]:`${questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]} %` }}
</template>
<template v-else>
{{ isNaN(parseInt(questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId])) ? questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]:`${questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]}` }}
</template>
</span>
<el-input
v-else-if="scope.row.DataSource !== 1 && scope.row.Type==='number' && !scope.row.IsShowInDicom && ((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)"
v-model="questionForm[scope.row.QuestionId]"
:disabled="scope.row.DataSource === 1"
onblur="value=parseFloat(value).toFixed(parseInt(localStorage.getItem('digitPlaces')))"
@input="limitInput($event, questionForm, scope.row.QuestionId)"
size="mini"
@focus="() => {questionId = scope.row.QuestionId}"
>
<template slot="append" v-if="scope.row.Unit !== 0">{{scope.row.Unit !== 4 ? $fd('ValueUnit', scope.row.Unit) : scope.row.CustomUnit}}</template>
<template slot="append" v-else-if="scope.row.ValueType === 2">%</template>
</el-input>
<span v-else-if="scope.row.Type==='number'">
<template v-if="(scope.row.ValueType === 0 || scope.row.ValueType === 1) && scope.row.Unit">
{{ isNaN(parseInt(questionForm[scope.row.QuestionId]))? questionForm[scope.row.QuestionId]:`${questionForm[scope.row.QuestionId]} ${scope.row.Unit !== 4 ? $fd('ValueUnit',scope.row.Unit) : scope.row.CustomUnit}` }}
</template>
<template v-else-if="scope.row.ValueType === 2">
{{ isNaN(parseInt(questionForm[scope.row.QuestionId])) ? questionForm[scope.row.QuestionId]:`${questionForm[scope.row.QuestionId]} %` }}
</template>
<template v-else>
{{ isNaN(parseInt(questionForm[scope.row.QuestionId])) ? questionForm[scope.row.QuestionId] : questionForm[scope.row.QuestionId]}}
</template>
</span>
</div>
</template>
</template>
<template v-else-if="task.VisitTaskId === visitTaskId && scope.row.Type === 'upload'">
<UploadFile
v-if="scope.row.Type==='upload' && (scope.row.xfIndex || scope.row.xfIndex === 0)"
:visitTaskId="visitTaskId"
:question="scope.row"
:task="task"
:readingTaskState="readingTaskState"
:initUrl="questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]"
@setImageUrl="(url) => {setImageUrl(scope.row.QuestionId, scope.row.xfIndex, scope.row.TableQuestionId, url, scope.row.RowId)}"
></UploadFile>
<UploadFile
v-else-if="scope.row.Type==='upload'"
:visitTaskId="visitTaskId"
:question="scope.row"
:task="task"
:readingTaskState="readingTaskState"
:initUrl="questionForm[scope.row.QuestionId]"
@setImageUrl="(url) => {setImageUrl(scope.row.QuestionId, scope.row.xfIndex, scope.row.TableQuestionId, url)}"
></UploadFile>
</template>
<template v-else-if="scope.row.Type === 'upload'">
<UploadFile
v-if="scope.row.Type==='upload' && (scope.row.xfIndex || scope.row.xfIndex === 0)"
:visitTaskId="visitTaskId"
:question="scope.row"
:task="task"
:readingTaskState="readingTaskState"
:initUrl="scope.row.Answers[task.VisitTaskId]"
></UploadFile>
<UploadFile
v-else-if="scope.row.Type==='upload'"
:visitTaskId="visitTaskId"
:question="scope.row"
:task="task"
:readingTaskState="readingTaskState"
:initUrl="scope.row.Answers[task.VisitTaskId]"
></UploadFile>
</template>
<template v-else-if="scope.row.QuestionType=== 22">
{{ scope.row.Answers[task.VisitTaskId] === '-1' ? '未知' : scope.row.Answers[task.VisitTaskId] }}
</template>
<template v-else-if="scope.row.DictionaryCode">
{{ $fd(scope.row.DictionaryCode, scope.row.Answers[task.VisitTaskId]) }}
</template>
<template v-else-if="CriterionType === 10">
{{ isNaN(parseInt(scope.row.Answers[task.VisitTaskId]))?scope.row.Answers[task.VisitTaskId]:`${scope.row.Answers[task.VisitTaskId]}` }}
</template>
<template v-else-if="!((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)">
</template>
<template v-else-if="(scope.row.ValueType === 0 || scope.row.ValueType === 1) && scope.row.Unit">
{{ isNaN(parseInt(scope.row.Answers[task.VisitTaskId]))?scope.row.Answers[task.VisitTaskId]:`${scope.row.Answers[task.VisitTaskId]} ${scope.row.Unit !== 4 ? $fd('ValueUnit',scope.row.Unit) : scope.row.CustomUnit}` }}
</template>
<template v-else-if="scope.row.ValueType === 2">
{{ isNaN(parseInt(scope.row.Answers[task.VisitTaskId])) ? scope.row.Answers[task.VisitTaskId]:`${scope.row.Answers[task.VisitTaskId]} %` }}
</template>
<template v-else-if="scope.row.Answers && scope.row.Answers.hasOwnProperty(task.VisitTaskId)">
{{ scope.row.Answers[task.VisitTaskId] }}
</template>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
<!-- 签名框 -->
<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" />
</el-dialog>
</div>
</template>
<script>
import { changeCalculationAnswer, getReadingReportEvaluation, changeDicomReadingQuestionAnswer, submitDicomVisitTask, verifyVisitTaskQuestions, getQuestionCalculateRelation } from '@/api/trials'
import { setSkipReadingCache } from '@/api/reading'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import UploadFile from './UploadFile'
import const_ from '@/const/sign-code'
import SignForm from '@/views/trials/components/newSignForm'
import { getToken } from '@/utils/auth'
import store from '@/store'
export default {
name: 'Report',
components: { SignForm, UploadFile },
props: {
trialId: {
type: String,
required: true
},
visitTaskId: {
type: String,
required: true
},
subjectId: {
type: String,
required: true
},
readingTool: {
type: String,
required: true
},
criterionType: {
type: String,
required: true
},
isReadingTaskViewInOrder: {
type: Number,
required: true
}
},
data() {
return {
currentUser: zzSessionStorage.getItem('userName'),
signVisible: false,
signCode: null,
visitTaskList: [],
taskQuestions: [],
loading: false,
answers: [],
readingTaskState: 2,
tumorEvaluate: null,
currentEvaluateResult: null,
isExistDisease: null,
currentExistDisease: null,
currentTaskReason: '',
answerArr: [],
questions: [],
isShowDetail: false,
CriterionType: 0,
CalculationList: [],
TrialReadingCriterionId: null,
tableAnswers: {},
questionForm: {},
questionId: null
}
},
watch: {
questionForm: {
deep: true,
immediate: true,
handler(v, oldv) {
try {
if (!v[this.questionId] || !oldv[this.questionId]) return
} catch (e) {
}
this.formItemNumberChange(this.questionId, false)
}
},
taskQuestions() {
this.$nextTick(() => {
this.setScrollTop()
})
}
},
async mounted() {
this.CriterionType = parseInt(localStorage.getItem('CriterionType'))
this.digitPlaces = parseInt(localStorage.getItem('digitPlaces'))
this.TrialReadingCriterionId = this.$route.query.TrialReadingCriterionId
window.addEventListener('resize', () => {
this.handleResize()
this.setScrollTop()
})
DicomEvent.$on('getReportInfo', isRefresh => {
if (!isRefresh) return
this.getReportInfo()
})
await this.getQuestionCalculateRelation()
this.getReportInfo()
},
beforeDestroy() {
DicomEvent.$off('getReportInfo')
},
methods: {
limitInput(value, a, b) {
if (value.indexOf('.') > -1) {
if (value.split('.')[1].length >= this.digitPlaces) {
this.$set(a, b, parseFloat(value).toFixed(this.digitPlaces))
}
} else {
}
},
setImageUrl(qid, index, tqid, url, RowId) {
if (index || index === 0) {
//
this.$set(this.questionForm[qid][index], tqid, url)
this.$set(this.questionForm[qid][index], tqid + '_RowId', RowId)
// this.questionForm[qid][index][tqid] = url
} else {
//
this.questionForm[qid] = url
}
},
getTagterAnswers(list, questionId) {
let Answers
list.forEach(v => {
if (v.QuestionId === questionId) {
return Object.assign({}, v.Answers)
} else if (v.Childrens.length > 0){
return this.getTagterAnswers(v.Childrens, questionId)
}
})
},
formItemNumberChange(questionId, isTable) {
if (isTable) {
this.CalculationList.forEach((v, i) => {
var find = v.CalculateQuestionList.filter(o => {
return o.QuestionId === questionId
})
// findnumber
if (find) {
var num = this.logic(v)
if (num !== false) {
this.$set(this.questionForm, v.QuestionId, num)
// this.$emit('setFormItemData', { key: v.QuestionId, val: num })
}
}
})
} else {
this.CalculationList.forEach(v => {
var find = v.CalculateQuestionList.filter(o => {
return o.TableQuestionId === questionId
})
// findnumber
if (find) {
var num = this.logic(v)
if (num !== false) {
this.$set(this.questionForm, v.QuestionId, num)
// this.$emit('setFormItemData', { key: v.QuestionId, val: num })
}
}
})
}
// this.$emit('formItemNumberChange')
},
getTableAnswers(QuestionId, list) {
var arr = []
window.xfList = list
list.forEach((v, i) => {
var obj = {}
v.Childrens.forEach((o) => {
this.$set(o, 'xfIndex', i)
obj[o.TableQuestionId + '_RowId'] = o.RowId
obj[o.TableQuestionId] = o.Answers[this.visitTaskId]
})
arr.push(obj)
})
return arr
},
InitVisitTaskQuestionForm() {
this.taskQuestions.map((v, i) => {
if (v.Type === 'group' && v.Childrens.length === 0 && v.Type !== 'table') return
if (!v.IsPage && v.Type !== 'group' && v.Type !== 'summary' && v.Type !== 'table' && v.Type !== 'number') {
this.$set(this.questionForm, v.QuestionId, v.Answers[this.visitTaskId])
}
if (v.Type === 'table') {
var tableAnswers = this.getTableAnswers(v.QuestionId, v.Childrens, i)
this.$set(this.questionForm, v.QuestionId, tableAnswers)
// this.$set(v, 'xfIndex', i)
}
if (v.Type === 'number') {
this.$set(this.questionForm, v.QuestionId, v.Answers[this.visitTaskId] === '' ? parseFloat(0).toFixed(this.digitPlaces) : v.Answers[this.visitTaskId])
}
if (v.Childrens.length > 0) {
this.setChild(v.Childrens)
}
})
this.formItemNumberChange(this.questionId, false)
},
setChild(obj) {
obj.forEach((i, index) => {
if (i.Type !== 'group' && i.Type !== 'summary' && i.Id && i.Type !== 'table') {
this.$set(this.questionForm, i.QuestionId, i.Answers[this.visitTaskId])
}
if (i.Type === 'table') {
var tableAnswers = this.getTableAnswers(i.QuestionId, i.Childrens, index)
this.$set(this.questionForm, i.QuestionId, tableAnswers)
}
if (i.Type === 'number') {
this.$set(this.questionForm, i.QuestionId, i.Answers[this.visitTaskId] === '' ? parseFloat(0).toFixed(this.digitPlaces) : i.Answers[this.visitTaskId])
}
if (i.Childrens && i.Childrens.length > 0 && i.Type !== 'table') {
this.setChild(i.Childrens)
}
})
},
getQuestionCalculateRelation() {
return new Promise(resolve => {
getQuestionCalculateRelation({
TrialReadingCriterionId: this.TrialReadingCriterionId
}).then(res => {
this.CalculationList = res.Result
resolve()
})
})
},
logic(rules, num = 0) {
try {
if (rules.CalculateQuestionList.length === 0) {
return false
}
rules.CalculateQuestionList.forEach((o, i) => {
if (i === 0) {
if (rules.CustomCalculateMark > 4) {
switch (rules.CustomCalculateMark) {
case 5:
this.questionForm[o.QuestionId].forEach((q, qi) => {
if (qi === 0) {
num = parseFloat(q[o.TableQuestionId])
} else {
num *= parseFloat(q[o.TableQuestionId])
}
})
break;
case 6:
this.questionForm[o.QuestionId].forEach((q, qi) => {
if (qi === 0) {
num = isNaN(parseFloat(q[o.TableQuestionId])) ? null : parseFloat(q[o.TableQuestionId])
} else {
num += isNaN(parseFloat(q[o.TableQuestionId])) ? null : parseFloat(q[o.TableQuestionId])
}
})
break;
case 7:
this.questionForm[o.QuestionId].forEach((q, qi) => {
if (qi === 0) {
num = parseFloat(q[o.TableQuestionId])
} else {
num += parseFloat(q[o.TableQuestionId])
}
})
num = this.questionForm[o.QuestionId].length === 0 ? 0 : num / this.questionForm[o.QuestionId].length
break;
case 8:
var arr = []
this.questionForm[o.QuestionId].forEach(q => {
arr.push(q[o.TableQuestionId])
})
num = arr.length === 0 ? 0 : Math.max(...arr)
break;
case 9:
var arr = []
this.questionForm[o.QuestionId].forEach(q => {
arr.push(q[o.TableQuestionId])
})
num = arr.length === 0 ? 0 : Math.min(...arr)
break;
}
} else {
num = parseFloat(this.questionForm[o.TableQuestionId])
}
} else {
switch (rules.CustomCalculateMark) {
case 1:
num += parseFloat(this.questionForm[o.TableQuestionId])
break;
case 2:
num -= parseFloat(this.questionForm[o.TableQuestionId])
break;
case 3:
num *= parseFloat(this.questionForm[o.TableQuestionId])
break;
case 4:
num /= parseFloat(this.questionForm[o.TableQuestionId])
// num /= parseFloat(this.questionForm[o.TableQuestionId])
break;
}
}
})
} catch (e) {
console.log(e)
}
var digitPlaces = parseInt(localStorage.getItem('digitPlaces'))
if (rules.ValueType === 2) {
num = num * 100
}
return isNaN(num) ? '' : isFinite(num) ? num.toFixed(digitPlaces) : '∞'
},
getReportInfo() {
this.loading = true
var params = {
visitTaskId: this.visitTaskId,
trialId: this.$router.currentRoute.query.trialId
}
this.taskQuestions = []
getReadingReportEvaluation(params).then(res => {
this.readingTaskState = res.Result.ReadingTaskState
this.tumorEvaluate = res.Result.CalculateResult.TumorEvaluate ? parseInt(res.Result.CalculateResult.TumorEvaluate) : null
this.isExistDisease = res.Result.CalculateResult.IsExistDisease ? parseInt(res.Result.CalculateResult.IsExistDisease) : null
this.answerArr = []
this.questions = res.Result.TaskQuestions.concat()
var taskQuestions = this.getQuestions(res.Result.TaskQuestions, !this.isShowDetail, null, null)
taskQuestions.forEach(item => {
this.$set(this.taskQuestions, this.taskQuestions.length, item)
})
this.visitTaskList = res.Result.VisitTaskList
this.InitVisitTaskQuestionForm()
this.handleResize()
this.setScrollTop()
this.loading = false
}).catch(() => { this.loading = false })
},
setScrollTop(a) {
setTimeout(() => {
this.$nextTick(() => {
if(this.$refs.reportList){
this.$refs.reportList.bodyWrapper.scrollTop = this.$refs.reportList.bodyWrapper.scrollHeight
this.$refs.reportList.bodyWrapper.scrollTop = this.$refs.reportList.bodyWrapper.scrollHeight
}
})
},50)
},
getQuestions(questions, isNTFilterLength, lesionType, isLymphNodes) {
const arr = []
if (questions.length !== 0) {
questions.forEach((item) => {
//
//
//
lesionType = item.LesionType
var filterArr = []
if ((item.LesionType === 1 || item.LesionType === 2) && isNTFilterLength) {
filterArr = [0, 1, 3, 4, 5, 6, 2, 8, 10]
} else {
filterArr = [3, 4, 5, 6, 2, 8, 10]
}
if (lesionType === 0 && isLymphNodes === 0 && !this.isShowDetail && this.CriterionType === 1) {
filterArr.push(1)
}
if (!(filterArr.includes(item.QuestionMark))) {
const obj = item
this.$set(obj, 'Answers', {})
// obj.Answers = {}
if (item.RowIndex > 0) {
var idx = item.Childrens.findIndex(i => i.QuestionMark === 8)
var idxLoc = item.Childrens.findIndex(i => i.QuestionMark === 10)
if (idx > -1) {
if (item.Childrens[idx].Answer.length > 0) {
var k = item.Childrens[idx].Answer.findIndex(v => v.Answer !== '')
var part = ''
if (obj.IsCanEditPosition) {
part = `${item.Childrens[idx].Answer[k].Answer}--${item.Childrens[idxLoc].Answer[k].Answer}`
} else {
part = `${item.Childrens[idx].Answer[k].Answer}`
}
if (item.SplitOrMergeLesionName && k > -1) {
obj.QuestionName = `${obj.QuestionName} --${part} (Split from ${item.SplitOrMergeLesionName})`
// obj.QuestionName = `${obj.QuestionName} `
} else if (!item.SplitOrMergeLesionName && k > -1) {
obj.QuestionName = `${obj.QuestionName} --${part}`
// obj.QuestionName = `${obj.QuestionName} `
} else {
obj.QuestionName = `${obj.QuestionName} `
}
if (this.CriterionType === 1) {
var idxLymphNode = item.Childrens.findIndex(i => i.QuestionMark === 2)
if (idxLymphNode > -1) {
isLymphNodes = item.Childrens[idxLymphNode].Answer[k].Answer ? parseInt(item.Childrens[idxLymphNode].Answer[k].Answer) : null
}
}
}
}
}
var digitPlaces = parseInt(localStorage.getItem('digitPlaces')) || 0
item.Answer.forEach(i => {
if (item.DictionaryCode) {
this.$set(obj.Answers, i.VisitTaskId, i.Answer ? parseInt(i.Answer) : null)
// obj.Answers[i.VisitTaskId] = i.Answer ? parseInt(i.Answer) : null
} else {
if (item.Type === 'number') {
this.$set(obj.Answers, i.VisitTaskId, isNaN(parseFloat(i.Answer)) ? i.Answer : parseFloat(i.Answer).toFixed(digitPlaces))
} else {
this.$set(obj.Answers, i.VisitTaskId, i.Answer)
}
// obj.Answers[i.VisitTaskId] = i.Answer
}
})
if (item.Childrens.length >= 1) {
obj.Childrens = this.getQuestions(item.Childrens, isNTFilterLength, lesionType, isLymphNodes)
}
arr.push(obj)
}
})
}
return arr
},
handleShowDetail(val) {
this.getReportInfo()
// this.taskQuestions = this.getQuestions(res.Result.TaskQuestions, !this.isShowDetail, null)
},
handleExistDiseaseChange(val) {
// this.currentExistDisease = parseInt(val)
if (val === this.isExistDisease && this.tumorEvaluate === this.currentEvaluateResult) {
this.currentTaskReason = ''
this.evaluateReasonChange('')
}
var idx = this.answerArr.findIndex(i => i.questionType === 15)
if (idx > -1) {
this.answerArr[idx].answer = val
}
},
handleEvaluateResultChange(val) {
// this.currentEvaluateResult = parseInt(val)
if (val === this.tumorEvaluate && this.isExistDisease === this.currentExistDisease) {
this.currentTaskReason = ''
this.evaluateReasonChange('')
}
var idx = this.answerArr.findIndex(i => i.questionType === 13)
if (idx > -1) {
this.answerArr[idx].answer = val
}
},
evaluateReasonChange(val) {
var idx = this.answerArr.findIndex(i => i.questionType === 14)
if (idx > -1) {
this.answerArr[idx].answer = val
}
},
async handleConfirm() {
await this.handleSave(false)
await this.verifyVisitTaskQuestions()
const { ImageAssessmentReportConfirmation } = const_.processSignature
this.signCode = ImageAssessmentReportConfirmation
this.signVisible = true
},
verifyVisitTaskQuestions() {
return new Promise((resolve, reject) => {
this.loading = true
verifyVisitTaskQuestions({ visitTaskId: this.visitTaskId }).then(res => {
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
})
},
handleResize() {
this.$nextTick(() => {
this.$refs.reportList.doLayout()
})
},
//
closeSignDialog(isSign, signInfo) {
if (isSign) {
this.signConfirm(signInfo)
} else {
this.signVisible = false
}
},
//
signConfirm(signInfo) {
this.loading = true
var params = {
data: {
visitTaskId: this.visitTaskId
},
signInfo: signInfo
}
submitDicomVisitTask(params).then(res => {
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
if (this.$refs['signForm']) {
this.$refs['signForm'].btnLoading = false
}
this.signVisible = false
// window.location.reload()
// window.opener.postMessage('refreshTaskList', window.location)
//
this.readingTaskState = 2
store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
DicomEvent.$emit('setReadingState', 2)
window.opener.postMessage('refreshTaskList', window.location)
this.$confirm(this.$t('trials:oncologyReview:title:msg2'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
// var token = getToken()
// var subjectCode = this.$router.currentRoute.query.subjectCode
// var subjectId = this.$router.currentRoute.query.subjectId
// var trialId = this.$router.currentRoute.query.trialId
// this.$router.push({
// path: `/readingPage?subjectCode=${subjectCode}&subjectId=${subjectId}&trialId=${trialId}&TokenKey=${token}`
// })
// DicomEvent.$emit('getNextTask')
window.location.reload()
})
.catch(action => {
})
}
this.loading = false
}).catch(() => {
this.loading = false
if (this.$refs['signForm'] && this.$refs['signForm'].btnLoading) {
this.$refs['signForm'].btnLoading = false
}
})
},
previewDicoms(task) {
var token = getToken()
// var subjectCode = this.$router.currentRoute.query.subjectCode
var subjectCode = localStorage.getItem('subjectCode')
var subjectId = this.subjectId
var trialId = this.trialId
var isReadingTaskViewInOrder = this.isReadingTaskViewInOrder
var criterionType = this.criterionType
var readingTool = this.readingTool
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
var path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${trialId}&subjectCode=${subjectCode}&subjectId=${subjectId}&visitTaskId=${task.VisitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
const routeData = this.$router.resolve({ path })
window.open(routeData.href, '_blank')
},
handleSave(isPrompt) {
return new Promise((resolve, reject) => {
this.loading = true
var answers = []
var tableQuestionAnswer = []
for (const k in this.questionForm) {
if (this.questionForm[k] instanceof Array) {
this.questionForm[k].forEach((v, i) => {
Object.keys(v).forEach(o => {
if (o.indexOf('_RowId') === -1) {
tableQuestionAnswer.push({
questionId: k,
answer: v[o],
tableQuestionId: o,
rowId: v[o+'_RowId']
})
}
})
})
// tableQuestionAnswer.push({})
} else {
answers.push({ questionId: k, answer: this.questionForm[k].toString() })
}
}
var params = {
visitTaskId: this.visitTaskId,
questionAnswer: answers,
tableQuestionAnswer: tableQuestionAnswer
}
changeCalculationAnswer(params).then(res => {
if (isPrompt) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
DicomEvent.$emit('refreshQuestionAnswer')
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
})
},
async skipTask() {
try {
//
const confirm = await this.$confirm(
this.$t('trials:readingReport:message:skipConfirm'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
this.loading = true
const res = await setSkipReadingCache({ visitTaskId: this.visitTaskId })
this.loading = false
if (res.IsSuccess) {
window.location.reload()
}
} catch (e) {
this.loading = false
console.log(e)
}
}
}
}
</script>
<style lang="scss" scoped>
.report-wrapper{
height: 100%;
// background-color: #fff;
// background-color: #000;
::-webkit-scrollbar {
width: 7px;
height: 7px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
// background: #d0d0d0;
}
.report-header{
display: flex;
}
.el-card{
// background-color: #000;
// color: #ffffff;
border:none;
}
/deep/ .el-table--border th.gutter:last-of-type{
border: none;
}
/deep/ .el-card__header{
border: none;
padding: 10px;
}
/deep/ .el-upload-list--picture-card .el-upload-list__item{
width: 30px;
height: 30px;
}
/deep/ .el-upload--picture-card{
width: 30px;
height: 30px;
line-height: 40px;
}
}
/deep/ .el-switch__label.is-active{
color: #428bca;
}
.uploadWrapper{
display: flex;
flex-direction: column;
align-items: flex-start;
}
</style>
@@ -0,0 +1,167 @@
<template>
<div>
<el-upload
:action="accept"
:limit="question.ImageCount"
:on-preview="handlePictureCardPreview"
:before-upload="handleBeforeUpload"
:http-request="uploadScreenshot"
list-type="picture-card"
:on-remove="handleRemove"
:file-list="fileList"
:class="{disabled:readingTaskState >= 2 || (fileList.length >= question.ImageCount) || (task.VisitTaskId !== visitTaskId) || question.IsShowInDicom || ((task.IsBaseLine && question.LimitEdit === 2) || (!task.IsBaseLine && question.LimitEdit === 1))}"
:disabled="readingTaskState >= 2 || task.VisitTaskId !== visitTaskId || question.IsShowInDicom || ((task.IsBaseLine && question.LimitEdit === 2) || (!task.IsBaseLine && question.LimitEdit === 1))"
>
<i slot="default" class="el-icon-plus" />
<div slot="file" slot-scope="{file}">
<viewer
:ref="file.url"
:images="[imageUrl]"
style="
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
"
>
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
crossOrigin="anonymous"
alt=""
style="max-width: 100%; max-height: 100%"
/>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</viewer>
</div>
</el-upload>
</div>
</template>
<script>
export default {
name: "UploadFile",
props: {
task: {
Type: Object,
required: true
},
question: {
Type: Object,
required: true
},
visitTaskId: {
type: String,
required: true
},
readingTaskState: {
type: Number,
required: true
},
initUrl: {
type: String,
default: ''
}
},
data () {
return {
imgVisible: false,
imageUrl: null,
accept: '.png,.jpg,.jpeg',
fileList: [],
}
},
mounted() {
this.urls = this.initUrl === '' ? [] : this.initUrl.split('|')
console.log(this.visitTaskId, this.urls)
this.fileList = []
this.urls.map(url => {
this.fileList.push({ name: '', url: `${url}` })
})
console.log(this.fileList)
},
methods: {
checkFileSuffix(fileName) {
var index = fileName.lastIndexOf('.')
var suffix = fileName.substring(index + 1, fileName.length)
if (this.accept.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1) {
return false
} else {
return true
}
},
async uploadScreenshot(param) {
if (!this.visitTaskId) return
const loading = this.$loading({
target: document.querySelector('.ecrf-wrapper'),
fullscreen: false,
lock: true,
text: 'Loading',
spinner: 'el-icon-loading'
})
var trialId = this.$route.query.trialId
var subjectId = this.$route.query.trialId
var file = await this.fileToBlob(param.file)
const res = await this.OSSclient.put(`/${trialId}/Read/${subjectId}/Visit/${param.file.name}`, file)
console.log(res)
this.fileList.push({ name: param.file.name, path: this.$getObjectName(res.url), url: this.$getObjectName(res.url)})
this.urls.push(this.$getObjectName(res.url))
this.$emit('setImageUrl', this.urls.length > 0 ? this.urls.join('|') : '')
loading.close()
},
handleBeforeUpload(file) {
//
if (this.checkFileSuffix(file.name)) {
// this.fileList = []
return true
} else {
this.$alert(`必须是 ${this.accept} 格式`)
return false
}
},
//
handlePictureCardPreview(file) {
var suffix = file.url.substring(file.url.lastIndexOf(".")+1)
suffix = suffix ? suffix.toLowerCase() : ''
if (suffix === 'doc' || suffix === 'docx' || suffix === 'pdf'){
window.open(this.OSSclientConfig.basePath + file.url,'_blank')
}else{
this.imageUrl = this.OSSclientConfig.basePath + file.url
// this.imgVisible = true
this.$refs[file.url].$viewer.show()
}
},
//
handleRemove(file, fileList) {
this.imageUrl = ''
this.fileList.splice(this.fileList.findIndex(f => f.url === file.url), 1)
this.urls.splice(this.fileList.findIndex(f => f === file.url), 1)
this.$emit('setFormItemData', { key: this.question.Id, val: this.urls.length > 0 ? this.urls.join('|') : '' })
},
}
}
</script>
<style lang="scss" scoped>
.disabled{
/deep/ .el-upload--picture-card {
display: none;
}
}
</style>
@@ -1,9 +1,9 @@
<template>
<div v-loading="loading" class="img-container">
<el-card class="box-card left">
<div v-if="otherInfo && otherInfo.IsReadingShowSubjectInfo" class="title">
<span>{{ $t('trials:auditRecord:table:subject') }}{{ otherInfo.SubjectCode }} </span>
<span>({{ otherInfo.TaskBlindName }})</span>
<div v-if="isReadingShowSubjectInfo" class="title">
<h4>{{ subjectCode }} </h4>
<h4>{{ taskBlindName }}</h4>
</div>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane :label="$t('trials:clinicaldara:title:currentTask')" name="first" class="left-content">
@@ -93,6 +93,10 @@
:subject-id="subjectId"
:visit-task-id="visitTaskId"
:criterion-id="otherInfo.TrialCriterionId"
:subjectCode="subjectCode"
:taskBlindName="taskBlindName"
:isReadingShowSubjectInfo="isReadingShowSubjectInfo"
:iseCRFShowInDicomReading="iseCRFShowInDicomReading"
/>
</el-card>
</div>
@@ -127,9 +131,33 @@ export default {
type: String,
required: true
},
taskBlindName: {
type: String,
required: true
},
readingCategory: {
type: Number,
required: true
},
isReadingShowSubjectInfo: {
type: Boolean,
required: true
},
readingTool: {
type: String,
required: true
},
criterionType: {
type: String,
required: true
},
isReadingTaskViewInOrder: {
type: Number,
required: true
},
iseCRFShowInDicomReading: {
type: Boolean,
required: true
}
},
data() {
@@ -156,7 +184,8 @@ export default {
currentTaskId: '',
otherInfo: null,
isReadingShowPreviousResults: false,
bp: []
bp: [],
openWindow: null
}
},
computed: {
@@ -170,6 +199,11 @@ export default {
this.getNoneDicomList(this.isReadingShowPreviousResults)
},
beforeDestroy() {
if (this.openWindow) {
this.openWindow.close()
}
},
methods: {
// Dicom
getNoneDicomList() {
@@ -250,18 +284,21 @@ export default {
})
},
handleImageRead(task) {
if (this.openWindow) {
this.openWindow.close()
}
this.currentTaskId = task.VisitTaskId
var criterionType = this.$router.currentRoute.query.criterionType
var readingTool = this.$router.currentRoute.query.readingTool
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
var criterionType = this.criterionType
var readingTool = this.readingTool
var isReadingTaskViewInOrder = this.isReadingTaskViewInOrder
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
var token = getToken()
const path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
const path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId}&visitTaskId=${task.VisitTaskId}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
// const routeData = this.$router.resolve({
// path: `/readingPage?subjectId=${this.subjectId}&trialId=${this.trialId}&visitTaskId=${task.VisitTaskId}&TokenKey=${token}`
// })
const routeData = this.$router.resolve({ path })
window.open(routeData.href, '_blank')
this.openWindow = window.open(routeData.href, '_blank')
},
previewCD() {
var token = getToken()
@@ -295,7 +332,7 @@ export default {
flex: 1;
width: 100%;
height: 100%;
padding: 10px;
padding-bottom: 10px;
display: flex;
::-webkit-scrollbar {
width: 7px;
@@ -326,13 +363,19 @@ export default {
flex-direction: column;
}
.title{
height: 40px;
line-height: 40px;
// height: 40px;
// line-height: 40px;
border: 1ppx solid;
border: 1px solid #ebe7e7;
padding-left: 10px;
// padding-left: 10px;
background-color: #4e4e4e;
color: #ffffff;
h4{
padding: 5px 0px;
margin: 0;
text-align: center;
background-color: #4c4c4c;
}
}
.left-content{
flex: 1;
@@ -1,15 +1,40 @@
<template>
<div ref="container" v-loading="loading" class="none-dicom-reading-container">
<!-- 访视阅片 -->
<VisitReview
v-if="isShow && readingCategory && readingCategory=== 1"
:trial-id="trialId"
:subject-id="subjectId"
:subject-code="subjectCode"
:visit-task-id="visitTaskId"
:reading-category="readingCategory"
:is-exists-clinical-data="isExistsClinicalData"
/>
<div v-if="isShow && readingCategory && readingCategory=== 1" class="reading-wrapper">
<el-tabs v-model="activeName" :before-leave="beforeLeave">
<!-- 阅片 -->
<el-tab-pane :label="$t('trials:reading:tabTitle:review')" name="read">
<VisitReview
:trial-id="trialId"
:subject-id="subjectId"
:subject-code="subjectCode"
:visit-task-id="visitTaskId"
:task-blind-name="taskBlindName"
:reading-category="readingCategory"
:readingTool="readingTool"
:criterionType="criterionType"
:isReadingShowSubjectInfo="isReadingShowSubjectInfo"
:is-reading-task-view-in-order="isReadingTaskViewInOrder"
:iseCRFShowInDicomReading="iseCRFShowInDicomReading"
/>
</el-tab-pane>
<!-- 报告 -->
<el-tab-pane :label="$t('trials:reading:tabTitle:report')" name="report" v-if="!iseCRFShowInDicomReading">
<Report
v-if="tabs.includes('report')"
ref="reportPage"
:trialId="trialId"
:visit-task-id="visitTaskId"
:subject-id="subjectId"
:readingTool="readingTool"
:criterionType="criterionType"
:is-reading-task-view-in-order="isReadingTaskViewInOrder"
/>
</el-tab-pane>
</el-tabs>
</div>
<!-- 全局阅片 -->
<GlobalReview
v-else-if="isShow && readingCategory && readingCategory === 2"
@@ -83,6 +108,7 @@ import { getNextTask, readClinicalData } from '@/api/trials'
import store from '@/store'
import { changeURLStatic } from '@/utils/history.js'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import Report from './components/Report'
import VisitReview from './components/VisitReview'
import GlobalReview from '@/views/trials/trials-panel/reading/global-review'
import AdReview from '@/views/trials/trials-panel/reading/ad-review'
@@ -93,6 +119,7 @@ export default {
name: 'NoneDicomReading',
components: {
VisitReview,
Report,
AdReview,
GlobalReview,
OncologyReview,
@@ -113,18 +140,21 @@ export default {
isExistsClinicalData: false,
isNeedReadClinicalData: false,
isReadClinicalData: false,
iseCRFShowInDicomReading: false,
criterionType: null,
readingTool: null,
isNewSubject: null,
dialogVisible: false,
dialogH: 0,
isShow: false
isShow: false,
activeName:'',
tabs: []
}
},
mounted() {
DicomEvent.$on('getNextTask', () => {
this.getTaskInfo()
})
})
this.trialId = this.$router.currentRoute.query.trialId
this.subjectCode = this.$router.currentRoute.query.subjectCode
this.subjectId = this.$router.currentRoute.query.subjectId
@@ -169,6 +199,10 @@ export default {
// var token = getToken()
// window.location.href = `/noneDicomReading?trialId=${this.trialId}&subjectCode=${res.Result.SubjectCode}&subjectId=${res.Result.SubjectId}&isReadingShowPreviousResults=${this.isReadingShowPreviousResults}&isReadingShowSubjectInfo=${this.isReadingShowSubjectInfo}&criterionType=${this.criterionType}&readingTool=${this.readingTool}&isNewSubject=1&isReadingTaskViewInOrder=${res.Result.IsReadingTaskViewInOrder}&TokenKey=${token}`
// }
if (res.Result.ReadingCategory === 1) {
this.activeName = 'read'
this.tabs = [this.activeName]
}
this.subjectId = res.Result.SubjectId
this.visitTaskId = res.Result.VisitTaskId
this.subjectCode = res.Result.SubjectCode
@@ -176,7 +210,8 @@ export default {
this.isExistsClinicalData = res.Result.IsExistsClinicalData
this.isReadClinicalData = res.Result.IsReadClinicalData
this.isNeedReadClinicalData = res.Result.IsNeedReadClinicalData
this.iseCRFShowInDicomReading = res.Result.IseCRFShowInDicomReading
this.isReadingTaskViewInOrder = res.Result.IsReadingTaskViewInOrder
this.isReadingShowSubjectInfo = res.Result.IsReadingShowSubjectInfo
this.isReadingShowPreviousResults = res.Result.IsReadingShowPreviousResults
this.digitPlaces = res.Result.DigitPlaces
@@ -201,7 +236,21 @@ export default {
} catch (e) {
this.loading = false
}
}
},
beforeLeave(activeName, oldActiveName) {
if (!this.tabs.includes(activeName)) {
this.tabs.push(activeName)
}
if (oldActiveName === 'read') {
this.$nextTick(() => {
if (this.$refs.reportPage) {
// DicomEvent.$emit('getReportInfo', true)
this.$refs.reportPage.setScrollTop(1)
}
})
}
return Promise.resolve(true)
},
}
}
</script>
@@ -221,7 +270,38 @@ export default {
padding: 10px;
}
}
.reading-wrapper{
width: 100%;
height: 100%;
padding: 0 10px;
box-sizing: border-box;
/deep/.el-tabs{
box-sizing: border-box;
height: 100%;
display: flex;
flex-direction: column;
.el-tabs__item{
// color: #fff;
}
.el-tabs__header{
height: 50px;
margin:0px;
box-sizing: border-box;
}
.el-tabs__content{
flex: 1;
margin:0px;
box-sizing: border-box;
}
.el-tabs__item{
// color: #fff;
}
.el-tab-pane{
height: 100%;
}
}
}
/deep/ .dialog-container{
margin-top: 50px !important;
@@ -11,6 +11,7 @@
<!-- '表单问题' -->
<el-form-item :label="$t('trials:readingUnit:readingCriterion:title:formQs')">
<QuestionsList
:ref="`questionList${trialReadingCriterionId}`"
v-if="form.FormType===1"
:trial-reading-criterion-id="trialReadingCriterionId"
:list="readingInfo.TrialQuestionList"
@@ -170,6 +171,9 @@ export default {
})
})
},
getQuestionLength() {
return this.$refs['questionList' + this.trialReadingCriterionId].tblList.length
},
reloadArbitrationRules() {
this.$emit('reloadArbitrationRules')
},
@@ -131,6 +131,7 @@
<el-form-item
:label="$t('trials:processCfg:form:IsAdditionalAssessment')"
prop="IsAdditionalAssessment"
v-if="CriterionType === 1"
>
<el-radio-group
v-model="form.IsAdditionalAssessment"
@@ -370,11 +370,11 @@ export default {
isCheck: readingRules,
msg: this.$t("trials:readingUnit:readingRules"), // ''
});
// var readingCriterions = await this.$refs['readingCriterions' + this.TrialReadingCriterionId][0].handleSave(false)
// isCheckList.push({
// isCheck: readingCriterions,
// msg: ''
// })
var qsLength = this.$refs['readingCriterions' + this.TrialReadingCriterionId][0].getQuestionLength()
isCheckList.push({
isCheck: qsLength > 0,
msg: this.$t('trials:readingUnit:readingCriterion')
})
if (
this.$refs["globalReading" + this.TrialReadingCriterionId] &&
this.$refs["globalReading" + this.TrialReadingCriterionId]
@@ -497,12 +497,16 @@ export default {
});
},
reloadArbitrationRules() {
this.$refs[
if (this.$refs[
"arbitrationRules" + this.TrialReadingCriterionId
][0].getList();
this.$refs[
"arbitrationRules" + this.TrialReadingCriterionId
][0].getTrialJudgyInfo();
]) {
this.$refs[
"arbitrationRules" + this.TrialReadingCriterionId
][0].getList();
this.$refs[
"arbitrationRules" + this.TrialReadingCriterionId
][0].getTrialJudgyInfo();
}
},
},
};
@@ -1004,7 +1004,7 @@ export default {
}
}
item.key = o ? ((o[v.Code] && o[v.Code] !== null && o[v.Code] !== '' || o[v.Code] !== 0) ? o[v.Code] : '--') : '--'
item.Enum = v.ValueCN
item.Enum = this.$i18n.locale === 'zh' ? v.ValueCN : v.Value
item.DataType = v.DataType
item.IsTableFiled = v.IsTableFiled
this[auditData].push(item)
@@ -1019,21 +1019,21 @@ export default {
if (row.OptType === 'Add' || row.OptType === 'Init') {
item = {
key: v.Code,
Enum: v.ValueCN,
Enum: this.$i18n.locale === 'zh' ? v.ValueCN : v.Value,
newValue: obj[v.Code] ? obj[v.Code] : '--',
oldValue: ''
}
} else if (row.OptType === 'Delete') {
item = {
key: v.Code,
Enum: v.ValueCN,
Enum: this.$i18n.locale === 'zh' ? v.ValueCN : v.Value,
oldValue: obj[v.Code] ? obj[v.Code] : '--',
newValue: '--'
}
} else {
item = {
key: v.Code,
Enum: v.ValueCN,
Enum: this.$i18n.locale === 'zh' ? v.ValueCN : v.Value,
newValue: obj[v.Code] ? obj[v.Code] : '--',
oldValue: upObj[v.Code].length > 0 ? upObj[v.Code] : '--'
}
@@ -1051,21 +1051,21 @@ export default {
if (row.OptType === 'Add' || row.OptType === 'Init') {
item = {
key: o[v.ChildDataLabel],
Enum: o[v.ChildDataLabel],
Enum: this.$i18n.locale === 'zh' ? o[v.ChildDataLabel] : o[v.ChildDataEnLabel] ? o[v.ChildDataEnLabel] : o[v.ChildDataLabel],
newValue: o[v.ChildDataValue] ? (o[v.ChildDataValue] ? o[v.ChildDataValue] : '--') : '--',
oldValue: ''
}
} else if (row.OptType === 'Delete') {
item = {
key: o[v.ChildDataLabel],
Enum: o[v.ChildDataLabel],
Enum: this.$i18n.locale === 'zh' ? o[v.ChildDataLabel] : o[v.ChildDataEnLabel] ? o[v.ChildDataEnLabel] : o[v.ChildDataLabel],
oldValue: o[v.ChildDataValue] ? (o[v.ChildDataValue] ? o[v.ChildDataValue] : '--') : '--',
newValue: '--'
}
} else {
item = {
key: o[v.ChildDataLabel],
Enum: o[v.ChildDataLabel],
Enum: this.$i18n.locale === 'zh' ? o[v.ChildDataLabel] : o[v.ChildDataEnLabel] ? o[v.ChildDataEnLabel] : o[v.ChildDataLabel],
newValue: o[v.ChildDataValue] ? (o[v.ChildDataValue] ? o[v.ChildDataValue] : '--') : '--',
oldValue: uo ? uo[v.ChildDataValue] : '--'
}
@@ -1098,7 +1098,7 @@ export default {
item.IsTableFiled = v.IsTableFiled
item.DataType = v.DataType
item.key = v.Code
item.Enum = v.ValueCN
item.Enum = this.$i18n.locale === 'zh' ? v.ValueCN : v.Value
this[auditData].push(item)
return
}
@@ -1107,14 +1107,14 @@ export default {
var body = []
v.TableConfigList.forEach((j, i) => {
if (j.IsFixedColumn) {
head.push({IsPicture: j.IsPicture, headName: j.FixedColumnName, IsMerge: j.IsMerge, ColumnName: j.ColumnName, ColumnValue: j.ColumnValue, ListName: j.ListName, MergeColumnName: j.IsMerge ? j.MergeColumnName : null, ChildrenList: []})
head.push({IsPicture: j.IsPicture, headName: this.$i18n.locale === 'zh' ? j.FixedColumnName : j.FixedColumnEnName, IsMerge: j.IsMerge, ColumnName: j.ColumnName, ColumnValue: j.ColumnValue, ListName: j.ListName, MergeColumnName: j.IsMerge ? this.$i18n.locale === 'zh' ? j.MergeColumnName : j.MergeColumnEnName : null, ChildrenList: []})
} else {
if (j.ListName) {
obj[v.Code][0][j.ListName].forEach((x, o) => {
head.push({IsPicture: j.IsPicture, headName: x[j.ColumnName], IsMerge: j.IsMerge, ColumnName: j.ColumnName, ColumnValue: j.ColumnValue, ListName: j.ListName, MergeColumnName: j.IsMerge ? j.MergeColumnName : null, ChildrenList: []})
head.push({IsPicture: j.IsPicture, headName: x[this.$i18n.locale === 'zh' ? j.ColumnName : j.ColumnEnName ? j.ColumnEnName : j.ColumnName], IsMerge: j.IsMerge, ColumnName: j.ColumnName, ColumnValue: j.ColumnValue, ListName: j.ListName, MergeColumnName: j.IsMerge ? this.$i18n.locale === 'zh' ? j.MergeColumnName : j.MergeColumnEnName : null, ChildrenList: []})
})
} else {
head.push({IsPicture: j.IsPicture, headName: j.ColumnName, IsMerge: j.IsMerge, ColumnName: j.ColumnName, ColumnValue: j.ColumnValue, ListName: j.ListName, MergeColumnName: j.IsMerge ? j.MergeColumnName : null, ChildrenList: []})
head.push({IsPicture: j.IsPicture, headName: this.$i18n.locale === 'zh' ? j.ColumnName : j.ColumnEnName ? j.ColumnEnName : j.ColumnName, IsMerge: j.IsMerge, ColumnName: j.ColumnName, ColumnValue: j.ColumnValue, ListName: j.ListName, MergeColumnName: j.IsMerge ? this.$i18n.locale === 'zh' ? j.MergeColumnName : j.MergeColumnEnName : null, ChildrenList: []})
}
}
})
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -16,7 +16,7 @@ const name = process.env.NODE_ENV === 'usa' ? 'LILI' : defaultSettings.title ||
// eslint-disable-next-line no-undef
module.exports = {
// lintOnSave: false,
transpileDependencies: ['@cornerstonejs', 'minio'],
transpileDependencies: ['@cornerstonejs', 'minio', '@aws-sdk', '@smithy'],
publicPath: process.env.NODE_ENV === 'development' || process.env.VUE_APP_OSS_CONFIG_BUCKET === 'zyypacs-usa' ? process.env.VUE_APP_BASE_PATH : `${process.env.VUE_FILE_PATH}${process.env.VUE_APP_OSS_PATH}${distDate}/`,
// publicPath: '/',
outputDir: 'dist',