feat: 更新睡眠详情页面,集成真实睡眠数据生成逻辑,优化睡眠阶段图表展示,添加睡眠样本数据处理功能,提升用户体验

This commit is contained in:
richarjiang
2025-09-08 19:26:02 +08:00
parent bf3304eb06
commit 1de4b9fe4c
3 changed files with 270 additions and 48 deletions

View File

@@ -57,6 +57,9 @@ export type SleepDetailData = {
// 睡眠阶段统计
sleepStages: SleepStageStats[];
// 原始睡眠样本数据(用于图表显示)
rawSleepSamples: SleepSample[];
// 心率数据
averageHeartRate: number | null; // 平均心率
sleepHeartRateData: HeartRateData[]; // 睡眠期间心率数据
@@ -96,7 +99,19 @@ async function fetchSleepSamples(date: Date): Promise<SleepSample[]> {
return;
}
// 添加详细日志,了解实际获取到的数据类型
console.log('获取到睡眠样本:', results.length);
console.log('睡眠样本详情:', results.map(r => ({
value: r.value,
start: r.startDate?.substring(11, 16),
end: r.endDate?.substring(11, 16),
duration: `${Math.round((new Date(r.endDate).getTime() - new Date(r.startDate).getTime()) / 60000)}min`
})));
// 检查可用的睡眠阶段类型
const uniqueValues = [...new Set(results.map(r => r.value))];
console.log('可用的睡眠阶段类型:', uniqueValues);
resolve(results as unknown as SleepSample[]);
});
});
@@ -274,7 +289,6 @@ export function getSleepStageColor(stage: SleepStage): string {
case SleepStage.Core:
return '#3B82F6'; // 蓝色
case SleepStage.REM:
return '#8B5CF6'; // 紫色
case SleepStage.Asleep:
return '#06B6D4'; // 青色
case SleepStage.Awake:
@@ -340,6 +354,7 @@ export async function fetchSleepDetailForDate(date: Date): Promise<SleepDetailDa
wakeupTime,
timeInBed,
sleepStages,
rawSleepSamples: sleepSamples, // 保存原始睡眠样本数据
averageHeartRate,
sleepHeartRateData,
sleepEfficiency,
@@ -373,4 +388,64 @@ export function formatSleepTime(minutes: number): string {
// 格式化时间显示 (HH:MM)
export function formatTime(dateString: string): string {
return dayjs(dateString).format('HH:mm');
}
// 将睡眠样本数据转换为15分钟间隔的睡眠阶段数据
export function convertSleepSamplesToIntervals(sleepSamples: SleepSample[], bedtime: string, wakeupTime: string): { time: string; stage: SleepStage }[] {
const data: { time: string; stage: SleepStage }[] = [];
if (sleepSamples.length === 0) {
console.log('没有睡眠样本数据可用于图表显示');
return [];
}
// 过滤掉InBed阶段只保留实际睡眠阶段
const sleepOnlySamples = sleepSamples.filter(sample =>
sample.value !== SleepStage.InBed
);
if (sleepOnlySamples.length === 0) {
console.log('只有InBed数据没有详细睡眠阶段数据');
return [];
}
console.log('处理睡眠阶段数据 - 样本数量:', sleepOnlySamples.length);
console.log('时间范围:', formatTime(bedtime), '-', formatTime(wakeupTime));
const startTime = dayjs(bedtime);
const endTime = dayjs(wakeupTime);
let currentTime = startTime.clone();
// 创建一个映射,用于快速查找每个时间点的睡眠阶段
while (currentTime.isBefore(endTime)) {
const currentTimestamp = currentTime.toDate().getTime();
// 找到当前时间点对应的睡眠阶段
let currentStage = SleepStage.Awake; // 默认为清醒
for (const sample of sleepOnlySamples) {
const sampleStart = new Date(sample.startDate).getTime();
const sampleEnd = new Date(sample.endDate).getTime();
// 如果当前时间在这个样本的时间范围内
if (currentTimestamp >= sampleStart && currentTimestamp < sampleEnd) {
currentStage = sample.value;
break;
}
}
const timeStr = currentTime.format('HH:mm');
data.push({ time: timeStr, stage: currentStage });
// 移动到下一个15分钟间隔
currentTime = currentTime.add(15, 'minute');
}
console.log('生成的睡眠阶段间隔数据点数量:', data.length);
console.log('阶段分布:', data.reduce((acc, curr) => {
acc[curr.stage] = (acc[curr.stage] || 0) + 1;
return acc;
}, {} as Record<string, number>));
return data;
}