Files
digital-pilates/utils/health.ts
richarjiang 1c44c3083b feat: 优化统计页面和BMI卡片,移除压力分析相关代码
- 在统计页面中移除压力分析模态框及相关状态管理,简化组件逻辑
- 更新BMI卡片,改进弹窗展示方式,增加渐变背景和健康建议
- 新增更新体重的功能,支持将体重数据写入健康数据中
- 优化压力计组件,调整数据展示逻辑,提升用户体验
2025-08-20 17:25:42 +08:00

215 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import dayjs from 'dayjs';
import type { HealthKitPermissions } from 'react-native-health';
import AppleHealthKit from 'react-native-health';
const PERMISSIONS: HealthKitPermissions = {
permissions: {
read: [
AppleHealthKit.Constants.Permissions.StepCount,
AppleHealthKit.Constants.Permissions.ActiveEnergyBurned,
AppleHealthKit.Constants.Permissions.SleepAnalysis,
AppleHealthKit.Constants.Permissions.HeartRateVariability,
],
write: [
// 支持体重写入
AppleHealthKit.Constants.Permissions.Weight,
],
},
};
export type TodayHealthData = {
steps: number;
activeEnergyBurned: number; // kilocalories
sleepDuration: number; // 睡眠时长(分钟)
hrv: number | null; // 心率变异性 (ms)
};
export async function ensureHealthPermissions(): Promise<boolean> {
return new Promise((resolve) => {
console.log('开始初始化HealthKit...');
AppleHealthKit.initHealthKit(PERMISSIONS, (error) => {
if (error) {
console.error('HealthKit初始化失败:', error);
// 常见错误处理
if (typeof error === 'string') {
if (error.includes('not available')) {
console.warn('HealthKit不可用 - 可能在模拟器上运行或非iOS设备');
}
}
resolve(false);
return;
}
console.log('HealthKit初始化成功');
resolve(true);
});
});
}
export async function fetchHealthDataForDate(date: Date): Promise<TodayHealthData> {
console.log('开始获取指定日期健康数据...', date);
const start = dayjs(date).startOf('day').toDate();
const end = dayjs(date).endOf('day').toDate();
const options = {
startDate: start.toISOString(),
endDate: end.toISOString()
} as any;
console.log('查询选项:', options);
// 并行获取所有健康数据
const [steps, calories, sleepDuration, hrv] = await Promise.all([
// 获取步数
new Promise<number>((resolve) => {
AppleHealthKit.getStepCount(options, (err, res) => {
if (err) {
console.error('获取步数失败:', err);
return resolve(0);
}
if (!res) {
console.warn('步数数据为空');
return resolve(0);
}
console.log('步数数据:', res);
resolve(res.value || 0);
});
}),
// 获取消耗卡路里
new Promise<number>((resolve) => {
AppleHealthKit.getActiveEnergyBurned(options, (err, res) => {
if (err) {
console.error('获取消耗卡路里失败:', err);
return resolve(0);
}
if (!res || !Array.isArray(res) || res.length === 0) {
console.warn('卡路里数据为空或格式错误');
return resolve(0);
}
console.log('卡路里数据:', res);
// 求和该日内的所有记录(单位:千卡)
const total = res.reduce((acc: number, item: any) => acc + (item?.value || 0), 0);
resolve(total);
});
}),
// 获取睡眠时长
new Promise<number>((resolve) => {
AppleHealthKit.getSleepSamples(options, (err, res) => {
if (err) {
console.error('获取睡眠数据失败:', err);
return resolve(0);
}
if (!res || !Array.isArray(res) || res.length === 0) {
console.warn('睡眠数据为空或格式错误');
return resolve(0);
}
console.log('睡眠数据:', res);
// 计算总睡眠时间(单位:分钟)
let totalSleepDuration = 0;
res.forEach((sample: any) => {
if (sample && sample.startDate && sample.endDate) {
const startTime = new Date(sample.startDate).getTime();
const endTime = new Date(sample.endDate).getTime();
const durationMinutes = (endTime - startTime) / (1000 * 60);
totalSleepDuration += durationMinutes;
}
});
resolve(totalSleepDuration);
});
}),
// 获取HRV数据
new Promise<number | null>((resolve) => {
AppleHealthKit.getHeartRateVariabilitySamples(options, (err, res) => {
if (err) {
console.error('获取HRV数据失败:', err);
return resolve(null);
}
if (!res || !Array.isArray(res) || res.length === 0) {
console.warn('HRV数据为空或格式错误');
return resolve(null);
}
console.log('HRV数据:', res);
// 获取最新的HRV值
const latestHrv = res[res.length - 1];
if (latestHrv && latestHrv.value) {
resolve(latestHrv.value);
} else {
resolve(null);
}
});
})
]);
console.log('指定日期健康数据获取完成:', { steps, calories, sleepDuration, hrv });
return { steps, activeEnergyBurned: calories, sleepDuration, hrv };
}
export async function fetchTodayHealthData(): Promise<TodayHealthData> {
return fetchHealthDataForDate(new Date());
}
// 新增专门获取HRV数据的函数
export async function fetchHRVForDate(date: Date): Promise<number | null> {
console.log('开始获取指定日期HRV数据...', date);
const start = new Date(date);
start.setHours(0, 0, 0, 0);
const end = new Date(date);
end.setHours(23, 59, 59, 999);
const options = {
startDate: start.toISOString(),
endDate: end.toISOString()
} as any;
return new Promise<number | null>((resolve) => {
AppleHealthKit.getHeartRateVariabilitySamples(options, (err, res) => {
if (err) {
console.error('获取HRV数据失败:', err);
return resolve(null);
}
if (!res || !Array.isArray(res) || res.length === 0) {
console.warn('HRV数据为空或格式错误');
return resolve(null);
}
console.log('HRV数据:', res);
// 获取最新的HRV值
const latestHrv = res[res.length - 1];
if (latestHrv && latestHrv.value) {
resolve(latestHrv.value);
} else {
resolve(null);
}
});
});
}
// 新增获取今日HRV数据
export async function fetchTodayHRV(): Promise<number | null> {
return fetchHRVForDate(new Date());
}
// 更新healthkit中的体重
export async function updateWeight(weight: number) {
return new Promise((resolve) => {
AppleHealthKit.saveWeight({
value: weight,
}, (err, res) => {
if (err) {
console.error('更新体重失败:', err);
return resolve(false);
}
console.log('体重更新成功:', res);
resolve(true);
});
});
}