- 在 CoachScreen 中调整键盘高度计算,移除不必要的 insets.bottom - 更新 Statistics 组件,移除未使用的健康数据相关函数,简化代码 - 修改多个统计卡片,移除不必要的图标属性,提升组件简洁性 - 优化 HealthDataCard 和其他统计卡片的样式,提升视觉一致性 - 更新健康数据获取逻辑,确保数据处理更为准确 - 移除 MoodCard 中的多余元素,简化心情记录展示 - 调整 StressMeter 和其他组件的样式,提升用户体验
210 lines
5.0 KiB
TypeScript
210 lines
5.0 KiB
TypeScript
import { LinearGradient } from 'expo-linear-gradient';
|
||
import React, { useState } from 'react';
|
||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||
import { StressAnalysisModal } from './StressAnalysisModal';
|
||
|
||
interface StressMeterProps {
|
||
value: number | null;
|
||
updateTime?: Date;
|
||
style?: any;
|
||
hrvValue: number;
|
||
}
|
||
|
||
export function StressMeter({ value, updateTime, style, hrvValue }: StressMeterProps) {
|
||
// 将HRV值转换为压力指数(0-100)
|
||
// HRV值范围:30-110ms,映射到压力指数100-0
|
||
// HRV值越高,压力越小;HRV值越低,压力越大
|
||
// 根据压力指数计算状态
|
||
const getStressStatus = () => {
|
||
if (value === null) {
|
||
return '未知';
|
||
} else if (value >= 70) {
|
||
return '放松';
|
||
} else if (value >= 30) {
|
||
return '正常';
|
||
} else {
|
||
return '紧张';
|
||
}
|
||
};
|
||
|
||
// 根据状态获取表情
|
||
const getStatusEmoji = () => {
|
||
// 当HRV值为null或0时,不展示表情
|
||
if (value === null || value === 0) {
|
||
return '';
|
||
}
|
||
|
||
const status = getStressStatus();
|
||
switch (status) {
|
||
case '未知': return '';
|
||
case '放松': return '😌';
|
||
case '正常': return '😊';
|
||
case '紧张': return '😰';
|
||
default: return '😊';
|
||
}
|
||
};
|
||
|
||
// 计算进度条位置(0-100%)
|
||
// 压力指数越高,进度条越满(红色区域越多)
|
||
const progressPercentage = value !== null ? Math.max(0, Math.min(100, value)) : 0;
|
||
|
||
// 在组件内部添加状态
|
||
const [showStressModal, setShowStressModal] = useState(false);
|
||
|
||
// 修改 onPress 处理函数
|
||
const handlePress = () => {
|
||
setShowStressModal(true);
|
||
};
|
||
|
||
return (
|
||
<>
|
||
<TouchableOpacity
|
||
style={[styles.container, style]}
|
||
onPress={handlePress}
|
||
activeOpacity={0.8}
|
||
>
|
||
{/* 头部区域 */}
|
||
<View style={styles.header}>
|
||
<View style={styles.leftSection}>
|
||
<Text style={styles.title}>压力</Text>
|
||
</View>
|
||
</View>
|
||
|
||
{/* 数值显示区域 */}
|
||
<View style={styles.valueSection}>
|
||
<Text style={styles.value}>{value === null ? '--' : value}</Text>
|
||
<Text style={styles.unit}>指数</Text>
|
||
</View>
|
||
|
||
{/* 进度条区域 */}
|
||
<View style={styles.progressContainer}>
|
||
<View style={styles.progressTrack}>
|
||
{/* 渐变背景进度条 */}
|
||
<View style={[styles.progressBar, { width: '100%' }]}>
|
||
<LinearGradient
|
||
colors={['#EF4444', '#FCD34D', '#10B981']}
|
||
start={{ x: 0, y: 0 }}
|
||
end={{ x: 1, y: 0 }}
|
||
style={styles.gradientBar}
|
||
/>
|
||
</View>
|
||
{/* 白色圆形指示器 */}
|
||
<View style={[styles.indicator, { left: `${Math.max(0, Math.min(100, progressPercentage - 2))}%` }]} />
|
||
</View>
|
||
</View>
|
||
|
||
{/* 更新时间
|
||
{updateTime && (
|
||
<Text style={styles.updateTime}>{formatUpdateTime(updateTime)}</Text>
|
||
)} */}
|
||
</TouchableOpacity>
|
||
|
||
{/* 压力分析浮窗 */}
|
||
<StressAnalysisModal
|
||
visible={showStressModal}
|
||
onClose={() => setShowStressModal(false)}
|
||
hrvValue={hrvValue}
|
||
updateTime={updateTime || new Date()}
|
||
/>
|
||
</>
|
||
);
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
container: {
|
||
shadowColor: '#000',
|
||
shadowOffset: {
|
||
width: 0,
|
||
height: 2,
|
||
},
|
||
shadowOpacity: 0.08,
|
||
shadowRadius: 8,
|
||
elevation: 3,
|
||
position: 'relative',
|
||
overflow: 'hidden',
|
||
},
|
||
header: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
marginBottom: 8,
|
||
},
|
||
leftSection: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
},
|
||
iconContainer: {
|
||
width: 24,
|
||
height: 24,
|
||
borderRadius: 6,
|
||
backgroundColor: '#EBF4FF',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
marginRight: 6,
|
||
},
|
||
title: {
|
||
fontSize: 14,
|
||
fontWeight: '800',
|
||
color: '#192126',
|
||
},
|
||
valueSection: {
|
||
flexDirection: 'row',
|
||
alignItems: 'baseline',
|
||
marginBottom: 12,
|
||
},
|
||
value: {
|
||
fontSize: 24,
|
||
fontWeight: '800',
|
||
color: '#192126',
|
||
lineHeight: 32,
|
||
},
|
||
unit: {
|
||
fontSize: 12,
|
||
fontWeight: '500',
|
||
color: '#9AA3AE',
|
||
marginLeft: 4,
|
||
},
|
||
progressContainer: {
|
||
height: 16,
|
||
},
|
||
progressTrack: {
|
||
height: 8,
|
||
borderRadius: 4,
|
||
position: 'relative',
|
||
overflow: 'visible',
|
||
},
|
||
progressBar: {
|
||
height: '100%',
|
||
borderRadius: 4,
|
||
overflow: 'hidden',
|
||
},
|
||
gradientBar: {
|
||
height: '100%',
|
||
borderRadius: 4,
|
||
},
|
||
indicator: {
|
||
position: 'absolute',
|
||
top: -4,
|
||
width: 16,
|
||
height: 16,
|
||
borderRadius: 8,
|
||
backgroundColor: '#FFFFFF',
|
||
shadowColor: '#000',
|
||
shadowOffset: {
|
||
width: 0,
|
||
height: 1,
|
||
},
|
||
shadowOpacity: 0.1,
|
||
shadowRadius: 2,
|
||
elevation: 2,
|
||
borderWidth: 1.5,
|
||
borderColor: '#E5E7EB',
|
||
},
|
||
updateTime: {
|
||
fontSize: 10,
|
||
color: '#9AA3AE',
|
||
textAlign: 'right',
|
||
marginTop: 2,
|
||
},
|
||
});
|