feat: 更新统计页面,优化HRV数据展示和逻辑

- 移除模拟HRV数据,改为从健康数据中获取实际HRV值
- 新增HRV更新时间显示,提升用户信息获取体验
- 优化日期推导逻辑,确保数据加载一致性
- 更新BMI卡片和营养雷达图组件,支持紧凑模式展示
- 移除不再使用的图片资源,简化项目结构
This commit is contained in:
2025-08-19 22:04:39 +08:00
parent 63b1c52909
commit 7d7d233bbb
10 changed files with 445 additions and 234 deletions

View File

@@ -5,25 +5,35 @@ import { StyleSheet, Text, View } from 'react-native';
interface StressMeterProps {
value: number;
status: '放松' | '正常' | '紧张';
updateTime?: Date;
style?: any;
}
export function StressMeter({ value, status }: StressMeterProps) {
export function StressMeter({ value, updateTime, style }: StressMeterProps) {
// 计算进度条位置0-100%
const progressPercentage = Math.min(100, Math.max(0, (value / 150) * 100));
// HRV值范围30-110ms对应进度条0-100%
const progressPercentage = Math.min(100, Math.max(0, ((value - 30) / 80) * 100));
// 根据状态获取颜色
const getStatusColor = () => {
switch (status) {
case '放松': return '#10B981';
case '正常': return '#F59E0B';
case '紧张': return '#EF4444';
default: return '#F59E0B';
// 根据HRV值计算状态
const getHrvStatus = () => {
if (value >= 70) {
return '放松';
} else if (value >= 50) {
return '正常';
} else {
return '紧张';
}
};
// 根据状态获取表情
const getStatusEmoji = () => {
// 当HRV值为0时不展示表情
if (value === 0) {
return '';
}
const status = getHrvStatus();
switch (status) {
case '放松': return '😌';
case '正常': return '😊';
@@ -32,13 +42,29 @@ export function StressMeter({ value, status }: StressMeterProps) {
}
};
// 格式化更新时间
const formatUpdateTime = (date?: Date) => {
if (!date) return '';
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
};
return (
<View style={styles.container}>
<View style={[styles.container, style]}>
{/* 渐变背景 */}
<LinearGradient
colors={['#F8F9FF', '#F0F4FF']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.gradientBackground}
/>
{/* 头部区域 */}
<View style={styles.header}>
<View style={styles.leftSection}>
<View style={styles.iconContainer}>
<Ionicons name="heart" size={20} color="#3B82F6" />
<Ionicons name="heart" size={16} color="#3B82F6" />
</View>
<Text style={styles.title}></Text>
</View>
@@ -55,18 +81,21 @@ export function StressMeter({ value, status }: StressMeterProps) {
<View style={styles.progressContainer}>
<View style={styles.progressTrack}>
{/* 渐变背景进度条 */}
<View style={[styles.progressBar, { width: `${progressPercentage}%` }]}>
<LinearGradient
colors={['#F97316', '#FCD34D', '#84CC16', '#10B981']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={styles.gradientBar}
/>
</View>
<LinearGradient
colors={['#FFD700', '#87CEEB', '#98FB98']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={styles.gradientTrack}
/>
{/* 白色圆形指示器 */}
<View style={[styles.indicator, { left: `${Math.max(0, progressPercentage - 2)}%` }]} />
<View style={[styles.indicator, { left: `${Math.max(0, Math.min(100, progressPercentage - 2))}%` }]} />
</View>
</View>
{/* 更新时间 */}
{updateTime && (
<Text style={styles.updateTime}>{formatUpdateTime(updateTime)}</Text>
)}
</View>
);
}
@@ -74,8 +103,8 @@ export function StressMeter({ value, status }: StressMeterProps) {
const styles = StyleSheet.create({
container: {
backgroundColor: '#FFFFFF',
borderRadius: 20,
padding: 16,
borderRadius: 16,
padding: 14,
marginBottom: 12,
shadowColor: '#000',
shadowOffset: {
@@ -83,88 +112,101 @@ const styles = StyleSheet.create({
height: 2,
},
shadowOpacity: 0.08,
shadowRadius: 12,
shadowRadius: 8,
elevation: 3,
position: 'relative',
overflow: 'hidden',
minHeight: 110,
width: 140,
},
gradientBackground: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
borderRadius: 16,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 12,
marginBottom: 8,
},
leftSection: {
flexDirection: 'row',
alignItems: 'center',
},
iconContainer: {
width: 28,
height: 28,
borderRadius: 8,
width: 24,
height: 24,
borderRadius: 6,
backgroundColor: '#EBF4FF',
alignItems: 'center',
justifyContent: 'center',
marginRight: 8,
marginRight: 6,
},
title: {
fontSize: 16,
fontSize: 14,
fontWeight: '700',
color: '#1F2937',
color: '#192126',
},
emoji: {
fontSize: 24,
fontSize: 16,
},
valueSection: {
flexDirection: 'row',
alignItems: 'baseline',
marginBottom: 16,
marginBottom: 12,
},
value: {
fontSize: 42,
fontSize: 28,
fontWeight: '800',
color: '#1F2937',
lineHeight: 46,
color: '#192126',
lineHeight: 32,
},
unit: {
fontSize: 14,
fontSize: 12,
fontWeight: '500',
color: '#6B7280',
color: '#9AA3AE',
marginLeft: 4,
},
progressContainer: {
height: 20,
height: 16,
marginBottom: 4,
},
progressTrack: {
height: 12,
backgroundColor: '#F3F4F6',
borderRadius: 6,
height: 8,
borderRadius: 4,
position: 'relative',
overflow: 'visible',
},
progressBar: {
gradientTrack: {
height: '100%',
borderRadius: 6,
overflow: 'hidden',
},
gradientBar: {
height: '100%',
borderRadius: 6,
borderRadius: 4,
},
indicator: {
position: 'absolute',
top: -4,
width: 20,
height: 20,
borderRadius: 10,
width: 16,
height: 16,
borderRadius: 8,
backgroundColor: '#FFFFFF',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
height: 1,
},
shadowOpacity: 0.15,
shadowRadius: 4,
elevation: 4,
borderWidth: 2,
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2,
borderWidth: 1.5,
borderColor: '#E5E7EB',
},
updateTime: {
fontSize: 10,
color: '#9AA3AE',
textAlign: 'right',
marginTop: 2,
},
});