增加心电模块打印以及分享功能
This commit is contained in:
parent
e683cd1604
commit
36e95da4bd
@ -67,6 +67,7 @@
|
||||
"vue": "3.4.21",
|
||||
"vue-dompurify-html": "^4.1.4",
|
||||
"vue-i18n": "9.10.2",
|
||||
"vue-qrcode": "^2.2.2",
|
||||
"vue-router": "^4.3.0",
|
||||
"vue-types": "^5.1.1",
|
||||
"vue-video-player": "^6.0.0",
|
||||
|
@ -134,6 +134,9 @@ importers:
|
||||
vue-i18n:
|
||||
specifier: 9.10.2
|
||||
version: 9.10.2(vue@3.4.21(typescript@5.3.3))
|
||||
vue-qrcode:
|
||||
specifier: ^2.2.2
|
||||
version: 2.2.2(qrcode@1.5.3)(vue@3.4.21(typescript@5.3.3))
|
||||
vue-router:
|
||||
specifier: ^4.3.0
|
||||
version: 4.3.2(vue@3.4.21(typescript@5.3.3))
|
||||
@ -4893,7 +4896,7 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
source-map@0.6.1:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, tarball: https://r2.cnpmjs.org/source-map/-/source-map-0.6.1.tgz}
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
split-string@3.1.0:
|
||||
@ -5423,6 +5426,12 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
|
||||
vue-qrcode@2.2.2:
|
||||
resolution: {integrity: sha512-SbrXq/mSb1g2tbDyXPe9gy9KiMYsvxWKRErlpij1BqiFoHwQckheZV63CTw6yRLLUVG2RXAVlX+APkpdCK7SQQ==}
|
||||
peerDependencies:
|
||||
qrcode: ^1.0.0
|
||||
vue: ^2.7.0 || ^3.0.0
|
||||
|
||||
vue-router@4.3.2:
|
||||
resolution: {integrity: sha512-hKQJ1vDAZ5LVkKEnHhmm1f9pMiWIBNGF5AwU67PdH7TyXCj/a4hTccuUuYCAMgJK6rO/NVYtQIEN3yL8CECa7Q==}
|
||||
peerDependencies:
|
||||
@ -11307,6 +11316,12 @@ snapshots:
|
||||
'@vue/devtools-api': 6.6.1
|
||||
vue: 3.4.21(typescript@5.3.3)
|
||||
|
||||
vue-qrcode@2.2.2(qrcode@1.5.3)(vue@3.4.21(typescript@5.3.3)):
|
||||
dependencies:
|
||||
qrcode: 1.5.3
|
||||
tslib: 2.6.2
|
||||
vue: 3.4.21(typescript@5.3.3)
|
||||
|
||||
vue-router@4.3.2(vue@3.4.21(typescript@5.3.3)):
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.6.1
|
||||
|
@ -34,6 +34,7 @@ export interface PatientexamlistVO {
|
||||
billDoctorDepartment:string// 开单科室 送检科室
|
||||
StudyInsta:string
|
||||
isFavourite:string //是否收藏
|
||||
pdfurl:String//pdf地址
|
||||
}
|
||||
export interface inspdfscreenshotVO {
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import html2Canvas from 'html2canvas';
|
||||
import jsPDF from 'jspdf';
|
||||
import html2Canvas from 'html2canvas'
|
||||
import jsPDF from 'jspdf'
|
||||
|
||||
const htmlToPdf = {
|
||||
getPdfBase64(title, id) {
|
||||
@ -10,52 +10,43 @@ const htmlToPdf = {
|
||||
logging: false,
|
||||
useCORS: true,
|
||||
dpi: window.devicePixelRatio * 4, // 将分辨率提高到特定的DPI 提高四倍
|
||||
scale: 4, // 按比例增加分辨率
|
||||
scale: 4 // 按比例增加分辨率
|
||||
}).then((canvas) => {
|
||||
const pdf = new jsPDF('p', 'mm', 'a4'); // A4纸,纵向
|
||||
const pdf = new jsPDF('p', 'mm', 'a4') // A4纸,纵向
|
||||
const ctx = canvas.getContext('2d'),
|
||||
a4w = 190,
|
||||
a4h = 272; // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
|
||||
a4h = 272 // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
|
||||
let imgHeight = Math.floor((a4h * canvas.width) / a4w), // 按A4显示比例换算一页图像的像素高度
|
||||
renderedHeight = 0;
|
||||
renderedHeight = 0
|
||||
|
||||
while (renderedHeight < canvas.height) {
|
||||
const page = document.createElement('canvas');
|
||||
page.width = canvas.width;
|
||||
const pageHeight = Math.min(imgHeight, canvas.height - renderedHeight); // 可能内容不足一页
|
||||
page.height = pageHeight;
|
||||
const page = document.createElement('canvas')
|
||||
page.width = canvas.width
|
||||
const pageHeight = Math.min(imgHeight, canvas.height - renderedHeight) // 可能内容不足一页
|
||||
page.height = pageHeight
|
||||
|
||||
// 用getImageData剪裁指定区域,并画到前面创建的canvas对象中
|
||||
page
|
||||
.getContext('2d')
|
||||
.putImageData(
|
||||
ctx.getImageData(
|
||||
0,
|
||||
renderedHeight,
|
||||
canvas.width,
|
||||
pageHeight,
|
||||
),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
.putImageData(ctx.getImageData(0, renderedHeight, canvas.width, pageHeight), 0, 0)
|
||||
pdf.addImage(
|
||||
page.toDataURL('image/jpeg', 1.0),
|
||||
'JPEG',
|
||||
10,
|
||||
10,
|
||||
a4w,
|
||||
Math.min(a4h, (a4w * page.height) / page.width),
|
||||
); // 添加图像到页面,保留10mm边距
|
||||
Math.min(a4h, (a4w * page.height) / page.width)
|
||||
) // 添加图像到页面,保留10mm边距
|
||||
|
||||
renderedHeight += pageHeight;
|
||||
renderedHeight += pageHeight
|
||||
if (renderedHeight < canvas.height) {
|
||||
pdf.addPage(); // 如果后面还有内容,添加一个空页
|
||||
pdf.addPage() // 如果后面还有内容,添加一个空页
|
||||
}
|
||||
}
|
||||
const pdfBase64String = pdf.output('datauristring'); // 获取 base64 编码的 PDF 文件
|
||||
resolve(pdfBase64String); // 解析 base64 字符串
|
||||
});
|
||||
});
|
||||
const pdfBase64String = pdf.output('datauristring') // 获取 base64 编码的 PDF 文件
|
||||
resolve(pdfBase64String) // 解析 base64 字符串
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
getPdf(title, id) {
|
||||
@ -65,51 +56,181 @@ const htmlToPdf = {
|
||||
logging: false,
|
||||
useCORS: true,
|
||||
dpi: window.devicePixelRatio * 4, // 将分辨率提高到特定的DPI 提高四倍
|
||||
scale: 4, // 按比例增加分辨率
|
||||
scale: 4 // 按比例增加分辨率
|
||||
}).then((canvas) => {
|
||||
const pdf = new jsPDF('p', 'mm', 'a4'); // A4纸,纵向
|
||||
const pdf = new jsPDF('p', 'mm', 'a4') // A4纸,纵向
|
||||
const ctx = canvas.getContext('2d'),
|
||||
a4w = 190,
|
||||
a4h = 272; // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
|
||||
a4h = 272 // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
|
||||
let imgHeight = Math.floor((a4h * canvas.width) / a4w), // 按A4显示比例换算一页图像的像素高度
|
||||
renderedHeight = 0;
|
||||
renderedHeight = 0
|
||||
|
||||
while (renderedHeight < canvas.height) {
|
||||
const page = document.createElement('canvas');
|
||||
page.width = canvas.width;
|
||||
const pageHeight = Math.min(imgHeight, canvas.height - renderedHeight); // 可能内容不足一页
|
||||
page.height = pageHeight;
|
||||
const page = document.createElement('canvas')
|
||||
page.width = canvas.width
|
||||
const pageHeight = Math.min(imgHeight, canvas.height - renderedHeight) // 可能内容不足一页
|
||||
page.height = pageHeight
|
||||
|
||||
// 用getImageData剪裁指定区域,并画到前面创建的canvas对象中
|
||||
page
|
||||
.getContext('2d')
|
||||
.putImageData(
|
||||
ctx.getImageData(
|
||||
0,
|
||||
renderedHeight,
|
||||
canvas.width,
|
||||
pageHeight,
|
||||
),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
.putImageData(ctx.getImageData(0, renderedHeight, canvas.width, pageHeight), 0, 0)
|
||||
pdf.addImage(
|
||||
page.toDataURL('image/jpeg', 1.0),
|
||||
'JPEG',
|
||||
10,
|
||||
10,
|
||||
a4w,
|
||||
Math.min(a4h, (a4w * page.height) / page.width),
|
||||
); // 添加图像到页面,保留10mm边距
|
||||
Math.min(a4h, (a4w * page.height) / page.width)
|
||||
) // 添加图像到页面,保留10mm边距
|
||||
|
||||
renderedHeight += pageHeight;
|
||||
renderedHeight += pageHeight
|
||||
if (renderedHeight < canvas.height) {
|
||||
pdf.addPage(); // 如果后面还有内容,添加一个空页
|
||||
pdf.addPage() // 如果后面还有内容,添加一个空页
|
||||
}
|
||||
}
|
||||
pdf.save(title + '.pdf'); // 保存 PDF 文件
|
||||
});
|
||||
pdf.save(title + '.pdf') // 保存 PDF 文件
|
||||
})
|
||||
},
|
||||
};
|
||||
getPdfh(title, id) {
|
||||
html2Canvas(document.querySelector(id), {
|
||||
allowTaint: false,
|
||||
taintTest: false,
|
||||
logging: false,
|
||||
useCORS: true,
|
||||
dpi: 300, // 将分辨率提高到特定的DPI 提高四倍
|
||||
scale: 1 // 按比例增加分辨率
|
||||
}).then((canvas) => {
|
||||
const pdf = new jsPDF('l', 'mm', 'a4') // A4纸,纵向
|
||||
const ctx = canvas.getContext('2d'),
|
||||
a4w = 190,
|
||||
a4h = 272 // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
|
||||
let imgHeight = Math.floor((a4h * canvas.width) / a4w), // 按A4显示比例换算一页图像的像素高度
|
||||
renderedHeight = 0
|
||||
|
||||
export default htmlToPdf;
|
||||
while (renderedHeight < canvas.height) {
|
||||
const page = document.createElement('canvas')
|
||||
page.width = canvas.width
|
||||
const pageHeight = Math.min(imgHeight, canvas.height - renderedHeight) // 可能内容不足一页
|
||||
page.height = pageHeight
|
||||
|
||||
// 用getImageData剪裁指定区域,并画到前面创建的canvas对象中
|
||||
page
|
||||
.getContext('2d')
|
||||
.putImageData(ctx.getImageData(0, renderedHeight, canvas.width, pageHeight), 0, 0)
|
||||
pdf.addImage(
|
||||
page.toDataURL('image/jpeg', 1.0),
|
||||
'JPEG',
|
||||
10,
|
||||
10,
|
||||
a4w,
|
||||
Math.min(a4h, (a4w * page.height) / page.width)
|
||||
) // 添加图像到页面,保留10mm边距
|
||||
|
||||
renderedHeight += pageHeight
|
||||
if (renderedHeight < canvas.height) {
|
||||
pdf.addPage() // 如果后面还有内容,添加一个空页
|
||||
}
|
||||
}
|
||||
pdf.save(title + '.pdf') // 保存 PDF 文件
|
||||
})
|
||||
},
|
||||
getDivContentAsBase64(divId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 获取div元素
|
||||
const divElement = document.querySelector(divId)
|
||||
if (!divElement) {
|
||||
console.error('未找到指定ID的div元素')
|
||||
reject('未找到指定ID的div元素')
|
||||
return
|
||||
}
|
||||
|
||||
// 使用html2canvas捕捉div内容
|
||||
html2Canvas(divElement, {
|
||||
dpi: 300, // 设置300DPI
|
||||
scale: 3.125, // 保持原始比例
|
||||
useCORS: true, // 如果内容包含跨域图片,需要设置为true
|
||||
allowTaint: false // 是否允许跨域图片导致画布被污染
|
||||
})
|
||||
.then((canvas) => {
|
||||
// const pdf = new jsPDF({
|
||||
// orientation: 'l', // 纵向
|
||||
// unit: 'px', // 使用像素作为单位
|
||||
// format: [1920, 820] // 自定义页面尺寸,与图片尺寸一致
|
||||
// });
|
||||
const pdf = new jsPDF({
|
||||
orientation: 'l',
|
||||
unit: 'px',
|
||||
format: [canvas.width, canvas.height]
|
||||
});
|
||||
// 将canvas转换为图片数据URL
|
||||
const imgData = canvas.toDataURL('image/jpeg', 1) // 1.0表示最高质量
|
||||
// pdf.addImage(imgData, 'JPEG', 0, 0, canvas.width, canvas.height);
|
||||
// // 设置 PDF 页面尺寸
|
||||
// // pdf.addImage(imgData, 'JPEG', 10, 10, 1920, 820)
|
||||
// const link = document.createElement('a');
|
||||
// link.download = `${divId}-image.jpeg`;
|
||||
// link.href = imgData;
|
||||
// link.click();
|
||||
// pdf.save('111.pdf')
|
||||
// 解决Promise并返回图片的base64字符串
|
||||
// pdf.save(`${divId}-content.pdf`);
|
||||
resolve(imgData)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('生成图片过程中发生错误:', error)
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
createPdfFromBase64(base64Image) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 使用html2canvas捕捉div内容(这里不需要,但保留以备后续使用)
|
||||
// html2Canvas(divElement, { ... }).then(canvas => {
|
||||
// 将base64字符串转换为Blob对象
|
||||
const blob = base64ToBlob(base64Image);
|
||||
|
||||
// 创建一个URL对象
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
// 创建一个新的Image对象
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
// 创建PDF对象
|
||||
const pdf = new jsPDF({
|
||||
orientation: 'l',
|
||||
unit: 'px',
|
||||
format: [img.width, img.height]
|
||||
});
|
||||
|
||||
// 将图片添加到PDF
|
||||
pdf.addImage(img, 'JPEG', 0, 0, img.width, img.height);
|
||||
|
||||
// 获取PDF的base64字符串
|
||||
const pdfBase64 = pdf.output('datauristring');
|
||||
|
||||
// 解决Promise并返回PDF的base64字符串
|
||||
resolve(pdfBase64);
|
||||
|
||||
// 清理URL对象
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
img.onerror = (error) => {
|
||||
console.error('图片加载失败:', error);
|
||||
reject(error);
|
||||
};
|
||||
img.src = url;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
// 辅助函数:将base64字符串转换为Blob对象
|
||||
function base64ToBlob(base64) {
|
||||
const binary = atob(base64.split(',')[1]);
|
||||
const array = [];
|
||||
for (let i = 0; i < binary.length; i++) {
|
||||
array.push(binary.charCodeAt(i));
|
||||
}
|
||||
return new Blob([new Uint8Array(array)], { type: 'image/jpeg' });
|
||||
}
|
||||
export default htmlToPdf
|
||||
|
@ -31,7 +31,23 @@
|
||||
<el-button type="primary" plain
|
||||
><el-icon><Filter /></el-icon> 采集</el-button
|
||||
>
|
||||
<el-button type="primary" plain :icon="Share">分享</el-button>
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
title="分享二维码"
|
||||
:width="250"
|
||||
trigger="click"
|
||||
:disabled="isshare"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button type="primary" plain :icon="Share" @click="shareclick()"
|
||||
>分享</el-button
|
||||
>
|
||||
</template>
|
||||
<div>
|
||||
<canvas ref="qrcodeRef" width="200" height="200"></canvas>
|
||||
</div>
|
||||
</el-popover>
|
||||
|
||||
<el-button type="primary" plain>
|
||||
<el-icon><RefreshLeft /></el-icon> 重采</el-button
|
||||
>
|
||||
@ -42,7 +58,7 @@
|
||||
@click="getuporghiorgid(Primarykey, orgid)"
|
||||
>申请诊断</el-button
|
||||
>
|
||||
<el-button type="primary" plain
|
||||
<el-button type="primary" plain @click="print"
|
||||
><el-icon><Printer /></el-icon>打印</el-button
|
||||
>
|
||||
</el-button-group>
|
||||
@ -305,7 +321,9 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-tag type="info" style="font-size: 16px; width: 100%;color: black;">心电事件快照</el-tag>
|
||||
<el-tag type="info" style="font-size: 16px; width: 100%; color: black"
|
||||
>心电事件快照</el-tag
|
||||
>
|
||||
<el-input
|
||||
v-model="snapshotTime"
|
||||
style="width: 100%"
|
||||
@ -313,7 +331,7 @@
|
||||
type="textarea"
|
||||
placeholder=""
|
||||
/>
|
||||
<el-tag type="info" style="font-size: 16px; width: 100%;color: black;">智能词库</el-tag>
|
||||
<el-tag type="info" style="font-size: 16px; width: 100%; color: black">智能词库</el-tag>
|
||||
<el-input
|
||||
v-model="queryParams.autoDiagResult"
|
||||
style="width: 100%"
|
||||
@ -321,7 +339,7 @@
|
||||
type="textarea"
|
||||
placeholder=""
|
||||
/>
|
||||
<el-tag type="info" style="font-size: 16px; width: 100%;color: black;"
|
||||
<el-tag type="info" style="font-size: 16px; width: 100%; color: black"
|
||||
>医生诊断结论 <el-button @click="zdmodle" type="primary" text>诊断模版</el-button>
|
||||
</el-tag>
|
||||
<el-input
|
||||
@ -406,19 +424,62 @@
|
||||
:expand-on-click-node="false"
|
||||
/>
|
||||
</el-drawer>
|
||||
<!--打印弹窗-->
|
||||
<el-dialog
|
||||
title=""
|
||||
v-model="isdiagshow"
|
||||
:fullscreen="true"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
@close="printclose"
|
||||
>
|
||||
<!--width: 1000px; height: 700px;-->
|
||||
<ECGprint :imagebase64="imagebase64" />
|
||||
</el-dialog>
|
||||
<!--生成图片的组件-->
|
||||
<div style="position: fixed; top: 0; left: 0; z-index: -100">
|
||||
<div id="hiddenPdfDiv" v-if="isprintimage">
|
||||
<!--心电图-->
|
||||
<ECGhtmlprint
|
||||
style="height: 1080px; width: 1920px; page-break-after: always; visibility: hidden"
|
||||
:extraInfo="queryParams"
|
||||
:jsonurl="queryParams.ecgJsonDataFilePath"
|
||||
:-isgrid="Isgrid"
|
||||
:-ismeasure="Ismeasure"
|
||||
:-isfd="IsFD"
|
||||
:lineratio="0.025"
|
||||
:suduratio="suduratio"
|
||||
:isrefresh="isrefresh"
|
||||
:iscorrect="correct"
|
||||
@update:value="handleUpdate"
|
||||
:printimage="isprintimage"
|
||||
@update:image="updateimagebase"
|
||||
:age="age"
|
||||
:billDoctorDepartment="rowinfo.billDoctorDepartment"
|
||||
:doctorname="rowinfo.doctorname"
|
||||
:regId="id"
|
||||
:pname="rowinfo.pname"
|
||||
:gender="rowinfo.gender"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ArrowLeft, ArrowRight, Delete, Edit, Share, Check } from '@element-plus/icons-vue'
|
||||
import ECGhtml from '@/views/ECG/ECGhtml.vue'
|
||||
import ECGhtmlprint from '@/views/ECG/ECGhtmlprint.vue'
|
||||
import { EcganalysisparasApi, EcganalysisparasVO } from '@/api/tblist/ecganalysisparas'
|
||||
import { dateFormatter, beginOfDay } from '@/utils/formatTime'
|
||||
import { formatDate, getCurrentLocalDateTime } from '@/utils/formatTime'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { getUserProfile, ProfileVO } from '@/api/system/user/profile'
|
||||
import { ultrasoniccomApi, updateexamineimageVO } from '@/api/ultrasoniccom'
|
||||
import { PatientexamlistApi, PatientexamlistVO } from '@/api/tblist/patientexamlist'
|
||||
|
||||
import QRCode from 'qrcode'
|
||||
import ECGprint from '@/views/ECG/ECGprint.vue'
|
||||
import htmlToPdf from '@/utils/htmlPdf'
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const message = useMessage() // 消息弹窗
|
||||
@ -445,6 +506,11 @@ const diagnosemodel = ref() //存放诊断模版
|
||||
const isrefresh = ref(false) //是否刷新
|
||||
const correct = ref(false) //纠错功能
|
||||
const eltextrow = ref(4) //诊断内容占据的行
|
||||
const isshare = ref(true) //是否展示分享二维码
|
||||
const qrcodeRef = ref(null)
|
||||
const isdiagshow = ref(false) //是否显示打印窗体
|
||||
const imagebase64 = ref()
|
||||
const isprintimage = ref(false) //是否生成打印文件base64
|
||||
|
||||
// 树配置项
|
||||
const treeDefaultProps = {
|
||||
@ -458,10 +524,12 @@ const treeData = ref([])
|
||||
const open = async (row: any) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = '心电分析'
|
||||
resetForm()
|
||||
id.value = row.regId
|
||||
Primarykey.value = row.id
|
||||
orgid.value = row.orgId
|
||||
rowinfo.value = row
|
||||
// console.log( rowinfo.value)
|
||||
const data = await EcganalysisparasApi.getexamIDdata('MZCF0191729074962197_44')
|
||||
queryParams.value = data
|
||||
snapshotTime.value = formattedDate(queryParams.value.snapshotTime)
|
||||
@ -544,10 +612,68 @@ async function save() {
|
||||
// saveFormVO.value.departName=queryParams.value.departName
|
||||
const ret = await EcganalysisparasApi.SaveEcganalysisparas(saveFormVO.value)
|
||||
if (ret) {
|
||||
//进行生成pdf
|
||||
const data = await EcganalysisparasApi.getexamIDdata('MZCF0191729074962197_44')
|
||||
queryParams.value = data
|
||||
message.alertSuccess('保存成功')
|
||||
isprintimage.value = true
|
||||
|
||||
nextTick(async () => {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
//生成PDF
|
||||
htmlToPdf
|
||||
.createPdfFromBase64(imagebase64.value)
|
||||
.then((pdfBase64) => {
|
||||
//生成PDF
|
||||
PatientexamlistApi.ftppdf({
|
||||
id: Primarykey.value,
|
||||
imagebase: pdfBase64,
|
||||
model: '0',
|
||||
folderPath: 'C:\\work\\pacs' + '\\' + Primarykey.value
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
} finally {
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
}
|
||||
//展示图片
|
||||
function updateimagebase(newValue) {
|
||||
imagebase64.value = newValue
|
||||
}
|
||||
|
||||
//重置数据
|
||||
function resetForm()
|
||||
{
|
||||
Isgrid.value=1 //是否显示网格 1显示 0不显示
|
||||
Ismeasure.value =0 //是否开启测量 1开启 0 不开启
|
||||
IsFD.value = false //是否开启放大
|
||||
lineratio.value =0.05 //画线的系数 振幅
|
||||
suduratio.value = 1 //画线的系数 走速
|
||||
}
|
||||
|
||||
//关闭打印窗口
|
||||
function printclose() {
|
||||
imagebase64.value=''
|
||||
isdiagshow.value=false
|
||||
isprintimage.value=false
|
||||
}
|
||||
//打印
|
||||
function print() {
|
||||
if (!queryParams.value.autoDiagTime) {
|
||||
isdiagshow.value = false
|
||||
isprintimage.value = false
|
||||
message.alertError('请保存后再进行打印')
|
||||
} else {
|
||||
isdiagshow.value = true
|
||||
isprintimage.value = true
|
||||
}
|
||||
}
|
||||
//纠错窗体回传值
|
||||
const handleUpdate = (newValue) => {
|
||||
correct.value = newValue
|
||||
@ -634,7 +760,20 @@ function FD() {
|
||||
message.alertSuccess('关闭放大')
|
||||
}
|
||||
}
|
||||
|
||||
//分享点击
|
||||
function shareclick() {
|
||||
if (rowinfo.value.pdfurl) {
|
||||
isshare.value = false
|
||||
// 确保 DOM 已经渲染
|
||||
QRCode.toCanvas(qrcodeRef.value, rowinfo.value.pdfurl, function (error) {
|
||||
if (error) console.error(error)
|
||||
console.log('QR code generated!')
|
||||
})
|
||||
} else {
|
||||
isshare.value = true
|
||||
message.alertError('请保存后再进行分享')
|
||||
}
|
||||
}
|
||||
//根据光标位置 插入值
|
||||
const insertValue = (textvalue: String) => {
|
||||
const textarea = inputRef.value.$el.querySelector('textarea') // 获取textarea DOM元素
|
||||
@ -731,6 +870,7 @@ defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/*心电图区域样式*/
|
||||
|
||||
/*心电图区域样式*/
|
||||
|
@ -240,7 +240,7 @@ nextTick(() => {
|
||||
const integer = Math.round(adjustedWidth)
|
||||
const awidth = integer * 20
|
||||
canvasContainer.style.width = awidth - 20 + 'px'
|
||||
canvasContainer1.style.width = awidth - 20 + 'px'
|
||||
canvasContainer1.style.width = (awidth+41) - 20 + 'px' //加61 是多加3个格子 让边距小一点
|
||||
console.log('adjustedWidth 不是整数')
|
||||
}
|
||||
if (Number.isInteger(adjustedheight)) {
|
||||
|
939
src/views/ECG/ECGhtmlprint.vue
Normal file
939
src/views/ECG/ECGhtmlprint.vue
Normal file
@ -0,0 +1,939 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="ecg-report" id="PDF">
|
||||
<!-- 头部 -->
|
||||
<div>
|
||||
<div style="text-align: center; margin-bottom: -16px">
|
||||
Shenzhen Bao'an People's Hospital
|
||||
</div>
|
||||
<div style="text-align: right; font-size: 12px">报告日期:{{formatDate(extraInfo.doctorDiagTime, 'YYYY-MM-DD HH:mm:ss')}}</div>
|
||||
</div>
|
||||
<hr v-if="true" />
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<div style="margin: 4px 6px; line-height: 1.14">
|
||||
<el-row>
|
||||
<el-col :span="7">编号:{{extraInfo.examId}}</el-col>
|
||||
<el-col :span="5">HR:{{extraInfo.hr}}</el-col>
|
||||
<el-col :span="12">诊断提示:{{extraInfo.autoDiagResult}}</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="7">姓名:{{ infoParams.pname }}</el-col>
|
||||
<el-col :span="5">P-R:{{ extraInfo.pr }}</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="7">性别:{{ infoParams.gender }}</el-col>
|
||||
<el-col :span="5">QRS:{{ extraInfo.qrsAxle }}</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="7">年龄:{{infoParams.age }}岁</el-col>
|
||||
<el-col :span="5">QT/QTc:{{ extraInfo.qt }}/{{ extraInfo.qtc }}</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="7">病室:</el-col>
|
||||
<el-col :span="5">RV5/SV1:{{ extraInfo.rv5 }}/{{ extraInfo.sv1 }}</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="7">住院号:{{infoParams.regId}}</el-col>
|
||||
<el-col :span="5">RV5+SV1:{{ extraInfo.rv5Sv1 }}</el-col>
|
||||
<el-col :span="7">科室:{{ infoParams.billDoctorDepartment }}</el-col>
|
||||
<el-col :span="5">报告医生:{{infoParams.doctorname}}</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<hr v-if="false" />
|
||||
|
||||
<div class="ecg-main">
|
||||
<div class="printcontainer">
|
||||
<div id="canvas-containerprint">
|
||||
<canvas ref="leftCanvasprint" id="leftCanvasprint" width="21" height="921"></canvas>
|
||||
<canvas
|
||||
ref="bottomCanvasprint"
|
||||
id="bottomCanvasprint"
|
||||
width="1920"
|
||||
height="921"
|
||||
></canvas>
|
||||
<canvas
|
||||
ref="topCanvasprint"
|
||||
id="topCanvasprint"
|
||||
width="1920"
|
||||
height="921"
|
||||
@mousedown="handleMouseDown($event, 'L')"
|
||||
></canvas>
|
||||
</div>
|
||||
|
||||
<div id="canvas-containerprint1">
|
||||
<canvas ref="rightCanvasprint" id="rightCanvasprint" width="21" height="921"></canvas>
|
||||
<canvas
|
||||
ref="bottomCanvasprint1"
|
||||
id="bottomCanvasprint1"
|
||||
width="1920"
|
||||
height="921"
|
||||
></canvas>
|
||||
<canvas
|
||||
ref="topCanvasprint1"
|
||||
id="topCanvasprint1"
|
||||
width="1920"
|
||||
height="921"
|
||||
@mousedown="handleMouseDown($event, 'R')"
|
||||
></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 心电图 -->
|
||||
|
||||
<!-- 尾部 -->
|
||||
<div style="width: 96.88%; position: absolute; bottom: 20px; right: 20px; z-index: 1000;">
|
||||
<hr v-if="false" />
|
||||
<el-row style="font-size: 12px">
|
||||
<el-col :span="8" class="pl-6px"> 地址: </el-col>
|
||||
|
||||
<el-col :span="5" style="text-align: right">{{formatDate(extraInfo.doctorDiagTime, 'YYYY-MM-DD HH:mm:ss')}}</el-col>
|
||||
<el-col :span="10" style="text-align: right"> <span class="myrtip"
|
||||
>{{ ' 走速:' + convert(suduratio, 0) + 'mm/s ' }}
|
||||
{{ ' 振幅:' + convert(lineratio, 1) + 'mm/mv ' }}
|
||||
{{ ' 报告模式:6X2' }}</span
|
||||
> </el-col>
|
||||
|
||||
</el-row>
|
||||
<div style="font-size: 10.6px; text-align: center; margin-top: 4.2px; margin-bottom: -14px">
|
||||
注:本报告仅供临床医师参考,不作疾病证明
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <el-button @click="image">打印1</el-button> -->
|
||||
</div>
|
||||
|
||||
<!--纠错功能-->
|
||||
<el-dialog v-model="isdialog" title="导联纠错" width="600" @close="close">
|
||||
<div>
|
||||
<el-checkbox-group v-model="transfer" :min="0" :max="2" class="mycheckbox-group">
|
||||
<el-checkbox
|
||||
v-for="city in transferleads"
|
||||
:key="city"
|
||||
:label="city"
|
||||
:value="city"
|
||||
class="mycheckbox-item"
|
||||
@change="checkboxchage"
|
||||
>
|
||||
{{ city }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
<div style="margin-top: 10px">
|
||||
<el-button type="primary" @click="clickdl" :disabled="checkes">交换导联</el-button>
|
||||
<el-button type="primary" @click="recovery()">恢复正常</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { number, object } from 'vue-types'
|
||||
import sb from './ECGSB.vue'
|
||||
import htmlToPdf from '@/utils/htmlPdf'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
//读取出来的json
|
||||
const text = ref()
|
||||
const sliderValue = ref(0) // 初始化滑块值
|
||||
//给父组件回传值
|
||||
const emits = defineEmits(['update:value', 'update:image'])
|
||||
const lineratio1 = ref(0.05) //画线的系数 振幅
|
||||
const previousValue = ref(0) // 记录上一个滑块值
|
||||
const swipeDirection = ref('') // 用于存储滑动方向
|
||||
// 响应式引用
|
||||
const leftCanvasprint = ref(null)
|
||||
const rightCanvasprint = ref(null)
|
||||
const bottomCanvasprint = ref(null)
|
||||
const bottomCanvasprint1 = ref(null)
|
||||
const topCanvasprint = ref(null)
|
||||
const topCanvasprint1 = ref(null)
|
||||
const url = ref()
|
||||
/*鼠标绘制相关*/
|
||||
const ctx = ref(null)
|
||||
const isVertical = ref(false) // 控制线条类型,true 为竖直,false 为水平
|
||||
const lastPoint = ref(null) // 用于存储上一个点的坐标
|
||||
const isdialog = ref(false) //用于开启纠错
|
||||
const transfer = ref() //纠错导联 值
|
||||
|
||||
const checkes = ref(true) //控制导联交换按钮是否可用
|
||||
|
||||
// 响应式数据
|
||||
const beatArray1 = ref([])
|
||||
const beatArray2 = ref([]) //备份数据
|
||||
let offset = 0
|
||||
const spacing = 20
|
||||
|
||||
const heightoff = ref(0) //心电图的偏移量 高度变化的时候
|
||||
const FD = ref(false)
|
||||
const Info = ref()
|
||||
|
||||
async function image() {
|
||||
const imagebase64 = await htmlToPdf.getDivContentAsBase64('#PDF')
|
||||
emits('update:image', imagebase64)
|
||||
}
|
||||
|
||||
function handleMouseDown(event, type) {
|
||||
if (type === 'L') {
|
||||
ctx.value = topCanvasprint.value.getContext('2d')
|
||||
} else if (type === 'R') {
|
||||
ctx.value = topCanvasprint1.value.getContext('2d')
|
||||
}
|
||||
|
||||
if (infoParams.Ismeasure !== 1) {
|
||||
return
|
||||
}
|
||||
const rect =
|
||||
type === 'L'
|
||||
? topCanvasprint.value.getBoundingClientRect()
|
||||
: topCanvasprint1.value.getBoundingClientRect()
|
||||
const x = event.clientX - rect.left
|
||||
const y = event.clientY - rect.top
|
||||
|
||||
if (lastPoint.value) {
|
||||
// 如果已经有一个点被记录,那么使用这个新点和上一个点绘制线条
|
||||
drawLineAndDistance(lastPoint.value, { x, y })
|
||||
lastPoint.value = null // 重置上一个点
|
||||
} else {
|
||||
// 记录这个点作为上一个点
|
||||
lastPoint.value = { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
function calculateDistance(x1, y1, x2, y2) {
|
||||
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
|
||||
}
|
||||
|
||||
function drawLineAndDistance(point1, point2) {
|
||||
//ctx.value.clearRect(0, 0, topCanvasprint.value.width, topCanvasprint.value.height); // 清除画布
|
||||
|
||||
//竖线
|
||||
if (Math.abs(point2.x - point1.x) <= 20) {
|
||||
point2.x = point1.x
|
||||
lineTo(point1.x, point1.y, point2.x, point2.y, 1)
|
||||
} else if (Math.abs(point2.y - point1.y) <= 20) {
|
||||
//横
|
||||
point2.y = point1.y
|
||||
lineTo(point1.x, point1.y, point2.x, point2.y, 0)
|
||||
} else {
|
||||
lastPoint.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function lineTo(x, y, x1, y1, type) {
|
||||
// 绘制两个点
|
||||
ctx.value.fillStyle = 'black'
|
||||
ctx.value.beginPath()
|
||||
ctx.value.arc(x, y, 1, 0, 2 * Math.PI)
|
||||
ctx.value.fill()
|
||||
ctx.value.beginPath()
|
||||
ctx.value.arc(x1, y1, 1, 0, 2 * Math.PI)
|
||||
ctx.value.fill()
|
||||
|
||||
// 绘制直线
|
||||
ctx.value.beginPath()
|
||||
ctx.value.moveTo(x, y)
|
||||
ctx.value.lineTo(x1, y1)
|
||||
ctx.value.strokeStyle = 'black'
|
||||
ctx.value.lineWidth = 1
|
||||
ctx.value.stroke()
|
||||
|
||||
// 计算并绘制距离文本
|
||||
const distance = calculateDistance(x, y, x1, y1)
|
||||
const ms = (25.4 / 96) * (25 / 1000) * distance
|
||||
ctx.value.font = '11px Arial'
|
||||
ctx.value.fillStyle = 'black'
|
||||
if (type === 1) {
|
||||
ctx.value.fillText(`距离: ${ms.toFixed(2)}ms`, x + 5, y + 30)
|
||||
} else {
|
||||
ctx.value.fillText(`距离: ${ms.toFixed(2)}ms`, x + 10, y - 10)
|
||||
}
|
||||
}
|
||||
|
||||
/*鼠标绘制相关*/
|
||||
|
||||
const handleSliderChange = (value) => {
|
||||
// 计算当前值与上一次值之间的差值
|
||||
const difference = value - previousValue.value
|
||||
const swipeDifference = Math.abs(difference)
|
||||
console.log('滑动差值' + swipeDifference)
|
||||
if (value > previousValue.value) {
|
||||
swipeDirection.value = '右滑' // 向右滑动
|
||||
moveCanvas('left', swipeDifference)
|
||||
} else if (value < previousValue.value) {
|
||||
swipeDirection.value = '左滑' // 向左滑动
|
||||
moveCanvas('right', swipeDifference)
|
||||
} else {
|
||||
swipeDirection.value = '未滑动' // 没有滑动
|
||||
}
|
||||
previousValue.value = value // 更新上一个值
|
||||
console.log('滑块值改变为:', value)
|
||||
}
|
||||
|
||||
//渲染组件后执行
|
||||
onMounted(async () => {
|
||||
console.log('挂载完成')
|
||||
Info.value = infoParams.extraInfo
|
||||
// topCanvasprint.value = document.getElementById('topCanvasprint');
|
||||
})
|
||||
|
||||
nextTick(async () => {
|
||||
/* 计算宽度和高度*/
|
||||
var canvasContainer = document.getElementById('canvas-containerprint')
|
||||
var canvasContainer1 = document.getElementById('canvas-containerprint1')
|
||||
|
||||
var leftCanvas = document.getElementById('leftCanvasprint')
|
||||
// 获取元素的计算后的样式
|
||||
var style = window.getComputedStyle(canvasContainer)
|
||||
|
||||
// 获取宽度
|
||||
var widthPercentage = style.width
|
||||
|
||||
//获取高度
|
||||
var heightPercentage = style.height
|
||||
|
||||
var widthInPixels = canvasContainer.offsetWidth
|
||||
var heightInPixels = canvasContainer.offsetHeight
|
||||
|
||||
// 宽度除以 20 并取整
|
||||
var adjustedWidth = widthInPixels / 20
|
||||
var adjustedheight = heightInPixels / 20
|
||||
// 判断 adjustedWidth 是否为整数
|
||||
if (Number.isInteger(adjustedWidth)) {
|
||||
canvasContainer.style.width = widthInPixels + 'px'
|
||||
} else {
|
||||
const integer = Math.round(adjustedWidth)
|
||||
const awidth = integer * 20
|
||||
canvasContainer.style.width = awidth - 20 + 'px'
|
||||
canvasContainer1.style.width = awidth + 1 + 'px' //加61 是多加3个格子 让边距小一点
|
||||
console.log('adjustedWidth 不是整数')
|
||||
}
|
||||
if (Number.isInteger(adjustedheight)) {
|
||||
canvasContainer.style.height = heightInPixels + 'px'
|
||||
// leftCanvas.height = heightInPixels + 'px'
|
||||
} else {
|
||||
const integer = Math.round(adjustedheight)
|
||||
const ahe = integer * 20
|
||||
canvasContainer.style.height = ahe - 118 + 'px'
|
||||
canvasContainer1.style.height = ahe - 118 + 'px'
|
||||
// leftCanvas.height = ahe - 118
|
||||
heightoff.value = (ahe - 118) / 6
|
||||
}
|
||||
await fetchData()
|
||||
nextTick(() => {
|
||||
image()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
// 计算年龄
|
||||
const calculateAge = (birthdate) => {
|
||||
if (!birthdate) {
|
||||
age.value = null
|
||||
return
|
||||
}
|
||||
|
||||
const today = new Date()
|
||||
const birth = new Date(birthdate)
|
||||
|
||||
let yearsDiff = today.getFullYear() - birth.getFullYear()
|
||||
|
||||
// Check if the birthday hasn't occurred yet this year
|
||||
const hasBirthdayPassed =
|
||||
today.getMonth() > birth.getMonth() ||
|
||||
(today.getMonth() === birth.getMonth() && today.getDate() >= birth.getDate())
|
||||
|
||||
if (!hasBirthdayPassed) {
|
||||
yearsDiff--
|
||||
}
|
||||
|
||||
age.value = yearsDiff
|
||||
}
|
||||
|
||||
// 定义异步方法
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
text.value = await fetchFileContent(infoParams.jsonurl)
|
||||
handleFileChange()
|
||||
Bottom()
|
||||
// console.log('获取到的数据:', text.value);
|
||||
} catch (error) {
|
||||
console.error('获取数据时出错:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const inputSlider = (value) => {
|
||||
setScrollTop(value)
|
||||
}
|
||||
const formatTooltip = (value) => {
|
||||
return `${value} px`
|
||||
}
|
||||
async function fetchFileContent(url) {
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
if (!response.ok) {
|
||||
throw new Error('网络响应不是 ok')
|
||||
}
|
||||
return await response.text() // 返回文本内容
|
||||
} catch (error) {
|
||||
console.error('获取文件内容时出错:', error)
|
||||
throw error // 抛出错误以便调用者可以处理
|
||||
}
|
||||
}
|
||||
///接受参数
|
||||
const infoParams = defineProps({
|
||||
jsonurl: String,
|
||||
examId: String,
|
||||
age:String,
|
||||
regId:String,
|
||||
pname:String,
|
||||
gender:String,
|
||||
billDoctorDepartment:String,
|
||||
doctorname:String,
|
||||
Isgrid: Number,
|
||||
Ismeasure: Number,
|
||||
Isfd: Boolean,
|
||||
lineratio: Number,
|
||||
suduratio: Number,
|
||||
isrefresh: Boolean,
|
||||
iscorrect: Boolean,
|
||||
printimage: Boolean,
|
||||
extraInfo: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
id: '',
|
||||
orgId: '',
|
||||
examId: '',
|
||||
collectionTime: undefined,
|
||||
hr: '',
|
||||
paxle: '',
|
||||
qrsAxle: '',
|
||||
taxle: '',
|
||||
ptimeLimit: '',
|
||||
pr: '',
|
||||
qrsTimeLimit: '',
|
||||
qt: '',
|
||||
qtc: '',
|
||||
rv5: '',
|
||||
sv1: '',
|
||||
rv5Sv1: '',
|
||||
snapshotTime: undefined,
|
||||
autoDiagResult: '',
|
||||
autoDiagTime: undefined,
|
||||
doctorDiagResult: '',
|
||||
doctorDiagTime: undefined,
|
||||
doctorName: '',
|
||||
doctorId: '',
|
||||
departId: '',
|
||||
departName: '',
|
||||
isDelete: '',
|
||||
deleteTime: undefined,
|
||||
deleteDoctorName: '',
|
||||
deleteDoctorId: '',
|
||||
ecgDataFilePath: '',
|
||||
ecgJsonDataFilePath: '',
|
||||
createDate: undefined
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 监听多个值的变化
|
||||
watch(
|
||||
[
|
||||
() => infoParams.Isgrid,
|
||||
() => infoParams.Ismeasure,
|
||||
() => infoParams.lineratio,
|
||||
() => infoParams.suduratio,
|
||||
() => infoParams.isrefresh,
|
||||
() => infoParams.iscorrect,
|
||||
() => infoParams.Isfd
|
||||
],
|
||||
(
|
||||
[
|
||||
newIsGridValue,
|
||||
newAnotherValue,
|
||||
newlineratio,
|
||||
newsuduratio,
|
||||
newisrefresh,
|
||||
newiniscorrect,
|
||||
newfd
|
||||
],
|
||||
[oldIsGridView, oldAnotherValue, oldlineratio, oldsuduratio, oldisrefresh, oldiscorrect, oldds]
|
||||
) => {
|
||||
// 检查 isGrid 是否发生变化
|
||||
if (newIsGridValue !== oldIsGridView) {
|
||||
// 值发送变化的时候 重新绘制 先清除画布
|
||||
Repaint()
|
||||
}
|
||||
// 检查 anotherValue 是否发生变化
|
||||
if (newAnotherValue !== oldAnotherValue) {
|
||||
// 是否开启测量
|
||||
}
|
||||
//振幅
|
||||
if (newlineratio !== oldlineratio) {
|
||||
Repaint()
|
||||
}
|
||||
//走速
|
||||
if (newsuduratio !== oldsuduratio) {
|
||||
Repaint()
|
||||
}
|
||||
//刷新
|
||||
if (newisrefresh !== oldisrefresh) {
|
||||
console.log(newisrefresh)
|
||||
Repaint()
|
||||
}
|
||||
//
|
||||
if (newiniscorrect !== oldiscorrect) {
|
||||
isdialog.value = newiniscorrect
|
||||
}
|
||||
if (newfd !== oldds) {
|
||||
FD.value = newfd
|
||||
console.log(FD.value)
|
||||
}
|
||||
}
|
||||
)
|
||||
//恢复导联
|
||||
function recovery() {
|
||||
transfer.value = []
|
||||
beatArray1.value = beatArray2.value.slice()
|
||||
Repaint()
|
||||
}
|
||||
|
||||
//交换导联按钮
|
||||
function checkboxchage() {
|
||||
checkes.value = transfer.value.length === 2 ? false : true
|
||||
}
|
||||
//交换导联
|
||||
function clickdl() {
|
||||
const one = transfer.value[0]
|
||||
const two = transfer.value[1]
|
||||
const oneindex = transferleads.indexOf(one)
|
||||
const twoindex = transferleads.indexOf(two)
|
||||
swapElements(oneindex, twoindex)
|
||||
Repaint()
|
||||
}
|
||||
|
||||
// 定义一个方法来交换数组中的两个元素
|
||||
const swapElements = (index1, index2) => {
|
||||
// 检查索引是否有效
|
||||
if (
|
||||
index1 < 0 ||
|
||||
index2 < 0 ||
|
||||
index1 >= beatArray1.value.length ||
|
||||
index2 >= beatArray1.value.length
|
||||
) {
|
||||
console.log('索引超出数组范围')
|
||||
return
|
||||
}
|
||||
// 交换元素
|
||||
const temp = beatArray1.value[index1]
|
||||
beatArray1.value[index1] = beatArray1.value[index2]
|
||||
beatArray1.value[index2] = temp
|
||||
}
|
||||
|
||||
//纠错
|
||||
function close() {
|
||||
isdialog.value = false
|
||||
transfer.value = []
|
||||
emits('update:value', isdialog.value)
|
||||
}
|
||||
|
||||
// 定义一个对象来存储命令值和对应的比例文本
|
||||
const lineratioMap = {
|
||||
0.012: '2.5',
|
||||
0.025: '5',
|
||||
0.05: '10',
|
||||
0.1: '20',
|
||||
0.2: '40'
|
||||
}
|
||||
const suduratioMap = {
|
||||
0.2: '5',
|
||||
0.4: '10',
|
||||
0.5: '12.5',
|
||||
1: '25',
|
||||
2: '50'
|
||||
}
|
||||
function convert(command, type) {
|
||||
if (type === 0) {
|
||||
return suduratioMap[command] || null
|
||||
} else {
|
||||
return lineratioMap[command] || null
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新 重绘
|
||||
function Repaint() {
|
||||
eliminate(leftCanvasprint.value)
|
||||
eliminate(rightCanvasprint.value)
|
||||
eliminate(bottomCanvasprint.value)
|
||||
eliminate(bottomCanvasprint1.value)
|
||||
eliminate(topCanvasprint.value)
|
||||
eliminate(topCanvasprint1.value)
|
||||
Bottom()
|
||||
}
|
||||
|
||||
// 定义要处理的键
|
||||
const leads = [
|
||||
'LEAD_I',
|
||||
'LEAD_II',
|
||||
'LEAD_III',
|
||||
'LEAD_AVR',
|
||||
'LEAD_AVL',
|
||||
'LEAD_AVF',
|
||||
'LEAD_V1',
|
||||
'LEAD_V2',
|
||||
'LEAD_V3',
|
||||
'LEAD_V4',
|
||||
'LEAD_V5',
|
||||
'LEAD_V6'
|
||||
]
|
||||
|
||||
const transferleads = ['I', 'II', 'III', 'avR', 'avL', 'avF', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6']
|
||||
// 每个心电波形名称数组
|
||||
const lead_name = ['I', 'II', 'III', 'aVR', 'aVL', 'aVF']
|
||||
|
||||
const rlead_name = ['V1', 'V2', 'V3', 'V4', 'V5', 'V6']
|
||||
|
||||
//将读取到的文件转换成数组
|
||||
function handleFileChange() {
|
||||
const json = JSON.parse(text.value)
|
||||
|
||||
leads.forEach((lead, index) => {
|
||||
const value = json[lead].trim()
|
||||
const commaSeparatedValue = value.replace(/\s+/g, ',')
|
||||
const numberArray = commaSeparatedValue.split(',').map(Number)
|
||||
beatArray1.value.push(numberArray)
|
||||
})
|
||||
beatArray2.value = beatArray1.value.slice()
|
||||
}
|
||||
|
||||
// 定义移动画布的函数
|
||||
function moveCanvas(direction, count) {
|
||||
const xoffset = count === 1 ? 1 : count
|
||||
|
||||
if (direction === 'left') {
|
||||
if (offset !== -20 * 60) offset -= spacing * xoffset
|
||||
} else if (direction === 'right') {
|
||||
if (offset !== 0) offset += spacing * xoffset
|
||||
}
|
||||
|
||||
// if (direction === 'left') {
|
||||
// offset -= spacing
|
||||
// } else if (direction === 'right') {
|
||||
// offset += spacing
|
||||
// }
|
||||
topCanvasprint.value.style.left = offset + 'px'
|
||||
topCanvasprint1.value.style.left = offset + 'px'
|
||||
}
|
||||
|
||||
//清除画布内容
|
||||
function eliminate(c_canvas) {
|
||||
const ctx = c_canvas.getContext('2d')
|
||||
ctx.clearRect(0, 0, c_canvas.width, c_canvas.height)
|
||||
}
|
||||
function Bottom() {
|
||||
drawbottomCanvasprint()
|
||||
drawtopCanvasprint()
|
||||
}
|
||||
// 绘制底层画布的内容
|
||||
function drawbottomCanvasprint() {
|
||||
if (infoParams.Isgrid === 1) {
|
||||
lbackcanvas(leftCanvasprint.value)
|
||||
rbackcanvas(rightCanvasprint.value)
|
||||
drawMultipleLines(bottomCanvasprint.value, beatArray1.value)
|
||||
drawMultipleLines(bottomCanvasprint1.value, beatArray1.value)
|
||||
}
|
||||
begin(leftCanvasprint.value, beatArray1.value)
|
||||
beginr(rightCanvasprint.value, beatArray1.value)
|
||||
}
|
||||
|
||||
// 绘制顶层画布的内容
|
||||
function drawtopCanvasprint() {
|
||||
drawMultipleLinesl(topCanvasprint.value, beatArray1.value)
|
||||
drawMultipleLinesr(topCanvasprint1.value, beatArray1.value)
|
||||
}
|
||||
//定标电压的画布
|
||||
function lbackcanvas(c_canvas) {
|
||||
drawBigBG(c_canvas)
|
||||
drawSmallGrid(c_canvas)
|
||||
drawMediumGrid(c_canvas)
|
||||
}
|
||||
function rbackcanvas(c_canvas) {
|
||||
drawBigBG(c_canvas)
|
||||
drawSmallGrid(c_canvas)
|
||||
drawMediumGrid(c_canvas)
|
||||
}
|
||||
|
||||
// 绘制函数
|
||||
function drawBigBG(c_canvas) {
|
||||
const context = c_canvas.getContext('2d')
|
||||
context.globalAlpha = 1
|
||||
context.fillStyle = 'rgba(0,0,0,0)'
|
||||
context.fillRect(0, 0, c_canvas.width + 21, c_canvas.height + 21)
|
||||
}
|
||||
//画小格子
|
||||
function drawSmallGrid(c_canvas) {
|
||||
const context = c_canvas.getContext('2d')
|
||||
context.globalAlpha = 1
|
||||
context.fillStyle = '#ff6b64'
|
||||
const dotMarginX = 20 / 5,
|
||||
dotMarginY = 20 / 5
|
||||
for (let i = dotMarginX; i < c_canvas.width; i += dotMarginX) {
|
||||
if (i % 20 !== 0) {
|
||||
for (let j = dotMarginY; j < c_canvas.height; j += dotMarginY) {
|
||||
if (j % 20 !== 0) {
|
||||
context.rect(i, j, 1, 0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
context.fill()
|
||||
}
|
||||
//中格子
|
||||
function drawMediumGrid(c_canvas) {
|
||||
const context = c_canvas.getContext('2d')
|
||||
context.globalAlpha = 1
|
||||
context.strokeStyle = '#ff6b64'
|
||||
context.strokeWidth = 1.5
|
||||
context.beginPath()
|
||||
for (let x = 0.5; x < c_canvas.width; x += 20) {
|
||||
context.moveTo(x, 0)
|
||||
context.lineTo(x, c_canvas.height)
|
||||
}
|
||||
for (let y = 0.5; y < c_canvas.height; y += 20) {
|
||||
context.moveTo(0, y)
|
||||
context.lineTo(c_canvas.width, y)
|
||||
}
|
||||
context.stroke()
|
||||
context.closePath()
|
||||
}
|
||||
|
||||
function begin(c_canvas, beatArray) {
|
||||
const ctx = c_canvas.getContext('2d')
|
||||
ctx.globalAlpha = 1
|
||||
ctx.strokeStyle = '#000000'
|
||||
let offset = -50
|
||||
const spacing = 100
|
||||
beatArray.forEach((dataArray, index) => {
|
||||
if (index <= 6) {
|
||||
const x = 0
|
||||
const y = offset - dataArray[index - 1] * 0.025
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(x, y)
|
||||
ctx.lineTo(x + 5, y)
|
||||
ctx.moveTo(x + 5, y - 40)
|
||||
ctx.lineTo(x + 15, y - 40)
|
||||
ctx.moveTo(x + 5, y - 40)
|
||||
ctx.lineTo(x + 5, y)
|
||||
ctx.moveTo(x + 15, y - 40)
|
||||
ctx.lineTo(x + 15, y)
|
||||
ctx.moveTo(x + 15, y)
|
||||
ctx.lineTo(x + 20, y)
|
||||
ctx.stroke()
|
||||
ctx.closePath()
|
||||
if (index > 0 && index < 4) {
|
||||
ctx.font = '15px Arial'
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.fillText(lead_name[index - 1], x + 4, y + 14)
|
||||
} else {
|
||||
ctx.font = '10px Arial'
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.fillText(lead_name[index - 1], x + 1, y + 12)
|
||||
}
|
||||
offset += spacing
|
||||
}
|
||||
})
|
||||
ctx.strokeStyle = '#000000'
|
||||
}
|
||||
|
||||
function beginr(c_canvas, beatArray) {
|
||||
const ctx = c_canvas.getContext('2d')
|
||||
ctx.globalAlpha = 1
|
||||
ctx.strokeStyle = '#000000'
|
||||
let offset = -50
|
||||
const spacing = 100
|
||||
beatArray.forEach((dataArray, index) => {
|
||||
if (index <= 6) {
|
||||
const x = 0
|
||||
const y = offset - dataArray[index - 1] * 0.025
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(x, y)
|
||||
ctx.lineTo(x + 5, y)
|
||||
ctx.moveTo(x + 5, y - 40)
|
||||
ctx.lineTo(x + 15, y - 40)
|
||||
ctx.moveTo(x + 5, y - 40)
|
||||
ctx.lineTo(x + 5, y)
|
||||
ctx.moveTo(x + 15, y - 40)
|
||||
ctx.lineTo(x + 15, y)
|
||||
ctx.moveTo(x + 15, y)
|
||||
ctx.lineTo(x + 20, y)
|
||||
ctx.stroke()
|
||||
ctx.closePath()
|
||||
ctx.font = '11px Arial'
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.fillText(rlead_name[index - 1], x + 2, y + 12)
|
||||
offset += spacing
|
||||
}
|
||||
})
|
||||
ctx.strokeStyle = '#000000'
|
||||
}
|
||||
|
||||
//背景
|
||||
function drawMultipleLines(c_canvas, beatArrays) {
|
||||
const ctx = c_canvas.getContext('2d')
|
||||
ctx.clearRect(0, 0, c_canvas.width, c_canvas.height)
|
||||
drawBigBG(c_canvas)
|
||||
drawSmallGrid(c_canvas)
|
||||
drawMediumGrid(c_canvas)
|
||||
}
|
||||
//左边心电图
|
||||
function drawMultipleLinesl(c_canvas, beatArrays) {
|
||||
const ctx = c_canvas.getContext('2d')
|
||||
ctx.clearRect(0, 0, c_canvas.width, c_canvas.height)
|
||||
let offset = 50
|
||||
const spacing = 100
|
||||
beatArrays.forEach((dataArray, index) => {
|
||||
if (index <= 5) {
|
||||
drawLine1(c_canvas, dataArray, offset, index)
|
||||
offset += spacing
|
||||
}
|
||||
})
|
||||
}
|
||||
//右边心电图
|
||||
function drawMultipleLinesr(c_canvas, beatArrays) {
|
||||
const ctx = c_canvas.getContext('2d')
|
||||
ctx.clearRect(0, 0, c_canvas.width, c_canvas.height)
|
||||
let offset = 50
|
||||
const spacing = 100
|
||||
beatArrays.forEach((dataArray, index) => {
|
||||
if (index > 5) {
|
||||
drawLine1(c_canvas, dataArray, offset, index)
|
||||
offset += spacing
|
||||
}
|
||||
})
|
||||
}
|
||||
//画心电图线
|
||||
function drawLine1(c_canvas, beatArray, offset, index) {
|
||||
const ctx = c_canvas.getContext('2d')
|
||||
ctx.lineCap = 'butt' // 设置线条端点样式为平直
|
||||
ctx.lineJoin = 'miter' // 设置线条交点样式为尖角
|
||||
ctx.strokeStyle = '#030101'
|
||||
ctx.lineWidth = 1
|
||||
ctx.beginPath()
|
||||
if (index <= 5) {
|
||||
if (beatArray.length > 0) {
|
||||
const firstX = 0
|
||||
const firstY =0- beatArray[0] * infoParams.lineratio + offset
|
||||
ctx.moveTo(firstX, firstY)
|
||||
for (let i = 0; i < beatArray.length - 1; i++) {
|
||||
const x2 = (0 + (i + 1) / 10) * infoParams.suduratio
|
||||
const y2 = (0)-beatArray[i + 1] * infoParams.lineratio + offset
|
||||
ctx.lineTo(x2, y2)
|
||||
}
|
||||
ctx.stroke()
|
||||
ctx.closePath()
|
||||
}
|
||||
}
|
||||
if (index > 5) {
|
||||
if (beatArray.length > 0) {
|
||||
const firstX = 0
|
||||
const firstY = (0)-beatArray[0] * infoParams.lineratio + offset
|
||||
ctx.moveTo(firstX, firstY)
|
||||
for (let i = 0; i < beatArray.length - 1; i++) {
|
||||
const x2 = (0 + (i + 1) / 10) * infoParams.suduratio
|
||||
const y2 =(0)- beatArray[i + 1] * infoParams.lineratio + offset
|
||||
ctx.lineTo(x2, y2)
|
||||
}
|
||||
ctx.stroke()
|
||||
ctx.closePath()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.printcontainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
width: 1920px;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
height: 100%;
|
||||
max-width: 3920px;
|
||||
max-height: 3920px;
|
||||
}
|
||||
#canvas-containerprint,
|
||||
#canvas-containerprint1 {
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
position: relative;
|
||||
width: 50%;
|
||||
height: 921px;
|
||||
margin-right: 0;
|
||||
}
|
||||
#bottomCanvasprint,
|
||||
#bottomCanvasprint1 {
|
||||
position: absolute;
|
||||
/* width: 1240; */
|
||||
/* height: 75vh; */
|
||||
z-index: 0;
|
||||
margin-left: 20px;
|
||||
}
|
||||
#leftCanvasprint,
|
||||
#rightCanvasprint {
|
||||
position: absolute;
|
||||
width: 21;
|
||||
/* height: 1080px; */
|
||||
z-index: 99;
|
||||
background-color: rgb(255, 255, 255);
|
||||
}
|
||||
#topCanvasprint,
|
||||
#topCanvasprint1 {
|
||||
position: absolute;
|
||||
width: 1920px;
|
||||
/* height: 600px; */
|
||||
z-index: 1;
|
||||
background: transparent;
|
||||
margin-left: 20px;
|
||||
}
|
||||
canvas {
|
||||
position: absolute;
|
||||
}
|
||||
.myrtip {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
margin-right: 30px;
|
||||
}
|
||||
.mycheckbox-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.mycheckbox-item {
|
||||
margin-left: 5px;
|
||||
margin-right: 30px; /* 根据需要调整间距 */
|
||||
margin-bottom: 10px; /* 根据需要调整间距 */
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.ecg-report {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 3px auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
/* width: 90%; */
|
||||
width: 1920px;
|
||||
/* height: 921px; */
|
||||
}
|
||||
|
||||
.ecg-main {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 32px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
/* 添加其他样式 */
|
||||
</style>
|
96
src/views/ECG/ECGprint.vue
Normal file
96
src/views/ECG/ECGprint.vue
Normal file
@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div class="myecg-report">
|
||||
<!-- 心电图 -->
|
||||
<div class="ecg-main">
|
||||
<div class="container">
|
||||
<el-image
|
||||
style="width: 100%; height: 90vh"
|
||||
:src="infoParams.imagebase64"
|
||||
fit="fill"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
defineOptions({ name: 'ReportInfoECG' })
|
||||
/*
|
||||
* 李传洋
|
||||
* ReportInfoECG
|
||||
**/
|
||||
/** 导入内容 **/
|
||||
const infoParams = defineProps({
|
||||
id: String,
|
||||
examId: String,
|
||||
regId: String,
|
||||
orgId: String,
|
||||
pname: String,
|
||||
gender: String,
|
||||
birthday: String,
|
||||
billDoctorDepartment: String,
|
||||
applicationDate: String,
|
||||
examDescription: String,
|
||||
diagResults: String,
|
||||
examItemName: String,
|
||||
deviceName: String,
|
||||
deviceType: String,
|
||||
diagDoctor: String,
|
||||
diagDate: String,
|
||||
reviewDoctor: String,
|
||||
reviewDate: String,
|
||||
imagebase64: String
|
||||
})
|
||||
|
||||
/** 钩子方法 **/
|
||||
onMounted(async () => {})
|
||||
|
||||
/** 导出内容 **/
|
||||
/**
|
||||
* 备注:
|
||||
*
|
||||
* **/
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/*** 报告单 ***/
|
||||
.myecg-report {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 3px auto;
|
||||
padding: 5px;
|
||||
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
width: 90%;
|
||||
overflow: hidden;
|
||||
//min-height: 200mm;
|
||||
}
|
||||
|
||||
.ecg-main {
|
||||
// margin-top: 6px;
|
||||
// margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
height: 100%;
|
||||
max-width: 3920px;
|
||||
max-height: 3920px;
|
||||
}
|
||||
canvas {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.image-slot {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
@ -333,7 +333,8 @@ const queryParams = reactive({
|
||||
reviewDate: [],
|
||||
thumbnailImgUrl: undefined,
|
||||
createTime: [],
|
||||
isFavourite: ''
|
||||
isFavourite: '',
|
||||
pdfurl:''
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
Loading…
Reference in New Issue
Block a user