feat: 优化统计和步数详情页面,添加活动等级计算和展示,更新压力计组件以支持HRV值直接显示

This commit is contained in:
2025-09-05 22:28:04 +08:00
parent e6708e68c2
commit c37c3a16b1
4 changed files with 261 additions and 155 deletions

View File

@@ -1,3 +1,4 @@
import dayjs from 'dayjs';
import { LinearGradient } from 'expo-linear-gradient';
import React, { useState } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
@@ -11,42 +12,47 @@ interface StressMeterProps {
}
export function StressMeter({ value, updateTime, style, hrvValue }: StressMeterProps) {
// 格式化更新时间显示
const formatUpdateTime = (date: Date): string => {
const now = dayjs();
const updateTime = dayjs(date);
const diffMinutes = now.diff(updateTime, 'minute');
const diffHours = now.diff(updateTime, 'hour');
const diffDays = now.diff(updateTime, 'day');
if (diffMinutes < 1) {
return '刚刚更新';
} else if (diffMinutes < 60) {
return `${diffMinutes}分钟前更新`;
} else if (diffHours < 24) {
return `${diffHours}小时前更新`;
} else if (diffDays < 7) {
return `${diffDays}天前更新`;
} else {
return updateTime.format('MM-DD HH:mm');
}
};
// 将HRV值转换为压力指数0-100
// HRV值范围30-110ms映射到压力指数100-0
// HRV值越高压力越小HRV值越低压力越大
// 根据压力指数计算状态
const getStressStatus = () => {
if (value === null) {
return '未知';
} else if (value >= 70) {
return '放松';
} else if (value >= 30) {
return '正常';
} else {
return '紧张';
}
const convertHrvToStressIndex = (hrv: number | null): number | null => {
if (hrv === null || hrv === 0) return null;
// HRV 范围: 30-110ms对应压力指数: 100-0
// 线性映射: stressIndex = 100 - ((hrv - 30) / (110 - 30)) * 100
const normalizedHrv = Math.max(30, Math.min(110, hrv));
const stressIndex = 100 - ((normalizedHrv - 30) / (110 - 30)) * 100;
return Math.round(stressIndex);
};
// 根据状态获取表情
const getStatusEmoji = () => {
// 当HRV值为null或0时不展示表情
if (value === null || value === 0) {
return '';
}
const status = getStressStatus();
switch (status) {
case '未知': return '';
case '放松': return '😌';
case '正常': return '😊';
case '紧张': return '😰';
default: return '😊';
}
};
// 使用传入的 hrvValue 进行转换
const stressIndex = convertHrvToStressIndex(hrvValue);
// 计算进度条位置0-100%
// 压力指数越高,进度条越满(红色区域越多)
const progressPercentage = value !== null ? Math.max(0, Math.min(100, value)) : 0;
const progressPercentage = stressIndex !== null ? Math.max(0, Math.min(100, stressIndex)) : 0;
// 在组件内部添加状态
const [showStressModal, setShowStressModal] = useState(false);
@@ -68,12 +74,15 @@ export function StressMeter({ value, updateTime, style, hrvValue }: StressMeterP
<View style={styles.leftSection}>
<Text style={styles.title}></Text>
</View>
{updateTime && (
<Text style={styles.headerUpdateTime}>{formatUpdateTime(updateTime)}</Text>
)}
</View>
{/* 数值显示区域 */}
<View style={styles.valueSection}>
<Text style={styles.value}>{value === null ? '--' : value}</Text>
<Text style={styles.unit}></Text>
<Text style={styles.value}>{stressIndex === null ? '--' : stressIndex}</Text>
<Text style={styles.unit}>ms</Text>
</View>
{/* 进度条区域 */}
@@ -89,14 +98,10 @@ export function StressMeter({ value, updateTime, style, hrvValue }: StressMeterP
/>
</View>
{/* 白色圆形指示器 */}
<View style={[styles.indicator, { left: `${Math.max(0, Math.min(100, progressPercentage - 2))}%` }]} />
<View style={[styles.indicator, { left: `${Math.max(0, Math.min(100, progressPercentage))}%` }]} />
</View>
</View>
{/* 更新时间
{updateTime && (
<Text style={styles.updateTime}>{formatUpdateTime(updateTime)}</Text>
)} */}
</TouchableOpacity>
{/* 压力分析浮窗 */}
@@ -207,4 +212,9 @@ const styles = StyleSheet.create({
textAlign: 'right',
marginTop: 2,
},
headerUpdateTime: {
fontSize: 11,
color: '#9AA3AE',
fontWeight: '500',
},
});