From 1d45d4d6293afa52ef4013f68fd593b43e208e8b Mon Sep 17 00:00:00 2001 From: richarjiang Date: Tue, 12 Aug 2025 14:33:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=81=A5=E5=BA=B7?= =?UTF-8?q?=E5=92=A8=E8=AF=A2=E9=A1=B5=E9=9D=A2=E5=8F=8A=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在应用中新增健康咨询页面,展示用户健康数据和建议 - 集成 expo-linear-gradient 以实现页面背景渐变效果 - 更新 package.json 和 package-lock.json,添加 expo-linear-gradient 依赖 - 在首页中添加健康咨询的导航链接 - 修改布局以支持新页面的显示和交互 --- app/(tabs)/index.tsx | 2 + app/health-consultation.tsx | 481 ++++++++++++++++++++++++++++++++++++ ios/Podfile.lock | 6 + package-lock.json | 12 + package.json | 1 + 5 files changed, 502 insertions(+) create mode 100644 app/health-consultation.tsx diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 196e214..5b2c1d7 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -56,6 +56,8 @@ export default function HomeScreen() { onPress={() => { if (workout.title === 'AI体态评估') { router.push('/ai-posture-assessment'); + } else if (workout.title === '认证教练') { + router.push('/health-consultation' as any); } else { console.log(`Pressed ${workout.title}`); } diff --git a/app/health-consultation.tsx b/app/health-consultation.tsx new file mode 100644 index 0000000..3b974a3 --- /dev/null +++ b/app/health-consultation.tsx @@ -0,0 +1,481 @@ +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 ( + + + + {/* 顶部导航栏 */} + + router.back()} style={styles.backButton}> + + + 健康咨询 + + + + + + {/* 教练问候卡片 */} + + + + + 👩‍⚕️ + + + {greeting}, + 我是您的普拉提教练 Sarah + + + 今天感觉怎么样? + 让我们一起了解您的身体状况 + + + + {/* 快速操作按钮 */} + + + + 体态检测 + + + + 咨询教练 + + + + 预约课程 + + + + {/* 健康状况标题 */} + + 您的健康状况 + + 需要关注 + + + + {/* 健康数据网格 */} + + {healthData.map((item) => ( + handleHealthItemPress(item)} + > + + {item.icon} + {item.value && ( + + {item.value} + + )} + + {item.title} + {item.subtitle} + + + + {item.recommendation} + + + + + ))} + + + {/* 今日建议 */} + + 今日建议 + + + 💡 + + + + 根据您的身体状况,建议今天进行轻度核心训练 + + + 配合呼吸练习,效果更佳 + + + 开始训练 + + + + + + + {/* 底部间距 */} + + + + + ); +} + +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, + }, +}); \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index deabe58..92e8bb2 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -51,6 +51,8 @@ PODS: - SDWebImageWebPCoder (~> 0.14.6) - ExpoKeepAwake (14.1.4): - ExpoModulesCore + - ExpoLinearGradient (14.1.5): + - ExpoModulesCore - ExpoLinking (7.1.7): - ExpoModulesCore - ExpoModulesCore (2.5.0): @@ -2107,6 +2109,7 @@ DEPENDENCIES: - ExpoHead (from `../node_modules/expo-router/ios`) - ExpoImage (from `../node_modules/expo-image/ios`) - ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`) + - ExpoLinearGradient (from `../node_modules/expo-linear-gradient/ios`) - ExpoLinking (from `../node_modules/expo-linking/ios`) - ExpoModulesCore (from `../node_modules/expo-modules-core`) - ExpoSplashScreen (from `../node_modules/expo-splash-screen/ios`) @@ -2227,6 +2230,8 @@ EXTERNAL SOURCES: :path: "../node_modules/expo-image/ios" ExpoKeepAwake: :path: "../node_modules/expo-keep-awake/ios" + ExpoLinearGradient: + :path: "../node_modules/expo-linear-gradient/ios" ExpoLinking: :path: "../node_modules/expo-linking/ios" ExpoModulesCore: @@ -2404,6 +2409,7 @@ SPEC CHECKSUMS: ExpoHead: a7b66cbaeeb51f4a85338d335a0f5467e29a2c90 ExpoImage: e4102c93d1dbe99ff54b075452d1bc9d6ec21b7c ExpoKeepAwake: bf0811570c8da182bfb879169437d4de298376e7 + ExpoLinearGradient: 7734c8059972fcf691fb4330bcdf3390960a152d ExpoLinking: d5c183998ca6ada66ff45e407e0f965b398a8902 ExpoModulesCore: 00a1b5c73248465bd0b93f59f8538c4573dac579 ExpoSplashScreen: 0ad5acac1b5d2953c6e00d4319f16d616f70d4dd diff --git a/package-lock.json b/package-lock.json index f703d0a..656fd1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "expo-font": "~13.3.2", "expo-haptics": "~14.1.4", "expo-image": "~2.4.0", + "expo-linear-gradient": "^14.1.5", "expo-linking": "~7.1.7", "expo-router": "~5.1.4", "expo-splash-screen": "~0.30.10", @@ -6310,6 +6311,17 @@ "react": "*" } }, + "node_modules/expo-linear-gradient": { + "version": "14.1.5", + "resolved": "https://mirrors.tencent.com/npm/expo-linear-gradient/-/expo-linear-gradient-14.1.5.tgz", + "integrity": "sha512-BSN3MkSGLZoHMduEnAgfhoj3xqcDWaoICgIr4cIYEx1GcHfKMhzA/O4mpZJ/WC27BP1rnAqoKfbclk1eA70ndQ==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, "node_modules/expo-linking": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-7.1.7.tgz", diff --git a/package.json b/package.json index dd1d860..a7bbc1f 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "expo-font": "~13.3.2", "expo-haptics": "~14.1.4", "expo-image": "~2.4.0", + "expo-linear-gradient": "^14.1.5", "expo-linking": "~7.1.7", "expo-router": "~5.1.4", "expo-splash-screen": "~0.30.10",