import { ProgressBar } from '@/components/ProgressBar'; import { Colors } from '@/constants/Colors'; import { getTabBarBottomPadding } from '@/constants/TabBar'; import { getMonthDaysZh, getMonthTitleZh, getTodayIndexInMonth } from '@/utils/date'; import { Ionicons } from '@expo/vector-icons'; import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import { SafeAreaView, ScrollView, StyleSheet, Text, TouchableOpacity, View, } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; export default function ExploreScreen() { // 使用 dayjs:当月日期与默认选中“今天” const days = getMonthDaysZh(); const [selectedIndex, setSelectedIndex] = useState(getTodayIndexInMonth()); const tabBarHeight = useBottomTabBarHeight(); const insets = useSafeAreaInsets(); const bottomPadding = useMemo(() => { return getTabBarBottomPadding(tabBarHeight) + (insets?.bottom ?? 0); }, [tabBarHeight, insets?.bottom]); const monthTitle = getMonthTitleZh(); // 日期条自动滚动到选中项 const daysScrollRef = useRef(null); const [scrollWidth, setScrollWidth] = useState(0); const DAY_PILL_WIDTH = 68; const DAY_PILL_SPACING = 12; const scrollToIndex = (index: number, animated = true) => { const baseOffset = index * (DAY_PILL_WIDTH + DAY_PILL_SPACING); const centerOffset = Math.max(0, baseOffset - (scrollWidth / 2 - DAY_PILL_WIDTH / 2)); daysScrollRef.current?.scrollTo({ x: centerOffset, animated }); }; useEffect(() => { if (scrollWidth > 0) { scrollToIndex(selectedIndex, false); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [scrollWidth]); return ( {/* 标题与日期选择 */} {monthTitle} setScrollWidth(e.nativeEvent.layout.width)} > {days.map((d, i) => { const selected = i === selectedIndex; return ( { setSelectedIndex(i); scrollToIndex(i); }} activeOpacity={0.8} > {d.weekdayZh} {d.dayOfMonth} {selected && } ); })} {/* 今日报告 标题 */} 今日报告 {/* 指标行:左大卡(训练时间),右两小卡(消耗卡路里、步数) */} 训练时间 80% 消耗卡路里 645 千卡 步数 999/2000 ); } const primary = Colors.light.primary; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#F6F7F8', }, safeArea: { flex: 1, }, scrollView: { flex: 1, paddingHorizontal: 20, }, monthTitle: { fontSize: 24, fontWeight: '800', color: '#192126', marginTop: 8, marginBottom: 14, }, daysContainer: { paddingBottom: 8, }, dayItemWrapper: { alignItems: 'center', width: 68, marginRight: 12, }, dayPill: { width: 68, height: 68, borderRadius: 18, alignItems: 'center', justifyContent: 'center', }, dayPillNormal: { backgroundColor: '#C8F852', }, dayPillSelected: { backgroundColor: '#192126', }, dayLabel: { fontSize: 16, fontWeight: '700', color: '#192126', marginBottom: 2, }, dayLabelSelected: { color: '#FFFFFF', }, dayDate: { fontSize: 16, fontWeight: '800', color: '#192126', }, dayDateSelected: { color: '#FFFFFF', }, selectedDot: { width: 8, height: 8, borderRadius: 4, backgroundColor: '#192126', marginTop: 10, marginBottom: 4, alignSelf: 'center', }, sectionTitle: { fontSize: 24, fontWeight: '800', color: '#192126', marginTop: 24, marginBottom: 14, }, metricsRow: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 16, }, card: { backgroundColor: '#0F1418', borderRadius: 22, padding: 18, marginBottom: 16, }, metricsLeft: { flex: 1, backgroundColor: '#EEE9FF', borderRadius: 22, padding: 18, marginRight: 12, }, metricsRight: { width: 160, gap: 12, }, metricsRightCard: { backgroundColor: '#FFFFFF', borderRadius: 22, padding: 16, }, caloriesCard: { backgroundColor: '#FFFFFF', }, trainingCard: { backgroundColor: '#EEE9FF', }, cardTitleSecondary: { color: '#9AA3AE', fontSize: 14, fontWeight: '600', marginBottom: 10, }, caloriesValue: { color: '#192126', fontSize: 22, fontWeight: '800', }, trainingContent: { marginTop: 8, width: 120, height: 120, borderRadius: 60, alignItems: 'center', justifyContent: 'center', alignSelf: 'center', }, trainingRingTrack: { position: 'absolute', width: '100%', height: '100%', borderRadius: 60, borderWidth: 12, borderColor: '#E2D9FD', }, trainingRingProgress: { position: 'absolute', width: '100%', height: '100%', borderRadius: 60, borderWidth: 12, borderColor: 'transparent', borderTopColor: '#8B74F3', borderRightColor: '#8B74F3', transform: [{ rotateZ: '45deg' }], }, trainingPercent: { fontSize: 18, fontWeight: '800', color: '#8B74F3', }, cyclingHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 12, }, cyclingIconBadge: { width: 30, height: 30, borderRadius: 6, backgroundColor: primary, alignItems: 'center', justifyContent: 'center', marginRight: 8, }, cyclingTitle: { color: '#FFFFFF', fontSize: 20, fontWeight: '800', }, mapArea: { backgroundColor: 'rgba(255,255,255,0.08)', borderRadius: 14, height: 180, padding: 8, flexDirection: 'row', flexWrap: 'wrap', overflow: 'hidden', }, mapTile: { width: '25%', height: '25%', borderWidth: 1, borderColor: 'rgba(255,255,255,0.12)', }, routeLine: { position: 'absolute', height: 6, backgroundColor: primary, borderRadius: 3, }, cardHeaderRow: { flexDirection: 'row', alignItems: 'center', marginBottom: 12, }, iconSquare: { width: 30, height: 30, borderRadius: 8, backgroundColor: '#FFFFFF', alignItems: 'center', justifyContent: 'center', marginRight: 10, }, cardTitle: { fontSize: 18, fontWeight: '800', color: '#192126', }, heartCard: { backgroundColor: '#FFE5E5', }, waveContainer: { flexDirection: 'row', alignItems: 'flex-end', height: 70, gap: 6, marginBottom: 8, }, waveBar: { width: 6, borderRadius: 3, backgroundColor: '#E54D4D', }, heartValue: { alignSelf: 'flex-end', color: '#5B5B5B', fontWeight: '600', }, stepsCard: { backgroundColor: '#FFE4B8', }, stepsValue: { fontSize: 16, color: '#7A6A42', fontWeight: '700', marginBottom: 8, } });