diff --git a/app/(tabs)/statistics.tsx b/app/(tabs)/statistics.tsx
index 18b0a42..fd4f424 100644
--- a/app/(tabs)/statistics.tsx
+++ b/app/(tabs)/statistics.tsx
@@ -3,7 +3,6 @@ import { DateSelector } from '@/components/DateSelector';
import { FitnessRingsCard } from '@/components/FitnessRingsCard';
import { MoodCard } from '@/components/MoodCard';
import { NutritionRadarCard } from '@/components/NutritionRadarCard';
-import HeartRateCard from '@/components/statistic/HeartRateCard';
import OxygenSaturationCard from '@/components/statistic/OxygenSaturationCard';
import StepsCard from '@/components/StepsCard';
import { StressMeter } from '@/components/StressMeter';
@@ -655,13 +654,13 @@ export default function ExploreScreen() {
{/* 心率卡片 */}
-
+ {/*
-
+ */}
diff --git a/app/profile/edit.tsx b/app/profile/edit.tsx
index 88fdd15..1d22c7f 100644
--- a/app/profile/edit.tsx
+++ b/app/profile/edit.tsx
@@ -4,6 +4,7 @@ import { useAppDispatch, useAppSelector } from '@/hooks/redux';
import { useColorScheme } from '@/hooks/useColorScheme';
import { useCosUpload } from '@/hooks/useCosUpload';
import { fetchMyProfile, updateUserProfile } from '@/store/userSlice';
+import { fetchMaximumHeartRate } from '@/utils/health';
import { Ionicons } from '@expo/vector-icons';
import AsyncStorage from '@react-native-async-storage/async-storage';
import DateTimePicker from '@react-native-community/datetimepicker';
@@ -41,6 +42,7 @@ interface UserProfile {
avatarUri?: string | null;
avatarBase64?: string | null; // 兼容旧逻辑(不再上报)
activityLevel?: number; // 活动水平 1-4
+ maxHeartRate?: number; // 最大心率
}
const STORAGE_KEY = '@user_profile';
@@ -68,6 +70,7 @@ export default function EditProfileScreen() {
height: undefined,
avatarUri: null,
activityLevel: undefined,
+ maxHeartRate: undefined,
});
// 出生日期选择器
@@ -93,6 +96,7 @@ export default function EditProfileScreen() {
height: undefined,
avatarUri: null,
activityLevel: undefined,
+ maxHeartRate: undefined,
};
if (fromOnboarding) {
try {
@@ -122,6 +126,29 @@ export default function EditProfileScreen() {
loadLocalProfile();
}, []);
+ // 获取最大心率数据
+ useEffect(() => {
+ const loadMaximumHeartRate = async () => {
+ try {
+ const today = new Date();
+ const startDate = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000); // 过去7天
+
+ const maxHeartRate = await fetchMaximumHeartRate({
+ startDate: startDate.toISOString(),
+ endDate: today.toISOString(),
+ });
+
+ if (maxHeartRate !== null) {
+ setProfile(prev => ({ ...prev, maxHeartRate }));
+ }
+ } catch (error) {
+ console.warn('获取最大心率失败', error);
+ }
+ };
+
+ loadMaximumHeartRate();
+ }, []);
+
// 页面聚焦时拉取最新用户信息,并刷新本地 UI
useFocusEffect(
React.useCallback(() => {
@@ -152,6 +179,8 @@ export default function EditProfileScreen() {
weight: accountProfile?.weight ?? prev.weight ?? undefined,
height: accountProfile?.height ?? prev.height ?? undefined,
activityLevel: accountProfile?.activityLevel ?? prev.activityLevel ?? undefined,
+ // maxHeartRate 不从后端获取,保持本地状态
+ maxHeartRate: prev.maxHeartRate,
}));
}, [accountProfile]);
@@ -366,6 +395,20 @@ export default function EditProfileScreen() {
openDatePicker();
}}
/>
+
+ {/* 最大心率 */}
+ {
+ // 最大心率不可编辑,只显示
+ Alert.alert('提示', '最大心率数据从健康应用自动获取');
+ }}
+ disabled={true}
+ hideArrow={true}
+ />
{/* 编辑弹窗 */}
@@ -460,16 +503,20 @@ export default function EditProfileScreen() {
);
}
-function ProfileCard({ icon, iconUri, iconColor, title, value, onPress }: {
+function ProfileCard({ icon, iconUri, iconColor, title, value, onPress, disabled, hideArrow }: {
icon?: keyof typeof Ionicons.glyphMap;
iconUri?: string;
iconColor?: string;
title: string;
value: string;
onPress: () => void;
+ disabled?: boolean;
+ hideArrow?: boolean;
}) {
+ const Container = disabled ? View : TouchableOpacity;
+
return (
-
+
{iconUri ?
{value}
-
+ {!hideArrow && }
-
+
);
}
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 557c29b..ce48827 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -2376,10 +2376,10 @@ SPEC CHECKSUMS:
ExpoSymbols: c5612a90fb9179cdaebcd19bea9d8c69e5d3b859
ExpoSystemUI: 433a971503b99020318518ed30a58204288bab2d
ExpoWebBrowser: dc39a88485f007e61a3dff05d6a75f22ab4a2e92
- fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6
+ fast_float: 23278fd30b349f976d2014f4aec9e2d7bc1c3806
FBLazyVector: d2a9cd223302b6c9aa4aa34c1a775e9db609eb52
- fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
- glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
+ fmt: b85d977e8fe789fd71c77123f9f4920d88c4d170
+ glog: 682871fb30f4a65f657bf357581110656ea90b08
libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7
libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
@@ -2389,7 +2389,7 @@ SPEC CHECKSUMS:
QCloudCore: 6f8c67b96448472d2c6a92b9cfe1bdb5abbb1798
QCloudCOSXML: 92f50a787b4e8d9a7cb6ea8e626775256b4840a7
QCloudTrack: 20b79388365b4c8ed150019c82a56f1569f237f8
- RCT-Folly: e78785aa9ba2ed998ea4151e314036f6c49e6d82
+ RCT-Folly: 031db300533e2dfa954cdc5a859b792d5c14ed7b
RCTDeprecation: 5f638f65935e273753b1f31a365db6a8d6dc53b5
RCTRequired: 8b46a520ea9071e2bc47d474aa9ca31b4a935bd8
RCTTypeSafety: cc4740278c2a52cbf740592b0a0a40df1587c9ab
diff --git a/ios/digitalpilates/Info.plist b/ios/digitalpilates/Info.plist
index 3dae8a1..a7c4107 100644
--- a/ios/digitalpilates/Info.plist
+++ b/ios/digitalpilates/Info.plist
@@ -89,53 +89,5 @@
Light
UIViewControllerBasedStatusBarAppearance
- UIApplicationShortcutItems
-
-
- UIApplicationShortcutItemIconFile
- IconGlass
- UIApplicationShortcutItemTitle
- 喝水
- UIApplicationShortcutItemSubtitle
- 快速记录饮水
- UIApplicationShortcutItemType
- $(PRODUCT_BUNDLE_IDENTIFIER).drink_water
- UIApplicationShortcutItemUserInfo
-
- amount
- 250
-
-
-
- UIApplicationShortcutItemIconFile
- IconGlass
- UIApplicationShortcutItemTitle
- 喝水 100ml
- UIApplicationShortcutItemSubtitle
- 快速记录饮水
- UIApplicationShortcutItemType
- $(PRODUCT_BUNDLE_IDENTIFIER).drink_water_100
- UIApplicationShortcutItemUserInfo
-
- amount
- 100
-
-
-
- UIApplicationShortcutItemIconFile
- IconGlass
- UIApplicationShortcutItemTitle
- 喝水 200ml
- UIApplicationShortcutItemSubtitle
- 快速记录饮水
- UIApplicationShortcutItemType
- $(PRODUCT_BUNDLE_IDENTIFIER).drink_water_200
- UIApplicationShortcutItemUserInfo
-
- amount
- 200
-
-
-
diff --git a/services/quickActions.ts b/services/quickActions.ts
index aeac99b..3c3e390 100644
--- a/services/quickActions.ts
+++ b/services/quickActions.ts
@@ -60,9 +60,7 @@ export const setupQuickActions = async () => {
params: { amount: quickAmount }
},
// 固定选项
- QUICK_ACTIONS.DRINK_WATER_100,
- QUICK_ACTIONS.DRINK_WATER_200,
- QUICK_ACTIONS.DRINK_WATER_250
+ QUICK_ACTIONS.DRINK_WATER_100
];
// 设置快捷动作
diff --git a/utils/health.ts b/utils/health.ts
index 209783c..55f9833 100644
--- a/utils/health.ts
+++ b/utils/health.ts
@@ -183,12 +183,6 @@ async function fetchStepCount(date: Date): Promise {
});
}
-// 获取指定日期每小时步数数据 (已弃用,使用 fetchHourlyStepSamples 替代)
-// 保留此函数以防后向兼容需求
-async function fetchHourlyStepCount(date: Date): Promise {
- // 直接调用更准确的样本数据获取函数
- return fetchHourlyStepSamples(date);
-}
// 使用样本数据获取每小时步数
async function fetchHourlyStepSamples(date: Date): Promise {
@@ -483,7 +477,7 @@ async function fetchActivitySummary(options: HealthDataOptions): Promise {
AppleHealthKit.getActivitySummary(
options,
- (err: Object, results: HealthActivitySummary[]) => {
+ (err: string, results: HealthActivitySummary[]) => {
if (err) {
logError('ActivitySummary', err);
return resolve(null);
@@ -537,6 +531,44 @@ async function fetchHeartRate(options: HealthDataOptions): Promise {
+ return new Promise((resolve) => {
+ AppleHealthKit.getHeartRateSamples(options, (err, res) => {
+ if (err) {
+ logError('最大心率', err);
+ return resolve(null);
+ }
+ if (!res || !Array.isArray(res) || res.length === 0) {
+ logWarning('最大心率', '为空或格式错误');
+ return resolve(null);
+ }
+
+ // 从所有心率样本中找出最大值
+ let maxHeartRate = 0;
+ let validSamplesCount = 0;
+
+ res.forEach((sample: any) => {
+ if (sample && sample.value !== undefined) {
+ const heartRate = validateHeartRate(sample.value);
+ if (heartRate !== null) {
+ maxHeartRate = Math.max(maxHeartRate, heartRate);
+ validSamplesCount++;
+ }
+ }
+ });
+
+ if (validSamplesCount > 0 && maxHeartRate > 0) {
+ logSuccess('最大心率', { maxHeartRate, validSamplesCount });
+ resolve(maxHeartRate);
+ } else {
+ logWarning('最大心率', '没有找到有效的样本数据');
+ resolve(null);
+ }
+ });
+ });
+}
+
// 默认健康数据
function getDefaultHealthData(): TodayHealthData {
return {
@@ -721,7 +753,7 @@ export async function saveWaterIntakeToHealthKit(amount: number, recordedAt?: st
endDate: recordedAt ? new Date(recordedAt).toISOString() : new Date().toISOString(),
};
- AppleHealthKit.saveWater(waterOptions, (error: Object, result) => {
+ AppleHealthKit.saveWater(waterOptions, (error: string, result) => {
if (error) {
console.error('添加饮水记录到 HealthKit 失败:', error);
resolve(false);
@@ -742,7 +774,7 @@ export async function saveWaterIntakeToHealthKit(amount: number, recordedAt?: st
// 获取 HealthKit 中的饮水记录
export async function getWaterIntakeFromHealthKit(options: HealthDataOptions): Promise {
return new Promise((resolve) => {
- AppleHealthKit.getWaterSamples(options, (error: Object, results: any[]) => {
+ AppleHealthKit.getWaterSamples(options, (error: string, results: any[]) => {
if (error) {
console.error('获取 HealthKit 饮水记录失败:', error);
resolve([]);
@@ -850,3 +882,4 @@ export async function fetchActivityRingsForDate(date: Date): Promise