ecg打印、sso单点登录
This commit is contained in:
parent
3132ed4506
commit
2a2f65296c
@ -42,7 +42,7 @@ import VueDOMPurifyHTML from 'vue-dompurify-html' // 解决v-html 的安全隐
|
||||
import print from "vue3-print-nb";//打印的
|
||||
import vue3videoPlay from 'vue3-video-play' // 引入组件
|
||||
import 'vue3-video-play/dist/style.css' // 引入css
|
||||
|
||||
import '@/views/applyregistration/reportPrintStatistics/reportPrint.scss' //打印(全局)
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
// 创建实例
|
||||
const setupAll = async () => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import router from './router'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import { isRelogin } from '@/config/axios/service'
|
||||
import { getAccessToken } from '@/utils/auth'
|
||||
import { getAccessToken, setToken, removeToken } from '@/utils/auth'
|
||||
import { useTitle } from '@/hooks/web/useTitle'
|
||||
import { useNProgress } from '@/hooks/web/useNProgress'
|
||||
import { usePageLoading } from '@/hooks/web/usePageLoading'
|
||||
@ -56,10 +56,44 @@ const whiteList = [
|
||||
'/oauthLogin/gitee'
|
||||
]
|
||||
|
||||
// 路由加载前
|
||||
//获取URL参数
|
||||
const getQueryString = (name) => {
|
||||
const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
|
||||
const r = window.location.search.substr(1).match(reg);
|
||||
if (r != null) return unescape(r[2]);
|
||||
return null;
|
||||
}
|
||||
|
||||
//移除URL参数
|
||||
const removeQueryString = (url, paramToRemove) => {
|
||||
if (url && url.trim()) {
|
||||
const urlObj = new URL(url);
|
||||
if (urlObj.searchParams && urlObj.searchParams.has(paramToRemove)) {
|
||||
urlObj.searchParams.delete(paramToRemove);
|
||||
}
|
||||
return urlObj.toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
//路由加载前
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
start()
|
||||
loadStart()
|
||||
//sso单点登录
|
||||
if (getQueryString("accessToken") && getQueryString("refreshToken")) {
|
||||
const token: any = {
|
||||
accessToken: getQueryString("accessToken")?.trim(),
|
||||
refreshToken: getQueryString("refreshToken")?.trim()
|
||||
}
|
||||
let urlStr = window.location.href;
|
||||
urlStr = removeQueryString(urlStr, "accessToken");
|
||||
urlStr = removeQueryString(urlStr, "refreshToken");
|
||||
removeToken()
|
||||
setToken(token)
|
||||
window.location.href = urlStr;
|
||||
return;
|
||||
}
|
||||
if (getAccessToken()) {
|
||||
if (to.path === '/login') {
|
||||
next({ path: '/' })
|
||||
|
@ -64,7 +64,7 @@
|
||||
<el-button type="primary" plain @click="openECGDialog" v-if="isshowwjz"
|
||||
><el-icon><Warning /></el-icon>危急值</el-button
|
||||
>
|
||||
<el-button type="primary" plain @click="openreprotdiag"
|
||||
<el-button type="primary" plain @click="openreprotdiag"
|
||||
><el-icon><Check /></el-icon>已上报</el-button
|
||||
>
|
||||
</el-button-group>
|
||||
@ -430,9 +430,10 @@
|
||||
:expand-on-click-node="false"
|
||||
/>
|
||||
</el-drawer>
|
||||
<!--打印弹窗-->
|
||||
<!--打印弹窗(老)-->
|
||||
<el-dialog
|
||||
title=""
|
||||
v-if="false"
|
||||
v-model="isdiagshow"
|
||||
:fullscreen="true"
|
||||
append-to-body
|
||||
@ -444,9 +445,8 @@
|
||||
<!--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">
|
||||
<div id="hiddenPdfDiv" v-if="false && isprintimage">
|
||||
<!--心电图-->
|
||||
<ECGhtmlprint
|
||||
style="height: 1080px; width: 1920px; page-break-after: always; visibility: hidden"
|
||||
@ -471,6 +471,74 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!--打印弹窗(新)-->
|
||||
<el-dialog
|
||||
v-model="isdiagshow"
|
||||
:fullscreen="true"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
@close="printclose"
|
||||
>
|
||||
<template #title>
|
||||
<div
|
||||
style="
|
||||
margin-bottom: -18px;
|
||||
text-align: center;
|
||||
font-family: Arial, sans-serif;
|
||||
color: #6e6666;
|
||||
font-size: 22px;
|
||||
"
|
||||
>
|
||||
打印预览
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div style="text-align: right">
|
||||
<el-button type="primary" style="width: 90px" v-print="{ id: 'printMe_ecg' }">
|
||||
<span>打印</span>
|
||||
</el-button>
|
||||
</div>
|
||||
<el-divider style="margin-top: 8px; margin-bottom: 10px" />
|
||||
</div>
|
||||
<div id="printMe_ecg">
|
||||
<div class="page-item-ecg">
|
||||
<ReportInfoECG
|
||||
style="height: 99.88%"
|
||||
:extraInfo="queryParams"
|
||||
:jsonurl="queryParams.ecgJsonDataFilePath"
|
||||
:lineratio="0.025"
|
||||
:suduratio="suduratio"
|
||||
:age="age"
|
||||
:billDoctorDepartment="rowinfo.billDoctorDepartment"
|
||||
:doctorname="rowinfo.doctorname"
|
||||
:regId="id"
|
||||
:orgName="rowinfo.orgName"
|
||||
:pname="rowinfo.pname"
|
||||
:gender="rowinfo.gender"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<div style="position: fixed; top: 0; left: 0; z-index: -1100">
|
||||
<ReportInfoECG
|
||||
v-if="isprintimage"
|
||||
@update:image="updateimagebase"
|
||||
style="height: 1080px; width: 1920px; page-break-after: always; visibility: hidden"
|
||||
:extraInfo="queryParams"
|
||||
:jsonurl="queryParams.ecgJsonDataFilePath"
|
||||
:lineratio="0.025"
|
||||
:suduratio="suduratio"
|
||||
:age="age"
|
||||
:billDoctorDepartment="rowinfo.billDoctorDepartment"
|
||||
:doctorname="rowinfo.doctorname"
|
||||
:regId="id"
|
||||
:orgName="rowinfo.orgName"
|
||||
:pname="rowinfo.pname"
|
||||
:gender="rowinfo.gender"
|
||||
/>
|
||||
</div>
|
||||
<ECGWarningDialog ref="ECGDialog" />
|
||||
<ECGReport ref="ECGReportDialog" />
|
||||
</template>
|
||||
@ -487,9 +555,11 @@ 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 ReportInfoECG from '@/views/applyregistration/reportPrintStatistics/ReportInfoECG.vue'
|
||||
import htmlToPdf from '@/utils/htmlPdf'
|
||||
import ECGWarningDialog from '@/views/ECG/ECGWaring/ECGWarningDialog.vue'
|
||||
import ECGReport from '@/views/ECG/ECGWaring/ECGReport.vue'
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const message = useMessage() // 消息弹窗
|
||||
@ -519,7 +589,7 @@ const eltextrow = ref(4) //诊断内容占据的行
|
||||
const isshare = ref(true) //是否展示分享二维码
|
||||
const qrcodeRef = ref(null)
|
||||
const isdiagshow = ref(false) //是否显示打印窗体
|
||||
const imagebase64 = ref()
|
||||
const imagebase64 = ref('')
|
||||
const isprintimage = ref(false) //是否生成打印文件base64
|
||||
const ECGDialog = ref() //危急值消息弹窗
|
||||
const isshowwjz = ref(false)
|
||||
@ -549,9 +619,9 @@ const open = async (row: any) => {
|
||||
calculateAge(row.birthday)
|
||||
Isgrid.value = 1 // 没次打开都是显示的 只有点击网格才会变化
|
||||
isChildVisible.value = true
|
||||
isshowwjz.value=true
|
||||
isshowwjz.value = true
|
||||
//判断是否显示危急值上报功能 条件是当前机构等于上级机构
|
||||
// isshowwjz.value=Profilevo.value.orgId===row.highLevelOrgId?true:false
|
||||
// isshowwjz.value=Profilevo.value.orgId===row.highLevelOrgId?true:false
|
||||
nextTick(() => {
|
||||
/* 计算右侧诊断*/
|
||||
const canvasContainer = document.getElementById('elform')
|
||||
@ -652,6 +722,8 @@ async function save() {
|
||||
console.log(error)
|
||||
})
|
||||
} finally {
|
||||
isprintimage.value = false
|
||||
imagebase64.value = ''
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
@ -673,19 +745,19 @@ function resetForm() {
|
||||
|
||||
//关闭打印窗口
|
||||
function printclose() {
|
||||
isprintimage.value = false
|
||||
imagebase64.value = ''
|
||||
isdiagshow.value = false
|
||||
isprintimage.value = false
|
||||
}
|
||||
//打印
|
||||
function print() {
|
||||
if (!queryParams.value.autoDiagTime) {
|
||||
isdiagshow.value = false
|
||||
isprintimage.value = false
|
||||
//isprintimage.value = false
|
||||
message.alertError('请保存后再进行打印')
|
||||
} else {
|
||||
isdiagshow.value = true
|
||||
isprintimage.value = true
|
||||
//isprintimage.value = true
|
||||
}
|
||||
}
|
||||
//纠错窗体回传值
|
||||
@ -697,7 +769,7 @@ function openECGDialog() {
|
||||
ECGDialog.value.opendiag('1', rowinfo.value)
|
||||
}
|
||||
//已上报危急值弹窗
|
||||
function openreprotdiag(){
|
||||
function openreprotdiag() {
|
||||
ECGReportDialog.value.opendiag(rowinfo.value)
|
||||
}
|
||||
//纠错
|
||||
@ -849,7 +921,7 @@ defineOptions({ name: 'ECGForm' })
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang="scss" scoped>
|
||||
.mycontainer {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
@ -895,4 +967,26 @@ defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
/*心电图区域样式*/
|
||||
|
||||
/*心电图区域样式*/
|
||||
|
||||
/* 打印(报告单) */
|
||||
.page-item-ecg {
|
||||
padding: 1mm 0.8mm 1mm 2mm;
|
||||
border: 2px solid #ccc;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.ignore-print {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.page-item-ecg {
|
||||
border-width: 0;
|
||||
height: 206mm;
|
||||
page-break-after: always;
|
||||
padding: 0;
|
||||
width: calc(100% - 4.2mm);
|
||||
margin-left: 2.6mm;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,681 @@
|
||||
<template>
|
||||
<div id="PDF" class="ecg-report" v-loading="ecg_loading" element-loading-text="加载中...">
|
||||
<!-- 头部 -->
|
||||
<div>
|
||||
<div style="text-align: center; font-size: 21px; margin-bottom: -16px">
|
||||
{{ infoParams.orgName }}
|
||||
</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: 3.4px 6px; font-size: 13.6px; line-height: 1.28">
|
||||
<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">
|
||||
<el-image
|
||||
alt=""
|
||||
fit="fill"
|
||||
loading="eager"
|
||||
style="width: 100%; height: 144mm"
|
||||
:src="reportimage"
|
||||
:preview-src-list="[reportimage]"
|
||||
crossorigin="anonymous"
|
||||
v-if="true"
|
||||
/>
|
||||
<el-scrollbar v-show="false">
|
||||
<div id="ecg-substance" style="width: 1760px">
|
||||
<div class="ecg-container" style="height: 872px">
|
||||
<div id="canvas-container-l" style="height: 872px">
|
||||
<canvas
|
||||
crossorigin="anonymous"
|
||||
ref="leftCanvas"
|
||||
id="leftCanvas"
|
||||
width="21"
|
||||
height="861"
|
||||
></canvas>
|
||||
<canvas
|
||||
crossorigin="anonymous"
|
||||
ref="bottomCanvasL"
|
||||
id="bottomCanvas-l"
|
||||
width="1700"
|
||||
height="861"
|
||||
></canvas>
|
||||
<canvas
|
||||
crossorigin="anonymous"
|
||||
ref="topCanvasL"
|
||||
id="topCanvas-l"
|
||||
width="1700"
|
||||
height="861"
|
||||
></canvas>
|
||||
</div>
|
||||
<div id="canvas-container-r" style="height: 872px">
|
||||
<canvas
|
||||
crossorigin="anonymous"
|
||||
ref="rightCanvas"
|
||||
id="rightCanvas"
|
||||
width="21"
|
||||
height="861"
|
||||
></canvas>
|
||||
<canvas
|
||||
crossorigin="anonymous"
|
||||
ref="bottomCanvasR"
|
||||
id="bottomCanvas-r"
|
||||
width="1700"
|
||||
height="861"
|
||||
></canvas>
|
||||
<canvas
|
||||
crossorigin="anonymous"
|
||||
ref="topCanvasR"
|
||||
id="topCanvas-r"
|
||||
width="1700"
|
||||
height="861"
|
||||
></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<el-slider
|
||||
v-show="false"
|
||||
v-model="sliderValue"
|
||||
:min="0"
|
||||
:max="50"
|
||||
:step="1"
|
||||
@input="handleSliderChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 尾部 -->
|
||||
<div style="width: 100%; position: absolute; bottom: 20px; right: 4px">
|
||||
<hr v-if="false" />
|
||||
<el-row style="font-size: 12px">
|
||||
<el-col :span="8" class="pl-6px"> 地址: </el-col>
|
||||
<el-col :span="8" style="text-align: center">
|
||||
{{ '走速:' + convertProps(suduratio, 0) + 'mm/s' }}
|
||||
|
||||
{{ '振幅:' + convertProps(lineratio, 1) + 'mm/mV' }}
|
||||
{{ '报告模式:6X2' }}
|
||||
</el-col>
|
||||
<el-col :span="8" style="text-align: right">
|
||||
{{ formatDate(extraInfo.doctorDiagTime, 'YYYY-MM-DD HH:mm:ss') }}
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div style="font-size: 10.6px; text-align: center; margin-top: 0.4px; margin-bottom: -20px">
|
||||
注:本报告仅供临床医师参考,不作疾病证明
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import html2canvas from 'html2canvas'
|
||||
import htmlToPdf from '@/utils/htmlPdf'
|
||||
|
||||
defineOptions({ name: 'ReportInfoECG' })
|
||||
/*
|
||||
* 李传洋
|
||||
* ReportInfoECG
|
||||
**/
|
||||
/** 导入内容 **/
|
||||
const emits = defineEmits(['update:image'])
|
||||
const infoParams = defineProps({
|
||||
lineratio: {
|
||||
required: false,
|
||||
type: Number,
|
||||
default: 0.05
|
||||
},
|
||||
suduratio: {
|
||||
required: false,
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
jsonurl: String,
|
||||
regId: String,
|
||||
orgId: String,
|
||||
orgName: String,
|
||||
pname: String,
|
||||
gender: String,
|
||||
age: String,
|
||||
birthday: String,
|
||||
billDoctorDepartment: String,
|
||||
doctorname: String,
|
||||
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
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
/** 组件引用 **/
|
||||
const leftCanvas = ref<any>(null)
|
||||
const rightCanvas = ref(null)
|
||||
const bottomCanvasL = ref(null)
|
||||
const bottomCanvasR = ref(null)
|
||||
const topCanvasL = ref<any>(null)
|
||||
const topCanvasR = ref<any>(null)
|
||||
|
||||
/** 数据内容 **/
|
||||
const age = computed(() => {
|
||||
let birthdate = infoParams.birthday
|
||||
if (!birthdate) {
|
||||
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--
|
||||
}
|
||||
return yearsDiff
|
||||
})
|
||||
|
||||
/** 数据内容(ecg) **/
|
||||
const beatArrayECG = ref<any[]>([])
|
||||
const ecg_loading = ref(true)
|
||||
const reportimage = ref<any>('')
|
||||
const heightoff = ref(0) //心电图的偏移量 高度变化的时候
|
||||
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'
|
||||
}
|
||||
const convertProps = (command, type) => {
|
||||
if (type === 0) {
|
||||
return suduratioMap[command] || ''
|
||||
} else {
|
||||
return lineratioMap[command] || ''
|
||||
}
|
||||
}
|
||||
|
||||
const 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)
|
||||
}
|
||||
|
||||
const 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()
|
||||
}
|
||||
|
||||
const 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()
|
||||
}
|
||||
|
||||
const drawLine = (c_canvas, beatArray, offset, index) => {
|
||||
const ctx = c_canvas.getContext('2d')
|
||||
ctx.strokeStyle = '#030101'
|
||||
ctx.lineWidth = 1
|
||||
ctx.beginPath()
|
||||
if (index <= 5) {
|
||||
if (beatArray.length > 0) {
|
||||
const firstX = 0
|
||||
const firstY = heightoff.value - 50 - 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 = heightoff.value - 50 - 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 = heightoff.value - 50 - 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 = heightoff.value - 50 - beatArray[i + 1] * infoParams.lineratio + offset
|
||||
ctx.lineTo(x2, y2)
|
||||
}
|
||||
ctx.stroke()
|
||||
ctx.closePath()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const lead_name = ['I', 'II', 'III', 'aVR', 'aVL', 'aVF'] //每个心电波形名称数组
|
||||
const rlead_name = ['V1', 'V2', 'V3', 'V4', 'V5', 'V6']
|
||||
const begin = (c_canvas, beatArray) => {
|
||||
const ctx = c_canvas.getContext('2d')
|
||||
ctx.globalAlpha = 1
|
||||
ctx.strokeStyle = '#000000'
|
||||
let offset = -50
|
||||
const spacing = heightoff.value
|
||||
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'
|
||||
}
|
||||
|
||||
const beginr = (c_canvas, beatArray) => {
|
||||
const ctx = c_canvas.getContext('2d')
|
||||
ctx.globalAlpha = 1
|
||||
ctx.strokeStyle = '#000000'
|
||||
let offset = -50
|
||||
const spacing = heightoff.value
|
||||
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'
|
||||
}
|
||||
|
||||
const drawMultipleLinesl = (c_canvas, beatArrays) => {
|
||||
const ctx = c_canvas.getContext('2d')
|
||||
ctx.clearRect(0, 0, c_canvas.width, c_canvas.height)
|
||||
let offset = 0
|
||||
const spacing = heightoff.value
|
||||
beatArrays.forEach((dataArray, index) => {
|
||||
if (index <= 5) {
|
||||
drawLine(c_canvas, dataArray, offset, index)
|
||||
offset += spacing
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const drawMultipleLinesr = (c_canvas, beatArrays) => {
|
||||
const ctx = c_canvas.getContext('2d')
|
||||
ctx.clearRect(0, 0, c_canvas.width, c_canvas.height)
|
||||
let offset = 0
|
||||
const spacing = heightoff.value
|
||||
beatArrays.forEach((dataArray, index) => {
|
||||
if (index > 5) {
|
||||
drawLine(c_canvas, dataArray, offset, index)
|
||||
offset += spacing
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const backcanvas = (c_canvas) => {
|
||||
drawBigBG(c_canvas)
|
||||
drawSmallGrid(c_canvas)
|
||||
drawMediumGrid(c_canvas)
|
||||
}
|
||||
|
||||
const 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)
|
||||
}
|
||||
|
||||
const drawBottomCanvas = () => {
|
||||
backcanvas(leftCanvas.value)
|
||||
backcanvas(rightCanvas.value)
|
||||
drawMultipleLines(bottomCanvasL.value, beatArrayECG.value)
|
||||
drawMultipleLines(bottomCanvasR.value, beatArrayECG.value)
|
||||
begin(leftCanvas.value, beatArrayECG.value)
|
||||
beginr(rightCanvas.value, beatArrayECG.value)
|
||||
}
|
||||
|
||||
const drawTopCanvas = () => {
|
||||
drawMultipleLinesl(topCanvasL.value, beatArrayECG.value)
|
||||
drawMultipleLinesr(topCanvasR.value, beatArrayECG.value)
|
||||
}
|
||||
|
||||
const RenderECG = () => {
|
||||
drawBottomCanvas()
|
||||
drawTopCanvas()
|
||||
}
|
||||
|
||||
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 LoadECG = async () => {
|
||||
//const response = await fetch('http://localhost:8080/static/abcde.txt')
|
||||
const response = await fetch(infoParams.jsonurl as any)
|
||||
const json = JSON.parse(await response.text())
|
||||
leads.forEach((lead, index) => {
|
||||
const value = json[lead].trim()
|
||||
const commaSeparatedValue = value.replace(/\s+/g, ',')
|
||||
const numberArray = commaSeparatedValue.split(',').map(Number)
|
||||
beatArrayECG.value.push(numberArray)
|
||||
})
|
||||
}
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
//生成ecg
|
||||
await LoadECG()
|
||||
RenderECG()
|
||||
|
||||
//导出ecg
|
||||
nextTick(async () => {
|
||||
let ecgCenter = document.getElementById('ecg-substance') as any
|
||||
let ecgCenter_parent = ecgCenter.parentNode
|
||||
try {
|
||||
ecgCenter_parent.removeChild(ecgCenter)
|
||||
ecgCenter.style.position = 'absolute'
|
||||
ecgCenter.style.top = '0px'
|
||||
ecgCenter.style.zIndex = '-5000'
|
||||
document.getElementsByTagName('body')[0].append(ecgCenter)
|
||||
|
||||
//ecgCenter.offsetWidth
|
||||
//ecgCenter.offsetHeight
|
||||
let targetWidth = 1760
|
||||
let targetHeight = 862
|
||||
let options = {
|
||||
allowTaint: false,
|
||||
taintTest: false,
|
||||
logging: false,
|
||||
useCORS: true,
|
||||
width: targetWidth,
|
||||
height: targetHeight,
|
||||
dpi: 300, //将分辨率提高到特定的DPI 提高四倍
|
||||
scale: 1.14 //按比例增加分辨率
|
||||
} as any
|
||||
const canvas = await html2canvas(ecgCenter, options)
|
||||
if (canvas) {
|
||||
let canvasObj = canvas as any
|
||||
canvasObj.crossorigin = 'anonymous'
|
||||
let imageDataUrl = canvas.toDataURL('image/jpeg', 1)
|
||||
reportimage.value = imageDataUrl
|
||||
}
|
||||
} finally {
|
||||
document.getElementsByTagName('body')[0].removeChild(ecgCenter)
|
||||
ecgCenter.style.position = ''
|
||||
ecgCenter.style.top = ''
|
||||
ecgCenter.style.zIndex = ''
|
||||
ecgCenter_parent.append(ecgCenter)
|
||||
ecg_loading.value = false
|
||||
nextTick(() => {
|
||||
createImage()
|
||||
})
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('获取数据时出错:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const sliderValue = ref(0) // 初始化滑块值
|
||||
const previousValue = ref(0) // 记录上一个滑块值
|
||||
const swipeDirection = ref('') // 用于存储滑动方向
|
||||
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)
|
||||
}
|
||||
const spacing = 20
|
||||
let offset = 0
|
||||
const 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
|
||||
// }
|
||||
topCanvasL.value.style.left = offset + 'px'
|
||||
topCanvasR.value.style.left = offset + 'px'
|
||||
}
|
||||
|
||||
const createImage = async () => {
|
||||
const imagebase64 = await htmlToPdf.getDivContentAsBase64('#PDF')
|
||||
emits('update:image', imagebase64)
|
||||
}
|
||||
|
||||
/** 钩子方法 **/
|
||||
onMounted(async () => {
|
||||
//heightoff.value = (leftCanvas.value.height - 100) / 6
|
||||
heightoff.value = (821 - 0) / 6
|
||||
|
||||
await fetchData()
|
||||
})
|
||||
|
||||
/** 导出内容 **/
|
||||
defineExpose({ ecg_loading })
|
||||
|
||||
/**
|
||||
* 备注:
|
||||
*
|
||||
* **/
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/*** 报告单 ***/
|
||||
.ecg-report {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 1px;
|
||||
padding: 1px;
|
||||
position: relative;
|
||||
width: 99.2%;
|
||||
min-height: 204mm;
|
||||
}
|
||||
|
||||
.ecg-main {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 26px;
|
||||
}
|
||||
|
||||
/*** ecg核心 ***/
|
||||
.ecg-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
width: 100%;
|
||||
border: 0px solid black;
|
||||
}
|
||||
|
||||
#canvas-container-l,
|
||||
#canvas-container-r {
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
width: 50%;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#leftCanvas,
|
||||
#rightCanvas {
|
||||
position: absolute;
|
||||
background-color: rgb(255, 255, 255);
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
#bottomCanvas-l,
|
||||
#bottomCanvas-r {
|
||||
position: absolute;
|
||||
margin-left: 20px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#topCanvas-l,
|
||||
#topCanvas-r {
|
||||
position: absolute;
|
||||
margin-left: 20px;
|
||||
background: transparent;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
canvas {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
@ -779,19 +779,3 @@ console.log(download)
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
/* 打印(全局) */
|
||||
@media print {
|
||||
@page {
|
||||
size: auto;
|
||||
margin-top: 2mm;
|
||||
margin-bottom: 2mm;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,12 @@
|
||||
/* 打印(全局) */
|
||||
@media print {
|
||||
@page {
|
||||
size: auto;
|
||||
margin: 1mm 0;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user