Files
digital-pilates/app/health-consultation.tsx
richarjiang 1d45d4d629 feat: 添加健康咨询页面及相关功能
- 在应用中新增健康咨询页面,展示用户健康数据和建议
- 集成 expo-linear-gradient 以实现页面背景渐变效果
- 更新 package.json 和 package-lock.json,添加 expo-linear-gradient 依赖
- 在首页中添加健康咨询的导航链接
- 修改布局以支持新页面的显示和交互
2025-08-12 14:33:32 +08:00

481 lines
12 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
import { useThemeColor } from '@/hooks/useThemeColor';
import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import { useRouter } from 'expo-router';
import React, { useEffect, useState } from 'react';
import {
Dimensions,
Pressable,
SafeAreaView,
ScrollView,
StyleSheet,
Text,
View
} from 'react-native';
const { width: screenWidth } = Dimensions.get('window');
// 健康数据项类型
interface HealthItem {
id: string;
title: string;
subtitle: string;
status: 'warning' | 'good' | 'info';
icon: string;
recommendation: string;
value?: string;
color: string;
bgColor: string;
}
// 健康数据
const healthData: HealthItem[] = [
{
id: '1',
title: '运动状态',
subtitle: '本周运动不足',
status: 'warning',
icon: '🏃‍♀️',
recommendation: '建议每天进行30分钟普拉提训练',
value: '2天/周',
color: '#FF6B6B',
bgColor: '#FFE5E5',
},
{
id: '2',
title: '体态评估',
subtitle: '需要进行评估',
status: 'info',
icon: '🧘‍♀️',
recommendation: '进行AI体态评估了解身体状况',
color: '#4ECDC4',
bgColor: '#E5F9F7',
},
{
id: '3',
title: '核心力量',
subtitle: '待加强',
status: 'warning',
icon: '💪',
recommendation: '推荐核心训练课程',
value: '初级',
color: '#FFB84D',
bgColor: '#FFF4E5',
},
{
id: '4',
title: '柔韧性',
subtitle: '良好',
status: 'good',
icon: '🤸‍♀️',
recommendation: '保持每日拉伸习惯',
value: '良好',
color: '#95E1D3',
bgColor: '#E5F9F5',
},
{
id: '5',
title: '平衡能力',
subtitle: '需要提升',
status: 'info',
icon: '⚖️',
recommendation: '尝试单腿站立训练',
color: '#A8E6CF',
bgColor: '#E8F8F0',
},
{
id: '6',
title: '呼吸质量',
subtitle: '待改善',
status: 'warning',
icon: '🌬️',
recommendation: '学习普拉提呼吸法',
color: '#C7CEEA',
bgColor: '#F0F1F8',
},
];
export default function HealthConsultationScreen() {
const router = useRouter();
const primaryColor = useThemeColor({}, 'primary');
const backgroundColor = useThemeColor({}, 'background');
const textColor = useThemeColor({}, 'text');
const [greeting, setGreeting] = useState('');
useEffect(() => {
const hour = new Date().getHours();
if (hour < 12) {
setGreeting('早上好');
} else if (hour < 18) {
setGreeting('下午好');
} else {
setGreeting('晚上好');
}
}, []);
const handleHealthItemPress = (item: HealthItem) => {
// 根据不同的健康项导航到相应页面
if (item.title === '体态评估') {
router.push('/ai-posture-assessment');
} else {
console.log(`点击了 ${item.title}`);
// 可以添加更多导航逻辑
}
};
return (
<SafeAreaView style={[styles.safeArea, { backgroundColor }]}>
<ThemedView style={styles.container}>
<ScrollView showsVerticalScrollIndicator={false}>
{/* 顶部导航栏 */}
<View style={styles.header}>
<Pressable onPress={() => router.back()} style={styles.backButton}>
<Ionicons name="chevron-back" size={24} color={textColor} />
</Pressable>
<ThemedText style={styles.headerTitle}></ThemedText>
<Pressable style={styles.notificationButton}>
<Ionicons name="notifications-outline" size={24} color={textColor} />
</Pressable>
</View>
{/* 教练问候卡片 */}
<LinearGradient
colors={[primaryColor, '#A8E063']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.coachCard}
>
<View style={styles.coachContent}>
<View style={styles.coachInfo}>
<View style={styles.coachAvatar}>
<Text style={styles.coachAvatarEmoji}>👩</Text>
</View>
<View style={styles.coachTextContainer}>
<Text style={styles.coachGreeting}>{greeting}</Text>
<Text style={styles.coachName}> Sarah</Text>
</View>
</View>
<Text style={styles.coachQuestion}></Text>
<Text style={styles.coachSubtext}></Text>
</View>
</LinearGradient>
{/* 快速操作按钮 */}
<View style={styles.quickActions}>
<Pressable style={[styles.actionButton, { backgroundColor: primaryColor }]}>
<Ionicons name="body-outline" size={20} color="#192126" />
<Text style={styles.actionButtonText}></Text>
</Pressable>
<Pressable style={[styles.actionButton, styles.actionButtonOutline]}>
<Ionicons name="chatbubble-outline" size={20} color={textColor} />
<Text style={[styles.actionButtonText, { color: textColor }]}></Text>
</Pressable>
<Pressable style={[styles.actionButton, styles.actionButtonOutline]}>
<Ionicons name="calendar-outline" size={20} color={textColor} />
<Text style={[styles.actionButtonText, { color: textColor }]}></Text>
</Pressable>
</View>
{/* 健康状况标题 */}
<View style={styles.sectionHeader}>
<ThemedText style={styles.sectionTitle}></ThemedText>
<View style={[styles.healthBadge, { backgroundColor: primaryColor }]}>
<Text style={styles.healthBadgeText}></Text>
</View>
</View>
{/* 健康数据网格 */}
<View style={styles.healthGrid}>
{healthData.map((item) => (
<Pressable
key={item.id}
style={[styles.healthCard, { backgroundColor: item.bgColor }]}
onPress={() => handleHealthItemPress(item)}
>
<View style={styles.healthCardHeader}>
<Text style={styles.healthIcon}>{item.icon}</Text>
{item.value && (
<Text style={[styles.healthValue, { color: item.color }]}>
{item.value}
</Text>
)}
</View>
<Text style={styles.healthTitle}>{item.title}</Text>
<Text style={styles.healthSubtitle}>{item.subtitle}</Text>
<View style={styles.healthRecommendation}>
<Ionicons name="bulb-outline" size={12} color={item.color} />
<Text style={[styles.recommendationText, { color: item.color }]} numberOfLines={2}>
{item.recommendation}
</Text>
</View>
<Ionicons
name="arrow-forward-circle"
size={20}
color={item.color}
style={styles.cardArrow}
/>
</Pressable>
))}
</View>
{/* 今日建议 */}
<View style={styles.suggestionSection}>
<ThemedText style={styles.suggestionTitle}></ThemedText>
<View style={[styles.suggestionCard, { backgroundColor: '#F0F8FF' }]}>
<View style={styles.suggestionIcon}>
<Text style={{ fontSize: 24 }}>💡</Text>
</View>
<View style={styles.suggestionContent}>
<Text style={styles.suggestionMainText}>
</Text>
<Text style={styles.suggestionSubText}>
</Text>
<Pressable style={[styles.startButton, { backgroundColor: primaryColor }]}>
<Text style={styles.startButtonText}></Text>
<Ionicons name="arrow-forward" size={16} color="#192126" />
</Pressable>
</View>
</View>
</View>
{/* 底部间距 */}
<View style={styles.bottomSpacing} />
</ScrollView>
</ThemedView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safeArea: {
flex: 1,
},
container: {
flex: 1,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 20,
paddingVertical: 12,
},
backButton: {
padding: 8,
},
headerTitle: {
fontSize: 18,
fontWeight: '600',
},
notificationButton: {
padding: 8,
},
coachCard: {
marginHorizontal: 20,
marginTop: 12,
borderRadius: 20,
padding: 24,
elevation: 5,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
},
coachContent: {
gap: 16,
},
coachInfo: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
},
coachAvatar: {
width: 50,
height: 50,
borderRadius: 25,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
coachAvatarEmoji: {
fontSize: 30,
},
coachTextContainer: {
flex: 1,
},
coachGreeting: {
fontSize: 14,
color: '#192126',
opacity: 0.8,
},
coachName: {
fontSize: 16,
fontWeight: '600',
color: '#192126',
},
coachQuestion: {
fontSize: 28,
fontWeight: 'bold',
color: '#192126',
marginTop: 8,
},
coachSubtext: {
fontSize: 14,
color: '#192126',
opacity: 0.7,
},
quickActions: {
flexDirection: 'row',
paddingHorizontal: 20,
marginTop: 20,
gap: 12,
},
actionButton: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 12,
borderRadius: 12,
gap: 6,
},
actionButtonOutline: {
borderWidth: 1,
borderColor: '#E0E0E0',
},
actionButtonText: {
fontSize: 14,
fontWeight: '500',
},
sectionHeader: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 20,
marginTop: 32,
marginBottom: 16,
},
sectionTitle: {
fontSize: 22,
fontWeight: 'bold',
},
healthBadge: {
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 12,
},
healthBadgeText: {
fontSize: 12,
fontWeight: '600',
color: '#192126',
},
healthGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
paddingHorizontal: 16,
gap: 12,
},
healthCard: {
width: (screenWidth - 44) / 2,
padding: 16,
borderRadius: 16,
position: 'relative',
},
healthCardHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
},
healthIcon: {
fontSize: 28,
},
healthValue: {
fontSize: 12,
fontWeight: '600',
},
healthTitle: {
fontSize: 16,
fontWeight: '600',
color: '#192126',
marginBottom: 4,
},
healthSubtitle: {
fontSize: 13,
color: '#666',
marginBottom: 12,
},
healthRecommendation: {
flexDirection: 'row',
alignItems: 'flex-start',
gap: 6,
paddingRight: 20,
},
recommendationText: {
fontSize: 11,
flex: 1,
},
cardArrow: {
position: 'absolute',
bottom: 12,
right: 12,
},
suggestionSection: {
paddingHorizontal: 20,
marginTop: 32,
},
suggestionTitle: {
fontSize: 22,
fontWeight: 'bold',
marginBottom: 16,
},
suggestionCard: {
flexDirection: 'row',
padding: 20,
borderRadius: 16,
gap: 16,
},
suggestionIcon: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
suggestionContent: {
flex: 1,
gap: 8,
},
suggestionMainText: {
fontSize: 15,
fontWeight: '500',
color: '#192126',
},
suggestionSubText: {
fontSize: 13,
color: '#666',
},
startButton: {
flexDirection: 'row',
alignItems: 'center',
alignSelf: 'flex-start',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
marginTop: 8,
gap: 6,
},
startButtonText: {
fontSize: 14,
fontWeight: '600',
color: '#192126',
},
bottomSpacing: {
height: 100,
},
});