ai-watch-app/components/ecg-info/ecg-info.vue

730 lines
17 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>
<view class="content" style="width: 60%;">
<uni-popup ref="popup" type="bottom" border-radius="10px 10px 0 0">
<view class="main">
<view class="pop-main" v-if="typeIndex == 1">
<view class="pops">
<view class="pop-close" @click="close()">
×
</view>
<view class="dxxl">
<scroll-view class="myScoll" scroll-x :show-scrollbar="true">
<view class="box1">
<canvas canvas-id="ecg" style="width: 3000px; height: 300px;"></canvas>
</view>
<view class="box2" :style="{'left': pLeft + 'px'}">
<canvas canvas-id="myCanvas" style="width: 3000px; height: 300px;"></canvas>
</view>
</scroll-view>
</view>
<view class="list">
<view class="flx flx_ac ">
<view class="" style="margin-top: 10rpx;margin-right: 10rpx;">
<image src="../../static/icon/xinlv-small.png" style="width: 40rpx;" mode="widthFix">
</image>
</view>
<view class="" style="color: red;">
心率
</view>
</view>
<view class="flx flx_sb sl_box flx_wp">
<view class="all" v-for="(item,index) in xinlv.jisuan">
<view class="">
<span class="sl_box_wz">{{item.times?item.times:0}}</span>
<span class="sl_box_unit">{{item.unit}}</span>
</view>
<view class="sl_box_desc">
{{item.desc}}
</view>
<view class="sl_box_cankao" v-if="item.cankao">
{{item.cankao}}
</view>
</view>
<view class="all" v-for="(item,index) in xinlv.cankao">
<view class="">
<span class="sl_box_wz">{{item.times.toFixed(2)}}</span>
<span class="sl_box_unit">{{item.unit}}</span>
</view>
<view class="sl_box_desc">
{{item.desc}}
</view>
<view class="sl_box_cankao" v-if="item.cankao">
{{item.cankao}}
</view>
</view>
</view>
</view>
<view class="list">
<view class="flx flx_ac ">
<view class="" style="margin-top: 10rpx;margin-right: 10rpx;">
<image src="../../static/icon/xinlv-small.png" style="width: 40rpx;" mode="widthFix">
</image>
</view>
<view class="" style="color: red;">
心率变异性
</view>
</view>
<view class="">
<xdt :datas="ybx.hrv"></xdt>
<xdt :datas="ybx.sdnn"></xdt>
<xdt :datas="ybx.rmssd"></xdt>
</view>
</view>
<view class="list">
<view class="flx flx_ac ">
<view class="" style="margin-top: 10rpx;margin-right: 10rpx;">
<image src="../../static/icon/xinlv-small.png" style="width: 40rpx;" mode="widthFix">
</image>
</view>
<view class="" style="color: red;">
心电图
</view>
</view>
<view class="">
<xdt :datas="xdt.pwv"></xdt>
<xdt :datas="xdt.qtc"></xdt>
<xdt :datas="xdt.qsrf"></xdt>
<xdt :datas="xdt.qsrx"></xdt>
<xdt :datas="xdt.qsrfx"></xdt>
<xdt :datas="xdt.st"></xdt>
</view>
</view>
<view class="" style="height: 20rpx;"></view>
</view>
</view>
<view class="pop-main" v-if="typeIndex == 2">
<view class="pops">
<view class="pop-close" @click="close()">
×
</view>
<view>
<view class="flx flx_ac" style="background-color: #fff;padding: 30rpx 0 0 30rpx;">
<view class="">
<text class="iconfont icon-wode"
:style="'font-size: 50rpx;margin-right:10rpx;color:' + bodyDesc.color "></text>
</view>
<view class="">
<text>{{bodyDesc.name}}</text>
</view>
</view>
<view class="" style="position: relative;">
<image src="../../static/image/body.png" style="width: 100%;" mode="widthFix"></image>
<view class="left_position postion ">
<view class="desc_box">
<view class="flx flx_ac">
<view class="tt">
{{ dataInfo.data_msg.basalMetabolicRate }}
</view>
<view class="unit">
千卡
</view>
</view>
<view class="name">
基础代谢
</view>
</view>
<view class="desc_box">
<view class="flx flx_ac">
<view class="tt">
{{ dataInfo.data_msg.boneMass }}
</view>
<view class="unit">
千克
</view>
</view>
<view class="name">
骨重
</view>
</view>
<view class="desc_box">
<view class="flx flx_ac">
<view class="tt">
{{ dataInfo.data_msg.waterContent }}
</view>
<view class="unit">
千克
</view>
</view>
<view class="name">
水分
</view>
</view>
</view>
<view class="right_position postion">
<view class="desc_box">
<view class="flx flx_ac">
<view class="tt">
{{ dataInfo.data_msg.fatMass }}
</view>
<view class="unit">
千克
</view>
</view>
<view class="name">
脂肪
</view>
</view>
<view class="desc_box" style="margin-bottom: 0;margin-top: 174rpx;">
<view class="flx flx_ac">
<view class="tt">
{{ dataInfo.data_msg.proteinAmount }}
</view>
<view class="unit">
千克
</view>
</view>
<view class="name">
蛋白质
</view>
</view>
</view>
<view class="postion center_posion">
{{ dataInfo.data_msg.userInfo.gender == '2'?'女':'男' }}性,{{ dataInfo.data_msg.userInfo.weight }}千克
</view>
<view class="postion right_center">
{{ dataInfo.data_msg.userInfo.height }}厘米
</view>
</view>
<view class="radius_box" style="padding: 0;margin: 30rpx;">
<view class="" v-for="(v,i) in arr" :key="i">
<body_info_components :key_name="v" :value="parseFloat(dataInfo.data_msg[v])">
</body_info_components>
</view>
</view>
<view class="" style="height: 40rpx;">
</view>
</view>
<view class="" style="height: 20rpx;"></view>
</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
export default {
name: "pop-info",
data() {
return {
typeIndex: 1,
dataInfo: {},
bodyDesc: {},
arr: [
'BMI',
'bodyFatPercentage',
'leanBodyMass',
'muscleRate',
'muscleMass',
'subcutaneousFat',
'bodyMoisture',
'skeletalMuscleRate',
'boneMass',
'proteinAmount',
'basalMetabolicRate',
],
title: '',
list: [],
height: 300,
width: 3000,
centerY: 200,
pLeft: '0',
dataInfo: {},
xinlv: {
jisuan: [{
times: 0,
unit: '次/分',
desc: '平均心率',
cankao: ''
},
{
times: 0,
unit: '次/分',
desc: '最高心率',
cankao: ''
},
{
times: 0,
unit: '次/分',
desc: '最低心率',
cankao: ''
}
],
cankao: [{
times: 0,
unit: '%',
desc: '正常',
cankao: '(60~100次/分)'
},
{
times: 0,
unit: '%',
desc: '心率偏快',
cankao: '>100次/分)'
},
{
times: 0,
unit: '%',
desc: '心率偏慢',
cankao: '(<60次/分)'
},
],
},
// 心率异变性
ybx: {
hrv: {
name: 'HRV',
max: 210,
unit: '毫秒',
min: 0,
val: 0
},
sdnn: {
name: 'SDNN',
max: 180,
unit: '毫秒',
min: 102,
val: 0
},
rmssd: {
name: 'RMSSD',
max: 39,
unit: '毫秒',
min: 15,
val: 0
}
},
xdt: {
pwv: {
name: 'PWV',
max: 15,
unit: 'm/s',
min: 6,
val: 0
},
qtc: {
name: 'QTc',
max: 400,
unit: '毫秒',
min: 260,
val: 0
},
qsrf: {
name: 'QRS 波振幅',
max: 1.5,
unit: 'mV',
min: 0.05,
val: 0
},
qsrx: {
name: 'QRS 波时限',
max: 120,
unit: '毫秒',
min: 80,
val: 0
},
qsrfx: {
name: 'QRS 主波方向',
max: 0,
unit: '向上,向下',
min: 0,
val: 0
},
st: {
name: 'ST 段振幅',
max: 0.1,
unit: 'mV',
min: -0.05,
val: 0
}
}
}
},
methods: {
close() {
this.$refs.popup.close()
},
open(val) {
val == 1 ? this.typeIndex = 1 : this.typeIndex = 2;
// 通过组件定义的ref调用uni-popup方法 ,如果传入参数 type 属性将失效 ,仅支持 ['top','left','bottom','right','center']
this.$refs.popup.open('bottom')
if (val == 1) {
const dataKey = 'infoData'; // 同上面设置的key
let get = uni.getStorageSync(dataKey)
if (!get) {
uni.showToast({
title: "读取数据失败"
})
setTimeout(() => {
uni.navigateBack({
delta: -1
})
}, 2000)
return
}
const data = JSON.parse(get);
console.log(data)
this.dataInfo = data;
this.changeData(this.dataInfo)
this.list = this.dataInfo.data_msg.list;
// 绘制
setTimeout(() => {
this.list = this.list.filter(val => val !== 0)
const ctx = uni.createCanvasContext('ecg', this);
ctx.setStrokeStyle('#d5d5d5') //
ctx.setLineWidth(1) // 线条宽度
for (var x = 0.5; x < 3000; x += 15) {
ctx.moveTo(x, 0)
// 结束点
ctx.lineTo(x, 3000)
// 描边不调用stroke则看不到画的内容
ctx.stroke()
}
for (var y = 0.5; y < 3000; y += 15) {
ctx.moveTo(0, y)
// 结束点
ctx.lineTo(3000, y)
// 描边不调用stroke则看不到画的内容
ctx.stroke()
}
// ctx.fill();
ctx.draw()
this.drawCurve()
}, 1000)
} else {
const dataKey = 'bodyData'; // 同上面设置的key
let get = uni.getStorageSync(dataKey)
if (!get) {
uni.showToast({
title: "读取数据失败"
})
setTimeout(() => {
uni.navigateBack({
delta: -1
})
}, 2000)
return
}
const data = JSON.parse(get);
this.dataInfo = data;
console.log(this.dataInfo)
// this.getUserInfo()
this.bodyDesc = this.$utils.getBodyKeyInfo(this.dataInfo.data_msg.BMI, 'BMI')
// uni.removeStorageSync(dataKey);
}
},
drawCurve() {
let ctx = uni.createCanvasContext('myCanvas', this)
let list = this.list;
let centerY = this.centerY;
let width = this.width;
let xScale = width / (list.length - 1); // 计算每个数据点占据的宽度
let yScale = centerY / (Math.max(...list) - Math.min(...list))
// yScale *= 3
let x = 0;
let y = centerY / 2 - (list[0] * yScale)
ctx.beginPath(); // 开始绘制
ctx.moveTo(x, y);
ctx.setStrokeStyle('#c96d79'); // 设置线条颜色
ctx.setLineWidth(2); // 设置线条宽度
for (let i = 1; i < list.length; i++) {
let x = i * xScale;
let y = centerY / 2 - ((list[i] / 2) * yScale); // 负数在中心下方,正数在中心上方
ctx.lineTo(x, y);
}
ctx.stroke() // 绘制线条
// ctx.fill();
ctx.draw(false);
// 绘制到canvas上不需要等待上一步绘制完成
},
changeData(data) {
let HRV = []
let QT = []
let heart = []
let pwv = []
let resRate = []
this.xinlv.jisuan[0].times = data.data_msg.meanHeartRate == undefined ? data.data_msg.heartRate : data
.data_msg.meanHeartRate
if (data.data_msg.wavefrom == undefined) {
return
}
data.data_msg.wavefrom.map((v, i) => {
if (!(v instanceof Array)) {
if (v.HRV != 0) {
HRV.push(v.HRV)
}
if (v.QT != 0) {
QT.push(v.QT)
}
if (v.heart != 0 && v.heart != null) {
heart.push(v.heart)
}
if (v.pwv != 0) {
pwv.push(v.pwv)
}
if (v.resRate != 0) {
resRate.push(v.resRate)
}
}
})
this.xinlv.jisuan[1].times = Math.max(...heart)
this.xinlv.jisuan[2].times = Math.min(...heart)
// 获取心率每个区间的数据数量
let heart_zc = heart.filter(value => (value >= 60 && value <= 100)).length;
let heart_fast = heart.filter(value => value > 100).length;
let heart_slow = heart.filter(value => value < 60).length;
let heart_length = heart.length
this.xinlv.cankao[0].times = (heart_zc / heart_length).toFixed(2) * 100
this.xinlv.cankao[1].times = (heart_fast / heart_length).toFixed(2) * 100
this.xinlv.cankao[2].times = (heart_slow / heart_length).toFixed(2) * 100
if (this.xinlv.cankao[0].times > 50) {
this.title = '窦性心率';
} else if (this.xinlv.cankao[1].times >= 50) {
this.title = '心率偏快';
} else if (this.xinlv.cankao[2].times >= 50) {
this.title = '心率偏慢';
} else {
this.title = '窦性心率不齐';
}
if (data.diseaseRisk != undefined && data.diseaseRisk >= 30 && this.xinlv.cankao[0].times > 50) {
this.title = '窦性心率不齐';
}
if (data.data_msg.hrv != undefined) {
this.ybx.hrv.val = data.data_msg.hrv
this.ybx.sdnn.val = data.data_msg.diseaseSdnn
this.ybx.rmssd.val = data.data_msg.diseaseRmssd
this.xdt.pwv.val = (data.data_msg.pwvMeanVal / 10).toFixed(2)
this.xdt.qtc.val = data.data_msg.QTC
this.xdt.qsrf.val = (data.data_msg.qrsAmp / 100).toFixed(2)
this.xdt.qsrx.val = data.data_msg.qrsTime
this.xdt.qsrfx.val = data.data_msg.leadOffType == 0 ? '向上' : '向下'
this.xdt.st.val = (data.data_msg.stMeanVal / 100).toFixed(2)
} else {
console.log('is undefined')
this.ybx.hrv.val = data.data_msg.averageHRV
this.xdt.qtc.val = data.data_msg.averageTimeInterval
}
}
}
}
</script>
<style lang="scss" scoped>
.pop-close {
display: flex;
flex-direction: row-reverse;
font-size: 100rpx;
padding-right: 50rpx;
}
.pop-main {
width: 100%;
height: 100vh;
background-color: #fff;
overflow-y: scroll;
padding: 50rpx;
}
.pops {
border: 1rpx solid gray;
border-radius: 20rpx;
margin: 50rpx 200rpx;
margin-top: 100rpx;
padding: 0rpx 50rpx;
}
.box1 {
position: absolute;
left: 0px;
width: 3000rpx;
}
.box2 {
position: absolute;
left: 0px;
width: 3000rpx;
}
.myScoll {
width: 100%;
height: 396rpx;
white-space: nowrap;
position: relative;
}
.list {
background-color: #fff;
overflow: hidden;
padding: 30rpx;
margin: 30rpx 30rpx;
border-radius: 30rpx;
}
.dxxl {
background-color: #fff;
padding: 20rpx;
padding-top: 40rpx;
}
.sl_box {
text-align: center;
.all {
width: calc(100% / 3);
margin-top: 40rpx;
}
.sl_box_cankao,
.sl_box_desc {
font-size: 24rpx;
color: #999;
}
.sl_box_unit {
font-size: 26rpx;
}
.sl_box_wz {
margin-right: 10rpx;
font-size: 30rpx;
font-weight: bold;
}
}
</style>
<style lang="scss" scoped>
.pop-close {
display: flex;
flex-direction: row-reverse;
font-size: 100rpx;
padding-right: 50rpx;
margin-top: 50rpx;
}
.pop-main {
width: 60%;
height: 100vh;
background-color: #fff;
overflow-y: scroll;
}
.box1 {
position: absolute;
left: 0px;
width: 3000rpx;
}
.box2 {
position: absolute;
left: 0px;
width: 3000rpx;
}
.myScoll {
width: 100%;
height: 396rpx;
white-space: nowrap;
position: relative;
}
.list {
background-color: #fff;
overflow: hidden;
padding: 30rpx;
margin: 30rpx 30rpx;
border-radius: 30rpx;
}
.dxxl {
background-color: #fff;
padding: 20rpx;
padding-top: 40rpx;
}
.sl_box {
text-align: center;
.all {
width: calc(100% / 3);
margin-top: 40rpx;
}
.sl_box_cankao,
.sl_box_desc {
font-size: 24rpx;
color: #999;
}
.sl_box_unit {
font-size: 26rpx;
}
.sl_box_wz {
margin-right: 10rpx;
font-size: 30rpx;
font-weight: bold;
}
}
</style>
<style lang="scss" scoped>
.postion{
position: absolute;
}
.left_position{
left: 40rpx;
top:12%
}
.right_position{
right: 40rpx;
top:15%
}
.right_center{
right: 160rpx;
top:42%;
font-size: 26rpx;
}
.center_posion{
width: 100%;
text-align: center;
bottom: 20rpx;
}
.desc_box{
margin-bottom: 64rpx;
.tt{
font-size: 30rpx;
font-weight: bold;
margin-right: 10rpx;
}
.unit{
font-size: 24rpx;
color: #666;
}
.name{
font-size: 24rpx;
color: #999;
}
}
</style>