Files
digital-pilates/components/FitnessRingsCard.tsx
richarjiang 78620f18ee feat: 更新依赖项并优化组件结构
- 在 package.json 和 package-lock.json 中新增 @sentry/react-native、react-native-device-info 和 react-native-purchases 依赖
- 更新统计页面,替换 CircularRing 组件为 FitnessRingsCard,增强健身数据展示
- 在布局文件中引入 ToastProvider,优化用户通知体验
- 新增 SuccessToast 组件,提供全局成功提示功能
- 更新健康数据获取逻辑,支持健身圆环数据的提取
- 优化多个组件的样式和交互,提升用户体验
2025-08-21 09:51:25 +08:00

182 lines
4.9 KiB
TypeScript

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { CircularRing } from './CircularRing';
type FitnessRingsCardProps = {
style?: any;
// 活动卡路里数据
activeCalories?: number;
activeCaloriesGoal?: number;
// 锻炼分钟数据
exerciseMinutes?: number;
exerciseMinutesGoal?: number;
// 站立小时数据
standHours?: number;
standHoursGoal?: number;
// 动画重置令牌
resetToken?: unknown;
};
/**
* 健身圆环卡片组件,模仿 Apple Watch 的健身圆环
*/
export function FitnessRingsCard({
style,
activeCalories = 25,
activeCaloriesGoal = 350,
exerciseMinutes = 1,
exerciseMinutesGoal = 5,
standHours = 2,
standHoursGoal = 13,
resetToken,
}: FitnessRingsCardProps) {
// 计算进度百分比
const caloriesProgress = Math.min(1, Math.max(0, activeCalories / activeCaloriesGoal));
const exerciseProgress = Math.min(1, Math.max(0, exerciseMinutes / exerciseMinutesGoal));
const standProgress = Math.min(1, Math.max(0, standHours / standHoursGoal));
return (
<View style={[styles.container, style]}>
<View style={styles.contentContainer}>
{/* 左侧圆环 */}
<View style={styles.ringsContainer}>
<View style={styles.ringWrapper}>
{/* 外圈 - 活动卡路里 (红色) */}
<View style={[styles.ringPosition]}>
<CircularRing
size={36}
strokeWidth={2.5}
trackColor="rgba(255, 59, 48, 0.15)"
progressColor="#FF3B30"
progress={caloriesProgress}
showCenterText={false}
resetToken={resetToken}
startAngleDeg={-90}
/>
</View>
{/* 中圈 - 锻炼分钟 (橙色) */}
<View style={[styles.ringPosition]}>
<CircularRing
size={26}
strokeWidth={2}
trackColor="rgba(255, 149, 0, 0.15)"
progressColor="#FF9500"
progress={exerciseProgress}
showCenterText={false}
resetToken={resetToken}
startAngleDeg={-90}
/>
</View>
{/* 内圈 - 站立小时 (蓝色) */}
<View style={[styles.ringPosition]}>
<CircularRing
size={16}
strokeWidth={1.5}
trackColor="rgba(0, 122, 255, 0.15)"
progressColor="#007AFF"
progress={standProgress}
showCenterText={false}
resetToken={resetToken}
startAngleDeg={-90}
/>
</View>
</View>
</View>
{/* 右侧数据显示 */}
<View style={styles.dataContainer}>
<View style={styles.dataRow}>
<Text style={styles.dataText}>
<Text style={styles.dataValue}>{activeCalories}</Text>
<Text style={styles.dataGoal}>/{activeCaloriesGoal}</Text>
</Text>
<Text style={styles.dataUnit}></Text>
</View>
<View style={styles.dataRow}>
<Text style={styles.dataText}>
<Text style={styles.dataValue}>{exerciseMinutes}</Text>
<Text style={styles.dataGoal}>/{exerciseMinutesGoal}</Text>
</Text>
<Text style={styles.dataUnit}></Text>
</View>
<View style={styles.dataRow}>
<Text style={styles.dataText}>
<Text style={styles.dataValue}>{standHours}</Text>
<Text style={styles.dataGoal}>/{standHoursGoal}</Text>
</Text>
<Text style={styles.dataUnit}></Text>
</View>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#FFFFFF',
borderRadius: 16,
padding: 12,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.08,
shadowRadius: 8,
elevation: 3,
},
contentContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
ringsContainer: {
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
ringWrapper: {
position: 'relative',
width: 36,
height: 36,
alignItems: 'center',
justifyContent: 'center',
},
ringPosition: {
position: 'absolute',
alignItems: 'center',
justifyContent: 'center',
},
dataContainer: {
flex: 1,
gap: 3,
},
dataRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
dataText: {
fontSize: 12,
fontWeight: '700',
flex: 1,
},
dataValue: {
color: '#192126',
},
dataGoal: {
color: '#9AA3AE',
},
dataUnit: {
fontSize: 10,
color: '#9AA3AE',
fontWeight: '500',
minWidth: 25,
textAlign: 'right',
},
});