inspect-front/src/views/summary/reprot-print/components/ReportPreview.vue
2025-03-19 15:18:04 +08:00

469 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-dialog
v-model="dialogVisible"
title="体检报告预览"
width="90%"
top="3vh"
:destroy-on-close="true"
:close-on-click-modal="false"
@close="handleClose"
class="report-preview-dialog"
>
<div class="iframe-container">
<iframe
v-if="dialogVisible"
src="/templates/report-template.html"
frameborder="0"
style="width: 100%; height: 100%; border: none"
@load="handleIframeLoad"
></iframe>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">关闭</el-button>
<el-button type="primary" @click="handlePrint"> 打印报告 </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import type { App } from 'vue'
import { ref, defineProps, defineEmits, watch } from 'vue'
import { PatientApi } from '@/api/inspect/inspectpatient/index'
import Message from '@/layout/components/Message/src/Message.vue'
const props = defineProps({
visible: {
type: Boolean,
default: false
},
reportData: {
type: Object,
default: () => ({})
}
})
const emit = defineEmits(['update:visible', 'close'])
const dialogVisible = ref(props.visible)
watch(
() => props.visible,
(newVal) => {
dialogVisible.value = newVal
if (newVal) {
// 当对话框显示时设置iframe内容
setTimeout(() => {
// setIframeContent()
}, 100) // 给一点时间让DOM更新
}
}
)
watch(
() => dialogVisible.value,
(newVal) => {
emit('update:visible', newVal)
}
)
const handleIframeLoad = async () => {
const iframe = document.querySelector('iframe')
const reportData = await PatientApi.getReportAll(props.reportData.medicalSn)
console.log('reportData', reportData)
if (iframe && iframe.contentWindow) {
const doc = iframe.contentWindow.document
// 更新首页体检编号
const reportTitle = doc.querySelector('.report-title')
if (reportTitle) {
reportTitle.textContent = `体检编号:${reportData.medicalSn || '--'}`
}
// 更新个人信息
// 姓名
const nameContent = doc.querySelector('.person_detail:nth-child(1) .person_content')
if (nameContent) nameContent.textContent = reportData.pName || '--'
// 性别
const sexContent = doc.querySelector('.person_detail:nth-child(2) .person_content')
if (sexContent) sexContent.textContent = reportData.gender || '--'
let age = '--'
// 年龄 - 从出生日期计算
const ageContent = doc.querySelector('.person_detail:nth-child(3) .person_content')
if (ageContent) {
if (reportData.birthday) {
const birthDate = new Date(reportData.birthday)
const today = new Date()
let calculatedAge = today.getFullYear() - birthDate.getFullYear()
const m = today.getMonth() - birthDate.getMonth()
if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
calculatedAge--
}
age = calculatedAge.toString()
}
ageContent.textContent = age
}
// 单位
const companyContent = doc.querySelector('.person_detail:nth-child(4) .person_content')
if (companyContent) companyContent.textContent = reportData.cardId || '--'
// 联系电话
const phoneContent = doc.querySelector('.person_detail:nth-child(5) .person_content')
if (phoneContent) phoneContent.textContent = reportData.phoneNum|| '--'
// 体检日期
const examDateContent = doc.querySelector('.person_detail:nth-child(6) .person_content')
if (examDateContent) {
const date = reportData.medicalDateTime ? new Date(reportData.medicalDateTime) : null
const formattedDate = date
? `${date.getFullYear()}${String(date.getMonth() + 1).padStart(2, '0')}${String(date.getDate()).padStart(2, '0')}`
: '--'
examDateContent.textContent = formattedDate
}
// 更新头像
const avatarImg = doc.getElementById('avatar-image')
if (avatarImg) {
avatarImg.src = reportData.headPicUrl || ''
}
// 更新前言中的姓名 - 修复选择器
const prefaceGreeting = doc.querySelector('.preface-greeting .underline')
if (prefaceGreeting) {
prefaceGreeting.textContent = reportData.pName || '________'
console.log('已更新前言姓名为:', reportData.pName)
} else {
console.log('未找到前言姓名元素,尝试使用备用选择器')
// 备用选择器
const altPrefaceGreeting = doc.querySelector('.preface-content .underline')
if (altPrefaceGreeting) {
altPrefaceGreeting.textContent = reportData.pName || '________'
console.log('使用备用选择器更新前言姓名为:', reportData.pName)
} else {
console.error('无法找到前言姓名元素')
}
}
// 更新一般检查数据(身高等)
const generalData = reportData.data.reduce((acc, item) => {
acc[item.itemName] = item
return acc
}, {})
const tbody = doc.querySelector('.general-exam .exam-table tbody')
if (tbody) {
tbody.innerHTML = `
<tr><td>体温</td><td>${generalData['体温']?.itemResult || '--'}℃</td></tr>
<tr><td>脉率</td><td>${generalData['脉率']?.itemResult || '--'}次/分</td></tr>
<tr><td>呼吸频率</td><td>${generalData['呼吸频率']?.itemResult || '--'}次/分</td></tr>
<tr><td>血压</td><td>${generalData['血压']?.itemResult || '--'} mmHg</td></tr>
<tr><td>腰围</td><td>${generalData['腰围']?.itemResult || '--'} cm</td></tr>
<tr><td>身高</td><td>${generalData['身高']?.itemResult || '--'} cm</td></tr>
<tr><td>体重</td><td>${generalData['体重']?.itemResult || '--'} kg</td></tr>
<tr><td>体质指数(BMI)</td><td>${generalData['BMI']?.itemResult || '--'} kg/m²</td></tr>
`
}
// 更新一般检查小结
const generalSummary = doc.querySelector('.general-exam .report-summary p')
if (generalSummary) {
// 只取身高项目的 analyse 作为小结
const heightData = generalData['身高']
generalSummary.textContent = heightData?.analyse || '暂无小结'
}
// 更新超声检查报告
const ultrasoundData = reportData.data.find(
(item) => item.itemName === '超声' || item.itemName === 'US'
)
if (ultrasoundData) {
// 更新超声检查报告内容
const ultrasoundSummary = doc.querySelector('.report-item.ultrasound-exam .report-summary')
if (ultrasoundSummary) {
// 解析 analyse 字段(格式:"检查所见xxx\n检查结果xxx"
const findings = ultrasoundData.analyse.split('\n')[0].replace('检查所见:', '') || ''
const conclusion = ultrasoundData.analyse.split('\n')[1].replace('检查结果:', '') || ''
ultrasoundSummary.innerHTML = `
<p><strong>【所见】</strong><br>${findings}</p>
<p><strong>【所得】</strong><br>${conclusion}</p>
`
} else {
console.error('未找到超声检查小结元素')
}
// 更新超声检查PDF链接
if (ultrasoundData.data) {
// 为PDF URL添加参数
const pdfUrl = ultrasoundData.data + '#toolbar=0&navpanes=0&view=Fit'
// 更新屏幕显示的iframe
const iframeElement = doc.querySelector('.ultrasound-exam .screen-only iframe')
if (iframeElement) {
iframeElement.src = pdfUrl
} else {
console.error('未找到超声检查iframe元素')
}
// 更新打印用的PDF容器
const pdfContainer = doc.querySelector('.ultrasound-exam .print-only.pdf-container')
if (pdfContainer) {
pdfContainer.setAttribute('data-pdf-url', pdfUrl)
} else {
console.error('未找到超声检查PDF容器')
}
}
}
// 更新其他检查报告
// 尿常规
const urineData = reportData.data.find((item) => item.itemName === '尿常规')
if (urineData) {
const urineSummary = doc.querySelector('.report-item:nth-of-type(6) .report-summary p')
if (urineSummary) {
urineSummary.textContent = urineData.analyse || ''
}
}
// 更新生化检查
const biochemData = reportData.data.find((item) => item.itemName === '生化')
if (biochemData) {
// 改为第7个report-item原4→7
const biochemSummary = doc.querySelector('.report-item:nth-of-type(7) .report-summary p')
if (biochemSummary) {
biochemSummary.textContent = biochemData.analyse || ''
} else {
console.error('未找到生化检查小结元素')
}
}
// 更新血常规
const bloodData = reportData.data.find((item) => item.itemName === '血常规')
if (bloodData) {
// 改为第8个report-item原5→8
const bloodSummary = doc.querySelector('.report-item:nth-of-type(8) .report-summary p')
if (bloodSummary) {
bloodSummary.textContent = bloodData.analyse || ''
} else {
console.error('未找到血常规小结元素')
}
}
// 获取所有报告标题中的第二个span填充值所在位置
const reportTitles = doc.querySelectorAll('.report-title > div > span:nth-child(2)')
// 更新每个填充值
reportTitles.forEach((span) => {
span.textContent =
`${reportData.medicalSn || '--'} ` +
`${reportData.pName || '--'} ` +
`${reportData.gender || '--'} ` +
`${age || '--'}`
})
// 更新心电图
const ecgData = reportData.data.find((item) => item.itemName === '心电图')
if (ecgData) {
// 改为第9个report-item原last-child→9
const ecgSummary = doc.querySelector('.report-item:nth-of-type(9) .report-summary')
if (ecgSummary) {
ecgSummary.innerHTML = `
<p><strong>【所见】</strong><br>${ecgData.analyse.split('\n')[0].replace('检查所见:', '') || ''}</p>
<p><strong>【所得】</strong><br>${ecgData.analyse.split('\n')[1].replace('检查结果:', '') || ''}</p>
`
} else {
console.error('未找到心电图小结元素')
}
}
// 设置体质辨识结果
const constitutionValue = doc.querySelector('#constitution-result .constitution-value')
if (constitutionValue) {
constitutionValue.textContent = reportData.zybs || '--'
}
// 更新汇总结果
const summaryElement = doc.getElementById('summary-year')
if (summaryElement) {
summaryElement.textContent = reportData.summaryResult || '--'
}
// 更新图片和 PDF 链接
reportData.data.forEach((item) => {
if (item.data) {
let reportTitle = ''
switch (item.pacsDataType) {
case 'cbc':
reportTitle = '血常规检查报告'
break
case 'ecg':
reportTitle = '心电图检查报告'
break
case 'bt':
reportTitle = '生化检查报告'
break
case 'US':
reportTitle = '超声检查报告'
break
case 'rt':
reportTitle = '尿常规检查报告'
break
default:
break
}
if (reportTitle) {
const reportSection = Array.from(doc.querySelectorAll('.report-item')).find((section) => {
const titleElement = section.querySelector('.report-title')
return titleElement && titleElement.textContent?.trim().includes(reportTitle)
})
if (reportSection) {
// 为PDF URL添加参数
const pdfUrl = item.data + '#toolbar=0&navpanes=0&view=Fit'
console.log('pdfUrl', pdfUrl)
// 更新 iframe src
const iframeElement = reportSection.querySelector('.screen-only iframe')
if (iframeElement) {
iframeElement.src = pdfUrl
}
// 更新图片 src如果是图片保持原样
const img = reportSection.querySelector('img')
if (img) {
img.src = item.data
}
// 更新打印时显示的 PDF 容器的 data-pdf-url
const pdfContainer = reportSection.querySelector('.print-only.pdf-container')
if (pdfContainer) {
pdfContainer.setAttribute('data-pdf-url', pdfUrl)
}
}
}
}
})
}
}
// 修改等待渲染完成的函数,添加更多检查和日志
const waitForAllPDFsRendered = () => {
return new Promise((resolve) => {
const checkRendering = () => {
const containers = document.querySelectorAll('.pdf-container')
let allDone = true
containers.forEach((container, index) => {
const imageContainer = container.querySelector('.pdf-image-container')
const img = imageContainer?.querySelector('img')
// 更严格的检查条件
if (
!imageContainer ||
!img ||
!img.complete ||
imageContainer.style.display !== 'block' ||
!img.src
) {
allDone = false
}
})
if (allDone) {
resolve(true)
} else {
setTimeout(checkRendering, 1000) // 增加检查间隔到1秒
}
}
checkRendering()
})
}
// 修改打印处理函数
const handlePrint = async () => {
const iframe = document.querySelector('iframe')
if (iframe && iframe.contentWindow) {
const iframeDoc = iframe.contentWindow.document
// 显示加载提示
const loadingMessage = iframeDoc.createElement('div')
loadingMessage.style.position = 'fixed'
loadingMessage.style.top = '50%'
loadingMessage.style.left = '50%'
loadingMessage.style.transform = 'translate(-50%, -50%)'
loadingMessage.style.padding = '20px'
loadingMessage.style.background = 'rgba(0, 0, 0, 0.7)'
loadingMessage.style.color = 'white'
loadingMessage.style.borderRadius = '5px'
loadingMessage.style.zIndex = '9999'
loadingMessage.innerHTML = '正在准备打印,请稍候...'
iframeDoc.body.appendChild(loadingMessage)
try {
// 获取所有需要转换的PDF容器
const containers = iframeDoc.querySelectorAll('.pdf-container')
// 执行所有PDF转换
for (const container of containers) {
const pdfUrl = container.getAttribute('data-pdf-url')
if (pdfUrl) {
await iframe.contentWindow.renderPDFAsImage(pdfUrl, container)
}
}
iframeDoc.body.removeChild(loadingMessage)
// 执行打印
iframe.contentWindow?.print()
handleClose()
} catch (error) {
console.error('PDF转换过程出错:', error)
iframeDoc.body.removeChild(loadingMessage)
}
}
}
const handleClose = () => {
dialogVisible.value = false
emit('close')
}
</script>
<style scoped>
.dialog-footer {
padding: 10px 0;
}
.iframe-container {
height: 80vh;
overflow: hidden; /* 改为hidden去掉滚动条 */
}
:deep(.el-dialog__body) {
padding: 0;
overflow: hidden; /* 确保对话框内容区域也没有滚动条 */
}
:deep(.el-dialog__header) {
margin-right: 0;
padding: 15px 20px;
border-bottom: 1px solid #e0e0e0;
}
:deep(.el-dialog__footer) {
border-top: 1px solid #e0e0e0;
padding: 10px 20px;
}
:deep(.report-preview-dialog) {
max-height: 94vh;
margin: 3vh auto !important;
display: flex;
flex-direction: column;
overflow: hidden; /* 确保整个对话框没有滚动条 */
}
:deep(.el-dialog__body) {
flex: 1;
overflow: hidden; /* 再次确保没有滚动条 */
}
</style>