refactor(sleep): 重构睡眠数据获取逻辑,移除冗余代码并优化组件结构
- 从 healthSlice 和 health.ts 中移除 sleepDuration 字段及相关获取逻辑 - 将 SleepCard 改为按需异步获取睡眠数据,支持传入指定日期 - 睡眠详情页改为通过路由参数接收日期,支持查看历史记录 - 移除 statistics 页面对 sleepDuration 的直接依赖,统一由 SleepCard 管理 - 删除未使用的 SleepStageChart 组件,简化页面结构
This commit is contained in:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user