<template>
|
<div class="case-detail">
|
<!-- 基本信息模块 -->
|
<el-card class="detail-section">
|
<div slot="header" class="section-header">
|
<span class="section-title">基本信息</span>
|
</div>
|
<el-descriptions :column="2" border>
|
<el-descriptions-item label="案例编号">{{
|
caseData.caseNo || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="捐献者姓名">{{
|
caseData.name || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="性别">
|
<dict-tag
|
:options="dict.type.sys_user_sex"
|
:value="caseData.sex ? parseInt(caseData.sex) : ''"
|
/>
|
</el-descriptions-item>
|
<el-descriptions-item label="年龄">
|
{{ caseData.age || "-"
|
}}{{
|
caseData.ageunit ? `(${getAgeUnitText(caseData.ageunit)})` : ""
|
}}
|
</el-descriptions-item>
|
<el-descriptions-item label="血型">
|
<dict-tag
|
:options="dict.type.sys_BloodType"
|
:value="caseData.bloodType"
|
/>
|
</el-descriptions-item>
|
<el-descriptions-item label="Rh阴性">
|
{{
|
caseData.rhYin === "1" ? "是" : caseData.rhYin === "0" ? "否" : "-"
|
}}
|
</el-descriptions-item>
|
<el-descriptions-item label="证件类型">
|
{{ getIdCardTypeText(caseData.idcardtype) }}
|
</el-descriptions-item>
|
<el-descriptions-item label="证件号码">{{
|
caseData.idcardno || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="民族">{{
|
caseData.nation || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="国籍">{{
|
caseData.nationality || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="籍贯">{{
|
caseData.nativeplace || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="学历">{{
|
caseData.education || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="职业">{{
|
caseData.occupation || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="出生日期">{{
|
formatDate(caseData.birthday)
|
}}</el-descriptions-item>
|
<el-descriptions-item label="联系电话">{{
|
caseData.phone || "-"
|
}}</el-descriptions-item>
|
</el-descriptions>
|
</el-card>
|
|
<!-- 地址信息模块 -->
|
<el-card class="detail-section">
|
<div slot="header" class="section-header">
|
<span class="section-title">地址信息</span>
|
</div>
|
<el-descriptions :column="1" border>
|
<el-descriptions-item label="户籍地址">
|
{{ getFullRegisterAddress() }}
|
</el-descriptions-item>
|
<el-descriptions-item label="现住地址">
|
{{ getFullResidenceAddress() }}
|
</el-descriptions-item>
|
</el-descriptions>
|
</el-card>
|
|
<!-- 医疗信息模块 -->
|
<el-card class="detail-section">
|
<div slot="header" class="section-header">
|
<span class="section-title">医疗信息</span>
|
</div>
|
<el-descriptions :column="1" border>
|
<el-descriptions-item label="疾病诊断名称">{{
|
caseData.diagnosisname || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="病情概况">{{
|
caseData.illnessoverview || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="病人状况">{{
|
caseData.patientstate || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="GCS评分">{{
|
caseData.gcsScore || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="传染病情况">{{
|
caseData.infectious || "无"
|
}}</el-descriptions-item>
|
<el-descriptions-item
|
label="传染病其他说明"
|
v-if="caseData.infectiousOther"
|
>{{ caseData.infectiousOther }}</el-descriptions-item
|
>
|
<el-descriptions-item label="是否需要转运">
|
{{ caseData.isTransport === "2" ? "需要" : "不需要" }}
|
</el-descriptions-item>
|
</el-descriptions>
|
</el-card>
|
|
<!-- 医院信息模块 -->
|
<el-card class="detail-section">
|
<div slot="header" class="section-header">
|
<span class="section-title">医院信息</span>
|
</div>
|
<el-descriptions :column="2" border>
|
<el-descriptions-item label="治疗医院名称">{{
|
caseData.treatmenthospitalname || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="治疗科室名称">{{
|
caseData.treatmentdeptname || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="住院号">{{
|
caseData.inpatientno || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="部门名称">{{
|
caseData.deptName || "-"
|
}}</el-descriptions-item>
|
</el-descriptions>
|
</el-card>
|
|
<!-- 上报信息模块 -->
|
<el-card class="detail-section">
|
<div slot="header" class="section-header">
|
<span class="section-title">上报信息</span>
|
</div>
|
<el-descriptions :column="2" border>
|
<el-descriptions-item label="报告者姓名">{{
|
caseData.infoName || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="报告者编号">{{
|
caseData.infoNo || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="报告者联系电话">{{
|
caseData.reporterphone || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="协调员姓名">{{
|
caseData.coordinatorName || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="协调员编号">{{
|
caseData.coordinatorNo || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="上报状态">
|
<el-tag :type="getStatusType(caseData.reportStatus)">
|
{{ getStatusText(caseData.reportStatus) }}
|
</el-tag>
|
</el-descriptions-item>
|
<el-descriptions-item label="报告时间">{{
|
formatDateTime(caseData.reporttime)
|
}}</el-descriptions-item>
|
<el-descriptions-item label="是否终止案例">
|
{{ caseData.terminationCase === "1" ? "已终止" : "进行中" }}
|
</el-descriptions-item>
|
</el-descriptions>
|
</el-card>
|
|
<!-- 附件信息模块 -->
|
<el-card class="detail-section" v-if="attachmentList.length > 0">
|
<div slot="header" class="section-header">
|
<span class="section-title">附件信息</span>
|
</div>
|
<el-table :data="attachmentList" style="width: 100%">
|
<el-table-column label="文件名" width="300">
|
<template slot-scope="scope">
|
<i class="el-icon-document" style="margin-right: 8px;"></i>
|
<span>{{ scope.row.fileName }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="文件类型" width="120">
|
<template slot-scope="scope">
|
<el-tag size="small">{{ scope.row.fileType }}</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="大小" width="100">
|
<template slot-scope="scope">
|
<span>{{ formatFileSize(scope.row.fileSize) }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="上传时间" width="180">
|
<template slot-scope="scope">
|
<span>{{ scope.row.uploadTime }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="操作">
|
<template slot-scope="scope">
|
<el-button size="mini" @click="handlePreview(scope.row)"
|
>预览</el-button
|
>
|
<el-button
|
size="mini"
|
type="success"
|
@click="handleDownload(scope.row)"
|
>下载</el-button
|
>
|
</template>
|
</el-table-column>
|
</el-table>
|
</el-card>
|
|
<!-- 审批信息模块(状态为已同意或已驳回时显示) -->
|
<el-card
|
class="detail-section"
|
v-if="caseData.reportStatus === '3' || caseData.reportStatus === '4'"
|
>
|
<div slot="header" class="section-header">
|
<span class="section-title">审批信息</span>
|
</div>
|
<el-descriptions :column="1" border>
|
<el-descriptions-item label="审批结果">
|
<el-tag :type="caseData.reportStatus === '3' ? 'success' : 'danger'">
|
{{ caseData.reportStatus === "3" ? "已同意" : "已驳回" }}
|
</el-tag>
|
</el-descriptions-item>
|
<el-descriptions-item label="审批时间">{{
|
formatDateTime(caseData.updateTime)
|
}}</el-descriptions-item>
|
<el-descriptions-item label="审批人">{{
|
caseData.updateBy || "-"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="审批意见">{{
|
caseData.remark || "无"
|
}}</el-descriptions-item>
|
</el-descriptions>
|
</el-card>
|
|
<!-- PDF预览弹窗 -->
|
<el-dialog
|
:title="previewTitle"
|
:append-to-body="true"
|
:visible.sync="pdfPreviewVisible"
|
width="90%"
|
top="5vh"
|
:close-on-click-modal="true"
|
class="pdf-preview-dialog"
|
@close="handlePdfDialogClose"
|
>
|
<div class="pdf-preview-container" v-loading="pdfLoading">
|
<!-- PDF控制工具栏 -->
|
<div class="pdf-toolbar">
|
<el-button-group>
|
<el-button
|
size="mini"
|
@click="changePage(currentPage - 1)"
|
:disabled="currentPage <= 1"
|
icon="el-icon-arrow-left"
|
>
|
上一页
|
</el-button>
|
<el-button size="mini" disabled>
|
第 {{ currentPage }} 页 / 共 {{ pageCount }} 页
|
</el-button>
|
<el-button
|
size="mini"
|
@click="changePage(currentPage + 1)"
|
:disabled="currentPage >= pageCount"
|
icon="el-icon-arrow-right"
|
>
|
下一页
|
</el-button>
|
</el-button-group>
|
|
<el-button-group class="zoom-controls">
|
<el-button size="mini" @click="zoomOut" :disabled="scale <= 50">
|
<i class="el-icon-zoom-out"></i> 缩小
|
</el-button>
|
<el-button size="mini" disabled> {{ scale }}% </el-button>
|
<el-button size="mini" @click="zoomIn" :disabled="scale >= 200">
|
<i class="el-icon-zoom-in"></i> 放大
|
</el-button>
|
<el-button size="mini" @click="resetZoom">
|
<i class="el-icon-refresh-left"></i> 重置
|
</el-button>
|
</el-button-group>
|
|
<el-button
|
size="mini"
|
type="success"
|
@click="downloadPdf(currentFile)"
|
icon="el-icon-download"
|
>
|
下载
|
</el-button>
|
</div>
|
|
<!-- PDF渲染区域 -->
|
<div class="pdf-viewport">
|
<pdf
|
ref="pdf"
|
:src="pdfUrl"
|
:page="currentPage"
|
:rotate="pageRotate"
|
@num-pages="pageCount = $event"
|
@page-loaded="currentPage = $event"
|
@loaded="loadPdfHandler"
|
@error="pdfErrorHandler"
|
:style="{
|
width: scale + '%',
|
transform: 'scale(' + scale / 100 + ')',
|
transformOrigin: '0 0'
|
}"
|
></pdf>
|
</div>
|
</div>
|
</el-dialog>
|
|
<!-- 图片预览弹窗 -->
|
<el-dialog
|
:append-to-body="true"
|
:title="previewTitle"
|
:visible.sync="imagePreviewVisible"
|
width="60%"
|
top="10vh"
|
:close-on-click-modal="true"
|
>
|
<div class="image-preview-container">
|
<img :src="previewUrl" alt="预览图片" class="preview-image" />
|
</div>
|
</el-dialog>
|
|
<!-- 不支持预览的文件类型 -->
|
<el-dialog
|
:title="previewTitle"
|
:visible.sync="unsupportedPreviewVisible"
|
width="400px"
|
:close-on-click-modal="true"
|
>
|
<div class="unsupported-preview">
|
<el-alert
|
title="该文件格式不支持在线预览,请下载后查看"
|
type="warning"
|
show-icon
|
:closable="false"
|
/>
|
<div style="text-align: center; margin-top: 20px;">
|
<el-button type="primary" @click="handleDownload(currentFile)">
|
<i class="el-icon-download"></i> 下载文件
|
</el-button>
|
</div>
|
</div>
|
</el-dialog>
|
|
<div class="detail-footer" v-if="showtitle">
|
<el-button @click="handleClose">关闭</el-button>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import pdf from "vue-pdf";
|
|
export default {
|
name: "CaseDetail",
|
components: {
|
pdf
|
},
|
props: {
|
caseData: {
|
type: Object,
|
default: () => ({})
|
},
|
showtitle: {
|
type: Boolean,
|
default: true
|
}
|
},
|
dicts: ["sys_user_sex", "sys_BloodType"],
|
filters: {
|
statusFilter(status) {
|
const statusMap = {
|
"0": "warning",
|
"1": "success",
|
"2": "danger"
|
};
|
return statusMap[status];
|
},
|
statusTextFilter(status) {
|
const statusMap = {
|
"0": "待审批",
|
"1": "已通过",
|
"2": "已驳回"
|
};
|
return statusMap[status];
|
}
|
},
|
data() {
|
return {
|
// 预览相关数据(保持原有代码)
|
pdfPreviewVisible: false,
|
imagePreviewVisible: false,
|
unsupportedPreviewVisible: false,
|
pdfLoading: false,
|
pdfUrl: "",
|
currentPage: 1,
|
pageCount: 0,
|
scale: 100,
|
pageRotate: 0,
|
previewTitle: "",
|
previewUrl: "",
|
currentFile: null,
|
|
// 附件列表
|
attachmentList: []
|
};
|
},
|
watch: {
|
caseData: {
|
immediate: true,
|
handler(newVal) {
|
if (newVal && newVal.annexfilesList) {
|
this.loadAttachments(newVal.annexfilesList);
|
}
|
}
|
}
|
},
|
methods: {
|
handleClose() {
|
this.$emit("close");
|
},
|
|
// 加载附件
|
loadAttachments(annexfilesList) {
|
if (!annexfilesList || !Array.isArray(annexfilesList)) {
|
this.attachmentList = [];
|
return;
|
}
|
|
this.attachmentList = annexfilesList.map((file, index) => ({
|
id: index + 1,
|
fileName: file.fileName || `附件${index + 1}`,
|
fileType: this.getFileExtension(file.fileUrl || ""),
|
fileSize: file.fileSize || 0,
|
uploadTime: file.uploadTime || this.formatDateTime(new Date()),
|
fileUrl: file.fileUrl || ""
|
}));
|
},
|
|
// 获取文件扩展名
|
getFileExtension(filename) {
|
return (
|
filename
|
.split(".")
|
.pop()
|
?.toLowerCase() || "unknown"
|
);
|
},
|
|
// 格式化日期时间
|
formatDateTime(dateString) {
|
if (!dateString) return "-";
|
return dateString.replace("T", " ").substring(0, 19);
|
},
|
|
// 格式化日期
|
formatDate(dateString) {
|
if (!dateString) return "-";
|
return dateString.split("T")[0];
|
},
|
|
// 获取完整户籍地址
|
getFullRegisterAddress() {
|
const {
|
registerprovincename,
|
registercityname,
|
registertownname,
|
registercommunityname,
|
registeraddress
|
} = this.caseData;
|
const addressParts = [
|
registerprovincename,
|
registercityname,
|
registertownname,
|
registercommunityname,
|
registeraddress
|
];
|
return addressParts.filter(part => part).join("") || "-";
|
},
|
|
// 获取完整现住地址
|
getFullResidenceAddress() {
|
const {
|
residenceprovincename,
|
residencecountyname,
|
residencetownname,
|
residencecommunityname,
|
residenceaddress
|
} = this.caseData;
|
const addressParts = [
|
residenceprovincename,
|
residencecountyname,
|
residencetownname,
|
residencecommunityname,
|
residenceaddress
|
];
|
return addressParts.filter(part => part).join("") || "-";
|
},
|
|
// 获取状态文本
|
getStatusText(status) {
|
const statusMap = {
|
"1": "已上报",
|
"2": "已阅读",
|
"3": "已同意",
|
"4": "已驳回"
|
};
|
return statusMap[status] || "未知状态";
|
},
|
|
// 获取状态类型
|
getStatusType(status) {
|
const statusMap = {
|
"1": "info",
|
"2": "warning",
|
"3": "success",
|
"4": "danger"
|
};
|
return statusMap[status] || "info";
|
},
|
|
// 获取年龄单位文本
|
getAgeUnitText(unit) {
|
const unitMap = {
|
year: "岁",
|
month: "月",
|
day: "天"
|
};
|
return unitMap[unit] || unit;
|
},
|
|
// 获取证件类型文本
|
getIdCardTypeText(type) {
|
const typeMap = {
|
"1": "身份证",
|
"2": "护照",
|
"3": "军官证"
|
};
|
return typeMap[type] || type || "-";
|
},
|
|
// 文件预览相关方法(保持原有代码)
|
getFileType(fileName) {
|
const extension = fileName
|
.split(".")
|
.pop()
|
.toLowerCase();
|
const imageTypes = ["jpg", "jpeg", "png", "gif", "bmp", "webp"];
|
const pdfTypes = ["pdf"];
|
if (imageTypes.includes(extension)) return "image";
|
if (pdfTypes.includes(extension)) return "pdf";
|
return "other";
|
},
|
|
handlePreview(file) {
|
this.currentFile = file;
|
this.previewTitle = `预览 - ${file.fileName}`;
|
this.previewUrl = file.fileUrl;
|
const fileType = this.getFileType(file.fileName);
|
|
switch (fileType) {
|
case "pdf":
|
this.previewPdf(file);
|
break;
|
case "image":
|
this.previewImage(file);
|
break;
|
default:
|
this.previewUnsupported(file);
|
break;
|
}
|
},
|
|
previewPdf(file) {
|
this.pdfPreviewVisible = true;
|
this.pdfLoading = true;
|
this.currentPage = 1;
|
this.scale = 100;
|
this.pageRotate = 0;
|
this.pdfUrl = file.fileUrl;
|
},
|
|
loadPdfHandler() {
|
this.pdfLoading = false;
|
this.currentPage = 1;
|
},
|
|
pdfErrorHandler(error) {
|
console.error("PDF加载失败:", error);
|
this.pdfLoading = false;
|
this.$message.error("PDF文件加载失败,请尝试下载后查看");
|
this.pdfPreviewVisible = false;
|
},
|
|
changePage(newPage) {
|
if (newPage < 1 || newPage > this.pageCount) return;
|
this.currentPage = newPage;
|
},
|
|
zoomIn() {
|
if (this.scale >= 200) return;
|
this.scale += 10;
|
},
|
|
zoomOut() {
|
if (this.scale <= 50) return;
|
this.scale -= 10;
|
},
|
|
resetZoom() {
|
this.scale = 100;
|
},
|
|
previewImage(file) {
|
this.imagePreviewVisible = true;
|
},
|
|
previewUnsupported(file) {
|
this.unsupportedPreviewVisible = true;
|
},
|
|
handlePdfDialogClose() {
|
this.pdfUrl = "";
|
this.currentPage = 1;
|
this.pageCount = 0;
|
},
|
|
handleDownload(file) {
|
const link = document.createElement("a");
|
link.href = file.fileUrl;
|
link.download = file.fileName;
|
link.style.display = "none";
|
document.body.appendChild(link);
|
link.click();
|
document.body.removeChild(link);
|
this.$message.success("开始下载文件");
|
},
|
|
downloadPdf(file) {
|
this.handleDownload(file);
|
},
|
|
formatFileSize(bytes) {
|
if (bytes === 0) return "0 B";
|
const k = 1024;
|
const sizes = ["B", "KB", "MB", "GB"];
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
|
}
|
}
|
};
|
</script>
|
|
<style scoped>
|
.case-detail {
|
padding: 0 20px;
|
}
|
|
/* PDF预览对话框样式 */
|
.pdf-preview-dialog {
|
margin-top: 5vh !important;
|
}
|
|
.pdf-preview-dialog >>> .el-dialog {
|
min-height: 80vh;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.pdf-preview-dialog >>> .el-dialog__body {
|
flex: 1;
|
padding: 0;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.pdf-preview-container {
|
display: flex;
|
flex-direction: column;
|
height: 100%;
|
}
|
|
/* PDF工具栏样式 */
|
.pdf-toolbar {
|
padding: 15px 20px;
|
background: #f5f7fa;
|
border-bottom: 1px solid #ebeef5;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
flex-wrap: wrap;
|
gap: 10px;
|
}
|
|
.zoom-controls {
|
margin: 0 15px;
|
}
|
|
/* PDF视图区域样式 */
|
.pdf-viewport {
|
flex: 1;
|
overflow: auto;
|
padding: 20px;
|
background: #f8f9fa;
|
display: flex;
|
justify-content: center;
|
align-items: flex-start;
|
}
|
|
/* 图片预览样式 */
|
.image-preview-container {
|
text-align: center;
|
padding: 20px;
|
}
|
|
.preview-image {
|
max-width: 100%;
|
max-height: 70vh;
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
}
|
|
/* 响应式设计 */
|
@media (max-width: 768px) {
|
.pdf-toolbar {
|
flex-direction: column;
|
gap: 10px;
|
}
|
|
.zoom-controls {
|
margin: 10px 0;
|
}
|
|
.pdf-preview-dialog {
|
width: 95% !important;
|
}
|
}
|
|
.detail-footer {
|
text-align: center;
|
margin-top: 20px;
|
padding-top: 20px;
|
border-top: 1px solid #ebeef5;
|
}
|
|
::v-deep .el-descriptions__label {
|
width: 120px;
|
background-color: #f5f7fa;
|
font-weight: bold;
|
}
|
</style>
|