refactor(sleep): 重构睡眠数据获取逻辑,移除冗余代码并优化组件结构

- 从 healthSlice 和 health.ts 中移除 sleepDuration 字段及相关获取逻辑
- 将 SleepCard 改为按需异步获取睡眠数据,支持传入指定日期
- 睡眠详情页改为通过路由参数接收日期,支持查看历史记录
- 移除 statistics 页面对 sleepDuration 的直接依赖,统一由 SleepCard 管理
- 删除未使用的 SleepStageChart 组件,简化页面结构
This commit is contained in:
richarjiang
2025-09-11 09:08:51 +08:00
parent aee87e8900
commit 62690ee3fc
5 changed files with 43 additions and 181 deletions

View File

@@ -9,7 +9,7 @@ import {
import { Ionicons } from '@expo/vector-icons';
import dayjs from 'dayjs';
import { LinearGradient } from 'expo-linear-gradient';
import { router } from 'expo-router';
import { router, useLocalSearchParams } from 'expo-router';
import React, { useCallback, useEffect, useState } from 'react';
import {
ActivityIndicator,
@@ -27,113 +27,6 @@ import { HeaderBar } from '@/components/ui/HeaderBar';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
// 简化的睡眠阶段图表组件
const SleepStageChart = ({
sleepData,
onInfoPress
}: {
sleepData: SleepDetailData;
onInfoPress: () => void;
}) => {
const theme = (useColorScheme() ?? 'light') as 'light' | 'dark';
const colorTokens = Colors[theme];
// 使用真实的睡眠阶段数据,如果没有则使用默认数据
const stages = sleepData.sleepStages.length > 0
? sleepData.sleepStages
.filter(stage => stage.percentage > 0) // 只显示有数据的阶段
.map(stage => ({
stage: stage.stage,
percentage: stage.percentage,
duration: stage.duration
}))
: [
{ stage: SleepStage.Awake, percentage: 1, duration: 3 },
{ stage: SleepStage.REM, percentage: 20, duration: 89 },
{ stage: SleepStage.Core, percentage: 67, duration: 295 },
{ stage: SleepStage.Deep, percentage: 12, duration: 51 }
];
return (
<View style={styles.simplifiedChartContainer}>
<View style={styles.chartTitleContainer}>
<Text style={styles.chartTitle}></Text>
<TouchableOpacity
style={styles.chartInfoButton}
onPress={onInfoPress}
>
<Ionicons name="help-circle-outline" size={20} color="#6B7280" />
</TouchableOpacity>
</View>
{/* 入睡时间和起床时间显示 */}
<View style={styles.sleepTimeLabels}>
<View style={styles.sleepTimeLabel}>
<Text style={[styles.sleepTimeText, { color: colorTokens.textSecondary }]}>
</Text>
<Text style={[styles.sleepTimeValue, { color: colorTokens.text }]}>
{sleepData.bedtime ? formatTime(sleepData.bedtime) : '--:--'}
</Text>
</View>
<View style={styles.sleepTimeLabel}>
<Text style={[styles.sleepTimeText, { color: colorTokens.textSecondary }]}>
</Text>
<Text style={[styles.sleepTimeValue, { color: colorTokens.text }]}>
{sleepData.wakeupTime ? formatTime(sleepData.wakeupTime) : '--:--'}
</Text>
</View>
</View>
{/* 简化的睡眠阶段条 */}
<View style={styles.simplifiedChartBar}>
{stages.map((stageData, index) => {
const color = getSleepStageColor(stageData.stage);
// 确保最小宽度,避免清醒阶段等小比例的阶段完全不可见
const flexValue = Math.max(stageData.percentage || 1, 3);
return (
<View
key={index}
style={[
styles.stageSegment,
{
backgroundColor: color,
flex: flexValue,
}
]}
/>
);
})}
</View>
{/* 图例 */}
<View style={styles.chartLegend}>
<View style={styles.legendRow}>
<View style={styles.legendItem}>
<View style={[styles.legendDot, { backgroundColor: getSleepStageColor(SleepStage.Awake) }]} />
<Text style={styles.legendText}></Text>
</View>
<View style={styles.legendItem}>
<View style={[styles.legendDot, { backgroundColor: getSleepStageColor(SleepStage.REM) }]} />
<Text style={styles.legendText}></Text>
</View>
<View style={styles.legendItem}>
<View style={[styles.legendDot, { backgroundColor: getSleepStageColor(SleepStage.Core) }]} />
<Text style={styles.legendText}></Text>
</View>
<View style={styles.legendItem}>
<View style={[styles.legendDot, { backgroundColor: getSleepStageColor(SleepStage.Deep) }]} />
<Text style={styles.legendText}></Text>
</View>
</View>
</View>
</View>
);
};
// SleepGradeCard 组件现在在 InfoModal 组件内部
// SleepStagesInfoModal 组件现在从独立文件导入
@@ -145,7 +38,15 @@ export default function SleepDetailScreen() {
const colorTokens = Colors[theme];
const [sleepData, setSleepData] = useState<CompleteSleepData | null>(null);
const [loading, setLoading] = useState(true);
const [selectedDate] = useState(dayjs().toDate());
// 从导航参数获取日期,如果没有则使用今天
const { date: dateParam } = useLocalSearchParams<{ date?: string }>();
const [selectedDate] = useState(() => {
if (dateParam) {
return dayjs(dateParam).toDate();
}
return dayjs().toDate();
});
const [infoModal, setInfoModal] = useState<{ visible: boolean; title: string; type: 'sleep-time' | 'sleep-quality' | null }>({
visible: false,
@@ -220,7 +121,7 @@ export default function SleepDetailScreen() {
{/* 顶部导航 */}
<HeaderBar
title={`今天, ${dayjs(selectedDate).format('M月DD日')}`}
title={`${dayjs(selectedDate).isSame(dayjs(), 'day') ? '今天' : dayjs(selectedDate).format('M月DD日')}`}
onBack={() => router.back()}
withSafeTop={true}
transparent={true}