影像下载
continuous-integration/drone/push Build is passing Details

main
wangxiaoshuang 2025-11-21 16:12:55 +08:00
parent 3d9021acc6
commit 8f6121dcd0
8 changed files with 267 additions and 186 deletions

Binary file not shown.

View File

@ -195,4 +195,12 @@ export function getUserJoinedTrialPatientList() {
url: '/Patient/getUserJoinedTrialPatientList',
method: 'get',
})
}
// 获取影像下载数据
export function getDownloadPatientStudyInfo(data) {
return request({
url: '/Patient/getDownloadPatientStudyInfo',
method: 'post',
data
})
}

View File

@ -66,6 +66,7 @@ async function ossGenerateSTS() {
objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
objectName = objectItem.join('/')
}
objectName = objectName.slice(1)
const reader = new FileReader();
reader.onload = (ex) => {
const bufferStream = new stream.PassThrough()
@ -78,7 +79,7 @@ async function ossGenerateSTS() {
} else {
resolve({
name: objectName,
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(objectName)
url: Vue.prototype.OSSclientConfig.viewEndpoint + '/' + decodeUtf8(objectName)
})
}
})

View File

@ -64,6 +64,11 @@
:disabled="multipleSelection.length <= 0" @click="openGroup({})">
{{ $t('trials:inspection:button:checkGroupId') }}
</el-button>
<!-- 下载影像 -->
<el-button type="primary" v-hasPermi="['trials:trialsInspection:downloadImage']" v-if="!isAll"
:disabled="multipleSelection.length <= 0" @click="getDownloadPatientStudyInfo(multipleSelection)">
{{ $t('trials:inspection:button:downloadImage') }}
</el-button>
</el-form-item>
</el-form>
</div>
@ -126,9 +131,9 @@
<!--检查日期-->
<el-table-column align="center" prop="StudyTime" :label="$t('trials:audit:table:studyDate')" show-overflow-tooltip
min-width="180" sortable="custom"></el-table-column>
<!--uid-->
<el-table-column align="center" prop="StudyInstanceUid" :label="$t('trials:audit:table:StudyInstanceUid')" show-overflow-tooltip
min-width="180" sortable="custom"></el-table-column>
<!--uid-->
<el-table-column align="center" prop="StudyInstanceUid" :label="$t('trials:audit:table:StudyInstanceUid')"
show-overflow-tooltip min-width="180" sortable="custom"></el-table-column>
<!--操作-->
<el-table-column :label="$t('common:action:action')" min-width="200" fixed="right">
<template slot-scope="scope">
@ -280,6 +285,9 @@ export default {
this.getList()
},
methods: {
getDownloadPatientStudyInfo(arr) {
this.$emit('getDownloadPatientStudyInfo', arr)
},
async getHospitalGroupList() {
try {
let res = await getHospitalGroupList({})

View File

@ -72,14 +72,29 @@
{{ $t('common:button:viewAllStudy') }}
</el-button>
</el-form-item>
<el-form-item v-if="hasPermi(['trials:trialsInspection:downloadImage'])">
<!-- 下载影像 -->
<el-button type="primary" @click="getDownloadPatientStudyInfo(multipleSelection)"
:disabled="multipleSelection.length <= 0">
{{ $t('trials:inspection:button:downloadImage') }}
</el-button>
</el-form-item>
<el-form-item v-if="hasPermi(['trials:trialsInspection:downloadImage'])">
<!-- 下载记录 -->
<el-button type="primary" @click="() => RecordVisible = true">
{{ $t('trials:inspection:button:downloadRecord') }}
</el-button>
</el-form-item>
</el-form>
</template>
<!-- 检查列表 -->
<template slot="main-container">
<el-table ref="inspectionList" v-loading="loading" v-adaptive="{ bottomOffset: 60 }" :data="list" stripe
height="100" @sort-change="handleSortByColumn" :default-sort="{ prop: 'LatestPushTime', order: 'descending' }">
<el-table-column type="index" width="40" />
height="100" @sort-change="handleSortByColumn" @selection-change="handleSelectionChange"
:default-sort="{ prop: 'LatestPushTime', order: 'descending' }">
<el-table-column type="selection" width="55" v-if="hasPermi(['trials:trialsInspection:downloadImage'])" />
<el-table-column type="index" width="40" v-else />
<!--患者ID-->
<el-table-column prop="PatientIdStr" :label="$t('trials:uploadDicomList:table:pId')" show-overflow-tooltip
min-width="120" sortable="custom"></el-table-column>
@ -185,13 +200,17 @@
<!--查看检查列表-->
<viewStudyList v-if="studyTrialsVisible" :visible.sync="studyTrialsVisible" :Patient="selectPatient"
:isAll="studyAll" :calledAeList="calledAeList" :callingAeList="callingAeList"
:hospitalGroupList="hospitalGroupList" @getList="getList" />
:hospitalGroupList="hospitalGroupList" @getList="getList"
@getDownloadPatientStudyInfo="getDownloadPatientStudyInfo" />
<!--推送记录列表-->
<pushRecordList v-if="pushTrialsVisible" :visible.sync="pushTrialsVisible" :calledAeList="calledAeList"
:callingAeList="callingAeList" />
<!--pacs拉取列表-->
<pullImage v-if="pullTrialsVisible" :visible.sync="pullTrialsVisible" :calledAeList="calledAeList"
:callingAeList="callingAeList" @getList="getList" />
<el-dialog title="" :visible.sync="RecordVisible" :fullscreen="true">
<downloadRecord :isSystem="true" />
</el-dialog>
</BaseContainer>
</template>
<script>
@ -202,9 +221,14 @@ import addTrialsList from './components/add-trials-list'
import viewStudyList from './components/view-study-list'
import pushRecordList from './components/push-record-list'
import pullImage from './components/pullImage/index.vue'
import { getPatientList, deletePatientStudyAllData } from '@/api/inspection.js'
import { getPatientList, deletePatientStudyAllData, getDownloadPatientStudyInfo } from '@/api/inspection.js'
import { getDicomCalledAEList, getDicomCallingAEList } from '@/api/dicomAE.js'
import downloadRecord from "@/views/trials/trials-panel/trial-summary/download-record"
import {
downloadImageSuccess,
} from '@/api/trials.js'
import { getHospitalGroupList } from '@/api/admin.js'
import { downLoadFile } from '@/utils/stream.js'
const defaultSearchData = () => {
return {
PatientIdStr: null,
@ -231,6 +255,7 @@ export default {
viewStudyList,
pushRecordList,
pullImage,
downloadRecord
},
data() {
return {
@ -243,6 +268,9 @@ export default {
total: 0,
loading: false,
list: [],
multipleSelection: [],
downloadId: null,
RecordVisible: false,
//
selectPatient: {},
//
@ -266,6 +294,125 @@ export default {
this.getHospitalGroupList()
},
methods: {
async getDownloadPatientStudyInfo(arr) {
try {
let data = {
PatientIdList: [],
ScpStudyIdList: []
}
arr.forEach(item => {
data.PatientIdList.push(item.PatientId)
data.ScpStudyIdList.push(item.SCPStudyId)
})
let res = await getDownloadPatientStudyInfo(data)
if (res.IsSuccess) {
this.downloadId = res.OtherInfo
this.downloadImage(res.Result)
}
} catch (err) {
console.log(err)
}
},
//
async downloadImage(data) {
try {
let { files, name } = this.formatDownloadFile(data)
let res = await downLoadFile(files, name, 'zip')
if (res && this.downloadId) {
this.downloadImageSuccess()
}
} catch (err) {
console.log(err)
}
},
//
formatDownloadFile(data) {
let files = [],
name = `Image_${Date.now()}.zip`
data.forEach(patient => {
if (Array.isArray(patient.StudyList) && patient.StudyList.length > 0) {
patient.StudyList.forEach((study) => {
if (Array.isArray(study.SeriesList) && study.SeriesList.length > 0) {
study.SeriesList.forEach((series) => {
if (
Array.isArray(series.InstanceList) &&
series.InstanceList.length > 0
) {
if (study.StudyDIRPath) {
let obj = {
name: `${study.PatientId}/${study.StudyTime.split(' ')[0]}_${series.Modality
}/DICOMDIR`,
url: this.OSSclientConfig.basePath + study.StudyDIRPath,
}
files.push(obj)
}
series.InstanceList.forEach((instance) => {
let fileName = instance.Path.split('/').pop()
let obj = {
name: `${study.PatientId}/${study.StudyTime.split(' ')[0]}_${series.Modality
}/IMAGE/${fileName}`,
url: this.OSSclientConfig.basePath + instance.Path,
}
files.push(obj)
})
}
})
}
})
}
})
if (Array.isArray(data.StudyList) && data.StudyList.length > 0) {
data.StudyList.forEach((study) => {
if (Array.isArray(study.SeriesList) && study.SeriesList.length > 0) {
study.SeriesList.forEach((series) => {
if (
Array.isArray(series.InstanceList) &&
series.InstanceList.length > 0
) {
series.InstanceList.forEach((instance) => {
let fileName = instance.Path.split('/').pop()
let obj = {
name: `${data.SubjectCode}_${data.VisitName}/${study.StudyCode
}_${study.StudyTime.split(' ')[0]}_${series.Modality
}/${fileName}`,
url: this.OSSclientConfig.basePath + instance.Path,
}
files.push(obj)
})
}
})
}
})
}
return { files, name }
},
//
async downloadImageSuccess() {
try {
let params = {
TrialImageDownloadId: this.downloadId,
}
await downloadImageSuccess(params)
} catch (err) {
console.log(err)
}
},
// calledAE
async getDicomCalledAEList() {
try {
let res = await getDicomCalledAEList()
if (res.IsSuccess) {
this.calledAeList = res.Result
}
} catch (err) {
console.log(err)
}
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
async getHospitalGroupList() {
try {
let res = await getHospitalGroupList({})
@ -301,17 +448,6 @@ export default {
console.log(err)
}
},
// calledAE
async getDicomCalledAEList() {
try {
let res = await getDicomCalledAEList()
if (res.IsSuccess) {
this.calledAeList = res.Result
}
} catch (err) {
console.log(err)
}
},
// callingAE
async getDicomCallingAEList() {
try {

View File

@ -230,7 +230,7 @@ export default {
this.activeName = this.$router.currentRoute.query.tabName ? this.$router.currentRoute.query.tabName : 'read'
this.tabs.push(this.activeName)
this.trialId = this.$router.currentRoute.query.trialId
this.subjectCode = this.$router.currentRoute.query.subjectCode
this.subjectCode = unescape(this.$router.currentRoute.query.subjectCode)
this.subjectId = this.$router.currentRoute.query.subjectId
this.visitTaskId = this.$router.currentRoute.query.visitTaskId
// this.isReadingTaskViewInOrder = parseInt(this.$router.currentRoute.query.isReadingTaskViewInOrder)

View File

@ -219,9 +219,9 @@ export default {
var path = "";
var criterionName = this.trialCriterionInfo.TrialReadingCriterionName;
if (this.readingTool === 0) {
path = `/readingDicoms?TrialReadingCriterionId=${this.trialCriterionInfo.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&isReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}&criterionType=${this.criterionType}&criterionName=${criterionName}&readingTool=${this.readingTool}&TokenKey=${token}`;
path = `/readingDicoms?TrialReadingCriterionId=${this.trialCriterionInfo.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${escape(row.SubjectCode)}&subjectId=${row.SubjectId}&isReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}&criterionType=${this.criterionType}&criterionName=${criterionName}&readingTool=${this.readingTool}&TokenKey=${token}`;
} else {
path = `/noneDicomReading?TrialReadingCriterionId=${this.trialCriterionInfo.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&isReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}&criterionType=${this.criterionType}&criterionName=${criterionName}&readingTool=${this.readingTool}&TokenKey=${token}`;
path = `/noneDicomReading?TrialReadingCriterionId=${this.trialCriterionInfo.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${escape(row.SubjectCode)}&subjectId=${row.SubjectId}&isReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}&criterionType=${this.criterionType}&criterionName=${criterionName}&readingTool=${this.readingTool}&TokenKey=${token}`;
}
var routeData = this.$router.resolve({ path });
const res = await getAutoCutNextTask()

View File

@ -2,74 +2,29 @@
<BaseContainer>
<template slot="search-container">
<el-form :inline="true">
<el-form-item
:label="$t('trials:downloadRecord:table:SubjectCode')"
prop="SubjectCode"
>
<el-input
v-model="searchData.SubjectCode"
size="small"
clearable
style="width: 120px"
/>
<el-form-item :label="$t('trials:downloadRecord:table:SubjectCode')" prop="SubjectCode">
<el-input v-model="searchData.SubjectCode" size="small" clearable style="width: 120px" />
</el-form-item>
<el-form-item :label="$t('trials:downloadRecord:table:IP')" prop="IP">
<el-input
v-model="searchData.IP"
size="small"
clearable
style="width: 120px"
/>
<el-input v-model="searchData.IP" size="small" clearable style="width: 120px" />
</el-form-item>
<el-form-item
:label="$t('trials:downloadRecord:table:VisitName')"
prop="VisitName"
>
<el-input
v-model="searchData.VisitName"
size="small"
clearable
style="width: 120px"
/>
<el-form-item :label="$t('trials:downloadRecord:table:VisitName')" prop="VisitName">
<el-input v-model="searchData.VisitName" size="small" clearable style="width: 120px" />
</el-form-item>
<el-form-item
:label="$t('trials:downloadRecord:table:Name')"
prop="LoginUserName"
>
<el-input
v-model="searchData.Name"
size="small"
clearable
style="width: 120px"
/>
<el-form-item :label="$t('trials:downloadRecord:table:Name')" prop="LoginUserName">
<el-input v-model="searchData.Name" size="small" clearable style="width: 120px" />
</el-form-item>
<el-form-item
:label="$t('trials:downloadRecord:table:userType')"
prop="UserTypeEnum"
>
<el-select
v-model="searchData.UserTypeEnum"
clearable
style="width: 120px"
>
<el-option
v-for="item of userTypeOptions"
:key="item.Id"
:label="item.UserType"
:value="item.UserTypeEnum"
/>
<el-form-item :label="$t('trials:downloadRecord:table:userType')" prop="UserTypeEnum">
<el-select v-model="searchData.UserTypeEnum" clearable style="width: 120px">
<el-option v-for="item of userTypeOptions" :key="item.Id" :label="item.UserType"
:value="item.UserTypeEnum" />
</el-select>
</el-form-item>
<!-- 下载日期 -->
<el-form-item :label="$t('trials:downloadRecord:table:DownloadTime')">
<el-date-picker
v-model="dateValue"
type="datetimerange"
range-separator="-"
<el-date-picker v-model="dateValue" type="datetimerange" range-separator="-"
:start-placeholder="$t('trials:uploadClinicalData:table:beginDate')"
:end-placeholder="$t('trials:uploadClinicalData:table:endDate')"
:default-time="['00:00:00', '23:59:59']"
>
:end-placeholder="$t('trials:uploadClinicalData:table:endDate')" :default-time="['00:00:00', '23:59:59']">
</el-date-picker>
</el-form-item>
<el-form-item>
@ -77,105 +32,49 @@
{{ $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>
</template>
<template slot="main-container">
<el-table
v-loading="loading"
v-adaptive="{ bottomOffset: 60 }"
height="100"
:data="list"
class="table"
@sort-change="handleSortByColumn"
:default-sort="{ prop: 'DownloadTime', order: 'descending' }"
>
<el-table v-loading="loading" v-adaptive="{ bottomOffset: 60 }" height="100" :data="list" class="table"
@sort-change="handleSortByColumn" :default-sort="{ prop: 'DownloadTime', order: 'descending' }">
<el-table-column type="index" width="50" />
<!--受试者-->
<el-table-column
:label="$t('trials:downloadRecord:table:SubjectCode')"
prop="SubjectCode"
min-width="90"
show-overflow-tooltip
sortable="custom"
>
<el-table-column :label="$t('trials:downloadRecord:table:SubjectCode')" prop="SubjectCode" min-width="90"
show-overflow-tooltip sortable="custom">
</el-table-column>
<!--ip-->
<el-table-column
:label="$t('trials:downloadRecord:table:IP')"
prop="IP"
min-width="90"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column :label="$t('trials:downloadRecord:table:IP')" prop="IP" min-width="90" show-overflow-tooltip
sortable="custom" />
<!--访视名-->
<el-table-column
:label="$t('trials:downloadRecord:table:VisitName')"
prop="VisitName"
min-width="90"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column :label="$t('trials:downloadRecord:table:VisitName')" prop="VisitName" min-width="90"
show-overflow-tooltip sortable="custom" />
<!--下载用户名称-->
<el-table-column
:label="$t('trials:downloadRecord:table:DownloadUserName')"
prop="DownloadUserName"
min-width="90"
show-overflow-tooltip
sortable="custom"
>
<el-table-column :label="$t('trials:downloadRecord:table:DownloadUserName')" prop="DownloadUserName"
min-width="90" show-overflow-tooltip sortable="custom">
</el-table-column>
<!--下载用户别名--->
<el-table-column
:label="$t('trials:downloadRecord:table:DownLoadUserFullName')"
prop="DownLoadUserFullName"
min-width="90"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column :label="$t('trials:downloadRecord:table:DownLoadUserFullName')" prop="DownLoadUserFullName"
min-width="90" show-overflow-tooltip sortable="custom" />
<!--用户类型--->
<el-table-column
:label="$t('trials:downloadRecord:table:userType')"
prop="UserTypeEnum"
min-width="90"
show-overflow-tooltip
sortable="custom"
>
<el-table-column :label="$t('trials:downloadRecord:table:userType')" prop="UserTypeEnum" min-width="90"
show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{ $fd('UserType', scope.row.UserTypeEnum) }}
</template>
</el-table-column>
<!--检查数量--->
<el-table-column
:label="$t('trials:downloadRecord:table:StudyCount')"
prop="StudyCount"
min-width="90"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column :label="$t('trials:downloadRecord:table:StudyCount')" prop="StudyCount" min-width="90"
show-overflow-tooltip sortable="custom" />
<!--下载图像数量--->
<el-table-column
:label="$t('trials:downloadRecord:table:VisitImageFileCount')"
prop="ImageCount"
min-width="90"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column :label="$t('trials:downloadRecord:table:VisitImageFileCount')" prop="ImageCount" min-width="90"
show-overflow-tooltip sortable="custom" />
<!--下载图像大小--->
<el-table-column
:label="$t('trials:downloadRecord:table:VisitImageFileSize')"
prop="ImageSize"
min-width="90"
show-overflow-tooltip
sortable="custom"
>
<el-table-column :label="$t('trials:downloadRecord:table:VisitImageFileSize')" prop="ImageSize" min-width="90"
show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
<span>{{
scope.row.ImageSize && scope.row.ImageSize > 0
@ -185,42 +84,50 @@
</template>
</el-table-column>
<!--下载时间--->
<el-table-column
:label="$t('trials:downloadRecord:table:DownloadStartTime')"
prop="DownloadStartTime"
min-width="130"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column :label="$t('trials:downloadRecord:table:DownloadStartTime')" prop="DownloadStartTime"
min-width="130" show-overflow-tooltip sortable="custom" />
<!-- 下载结束时间 -->
<el-table-column
prop="DownloadEndTime"
min-width="130"
:label="$t('trials:downloadRecord:table:downloadEndTime')"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column prop="DownloadEndTime" min-width="130"
:label="$t('trials:downloadRecord:table:downloadEndTime')" show-overflow-tooltip sortable="custom" />
<!-- 总共用时 -->
<el-table-column
prop="UploadIntervalStr"
min-width="100"
:label="$t('trials:downloadRecord:table:totalTime')"
show-overflow-tooltip
>
<el-table-column prop="UploadIntervalStr" min-width="100" :label="$t('trials:downloadRecord:table:totalTime')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.UploadIntervalStr }}
</template>
</el-table-column>
<!--操作-->
<el-table-column :label="$t('common:action:action')" min-width="100" fixed="right">
<template slot-scope="scope">
<el-button circle icon="el-icon-info" :title="$t('trials:downloadRecord:button:view')"
@click.stop="view(scope.row)" />
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
class="page"
:total="total"
:page.sync="searchData.PageIndex"
:limit.sync="searchData.PageSize"
@pagination="getList"
/>
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</template>
<base-model v-if="detail_model.visible" :config="detail_model">
<template slot="dialog-body">
<el-table :data="detailData" style="width: 100%">
<el-table-column prop="PatientId" :label="$t('trials:downloadRecord:table:PatientId')" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="StudyInstanceUid" :label="$t('trials:downloadRecord:table:StudyInstanceUid')"
show-overflow-tooltip>
</el-table-column>
<el-table-column prop="ImageCount" :label="$t('trials:downloadRecord:table:ImageCount')"
show-overflow-tooltip>
</el-table-column>
</el-table>
</template>
<template slot="dialog-footer">
<!-- 保存 -->
<el-button size="small" type="primary" @click="detail_model.visible = false">
{{ $t("common:button:confirm") }}
</el-button>
</template>
</base-model>
</BaseContainer>
</template>
<script>
@ -228,6 +135,7 @@ import { getUserTypeListByUserType } from '@/api/admin'
import { getTrialSubjectVisitDownloadList } from '@/api/inspection'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import BaseModel from '@/components/BaseModel'
const searchDataDefault = () => {
return {
TrialId: '',
@ -244,7 +152,14 @@ const searchDataDefault = () => {
}
}
export default {
components: { BaseContainer, Pagination },
components: { BaseContainer, Pagination, BaseModel },
name: "downloadRecord",
props: {
isSystem: {
type: Boolean,
default: false
}
},
data() {
return {
userTypeOptions: [],
@ -253,6 +168,13 @@ export default {
total: 0,
loading: false,
dateValue: [],
detail_model: {
visible: false,
title: this.$t('trials:inspection:dialogTitle:detail'),
width: '800px',
appendToBody: true
},
detailData: []
}
},
mounted() {
@ -260,6 +182,10 @@ export default {
this.getUserType()
},
methods: {
view(row) {
this.detailData = JSON.parse(row.DownloadListStr).DownLoadList
this.detail_model.visible = true
},
//
getUserType() {
getUserTypeListByUserType(0).then((res) => {
@ -282,7 +208,9 @@ export default {
},
getList() {
this.loading = true
this.searchData.TrialId = this.$route.query.trialId
if (!this.isSystem) {
this.searchData.TrialId = this.$route.query.trialId
}
if (this.dateValue && this.dateValue[0] && this.dateValue[1]) {
this.searchData.BeginDownloadTime = this.$moment(
this.dateValue[0]