feat: 新增基础代谢率功能及相关组件
- 在健康数据中引入基础代谢率的读取和展示,支持用户记录健身进度 - 更新统计页面,替换BMI卡片为基础代谢卡片,提升用户体验 - 优化健康数据获取逻辑,确保基础代谢数据的准确性 - 更新权限描述,明确应用对健康数据的访问需求
This commit is contained in:
188
components/BasalMetabolismCard.tsx
Normal file
188
components/BasalMetabolismCard.tsx
Normal file
@@ -0,0 +1,188 @@
|
||||
import { AnimatedNumber } from '@/components/AnimatedNumber';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import React from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
interface BasalMetabolismCardProps {
|
||||
value: number | null;
|
||||
resetToken?: number;
|
||||
style?: any;
|
||||
}
|
||||
|
||||
export function BasalMetabolismCard({ value, resetToken, style }: BasalMetabolismCardProps) {
|
||||
// 获取基础代谢状态描述
|
||||
const getMetabolismStatus = () => {
|
||||
if (value === null || value === 0) {
|
||||
return { text: '未知', color: '#9AA3AE' };
|
||||
}
|
||||
|
||||
// 基于常见的基础代谢范围来判断状态
|
||||
if (value >= 1800) {
|
||||
return { text: '高代谢', color: '#10B981' };
|
||||
} else if (value >= 1400) {
|
||||
return { text: '正常', color: '#3B82F6' };
|
||||
} else if (value >= 1000) {
|
||||
return { text: '偏低', color: '#F59E0B' };
|
||||
} else {
|
||||
return { text: '较低', color: '#EF4444' };
|
||||
}
|
||||
};
|
||||
|
||||
const status = getMetabolismStatus();
|
||||
|
||||
return (
|
||||
<View style={[styles.container, style]}>
|
||||
{/* 渐变背景 */}
|
||||
<LinearGradient
|
||||
colors={['#F0F9FF', '#E0F2FE']}
|
||||
style={styles.gradientBackground}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
/>
|
||||
|
||||
{/* 装饰性圆圈 */}
|
||||
<View style={styles.decorativeCircle1} />
|
||||
<View style={styles.decorativeCircle2} />
|
||||
|
||||
{/* 头部区域 */}
|
||||
<View style={styles.header}>
|
||||
<View style={styles.leftSection}>
|
||||
|
||||
<Text style={styles.title}>基础代谢</Text>
|
||||
</View>
|
||||
<View style={[styles.statusBadge, { backgroundColor: status.color + '20' }]}>
|
||||
<Text style={[styles.statusText, { color: status.color }]}>{status.text}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 数值显示区域 */}
|
||||
<View style={styles.valueSection}>
|
||||
{value != null && value > 0 ? (
|
||||
<AnimatedNumber
|
||||
value={value}
|
||||
resetToken={resetToken}
|
||||
style={styles.value}
|
||||
format={(v) => Math.round(v).toString()}
|
||||
/>
|
||||
) : (
|
||||
<Text style={styles.value}>--</Text>
|
||||
)}
|
||||
<Text style={styles.unit}>千卡/日</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderRadius: 20,
|
||||
padding: 16,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 4,
|
||||
},
|
||||
shadowOpacity: 0.08,
|
||||
shadowRadius: 12,
|
||||
elevation: 4,
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
gradientBackground: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
opacity: 0.6,
|
||||
},
|
||||
decorativeCircle1: {
|
||||
position: 'absolute',
|
||||
top: -20,
|
||||
right: -20,
|
||||
width: 60,
|
||||
height: 60,
|
||||
borderRadius: 30,
|
||||
backgroundColor: '#0EA5E9',
|
||||
opacity: 0.1,
|
||||
},
|
||||
decorativeCircle2: {
|
||||
position: 'absolute',
|
||||
bottom: -15,
|
||||
left: -15,
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 20,
|
||||
backgroundColor: '#0EA5E9',
|
||||
opacity: 0.05,
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 16,
|
||||
zIndex: 1,
|
||||
},
|
||||
leftSection: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
iconContainer: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 10,
|
||||
backgroundColor: '#FFFFFF',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginRight: 8,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 1,
|
||||
},
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 2,
|
||||
elevation: 1,
|
||||
},
|
||||
fireIcon: {
|
||||
width: 14,
|
||||
height: 18,
|
||||
backgroundColor: '#EF4444',
|
||||
borderTopLeftRadius: 7,
|
||||
borderTopRightRadius: 7,
|
||||
borderBottomLeftRadius: 2,
|
||||
borderBottomRightRadius: 2,
|
||||
},
|
||||
title: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
color: '#0F172A',
|
||||
},
|
||||
statusBadge: {
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 12,
|
||||
},
|
||||
statusText: {
|
||||
fontSize: 11,
|
||||
fontWeight: '600',
|
||||
},
|
||||
valueSection: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
zIndex: 1,
|
||||
},
|
||||
value: {
|
||||
fontSize: 24,
|
||||
fontWeight: '800',
|
||||
color: '#0F172A',
|
||||
lineHeight: 28,
|
||||
},
|
||||
unit: {
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
color: '#64748B',
|
||||
marginLeft: 6,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user