feat: 基本改版完成

This commit is contained in:
white 2024-09-25 16:03:57 +08:00
parent 15f947e329
commit 1f26bd7cf3
41 changed files with 22881 additions and 14663 deletions

View File

@ -0,0 +1,297 @@
<!-- 全部数据 -->
<template>
<view class="">
<tm-nav-bar left-icon="arrow-left" :title="desc? getNameByKey(desc) : getNameByKey(activeType)" @clickLeft="back" />
<view class="content">
<view class="over" style="margin-top: 30rpx" @click="flag = !flag">
<text>日期筛选</text>
<uni-icons type="down" size="24"></uni-icons>
</view>
<view class="time" v-show="flag">
<punch-calendar @switchMonth="switchMonth" @chooseDay="chooseDay" :errorList="errorList" :successList="successList" ref="calendar"></punch-calendar>
</view>
<view class="flx flx_sb bloodl_box" v-if="activeType == 'bloodLiquidAll'" style="flex-wrap: wrap;">
<view class="flx flx_ac" v-for="(v,i) in blData" :key="i" style="flex: 0 0 calc((100% - 0rpx) / 2);height: 50rpx;">
<view class="yuan" :style="{backgroundColor:v.color}"></view>
<view class="name">
{{ v.name }}
</view>
</view>
</view>
<view :class="'list'" v-for="(item, index) in dataList" :key="index" @click="goInfo(index)">
<view v-if="!customType.includes(activeType)" class="row" >
<view class="data">
<image src="https://img.agrimedia.cn/apptest/%E6%89%8B%E8%A1%A8-%E7%BA%BF%E6%80%A73-0.png" mode="widthFix"></image>
<view v-if="activeType == 'stepIndex'">
步数{{item.data_msg.step}}
</view>
<view v-if="activeType == 'bloodPressure'">
{{item.data_msg.bloodPressureHigh}}/{{item.data_msg.bloodPressureLow}} mmHg
</view>
<view v-if="activeType == 'bloodOxygen'">
{{ item.data_msg[0] ? item.data_msg[0] + (item.data_msg[1]?' | ':'') : ''}}
{{ item.data_msg[1] ? item.data_msg[1] + (item.data_msg[2]?' | ':'') : ''}}
{{ item.data_msg[2] ? item.data_msg[2] + (item.data_msg[3]?' | ':'') : ''}}
{{ item.data_msg[3] ? item.data_msg[3] + (item.data_msg[4]?' | ':'') : ''}}
{{ item.data_msg[4] ? item.data_msg[4] : ''}}
</view>
<view v-if="activeType == 'bloodGlucose'">
{{item.data_msg}}毫摩尔/
</view>
<view v-if="activeType == 'bodyTemperature'">
{{item.data_msg}}
</view>
<view v-if="activeType == 'pulseReat'">
{{ item.data_msg[0] ? item.data_msg[0] + (item.data_msg[1]?' | ':'') : ''}}
{{ item.data_msg[1] ? item.data_msg[1] + (item.data_msg[2]?' | ':'') : ''}}
{{ item.data_msg[2] ? item.data_msg[2] + (item.data_msg[3]?' | ':'') : ''}}
{{ item.data_msg[3] ? item.data_msg[3] + (item.data_msg[4]?' | ':'') : ''}}
{{ item.data_msg[4] ? item.data_msg[4] : ''}}
</view>
<view v-if="activeType == 'bloodLiquid'">
{{(item.data_msg.uricAcidVal /10).toFixed(2)}} 微摩尔/
</view>
<view v-if="activeType == 'bloodLiquidAll'" style="font-size: 24rpx;" >
<view>总胆固醇{{(item.data_msg.cholesterol/100).toFixed(2)}} </view>
<view>甘油三脂{{(item.data_msg.triacylglycerol/100).toFixed(2)}} </view>
<view>高密度脂蛋白:{{(item.data_msg.highDensity/100).toFixed(2)}}</view>
<view>低胆固醇{{(item.data_msg.lowDensity/100).toFixed(2)}} </view>
</view>
<view v-if="activeType == 'ECGData'" style="font-size: 24rpx;">
<view>心率:{{item.data_msg.meanHeartRate || item.data_msg.heartRate}} /</view>
<view>HRV{{item.data_msg.averageHRV || item.data_msg.hrv}} 毫秒</view>
<view>QTc{{item.data_msg.averageTimeInterval || item.data_msg.QTC}} 毫秒</view>
</view>
<view v-if="activeType == 'bodyData'" style="font-size: 24rpx;">
<view>BMI:{{item.data_msg.BMI}}</view>
<view>体脂率{{item.data_msg.bodyFatPercentage}}</view>
<view>肌肉率{{item.data_msg.muscleRate}}</view>
</view>
</view>
<view class="time">
{{item.date_str}} {{item.hour_minute}}
</view>
</view>
<view class="" v-else>
<detail_components :key_name="activeType" :item="item"></detail_components>
</view>
</view>
<view class="" style="height: 30rpx;"></view>
</view>
<ecg-info ref="popShow" :ecgData="ecgData"></ecg-info>
</view>
</template>
<script>
import { getArrMaxValue } from '@/utils/utils.js'
import { mapGetters } from "vuex";
export default {
props: {
activeType: {
type: String,
default: ''
}
},
data() {
return {
customType:[
// 'bodyData',
// 'bloodLiquid',
// 'bloodLiquidAll',
// 'ECGData'
],
blData:[
{
name:'总胆固醇',
color:'#46b4ff',
},
{
name:'甘油三酯',
color:'#29cf6c',
},
{
name:'高密度脂蛋白',
color:'#fcc05d',
},
{
name:'低密度脂蛋白',
color:'#fa4e33',
}
],
desc: '',
dataList: [],
keyNameMap: {
bloodGlucose: "血糖",
bloodOxygen: "血氧",
bloodPressure: '血压',
pulseReat: '心率',
bloodLiquid: "尿酸",
meiTuo: '梅脱',
updataDate: '更新时间',
bodyTemperature: '体温',
ECGData: '心电图',
stepIndex: '步数',
bodyData:'身体成分',
bloodLiquidAll: '血脂'
},
time: new Date().toISOString().substring(0, 10),
flag: true,
successList: ['1676995200000'],
errorList: ['1676908800000'],
showPopup: false,
ecgData: [],
bodyData: []
}
},
mounted() {
//
this.$refs.calendar.initCalendar()
this.queryList()
//
let timer = setTimeout(()=>{
this.addTimer()
},2000)
},
// onShow(e) {
// let routes = getCurrentPages(); //
// let curRoute = routes[routes.length - 1].route //
// let curParam = routes[routes.length - 1].options; //
// //
// let param = ''
// for (let key in curParam) {
// param += '&' + key + '=' + curParam[key]
// }
computed: {
...mapGetters({
getActiceDevice: "api/getActiceDevice"
})
},
methods: {
goInfo(idx){
if(this.activeType == 'ECGData'){
uni.setStorageSync('infoData', JSON.stringify(this.dataList[idx]));
this.$refs.popShow.open(1)
}
if(this.activeType == 'bodyData'){
uni.setStorageSync('bodyData', JSON.stringify(this.dataList[idx]));
this.$refs.popShow.open(2)
}
},
getArrMaxValue,
queryList() {
let type = '';
if (this.activeType == 'step') {
type = 'step_split'
} else {
type = this.activeType
};
if (this.activeType == 'bloodLiquidAll') {
type = 'bloodLiquid'
} else {
type = this.activeType
};
this.$store.dispatch('api/getDeviceListDays', {
type: type,
device_real_time: this.time,
device_id:this.getActiceDevice.device_id
}).then(res => {
this.dataList = res.data
console.log(this.dataList)
});
},
getNameByKey(key) {
return this.keyNameMap[key] || "Key not found";
},
addTimer() {
this.successList = [...this.successList, '1681920000000']
},
//
chooseDay(val) {
this.time = val.year + '-' + val.month + '-' + val.day;
this.queryList()
},
back() {
if (this.activeType == 'ECGData' || this.activeType == 'bodyData') {
this.$emit('back', 1)
} else {
this.$emit('back', 2)
}
}
}
}
</script>
<style lang="scss" scoped>
.bloodl_box{
margin-top: 30rpx;
padding: 0 40rpx;
.name{
font-size: 24rpx;
color: #999;
}
.yuan{
width: 14rpx;
height: 14rpx;
border-radius: 50%;
margin-right: 10rpx;
}
}
.content {
.over {
margin: 0rpx 30rpx;
background-color: #fff;
padding: 30rpx;
border-radius: 20rpx;
font-weight: 800;
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 20rpx;
}
.time {
width: 100%;
}
.list {
background-color: #fff;
overflow: hidden;
padding: 30rpx;
margin: 30rpx 30rpx;
border-radius: 30rpx;
.row {
// height: 100rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.data {
display: flex;
align-items: center;
font-weight: 500;
font-size: 36rpx;
width: 900rpx;
image {
width: 50rpx;
}
}
.time {
font-weight: 300;
font-size: 24rpx;
}
}
}
</style>

View File

@ -0,0 +1,717 @@
<template>
<view class="content" style="width: 60%;">
<uni-popup ref="popup" type="bottom" border-radius="10px 10px 0 0">
<view class="pop-main" v-if="typeIndex == 1">
<view>
<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>
<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>
</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;
// refuni-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;
}
.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;
}
.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>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,731 @@
<template>
<view>
<z-paging ref="paging"refresher-only @onRefresh="onRefresh">
<view class="flx jcsb ac">
<view class="left">
<view class="content-header">
<view class="sha-dow"></view>
<view class="text">
<view class="left" style="width: 100%">
<view class="title">
家庭健康管理
</view>
<view class="desc">
<text class="">共享健康 · 守护家人</text>
</view>
</view>
</view>
</view>
<view class="control flx flx_sb">
<view class="btn">
<liu-data-select elementId="data-select3" :dataList="deviceList" @change="changeDev" bgColor="#fff"
color="#000">
<view id="data-select3" class="btn-info1">{{name2? name2 : '选择设备'}}</view>
</liu-data-select>
</view>
<view class="btn" @click="outlogin">
退出登录
</view>
</view>
<view class="list-wrap">
<view class="content-item row header-item" @click="toPageCom('stepIndex')">
<view class="item">
<view class="title">卡路里</view>
<view class="row-date">
<view>{{dataListsNew.stepIndex.data_msg ? dataListsNew.stepIndex.data_msg.calorie / 10 : '--'}}</view>
<text>千卡</text>
</view>
</view>
<view class="item">
<view class="title">步数</view>
<view class="row-date">
<view>{{dataListsNew.stepIndex.data_msg ? dataListsNew.stepIndex.data_msg.step : '--'}}</view>
<text></text>
</view>
</view>
<view class="item">
<view class="title">距离</view>
<view class="row-date">
<view>{{dataListsNew.stepIndex.data_msg ? (dataListsNew.stepIndex.data_msg.distance / 1000).toFixed(2) : '--'}}</view>
<text>公里</text>
</view>
</view>
<view class="row-time" v-if="dataListsNew.stepIndex">
{{(dataListsNew.stepIndex.device_real_time)}}
</view>
<view class="bg">
<!-- <image src="../../static/icon/血糖.png" mode="widthFix"></image> -->
</view>
</view>
<view class="content-item row header-item" style="flex-direction: column" @click="toPageCom('SleepDatas')">
<view class="title" style="width: 100%">
{{getNameByKey('sleep')}}
</view>
<view class="date" style="width: 100%">
<view class="flex_data" v-if="dataListsNew.SleepDatas.data_msg">
<view v-if="!(i == 0 && v == 0)" class="" v-for="(v,i) in mkHourMin(dataListsNew.SleepDatas.data_msg[0].sleepTotalTime)" :key="i" class="flex_data">
<view class="f_left" >
{{v}}
</view>
<view class="f_right">
<text class="icon">{{(i == 0?'时':'分')}}</text>
</view>
</view>
</view>
</view>
<view class="time" style="width: 100%; text-align: right; padding-bottom: 0rpx">
{{dataListsNew.SleepDatas.data_msg? dataListsNew.SleepDatas.device_real_time + '更新' : '--'}}
</view>
<view class="bg">
<image src="../../static/icon/sleep.png" style="width: 200rpx;" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPageCom('bloodGlucose')">
<view class="title">
{{getNameByKey('bloodGlucose')}}
</view>
<view class="time">
{{dataListsNew.bloodGlucose.data_msg? dataListsNew.bloodGlucose.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.bloodGlucose.data_msg || '--'}}
</view>
<view class="f_right">
<text class="icon">毫摩尔/</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/xuetang.png" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPageCom('bloodOxygen')">
<view class="title">
{{getNameByKey('bloodOxygen')}}
</view>
<view class="time">
{{dataListsNew.bloodOxygen.data_msg? dataListsNew.bloodOxygen.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.bloodOxygen.data_msg? (dataListsNew.bloodOxygen.data_msg[0]) : '--'}}
</view>
<view class="f_right">
<text class="icon">%</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/xueyang.png" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPageCom('bloodPressure')">
<view class="title">
{{getNameByKey('bloodPressure')}}
</view>
<view class="time">
{{dataListsNew.bloodPressure.data_msg? dataListsNew.bloodPressure.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.bloodPressure.data_msg? dataListsNew.bloodPressure.data_msg.bloodPressureHigh + '/' + dataListsNew.bloodPressure.data_msg.bloodPressureLow : '--'}}
</view>
<view class="f_right">
<text class="icon">mmHG</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/xueya.png" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPageCom('bodyTemperature')">
<view class="title">
{{getNameByKey('bodyTemperature')}}
</view>
<view class="time">
{{dataListsNew.bodyTemperature.data_msg? dataListsNew.bodyTemperature.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.bodyTemperature.data_msg? (dataListsNew.bodyTemperature.data_msg) : '--'}}
</view>
<view class="f_right">
<text class="icon"></text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/wendu.png" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPageCom('pulseReat')">
<view class="title">
{{getNameByKey('pulseReat')}}
</view>
<view class="time">
{{dataListsNew.pulseReat.data_msg? dataListsNew.pulseReat.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.pulseReat.data_msg? (dataListsNew.pulseReat.data_msg[0]) : '--'}}
</view>
<view class="f_right">
<text class="icon">/</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/mailv.png" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPageCom('ECGData')">
<view class="title">
心电图
</view>
<view class="time">
{{dataListsNew.ECGData.data_msg? dataListsNew.ECGData.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.ECGData.data_msg? (dataListsNew.ECGData.data_msg.heartRate || dataListsNew.ECGData.data_msg.meanHeartRate) : '--'}}
</view>
<view class="f_right">
<text class="icon">/</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/xindiantu.png" style="width: 220rpx;" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPageCom('bodyData')">
<view class="title">
身体成分
</view>
<view class="time">
{{dataListsNew.bodyData.data_msg? dataListsNew.bodyData.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.bodyData.data_msg? (dataListsNew.bodyData.data_msg.BMI ) : '--'}}
</view>
<view class="f_right">
<text class="icon">BMI</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/rtcf.png" style="width: 220rpx;" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPageCom('bloodLiquid')">
<view class="title">
尿酸
</view>
<view class="time">
{{dataListsNew.bloodLiquid.data_msg? dataListsNew.bloodLiquid.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.bloodLiquid.data_msg? (dataListsNew.bloodLiquid.data_msg.uricAcidVal / 10).toFixed(2) : '--'}}
</view>
<view class="f_right">
<text class="icon">微摩尔/</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/niaosuan.png" style="width: 220rpx;" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPageCom('bloodLiquidAll')">
<view class="title">
{{getNameByKey('bloodLiquidAll')}}
</view>
<view class="time">
{{dataListsNew.bloodLiquid.data_msg? dataListsNew.bloodLiquid.device_real_time + '更新' : '--'}}
</view>
<view class="gap-content">
<view class="gap-item">
<view class="title">
总胆固醇
</view>
<view class="num">
{{dataListsNew.bloodLiquid.data_msg? (dataListsNew.bloodLiquid.data_msg.cholesterol / 100).toFixed(2) : '--'}}
<text class="icon">毫摩尔/</text>
</view>
</view>
<view class="gap-item">
<view class="title">
甘油三脂
</view>
<view class="num">
{{dataListsNew.bloodLiquid.data_msg? (dataListsNew.bloodLiquid.data_msg.triacylglycerol / 100).toFixed(2) : '--'}}
<text class="icon">毫摩尔/</text>
</view>
</view>
<view class="gap-item">
<view class="title">
高密度脂蛋白
</view>
<view class="num">
{{dataListsNew.bloodLiquid.data_msg? (dataListsNew.bloodLiquid.data_msg.highDensity / 100).toFixed(2) : '--'}}
<text class="icon">毫摩尔/</text>
</view>
</view>
<view class="gap-item">
<view class="title">
低密度脂蛋白
</view>
<view class="num">
{{dataListsNew.bloodLiquid.data_msg? (dataListsNew.bloodLiquid.data_msg.lowDensity / 100).toFixed(2) : '--'}}
<text class="icon">毫摩尔/</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</z-paging>
</view>
</template>
<script>
import navtab from '../../components/navtab/navtab.vue'
import homeData from '../../components/home-data/home-data.vue'
import { getArrMaxValue } from '@/utils/utils.js'
import store from "@/store/index.js";
import { mapGetters } from "vuex";
export default {
name:"home-data",
props: {
deviceList: {
default: []
},
dataListsNew: {
default: []
}
},
data() {
return {
keyNameMap: {
bloodGlucose: "血糖",
sleep: "睡眠",
bloodOxygen: "血氧",
bloodPressure: '血压',
bloodLiquid: "血脂",
meiTuo: '梅脱',
pulseReat: '心率',
updataDate: '更新时间',
bodyTemperature: '体温',
bloodLiquidAll: '血脂'
},
dataLists: {},
name: '',
name1: '',
name2: '',
}
},
mounted() {
this.name2 = this.getActiceDevice.device_id
},
onLoad() {
/*
* 首页数据
*/
this.onRefresh();
// #ifdef APP-PLUS
setTimeout(() => {
var avaudiosession = plus.ios.import("AVAudioSession");
var avaudio = avaudiosession.sharedInstance();
avaudio.requestRecordPermission(()=>{
console.log('申请麦克风权限');
});
}, 1000);
// #endif
this.urlLink = `/hybrid/html/ai.html`;
},
computed: {
...mapGetters({
getActiceDevice: "api/getActiceDevice"
})
},
onReady() {
// #ifdef APP-PLUS
let pages = getCurrentPages();
let page = pages[pages.length - 1];
let currentWebview = page.$getAppWebview();
setTimeout(function() {
const wv = currentWebview.children()[0]
//setStylewebview
wv.setStyle({
top: 0,
right: 0,
height: uni.getSystemInfoSync().windowHeight,
width: uni.getSystemInfoSync().windowWidth - 660
});
}, 100);
// #endif
},
methods: {
getArrMaxValue,
onRefresh(pageNo, pageSize) {
this.$store.dispatch('api/getIndexData', {
device_id: this.getActiceDevice.device_id ,
need_arr :[
'pulseReat',
'bloodOxygen',
'bloodPressure',
'bloodGlucose',
'meiTuo',
'bodyTemperature',
'bloodLiquid',
'stepIndex',
'ECGData',
'SleepDatas',
'bodyData'
],
}).then(res => {
for(let i = 0;i < res.length; i++){
this.dataListsNew[res[i].type] = res[i]
this.dataListsNew[res[i].name] = this.getNameByKey(res[i].type)
}
this.urlLink = `/hybrid/html/ai.html?data=${JSON.stringify(res)}`;
this.$refs.paging.complete();
}
);
this.$store.dispatch('api/getBindUserDeviceId').then(res => {
this.$emit('deviceGet', res);
});
},
mkHourMin(min){
if(min < 60){
return [0,min]
}
return [(Math.floor(min / 60)),(min % 60)]
},
toPage(url) {
uni.navigateTo({
url
})
},
getNameByKey(key) {
return this.keyNameMap[key] || "Key not found";
},
formatDateTime(dateTimeStr) {
if (dateTimeStr) {
return dateTimeStr.replace(/-/g, ' ').replace(' ', ' ', 2).replace(' ', ':');
}
},
toPageCom(val) {
this.$emit('pageDetail', val)
},
// 退
outlogin() {
this.$store.commit('api/setApiToken', '')
this.$store.commit('api/setActiceDevice', '')
this.$store.commit('api/setUserInfo', {})
uni.reLaunch({
url: "/pages/login/login"
})
},
//
changeDev(item) {
this.name2 = item.device_id;
this.$store.commit('api/setActiceDevice', item);
//
this.$store.dispatch('api/getUserInfo').then(res => {
this.$store.commit('api/setUserInfo', res);
});
this.onRefresh();
}
}
}
</script>
<style lang="scss" scoped>
.flex_data{
display: flex;
.f_left{
margin-right: 10rpx;
}
.f_right{
padding-top: 16rpx;
}
}
.content {
padding: 20rpx 30rpx;
margin-top:20rpx;
display: flex;
align-items: center;
.left {
width: 60%;
}
.right {
position: fixed;
right: 0rpx;
height: 100vh;
width: 40%;
padding: 30rpx;
box-sizing: border-box;
overflow: hidden;
border-radius: 30rpx;
}
.content-header {
margin-bottom: 30rpx;
padding: 30rpx;
height: 600rpx;
display: flex;
align-items: center;
justify-content: space-between;
margin: 0rpx 0rpx 30rpx 0rpx;
background-image: url('https://img.agrimedia.cn/watch-app/header-bg.png');
background-repeat: no-repeat;
background-size: 100% 100%;
position: relative;
.sha-dow {
width: 100%;
height: 640rpx;
position: absolute;
top: 0;
left: 0;
background-color: #fff;
opacity: .4;
}
.text {
width: 800rpx;
// height: 300rpx;
left: 0px;
top: 0px;
right:0px;
bottom:0px;
margin:auto;
margin: 0 auto;
z-index: 999;
backdrop-filter: blur(6rpx);
border-radius: 20rpx;
padding:20rpx;
margin-top: 60rpx;
}
.left {
.title {
font-size: 100rpx;
text-align: center;
font-weight: 900;
margin-bottom: 20rpx;
text-stroke: 10rpx white;
}
.desc {
text-align: center;
font-size: 34rpx;
}
}
}
.row {
display: flex;
align-items: center;
justify-content: space-around;
}
.list-wrap {
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: space-between;
padding: 0rpx 40rpx;
}
.control {
margin: 0rpx 40rpx;
.btn {
font-size: 36rpx;
font-weight: 800;
margin-bottom: 30rpx;
padding: 30rpx 50rpx;
border-radius: 20rpx ;
-webkit-box-shadow: 0 0 60rpx 0 rgba(43,86,112,.1) ;
}
}
.header-item {
height: 280rpx !important;
padding-top: 0rpx !important;
}
.content-item {
width: 42%;
height: auto;
margin: 20rpx 10rpx;
padding: 30rpx;
border: none;
border-radius: 30rpx ;
-webkit-box-shadow: 0 0 60rpx 0 rgba(43,86,112,.1) ;
box-shadow: 0 0 60rpx 0 rgba(43,86,112,.1) ;
position: relative;
overflow: hidden;
.title {
font-size: 38rpx;
font-weight: 800;
margin-bottom: 14rpx;
}
.row-time {
position: absolute;
bottom: 30rpx;
right: 30rpx;
font-size: 28rpx;
color: grey;
}
.bg {
position: absolute;
bottom: 0rpx;
right: 50rpx;
opacity: .4;
z-index: -1;
image {
width: 250rpx;
}
}
.time {
font-size: 28rpx;
color: grey;
padding: 20rpx 0rpx 20rpx 0rpx;
}
.row-date {
font-size: 46rpx;
font-weight: 800;
display: flex;
align-items: baseline;
justify-content: center;
text {
opacity: .5;
font-size: 24rpx;
font-weight: 500;
padding-left: 10rpx;
}
}
.date {
font-size: 46rpx;
font-weight: 800;
display: flex;
align-items: baseline;
.icon {
font-weight: 100rpx;
font-size: 28rpx;
color: grey;
display: flex;
flex-direction: column-reverse;
}
}
.gap-content {
display: flex;
flex-wrap: wrap;
.gap-item {
margin-top: 20rpx;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
width: 50%;
.title {
font-size: 36rpx;
padding-bottom: 10rpx;
}
.num {
font-size: 28rpx;
font-weight: 600;
}
}
}
}
}
// ********************************
.video {
width: 80%;
padding: 0rpx 20rpx;
}
/* 隐藏所有默认控件 */
video::-webkit-media-controls {
display: none !important;
}
video::-moz-media-controls {
display: none !important;
}
video::-ms-media-controls {
display: none !important;
}
/* 针对不同浏览器的隐藏控件的方式 */
video::part(media-controls) {
display: none !important;
}
video {
border-radius: 30rpx;
overflow: hidden;
}
.video-wrap{
display: flex;
align-items: center;
background-color: #06193a;
margin: 0rpx 20rpx;
justify-content: center;
}
.page-main {
padding: 20rpx;
}
.btn-info {
width: 600rpx;
height: 88rpx;
margin: 0 auto;
margin-top: 30rpx;
border: solid #f0f0f0 1px;
border-radius: 12rpx;
padding: 0 30rpx;
}
.btn-info1 {
width: 300rpx;
border-radius: 12rpx;
}
.card {
width: 800rpx !important;
}
</style>

View File

@ -0,0 +1,466 @@
<template>
<view>
<tm-nav-bar left-icon="arrow-left" title="睡眠" @clickLeft="back" />
<view class="flx jcsb ac" style="height: 40rpx;background-color: #fff;padding: 20rpx;">
<view class="" style="font-size: 40rpx;font-weight: bold;">
</view>
<view class="" style="color: #b8b8b8;font-size: 24rpx;">
{{time}}
</view>
</view>
<view class="flx">
<view class="w-5">
<view class="canvas_box">
<canvas canvas-id="circleCanvas" style="width: 250px; height: 250px;margin: 0 auto;"></canvas>
<view class="" style="height: 40rpx;"></view>
<view class="flx flx_sb">
<view class="flx flx_ac">
<!-- color:'#e933dd',
bigColor:'#6452da', -->
<view class="circle" style="background-color:#6452da ;" >
</view>
<view class="sleep_info">
<view class="info_title">
睡眠时长
</view>
<view class="info_val">
{{sleep.shen}}
</view>
</view>
</view>
<view class="flx flx_ac">
<view class="sleep_info">
<view class="info_title" style="text-align: right;">
深度睡眠
</view>
<view class="info_val">
{{sleep.qian}}
</view>
</view>
<view class="circle" style="background-color:#e933dd ;">
</view>
</view>
</view>
</view>
</view>
<view class="w-5">
<view class="radius_box" style="margin: 20rpx;margin-top: 40rpx ;">
<view class="flx flx_sb title_box">
<view class="title">
{{sleep.shen}}
</view>
</view>
<sleep-list-data :canvas-id="'sleep-canvas-id'" :dataList="sleepList"></sleep-list-data>
<view class="flx flx_sb flx_ac" style="margin-top: 20rpx;">
<view class="flx flx_ac">
<view class="flx flx_ac">
<image src="../../static/icon/sleep.png" style="width: 30rpx;margin-right: 5rpx;" mode="widthFix"></image>
</view>
<view class="small-title" v-if="fallAsleepTime.length > 0">
{{ fallAsleepTime[2] }}:{{ fallAsleepTime[3] }} , {{ fallAsleepTime[0] }}/{{ fallAsleepTime[1] }}
</view>
<view class="small-title" v-else>
</view>
</view>
<view class="flx flx_ac">
<view class="small-title" v-if="exitSleepTime.length > 0">
{{ exitSleepTime[2] }}:{{ exitSleepTime[3] }} , {{ exitSleepTime[0] }}/{{ exitSleepTime[1] }}
</view>
<view class="small-title" v-else>
</view>
<view class="flx flx_ac">
<image src="../../static/image/richu.png" style="width: 50rpx;" mode="widthFix"></image>
</view>
</view>
</view>
</view>
<view class="radius_box" style="margin: 20rpx;padding-bottom: 20rpx;margin-top: 40rpx;" v-if="option.series[0].data.length > 0">
<view class="" style="font-size: 26rpx;margin-bottom: 30rpx;">
睡眠阶段
</view>
<view class="flx flx_sb">
<view class="">
<view style="width: 200rpx; height:200rpx;background-color: #fff;"><l-echart ref="chartRef" @finished="init"></l-echart></view>
</view>
<view class="flx flx_sb" style="width: 100%;margin-left: 40rpx;flex-direction: column;padding: 20rpx 0;padding-right: 40rpx;">
<view class="flx flx_sb line">
<view class="flx small-title flx_ac">
<view class="dian0 yuan" style="margin-right: 10rpx;">
</view>
<view class="">
深睡
</view>
</view>
<view class="">
<view class="c0">
{{ sleepData.shen }}%
</view>
</view>
</view>
<view class="flx flx_sb line">
<view class="flx small-title flx_ac">
<view class="dian1 yuan" style="margin-right: 10rpx;">
</view>
<view class="">
浅睡
</view>
</view>
<view class="">
<view class="c1">
{{ sleepData.qian }}%
</view>
</view>
</view>
<view class="flx flx_sb line">
<view class="flx small-title flx_ac">
<view class="dian2 yuan" style="margin-right: 10rpx;">
</view>
<view class="">
快速动眼
</view>
</view>
<view class="">
<view class="c2">
{{ sleepData.kuai }}%
</view>
</view>
</view>
</view>
</view>
</view>
<view class="radius_box" style="margin: 20rpx;padding-bottom: 20rpx;margin-top: 40rpx;">
<view class="flx flx_sb title_box flx_ac">
<view class="title" >
目标
</view>
<view class="more" style="font-size: 24rpx;">
8 小时
</view>
</view>
</view>
</view>
</view>
<view class="" style="height: 40rpx"></view>
</view>
</template>
<script>
import { mapGetters } from "vuex";
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min'
export default {
name: 'sleep-info',
data() {
return {
sleepData:{
shen:0,
qian:0,
kuai:0
},
isOption:false,
isInit:false,
exitSleepTime:[],
fallAsleepTime:[],
option:{
tooltip: {
show:false
},
legend: {
top: '5%',
left: 'center'
},
series: [
{
data:[],
type: 'pie',
radius: ['70%', '90%'],
avoidLabelOverlap: false,
labelLine: {
show: false
},
}
]
},
cavasConfig:{
canvasWidth: 250,
canvasHeight: 250,
lineWidth:20,
radius: 68, //
startAngle: -90, // -903
bigRadius:90,//
cxt:null,
colorBg:'#f7c1f5',
bigColorBg:'#d1ccf4',
color:'#e933dd',
bigColor:'#6452da',
bigAngle:0,
angle:0,
startSmallAngle:0,
bigStartAngle:0,
stop:0,
bigStop:0,
maxTime:480
},
sleepList:[],
time: new Date().toISOString().substring(0, 10),
sleep:{
shen:'0 分',
qian:'0 分'
}
}
},
watch:{
isInit(newVal,oldVal){
if(this.isOption == true){
this.doDrawPie()
}
},
isOption(newVal,oldVal){
if(this.isInit == true){
this.doDrawPie()
}
}
},
mounted() {
this.getDataList()
this.cavasConfig.ctx = uni.createCanvasContext('circleCanvas', this);
this.drawCircle('bottom',360,this.cavasConfig.bigRadius,this.cavasConfig.bigColorBg);
this.drawCircle('bottom',360,this.cavasConfig.radius,this.cavasConfig.colorBg);
},
computed: {
...mapGetters({
getActiceDevice: "api/getActiceDevice"
})
},
methods: {
init() {
this.isInit = true
console.log('iiiiiiiiiiiiint')
},
async doDrawPie(){
const chart = await this.$refs.chartRef.init(echarts);
chart.setOption(this.option)
},
countOccurrences(array, value) {
return array.reduce((count, current) => {
return current === value ? count + 1 : count;
}, 0);
},
back() {
this.$emit('back', 1)
},
getDataList(){
let type = 'SleepDatas'
let res = this.$store.dispatch('api/getDeviceListDays', {
type: type,
// type: (this.type),
device_real_time: this.time,
device_id:this.getActiceDevice.device_id
}).then(res => {
if(res.data.length > 0){
this.sleep.shen = this.min2Hour(res.data[0].data_msg[0].sleepTotalTime)
this.sleep.qian = this.min2Hour(res.data[0].data_msg[0].deepSleepTime)
this.cavasConfig.angle = this.getAngle(res.data[0].data_msg[0].deepSleepTime)
this.cavasConfig.bigAngle = this.getAngle(res.data[0].data_msg[0].sleepTotalTime)
this.sleepList = res.data[0].data_msg[0].sleepCurve
this.option.series[0].data = [
{
value:this.countOccurrences(this.sleepList,0),
itemStyle:{
color:'#e933dd'
}
},
{
value:this.countOccurrences(this.sleepList,1),
itemStyle:{
color:'#6452da'
}
},
{
value:this.countOccurrences(this.sleepList,2),
itemStyle:{
color:'#4faffc'
}
},
];
let len = this.sleepList.length
this.sleepData.shen = this.getPercent(this.sleepList,0)
this.sleepData.qian = this.getPercent(this.sleepList,1)
this.sleepData.kuai = this.getPercent(this.sleepList,2)
this.exitSleepTime = res.data[0].data_msg[0].exitSleepTime.split('-')
this.fallAsleepTime = res.data[0].data_msg[0].fallAsleepTime.split('-')
this.isOption = true
}
this.doAnimation(0)
this.doAnimation(1)
})
},
getPercent(data,find){
let len = data.length
return (this.countOccurrences(data,find) / len * 100).toFixed(1)
},
getAngle(min){
return min/this.cavasConfig.maxTime * 360
},
min2Hour(min){
let hour = (Math.floor(min / 60))
let minute = (min % 60)
return (hour?hour + ' 小时 ' + minute+ ' 分':minute+ ' 分')
},
doAnimation(type){
if(type == 0){
if(this.cavasConfig.bigStop === 1){
return
}
if(this.cavasConfig.bigStartAngle >= this.cavasConfig.bigAngle){
this.cavasConfig.bigStartAngle = this.cavasConfig.bigAngle
this.cavasConfig.bigStop = 1;
}else{
this.cavasConfig.bigStartAngle += this.cavasConfig.bigAngle/50
}
this.drawCircle('round',this.cavasConfig.bigStartAngle,this.cavasConfig.bigRadius,this.cavasConfig.bigColor);
}else{
if(this.cavasConfig.stop == 1){
return
}
if(this.cavasConfig.startSmallAngle >= this.cavasConfig.angle){
this.cavasConfig.startSmallAngle = this.cavasConfig.angle
this.cavasConfig.stop = 1;
}else{
this.cavasConfig.startSmallAngle += this.cavasConfig.angle/50
}
this.drawCircle('round',this.cavasConfig.startSmallAngle,this.cavasConfig.radius,this.cavasConfig.color);
}
// window.requestAnimationFrame(() => {
setTimeout(() => {
this.doAnimation(type)
},(17))
// })
},
// lineCap线 edangle radius color 线
drawCircle(lineCap,edangle,radius,color) {
this.cavasConfig.ctx.save()
const cx = this.cavasConfig.canvasWidth / 2
const cy = this.cavasConfig.canvasHeight / 2
this.cavasConfig.ctx.translate(cx,cy)
this.cavasConfig.ctx.scale(1, 1);
this.cavasConfig.ctx.lineCap = lineCap
let start = this.cavasConfig.startAngle*Math.PI/180
let end = (edangle+this.cavasConfig.startAngle)*Math.PI/180
this.cavasConfig.ctx.beginPath()
this.cavasConfig.ctx.arc(0,0, radius , start, end);
this.cavasConfig.ctx.lineWidth = this.cavasConfig.lineWidth; // 线
this.cavasConfig.ctx.strokeStyle = color; //
this.cavasConfig.ctx.stroke(); //
this.cavasConfig.ctx.closePath()
this.cavasConfig.ctx.restore()
this.cavasConfig.ctx.draw(true)
}
}
}
</script>
<style lang="scss" scoped>
.dian0{
background-color: #e933dd;
}
.dian1{
background-color: #6452da;
}
.dian2{
background-color: #4faffc;
}
.c0{
color: #e933dd;
font-size: 26rpx;
}
.c1{
color: #6452da;
font-size: 26rpx;
}
.c2{
color: #4faffc;
font-size: 26rpx;
}
.yuan{
width: 16rpx;
height: 16rpx;
border-radius: 50%;
}
.small-title{
font-size: 24rpx;
color: #999;
}
.canvas_box{
background-color: #fff;
padding: 20rpx;
.circle {
width: 40rpx;
height: 40rpx;
margin: 0 auto;
border-radius: 50%;
margin: 0 10rpx;
}
.sleep_info{
.info_title{
font-size: 24rpx;
color: #999;
}
.info_val{
}
}
}
.title_box{
margin-bottom: 20rpx;
.title{
font-size: 28rpx;
}
.more{
color: #999;
}
}
.video {
width: 80%;
padding: 0rpx 20rpx;
}
/* 隐藏所有默认控件 */
video::-webkit-media-controls {
display: none !important;
}
video::-moz-media-controls {
display: none !important;
}
video::-ms-media-controls {
display: none !important;
}
/* 针对不同浏览器的隐藏控件的方式 */
video::part(media-controls) {
display: none !important;
}
</style>

View File

@ -218,6 +218,9 @@
var token = null;
let times = null;
// 个人信息
var user = userInfo(JSON.parse(getURLParameter('user')));
// 获取微软token
fetch("https://eastasia.api.cognitive.microsoft.com/sts/v1.0/issueToken", {
method: 'POST',
@ -243,6 +246,14 @@
for (let i = 0; i < exampleData.length; i++) {
exampleData[i].name = demoData[exampleData[i].type]
}
// 过滤掉非对象类型的值
const filteredData = Object.entries(exampleData).filter(([key, value]) => typeof value === 'object');
// 将键值对转换为所需格式的数组
const arrayOfObjects = filteredData.map(([key, value]) => ({
type: key,
...value
}));
/*
* 实例化迅飞语音听写流式版WebAPI
@ -272,97 +283,100 @@
/*
* 拿到匹配的文字下标
*/
var QSindex = containsKeywordRegex(params.msg);
if (QSindex == 0) {
const obj = exampleData.filter(item => item.type == "bloodGlucose");
const obj = arrayOfObjects.filter(item => item.type == "bloodGlucose");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的血糖为${obj[0].data_msg}毫摩尔/升`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的血糖为${obj[0].data_msg}毫摩尔/升`
} else {
speed(`血糖数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 1) {
const obj = exampleData.filter(item => item.type == "SleepDatas");
const obj = arrayOfObjects.filter(item => item.type == "SleepDatas");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近睡眠时长为${obj[0].data_msg[0].sleepTotalTime}分钟`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近睡眠时长为${minutesToHoursMinutesStringSimplified(obj[0].data_msg[0].sleepTotalTime)}分钟`
} else {
speed(`睡眠数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 2) {
const obj = exampleData.filter(item => item.type == "bloodOxygen");
const obj = arrayOfObjects.filter(item => item.type == "bloodOxygen");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的血氧为${obj[0].data_msg}毫摩尔/升`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的血氧为${obj[0].data_msg}毫摩尔/升`
} else {
speed(`血氧数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 3) {
const obj = exampleData.filter(item => item.type == "bloodPressure");
const obj = arrayOfObjects.filter(item => item.type == "bloodPressure");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的血压为${obj[0].data_msg.bloodPressureLow}/${obj[0].data_msg.bloodPressureHigh}毫摩尔/升`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的血压为${obj[0].data_msg.bloodPressureLow}/${obj[0].data_msg.bloodPressureHigh}毫摩尔/升`
} else {
speed(`血压数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 4 || QSindex == 11) {
const obj = exampleData.filter(item => item.type == "bloodLiquid");
const obj = arrayOfObjects.filter(item => item.type == "bloodLiquid");
if (obj[0].data_msg.cholesterol) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的血脂状况为,
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的血脂状况为,
尿酸为${obj[0].data_msg.uricAcidVal/10},
总胆固醇为${obj[0].data_msg.cholesterol/100},
甘油三酯为${obj[0].data_msg.cholesterol/100},
高密度脂蛋白为${obj[0].data_msg.cholesterol/100},
低密度脂蛋白为${obj[0].data_msg.cholesterol/100}, `
甘油三酯为${obj[0].data_msg.triacylglycerol/100},
高密度脂蛋白为${obj[0].data_msg.highDensity/100},
低密度脂蛋白为${obj[0].data_msg.lowDensity/100}, `
} else {
speed(`血脂数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 6) {
const obj = exampleData.filter(item => item.type == "pulseReat");
const obj = arrayOfObjects.filter(item => item.type == "pulseReat");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的心率为${obj[0].data_msg[0]}, `
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的心率为${obj[0].data_msg[0]}, `
} else {
speed(`心率数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 7) {
const obj = exampleData.filter(item => item.type == "bodyTemperature");
const obj = arrayOfObjects.filter(item => item.type == "bodyTemperature");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的体温为${obj[0].data_msg}摄氏度`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的体温为${obj[0].data_msg}摄氏度`
} else {
speed(`体温数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 8) {
const obj = exampleData.filter(item => item.type == "ECGData");
const obj = arrayOfObjects.filter(item => item.type == "ECGData");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近心电图测量结果为${obj[0].data_msg.heartRate}次/分`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近心电图测量结果为${obj[0].data_msg.heartRate}次/分`
} else {
speed(`心电图数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 9) {
const obj = exampleData.filter(item => item.type == "bodyData");
const obj = arrayOfObjects.filter(item => item.type == "bodyData");
if (obj[0].data_msg.BMI) {
Question = `请模仿全科医生的口吻与我对话,我最近身体成分结果为${obj[0].data_msg.BMI}`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近身体成分结果为${obj[0].data_msg.BMI}`
} else {
speed(`身体成分数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 10) {
const obj = exampleData.filter(item => item.type == "stepIndex");
const obj = arrayOfObjects.filter(item => item.type == "stepIndex");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的运动为${obj[0].data_msg.step}步数,
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的运动为${obj[0].data_msg.step}步数,
${obj[0].data_msg.calorie/10}千卡,
${obj[0].data_msg.distance/1000}公里`
} else {
@ -403,7 +417,7 @@
<voice name="zh-CN-XiaoxiaoNeural">
<mstts:express-as style="Default" >
<prosody rate="0%" pitch="0%">
${str.output.text}
${filterString(str.output.text, ['*', ' '])}
</prosody>
</mstts:express-as>
</voice>
@ -489,7 +503,7 @@
<voice name="zh-CN-XiaoxiaoNeural">
<mstts:express-as style="Default" >
<prosody rate="0%" pitch="0%">
${str.output.text}
${filterString(str.output.text, ['*', ' '])}
</prosody>
</mstts:express-as>
</voice>
@ -654,6 +668,7 @@
runRec.style.display = 'none';
}
// 防抖
function throttle(fn, wait) {
let lastTime = 0; // 上一次调用的时间
@ -667,6 +682,33 @@
}
};
}
// 分钟转小时
function minutesToHoursMinutesStringSimplified(minutes) {
let hours = Math.floor(minutes / 60);
let remainingMinutes = minutes % 60;
let formattedMinutes = remainingMinutes.toString().padStart(2, '0');
if (hours > 0) {
return `${hours}小时${formattedMinutes}分`;
} else {
return `${formattedMinutes}分`;
}
}
function filterString(str, charsToRemove) {
// 这里的正则表达式是通过将charsToRemove数组中的字符转换为字符类character class来构建的
// 例如如果charsToRemove是['*', ' '],则正则表达式将是/[* ]/g
const regex = new RegExp(`[${charsToRemove.join('')}]`, 'g');
// 使用replace方法和正则表达式来移除所有匹配的字符
return str.replace(regex, '');
}
// 个人信息
function userInfo(user) {
return `年龄${user.birthday}, 身高${user.height}, 体重${user.weight}`
}
};
</script>

View File

@ -64,13 +64,6 @@
"navigationBarTitleText" : ""
}
},
{
"path" : "pages/index/detail_info",
"style" :
{
"navigationBarTitleText" : ""
}
},
{
"path" : "pages/only_test/ot3",
"style" :
@ -84,21 +77,6 @@
{
"navigationBarTitleText" : ""
}
},
{
"path" : "pages/index/sleep_info",
"style" :
{
"navigationBarTitleText" : ""
}
},
{
"path" : "pages/index/body_info",
"style" :
{
"navigationBarTitleText" : ""
}
}
],
// "tabBar": {

View File

@ -1,202 +0,0 @@
<template>
<view>
<view class="">
<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>
</template>
<script>
import { mapGetters } from "vuex";
export default {
data() {
return {
dataInfo:{},
bodyDesc:{},
arr:[
'BMI',
'bodyFatPercentage',
'leanBodyMass',
'muscleRate',
'muscleMass',
'subcutaneousFat',
'bodyMoisture',
'skeletalMuscleRate',
'boneMass',
'proteinAmount',
'basalMetabolicRate',
]
}
},
onLoad() {
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);
},
computed: {
...mapGetters({
getActiceDevice: "api/getActiceDevice"
})
},
methods: {
getUserInfo(){
this.$store.dispatch('api/getUserInfo',{
device_id:this.getActiceDevice.device_id
}).then(res => {
})
}
}
}
</script>
<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>

View File

@ -1,7 +1,5 @@
<template>
<view class="">
<tm-nav-bar left-icon="arrow-left" :title="desc? getNameByKey(desc) : getNameByKey(type)" @clickLeft="back" />
<view class="content">
<view class="over" style="margin-top: 30rpx" @click="flag = !flag">

View File

@ -1,415 +0,0 @@
<template>
<view>
<tm-nav-bar left-icon="arrow-left" :title="title" @clickLeft="$utils.back" />
<view class="">
<view class="dxxl">
<!-- <view class="" style="margin-bottom: 10rpx;">
窦性心律
</view> -->
<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>
</template>
<script>
export default {
data() {
return {
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
}
}
}
},
onLoad() {
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);
this.dataInfo = data;
this.changeData(this.dataInfo)
this.list = this.dataInfo.data_msg.list
// uni.removeStorageSync(dataKey);
},
onReady() {
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()
},
methods: {
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>
.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>

View File

@ -12,9 +12,6 @@
{{time}}
</view>
</view>
<view style="height: 40rpx;background-color: #fff;">
</view>
<view class="flx" style="height:600rpx;background-color: #fff;">
<view class="w-5">
@ -360,7 +357,6 @@ import { mapGetters } from "vuex";
],
},
}
},
onLoad(e) {
@ -528,7 +524,7 @@ import { mapGetters } from "vuex";
})
avg = sum / data.length
avg = parseFloat(avg.toFixed(2))
// console.log(dataArr)
console.log(dataArr)
for (let key in dataArr) {
if (dataArr.hasOwnProperty(key)) { // key
let timeArr2 = key.split(':');

View File

@ -1,327 +1,51 @@
<template>
<view>
<view class="content">
<z-paging ref="paging"refresher-only @onRefresh="onRefresh">
<view class="flx jcsb ac">
<view class="left">
<view class="content-header">
<view class="sha-dow"></view>
<view class="text">
<view class="left" style="width: 100%">
<view class="title">
家庭健康管理
</view>
<view class="desc">
<text class="">共享健康 · 守护家人</text>
</view>
</view>
</view>
</view>
<view class="control flx flx_sb">
<view class="btn">
<liu-data-select elementId="data-select3" :dataList="deviceList" @change="changeDev" bgColor="#fff"
color="#000">
<view id="data-select3" class="btn-info1">{{name2 || '选择设备'}}</view>
</liu-data-select>
</view>
<view class="btn" @click="outlogin">
退出登录
</view>
</view>
<view class="list-wrap">
<view class="content-item row" style="padding-bottom: 60rpx;" @click="toPage('/pages/index/echarts?type=step&calorie='+(dataListsNew.stepIndex.data_msg?dataListsNew.stepIndex.data_msg.calorie:0)+'&step='+(dataListsNew.stepIndex.data_msg?dataListsNew.stepIndex.data_msg.step:0)+'&distance='+ (dataListsNew.stepIndex.data_msg?(dataListsNew.stepIndex.data_msg.distance / 1000).toFixed(2):0))">
<view class="item">
<view class="title">卡路里</view>
<view class="row-date">
<view>{{dataListsNew.stepIndex.data_msg ? dataListsNew.stepIndex.data_msg.calorie / 10 : '--'}}</view>
<text>千卡</text>
</view>
</view>
<view class="item">
<view class="title">步数</view>
<view class="row-date">
<view>{{dataListsNew.stepIndex.data_msg ? dataListsNew.stepIndex.data_msg.step : '--'}}</view>
<text></text>
</view>
</view>
<view class="item">
<view class="title">距离</view>
<view class="row-date">
<view>{{dataListsNew.stepIndex.data_msg ? (dataListsNew.stepIndex.data_msg.distance / 1000).toFixed(2) : '--'}}</view>
<text>公里</text>
</view>
</view>
<view class="row-time" v-if="dataListsNew.stepIndex">
{{(dataListsNew.stepIndex.device_real_time)}}
</view>
<view class="bg">
<!-- <image src="../../static/icon/血糖.png" mode="widthFix"></image> -->
</view>
</view>
<view class="content-item" style="padding-bottom: 60rpx;" @click="toPage('/pages/index/sleep_info')">
<view class="title">
{{getNameByKey('sleep')}}
</view>
<view class="time">
{{dataListsNew.SleepDatas.data_msg? dataListsNew.SleepDatas.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data" v-if="dataListsNew.SleepDatas.data_msg">
<view v-if="!(i == 0 && v == 0)" class="" v-for="(v,i) in mkHourMin(dataListsNew.SleepDatas.data_msg[0].sleepTotalTime)" :key="i" class="flex_data">
<view class="f_left" >
{{v}}
</view>
<view class="f_right">
<text class="icon">{{(i == 0?'时':'分')}}</text>
</view>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/sleep.png" style="width: 200rpx;" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPage('/pages/index/echarts?type=bloodGlucose')">
<view class="title">
{{getNameByKey('bloodGlucose')}}
</view>
<view class="time">
{{dataListsNew.bloodGlucose.data_msg? dataListsNew.bloodGlucose.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.bloodGlucose.data_msg || '--'}}
</view>
<view class="f_right">
<text class="icon">毫摩尔/</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/xuetang.png" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPage('/pages/index/echarts?type=bloodOxygen')">
<view class="title">
{{getNameByKey('bloodOxygen')}}
</view>
<view class="time">
{{dataListsNew.bloodOxygen.data_msg? dataListsNew.bloodOxygen.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.bloodOxygen.data_msg? (dataListsNew.bloodOxygen.data_msg[0]) : '--'}}
</view>
<view class="f_right">
<text class="icon">%</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/xueyang.png" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPage('/pages/index/echarts?type=bloodPressure')">
<view class="title">
{{getNameByKey('bloodPressure')}}
</view>
<view class="time">
{{dataListsNew.bloodPressure.data_msg? dataListsNew.bloodPressure.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.bloodPressure.data_msg? dataListsNew.bloodPressure.data_msg.bloodPressureHigh + '/' + dataListsNew.bloodPressure.data_msg.bloodPressureLow : '--'}}
</view>
<view class="f_right">
<text class="icon">mmHG</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/xueya.png" mode="widthFix"></image>
</view>
</view>
<!-- 首页数据 -->
<view class="content-wrap" v-if="Sindex == 1">
<homeData :dataListsNew="dataListsNew" :deviceList="deviceList" @deviceGet="deviceGet" @pageDetail="pageDetail"></homeData>
</view>
<view class="content-item" @click="toPage('/pages/index/echarts?type=bodyTemperature')">
<view class="title">
{{getNameByKey('bodyTemperature')}}
</view>
<view class="time">
{{dataListsNew.bodyTemperature.data_msg? dataListsNew.bodyTemperature.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.bodyTemperature.data_msg? (dataListsNew.bodyTemperature.data_msg) : '--'}}
</view>
<view class="f_right">
<text class="icon"></text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/wendu.png" mode="widthFix"></image>
</view>
</view>
<!-- 图表 -->
<view class="content-wrap" v-if="Sindex == 2">
<echartsData :pageData="pageData" @back="back" @allData="allData"></echartsData>
</view>
<view class="content-item" @click="toPage('/pages/index/echarts?type=pulseReat')">
<view class="title">
{{getNameByKey('pulseReat')}}
</view>
<view class="time">
{{dataListsNew.pulseReat.data_msg? dataListsNew.pulseReat.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.pulseReat.data_msg? (dataListsNew.pulseReat.data_msg[0]) : '--'}}
</view>
<view class="f_right">
<text class="icon">/</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/mailv.png" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPage('/pages/index/detail?type=ECGData')">
<view class="title">
心电图
</view>
<view class="time">
{{dataListsNew.ECGData.data_msg? dataListsNew.ECGData.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.ECGData.data_msg? (dataListsNew.ECGData.data_msg.heartRate || dataListsNew.ECGData.data_msg.meanHeartRate) : '--'}}
</view>
<view class="f_right">
<text class="icon">/</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/xindiantu.png" style="width: 220rpx;" mode="widthFix"></image>
</view>
</view>
<view class="content-item" @click="toPage('/pages/index/detail?type=bodyData')">
<view class="title">
身体成分
</view>
<view class="time">
{{dataListsNew.bodyData.data_msg? dataListsNew.bodyData.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.bodyData.data_msg? (dataListsNew.bodyData.data_msg.BMI ) : '--'}}
</view>
<view class="f_right">
<text class="icon">BMI</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/rtcf.png" style="width: 220rpx;" mode="widthFix"></image>
</view>
</view>
<!-- 日期筛选 全部数据 -->
<view class="content-wrap" v-if="Sindex == 3">
<allData @back="back" :activeType="activeType"></allData>
</view>
<view class="content-item" @click="toPage('/pages/index/echarts?type=bloodLiquid&desc=uricAcidVal')">
<view class="title">
尿酸
</view>
<view class="time">
{{dataListsNew.bloodLiquid.data_msg? dataListsNew.bloodLiquid.device_real_time + '更新' : '--'}}
</view>
<view class="date">
<view class="flex_data">
<view class="f_left">
{{dataListsNew.bloodLiquid.data_msg? (dataListsNew.bloodLiquid.data_msg.uricAcidVal / 10).toFixed(2) : '--'}}
</view>
<view class="f_right">
<text class="icon">微摩尔/</text>
</view>
</view>
</view>
<view class="bg">
<image src="../../static/icon/niaosuan.png" style="width: 220rpx;" mode="widthFix"></image>
</view>
</view>
<!-- 睡眠 -->
<view class="content-wrap" v-if="Sindex == 4">
<sleep-info ref="sleep" @back="back"></sleep-info>
</view>
<view class="content-item" @click="toPage('/pages/index/echarts?type=bloodLiquidAll')">
<view class="title">
{{getNameByKey('bloodLiquid')}}
</view>
<view class="time">
{{dataListsNew.bloodLiquid.data_msg? dataListsNew.bloodLiquid.device_real_time + '更新' : '--'}}
</view>
<view class="gap-content">
<view class="gap-item">
<view class="title">
总胆固醇
</view>
<view class="num">
{{dataListsNew.bloodLiquid.data_msg? (dataListsNew.bloodLiquid.data_msg.cholesterol / 100).toFixed(2) : '--'}}
<text class="icon">毫摩尔/</text>
</view>
</view>
<view class="gap-item">
<view class="title">
甘油三脂
</view>
<view class="num">
{{dataListsNew.bloodLiquid.data_msg? (dataListsNew.bloodLiquid.data_msg.triacylglycerol / 100).toFixed(2) : '--'}}
<text class="icon">毫摩尔/</text>
</view>
</view>
<view class="gap-item">
<view class="title">
高密度脂蛋白
</view>
<view class="num">
{{dataListsNew.bloodLiquid.data_msg? (dataListsNew.bloodLiquid.data_msg.highDensity / 100).toFixed(2) : '--'}}
<text class="icon">毫摩尔/</text>
</view>
</view>
<view class="gap-item">
<view class="title">
低密度脂蛋白
</view>
<view class="num">
{{dataListsNew.bloodLiquid.data_msg? (dataListsNew.bloodLiquid.data_msg.lowDensity / 100).toFixed(2) : '--'}}
<text class="icon">毫摩尔/</text>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 数字人webview原生页面 -->
<view class="right">
<web-view :src="urlLink"></web-view>
</view>
</view>
<view style="height: 200rpx"></view>
</z-paging>
<!-- 数字人webview原生页面 -->
<view class="right">
<web-view :src="urlLink"></web-view>
</view>
</view>
</view>
</template>
<script>
import navtab from '../../components/navtab/navtab.vue'
import homeData from '../../components/home-data/home-data.vue'
import echartsData from '../../components/echarts-data/echarts-data.vue'
import allData from '../../components/all-data/all-data.vue'
import { getArrMaxValue } from '@/utils/utils.js'
import store from "@/store/index.js";
import { mapGetters } from "vuex";
export default {
components: { homeData, echartsData, allData },
data() {
return {
Sindex: 1,
keyNameMap: {
bloodGlucose: "血糖",
sleep: "睡眠",
@ -345,8 +69,7 @@
bloodLiquid:{},
ECGData:{},
bodyData:{},
SleepDatas:{
}
SleepDatas:{}
},
name: '',
@ -356,6 +79,11 @@
urlLink: '',
activeType: '', //
pageData: {
type: ''
}
}
},
onLoad() {
@ -377,7 +105,8 @@
},
computed: {
...mapGetters({
getActiceDevice: "api/getActiceDevice"
getActiceDevice: "api/getActiceDevice",
getUserInfo: "api/getUserInfo",
})
},
@ -399,6 +128,10 @@
// #endif
},
onPageScroll(e) {
this.$refs.xtotop_ref.scroll(e);
},
methods: {
getArrMaxValue,
onRefresh(pageNo, pageSize) {
@ -418,39 +151,63 @@
'bodyData'
],
}).then(res => {
for(let i = 0;i<res.length;i++){
for(let i = 0;i < res.length; i++){
this.dataListsNew[res[i].type] = res[i]
this.dataListsNew[res[i].name] = this.getNameByKey(res[i].type)
}
// console.log(this.dataListsNew, '222222222')
this.urlLink = `/hybrid/html/ai.html?data=${JSON.stringify(res)}`;
this.$refs.paging.complete();
//
this.dataListsNew.bloodLiquidAll = JSON.parse(JSON.stringify(this.dataListsNew.bloodLiquid));
this.dataListsNew.bloodLiquidAll.type = 'bloodLiquidAll';
this.urlLink = `/hybrid/html/ai.html?data=${JSON.stringify(this.dataListsNew)}&user=${JSON.stringify(this.getUserInfo)}`;
}
);
this.$store.dispatch('api/getBindUserDeviceId').then(res => {
this.deviceList = res;
});
},
mkHourMin(min){
if(min < 60){
return [0,min]
}
return [(Math.floor(min / 60)),(min % 60)]
},
//
pageDetail(str) {
if (str == 'ECGData' || str == 'bodyData') {
this.Sindex = 3;
this.activeType = str;
return
} else if (str == 'SleepDatas') {
this.Sindex = 4;
} else {
this.Sindex = 2;
this.activeType = str;
for (let val in this.dataListsNew) {
if (str == val) {
this.pageData = this.dataListsNew[val]
}
}
}
},
toPage(url) {
uni.navigateTo({
url
})
},
getNameByKey(key) {
return this.keyNameMap[key] || "Key not found";
},
formatDateTime(dateTimeStr) {
if (dateTimeStr) {
return dateTimeStr.replace(/-/g, ' ').replace(' ', ' ', 2).replace(' ', ':');
}
},
// 退
outlogin() {
this.$store.commit('api/setApiToken', '')
@ -462,10 +219,21 @@
})
},
//
changeDev(item) {
this.$store.commit('api/setActiceDevice', item)
this.onRefresh();
getNameByKey(key) {
return this.keyNameMap[key] || "Key not found";
},
deviceGet(val) {
this.deviceList = val;
},
allData(index) {
this.Sindex = index;
},
back(index) {
this.Sindex = index;
console.log(this.Sindex)
}
}
}
@ -483,15 +251,19 @@
}
.content {
padding: 20rpx 30rpx;
margin-top:20rpx;
display: flex;
align-items: center;
.content-wrap {
width: 60%;
height: 100vh;
overflow: scroll;
}
.left {
width: 60%;
}
.right {
position: fixed;
top: 0rpx;
right: 0rpx;
height: 100vh;
width: 40%;

View File

@ -5,7 +5,7 @@
<view class="login flx flx_jc">
<view class="flx flx_jc flx_ac" style="width:100%; height: 100vh;">
<view class="content" style="height: 50vh">
<view style="margin-bottom: 60rpx; margin-top: 100rpx;" class="flx flx_jc">
<view style="margin-bottom: 60rpx" class="flx flx_jc">
<uqrcode v-if="qrCode" ref="uqrcode" canvas-id="qrcode" :value="qrCode" :options="{ margin: 10 }"></uqrcode>
<view class="flx flx_ac" v-else style="width: 200px;height: 200px;border: 2rpx #000 dashed;border-radius: 10rpx;">
<view class="" style="text-align: center; width: 100%;">

View File

@ -15,7 +15,7 @@ import {
} from '@/common/api.js';
let state = {
user: {},
userInfo: {},
apiToken: '',
serverTime:0,
activeDevice: ''

View File

@ -174,6 +174,7 @@
.card-list {
padding: 0rpx 20rpx;
margin-bottom: 20rpx;
}
.arrow-tip {
}

View File

@ -168,6 +168,7 @@
padding: 0 20rpx;
box-sizing: border-box;
position: relative;
top: 0rpx;
z-index: 1;
padding-top: constant(safe-area-inset-top); /* 兼容 iOS 12.0-12.1 */
padding-top: env(safe-area-inset-top); /* 兼容 iOS 11.0-11.4 */

View File

@ -0,0 +1,86 @@
## 1.9.22024-09-21
- 修复 uni-popup在android上的重复点击弹出位置不正确的bug
## 1.9.12024-04-02
- 修复 uni-popup-dialog vue3下使用value无法进行绑定的bug(双向绑定兼容旧写法)
## 1.9.02024-03-28
- 修复 uni-popup-dialog 双向绑定时初始化逻辑修正
## 1.8.92024-03-20
- 修复 uni-popup-dialog 数据输入时修正为双向绑定
## 1.8.82024-02-20
- 修复 uni-popup 在微信小程序下出现文字向上闪动的bug
## 1.8.72024-02-02
- 新增 uni-popup-dialog 新增属性focusinput模式下是否自动自动聚焦
## 1.8.62024-01-30
- 新增 uni-popup-dialog 新增属性maxLength:限制输入框字数
## 1.8.52024-01-26
- 新增 uni-popup-dialog 新增属性showClose:控制关闭按钮的显示
## 1.8.42023-11-15
- 新增 uni-popup 支持uni-app-x 注意暂时仅支持 `maskClick` `@open` `@close`
## 1.8.32023-04-17
- 修复 uni-popup 重复打开时的 bug
## 1.8.22023-02-02
- uni-popup-dialog 组件新增 inputType 属性
## 1.8.12022-12-01
- 修复 nvue 下 v-show 报错
## 1.8.02022-11-29
- 优化 主题样式
## 1.7.92022-04-02
- 修复 弹出层内部无法滚动的bug
## 1.7.82022-03-28
- 修复 小程序中高度错误的bug
## 1.7.72022-03-17
- 修复 快速调用open出现问题的Bug
## 1.7.62022-02-14
- 修复 safeArea 属性不能设置为false的bug
## 1.7.52022-01-19
- 修复 isMaskClick 失效的bug
## 1.7.42022-01-19
- 新增 cancelText \ confirmText 属性 ,可自定义文本
- 新增 maskBackgroundColor 属性 ,可以修改蒙版颜色
- 优化 maskClick属性 更新为 isMaskClick ,解决微信小程序警告的问题
## 1.7.32022-01-13
- 修复 设置 safeArea 属性不生效的bug
## 1.7.22021-11-26
- 优化 组件示例
## 1.7.12021-11-26
- 修复 vuedoc 文字错误
## 1.7.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-popup](https://uniapp.dcloud.io/component/uniui/uni-popup)
## 1.6.22021-08-24
- 新增 支持国际化
## 1.6.12021-07-30
- 优化 vue3下事件警告的问题
## 1.6.02021-07-13
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.5.02021-06-23
- 新增 mask-click 遮罩层点击事件
## 1.4.52021-06-22
- 修复 nvue 平台中间弹出后点击内容再点击遮罩无法关闭的Bug
## 1.4.42021-06-18
- 修复 H5平台中间弹出后点击内容再点击遮罩无法关闭的Bug
## 1.4.32021-06-08
- 修复 错误的 watch 字段
- 修复 safeArea 属性不生效的问题
- 修复 点击内容再点击遮罩无法关闭的Bug
## 1.4.22021-05-12
- 新增 组件示例地址
## 1.4.12021-04-29
- 修复 组件内放置 input 、textarea 组件,无法聚焦的问题
## 1.4.0 2021-04-29
- 新增 type 属性的 left\right 值,支持左右弹出
- 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗
- 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色
- 新增 safeArea 属性,是否适配底部安全区
- 修复 App\h5\微信小程序底部安全区占位不对的Bug
- 修复 App 端弹出等待的Bug
- 优化 提升低配设备性能,优化动画卡顿问题
- 优化 更简单的组件自定义方式
## 1.2.92021-02-05
- 优化 组件引用关系通过uni_modules引用组件
## 1.2.82021-02-05
- 调整为uni_modules目录规范
## 1.2.72021-02-05
- 调整为uni_modules目录规范
- 新增 支持 PC 端
- 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端

View File

@ -0,0 +1,45 @@
// #ifdef H5
export default {
name: 'Keypress',
props: {
disable: {
type: Boolean,
default: false
}
},
mounted () {
const keyNames = {
esc: ['Esc', 'Escape'],
tab: 'Tab',
enter: 'Enter',
space: [' ', 'Spacebar'],
up: ['Up', 'ArrowUp'],
left: ['Left', 'ArrowLeft'],
right: ['Right', 'ArrowRight'],
down: ['Down', 'ArrowDown'],
delete: ['Backspace', 'Delete', 'Del']
}
const listener = ($event) => {
if (this.disable) {
return
}
const keyName = Object.keys(keyNames).find(key => {
const keyName = $event.key
const value = keyNames[key]
return value === keyName || (Array.isArray(value) && value.includes(keyName))
})
if (keyName) {
// 避免和其他按键事件冲突
setTimeout(() => {
this.$emit(keyName, {})
}, 0)
}
}
document.addEventListener('keyup', listener)
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('keyup', listener)
})
},
render: () => {}
}
// #endif

View File

@ -0,0 +1,316 @@
<template>
<view class="uni-popup-dialog">
<view class="uni-dialog-title">
<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{titleText}}</text>
</view>
<view v-if="mode === 'base'" class="uni-dialog-content">
<slot>
<text class="uni-dialog-content-text">{{content}}</text>
</slot>
</view>
<view v-else class="uni-dialog-content">
<slot>
<input class="uni-dialog-input" :maxlength="maxlength" v-model="val" :type="inputType"
:placeholder="placeholderText" :focus="focus">
</slot>
</view>
<view class="uni-dialog-button-group">
<view class="uni-dialog-button" v-if="showClose" @click="closeDialog">
<text class="uni-dialog-button-text">{{closeText}}</text>
</view>
<view class="uni-dialog-button" :class="showClose?'uni-border-left':''" @click="onOk">
<text class="uni-dialog-button-text uni-button-color">{{okText}}</text>
</view>
</view>
</view>
</template>
<script>
import popup from '../uni-popup/popup.js'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from '../uni-popup/i18n/index.js'
const {
t
} = initVueI18n(messages)
/**
* PopUp 弹出层-对话框样式
* @description 弹出层-对话框样式
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} value input 模式下的默认值
* @property {String} placeholder input 模式下输入提示
* @property {Boolean} focus input模式下是否自动聚焦默认为true
* @property {String} type = [success|warning|info|error] 主题样式
* @value success 成功
* @value warning 提示
* @value info 消息
* @value error 错误
* @property {String} mode = [base|input] 模式
* @value base 基础对话框
* @value input 可输入对话框
* @showClose {Boolean} 是否显示关闭按钮
* @property {String} content 对话框内容
* @property {Boolean} beforeClose 是否拦截取消事件
* @property {Number} maxlength 输入
* @event {Function} confirm 点击确认按钮触发
* @event {Function} close 点击取消按钮触发
*/
export default {
name: "uniPopupDialog",
mixins: [popup],
emits: ['confirm', 'close', 'update:modelValue', 'input'],
props: {
inputType: {
type: String,
default: 'text'
},
showClose: {
type: Boolean,
default: true
},
// #ifdef VUE2
value: {
type: [String, Number],
default: ''
},
// #endif
// #ifdef VUE3
modelValue: {
type: [Number, String],
default: ''
},
// #endif
placeholder: {
type: [String, Number],
default: ''
},
type: {
type: String,
default: 'error'
},
mode: {
type: String,
default: 'base'
},
title: {
type: String,
default: ''
},
content: {
type: String,
default: ''
},
beforeClose: {
type: Boolean,
default: false
},
cancelText: {
type: String,
default: ''
},
confirmText: {
type: String,
default: ''
},
maxlength: {
type: Number,
default: -1,
},
focus: {
type: Boolean,
default: true,
}
},
data() {
return {
dialogType: 'error',
val: ""
}
},
computed: {
okText() {
return this.confirmText || t("uni-popup.ok")
},
closeText() {
return this.cancelText || t("uni-popup.cancel")
},
placeholderText() {
return this.placeholder || t("uni-popup.placeholder")
},
titleText() {
return this.title || t("uni-popup.title")
}
},
watch: {
type(val) {
this.dialogType = val
},
mode(val) {
if (val === 'input') {
this.dialogType = 'info'
}
},
value(val) {
if (this.maxlength != -1 && this.mode === 'input') {
this.val = val.slice(0, this.maxlength);
} else {
this.val = val
}
},
val(val) {
// #ifdef VUE2
// TODO vue2
this.$emit('input', val);
// #endif
// #ifdef VUE3
// TODO  vue3
this.$emit('update:modelValue', val);
// #endif
}
},
created() {
//
this.popup.disableMask()
// this.popup.closeMask()
if (this.mode === 'input') {
this.dialogType = 'info'
this.val = this.value;
// #ifdef VUE3
this.val = this.modelValue;
// #endif
} else {
this.dialogType = this.type
}
},
methods: {
/**
* 点击确认按钮
*/
onOk() {
if (this.mode === 'input') {
this.$emit('confirm', this.val)
} else {
this.$emit('confirm')
}
if (this.beforeClose) return
this.popup.close()
},
/**
* 点击取消按钮
*/
closeDialog() {
this.$emit('close')
if (this.beforeClose) return
this.popup.close()
},
close() {
this.popup.close()
}
}
}
</script>
<style lang="scss">
.uni-popup-dialog {
width: 300px;
border-radius: 11px;
background-color: #fff;
}
.uni-dialog-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
padding-top: 25px;
}
.uni-dialog-title-text {
font-size: 16px;
font-weight: 500;
}
.uni-dialog-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
padding: 20px;
}
.uni-dialog-content-text {
font-size: 14px;
color: #6C6C6C;
}
.uni-dialog-button-group {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
border-top-color: #f5f5f5;
border-top-style: solid;
border-top-width: 1px;
}
.uni-dialog-button {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
justify-content: center;
align-items: center;
height: 45px;
}
.uni-border-left {
border-left-color: #f0f0f0;
border-left-style: solid;
border-left-width: 1px;
}
.uni-dialog-button-text {
font-size: 16px;
color: #333;
}
.uni-button-color {
color: #007aff;
}
.uni-dialog-input {
flex: 1;
font-size: 14px;
border: 1px #eee solid;
height: 40px;
padding: 0 10px;
border-radius: 5px;
color: #555;
}
.uni-popup__success {
color: #4cd964;
}
.uni-popup__warn {
color: #f0ad4e;
}
.uni-popup__error {
color: #dd524d;
}
.uni-popup__info {
color: #909399;
}
</style>

View File

@ -0,0 +1,143 @@
<template>
<view class="uni-popup-message">
<view class="uni-popup-message__box fixforpc-width" :class="'uni-popup__'+type">
<slot>
<text class="uni-popup-message-text" :class="'uni-popup__'+type+'-text'">{{message}}</text>
</slot>
</view>
</view>
</template>
<script>
import popup from '../uni-popup/popup.js'
/**
* PopUp 弹出层-消息提示
* @description 弹出层-消息提示
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} type = [success|warning|info|error] 主题样式
* @value success 成功
* @value warning 提示
* @value info 消息
* @value error 错误
* @property {String} message 消息提示文字
* @property {String} duration 显示时间设置为 0 则不会自动关闭
*/
export default {
name: 'uniPopupMessage',
mixins:[popup],
props: {
/**
* 主题 success/warning/info/error 默认 success
*/
type: {
type: String,
default: 'success'
},
/**
* 消息文字
*/
message: {
type: String,
default: ''
},
/**
* 显示时间设置为 0 则不会自动关闭
*/
duration: {
type: Number,
default: 3000
},
maskShow:{
type:Boolean,
default:false
}
},
data() {
return {}
},
created() {
this.popup.maskShow = this.maskShow
this.popup.messageChild = this
},
methods: {
timerClose(){
if(this.duration === 0) return
clearTimeout(this.timer)
this.timer = setTimeout(()=>{
this.popup.close()
},this.duration)
}
}
}
</script>
<style lang="scss" >
.uni-popup-message {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
}
.uni-popup-message__box {
background-color: #e1f3d8;
padding: 10px 15px;
border-color: #eee;
border-style: solid;
border-width: 1px;
flex: 1;
}
@media screen and (min-width: 500px) {
.fixforpc-width {
margin-top: 20px;
border-radius: 4px;
flex: none;
min-width: 380px;
/* #ifndef APP-NVUE */
max-width: 50%;
/* #endif */
/* #ifdef APP-NVUE */
max-width: 500px;
/* #endif */
}
}
.uni-popup-message-text {
font-size: 14px;
padding: 0;
}
.uni-popup__success {
background-color: #e1f3d8;
}
.uni-popup__success-text {
color: #67C23A;
}
.uni-popup__warn {
background-color: #faecd8;
}
.uni-popup__warn-text {
color: #E6A23C;
}
.uni-popup__error {
background-color: #fde2e2;
}
.uni-popup__error-text {
color: #F56C6C;
}
.uni-popup__info {
background-color: #F2F6FC;
}
.uni-popup__info-text {
color: #909399;
}
</style>

View File

@ -0,0 +1,187 @@
<template>
<view class="uni-popup-share">
<view class="uni-share-title"><text class="uni-share-title-text">{{shareTitleText}}</text></view>
<view class="uni-share-content">
<view class="uni-share-content-box">
<view class="uni-share-content-item" v-for="(item,index) in bottomData" :key="index" @click.stop="select(item,index)">
<image class="uni-share-image" :src="item.icon" mode="aspectFill"></image>
<text class="uni-share-text">{{item.text}}</text>
</view>
</view>
</view>
<view class="uni-share-button-box">
<button class="uni-share-button" @click="close">{{cancelText}}</button>
</view>
</view>
</template>
<script>
import popup from '../uni-popup/popup.js'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from '../uni-popup/i18n/index.js'
const { t } = initVueI18n(messages)
export default {
name: 'UniPopupShare',
mixins:[popup],
emits:['select'],
props: {
title: {
type: String,
default: ''
},
beforeClose: {
type: Boolean,
default: false
}
},
data() {
return {
bottomData: [{
text: '微信',
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/c2b17470-50be-11eb-b680-7980c8a877b8.png',
name: 'wx'
},
{
text: '支付宝',
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/d684ae40-50be-11eb-8ff1-d5dcf8779628.png',
name: 'ali'
},
{
text: 'QQ',
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/e7a79520-50be-11eb-b997-9918a5dda011.png',
name: 'qq'
},
{
text: '新浪',
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/0dacdbe0-50bf-11eb-8ff1-d5dcf8779628.png',
name: 'sina'
},
// {
// text: '',
// icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/1ec6e920-50bf-11eb-8a36-ebb87efcf8c0.png',
// name: 'copy'
// },
// {
// text: '',
// icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/2e0fdfe0-50bf-11eb-b997-9918a5dda011.png',
// name: 'more'
// }
]
}
},
created() {},
computed: {
cancelText() {
return t("uni-popup.cancel")
},
shareTitleText() {
return this.title || t("uni-popup.shareTitle")
}
},
methods: {
/**
* 选择内容
*/
select(item, index) {
this.$emit('select', {
item,
index
})
this.close()
},
/**
* 关闭窗口
*/
close() {
if(this.beforeClose) return
this.popup.close()
}
}
}
</script>
<style lang="scss" >
.uni-popup-share {
background-color: #fff;
border-top-left-radius: 11px;
border-top-right-radius: 11px;
}
.uni-share-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
height: 40px;
}
.uni-share-title-text {
font-size: 14px;
color: #666;
}
.uni-share-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
padding-top: 10px;
}
.uni-share-content-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: wrap;
width: 360px;
}
.uni-share-content-item {
width: 90px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
padding: 10px 0;
align-items: center;
}
.uni-share-content-item:active {
background-color: #f5f5f5;
}
.uni-share-image {
width: 30px;
height: 30px;
}
.uni-share-text {
margin-top: 10px;
font-size: 14px;
color: #3B4144;
}
.uni-share-button-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
padding: 10px 15px;
}
.uni-share-button {
flex: 1;
border-radius: 50px;
color: #666;
font-size: 16px;
}
.uni-share-button::after {
border-radius: 50px;
}
</style>

View File

@ -0,0 +1,7 @@
{
"uni-popup.cancel": "cancel",
"uni-popup.ok": "ok",
"uni-popup.placeholder": "pleace enter",
"uni-popup.title": "Hint",
"uni-popup.shareTitle": "Share to"
}

View File

@ -0,0 +1,8 @@
import en from './en.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
en,
'zh-Hans': zhHans,
'zh-Hant': zhHant
}

View File

@ -0,0 +1,7 @@
{
"uni-popup.cancel": "取消",
"uni-popup.ok": "确定",
"uni-popup.placeholder": "请输入",
"uni-popup.title": "提示",
"uni-popup.shareTitle": "分享到"
}

View File

@ -0,0 +1,7 @@
{
"uni-popup.cancel": "取消",
"uni-popup.ok": "確定",
"uni-popup.placeholder": "請輸入",
"uni-popup.title": "提示",
"uni-popup.shareTitle": "分享到"
}

View File

@ -0,0 +1,45 @@
// #ifdef H5
export default {
name: 'Keypress',
props: {
disable: {
type: Boolean,
default: false
}
},
mounted () {
const keyNames = {
esc: ['Esc', 'Escape'],
tab: 'Tab',
enter: 'Enter',
space: [' ', 'Spacebar'],
up: ['Up', 'ArrowUp'],
left: ['Left', 'ArrowLeft'],
right: ['Right', 'ArrowRight'],
down: ['Down', 'ArrowDown'],
delete: ['Backspace', 'Delete', 'Del']
}
const listener = ($event) => {
if (this.disable) {
return
}
const keyName = Object.keys(keyNames).find(key => {
const keyName = $event.key
const value = keyNames[key]
return value === keyName || (Array.isArray(value) && value.includes(keyName))
})
if (keyName) {
// 避免和其他按键事件冲突
setTimeout(() => {
this.$emit(keyName, {})
}, 0)
}
}
document.addEventListener('keyup', listener)
// this.$once('hook:beforeDestroy', () => {
// document.removeEventListener('keyup', listener)
// })
},
render: () => {}
}
// #endif

View File

@ -0,0 +1,26 @@
export default {
data() {
return {
}
},
created(){
this.popup = this.getParent()
},
methods:{
/**
* 获取父元素实例
*/
getParent(name = 'uniPopup') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false
parentName = parent.$options.name;
}
return parent;
},
}
}

View File

@ -0,0 +1,90 @@
<template>
<view class="popup-root" v-if="isOpen" v-show="isShow" @click="clickMask">
<view @click.stop>
<slot></slot>
</view>
</view>
</template>
<script>
type CloseCallBack = ()=> void;
let closeCallBack:CloseCallBack = () :void => {};
export default {
emits:["close","clickMask"],
data() {
return {
isShow:false,
isOpen:false
}
},
props: {
maskClick: {
type: Boolean,
default: true
},
},
watch: {
// 设置show = true 时,如果没有 open 需要设置为 open
isShow:{
handler(isShow) {
// console.log("isShow",isShow)
if(isShow && this.isOpen == false){
this.isOpen = true
}
},
immediate:true
},
// 设置isOpen = true 时,如果没有 isShow 需要设置为 isShow
isOpen:{
handler(isOpen) {
// console.log("isOpen",isOpen)
if(isOpen && this.isShow == false){
this.isShow = true
}
},
immediate:true
}
},
methods:{
open(){
// ...funs : CloseCallBack[]
// if(funs.length > 0){
// closeCallBack = funs[0]
// }
this.isOpen = true;
},
clickMask(){
if(this.maskClick == true){
this.$emit('clickMask')
this.close()
}
},
close(): void{
this.isOpen = false;
this.$emit('close')
closeCallBack()
},
hiden(){
this.isShow = false
},
show(){
this.isShow = true
}
}
}
</script>
<style>
.popup-root {
position: fixed;
top: 0;
left: 0;
width: 750rpx;
height: 100%;
flex: 1;
background-color: rgba(0, 0, 0, 0.3);
justify-content: center;
align-items: center;
z-index: 99;
}
</style>

View File

@ -0,0 +1,506 @@
<template>
<view v-if="showPopup" class="uni-popup" :class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']">
<view @touchstart="touchstart">
<uni-transition key="1" v-if="maskShow" name="mask" mode-class="fade" :styles="maskClass"
:duration="duration" :show="showTrans" @click="onTap" />
<uni-transition key="2" :mode-class="ani" name="content" :styles="transClass" :duration="duration"
:show="showTrans" @click="onTap">
<view class="uni-popup__wrapper" :style="getStyles" :class="[popupstyle]" @click="clear">
<slot />
</view>
</uni-transition>
</view>
<!-- #ifdef H5 -->
<keypress v-if="maskShow" @esc="onTap" />
<!-- #endif -->
</view>
</template>
<script>
// #ifdef H5
import keypress from './keypress.js'
// #endif
/**
* PopUp 弹出层
* @description 弹出层组件为了解决遮罩弹层的问题
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式
* @value top 顶部弹出
* @value center 中间弹出
* @value bottom 底部弹出
* @value left 左侧弹出
* @value right 右侧弹出
* @value message 消息提示
* @value dialog 对话框
* @value share 底部分享示例
* @property {Boolean} animation = [true|false] 是否开启动画
* @property {Boolean} maskClick = [true|false] 蒙版点击是否关闭弹窗(废弃)
* @property {Boolean} isMaskClick = [true|false] 蒙版点击是否关闭弹窗
* @property {String} backgroundColor 主窗口背景色
* @property {String} maskBackgroundColor 蒙版颜色
* @property {String} borderRadius 设置圆角(左上右上右下和左下) 示例:"10px 10px 10px 10px"
* @property {Boolean} safeArea 是否适配底部安全区
* @event {Function} change 打开关闭弹窗触发e={show: false}
* @event {Function} maskClick 点击遮罩触发
*/
export default {
name: 'uniPopup',
components: {
// #ifdef H5
keypress
// #endif
},
emits: ['change', 'maskClick'],
props: {
//
animation: {
type: Boolean,
default: true
},
// top: bottomcenter
// message: ; dialog :
type: {
type: String,
default: 'center'
},
// maskClick
isMaskClick: {
type: Boolean,
default: null
},
// TODO 2 使 isMaskClick
maskClick: {
type: Boolean,
default: null
},
backgroundColor: {
type: String,
default: 'none'
},
safeArea: {
type: Boolean,
default: true
},
maskBackgroundColor: {
type: String,
default: 'rgba(0, 0, 0, 0.4)'
},
borderRadius:{
type: String,
}
},
watch: {
/**
* 监听type类型
*/
type: {
handler: function(type) {
if (!this.config[type]) return
this[this.config[type]](true)
},
immediate: true
},
isDesktop: {
handler: function(newVal) {
if (!this.config[newVal]) return
this[this.config[this.type]](true)
},
immediate: true
},
/**
* 监听遮罩是否可点击
* @param {Object} val
*/
maskClick: {
handler: function(val) {
this.mkclick = val
},
immediate: true
},
isMaskClick: {
handler: function(val) {
this.mkclick = val
},
immediate: true
},
// H5
showPopup(show) {
// #ifdef H5
// fix by mehaotian h5 穿
document.getElementsByTagName('body')[0].style.overflow = show ? 'hidden' : 'visible'
// #endif
}
},
data() {
return {
duration: 300,
ani: [],
showPopup: false,
showTrans: false,
popupWidth: 0,
popupHeight: 0,
config: {
top: 'top',
bottom: 'bottom',
center: 'center',
left: 'left',
right: 'right',
message: 'top',
dialog: 'center',
share: 'bottom'
},
maskClass: {
position: 'fixed',
bottom: 0,
top: 0,
left: 0,
right: 0,
backgroundColor: 'rgba(0, 0, 0, 0.4)'
},
transClass: {
backgroundColor: 'transparent',
borderRadius: this.borderRadius || "0",
position: 'fixed',
left: 0,
right: 0
},
maskShow: true,
mkclick: true,
popupstyle: 'top'
}
},
computed: {
getStyles() {
let res = { backgroundColor: this.bg };
if (this.borderRadius || "0") {
res = Object.assign(res, { borderRadius: this.borderRadius })
}
return res;
},
isDesktop() {
return this.popupWidth >= 500 && this.popupHeight >= 500
},
bg() {
if (this.backgroundColor === '' || this.backgroundColor === 'none') {
return 'transparent'
}
return this.backgroundColor
}
},
mounted() {
const fixSize = () => {
const {
windowWidth,
windowHeight,
windowTop,
safeArea,
screenHeight,
safeAreaInsets
} = uni.getSystemInfoSync()
this.popupWidth = windowWidth
this.popupHeight = windowHeight + (windowTop || 0)
// TODO fix by mehaotian ,ios app ios
if (safeArea && this.safeArea) {
// #ifdef MP-WEIXIN
this.safeAreaInsets = screenHeight - safeArea.bottom
// #endif
// #ifndef MP-WEIXIN
this.safeAreaInsets = safeAreaInsets.bottom
// #endif
} else {
this.safeAreaInsets = 0
}
}
fixSize()
// #ifdef H5
// window.addEventListener('resize', fixSize)
// this.$once('hook:beforeDestroy', () => {
// window.removeEventListener('resize', fixSize)
// })
// #endif
},
// #ifndef VUE3
// TODO vue2
destroyed() {
this.setH5Visible()
},
// #endif
// #ifdef VUE3
// TODO vue3
unmounted() {
this.setH5Visible()
},
// #endif
activated() {
this.setH5Visible(!this.showPopup);
},
deactivated() {
this.setH5Visible(true);
},
created() {
// this.mkclick = this.isMaskClick || this.maskClick
if (this.isMaskClick === null && this.maskClick === null) {
this.mkclick = true
} else {
this.mkclick = this.isMaskClick !== null ? this.isMaskClick : this.maskClick
}
if (this.animation) {
this.duration = 300
} else {
this.duration = 0
}
// TODO message
this.messageChild = null
// TODO
this.clearPropagation = false
this.maskClass.backgroundColor = this.maskBackgroundColor
},
methods: {
setH5Visible(visible = true) {
// #ifdef H5
// fix by mehaotian h5 穿
document.getElementsByTagName('body')[0].style.overflow = visible ? "visible" : "hidden";
// #endif
},
/**
* 公用方法不显示遮罩层
*/
closeMask() {
this.maskShow = false
},
/**
* 公用方法遮罩层禁止点击
*/
disableMask() {
this.mkclick = false
},
// TODO nvue
clear(e) {
// #ifndef APP-NVUE
e.stopPropagation()
// #endif
this.clearPropagation = true
},
open(direction) {
// fix by mehaotian
if (this.showPopup) {
return
}
let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share']
if (!(direction && innerType.indexOf(direction) !== -1)) {
direction = this.type
}
if (!this.config[direction]) {
console.error('缺少类型:', direction)
return
}
this[this.config[direction]]()
this.$emit('change', {
show: true,
type: direction
})
},
close(type) {
this.showTrans = false
this.$emit('change', {
show: false,
type: this.type
})
clearTimeout(this.timer)
// //
// this.customOpen && this.customClose()
this.timer = setTimeout(() => {
this.showPopup = false
}, 300)
},
// TODO
touchstart() {
this.clearPropagation = false
},
onTap() {
if (this.clearPropagation) {
// fix by mehaotian nvue
this.clearPropagation = false
return
}
this.$emit('maskClick')
if (!this.mkclick) return
this.close()
},
/**
* 顶部弹出样式处理
*/
top(type) {
this.popupstyle = this.isDesktop ? 'fixforpc-top' : 'top'
this.ani = ['slide-top']
this.transClass = {
position: 'fixed',
left: 0,
right: 0,
backgroundColor: this.bg,
borderRadius:this.borderRadius || "0"
}
// TODO type
if (type) return
this.showPopup = true
this.showTrans = true
this.$nextTick(() => {
this.showPoptrans()
if (this.messageChild && this.type === 'message') {
this.messageChild.timerClose()
}
})
},
/**
* 底部弹出样式处理
*/
bottom(type) {
this.popupstyle = 'bottom'
this.ani = ['slide-bottom']
this.transClass = {
position: 'fixed',
left: 0,
right: 0,
bottom: 0,
paddingBottom: this.safeAreaInsets + 'px',
backgroundColor: this.bg,
borderRadius:this.borderRadius || "0",
}
// TODO type
if (type) return
this.showPoptrans()
},
/**
* 中间弹出样式处理
*/
center(type) {
this.popupstyle = 'center'
//
// #ifdef MP-WEIXIN
this.ani = ['fade']
// #endif
// #ifndef MP-WEIXIN
this.ani = ['zoom-out', 'fade']
// #endif
this.transClass = {
position: 'fixed',
/* #ifndef APP-NVUE */
display: 'flex',
flexDirection: 'column',
/* #endif */
bottom: 0,
left: 0,
right: 0,
top: 0,
justifyContent: 'center',
alignItems: 'center',
borderRadius:this.borderRadius || "0"
}
// TODO type
if (type) return
this.showPoptrans()
},
left(type) {
this.popupstyle = 'left'
this.ani = ['slide-left']
this.transClass = {
position: 'fixed',
left: 0,
bottom: 0,
top: 0,
backgroundColor: this.bg,
borderRadius:this.borderRadius || "0",
/* #ifndef APP-NVUE */
display: 'flex',
flexDirection: 'column'
/* #endif */
}
// TODO type
if (type) return
this.showPoptrans()
},
right(type) {
this.popupstyle = 'right'
this.ani = ['slide-right']
this.transClass = {
position: 'fixed',
bottom: 0,
right: 0,
top: 0,
backgroundColor: this.bg,
borderRadius:this.borderRadius || "0",
/* #ifndef APP-NVUE */
display: 'flex',
flexDirection: 'column'
/* #endif */
}
// TODO type
if (type) return
this.showPoptrans()
},
showPoptrans(){
this.$nextTick(()=>{
this.showPopup = true
this.showTrans = true
})
}
}
}
</script>
<style lang="scss">
.uni-popup {
position: fixed;
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
&.top,
&.left,
&.right {
/* #ifdef H5 */
top: var(--window-top);
/* #endif */
/* #ifndef H5 */
top: 0;
/* #endif */
}
.uni-popup__wrapper {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: relative;
/* iphonex 等安全区设置,底部安全区适配 */
/* #ifndef APP-NVUE */
// padding-bottom: constant(safe-area-inset-bottom);
// padding-bottom: env(safe-area-inset-bottom);
/* #endif */
&.left,
&.right {
/* #ifdef H5 */
padding-top: var(--window-top);
/* #endif */
/* #ifndef H5 */
padding-top: 0;
/* #endif */
flex: 1;
}
}
}
.fixforpc-z-index {
/* #ifndef APP-NVUE */
z-index: 999;
/* #endif */
}
.fixforpc-top {
top: 0;
}
</style>

View File

@ -0,0 +1,88 @@
{
"id": "uni-popup",
"displayName": "uni-popup 弹出层",
"version": "1.9.2",
"description": " Popup 组件,提供常用的弹层",
"keywords": [
"uni-ui",
"弹出层",
"弹窗",
"popup",
"弹框"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-transition"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,17 @@
## Popup 弹出层
> **组件名uni-popup**
> 代码块: `uPopup`
> 关联组件:`uni-transition`
弹出层组件,在应用中弹出一个消息提示窗口、提示框等
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-popup)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,24 @@
## 1.3.32024-04-23
- 修复 当元素会受变量影响自动隐藏的bug
## 1.3.22023-05-04
- 修复 NVUE 平台报错的问题
## 1.3.12021-11-23
- 修复 init 方法初始化问题
## 1.3.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-transition](https://uniapp.dcloud.io/component/uniui/uni-transition)
## 1.2.12021-09-27
- 修复 init 方法不生效的 Bug
## 1.2.02021-07-30
- 组件兼容 vue3如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.12021-05-12
- 新增 示例地址
- 修复 示例项目缺少组件的 Bug
## 1.1.02021-04-22
- 新增 通过方法自定义动画
- 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式
- 优化 动画触发逻辑,使动画更流畅
- 优化 支持单独的动画类型
- 优化 文档示例
## 1.0.22021-02-05
- 调整为 uni_modules 目录规范

View File

@ -0,0 +1,131 @@
// const defaultOption = {
// duration: 300,
// timingFunction: 'linear',
// delay: 0,
// transformOrigin: '50% 50% 0'
// }
// #ifdef APP-NVUE
const nvueAnimation = uni.requireNativePlugin('animation')
// #endif
class MPAnimation {
constructor(options, _this) {
this.options = options
// 在iOS10+QQ小程序平台下传给原生的对象一定是个普通对象而不是Proxy对象否则会报parameter should be Object instead of ProxyObject的错误
this.animation = uni.createAnimation({
...options
})
this.currentStepAnimates = {}
this.next = 0
this.$ = _this
}
_nvuePushAnimates(type, args) {
let aniObj = this.currentStepAnimates[this.next]
let styles = {}
if (!aniObj) {
styles = {
styles: {},
config: {}
}
} else {
styles = aniObj
}
if (animateTypes1.includes(type)) {
if (!styles.styles.transform) {
styles.styles.transform = ''
}
let unit = ''
if(type === 'rotate'){
unit = 'deg'
}
styles.styles.transform += `${type}(${args+unit}) `
} else {
styles.styles[type] = `${args}`
}
this.currentStepAnimates[this.next] = styles
}
_animateRun(styles = {}, config = {}) {
let ref = this.$.$refs['ani'].ref
if (!ref) return
return new Promise((resolve, reject) => {
nvueAnimation.transition(ref, {
styles,
...config
}, res => {
resolve()
})
})
}
_nvueNextAnimate(animates, step = 0, fn) {
let obj = animates[step]
if (obj) {
let {
styles,
config
} = obj
this._animateRun(styles, config).then(() => {
step += 1
this._nvueNextAnimate(animates, step, fn)
})
} else {
this.currentStepAnimates = {}
typeof fn === 'function' && fn()
this.isEnd = true
}
}
step(config = {}) {
// #ifndef APP-NVUE
this.animation.step(config)
// #endif
// #ifdef APP-NVUE
this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
this.next++
// #endif
return this
}
run(fn) {
// #ifndef APP-NVUE
this.$.animationData = this.animation.export()
this.$.timer = setTimeout(() => {
typeof fn === 'function' && fn()
}, this.$.durationTime)
// #endif
// #ifdef APP-NVUE
this.isEnd = false
let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref
if(!ref) return
this._nvueNextAnimate(this.currentStepAnimates, 0, fn)
this.next = 0
// #endif
}
}
const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
'translateZ'
]
const animateTypes2 = ['opacity', 'backgroundColor']
const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom']
animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
MPAnimation.prototype[type] = function(...args) {
// #ifndef APP-NVUE
this.animation[type](...args)
// #endif
// #ifdef APP-NVUE
this._nvuePushAnimates(type, args)
// #endif
return this
}
})
export function createAnimation(option, _this) {
if(!_this) return
clearTimeout(_this.timer)
return new MPAnimation(option, _this)
}

View File

@ -0,0 +1,286 @@
<template>
<!-- #ifndef APP-NVUE -->
<view v-show="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
<!-- #endif -->
</template>
<script>
import { createAnimation } from './createAnimation'
/**
* Transition 过渡动画
* @description 简单过渡动画组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=985
* @property {Boolean} show = [false|true] 控制组件显示或隐藏
* @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
* @value fade 渐隐渐出过渡
* @value slide-top 由上至下过渡
* @value slide-right 由右至左过渡
* @value slide-bottom 由下至上过渡
* @value slide-left 由左至右过渡
* @value zoom-in 由小到大过渡
* @value zoom-out 由大到小过渡
* @property {Number} duration 过渡动画持续时间
* @property {Object} styles 组件样式 css 样式注意带-连接符的属性需要使用小驼峰写法如`backgroundColor:red`
*/
export default {
name: 'uniTransition',
emits:['click','change'],
props: {
show: {
type: Boolean,
default: false
},
modeClass: {
type: [Array, String],
default() {
return 'fade'
}
},
duration: {
type: Number,
default: 300
},
styles: {
type: Object,
default() {
return {}
}
},
customClass:{
type: String,
default: ''
},
onceRender:{
type:Boolean,
default:false
},
},
data() {
return {
isShow: false,
transform: '',
opacity: 1,
animationData: {},
durationTime: 300,
config: {}
}
},
watch: {
show: {
handler(newVal) {
if (newVal) {
this.open()
} else {
// close,
if (this.isShow) {
this.close()
}
}
},
immediate: true
}
},
computed: {
//
stylesObject() {
let styles = {
...this.styles,
'transition-duration': this.duration / 1000 + 's'
}
let transform = ''
for (let i in styles) {
let line = this.toLine(i)
transform += line + ':' + styles[i] + ';'
}
return transform
},
//
transformStyles() {
return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject
}
},
created() {
//
this.config = {
duration: this.duration,
timingFunction: 'ease',
transformOrigin: '50% 50%',
delay: 0
}
this.durationTime = this.duration
},
methods: {
/**
* ref 触发 初始化动画
*/
init(obj = {}) {
if (obj.duration) {
this.durationTime = obj.duration
}
this.animation = createAnimation(Object.assign(this.config, obj),this)
},
/**
* 点击组件触发回调
*/
onClick() {
this.$emit('click', {
detail: this.isShow
})
},
/**
* ref 触发 动画分组
* @param {Object} obj
*/
step(obj, config = {}) {
if (!this.animation) return
for (let i in obj) {
try {
if(typeof obj[i] === 'object'){
this.animation[i](...obj[i])
}else{
this.animation[i](obj[i])
}
} catch (e) {
console.error(`方法 ${i} 不存在`)
}
}
this.animation.step(config)
return this
},
/**
* ref 触发 执行动画
*/
run(fn) {
if (!this.animation) return
this.animation.run(fn)
},
//
open() {
clearTimeout(this.timer)
this.transform = ''
this.isShow = true
let { opacity, transform } = this.styleInit(false)
if (typeof opacity !== 'undefined') {
this.opacity = opacity
}
this.transform = transform
// nextTick wx
this.$nextTick(() => {
// TODO
this.timer = setTimeout(() => {
this.animation = createAnimation(this.config, this)
this.tranfromInit(false).step()
this.animation.run()
this.$emit('change', {
detail: this.isShow
})
}, 20)
})
},
//
close(type) {
if (!this.animation) return
this.tranfromInit(true)
.step()
.run(() => {
this.isShow = false
this.animationData = null
this.animation = null
let { opacity, transform } = this.styleInit(false)
this.opacity = opacity || 1
this.transform = transform
this.$emit('change', {
detail: this.isShow
})
})
},
//
styleInit(type) {
let styles = {
transform: ''
}
let buildStyle = (type, mode) => {
if (mode === 'fade') {
styles.opacity = this.animationType(type)[mode]
} else {
styles.transform += this.animationType(type)[mode] + ' '
}
}
if (typeof this.modeClass === 'string') {
buildStyle(type, this.modeClass)
} else {
this.modeClass.forEach(mode => {
buildStyle(type, mode)
})
}
return styles
},
//
tranfromInit(type) {
let buildTranfrom = (type, mode) => {
let aniNum = null
if (mode === 'fade') {
aniNum = type ? 0 : 1
} else {
aniNum = type ? '-100%' : '0'
if (mode === 'zoom-in') {
aniNum = type ? 0.8 : 1
}
if (mode === 'zoom-out') {
aniNum = type ? 1.2 : 1
}
if (mode === 'slide-right') {
aniNum = type ? '100%' : '0'
}
if (mode === 'slide-bottom') {
aniNum = type ? '100%' : '0'
}
}
this.animation[this.animationMode()[mode]](aniNum)
}
if (typeof this.modeClass === 'string') {
buildTranfrom(type, this.modeClass)
} else {
this.modeClass.forEach(mode => {
buildTranfrom(type, mode)
})
}
return this.animation
},
animationType(type) {
return {
fade: type ? 0 : 1,
'slide-top': `translateY(${type ? '0' : '-100%'})`,
'slide-right': `translateX(${type ? '0' : '100%'})`,
'slide-bottom': `translateY(${type ? '0' : '100%'})`,
'slide-left': `translateX(${type ? '0' : '-100%'})`,
'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
}
},
//
animationMode() {
return {
fade: 'opacity',
'slide-top': 'translateY',
'slide-right': 'translateX',
'slide-bottom': 'translateY',
'slide-left': 'translateX',
'zoom-in': 'scale',
'zoom-out': 'scale'
}
},
// 线
toLine(name) {
return name.replace(/([A-Z])/g, '-$1').toLowerCase()
}
}
}
</script>
<style></style>

View File

@ -0,0 +1,85 @@
{
"id": "uni-transition",
"displayName": "uni-transition 过渡动画",
"version": "1.3.3",
"description": "元素的简单过渡动画",
"keywords": [
"uni-ui",
"uniui",
"动画",
"过渡",
"过渡动画"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,11 @@
## Transition 过渡动画
> **组件名uni-transition**
> 代码块: `uTransition`
元素过渡动画
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-transition)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -1,8 +1,8 @@
var isReady=false;var onReadyCallbacks=[];
var isServiceReady=false;var onServiceReadyCallbacks=[];
var __uniConfig = {"pages":["pages/login/login","pages/index/detail","pages/login/forget","pages/login/register","pages/index/index","pages/device/device","pages/my/my","pages/login/explain","pages/index/echarts","pages/only_test/only_test","pages/only_test/ot1","pages/only_test/ot2","pages/index/detail_info","pages/only_test/ot3","pages/only_test/ot5","pages/index/sleep_info","pages/index/body_info"],"window":{"navigationBarTextStyle":"black","navigationBarTitleText":"中鼎云医","navigationBarBackgroundColor":"#F8F8F8","backgroundColor":"#F8F8F8","navigationStyle":"custom","pageOrientation":"portrait"},"darkmode":false,"nvueCompiler":"uni-app","nvueStyleCompiler":"uni-app","renderer":"auto","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":false},"appname":"中鼎云医","compilerVersion":"4.15","entryPagePath":"pages/login/login","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000}};
var __uniRoutes = [{"path":"/pages/login/login","meta":{"isQuit":true},"window":{}},{"path":"/pages/index/detail","meta":{},"window":{}},{"path":"/pages/login/forget","meta":{},"window":{}},{"path":"/pages/login/register","meta":{},"window":{}},{"path":"/pages/index/index","meta":{},"window":{}},{"path":"/pages/device/device","meta":{},"window":{}},{"path":"/pages/my/my","meta":{},"window":{}},{"path":"/pages/login/explain","meta":{},"window":{}},{"path":"/pages/index/echarts","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/only_test/only_test","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/only_test/ot1","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/only_test/ot2","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/index/detail_info","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/only_test/ot3","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/only_test/ot5","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/index/sleep_info","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/index/body_info","meta":{},"window":{"navigationBarTitleText":""}}];
var __uniConfig = {"pages":["pages/login/login","pages/index/detail","pages/login/forget","pages/login/register","pages/index/index","pages/device/device","pages/my/my","pages/login/explain","pages/index/echarts","pages/only_test/only_test","pages/only_test/ot1","pages/only_test/ot2","pages/only_test/ot3","pages/only_test/ot5"],"window":{"navigationBarTextStyle":"black","navigationBarTitleText":"中鼎云医","navigationBarBackgroundColor":"#F8F8F8","backgroundColor":"#F8F8F8","navigationStyle":"custom","pageOrientation":"portrait"},"darkmode":false,"nvueCompiler":"uni-app","nvueStyleCompiler":"uni-app","renderer":"auto","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":false},"appname":"中鼎云医","compilerVersion":"4.15","entryPagePath":"pages/login/login","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000}};
var __uniRoutes = [{"path":"/pages/login/login","meta":{"isQuit":true},"window":{}},{"path":"/pages/index/detail","meta":{},"window":{}},{"path":"/pages/login/forget","meta":{},"window":{}},{"path":"/pages/login/register","meta":{},"window":{}},{"path":"/pages/index/index","meta":{},"window":{}},{"path":"/pages/device/device","meta":{},"window":{}},{"path":"/pages/my/my","meta":{},"window":{}},{"path":"/pages/login/explain","meta":{},"window":{}},{"path":"/pages/index/echarts","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/only_test/only_test","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/only_test/ot1","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/only_test/ot2","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/only_test/ot3","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/only_test/ot5","meta":{},"window":{"navigationBarTitleText":""}}];
__uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
__uniConfig.onServiceReady=function(callback){if(__uniConfig.serviceReady){callback()}else{onServiceReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"serviceReady",{get:function(){return isServiceReady},set:function(val){isServiceReady=val;if(!isServiceReady){return}const callbacks=onServiceReadyCallbacks.slice(0);onServiceReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
service.register("uni-app-config",{create(a,b,c){if(!__uniConfig.viewport){var d=b.weex.config.env.scale,e=b.weex.config.env.deviceWidth,f=Math.ceil(e/d);Object.assign(__uniConfig,{viewport:f,defaultFontSize:Math.round(f/20)})}return{instance:{__uniConfig:__uniConfig,__uniRoutes:__uniRoutes,global:void 0,window:void 0,document:void 0,frames:void 0,self:void 0,location:void 0,navigator:void 0,localStorage:void 0,history:void 0,Caches:void 0,screen:void 0,alert:void 0,confirm:void 0,prompt:void 0,fetch:void 0,XMLHttpRequest:void 0,WebSocket:void 0,webkit:void 0,print:void 0}}}});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -218,6 +218,9 @@
var token = null;
let times = null;
// 个人信息
var user = userInfo(JSON.parse(getURLParameter('user')));
// 获取微软token
fetch("https://eastasia.api.cognitive.microsoft.com/sts/v1.0/issueToken", {
method: 'POST',
@ -243,6 +246,14 @@
for (let i = 0; i < exampleData.length; i++) {
exampleData[i].name = demoData[exampleData[i].type]
}
// 过滤掉非对象类型的值
const filteredData = Object.entries(exampleData).filter(([key, value]) => typeof value === 'object');
// 将键值对转换为所需格式的数组
const arrayOfObjects = filteredData.map(([key, value]) => ({
type: key,
...value
}));
/*
* 实例化迅飞语音听写流式版WebAPI
@ -272,97 +283,100 @@
/*
* 拿到匹配的文字下标
*/
var QSindex = containsKeywordRegex(params.msg);
if (QSindex == 0) {
const obj = exampleData.filter(item => item.type == "bloodGlucose");
const obj = arrayOfObjects.filter(item => item.type == "bloodGlucose");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的血糖为${obj[0].data_msg}毫摩尔/升`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的血糖为${obj[0].data_msg}毫摩尔/升`
} else {
speed(`血糖数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 1) {
const obj = exampleData.filter(item => item.type == "SleepDatas");
const obj = arrayOfObjects.filter(item => item.type == "SleepDatas");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近睡眠时长为${obj[0].data_msg[0].sleepTotalTime}分钟`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近睡眠时长为${minutesToHoursMinutesStringSimplified(obj[0].data_msg[0].sleepTotalTime)}分钟`
} else {
speed(`睡眠数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 2) {
const obj = exampleData.filter(item => item.type == "bloodOxygen");
const obj = arrayOfObjects.filter(item => item.type == "bloodOxygen");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的血氧为${obj[0].data_msg}毫摩尔/升`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的血氧为${obj[0].data_msg}毫摩尔/升`
} else {
speed(`血氧数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 3) {
const obj = exampleData.filter(item => item.type == "bloodPressure");
const obj = arrayOfObjects.filter(item => item.type == "bloodPressure");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的血压为${obj[0].data_msg.bloodPressureLow}/${obj[0].data_msg.bloodPressureHigh}毫摩尔/升`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的血压为${obj[0].data_msg.bloodPressureLow}/${obj[0].data_msg.bloodPressureHigh}毫摩尔/升`
} else {
speed(`血压数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 4 || QSindex == 11) {
const obj = exampleData.filter(item => item.type == "bloodLiquid");
const obj = arrayOfObjects.filter(item => item.type == "bloodLiquid");
if (obj[0].data_msg.cholesterol) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的血脂状况为,
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的血脂状况为,
尿酸为${obj[0].data_msg.uricAcidVal/10},
总胆固醇为${obj[0].data_msg.cholesterol/100},
甘油三酯为${obj[0].data_msg.cholesterol/100},
高密度脂蛋白为${obj[0].data_msg.cholesterol/100},
低密度脂蛋白为${obj[0].data_msg.cholesterol/100}, `
甘油三酯为${obj[0].data_msg.triacylglycerol/100},
高密度脂蛋白为${obj[0].data_msg.highDensity/100},
低密度脂蛋白为${obj[0].data_msg.lowDensity/100}, `
} else {
speed(`血脂数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 6) {
const obj = exampleData.filter(item => item.type == "pulseReat");
const obj = arrayOfObjects.filter(item => item.type == "pulseReat");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的心率为${obj[0].data_msg[0]}, `
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的心率为${obj[0].data_msg[0]}, `
} else {
speed(`心率数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 7) {
const obj = exampleData.filter(item => item.type == "bodyTemperature");
const obj = arrayOfObjects.filter(item => item.type == "bodyTemperature");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的体温为${obj[0].data_msg}摄氏度`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的体温为${obj[0].data_msg}摄氏度`
} else {
speed(`体温数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 8) {
const obj = exampleData.filter(item => item.type == "ECGData");
const obj = arrayOfObjects.filter(item => item.type == "ECGData");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近心电图测量结果为${obj[0].data_msg.heartRate}次/分`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近心电图测量结果为${obj[0].data_msg.heartRate}次/分`
} else {
speed(`心电图数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 9) {
const obj = exampleData.filter(item => item.type == "bodyData");
const obj = arrayOfObjects.filter(item => item.type == "bodyData");
if (obj[0].data_msg.BMI) {
Question = `请模仿全科医生的口吻与我对话,我最近身体成分结果为${obj[0].data_msg.BMI}`
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近身体成分结果为${obj[0].data_msg.BMI}`
} else {
speed(`身体成分数据为空, 请连接或同步设备数据`)
return
}
}
if (QSindex == 10) {
const obj = exampleData.filter(item => item.type == "stepIndex");
const obj = arrayOfObjects.filter(item => item.type == "stepIndex");
if (obj[0].data_msg) {
Question = `请模仿全科医生的口吻与我对话,我最近测量的运动为${obj[0].data_msg.step}步数,
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的运动为${obj[0].data_msg.step}步数,
${obj[0].data_msg.calorie/10}千卡,
${obj[0].data_msg.distance/1000}公里`
} else {
@ -403,7 +417,7 @@
<voice name="zh-CN-XiaoxiaoNeural">
<mstts:express-as style="Default" >
<prosody rate="0%" pitch="0%">
${str.output.text}
${filterString(str.output.text, ['*', ' '])}
</prosody>
</mstts:express-as>
</voice>
@ -489,7 +503,7 @@
<voice name="zh-CN-XiaoxiaoNeural">
<mstts:express-as style="Default" >
<prosody rate="0%" pitch="0%">
${str.output.text}
${filterString(str.output.text, ['*', ' '])}
</prosody>
</mstts:express-as>
</voice>
@ -653,6 +667,48 @@
startRec.style.display = 'block';
runRec.style.display = 'none';
}
// 防抖
function throttle(fn, wait) {
let lastTime = 0; // 上一次调用的时间
return function (...args) {
const now = Date.now(); // 当前时间
// 如果距离上次调用已经超过设定时间,则调用函数
if (now - lastTime > wait) {
lastTime = now; // 更新上一次调用的时间
fn.apply(this, args); // 以正确的`this`和参数调用函数
}
};
}
// 分钟转小时
function minutesToHoursMinutesStringSimplified(minutes) {
let hours = Math.floor(minutes / 60);
let remainingMinutes = minutes % 60;
let formattedMinutes = remainingMinutes.toString().padStart(2, '0');
if (hours > 0) {
return `${hours}小时${formattedMinutes}分`;
} else {
return `${formattedMinutes}分`;
}
}
function filterString(str, charsToRemove) {
// 这里的正则表达式是通过将charsToRemove数组中的字符转换为字符类character class来构建的
// 例如如果charsToRemove是['*', ' '],则正则表达式将是/[* ]/g
const regex = new RegExp(`[${charsToRemove.join('')}]`, 'g');
// 使用replace方法和正则表达式来移除所有匹配的字符
return str.replace(regex, '');
}
// 个人信息
function userInfo(user) {
return `年龄${user.birthday}, 身高${user.height}, 体重${user.weight}`
}
};
</script>

Binary file not shown.