feat: 优化健康数据相关组件及功能
- 在 CoachScreen 中调整键盘高度计算,移除不必要的 insets.bottom - 更新 Statistics 组件,移除未使用的健康数据相关函数,简化代码 - 修改多个统计卡片,移除不必要的图标属性,提升组件简洁性 - 优化 HealthDataCard 和其他统计卡片的样式,提升视觉一致性 - 更新健康数据获取逻辑,确保数据处理更为准确 - 移除 MoodCard 中的多余元素,简化心情记录展示 - 调整 StressMeter 和其他组件的样式,提升用户体验
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { AnimatedNumber } from '@/components/AnimatedNumber';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import React from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
@@ -32,17 +31,6 @@ export function BasalMetabolismCard({ value, resetToken, style }: BasalMetabolis
|
||||
|
||||
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}>
|
||||
@@ -88,34 +76,7 @@ const styles = StyleSheet.create({
|
||||
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',
|
||||
|
||||
@@ -67,20 +67,39 @@ export const DateSelector: React.FC<DateSelectorProps> = ({
|
||||
const maxScrollOffset = Math.max(0, (days.length * itemWidth) - scrollWidth);
|
||||
const finalOffset = Math.min(centerOffset, maxScrollOffset);
|
||||
|
||||
daysScrollRef.current.scrollTo({ x: finalOffset, animated });
|
||||
if (animated) {
|
||||
// 使用原生动画API实现更平滑的滚动
|
||||
requestAnimationFrame(() => {
|
||||
daysScrollRef.current?.scrollTo({
|
||||
x: finalOffset,
|
||||
animated: true
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// 非动画情况直接跳转
|
||||
daysScrollRef.current?.scrollTo({
|
||||
x: finalOffset,
|
||||
animated: false
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化时滚动到选中项
|
||||
useEffect(() => {
|
||||
if (scrollWidth > 0 && autoScrollToSelected) {
|
||||
scrollToIndex(selectedIndex, false);
|
||||
scrollToIndex(selectedIndex, true);
|
||||
}
|
||||
}, [scrollWidth, selectedIndex, autoScrollToSelected]);
|
||||
|
||||
// 当选中索引变化时,滚动到对应位置
|
||||
useEffect(() => {
|
||||
if (scrollWidth > 0 && autoScrollToSelected) {
|
||||
scrollToIndex(selectedIndex, true);
|
||||
// 添加微小延迟以确保动画效果更明显
|
||||
const timer = setTimeout(() => {
|
||||
scrollToIndex(selectedIndex, true);
|
||||
}, 50);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [selectedIndex, autoScrollToSelected]);
|
||||
|
||||
@@ -94,6 +113,11 @@ export const DateSelector: React.FC<DateSelectorProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// 先滚动到目标位置,再更新状态
|
||||
if (autoScrollToSelected) {
|
||||
scrollToIndex(index, true);
|
||||
}
|
||||
|
||||
// 更新内部状态(如果使用外部控制则不更新)
|
||||
if (externalSelectedIndex === undefined) {
|
||||
setInternalSelectedIndex(index);
|
||||
|
||||
@@ -14,20 +14,8 @@ export function MoodCard({ moodCheckin, onPress, isLoading = false }: MoodCardPr
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={onPress} style={styles.moodCardContent} disabled={isLoading}>
|
||||
<View style={styles.cardHeaderRow}>
|
||||
<View style={styles.moodIconContainer}>
|
||||
{moodCheckin ? (
|
||||
<Text style={styles.moodIcon}>
|
||||
{moodConfig?.emoji || '😊'}
|
||||
</Text>
|
||||
) : (
|
||||
<Text style={styles.moodIcon}>😊</Text>
|
||||
)}
|
||||
</View>
|
||||
<Text style={styles.cardTitle}>心情</Text>
|
||||
</View>
|
||||
<Text style={styles.cardTitle}>心情</Text>
|
||||
|
||||
<Text style={styles.moodSubtitle}>记录你的每日心情</Text>
|
||||
|
||||
{isLoading ? (
|
||||
<View style={styles.moodPreview}>
|
||||
@@ -53,39 +41,18 @@ const styles = StyleSheet.create({
|
||||
moodCardContent: {
|
||||
width: '100%',
|
||||
},
|
||||
cardHeaderRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: 12,
|
||||
},
|
||||
moodIconContainer: {
|
||||
width: 24,
|
||||
height: 24,
|
||||
borderRadius: 8,
|
||||
backgroundColor: '#DCFCE7',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginRight: 10,
|
||||
},
|
||||
moodIcon: {
|
||||
fontSize: 14,
|
||||
},
|
||||
|
||||
cardTitle: {
|
||||
fontSize: 14,
|
||||
fontWeight: '800',
|
||||
color: '#192126',
|
||||
},
|
||||
moodSubtitle: {
|
||||
fontSize: 12,
|
||||
color: '#6B7280',
|
||||
marginTop: 4,
|
||||
marginBottom: 8,
|
||||
},
|
||||
|
||||
moodPreview: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginTop: 4,
|
||||
marginTop: 22,
|
||||
},
|
||||
moodPreviewText: {
|
||||
fontSize: 14,
|
||||
@@ -100,12 +67,12 @@ const styles = StyleSheet.create({
|
||||
fontSize: 12,
|
||||
color: '#9CA3AF',
|
||||
fontStyle: 'italic',
|
||||
marginTop: 4,
|
||||
marginTop: 22,
|
||||
},
|
||||
moodLoadingText: {
|
||||
fontSize: 12,
|
||||
color: '#9CA3AF',
|
||||
fontStyle: 'italic',
|
||||
marginTop: 4,
|
||||
marginTop: 22,
|
||||
},
|
||||
});
|
||||
@@ -68,7 +68,6 @@ export function StressMeter({ value, updateTime, style, hrvValue }: StressMeterP
|
||||
<View style={styles.leftSection}>
|
||||
<Text style={styles.title}>压力</Text>
|
||||
</View>
|
||||
<Text style={styles.emoji}>{getStatusEmoji()}</Text>
|
||||
</View>
|
||||
|
||||
{/* 数值显示区域 */}
|
||||
@@ -83,7 +82,7 @@ export function StressMeter({ value, updateTime, style, hrvValue }: StressMeterP
|
||||
{/* 渐变背景进度条 */}
|
||||
<View style={[styles.progressBar, { width: '100%' }]}>
|
||||
<LinearGradient
|
||||
colors={['#EF4444', '#FCD34D', '#10B981']}
|
||||
colors={['#EF4444', '#FCD34D', '#10B981']}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={styles.gradientBar}
|
||||
@@ -113,8 +112,6 @@ export function StressMeter({ value, updateTime, style, hrvValue }: StressMeterP
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '#FFFFFF',
|
||||
padding: 2,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
@@ -147,19 +144,16 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
fontWeight: '700',
|
||||
fontWeight: '800',
|
||||
color: '#192126',
|
||||
},
|
||||
emoji: {
|
||||
fontSize: 16,
|
||||
},
|
||||
valueSection: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'baseline',
|
||||
marginBottom: 12,
|
||||
},
|
||||
value: {
|
||||
fontSize: 28,
|
||||
fontSize: 24,
|
||||
fontWeight: '800',
|
||||
color: '#192126',
|
||||
lineHeight: 32,
|
||||
@@ -172,7 +166,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
progressContainer: {
|
||||
height: 16,
|
||||
marginBottom: 4,
|
||||
},
|
||||
progressTrack: {
|
||||
height: 8,
|
||||
|
||||
@@ -6,7 +6,6 @@ interface HealthDataCardProps {
|
||||
title: string;
|
||||
value: string;
|
||||
unit: string;
|
||||
icon: React.ReactNode;
|
||||
style?: object;
|
||||
}
|
||||
|
||||
@@ -14,7 +13,6 @@ const HealthDataCard: React.FC<HealthDataCardProps> = ({
|
||||
title,
|
||||
value,
|
||||
unit,
|
||||
icon,
|
||||
style
|
||||
}) => {
|
||||
return (
|
||||
@@ -23,10 +21,6 @@ const HealthDataCard: React.FC<HealthDataCardProps> = ({
|
||||
exiting={FadeOut.duration(300)}
|
||||
style={[styles.card, style]}
|
||||
>
|
||||
|
||||
<View style={styles.iconContainer}>
|
||||
{icon}
|
||||
</View>
|
||||
<View style={styles.content}>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
<View style={styles.valueContainer}>
|
||||
@@ -65,9 +59,9 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
color: '#666',
|
||||
color: '#192126',
|
||||
marginBottom: 4,
|
||||
fontWeight: '600',
|
||||
fontWeight: '800',
|
||||
},
|
||||
valueContainer: {
|
||||
flexDirection: 'row',
|
||||
|
||||
@@ -23,7 +23,6 @@ const HeartRateCard: React.FC<HeartRateCardProps> = ({
|
||||
title="心率"
|
||||
value={heartRate !== null && heartRate !== undefined ? Math.round(heartRate).toString() : '--'}
|
||||
unit="bpm"
|
||||
icon={heartIcon}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -23,7 +23,6 @@ const OxygenSaturationCard: React.FC<OxygenSaturationCardProps> = ({
|
||||
title="血氧饱和度"
|
||||
value={oxygenSaturation !== null && oxygenSaturation !== undefined ? oxygenSaturation.toFixed(1) : '--'}
|
||||
unit="%"
|
||||
icon={oxygenIcon}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user