irc_web/src/views/trials/trials-panel/study/index.vue

529 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<BaseContainer>
<!-- 搜索框 -->
<template slot="search-container">
<el-form :inline="true">
<!-- Site -->
<el-form-item :label="$t('trials:studyList:table:site')">
<el-select v-model="searchData.SiteId" clearable filterable style="width: 150px">
<el-option v-for="item of siteOptions" :key="item.SiteId" :label="item.TrialSiteCode"
:value="item.SiteId" />
</el-select>
</el-form-item>
<!-- Subject -->
<el-form-item :label="$t('trials:studyList:table:subject')">
<el-input v-model="searchData.SubjectInfo" style="width: 140px" />
</el-form-item>
<!-- Visit -->
<el-form-item :label="$t('trials:studyList:table:visit')">
<el-select v-model="searchData.VisitPlanArray" clearable filterable multiple collapse-tags>
<el-option v-for="(item, index) of visitPlanOptions" :key="index" :label="item.VisitName"
:value="item.VisitNum">
<span style="float: left">{{ item.VisitName }}</span>
</el-option>
<el-option key="Other" label="Out of Plan" value="1.11" />
</el-select>
</el-form-item>
<el-form-item :label="$t('trials:studyList:table:isFromPacs')">
<el-select v-model="searchData.IsFromPACS" clearable>
<el-option v-for="item of $d.UploadType" :key="item.id" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t("common:button:search") }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t("common:button:reset") }}
</el-button>
</el-form-item>
</el-form>
</template>
<!-- 检查列表 -->
<template slot="main-container">
<el-table v-loading="loading" v-adaptive="{ bottomOffset: 60 }" :data="list" stripe height="100"
@sort-change="handleSortByColumn">
<el-table-column type="index" width="40" />
<!-- Study ID -->
<el-table-column prop="StudyCode" :label="$t('trials:studyList:table:studyId')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- Image Type -->
<el-table-column prop="IsDicom" :label="$t('trials:studyList:table:imageType')" sortable="custom"
min-width="100">
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDicom" type="primary">DICOM</el-tag>
<el-tag v-else type="danger">Non-DICOM</el-tag>
</template>
</el-table-column>
<el-table-column prop="StudyTime" sortable="custom" min-width="150" show-overflow-tooltip>
<template slot="header">
<el-tooltip placement="top">
<!-- Modality,Body Part Examined,Count,Study Date -->
<!-- Study Info -->
<div slot="content">
{{
`${$t("trials:studyList:table:modality")}, ${$t(
"trials:studyList:table:bodyPart"
)}, ${$t("trials:studyList:table:studyDate")}, ${$t(
"trials:studyList:table:count"
)}`
}}
</div>
<span>{{ $t("trials:studyList:table:studyInfo") }}</span>
</el-tooltip>
</template>
<template slot-scope="scope">
<div style="display: flex; align-items: center">
<div v-if="scope.row.Modalities">
{{ scope.row.Modalities }}({{ scope.row.Count }}),
</div>
<div v-else style="color: #f44336">N/A,</div>
<div v-if="scope.row.Bodypart" style="margin: 0 3px">
{{ getBodyPart(scope.row.Bodypart, scope.row.BodyPartForEditOther) }},
</div>
<div v-else style="color: #f44336; margin: 0 3px">N/A,</div>
<div v-if="scope.row.StudyTime">
{{ moment(scope.row.StudyTime).format("YYYY-MM-DD") }}
</div>
<div v-else style="color: #f44336">N/A</div>
<div>
<!-- <el-popover v-if="scope.row.Count && scope.row.IsDicom" trigger="click" placement="bottom">
<el-table v-loading="seriesLoading" :data="seriesList" height="200" size="small" class="seriesListOfStudy">
<el-table-column
prop="SeriesNumber"
:label="$t('trials:studyList:table:seriesNum')"
width="120"
/>
<el-table-column prop="Modality" :label="$t('trials:studyList:table:modality')" width="120" />
<el-table-column
prop="Description"
:label="$t('trials:studyList:table:description')"
width="150"
show-overflow-tooltip
/>
<el-table-column
prop="SliceThickness"
:label="$t('trials:studyList:table:sliceThickness')"
width="120"
/>
<el-table-column
prop="InstanceCount"
:label="$t('trials:studyList:table:instanceCount')"
width="120"
/>
<el-table-column :label="$t('common:action:action')" width="80">
<template slot-scope="series">
<router-link
style="color: #428bca; padding: 2px 5px"
tag="a"
:to="{
path: `/showdicom?studyId=${scope.row.Id}&studyCode=${scope.row.StudyCode}&modality=${scope.row.Modalities}&description=${scope.row.Description}&series=${JSON.stringify(series.row)}&type=Series&TokenKey=${tokenKey}`,
}"
target="_blank"
>
{{ $t('trials:studyList:button:view') }}
</router-link>
</template>
</el-table-column>
</el-table>
<div slot="reference" class="name-wrapper">
<span style="color:#428bca;cursor:pointer;" @click="handleGetseriesList(scope.row)">
{{ `${scope.row.Count}` }}
</span>
</div>
</el-popover>
<el-button v-else-if="scope.row.Count && !scope.row.IsDicom" type="text"
@click.native.prevent="handlePreviewNonDicom(scope.row)">
{{ scope.row.Count }}
</el-button> -->
<!-- <span>({{ scope.row.Count }})</span> -->
<!-- <span v-else style="color:#f44336;">N/A</span> -->
</div>
</div>
</template>
</el-table-column>
<el-table-column min-width="120" show-overflow-tooltip>
<template slot="header">
<el-tooltip placement="top">
<div slot="content">
{{
`${$t("trials:studyList:table:siteId")}, ${$t(
"trials:studyList:table:siteName"
)}`
}}
</div>
<span>{{ $t("trials:studyList:table:site") }}</span>
</el-tooltip>
</template>
<template slot-scope="scope">
<span v-if="scope.row.TrialSiteCode">{{ scope.row.TrialSiteCode }},
</span>
<span v-else style="color: #f44336">N/A, </span>
<span v-if="scope.row.TrialSiteAliasName">{{
scope.row.TrialSiteAliasName
}}</span>
<span v-else style="color: #f44336">N/A</span>
</template>
</el-table-column>
<el-table-column min-width="120" prop="SubjectCode" :label="$t('trials:studyList:table:subject')" sortable="custom"
show-overflow-tooltip />
<el-table-column min-width="90" show-overflow-tooltip>
<template slot="header">
<el-tooltip placement="top">
<div slot="content">
{{
`${$t("trials:studyList:table:visitName")}, ${$t(
"trials:studyList:table:visitNum"
)}`
}}<br />
</div>
<span>{{ $t("trials:studyList:table:visit") }}</span>
</el-tooltip>
</template>
<template slot-scope="scope">
<span v-if="scope.row.VisitName">{{ scope.row.VisitName }}, </span>
<span v-else style="color: #f44336">N/A, </span>
<span>{{ scope.row.VisitNum }}</span>
</template>
</el-table-column>
<el-table-column prop="Uploader" :label="$t('trials:studyList:table:uploader')" show-overflow-tooltip
sortable="custom" min-width="80" />
<el-table-column prop="UploadTime" :label="$t('trials:studyList:table:uploadTime')" show-overflow-tooltip
sortable="custom" min-width="120" />
<el-table-column prop="IsFromPACS" :label="$t('trials:studyList:table:isFromPacs')" show-overflow-tooltip
sortable="custom" min-width="120">
<template slot-scope="scope">
<el-tag :type="scope.row.IsFromPACS ? 'warning' : ''">
{{ $fd("UploadType", scope.row.IsFromPACS) }}
</el-tag>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" min-width="100" fixed="right">
<template slot-scope="scope">
<!-- 查看 -->
<el-button :disabled="!scope.row.Count" circle :title="$t('trials:studyList:button:view')"
icon="el-icon-view" @click="handleViewStudy(scope.row)" />
<!-- 分享检查 -->
<el-button v-hasPermi="['trials:trials-panel:study:share']"
:disabled="!(scope.row.Count > 0 && scope.row.IsDicom)" circle
:title="$t('trials:studyList:action:share')" icon="el-icon-share" @click="handleShareImage(scope.row)" />
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</template>
<base-model :config="share_model">
<template slot="dialog-body">
<div>
<i style="color: #428bca" class="el-icon-success" />
<span>成功创建分享链接</span>
</div>
<div style="margin: 10px 0">
链接<el-input v-model="shareLink" readonly style="width: 420px" />
</div>
<div style="margin-bottom: 20px">
提取码:
<el-input v-model="extractionCode" style="width: 100px" readonly />
</div>
<div>
<el-button type="primary" round @click="copyCode">复制链接及提取码</el-button>
</div>
</template>
</base-model>
<!-- 预览非Dicom影像 -->
<el-dialog v-if="previewNonDicomVisible" :visible.sync="previewNonDicomVisible"
:title="$t('trials:studyList:dialogTitle:view')" :fullscreen="true" append-to-body
custom-class="base-dialog-wrapper">
<div class="base-modal-body" style="border: 2px solid #ccc; padding: 10px">
<NonDicomPreview v-if="previewNonDicomVisible" :none-dicom-id="rowData.Id" />
</div>
</el-dialog>
</BaseContainer>
</template>
<script>
import {
getDicomAndNoneDicomStudyList,
getTrialSiteSelect,
getSeriesList,
getTrialVisitStageSelect,
getNoneDicomStudyFileList,
} from "@/api/trials";
import { createImageShare } from "@/api/share";
import BaseContainer from "@/components/BaseContainer";
import Pagination from "@/components/Pagination";
import BaseModel from "@/components/BaseModel";
import NonDicomPreview from "./components/nonDicomPreview";
import moment from "moment";
import { getToken } from "@/utils/auth";
const searchDataDefault = () => {
return {
SubjectInfo: "",
VisitPlanArray: [],
IsFromPACS: null,
Status: "",
SiteId: "",
PageIndex: 1,
PageSize: 20,
};
};
export default {
name: "StudyList",
components: { BaseContainer, Pagination, BaseModel, NonDicomPreview },
data() {
return {
searchData: searchDataDefault(),
list: [],
loading: false,
total: 0,
siteOptions: [],
visitPlanOptions: [],
seriesList: [],
nonDicomfilesList: [],
rowData: {},
userTypeEnumInt: zzSessionStorage.getItem("userTypeEnumInt") * 1,
share_model: {
visible: false,
title: "影像分享",
showClose: true,
width: "500px",
},
shareLink: "",
extractionCode: "",
moment,
filesLoading: "",
seriesLoading: "",
previewNonDicomVisible: false,
trialId: this.$route.query.trialId,
tokenKey: getToken(),
openWindow: null,
BodyPart: {}
};
},
async mounted() {
this.BodyPart.Bodypart = await this.$getBodyPart(this.$route.query.trialId)
this.getSite();
this.getVisitPlanOptions();
this.getList();
},
methods: {
getBodyPart(bodyPart, other) {
if (!bodyPart && !other) return ''
var separator = ','
if (bodyPart.indexOf('|') > -1) {
separator = '|'
} else if (bodyPart.indexOf(',') > -1) {
separator = ','
} else if (bodyPart.indexOf('') > -1) {
separator = ''
}
var arr = bodyPart.split(separator)
var newArr = arr.map((i) => {
return this.$fd('Bodypart', i.trim(), 'Code', this.BodyPart, 'Name')
})
if (other) {
newArr.push(other)
}
newArr = newArr.filter(Boolean)
return newArr.join(' | ')
},
// 获取检查列表数据
getList() {
this.loading = true;
this.searchData.TrialId = this.trialId;
getDicomAndNoneDicomStudyList(this.searchData)
.then((res) => {
this.loading = false;
this.list = res.Result.CurrentPageData;
this.total = res.Result.TotalCount;
})
.catch(() => {
this.loading = false;
});
},
// 获取某个Dicom检查下的序列信息
handleGetseriesList(row) {
this.seriesLoading = true;
this.seriesList = [];
getSeriesList(row.Id)
.then((res) => {
this.seriesLoading = false;
this.seriesList = res.Result;
})
.catch(() => {
this.seriesLoading = false;
});
},
// 获取某个非Dicom检查下的文件
handleGetFilesList(id) {
this.filesLoading = true;
getNoneDicomStudyFileList(id)
.then((res) => {
this.nonDicomfilesList = res.Result;
this.filesLoading = false;
})
.catch(() => {
this.filesLoading = false;
});
},
// 预览当前检查下的影像
handleViewStudy(row) {
if (this.openWindow) {
this.openWindow.close();
}
if (row.IsDicom) {
const routeData = this.$router.resolve({
path: `/showdicom?studyId=${row.Id}&TokenKey=${this.tokenKey}&type=Study`,
});
this.openWindow = window.open(routeData.href, "_blank");
} else {
// this.rowData = { ...row }
// this.previewNonDicomVisible = true
let trialId = this.$route.query.trialId;
const routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${row.SubjectVisitId}&studyId=${row.Id}&TokenKey=${this.tokenKey}`,
});
this.openWindow = window.open(routeData.href, "_blank");
}
},
handlePreviewNonDicom(row) {
if (this.openWindow) {
this.openWindow.close();
}
let trialId = this.$route.query.trialId;
const routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${row.SubjectVisitId}&studyId=${row.Id}&TokenKey=${this.tokenKey}`,
});
this.openWindow = window.open(routeData.href, "_blank");
},
// 预览
handlePreview(row) {
if (this.openWindow) {
this.openWindow.close();
}
this.openWindow = window.open(row.FullFilePath, "_blank");
},
// 格式化状态码
studyStatusFormatter(StudyStatus) {
if (StudyStatus === 5) return "Uploaded";
else if (StudyStatus === 1) return "Uploading";
else if (StudyStatus === 7) return "QA Requested";
else if (StudyStatus === 10) return "In QA";
else if (StudyStatus === 25) return "QA-Passed";
else if (StudyStatus === 26) return "QA-Failed";
else if (StudyStatus === 28) return "Anonymizing";
else if (StudyStatus === 30) return "Anonymized";
else if (StudyStatus === 32) return "Anonymizing Failed";
else if (StudyStatus === 34) return "Forwarding";
else if (StudyStatus === 36) return "Forwarded";
else if (StudyStatus === 38) return "Forwarding Failed";
else return "";
},
// 获取site下拉框数据
getSite() {
getTrialSiteSelect(this.trialId).then((res) => {
this.siteOptions = res.Result;
});
},
// 获取visit下拉框数据
getVisitPlanOptions() {
getTrialVisitStageSelect(this.trialId).then((res) => {
this.visitPlanOptions = res.Result;
});
},
// 分享影像链接
handleShareImage(row) {
this.shareLink = "";
this.extractionCode = "";
const params = {
TrialId: row.TrialId,
SiteId: row.SiteId,
SubjectId: row.SubjectId,
StudyId: row.Id,
};
this.loading = true;
createImageShare(params)
.then((res) => {
this.loading = false;
if (res.IsSuccess) {
// this.shareLink = `${window.location.origin}${window.location.pathname}#/imagesShare?id=${res.Result.ResourceId}`
this.shareLink = `${window.location.origin}/imagesShare?id=${res.Result.ResourceId}`;
this.extractionCode = res.Result.Password;
this.share_model.visible = true;
}
})
.catch(() => {
this.loading = false;
});
},
// getBodyPart(bodyPart) {
// if (!bodyPart) return "";
// var separator = ",";
// if (bodyPart.indexOf("|") > -1) {
// separator = "|";
// } else if (bodyPart.indexOf(",") > -1) {
// separator = ",";
// } else if (bodyPart.indexOf("") > -1) {
// separator = "";
// }
// var arr = bodyPart.split(separator);
// var newArr = arr.map((i) => {
// return this.$fd(
// "Bodypart",
// i.trim(),
// "Code",
// { Bodypart: this.bp },
// "Name"
// );
// });
// return newArr.join(" | ");
// },
// 复制
copyCode() {
this.$copyText(`链接: ${this.shareLink} 提取码: ${this.extractionCode}`)
.then((res) => {
this.$message.success("复制成功");
})
.catch(() => {
this.$alert("复制失败");
});
},
onCopyError() {
this.$alert("复制失败");
},
// 查询
handleSearch() {
this.searchData.PageIndex = 1;
this.getList();
},
// 重置
handleReset() {
this.searchData = searchDataDefault();
this.getList();
},
// 排序
handleSortByColumn(column) {
if (column.order === "ascending") {
this.searchData.Asc = true;
} else {
this.searchData.Asc = false;
}
this.searchData.SortField = column.prop;
this.searchData.PageIndex = 1;
this.getList();
},
},
};
</script>