v1.7.0发布

uat_us
DESKTOP-6C3NK6N\WXS 2024-10-14 17:05:51 +08:00
commit a2939e52b2
152 changed files with 21788 additions and 6522 deletions

View File

@ -2,7 +2,7 @@
ENV = 'usa'
NODE_ENV = 'usa'
# base public path
VUE_APP_BASE_PATH = 'https://ei-med-s3-uat-code.s3.amazonaws.com/2024-09-14/'
VUE_APP_BASE_PATH = 'https://ei-med-s3-uat-code.s3.amazonaws.com/2024-10-14/'
# 是否开启登陆限制 true:是 false:否
VUE_APP_LOGIN_FOR_PERMISSION = true

View File

@ -2,7 +2,7 @@
ENV = 'usa'
NODE_ENV = 'usa'
# base public path
VUE_APP_BASE_PATH = 'https://ei-med-s3-code.s3.amazonaws.com/2024-09-05/'
VUE_APP_BASE_PATH = 'https://ei-med-s3-code.s3.amazonaws.com/2024-10-14/'
# 是否开启登陆限制 true:是 false:否
VUE_APP_LOGIN_FOR_PERMISSION = true

View File

@ -28,11 +28,13 @@
"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",
"cornerstone-tools": "^6.0.10",
"cornerstone-wado-image-loader": "^4.13.2",
"crypto-js": "^4.2.0",
"dcmjs": "^0.29.8",
"dicom-parser": "^1.8.9",
"dicomedit": "^0.1.0",
@ -43,6 +45,7 @@
"hammerjs": "^2.0.8",
"html2canvas": "^1.4.1",
"js-md5": "^0.7.3",
"jsencrypt": "^3.3.2",
"jszip": "^3.7.1",
"moment": "^2.27.0",
"node-polyfill-webpack-plugin": "^2.0.1",

167
public/mitm.html Normal file
View File

@ -0,0 +1,167 @@
<!--
mitm.html is the lite "man in the middle"
This is only meant to signal the opener's messageChannel to
the service worker - when that is done this mitm can be closed
but it's better to keep it alive since this also stops the sw
from restarting
The service worker is capable of intercepting all request and fork their
own "fake" response - wish we are going to craft
when the worker then receives a stream then the worker will tell the opener
to open up a link that will start the download
-->
<script>
// This will prevent the sw from restarting
let keepAlive = () => {
keepAlive = () => {}
var ping = location.href.substr(0, location.href.lastIndexOf('/')) + '/ping'
var interval = setInterval(() => {
if (sw) {
sw.postMessage('ping')
} else {
fetch(ping).then(res => res.text(!res.ok && clearInterval(interval)))
}
}, 10000)
}
// message event is the first thing we need to setup a listner for
// don't want the opener to do a random timeout - instead they can listen for
// the ready event
// but since we need to wait for the Service Worker registration, we store the
// message for later
let messages = []
window.onmessage = evt => messages.push(evt)
let sw = null
let scope = ''
function registerWorker() {
return navigator.serviceWorker.getRegistration('./').then(swReg => {
return swReg || navigator.serviceWorker.register('sw.js', { scope: './' })
}).then(swReg => {
const swRegTmp = swReg.installing || swReg.waiting
scope = swReg.scope
return (sw = swReg.active) || new Promise(resolve => {
swRegTmp.addEventListener('statechange', fn = () => {
if (swRegTmp.state === 'activated') {
swRegTmp.removeEventListener('statechange', fn)
sw = swReg.active
resolve()
}
})
})
})
}
// Now that we have the Service Worker registered we can process messages
function onMessage (event) {
let { data, ports, origin } = event
// It's important to have a messageChannel, don't want to interfere
// with other simultaneous downloads
if (!ports || !ports.length) {
throw new TypeError("[StreamSaver] You didn't send a messageChannel")
}
if (typeof data !== 'object') {
throw new TypeError("[StreamSaver] You didn't send a object")
}
// the default public service worker for StreamSaver is shared among others.
// so all download links needs to be prefixed to avoid any other conflict
data.origin = origin
// if we ever (in some feature versoin of streamsaver) would like to
// redirect back to the page of who initiated a http request
data.referrer = data.referrer || document.referrer || origin
// pass along version for possible backwards compatibility in sw.js
data.streamSaverVersion = new URLSearchParams(location.search).get('version')
if (data.streamSaverVersion === '1.2.0') {
console.warn('[StreamSaver] please update streamsaver')
}
/** @since v2.0.0 */
if (!data.headers) {
console.warn("[StreamSaver] pass `data.headers` that you would like to pass along to the service worker\nit should be a 2D array or a key/val object that fetch's Headers api accepts")
} else {
// test if it's correct
// should thorw a typeError if not
new Headers(data.headers)
}
/** @since v2.0.0 */
if (typeof data.filename === 'string') {
console.warn("[StreamSaver] You shouldn't send `data.filename` anymore. It should be included in the Content-Disposition header option")
// Do what File constructor do with fileNames
data.filename = data.filename.replace(/\//g, ':')
}
/** @since v2.0.0 */
if (data.size) {
console.warn("[StreamSaver] You shouldn't send `data.size` anymore. It should be included in the content-length header option")
}
/** @since v2.0.0 */
if (data.readableStream) {
console.warn("[StreamSaver] You should send the readableStream in the messageChannel, not throught mitm")
}
/** @since v2.0.0 */
if (!data.pathname) {
console.warn("[StreamSaver] Please send `data.pathname` (eg: /pictures/summer.jpg)")
data.pathname = Math.random().toString().slice(-6) + '/' + data.filename
}
// remove all leading slashes
data.pathname = data.pathname.replace(/^\/+/g, '')
// remove protocol
let org = origin.replace(/(^\w+:|^)\/\//, '')
// set the absolute pathname to the download url.
data.url = new URL(`${scope + org}/${data.pathname}`).toString()
if (!data.url.startsWith(`${scope + org}/`)) {
throw new TypeError('[StreamSaver] bad `data.pathname`')
}
// This sends the message data as well as transferring
// messageChannel.port2 to the service worker. The service worker can
// then use the transferred port to reply via postMessage(), which
// will in turn trigger the onmessage handler on messageChannel.port1.
const transferable = data.readableStream
? [ ports[0], data.readableStream ]
: [ ports[0] ]
if (!(data.readableStream || data.transferringReadable)) {
keepAlive()
}
return sw.postMessage(data, transferable)
}
if (window.opener) {
// The opener can't listen to onload event, so we need to help em out!
// (telling them that we are ready to accept postMessage's)
window.opener.postMessage('StreamSaver::loadedPopup', '*')
}
if (navigator.serviceWorker) {
registerWorker().then(() => {
window.onmessage = onMessage
messages.forEach(window.onmessage)
})
}
// FF v102 just started to supports transferable streams, but still needs to ping sw.js
// even tough the service worker dose not have to do any kind of work and listen to any
// messages... #305
keepAlive()
</script>

130
public/sw.js Normal file
View File

@ -0,0 +1,130 @@
/* global self ReadableStream Response */
self.addEventListener('install', () => {
self.skipWaiting()
})
self.addEventListener('activate', event => {
event.waitUntil(self.clients.claim())
})
const map = new Map()
// This should be called once per download
// Each event has a dataChannel that the data will be piped through
self.onmessage = event => {
// We send a heartbeat every x second to keep the
// service worker alive if a transferable stream is not sent
if (event.data === 'ping') {
return
}
const data = event.data
const downloadUrl = data.url || self.registration.scope + Math.random() + '/' + (typeof data === 'string' ? data : data.filename)
const port = event.ports[0]
const metadata = new Array(3) // [stream, data, port]
metadata[1] = data
metadata[2] = port
// Note to self:
// old streamsaver v1.2.0 might still use `readableStream`...
// but v2.0.0 will always transfer the stream through MessageChannel #94
if (event.data.readableStream) {
metadata[0] = event.data.readableStream
} else if (event.data.transferringReadable) {
port.onmessage = evt => {
port.onmessage = null
metadata[0] = evt.data.readableStream
}
} else {
metadata[0] = createStream(port)
}
map.set(downloadUrl, metadata)
port.postMessage({ download: downloadUrl })
}
function createStream (port) {
// ReadableStream is only supported by chrome 52
return new ReadableStream({
start (controller) {
// When we receive data on the messageChannel, we write
port.onmessage = ({ data }) => {
if (data === 'end') {
return controller.close()
}
if (data === 'abort') {
controller.error('Aborted the download')
return
}
controller.enqueue(data)
}
},
cancel (reason) {
console.log('user aborted', reason)
port.postMessage({ abort: true })
}
})
}
self.onfetch = event => {
const url = event.request.url
// this only works for Firefox
if (url.endsWith('/ping')) {
return event.respondWith(new Response('pong'))
}
const hijacke = map.get(url)
if (!hijacke) return null
const [ stream, data, port ] = hijacke
map.delete(url)
// Not comfortable letting any user control all headers
// so we only copy over the length & disposition
const responseHeaders = new Headers({
'Content-Type': 'application/octet-stream; charset=utf-8',
// To be on the safe side, The link can be opened in a iframe.
// but octet-stream should stop it.
'Content-Security-Policy': "default-src 'none'",
'X-Content-Security-Policy': "default-src 'none'",
'X-WebKit-CSP': "default-src 'none'",
'X-XSS-Protection': '1; mode=block',
'Cross-Origin-Embedder-Policy': 'require-corp'
})
let headers = new Headers(data.headers || {})
if (headers.has('Content-Length')) {
responseHeaders.set('Content-Length', headers.get('Content-Length'))
}
if (headers.has('Content-Disposition')) {
responseHeaders.set('Content-Disposition', headers.get('Content-Disposition'))
}
// data, data.filename and size should not be used anymore
if (data.size) {
console.warn('Depricated')
responseHeaders.set('Content-Length', data.size)
}
let fileName = typeof data === 'string' ? data : data.filename
if (fileName) {
console.warn('Depricated')
// Make filename RFC5987 compatible
fileName = encodeURIComponent(fileName).replace(/['()]/g, escape).replace(/\*/g, '%2A')
responseHeaders.set('Content-Disposition', "attachment; filename*=UTF-8''" + fileName)
}
event.respondWith(new Response(stream, { headers: responseHeaders }))
port.postMessage({ debug: 'Download started' })
}

View File

@ -8,11 +8,11 @@ export function requestPackageAndAnonymizImage(params) {
})
}
// 获取影像上传列表
export function getSubjectImageUploadList(params) {
export function getSubjectImageUploadList(data) {
return request({
url: '/DownloadAndUpload/getSubjectImageUploadList',
method: 'get',
params
method: 'post',
data
})
}
// 预上传
@ -40,3 +40,67 @@ export function deleteTaskStudy(params) {
params
})
}
// 获取iqc下载文件信息
export function getCRCUploadedStudyInfo(data) {
return request({
url: '/DownloadAndUpload/getCRCUploadedStudyInfo',
method: 'post',
data
})
}
// 获取ir阅片和任务列表
export function getSubjectImageDownloadSelectList(data) {
return request({
url: '/DownloadAndUpload/getSubjectImageDownloadSelectList',
method: 'post',
data
})
}
// 获取ir阅片和任务下载文件信息
export function getIRReadingDownloadStudyInfo(data) {
return request({
url: '/DownloadAndUpload/getIRReadingDownloadStudyInfo',
method: 'post',
data
})
}
// 获取ir阅片和任务非dicom上传列表
export function getIRUploadTaskNoneDicomStudyList(data) {
return request({
url: '/DownloadAndUpload/getIRUploadTaskNoneDicomStudyList',
method: 'post',
data
})
}
// 校验上传影像
export function verifyIRStudyAllowUpload(data) {
return request({
url: '/DownloadAndUpload/verifyIRStudyAllowUpload',
method: 'post',
data
})
}
// 影像下载成功确认
export function downloadImageSuccess(params) {
return request({
url: '/DownloadAndUpload/downloadImageSuccess',
method: 'post',
params
})
}
// 影像下载记录列表
export function getTrialDownloadList(data) {
return request({
url: '/DownloadAndUpload/getTrialDownloadList',
method: 'post',
data
})
}
// 修改后处理检查类型
export function updateTaskStudyModality(params) {
return request({
url: '/DownloadAndUpload/updateTaskStudyModality',
method: 'put',
params
})
}

View File

@ -1,5 +1,5 @@
import request from '@/utils/request'
import requestDownload from '@/utils/request-download'
export function addBaseLineLesion(param) {
return request({
url: '/report/addBaseLineLesion',
@ -133,9 +133,11 @@ export function AddAdjudicationReport(param) {
})
}
export function getVisitStudyList(trialId, subjectVisitId, isReading) {
export function getVisitStudyList(trialId, subjectVisitId, isReading, visitTaskId) {
let url = `/SubjectVisit/getVisitStudyList/${trialId}/${subjectVisitId}/${isReading}`
url = visitTaskId ? `${url}?visitTaskId=${visitTaskId}` : url
return request({
url: `/SubjectVisit/getVisitStudyList/${trialId}/${subjectVisitId}/${isReading}`,
url: url,
method: 'get'
})
}
@ -194,3 +196,58 @@ export function resetReadingTask(param) {
data: param
})
}
export function getTaskUploadedDicomStudyList(param) {
return request({
url: `/DownloadAndUpload/getTaskUploadedDicomStudyList`,
method: 'post',
data: param
})
}
export function getIVUSTemplate(param) {
return requestDownload({
url: '/IVUSCalculate/getIVUSTemplate',
method: 'post',
responseType: 'blob',
data: param
})
}
export function uploadIVUSTemplate(param) {
return request({
url: `/IVUSCalculate/uploadIVUSTemplate`,
method: 'post',
data: param
})
}
export function getOCTFCTTemplate(param) {
return requestDownload({
url: '/OCTCalculate/getOCTFCTTemplate',
method: 'post',
responseType: 'blob',
data: param
})
}
export function uploadOCTFCTTemplate(param) {
return request({
url: `/OCTCalculate/uploadOCTFCTTemplate`,
method: 'post',
data: param
})
}
export function getOCTLipidAngleTemplate(param) {
return requestDownload({
url: '/OCTCalculate/getOCTLipidAngleTemplate',
method: 'post',
responseType: 'blob',
data: param
})
}
export function uploadOCTLipidAngleTemplate(param) {
return request({
url: `/OCTCalculate/uploadOCTLipidAngleTemplate`,
method: 'post',
data: param
})
}

View File

@ -1364,9 +1364,9 @@ export function getForwardList(param) {
})
}
export function getNoneDicomStudyList(subjectVisitId, sudyId = '', isFilterZip = false) {
export function getNoneDicomStudyList(subjectVisitId, sudyId = '', isFilterZip = false, visitTaskId = '') {
return request({
url: `/NoneDicomStudy/getNoneDicomStudyList?subjectVisitId=${subjectVisitId}&nonedicomStudyId=${sudyId}&isFilterZip=${isFilterZip}`,
url: `/NoneDicomStudy/getNoneDicomStudyList?subjectVisitId=${subjectVisitId}&nonedicomStudyId=${sudyId}&isFilterZip=${isFilterZip}&visitTaskId=${visitTaskId}`,
method: 'get'
})
}

View File

@ -4,7 +4,8 @@ export function login(data) {
return request({
url: '/user/login',
method: 'post',
data
data,
ENCRYPT: true
})
}
export function loginOut(params) {
@ -185,3 +186,10 @@ export function sendMFAEmail(params) {
params
})
}
// 获取公钥
export function getPublicKey() {
return request({
url: `/user/getPublicKey`,
method: 'get',
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 61 KiB

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-id="stack.imageIds[stack.currentImageIdIndex]" @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,8 @@ export default {
mousePosition: { x: '', y: '', mo: '' },
markers: { top: '', right: '', bottom: '', left: '' },
orientationMarkers: [],
originalMarkers: []
originalMarkers: [],
dcmTag: { visible: false, title: this.$t('trials:dicom-tag:title') }
}
},
@ -364,8 +376,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
@ -636,7 +648,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 +660,7 @@ export default {
},
toggleClipPlay() {
if(this.loading) return
if (this.loading) return
if (this.toolState.clipPlaying) {
cornerstoneTools.stopClip(this.canvas)
this.toolState.clipPlaying = false
@ -668,7 +680,7 @@ export default {
resetWwwc() {
this.toolState.viewportInvert = false
var viewport = cornerstone.getViewport(this.canvas)
viewport.invert = false
// viewport.invert = false
var image = cornerstone.getImage(this.canvas)
viewport.voi.windowWidth = image.windowWidth
viewport.voi.windowCenter = image.windowCenter
@ -747,6 +759,9 @@ export default {
var uid = cornerstone.getImage(this.canvas).data.string('x00080018')
cornerstoneTools.SaveAs(this.canvas, `${uid}.png`)
},
showTags() {
this.dcmTag.visible = true
},
fitToWindow() {
if (this.stack.seriesNumber) {
cornerstone.fitToWindow(this.canvas)

View File

@ -0,0 +1,287 @@
<template>
<div class="dcm-tag">
<el-input
v-model="search"
size="mini"
:placeholder="$t('trials:dicom-tag:keywords')"
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 * as cornerstone from 'cornerstone-core'
import dicomParser from 'dicom-parser'
export default {
name: 'DicomTags',
props: {
imageId: {
type: String,
required: true
}
},
data() {
return {
list: [],
idx: 0,
search: ''
}
},
async mounted() {
const image = await cornerstone.loadAndCacheImage(this.imageId)
var dataSet = dicomParser.parseDicom(image.data.byteArray)
var output = []
this.dumpDataSet(dataSet, output)
this.list = output
},
methods: {
filterList(list) {
if (list.length === 0) return []
if (!this.search) {
return list
} else {
const search = isNaN(parseFloat(this.search)) ? this.search.toLowerCase() : String(this.search)
const arr = list.filter(data => {
if (data.tagCode && data.tagCode.toLowerCase().includes(search)) {
return data
} else if (data.tagName && data.tagName.toLowerCase().includes(search)) {
return data
} else if (data.value) {
let v = ''
if (!isNaN(parseFloat(data.value))) {
v = String(data.value)
} else {
v = data.value.toLowerCase()
}
if (v.includes(search)) {
return data
}
}
})
return arr
}
},
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()}`
if (!tag) {
const group = element.tag.substring(1, 5)
const el = element.tag.substring(5, 9)
elementObject.tagCode = ('(' + group + ',' + el + ')').toUpperCase()
} else {
elementObject.tagCode = tag ? tag.tag : ''
elementObject.tagName = tag ? tag.name : ''
}
elementObject.tagLength = element.length
elementObject.value = ''
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
} else {
if (tag !== undefined) {
vr = tag.vr
}
}
elementObject.vr = 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
}
console.log(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>

View File

@ -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

View File

@ -9,7 +9,7 @@
/>
<!-- <embed v-else-if="fileType.indexOf('pdf') !== -1" :src="filePath+'#toolbar=0'" style="width: 100%; height: 100%"> -->
<!-- <iframe v-else-if="fileType.indexOf('pdf') !== -1" :src="filePath+'#toolbar=0'" width="100%" height="100%" frameborder="0" /> -->
<iframe v-else-if="fileType.indexOf('pdf') !== -1" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${filePath}?userName=${currentUser}`" width="100%" height="100%" frameborder="0" crossorigin="anonymous" />
<iframe v-else-if="fileType.indexOf('pdf') !== -1" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${filePath}?userName=${currentUser}&COMPANY=${COMPANY}`" width="100%" height="100%" frameborder="0" crossorigin="anonymous" />
<!-- <pdf-->
<!-- v-else-if="fileType.indexOf('pdf') !== -1"-->
<!-- :src="`/static/pdfjs/web/viewer.html?file=${filePath}`">-->
@ -40,7 +40,8 @@ export default {
},
data() {
return {
currentUser: zzSessionStorage.getItem('userName')
currentUser: zzSessionStorage.getItem('userName'),
COMPANY:process.env.VUE_APP_COMPANY_NAME
}
},
mounted() {

View File

@ -0,0 +1,511 @@
<template>
<el-dialog
:visible.sync="visible"
:fullscreen="true"
:close-on-click-modal="false"
:before-close="beforeClose"
:append-to-body="true"
v-loading="btnLoading"
class="downloadDicomAndNonedicom"
>
<span slot="title">{{ title }}</span>
<div class="top">
<span>{{ $t('download:top:title') }}</span>
<div class="btnBox">
<el-button
type="primary"
size="mini"
v-if="hasDicom"
@click.stop="getIRReadingDownloadStudyInfo('dicom')"
>
{{ $t('download:button:downloadDicom') }}
</el-button>
<el-button
type="primary"
size="mini"
v-if="hasNonedicom"
@click.stop="getIRReadingDownloadStudyInfo('noneDicom')"
>
{{ $t('download:button:downloadNonedicom') }}
</el-button>
<el-button
type="primary"
size="mini"
v-if="hasDicom || hasNonedicom"
@click.stop="getIRReadingDownloadStudyInfo('all')"
>
{{ $t('download:button:downloadAll') }}
</el-button>
</div>
</div>
<div class="tip">
<i class="el-icon-warning-outline"></i>
<div v-html="$t('download:tip:message')"></div>
</div>
<!--上传列表@selection-change="handleSelectionChange"-->
<el-table
ref="dicomFilesTable"
v-adaptive="{ bottomOffset: 85 }"
height="100"
:data="list"
:loading="loading"
class="dicomFiles-table"
@sort-change="handleSortByColumn"
:default-sort="{ prop: 'TaskBlindName', order: 'descending' }"
>
<!-- <el-table-column
type="selection"
width="55"
:selectable="handleSelectable"
/> -->
<el-table-column type="index" width="40" />
<!--受试者-->
<el-table-column
:label="$t('download:table:subjectCode')"
min-width="130"
prop="SubjectCode"
show-overflow-tooltip
/>
<!--任务名称-->
<el-table-column
:label="$t('download:table:taskName')"
min-width="130"
show-overflow-tooltip
prop="TaskBlindName"
sortable="custom"
/>
<!--检查类型-->
<el-table-column
:label="$t('download:table:studyType')"
min-width="130"
show-overflow-tooltip
prop="IsDicom"
sortable="custom"
>
<template slot-scope="scope">
<span>{{ $fd('IsDicom', scope.row.IsDicom) }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('download:table:orginalStudyListNum')"
min-width="150"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-button
v-if="
(scope.row.IsDicom &&
scope.row.DicomStudyList &&
scope.row.DicomStudyList.length >= 1) ||
(!scope.row.IsDicom &&
scope.row.NoneDicomStudyList &&
scope.row.NoneDicomStudyList.length >= 1)
"
type="text"
@click="handleOpenDialog(scope.row)"
>
<span>{{
scope.row.IsDicom
? scope.row.DicomStudyList.length
: scope.row.NoneDicomStudyList.length
}}</span>
</el-button>
<span v-else>0</span>
</template>
</el-table-column>
<el-table-column
:label="$t('common:action:action')"
fixed="right"
width="150"
>
<template slot-scope="scope">
<!--预览--->
<el-button
circle
icon="el-icon-view"
:title="$t('download:button:preview')"
@click.stop="preview(scope.row)"
/>
<!--下载--->
<el-button
circle
icon="el-icon-download"
:title="$t('download:button:download')"
@click.stop="getIRReadingDownloadStudyInfo('one', scope.row)"
/>
</template>
</el-table-column>
</el-table>
<study-view
v-if="model_cfg.visible"
:model_cfg="model_cfg"
:modelList="modelList"
:bodyPart="bodyPart"
:IsDicom="IsDicom"
:visitTaskId="modelTaskId"
/>
</el-dialog>
</template>
<script>
import {
getSubjectImageDownloadSelectList,
getIRReadingDownloadStudyInfo,
downloadImageSuccess,
} from '@/api/load.js'
import studyView from '@/components/uploadDicomAndNonedicom/study-view.vue'
import store from '@/store'
import { downLoadFile } from '@/utils/stream.js'
import { getToken } from '@/utils/auth'
let defaultSearchData = () => {
return {
SubjectId: null,
TrialReadingCriterionId: null,
SubjectCode: null,
Asc: false,
SortField: 'TaskBlindName',
}
}
export default {
name: 'downloadDicomAndNonedicom',
components: {
'study-view': studyView,
},
props: {
visible: {
required: true,
type: Boolean,
default: false,
},
SubjectId: {
type: String,
default: '',
},
TaskId: {
type: String,
default: '',
},
SubjectCode: {
type: String,
default: '',
},
Criterion: {
type: Object,
default: () => {
return {}
},
},
},
data() {
return {
title: null,
loading: false,
list: [],
searchData: defaultSearchData(),
btnLoading: false,
hasDicom: false,
hasNonedicom: false,
//
model_cfg: {
visible: false,
showClose: true,
width: '1000px',
title: '',
appendToBody: true,
},
modelList: [],
IsDicom: true,
open: null,
downloadId: null,
IsReadingTaskViewInOrder: 0, //
bodyPart: [],
modelTaskId: null,
}
},
async mounted() {
this.getList()
this.bodyPart = await this.$getBodyPart(this.$route.query.trialId)
this.title = `Download Images${this.SubjectCode}${this.Criterion.TrialReadingCriterionName}`
},
beforeDestroy() {
store.dispatch('trials/setUnLock', false)
},
methods: {
beforeClose() {
this.$emit('update:visible', false)
},
//
async getList() {
try {
this.searchData.SubjectId = this.SubjectId
this.searchData.TrialReadingCriterionId =
this.Criterion.TrialReadingCriterionId
this.searchData.SubjectCode = this.SubjectCode
if (this.TaskId) {
this.searchData.VisitTaskId = this.TaskId
}
this.loading = true
let res = await getSubjectImageDownloadSelectList(this.searchData)
this.loading = false
if (res.IsSuccess) {
this.list = res.Result
this.hasDicom = this.list.some((item) => item.IsDicom)
this.hasNonedicom = this.list.some((item) => !item.IsDicom)
}
} catch (err) {
this.loading = false
console.log(err)
}
},
//
async getIRReadingDownloadStudyInfo(type, row) {
try {
let data = {
SubjectId: this.SubjectId || this.list[0].SubjectId,
TrialReadingCriterionId: this.Criterion.TrialReadingCriterionId,
SubjectCode: this.SubjectCode || this.list[0].SubjectCode,
SubjectVisitTaskList: [],
DicomStudyIdList: [],
NoneDicomStudyIdList: [],
}
if (type === 'dicom' || type === 'all') {
this.list.forEach((item) => {
if (
item.IsDicom &&
item.DicomStudyList &&
item.DicomStudyList.length > 0
) {
data.SubjectVisitTaskList.push({
SubjectvisitId: item.SourceSubjectVisitId,
TaskId: item.VisitTaskId,
})
let arr = item.DicomStudyList.map((d) => d.Id)
data.DicomStudyIdList = [...data.DicomStudyIdList, ...arr]
}
})
}
if (type === 'noneDicom' || type === 'all') {
this.list.forEach((item) => {
if (
!item.IsDicom &&
item.NoneDicomStudyList &&
item.NoneDicomStudyList.length > 0
) {
data.SubjectVisitTaskList.push({
SubjectvisitId: item.SourceSubjectVisitId,
TaskId: item.VisitTaskId,
})
let arr = item.NoneDicomStudyList.map((d) => d.Id)
data.NoneDicomStudyIdList = [...data.NoneDicomStudyIdList, ...arr]
}
})
}
if (type === 'one') {
if (
row.IsDicom &&
row.DicomStudyList &&
row.DicomStudyList.length > 0
) {
data.SubjectVisitTaskList.push({
SubjectvisitId: row.SourceSubjectVisitId,
TaskId: row.VisitTaskId,
})
let arr = row.DicomStudyList.map((d) => d.Id)
data.DicomStudyIdList = [...data.DicomStudyIdList, ...arr]
}
if (
!row.IsDicom &&
row.NoneDicomStudyList &&
row.NoneDicomStudyList.length > 0
) {
data.SubjectVisitTaskList.push({
SubjectvisitId: row.SourceSubjectVisitId,
TaskId: row.VisitTaskId,
})
let arr = row.NoneDicomStudyList.map((d) => d.Id)
data.NoneDicomStudyIdList = [...data.NoneDicomStudyIdList, ...arr]
}
}
this.btnLoading = true
let res = await getIRReadingDownloadStudyInfo(data)
this.btnLoading = false
if (res.IsSuccess) {
this.downloadId = res.OtherInfo.PreDownloadId
this.IsReadingTaskViewInOrder = res.OtherInfo.IsReadingTaskViewInOrder
this.downloadImage(res.Result)
}
} catch (err) {
this.btnLoading = false
console.log(err)
}
},
//
async downloadImage(data) {
try {
let { files, name, fileType } = this.formatDownloadFile(data)
let res = await downLoadFile(files, name, 'zip')
if (res && this.downloadId) {
this.downloadImageSuccess()
}
} catch (err) {
console.log(err)
}
},
//
formatDownloadFile(list) {
let files = [],
name = `${list[0].SubjectCode}_${new Date().getTime()}.zip`
if (this.IsReadingTaskViewInOrder === 1) {
name = `${list[0].SubjectCode}_${list[0].TaskBlindName}.zip`
}
if (this.IsReadingTaskViewInOrder === 0) {
name = `${list[0].TaskBlindName}.zip`
}
list.forEach((data) => {
if (data.StudyList && data.StudyList.length > 0) {
let StudyList = data.StudyList
StudyList.forEach((study) => {
if (study.SeriesList.length > 0) {
study.SeriesList.forEach((series) => {
if (series.InstancePathList.length > 0) {
series.InstancePathList.forEach((instance) => {
let fileName = instance.Path.split('/').pop()
let obj = {
name: `${data.SubjectCode}/${
data.TaskBlindName
}/${this.$fd('IsDicom', true)}/${
study.StudyCode
}/${fileName}`,
url: this.OSSclientConfig.basePath + instance.Path,
}
if (this.IsReadingTaskViewInOrder === 0) {
obj = {
name: `${data.TaskBlindName}/${this.$fd(
'IsDicom',
true
)}/${fileName}`,
url: this.OSSclientConfig.basePath + instance.Path,
}
}
files.push(obj)
})
}
})
}
})
}
if (data.NoneDicomStudyList && data.NoneDicomStudyList.length > 0) {
let NoneDicomStudyList = data.NoneDicomStudyList
//
NoneDicomStudyList.forEach((study) => {
if (study.FileList.length > 0) {
study.FileList.forEach((item) => {
let obj = {
name: `${data.SubjectCode}/${data.TaskBlindName}/${this.$fd(
'IsDicom',
false
)}/${study.StudyCode}/${item.FileName}`,
url: this.OSSclientConfig.basePath + item.Path,
}
if (this.IsReadingTaskViewInOrder === 0) {
obj = {
name: `${data.TaskBlindName}/${this.$fd(
'IsDicom',
false
)}/${item.FileName}`,
url: this.OSSclientConfig.basePath + item.Path,
}
}
files.push(obj)
})
}
})
}
})
return { files, name }
},
//
async downloadImageSuccess() {
try {
let params = {
TrialImageDownloadId: this.downloadId,
}
await downloadImageSuccess(params)
} catch (err) {
console.log(err)
}
},
handleOpenDialog(item) {
this.model_cfg.title = `${item.SubjectCode || ''} > ${item.TaskBlindName}`
if (item.IsDicom) {
this.modelList = item.DicomStudyList
} else {
this.modelList = item.NoneDicomStudyList
this.modelList.forEach((data) => {
data.SourceSubjectVisitId = item.SourceSubjectVisitId
})
}
this.modelTaskId = item.VisitTaskId
this.IsDicom = item.IsDicom
this.model_cfg.visible = true
},
//
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
},
preview(row) {
if (!row.IsDicom) {
this.handlePreviewNoneDicomFiles(row)
} else {
this.handleViewReadingImages(row)
}
},
// Dicom
handlePreviewNoneDicomFiles(row) {
if (this.open) {
this.open.close()
}
let trialId = this.$route.query.trialId
var token = getToken()
const routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}`,
})
this.open = window.open(routeData.href, '_blank')
},
//
handleViewReadingImages(row) {
if (this.open) {
this.open.close()
}
var token = getToken()
let trialId = this.$route.query.trialId
const routeData = this.$router.resolve({
path: `/showvisitdicoms?page=download&trialId=${trialId}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&isReading=1&TokenKey=${token}`,
})
this.open = window.open(routeData.href, '_blank')
},
},
}
</script>
<style lang="scss" scoped>
.top {
display: flex;
align-items: center;
justify-content: space-between;
margin: 10px 0;
}
.tip {
display: flex;
align-items: flex-start;
margin-top: 5px;
i {
margin: 3px 5px 0 0;
}
}
</style>

View File

@ -52,7 +52,7 @@
class="el-icon-warning-outline"
style="color: #f56c6c; font-size: 24px"
></i>
<span>{{ $t("feedBack:imgfail:tip") }}</span>
<span>{{ $t('feedBack:imgfail:tip') }}</span>
</p>
<!-- 问题反馈 -->
<el-form-item
@ -140,7 +140,7 @@
<div slot="footer" v-if="type !== 'detail' || isImgfail">
<!-- 取消 -->
<el-button size="small" @click.stop="cancel">
{{ $t("feedBack:button:cancel") }}
{{ $t('feedBack:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button
@ -149,21 +149,21 @@
@click.stop="save"
:loading="loading"
>
{{ $t("feedBack:button:save") }}
{{ $t('feedBack:button:save') }}
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import uploadImage from "./uploadImage.vue";
import uploadImage from './uploadImage.vue'
import {
addOrUpdateUserFeedBack,
getUserFeedBackInfo,
batchUpdateFeedBackState,
} from "@/api/trials.js";
} from '@/api/trials.js'
export default {
name: "FB",
name: 'FB',
components: { uploadImage },
data() {
return {
@ -195,131 +195,131 @@ export default {
QuestionType: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: "blur",
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
],
QuestionDescription: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: "blur",
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
{
validator: (rule, value, callback) => {
if (value.length < 5) {
callback(
new Error(this.$t("feedBack:ruleMessage:lengthLimitMin5"))
);
new Error(this.$t('feedBack:ruleMessage:lengthLimitMin5'))
)
}
callback();
callback()
},
trigger: "blur",
trigger: 'blur',
},
],
},
isUpload: false,
};
}
},
computed: {
level() {
if (this.hasPermi(["role:dev", "role:admin"])) {
return 9;
if (this.hasPermi(['role:dev', 'role:admin'])) {
return 9
}
if (this.hasPermi(["role:pm"])) {
return 8;
if (this.hasPermi(['role:pm', 'role:apm'])) {
return 8
}
if (this.hasPermi(["role:ir", "role:crc"])) {
return 7;
if (this.hasPermi(['role:ir', 'role:crc'])) {
return 7
}
return 0;
return 0
},
QuestionTypeOptions() {
if (this.level > 7) {
return [
...this.$d.FeedBackTypeToCRC.filter((item) => item.value > 0),
...this.$d.FeedBackTypeToIR,
];
]
}
if (this.hasPermi(["role:ir"])) {
return this.$d.FeedBackTypeToIR;
if (this.hasPermi(['role:ir'])) {
return this.$d.FeedBackTypeToIR
}
if (this.hasPermi(["role:crc"])) {
return this.$d.FeedBackTypeToCRC;
if (this.hasPermi(['role:crc'])) {
return this.$d.FeedBackTypeToCRC
}
return [];
return []
},
},
methods: {
open(data) {
let { type, trialId, Id, visitTaskId, SubjectVisitId } = data;
this.type = type;
this.trialId = trialId;
this.Id = Id;
this.visitTaskId = visitTaskId;
this.SubjectVisitId = SubjectVisitId;
let { type, trialId, Id, visitTaskId, SubjectVisitId } = data
this.type = type
this.trialId = trialId
this.Id = Id
this.visitTaskId = visitTaskId
this.SubjectVisitId = SubjectVisitId
if (visitTaskId) {
this.isImgfail = true;
this.isImgfail = true
}
this.setTypeOption();
this.setTypeOption()
if (!Id) {
this.title = this.setTitle();
this.title = this.setTitle()
}
if (Id || visitTaskId) {
this.getInfo(Id, visitTaskId);
this.getInfo(Id, visitTaskId)
}
this.visible = true;
this.visible = true
},
cancel() {
this.visible = false;
this.$emit("closed");
this.visible = false
this.$emit('closed')
},
async save() {
try {
let validate = await this.$refs.feedBackForm.validate();
if (!validate) return;
let validate = await this.$refs.feedBackForm.validate()
if (!validate) return
if (this.trialId) {
this.form.TrialId = this.trialId;
this.form.TrialId = this.trialId
}
if (this.visitTaskId) {
this.form.VisitTaskId = this.visitTaskId;
this.form.VisitTaskId = this.visitTaskId
}
if (this.SubjectVisitId) {
this.form.SubjectVisitId = this.SubjectVisitId;
this.form.SubjectVisitId = this.SubjectVisitId
}
if (this.Id) {
this.form.Id = this.Id;
this.form.Id = this.Id
}
this.loading = true;
let res = await addOrUpdateUserFeedBack(this.form);
this.loading = false;
this.loading = true
let res = await addOrUpdateUserFeedBack(this.form)
this.loading = false
if (res.IsSuccess) {
this.$emit("success");
this.$message.success(this.$t("feedBack:save:success"));
this.cancel();
this.$emit('success')
this.$message.success(this.$t('feedBack:save:success'))
this.cancel()
}
} catch (err) {
this.loading = false;
console.log(err);
this.loading = false
console.log(err)
}
},
//
async changeState() {
if (this.isImgfail) return;
if (this.isImgfail) return
try {
let data = {
IdList: [this.Id],
State: this.form.State,
};
let res = await batchUpdateFeedBackState(data);
}
let res = await batchUpdateFeedBackState(data)
if (res.IsSuccess) {
this.$message.success(this.$t("feedBack:changeState:success"));
this.$emit("success");
this.$message.success(this.$t('feedBack:changeState:success'))
this.$emit('success')
// this.cancel();
this.getInfo(this.Id, this.visitTaskId);
this.getInfo(this.Id, this.visitTaskId)
}
} catch (err) {
console.log(err);
console.log(err)
}
},
//
@ -328,56 +328,56 @@ export default {
let data = {
Id,
visitTaskId,
};
let res = await getUserFeedBackInfo(data);
}
let res = await getUserFeedBackInfo(data)
if (res.IsSuccess && res.Result) {
this.form = res.Result;
this.Id = res.Result.Id;
this.form = res.Result
this.form.ScreenshotList = this.form.ScreenshotList || []
this.Id = res.Result.Id
if (res.Result.State > 0) {
this.isStateChange = false;
this.isStateChange = false
}
if (res.Result.VisitTaskId) {
this.visitTaskId = res.Result.VisitTaskId;
this.visitTaskId = res.Result.VisitTaskId
}
if (res.Result.SubjectVisitId) {
this.SubjectVisitId = res.Result.SubjectVisitId;
this.SubjectVisitId = res.Result.SubjectVisitId
}
let code = this.$fd("UserType", res.Result.UserTypeEnum);
this.title = this.setTitle(code, res.Result.FeedBackFullName);
let code = this.$fd('UserType', res.Result.UserTypeEnum)
this.title = this.setTitle(code, res.Result.FeedBackFullName)
if (visitTaskId) {
code = `${res.Result.SubjectCode}-${res.Result.SubjectVisitName}`;
this.title = this.setTitle(code, res.Result.FeedBackFullName);
code = `${res.Result.SubjectCode}-${res.Result.SubjectVisitName}`
this.title = this.setTitle(code, res.Result.FeedBackFullName)
}
this.$refs.uploadImage.initFileList(res.Result.ScreenshotList);
this.$refs.uploadImage.initFileList(res.Result.ScreenshotList)
}
} catch (err) {
console.log(err);
console.log(err)
}
},
setTypeOption() {
if (!this.trialId) return (this.options = []);
if (this.hasPermi(["role:ir"]))
return (this.option = this.$d.FeedBackTypeToIR);
if (this.hasPermi(["role:crc"]))
return (this.option = this.$d.FeedBackTypeToCRC);
if (!this.trialId) return (this.options = [])
if (this.hasPermi(['role:ir']))
return (this.option = this.$d.FeedBackTypeToIR)
if (this.hasPermi(['role:crc']))
return (this.option = this.$d.FeedBackTypeToCRC)
},
setTitle(code, name) {
console.log(code, name);
if (this.hasPermi(["role:pm"]) && this.visitTaskId) {
return `${this.$t("feedBack:form:title:pm2")}(${code}/${name})`;
if (this.hasPermi(['role:pm']) && this.visitTaskId) {
return `${this.$t('feedBack:form:title:pm2')}(${code}/${name})`
}
if (this.hasPermi(["role:pm", "role:dev", "role:admin"])) {
return `${this.$t("feedBack:form:title:pm")}(${code},${name})`;
if (this.hasPermi(['role:pm', 'role:dev', 'role:admin'])) {
return `${this.$t('feedBack:form:title:pm')}(${code},${name})`
}
if (this.hasPermi(["role:ir", "role:crc"]) && this.type === "detail") {
return `${this.$t("feedBack:form:detail:title")}`;
if (this.hasPermi(['role:ir', 'role:crc']) && this.type === 'detail') {
return `${this.$t('feedBack:form:detail:title')}`
}
if (this.hasPermi(["role:ir", "role:crc"])) {
return `${this.$t("feedBack:form:title")}`;
if (this.hasPermi(['role:ir', 'role:crc'])) {
return `${this.$t('feedBack:form:title')}`
}
},
},
};
}
</script>
<style lang="scss" scoped>
.tip {

View File

@ -58,14 +58,14 @@
</div>
</template>
<script>
const type = "Statement of Work";
const type = 'Statement of Work'
export default {
name: "UploadImage",
name: 'UploadImage',
props: {
path: {
required: true,
default: () => {
return [];
return []
},
},
disabled: {
@ -78,14 +78,14 @@ export default {
},
trialId: {
type: String,
default: "",
default: '',
},
},
data() {
return {
fileList: [],
btnDisabled: false,
};
}
},
methods: {
remove(file, fileList) {
@ -93,115 +93,113 @@ export default {
},
fileToBlob(file) {
// FileReader
const reader = new FileReader();
const reader = new FileReader()
return new Promise((resolve) => {
// FileReader load
reader.addEventListener("load", (e) => {
let blob;
if (typeof e.target.result === "object") {
blob = new Blob([e.target.result]);
reader.addEventListener('load', (e) => {
let blob
if (typeof e.target.result === 'object') {
blob = new Blob([e.target.result])
} else {
blob = e.target.result;
blob = e.target.result
}
resolve(blob);
});
resolve(blob)
})
// FileReader ArrayBuffer File
reader.readAsArrayBuffer(file);
});
reader.readAsArrayBuffer(file)
})
},
// oss
async uploadToOSS(name, file) {
try {
let defaultPath = "/System/FeedBack/";
let defaultPath = '/System/FeedBack/'
if (this.trialId) {
defaultPath = `/${this.trialId}/FeedBack/`;
defaultPath = `/${this.trialId}/FeedBack/`
}
let res = await this.OSSclient.put(`${defaultPath}${name}`, file);
return res;
let res = await this.OSSclient.put(`${defaultPath}${name}`, file)
return res
} catch (err) {
console.log(err);
return false;
console.log(err)
return false
}
},
initFileList(list) {
if (list && list.length > 0) {
this.fileList = [];
this.fileList = []
list.forEach((item, index) => {
let name = item.split("/");
let name = item.split('/')
this.fileList.push({
name: name[name.length - 1],
path: item,
fullPath: this.OSSclientConfig.basePath + item,
url: this.OSSclientConfig.basePath + item,
uid: `${name[name.length - 1]}${index}`,
});
});
console.log(this.fileList);
})
})
}
},
async uploadFile(param) {
var fileName = param.file.name;
this.$emit("update:isUpload", true);
this.btnDisabled = true;
var fileName = param.file.name
this.$emit('update:isUpload', true)
this.btnDisabled = true
this.fileList.push({
url: param.file.url,
path: fileName,
uid: param.file.uid,
});
let file = await this.fileToBlob(param.file);
let res = await this.uploadToOSS(fileName, file);
this.btnDisabled = false;
let index = this.fileList.findIndex(
(item) => item.uid === param.file.uid
);
})
let file = await this.fileToBlob(param.file)
let res = await this.uploadToOSS(fileName, file)
this.btnDisabled = false
let index = this.fileList.findIndex((item) => item.uid === param.file.uid)
if (!res) {
if (index >= 0) {
this.fileList.splice(index, 1);
this.fileList.splice(index, 1)
}
return this.$emit("update:isUpload", false);
return this.$emit('update:isUpload', false)
}
this.fileList[index].url = this.OSSclientConfig.basePath + res.name;
this.fileList[index].path = res.name;
this.$emit("update:path", [...this.path, res.name]);
this.$emit("update:isUpload", false);
return false;
let name = this.$getObjectName(res.url)
this.fileList[index].url = this.OSSclientConfig.basePath + name
this.fileList[index].path = name
this.$emit('update:path', [...this.path, res.name])
this.$emit('update:isUpload', false)
return false
},
beforeUpload(file, fileList) {
const isValidFile = this.fileValid(file.name, ["png", "jpg", "jpeg"]);
const isValidFile = this.fileValid(file.name, ['png', 'jpg', 'jpeg'])
if (isValidFile) {
// this.fileList = [];
} else {
this.$alert(this.$t("feedBack:uploadImg:format"));
return false;
this.$alert(this.$t('feedBack:uploadImg:format'))
return false
}
},
handlePreview(file) {
file.fullPath ? window.open(file.fullPath, "_blank") : "";
file.fullPath ? window.open(file.fullPath, '_blank') : ''
},
handleExceed(files, fileList) {
this.$message.warning(`Upload is currently limited to 1 file`);
this.$message.warning(`Upload is currently limited to 1 file`)
},
fileValid(fileName, typeArr) {
var extendName = fileName
.substring(fileName.lastIndexOf(".") + 1)
.toLocaleLowerCase();
.substring(fileName.lastIndexOf('.') + 1)
.toLocaleLowerCase()
if (typeArr.indexOf(extendName) > -1) {
return true;
return true
} else {
return false;
return false
}
},
handlePictureCardPreview(file) {
this.$refs[file.url].$viewer.show();
this.$refs[file.url].$viewer.show()
},
handleRemove(file) {
let index = this.fileList.findIndex((item) => item.uid === file.uid);
this.fileList.splice(index, 1);
let arr = this.fileList.map((item) => item.path);
this.$emit("update:path", arr);
let index = this.fileList.findIndex((item) => item.uid === file.uid)
this.fileList.splice(index, 1)
let arr = this.fileList.map((item) => item.path)
this.$emit('update:path', arr)
},
},
};
}
</script>
<style>
.upload-container .el-upload--text {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
<template>
<el-dialog
:visible.sync="visible"
:fullscreen="true"
:close-on-click-modal="false"
:before-close="beforeClose"
:append-to-body="true"
class="uploadDicomAndNonedicom"
>
<span slot="title">{{ title }}</span>
<el-tabs type="border-card" v-model="activeName">
<el-tab-pane
:label="$t('uploadDicomAndNonedicom:label:dicom')"
name="dicom"
>
<dicomFile
v-if="activeName === 'dicom'"
:SubjectId="SubjectId"
:SubjectCode="SubjectCode"
:Criterion="Criterion"
:TaskId="VisitTaskId"
:isUpload.sync="isUpload"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder"
/>
</el-tab-pane>
<el-tab-pane
:label="$t('uploadDicomAndNonedicom:label:nonedicom')"
name="nonedicom"
>
<nonedicomFile
v-if="activeName === 'nonedicom'"
:SubjectId="SubjectId"
:SubjectCode="SubjectCode"
:Criterion="Criterion"
:VisitTaskId="VisitTaskId"
:isUpload.sync="isUpload"
/>
</el-tab-pane>
</el-tabs>
</el-dialog>
</template>
<script>
import dicomFile from './dicomFile.vue'
import nonedicomFile from './nonedicomFile.vue'
import store from '@/store'
export default {
name: 'uploadDicomAndNonedicom',
components: { dicomFile, nonedicomFile },
props: {
visible: {
required: true,
type: Boolean,
default: false,
},
SubjectId: {
type: String,
default: '',
},
SubjectCode: {
type: String,
default: '',
},
Criterion: {
type: Object,
default: () => {
return {}
},
},
VisitTaskId: {
type: String,
default: '',
},
isReadingTaskViewInOrder: {
type: Number,
default: 0,
},
},
data() {
return {
title: '',
activeName: 'dicom',
isUpload: false,
}
},
mounted() {
this.title = `Upload Images${this.SubjectCode}${this.Criterion.TrialReadingCriterionName}`
store.dispatch('trials/setUnLock', true)
},
methods: {
beforeClose() {
if (
this.$route.path !== '/trials/trials-panel/reading/readingTask' &&
this.isUpload
) {
return window.location.reload()
}
this.$emit('update:visible', false)
},
},
}
</script>
<style lang="scss" scoped>
::v-deep.el-tabs--left .el-tabs__header.is-left {
margin-right: 0;
}
.uploadDicomAndNonedicom {
::v-deep.el-tabs--border-card > .el-tabs__header .el-tabs__item {
color: #909399;
}
::v-deep.el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active {
color: #428bca;
background-color: #fff;
border-right-color: #dcdfe6;
border-left-color: #dcdfe6;
}
}
</style>

View File

@ -0,0 +1,919 @@
<template>
<div class="nonedicomFile">
<div class="top">
<span>{{ $t('upload:nonedicom:title') }}</span>
<div class="tip">
<i class="el-icon-warning-outline"></i>
<div v-html="$t('upload:nonedicom:tip:message')"></div>
</div>
</div>
<!--检查列表-->
<el-table
:data="list"
style="width: 100%"
v-adaptive="{ bottomOffset: 60 }"
:loading="loading"
@sort-change="handleSortByColumn"
:default-sort="{ prop: 'TaskBlindName', order: 'descending' }"
>
<!--受试者-->
<el-table-column
prop="SubjectCode"
:label="$t('upload:nonedicom:table:subject')"
/>
<!--任务名称-->
<el-table-column
prop="TaskBlindName"
:label="$t('upload:nonedicom:table:taskName')"
sortable="custom"
/>
<!--检查类型-->
<el-table-column
prop="Modality"
:label="$t('upload:nonedicom:table:molityType')"
sortable="custom"
>
</el-table-column>
<!--检查部位-->
<el-table-column
prop="BodyPart"
:label="$t('upload:nonedicom:table:bodyPart')"
sortable="custom"
/>
<!--原文件数-->
<el-table-column
prop="FileCount"
:label="$t('upload:nonedicom:table:fileCount')"
>
<template slot-scope="scope">
<el-popover
v-if="scope.row.FileCount"
trigger="click"
placement="bottom"
>
<el-table :data="scope.row.FileList" height="300" size="small">
<!-- 文件名称 -->
<el-table-column
prop="FileName"
:label="$t('trials:audit:table:nonDicomsFileName')"
width="200"
/>
<el-table-column
prop="FileSize"
:label="$t('trials:audit:table:nonDicomsFileSize')"
width="100"
>
<template slot-scope="scope">
<span>{{
scope.row.FileSize && scope.row.FileSize > 0
? `${(scope.row.FileSize / 1024 / 1024).toFixed(2)}MB`
: ''
}}</span>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" width="120">
<template slot-scope="files">
<!-- 预览 -->
<viewer
:ref="files.row.Path"
style="margin: 0 10px"
:images="[`${OSSclientConfig.basePath}${files.row.Path}`]"
>
<el-button
circle
icon="el-icon-view"
:title="$t('upload:nonedicom:button:preview')"
:disabled="
files.row.FileType &&
files.row.FileType.indexOf('zip') >= 0
"
@click.native.prevent="previewFile(files.row)"
/>
<img
v-show="false"
crossorigin="anonymous"
:src="`${OSSclientConfig.basePath}${files.row.Path}`"
alt="Image"
/>
</viewer>
</template>
</el-table-column>
</el-table>
<div slot="reference" class="name-wrapper">
<el-button type="text">
{{ scope.row.FileCount }}
</el-button>
</div>
</el-popover>
<span v-else>{{ scope.row.FileCount }}</span>
</template>
</el-table-column>
<!--后处理文件数-->
<el-table-column
prop="UploadedFileCount"
:label="$t('upload:nonedicom:table:uploadFileCount')"
>
<template slot-scope="scope">
<el-popover
v-if="scope.row.UploadedFileCount"
trigger="click"
placement="bottom"
>
<el-table
:data="scope.row.UploadedFileList"
height="300"
size="small"
>
<!-- 文件名称 -->
<el-table-column
prop="FileName"
:label="$t('trials:audit:table:nonDicomsFileName')"
width="200"
/>
<el-table-column
prop="FileSize"
:label="$t('trials:audit:table:nonDicomsFileSize')"
width="100"
>
<template slot-scope="scope">
<span>{{
scope.row.FileSize && scope.row.FileSize > 0
? `${(scope.row.FileSize / 1024 / 1024).toFixed(2)}MB`
: ''
}}</span>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" width="120">
<template slot-scope="files">
<!-- 预览 -->
<viewer
:ref="files.row.Path"
style="margin: 0 10px"
:images="[`${OSSclientConfig.basePath}${files.row.Path}`]"
>
<el-button
circle
icon="el-icon-view"
:title="$t('upload:nonedicom:button:preview')"
:disabled="
files.row.FileType &&
files.row.FileType.indexOf('zip') >= 0
"
@click.native.prevent="previewFile(files.row)"
/>
<img
v-show="false"
crossorigin="anonymous"
:src="`${OSSclientConfig.basePath}${files.row.Path}`"
alt="Image"
/>
</viewer>
</template>
</el-table-column>
</el-table>
<div slot="reference" class="name-wrapper">
<el-button type="text">
{{ scope.row.UploadedFileCount }}
</el-button>
</div>
</el-popover>
<span v-else>{{ scope.row.UploadedFileCount }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('common:action:action')"
fixed="right"
width="180"
>
<template slot-scope="scope">
<!--预览--->
<el-button
circle
:disabled="scope.row.UploadedFileCount <= 0"
icon="el-icon-view"
:title="$t('upload:nonedicom:button:preview')"
@click.stop="handlePreviewNoneDicomFiles(scope.row)"
/>
<!--上传--->
<el-button
circle
icon="el-icon-upload2"
:title="$t('upload:nonedicom:button:upload')"
@click.native.prevent="handleUpload(scope.row)"
/>
<!--删除--->
<el-button
:disabled="
scope.row.UploadedFileCount <= 0 ||
scope.row.ReadingTaskState === 2
"
circle
icon="el-icon-delete"
:title="$t('upload:nonedicom:button:delete')"
@click.stop="remove(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<!-- 预览单个图像 -->
<el-dialog
v-if="imgObj.visible"
:visible.sync="imgObj.visible"
:title="$t('upload:nonedicom:dialogTitle:preview')"
append-to-body
width="565px"
>
<div
v-loading="imgObj.loading"
class="base-modal-body"
style="border: 2px solid #ccc; padding: 10px"
>
<el-image
:src="`${OSSclientConfig.basePath}${imgObj.url}`"
crossorigin="anonymous"
fit="fit"
style="height: 500px; width: 500px"
@error="imgObj.loading = false"
@load="imgObj.loading = false"
/>
</div>
</el-dialog>
<!-- 上传非dicom文件 -->
<el-dialog
v-if="uploadVisible"
:visible.sync="uploadVisible"
:close-on-click-modal="false"
:title="$t('trials:uploadNonDicoms:dialogTitle:upload')"
width="800px"
append-to-body
custom-class="base-dialog-wrapper"
@close="resetFileDiaolg"
>
<!-- 多文件上传 -->
<form id="inputForm" ref="uploadForm">
<el-divider content-position="left">{{
$t('trials:uploadNonDicoms:label:fileType')
}}</el-divider>
<div class="form-group">
<div class="upload" style="margin-right: 10px">
<input
multiple="multiple"
webkitdirectory=""
directory
accept="*/*"
type="file"
name="uploadFolder"
class="select-file"
title=""
@change="beginScanFiles($event)"
v-if="!btnLoading"
/>
<div class="btn-select">
{{ $t('trials:uploadNonDicoms:button:selectFolder') }}
</div>
</div>
<div class="upload">
<input
class="select-file"
multiple=""
:accept="faccept.join(',')"
type="file"
name="uploadFile"
title=""
@change="beginScanFiles($event)"
v-if="!btnLoading"
/>
<div class="btn-select">
{{ $t('trials:uploadNonDicoms:button:select') }}
</div>
</div>
<!-- <span style="margin-left: 10px">{{
$store.state.trials.uploadTip
}}</span> -->
</div>
</form>
<!-- 文件列表 -->
<el-table
ref="filesTable"
:data="fileList"
class="dicomFiles-table"
height="300"
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
width="55"
:selectable="(row, index) => row.status !== 2 && !btnLoading"
/>
<el-table-column type="index" width="50" />
<!-- 文件名称 -->
<el-table-column
prop="name"
:label="$t('trials:uploadNonDicoms:table:fileName')"
min-width="100"
/>
<!-- 文件大小 -->
<el-table-column
prop="size"
:label="$t('trials:uploadNonDicoms:table:fileSize')"
>
<template slot-scope="scope">
<span>{{
scope.row.size && scope.row.size > 0
? `${(scope.row.size / 1024 / 1024).toFixed(2)}MB`
: ''
}}</span>
</template>
</el-table-column>
<!-- 文件类型 -->
<el-table-column
prop="type"
:label="$t('trials:uploadNonDicoms:table:fileType')"
/>
<!-- 上传状态 -->
<el-table-column
prop="status"
:label="$t('trials:uploadNonDicoms:table:uploadStatus')"
min-width="100"
>
<template slot-scope="scope">
<el-tag
:type="['warning', 'info', 'success', 'danger'][scope.row.status]"
v-if="scope.row.status || scope.row.status === 0"
>{{ $fd('NoneDicomUploadStatus', scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('trials:uploadNonDicoms:table:failedFileCount')"
min-width="150"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-progress
color="#409eff"
:percentage="
((scope.row.uploadFileSize * 100) / scope.row.size).toFixed(2) *
1
"
/>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')">
<template slot-scope="scope">
<el-button
size="mini"
icon="el-icon-delete"
circle
:disabled="btnLoading"
:title="$t('trials:crcUpload:action:delete')"
@click="handleRemoveFile(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<div style="text-align: right; padding: 10px 0px">
<span style="margin-right: 10px">{{
$store.state.trials.uploadTip
}}</span>
<el-button
size="small"
type="primary"
:disabled="selectArr.length == 0"
:loading="btnLoading"
@click="beginUpload"
>
{{ $t('trials:uploadNonDicoms:action:upload') }}
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
getIRUploadTaskNoneDicomStudyList,
deleteTaskStudy,
} from '@/api/load.js'
import { preArchiveStudy, uploadNoneDicomFile } from '@/api/trials'
import store from '@/store'
import { getToken } from '@/utils/auth'
let defaultSearchData = () => {
return {
SubjectId: null,
TrialReadingCriterionId: null,
SubjectCode: null,
Asc: false,
SortField: 'TaskBlindName',
}
}
export default {
name: 'nonedicomFile',
props: {
SubjectId: {
type: String,
default: '',
},
SubjectCode: {
type: String,
default: '',
},
Criterion: {
type: Object,
default: () => {
return {}
},
},
VisitTaskId: {
type: String,
default: '',
},
isUpload: {
type: Boolean,
default: false,
},
},
data() {
return {
loading: false,
list: [],
searchData: defaultSearchData(),
imgObj: { url: '', visible: false, loading: false },
uploadVisible: false,
fileList: [],
selectArr: [],
successFileList: [],
isFail: false,
faccept: ['.jpg', '.jpeg', '.png', '.bmp', '.zip'],
fCount: 0,
btnLoading: false,
currentRow: {},
studyMonitorId: null,
open: null,
}
},
watch: {
isFail() {
if (this.isFail) {
this.$confirm(
this.$t('trials:visit:crcUpload:nomDicomFiles:failUpload'),
{
type: 'warning',
distinguishCancelAndClose: true,
confirmButtonText: this.$t('common:button:confirm'),
cancelButtonText: this.$t('common:button:cancel'),
}
)
.then(() => {
this.beginUpload()
})
.catch((err) => {
this.btnLoading = false
console.log(err)
})
}
},
btnLoading() {
store.dispatch('trials/setUnLock', this.btnLoading)
},
},
created() {
this.getList()
},
beforeDestroy() {
store.dispatch('trials/setUnLock', false)
},
methods: {
async getList() {
try {
this.searchData.SubjectId = this.SubjectId
this.searchData.TrialReadingCriterionId =
this.Criterion.TrialReadingCriterionId
this.searchData.SubjectCode = this.SubjectCode
if (this.VisitTaskId) {
this.searchData.VisitTaskId = this.VisitTaskId
}
this.loading = true
let res = await getIRUploadTaskNoneDicomStudyList(this.searchData)
this.loading = false
if (res.IsSuccess) {
this.list = res.Result
}
} catch (err) {
this.loading = false
console.log(err)
}
},
//
beginScanFiles(e) {
var files = [...e.target.files]
var sameFiles = []
files.forEach((file) => {
var extendName = file.name
.substring(file.name.lastIndexOf('.'))
.toLocaleLowerCase()
if (
this.faccept.indexOf(extendName) !== -1 &&
this.fileList.findIndex((v) => v.name === file.name) > -1
) {
sameFiles.push(file.name)
}
})
var scope = this
if (sameFiles.length > 0) {
const h = this.$createElement
var msg = this.$t(
'trials:uploadNonDicoms:message:exsitSameFile'
).replace('xxx', sameFiles.join(', '))
this.$msgbox({
message: h('div', { style: 'maxHeight:300px;overflow: auto;' }, [
h('p', null, msg),
h(
'p',
null,
this.$t('trials:uploadNonDicoms:message:isContinueUpload')
),
]),
type: 'warning',
showCancelButton: true,
})
.then(() => {
scope.pendingUploadQuene(files)
})
.catch(() => {
scope.resetUploadForm()
})
} else {
scope.pendingUploadQuene(files)
}
},
pendingUploadQuene(files) {
for (var i = 0; i < files.length; ++i) {
const fileName = files[i].name
var extendName = fileName
.substring(fileName.lastIndexOf('.'))
.toLocaleLowerCase()
if (this.faccept.indexOf(extendName) !== -1) {
files[i].id = `${files[i].lastModified}${files[i].name}`
let obj = {
name: files[i].name,
size: files[i].size,
type: extendName.split('.')[1],
status: 0,
file: files[i],
id: `${files[i].lastModified}${files[i].name}`,
fileType: files[i].type,
uploadFileSize: 0,
}
this.fileList.push(obj)
this.$refs.filesTable.toggleRowSelection(obj, true)
}
}
this.resetUploadForm()
},
handleRemoveFile(row) {
this.$confirm(this.$t('trials:uploadNonDicoms:message:delete'), {
type: 'warning',
distinguishCancelAndClose: true,
})
.then(() => {
this.fileList.splice(
this.fileList.findIndex((item) => item.id === row.id),
1
)
let flag = this.successFileList.some((item) => item.id === row.id)
if (flag) {
this.successFileList.splice(
this.successFileList.findIndex((item) => item.id === row.id),
1
)
}
})
.catch(() => {})
},
resetUploadForm() {
this.$nextTick(() => {
this.$refs.uploadForm.reset()
})
},
//
handleUpload(row) {
this.fileList = []
this.uploadVisible = true
this.fCount = 0
this.currentRow = { ...row }
},
resetFileDiaolg() {
this.btnLoading = false
this.uploadVisible = false
this.selectArr = []
this.successFileList = []
this.OSSclient.close()
},
//
previewFile(row) {
// window.open(row.FullFilePath, '_blank')
// this.imgObj.url = row.FullFilePath || row.Path
// this.imgObj.loading = true
// this.imgObj.visible = true
this.$refs[row.Path].$viewer.show()
},
// Dicom
handlePreviewNoneDicomFiles(row) {
if (this.open) {
this.open.close()
}
let trialId = this.$route.query.trialId
var token = getToken()
const routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${row.SourceSubjectVisitId}&studyId=${row.Id}&visitTaskId=${row.VisitTaskId}&TokenKey=${token}`,
})
this.open = window.open(routeData.href, '_blank')
},
//
async remove(item) {
try {
let confirm = await this.$confirm(
this.$t('upload:nonedicom:confirm:delMessage'),
{
type: 'warning',
distinguishCancelAndClose: true,
confirmButtonText: this.$t('common:button:confirm'),
cancelButtonText: this.$t('common:button:cancel'),
}
)
if (confirm !== 'confirm') return
let params = {
VisitTaskId: item.VisitTaskId,
IsDicom: false,
NoneDicomStudyId: item.Id,
}
let res = await deleteTaskStudy(params)
if (res.IsSuccess) {
this.getList()
this.$emit('update:isUpload', true)
this.$message.success(
this.$t('trials:uploadImage:message:delSuccess')
)
}
} catch (err) {
console.log(err)
}
},
//
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
},
//
handleSelectionChange(selection) {
this.selectArr = selection
},
//
async beginUpload() {
this.btnLoading = true
this.isFail = false
const fileMaxSize = 1024 * 1024 * 1024 * 2 // 1G
var currentFilesSize = 0
this.selectArr.forEach((item) => {
currentFilesSize += item.size
})
if (currentFilesSize / fileMaxSize > 1) {
// 'Upload file size cannot exceed 1G'
this.$alert(this.$t('trials:uploadNonDicoms:message:uploadSize'))
this.btnLoading = false
} else {
this.selectArr.forEach((item) => (item.status = 0))
let num = this.selectArr.length > 6 ? 6 : this.selectArr.length
let funArr = []
let res = await preArchiveStudy({
subjectVisitId: this.currentRow.SourceSubjectVisitId,
isDicom: false,
FileCount: this.selectArr.length,
})
if (res.IsSuccess) {
this.studyMonitorId = res.Result
for (let i = 0; i < num; i++) {
funArr.push(this.handleUploadTask(this.selectArr, i))
}
if (funArr.length > 0) {
let res = await Promise.all(funArr)
}
} else {
this.isFail = true
}
}
},
//
async handleUploadTask(arr, index) {
if (!this.uploadVisible) return
let file = this.fileList.filter((item) => item.id === arr[index].id)[0]
file.status = 1
let path = `/${this.$route.query.trialId}/TaskImage/${
this.currentRow.SubjectId
}/${this.currentRow.VisitTaskId}/${this.$guid()}${file.name
.substring(file.name.lastIndexOf('.'))
.toLocaleLowerCase()}`
file.curPath = path
const fileData = await this.fileToBlob(file.file)
let res = await this.fileToOss(path, fileData, file)
if (res) {
file.status = 2
this.successFileList.push({
fileName: file.name,
filePath: this.$getObjectName(res.url),
fileFize: file.size,
fileType: file.fileType,
})
let flag = arr.every((item) => item.status === 2)
if (flag) {
let RecordPath = await this.uploadRecord(arr)
return this.submitFile(this.successFileList, RecordPath)
}
} else {
file.status = 3
}
let flag = arr.every((item) => item.status > 1)
if (flag) {
let failFileList = arr.filter((item) => item.status === 3)
if (failFileList && failFileList.length > 0) {
let RecordPath = await this.uploadRecord(arr)
this.$refs.filesTable.clearSelection()
failFileList.forEach((row) => {
row.uploadFileSize = 0
this.$refs.filesTable.toggleRowSelection(row)
})
this.isFail = true
this.submitFile(this.successFileList, RecordPath, true)
return false
}
}
let ind = arr.findIndex((item) => item.status === 0)
if (ind >= 0) {
return this.handleUploadTask(arr, ind)
} else {
return false
}
},
// fileoss
async fileToOss(path, file, item) {
try {
let res = await this.OSSclient.multipartUpload(
{
path,
file,
speed: true,
},
(percentage, checkpoint, lastPer) => {
item.uploadFileSize += checkpoint.size * (percentage - lastPer)
if (item.uploadFileSize > file.fileSize) {
item.uploadFileSize = file.fileSize
}
}
)
if (res) {
return res
} else {
return false
}
} catch (err) {
console.log(err)
return false
}
},
// Dicom
submitFile(uploadedFileList, RecordPath, isReLoad = false) {
if (!this.uploadVisible) return
this.btnLoading = true
var params = {
trialId: this.$route.query.trialId,
subjectVisitId: this.currentRow.SourceSubjectVisitId,
noneDicomStudyId: this.currentRow.Id,
studyMonitorId: this.studyMonitorId,
VisitTaskId: this.currentRow.VisitTaskId,
uploadedFileList: uploadedFileList,
}
if (RecordPath) {
params.RecordPath = RecordPath.path
params.FailedFileCount = RecordPath.Record.Failed.length
}
uploadNoneDicomFile(params)
.then((res) => {
if (!isReLoad) {
this.resetFileDiaolg()
}
this.getList()
this.$emit('update:isUpload', true)
//
this.$emit('getList')
this.$message.success(
this.$t('trials:uploadNonDicoms:message:uploadedSuccessfully')
)
})
.catch(() => {
this.btnLoading = false
})
},
//
uploadRecord(arr) {
return new Promise(async (resolve) => {
try {
let Record = {
Failed: [],
Existed: [],
Uploaded: [],
FileCount: arr.length,
}
arr.forEach((item) => {
let file = this.fileList.find((data) => data.id === item.id)
if (file.status === 2) {
Record.Uploaded.push(file.curPath)
}
if (file.status === 3) {
Record.Failed.push(file.curPath)
}
})
let text = JSON.stringify(Record)
let logJsonBlob = this.generateTxtFile(text)
let logJsonObjectName = `/${this.$route.query.trialId}/TaskImage/${this.currentRow.SubjectId}/${this.currentRow.VisitTaskId}/${this.studyMonitorId}.txt`
let logRes
try {
logRes = await this.OSSclient.put(logJsonObjectName, logJsonBlob)
if (logRes && logRes.url) {
resolve({ path: this.$getObjectName(logRes.url), Record })
} else {
resolve(false)
}
} catch (e) {
resolve(false)
}
} catch (err) {
console.log(err)
resolve(false)
}
})
},
generateTxtFile(text) {
let blob = new Blob(['\ufeff', text], { type: 'text/plain' })
return blob
},
},
}
</script>
<style lang="scss" scoped>
.top {
margin: 10px 0;
}
.upload {
display: inline-block;
height: 30px;
width: 90px;
padding: 2px 10px;
line-height: 23px;
position: relative;
text-decoration: none;
border-radius: 3px;
overflow: hidden;
text-align: center;
background: #428bca;
border-color: #428bca;
color: #fff;
.select-file {
height: 30px;
width: 90px;
position: absolute;
overflow: hidden;
left: 0;
top: 0;
opacity: 0;
font-size: 0;
}
.btn-select {
//
width: 90px;
height: 30px;
line-height: 30px;
text-align: center;
cursor: pointer;
border-radius: 24px;
overflow: hidden;
position: absolute;
top: 0;
left: 0;
pointer-events: none; //pointer-events:none穿
}
}
.tip {
display: flex;
align-items: flex-start;
margin-top: 5px;
i {
margin: 3px 5px 0 0;
}
}
</style>

View File

@ -0,0 +1,291 @@
<template>
<div v-if="model_cfg.visible">
<base-model :config="model_cfg">
<div slot="dialog-body">
<el-table :data="modelList" style="width: 100%" height="300">
<!--检查编号-->
<el-table-column
prop="StudyCode"
:label="$t('trials:uploadImage:table:StudyCode')"
/>
<!--检查类型-->
<el-table-column
prop="ModalityForEdit"
:label="$t('trials:uploadImage:table:ModalityForEdit')"
v-if="IsDicom"
/>
<!--检查模态-->
<el-table-column
prop="Modalities"
:label="$t('trials:uploadImage:table:Modalities')"
v-if="IsDicom"
/>
<!--检查部位-->
<el-table-column
prop="BodyPartForEdit"
:label="$t('trials:uploadImage:table:BodyPartForEdit')"
v-if="IsDicom"
>
<template slot-scope="scope">
<span>{{
$fd(
'Bodypart',
scope.row.BodyPartForEdit,
'Code',
{ Bodypart: bodyPart },
'Name'
)
}}</span>
</template>
</el-table-column>
<!--序列数量-->
<el-table-column
prop="SeriesCount"
:label="$t('trials:uploadImage:table:SeriesCount')"
v-if="IsDicom"
/>
<!--图像数量-->
<el-table-column
prop="InstanceCount"
:label="$t('trials:uploadImage:table:InstanceCount')"
v-if="IsDicom"
/>
<!--检查时间-->
<el-table-column
prop="StudyTime"
:label="$t('trials:uploadImage:table:StudyTime')"
v-if="IsDicom"
min-width="130"
/>
<!--检查模态-->
<el-table-column
prop="Modality"
:label="$t('trials:uploadImage:table:Modalities')"
v-if="!IsDicom"
/>
<!--检查部位-->
<el-table-column
prop="BodyPart"
:label="$t('trials:uploadImage:table:BodyPartForEdit')"
v-if="!IsDicom"
>
<template slot-scope="scope">
<span>{{
$fd(
'Bodypart',
scope.row.BodyPart,
'Code',
{ Bodypart: bodyPart },
'Name'
)
}}</span>
</template>
</el-table-column>
<!--文件数量-->
<el-table-column
prop="FileCount"
:label="$t('trials:uploadImage:table:FileCount')"
v-if="!IsDicom"
/>
<!--检查时间-->
<el-table-column
prop="ImageDate"
:label="$t('trials:uploadImage:table:StudyTime')"
v-if="!IsDicom"
min-width="130"
/>
<el-table-column
:label="$t('common:action:action')"
fixed="right"
width="80"
>
<template slot-scope="scope">
<!--预览--->
<el-button
circle
icon="el-icon-view"
:title="$t('trials:uploadImage:button:preview')"
v-if="!isUpload"
@click.stop="preview(scope.row)"
/>
<!--编辑--->
<el-button
circle
icon="el-icon-edit-outline"
:title="$t('trials:uploadImage:button:edit')"
v-else
@click.stop="openEdit(scope.row)"
/>
</template>
</el-table-column>
</el-table>
</div>
</base-model>
<!--新增检查部位-->
<base-model v-if="editModality_model.visible" :config="editModality_model">
<template slot="dialog-body">
<el-form
ref="editModalityform"
:inline="true"
:model="form"
class="demo-form-inline"
:rules="rules"
>
<el-form-item
:label="$t('trials:uploadImage:form:ModalityForEdit')"
prop="Modality"
label-width="150px"
>
<el-select v-model="form.Modality" placeholder="">
<el-option
v-for="item in TrialModality"
:key="item"
:label="item"
:value="item"
>
</el-option>
</el-select>
</el-form-item>
</el-form>
</template>
<template slot="dialog-footer">
<el-button
type="primary"
@click="updateTaskStudyModality"
:loading="loading"
>
{{ $t('common:button:confirm') }}
</el-button>
<el-button
@click="editModality_model.visible = false"
:loading="loading"
>
{{ $t('common:button:cancel') }}
</el-button>
</template>
</base-model>
</div>
</template>
<script>
import baseModel from '@/components/BaseModel'
import { getToken } from '@/utils/auth'
import { updateTaskStudyModality } from '@/api/load.js'
export default {
name: 'studyView',
props: {
model_cfg: {
type: Object,
default: () => {
return {}
},
},
bodyPart: {
type: Array,
default: () => {
return []
},
},
modelList: {
type: Array,
default: () => {
return []
},
},
TrialModality: {
type: Array,
default: () => {
return []
},
},
visitTaskId: {
type: String,
},
IsDicom: {
required: true,
type: Boolean,
default: true,
},
isUpload: {
required: true,
type: Boolean,
default: false,
},
},
components: {
'base-model': baseModel,
},
data() {
return {
form: {
Modality: null,
TaskStudyId: null,
},
editModality_model: {
visible: false,
title: this.$t('trials:uploadImage:button:edit'),
width: '500px',
appendToBody: true,
},
rules: {
Modality: [
{
required: true,
message: this.$t('trials:uploadImage:format:notModality'),
trigger: ['blur', 'change'],
},
],
},
loading: false,
}
},
methods: {
//
preview(row) {
let routeData = null
if (this.IsDicom) {
var token = getToken()
routeData = this.$router.resolve({
path: `/showdicom?studyId=${row.Id}&TokenKey=${token}&type=Study&visitTaskId=${this.visitTaskId}`,
})
} else {
let trialId = this.$route.query.trialId
var token = getToken()
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${row.SourceSubjectVisitId}&studyId=${row.Id}&TokenKey=${token}`,
})
}
window.open(routeData.href, '_blank')
},
//
openEdit(row) {
this.form.TaskStudyId = row.Id
this.form.Modality = row.ModalityForEdit
this.editModality_model.visible = true
},
//
async updateTaskStudyModality() {
try {
let validate = await this.$refs.editModalityform.validate()
if (!validate) return false
this.loading = true
let res = await updateTaskStudyModality(this.form)
this.loading = false
if (res.IsSuccess) {
this.modelList.some((item) => {
if (this.form.TaskStudyId === item.Id) {
item.ModalityForEdit = this.form.Modality
}
return this.form.TaskStudyId === item.Id
})
this.editModality_model.visible = false
this.$emit('getList')
}
} catch (err) {
console.log(err)
}
},
},
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -102,7 +102,6 @@
import uploadList from "./components/upload-list.vue";
import studyView from "./components/study-view.vue";
import { getSubjectImageUploadList, deleteTaskStudy } from "@/api/load.js";
import { downloadImage } from "@/utils/uploadZip.js";
import store from "@/store";
export default {
name: "uploadImage",
@ -591,7 +590,7 @@ export default {
files.push(obj);
}
store.dispatch("trials/setUnLock", true);
let res = await downloadImage(zipName, files);
// let res = await downloadImage(zipName, files);
store.dispatch("trials/setUnLock", false);
},
},

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)
@ -148,6 +145,12 @@ Vue.config.productionTip = false
Vue.prototype.$upload = () => {
_vm.$forceUpdate()
}
Vue.prototype.$guid = () => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
Vue.prototype.fileToBlob = (file) => {
// 创建 FileReader 对象
const reader = new FileReader()
@ -252,6 +255,19 @@ async function VueInit() {
} catch (e) {
}
}
Vue.prototype.$company = (isShort = true) => {
let companyName = null
if (localStorage.getItem('CompanyInfo')) {
let CompanyInfo = JSON.parse(localStorage.getItem('CompanyInfo'))
let isZh = zzSessionStorage.getItem('lang') === 'zh' || _vm.$i18n.locale == 'zh'
if (isZh) {
companyName = isShort ? CompanyInfo.CompanyShortNameCN : CompanyInfo.CompanyNameCN
} else {
companyName = isShort ? CompanyInfo.CompanyShortName : CompanyInfo.CompanyName
}
}
return companyName;
}
Vue.prototype.$updateDictionary = function () {
Vue.prototype.$d = function (code) {
var dictInfo = res.Result
@ -296,7 +312,8 @@ async function VueInit() {
data: {
unlock: {
my_username: null,
my_password: null
my_password: null,
view: false
}
},
render: h => h(App)
@ -368,7 +385,7 @@ async function VueInit() {
// process.env.VUE_APP_LOGOUT_FOR_TIME
// eval(process.env.VUE_APP_LOCK_FOR_PERMISSION)
// process.env.VUE_APP_LOCK_FOR_TIME
window.VUE_APP_COMPANY_NAME = process.env.VUE_APP_COMPANY_NAME;
// window.VUE_APP_COMPANY_NAME = process.env.VUE_APP_COMPANY_NAME;
waitOperate(eval(process.env.VUE_APP_LOGOUT_FOR_PERMISSION) ? () => {
var lang = zzSessionStorage.getItem('lang') ? zzSessionStorage.getItem('lang') : 'zh'
if (_vm.$store.state.trials.unlock || _vm.$route.path === '/ReviewersResearchForm' || _vm.$route.path === '/ReviewersResearch' || _vm.$route.path === '/login' || _vm.$route.path === '/researchForm' || _vm.$route.path === '/researchDetail_m' || _vm.$route.path === '/researchLogin_m' || _vm.$route.path === '/researchLogin' || _vm.$route.path === '/email-recompose' || _vm.$route.path === '/recompose' || _vm.$route.path === '/resetpassword' || _vm.$route.path === '/error') {
@ -440,7 +457,8 @@ async function VueInit() {
_vm.$message.success(_vm.$t("env:lock:msgBox:lockSuccess"))
_vm.unlock = {
my_username: null,
my_password: null
my_password: null,
view: false
}
isOpen = false
count = 0;
@ -504,16 +522,21 @@ async function VueInit() {
]),
h('el-form-item', {
props: { label: _vm.$t("env:lock:msgBox:form:Password") },
attrs: {
style: "position: relative;"
}
}, [
h('input', {
props: {
value: _vm.unlock.my_password
},
ref: "unlock_my_password_input",
attrs: {
id: 'my_password',
class: 'el-input__inner',
type: 'password',
autocomplete: 'new-password'
type: _vm.unlock.view ? 'text' : 'password',
autocomplete: 'new-password',
style: "padding-right:25px"
},
on: {
change: (event) => {
@ -523,7 +546,24 @@ async function VueInit() {
_vm.unlock.my_password = event.target.value
}
}
})
}),
h('i', {
attrs: {
id: 'my_password_view',
class: "el-icon-view",
style: "cursor: pointer;position: absolute;top:35%;right:10px"
},
on: {
click: (event) => {
_vm.unlock.view = !_vm.unlock.view
if (_vm.unlock.view) {
_vm.$refs['unlock_my_password_input'].type = "text"
} else {
_vm.$refs['unlock_my_password_input'].type = "password"
}
},
}
}),
])
])
])

View File

@ -6,11 +6,12 @@ import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import Vue from 'vue'
import { OSSclient } from './utils/oss'
import WHITELIST from "./utils/whiteList"
// import getPageTitle from '@/utils/get-page-title'
NProgress.configure({ showSpinner: false })
const whiteList = ['/ReviewersResearch', '/login', '/error', '/resetpassword', '/recompose', '/email-recompose', '/trialStats', '/showdicom', '/imagesShare', '/audit', '/preview', '/researchLogin', '/researchLogin_m', '/blindResumeInfo', '/trialsResume', '/joinVerify', '/showNoneDicoms', '/noneDicomReading', '/clinicalData', '/readingDicoms', '/readingPage', '/visitDicomReview', '/visitNondicomReview', '/globalReview', '/adReview', '/oncologyReview', '/nonedicoms']
const whiteList = WHITELIST
store.state.trials.whiteList = whiteList;
router.beforeEach(async (to, from, next) => {
NProgress.start()
@ -25,15 +26,15 @@ router.beforeEach(async (to, from, next) => {
if (to.path === '/login' || to.path === '/recompose' || to.path === '/email-recompose' || to.path === '/error' || to.path === '/ReviewersResearchForm' || to.path === '/ReviewersResearch') {
if (to.path === '/ReviewersResearch') {
await this.$store.dispatch('user/logout')
OSSclient()
await OSSclient()
}
if (to.path === '/ReviewersResearchForm') {
OSSclient()
await OSSclient()
}
next()
NProgress.done()
} else {
OSSclient()
await OSSclient()
const hasGetUserInfo = store.getters.userId
if (hasGetUserInfo) {
next()
@ -66,7 +67,7 @@ router.beforeEach(async (to, from, next) => {
} else {
// 在免登录whiteList中直接进入
if (to.path === '/readingDicoms' || to.path === '/noneDicomReading') {
OSSclient()
await OSSclient()
}
next()
}

View File

@ -35,26 +35,50 @@ const getDefaultState = () => {
}
}
function getQuestions(questions) {
const criterionType = parseInt(localStorage.getItem('CriterionType'))
questions.forEach(item => {
if (item.Type === 'table' && item.TableQuestions && item.TableQuestions.Answers.length > 0) {
if ((item.Type === 'table' || item.Type === 'basicTable') && item.TableQuestions && item.TableQuestions.Answers.length > 0) {
item.TableQuestions.Answers.forEach(answerObj => {
answerObj.isDicomReading = answerObj.IsDicomReading === 'True'
if (answerObj.RowId) {
if (criterionType === 10) {
// pcwg
var lesionNum = getQuestionAnswer(item.TableQuestions.Questions, 11, answerObj)
answerObj.lesionNum = lesionNum
answerObj.saveTypeEnum = isNaN(parseInt(answerObj.lesionNum)) ? 1 : 2
} else if (criterionType === 19) {
// ivus
answerObj.area1 = getQuestionAnswer(item.TableQuestions.Questions, 1001, answerObj)
answerObj.area2 = getQuestionAnswer(item.TableQuestions.Questions, 1002, answerObj)
const v = getQuestionAnswer(item.TableQuestions.Questions, 1003, answerObj)
answerObj.diff = v
answerObj.saveTypeEnum = isNaN(parseFloat(v)) ? 1 : 2
} else if (criterionType === 20) {
// oct
if (item.LesionType === 101) {
answerObj.l1 = getQuestionAnswer(item.TableQuestions.Questions, 1011, answerObj)
answerObj.l2 = getQuestionAnswer(item.TableQuestions.Questions, 1012, answerObj)
answerObj.l3 = getQuestionAnswer(item.TableQuestions.Questions, 1013, answerObj)
const min = getQuestionAnswer(item.TableQuestions.Questions, 1014, answerObj)
answerObj.min = min
const mean = getQuestionAnswer(item.TableQuestions.Questions, 1015, answerObj)
answerObj.mean = mean
answerObj.saveTypeEnum = (isNaN(parseFloat(min)) || isNaN(parseFloat(mean))) ? 1 : 2
} else if (item.LesionType === 103) {
const angle = getQuestionAnswer(item.TableQuestions.Questions, 1016, answerObj)
answerObj.angle = angle
answerObj.saveTypeEnum = isNaN(parseFloat(angle)) ? 1 : 2
}
} else {
answerObj.lesionPart = getQuestionAnswer(item.TableQuestions.Questions, 8, answerObj)
answerObj.loctation = getQuestionAnswer(item.TableQuestions.Questions, 6, answerObj)
answerObj.lesionLength = getQuestionAnswer(item.TableQuestions.Questions, 0, answerObj)
answerObj.lesionShort = getQuestionAnswer(item.TableQuestions.Questions, 1, answerObj)
answerObj.isDicomReading = answerObj.IsDicomReading === 'True'
var isLymphLesion = getQuestionAnswer(item.TableQuestions.Questions, 2, answerObj)
let isLymphLesion = getQuestionAnswer(item.TableQuestions.Questions, 2, answerObj)
isLymphLesion = isLymphLesion ? parseInt(isLymphLesion) : null
answerObj.isLymphLesion = isLymphLesion
answerObj.lesionState = getQuestionAnswer(item.TableQuestions.Questions, 7, answerObj)
var lesionNum = getQuestionAnswer(item.TableQuestions.Questions, 11, answerObj)
answerObj.lesionNum = lesionNum
if (answerObj.RowId) {
var idx = item.TableQuestions.Questions.findIndex(i => i.QuestionMark === 11)
if (idx > -1) {
// pcwg
answerObj.saveTypeEnum = isNaN(parseInt(answerObj.lesionNum)) ? 1 : 2
} else {
answerObj.saveTypeEnum = isNaN(parseInt(answerObj.lesionState)) ? 1 : 2
}
} else {
@ -70,7 +94,7 @@ function getQuestions(questions) {
}
function findQuestionAndRemoveLesion(questions, obj) {
for (var i = 0; i < questions.length; i++) {
if (questions[i].Type === 'table' && questions[i].TableQuestions && (questions[i].LesionType === obj.lesionType) && questions[i].TableQuestions.Answers.length > 0) {
if ((questions[i].Type === 'table' || questions[i].Type === 'basicTable') && questions[i].TableQuestions && (questions[i].LesionType === obj.lesionType) && questions[i].TableQuestions.Answers.length > 0) {
var idx = questions[i].TableQuestions.Answers.findIndex(i => String(i.RowIndex) === String(obj.rowIndex))
if (idx > -1) {
questions[i].TableQuestions.Answers.splice(idx, 1)
@ -86,7 +110,7 @@ function findQuestionAndRemoveLesion(questions, obj) {
function findQuestionAndUpdateLesion(questions, obj) {
for (var i = 0; i < questions.length; i++) {
var item = questions[i]
if (item.Type === 'table' && item.Id === obj.questionId) {
if ((item.Type === 'table' || item.Type === 'basicTable') && item.Id === obj.questionId) {
var idx = item.TableQuestions.Answers.findIndex(i => i.RowIndex === obj.rowIndex)
item.TableQuestions.Answers[idx].isLymphLesion = obj.isLymphLesion
item.TableQuestions.Answers[idx].loctation = obj.lesionOrgan
@ -115,10 +139,10 @@ function findQuestionAndUpdateLesion(questions, obj) {
}
function findQuestionAndAddLesion(questions, obj) {
for (var i = 0; i < questions.length; i++) {
if (questions[i].Type === 'table' && questions[i].TableQuestions && (questions[i].LesionType === obj.lesionType)) {
if ((questions[i].Type === 'table' || questions[i].Type === 'basicTable') && questions[i].TableQuestions && (questions[i].LesionType === obj.lesionType)) {
var sourceObj = {}
questions[i].TableQuestions.Questions.forEach(item => {
sourceObj[item.Id] = ''
sourceObj[item.Id] = null
})
var targetObj = Object.assign(sourceObj, obj.lesionObj)
targetObj.IsCurrentTaskAdd = 'True'
@ -811,10 +835,17 @@ const actions = {
// }
series.InstanceInfoList.forEach(instance => {
if (instance.NumberOfFrames && instance.NumberOfFrames > 1) {
if (study.IsCriticalSequence && instance.KeyFramesList.length > 0) {
instance.KeyFramesList.map(i => {
const imageId = `wadouri:${localStorage.getItem('location') !== 'USA' ? Vue.prototype.OSSclientConfig.basePath : Vue.prototype.OSSclientConfig.basePath}${instance.Path}?frame=${i}&instanceId=${instance.Id}&visitTaskId=${obj.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${index}`
imageIds.push(imageId)
})
} else {
for (let i = 0; i < instance.NumberOfFrames; i++) {
const imageId = `wadouri:${localStorage.getItem('location') !== 'USA' ? Vue.prototype.OSSclientConfig.basePath : Vue.prototype.OSSclientConfig.basePath}${instance.Path}?frame=${i}&instanceId=${instance.Id}&visitTaskId=${obj.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${index}`
imageIds.push(imageId)
}
}
instance.ImageId = `wadouri:${localStorage.getItem('location') !== 'USA' ? Vue.prototype.OSSclientConfig.basePath : Vue.prototype.OSSclientConfig.basePath}${instance.Path}?frame=${0}&instanceId=${instance.Id}&visitTaskId=${obj.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${index}`
} else {
const imageId = `wadouri:${localStorage.getItem('location') !== 'USA' ? Vue.prototype.OSSclientConfig.basePath : Vue.prototype.OSSclientConfig.basePath}${instance.Path}?instanceId=${instance.Id}&visitTaskId=${obj.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${index}`
@ -1053,7 +1084,10 @@ const actions = {
return new Promise(resolve => {
var isReadingTaskViewInOrder = localStorage.getItem('isReadingTaskViewInOrder')
if (parseInt(isReadingTaskViewInOrder) === 2) {
if (!state.lastCanvasTaskId) state.lastCanvasTaskId = taskId
if (!state.lastCanvasTaskId) {
console.log('setLastCanvasTaskId')
state.lastCanvasTaskId = taskId
}
} else {
state.lastCanvasTaskId = taskId
}

View File

@ -9,7 +9,7 @@ const getDefaultState = () => {
studyListQuery: null,
unlock: false,
config: {},
uploadTip: null,
uploadTip: '0.00KB/s',
timer: null,
whiteList: [],
checkTaskId: null

View File

@ -97,6 +97,7 @@ const actions = {
zzSessionStorage.setItem('my_username', username.trim())
zzSessionStorage.setItem('my_password', md5(password))
zzSessionStorage.setItem('my_EMail', response.Result.BasicInfo.EMail)
localStorage.setItem('CompanyInfo', JSON.stringify(response.Result.CompanyInfo))
const data = response.Result
if (data.BasicInfo.IsFirstAdd || data.BasicInfo.LoginState === 1) {
try {

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)
}

View File

@ -0,0 +1,44 @@
// crypto.js
import CryptoJS from 'crypto-js';
import { JSEncrypt } from "jsencrypt";
import { getPublicKey } from "@/api/user.js"
export const Crypto = {
AES: {
encrypt: function (plaintext, secretKey) {
return CryptoJS.AES.encrypt(plaintext, CryptoJS.enc.Utf8.parse(secretKey), {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
iv: ''
}).ciphertext.toString(CryptoJS.enc.Base64);
},
decrypt: function (ciphertext, secretKey) {
const bytes = CryptoJS.AES.decrypt(ciphertext, secretKey);
return bytes.toString(CryptoJS.enc.Utf8);
}
}
};
export const Encrypt = {
encrypt: async function (plaintext) {
let PublicKey = null;
if (sessionStorage.getItem('PublicKey')) {
PublicKey = atob(sessionStorage.getItem('PublicKey'));
} else {
try {
let res = await getPublicKey();
if (res.IsSuccess) {
PublicKey = atob(res.Result)
sessionStorage.setItem('PublicKey', res.Result)
} else {
return false;
}
} catch (err) {
console.log(err)
}
}
let encryptor = new JSEncrypt()
encryptor.setPublicKey(PublicKey)
return encryptor.encrypt(plaintext)
},
};

View File

@ -0,0 +1,30 @@
import { Encrypt, Crypto } from "./crypto";
export const encryptConfig = async (config) => {
let secretKey = randomRange(32, 32);
let encryptSecretKey = await Encrypt.encrypt(secretKey); // 密钥进行非对称加密
if (encryptSecretKey) {
config.headers['X-Encrypted-Key'] = encryptSecretKey;
}
if (config.data && Object.prototype.toString.call(config.data) === '[object Object]') {
Object.keys(config.data).forEach(async key => {
config.data[key] = Crypto.AES.encrypt(config.data[key], secretKey)
console.log(config.data[key], 'KEY')
// config.data[key] = await Encrypt.encrypt(config.data[key])
})
}
return config;
}
const randomRange = (min, max, charStr) => {
var returnStr = "",
range;
if (typeof max == 'string') {
charStr = max;
}
range = ((max && typeof max == 'number') ? Math.round(Math.random() * (max - min)) + min : min);
charStr = charStr || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (var i = 0; i < range; i++) {
var index = Math.round(Math.random() * (charStr.length - 1));
returnStr += charStr.substring(index, index + 1);
}
return returnStr;
}

View File

@ -0,0 +1,457 @@
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 < shardSize) {
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 });
if (existBucket === 'true') {
progressFn(1, fileInformation.file, 0);
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 - lastPercentage);
} else {
console.log("未查询到时间")
if (timeList.length > 0) {
bytesReceivedPerSecond[timeList[timeList.length - 1]] += fileInformation.file.size * (percentage - lastPercentage);
} else {
bytesReceivedPerSecond[time] = fileInformation.file.size * (percentage - lastPercentage);
}
}
}
}
} 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);
// 将传入文件的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 }) {
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)
}

View File

@ -0,0 +1,161 @@
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 - checkData[saveFileId]);
} else {
// console.log("未查询到时间")
if (timeList.length > 0) {
bytesReceivedPerSecond[timeList[timeList.length - 1]] += data.file.size * (percentage - checkData[saveFileId]);
} else {
bytesReceivedPerSecond[time] = data.file.size * (percentage - checkData[saveFileId]);
}
}
}
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);
} else if (res.res.status === 404) {
delete savaData[saveFileId];
saveFinishedData(savaData);
}
return res;
} catch (e) {
console.log(e);
delete savaData[saveFileId];
saveFinishedData(savaData);
// 捕获超时异常。
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(() => {
// console.log(Object.assign({}, bytesReceivedPerSecond))
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

View File

@ -3,30 +3,39 @@ 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()
let res = null;
if (localStorage.getItem('stsToken')) {
res = JSON.parse(localStorage.getItem('stsToken'))
} else {
res = await GetObjectStoreToken()
localStorage.setItem('stsToken', JSON.stringify(res))
}
// res.Result.ObjectStoreUse = 'AWS';
Vue.prototype.OSSclientConfig = { ...res.Result[res.Result.ObjectStoreUse] }
Vue.prototype.OSSclientConfig.ObjectStoreUse = res.Result.ObjectStoreUse;
Vue.prototype.OSSclientConfig.basePath = Vue.prototype.OSSclientConfig.viewEndpoint
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) {
put: async function (objectName, object) {
OSSclient = await RefreshClient(OSSclient)
return new Promise(async (resolve, reject) => {
try {
let _vm = router.default.app
@ -49,6 +58,46 @@ async function ossGenerateSTS() {
reject()
}
})
},
multipartUpload: async (data, progress) => {
OSSclient = await RefreshClient(OSSclient)
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[config.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 + decodeUtf8(res.name)
})
} else {
reject()
}
} catch (err) {
console.log(err)
reject(err)
}
})
},
close: () => {
OSSclose();
}
}
break
@ -86,49 +135,121 @@ async function ossGenerateSTS() {
console.log(e)
}
})
},
close: () => {
return false
}
}
break
case 'AWS':
let aws = new Minio.Client(Vue.prototype.OSSclientConfig);
Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
let aws = new S3Client({
endpoint: Vue.prototype.OSSclientConfig.viewEndpoint,
region: Vue.prototype.OSSclientConfig.region,
s3ForcePathStyle: true,
signatureVersion: 'v4',
forcePathStyle: true,
// SessionToken: '',
credentials: {
accessKeyId: Vue.prototype.OSSclientConfig.accessKeyId,
secretAccessKey: Vue.prototype.OSSclientConfig.secretAccessKey,
sessionToken: Vue.prototype.OSSclientConfig.sessionToken
}
});
Vue.prototype.OSSclient = {
put: function (objectName, object) {
return new Promise(async (resolve, reject) => {
try {
var name = objectName.split('/')[objectName.split('/').length - 1]
let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
var objectItem = objectName.split('/')
objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
objectName = objectItem.join('/')
put: async function (objectName, object) {
let data = {
file: object,
path: objectName
}
const reader = new FileReader();
reader.onload = (ex) => {
const bufferStream = new stream.PassThrough()
bufferStream.end(Buffer.from(ex.target.result))
aws.putObject(Vue.prototype.OSSclientConfig.bucketName, objectName, bufferStream, function (err, etag) {
if (err) {
console.log(err)
reject()
} else {
console.log(objectName);
resolve({
name: objectName,
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(objectName)
})
}
})
};
reader.readAsArrayBuffer(object);
} catch (e) {
console.log(e)
}
})
aws = await RefreshClient(aws);
return uploadAWS(aws, data, () => { });
},
multipartUpload: async (data, progress) => {
aws = await RefreshClient(aws);
return uploadAWS(aws, data, progress);
},
close: () => {
AWSclose();
}
}
}
return
}
// AWS上传函数
function uploadAWS(aws, data, progress) {
return new Promise(async (resolve, reject) => {
try {
const { file, path } = data;
if (!file || !path) return reject('file and path be required');
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 bucketName = data.path.split("/")[1] || Vue.prototype.OSSclientConfig.bucket
let curPath = data.path;
data.path = data.path.replace(`/${bucketName}/`, '');
await exist(aws, bucketName, data, progress, (path, status) => {
if (status === 'success') {
resolve({
name: decodeUtf8(curPath),
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(curPath)
})
} else {
reject()
}
});
} catch (err) {
console.log(err)
reject(err)
}
})
}
// client过期刷新
async function RefreshClient(client) {
let config = await getSTSToken(Vue.prototype.OSSclientConfig.expiration);
if (config) {
// config.Result.ObjectStoreUse = 'AWS'
switch (config.Result.ObjectStoreUse) {
case 'AliyunOSS': {
Vue.prototype.OSSclientConfig = { ...config.Result[config.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
return new OSS(Vue.prototype.OSSclientConfig);
}
case "MinIO": {
return client;
}
case "AWS": {
Vue.prototype.OSSclientConfig = { ...config.Result[config.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
return new S3Client({
endpoint: Vue.prototype.OSSclientConfig.viewEndpoint,
region: Vue.prototype.OSSclientConfig.region,
s3ForcePathStyle: true,
signatureVersion: 'v4',
forcePathStyle: true,
credentials: {
accessKeyId: Vue.prototype.OSSclientConfig.accessKeyId,
secretAccessKey: Vue.prototype.OSSclientConfig.secretAccessKey,
sessionToken: Vue.prototype.OSSclientConfig.sessionToken
}
});
}
}
} else {
return client;
}
}
function decodeUtf8(bytes) {
let str = bytes.split('?');
let str2 = str[0].split('/');
@ -138,6 +259,48 @@ 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;
localStorage.setItem('stsToken', JSON.stringify(res))
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);
const now = new Date();
// 如果有效期不足五分钟,视为过期。
return expireDate.getTime() - now.getTime() <= 300000;
}
export const OSSclient = ossGenerateSTS

View File

@ -113,10 +113,14 @@ export const parseDicom = (file, name = false) => {
}
})
uintKey.forEach(key => {
if (res.hasOwnProperty(key)) {
res[key] = data.uint16(dicom[key])
}
})
intStringKey.forEach(key => {
if (res.hasOwnProperty(key)) {
res[key] = data.intString(dicom[key])
}
})
defaultKey.forEach(key => {
if (!res[key] && res.hasOwnProperty(key)) {

View File

@ -2,9 +2,9 @@ import axios from 'axios'
import { Message, MessageBox, Alert } from 'element-ui'
import store from '@/store'
import router from '@/router'
import Vue from 'vue'
import WHITELIST from "./whiteList"
import moment from 'moment-timezone';
console.log(moment.tz.guess())
import { encryptConfig } from "@/utils/encrypt"
axios.defaults.withCredentials = false
const service = axios.create({
baseURL: '/api',
@ -16,18 +16,28 @@ var path
// var lang = store.state.lang.language
service.interceptors.request.use(
config => {
async config => {
path = router && router.app && router.app._route && router.app._route.path
config.headers['Content-Type'] = 'application/json;charset=UTF-8'
var language = zzSessionStorage.getItem('lang')
config.headers['Accept-Language'] = language === 'en' ? 'en-US,en;q=0.5' : 'zh-CN,zh;q=0.9'
config.headers['TimeZoneId'] = moment.tz.guess()
if (store.getters.token) {
if (config.ENCRYPT) {
try {
config = await encryptConfig(config)
} catch (err) {
console.log(err)
}
}
if (store.getters.token && !config.clearToken) {
config.headers.Authorization = `Bearer ${store.getters.token}`
}
try {
if (eval(process.env.VUE_APP_LOGIN_FOR_PERMISSION)) {
if (!path || path === '/ReviewersResearchForm' || path === '/ReviewersResearch' || path === '/login' || path === '/researchForm' || path === '/researchDetail_m' || path === '/researchLogin_m' || path === '/researchLogin' || path === '/email-recompose' || path === '/recompose' || path === '/resetpassword' || path === '/error') {
// if (!path || path === '/ReviewersResearchForm' || path === '/ReviewersResearch' || path === '/login' || path === '/researchForm' || path === '/researchDetail_m' || path === '/researchLogin_m' || path === '/researchLogin' || path === '/email-recompose' || path === '/recompose' || path === '/resetpassword' || path === '/error') {
// return config
// }
if (!path || WHITELIST.some(item => item === path)) {
return config
}
if (store.state.user.userId !== zzSessionStorage.getItem('userId')) {

106
src/utils/stream.js Normal file
View File

@ -0,0 +1,106 @@
import streamSaver from "streamsaver";
import "streamsaver/examples/zip-stream.js";
import store from '@/store'
streamSaver.mitm = `${window.location.origin}/mitm.html?version=2.0.0`
// 下载文件并压缩
function zipFiles(zipName, files) {
return new Promise((resolve) => {
try {
console.log("同步下载打包开始时间:" + new Date());
store.dispatch('trials/setUnLock', true)
files = formatFiles(files)
// 创建压缩文件输出流
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 {
let { name, url } = fileInfo.value;
url = decodeUtf8(url);
return fetch(url).then(res => {
ctrl.enqueue({
name,
stream: () => res.body
});
})
}
}
});
if (window.WritableStream && readableZipStream.pipeTo) {
// 开始下载
readableZipStream
.pipeTo(zipFileOutputStream)
.then(() => {
console.log("同步下载打包结束时间:" + new Date());
store.dispatch('trials/setUnLock', false)
resolve(true)
}).catch(err => {
console.log(err);
resolve(false)
});
} else {
resolve(false)
}
} catch (err) {
console.log(err);
resolve(false)
}
})
}
// 下载文件并修改名称
async function updateFile(file, name) {
return new Promise(async resolve => {
try {
store.dispatch('trials/setUnLock', true)
const fileOutputStream = streamSaver.createWriteStream(name);
file = decodeUtf8(file);
let res = await fetch(file);
res.body.pipeTo(fileOutputStream).then(() => {
store.dispatch('trials/setUnLock', true)
resolve(true)
}).catch(err => {
console.log(err)
resolve(false)
});
} catch (err) {
console.log(err)
resolve(false)
}
})
}
// 同名文件修改名称
function formatFiles(files) {
let fileObj = {};
files.forEach(file => {
let arr = Object.keys(fileObj);
if (!~arr.indexOf(file.name)) {
fileObj[file.name] = 1;
} else {
let name = file.name;
file.name = name.split(".")[0] + `(${fileObj[name]})` + name
.substring(name.lastIndexOf('.'))
.toLocaleLowerCase()
fileObj[name]++;
}
})
return files;
}
function decodeUtf8(bytes) {
let str = bytes.split('?');
let str2 = str[0].split('/');
let name = str2[str2.length - 1];
name = encodeURIComponent(name);
str.shift();
str2.pop();
return str2.join("/") + '/' + name;
}
export async function downLoadFile(file, name, type = 'file') {
if (type === 'zip') return await zipFiles(name, file);
return await updateFile(file, name)
}

View File

@ -1,168 +0,0 @@
import Vue from 'vue';
import store from "@/store";
import {
requestPackageAndAnonymizImage,
} from "@/api/load.js";
import streamSaver from "streamsaver";
import "streamsaver/examples/zip-stream.js"
let flag = {};
export const resetFlag = () => {
flag = {};
store.state.trials.uploadTip = null;
if (store.state.trials.timer) {
clearInterval(store.state.trials.timer);
store.state.trials.timer = null;
}
store.dispatch("trials/setUnLock", false);
}
export const downloadImage = async (id, id2, IsDicom = true) => {
// if (flag[`${id2}_${IsDicom}`]) return Vue.prototype.$message.warning(Vue.prototype.$t('trials:upload:tip:uploading'));
if (flag[`${id2}_${IsDicom}`]) return false;
flag[`${id2}_${IsDicom}`] = true
try {
let params = {
TrialId: id,
SubjectVisitId: id2,
IsDicom: IsDicom
}
store.dispatch("trials/setUnLock", true);
let res = await requestPackageAndAnonymizImage(params);
if (res.IsSuccess) {
if (!res.Result) {
flag[`${id2}_${IsDicom}`] = false;
// Vue.prototype.$message.warning(Vue.prototype.$t("trials:upload:message:not"))
let message = Vue.prototype.$t('trials:upload:tip:uploading').replace("xxx", res.OtherInfo.FileName);
store.state.trials.uploadTip = message;
if (!store.state.trials.timer) {
store.state.trials.timer = setInterval(() => {
downloadImage(id, id2, IsDicom);
}, 2000);
}
return false;
}
if (store.state.trials.timer) {
clearInterval(store.state.trials.timer);
store.state.trials.timer = null;
}
let fileName = res.Result.split("/").pop();
let href = Vue.prototype.OSSclientConfig.basePath + res.Result;
if (fileName !== res.OtherInfo.FileName) {
let message = Vue.prototype.$t('trials:upload:tip:uploading').replace("xxx", res.OtherInfo.FileName);
store.state.trials.uploadTip = message;
// Vue.prototype.$message.success(Vue.prototype.$t("trials:upload:message:startUpload"));
return download(href, res.OtherInfo.FileName, { id2, IsDicom });
}
let a = document.createElement("a");
a.download = res.OtherInfo.FileName;
a.href = href;
a.click();
URL.revokeObjectURL(href);
let timer = setTimeout(() => {
a = null;
href = null;
timer = null;
}, 500)
store.state.trials.uploadTip = null;
return true;
} else {
flag[`${id2}_${IsDicom}`] = false;
return false;
}
} catch (err) {
flag[`${id2}_${IsDicom}`] = false;
console.log(err);
}
};
export const fileDownload = (content, filename) => {
const eleLink = document.createElement("a");
eleLink.download = filename;
eleLink.style.display = "none";
const blob = new Blob([content]);
eleLink.href = URL.createObjectURL(blob);
document.body.appendChild(eleLink);
eleLink.click();
document.body.removeChild(eleLink);
};
let download = async (downloadUrl, downloadFileName, res) => {
const blob = await getBlob(downloadUrl);
flag[`${res.id2}_${res.IsDicom}`] = false;
store.state.trials.uploadTip = null;
store.dispatch("trials/setUnLock", false);
saveAsB(blob, downloadFileName);
return true;
}
let getBlob = (url) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response);
} else {
reject(new Error(`Request failed with status ${xhr.status}`));
}
};
xhr.onerror = () => {
reject(new Error('Request failed'));
};
xhr.send();
});
}
let saveAsB = (blob, filename) => {
const link = document.createElement('a');
const body = document.body;
link.href = window.URL.createObjectURL(blob);
link.download = filename;
// hide the link
link.style.display = 'none';
body.appendChild(link);
link.click();
body.removeChild(link);
window.URL.revokeObjectURL(link.href);
}
// 前端流式打包下载
const handleBatchDown = async (item, zip) => {
return new Promise((resolve) => {
console.log("同步下载打包开始时间:" + new Date());
// 创建压缩文件输出流
const zipFileOutputStream = streamSaver.createWriteStream(zipName);
// 创建下载文件流
const fileIterator = files.values();
const readableZipStream = new window.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()); resolve(true) })
} else {
resolve(false);
}
})
};

2
src/utils/whiteList.js Normal file
View File

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

View File

@ -251,7 +251,7 @@ export default {
},
async loadPatientStudy() {
try {
let data = await getPatientSeriesList(this.studyId);
const data = await getPatientSeriesList(this.studyId)
if (data.IsSuccess) {
const { Result } = data
var seriesList = []

View File

@ -6,7 +6,7 @@
<div class="sidePanelThumbs">
<el-tabs v-model="activeName" type="border-card" @tab-click="handleTabClick">
<el-tab-pane :label="$t('trials:dicom-show:currentVisit')" name="current-study">
<div class="text-desc" style="background:rgb(55 55 55);">
<div v-if="visitInfo" class="text-desc" style="background:rgb(55 55 55);">
{{ visitInfo }}
</div>
<div class="viewerSidethumbs ps" style="position: relative;">
@ -143,7 +143,7 @@
</div>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('trials:dicom-show:relatedVisit')" name="relation-study" class="pane-relation-wrapper">
<el-tab-pane :label="$t('trials:dicom-show:relatedVisit')" name="relation-study" class="pane-relation-wrapper" v-if="!visitTaskId">
<div class="viewerSidethumbinner">
<el-collapse v-model="relationActiveName" @change="handelRelationActiveChange">
<el-collapse-item v-for="(study,studyIndex) in relationStudyList" :key="`${study.StudyId}`" :name="`${study.StudyId}`">
@ -261,6 +261,7 @@ import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
import dicomViewer from '@/components/Dicom/DicomViewer'
import { getVisitStudyList, getAllRelationStudyList, getSeriesList } from '@/api/reading'
import { setSeriesStatus } from '@/api/trials'
import { getTaskUploadedDicomStudyList } from '@/api/reading'
import requestPoolManager from '@/utils/request-pool'
import store from '@/store'
import { changeURLStatic } from '@/utils/history.js'
@ -301,7 +302,9 @@ export default {
relationActiveName: [],
showSeriesList: [],
currentLoadIns: [],
isFromCRCUpload: false
isFromCRCUpload: false,
visitTaskId: null,
page: ''
}
},
mounted() {
@ -318,6 +321,8 @@ export default {
this.isReading = this.$router.currentRoute.query.isReading ? this.$router.currentRoute.query.isReading * 1 : 0
this.showDelete = parseInt(this.$router.currentRoute.query.showDelete)
this.isFromCRCUpload = !!this.$router.currentRoute.query.isFromCRCUpload
this.visitTaskId = this.$router.currentRoute.query.visitTaskId
this.page = this.$route.query.page
// cornerstone.events.addEventListener('cornerstoneimageloaded', this.cornerstoneImageLoaded)
this.getStudiesInfo()
cornerstone.events.addEventListener('cornerstoneimageloadprogress', this.cornerstoneimageloadprogress)
@ -338,7 +343,14 @@ export default {
async getStudiesInfo() {
this.studyList = []
try {
const res = await getVisitStudyList(this.trialId, this.subjectVisitId, this.isReading)
let res = null
if (this.page === 'upload') {
res = await getTaskUploadedDicomStudyList({ visitTaskId: this.visitTaskId })
} else if (this.page === 'download'){
res = await getVisitStudyList(this.trialId, this.subjectVisitId, this.isReading, this.visitTaskId)
} else {
res = await getVisitStudyList(this.trialId, this.subjectVisitId, this.isReading)
}
if (res.IsSuccess) {
res.Result.forEach((study, studyIndex) => {
const data = {}

View File

@ -3,7 +3,7 @@
ref="sysTemplateFrom"
v-loading="loading"
:model="form"
label-width="90px"
label-width="140px"
size="small"
:rules="rules"
class="upload-temporary-file"
@ -15,7 +15,7 @@
<el-form-item label="业务场景: " prop="BusinessScenarioEnum">
<el-select
v-model="form.BusinessScenarioEnum"
style="width:100%;"
style="width: 100%"
size="small"
filterable
>
@ -44,12 +44,14 @@
:on-exceed="handleExceed"
:disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum"
>
<el-button size="small" type="primary" :disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum" :loading="btnLoading">Select</el-button>
<span
slot="tip"
style="margin-left:10px;"
class="el-upload__tip"
<el-button
size="small"
type="primary"
:disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum"
:loading="btnLoading"
>Select</el-button
>
<span slot="tip" style="margin-left: 10px" class="el-upload__tip">
(must be in xlsx/xls/doc/docx format)
</span>
</el-upload>
@ -58,37 +60,61 @@
<el-form-item label="文件名" prop="Name">
<el-input v-model="form.Name" />
</el-form-item>
<el-form-item
:label="$t('dictionary:attachment:export:form:nameCN')"
prop="NameCN"
>
<el-input v-model="form.NameCN" />
</el-form-item>
<el-form-item v-if="form.Id !== ''" label="是否废除: ">
<el-radio-group v-model="form.IsDeleted">
<el-radio v-for="item of $d.YesOrNo" :label="item.value">{{item.label}}</el-radio>
<el-radio
v-for="item of $d.YesOrNo"
:label="item.value"
:key="item.id"
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
<el-form-item label="描述">
<el-input
v-model="form.Description"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:autosize="{ minRows: 2, maxRows: 4 }"
placeholder="请输入内容"
/>
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item style="text-align:right;">
<el-button size="small" type="primary" :disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum" :loading="saveBtnLoading" @click="handleSave">Save</el-button>
<div class="base-dialog-footer" style="text-align: right; margin-top: 10px">
<el-form-item style="text-align: right">
<el-button
size="small"
type="primary"
:disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum"
:loading="saveBtnLoading"
@click="handleSave"
>Save</el-button
>
</el-form-item>
</div>
</el-form>
</template>
<script>
import { addOrUpdateCommonDocument, uploadCommonDoc, Upload } from '@/api/dictionary'
import {
addOrUpdateCommonDocument,
uploadCommonDoc,
Upload,
} from '@/api/dictionary'
import { getBasicDataSelects } from '@/api/dictionary/dictionary'
export default {
name: 'TemplateForm',
props: {
data: {
type: Object,
default() { return {} }
}
default() {
return {}
},
},
},
data() {
@ -99,20 +125,30 @@ export default {
FileTypeEnum: null,
BusinessScenarioEnum: null,
Name: '',
NameCN: '',
Path: '',
Description: '',
IsDeleted: false
IsDeleted: false,
},
rules: {
Code: [{ required: true, message: 'Please specify', trigger: ['blur'] }],
Name: [{ required: true, message: 'Please specify', trigger: ['blur'] }],
BusinessScenarioEnum: [{ required: true, message: 'Please select', trigger: ['blur'] }]
Code: [
{ required: true, message: 'Please specify', trigger: ['blur'] },
],
Name: [
{ required: true, message: 'Please specify', trigger: ['blur'] },
],
NameCN: [
{ required: true, message: 'Please specify', trigger: ['blur'] },
],
BusinessScenarioEnum: [
{ required: true, message: 'Please select', trigger: ['blur'] },
],
},
fileList: [],
btnLoading: false,
saveBtnLoading: false,
loading: false,
dictionaryList: {}
dictionaryList: {},
}
},
mounted() {
@ -128,8 +164,8 @@ export default {
name: this.data.Name,
path: this.data.Path,
url: this.data.Path,
type: this.data.Type
}
type: this.data.Type,
},
]
}
for (const k in this.form) {
@ -142,10 +178,12 @@ export default {
//
getDicData() {
this.loading = true
getBasicDataSelects(['Common_File_ModuleType', 'Common_File_Type']).then(res => {
getBasicDataSelects(['Common_File_ModuleType', 'Common_File_Type'])
.then((res) => {
this.dictionaryList = { ...res.Result }
this.loading = false
}).catch(() => {
})
.catch(() => {
this.loading = false
})
},
@ -165,28 +203,35 @@ export default {
const formData = new FormData()
formData.append('file', param.file)
this.form.FileName = param.file.name
Upload(formData, 1).then(res => {
this.fileList.push({ name: param.file.name, path: res.Result.FilePath, fullPath: res.Result.FullFilePath, url: res.Result.FilePath })
Upload(formData, 1).then((res) => {
this.fileList.push({
name: param.file.name,
path: res.Result.FilePath,
fullPath: res.Result.FullFilePath,
url: res.Result.FilePath,
})
this.form.Path = res.Result.FilePath
this.form.Name = param.file.name
this.form.NameCN = param.file.name
this.loading = false
this.btnLoading = false
})
},
handleSave() {
this.$refs.sysTemplateFrom.validate(valid => {
this.$refs.sysTemplateFrom.validate((valid) => {
if (!valid) return
if (!this.form.Name) {
this.$alert('Please select file.')
return
}
this.saveBtnLoading = true
addOrUpdateCommonDocument(this.form).then(res => {
addOrUpdateCommonDocument(this.form)
.then((res) => {
this.saveBtnLoading = false
this.$emit('closeDialog')
this.$emit('getList')
this.$message.success('Uploaded successfully')
}).catch(() => {
})
.catch(() => {
this.saveBtnLoading = false
})
})
@ -194,6 +239,7 @@ export default {
handleRemoveFile() {
this.fileList = []
this.form.Path = ''
this.form.NameCN = ''
this.form.Name = ''
},
handlePreview(file) {
@ -206,19 +252,21 @@ export default {
},
checkFileSuffix(fileName) {
var typeArr = ['xls', 'xlsx', 'doc', 'docx']
var extendName = fileName.substring(fileName.lastIndexOf('.') + 1).toLocaleLowerCase()
var extendName = fileName
.substring(fileName.lastIndexOf('.') + 1)
.toLocaleLowerCase()
if (typeArr.indexOf(extendName) !== -1) {
return true
} else {
return false
}
}
}
},
},
}
</script>
<style lang="scss">
.upload-temporary-file{
.upload-temporary-file {
.upload-container .el-upload--text {
border: none;
width: 80px;
@ -231,8 +279,8 @@ export default {
color: #428bca;
font-size: 13px;
}
.account_item_clear{
.el-tag__close{
.account_item_clear {
.el-tag__close {
display: none !important;
}
}

View File

@ -5,7 +5,7 @@
<el-form-item label="业务场景">
<el-select
v-model="searchData.BusinessScenarioEnum"
style="width:150px;"
style="width: 150px"
>
<el-option
v-for="item of $d.Common_File_BusinessScenario"
@ -13,15 +13,10 @@
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="文件名称">
<el-input
v-model="searchData.Name"
style="width:130px;"
clearable
/>
<el-input v-model="searchData.Name" style="width: 130px" clearable />
</el-form-item>
<el-form-item>
@ -30,24 +25,28 @@
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
<el-button
type="primary"
icon="el-icon-refresh-left"
@click="handleReset"
>
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
</el-form>
<el-button
type="primary"
icon="el-icon-plus"
style="margin-left:auto;"
style="float: right"
size="small"
@click="handleAdd"
>
{{ $t('common:button:new') }}
</el-button>
</el-form>
</template>
<template slot="main-container">
<el-table
v-adaptive="{bottomOffset:60}"
v-adaptive="{ bottomOffset: 60 }"
v-loading="loading"
:data="list"
stripe
@ -69,9 +68,13 @@
sortable="custom"
>
<template slot-scope="scope">
{{ $fd('Common_File_BusinessScenario',scope.row.BusinessScenarioEnum) }}
{{
$fd(
'Common_File_BusinessScenario',
scope.row.BusinessScenarioEnum
)
}}
</template>
</el-table-column>
<el-table-column
@ -80,6 +83,12 @@
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="NameCN"
:label="$t('dictionary:attachment:upload:table:nameCN')"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="Description"
label="描述"
@ -92,8 +101,12 @@
sortable="custom"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted" type="danger">{{ $fd('YesOrNo', scope.row.IsDeleted) }}</el-tag>
<el-tag v-else type="primary">{{ $fd('YesOrNo', scope.row.IsDeleted) }}</el-tag>
<el-tag v-if="scope.row.IsDeleted" type="danger">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
<el-tag v-else type="primary">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
</template>
</el-table-column>
<el-table-column
@ -105,29 +118,26 @@
<el-table-column label="Action">
<template slot-scope="scope">
<el-button
type="text"
@click="handleDownload(scope.row)"
>
<el-button type="text" @click="handleDownload(scope.row)">
下载
</el-button>
<el-button
type="text"
@click="handleEdit(scope.row)"
>
<el-button type="text" @click="handleEdit(scope.row)">
编辑
</el-button>
<el-button
type="text"
@click="handleDelete(scope.row)"
>
<el-button type="text" @click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
<pagination
class="page"
:total="total"
:page.sync="searchData.PageIndex"
:limit.sync="searchData.PageSize"
@pagination="getList"
/>
<!-- 新增/编辑 -->
<el-dialog
@ -138,13 +148,21 @@
width="600px"
custom-class="base-dialog-wrapper"
>
<TemplateForm :data="currentRow" @closeDialog="closeDialog" @getList="getList" />
<TemplateForm
:data="currentRow"
@closeDialog="closeDialog"
@getList="getList"
/>
</el-dialog>
</template>
</BaseContainer>
</template>
<script>
import { getCommonDocumentList, DownloadCommonDoc, deleteCommonDocument } from '@/api/dictionary'
import {
getCommonDocumentList,
DownloadCommonDoc,
deleteCommonDocument,
} from '@/api/dictionary'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import TemplateForm from './TemplateForm'
@ -155,7 +173,7 @@ const searchDataDefault = () => {
BusinessScenarioEnum: null,
Name: '',
PageIndex: 1,
PageSize: 20
PageSize: 20,
}
}
export default {
@ -168,7 +186,7 @@ export default {
total: 0,
currentRow: {},
editDialog: { title: '', visible: false },
loading: false
loading: false,
}
},
mounted() {
@ -185,11 +203,13 @@ export default {
},
getList() {
this.loading = true
getCommonDocumentList(this.searchData).then(res => {
getCommonDocumentList(this.searchData)
.then((res) => {
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
}).catch(() => {
})
.catch(() => {
this.loading = false
})
},
@ -202,9 +222,13 @@ export default {
//
handleDownload(row) {
this.loading = true
DownloadCommonDoc(row.Code).then(data => {
DownloadCommonDoc(row.Code)
.then((data) => {
this.loading = false
}).catch(() => { this.loading = false })
})
.catch(() => {
this.loading = false
})
},
//
handleEdit(row) {
@ -233,7 +257,12 @@ export default {
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
}
}
},
},
}
</script>
<style lang="scss" scoped>
::v-deep .search {
display: block;
}
</style>

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>
@ -142,11 +142,16 @@
<el-input v-model="form.Identification" placeholder="请输入标识" />
</el-form-item>
</el-col>
<el-col v-show="form.ConfigType === 'C' && title !== '复制'" :span="24">
<el-col v-show="form.ConfigType === 'C' && title !== '复制'" :span="12">
<el-form-item label="字段key">
<el-input v-model="form.Code" placeholder="请输入字段key" />
</el-form-item>
</el-col>
<el-col v-show="form.ConfigType === 'C' && title !== '复制'" :span="12">
<el-form-item label="字段key英文">
<el-input v-model="form.CodeEn" placeholder="请输入字段key英文" />
</el-form-item>
</el-col>
<el-col v-show="form.ConfigType === 'C' && title !== '复制'" :span="12">
<el-form-item label="字段英文">
<el-input v-model="form.Value" placeholder="请输入字段英文" />
@ -372,6 +377,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 +397,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 +440,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 +524,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 +602,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">
@ -643,6 +691,7 @@
height="100"
>
<el-table-column prop="Code" label="字段名" min-width="120px" :show-overflow-tooltip="true" />
<el-table-column prop="CodeEn" label="字段名英文" min-width="120px" :show-overflow-tooltip="true" />
<el-table-column prop="Value" label="字段英文" min-width="120px" :show-overflow-tooltip="true" />
<el-table-column prop="ValueCN" label="字段中文" min-width="120px" :show-overflow-tooltip="true" />
<el-table-column
@ -821,10 +870,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,
@ -1097,6 +1149,7 @@ export default {
IsShowParent: 0,
Sort: 0,
Code: null,
CodeEn: null,
Value: null,
ValueCN: null,
EnumType: '',
@ -1111,6 +1164,7 @@ export default {
IsSpecialType: false,
DataType: '',
ChildDataLabel: null,
ChildDataEnLabel: null,
ChildDataValue: null,
DateType: null,
DictionaryCode: null,
@ -1118,6 +1172,7 @@ export default {
ForeignKeyTableName: null,
ForeignKeyValue: null,
ForeignKeyText: null,
ForeignKeyEnText: null,
TableConfigList: [],
UrlConfig: {
RoutePath: null,

View File

@ -213,7 +213,6 @@ export default {
}).then(res => {
this.loading = false
this.list = res.Result
console.log(this.$d.GlobalAssessType)
})
},
handleAdd() {
@ -265,7 +264,7 @@ export default {
}
</script>
<style scoped>
<style lang="scss" scoped>
/deep/ .el-form-item__content{
width: calc(100% - 110px);
}

View File

@ -62,7 +62,7 @@
<template slot-scope="scope">
<div>
<el-radio-group v-model="scope.row.CrterionDictionaryGroup" @change="(v) => {crterionDictionaryGroupChange(v, scope.row)}" size="mini">
<el-radio-button v-for="item of $d.CrterionDictionaryGroup" :label="item.value">{{item.label}}</el-radio-button>
<el-radio-button v-for="item of $d.CrterionDictionaryGroup" :label="item.value" :key="item.id">{{item.label}}</el-radio-button>
</el-radio-group>
</div>
</template>
@ -158,7 +158,6 @@ export default {
},
mounted() {
this.getList()
console.log(this.$d[this.parentCode])
},
methods: {
crterionDictionaryGroupChange(v, row) {
@ -231,7 +230,7 @@ export default {
}
</script>
<style scoped>
<style lang="scss" scoped>
/deep/ .el-form-item__content{
width: calc(100% - 110px);
}

View File

@ -10,7 +10,15 @@
>
<div class="base-dialog-body">
<el-form-item label="类型" prop="Type">
<el-select v-model="form.Type" clearable @change="((val)=>{typeChange(val, form)})">
<el-select
v-model="form.Type"
clearable
@change="
(val) => {
typeChange(val, form)
}
"
>
<el-option
v-for="item of $d.Criterion_Question_Type"
:key="item.value"
@ -31,10 +39,7 @@
v-if="form.Type === 'group'"
prop="GroupClassify"
>
<el-select
v-model="form.GroupClassify"
clearable
>
<el-select v-model="form.GroupClassify" clearable>
<el-option
v-for="item of groupClassifyList"
:key="item.Id"
@ -70,13 +75,9 @@
v-if="form.Type === 'number' || form.Type === 'calculation'"
label="数值类型"
prop="ValueType"
:rules="[
{ required: true, message: '请选择'}
]"
>
<el-radio-group
v-model="form.ValueType"
:rules="[{ required: true, message: '请选择' }]"
>
<el-radio-group v-model="form.ValueType">
<el-radio
v-for="item of $d.ValueType"
:key="item.id"
@ -92,9 +93,7 @@
v-if="form.Type === 'number' || form.Type === 'calculation'"
label="单位"
>
<el-radio-group
v-model="form.Unit"
>
<el-radio-group v-model="form.Unit">
<el-radio
v-for="item of $d.ValueUnit"
:key="item.id"
@ -109,15 +108,26 @@
label="表格类型标识"
prop="LesionType"
>
<el-select
v-model="form.LesionType"
clearable
>
<el-select v-model="form.LesionType" clearable>
<el-option
v-for="item of lesionTypes"
:key="item.Code"
:value="parseInt(item.Code)"
:label="$fd('LesionType',parseInt(item.Code))"
:label="$fd('LesionType', parseInt(item.Code))"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.Type === 'basicTable'"
label="表格类型标识"
prop="LesionType"
>
<el-select v-model="form.LesionType" clearable>
<el-option
v-for="item of lesionTypeList"
:key="item.Id"
:value="parseInt(item.Code)"
:label="item.ValueCN"
/>
</el-select>
</el-form-item>
@ -133,25 +143,27 @@
</el-form-item> -->
<!-- 选项类型 -->
<el-form-item
v-if="form.Type === 'select' || form.Type === 'radio' || form.Type === 'calculation'"
v-if="
form.Type === 'select' ||
form.Type === 'radio' ||
form.Type === 'calculation'
"
label="选项类型"
prop="QuestionGenre"
:rules="[
{ required: form.Type !== 'calculation', message: '请选择'}
]"
:rules="[{ required: form.Type !== 'calculation', message: '请选择' }]"
>
<el-radio-group
v-model="form.QuestionGenre"
@change="((val)=>{questionGenreChange(val, form)})"
@change="
(val) => {
questionGenreChange(val, form)
}
"
>
<el-radio
:label="-1"
>
</el-radio>
<el-radio :label="-1"> </el-radio>
<el-radio
v-for="item of $d.TableQuestionType"
v-show="item.value===0 || item.value===3"
v-show="item.value === 0 || item.value === 3"
:key="item.id"
:label="item.value"
>
@ -183,13 +195,8 @@
/>
</el-select>
</el-form-item>
<el-form-item
label="转化显示类型"
prop="ConvertShowType"
>
<el-radio-group
v-model="form.ConvertShowType"
>
<el-form-item label="转化显示类型" prop="ConvertShowType">
<el-radio-group v-model="form.ConvertShowType">
<el-radio
v-for="item of $d.ConvertShowType"
:key="item.id"
@ -200,11 +207,19 @@
</el-radio-group>
</el-form-item>
<el-form-item
v-if="form.QuestionGenre === 3 "
v-if="form.QuestionGenre === 3"
label="关联字典"
prop="DictionaryCode"
>
<el-select v-model="form.DictionaryCode" clearable @change="() => {form.DefaultValue = null}">
<el-select
v-model="form.DictionaryCode"
clearable
@change="
() => {
form.DefaultValue = null
}
"
>
<el-option
v-for="item of dicList"
:key="item.Id"
@ -213,10 +228,7 @@
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.QuestionGenre === 3 "
label="默认值"
>
<el-form-item v-if="form.QuestionGenre === 3" label="默认值">
<el-select v-model="form.DefaultValue" clearable>
<el-option
v-for="item of form.DictionaryCode ? $d[form.DictionaryCode] : []"
@ -226,10 +238,7 @@
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.QuestionGenre === 0 "
label="默认值"
>
<el-form-item v-if="form.QuestionGenre === 0" label="默认值">
<el-select v-model="form.DefaultValue" clearable>
<el-option
v-for="item of form.TypeValue ? form.TypeValue.split('|') : []"
@ -240,10 +249,18 @@
</el-select>
</el-form-item>
<el-form-item v-if="form.Type !== 'group'" label="是否显示" prop="ShowQuestion">
<el-form-item
v-if="form.Type !== 'group'"
label="是否显示"
prop="ShowQuestion"
>
<el-radio-group
v-model="form.ShowQuestion"
@change="((val)=>{isShowQuestionChange(val, form)})"
@change="
(val) => {
isShowQuestionChange(val, form)
}
"
>
<el-radio
v-for="item of $d.ShowQuestion"
@ -257,14 +274,18 @@
<!-- 显示时依赖父问题 -->
<el-form-item
v-if="form.Type !== 'group' && form.ShowQuestion===1"
v-if="form.Type !== 'group' && form.ShowQuestion === 1"
label="显示依赖父问题"
prop="ParentId"
>
<el-select
v-model="form.ParentId"
clearable
@change="((val)=>{parentQuestionChange(val, form)})"
@change="
(val) => {
parentQuestionChange(val, form)
}
"
>
<el-option
v-for="item of parentOptions"
@ -275,15 +296,19 @@
</el-select>
</el-form-item>
<!-- 显示时依赖父问题触发值 -->
<el-form-item v-if="form.ParentId && form.ShowQuestion===1" label="显示触发值" prop="ParentTriggerValueList">
<!-- <el-select v-model="form.ParentTriggerValue" clearable>-->
<!-- <el-option-->
<!-- v-for="item of parentTriggerValOptions"-->
<!-- :key="item.id"-->
<!-- :label="item.label"-->
<!-- :value="item.value.toString()"-->
<!-- />-->
<!-- </el-select>-->
<el-form-item
v-if="form.ParentId && form.ShowQuestion === 1"
label="显示触发值"
prop="ParentTriggerValueList"
>
<!-- <el-select v-model="form.ParentTriggerValue" clearable>-->
<!-- <el-option-->
<!-- v-for="item of parentTriggerValOptions"-->
<!-- :key="item.id"-->
<!-- :label="item.label"-->
<!-- :value="item.value.toString()"-->
<!-- />-->
<!-- </el-select>-->
<el-select v-model="form.ParentTriggerValueList" clearable multiple>
<el-option
v-for="item of parentTriggerValOptions"
@ -293,16 +318,44 @@
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.Type === 'select' || form.Type === 'radio' || form.Type === 'calculation'" label="是否裁判问题">
<el-radio-group v-model="form.IsJudgeQuestion" @change="((val)=>{isJudgeQuestionChange(val, form)})">
<el-radio v-for="item of $d.YesOrNo" :key="`YesOrNo${item.value}`" :label="item.value">{{ item.label }}</el-radio>
<el-form-item
v-if="
form.Type === 'select' ||
form.Type === 'radio' ||
form.Type === 'calculation' ||
form.Type === 'number'
"
label="是否裁判问题"
>
<el-radio-group
v-model="form.IsJudgeQuestion"
@change="
(val) => {
isJudgeQuestionChange(val, form)
}
"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="`YesOrNo${item.value}`"
:label="item.value"
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.Type !== 'group' && form.Type !== 'table'" label="是否必填" prop="IsRequired">
<el-form-item
v-if="form.Type !== 'group' && form.Type !== 'table'"
label="是否必填"
prop="IsRequired"
>
<el-radio-group
v-model="form.IsRequired"
:disabled="form.IsJudgeQuestion===true || form.ShowQuestion===2"
@change="((val)=>{isRequiredChange(val, form)})"
:disabled="form.IsJudgeQuestion === true || form.ShowQuestion === 2"
@change="
(val) => {
isRequiredChange(val, form)
}
"
>
<el-radio
v-for="item of $d.QuestionRequired"
@ -323,7 +376,11 @@
<el-select
v-model="form.RelevanceId"
clearable
@change="((val)=>{relevanceQuestionChange(val, form)})"
@change="
(val) => {
relevanceQuestionChange(val, form)
}
"
>
<el-option
v-for="item of parentOptions"
@ -339,14 +396,14 @@
label="必填触发值"
prop="RelevanceValueList"
>
<!-- <el-select v-model="form.RelevanceValue" clearable>-->
<!-- <el-option-->
<!-- v-for="item of reParentTriggerValOptions"-->
<!-- :key="item.id"-->
<!-- :label="item.label"-->
<!-- :value="item.value.toString()"-->
<!-- />-->
<!-- </el-select>-->
<!-- <el-select v-model="form.RelevanceValue" clearable>-->
<!-- <el-option-->
<!-- v-for="item of reParentTriggerValOptions"-->
<!-- :key="item.id"-->
<!-- :label="item.label"-->
<!-- :value="item.value.toString()"-->
<!-- />-->
<!-- </el-select>-->
<el-select v-model="form.RelevanceValueList" clearable multiple>
<el-option
v-for="item of reParentTriggerValOptions"
@ -385,7 +442,11 @@
</el-form-item> -->
<el-form-item v-if="form.Type !== 'group'" label="问题分组">
<el-select v-model="form.GroupId" clearable :disabled="isParentExistGroup">
<el-select
v-model="form.GroupId"
clearable
:disabled="isParentExistGroup"
>
<el-option
v-for="group of groupOptions"
:key="group.GroupId"
@ -396,12 +457,22 @@
</el-form-item>
<el-form-item label="限制编辑">
<el-radio-group v-model="form.LimitEdit">
<el-radio v-for="item of $d.LimitEdit" :key="`LimitEdit${item.value}`" :label="item.value">{{ item.label }}</el-radio>
<el-radio
v-for="item of $d.LimitEdit"
:key="`LimitEdit${item.value}`"
:label="item.value"
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
<el-form-item label="全局阅片是否显示">
<el-radio-group v-model="form.GlobalReadingShowType">
<el-radio v-for="item of $d.GlobalReadingShowType" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
<el-radio
v-for="item of $d.GlobalReadingShowType"
:key="item.id"
:label="item.value"
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
<el-form-item
@ -409,10 +480,7 @@
v-if="form.Type !== 'group'"
prop="QuestionType"
>
<el-select
v-model="form.QuestionType"
clearable
>
<el-select v-model="form.QuestionType" clearable>
<el-option
v-for="item of CriterionDictionaryList.QuestionType"
:key="item.Id"
@ -428,9 +496,18 @@
</el-radio-group>
</el-form-item> -->
<el-form-item v-if="form.Type === 'upload'" label="最大上传个数">
<el-input-number v-model="form.ImageCount" controls-position="right" :min="1" :max="10" />
<el-input-number
v-model="form.ImageCount"
controls-position="right"
:min="1"
:max="10"
/>
</el-form-item>
<el-form-item v-if="form.Type === 'select' || form.Type === 'radio'" label="高亮标记值" prop="HighlightAnswerList">
<el-form-item
v-if="form.Type === 'select' || form.Type === 'radio'"
label="高亮标记值"
prop="HighlightAnswerList"
>
<el-select v-model="form.HighlightAnswerList" clearable multiple>
<template v-if="form.TypeValue">
<el-option
@ -471,20 +548,23 @@
<el-form-item label="是否在阅片页面显示">
<el-radio-group v-model="form.IsShowInDicom">
<el-radio v-for="item of $d.YesOrNo" :key="`YesOrNo${item.value}`" :label="item.value">{{ item.label }}</el-radio>
<el-radio
v-for="item of $d.YesOrNo"
:key="`YesOrNo${item.value}`"
:label="item.value"
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
<el-form-item
:label="$t('trials:readingUnit:qsList:title:Remark')"
prop="Remark"
>
<el-input
v-model="form.Remark"
/>
<el-input v-model="form.Remark" />
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<div class="base-dialog-footer" style="text-align: right; margin-top: 10px">
<el-form-item>
<!-- 取消 -->
<el-button
@ -496,7 +576,12 @@
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" type="primary" :loading="btnLoading" @click="save">
<el-button
size="small"
type="primary"
:loading="btnLoading"
@click="save"
>
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
@ -504,19 +589,30 @@
</el-form>
</template>
<script>
import { getBasicConfigSelect, addOrUpdateReadingQuestionSystem, getSystemCriterionOtherQuestion, getSystemGroupNameList, getCriterionDictionary, getCriterionDictionaryList } from '@/api/dictionary'
import {
getBasicConfigSelect,
addOrUpdateReadingQuestionSystem,
getSystemCriterionOtherQuestion,
getSystemGroupNameList,
getCriterionDictionary,
getCriterionDictionaryList,
} from '@/api/dictionary'
import { getCriterionLesionType } from '@/api/trials'
export default {
name: 'AddOrUpdateClinicalData',
props: {
data: {
type: Object,
default() { return {} }
default() {
return {}
},
},
type: {
type: String,
default() { return '' }
}
default() {
return ''
},
},
},
data() {
var validateTypeVal = (rule, value, callback) => {
@ -553,7 +649,7 @@ export default {
IsJudgeQuestion: false,
GroupName: '',
GroupEnName: '',
GroupClassify:null,
GroupClassify: null,
Remark: '',
ImageCount: 1,
RelevanceId: '',
@ -570,32 +666,38 @@ export default {
GroupId: null,
ConvertShowType: 0,
QuestionClassify: null,
HighlightAnswerList: []
HighlightAnswerList: [],
},
rules: {
Type: [
{ required: true, message: '请选择', trigger: 'blur' }
Type: [{ required: true, message: '请选择', trigger: 'blur' }],
LesionType: [{ required: true, message: '请选择', trigger: 'blur' }],
QuestionName: [
{ required: true, message: '请注明', trigger: 'blur' },
{ max: 300, message: '最大长度为 300' },
],
LesionType: [
{ required: true, message: '请选择', trigger: 'blur' }
],
QuestionName: [{ required: true, message: '请注明', trigger: 'blur' },
{ max: 300, message: '最大长度为 300' }],
QuestionGenre: [{ required: true, message: '请选择', trigger: 'blur' }],
TypeValue: [{ required: true, message: '请注明', trigger: 'blur' },
TypeValue: [
{ required: true, message: '请注明', trigger: 'blur' },
{ validator: validateTypeVal, trigger: 'blur' },
{ max: 500, message: '最大长度为 500' }],
DictionaryCode: [{ required: true, message: '请选择', trigger: 'blur' }],
{ max: 500, message: '最大长度为 500' },
],
DictionaryCode: [
{ required: true, message: '请选择', trigger: 'blur' },
],
ShowQuestion: [{ required: true, message: '请选择', trigger: 'blur' }],
IsRequired: [{ required: true, message: '请选择', trigger: 'blur' }],
ParentId: [{ required: true, message: '请选择', trigger: 'blur' }],
ParentTriggerValueList: [
{ required: true, message: '请选择', trigger: 'blur' }
{ required: true, message: '请选择', trigger: 'blur' },
],
RelevanceId: [{ required: true, message: '请选择', trigger: 'blur' }],
RelevanceValueList: [{ required: true, message: '请选择', trigger: 'blur' }],
GroupName: [{ required: true, message: '请注明', trigger: 'blur' },
{ max: 50, message: '最大长度为 50' }]
RelevanceValueList: [
{ required: true, message: '请选择', trigger: 'blur' },
],
GroupName: [
{ required: true, message: '请注明', trigger: 'blur' },
{ max: 50, message: '最大长度为 50' },
],
},
loading: false,
btnLoading: false,
@ -606,8 +708,9 @@ export default {
lesionTypes: [],
dicList: [],
CriterionDictionaryList: [],
groupClassifyList:[],
highlightAnswers: []
groupClassifyList: [],
highlightAnswers: [],
lesionTypeList: []
}
},
watch: {
@ -622,14 +725,14 @@ export default {
}
let res = await getCriterionDictionary({
ReadingCriterionId: this.data.ReadingQuestionCriterionSystemId,
DictionaryCode: this.form.DictionaryCode
DictionaryCode: this.form.DictionaryCode,
})
this.highlightAnswers = res.Result[this.form.DictionaryCode]
} catch (e) {
console.log(e)
}
}
}
},
},
},
mounted() {
this.initForm()
@ -638,22 +741,31 @@ export default {
getCriterionDictionary() {
getCriterionDictionary({
ReadingCriterionId: this.data.ReadingQuestionCriterionSystemId,
DictionaryCode: 'QuestionType'
}).then(res => {
DictionaryCode: 'QuestionType',
}).then((res) => {
this.CriterionDictionaryList = res.Result
})
},
getGroupClassifyDictionary() {
getCriterionDictionary({
ReadingCriterionId: this.data.ReadingQuestionCriterionSystemId,
DictionaryCode: 'GroupClassify'
}).then(res => {
DictionaryCode: 'GroupClassify',
}).then((res) => {
this.groupClassifyList = res.Result.GroupClassify
})
},
getLesionTypeDictionary() {
getCriterionDictionary({
ReadingCriterionId: this.data.ReadingQuestionCriterionSystemId,
DictionaryCode: 'LesionType',
}).then((res) => {
this.lesionTypeList = res.Result.LesionType
})
},
async initForm() {
await this.getGroupOptions()
await this.getParentQuestions()
await this.getLesionTypeDictionary()
await this.getLesionType()
if (Object.keys(this.data).length > 0) {
for (const k in this.form) {
@ -661,37 +773,47 @@ export default {
this.form[k] = this.data[k]
}
}
if (this.form.ParentId !== '' && this.form.ParentId !== null && this.form.GroupName !== '') {
if (
this.form.ParentId !== '' &&
this.form.ParentId !== null &&
this.form.GroupName !== ''
) {
this.isParentExistGroup = true
}
if (this.form.ParentId) {
var index = this.parentOptions.findIndex(item => {
var index = this.parentOptions.findIndex((item) => {
return item.QuestionId === this.form.ParentId
})
if (index !== -1) {
if (this.parentOptions[index].QuestionGenre === 3) {
this.parentTriggerValOptions = this.$d[this.parentOptions[index].DictionaryCode]
this.parentTriggerValOptions =
this.$d[this.parentOptions[index].DictionaryCode]
} else {
const options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
this.parentOptions[index].TypeValue.split('|').forEach(
(item, index) => {
options.push({ id: index, label: item, value: item })
})
}
)
this.parentTriggerValOptions = options
}
}
}
if (this.form.RelevanceId) {
var index = this.parentOptions.findIndex(item => {
var index = this.parentOptions.findIndex((item) => {
return item.QuestionId === this.form.RelevanceId
})
if (index !== -1) {
if (this.parentOptions[index].QuestionGenre === 3) {
this.reParentTriggerValOptions = this.$d[this.parentOptions[index].DictionaryCode]
this.reParentTriggerValOptions =
this.$d[this.parentOptions[index].DictionaryCode]
} else {
const options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
this.parentOptions[index].TypeValue.split('|').forEach(
(item, index) => {
options.push({ id: index, label: item, value: item })
})
}
)
this.reParentTriggerValOptions = options
}
}
@ -701,13 +823,14 @@ export default {
this.getGroupClassifyDictionary()
},
save() {
this.$refs.clinicalDataForm.validate(valid => {
this.$refs.clinicalDataForm.validate((valid) => {
if (!valid) return
this.btnLoading = true
if (this.form.Type !== 'upload') {
this.form.ImageCount = 0
}
addOrUpdateReadingQuestionSystem(this.form).then(res => {
addOrUpdateReadingQuestionSystem(this.form)
.then((res) => {
this.btnLoading = false
this.$emit('getList')
this.$emit('close')
@ -722,13 +845,15 @@ export default {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
criterionId: this.data.ReadingQuestionCriterionSystemId
criterionId: this.data.ReadingQuestionCriterionSystemId,
}
getSystemGroupNameList(param).then(res => {
getSystemGroupNameList(param)
.then((res) => {
this.groupOptions = res.Result
this.loading = false
resolve()
}).catch(() => {
})
.catch(() => {
this.loading = false
reject()
})
@ -738,14 +863,17 @@ export default {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
readingQuestionCriterionSystemId: this.data.ReadingQuestionCriterionSystemId,
id: this.data.Id
readingQuestionCriterionSystemId:
this.data.ReadingQuestionCriterionSystemId,
id: this.data.Id,
}
getSystemCriterionOtherQuestion(param).then(res => {
getSystemCriterionOtherQuestion(param)
.then((res) => {
this.parentOptions = res.Result
this.loading = false
resolve()
}).catch(() => {
})
.catch(() => {
this.loading = false
reject()
})
@ -819,17 +947,20 @@ export default {
parentQuestionChange(val, form) {
this.isParentExistGroup = false
if (val) {
var index = this.parentOptions.findIndex(item => {
var index = this.parentOptions.findIndex((item) => {
return item.QuestionId === val
})
if (index !== -1) {
if (this.parentOptions[index].QuestionGenre === 3) {
this.parentTriggerValOptions = this.$d[this.parentOptions[index].DictionaryCode]
this.parentTriggerValOptions =
this.$d[this.parentOptions[index].DictionaryCode]
} else {
var options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
this.parentOptions[index].TypeValue.split('|').forEach(
(item, index) => {
options.push({ id: index, label: item, value: item })
})
}
)
this.parentTriggerValOptions = options
}
if (this.parentOptions[index].GroupName) {
@ -845,17 +976,20 @@ export default {
},
relevanceQuestionChange(val, form) {
if (val) {
var index = this.parentOptions.findIndex(item => {
var index = this.parentOptions.findIndex((item) => {
return item.QuestionId === val
})
if (index !== -1) {
if (this.parentOptions[index].QuestionGenre === 3) {
this.reParentTriggerValOptions = this.$d[this.parentOptions[index].DictionaryCode]
this.reParentTriggerValOptions =
this.$d[this.parentOptions[index].DictionaryCode]
} else {
var options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
this.parentOptions[index].TypeValue.split('|').forEach(
(item, index) => {
options.push({ id: index, label: item, value: item })
})
}
)
this.reParentTriggerValOptions = options
}
}
@ -871,13 +1005,15 @@ export default {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
SystemCriterionId: this.data.ReadingQuestionCriterionSystemId
SystemCriterionId: this.data.ReadingQuestionCriterionSystemId,
}
getCriterionLesionType(param).then(res => {
getCriterionLesionType(param)
.then((res) => {
this.lesionTypes = res.Result
this.loading = false
resolve()
}).catch(() => {
})
.catch(() => {
this.loading = false
reject()
})
@ -885,9 +1021,8 @@ export default {
},
close() {
this.$emit('close')
}
}
},
},
}
</script>
<style lang="scss" scoped>

View File

@ -170,7 +170,7 @@
<el-button
type="primary"
size="mini"
:disabled="scope.row.Type !== 'table'"
:disabled="!(scope.row.Type === 'table' || scope.row.Type === 'basicTable')"
@click="handleConfig(scope.row)"
>
表格问题

View File

@ -69,7 +69,7 @@
>{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="序号">
<el-form-item label="序号" prop="ShowOrder">
<el-input v-model="form.ShowOrder" type="number" />
</el-form-item>
<!-- 描述 -->
@ -135,7 +135,8 @@ export default {
Part: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] }],
TULOC: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] }],
Remark: [{ max: 500, message: `${this.$t('common:ruleMessage:maxLength')} 500`, trigger: ['blur', 'change'] }],
IsLymphNodes: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur'] }]
IsLymphNodes: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur'] }],
ShowOrder: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] }]
},
lesionTypes: [],
loading: false,

View File

@ -5,7 +5,7 @@
<el-form :inline="true" size="mini">
<el-form-item label="器官类型">
<el-select v-model="searchData.OrganType" clearable style="width:130px;">
<el-option v-for="item of CriterionDictionaryOrganTypeList.OrganType" :key="item.Id" :value="parseInt(item.Code)" :label="item.ValueCN" />
<el-option v-for="item of CriterionDictionaryOrganTypeList && CriterionDictionaryOrganTypeList.OrganType" :key="item.Id" :value="parseInt(item.Code)" :label="item.ValueCN" />
</el-select>
</el-form-item>
<el-form-item label="部位">

View File

@ -355,11 +355,11 @@
<el-radio v-for="item of $d.LimitEdit" :key="item.value" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="全局阅片是否显示">
<!-- <el-form-item label="全局阅片是否显示">
<el-radio-group v-model="form.GlobalReadingShowType">
<el-radio v-for="item of $d.GlobalReadingShowType" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form-item> -->
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item>
@ -445,7 +445,7 @@ export default {
DependParentId: '',
QuestionMark: null,
LimitEdit: 0,
GlobalReadingShowType: null,
// GlobalReadingShowType: null,
QuestionClassify: null,
HighlightAnswerList: []

View File

@ -1,11 +1,29 @@
<template>
<div style="display: flex;justify-content: center">
<div style="width: 600px;text-align: center;border: 1px solid #e6e6e6;margin-top:40px;padding:10px;">
<div class="trial-myinfo-head" style="font-size: 30px;line-height: 120px;">
<div style="display: flex; justify-content: center">
<div
style="
width: 600px;
text-align: center;
border: 1px solid #e6e6e6;
margin-top: 40px;
padding: 10px;
"
>
<div
class="trial-myinfo-head"
style="font-size: 30px; line-height: 120px"
>
<!-- 首次登录修改密码 -->
{{ $t('recompose:title:init') }}
</div>
<el-form ref="passwordForm" v-loading="loading" label-position="right" :model="password" :rules="passwordFormRules" label-width="200px">
<el-form
ref="passwordForm"
v-loading="loading"
label-position="right"
:model="password"
:rules="passwordFormRules"
:label-width="$i18n.locale == 'zh' ? '100px' : '200px'"
>
<!-- 邮箱 -->
<el-form-item :label="$t('recompose:form:email')" prop="Email">
<el-input v-model="password.Email" disabled />
@ -19,70 +37,135 @@
<el-input v-model="password.NewUserName" />
</el-form-item>
<!-- 新密码 -->
<el-form-item :label="$t('recompose:form:newPassword')" prop="NewPassWord">
<el-input v-model="password.NewPassWord" type="password" show-password auto-complete="new-password" />
<el-form-item
:label="$t('recompose:form:newPassword')"
prop="NewPassWord"
>
<el-input
v-model="password.NewPassWord"
type="password"
show-password
auto-complete="new-password"
/>
</el-form-item>
<!-- 确认密码 -->
<el-form-item :label="$t('recompose:form:confirmPassword')" prop="ConfirmPassWord">
<el-input v-model="password.ConfirmPassWord" type="password" show-password auto-complete="new-password" />
</el-form-item>
<el-form-item style="text-align:right">
<!-- 取消 -->
<el-button
size="small"
@click="cancel"
<el-form-item
:label="$t('recompose:form:confirmPassword')"
prop="ConfirmPassWord"
>
<el-input
v-model="password.ConfirmPassWord"
type="password"
show-password
auto-complete="new-password"
/>
</el-form-item>
<el-form-item style="text-align: right">
<!-- 取消 -->
<el-button size="small" @click="cancel">
{{ $t('recompose:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button
type="primary"
size="small"
@click="save"
>
<el-button type="primary" size="small" @click="save">
{{ $t('recompose:button:save') }}
</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { initSetUserNameAndPwd } from '@/api/admin.js'
import md5 from 'js-md5'
import {mapGetters, mapMutations} from "vuex";
import { mapGetters, mapMutations } from 'vuex'
export default {
data() {
return {
password: {
NewUserName: null,
UserId: null
UserId: null,
},
passwordFormRules: {
NewUserName: [{ required: true, validator: (rule, value, callback) => {!value ? callback(new Error(this.$t('trials:researchForm:formRule:specify'))) : callback()}, trigger: 'blur' }],
NewUserName: [
{
required: true,
validator: (rule, value, callback) => {
var lang = zzSessionStorage.getItem('lang')
? zzSessionStorage.getItem('lang')
: 'zh'
/* eslint-disable */
var reg1 = /^[a-zA-Z0-9_]{4,16}$/ //8
console.log(!reg1.test(value))
if (!reg1.test(value)) {
callback(
lang === 'zh'
? new Error(
'1新建账号用户名字符长度最小为4个字符最大为16个字符只可使用字母、数字、下划线'
)
: new Error(
'For a new account, the username must have:1) At least 4 characters;2) At most 16 characters;3)Only letters, numbers, and underscores are allowed.'
)
)
} else {
callback()
}
},
trigger: 'blur',
},
],
NewPassWord: [
{ required: true, validator: (rule, value, callback) => {!value ? callback(new Error(this.$t('trials:researchForm:formRule:specify'))) : callback()}, trigger: 'blur' },
{
required: true,
validator: (rule, value, callback) => {
!value
? callback(
new Error(this.$t('trials:researchForm:formRule:specify'))
)
: callback()
},
trigger: 'blur',
},
{
required: true,
trigger: 'blur',
validator: this.$validatePassword
validator: this.$validatePassword,
},
],
ConfirmPassWord: [
{ required: true, validator: (rule, value, callback) => {!value ? callback(new Error(this.$t('trials:researchForm:formRule:specify'))) : callback()}, trigger: 'blur' }
]
{
required: true,
validator: (rule, value, callback) => {
!value
? callback(
new Error(this.$t('trials:researchForm:formRule:specify'))
)
: callback()
},
trigger: 'blur',
},
{
required: true,
trigger: 'blur',
validator: (rule, value, callback) => {
value !== this.password.NewPassWord
? callback(
new Error(
this.$t(
'trials:researchForm:formRule:NewPassWordAndConfirmPassWord'
)
)
)
: callback()
},
},
],
},
userId: null,
loading: false
loading: false,
}
},
computed: {
...mapGetters([
'asyncRoutes',
'routes',
'language'
])
...mapGetters(['asyncRoutes', 'routes', 'language']),
},
mounted() {
this.password.UserId = this.$route.query.UserId
@ -112,7 +195,7 @@ export default {
this.$updateDictionary()
},
save() {
this.$refs.passwordForm.validate(valid => {
this.$refs.passwordForm.validate((valid) => {
if (valid) {
if (this.password.NewPassWord !== this.password.ConfirmPassWord) {
//
@ -123,26 +206,32 @@ export default {
NewUserName: this.password.NewUserName,
UserId: this.password.UserId,
NewPwd: md5(this.password.NewPassWord),
access_token: this.password.access_token
access_token: this.password.access_token,
}
this.loading = true
initSetUserNameAndPwd(param).then(res => {
initSetUserNameAndPwd(param)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
// ,
this.$message.success(this.$t('recompose:message:updatedSuccessfully'))
this.$message.success(
this.$t('recompose:message:updatedSuccessfully')
)
setTimeout(() => {
this.logout()
}, 500)
}
}).catch(() => { this.loading = false })
})
.catch(() => {
this.loading = false
})
}
})
},
cancel() {
this.$refs['passwordForm'].resetFields()
}
}
},
},
}
</script>
<style>
@ -166,12 +255,15 @@ export default {
input:-webkit-autofill {
-webkit-text-fill-color: #ededed !important;
box-shadow: 0 0 0px 1000px transparent inset !important;
background-color:transparent;
background-color: transparent;
background-image: none;
transition: background-color 50000s ease-in-out 0s;
}
input {
background-color:transparent;
background-color: transparent;
caret-color: #fff;
}
.el-form-item {
margin-bottom: 30px;
}
</style>

View File

@ -114,7 +114,7 @@
size="mini"
@click="getList"
>
{{ $t("common:button:search") }}
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button
@ -122,7 +122,7 @@
icon="el-icon-refresh-left"
@click="handleReset"
>
{{ $t("common:button:reset") }}
{{ $t('common:button:reset') }}
</el-button>
<!-- <el-button
type="primary"
@ -141,7 +141,7 @@
v-if="level > 7"
:disabled="tableSelectData.length <= 0"
>
{{ $t("feedBack:button:resolve") }}
{{ $t('feedBack:button:resolve') }}
</el-button>
</el-form>
<el-table
@ -257,7 +257,7 @@
>
<template slot-scope="scope">
<el-tag :type="['danger', 'success'][scope.row.State]">{{
$fd("FeedBackStatus", scope.row.State)
$fd('FeedBackStatus', scope.row.State)
}}</el-tag>
</template>
</el-table-column>
@ -278,7 +278,7 @@
>
<template slot-scope="scope">
<el-button size="mini" type="text" @click="getDetail(scope.row)">
{{ $t("common:button:view") }}
{{ $t('common:button:view') }}
</el-button>
</template>
</el-table-column>
@ -295,8 +295,8 @@
</div>
</template>
<script>
import Pagination from "@/components/Pagination";
import { batchUpdateFeedBackState, getUserFeedBackList } from "@/api/trials.js";
import Pagination from '@/components/Pagination'
import { batchUpdateFeedBackState, getUserFeedBackList } from '@/api/trials.js'
const searchDataDefault = () => {
return {
TrialKeyInfo: null,
@ -312,23 +312,23 @@ const searchDataDefault = () => {
PageIndex: 1,
PageSize: 20,
Asc: false,
SortField: "CreateTime",
};
};
SortField: 'CreateTime',
}
}
export default {
components: { Pagination },
props: {
trialId: {
type: String,
default: "",
default: '',
},
},
dicts: [
"NoticeApplicableTrial",
"NoteLevel",
"NoteType",
"NoticeState",
"NoticeMode",
'NoticeApplicableTrial',
'NoteLevel',
'NoteType',
'NoticeState',
'NoticeMode',
],
data() {
return {
@ -340,100 +340,100 @@ export default {
searchData: searchDataDefault(),
tableSelectData: [],
UserTypeOptins: [
{ label: "CRC", value: 2 },
{ label: "IR", value: 13 },
{ label: 'CRC', value: 2 },
{ label: 'IR', value: 13 },
],
};
}
},
computed: {
level() {
if (this.hasPermi(["role:dev", "role:admin"])) {
return 9;
if (this.hasPermi(['role:dev', 'role:admin'])) {
return 9
}
if (this.hasPermi(["role:pm"])) {
return 8;
if (this.hasPermi(['role:pm', 'role:apm'])) {
return 8
}
if (this.hasPermi(["role:ir", "role:crc"])) {
return 7;
if (this.hasPermi(['role:ir', 'role:crc'])) {
return 7
}
return 0;
return 0
},
QuestionTypeOptions() {
if (this.level > 7) {
return [
...this.$d.FeedBackTypeToCRC.filter((item) => item.value > 0),
...this.$d.FeedBackTypeToIR,
];
]
}
if (this.hasPermi(["role:ir"])) {
return this.$d.FeedBackTypeToIR;
if (this.hasPermi(['role:ir'])) {
return this.$d.FeedBackTypeToIR
}
if (this.hasPermi(["role:crc"])) {
return this.$d.FeedBackTypeToCRC;
if (this.hasPermi(['role:crc'])) {
return this.$d.FeedBackTypeToCRC
}
return [];
return []
},
},
created() {
this.getList();
this.getList()
},
methods: {
// CheckBox
handleSelectTable(row) {
return row.State < 1;
return row.State < 1
},
async getList() {
try {
if (this.trialId) {
this.searchData.TrialId = this.trialId;
this.searchData.TrialId = this.trialId
}
this.loading = true;
let res = await getUserFeedBackList(this.searchData);
this.loading = false;
this.loading = true
let res = await getUserFeedBackList(this.searchData)
this.loading = false
if (res.IsSuccess) {
this.list = res.Result.CurrentPageData;
this.total = res.Result.TotalCount;
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
}
} catch (err) {
this.loading = false;
console.log(err);
this.loading = false
console.log(err)
}
},
handleDatetimeChange(val) {
if (val) {
this.searchData.BeginCreatime = val[0];
this.searchData.EndCreatime = val[1];
this.searchData.BeginCreatime = val[0]
this.searchData.EndCreatime = val[1]
} else {
this.searchData.BeginCreatime = "";
this.searchData.EndCreatime = "";
this.searchData.BeginCreatime = ''
this.searchData.EndCreatime = ''
}
},
//
handleReset() {
this.datetimerange = null;
this.handleDatetimeChange();
this.searchData = searchDataDefault();
this.getList();
this.datetimerange = null
this.handleDatetimeChange()
this.searchData = searchDataDefault()
this.getList()
},
//
async resolve() {
try {
let confirm = await this.$confirm(this.$t("feedBack:resolve:tip"), {
type: "warning",
let confirm = await this.$confirm(this.$t('feedBack:resolve:tip'), {
type: 'warning',
distinguishCancelAndClose: true,
});
if (!confirm) return;
})
if (!confirm) return
let data = {
IdList: this.tableSelectData.map((item) => item.Id),
state: 1,
};
let res = await batchUpdateFeedBackState(data);
}
let res = await batchUpdateFeedBackState(data)
if (res.IsSuccess) {
this.$message.success(this.$t("feedBack:resolve:success"));
this.getList();
this.$message.success(this.$t('feedBack:resolve:success'))
this.getList()
}
} catch (err) {
console.log(err);
console.log(err)
}
},
//
@ -441,32 +441,32 @@ export default {
//
handleSelectChange(selection) {
// console.log(selection, "handleSelectChange");
this.tableSelectData = selection;
this.tableSelectData = selection
},
//
handleSortByColumn(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
}
this.searchData.SortField = column.prop;
this.searchData.PageIndex = 1;
this.getList();
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
},
//
getDetail(row) {
this.$FB({
type: "detail",
type: 'detail',
Id: row.Id,
callBack: () => {
this.getList();
this.$emit("success");
this.getList()
this.$emit('success')
},
});
})
},
},
};
}
</script>
<style lang="scss">
.role {

View File

@ -61,16 +61,16 @@
:disabled="form.VerificationCode === '' || form.EmailOrPhone === ''"
@click="verifyCode"
>
{{ $t("passwordReset:button:verify") }}
{{ $t('passwordReset:button:verify') }}
</el-button>
</div>
<div class="box-wrapper" v-if="active === 1">
<div class="box-wrapper" v-show="active === 1">
<el-form
ref="resetForm"
ref="resetForm2"
v-loading="formLoading"
:model="form"
label-width="100px"
:rules="rules"
:label-width="$i18n.locale === 'en' ? '180px' : '100px'"
:rules="rules2"
class="demo-ruleForm"
size="small"
>
@ -131,7 +131,7 @@
<div style="width: 50%; margin: auto; display: flex">
<!-- 取消 -->
<el-button size="small" @click="onCancel" style="width: 46%">
{{ $t("passwordReset:button:cancel") }}
{{ $t('passwordReset:button:cancel') }}
</el-button>
<!-- 提交 -->
<el-button
@ -140,17 +140,17 @@
@click="onSubmit"
style="width: 46%"
>
{{ $t("passwordReset:button:submit") }}
{{ $t('passwordReset:button:submit') }}
</el-button>
</div>
</div>
<div class="box-wrapper flexBox" v-if="active === 3">
<svg-icon icon-class="resetSuccess" style="width: 300px; height: 300px" />
<p style="width: 100%; text-align: center">
{{ $t("resetPassword:successTip") }}
{{ $t('resetPassword:successTip') }}
</p>
<el-button size="small" type="primary" @click="goBack">
{{ $t("passwordReset:button:back") }}
{{ $t('passwordReset:button:back') }}
</el-button>
</div>
</div>
@ -160,32 +160,30 @@ import {
anonymousSendVerificationCode,
verifyAnonymousVerifyCode,
anonymousSetPassword,
} from "@/api/user";
import md5 from "js-md5";
import Img from "@/assets/icons/svg/resetSuccess.svg";
var timer = "";
var countdown = 60;
} from '@/api/user'
import md5 from 'js-md5'
import Img from '@/assets/icons/svg/resetSuccess.svg'
var timer = ''
var countdown = 60
export default {
data() {
var validateEmail = (rule, value, callback) => {
if (value === "") {
if (value === '') {
//
callback(new Error(this.$t("passwordReset:formRule:email")));
callback(new Error(this.$t('passwordReset:formRule:email')))
} else {
var reg =
/^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/;
/^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/
if (this.form.EmailOrPhone && reg.test(this.form.EmailOrPhone)) {
this.sendDisabled = false;
callback();
this.sendDisabled = false
callback()
} else {
//
callback(
new Error(this.$t("passwordReset:formRule:passwordinvalid"))
);
this.sendDisabled = true;
callback(new Error(this.$t('passwordReset:formRule:passwordinvalid')))
this.sendDisabled = true
}
}
}
};
// var validatePass = (rule, value, callback) => {
// if (value === '') {
// //
@ -198,181 +196,206 @@ export default {
// }
// }
var validatePass2 = (rule, value, callback) => {
if (value === "" || value === undefined) {
if (value === '' || value === undefined) {
//
callback(new Error(this.$t("passwordReset:formRule:confirmPassword")));
callback(new Error(this.$t('passwordReset:formRule:confirmPassword')))
} else if (value !== undefined && value !== this.form.NewPwd) {
//
callback(new Error(this.$t("passwordReset:formRule:passwordsDiffer")));
callback(new Error(this.$t('passwordReset:formRule:passwordsDiffer')))
} else {
callback();
callback()
}
}
};
return {
active: 0,
Img,
form: {
EmailOrPhone: "",
VerificationCode: "",
NewPwd: "",
UserId: "",
UserType: "",
EmailOrPhone: '',
VerificationCode: '',
NewPwd: '',
CheckPass: '',
UserId: '',
UserType: '',
},
users: [],
CheckPass: "",
CheckPass: '',
rules: {
EmailOrPhone: [
{
required: true,
validator: validateEmail,
trigger: ["blur", "change"],
trigger: ['blur', 'change'],
},
],
VerificationCode: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: ["blur", "change"],
message: this.$t('common:ruleMessage:specify'),
trigger: ['blur', 'change'],
},
],
NewPwd: [
{
required: true,
validator: this.$validatePassword,
trigger: ["blur", "change"],
trigger: ['blur', 'change'],
},
],
CheckPass: [
{
required: true,
validator: validatePass2,
trigger: ["blur", "change"],
trigger: ['blur', 'change'],
},
],
UserId: [
{
required: true,
message: this.$t("common:ruleMessage:select"),
trigger: ["blur", "change"],
message: this.$t('common:ruleMessage:select'),
trigger: ['blur', 'change'],
},
],
},
rules2: {
NewPwd: [
{
required: true,
validator: this.$validatePassword,
trigger: ['blur', 'change'],
},
],
CheckPass: [
{
required: true,
validator: validatePass2,
trigger: ['blur', 'change'],
},
],
UserId: [
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: ['blur', 'change'],
},
],
},
sendDisabled: true,
sendTitle: this.$t("passwordReset:button:send"),
sendTitle: this.$t('passwordReset:button:send'),
formLoading: false,
};
}
},
methods: {
onSubmit() {
this.$refs["resetForm"].validate((valid) => {
this.$refs['resetForm2'].validate((valid) => {
if (valid) {
this.formLoading = true;
this.formLoading = true
anonymousSetPassword(this.form.UserId, md5(this.form.NewPwd))
.then((res) => {
if (res.IsSuccess) {
//
this.$message.success(
this.$t("passwordReset:message:updatedSuccessfully")
);
this.formLoading = false;
this.active = 3;
this.$t('passwordReset:message:updatedSuccessfully')
)
this.formLoading = false
this.active = 3
// this.goBack();
} else {
this.$alert(res.ErrorMessage);
this.$alert(res.ErrorMessage)
}
})
.catch(() => {
this.formLoading = false;
});
this.formLoading = false
})
}
});
})
},
handleSendCode() {
const that = this;
this.sendDisabled = true;
const that = this
this.sendDisabled = true
// var isReviewer = JSON.parse(zzSessionStorage.getItem('IsReviewer'))
anonymousSendVerificationCode(this.form.EmailOrPhone)
.then((res) => {
if (res.IsSuccess) {
let msg = this.$t('passwordReset:message:tip:sendCode').replace("xxx", this.form.EmailOrPhone)
let msg = this.$t('passwordReset:message:tip:sendCode').replace(
'xxx',
this.form.EmailOrPhone
)
this.$message.success(msg)
that.settime(that);
that.settime(that)
} else {
that.$alert(res.ErrorMessage);
that.$alert(res.ErrorMessage)
}
})
.catch(() => {
this.sendDisabled = false;
});
this.sendDisabled = false
})
},
verifyCode() {
this.formLoading = true;
this.formLoading = true
verifyAnonymousVerifyCode(
this.form.EmailOrPhone,
this.form.VerificationCode
)
.then((res) => {
this.formLoading = false;
this.users = res.Result;
this.formLoading = false
this.users = res.Result
if (this.users.length === 1) {
this.form.UserId = this.users[0].UserId;
this.form.UserType = this.users[0].UserType;
this.form.UserId = this.users[0].UserId
this.form.UserType = this.users[0].UserType
}
//
this.$message.success(
this.$t("passwordReset:message:verifiedSuccessfully")
);
this.active = 1;
this.$t('passwordReset:message:verifiedSuccessfully')
)
this.active = 1
})
.catch(() => {
this.formLoading = false;
});
this.formLoading = false
})
},
handleEmailChange() {
var reg =
/^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/;
/^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/
if (this.form.EmailOrPhone && reg.test(this.form.EmailOrPhone)) {
this.sendDisabled = false;
this.sendDisabled = false
}
},
handleUserChange(val) {
const seleted = this.users.findIndex((user) => user.UserId === val);
const seleted = this.users.findIndex((user) => user.UserId === val)
if (seleted > -1) {
this.form.UserType = this.users[seleted].UserType;
this.form.UserType = this.users[seleted].UserType
}
},
settime(obj) {
if (countdown === 0) {
obj.sendDisabled = false;
obj.sendTitle = this.$t("passwordReset:button:send");
countdown = 60;
clearTimeout(timer);
return;
obj.sendDisabled = false
obj.sendTitle = this.$t('passwordReset:button:send')
countdown = 60
clearTimeout(timer)
return
} else {
obj.sendDisabled = true;
obj.sendTitle = `${this.$t(
"passwordReset:button:wait"
)}(${countdown}s)`;
countdown--;
obj.sendDisabled = true
obj.sendTitle = `${this.$t('passwordReset:button:wait')}(${countdown}s)`
countdown--
// eslint-disable-next-line no-self-assign
countdown = countdown;
countdown = countdown
timer = setTimeout(function () {
obj.settime(obj);
}, 1000);
obj.settime(obj)
}, 1000)
}
},
onCancel() {
this.$refs["resetForm"].resetFields();
this.$refs['resetForm'].resetFields()
Object.keys(this.form).forEach((key) => {
this.form[key] = "";
});
this.active = 0;
this.form[key] = ''
})
this.active = 0
},
goBack() {
this.$router.push("/login");
this.$router.push('/login')
},
},
};
}
</script>
<style>
.reset-wrapper {

View File

@ -27,7 +27,7 @@
style="width: 300px; height: 94px"
/>
</div>
<div class="title" v-else>{{ $t("login:title:system") }}</div>
<div class="title" v-else>{{ $t('login:title:system') }}</div>
</div>
<el-form
ref="loginForm"
@ -105,7 +105,7 @@
size="medium"
@click.native.prevent="handleLogin"
>
{{ $t("login:button:login") }}
{{ $t('login:button:login') }}
</el-button>
<div style="text-align: right">
<TopLang
@ -121,7 +121,7 @@
@click.native.prevent="handleResetPwd"
style="color: rgb(0, 147, 221)"
>
{{ $t("login:button:forgetPassword") }}
{{ $t('login:button:forgetPassword') }}
</el-button>
</div>
</el-form>
@ -174,10 +174,12 @@
>
<h1 style="text-align: center; margin-bottom: 20px">关于</h1>
<p style="margin-bottom: 20px" v-if="NODE_ENV === 'usa'">
{{ $t("login:title:system_title") }}
{{ $t('login:title:system_title') }}
</p>
<p style="margin-bottom: 20px" v-else>{{ $t('login:title:system') }}</p>
<p style="margin-bottom: 20px">
V{{ $version.IsEnv_US ? $version.Version_US : $version.Version }}
</p>
<p style="margin-bottom: 20px" v-else>{{ $t("login:title:system") }}</p>
<p style="margin-bottom: 20px">V{{ $version.Version }}</p>
<p style="margin-bottom: 20px" v-if="language === 'zh'">
Copyright © {{ new Date().getFullYear() }} 上海展影医疗科技有限公司
版权所有
@ -201,14 +203,14 @@
</template>
<script>
import { mapGetters, mapMutations } from "vuex";
import TopLang from "./topLang";
import { mapGetters, mapMutations } from 'vuex'
import TopLang from './topLang'
// import NoticeMarquee from '../trials/trials-layout/components/noticeMarquee'
import Vcode from "vue-puzzle-vcode";
import browserTip from "@/views/dictionary/template/browser/tip.vue";
import Img1 from "@/assets/pic-2.png";
import Vcode from 'vue-puzzle-vcode'
import browserTip from '@/views/dictionary/template/browser/tip.vue'
import Img1 from '@/assets/pic-2.png'
export default {
name: "Login",
name: 'Login',
components: { TopLang, Vcode, browserTip },
data() {
return {
@ -216,18 +218,18 @@ export default {
VUE_APP_OSS_CONFIG_REGION: process.env.VUE_APP_OSS_CONFIG_REGION,
aboutVisible: false,
loginForm: {
username: "",
password: "",
username: '',
password: '',
UserId: null,
},
loginRules: {
username: [
{
required: true,
message: this.$t("login:formRule:userName"),
trigger: "blur",
message: this.$t('login:formRule:userName'),
trigger: 'blur',
},
{ max: 20, message: `${this.$t("common:ruleMessage:maxLength")} 20` },
{ max: 20, message: `${this.$t('common:ruleMessage:maxLength')} 20` },
],
password: [
// {
@ -237,29 +239,29 @@ export default {
// },
{
required: true,
message: this.$t("login:formRule:password"),
trigger: "blur",
message: this.$t('login:formRule:password'),
trigger: 'blur',
},
{ max: 20, message: `${this.$t("common:ruleMessage:maxLength")} 20` },
{ max: 20, message: `${this.$t('common:ruleMessage:maxLength')} 20` },
],
},
loading: false,
passwordType: "password",
passwordType: 'password',
loginType: null,
location: null,
isShow: false,
showCode: false,
Img1,
};
}
},
computed: {
...mapGetters(["asyncRoutes", "routes", "language"]),
...mapGetters(['asyncRoutes', 'routes', 'language']),
},
watch: {
"$i18n.locale": {
'$i18n.locale': {
handler() {
if (this.$refs.browserTip) {
this.$refs.browserTip.open();
this.$refs.browserTip.open()
}
},
deep: true,
@ -267,88 +269,88 @@ export default {
},
},
mounted() {
this.loginType = this.$route.query.loginType;
this.location = this.$route.query.location;
zzSessionStorage.setItem("loginType", this.loginType);
localStorage.setItem("location", this.location);
if (process.env.VUE_APP_OSS_CONFIG_REGION === "oss-us-west-1") {
this.$i18n.locale = "en";
this.setLanguage("en");
this.$updateDictionary();
this.loginType = this.$route.query.loginType
this.location = this.$route.query.location
zzSessionStorage.setItem('loginType', this.loginType)
localStorage.setItem('location', this.location)
if (process.env.VUE_APP_OSS_CONFIG_REGION === 'oss-us-west-1') {
this.$i18n.locale = 'en'
this.setLanguage('en')
this.$updateDictionary()
} else {
if (this.location === "USA" || this.NODE_ENV === "usa") {
this.$i18n.locale = "en";
this.setLanguage("en");
this.$updateDictionary();
if (this.location === 'USA' || this.NODE_ENV === 'usa') {
this.$i18n.locale = 'en'
this.setLanguage('en')
this.$updateDictionary()
} else {
// this.$i18n.locale = 'zh'
// this.setLanguage('zh')
// this.$updateDictionary()
}
}
this.$refs.browserTip.open();
this.$refs.browserTip.open()
},
methods: {
...mapMutations({ setLanguage: "lang/setLanguage" }),
...mapMutations({ setLanguage: 'lang/setLanguage' }),
openAbout() {
this.aboutVisible = true;
this.aboutVisible = true
},
showPwd() {
if (this.passwordType === "password") {
this.passwordType = "";
if (this.passwordType === 'password') {
this.passwordType = ''
} else {
this.passwordType = "password";
this.passwordType = 'password'
}
this.$nextTick(() => {
this.$refs.password.focus();
});
this.$refs.password.focus()
})
},
handleLogin() {
this.loginType = this.$route.query.loginType;
this.loginType = this.$route.query.loginType
this.$refs.loginForm.validate((valid) => {
if (valid) {
if (this.showCode) {
this.isShow = true;
this.isShow = true
} else {
this.onSuccess();
this.onSuccess()
}
} else {
// console.log('error submit!!')
return false;
return false
}
});
})
},
loginIn(Id) {
this.loading = true;
this.showCode = false;
if (Id) this.loginForm.UserId = Id;
this.loading = true
this.showCode = false
if (Id) this.loginForm.UserId = Id
this.$store
.dispatch("user/login", this.loginForm)
.dispatch('user/login', this.loginForm)
.then((res) => {
if (res.BasicInfo.IsFirstAdd) {
// ,
this.$message.success(this.$t("login:message:login1"));
this.$message.success(this.$t('login:message:login1'))
setTimeout(() => {
this.$router.push({
path: `/recompose?userName=${this.loginForm.username}`,
});
}, 500);
return;
})
}, 500)
return
} else if (res.BasicInfo.LoginState === 1) {
//
this.$alert(
this.$t("login:message:login3"),
this.$t("common:title:warning"),
this.$t('login:message:login3'),
this.$t('common:title:warning'),
{
callback: (action) => {
this.$router.push({
path: `/recompose?userName=${this.loginForm.username}`,
});
return;
})
return
},
}
);
return;
)
return
} else if (res.IsMFA) {
this.$MFA({
UserId: res.BasicInfo.Id,
@ -356,55 +358,55 @@ export default {
username: this.loginForm.username,
callBack: this.loginIn,
cancelBack: () => {
this.loading = false;
this.loading = false
},
});
return;
})
return
} else if (res.BasicInfo.LoginState === 2) {
// IP'
// this.$alert(this.$t('login:message:login4'), this.$t('common:title:warning'))
this.$message.warning(this.$t("login:message:login4"));
this.$message.warning(this.$t('login:message:login4'))
}
this.$store.dispatch("permission/generateRoutes").then((res) => {
this.loading = false;
this.$store.dispatch('permission/generateRoutes').then((res) => {
this.loading = false
if (res && res.length > 0) {
this.$store.dispatch("global/getNoticeList");
this.$router.addRoutes(res);
if (this.loginType === "DevOps") {
this.$router.replace({ path: res[0].path });
return;
this.$store.dispatch('global/getNoticeList')
this.$router.addRoutes(res)
if (this.loginType === 'DevOps') {
this.$router.replace({ path: res[0].path })
return
}
if (this.hasPermi(["role:radmin"])) {
this.$router.replace({ path: res[0].path });
return;
if (this.hasPermi(['role:radmin'])) {
this.$router.replace({ path: res[0].path })
return
}
if (
this.hasPermi(["role:air", "role:rpm", "role:rcrc", "role:rir"])
this.hasPermi(['role:air', 'role:rpm', 'role:rcrc', 'role:rir'])
) {
this.$router.replace({ path: "/trials/trials-list" });
this.$router.replace({ path: '/trials/trials-list' })
} else {
this.$router.replace({ path: "/trials" });
this.$router.replace({ path: '/trials' })
}
} else {
//
this.$message.warning(this.$t("login:message:login2"));
this.$message.warning(this.$t('login:message:login2'))
}
});
})
})
.catch(() => {
this.showCode = true;
this.loading = false;
});
this.showCode = true
this.loading = false
})
},
onSuccess() {
this.isShow = false;
this.loginIn();
this.isShow = false
this.loginIn()
},
handleResetPwd() {
this.$router.push({ name: "Resetpassword" });
this.$router.push({ name: 'Resetpassword' })
},
},
};
}
</script>
<style lang="scss">

View File

@ -2,52 +2,65 @@
<div v-loading="loading" class="img-container">
<el-card class="box-card left">
<div class="title">
{{$t('trials:none-dicom-show:fileList')}}
{{ $t('trials:none-dicom-show:fileList') }}
</div>
<div class="left-content">
<!-- 检查层级 -->
<div v-for="(study,i) in studyList" :key="study.CodeView">
<div v-for="(study, i) in studyList" :key="study.CodeView">
<div class="study-desc">
<span>{{ study.CodeView }}</span>
<span style="margin:0 5px">{{ study.Modality }}</span>
<span style="margin: 0 5px">{{ study.Modality }}</span>
<span>{{ getBodyPart(study.BodyPart) }}</span>
</div>
<!-- 文件层级 -->
<div v-if="study.NoneDicomStudyFileList.length === 0" class="empty-text">
<slot name="empty">暂无数据</slot>
</div>
<div v-else id="imgList" style="height:100%;overflow: hidden">
<div
v-for="(item,j) in study.NoneDicomStudyFileList"
v-if="study.NoneDicomStudyFileList.length === 0"
class="empty-text"
>
<slot name="empty">{{ $t('trials:audit:message:noData') }}</slot>
</div>
<div v-else id="imgList" style="height: 100%; overflow: hidden">
<div
v-for="(item, j) in study.NoneDicomStudyFileList"
:id="`img${item.Id}`"
:key="item.Id"
:class="{
'is-boxActive': item.Id === currentFileId
'is-boxActive': item.Id === currentFileId,
}"
class="img-box"
@click="selected(item,i,j,true)"
@click="selected(item, i, j, true)"
>
{{ `${j+1}. ${item.FileName}` }}
<div v-if="item.FileName.length < 15" class="img-text">
{{ `${j + 1}. ${item.FileName}` }}
</div>
<el-tooltip v-else :content="item.FileName" placement="bottom">
<div class="img-text">
{{ `${j + 1}. ${item.FileName}` }}
</div>
</el-tooltip>
</div>
</div>
</div>
</div>
</el-card>
<!-- 预览图像 -->
<el-card class="box-card right">
<div style="width:100%;height: 100%;">
<Preview v-if="previewImage.imgList.length > 0" ref="previewImage" style="width:100%;" :preview-image="previewImage" :value="currentStudyFileIndex" @selectedImg="selectedImg" />
<div style="width: 100%; height: 100%">
<Preview
v-if="previewImage.imgList.length > 0"
ref="previewImage"
style="width: 100%"
:preview-image="previewImage"
:value="currentStudyFileIndex"
@selectedImg="selectedImg"
/>
</div>
</el-card>
<!-- <el-card class="box-card" style="width:300px;height:100%;padding: 10px;margin-left:10px;">
<CheckForm />
</el-card> -->
</div>
</template>
<script>
@ -59,7 +72,7 @@ import Preview from './components/preview'
export default {
name: 'Notice',
components: {
Preview
Preview,
// CheckForm
},
data() {
@ -76,18 +89,18 @@ export default {
popup: true, //
studyCode: '',
modality: '',
bodyPart: ''
bodyPart: '',
},
previewVisible: false,
studyList: [],
subjectVisitId: '',
sudyId: '',
loading: false,
bp:[]
bp: [],
}
},
async created(){
this.bp = await this.$getBodyPart(this.$route.query.trialId);
async created() {
this.bp = await this.$getBodyPart(this.$route.query.trialId)
},
async mounted() {
if (this.$router.currentRoute.query.TokenKey) {
@ -112,31 +125,47 @@ export default {
separator = ''
}
var arr = bodyPart.split(separator)
var newArr = arr.map(i => {
return this.$fd('Bodypart', i.trim(),'Code',{Bodypart:this.bp},'Name')
var newArr = arr.map((i) => {
return this.$fd(
'Bodypart',
i.trim(),
'Code',
{ Bodypart: this.bp },
'Name'
)
})
console.log(newArr,this.bp)
console.log(newArr, this.bp)
return newArr.join(' | ')
},
// Dicom
getNoneDicomList() {
this.loading = true
getNoneDicomStudyList(this.subjectVisitId, this.studyId).then(res => {
getNoneDicomStudyList(
this.subjectVisitId,
this.studyId,
false,
this.$route.query.visitTaskId
)
.then((res) => {
this.studyList = res.Result
this.loading = false
const studyIndex = this.studyList.findIndex(item => {
const studyIndex = this.studyList.findIndex((item) => {
return item.NoneDicomStudyFileList.length > 0
})
if (studyIndex > -1) {
var fileObj = this.studyList[studyIndex]['NoneDicomStudyFileList']
this.selected(fileObj[0], studyIndex, 0, true)
}
}).catch(() => { this.loading = false })
})
.catch(() => {
this.loading = false
})
},
selected(file, studyIndex, fileIndex, isChangeSub = false) {
this.currentFileId = file.Id
this.currentStudyIndex = studyIndex
this.previewImage.imgList = this.studyList[studyIndex].NoneDicomStudyFileList
this.previewImage.imgList =
this.studyList[studyIndex].NoneDicomStudyFileList
this.currentStudyFileIndex = fileIndex
this.previewImage.index = fileIndex
this.previewImage.studyCode = this.studyList[studyIndex].CodeView
@ -151,11 +180,17 @@ export default {
selectedImg(fileIndex) {
if (this.studyList.length > 0) {
this.currentStudyFileIndex = fileIndex
this.currentFileId = this.studyList[this.currentStudyIndex].NoneDicomStudyFileList[fileIndex].Id
this.currentFileId =
this.studyList[this.currentStudyIndex].NoneDicomStudyFileList[
fileIndex
].Id
this.previewImage.index = fileIndex
this.previewImage.studyCode = this.studyList[this.currentStudyIndex].CodeView
this.previewImage.bodyPart = this.studyList[this.currentStudyIndex].BodyPart
this.previewImage.modality = this.studyList[this.currentStudyIndex].Modality
this.previewImage.studyCode =
this.studyList[this.currentStudyIndex].CodeView
this.previewImage.bodyPart =
this.studyList[this.currentStudyIndex].BodyPart
this.previewImage.modality =
this.studyList[this.currentStudyIndex].Modality
this.$nextTick(() => {
const target = document.getElementById(`img${this.currentFileId}`)
const parent = document.getElementsByClassName('left-content')[0]
@ -165,14 +200,13 @@ export default {
},
preview() {
this.previewVisible = true
}
}
},
},
}
</script>
<style lang="scss" scoped>
.img-container{
.img-container {
position: relative;
width: 100%;
height: 100%;
@ -186,27 +220,27 @@ export default {
border-radius: 10px;
background: #d0d0d0;
}
/deep/ .el-card__body{
/deep/ .el-card__body {
padding: 0px;
}
}
.study-desc{
.study-desc {
padding: 15px 5px;
line-height: 20px;
background-color: #d5d5d5;
font-weight: 500;
}
.left{
width:220px;
.left {
width: 220px;
height: 100%;
/deep/ .el-card__body{
/deep/ .el-card__body {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
}
.title{
.title {
height: 40px;
line-height: 40px;
border: 1ppx solid;
@ -215,7 +249,7 @@ export default {
background-color: #4e4e4e;
color: #ffffff;
}
.left-content{
.left-content {
flex: 1;
overflow-y: auto;
}
@ -231,21 +265,26 @@ export default {
// overflow-y: auto;
// padding: 0;
// }
.img-box{
.img-box {
// position: relative;
display: inline-block;
box-sizing: border-box;
border-bottom: 2px solid #f3f3f3;
width: 180px;
height: 50px;
line-height: 50px;
cursor: pointer;
// margin-bottom: 5px;
padding-left: 5px;
text-overflow:ellipsis;
}
.img-text {
display: inline-block;
width: 200px;
height: 50px;
line-height: 50px;
overflow: hidden;
text-overflow: ellipsis; /* 用省略号表示溢出的文本 */
white-space: nowrap;
}
.img-box:nth-last-child(1){
.img-box:nth-last-child(1) {
margin-bottom: 0px;
}
.is-boxActive {
@ -256,14 +295,13 @@ export default {
opacity: 0;
}
}
.right{
.right {
flex: 1;
height: 100%;
margin-left: 10px;
/deep/ .el-card__body{
/deep/ .el-card__body {
height: 100%;
width: 100%;
}
}
</style>

View File

@ -1,76 +1,130 @@
<template>
<div style="display: flex;justify-content: center">
<div style="width: 600px;text-align: center;border: 1px solid #e6e6e6;margin-top:40px;padding:10px;">
<div class="trial-myinfo-head" style="font-size: 30px;line-height: 120px;">
<div style="display: flex; justify-content: center">
<div
style="
width: 600px;
text-align: center;
border: 1px solid #e6e6e6;
margin-top: 40px;
padding: 10px;
"
>
<div
class="trial-myinfo-head"
style="font-size: 30px; line-height: 120px"
>
<!-- 首次登录修改密码 -->
{{ $t('recompose:title:init') }}
</div>
<el-form ref="passwordForm" v-loading="loading" label-position="right" :model="password" :rules="passwordFormRules" label-width="120px">
<el-form
ref="passwordForm"
v-loading="loading"
label-position="right"
:model="password"
:rules="passwordFormRules"
:label-width="$i18n.locale === 'en' ? '180px' : '120px'"
>
<!-- 用户名 -->
<el-form-item :label="$t('recompose:form:userName')" prop="NewUserName">
<el-input v-model="password.NewUserName" />
</el-form-item>
<!-- 旧密码 -->
<el-form-item :label="$t('recompose:form:oldPassword')" prop="OldPassWord">
<el-input v-model="password.OldPassWord" type="password" show-password auto-complete="new-password" />
<el-form-item
:label="$t('recompose:form:oldPassword')"
prop="OldPassWord"
>
<el-input
v-model="password.OldPassWord"
type="password"
show-password
auto-complete="new-password"
/>
</el-form-item>
<!-- 新密码 -->
<el-form-item :label="$t('recompose:form:newPassword')" prop="NewPassWord">
<el-input v-model="password.NewPassWord" type="password" show-password auto-complete="new-password" />
<el-form-item
:label="$t('recompose:form:newPassword')"
prop="NewPassWord"
>
<el-input
v-model="password.NewPassWord"
type="password"
show-password
auto-complete="new-password"
/>
</el-form-item>
<!-- 确认密码 -->
<el-form-item :label="$t('recompose:form:confirmPassword')" prop="ConfirmPassWord">
<el-input v-model="password.ConfirmPassWord" type="password" show-password auto-complete="new-password" />
</el-form-item>
<el-form-item style="text-align:right">
<!-- 取消 -->
<el-button
size="small"
@click="cancel"
<el-form-item
:label="$t('recompose:form:confirmPassword')"
prop="ConfirmPassWord"
>
<el-input
v-model="password.ConfirmPassWord"
type="password"
show-password
auto-complete="new-password"
/>
</el-form-item>
<el-form-item style="text-align: right">
<!-- 取消 -->
<el-button size="small" @click="cancel">
{{ $t('recompose:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button
type="primary"
size="small"
@click="save"
>
<el-button type="primary" size="small" @click="save">
{{ $t('recompose:button:save') }}
</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { modifyPassword } from '@/api/admin.js'
import md5 from 'js-md5'
import {mapMutations} from "vuex";
import { mapMutations } from 'vuex'
export default {
data() {
return {
password: {
NewUserName: null
NewUserName: null,
},
passwordFormRules: {
NewUserName: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }],
OldPassWord: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }],
NewUserName: [
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
],
OldPassWord: [
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
],
NewPassWord: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
{
required: true,
trigger: 'blur',
validator: this.$validatePassword
validator: this.$validatePassword,
},
],
ConfirmPassWord: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
]
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
],
},
userId: null,
loading: false
loading: false,
}
},
mounted() {
@ -78,7 +132,6 @@ export default {
if (!this.password.NewUserName) {
// 使
this.$alert(this.$t('recompose:message:warning'))
}
},
methods: {
@ -96,7 +149,7 @@ export default {
this.$updateDictionary()
},
save() {
this.$refs.passwordForm.validate(valid => {
this.$refs.passwordForm.validate((valid) => {
if (valid) {
if (this.password.NewPassWord !== this.password.ConfirmPassWord) {
//
@ -106,58 +159,64 @@ export default {
const param = {
NewUserName: this.password.NewUserName,
NewPassWord: md5(this.password.NewPassWord),
OldPassWord: md5(this.password.OldPassWord)
OldPassWord: md5(this.password.OldPassWord),
}
this.loading = true
modifyPassword(param).then(res => {
modifyPassword(param)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
// ,
this.$message.success(this.$t('recompose:message:updatedSuccessfully'))
this.$message.success(
this.$t('recompose:message:updatedSuccessfully')
)
setTimeout(() => {
this.logout()
}, 500)
}
}).catch(() => { this.loading = false })
})
.catch(() => {
this.loading = false
})
}
})
},
cancel() {
this.$refs['passwordForm'].resetFields()
}
}
},
},
}
</script>
<style>
.reset-wrapper {
.reset-wrapper {
padding: 20px;
}
.reset-wrapper .el-page-header {
}
.reset-wrapper .el-page-header {
line-height: 50px;
border: 1px solid #ebeef5;
border-radius: 4px;
background-color: #fff;
}
.reset-wrapper .box-wrapper {
}
.reset-wrapper .box-wrapper {
width: 60%;
margin: 20px auto;
padding: 10px;
color: #303133;
}
}
</style>
<style scoped>
::v-deep .is-error{
::v-deep .is-error {
margin-bottom: 40px;
}
input:-webkit-autofill {
-webkit-text-fill-color: #ededed !important;
box-shadow: 0 0 0px 1000px transparent inset !important;
background-color:transparent;
background-color: transparent;
background-image: none;
transition: background-color 50000s ease-in-out 0s;
}
input {
background-color:transparent;
background-color: transparent;
caret-color: #fff;
}
</style>

View File

@ -38,7 +38,7 @@
<div class="login_content">
<el-form-item
:label="$t('trials:researchForm:form:siteName')"
prop="SiteId"
prop="TrialSiteId"
>
<el-select
v-model="form.TrialSiteId"

View File

@ -34,7 +34,7 @@
<el-input v-model="form.IndicationType" disabled />
</el-form-item>
<!-- 中心名称 -->
<el-form-item :label="$t('trials:researchForm:form:siteName')" prop="SiteId">
<el-form-item :label="$t('trials:researchForm:form:siteName')" prop="TrialSiteId">
<el-select v-model="form.TrialSiteId" filterable style="width:100%;" @change="handleSiteChange">
<el-option
v-for="(item,index) of siteOptions"

View File

@ -147,7 +147,7 @@
>
{{
$fd(
"InternationalizationType",
'InternationalizationType',
scope.row.InternationalizationType
)
}}
@ -158,7 +158,7 @@
>
{{
$fd(
"InternationalizationType",
'InternationalizationType',
scope.row.InternationalizationType
)
}}
@ -166,7 +166,7 @@
<el-tag v-else>
{{
$fd(
"InternationalizationType",
'InternationalizationType',
scope.row.InternationalizationType
)
}}
@ -176,7 +176,7 @@
<el-table-column
label="功能模块/服务名"
prop="Module"
min-width="120"
min-width="140"
show-overflow-tooltip
sortable="custom"
/>
@ -210,13 +210,13 @@
>
<template slot-scope="scope">
<el-tag v-if="scope.row.State === 0" type="danger">
{{ $fd("InternationalizationKeyState", scope.row.State) }}
{{ $fd('InternationalizationKeyState', scope.row.State) }}
</el-tag>
<el-tag v-else-if="scope.row.State === 1" type="primary">
{{ $fd("InternationalizationKeyState", scope.row.State) }}
{{ $fd('InternationalizationKeyState', scope.row.State) }}
</el-tag>
<el-tag v-else>
{{ $fd("InternationalizationKeyState", scope.row.State) }}
{{ $fd('InternationalizationKeyState', scope.row.State) }}
</el-tag>
</template>
</el-table-column>
@ -241,7 +241,13 @@
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
label="创建时间"
prop="CreateTime"
width="150"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
label="操作"
fixed="right"
@ -289,11 +295,11 @@ import {
deleteInternationalization,
getPublishVersionSelect,
batchUpdateInternationalInfo,
} from "@/api/admin";
import Pagination from "@/components/Pagination";
import I18nForm from "./components/I18nForm.vue";
import BatchAddForm from "./components/BatchAddForm.vue";
import moment from "moment";
} from '@/api/admin'
import Pagination from '@/components/Pagination'
import I18nForm from './components/I18nForm.vue'
import BatchAddForm from './components/BatchAddForm.vue'
import moment from 'moment'
const searchDataDefault = () => {
return {
Description: null,
@ -304,13 +310,13 @@ const searchDataDefault = () => {
Module: null,
PublishLogId: null,
Asc: true,
SortField: "",
SortField: '',
PageIndex: 1,
PageSize: 20,
};
};
}
}
export default {
name: "I18n",
name: 'I18n',
components: { Pagination, I18nForm, BatchAddForm },
data() {
return {
@ -321,140 +327,140 @@ export default {
loading: false,
PublishVersionList: [],
selectTableList: [],
};
}
},
mounted() {
this.getList();
this.getPublishVersionSelect();
this.getList()
this.getPublishVersionSelect()
},
methods: {
//
async getPublishVersionSelect() {
try {
let res = await getPublishVersionSelect();
let res = await getPublishVersionSelect()
if (res.IsSuccess) {
this.PublishVersionList = res.Result;
this.PublishVersionList = res.Result
}
} catch (err) {
console.log(err);
console.log(err)
}
},
getList() {
this.loading = true;
this.loading = true
getInternationalizationList(this.searchData)
.then((res) => {
this.loading = false;
this.list = res.Result.CurrentPageData;
this.total = res.Result.TotalCount;
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
})
.catch(() => {
this.loading = false;
});
this.loading = false
})
},
//
handleBatchUpdate() {
this.$nextTick(() => {
this.$refs["i18nForm"].openDialog(
"批量更新",
this.$refs['i18nForm'].openDialog(
'批量更新',
{},
this.PublishVersionList,
"batch"
);
});
'batch'
)
})
},
async batch(row) {
let { PublishLogId, State } = row;
let { PublishLogId, State } = row
let data = {
PublishLogId,
State,
IdList: this.selectTableList.map((item) => item.Id),
};
}
try {
let res = await batchUpdateInternationalInfo(data);
let res = await batchUpdateInternationalInfo(data)
if (res.IsSuccess) {
this.$message.success(this.$t("common:message:updatedSuccessfully"));
this.getList();
this.$message.success(this.$t('common:message:updatedSuccessfully'))
this.getList()
this.$nextTick(() => {
this.$refs["i18nForm"].handleCancle();
});
this.$refs['i18nForm'].handleCancle()
})
}
} catch (err) {
console.log(err);
console.log(err)
}
},
handleAdd() {
this.$nextTick(() => {
this.$refs["i18nForm"].openDialog(
"新增",
this.$refs['i18nForm'].openDialog(
'新增',
{},
this.PublishVersionList,
"add"
);
});
'add'
)
})
},
handleBatchAdd() {
this.$nextTick(() => {
this.$refs["batcnAddForm"].openDialog(
"批量新增",
this.$refs['batcnAddForm'].openDialog(
'批量新增',
{},
this.PublishVersionList
);
});
)
})
},
handleEdit(row) {
this.$nextTick(() => {
this.$refs["i18nForm"].openDialog(
"编辑",
this.$refs['i18nForm'].openDialog(
'编辑',
row,
this.PublishVersionList,
"update"
);
});
'update'
)
})
},
//
handleReset() {
this.searchData = searchDataDefault();
this.getList();
this.searchData = searchDataDefault()
this.getList()
},
//
handleDelete(row) {
this.$confirm("是否确认删除?", {
type: "warning",
this.$confirm('是否确认删除?', {
type: 'warning',
distinguishCancelAndClose: true,
}).then(() => {
this.loading = true;
this.loading = true
deleteInternationalization(row.Id)
.then((res) => {
this.loading = false;
this.loading = false
if (res.IsSuccess) {
this.getList();
this.getList()
this.$message.success(
this.$t("common:message:deletedSuccessfully")
);
this.$t('common:message:deletedSuccessfully')
)
}
})
.catch(() => {
this.loading = false;
});
});
this.loading = false
})
})
},
//
handleSortByColumn(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
}
this.searchData.SortField = column.prop;
this.searchData.PageIndex = 1;
this.getList();
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
},
//
handleSelectionChange(val) {
this.selectTableList = val;
this.selectTableList = val
},
},
};
}
</script>
<style lang="scss">
.log {

View File

@ -9,10 +9,16 @@
label-width="120px"
size="small"
>
<el-form-item label="发布版本" prop="Version">
<el-form-item :label="$t('system:log:form:version')" prop="Version">
<el-input v-model="form.Version" />
</el-form-item>
<el-form-item label="更新功能" prop="UpdateContent">
<el-form-item :label="$t('system:log:form:versionUS')" prop="Version">
<el-input v-model="form.Version_US" />
</el-form-item>
<el-form-item
:label="$t('system:log:form:updateContent')"
prop="UpdateContent"
>
<el-input
v-model="form.UpdateContent"
type="textarea"
@ -20,8 +26,8 @@
:autosize="{ minRows: 4, maxRows: 5 }"
/>
</el-form-item>
<el-form-item label="发布状态" prop="State">
<el-select v-model="form.State" placeholder="请选择">
<el-form-item :label="$t('system:log:form:State')" prop="State">
<el-select v-model="form.State" placeholder="">
<el-option
v-for="item in $d.PublishStatus"
:key="item.id"
@ -31,15 +37,18 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="发布日期">
<el-form-item :label="$t('system:log:form:PublishTime')">
<el-date-picker
v-model="form.PublishTime"
type="date"
value-format="yyyy-MM-dd HH:mm:ss"
/>
</el-form-item>
<el-form-item label="是否当前版本" prop="IsCurrentVersion">
<el-select v-model="form.IsCurrentVersion" placeholder="请选择">
<el-form-item
:label="$t('system:log:form:IsCurrentVersion')"
prop="IsCurrentVersion"
>
<el-select v-model="form.IsCurrentVersion" placeholder="">
<el-option
v-for="item in $d.YesOrNo"
:key="item.id"
@ -52,30 +61,31 @@
</el-form>
</template>
<template slot="dialog-footer">
<el-button size="small" type="primary" @click="handleCancle"
>取消</el-button
>
<el-button size="small" type="primary" @click="handleSave"
>保存</el-button
>
<el-button size="small" type="primary" @click="handleCancle">
{{ $t('common:button:cancel') }}
</el-button>
<el-button size="small" type="primary" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</template>
</base-model>
</template>
<script>
import { addOrUpdatePublishLog } from "@/api/admin";
import BaseModel from "@/components/BaseModel";
import { addOrUpdatePublishLog } from '@/api/admin'
import BaseModel from '@/components/BaseModel'
const formDataDefault = () => {
return {
Id: null,
Version: null,
Version_US: null,
PublishTime: null,
UpdateContent: null,
State: null,
IsCurrentVersion: null,
};
};
}
}
export default {
name: "LogForm",
name: 'LogForm',
components: { BaseModel },
data() {
return {
@ -83,69 +93,87 @@ export default {
form: formDataDefault(),
rules: {
Version: [
{ required: true, message: "请注明", trigger: "blur" },
{ max: 50, message: `${this.$t("common:ruleMessage:maxLength")} 50` },
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
{ max: 50, message: `${this.$t('common:ruleMessage:maxLength')} 50` },
],
// PublishTime: [
// { required: true, message: '', trigger: 'blur' }
// ],
IsCurrentVersion: [
{ required: true, message: "请选择", trigger: "blur" },
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: 'blur',
},
],
State: [
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: 'blur',
},
],
State: [{ required: true, message: "请选择", trigger: "blur" }],
UpdateContent: [
{ required: true, message: "请注明", trigger: "blur" },
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
{
max: 1000,
message: `${this.$t("common:ruleMessage:maxLength")} 500`,
message: `${this.$t('common:ruleMessage:maxLength')} 500`,
},
],
},
model_cfg: {
visible: false,
showClose: true,
width: "500px",
title: "",
width: '500px',
title: '',
appendToBody: true,
},
};
}
},
mounted() {},
methods: {
openDialog(title, data) {
this.model_cfg.visible = true;
this.model_cfg.title = title;
this.model_cfg.visible = true
this.model_cfg.title = title
if (Object.keys(data).length > 0) {
for (const k in this.form) {
if (data.hasOwnProperty(k)) {
this.form[k] = data[k];
this.form[k] = data[k]
}
}
} else {
this.form = formDataDefault();
this.form = formDataDefault()
}
},
handleSave() {
this.$refs.LogForm.validate((valid) => {
if (!valid) return;
this.loading = true;
if (!valid) return
this.loading = true
addOrUpdatePublishLog(this.form)
.then((res) => {
this.loading = false;
this.$message.success(this.$t("common:message:savedSuccessfully"));
this.model_cfg.visible = false;
this.$emit("getList");
this.loading = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.model_cfg.visible = false
this.$emit('getList')
})
.catch(() => {
this.loading = false;
});
});
this.loading = false
})
})
},
handleCancle() {
this.model_cfg.visible = false;
this.model_cfg.visible = false
},
},
};
}
</script>
<style lang="scss" scoped>
::v-deep .el-select,

View File

@ -2,9 +2,12 @@
<div class="log">
<div ref="leftContainer" class="left">
<el-form :inline="true">
<el-form-item label="发布版本" prop="Version">
<el-form-item :label="$t('system:log:table:version')" prop="Version">
<el-input v-model="searchData.Version" size="small" clearable />
</el-form-item>
<el-form-item :label="$t('system:log:table:versionUS')" prop="Version">
<el-input v-model="searchData.Version_US" size="small" clearable />
</el-form-item>
<el-form-item label="更新功能" prop="UpdateContent">
<el-input v-model="searchData.UpdateContent" size="small" clearable />
</el-form-item>
@ -38,30 +41,37 @@
>
<el-table-column type="index" width="50" />
<el-table-column
label="发布版本"
:label="$t('system:log:table:version')"
prop="Version"
min-width="90"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
label="更新功能"
:label="$t('system:log:table:versionUS')"
prop="Version_US"
min-width="90"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
:label="$t('system:log:table:UpdateContent')"
prop="UpdateContent"
min-width="150"
show-overflow-tooltip
/>
<el-table-column
label="发布状态"
:label="$t('system:log:table:State')"
prop="State"
min-width="150"
show-overflow-tooltip
>
<template slot-scope="scope">
<span>{{ $fd("PublishStatus", scope.row.State) }}</span>
<span>{{ $fd('PublishStatus', scope.row.State) }}</span>
</template>
</el-table-column>
<el-table-column
label="发布日期"
:label="$t('system:log:table:PublishTime')"
prop="PublishTime"
min-width="90"
show-overflow-tooltip
@ -70,23 +80,23 @@
<template slot-scope="scope">
{{
scope.row.PublishTime
? moment(scope.row.PublishTime).format("YYYY-MM-DD")
: ""
? moment(scope.row.PublishTime).format('YYYY-MM-DD')
: ''
}}
</template>
</el-table-column>
<el-table-column
label="是否当前版本"
:label="$t('system:log:table:IsCurrentVersion')"
prop="IsCurrentVersion"
min-width="150"
show-overflow-tooltip
>
<template slot-scope="scope">
<span>{{ $fd("YesOrNo", scope.row.IsCurrentVersion) }}</span>
<span>{{ $fd('YesOrNo', scope.row.IsCurrentVersion) }}</span>
</template>
</el-table-column>
<el-table-column
label="操作"
:label="$t('common:action:action')"
fixed="right"
prop=""
min-width="200"
@ -99,7 +109,7 @@
icon="el-icon-edit-outline"
@click="handleEdit(scope.row)"
>
编辑
{{ $t('common:button:edit') }}
</el-button>
<el-button
size="mini"
@ -107,7 +117,7 @@
icon="el-icon-delete"
@click="handleDelete(scope.row)"
>
删除
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
@ -125,20 +135,21 @@
</div>
</template>
<script>
import { getPublishLogList, deletePublishLog } from "@/api/admin";
import Pagination from "@/components/Pagination";
import LogForm from "./components/LogForm.vue";
import moment from "moment";
import { getPublishLogList, deletePublishLog } from '@/api/admin'
import Pagination from '@/components/Pagination'
import LogForm from './components/LogForm.vue'
import moment from 'moment'
const searchDataDefault = () => {
return {
Version: null,
Version_US: null,
UpdateContent: null,
Asc: false,
SortField: "PublishTime",
SortField: 'PublishTime',
PageIndex: 1,
PageSize: 20,
};
};
}
}
export default {
components: { Pagination, LogForm },
data() {
@ -148,74 +159,74 @@ export default {
list: [],
total: 0,
loading: false,
};
}
},
mounted() {
this.getList();
this.getList()
},
methods: {
getList() {
this.loading = true;
this.loading = true
getPublishLogList(this.searchData)
.then((res) => {
this.loading = false;
this.list = res.Result.CurrentPageData;
this.total = res.Result.TotalCount;
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
})
.catch(() => {
this.loading = false;
});
this.loading = false
})
},
handleAdd() {
this.$nextTick(() => {
this.$refs["LogForm"].openDialog("新增", {});
});
this.$refs['LogForm'].openDialog('新增', {})
})
},
handleEdit(row) {
this.$nextTick(() => {
this.$refs["LogForm"].openDialog("编辑", row);
});
this.$refs['LogForm'].openDialog('编辑', row)
})
},
//
handleReset() {
this.searchData = searchDataDefault();
this.getList();
this.searchData = searchDataDefault()
this.getList()
},
//
handleDelete(row) {
this.$confirm("是否确认删除?", {
type: "warning",
this.$confirm('是否确认删除?', {
type: 'warning',
distinguishCancelAndClose: true,
}).then(() => {
this.loading = true;
this.loading = true
deletePublishLog(row.Id)
.then((res) => {
this.loading = false;
this.loading = false
if (res.IsSuccess) {
this.getList();
this.getList()
this.$message.success(
this.$t("common:message:deletedSuccessfully")
);
this.$t('common:message:deletedSuccessfully')
)
}
})
.catch(() => {
this.loading = false;
});
});
this.loading = false
})
})
},
//
handleSortByColumn(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
}
this.searchData.SortField = column.prop;
this.searchData.PageIndex = 1;
this.getList();
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
},
},
};
}
</script>
<style lang="scss">
.log {

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)"
@ -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>

View File

@ -3,14 +3,19 @@
<div class="trial-myinfo-right-box">
<div class="trial-myinfo-head">
<!-- 账号信息 -->
{{ $t("trials:trials-myinfo:title:accountInfo") }}
{{ $t('trials:trials-myinfo:title:accountInfo') }}
</div>
<el-form label-position="right" label-width="100px">
<el-form
label-position="right"
label-width="100px"
:rules="rule"
:model="userForm"
ref="userFormRef"
>
<!-- 用户名 -->
<el-form-item
:label="$t('trials:trials-myinfo:form:userName')"
style="margin-bottom: 5px"
prop="UserName"
>
<span>{{ user.UserName }}</span>
</el-form-item>
@ -27,18 +32,18 @@
size="small"
@click="setNewUserName"
>
{{ $t("trials:trials-myinfo:button:update") }}
{{ $t('trials:trials-myinfo:button:update') }}
</el-button>
</el-form-item>
<!-- 电话 -->
<el-form-item
:label="$t('trials:trials-myinfo:form:phone')"
style="margin-bottom: 5px"
prop="UserName"
prop="Phone"
>
<span>{{ user.Phone }}</span>
</el-form-item>
<el-form-item label="" style="position: relative" prop="UserName">
<el-form-item label="" style="position: relative" prop="Phone">
<el-input
v-model="userForm.Phone"
:placeholder="$t('trials:trials-myinfo:form:phone')"
@ -51,14 +56,14 @@
size="small"
@click="setNewPhone"
>
{{ $t("trials:trials-myinfo:button:update") }}
{{ $t('trials:trials-myinfo:button:update') }}
</el-button>
</el-form-item>
<!-- 邮箱 -->
<el-form-item
:label="$t('trials:trials-myinfo:form:email')"
style="margin-bottom: 5px"
prop="UserName"
prop="EMail"
>
<span>{{ user.EMail }}</span>
</el-form-item>
@ -100,7 +105,7 @@
size="small"
@click="setNewEmail"
>
{{ $t("trials:trials-myinfo:button:update") }}
{{ $t('trials:trials-myinfo:button:update') }}
</el-button>
</el-form-item>
</el-form>
@ -114,19 +119,19 @@ import {
setNewEmail,
setNewPhone,
setNewUserName,
} from "@/api/system/user.js";
import { removeToken } from "@/utils/auth";
import password from "./password.vue";
var timer = "";
var countdown = 60;
} from '@/api/system/user.js'
import { removeToken } from '@/utils/auth'
import password from './password.vue'
var timer = ''
var countdown = 60
export default {
name: "account",
name: 'account',
components: { password },
props: {
user: {
required: true,
default: () => {
return {};
return {}
},
},
IsCanConnectInternet: {
@ -138,108 +143,138 @@ export default {
return {
userForm: {},
sendDisabled: true,
sendTitle: this.$t("trials:trials-myinfo:button:getVCode"),
};
sendTitle: this.$t('trials:trials-myinfo:button:getVCode'),
rule: {
UserName: [
{
validator: (rule, value, callback) => {
if (!value) return callback()
var lang = zzSessionStorage.getItem('lang')
? zzSessionStorage.getItem('lang')
: 'zh'
/* eslint-disable */
var reg1 = /^[a-zA-Z0-9_]{4,16}$/ //8
if (!reg1.test(value)) {
callback(
lang === 'zh'
? new Error(
'1用户名字符长度最小为4个字符最大为16个字符只可使用字母、数字、下划线'
)
: new Error(
'the username must have:1) At least 4 characters;2) At most 16 characters;3)Only letters, numbers, and underscores are allowed.'
)
)
} else {
callback()
}
},
trigger: 'blur',
},
],
},
}
},
methods: {
setNewEmail() {
setNewEmail(this.userForm.EMail, this.userForm.VerificationCode).then(
() => {
this.userForm.EMail = "";
this.userForm.VerificationCode = "";
this.userForm.EMail = ''
this.userForm.VerificationCode = ''
this.$message.success(
this.$t("trials:trials-myinfo:message:updateSuccessfully")
);
this.$emit("getUserInfo");
this.$t('trials:trials-myinfo:message:updateSuccessfully')
)
this.$emit('getUserInfo')
}
);
)
},
async setNewUserName() {
try {
let validate = await this.$refs.userFormRef.validate()
if (!validate) return false
let confirm = await this.$confirm(
this.$t("trials:trials-myInfo:confirmMessage:updateUserName"),
this.$t('trials:trials-myInfo:confirmMessage:updateUserName'),
{
type: "warning",
type: 'warning',
distinguishCancelAndClose: true,
confirmButtonText: this.$t("common:button:confirm"),
cancelButtonText: this.$t("common:button:cancel"),
confirmButtonText: this.$t('common:button:confirm'),
cancelButtonText: this.$t('common:button:cancel'),
}
);
if (confirm !== "confirm") return;
let res = await setNewUserName(this.userForm.UserName);
)
if (confirm !== 'confirm') return
let res = await setNewUserName(this.userForm.UserName)
if (res.IsSuccess) {
this.userForm.UserName = "";
this.userForm.UserName = ''
this.$message.success(
this.$t("trials:trials-myinfo:message:updateSuccessfully")
);
this.$emit("getUserInfo");
removeToken();
this.logout();
this.$t('trials:trials-myinfo:message:updateSuccessfully')
)
this.$emit('getUserInfo')
removeToken()
this.logout()
}
} catch (err) {
console.log(err);
console.log(err)
}
},
setNewPhone() {
setNewPhone(this.userForm.Phone).then(() => {
this.userForm.Phone = "";
this.userForm.Phone = ''
this.$message.success(
this.$t("trials:trials-myinfo:message:updateSuccessfully")
);
this.$emit("getUserInfo");
});
this.$t('trials:trials-myinfo:message:updateSuccessfully')
)
this.$emit('getUserInfo')
})
},
handleEmailChange() {
var reg =
/^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/;
/^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/
if (this.userForm.EMail && reg.test(this.userForm.EMail)) {
this.sendDisabled = false;
this.sendDisabled = false
} else {
this.sendDisabled = true;
this.sendDisabled = true
}
},
sendVerificationCode() {
sendVerificationCode(this.userForm.EMail).then(() => {
this.settime(this);
this.settime(this)
//
this.$message.success(
this.$t("trials:trials-myinfo:message:sendSuccessfully")
);
});
this.$t('trials:trials-myinfo:message:sendSuccessfully')
)
})
},
settime(obj) {
if (countdown === 0) {
obj.sendDisabled = false;
obj.sendTitle = this.$t("trials:trials-myinfo:button:getVCode"); // ''
countdown = 60;
clearTimeout(timer);
return;
obj.sendDisabled = false
obj.sendTitle = this.$t('trials:trials-myinfo:button:getVCode') // ''
countdown = 60
clearTimeout(timer)
return
} else {
obj.sendDisabled = true;
obj.sendDisabled = true
obj.sendTitle = `${this.$t(
"trials:trials-myinfo:button:wait"
)}(${countdown}s)`;
countdown--;
'trials:trials-myinfo:button:wait'
)}(${countdown}s)`
countdown--
// eslint-disable-next-line no-self-assign
countdown = countdown;
countdown = countdown
timer = setTimeout(function () {
obj.settime(obj);
}, 1000);
obj.settime(obj)
}, 1000)
}
},
async logout() {
/* eslint-disable */
var loginType = zzSessionStorage.getItem("loginType");
await this.$store.dispatch("user/logout");
var loginType = zzSessionStorage.getItem('loginType')
await this.$store.dispatch('user/logout')
if (loginType) {
this.$router.push(`/login?loginType=${loginType}`);
this.$router.push(`/login?loginType=${loginType}`)
} else {
this.$router.push(`/login`);
this.$router.push(`/login`)
}
this.$i18n.locale = "zh";
this.setLanguage("zh");
this.$updateDictionary();
this.$i18n.locale = 'zh'
this.setLanguage('zh')
this.$updateDictionary()
},
},
};
}
</script>

View File

@ -2,7 +2,7 @@
<div class="trial-myinfo-right-box">
<div class="trial-myinfo-head">
<!-- 修改密码 -->
{{ $t("trials:trials-myinfo:title:updatePaasord") }}
{{ $t('trials:trials-myinfo:title:updatePaasord') }}
</div>
<el-form
ref="passwordForm"
@ -52,17 +52,17 @@
</el-form-item>
</el-form>
<el-button type="primary" size="small" class="trial-info-btn" @click="save">
{{ $t("trials:trials-myinfo:button:save") }}
{{ $t('trials:trials-myinfo:button:save') }}
</el-button>
</div>
</template>
<script>
import md5 from "js-md5";
import { mapGetters, mapMutations } from "vuex";
import { modifyPassword } from "@/api/admin.js";
import { removeToken } from "@/utils/auth";
import md5 from 'js-md5'
import { mapGetters, mapMutations } from 'vuex'
import { modifyPassword } from '@/api/admin.js'
import { removeToken } from '@/utils/auth'
export default {
name: "password",
name: 'password',
data() {
return {
password: {},
@ -70,91 +70,96 @@ export default {
OldPassWord: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: "blur",
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
],
NewPassWord: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: "blur",
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
{
required: true,
trigger: "blur",
trigger: 'blur',
validator: this.$validatePassword,
},
],
ConfirmPassWord: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: "blur",
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
{
required: true,
trigger: "blur",
trigger: 'blur',
validator: this.$validatePassword,
},
],
},
};
}
},
computed: {
...mapGetters(["userId"]),
...mapGetters(['userId']),
},
methods: {
...mapMutations({ setLanguage: "lang/setLanguage" }),
...mapMutations({ setLanguage: 'lang/setLanguage' }),
async save() {
try {
let validate = await this.$refs.passwordForm.validate();
if (!validate) return;
let validate = await this.$refs.passwordForm.validate()
if (!validate) return
if (this.password.NewPassWord !== this.password.ConfirmPassWord) {
this.$alert(this.$t("passwordReset:formRule:passwordsDiffer"));
return;
this.$alert(this.$t('passwordReset:formRule:passwordsDiffer'))
return
}
let confirm = await this.$confirm(
this.$t("trials:trials-myInfo:confirmMessage:updatePassWord"),
this.$t('trials:trials-myInfo:confirmMessage:updatePassWord'),
{
type: "warning",
type: 'warning',
distinguishCancelAndClose: true,
confirmButtonText: this.$t("common:button:confirm"),
cancelButtonText: this.$t("common:button:cancel"),
confirmButtonText: this.$t('common:button:confirm'),
cancelButtonText: this.$t('common:button:cancel'),
}
);
if (confirm !== "confirm") return;
)
if (confirm !== 'confirm') return
const param = {
UserId: this.userId,
NewPassWord: md5(this.password.NewPassWord),
OldPassWord: md5(this.password.OldPassWord),
};
let res = await modifyPassword(param);
}
let res = await modifyPassword(param)
if (res.IsSuccess) {
// ,
this.$message.success(
this.$t("trials:trials-myinfo:message:modifyPWSuccessfully")
);
removeToken();
this.logout();
this.$t('trials:trials-myinfo:message:modifyPWSuccessfully')
)
removeToken()
this.logout()
}
} catch (err) {
console.log(err);
console.log(err)
}
},
async logout() {
/* eslint-disable */
var loginType = zzSessionStorage.getItem("loginType");
await this.$store.dispatch("user/logout");
var loginType = zzSessionStorage.getItem('loginType')
await this.$store.dispatch('user/logout')
if (loginType) {
this.$router.push(`/login?loginType=${loginType}`);
this.$router.push(`/login?loginType=${loginType}`)
} else {
this.$router.push(`/login`);
this.$router.push(`/login`)
}
this.$i18n.locale = "zh";
this.setLanguage("zh");
this.$updateDictionary();
this.$i18n.locale = 'zh'
this.setLanguage('zh')
this.$updateDictionary()
},
},
};
}
</script>
<style lang="scss" scoped>
.el-form-item {
margin-bottom: 30px;
}
</style>

View File

@ -5,7 +5,7 @@
<el-form :inline="true">
<!-- 文件类型 -->
<el-form-item :label="$t('trials:signRecords:table:fileType')">
<el-select v-model="searchData.FileTypeId" style="width: 150px">
<el-select v-model="searchData.FileTypeId" clearable style="width: 150px">
<el-option
v-for="item of typeOptions"
:key="item.FileTypeId"

View File

@ -1,19 +1,22 @@
<template>
<div v-loading="loading" class="sign-form-wrapper">
<div class="sign-form-body">
<h4 v-if="signText" style="color:red">*{{ signText }}</h4>
<h4 v-if="signText" style="color: red">*{{ signText }}</h4>
<el-form
ref="signForm"
:model="signForm"
size="small"
label-width="100px"
>
<el-form-item
:label="$t('common:form:sign:userName')"
prop="UserName"
:rules="[
{ required: true, message: $t('common:ruleMessage:specify'), trigger: 'blur' }
{
required: true,
message: $t('common:ruleMessage:specify'),
trigger: 'blur',
},
]"
>
<el-input v-model="signForm.UserName" />
@ -22,18 +25,36 @@
:label="$t('common:form:sign:password')"
prop="Password"
:rules="[
{ required: true, message: $t('common:ruleMessage:specify'), trigger: 'blur' }
{
required: true,
message: $t('common:ruleMessage:specify'),
trigger: 'blur',
},
]"
>
<el-input v-model="signForm.Password" show-password auto-complete="new-password" />
<el-input
v-model="signForm.Password"
show-password
auto-complete="new-password"
/>
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer sign-form-footer">
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleclose">
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="handleclose"
>
{{ $t('common:button:cancel') }}
</el-button>
<el-button :loading="btnLoading" size="small" type="primary" @click="userConfirm">
<el-button
:loading="btnLoading"
size="small"
type="primary"
@click="userConfirm"
>
{{ $t('common:button:sign') }}
</el-button>
</div>
@ -49,35 +70,36 @@ export default {
props: {
fileName: {
type: String,
required: true
required: true,
},
isSystemDoc: {
type: Boolean,
required: true
required: true,
},
documentId: {
type: String,
required: true
required: true,
},
trialId: {
type: String,
default: ''
}
default: '',
},
},
data() {
return {
signForm: {
UserName: '',
Password: ''
Password: '',
},
signText: '',
btnLoading: false,
loading: false
loading: false,
}
},
mounted() {
this.loading = true
getSystemBasicData(attachmentSignCode).then(res => {
getSystemBasicData(attachmentSignCode)
.then((res) => {
var text
if (this.$i18n.locale === 'zh') {
text = res.Result.ValueCN
@ -86,11 +108,16 @@ export default {
}
this.signText = text.replace('xxx', this.fileName)
this.loading = false
}).catch(() => { this.loading = false })
})
.catch(() => {
this.loading = false
})
},
methods: {
userConfirm() {
const currentUser = zzSessionStorage.getItem('userName').toLocaleLowerCase()
const currentUser = zzSessionStorage
.getItem('userName')
.toLocaleLowerCase()
this.$refs.signForm.validate((valid) => {
console.log(valid)
if (!valid) return
@ -102,44 +129,52 @@ export default {
this.btnLoading = true
var params = {
data: {
trialId: this.trialId,
// trialId: this.trialId,
documentId: this.documentId,
isSystemDoc: this.isSystemDoc
isSystemDoc: this.isSystemDoc,
},
signInfo: {
UserName: this.signForm.UserName,
PassWord: md5(this.signForm.Password),
TrialId: this.trialId,
// c: this.trialId,
SignCode: '',
SignText: this.signText,
SignCodeId: '',
SubjectVisitId: ''
SubjectVisitId: '',
},
}
if (!this.isSystemDoc) {
params.data.TrialId = this.trialId
params.signInfo.TrialId = this.trialId
}
userConfirm(params).then(res => {
userConfirm(params)
.then((res) => {
if (res.IsSuccess) {
//
this.$message.success(this.$t('common:message:signSuccessfully'))
this.$emit('closeDialog', true)
}
}).catch(() => { this.btnLoading = false })
})
.catch(() => {
this.btnLoading = false
})
})
},
handleclose() {
this.$emit('closeDialog', false)
}
}
},
},
}
</script>
<style lang="scss" scoped>
.sign-form-wrapper{
.sign-form-body{
padding:10px;
.sign-form-wrapper {
.sign-form-body {
padding: 10px;
border: 1px solid #e0e0e0;
max-height:650px;
max-height: 650px;
overflow-y: auto;
}
.sign-form-footer{
.sign-form-footer {
margin-top: 10px;
text-align: right;
}

View File

@ -7,6 +7,7 @@
<el-form-item :label="$t('trials:self-attachment:table:fileType')">
<el-select
v-model="searchData.FileTypeId"
clearable
style="width:150px;"
>
<el-option

View File

@ -15,7 +15,7 @@
<el-table
ref="myTable"
v-loading="listLoading"
v-adaptive="{bottomOffset:55}"
v-adaptive="{ bottomOffset: 55 }"
:data="list"
stripe
height="100"
@ -32,7 +32,15 @@
v-if="!hasPermi(['role:ea'])"
>
<template slot-scope="scope">
<el-button type="text" @click="go(`/trialsResume?doctorId=${scope.row.DoctorId}&token=${token}`)">{{ scope.row.LastName }} / {{ scope.row.FirstName }}</el-button>
<el-button
type="text"
@click="
go(
`/trialsResume?doctorId=${scope.row.DoctorId}&token=${token}`
)
"
>{{ scope.row.LastName }} / {{ scope.row.FirstName }}</el-button
>
</template>
</el-table-column>
<!-- Name CN -->
@ -48,7 +56,7 @@
<el-table-column
prop="UserName"
:label="$t('trials:enrolledReviews:table:userName')"
:width="hasPermi(['role:ea'])?300:150"
:width="hasPermi(['role:ea']) ? 300 : 150"
sortable="custom"
show-overflow-tooltip
/>
@ -56,12 +64,16 @@
<el-table-column
prop="EnrollTimeStr"
:label="$t('trials:enrolledReviews:table:enrollmentTime')"
:width="hasPermi(['role:ea'])?320:130"
:width="hasPermi(['role:ea']) ? 320 : 130"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ scope.row.EnrollTime?moment(scope.row.EnrollTime).format('YYYY-MM-DD'):'' }}
{{
scope.row.EnrollTime
? moment(scope.row.EnrollTime).format('YYYY-MM-DD')
: ''
}}
</template>
</el-table-column>
<el-table-column
@ -78,8 +90,16 @@
:inactive-value="false"
:active-text="$fd('IsEnable', true)"
:inactive-text="$fd('IsEnable', false)"
@change="(v) => {return isEnableChange(scope.row, v)}"
:disabled="!hasPermi(['trials:trials-panel:enrolled-reviewers:list:edit'])"
@change="
(v) => {
return isEnableChange(scope.row, v)
}
"
:disabled="
!hasPermi([
'trials:trials-panel:enrolled-reviewers:list:edit',
])
"
/>
<!-- <span :style="{color:scope.row.IsEnable?'#409eff':'#dcdfe6'}"> {{$fd('IsEnable', scope.row.IsEnable)}}</span> -->
</div>
@ -98,16 +118,46 @@
width="300"
>
<template slot-scope="scope">
<span v-for="item of scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}) ? scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).ReadingCategorys : []" :key="item.TrialReadingCriterionId" style="margin:5px 5px 5px 0;">
<el-tag v-if="item === 1" type="primary">{{ $fd('ReadingCategory', item) + ' & ' + $fd('ReadingCategory', 2) }}</el-tag>
<span
v-for="item of scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
})
? scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId ===
ite.TrialReadingCriterionId
)
}).ReadingCategorys
: []"
:key="item.TrialReadingCriterionId"
style="margin: 5px 5px 5px 0"
>
<el-tag v-if="item === 1" type="primary">{{
$fd('ReadingCategory', item) +
' & ' +
$fd('ReadingCategory', 2)
}}</el-tag>
<!-- <el-tag v-if="item === 2" type="info">{{ $fd('ReadingCategory', item) }}</el-tag>-->
<el-tag v-if="item === 4" type="danger">{{ $fd('ReadingCategory', item) }}</el-tag>
<el-tag v-if="item === 5" type="warning">{{ $fd('ReadingCategory', item) }}</el-tag>
<el-tag v-if="item === 4" type="danger">{{
$fd('ReadingCategory', item)
}}</el-tag>
<el-tag v-if="item === 5" type="warning">{{
$fd('ReadingCategory', item)
}}</el-tag>
</span>
<el-button
v-hasPermi="['trials:trials-panel:enrolled-reviewers:list:edit']"
v-hasPermi="[
'trials:trials-panel:enrolled-reviewers:list:edit',
]"
type="text"
@click="openSetEnrollReadingCategory(scope.row, ite.TrialReadingCriterionId)"
@click="
openSetEnrollReadingCategory(
scope.row,
ite.TrialReadingCriterionId
)
"
>
{{ $t('trials:enrolledReviews:button:config') }}
</el-button>
@ -120,13 +170,27 @@
>
<template slot-scope="scope">
<el-switch
v-model="scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).IsJoinAnalysis"
v-model="
scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
}).IsJoinAnalysis
"
:active-value="true"
:inactive-value="false"
:active-text="$fd('YesOrNo', true)"
:inactive-text="$fd('YesOrNo', false)"
@change="(v) => {return isConsistencyChange(scope.row, ite, v, true)}"
:disabled="!hasPermi(['trials:trials-panel:enrolled-reviewers:list:edit'])"
@change="
(v) => {
return isConsistencyChange(scope.row, ite, v, true)
}
"
:disabled="
!hasPermi([
'trials:trials-panel:enrolled-reviewers:list:edit',
])
"
/>
<!-- <span v-if="scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).IsJoinAnalysis"> {{$fd('YesOrNo', scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).IsJoinAnalysis)}}</span>
<span v-else> {{$fd('YesOrNo',false)}}</span> -->
@ -138,8 +202,20 @@
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).StatementCriterionFileList.length > 0">
<el-button type="text" @click="preview(scope.row,ite.TrialReadingCriterionId)">View</el-button>
<div
v-if="
scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
}).StatementCriterionFileList.length > 0
"
>
<el-button
type="text"
@click="preview(scope.row, ite.TrialReadingCriterionId)"
>View</el-button
>
<!-- <el-button type="text" @click="windowOpen(scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).StatementCriterionFileList[0].FilePath)">View</el-button> -->
</div>
<div v-else>
@ -147,10 +223,19 @@
<!-- 上传 -->
<el-button
v-if="ite.CriterionType === 0"
v-hasPermi="['trials:trials-panel:enrolled-reviewers:list:edit']"
v-hasPermi="[
'trials:trials-panel:enrolled-reviewers:list:edit',
]"
type="text"
style="margin-left: 10px"
@click="addCol(0, scope.row, ite, $t('trials:enrolledReviews:message:SOW'))"
@click="
addCol(
0,
scope.row,
ite,
$t('trials:enrolledReviews:message:SOW')
)
"
>
{{ $t('trials:enrolledReviews:button:upload') }}
</el-button>
@ -163,18 +248,40 @@
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).AcknowledgementCriterionFileList.length > 0">
<el-button type="text" @click="windowOpen(scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).AcknowledgementCriterionFileList[0].FilePath)">View</el-button>
<div
v-if="
scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
}).AcknowledgementCriterionFileList.length > 0
"
>
<el-button
type="text"
@click="preview(scope.row, ite.TrialReadingCriterionId)"
>
View
</el-button>
</div>
<div v-else>
<span>{{ $t('trials:enrolledReviews:label:notUpload') }}</span>
<!-- 上传 -->
<el-button
v-if="ite.CriterionType === 0"
v-hasPermi="['trials:trials-panel:enrolled-reviewers:list:edit']"
v-hasPermi="[
'trials:trials-panel:enrolled-reviewers:list:edit',
]"
type="text"
style="margin-left: 10px"
@click="addCol(1, scope.row, ite, $t('trials:enrolledReviews:message:SOW'))"
@click="
addCol(
1,
scope.row,
ite,
$t('trials:enrolledReviews:message:SOW')
)
"
>
{{ $t('trials:enrolledReviews:button:upload') }}
</el-button>
@ -188,30 +295,47 @@
>
<template slot-scope="scope">
<div>
{{ scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).PendingCount }}
{{
scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
}).PendingCount
}}
/
{{ scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).ComplectedCount }}
{{
scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
}).ComplectedCount
}}
/
{{ scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).TotalCount }}
{{
scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
}).TotalCount
}}
</div>
</template>
</el-table-column>
</el-table-column>
</el-table>
</template>
<!-- 上传SOW -->
<!-- <base-model :config="sow_model">-->
<!-- <template slot="dialog-body">-->
<!-- <upload-acksow-->
<!-- ref="uploadAckSow"-->
<!-- :trial-id="listQuery.TrialId"-->
<!-- :doctor-id="doctorId"-->
<!-- @getFileList="getFileList"-->
<!-- />-->
<!-- </template>-->
<!-- </base-model>-->
<!-- <base-model :config="sow_model">-->
<!-- <template slot="dialog-body">-->
<!-- <upload-acksow-->
<!-- ref="uploadAckSow"-->
<!-- :trial-id="listQuery.TrialId"-->
<!-- :doctor-id="doctorId"-->
<!-- @getFileList="getFileList"-->
<!-- />-->
<!-- </template>-->
<!-- </base-model>-->
<!-- 修改 Reading Type -->
<base-model :config="readingType_model">
@ -258,21 +382,38 @@
allow-create
multiple
default-first-option
style="width: 50%;"
style="width: 50%"
>
<template v-for="item of $d.ReadingCategory">
<el-option v-if="item.value === 1" :key="item.id" :value="1" :label="$fd('ReadingCategory', 1) + ' & ' + $fd('ReadingCategory', 2)" />
<el-option
v-if="item.value === 1"
:key="item.id"
:value="1"
:label="
$fd('ReadingCategory', 1) + ' & ' + $fd('ReadingCategory', 2)
"
/>
</template>
<template v-for="item of $d.ReadingCategory">
<el-option v-if="item.value === 4 && IsArbitrationReading" :key="item.id" :value="item.value" :label="$fd('ReadingCategory', 4)" />
<el-option
v-if="item.value === 4 && IsArbitrationReading"
:key="item.id"
:value="item.value"
:label="$fd('ReadingCategory', 4)"
/>
</template>
<template v-for="item of $d.ReadingCategory">
<el-option v-if="item.value === 5 && IsOncologyReading" :key="item.id" :value="item.value" :label="$fd('ReadingCategory', 5)" />
<el-option
v-if="item.value === 5 && IsOncologyReading"
:key="item.id"
:value="item.value"
:label="$fd('ReadingCategory', 5)"
/>
</template>
</el-select>
</template>
<template slot="dialog-footer">
<div style="margin-top:30px;">
<div style="margin-top: 30px">
<el-button
:disabled="readingCategory_model.btnLoading"
type="primary"
@ -298,17 +439,23 @@
:model="updateStatusForm"
:rules="updateStatusFormrules"
>
<el-form-item :label="$t('trials:enrolledReviews:label:enrollmentStatus')" prop="Status">
<el-select
v-model="updateStatusForm.Status"
style="width:60%;"
<el-form-item
:label="$t('trials:enrolledReviews:label:enrollmentStatus')"
prop="Status"
>
<el-select v-model="updateStatusForm.Status" style="width: 60%">
<!-- 0回退1出组 -->
<el-option v-for="item of $d.OutOrInEnrollment" :key="`OutOrInEnrollment${item.value}`" :value="item.value" :label="item.label" :disabled="item.value === 0 && currentWorkload>0" />
<el-option
v-for="item of $d.OutOrInEnrollment"
:key="`OutOrInEnrollment${item.value}`"
:value="item.value"
:label="item.label"
:disabled="item.value === 0 && currentWorkload > 0"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="updateStatusForm.Status== 1"
v-if="updateStatusForm.Status == 1"
:label="$t('trials:enrolledReviews:label:outOfEnrollmentTime')"
prop="OutEnrollmentTime"
>
@ -346,10 +493,16 @@
label-width="120px"
size="small"
>
<el-form-item :label="$t('trials:enrolledReviews:table:criterionName')" prop="CriterionType">
<el-form-item
:label="$t('trials:enrolledReviews:table:criterionName')"
prop="CriterionType"
>
<el-input v-model="form.CriterionName" disabled />
</el-form-item>
<el-form-item :label="$t('trials:enrolledReviews:table:file')" prop="FilePath">
<el-form-item
:label="$t('trials:enrolledReviews:table:file')"
prop="FilePath"
>
<el-upload
class="upload-demo"
action
@ -358,19 +511,35 @@
:on-preview="handlePreview2"
:on-remove="handleRemoveFile2"
:show-file-list="true"
accept=".pdf"
:limit="1"
:file-list="fileList"
>
<el-button size="small" type="primary" :disabled="fileList.length > 0">{{$t('common:button:upload')}}</el-button>
<el-button
size="small"
type="primary"
:disabled="fileList.length > 0"
>{{ $t('common:button:upload') }}</el-button
>
</el-upload>
</el-form-item>
</el-form>
</template>
<template slot="dialog-footer">
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancle">
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="handleCancle"
>
{{ $t('common:button:cancel') }}
</el-button>
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">
<el-button
size="small"
type="primary"
:loading="btnLoading"
@click="handleSave"
>
{{ $t('common:button:save') }}
</el-button>
</template>
@ -384,7 +553,7 @@ import {
updateReviewerReadingType,
enrollBackOrOut,
setEnrollReadingCategory,
batchSetCriterionJoinJoinAnalysis
batchSetCriterionJoinJoinAnalysis,
} from '@/api/trials'
import { addDoctorCriterionFile } from '@/api/reviewers'
import { uploadFile } from '@/api/attachment'
@ -405,7 +574,7 @@ export default {
PageIndex: 1,
PageSize: 100,
Asc: false,
SortField: ''
SortField: '',
},
doctorId: '',
currentRow: {},
@ -415,41 +584,59 @@ export default {
currentClickRowData: '',
btnLoading: false,
readingType: '',
model_cfg: { visible: false, showClose: true, width: '600px', title: '', appendToBody: true },
model_cfg: {
visible: false,
showClose: true,
width: '600px',
title: '',
appendToBody: true,
},
sow_model: {
visible: false,
title: this.$t('trials:enrolledReviews:dialogTitle:signedAckOfSow'),
showClose: true,
width: '500px'
width: '500px',
},
readingType_model: {
visible: false,
title: this.$t('trials:enrolledReviews:dialogTitle:changeReadingType'),
showClose: true,
width: '500px',
btnLoading: false
btnLoading: false,
},
readingCategory_model: {
visible: false,
title: this.$t('trials:enrolledReviews:dialogTitle:changeReadingType'),
showClose: true,
width: '600px',
btnLoading: false
btnLoading: false,
},
reviewerStatus_model: {
visible: false,
title: this.$t('trials:enrolledReviews:dialogTitle:changeStatus'),
showClose: true,
width: '500px',
btnLoading: false
btnLoading: false,
},
updateStatusForm: {
Status: '',
OutEnrollmentTime: ''
OutEnrollmentTime: '',
},
updateStatusFormrules: {
Status: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }],
OutEnrollmentTime: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }]
Status: [
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: ['blur', 'change'],
},
],
OutEnrollmentTime: [
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: ['blur', 'change'],
},
],
},
token: store.getters.token,
ReadingCategorys: [],
@ -460,15 +647,25 @@ export default {
fileList: [],
form: {},
rowData: {},
moment
moment,
}
},
computed: {
currentWorkload() {
if (Object.keys(this.currentRow).length === 0) return 0
var total = this.currentRow.Adjudication + this.currentRow.AdjudicationIn24H + this.currentRow.AdjudicationIn48H + this.currentRow.Timepoint + this.currentRow.TimepointIn24H + this.currentRow.TimepointIn48H + this.currentRow.TrainingTimes + this.currentRow.RefresherTraining + this.currentRow.Global + this.currentRow.Downtime
var total =
this.currentRow.Adjudication +
this.currentRow.AdjudicationIn24H +
this.currentRow.AdjudicationIn48H +
this.currentRow.Timepoint +
this.currentRow.TimepointIn24H +
this.currentRow.TimepointIn48H +
this.currentRow.TrainingTimes +
this.currentRow.RefresherTraining +
this.currentRow.Global +
this.currentRow.Downtime
return total
}
},
},
mounted() {
this.trialId = this.$route.query.trialCode
@ -477,14 +674,16 @@ export default {
},
methods: {
//
preview(row,TrialReadingCriterionId){
let file = row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === TrialReadingCriterionId}).StatementCriterionFileList[0];
let arr = file.FilePath.split(".");
preview(row, TrialReadingCriterionId) {
let file = row.CriterionCategoryList.find((v) => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}).StatementCriterionFileList[0]
let arr = file.FilePath.split('.')
this.$preview({
path:file.FilePath,
type:arr[arr.length - 1],
title:file.FileName
path: file.FilePath,
type: arr[arr.length - 1],
title: file.FileName,
})
},
isConsistencyChange(row, ite, value, showMessage) {
@ -492,39 +691,50 @@ export default {
},
batchSetCriterionJoinJoinAnalysis(row, ite, value, showMessage) {
let params
if (row.DoctorCriterionStatusList && row.DoctorCriterionStatusList.length > 0) {
if (
row.DoctorCriterionStatusList &&
row.DoctorCriterionStatusList.length > 0
) {
if (ite) {
let item = row.CriterionCategoryList.find(v => v.TrialReadingCriterionId === ite.TrialReadingCriterionId)
let item = row.CriterionCategoryList.find(
(v) => v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
if (item) {
params = [{
params = [
{
Id: item.Id,
EnrollId: item.EnrollId,
TrialReadingCriterionId: item.TrialReadingCriterionId,
IsJoinAnalysis: value
}]
IsJoinAnalysis: value,
},
]
}
} else {
params = row.DoctorCriterionStatusList.map(v => {
params = row.DoctorCriterionStatusList.map((v) => {
return {
Id: v.Id,
EnrollId: v.EnrollId,
TrialReadingCriterionId: v.TrialReadingCriterionId,
IsJoinAnalysis: v.IsJoinAnalysis
IsJoinAnalysis: v.IsJoinAnalysis,
}
})
}
} else {
params = row.CriterionCategoryList.map(v => {
params = row.CriterionCategoryList.map((v) => {
return {
EnrollId: v.EnrollId,
TrialReadingCriterionId: v.TrialReadingCriterionId,
IsJoinAnalysis: value
IsJoinAnalysis: value,
}
})
}
batchSetCriterionJoinJoinAnalysis(params).then(res => {
batchSetCriterionJoinJoinAnalysis(params).then((res) => {
if (showMessage) {
this.$message.success(this.$t('trials:enrolledReviews:message:batchSetCriterionJoinJoinAnalysis1'))
this.$message.success(
this.$t(
'trials:enrolledReviews:message:batchSetCriterionJoinJoinAnalysis1'
)
)
}
this.initPage()
})
@ -532,17 +742,25 @@ export default {
go(path) {
window.open(path)
},
beforeUpload() {
beforeUpload(file) {
if (this.fileList.length > 0) {
// 1
this.$alert(this.$t('trials:enrolledReviews:message:uploadSowWaring1'))
return
return false
}
const fileName = file.name
var extendName = fileName
.substring(fileName.lastIndexOf('.'))
.toLocaleLowerCase()
if (extendName !== '.pdf') {
this.$alert(this.$t('trials:enrolledReviews:message:uploadSowWaring2'))
return false
}
},
handleSave() {
this.$refs['DictionaryTypeConfigForm'].validate(valid => {
this.$refs['DictionaryTypeConfigForm'].validate((valid) => {
if (!valid) return
addDoctorCriterionFile(this.form).then(res => {
addDoctorCriterionFile(this.form).then((res) => {
//
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.initPage()
@ -560,11 +778,19 @@ export default {
this.btnLoading = true
var fileName = param.file.name
let file = await this.fileToBlob(param.file)
let res = await this.OSSclient.put(`/SystemData/reviewer/${this.form.FileType === 0 ? '既往阅片情况声明' : '入项资格确认书'}/${this.rowData.DoctorId}/${fileName}`, file)
let res = await this.OSSclient.put(
`/SystemData/reviewer/${
this.form.FileType === 0 ? '既往阅片情况声明' : '入项资格确认书'
}/${this.rowData.DoctorId}/${fileName}`,
file
)
this.form.FileName = param.file.name
this.form.FilePath = this.$getObjectName(res.url)
param.file.url = this.$getObjectName(res.url)
this.$set(this.fileList, 0, { name: this.form.FileName, url: this.$getObjectName(res.url) })
this.$set(this.fileList, 0, {
name: this.form.FileName,
url: this.$getObjectName(res.url),
})
this.btnLoading = false
// this.btnLoading = true
@ -587,7 +813,7 @@ export default {
this.fileList = []
},
addCol(fileType, row, item) {
this.rowData = {...row}
this.rowData = { ...row }
this.model_cfg.visible = true
this.form = {
Remark: null,
@ -599,10 +825,13 @@ export default {
DoctorId: row.DoctorId,
CriterionType: item.CriterionType,
CriterionName: item.TrialReadingCriterionName,
TrialReadingCriterionId: item.TrialReadingCriterionId
TrialReadingCriterionId: item.TrialReadingCriterionId,
}
this.handleRemoveFile2()
this.model_cfg.title = fileType === 0 ? this.$t('trials:enrolledReviews:message:SOW') : this.$t('trials:enrolledReviews:message:EQC')// '' : ''
this.model_cfg.title =
fileType === 0
? this.$t('trials:enrolledReviews:message:SOW')
: this.$t('trials:enrolledReviews:message:EQC') // '' : ''
},
windowOpen(url) {
console.log(url)
@ -614,12 +843,14 @@ export default {
updateTrialReviewerState({
TrialId: this.listQuery.TrialId,
IsEnable: v,
EnrollId: row.EnrollId
}).then(res => {
EnrollId: row.EnrollId,
})
.then((res) => {
// ''
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.listLoading = false
}).catch(() => {
})
.catch(() => {
this.listLoading = false
})
},
@ -630,19 +861,20 @@ export default {
TrialId: this.$route.query.trialId,
EnrollId: this.currentRow.EnrollId,
ReadingCategorys: Object.assign([], this.ReadingCategorys),
TrialReadingCriterionId: this.TrialReadingCriterionId
TrialReadingCriterionId: this.TrialReadingCriterionId,
}
if (~this.ReadingCategorys.indexOf(1)) {
params.ReadingCategorys.push(2)
}
setEnrollReadingCategory(params)
.then(res => {
.then((res) => {
this.readingCategory_model.visible = false
this.readingCategory_model.btnLoading = false
this.readingCategory_model.showClose = true
this.initPage()
this.$message.success(this.$t('common:message:savedSuccessfully'))
}).catch(_ => {
})
.catch((_) => {
this.readingCategory_model.btnLoading = false
this.readingCategory_model.showClose = true
})
@ -653,16 +885,20 @@ export default {
this.currentRow = row
this.TrialReadingCriterionId = TrialReadingCriterionId
this.readingCategory_model.visible = true
var o = row.CriterionCategoryList.find(v => {
var o = row.CriterionCategoryList.find((v) => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}) ? row.CriterionCategoryList.find(v => {
})
? row.CriterionCategoryList.find((v) => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}) : null
var i = row.TrialReadingCriterionList.find(v => {
})
: null
var i = row.TrialReadingCriterionList.find((v) => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}) ? row.TrialReadingCriterionList.find(v => {
})
? row.TrialReadingCriterionList.find((v) => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}) : null
})
: null
if (i) {
this.IsArbitrationReading = i.IsArbitrationReading
this.IsOncologyReading = i.IsOncologyReading
@ -678,17 +914,23 @@ export default {
//
initPage() {
this.listLoading = true
getTrialEnrollmentWorkloadStats(this.listQuery).then((res) => {
getTrialEnrollmentWorkloadStats(this.listQuery)
.then((res) => {
this.listLoading = false
let resArray = res.Result.CurrentPageData
try {
resArray.forEach(v => {
if (v.DoctorCriterionStatusList && v.DoctorCriterionStatusList.length === 0) {
this.isConsistencyChange(v, null,true, false)
resArray.forEach((v) => {
if (
v.DoctorCriterionStatusList &&
v.DoctorCriterionStatusList.length === 0
) {
this.isConsistencyChange(v, null, true, false)
}
v.CriterionCategoryList.forEach(v1 => {
let o = v.DoctorCriterionStatusList.find(v2 => {
return v2.TrialReadingCriterionId === v1.TrialReadingCriterionId
v.CriterionCategoryList.forEach((v1) => {
let o = v.DoctorCriterionStatusList.find((v2) => {
return (
v2.TrialReadingCriterionId === v1.TrialReadingCriterionId
)
})
if (o) {
this.$set(v1, 'Id', o.Id)
@ -702,7 +944,8 @@ export default {
}
this.list = resArray
if (this.list.length > 0) {
this.TrialReadingCriterionList = this.list[0].TrialReadingCriterionList
this.TrialReadingCriterionList =
this.list[0].TrialReadingCriterionList
}
this.total = res.Result.TotalCount
this.$nextTick(() => {
@ -711,7 +954,8 @@ export default {
this.$refs.myTable.doLayout()
}
})
}).catch(() => {
})
.catch(() => {
this.listLoading = false
})
},
@ -719,7 +963,7 @@ export default {
// Enroll
handleEnroll() {
this.$router.push({
path: `/trials/trials-panel/enrolled-reviewers/enroll?trialId=${this.listQuery.TrialId}&trialCode=${this.trialId}`
path: `/trials/trials-panel/enrolled-reviewers/enroll?trialId=${this.listQuery.TrialId}&trialCode=${this.trialId}`,
})
},
@ -731,12 +975,16 @@ export default {
//
handleUpdateReviewerStatus() {
this.$refs.updateStatusForm.validate(valid => {
this.$refs.updateStatusForm.validate((valid) => {
if (!valid) return
this.reviewerStatus_model.btnLoading = true
this.reviewerStatus_model.showClose = false
enrollBackOrOut(this.listQuery.TrialId, this.currentRow.DoctorId, this.updateStatusForm.Status)
.then(res => {
enrollBackOrOut(
this.listQuery.TrialId,
this.currentRow.DoctorId,
this.updateStatusForm.Status
)
.then((res) => {
this.$nextTick(() => {
this.$refs['updateStatusForm'].resetFields()
})
@ -765,14 +1013,19 @@ export default {
handleUpdateReadingType() {
this.readingType_model.btnLoading = true
this.readingType_model.showClose = false
updateReviewerReadingType(this.listQuery.TrialId, this.currentRow.DoctorId, this.readingType)
.then(res => {
updateReviewerReadingType(
this.listQuery.TrialId,
this.currentRow.DoctorId,
this.readingType
)
.then((res) => {
this.readingType_model.visible = false
this.readingType_model.btnLoading = false
this.readingType_model.showClose = true
this.initPage()
this.$message.success(this.$t('common:message:savedSuccessfully'))
}).catch(_ => {
})
.catch((_) => {
this.readingType_model.btnLoading = false
this.readingType_model.showClose = true
})
@ -791,8 +1044,6 @@ export default {
this.initPage()
},
handleTipCloseOK() {
zzSessionStorage.setItem(
'ReviewerName',
@ -801,7 +1052,7 @@ export default {
this.currentClickRowData.LastName
)
this.$router.push({
path: `/trials/trials-panel/enrolled-reviewers/stats?doctorId=${this.currentClickRowData.DoctorId}&trialId=${this.listQuery.TrialId}&trialCode=${this.trialId}&doctorName=${this.currentClickRowData.LastName}/${this.currentClickRowData.FirstName}`
path: `/trials/trials-panel/enrolled-reviewers/stats?doctorId=${this.currentClickRowData.DoctorId}&trialId=${this.listQuery.TrialId}&trialCode=${this.trialId}&doctorName=${this.currentClickRowData.LastName}/${this.currentClickRowData.FirstName}`,
})
},
@ -827,18 +1078,18 @@ export default {
}
})
return sums
}
}
},
},
}
</script>
<style lang="scss">
.enroll-list {
.enroll-list {
height: 100%;
.readingCategory_model{
.base-modal-body{
.readingCategory_model {
.base-modal-body {
min-height: 150px;
padding-bottom: 30px !important;
}
}
}
}
</style>

View File

@ -70,54 +70,21 @@
width="150"
>
<template slot-scope="scope">
<div
v-if="
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i]
.QuestionType === 1
"
>
<span
v-if="
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i]
.DictionaryCode
"
>{{
$fd(
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[
i
].DictionaryCode,
parseInt(
scope.row.VisitTaskInfoList[j.index]
.JudgeQuestionList[i].Answer
)
)
}}</span
>
<span v-else>{{
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i]
.Answer
}}</span>
<div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 1">
<span v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode">
{{ $fd(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode,parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))
}}
</span>
<span v-else>
{{ scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer }}
</span>
<span v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit && !isNaN(parseFloat(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))">{{ $fd('ValueUnit', parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit)) }}</span>
</div>
<div
v-else-if="
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i]
.QuestionType === 2
"
>
<div
v-if="
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i]
.Answer
"
>
<span>{{
$fd(
"YesOrNo",
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[
i
].Answer
)
}}</span>
<div v-else-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 2">
<div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer">
<span>
{{ $fd("YesOrNo",scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer) }}
</span>
<!-- 查看详情 -->
<el-button
type="text"
@ -144,10 +111,7 @@
</div>
</div>
<div v-else>
{{
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i]
.Answer
}}
{{ scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer }}
</div>
</template>
</el-table-column>
@ -321,7 +285,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 +399,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 +495,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 +756,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 +774,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 +782,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 +809,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 +817,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 +838,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 {

View File

@ -41,7 +41,7 @@
</div>
<div class="right-wrapper">
<div v-if="selected.type===1" class="right-content">
<iframe :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}`" width="100%" height="100%" frameborder="0" />
<iframe :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}&COMPANY=${COMPANY}`" width="100%" height="100%" frameborder="0" />
</div>
<div v-else-if="selected.type===2" class="right-content" style="padding: 10px">
<clinicalDataQuestions
@ -216,7 +216,8 @@ export default {
otherInfo: {},
moment,
loading: false,
currentUser: zzSessionStorage.getItem('userName')
currentUser: zzSessionStorage.getItem('userName'),
COMPANY:process.env.VUE_APP_COMPANY_NAME
}
},
mounted() {

View File

@ -1039,7 +1039,7 @@ export default {
loadImageStack(dicomSeries) {
return new Promise(resolve => {
this.isCurrentTask = dicomSeries.isCurrentTask
this.isBaseline = dicomSeries.isBaseline
this.isBaseline = dicomSeries.isBaseLineTask
this.readingTaskState = dicomSeries.readingTaskState
if (this.isCurrentTask && this.readingTaskState < 2) {
this.activeTool = 1
@ -1088,16 +1088,16 @@ export default {
var frame = imageInfo.frame
this.stack.frame = this.stack.isExistMutiFrames ? parseInt(frame) : null
this.stack.instanceId = instanceId
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
ToolStateManager.clearImageIdToolState(dicomSeries.imageIds)
this.toggleClipPlay(false)
this.toolState.viewportInvert = false
this.toolState.dicomInfoVisible = false
const element = this.$refs.canvas
cornerstone.enable(element)
element.tabIndex = 0
element.focus()
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
ToolStateManager.clearImageIdToolState(dicomSeries.imageIds)
this.toggleClipPlay(false)
this.toolState.viewportInvert = false
this.toolState.dicomInfoVisible = false
var scope = this
// var p = parseInt(new Date().getTime())
// requestPoolManager.loadAndCacheImagePlus(this.stack.imageIds[this.stack.currentImageIdIndex], this.stack.seriesId, p*100).then(image=>{
@ -1654,7 +1654,7 @@ export default {
resetWwwc() {
this.toolState.viewportInvert = false
var viewport = cornerstone.getViewport(this.canvas)
viewport.invert = false
// viewport.invert = false
var image = cornerstone.getImage(this.canvas)
viewport.voi.windowWidth = image.windowWidth
viewport.voi.windowCenter = image.windowCenter

View File

@ -352,7 +352,44 @@
<div class="text">{{ $t('trials:reading:button:reset') }}</div>
</div>
</el-tooltip>
<el-tooltip
v-if="trialCriterion.ImageUploadEnum > 0 && currentReadingTaskState < 2"
v-hasPermi="['role:ir']"
class="item"
effect="dark"
:content="$t('trials:reading:button:upload')"
placement="bottom"
>
<div class="tool-wrapper">
<div class="icon" @click.prevent="openUploadImage('upload')">
<i class="el-icon-upload2 svg-icon" />
</div>
<div class="text">{{ $t('trials:reading:button:upload') }}</div>
</div>
</el-tooltip>
<el-tooltip
v-if="trialCriterion.ImageDownloadEnum > 0"
v-hasPermi="[
'role:ir',
'role:mim',
'role:mc',
'role:pm',
'role:apm',
'role:ea',
'role:qa',
]"
class="item"
effect="dark"
:content="$t('trials:reading:button:download')"
placement="bottom"
>
<div class="tool-wrapper">
<div class="icon" @click.prevent="openUploadImage('download')">
<i class="el-icon-download svg-icon" />
</div>
<div class="text">{{ $t('trials:reading:button:download') }}</div>
</div>
</el-tooltip>
<div style="margin-left:auto;">
<div style="padding:5px">
<!-- 手册 -->
@ -397,8 +434,7 @@
</div>
</div>
<div ref="form-container" class="form-container">
<!-- 激活canvas测量数据 -->
<div class="form-wrapper">
<RecistBMQuestionList
v-if="CriterionType === 17"
ref="measurementList"
@ -433,13 +469,53 @@
/>
<RecistQuestionList
v-else-if="CriterionType !== 17"
v-else-if="CriterionType === 1"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<IVUSList
v-else-if="CriterionType === 19"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<OCTList
v-else-if="CriterionType === 20"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<h2 v-else style="color:#ddd">
Developing...
</h2>
</div>
<div v-if="iseCRFShowInDicomReading && currentReadingTaskState < 2" class="form-footer">
<el-button
type="primary"
size="small"
@click="skipTask"
>
<!-- 跳过 -->
{{ $t('trials:readingReport:button:skip') }}
</el-button>
<el-button
type="primary"
size="small"
@click="submit"
>
<!-- 提交 -->
{{ $t('common:button:submit') }}
</el-button>
</div>
</div>
</div>
@ -655,6 +731,37 @@
</el-form-item>
</el-form>
</el-dialog>
<upload-dicom-and-nonedicom
v-if="uploadImageVisible"
:subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode"
:criterion="uploadTrialCriterion"
:visible.sync="uploadImageVisible"
:visit-task-id="taskId"
:is-reading-task-view-in-order="isReadingTaskViewInOrder"
/>
<download-dicom-and-nonedicom
v-if="downloadImageVisible"
:subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode"
:criterion="uploadTrialCriterion"
:task-id="taskId"
:visible.sync="downloadImageVisible"
/>
<!-- 签名框 -->
<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;user-select: text !important;">{{ `(${$t('common:label:sign')}${ currentUser })` }}</span>
</div>
<SignForm ref="signForm" :sign-code-enum="signCode" @closeDialog="closeSignDialog" />
</el-dialog>
</div>
</template>
<script>
@ -677,6 +784,8 @@ import RecistBMQuestionList from './RecistBM/QuestionList'
import IRecistQuestionList from './IRecist/QuestionList'
import PCWGQuestionList from './PCWG/QuestionList'
import LuganoQuestionList from './Lugano/QuestionList'
import IVUSList from './IVUS/QuestionList'
import OCTList from './OCT/QuestionList'
import CustomWwwcForm from './CustomWwwcForm'
import Manuals from './Manuals'
import Hotkeys from './Hotkeys'
@ -684,6 +793,14 @@ import WL from './WL'
import Others from './Others'
import DicomEvent from './DicomEvent'
import html2canvas from 'html2canvas'
import uploadDicomAndNonedicom from '@/components/uploadDicomAndNonedicom'
import downloadDicomAndNonedicom from '@/components/downloadDicomAndNonedicom'
import SignForm from '@/views/trials/components/newSignForm'
import { getCriterionReadingInfo, verifyVisitTaskQuestions, submitDicomVisitTask } from '@/api/trials'
import { setSkipReadingCache } from '@/api/reading'
import { getAutoCutNextTask } from '@/api/user'
import const_ from '@/const/sign-code'
import { changeURLStatic } from '@/utils/history.js'
export default {
name: 'DicomViewer',
components: {
@ -698,7 +815,13 @@ export default {
PCWGQuestionList,
RecistBMQuestionList,
IRecistQuestionList,
LuganoQuestionList },
LuganoQuestionList,
IVUSList,
OCTList,
'download-dicom-and-nonedicom': downloadDicomAndNonedicom,
'upload-dicom-and-nonedicom': uploadDicomAndNonedicom,
SignForm
},
props: {
isShow: {
type: Boolean,
@ -724,6 +847,10 @@ export default {
type: Boolean,
required: true
},
iseCRFShowInDicomReading: {
type: Boolean,
required: true
},
questionFormChangeState: {
type: Boolean,
default() {
@ -839,7 +966,19 @@ export default {
activeCanvasWC: null,
activeTaskInfo: {},
clipPlaying: false,
fps: 15
fps: 15,
trialCriterion: {},
//
downloadImageVisible: false,
uploadImageVisible: false,
uploadSubjectId: null,
uploadSubjectCode: null,
uploadTrialCriterion: {},
uploadStatus: 'upload',
taskId: '',
signVisible: false,
signCode: null,
currentUser: zzSessionStorage.getItem('userName')
}
},
@ -912,6 +1051,7 @@ export default {
mounted() {
this.getHotKeys()
this.getWwcTpl()
this.getTrialCriterion()
// cornerstone.imageCache.setMaximumSizeBytes(0)
// const maximumSizeInBytes = 1024 * 1024 * 1024 // 1 GB
@ -923,6 +1063,10 @@ export default {
this.measuredTools = [{ toolName: 'ArrowAnnotate', text: this.$t('trials:reading:button:arrowAnnotate'), icon: 'arrow', isDisabled: false, disabledReason: '' }, { toolName: 'RectangleRoi', text: this.$t('trials:reading:button:rectangle'), icon: 'rectangle', isDisabled: false, disabledReason: '' }]
} else if (this.CriterionType === 17) {
this.measuredTools = [{ toolName: 'Length', text: this.$t('trials:reading:button:length'), icon: 'length', isDisabled: false, disabledReason: '' }, { toolName: 'RectangleRoi', text: this.$t('trials:reading:button:rectangle'), icon: 'rectangle', isDisabled: false, disabledReason: '' }, { toolName: 'ArrowAnnotate', text: this.$t('trials:reading:button:arrowAnnotate'), icon: 'arrow', isDisabled: false, disabledReason: '' }]
} else if (this.CriterionType === 19) {
this.measuredTools = []
} else if (this.CriterionType === 20) {
this.measuredTools = []
}
this.rotateList[0] = '1'
this.colorList[0] = ''
@ -1062,6 +1206,27 @@ export default {
})
},
methods: {
getTrialCriterion() {
getCriterionReadingInfo({
TrialId: this.trialId || this.$route.query.trialId,
TrialReadingCriterionId: this.trialReadingCriterionId || this.$route.query.TrialReadingCriterionId
})
.then((res) => {
this.trialCriterion = res.Result
})
.catch(() => {})
},
openUploadImage(status) {
const idx = this.visitTaskList.findIndex(i => i.IsCurrentTask)
if (idx > -1) {
this.taskId = this.visitTaskList[idx].VisitTaskId
}
this.uploadSubjectCode = this.$route.query.subjectCode||localStorage.getItem("subjectCode")
this.uploadSubjectId = this.$route.query.subjectId||localStorage.getItem("subjectId")
this.uploadTrialCriterion = this.trialCriterion
this.uploadStatus = status
this[`${status}ImageVisible`] = true
},
async getWwcTpl() {
// const loading = this.$loading({ fullscreen: true })
try {
@ -2070,17 +2235,119 @@ export default {
this.isFullscreen = false
this.manualsDialog.visible = true
},
async skipTask() {
//
const confirm = await this.$confirm(
this.$t('trials:readingReport:message:skipConfirm'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
// const loading = this.$loading({ fullscreen: true })
try {
const idx = this.visitTaskList.findIndex(i => i.IsCurrentTask)
if (idx > -1) {
var visitTaskId = this.visitTaskList[idx].VisitTaskId
const res = await setSkipReadingCache({ visitTaskId: visitTaskId })
if (res.IsSuccess) {
window.location.reload()
}
}
// loading.close()
} catch (e) {
// loading.close()
console.log(e)
}
},
async submit() {
const loading = this.$loading({ fullscreen: true })
try {
const idx = this.visitTaskList.findIndex(i => i.IsCurrentTask)
if (idx > -1) {
var visitTaskId = this.visitTaskList[idx].VisitTaskId
this.taskId = visitTaskId
await verifyVisitTaskQuestions({ visitTaskId: visitTaskId })
const { ImageAssessmentReportConfirmation } = const_.processSignature
this.signCode = ImageAssessmentReportConfirmation
this.signVisible = true
}
loading.close()
} catch (e) {
loading.close()
}
},
//
closeSignDialog(isSign, signInfo) {
if (isSign) {
this.signConfirm(signInfo)
} else {
this.signVisible = false
}
},
//
async signConfirm(signInfo) {
this.loading = true
try {
var params = {
data: {
visitTaskId: this.taskId
},
signInfo: signInfo
}
const res = await submitDicomVisitTask(params)
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
if (this.$refs['signForm']) {
this.$refs['signForm'].btnLoading = false
}
this.signVisible = false
//
this.readingTaskState = 2
await store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
await store.dispatch('reading/setCurrentReadingTaskState', 2)
const res = await getAutoCutNextTask()
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()
} else {
// ''
this.$confirm(this.$t('trials:readingReport:message:msg4'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
window.location.reload()
})
.catch(action => {
changeURLStatic('visitTaskId', this.visitTaskId)
})
}
window.opener.postMessage('refreshTaskList', window.location)
}
this.loading = false
} catch (e) {
this.loading = false
if (this.$refs['signForm'] && this.$refs['signForm'].btnLoading) {
this.$refs['signForm'].btnLoading = false
}
}
},
handleFusion() {
// https
if (!window.isSecureContext) {
this.$alert(this.$t('components:uploadvideo:message:xf3'))
return
}
//
if (!window.crossOriginIsolated) {
this.$alert(this.$t('components:uploadvideo:message:xf4'))
return
}
// if (!window.isSecureContext) {
// this.$alert(this.$t('components:uploadvideo:message:xf3'))
// return
// }
// //
// if (!window.crossOriginIsolated) {
// this.$alert(this.$t('components:uploadvideo:message:xf4'))
// return
// }
if (this.imageQualityIssues && parseInt(this.imageQualityIssues) === 6) {
this.$alert(this.$t('trials:lugano:message:fusionWarn'))
return
@ -2400,9 +2667,22 @@ export default {
.form-container{
// box-sizing: border-box;
width: 350px;
height: 100%;
// height: 100vh;
border: 1px solid #727272;
// overflow-y: auto;
// position: relative;
display: flex;
flex-direction: column;
}
.form-wrapper{
flex-grow: 1;
height: 0;
overflow: hidden;
}
.form-footer{
background: #000;
padding: 10px 0;
text-align: center;
}
.viewer-container{
box-sizing: border-box;

View File

@ -1,7 +1,7 @@
<template>
<div class="measurement-wrapper" :style="{'height':height+10+'px'}">
<div class="measurement-wrapper">
<div class="container" :style="{'height':height+'px'}">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
@ -203,7 +203,6 @@ export default {
},
data() {
return {
height: window.innerHeight - 140,
questions: [],
activeName: '',
activeItem: {
@ -269,8 +268,6 @@ export default {
DicomEvent.$on('split', measureData => {
this.handleSplit(measureData.RowId, measureData.QuestionId)
})
window.addEventListener('resize', this.setHeight)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
@ -457,9 +454,6 @@ export default {
} catch (e) { console.log(e) }
})
},
setHeight() {
this.height = window.innerHeight - 140
},
getQuestionAnswer(questions, questionMark, answers) {
var idx = questions.findIndex(i => i.QuestionMark === questionMark)
if (idx > -1) {
@ -1183,6 +1177,7 @@ export default {
</script>
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
// overflow: hidden;

View File

@ -0,0 +1,874 @@
<template>
<div class="measurement-wrapper">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
<span style="margin-left:5px;">{{ taskBlindName }}</span>
</h3>
<div v-if="readingTaskState < 2">
<el-tooltip class="item" effect="dark" :content="$t('trials:dicomReading:message:confirmReset')" placement="bottom">
<i
class="el-icon-refresh-left"
@click="resetMeasuredData"
/>
</el-tooltip>
</div>
</div>
<!-- 影像质量问题 -->
<div class="lesions">
<Questions
ref="ecrf"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="1"
/>
</div>
<!-- 测量问题 -->
<template v-if="questions.length > 0">
<div v-for="(qs,index) in questions" :key="index" v-loading="loading" class="lesions lesions_wrapper" style="margin-bottom: 10px">
<h4 v-if="qs.Type === 'group'" style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ language==='en'?qs.GroupEnName:qs.GroupName }}
</h4>
<div class="lesion_list">
<el-form
v-if="questions.length > 0"
:ref="`questions${index}`"
size="small"
:model="questionForm"
>
<div class="table-wrapper">
<div v-for="item in qs.Childrens" :key="item.Id">
<div v-if="item.Type === 'basicTable'" class="flex-row" style="margin:3px 0;">
<div class="title">{{ item.QuestionName }}</div>
<div v-if="item.LesionType === 102 && readingTaskState < 2">
<div class="add-icon" @click.prevent="downloadTpl">
<i class="el-icon-download" />
</div>
<div class="add-icon" style="margin: 0 5px;" @click.prevent="uploadTpl(item.LesionType)">
<i class="el-icon-upload2" />
</div>
<div class="add-icon" @click.prevent="handleAddOrEdit('add',item)">
<i class="el-icon-plus" />
</div>
</div>
</div>
<el-table
v-if="item.Type === 'basicTable' && item.TableQuestions"
:ref="item.Id"
:data="item.TableQuestions.Answers"
>
<!-- <el-table-column :label="$t('CustomizeQuestionFormItem:label:OrderMark')" width="60px" show-overflow-tooltip>
<template slot-scope="scope">
{{ getLesionName(item.OrderMark,scope.row.RowIndex) }}
</template>
</el-table-column> -->
<!-- <el-table-column
type="index"
width="40px"
/> -->
<el-table-column
v-for="q of item.TableQuestions.Questions"
:key="q.Id"
:prop="q.Id"
:label="q.QuestionName"
show-overflow-tooltip
:render-header="renderHeader"
>
<template slot-scope="scope">
<span v-if="q.Unit > 0 && !isNaN(parseFloat(scope.row[q.Id]))">
{{ `${scope.row[q.Id]} ${$fd('ValueUnit', parseInt(q.Unit))}` }}
</span>
<span v-else-if="q.DictionaryCode">
{{ `${$fd(q.DictionaryCode, parseInt(scope.row[q.Id]))}` }}
</span>
<span v-else>
{{ `${scope.row[q.Id]}` }}
</span>
</template>
</el-table-column>
<el-table-column
v-if="readingTaskState < 2 && item.LesionType === 102"
:label="$t('common:action:action')"
width="90px"
fixed="right"
>
<template slot-scope="scope">
<el-button type="text" size="mini" @click="handleAddOrEdit('edit',item, scope.$index)">
{{ $t('common:button:edit') }}
</el-button>
<el-button type="text" size="mini" @click="handleDelete(item, scope.$index)">
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
<template v-else>
<!-- 数值 -->
<el-form-item
v-if="item.ShowQuestion!==2"
:key="item.Id"
:label="`${item.QuestionName}`"
:prop="item.Id"
:rules="[
{ required: (item.IsRequired === 0 || (item.IsRequired ===1 && item.RelevanceId && (item.RelevanceValue.includes(questionForm[item.RelevanceId])))) && item.Type!=='group' && item.Type!=='summary',
message:['radio', 'select', 'checkbox'].includes(item.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur','change']},
]"
>
<!-- 数值类型 -->
<template v-if="item.Type==='textarea'">
<el-input
v-model="questionForm[qs.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:disabled="!isCurrentTask || readingTaskState>=2"
@change="((val)=>{formItemChange(val, qs)})"
/>
</template>
</el-form-item>
</template>
</div>
<div
v-if="isCurrentTask && readingTaskState<2 && qs.GroupClassify === 5"
class="base-dialog-footer"
style="text-align:right;margin-top:10px;"
>
<!-- 保存 -->
<el-button
type="primary"
size="mini"
:disabled="!formChanged"
@click="handleSave(index)"
>
{{ $t('common:button:save') }}
</el-button>
</div>
</div>
</el-form>
</div>
</div>
</template>
<!-- 计算值 -->
<Questions
ref="ecrf2"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="4"
style="margin-top:20px"
/>
<!-- 评估结果 -->
<Questions
ref="ecrf3"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="5"
/>
<!-- 新增编辑弹窗 -->
<el-dialog
v-if="addOrEdit.visible"
:visible.sync="addOrEdit.visible"
:close-on-click-modal="false"
:title="addOrEdit.title"
width="500px"
>
<el-form
ref="tableQsForm"
v-loading="loading"
:model="qsForm"
size="small"
>
<QuestionTableFormItem
v-for="item in qsList"
:key="item.Id"
:question="item"
:question-form="qsForm"
:reading-task-state="readingTaskState"
@setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData"
/>
<el-form-item style="text-align: right">
<el-button
size="small"
@click="addOrEdit.visible = false"
>
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" class="my_upload_btn" @click="saveFormData">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</el-form>
</el-dialog>
<!-- 导入 -->
<el-dialog
v-if="upload.visible"
:visible.sync="upload.visible"
:close-on-click-modal="false"
:title="upload.title"
width="500px"
>
<UploadExcel
:visit-task-id="visitTaskId"
@close="uploadDlgClose"
/>
</el-dialog>
</div>
</div>
</template>
<script>
import { saveTaskQuestion, submitTableQuestion, deleteReadingRowAnswer } from '@/api/trials'
import { resetReadingTask, getIVUSTemplate } from '@/api/reading'
import DicomEvent from './../DicomEvent'
import store from '@/store'
import { mapGetters } from 'vuex'
import Questions from './../Questions'
import QuestionTableFormItem from './QuestionTableFormItem'
import UploadExcel from './UploadExcel'
export default {
name: 'MeasurementList',
components: {
Questions,
QuestionTableFormItem,
UploadExcel
},
props: {
isShow: {
type: Boolean,
required: true
},
isReadingShowSubjectInfo: {
type: Boolean,
required: true
},
questionFormChangeState: {
type: Boolean,
default() {
return false
}
},
questionFormChangeNum: {
type: Number,
default() {
return 0
}
}
},
data() {
return {
questions: [],
visitTaskId: '',
isCurrentTask: false,
loading: false,
readingTaskState: 2,
isBaseLineTask: false,
taskBlindName: '',
tableQuestions: [],
CriterionType: null,
subjectCode: '',
questionForm: {},
formChanged: false,
digitPlaces: 2,
addOrEdit: { visible: false, title: '' },
upload: { visible: false, title: '' },
qsList: [],
answersList: [],
qsForm: {},
diffId: '',
eemId: '',
lumenId: ''
}
},
computed: {
...mapGetters(['visitTaskList', 'language', 'lastCanvasTaskId', 'currentReadingTaskState'])
},
watch: {
lastCanvasTaskId: {
immediate: true,
handler(val) {
if (val) {
this.initList()
}
}
},
currentReadingTaskState: {
immediate: true,
handler(val) {
if (val) {
this.readingTaskState = val
}
}
}
},
mounted() {
// this.subjectCode = this.$router.currentRoute.query.subjectCode
this.subjectCode = localStorage.getItem('subjectCode')
this.digitPlaces = Number(localStorage.getItem('digitPlaces'))
window.addEventListener('message', this.receiveMsg)
this.CriterionType = parseInt(localStorage.getItem('CriterionType'))
DicomEvent.$on('setCollapseActive', measureData => {
this.setCollapseActive(measureData)
console.log('setCollapseActive')
})
DicomEvent.$on('getAllUnSaveLesions', (callback) => {
callback([])
})
DicomEvent.$on('refreshQuestions', _ => {
this.refreshQuestions()
})
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
DicomEvent.$off('getUnSaveTarget')
DicomEvent.$off('refreshQuestions')
},
methods: {
async initList() {
var i = this.visitTaskList.findIndex(i => i.VisitTaskId === this.lastCanvasTaskId)
if (i > -1) {
this.visitTaskId = this.visitTaskList[i].VisitTaskId
this.taskBlindName = this.visitTaskList[i].TaskBlindName
this.readingTaskState = this.visitTaskList[i].ReadingTaskState
this.isBaseLineTask = this.visitTaskList[i].IsBaseLineTask
this.isCurrentTask = this.visitTaskList[i].IsCurrentTask
if (!this.visitTaskList[i].IsInit) {
var loading = this.$loading({ fullscreen: true })
var triald = this.trialId = this.$router.currentRoute.query.trialId
if (!this.visitTaskList[i].studyListInit) {
await store.dispatch('reading/getStudyInfo', { trialId: triald, subjectVisitId: this.visitTaskList[i].VisitId, visitTaskId: this.visitTaskList[i].VisitTaskId, taskBlindName: this.visitTaskList[i].TaskBlindName })
}
if (!this.visitTaskList[i].readingQuestionsInit) {
await store.dispatch('reading/getReadingQuestionAndAnswer', { trialId: triald, visitTaskId: this.visitTaskList[i].VisitTaskId })
}
if (!this.visitTaskList[i].questionsInit) {
await store.dispatch('reading/getDicomReadingQuestionAnswer', { trialId: triald, visitTaskId: this.visitTaskList[i].VisitTaskId })
}
await store.dispatch('reading/setStatus', { visitTaskId: this.visitTaskList[i].VisitTaskId })
loading.close()
}
this.questions = this.visitTaskList[i].ReadingQuestions
this.setQuestionForm(this.questions)
this.$nextTick(() => {
this.$refs['ecrf'].getQuestions(this.visitTaskId)
this.$refs['ecrf2'].getQuestions(this.visitTaskId)
this.$refs['ecrf3'].getQuestions(this.visitTaskId)
})
}
},
setQuestionForm(questions) {
const type = ['number', 'radio', 'select', 'input', 'textarea', 'calculation']
questions.forEach(item => {
if (type.includes(item.Type)) {
const answer = item.Type === 'number' || item.Type === 'calculation' ? isNaN(parseFloat(item.Answer)) ? null : parseFloat(item.Answer) : item.Answer
this.$set(this.questionForm, item.Id, answer)
}
if (item.Childrens.length > 0) {
this.setQuestionForm(item.Childrens)
}
})
},
async resetQuestions(obj) {
this.setQuestions(this.questions, obj)
await store.dispatch('reading/setReadingQuestionAndAnswer', { questions: this.questions, visitTaskId: this.visitTaskId })
this.getTableQuestions()
},
setQuestions(questions, obj) {
questions.forEach(item => {
if ((item.Type === 'table' || item.Type === 'basicTable') && item.Id === obj.questionId) {
var idx = item.TableQuestions.Answers.findIndex(i => i.RowIndex === obj.rowIndex)
item.TableQuestions.Answers[idx].saveTypeEnum = obj.saveTypeEnum
item.TableQuestions.Answers[idx].area1 = obj.area1
item.TableQuestions.Answers[idx].area2 = obj.area2
item.TableQuestions.Answers[idx].diff = obj.diff
for (const i in obj.anwsers) {
item.TableQuestions.Answers[idx][i] = String(obj.anwsers[i])
}
}
if (item.Childrens.length > 0) {
this.setQuestions(item.Childrens, obj)
}
})
},
getQuestions(questions) {
questions.forEach(item => {
if ((item.Type === 'table' || item.Type === 'basicTable') && item.TableQuestions && item.TableQuestions.Answers.length > 0) {
item.TableQuestions.Answers.forEach(answerObj => {
if (answerObj.RowId) {
var diffVal = this.getQuestionAnswer(item.TableQuestions.Questions, 1003, answerObj)
this.$set(answerObj, 'saveTypeEnum', isNaN(parseFloat(diffVal)) ? 1 : 2)
} else {
this.$set(answerObj, 'saveTypeEnum', 0)
}
})
}
if (item.Childrens.length > 0) {
this.getQuestions(item.Childrens)
}
})
return questions
},
getReadingQuestionAndAnswer() {
return new Promise(async resolve => {
try {
const loading = this.$loading({ fullscreen: true })
await store.dispatch('reading/refreshReadingQuestionAndAnswer', { trialId: this.$router.currentRoute.query.trialId, visitTaskId: this.visitTaskId }).then(() => {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx > -1) {
if (this.visitTaskList[idx].ReadingQuestions.length > 0) {
this.questions = this.visitTaskList[idx].ReadingQuestions
this.setQuestionForm(this.questions)
}
this.readingTaskState = this.visitTaskList[idx].ReadingTaskState
this.isBaseLineTask = this.visitTaskList[idx].IsBaseLineTask
this.isCurrentTask = this.visitTaskList[idx].IsCurrentTask
}
})
loading.close()
resolve()
} catch (e) { console.log(e) }
})
},
getQuestionAnswer(questions, questionMark, answers) {
var idx = questions.findIndex(i => i.QuestionMark === questionMark)
if (idx > -1) {
var questionId = questions[idx].Id
return answers[questionId]
} else {
return ''
}
},
isCanActiveTool(toolName) {
return { isCanActiveTool: true, reason: '' }
},
getMaxRowIndex(answerList) {
if (answerList.length > 0) {
var arr = []
answerList.forEach(item => {
var rowIndex = item.RowIndex
arr.push(parseInt(rowIndex))
})
var max = Math.max.apply(null, arr)
return max
} else {
return 0
}
},
getLesionName(orderMark, rowIndex) {
var lessionName = ''
var rowIndexArr = rowIndex.split('.')
var x = parseInt(rowIndexArr[0])
var y = parseInt(rowIndexArr[1])
if (y > 0) {
y = String.fromCharCode(parseInt(rowIndexArr[1]) - 1 + 65 + 32)
lessionName = `${orderMark}${String(x).padStart(2, '0')}${y}`
} else {
lessionName = `${orderMark}${String(x).padStart(2, '0')}`
}
return lessionName
},
modifyMeasuredData(measureObj) {
},
//
setMeasuredData(measureData) {
},
formItemChange() {
this.formChanged = true
},
limitBlur(qId, valueType) {
const value = this.questionForm[qId]
if (isNaN(parseInt(value))) {
this.$set(this.questionForm, qId, '')
} else {
if (valueType === 0) {
this.$set(this.questionForm, qId, parseInt(value))
} else if (valueType === 3) {
this.$set(this.questionForm, qId, parseFloat(value))
} else {
this.$set(this.questionForm, qId, this.numberToFixed(value))
}
}
},
numberToFixed(v) {
if (this.digitPlaces > -1) {
return isNaN(parseFloat(v)) ? null : `${parseFloat(v).toFixed(this.digitPlaces)}`
} else {
return v
}
},
async handleSave(index) {
const refName = `questions${index}`
const valid = await this.$refs[refName][0].validate()
if (!valid) return
const loading = this.$loading({ fullscreen: true })
var answers = []
for (const k in this.questionForm) {
answers.push({ id: k, answer: this.questionForm[k] })
}
var params = {
visitTaskId: this.visitTaskId,
answers: answers
}
try {
await saveTaskQuestion(8, params)
this.$message.success(this.$t('common:message:savedSuccessfully'))
loading.close()
DicomEvent.$emit('getReportInfo', true)
this.refreshQuestions()
this.formChanged = false
} catch (e) {
console.log(e)
loading.close()
}
},
getECRFQuestions(obj) {
this.$refs['ecrf'].getQuestions(obj.visitTaskId)
this.$refs['ecrf2'].getQuestions(obj.visitTaskId)
this.$refs['ecrf3'].getQuestions(obj.visitTaskId)
},
async resetMeasuredData() {
const confirm = await this.$confirm(
this.$t('trials:dicomReading:message:confirmReset1'),
this.$t('trials:dicomReading:message:confirmReset2'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
const loading = this.$loading({ fullscreen: true })
try {
const res = await resetReadingTask({ visitTaskId: this.visitTaskId })
this.loading = false
if (res.IsSuccess) {
await this.getReadingQuestionAndAnswer(this.visitTaskId)
const triald = this.$router.currentRoute.query.trialId
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: triald, visitTaskId: this.visitTaskId })
this.$refs['ecrf'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf2'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf3'].getQuestions(this.visitTaskId, true)
DicomEvent.$emit('getReportInfo', true)
}
loading.close()
} catch (e) {
loading.close()
console.log(e)
}
},
async refreshQuestions() {
const triald = this.$router.currentRoute.query.trialId
await this.getReadingQuestionAndAnswer(this.visitTaskId)
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: triald, visitTaskId: this.visitTaskId })
this.$refs['ecrf'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf2'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf3'].getQuestions(this.visitTaskId, true)
},
receiveMsg(event) {
},
resetFormItemData(v) {
this.qsForm[v] = null
},
setFormItemData(obj) {
this.qsForm[obj.key] = obj.val
if (obj.key === this.eemId || obj.key === this.lumenId) {
const eem = this.qsForm[this.eemId]
const lumen = this.qsForm[this.lumenId]
if (isNaN(parseFloat(eem)) || isNaN(parseFloat(lumen))) {
this.$set(this.qsForm, this.diffId, null)
} else {
const diff = parseFloat(eem) - parseFloat(lumen)
this.$set(this.qsForm, this.diffId, this.numberToFixed(diff))
}
}
},
handleAddOrEdit(type, row, index) {
this.addOrEdit.title = row.QuestionName
this.qsList = row.TableQuestions.Questions
this.qsForm = {}
this.qsForm.questionId = row.Id
this.qsForm.rowId = type === 'add' ? null : row.TableQuestions.Answers && row.TableQuestions.Answers[index] ? row.TableQuestions.Answers[index].RowId : null
if (type === 'add') {
var maxIndex = this.getMaxRowIndex(row.TableQuestions.Answers)
this.qsForm.rowIndex = `${maxIndex + 1}.00`
} else {
this.qsForm.rowIndex = row.TableQuestions.Answers && row.TableQuestions.Answers[index] ? row.TableQuestions.Answers[index].RowIndex : null
}
row.TableQuestions.Questions.map(i => {
if (i.QuestionMark === 1001) {
this.eemId = i.Id
}
if (i.QuestionMark === 1002) {
this.lumenId = i.Id
}
if (i.QuestionMark === 1003) {
this.diffId = i.Id
}
let v = null
if (type === 'add') {
v = i.Type === 'number' ? undefined : null
} else {
v = row.TableQuestions.Answers && row.TableQuestions.Answers[index] ? row.TableQuestions.Answers[index][i.Id] : null
}
this.$set(this.qsForm, i.Id, v)
})
this.addOrEdit.visible = true
},
async saveFormData() {
const valid = await this.$refs.tableQsForm.validate()
if (!valid) return
const loading = this.$loading({ fullscreen: true })
try {
var answers = []
var reg = new RegExp(/^[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}$/)
for (const k in this.qsForm) {
if (reg.test(k)) {
if (answers.findIndex(i => i.tableQuestionId === k) === -1) {
answers.push({ tableQuestionId: k, answer: this.qsForm[k] })
}
}
}
var params = {
questionId: this.qsForm.questionId,
rowId: this.qsForm.rowId,
rowIndex: this.qsForm.rowIndex,
visitTaskId: this.visitTaskId,
trialId: this.trialId,
answerList: answers
}
const res = await submitTableQuestion(params)
if (res.IsSuccess) {
// this.refreshQuestions()
await this.getReadingQuestionAndAnswer(this.visitTaskId)
this.$refs['ecrf'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf2'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf3'].getQuestions(this.visitTaskId, true)
DicomEvent.$emit('getReportInfo', true)
//
this.addOrEdit.visible = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
loading.close()
} catch (e) {
console.log(e)
loading.close()
}
},
async handleDelete(row, index) {
const loading = this.$loading({ fullscreen: true })
try {
const confirm = await this.$confirm(
this.$t('trials:reading:warnning:msg48'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
if (row.TableQuestions.Answers[index].RowId) {
var param = {
visitTaskId: this.visitTaskId,
questionId: row.Id,
rowId: row.TableQuestions.Answers[index].RowId
}
const res = await deleteReadingRowAnswer(param)
if (res.IsSuccess) {
await this.getReadingQuestionAndAnswer(this.visitTaskId)
DicomEvent.$emit('getReportInfo', true)
// ''
this.$message.success(this.$t('common:message:deletedSuccessfully'))
}
}
loading.close()
} catch (e) {
loading.close()
console.log(e)
}
},
uploadTpl(lesionType) {
this.upload.title = `导入( ${this.$fd('LesionType', lesionType)} `
this.upload.visible = true
},
async downloadTpl() {
try {
const params = {
visitTaskId: this.visitTaskId
}
await getIVUSTemplate(params)
} catch (e) {
console.log(e)
}
},
async uploadDlgClose() {
await this.getReadingQuestionAndAnswer(this.visitTaskId)
DicomEvent.$emit('getReportInfo', true)
this.upload.visible = false
},
renderHeader(h, { column, $index }) {
const span = document.createElement('span')
span.innerText = column.label
document.body.appendChild(span)
const w = span.getBoundingClientRect().width
column.minWidth = w > 65 ? w + 10 : w > 60 ? w + 15 : w + 20
document.body.removeChild(span)
return h('span', column.label)
}
}
}
</script>
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
.container{
padding: 10px;
.basic-info{
display: flex;
justify-content: space-between;
align-items: center;
h3{
color: #ddd;
padding: 5px 0px;
margin: 0;
}
i{
color: #fff;
font-size: 22px;
font-weight: bold;
cursor: pointer;
}
}
}
.title{
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
}
.add-icon{
display: inline-block;
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
border: 1px solid #938b8b;
margin-bottom: 2px;
cursor: pointer;
}
.add-icon:hover{
background-color: #607d8b;
}
.flex-row{
display: flex;
flex-direction: row;
justify-content: space-between;
background-color: #424242;
}
.lesion_list{
position: relative;
/deep/ .el-form-item__label{
color: #c3c3c3;
text-align: left;
}
/deep/ .el-input .el-input__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
/deep/ .el-textarea__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
/deep/ .el-form-item{
display: flex;
flex-direction: column;
justify-content: flex-start;
}
/deep/ .el-form-item__content{
flex: 1;
}
/deep/ .el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
/deep/ .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}
.el-form-item__content
.el-select{
width: 100%;
}
}
.table-wrapper {
::-webkit-scrollbar {
width: 5px;
height: 8px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
/deep/ .el-table,
.el-table__expanded-cell {
background-color: #000;
color: #fff;
border-color: #444444;
}
/deep/ .el-table th,
.el-table tr {
background-color: #000;
color: #fff;
border-color: #444444;
}
/deep/ .el-table__body tr > td {
background-color: #000 !important;
color: #fff;
border-color: #444444;
}
/deep/ .el-table__body tr:hover > td {
background-color: #858282 !important;
color: #fff;
border-color: #444444;
}
/deep/ .el-table--border th.gutter:last-of-type {
border: none;
}
/deep/ .el-table__fixed-right {
height: 100% !important;
}
// /deep/ .el-table .cell {
// line-height: 20px;
// }
/deep/ .el-table__cell {
padding: 5px 0;
}
/deep/.el-table__fixed-right-patch {
background-color: #000 !important;
border-color: #444444;
}
/deep/.el-table__fixed-body-wrapper tr:hover > td {
background-color: #000 !important;
}
/deep/.el-table--scrollable-x .el-table__body-wrapper {
z-index: 2;
}
}
}
</style>

View File

@ -0,0 +1,297 @@
<template>
<div>
<div
v-if="!!question.GroupName && question.Type==='group'"
>
<h4 style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ language==='en'?question.GroupEnName:question.GroupName }}
</h4>
</div>
<template v-else>
<el-form-item
v-if="(question.ShowQuestion===1 && question.ParentTriggerValueList.includes(String(questionForm[question.ParentId]))) || question.ShowQuestion===0 "
:label="`${question.QuestionName}`"
:prop="question.Id"
:rules="[
{ required: (question.IsRequired === 0 || (question.IsRequired ===1 && question.RelevanceId && (questionForm[question.RelevanceId] === question.RelevanceValue))) && question.Type!=='group' && question.Type!=='summary',
message: ['radio', 'select', 'checkbox'].includes(question.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur', 'change']},
]"
:class="[question.Type==='group' ? 'mb' : (question.Type==='upload' || question.QuestionName.length > 15) ?'uploadWrapper' : '']"
>
<!-- 输入框 -->
<el-input
v-if="question.Type==='input'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
/>
<!-- 多行文本输入框 -->
<el-input
v-else-if="question.Type==='textarea'"
v-model="questionForm[question.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
maxlength="500"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
/>
<!-- 下拉框 -->
<el-select
v-else-if="question.Type==='select'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2 || ((question.TableQuestionType === 2 || question.QuestionGenre === 2) && !!question.DictionaryCode)"
clearable
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.TableQuestionType === 1">
<el-option
v-for="item in organList"
:key="item.Id"
:label="item[question.DataTableColumn]"
:value="item[question.DataTableColumn]"
/>
</template>
<template v-else-if="question.TableQuestionType === 3 || question.QuestionGenre === 3">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:label="item.label"
/>
</template>
<template v-else-if="(question.TableQuestionType === 2 || question.QuestionGenre === 2) && question.DictionaryCode">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<!-- 单选 -->
<el-radio-group
v-else-if="question.Type==='radio'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.DictionaryCode">
<el-radio
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:label="String(item.value)"
>
{{ item.label }}
</el-radio>
</template>
<template v-else-if="question.TypeValue">
<el-radio
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-radio>
</template>
</el-radio-group>
<!-- 复选框 -->
<el-checkbox-group
v-else-if="question.Type==='checkbox'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
>
<el-checkbox
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-checkbox>
</el-checkbox-group>
<!-- 数值 -->
<!-- <template v-else-if="question.Type==='number'">
<el-input-number
v-if="question.ValueType === 0"
v-model="questionForm[question.Id]"
:precision="0"
:disabled="readingTaskState >= 2"
style="width: 100%"
@change="((val)=>{formItemChange(val, question)})"
/>
<el-input-number
v-else-if="question.ValueType === 3"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
style="width: 100%"
@change="((val)=>{formItemChange(val, question)})"
/>
<el-input-number
v-else-if="question.ValueType === 1 || question.ValueType === 2"
v-model="questionForm[question.Id]"
:precision="digitPlaces"
:disabled="readingTaskState >= 2"
style="width: 100%"
@change="((val)=>{formItemChange(val, question)})"
/>
</template> -->
<!-- 数值类型 -->
<el-input
v-else-if="question.Type==='number'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState>=2 "
type="number"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(question.Unit)) }}
</template>
</el-input>
<el-input
v-else-if="question.Type==='calculation'"
v-model="questionForm[question.Id]"
disabled
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(question.Unit)) }}
</template>
</el-input>
</el-form-item>
</template>
<QuestionTableFormItem
v-for="item in question.Childrens"
:key="item.Id"
:question="item"
:question-form="questionForm"
:reading-task-state="readingTaskState"
@setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData"
/>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'QuestionTableFormItem',
props: {
questionForm: {
type: Object,
default() {
return {}
}
},
question: {
type: Object,
default() {
return []
}
},
readingTaskState: {
type: Number,
required: true
}
},
data() {
return {
digitPlaces: 2
}
},
computed: {
...mapGetters(['language'])
},
mounted() {
this.digitPlaces = Number(localStorage.getItem('digitPlaces'))
},
methods: {
formItemChange(v, question) {
if (question.Childrens && question.Childrens.length > 0) {
this.resetChild(question.Childrens)
} else {
if (question.Type === 'number') {
v = this.limitBlur(question.ValueType, v)
}
this.$emit('setFormItemData', { key: question.Id, val: v })
}
},
resetChild(obj) {
obj.forEach(i => {
this.$emit('resetFormItemData', i.Id)
if (i.Childrens && i.Childrens.length > 0) {
this.resetChild(i.Childrens)
}
})
},
limitBlur(valueType, value) {
if (isNaN(parseInt(value))) {
value = null
} else {
if (valueType === 0) {
value = parseInt(value)
} else if (valueType === 3) {
value = parseFloat(value)
} else {
value = this.numberToFixed(value)
}
}
return value
},
numberToFixed(v) {
if (this.digitPlaces > -1) {
return isNaN(parseFloat(v)) ? null : `${parseFloat(v).toFixed(this.digitPlaces)}`
} else {
return v
}
},
resetFormItemData(v) {
this.$emit('resetFormItemData', v)
},
setFormItemData(obj) {
this.$emit('setFormItemData', obj)
},
limitNumbersEvent(e) {
//
let val = e.target.value
if (val.length > 6) val = val.slice(0, 6)
e.target.value = (val.match(/^\d*(\.?\d{0,3})/g)[0]) || null
}
}
}
</script>
<style lang="scss" scoped>
.mb{
margin-bottom: 0px;
}
.disabled{
/deep/ .el-upload--picture-card {
display: none;
}
}
.uploadWrapper{
display: flex;
flex-direction: column !important;
align-items: flex-start;
}
/deep/ .el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
/deep/ .el-input-group__append, .el-input-group__prepend{
padding: 0 10px;
}
/deep/ .el-form-item__content {
width: 100%;
}
/deep/ .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}
</style>

View File

@ -0,0 +1,117 @@
<template>
<el-form
ref="uploadExcel"
class="upload-excel-file"
>
<!-- 文件 -->
<el-form-item :label="$t('trials:consistencyCheck:label:file')">
<div class="upload-container">
<el-upload
class="upload-demo"
action
accept=".xlsx,.xls,.csv"
:before-upload="beforeUpload"
:http-request="handleUploadFile"
:on-preview="handlePreview"
:show-file-list="true"
:limit="1"
:on-exceed="handleExceed"
>
<el-button size="small" type="primary">
{{ $t('trials:consistencyCheck:dialogButton:upload') }}
</el-button>
<span
slot="tip"
style="margin-left:10px;"
class="el-upload__tip"
>
({{ $t('trials:consistencyCheck:message:excelFileOnly') }})
</span>
</el-upload>
</div>
</el-form-item>
</el-form>
</template>
<script>
import { uploadIVUSTemplate } from '@/api/reading'
export default {
props: {
visitTaskId: {
type: String,
required: true
}
},
data() {
return {
}
},
methods: {
beforeUpload(file) {
//
if (this.checkFileSuffix(file.name)) {
return true
} else {
// Must be xls or xlsx format
this.$alert(this.$t('trials:consistencyCheck:message:xlsx'))
return false
}
},
async handleUploadFile(param) {
const loading = this.$loading({ fullscreen: true })
try {
var data = new FormData()
data.append('file', param.file)
data.append('visitTaskId', this.visitTaskId)
await uploadIVUSTemplate(data)
this.$emit('close')
this.$message.success('导入成功!')
loading.close()
} catch (e) {
loading.close()
console.log(e)
}
},
handlePreview(file) {
if (file.fullPath) {
window.open(file.fullPath, '_blank')
}
},
handleExceed(files, fileList) {
// Upload is currently limited to 1 file
this.$message.warning(this.$t('trials:consistencyCheck:message:onlyOneFile'))
},
checkFileSuffix(fileName) {
var typeArr = ['xls', 'xlsx', 'csv']
var extendName = fileName.substring(fileName.lastIndexOf('.') + 1).toLocaleLowerCase()
if (typeArr.indexOf(extendName) !== -1) {
return true
} else {
return false
}
}
}
}
</script>
<style lang="scss">
.upload-excel-file{
.upload-container .el-upload--text {
border: none;
width: 80px;
height: 40px;
}
.upload-container .el-input--small {
margin-bottom: 5px;
}
.upload-container .el-icon-circle-check {
color: #428bca;
font-size: 13px;
}
.account_item_clear{
.el-tag__close{
display: none !important;
}
}
}
</style>

View File

@ -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()
},

View File

@ -1,7 +1,7 @@
<template>
<div class="measurement-wrapper" :style="{'height':height+10+'px'}">
<div class="measurement-wrapper">
<div class="container" :style="{'height':height+'px'}">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
@ -262,7 +262,6 @@ export default {
},
data() {
return {
height: window.innerHeight - 140,
questions: [],
activeName: '',
activeItem: {
@ -359,8 +358,6 @@ export default {
}
callback(true)
})
window.addEventListener('resize', this.setHeight)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
@ -573,9 +570,6 @@ export default {
} catch (e) { console.log(e) }
})
},
setHeight() {
this.height = window.innerHeight - 140
},
getQuestionAnswer(questions, questionMark, answers) {
var idx = questions.findIndex(i => i.QuestionMark === questionMark)
if (idx > -1) {
@ -1347,9 +1341,8 @@ export default {
</script>
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
// overflow: hidden;
.container{
padding: 10px;
.basic-info{

View File

@ -21,7 +21,7 @@
</div>
<div class="right-wrapper">
<div class="right-content">
<iframe v-if="selected.filePath" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}`" width="100%" height="100%" frameborder="0" />
<iframe v-if="selected.filePath" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}&COMPANY=${COMPANY}`" width="100%" height="100%" frameborder="0" />
</div>
</div>
</div>
@ -44,7 +44,8 @@ export default {
},
fileList: [],
loading: false,
currentUser: zzSessionStorage.getItem('userName')
currentUser: zzSessionStorage.getItem('userName'),
COMPANY:process.env.VUE_APP_COMPANY_NAME
}
},
mounted() {

View File

@ -0,0 +1,887 @@
<template>
<div class="measurement-wrapper">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
<span style="margin-left:5px;">{{ taskBlindName }}</span>
</h3>
<div v-if="readingTaskState < 2">
<el-tooltip class="item" effect="dark" :content="$t('trials:dicomReading:message:confirmReset')" placement="bottom">
<i
class="el-icon-refresh-left"
@click="resetMeasuredData"
/>
</el-tooltip>
</div>
</div>
<!-- 影像质量问题 -->
<div class="lesions">
<Questions
ref="ecrf"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="1"
/>
</div>
<!-- 测量问题 -->
<template v-if="questions.length > 0">
<div v-for="(qs,index) in questions" :key="index" v-loading="loading" class="lesions lesions_wrapper" style="margin-bottom: 10px">
<h4 v-if="qs.Type === 'group'" style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ language==='en'?qs.GroupEnName:qs.GroupName }}
</h4>
<div class="lesion_list">
<el-form
v-if="questions.length > 0"
:ref="`questions${index}`"
size="small"
:model="questionForm"
>
<div class="table-wrapper">
<div v-for="item in qs.Childrens" :key="item.Id">
<div v-if="item.Type === 'basicTable'" class="flex-row" style="margin:3px 0;">
<div class="title">{{ item.QuestionName }}</div>
<div v-if="(item.LesionType === 112 || item.LesionType === 111) && readingTaskState < 2">
<div class="add-icon" @click.prevent="downloadTpl(item.LesionType)">
<i class="el-icon-download" />
</div>
<div class="add-icon" style="margin: 0 5px;" @click.prevent="uploadTpl(item.LesionType)">
<i class="el-icon-upload2" />
</div>
<div class="add-icon" @click.prevent="handleAddOrEdit('add',item)">
<i class="el-icon-plus" />
</div>
</div>
</div>
<el-table
v-if="item.Type === 'basicTable' && item.TableQuestions"
:ref="item.Id"
:data="item.TableQuestions.Answers"
max-height="600"
>
<!-- <el-table-column :label="$t('CustomizeQuestionFormItem:label:OrderMark')" width="60px" show-overflow-tooltip>
<template slot-scope="scope">
{{ getLesionName(item.OrderMark,scope.row.RowIndex) }}
</template>
</el-table-column> -->
<!-- <el-table-column
type="index"
width="40px"
/> -->
<el-table-column
v-for="q of item.TableQuestions.Questions"
:key="q.Id"
:prop="q.Id"
:label="q.QuestionName"
show-overflow-tooltip
:render-header="renderHeader"
>
<template slot-scope="scope">
<span v-if="q.Unit > 0 && !isNaN(parseFloat(scope.row[q.Id]))">
{{ `${scope.row[q.Id]} ${$fd('ValueUnit', parseInt(q.Unit))}` }}
</span>
<span v-else-if="q.DictionaryCode">
{{ `${$fd(q.DictionaryCode, parseInt(scope.row[q.Id]))}` }}
</span>
<span v-else>
{{ `${scope.row[q.Id]}` }}
</span>
</template>
</el-table-column>
<el-table-column
v-if="readingTaskState < 2"
:label="$t('common:action:action')"
width="90px"
fixed="right"
>
<template slot-scope="scope">
<el-button type="text" size="mini" @click="handleAddOrEdit('edit',item, scope.$index)">
{{ $t('common:button:edit') }}
</el-button>
<el-button v-if="item.LesionType === 112 || item.LesionType === 111" type="text" size="mini" @click="handleDelete(item, scope.$index)">
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
<template v-else>
<!-- 数值 -->
<el-form-item
v-if="item.ShowQuestion!==2"
:key="item.Id"
:label="`${item.QuestionName}`"
:prop="item.Id"
:rules="[
{ required: (item.IsRequired === 0 || (item.IsRequired ===1 && item.RelevanceId && (item.RelevanceValue.includes(questionForm[item.RelevanceId])))) && item.Type!=='group' && item.Type!=='summary',
message:['radio', 'select', 'checkbox'].includes(item.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur','change']},
]"
>
<!-- 数值类型 -->
<template v-if="item.Type==='textarea'">
<el-input
v-model="questionForm[qs.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:disabled="!isCurrentTask || readingTaskState>=2"
@change="((val)=>{formItemChange(val, qs)})"
/>
</template>
</el-form-item>
</template>
</div>
<div
v-if="isCurrentTask && readingTaskState<2 && qs.GroupClassify === 5"
class="base-dialog-footer"
style="text-align:right;margin-top:10px;"
>
<!-- 保存 -->
<el-button
type="primary"
size="mini"
:disabled="!formChanged"
@click="handleSave(index)"
>
{{ $t('common:button:save') }}
</el-button>
</div>
</div>
</el-form>
</div>
</div>
</template>
<!-- 计算值 -->
<Questions
ref="ecrf2"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="4"
style="margin-top:20px"
/>
<!-- 评估结果 -->
<Questions
ref="ecrf3"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="5"
/>
<el-dialog
v-if="addOrEdit.visible"
:visible.sync="addOrEdit.visible"
:close-on-click-modal="false"
:title="addOrEdit.title"
width="500px"
>
<el-form
ref="tableQsForm"
v-loading="loading"
:model="qsForm"
size="small"
>
<QuestionTableFormItem
v-for="item in qsList"
:key="item.Id"
:question="item"
:question-form="qsForm"
:reading-task-state="readingTaskState"
@setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData"
/>
<el-form-item style="text-align: right">
<el-button
size="small"
@click="addOrEdit.visible = false"
>
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" class="my_upload_btn" @click="saveFormData">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</el-form>
</el-dialog>
<!-- 导入 -->
<el-dialog
v-if="upload.visible"
:visible.sync="upload.visible"
:close-on-click-modal="false"
:title="upload.title"
width="500px"
>
<UploadExcel
:visit-task-id="visitTaskId"
:lesion-type="upload.lesionType"
@close="uploadDlgClose"
/>
</el-dialog>
</div>
</div>
</template>
<script>
import { saveTaskQuestion, submitTableQuestion, deleteReadingRowAnswer } from '@/api/trials'
import { resetReadingTask, getOCTFCTTemplate, getOCTLipidAngleTemplate } from '@/api/reading'
import DicomEvent from './../DicomEvent'
import store from '@/store'
import { mapGetters } from 'vuex'
import Questions from './../Questions'
import QuestionTableFormItem from './QuestionTableFormItem'
import UploadExcel from './UploadExcel'
export default {
name: 'MeasurementList',
components: {
Questions,
QuestionTableFormItem,
UploadExcel
},
props: {
isShow: {
type: Boolean,
required: true
},
isReadingShowSubjectInfo: {
type: Boolean,
required: true
},
questionFormChangeState: {
type: Boolean,
default() {
return false
}
},
questionFormChangeNum: {
type: Number,
default() {
return 0
}
}
},
data() {
return {
questions: [],
visitTaskId: '',
isCurrentTask: false,
loading: false,
readingTaskState: 2,
isBaseLineTask: false,
taskBlindName: '',
tableQuestions: [],
CriterionType: null,
subjectCode: '',
questionForm: {},
formChanged: false,
digitPlaces: 2,
addOrEdit: { visible: false, title: '', lesionType: null },
upload: { visible: false, title: '', lesionType: null },
qsList: [],
answersList: [],
qsForm: {},
m1Id: '',
m2Id: '',
m3Id: '',
avgId: ''
}
},
computed: {
...mapGetters(['visitTaskList', 'language', 'lastCanvasTaskId', 'currentReadingTaskState'])
},
watch: {
lastCanvasTaskId: {
immediate: true,
handler(val) {
if (val) {
this.initList()
}
}
},
currentReadingTaskState: {
immediate: true,
handler(val) {
if (val) {
this.readingTaskState = val
}
}
}
},
mounted() {
// this.subjectCode = this.$router.currentRoute.query.subjectCode
this.subjectCode = localStorage.getItem('subjectCode')
this.digitPlaces = Number(localStorage.getItem('digitPlaces'))
window.addEventListener('message', this.receiveMsg)
this.CriterionType = parseInt(localStorage.getItem('CriterionType'))
DicomEvent.$on('setCollapseActive', measureData => {
this.setCollapseActive(measureData)
console.log('setCollapseActive')
})
DicomEvent.$on('getAllUnSaveLesions', (callback) => {
callback([])
})
DicomEvent.$on('refreshQuestions', _ => {
this.refreshQuestions()
})
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
DicomEvent.$off('getUnSaveTarget')
DicomEvent.$off('refreshQuestions')
},
methods: {
async initList() {
var i = this.visitTaskList.findIndex(i => i.VisitTaskId === this.lastCanvasTaskId)
if (i > -1) {
this.visitTaskId = this.visitTaskList[i].VisitTaskId
this.taskBlindName = this.visitTaskList[i].TaskBlindName
this.readingTaskState = this.visitTaskList[i].ReadingTaskState
this.isBaseLineTask = this.visitTaskList[i].IsBaseLineTask
this.isCurrentTask = this.visitTaskList[i].IsCurrentTask
if (!this.visitTaskList[i].IsInit) {
var loading = this.$loading({ fullscreen: true })
var triald = this.trialId = this.$router.currentRoute.query.trialId
if (!this.visitTaskList[i].studyListInit) {
await store.dispatch('reading/getStudyInfo', { trialId: triald, subjectVisitId: this.visitTaskList[i].VisitId, visitTaskId: this.visitTaskList[i].VisitTaskId, taskBlindName: this.visitTaskList[i].TaskBlindName })
}
if (!this.visitTaskList[i].readingQuestionsInit) {
await store.dispatch('reading/getReadingQuestionAndAnswer', { trialId: triald, visitTaskId: this.visitTaskList[i].VisitTaskId })
}
if (!this.visitTaskList[i].questionsInit) {
await store.dispatch('reading/getDicomReadingQuestionAnswer', { trialId: triald, visitTaskId: this.visitTaskList[i].VisitTaskId })
}
await store.dispatch('reading/setStatus', { visitTaskId: this.visitTaskList[i].VisitTaskId })
loading.close()
}
this.questions = this.visitTaskList[i].ReadingQuestions
this.setQuestionForm(this.questions)
this.$nextTick(() => {
this.$refs['ecrf'].getQuestions(this.visitTaskId)
this.$refs['ecrf2'].getQuestions(this.visitTaskId)
this.$refs['ecrf3'].getQuestions(this.visitTaskId)
})
}
},
setQuestionForm(questions) {
const type = ['number', 'radio', 'select', 'input', 'textarea', 'calculation']
questions.forEach(item => {
if (type.includes(item.Type)) {
const answer = item.Type === 'number' || item.Type === 'calculation' ? isNaN(parseFloat(item.Answer)) ? null : parseFloat(item.Answer) : item.Answer
this.$set(this.questionForm, item.Id, answer)
}
if (item.Childrens.length > 0) {
this.setQuestionForm(item.Childrens)
}
})
},
async resetQuestions(obj) {
this.setQuestions(this.questions, obj)
await store.dispatch('reading/setReadingQuestionAndAnswer', { questions: this.questions, visitTaskId: this.visitTaskId })
this.getTableQuestions()
},
setQuestions(questions, obj) {
questions.forEach(item => {
if ((item.Type === 'table' || item.Type === 'basicTable') && item.Id === obj.questionId) {
var idx = item.TableQuestions.Answers.findIndex(i => i.RowIndex === obj.rowIndex)
item.TableQuestions.Answers[idx].saveTypeEnum = obj.saveTypeEnum
item.TableQuestions.Answers[idx].area1 = obj.area1
item.TableQuestions.Answers[idx].area2 = obj.area2
item.TableQuestions.Answers[idx].diff = obj.diff
for (const i in obj.anwsers) {
item.TableQuestions.Answers[idx][i] = String(obj.anwsers[i])
}
}
if (item.Childrens.length > 0) {
this.setQuestions(item.Childrens, obj)
}
})
},
getQuestions(questions) {
questions.forEach(item => {
if ((item.Type === 'table' || item.Type === 'basicTable') && item.TableQuestions && item.TableQuestions.Answers.length > 0) {
item.TableQuestions.Answers.forEach(answerObj => {
if (answerObj.RowId) {
var diffVal = this.getQuestionAnswer(item.TableQuestions.Questions, 1003, answerObj)
this.$set(answerObj, 'saveTypeEnum', isNaN(parseFloat(diffVal)) ? 1 : 2)
} else {
this.$set(answerObj, 'saveTypeEnum', 0)
}
})
}
if (item.Childrens.length > 0) {
this.getQuestions(item.Childrens)
}
})
return questions
},
getReadingQuestionAndAnswer() {
return new Promise(async resolve => {
try {
const loading = this.$loading({ fullscreen: true })
await store.dispatch('reading/refreshReadingQuestionAndAnswer', { trialId: this.$router.currentRoute.query.trialId, visitTaskId: this.visitTaskId }).then(() => {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx > -1) {
if (this.visitTaskList[idx].ReadingQuestions.length > 0) {
this.questions = this.visitTaskList[idx].ReadingQuestions
this.setQuestionForm(this.questions)
}
this.readingTaskState = this.visitTaskList[idx].ReadingTaskState
this.isBaseLineTask = this.visitTaskList[idx].IsBaseLineTask
this.isCurrentTask = this.visitTaskList[idx].IsCurrentTask
}
})
loading.close()
resolve()
} catch (e) { console.log(e) }
})
},
getQuestionAnswer(questions, questionMark, answers) {
var idx = questions.findIndex(i => i.QuestionMark === questionMark)
if (idx > -1) {
var questionId = questions[idx].Id
return answers[questionId]
} else {
return ''
}
},
isCanActiveTool(toolName) {
return { isCanActiveTool: true, reason: '' }
},
getMaxRowIndex(answerList) {
if (answerList.length > 0) {
var arr = []
answerList.forEach(item => {
var rowIndex = item.RowIndex
arr.push(parseInt(rowIndex))
})
var max = Math.max.apply(null, arr)
return max
} else {
return 0
}
},
getLesionName(orderMark, rowIndex) {
var lessionName = ''
var rowIndexArr = rowIndex.split('.')
var x = parseInt(rowIndexArr[0])
var y = parseInt(rowIndexArr[1])
if (y > 0) {
y = String.fromCharCode(parseInt(rowIndexArr[1]) - 1 + 65 + 32)
lessionName = `${orderMark}${String(x).padStart(2, '0')}${y}`
} else {
lessionName = `${orderMark}${String(x).padStart(2, '0')}`
}
return lessionName
},
modifyMeasuredData(measureObj) {
},
//
setMeasuredData(measureData) {
},
formItemChange() {
this.formChanged = true
},
limitBlur(qId, valueType) {
const value = this.questionForm[qId]
if (isNaN(parseInt(value))) return
if (valueType === 0) {
this.$set(this.questionForm, qId, parseInt(value))
} else if (valueType === 3) {
this.$set(this.questionForm, qId, parseFloat(value))
} else {
this.$set(this.questionForm, qId, this.numberToFixed(value))
}
},
numberToFixed(v) {
if (this.digitPlaces > -1) {
return isNaN(parseFloat(v)) ? null : `${parseFloat(v).toFixed(this.digitPlaces)}`
} else {
return v
}
},
async handleSave(index) {
const refName = `questions${index}`
const valid = await this.$refs[refName][0].validate()
if (!valid) return
const loading = this.$loading({ fullscreen: true })
var answers = []
for (const k in this.questionForm) {
answers.push({ id: k, answer: this.questionForm[k] })
}
var params = {
visitTaskId: this.visitTaskId,
answers: answers
}
try {
await saveTaskQuestion(8, params)
this.$message.success(this.$t('common:message:savedSuccessfully'))
loading.close()
DicomEvent.$emit('getReportInfo', true)
this.refreshQuestions()
this.formChanged = false
} catch (e) {
console.log(e)
loading.close()
}
},
getECRFQuestions(obj) {
this.$refs['ecrf'].getQuestions(obj.visitTaskId)
this.$refs['ecrf2'].getQuestions(obj.visitTaskId)
this.$refs['ecrf3'].getQuestions(obj.visitTaskId)
},
async resetMeasuredData() {
const confirm = await this.$confirm(
this.$t('trials:dicomReading:message:confirmReset1'),
this.$t('trials:dicomReading:message:confirmReset2'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
const loading = this.$loading({ fullscreen: true })
try {
const res = await resetReadingTask({ visitTaskId: this.visitTaskId })
this.loading = false
if (res.IsSuccess) {
await this.getReadingQuestionAndAnswer(this.visitTaskId)
const triald = this.$router.currentRoute.query.trialId
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: triald, visitTaskId: this.visitTaskId })
this.$refs['ecrf'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf2'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf3'].getQuestions(this.visitTaskId, true)
DicomEvent.$emit('getReportInfo', true)
}
loading.close()
} catch (e) {
loading.close()
console.log(e)
}
},
async refreshQuestions() {
const triald = this.$router.currentRoute.query.trialId
await this.getReadingQuestionAndAnswer(this.visitTaskId)
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: triald, visitTaskId: this.visitTaskId })
this.$refs['ecrf'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf2'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf3'].getQuestions(this.visitTaskId, true)
},
receiveMsg(event) {
},
resetFormItemData(v) {
this.qsForm[v] = null
},
setFormItemData(obj) {
this.qsForm[obj.key] = obj.val
if (obj.key === this.m1Id || obj.key === this.m2Id || obj.key === this.m3Id) {
const m1 = this.qsForm[this.m1Id]
const m2 = this.qsForm[this.m2Id]
const m3 = this.qsForm[this.m3Id]
if (isNaN(parseFloat(m1)) || isNaN(parseFloat(m2)) || isNaN(parseFloat(m3))) {
this.$set(this.qsForm, this.avgId, null)
} else {
const avg = (parseFloat(m1) + parseFloat(m2) + parseFloat(m3)) / 3
this.$set(this.qsForm, this.avgId, this.numberToFixed(avg))
}
}
},
handleAddOrEdit(type, row, index) {
this.addOrEdit.title = row.QuestionName
this.qsList = row.TableQuestions.Questions
this.qsForm = {}
this.qsForm.questionId = row.Id
this.qsForm.rowId = type === 'add' ? null : row.TableQuestions.Answers && row.TableQuestions.Answers[index] ? row.TableQuestions.Answers[index].RowId : null
if (type === 'add') {
var maxIndex = this.getMaxRowIndex(row.TableQuestions.Answers)
this.qsForm.rowIndex = `${maxIndex + 1}.00`
} else {
this.qsForm.rowIndex = row.TableQuestions.Answers && row.TableQuestions.Answers[index] ? row.TableQuestions.Answers[index].RowIndex : null
}
row.TableQuestions.Questions.map(i => {
if (i.QuestionMark === 1011) {
this.m1Id = i.Id
}
if (i.QuestionMark === 1012) {
this.m2Id = i.Id
}
if (i.QuestionMark === 1013) {
this.m3Id = i.Id
}
if (i.QuestionMark === 1015) {
this.avgId = i.Id
}
let v = null
if (type === 'add') {
v = i.Type === 'number' ? undefined : null
} else {
v = row.TableQuestions.Answers && row.TableQuestions.Answers[index] ? row.TableQuestions.Answers[index][i.Id] : null
}
this.$set(this.qsForm, i.Id, v)
})
this.addOrEdit.visible = true
this.addOrEdit.lesionType = row.LesionType
},
async saveFormData() {
const valid = await this.$refs.tableQsForm.validate()
if (!valid) return
const loading = this.$loading({ fullscreen: true })
try {
var answers = []
var reg = new RegExp(/^[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}$/)
for (const k in this.qsForm) {
if (reg.test(k)) {
if (answers.findIndex(i => i.tableQuestionId === k) === -1) {
answers.push({ tableQuestionId: k, answer: this.qsForm[k] })
}
}
}
var params = {
questionId: this.qsForm.questionId,
rowId: this.qsForm.rowId,
rowIndex: this.qsForm.rowIndex,
visitTaskId: this.visitTaskId,
trialId: this.trialId,
answerList: answers
}
if (this.addOrEdit.lesionType === 101) {
params.computationTrigger = 9
}
const res = await submitTableQuestion(params)
if (res.IsSuccess) {
// this.refreshQuestions()
await this.getReadingQuestionAndAnswer(this.visitTaskId)
this.$refs['ecrf'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf2'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf3'].getQuestions(this.visitTaskId, true)
DicomEvent.$emit('getReportInfo', true)
//
this.addOrEdit.visible = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
loading.close()
} catch (e) {
console.log(e)
loading.close()
}
},
async handleDelete(row, index) {
const loading = this.$loading({ fullscreen: true })
try {
const confirm = await this.$confirm(
this.$t('trials:reading:warnning:msg48'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
if (row.TableQuestions.Answers[index].RowId) {
var param = {
visitTaskId: this.visitTaskId,
questionId: row.Id,
rowId: row.TableQuestions.Answers[index].RowId
}
const res = await deleteReadingRowAnswer(param)
if (res.IsSuccess) {
await this.getReadingQuestionAndAnswer(this.visitTaskId)
DicomEvent.$emit('getReportInfo', true)
// ''
this.$message.success(this.$t('common:message:deletedSuccessfully'))
}
}
loading.close()
} catch (e) {
loading.close()
console.log(e)
}
},
uploadTpl(lesionType) {
this.upload.lesionType = lesionType
this.upload.title = `导入( ${this.$fd('LesionType', lesionType)} `
this.upload.visible = true
},
async downloadTpl(lesionType) {
try {
const params = {
visitTaskId: this.visitTaskId
}
if (lesionType === 112) {
await getOCTFCTTemplate(params)
} else {
await getOCTLipidAngleTemplate(params)
}
} catch (e) {
console.log(e)
}
},
async uploadDlgClose() {
await this.getReadingQuestionAndAnswer(this.visitTaskId)
DicomEvent.$emit('getReportInfo', true)
this.upload.visible = false
},
renderHeader(h, { column, $index }) {
const span = document.createElement('span')
span.innerText = column.label
document.body.appendChild(span)
const w = span.getBoundingClientRect().width
column.minWidth = w > 65 ? w + 10 : w > 60 ? w + 15 : w + 30
document.body.removeChild(span)
return h('span', column.label)
}
}
}
</script>
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
// overflow: hidden;
.container{
padding: 10px;
.basic-info{
display: flex;
justify-content: space-between;
align-items: center;
h3{
color: #ddd;
padding: 5px 0px;
margin: 0;
}
i{
color: #fff;
font-size: 22px;
font-weight: bold;
cursor: pointer;
}
}
}
.title{
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
}
.add-icon{
display: inline-block;
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
border: 1px solid #938b8b;
margin-bottom: 2px;
cursor: pointer;
}
.add-icon:hover{
background-color: #607d8b;
}
.flex-row{
display: flex;
flex-direction: row;
justify-content: space-between;
background-color: #424242;
}
.lesion_list{
position: relative;
/deep/ .el-form-item__label{
color: #c3c3c3;
text-align: left;
}
/deep/ .el-input .el-input__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
/deep/ .el-textarea__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
/deep/ .el-form-item{
display: flex;
flex-direction: column;
justify-content: flex-start;
}
/deep/ .el-form-item__content{
flex: 1;
}
/deep/ .el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
/deep/ .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}
.el-form-item__content
.el-select{
width: 100%;
}
}
.table-wrapper {
::-webkit-scrollbar {
width: 5px;
height: 8px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
/deep/ .el-table,
.el-table__expanded-cell {
background-color: #000;
color: #fff;
border-color: #444444;
}
/deep/ .el-table th,
.el-table tr {
background-color: #000;
color: #fff;
border-color: #444444;
}
/deep/ .el-table__body tr > td {
background-color: #000 !important;
color: #fff;
border-color: #444444;
}
/deep/ .el-table__body tr:hover > td {
background-color: #858282 !important;
color: #fff;
border-color: #444444;
}
/deep/ .el-table--border th.gutter:last-of-type {
border: none;
}
/deep/ .el-table__fixed-right {
height: 100% !important;
}
// /deep/ .el-table .cell {
// line-height: 20px;
// }
/deep/ .el-table__cell {
padding: 5px 0;
}
/deep/.el-table__fixed-right-patch {
background-color: #000 !important;
border-color: #444444;
}
/deep/.el-table__fixed-body-wrapper tr:hover > td {
background-color: #000 !important;
}
/deep/.el-table--scrollable-x .el-table__body-wrapper {
z-index: 2;
}
}
}
</style>

View File

@ -0,0 +1,297 @@
<template>
<div>
<div
v-if="!!question.GroupName && question.Type==='group'"
>
<h4 style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ language==='en'?question.GroupEnName:question.GroupName }}
</h4>
</div>
<template v-else>
<el-form-item
v-if="(question.ShowQuestion===1 && question.ParentTriggerValueList.includes(String(questionForm[question.ParentId]))) || question.ShowQuestion===0 "
:label="`${question.QuestionName}`"
:prop="question.Id"
:rules="[
{ required: (question.IsRequired === 0 || (question.IsRequired ===1 && question.RelevanceId && (questionForm[question.RelevanceId] === question.RelevanceValue))) && question.Type!=='group' && question.Type!=='summary',
message: ['radio', 'select', 'checkbox'].includes(question.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur', 'change']},
]"
:class="[question.Type==='group' ? 'mb' : (question.Type==='upload' || question.QuestionName.length > 15) ?'uploadWrapper' : '']"
>
<!-- 输入框 -->
<el-input
v-if="question.Type==='input'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
/>
<!-- 多行文本输入框 -->
<el-input
v-else-if="question.Type==='textarea'"
v-model="questionForm[question.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
maxlength="500"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
/>
<!-- 下拉框 -->
<el-select
v-else-if="question.Type==='select'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2 || ((question.TableQuestionType === 2 || question.QuestionGenre === 2) && !!question.DictionaryCode)"
clearable
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.TableQuestionType === 1">
<el-option
v-for="item in organList"
:key="item.Id"
:label="item[question.DataTableColumn]"
:value="item[question.DataTableColumn]"
/>
</template>
<template v-else-if="question.TableQuestionType === 3 || question.QuestionGenre === 3">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:label="item.label"
/>
</template>
<template v-else-if="(question.TableQuestionType === 2 || question.QuestionGenre === 2) && question.DictionaryCode">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<!-- 单选 -->
<el-radio-group
v-else-if="question.Type==='radio'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.DictionaryCode">
<el-radio
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:label="String(item.value)"
>
{{ item.label }}
</el-radio>
</template>
<template v-else-if="question.TypeValue">
<el-radio
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-radio>
</template>
</el-radio-group>
<!-- 复选框 -->
<el-checkbox-group
v-else-if="question.Type==='checkbox'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
>
<el-checkbox
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-checkbox>
</el-checkbox-group>
<!-- 数值 -->
<!-- <template v-else-if="question.Type==='number'">
<el-input-number
v-if="question.ValueType === 0"
v-model="questionForm[question.Id]"
:precision="0"
:disabled="readingTaskState >= 2"
style="width: 100%"
@change="((val)=>{formItemChange(val, question)})"
/>
<el-input-number
v-else-if="question.ValueType === 3"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
style="width: 100%"
@change="((val)=>{formItemChange(val, question)})"
/>
<el-input-number
v-else-if="question.ValueType === 1 || question.ValueType === 2"
v-model="questionForm[question.Id]"
:precision="digitPlaces"
:disabled="readingTaskState >= 2"
style="width: 100%"
@change="((val)=>{formItemChange(val, question)})"
/>
</template> -->
<!-- 数值类型 -->
<el-input
v-else-if="question.Type==='number'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState>=2 "
type="number"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(question.Unit)) }}
</template>
</el-input>
<el-input
v-else-if="question.Type==='calculation'"
v-model="questionForm[question.Id]"
disabled
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(question.Unit)) }}
</template>
</el-input>
</el-form-item>
</template>
<QuestionTableFormItem
v-for="item in question.Childrens"
:key="item.Id"
:question="item"
:question-form="questionForm"
:reading-task-state="readingTaskState"
@setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData"
/>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'QuestionTableFormItem',
props: {
questionForm: {
type: Object,
default() {
return {}
}
},
question: {
type: Object,
default() {
return []
}
},
readingTaskState: {
type: Number,
required: true
}
},
data() {
return {
digitPlaces: 2
}
},
computed: {
...mapGetters(['language'])
},
mounted() {
this.digitPlaces = Number(localStorage.getItem('digitPlaces'))
},
methods: {
formItemChange(v, question) {
if (question.Childrens && question.Childrens.length > 0) {
this.resetChild(question.Childrens)
} else {
if (question.Type === 'number') {
v = this.limitBlur(question.ValueType, v)
}
this.$emit('setFormItemData', { key: question.Id, val: v })
}
},
resetChild(obj) {
obj.forEach(i => {
this.$emit('resetFormItemData', i.Id)
if (i.Childrens && i.Childrens.length > 0) {
this.resetChild(i.Childrens)
}
})
},
limitBlur(valueType, value) {
if (isNaN(parseInt(value))) {
value = null
} else {
if (valueType === 0) {
value = parseInt(value)
} else if (valueType === 3) {
value = parseFloat(value)
} else {
value = this.numberToFixed(value)
}
}
return value
},
numberToFixed(v) {
if (this.digitPlaces > -1) {
return isNaN(parseFloat(v)) ? null : `${parseFloat(v).toFixed(this.digitPlaces)}`
} else {
return v
}
},
resetFormItemData(v) {
this.$emit('resetFormItemData', v)
},
setFormItemData(obj) {
this.$emit('setFormItemData', obj)
},
limitNumbersEvent(e) {
//
let val = e.target.value
if (val.length > 6) val = val.slice(0, 6)
e.target.value = (val.match(/^\d*(\.?\d{0,3})/g)[0]) || null
}
}
}
</script>
<style lang="scss" scoped>
.mb{
margin-bottom: 0px;
}
.disabled{
/deep/ .el-upload--picture-card {
display: none;
}
}
.uploadWrapper{
display: flex;
flex-direction: column !important;
align-items: flex-start;
}
/deep/ .el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
/deep/ .el-input-group__append, .el-input-group__prepend{
padding: 0 10px;
}
/deep/ .el-form-item__content {
width: 100%;
}
/deep/ .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}
</style>

View File

@ -0,0 +1,125 @@
<template>
<el-form
ref="uploadExcel"
class="upload-excel-file"
>
<!-- 文件 -->
<el-form-item :label="$t('trials:consistencyCheck:label:file')">
<div class="upload-container">
<el-upload
class="upload-demo"
action
accept=".xlsx,.xls,.csv"
:before-upload="beforeUpload"
:http-request="handleUploadFile"
:on-preview="handlePreview"
:show-file-list="true"
:limit="1"
:on-exceed="handleExceed"
>
<el-button size="small" type="primary">
{{ $t('trials:consistencyCheck:dialogButton:upload') }}
</el-button>
<span
slot="tip"
style="margin-left:10px;"
class="el-upload__tip"
>
({{ $t('trials:consistencyCheck:message:excelFileOnly') }})
</span>
</el-upload>
</div>
</el-form-item>
</el-form>
</template>
<script>
import { uploadOCTFCTTemplate, uploadOCTLipidAngleTemplate } from '@/api/reading'
export default {
props: {
visitTaskId: {
type: String,
required: true
},
lesionType: {
type: Number,
required: true
}
},
data() {
return {
}
},
methods: {
beforeUpload(file) {
//
if (this.checkFileSuffix(file.name)) {
return true
} else {
// Must be xls or xlsx format
this.$alert(this.$t('trials:consistencyCheck:message:xlsx'))
return false
}
},
async handleUploadFile(param) {
const loading = this.$loading({ fullscreen: true })
try {
var data = new FormData()
data.append('file', param.file)
data.append('visitTaskId', this.visitTaskId)
if (this.lesionType === 112) {
await uploadOCTFCTTemplate(data)
} else {
await uploadOCTLipidAngleTemplate(data)
}
this.$emit('close')
this.$message.success('导入成功!')
loading.close()
} catch (e) {
loading.close()
console.log(e)
}
},
handlePreview(file) {
if (file.fullPath) {
window.open(file.fullPath, '_blank')
}
},
handleExceed(files, fileList) {
// Upload is currently limited to 1 file
this.$message.warning(this.$t('trials:consistencyCheck:message:onlyOneFile'))
},
checkFileSuffix(fileName) {
var typeArr = ['xls', 'xlsx', 'csv']
var extendName = fileName.substring(fileName.lastIndexOf('.') + 1).toLocaleLowerCase()
if (typeArr.indexOf(extendName) !== -1) {
return true
} else {
return false
}
}
}
}
</script>
<style lang="scss">
.upload-excel-file{
.upload-container .el-upload--text {
border: none;
width: 80px;
height: 40px;
}
.upload-container .el-input--small {
margin-bottom: 5px;
}
.upload-container .el-icon-circle-check {
color: #428bca;
font-size: 13px;
}
.account_item_clear{
.el-tag__close{
display: none !important;
}
}
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="measurement-wrapper" :style="{'height':height+10+'px'}">
<div class="measurement-wrapper">
<div class="container" :style="{'height':height+'px'}">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
@ -147,7 +147,6 @@ export default {
},
data() {
return {
height: window.innerHeight - 140,
questions: [],
activeName: '',
activeItem: {
@ -212,8 +211,6 @@ export default {
callback(list)
console.log('getAllUnSaveLesions')
})
window.addEventListener('resize', this.setHeight)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
@ -802,6 +799,7 @@ export default {
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
// overflow: hidden;

View File

@ -24,7 +24,7 @@
{ required: (question.IsRequired === 0 || (question.IsRequired ===1 && question.RelevanceId && (questionForm[question.RelevanceId] === question.RelevanceValue))) && question.Type!=='group' && question.Type!=='summary',
message: ['radio', 'select', 'checkbox'].includes(question.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur', 'change']},
]"
:class="[question.Type==='group'?'mb':question.Type==='upload'?'uploadWrapper':'']"
:class="[question.Type==='group' ? 'mb' : (question.Type==='upload' || question.QuestionName.length > 15) ?'uploadWrapper' : '']"
>
<template v-if="question.QuestionType === 60 || question.QuestionType === 61">
<div style="display: flex;flex-direction: row;justify-content: flex-start;align-items: center;">
@ -211,16 +211,40 @@
</el-checkbox>
</el-checkbox-group>
<!-- 数值 -->
<el-input-number
<!-- <el-input-number
v-else-if="question.Type==='number'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2 || isFirstChangeTask"
:precision="2"
/> -->
<template v-else-if="question.Type==='number'">
<el-input-number
v-if="question.ValueType === 0"
v-model="questionForm[question.Id]"
:precision="0"
:disabled="readingTaskState >= 2 || isFirstChangeTask"
/>
<el-input-number
v-else-if="question.ValueType === 3"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2 || isFirstChangeTask"
/>
<el-input-number
v-else-if="question.ValueType === 1 || question.ValueType === 2"
v-model="questionForm[question.Id]"
:precision="digitPlaces"
:disabled="readingTaskState >= 2 || isFirstChangeTask"
/>
</template>
<el-input
v-else-if="question.Type==='calculation'"
v-model="questionForm[question.Id]"
disabled
/>
>
<template v-if="question.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(question.Unit)) }}
</template>
</el-input>
<!-- 上传图像 -->
<el-upload
v-else-if="question.Type==='upload'"
@ -326,7 +350,8 @@ export default {
accept: '.png,.jpg,.jpeg',
imgVisible: false,
imageUrl: '',
urls: []
urls: [],
digitPlaces: 2
}
},
computed: {
@ -352,6 +377,8 @@ export default {
})
}
}
var digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
},
methods: {
formItemChange(v, question) {
@ -461,7 +488,7 @@ export default {
}
.uploadWrapper{
display: flex;
flex-direction: column;
flex-direction: column !important;
align-items: flex-start;
}
/deep/ .el-input.is-disabled .el-input__inner{
@ -470,6 +497,9 @@ export default {
/deep/ .el-input-group__append, .el-input-group__prepend{
padding: 0 10px;
}
/deep/ .el-form-item__content {
width: 100%;
}
/deep/ .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}

View File

@ -88,7 +88,7 @@ export default {
spleenInfo: null,
calculateSpleenStatus: '',
formChanged: false,
digitPlaces: null
digitPlaces: 2
}
},
computed: {
@ -374,7 +374,8 @@ export default {
DicomEvent.$emit('getReportInfo', true)
// DicomEvent.$emit('readingPageUpdate', {})
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx > -1 && !this.visitTaskList[idx].IsBaseLineTask) {
const criterionType = parseInt(localStorage.getItem('CriterionType'))
if (idx > -1 && !this.visitTaskList[idx].IsBaseLineTask && (criterionType === 1 || criterionType === 2 || criterionType === 3 || criterionType === 17)) {
if (parseInt(imageQuality) === 2) {
this.$confirm(this.$t('trials:reading:warnning:unableEvaluate'), '', {
type: 'warning'
@ -856,6 +857,7 @@ export default {
.ecrf-wrapper{
/deep/ .el-form-item__label{
color: #c3c3c3;
text-align: left;
}
/deep/ .el-input__inner{
background-color: transparent;

View File

@ -57,6 +57,7 @@
:is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-task-view-in-order="isReadingTaskViewInOrder"
:is-exists-manual="isExistsManual"
:iseCRFShowInDicomReading="iseCRFShowInDicomReading"
@previewCD="previewCD"
/>
</div>
@ -136,6 +137,10 @@ export default {
type: Boolean,
required: true
},
iseCRFShowInDicomReading: {
type: Boolean,
required: true
},
questionFormChangeState: {
type: Boolean,
default() {

View File

@ -1,7 +1,7 @@
<template>
<div class="measurement-wrapper" :style="{'height':height+10+'px'}">
<div class="measurement-wrapper">
<div class="container" :style="{'height':height+'px'}">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
@ -169,7 +169,6 @@ export default {
},
data() {
return {
height: window.innerHeight - 140,
questions: [],
activeName: '',
activeItem: {
@ -234,8 +233,6 @@ export default {
DicomEvent.$on('split', measureData => {
this.handleSplit(measureData.RowId, measureData.QuestionId)
})
window.addEventListener('resize', this.setHeight)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
@ -416,9 +413,6 @@ export default {
} catch (e) { console.log(e) }
})
},
setHeight() {
this.height = window.innerHeight - 140
},
getQuestionAnswer(questions, questionMark, answers) {
var idx = questions.findIndex(i => i.QuestionMark === questionMark)
if (idx > -1) {
@ -1065,6 +1059,7 @@ export default {
</script>
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
// overflow: hidden;

View File

@ -1,7 +1,7 @@
<template>
<div class="measurement-wrapper" :style="{'height':height+10+'px'}">
<div class="measurement-wrapper">
<div class="container" :style="{'height':height+'px'}">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
@ -165,7 +165,6 @@ export default {
},
data() {
return {
height: window.innerHeight - 140,
questions: [],
activeName: '',
activeItem: {
@ -229,8 +228,6 @@ export default {
DicomEvent.$on('split', measureData => {
this.handleSplit(measureData.RowId, measureData.QuestionId)
})
window.addEventListener('resize', this.setHeight)
},
beforeDestroy() {
DicomEvent.$off('loadMeasurementList')
@ -409,9 +406,6 @@ export default {
} catch (e) { console.log(e) }
})
},
setHeight() {
this.height = window.innerHeight - 140
},
getQuestionAnswer(questions, questionMark, answers) {
var idx = questions.findIndex(i => i.QuestionMark === questionMark)
if (idx > -1) {
@ -994,6 +988,7 @@ export default {
</script>
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
// overflow: hidden;

View File

@ -102,7 +102,7 @@
</div>
</template>
<template slot-scope="scope">
<span :style="{color: ((scope.row.isLymphNodes === 1 && scope.row.QuestionMark === 1) || (scope.row.isLymphNodes === 0 && scope.row.QuestionMark === 0)) && (scope.row.LesionType === 0 || scope.row.LesionType === 5) || (scope.row.QuestionMark === 12) || scope.row.HighlightAnswerList.includes(`${scope.row.Answers[task.VisitTaskId]}`) ? '#f66' : '#fff'}" >
<span :style="{color: ((scope.row.isLymphNodes === 1 && scope.row.QuestionMark === 1) || (scope.row.isLymphNodes === 0 && scope.row.QuestionMark === 0)) && (scope.row.LesionType === 0 || scope.row.LesionType === 5) || (scope.row.QuestionMark === 12) || scope.row.HighlightAnswerList.includes(`${scope.row.Answers[task.VisitTaskId]}`) ? '#f66' : '#fff'}">
<template v-if="task.VisitTaskId === visitTaskId && readingTaskState < 2 && [13,14,15,42].includes(scope.row.QuestionType)">
<!-- 是否存在疾病基线时可修改 -->
<template v-if="task.IsBaseLine && scope.row.QuestionType=== 15">

View File

@ -77,7 +77,7 @@
>
<div>
<div>{{ instance.InstanceNumber }}</div>
<div>{{ `${instance.NumberOfFrames > 0 ? instance.NumberOfFrames : 1} frame` }}</div>
<div>{{ `${instance.NumberOfFrames > 0 ? instance.KeyFramesList.length > 0 ? instance.KeyFramesList.length : instance.NumberOfFrames : 1} frame` }}</div>
</div>
</div>
@ -294,7 +294,14 @@ export default {
store.dispatch('reading/setImageloadedInfo', item)
}
})
var i = res.findIndex(s => s.isCurrentTask)
var i = -1
var isReadingTaskViewInOrder = parseInt(this.$router.currentRoute.query.isReadingTaskViewInOrder)
if (isReadingTaskViewInOrder === 2) {
//
i = res.length === 2 ? 1 : -1
} else {
i = res.findIndex(s => s.isCurrentTask)
}
if (i > -1) {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx > -1) {
@ -342,8 +349,9 @@ export default {
this.studyList = this.visitTaskList[idx].StudyList
var sIdx = this.studyList.findIndex(s => s.IsDicom)
if (sIdx > -1) {
this.studyIndex = sIdx
this.seriesIndex = 0
console.log(this.studyIndex, this.studyIndex)
// this.studyIndex = sIdx
// c = 0
this.activeNames = [`${this.studyList[sIdx].StudyId}`]
//
const i = this.studyList.findIndex(i => i.IsCriticalSequence)
@ -377,8 +385,23 @@ export default {
var isReadingTaskViewInOrder = parseInt(this.$router.currentRoute.query.isReadingTaskViewInOrder)
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
this.studyList = this.visitTaskList[idx].StudyList
if (this.visitTaskList[idx].IsBaseLineTask || isReadingTaskViewInOrder !== 1) {
// 线
const studyList = this.visitTaskList[idx].StudyList.filter(i => i.IsDicom)
const seriesArr = studyList.map(s => s.SeriesList).flat()
if (isReadingTaskViewInOrder === 2) {
//
if (seriesArr.length === 1) {
seriesList.push(seriesArr[0], seriesArr[0])
this.studyIndex = seriesArr[0].studyIndex
this.seriesIndex = seriesArr[0].seriesIndex
this.activeNames = [`${seriesArr[0].studyId}`]
} else if (seriesArr.length > 1) {
seriesList.push(seriesArr[0], seriesArr[1])
this.studyIndex = seriesArr[1].studyIndex
this.seriesIndex = seriesArr[1].seriesIndex
this.activeNames = [`${seriesArr[1].studyId}`]
}
} else if (this.visitTaskList[idx].IsBaseLineTask || isReadingTaskViewInOrder === 0) {
// 线
const obj = this.getFirstMarkedSeries(this.visitTaskList[idx].MeasureData, [...this.visitTaskList[idx].StudyList])
if (Object.keys(obj).length !== 0) {
this.studyIndex = obj.studyIndex
@ -387,9 +410,13 @@ export default {
this.activeNames = [`${this.studyList[ this.studyIndex].StudyId}`]
this.studyList[ obj.studyIndex].SeriesList[obj.seriesIndex].isFirstRender = true
} else {
//
if (this.studyList.length > 0) {
this.activeNames = [`${this.studyList[0].StudyId}`]
//
this.studyIndex = seriesArr[0].studyIndex
this.seriesIndex = seriesArr[0].seriesIndex
this.activeNames = [`${seriesArr[0].studyId}`]
seriesList.push(seriesArr[0])
// this.activeNames = [`${this.studyList[0].StudyId}`]
}
// DicomEvent.$emit('loadMeasurementList', { visitTaskId: this.visitTaskList[idx].VisitTaskId, taskBlindName: this.visitTaskList[idx].TaskBlindName })
@ -413,6 +440,7 @@ export default {
const firstObj = this.getFirstMarkedSeries(this.visitTaskList[bsIdx].MeasureData, [...this.visitTaskList[bsIdx].StudyList])
seriesList.push(firstObj.series)
const secondObj = this.getSecondMarkedSeries(firstObj, { ...this.visitTaskList[idx] })
this.studyIndex = secondObj.studyIndex
this.seriesIndex = secondObj.seriesIndex
@ -552,22 +580,20 @@ export default {
obj.series = seriesObj.series
obj.seriesId = seriesObj.series.seriesId
obj.isMarked = false
} else {
}
}
if (Object.keys(obj).length === 0) {
const sIdx = studyList.findIndex(s => s.IsDicom)
if (sIdx > -1) {
// 5
const series = studyList[sIdx].SeriesList[0]
var imageIdIndex = series.imageIds.length > 1 ? Math.floor(series.imageIds.length / 2) - 1 : 0
obj.studyIndex = sIdx
obj.seriesIndex = 0
obj.series = series
obj.series = studyList[obj.studyIndex].SeriesList[obj.seriesIndex]
const imageIdIndex = Math.floor(obj.series.imageIds.length / 2)
obj.series.imageIdIndex = imageIdIndex >= 0 ? imageIdIndex : 0
obj.seriesId = series.seriesId
// obj.seriesId = series.seriesId
obj.isMarked = false
}
}
}
return obj
},
strSimilarity2Number(s, t) {
@ -668,7 +694,11 @@ export default {
this.currentSeriesIndex = seriesIndex
var idx = this.visitTaskIdx
const imageIds = []
if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
if (instanceInfo.KeyFramesList.length > 0) {
instanceInfo.KeyFramesList.map(i => {
imageIds.push(`wadouri:${localStorage.getItem('location') !== 'USA' ? this.OSSclientConfig.basePath : this.OSSclientConfig.basePath}${instanceInfo.Path}?frame=${i}&instanceId=${instanceInfo.Id}&visitTaskId=${this.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${idx}`)
})
} else if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
for (let j = 0; j < instanceInfo.NumberOfFrames; j++) {
imageIds.push(`wadouri:${localStorage.getItem('location') !== 'USA' ? this.OSSclientConfig.basePath : this.OSSclientConfig.basePath}${instanceInfo.Path}?frame=${j}&instanceId=${instanceInfo.Id}&visitTaskId=${this.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${idx}`)
}

View File

@ -1064,16 +1064,15 @@ export default {
var frame = imageInfo.frame
this.stack.frame = this.stack.isExistMutiFrames ? parseInt(frame) : null
this.stack.instanceId = instanceId
const element = this.$refs.canvas
cornerstone.enable(element)
element.tabIndex = 0
element.focus()
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
ToolStateManager.clearImageIdToolState(dicomSeries.imageIds)
this.toggleClipPlay(false)
this.toolState.viewportInvert = false
this.toolState.dicomInfoVisible = false
const element = this.$refs.canvas
cornerstone.enable(element)
element.tabIndex = 0
element.focus()
var scope = this
// var p = parseInt(new Date().getTime())
// requestPoolManager.loadAndCacheImagePlus(this.stack.imageIds[this.stack.currentImageIdIndex], this.stack.seriesId, p*100).then(image=>{
@ -1637,7 +1636,7 @@ export default {
resetWwwc() {
this.toolState.viewportInvert = false
var viewport = cornerstone.getViewport(this.canvas)
viewport.invert = false
// viewport.invert = false
var image = cornerstone.getImage(this.canvas)
viewport.voi.windowWidth = image.windowWidth
viewport.voi.windowCenter = image.windowCenter

View File

@ -106,7 +106,8 @@
v-if="item.val === 1"
class="divider"
content-position="center"
>{{ ` ${$t("trials:reading:title:preset")}` }}</el-divider>
>{{ ` ${$t('trials:reading:title:preset')}` }}</el-divider
>
</li>
</ul>
</div>
@ -163,7 +164,7 @@
<svg-icon icon-class="magnifier" class="svg-icon" />
</div>
<!-- 缩放 -->
<div class="text">{{ $t("trials:reading:button:zoom") }}</div>
<div class="text">{{ $t('trials:reading:button:zoom') }}</div>
</div>
</el-tooltip>
@ -183,7 +184,7 @@
<svg-icon icon-class="move" class="svg-icon" />
</div>
<!-- 移动 -->
<div class="text">{{ $t("trials:reading:button:move") }}</div>
<div class="text">{{ $t('trials:reading:button:move') }}</div>
</div>
</el-tooltip>
@ -210,7 +211,7 @@
</div>
<!-- 旋转 -->
<div class="text">
{{ $t("trials:reading:button:rotate")
{{ $t('trials:reading:button:rotate')
}}<i class="el-icon-caret-bottom" />
</div>
<div class="dropdown-content">
@ -223,7 +224,8 @@
<a
href="#"
@click.prevent="setDicomCanvasRotate(rotate.val)"
>{{ rotate.label }}</a>
>{{ rotate.label }}</a
>
</li>
</ul>
</div>
@ -258,8 +260,8 @@
<div class="text">
{{
fitType === 1
? $t("trials:reading:button:fitWindow")
: $t("trials:reading:button:fitImage")
? $t('trials:reading:button:fitWindow')
: $t('trials:reading:button:fitImage')
}}
</div>
</div>
@ -282,7 +284,7 @@
</div>
<!-- 同步 -->
<div class="text">
{{ $t("trials:reading:button:imageIndexSync") }}
{{ $t('trials:reading:button:imageIndexSync') }}
</div>
</div>
</el-tooltip>
@ -307,7 +309,7 @@
<svg-icon icon-class="image" class="svg-icon" />
</div>
<!-- 截屏 -->
<div class="text">{{ $t("trials:reading:button:screenShot") }}</div>
<div class="text">{{ $t('trials:reading:button:screenShot') }}</div>
</div>
</el-tooltip>
@ -316,7 +318,7 @@
<div slot="content">
<div v-if="tool.disabledReason">
<h4 style="margin: 0; padding-bottom: 2px">{{ tool.text }}</h4>
<br>{{ tool.disabledReason }}
<br />{{ tool.disabledReason }}
</div>
<div v-else>{{ tool.text }}</div>
</div>
@ -365,7 +367,7 @@
<svg-icon icon-class="clear" class="svg-icon" />
</div>
<!-- 缩放 -->
<div class="text">{{ $t("trials:dicom-show:Eraser") }}</div>
<div class="text">{{ $t('trials:dicom-show:Eraser') }}</div>
</div>
</el-tooltip>
<div class="tool-frame">
@ -467,11 +469,13 @@
<div class="icon" @click.prevent="resetViewport">
<svg-icon icon-class="refresh" class="svg-icon" />
</div>
<div class="text">{{ $t("trials:reading:button:reset") }}</div>
<div class="text">{{ $t('trials:reading:button:reset') }}</div>
</div>
</el-tooltip>
<el-tooltip
v-if="trialCriterion.ImageUploadEnum > 0"
v-if="
trialCriterion.ImageUploadEnum > 0 && currentReadingTaskState < 2
"
v-hasPermi="['role:ir']"
class="item"
effect="dark"
@ -482,7 +486,7 @@
<div class="icon" @click.prevent="openUploadImage('upload')">
<i class="el-icon-upload2 svg-icon" />
</div>
<div class="text">{{ $t("trials:reading:button:upload") }}</div>
<div class="text">{{ $t('trials:reading:button:upload') }}</div>
</div>
</el-tooltip>
<el-tooltip
@ -505,7 +509,7 @@
<div class="icon" @click.prevent="openUploadImage('download')">
<i class="el-icon-download svg-icon" />
</div>
<div class="text">{{ $t("trials:reading:button:download") }}</div>
<div class="text">{{ $t('trials:reading:button:download') }}</div>
</div>
</el-tooltip>
@ -515,17 +519,19 @@
v-if="isExistsManual"
type="text"
@click="previewManuals"
>{{ $t("trials:reading:button:handbooks") }}</el-button>
>{{ $t('trials:reading:button:handbooks') }}</el-button
>
<!-- 临床数据 -->
<el-button
v-if="isExistsClinicalData"
type="text"
@click="previewCD"
>{{ $t("trials:reading:button:clinicalData") }}</el-button>
>{{ $t('trials:reading:button:clinicalData') }}</el-button
>
<!-- <el-button v-if="isExistsNoDicomFile" type="text" @click="previewNoneDicoms">Dicom</el-button> -->
<el-button type="text" @click="previewConfig">{{
$t("trials:reading:button:customCfg")
$t('trials:reading:button:customCfg')
}}</el-button>
</div>
</div>
@ -625,7 +631,7 @@
:fullscreen="isFullscreen"
>
<span slot="title" class="dialog-footer">
<span>{{ $t("trials:reading:button:handbooks") }}</span>
<span>{{ $t('trials:reading:button:handbooks') }}</span>
<span style="position: absolute; right: 20px; font-size: 20px">
<svg-icon
:icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'"
@ -646,12 +652,29 @@
<!-- <button :title="$t('trials:dicom-show:Eraser')" class="btn-link" data-tool="Eraser" @click="setToolActive($event,'Eraser')">-->
<!-- <svg-icon icon-class="clear" style="font-size:20px;" />-->
<!-- </button>-->
<upload-image
<!-- <upload-image
v-if="uploadImageVisible"
:visible.sync="uploadImageVisible"
:subject-id="subjectId"
:criterion="trialCriterion"
:status="uploadStatus"
/> -->
<upload-dicom-and-nonedicom
v-if="uploadImageVisible"
:SubjectId="uploadSubjectId"
:SubjectCode="uploadSubjectCode"
:Criterion="uploadTrialCriterion"
:visible.sync="uploadImageVisible"
:VisitTaskId="taskId"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder"
/>
<download-dicom-and-nonedicom
v-if="downloadImageVisible"
:SubjectId="uploadSubjectId"
:SubjectCode="uploadSubjectCode"
:Criterion="uploadTrialCriterion"
:TaskId="taskId"
:visible.sync="downloadImageVisible"
/>
</div>
</template>
@ -676,7 +699,8 @@ import DicomEvent from './../components/DicomEvent'
import { mapGetters } from 'vuex'
import store from '@/store'
import { getDoctorShortcutKey, getUserWLTemplateList } from '@/api/user'
import uploadImage from '@/components/uploadImage'
import uploadDicomAndNonedicom from '@/components/uploadDicomAndNonedicom'
import downloadDicomAndNonedicom from '@/components/downloadDicomAndNonedicom'
import { getCriterionReadingInfo } from '@/api/trials'
export default {
name: 'DicomViewer',
@ -688,48 +712,49 @@ export default {
Others,
Manuals,
MeasurementList,
'upload-image': uploadImage
'download-dicom-and-nonedicom': downloadDicomAndNonedicom,
'upload-dicom-and-nonedicom': uploadDicomAndNonedicom,
},
props: {
isExistsClinicalData: {
type: Boolean,
default: false
default: false,
},
isExistsNoDicomFile: {
type: Boolean,
default: false
default: false,
},
isExistsManual: {
type: Boolean,
required: true
required: true,
},
isReadingShowSubjectInfo: {
type: Boolean,
required: true
required: true,
},
isReadingTaskViewInOrder: {
type: Number,
required: true
required: true,
},
IseCRFShowInDicomReading: {
type: Boolean,
required: true
required: true,
},
subjectId: {
type: String,
required: true,
default: ''
default: '',
},
trialReadingCriterionId: {
type: String,
required: true,
default: ''
default: '',
},
trialId: {
type: String,
required: true,
default: ''
}
default: '',
},
},
data() {
return {
@ -738,7 +763,7 @@ export default {
{ index: 0, row: 1, col: 1, name: 'A' },
{ index: 1, row: 1, col: 2, name: 'A|A' },
{ index: 2, row: 1, col: 2, name: 'A|B' },
{ index: 3, row: 2, col: 2, name: 'A|A|A|A' }
{ index: 3, row: 2, col: 2, name: 'A|A|A|A' },
// { index: 2, row: 2, col: 1 },
// { index: 3, row: 2, col: 2 }
],
@ -747,7 +772,7 @@ export default {
{ label: this.$t('trials:reading:button:rotateVertical'), val: 2 }, //
{ label: this.$t('trials:reading:button:rotateHorizontal'), val: 3 }, //
{ label: this.$t('trials:reading:button:rotateTurnLeft'), val: 4 }, // 90
{ label: this.$t('trials:reading:button:rotateTurnRight'), val: 5 } // 90
{ label: this.$t('trials:reading:button:rotateTurnRight'), val: 5 }, // 90
],
maxCanvas: 1,
layoutRow: 1,
@ -755,8 +780,8 @@ export default {
currentDicomCanvasIndex: 0,
currentDicomCanvas: {
toolState: {
clipPlaying: false
}
clipPlaying: false,
},
},
colormapsList: [],
rotateList: [],
@ -773,7 +798,7 @@ export default {
text: this.$t('trials:reading:button:length'),
icon: 'length',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
//
{
@ -781,7 +806,7 @@ export default {
text: this.$t('trials:reading:button:bidirectional'),
icon: 'bidirection',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
//
{
@ -789,7 +814,7 @@ export default {
text: this.$t('trials:reading:button:rectangle'),
icon: 'rectangle',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
//
{
@ -797,7 +822,7 @@ export default {
text: this.$t('trials:reading:button:elliptical'),
icon: 'oval',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
//
{
@ -805,8 +830,8 @@ export default {
text: this.$t('trials:reading:button:arrowAnnotate'),
icon: 'arrow',
isDisabled: false,
disabledReason: ''
}
disabledReason: '',
},
],
fitType: 0,
@ -818,25 +843,25 @@ export default {
label: this.$t('trials:reading:button:wwwcDefault'),
val: -1,
ww: null,
wc: null
wc: null,
}, //
{
label: this.$t('trials:reading:button:wwwcCustom'),
val: 0,
ww: null,
wc: null
wc: null,
}, //
{
label: this.$t('trials:reading:button:wwwcRegion'),
val: 1,
ww: null,
wc: null
wc: null,
}, //
{ label: 'CT Brain', wc: 40, ww: 80 },
{ label: 'CT Lungs', wc: -400, ww: 1500 },
{ label: 'CT Abdomen', wc: 60, ww: 400 },
{ label: 'CT Liver', wc: 40, ww: 400 },
{ label: 'CT Bone', wc: 300, ww: 1500 }
{ label: 'CT Bone', wc: 300, ww: 1500 },
],
activeSeries: {},
seriesStack: [],
@ -844,13 +869,13 @@ export default {
isScrollSync: false,
imageIndexSync: {
sourceCanvas: '',
targetCanvas: []
targetCanvas: [],
},
isFirstRender: false,
customWwc: { visible: false, title: null }, //
personalConfigDialog: {
visible: false,
title: this.$t('trials:reading:button:customCfg')
title: this.$t('trials:reading:button:customCfg'),
}, //
layout: '',
isFirstNotLinked: false,
@ -861,15 +886,20 @@ export default {
manualsDialog: { visible: false },
clipPlaying: false,
fps: 15,
//
uploadImageVisible: false,
trialCriterion: {},
uploadStatus: 'upload'
//
downloadImageVisible: false,
uploadImageVisible: false,
uploadSubjectId: null,
uploadSubjectCode: null,
uploadTrialCriterion: {},
uploadStatus: 'upload',
taskId: '',
}
},
computed: {
...mapGetters(['visitTaskList', 'currentReadingTaskState'])
...mapGetters(['visitTaskList', 'currentReadingTaskState']),
},
watch: {
currentReadingTaskState: {
@ -888,7 +918,7 @@ export default {
this.activeTool = ''
}
}
}
},
},
currentDicomCanvasIndex: {
immediate: true,
@ -905,8 +935,8 @@ export default {
this.clipPlaying = false
this.fps = 15
}
}
}
},
},
},
mounted() {
this.getTrialCriterion()
@ -919,7 +949,7 @@ export default {
// cornerstone.imageCache.setMaximumSizeBytes(maximumSizeInBytes)
this.customWwc = {
visible: false,
title: this.$t('trials:reading:dagTitle:wwwcCustom')
title: this.$t('trials:reading:dagTitle:wwwcCustom'),
}
this.CriterionType = parseInt(localStorage.getItem('CriterionType'))
if (this.CriterionType === 10) {
@ -929,15 +959,15 @@ export default {
text: this.$t('trials:reading:button:arrowAnnotate'),
icon: 'arrow',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
{
toolName: 'RectangleRoi',
text: this.$t('trials:reading:button:rectangle'),
icon: 'rectangle',
isDisabled: false,
disabledReason: ''
}
disabledReason: '',
},
]
} else if (this.CriterionType === 17) {
this.measuredTools = [
@ -946,22 +976,22 @@ export default {
text: this.$t('trials:reading:button:length'),
icon: 'length',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
{
toolName: 'RectangleRoi',
text: this.$t('trials:reading:button:rectangle'),
icon: 'rectangle',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
{
toolName: 'ArrowAnnotate',
text: this.$t('trials:reading:button:arrowAnnotate'),
icon: 'arrow',
isDisabled: false,
disabledReason: ''
}
disabledReason: '',
},
]
}
this.rotateList[0] = '1'
@ -1041,7 +1071,7 @@ export default {
getTrialCriterion() {
getCriterionReadingInfo({
TrialId: this.trialId,
TrialReadingCriterionId: this.trialReadingCriterionId
TrialReadingCriterionId: this.trialReadingCriterionId,
})
.then((res) => {
this.trialCriterion = res.Result
@ -1049,8 +1079,15 @@ export default {
.catch(() => {})
},
openUploadImage(status) {
const idx = this.visitTaskList.findIndex((i) => i.IsCurrentTask)
if (idx > -1) {
this.taskId = this.visitTaskList[idx].VisitTaskId
}
this.uploadSubjectCode = this.$route.query.subjectCode||localStorage.getItem("subjectCode")
this.uploadSubjectId = this.$route.query.subjectId||localStorage.getItem("subjectId")
this.uploadTrialCriterion = this.trialCriterion
this.uploadStatus = status
this.uploadImageVisible = true
this[`${status}ImageVisible`] = true
},
async getWwcTpl() {
// const loading = this.$loading({ fullscreen: true })
@ -1061,14 +1098,13 @@ export default {
this.customWwcTpl.push({
label: i.TemplateName,
wc: i.WL,
ww: i.WW
ww: i.WW,
})
})
this.wwwcArr = [...this.defaultWwwc, ...this.customWwcTpl]
} catch(e) {
} catch (e) {
console.log(e)
}
},
async getHotKeys() {
// const loading = this.$loading({ fullscreen: true })
@ -1084,11 +1120,11 @@ export default {
key: item.Keyboardkey,
code: item.Code,
text: item.Text,
shortcutKeyEnum: item.ShortcutKeyEnum
shortcutKeyEnum: item.ShortcutKeyEnum,
})
})
this.bindHotKey()
} catch(e) {
} catch (e) {
console.log(e)
}
},
@ -1105,7 +1141,7 @@ export default {
key: item.keys.controlKey.key,
code: item.keys.controlKey.code,
text: item.keys.text,
shortcutKeyEnum: item.label
shortcutKeyEnum: item.label,
})
})
@ -1148,7 +1184,7 @@ export default {
DicomEvent.$emit('selectSeries', {
seriesId: series.seriesId,
studyId: series.studyId,
offset: -1
offset: -1,
})
} else if (shortcutKeyEnum === 4) {
//
@ -1157,7 +1193,7 @@ export default {
DicomEvent.$emit('selectSeries', {
seriesId: series.seriesId,
studyId: series.studyId,
offset: 1
offset: 1,
})
} else if (shortcutKeyEnum === 5) {
//
@ -1287,7 +1323,7 @@ export default {
top: '72px',
left: '205px',
right: '350px',
zIndex: 10
zIndex: 10,
}
this.canvasW = window.innerWidth - 570 + 'px'
this.canvasH = window.innerHeight - 130 + 'px'
@ -1387,10 +1423,7 @@ export default {
// this.$refs['measurementList'].initPage(dicomSeries)
store.dispatch('reading/setActiveSeries', dicomSeries)
if (this.currentDicomCanvasIndex === this.maxCanvas - 1) {
store.dispatch(
'reading/setLastCanvasTaskId',
dicomSeries.visitTaskId
)
store.dispatch('reading/setLastCanvasTaskId', dicomSeries.visitTaskId)
}
})
},
@ -1421,10 +1454,7 @@ export default {
store.dispatch('reading/setActiveSeries', dicomSeries)
if (this.currentDicomCanvasIndex === this.maxCanvas - 1) {
store.dispatch(
'reading/setLastCanvasTaskId',
dicomSeries.visitTaskId
)
store.dispatch('reading/setLastCanvasTaskId', dicomSeries.visitTaskId)
}
})
},
@ -1474,7 +1504,7 @@ export default {
visitTaskId = this.visitTaskList[i].VisitTaskId
}
this.getFirstSeries(activeSeries, visitTaskId).then(
async(baseSerires) => {
async (baseSerires) => {
this.seriesStack = [baseSerires, activeSeries]
this.currentDicomCanvasIndex = 1
store.dispatch(
@ -1502,7 +1532,7 @@ export default {
activeSeries,
activeSeries,
activeSeries,
activeSeries
activeSeries,
]
this.currentDicomCanvasIndex = 3
store.dispatch(
@ -1521,9 +1551,7 @@ export default {
for (let i = 0; i < this.maxCanvas && i < seriesStack.length; i++) {
this.canvasObj[i] = seriesStack[i]
let s = Object.assign({}, seriesStack[i])
promiseArr.push(
this.$refs[`dicomCanvas${i}`][0].loadImageStack(s)
)
promiseArr.push(this.$refs[`dicomCanvas${i}`][0].loadImageStack(s))
}
Promise.all(promiseArr)
.then(() => {
@ -1585,24 +1613,24 @@ export default {
trialId: this.trialId,
subjectVisitId: this.visitTaskList[taskIdx].VisitId,
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId,
taskBlindName: this.visitTaskList[taskIdx].TaskBlindName
taskBlindName: this.visitTaskList[taskIdx].TaskBlindName,
})
}
if (!this.visitTaskList[taskIdx].readingQuestionsInit) {
await store.dispatch('reading/getReadingQuestionAndAnswer', {
trialId: this.trialId,
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId,
})
}
if (!this.visitTaskList[taskIdx].questionsInit) {
await store.dispatch('reading/getDicomReadingQuestionAnswer', {
trialId: this.trialId,
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId,
})
}
await store.dispatch('reading/setStatus', {
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId,
})
}
firstAddSeries = this.getSeriesInfoByMark(firstAddVisitTaskId, obj)
@ -1696,8 +1724,7 @@ export default {
var studyList = this.visitTaskList[index].StudyList
var studyId = this.visitTaskList[index].MeasureData[idx].StudyId
var seriesId = this.visitTaskList[index].MeasureData[idx].SeriesId
var instanceId =
this.visitTaskList[index].MeasureData[idx].InstanceId
var instanceId = this.visitTaskList[index].MeasureData[idx].InstanceId
var studyIdx = studyList.findIndex(
(study) => study.StudyId === studyId
)
@ -1706,10 +1733,15 @@ export default {
(s) => s.seriesId === seriesId
)
if (seriesIdx > -1) {
var series = studyList[studyIdx].SeriesList[seriesIdx];
let frame = this.visitTaskList[index].MeasureData[idx].MeasureData.frame
let filterStr = series.isExistMutiFrames ? `frame=${frame}&instanceId=${instanceId}` : `instanceId=${instanceId}`
var instanceIdx = series.imageIds.findIndex(imageId => imageId.includes(filterStr))
var series = studyList[studyIdx].SeriesList[seriesIdx]
let frame =
this.visitTaskList[index].MeasureData[idx].MeasureData.frame
let filterStr = series.isExistMutiFrames
? `frame=${frame}&instanceId=${instanceId}`
: `instanceId=${instanceId}`
var instanceIdx = series.imageIds.findIndex((imageId) =>
imageId.includes(filterStr)
)
// var instanceIdx = series.instanceList.findIndex(
// (imageId) => !!~imageId.indexOf(instanceId)
// );
@ -1738,16 +1770,14 @@ export default {
i.description,
baseSeries.description
),
index
index,
}
})
similarArr = similarArr.sort((a, b) => {
return b.similar - a.similar
})
const i =
similarArr[0] && similarArr[0].similar > 0.85
? similarArr[0].index
: -1
similarArr[0] && similarArr[0].similar > 0.85 ? similarArr[0].index : -1
if (i > -1) {
obj = seriesList[i]
}
@ -1822,7 +1852,7 @@ export default {
trialId: this.trialId,
subjectVisitId: this.visitTaskList[index].VisitId,
visitTaskId: this.visitTaskList[index].VisitTaskId,
taskBlindName: this.visitTaskList[index].TaskBlindName
taskBlindName: this.visitTaskList[index].TaskBlindName,
})
await store.dispatch(
@ -1841,16 +1871,14 @@ export default {
i.description,
baseSeries.description
),
index
index,
}
})
similarArr = similarArr.sort((a, b) => {
return b.similar - a.similar
})
const i =
similarArr[0] && similarArr[0].similar > 0.85
? similarArr[0].index
: -1
similarArr[0] && similarArr[0].similar > 0.85 ? similarArr[0].index : -1
if (i > -1) {
obj = seriesList[i]
}
@ -2025,9 +2053,7 @@ export default {
},
//
enter(e, toolName) {
var i = this.measuredTools.findIndex(
(item) => item.toolName === toolName
)
var i = this.measuredTools.findIndex((item) => item.toolName === toolName)
if (i === -1) return
var isCurrentTask =
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0]
@ -2089,8 +2115,11 @@ export default {
`dicomCanvas${this.currentDicomCanvasIndex}`
][0].setToolActive(toolName)
}
} else if (dicomSeries.isCurrentTask &&
isMeasuredTool && dicomSeries.readingTaskState >= 2) {
} else if (
dicomSeries.isCurrentTask &&
isMeasuredTool &&
dicomSeries.readingTaskState >= 2
) {
if (this.activeTool === toolName) {
this.$refs[
`dicomCanvas${this.currentDicomCanvasIndex}`
@ -2162,9 +2191,7 @@ export default {
][0].fitToWindow()
} else if (toolName === 'fitToImage') {
this.fitType = 1
this.$refs[
`dicomCanvas${this.currentDicomCanvasIndex}`
][0].fitToImage()
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].fitToImage()
}
this.activeTool = toolName
},
@ -2181,7 +2208,9 @@ export default {
const isLoaded = this.getSeriesLoadStatus()
if (!isLoaded) return
this.clipPlaying = isPlay
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setFps(this.fps)
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setFps(
this.fps
)
this.$refs[
`dicomCanvas${this.currentDicomCanvasIndex}`
][0].toggleClipPlay(isPlay)
@ -2297,8 +2326,8 @@ export default {
previewManuals() {
this.isFullscreen = false
this.manualsDialog.visible = true
}
}
},
},
}
</script>
<style lang="scss" scoped>

View File

@ -25,14 +25,14 @@
</div>
<div class="questions-footer">
<i class="el-icon-question feedback-icon" @click="openFeedBackTable" />
<el-button v-if="readingTaskState<2" style="margin-right: 10px" size="small" @click="handleSave(true)">
<el-button v-if="readingTaskState<2" style="margin-right: 10px" type="primary" size="small" @click="handleSave(true)">
{{$t('common:button:save')}}
</el-button>
<el-button v-if="readingTaskState<2 && IseCRFShowInDicomReading" type="primary" size="small" @click="skipTask">
<el-button v-if="readingTaskState<2 && IseCRFShowInDicomReading" type="primary" style="margin-right: 10px" size="small" @click="skipTask">
<!-- 跳过 -->
{{ $t('trials:readingReport:button:skip') }}
</el-button>
<el-button style="margin-right: 10px" size="small" v-if="readingTaskState<2 && IseCRFShowInDicomReading" @click="handleConfirm">
<el-button style="margin-right: 10px" type="primary" size="small" v-if="readingTaskState<2 && IseCRFShowInDicomReading" @click="handleConfirm">
{{ $t('common:button:submit') }}</el-button>
</div>
<!-- 签名框 -->
@ -244,6 +244,7 @@ export default {
params.StudyId = params.questionInfo.StudyId
params.SeriesId = params.questionInfo.SeriesId
params.InstanceId = params.questionInfo.InstanceId
params.NumberOfFrames = measureData.frame
submitCustomTag(params).then(() => {
this.measuredDataVisible = false
DicomEvent.$emit('updateImage')
@ -282,6 +283,7 @@ export default {
} else {
let params = JSON.parse(JSON.stringify(this.measureData))
params.measureData = JSON.stringify(this.measureData)
params.NumberOfFrames = this.measureData.frame
submitCustomTag(params).then(async (res) => {
this.measuredDataVisible = false
this.form.measuredDataName = ''

View File

@ -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()
})
}
},
//

View File

@ -138,11 +138,24 @@
>
<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"
@ -159,6 +172,7 @@
<i class="el-icon-delete" />
</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();
}
},
//

View File

@ -148,11 +148,24 @@
>
<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"
@ -169,6 +182,7 @@
<i class="el-icon-delete" />
</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()
}
},

View File

@ -82,7 +82,7 @@ export default {
var answers = []
for (const k in this.questionForm) {
if (this.questionForm[k] instanceof Array) {} else {
answers.push({ id: k, answer: this.questionForm[k].toString() })
answers.push({ id: k, answer: this.questionForm[k] })
}
}
var params = {

View File

@ -14,12 +14,24 @@
>
<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"
crossOrigin="Anonymous"
: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"
@ -36,19 +48,9 @@
<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()
}
},
//

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