feat: 集成健康数据功能优化
- 在 app.json 中添加 react-native-health 的配置以启用健康 API - 在 Explore 页面中重构健康数据加载逻辑,增加加载状态提示 - 更新健康数据获取函数,增强错误处理和日志输出 - 修改 iOS 权限设置,确保健康数据访问权限的正确配置 - 更新 Info.plist 中的健康数据使用说明
This commit is contained in:
@@ -51,23 +51,38 @@ export default function ExploreScreen() {
|
||||
// HealthKit: 每次页面聚焦都拉取今日数据
|
||||
const [stepCount, setStepCount] = useState<number | null>(null);
|
||||
const [activeCalories, setActiveCalories] = useState<number | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const loadHealthData = async () => {
|
||||
try {
|
||||
console.log('=== 开始HealthKit初始化流程 ===');
|
||||
setIsLoading(true);
|
||||
|
||||
const ok = await ensureHealthPermissions();
|
||||
if (!ok) {
|
||||
const errorMsg = '无法获取健康权限,请确保在真实iOS设备上运行并授权应用访问健康数据';
|
||||
console.warn(errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('权限获取成功,开始获取健康数据...');
|
||||
const data = await fetchTodayHealthData();
|
||||
|
||||
console.log('设置UI状态:', data);
|
||||
setStepCount(data.steps);
|
||||
setActiveCalories(Math.round(data.activeEnergyBurned));
|
||||
console.log('=== HealthKit数据获取完成 ===');
|
||||
|
||||
} catch (error) {
|
||||
console.error('HealthKit流程出现异常:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
let isActive = true;
|
||||
const run = async () => {
|
||||
console.log('HealthKit init start');
|
||||
const ok = await ensureHealthPermissions();
|
||||
if (!ok) return;
|
||||
const data = await fetchTodayHealthData();
|
||||
if (!isActive) return;
|
||||
setStepCount(data.steps);
|
||||
setActiveCalories(Math.round(data.activeEnergyBurned));
|
||||
};
|
||||
run();
|
||||
return () => {
|
||||
isActive = false;
|
||||
};
|
||||
loadHealthData();
|
||||
}, [])
|
||||
);
|
||||
|
||||
@@ -113,6 +128,24 @@ export default function ExploreScreen() {
|
||||
{/* 今日报告 标题 */}
|
||||
<Text style={styles.sectionTitle}>今日报告</Text>
|
||||
|
||||
{/* 健康数据错误提示 */}
|
||||
{isLoading && (
|
||||
<View style={styles.errorContainer}>
|
||||
<Ionicons name="warning-outline" size={20} color="#E54D4D" />
|
||||
<Text style={styles.errorText}>加载中...</Text>
|
||||
<TouchableOpacity
|
||||
style={styles.retryButton}
|
||||
onPress={loadHealthData} disabled={isLoading}
|
||||
>
|
||||
<Ionicons
|
||||
name="refresh-outline"
|
||||
size={16}
|
||||
color={isLoading ? '#9AA3AE' : '#E54D4D'}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 指标行:左大卡(训练时间),右两小卡(消耗卡路里、步数) */}
|
||||
<View style={styles.metricsRow}>
|
||||
<View style={[styles.trainingCard, styles.metricsLeft]}>
|
||||
@@ -127,7 +160,7 @@ export default function ExploreScreen() {
|
||||
<View style={[styles.metricsRightCard, styles.caloriesCard, { minHeight: 88 }]}>
|
||||
<Text style={styles.cardTitleSecondary}>消耗卡路里</Text>
|
||||
<Text style={styles.caloriesValue}>
|
||||
{activeCalories != null ? `${activeCalories} 千卡` : '——'}
|
||||
{isLoading ? '加载中...' : activeCalories != null ? `${activeCalories} 千卡` : '——'}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={[styles.metricsRightCard, styles.stepsCard, { minHeight: 88 }]}>
|
||||
@@ -135,7 +168,7 @@ export default function ExploreScreen() {
|
||||
<View style={styles.iconSquare}><Ionicons name="footsteps-outline" size={18} color="#192126" /></View>
|
||||
<Text style={styles.cardTitle}>步数</Text>
|
||||
</View>
|
||||
<Text style={styles.stepsValue}>{stepCount != null ? `${stepCount}/2000` : '——/2000'}</Text>
|
||||
<Text style={styles.stepsValue}>{isLoading ? '加载中.../2000' : stepCount != null ? `${stepCount}/2000` : '——/2000'}</Text>
|
||||
<ProgressBar progress={Math.min(1, Math.max(0, (stepCount ?? 0) / 2000))} height={12} trackColor="#FFEBCB" fillColor="#FFC365" />
|
||||
</View>
|
||||
</View>
|
||||
@@ -386,5 +419,24 @@ const styles = StyleSheet.create({
|
||||
color: '#7A6A42',
|
||||
fontWeight: '700',
|
||||
marginBottom: 8,
|
||||
}
|
||||
},
|
||||
errorContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#FFE5E5',
|
||||
borderRadius: 12,
|
||||
padding: 12,
|
||||
marginBottom: 16,
|
||||
},
|
||||
errorText: {
|
||||
fontSize: 14,
|
||||
color: '#E54D4D',
|
||||
fontWeight: '600',
|
||||
marginLeft: 8,
|
||||
flex: 1,
|
||||
},
|
||||
retryButton: {
|
||||
padding: 4,
|
||||
marginLeft: 8,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user