fix: 修复压力数据
This commit is contained in:
@@ -64,7 +64,7 @@ export default function ExploreScreen() {
|
|||||||
|
|
||||||
// 开发调试:设置为true来使用mock数据
|
// 开发调试:设置为true来使用mock数据
|
||||||
// 在真机测试时,可以暂时设置为true来验证组件显示逻辑
|
// 在真机测试时,可以暂时设置为true来验证组件显示逻辑
|
||||||
const useMockData = __DEV__ || false; // 改为true来启用mock数据调试
|
const useMockData = false; // 改为true来启用mock数据调试
|
||||||
|
|
||||||
const { pushIfAuthedElseLogin, isLoggedIn } = useAuthGuard();
|
const { pushIfAuthedElseLogin, isLoggedIn } = useAuthGuard();
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ export default function ExploreScreen() {
|
|||||||
const hourlySteps = useMockData ? (mockData?.hourlySteps ?? []) : (healthData?.hourlySteps ?? []);
|
const hourlySteps = useMockData ? (mockData?.hourlySteps ?? []) : (healthData?.hourlySteps ?? []);
|
||||||
const activeCalories = useMockData ? (mockData?.activeEnergyBurned ?? null) : (healthData?.activeEnergyBurned ?? null);
|
const activeCalories = useMockData ? (mockData?.activeEnergyBurned ?? null) : (healthData?.activeEnergyBurned ?? null);
|
||||||
const basalMetabolism: number | null = useMockData ? (mockData?.basalEnergyBurned ?? null) : (healthData?.basalEnergyBurned ?? null);
|
const basalMetabolism: number | null = useMockData ? (mockData?.basalEnergyBurned ?? null) : (healthData?.basalEnergyBurned ?? null);
|
||||||
const hrvValue = useMockData ? (mockData?.hrv ?? null) : (healthData?.hrv ?? null);
|
|
||||||
const oxygenSaturation = useMockData ? (mockData?.oxygenSaturation ?? null) : (healthData?.oxygenSaturation ?? null);
|
const oxygenSaturation = useMockData ? (mockData?.oxygenSaturation ?? null) : (healthData?.oxygenSaturation ?? null);
|
||||||
|
|
||||||
// 调试HRV数据
|
// 调试HRV数据
|
||||||
@@ -100,7 +100,6 @@ export default function ExploreScreen() {
|
|||||||
console.log('useMockData:', useMockData);
|
console.log('useMockData:', useMockData);
|
||||||
console.log('mockData?.hrv:', mockData?.hrv);
|
console.log('mockData?.hrv:', mockData?.hrv);
|
||||||
console.log('healthData?.hrv:', healthData?.hrv);
|
console.log('healthData?.hrv:', healthData?.hrv);
|
||||||
console.log('final hrvValue:', hrvValue);
|
|
||||||
console.log('healthData:', healthData);
|
console.log('healthData:', healthData);
|
||||||
console.log('==================');
|
console.log('==================');
|
||||||
|
|
||||||
@@ -563,9 +562,7 @@ export default function ExploreScreen() {
|
|||||||
|
|
||||||
<FloatingCard style={styles.masonryCard} delay={0}>
|
<FloatingCard style={styles.masonryCard} delay={0}>
|
||||||
<StressMeter
|
<StressMeter
|
||||||
value={hrvValue}
|
curDate={currentSelectedDate}
|
||||||
updateTime={hrvUpdateTime}
|
|
||||||
hrvValue={hrvValue}
|
|
||||||
/>
|
/>
|
||||||
</FloatingCard>
|
</FloatingCard>
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
|
import { fetchHRVForDate } from '@/utils/health';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { LinearGradient } from 'expo-linear-gradient';
|
import { LinearGradient } from 'expo-linear-gradient';
|
||||||
import React, { useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import { StressAnalysisModal } from './StressAnalysisModal';
|
import { StressAnalysisModal } from './StressAnalysisModal';
|
||||||
|
|
||||||
interface StressMeterProps {
|
interface StressMeterProps {
|
||||||
value: number | null;
|
curDate: Date
|
||||||
updateTime?: Date;
|
|
||||||
style?: any;
|
|
||||||
hrvValue: number | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StressMeter({ value, updateTime, style, hrvValue }: StressMeterProps) {
|
export function StressMeter({ curDate }: StressMeterProps) {
|
||||||
// 格式化更新时间显示
|
// 格式化更新时间显示
|
||||||
const formatUpdateTime = (date: Date): string => {
|
const formatUpdateTime = (date: Date): string => {
|
||||||
const now = dayjs();
|
const now = dayjs();
|
||||||
@@ -41,12 +39,31 @@ export function StressMeter({ value, updateTime, style, hrvValue }: StressMeterP
|
|||||||
|
|
||||||
// HRV 范围: 30-110ms,对应压力指数: 100-0
|
// HRV 范围: 30-110ms,对应压力指数: 100-0
|
||||||
// 线性映射: stressIndex = 100 - ((hrv - 30) / (110 - 30)) * 100
|
// 线性映射: stressIndex = 100 - ((hrv - 30) / (110 - 30)) * 100
|
||||||
const normalizedHrv = Math.max(30, Math.min(110, hrv));
|
const normalizedHrv = Math.max(30, Math.min(130, hrv));
|
||||||
const stressIndex = 100 - ((normalizedHrv - 30) / (110 - 30)) * 100;
|
const stressIndex = 100 - ((normalizedHrv - 30) / (130 - 30)) * 100;
|
||||||
|
|
||||||
return Math.round(stressIndex);
|
return Math.round(stressIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [hrvValue, setHrvValue] = useState(0)
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getHrvData()
|
||||||
|
}, [curDate])
|
||||||
|
|
||||||
|
const getHrvData = async () => {
|
||||||
|
try {
|
||||||
|
const data = await fetchHRVForDate(curDate)
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
setHrvValue(data)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 使用传入的 hrvValue 进行转换
|
// 使用传入的 hrvValue 进行转换
|
||||||
const stressIndex = convertHrvToStressIndex(hrvValue);
|
const stressIndex = convertHrvToStressIndex(hrvValue);
|
||||||
|
|
||||||
@@ -72,7 +89,7 @@ export function StressMeter({ value, updateTime, style, hrvValue }: StressMeterP
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.container, style]}
|
style={[styles.container]}
|
||||||
onPress={handlePress}
|
onPress={handlePress}
|
||||||
activeOpacity={0.8}
|
activeOpacity={0.8}
|
||||||
>
|
>
|
||||||
@@ -81,15 +98,15 @@ export function StressMeter({ value, updateTime, style, hrvValue }: StressMeterP
|
|||||||
<View style={styles.leftSection}>
|
<View style={styles.leftSection}>
|
||||||
<Text style={styles.title}>压力</Text>
|
<Text style={styles.title}>压力</Text>
|
||||||
</View>
|
</View>
|
||||||
{updateTime && (
|
{/* {updateTime && (
|
||||||
<Text style={styles.headerUpdateTime}>{formatUpdateTime(updateTime)}</Text>
|
<Text style={styles.headerUpdateTime}>{formatUpdateTime(updateTime)}</Text>
|
||||||
)}
|
)} */}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 数值显示区域 */}
|
{/* 数值显示区域 */}
|
||||||
<View style={styles.valueSection}>
|
<View style={styles.valueSection}>
|
||||||
<Text style={styles.value}>{stressIndex === null ? '--' : stressIndex}</Text>
|
<Text style={styles.value}>{hrvValue || '--'}</Text>
|
||||||
<Text style={styles.unit}>%</Text>
|
<Text>ms</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 进度条区域 */}
|
{/* 进度条区域 */}
|
||||||
@@ -116,7 +133,7 @@ export function StressMeter({ value, updateTime, style, hrvValue }: StressMeterP
|
|||||||
visible={showStressModal}
|
visible={showStressModal}
|
||||||
onClose={() => setShowStressModal(false)}
|
onClose={() => setShowStressModal(false)}
|
||||||
hrvValue={hrvValue}
|
hrvValue={hrvValue}
|
||||||
updateTime={updateTime || new Date()}
|
// updateTime={updateTime || new Date()}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ PODS:
|
|||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- ExpoSystemUI (6.0.7):
|
- ExpoSystemUI (6.0.7):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- ExpoUI (0.2.0-beta.0):
|
- ExpoUI (0.2.0-beta.1):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- ExpoWebBrowser (15.0.6):
|
- ExpoWebBrowser (15.0.6):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
@@ -3018,9 +3018,9 @@ PODS:
|
|||||||
- ReactCommon/turbomodule/core
|
- ReactCommon/turbomodule/core
|
||||||
- SocketRocket
|
- SocketRocket
|
||||||
- Yoga
|
- Yoga
|
||||||
- SDWebImage (5.21.2):
|
- SDWebImage (5.21.1):
|
||||||
- SDWebImage/Core (= 5.21.2)
|
- SDWebImage/Core (= 5.21.1)
|
||||||
- SDWebImage/Core (5.21.2)
|
- SDWebImage/Core (5.21.1)
|
||||||
- SDWebImageAVIFCoder (0.11.0):
|
- SDWebImageAVIFCoder (0.11.0):
|
||||||
- libavif/core (>= 0.11.0)
|
- libavif/core (>= 0.11.0)
|
||||||
- SDWebImage (~> 5.10)
|
- SDWebImage (~> 5.10)
|
||||||
@@ -3444,7 +3444,7 @@ SPEC CHECKSUMS:
|
|||||||
ExpoSplashScreen: 1665809071bd907c6fdbfd9c09583ee4d51b41d4
|
ExpoSplashScreen: 1665809071bd907c6fdbfd9c09583ee4d51b41d4
|
||||||
ExpoSymbols: 3efee6865b1955fe3805ca88b36e8674ce6970dd
|
ExpoSymbols: 3efee6865b1955fe3805ca88b36e8674ce6970dd
|
||||||
ExpoSystemUI: 6cd74248a2282adf6dec488a75fa532d69dee314
|
ExpoSystemUI: 6cd74248a2282adf6dec488a75fa532d69dee314
|
||||||
ExpoUI: b219c02a788f7cefd7976e657e97c14aea64dac1
|
ExpoUI: 1e4b3045678eb66004d78d9a6602afdcbdc06bbd
|
||||||
ExpoWebBrowser: 84d4438464d9754a4c1f1eaa97cd747f3752187e
|
ExpoWebBrowser: 84d4438464d9754a4c1f1eaa97cd747f3752187e
|
||||||
EXTaskManager: eedcd03c1a574c47d3f48d83d4e4659b3c1fa29b
|
EXTaskManager: eedcd03c1a574c47d3f48d83d4e4659b3c1fa29b
|
||||||
fast_float: b32c788ed9c6a8c584d114d0047beda9664e7cc6
|
fast_float: b32c788ed9c6a8c584d114d0047beda9664e7cc6
|
||||||
@@ -3539,7 +3539,7 @@ SPEC CHECKSUMS:
|
|||||||
RNSentry: f2c39f1113e22413c9bb6e3faa6b27f110d95eaf
|
RNSentry: f2c39f1113e22413c9bb6e3faa6b27f110d95eaf
|
||||||
RNSVG: 6f39605a4c4d200b11435c35bd077553c6b5963a
|
RNSVG: 6f39605a4c4d200b11435c35bd077553c6b5963a
|
||||||
RNWorklets: ad0606bee2a8103c14adb412149789c60b72bfb2
|
RNWorklets: ad0606bee2a8103c14adb412149789c60b72bfb2
|
||||||
SDWebImage: 9f177d83116802728e122410fb25ad88f5c7608a
|
SDWebImage: f29024626962457f3470184232766516dee8dfea
|
||||||
SDWebImageAVIFCoder: 00310d246aab3232ce77f1d8f0076f8c4b021d90
|
SDWebImageAVIFCoder: 00310d246aab3232ce77f1d8f0076f8c4b021d90
|
||||||
SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c
|
SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c
|
||||||
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
|
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
|
||||||
|
|||||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -8,7 +8,7 @@
|
|||||||
"name": "digital-pilates",
|
"name": "digital-pilates",
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/ui": "~0.2.0-beta.0",
|
"@expo/ui": "~0.2.0-beta.1",
|
||||||
"@expo/vector-icons": "^15.0.2",
|
"@expo/vector-icons": "^15.0.2",
|
||||||
"@react-native-async-storage/async-storage": "^2.2.0",
|
"@react-native-async-storage/async-storage": "^2.2.0",
|
||||||
"@react-native-community/datetimepicker": "^8.4.4",
|
"@react-native-community/datetimepicker": "^8.4.4",
|
||||||
@@ -2295,9 +2295,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@expo/ui": {
|
"node_modules/@expo/ui": {
|
||||||
"version": "0.2.0-beta.0",
|
"version": "0.2.0-beta.1",
|
||||||
"resolved": "https://registry.npmjs.org/@expo/ui/-/ui-0.2.0-beta.0.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/ui/-/ui-0.2.0-beta.1.tgz",
|
||||||
"integrity": "sha512-bxLMKBbodqLzGMC8GVPZw6FugB2LpnocgdBP5JYJ8oWojy7RkGl6fcki+97w6EJagRr/+82XAGTCzyOUFBUCoQ==",
|
"integrity": "sha512-9eEbZmpWHLbpApwWNpeAC7Xf4rzlWsBcgHzGGN1QAfKvAq2S9eJfoIqPergrnnjN8Ju8G3EAos1DCk84q3oBgw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"expo": "*",
|
"expo": "*",
|
||||||
|
|||||||
@@ -40,10 +40,6 @@ async function executeWaterReminderTask(): Promise<void> {
|
|||||||
|
|
||||||
// 检查时间限制(避免深夜打扰)
|
// 检查时间限制(避免深夜打扰)
|
||||||
const currentHour = new Date().getHours();
|
const currentHour = new Date().getHours();
|
||||||
if (currentHour < 8 || currentHour >= 21) {
|
|
||||||
console.log(`当前时间${currentHour}点,不在提醒时间范围内,跳过喝水提醒`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取用户名
|
// 获取用户名
|
||||||
const userName = userProfile?.name || '朋友';
|
const userName = userProfile?.name || '朋友';
|
||||||
@@ -152,7 +148,7 @@ async function executeBackgroundTasks(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await sendTestNotification()
|
// await sendTestNotification()
|
||||||
|
|
||||||
// 执行喝水提醒检查任务
|
// 执行喝水提醒检查任务
|
||||||
await executeWaterReminderTask();
|
await executeWaterReminderTask();
|
||||||
|
|||||||
@@ -461,27 +461,7 @@ async function fetchHeartRateVariability(options: HealthDataOptions): Promise<nu
|
|||||||
return resolve(null);
|
return resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
logSuccess('HRV', res);
|
resolve(Math.round(res[0].value * 1000))
|
||||||
console.log('HRV数据样本数量:', res.length);
|
|
||||||
|
|
||||||
// 打印最新的几个样本用于调试
|
|
||||||
const latestSamples = res.slice(-3);
|
|
||||||
console.log('最新的HRV样本:', latestSamples.map(sample => ({
|
|
||||||
value: sample.value,
|
|
||||||
startDate: sample.startDate,
|
|
||||||
endDate: sample.endDate
|
|
||||||
})));
|
|
||||||
|
|
||||||
const latestHrv = res[res.length - 1];
|
|
||||||
if (latestHrv && latestHrv.value !== undefined && latestHrv.value !== null) {
|
|
||||||
// HealthKit 中的 HRV 数据已经是毫秒单位,无需转换
|
|
||||||
const hrvValue = Math.round(latestHrv.value);
|
|
||||||
console.log('最终HRV值:', hrvValue);
|
|
||||||
resolve(hrvValue);
|
|
||||||
} else {
|
|
||||||
console.warn('HRV样本值无效:', latestHrv);
|
|
||||||
resolve(null);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -657,7 +657,7 @@ export class WaterNotificationHelpers {
|
|||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
// 检查时间限制:早上9点以前和晚上9点以后不通知
|
// 检查时间限制:早上9点以前和晚上9点以后不通知
|
||||||
if (currentHour < 9 || currentHour >= 21) {
|
if (currentHour < 9 || currentHour >= 23) {
|
||||||
console.log(`当前时间${currentHour}点,不在通知时间范围内(9:00-21:00),跳过喝水提醒`);
|
console.log(`当前时间${currentHour}点,不在通知时间范围内(9:00-21:00),跳过喝水提醒`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user