Files
gym-manage/gym-manage-uniapp/pages/memberInfo/bodyTestMeasuring.vue
T

169 lines
5.5 KiB
Vue

<template>
<view class="scroll-container theme-light">
<view class="bt-page">
<MemberInfoSubNav title="测量中" @back="onCancel" />
<view class="bt-page__body">
<view class="bt-card">
<view class="bt-measure">
<view class="bt-measure__ring-wrap">
<view class="bt-measure__ring-bg"></view>
<view
class="bt-measure__ring-fill"
:style="{ transform: `rotate(${ringRotation}deg)` }"
></view>
<view class="bt-measure__center">
<text class="bt-measure__percent">{{ progress }}%</text>
<text class="bt-measure__hint">{{ phaseHint }}</text>
</view>
</view>
<text class="bt-card__desc">{{ statusText }}</text>
</view>
</view>
<view class="bt-card">
<text class="bt-card__title">实时数据</text>
<view class="bt-measure__live">
<view
v-for="item in liveDisplay"
:key="item.key"
class="bt-measure__live-item"
>
<text class="bt-measure__live-value">{{ item.value }}</text>
<text class="bt-measure__live-label">{{ item.label }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, onUnmounted } from 'vue'
import MemberInfoSubNav from '@/components/memberInfo/MemberInfoSubNav.vue'
import { PAGE, navigateToPage, goBackOrTab } from '@/common/constants/routes.js'
import {
loadMemberStore,
persistMemberStore
} from '@/common/memberInfo/store.js'
import {
interpolateMeasuringMetrics,
saveSimulatedBodyTestRecord
} from '@/common/memberInfo/bodyTestStore.js'
const PHASES = [
{ until: 20, hint: '校准中', text: '请保持站立姿势,双手自然下垂' },
{ until: 50, hint: '阻抗测量', text: '请勿移动,正在进行生物电阻抗分析' },
{ until: 80, hint: '数据分析', text: '正在计算体脂与肌肉分布' },
{ until: 100, hint: '即将完成', text: '生成健康报告中…' }
]
const progress = ref(0)
const liveMetrics = ref({})
let timer = null
const finished = ref(false)
const ringRotation = computed(() => {
return -90 + (progress.value / 100) * 360
})
const phaseHint = computed(() => {
const phase = PHASES.find((p) => progress.value <= p.until)
return phase?.hint || '完成'
})
const statusText = computed(() => {
const phase = PHASES.find((p) => progress.value <= p.until)
return phase?.text || '测量完成'
})
const liveDisplay = computed(() => {
const m = liveMetrics.value
return [
{ key: 'weight', label: '体重(kg)', value: m.weight ?? '--' },
{ key: 'bodyFat', label: '体脂率(%)', value: m.bodyFat ?? '--' },
{ key: 'muscleMass', label: '肌肉量(kg)', value: m.muscleMass ?? '--' },
{ key: 'bmr', label: '基础代谢', value: m.bmr ?? '--' }
]
})
function clearTimer() {
if (timer) {
clearInterval(timer)
timer = null
}
}
function startMeasurement() {
const store = loadMemberStore()
liveMetrics.value = interpolateMeasuringMetrics(0, store.profile)
timer = setInterval(() => {
if (progress.value >= 100) {
completeMeasurement()
return
}
progress.value = Math.min(100, progress.value + 2)
const s = loadMemberStore()
liveMetrics.value = interpolateMeasuringMetrics(progress.value, s.profile)
}, 120)
}
function completeMeasurement() {
if (finished.value) return
finished.value = true
clearTimer()
const store = loadMemberStore()
const record = saveSimulatedBodyTestRecord(store, {
...liveMetrics.value,
visceralFat: 6,
boneMass: 2.42,
bodyWater: liveMetrics.value.bodyWater || 52.8,
protein: 16.4
})
persistMemberStore(store)
uni.showToast({ title: '测量完成', icon: 'success' })
setTimeout(() => {
navigateToPage(`${PAGE.BODY_TEST_REPORT}?id=${record.id}&new=1`)
}, 600)
}
function onCancel() {
if (finished.value) return
uni.showModal({
title: '取消测量',
content: '确定要中断当前体测吗?',
success: (res) => {
if (res.confirm) {
clearTimer()
goBackOrTab(PAGE.BODY_TEST_HOME)
}
}
})
}
onUnmounted(() => {
clearTimer()
})
// Initialize
const store = loadMemberStore()
if (!store.bodyTest.device.connected) {
uni.showToast({ title: '请先连接设备', icon: 'none' })
setTimeout(() => {
navigateToPage(PAGE.BODY_TEST_CONNECT)
}, 800)
} else {
startMeasurement()
}
</script>
<style lang="scss">
@import '@/common/style/base.css';
@import '@/common/style/memberInfo/pages/page-reset.css';
@import '@/common/style/memberInfo/pages/sub-page-base.css';
@import '@/common/style/memberInfo/member-info-component-reset.css';
@import '@/common/style/memberInfo/member-info-sub-nav.css';
@import '@/common/style/memberInfo/member-info-tap.css';
@import '@/common/style/memberInfo/pages/body-test-common.css';
</style>