diff --git a/app.json b/app.json
index d68a0f4..9c1d9f7 100644
--- a/app.json
+++ b/app.json
@@ -2,7 +2,7 @@
"expo": {
"name": "Out Live",
"slug": "digital-pilates",
- "version": "1.0.12",
+ "version": "1.0.13",
"orientation": "portrait",
"scheme": "digitalpilates",
"userInterfaceStyle": "light",
diff --git a/app/(tabs)/statistics.tsx b/app/(tabs)/statistics.tsx
index e84bbcf..40d0cff 100644
--- a/app/(tabs)/statistics.tsx
+++ b/app/(tabs)/statistics.tsx
@@ -3,6 +3,7 @@ import { DateSelector } from '@/components/DateSelector';
import { FitnessRingsCard } from '@/components/FitnessRingsCard';
import { MoodCard } from '@/components/MoodCard';
import { NutritionRadarCard } from '@/components/NutritionRadarCard';
+import CircumferenceCard from '@/components/statistic/CircumferenceCard';
import OxygenSaturationCard from '@/components/statistic/OxygenSaturationCard';
import SleepCard from '@/components/statistic/SleepCard';
import StepsCard from '@/components/StepsCard';
@@ -544,6 +545,9 @@ export default function ExploreScreen() {
+
+ {/* 围度数据卡片 - 占满底部一行 */}
+
);
@@ -845,7 +849,6 @@ const styles = StyleSheet.create({
marginBottom: 16,
},
masonryContainer: {
- marginBottom: 16,
flexDirection: 'row',
gap: 16,
marginTop: 6,
@@ -908,6 +911,10 @@ const styles = StyleSheet.create({
top: 0,
padding: 4,
},
+ circumferenceCard: {
+ marginBottom: 36,
+ marginTop: 10,
+ },
});
diff --git a/app/_layout.tsx b/app/_layout.tsx
index 0456e8c..b15cb83 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -14,7 +14,7 @@ import { setupQuickActions } from '@/services/quickActions';
import { initializeWaterRecordBridge } from '@/services/waterRecordBridge';
import { WaterRecordSource } from '@/services/waterRecords';
import { store } from '@/store';
-import { rehydrateUserSync, setPrivacyAgreed } from '@/store/userSlice';
+import { fetchMyProfile, setPrivacyAgreed } from '@/store/userSlice';
import { createWaterRecordAction } from '@/store/waterSlice';
import { ensureHealthPermissions, initializeHealthPermissions } from '@/utils/health';
import { DailySummaryNotificationHelpers, MoodNotificationHelpers, NutritionNotificationHelpers } from '@/utils/notificationHelpers';
@@ -23,15 +23,16 @@ import React from 'react';
import { DialogProvider } from '@/components/ui/DialogProvider';
import { ToastProvider } from '@/contexts/ToastContext';
+import { STORAGE_KEYS } from '@/services/api';
import { BackgroundTaskManager } from '@/services/backgroundTaskManager';
+import AsyncStorage from '@/utils/kvStore';
import { Provider } from 'react-redux';
function Bootstrapper({ children }: { children: React.ReactNode }) {
const dispatch = useAppDispatch();
- const { privacyAgreed, profile } = useAppSelector((state) => state.user);
+ const { profile } = useAppSelector((state) => state.user);
const [showPrivacyModal, setShowPrivacyModal] = React.useState(false);
- const [userDataLoaded, setUserDataLoaded] = React.useState(false);
// 初始化快捷动作处理
useQuickActions();
@@ -39,8 +40,7 @@ function Bootstrapper({ children }: { children: React.ReactNode }) {
React.useEffect(() => {
const loadUserData = async () => {
// 数据已经在启动界面预加载,这里只需要快速同步到 Redux 状态
- await dispatch(rehydrateUserSync());
- setUserDataLoaded(true);
+ await dispatch(fetchMyProfile());
};
const initHealthPermissions = async () => {
@@ -129,14 +129,18 @@ function Bootstrapper({ children }: { children: React.ReactNode }) {
initializeNotifications();
// 冷启动时清空 AI 教练会话缓存
clearAiCoachSessionCache();
+
}, [dispatch]);
React.useEffect(() => {
- // 当用户数据加载完成后,检查是否需要显示隐私同意弹窗
- if (userDataLoaded && !privacyAgreed) {
- setShowPrivacyModal(true);
+
+ const getPrivacyAgreed = async () => {
+ const str = await AsyncStorage.getItem(STORAGE_KEYS.privacyAgreed)
+
+ setShowPrivacyModal(str !== 'true');
}
- }, [userDataLoaded, privacyAgreed]);
+ getPrivacyAgreed();
+ }, []);
const handlePrivacyAgree = () => {
dispatch(setPrivacyAgreed());
diff --git a/components/statistic/CircumferenceCard.tsx b/components/statistic/CircumferenceCard.tsx
new file mode 100644
index 0000000..a0af380
--- /dev/null
+++ b/components/statistic/CircumferenceCard.tsx
@@ -0,0 +1,189 @@
+import { FloatingSelectionModal, SelectionItem } from '@/components/ui/FloatingSelectionModal';
+import { useAppDispatch, useAppSelector } from '@/hooks/redux';
+import { useAuthGuard } from '@/hooks/useAuthGuard';
+import { selectUserProfile, updateUserBodyMeasurements } from '@/store/userSlice';
+import React, { useState } from 'react';
+import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
+
+interface CircumferenceCardProps {
+ style?: any;
+}
+
+const CircumferenceCard: React.FC = ({ style }) => {
+ const dispatch = useAppDispatch();
+ const userProfile = useAppSelector(selectUserProfile);
+
+
+ console.log('userProfile', userProfile);
+
+
+ const { ensureLoggedIn } = useAuthGuard()
+
+ const [modalVisible, setModalVisible] = useState(false);
+ const [selectedMeasurement, setSelectedMeasurement] = useState<{
+ key: string;
+ label: string;
+ currentValue?: number;
+ } | null>(null);
+
+ const measurements = [
+ {
+ key: 'chestCircumference',
+ label: '胸围',
+ value: userProfile?.chestCircumference,
+ },
+ {
+ key: 'waistCircumference',
+ label: '腰围',
+ value: userProfile?.waistCircumference,
+ },
+ {
+ key: 'upperHipCircumference',
+ label: '上臀围',
+ value: userProfile?.upperHipCircumference,
+ },
+ {
+ key: 'armCircumference',
+ label: '臂围',
+ value: userProfile?.armCircumference,
+ },
+ {
+ key: 'thighCircumference',
+ label: '大腿围',
+ value: userProfile?.thighCircumference,
+ },
+ {
+ key: 'calfCircumference',
+ label: '小腿围',
+ value: userProfile?.calfCircumference,
+ },
+ ];
+
+ // Generate circumference options (30-150 cm)
+ const circumferenceOptions: SelectionItem[] = Array.from({ length: 121 }, (_, i) => {
+ const value = i + 30;
+ return {
+ label: `${value} cm`,
+ value: value,
+ };
+ });
+
+ const handleMeasurementPress = async (measurement: typeof measurements[0]) => {
+ const isLoggedIn = await ensureLoggedIn();
+ if (!isLoggedIn) {
+ // 如果未登录,用户会被重定向到登录页面
+ return;
+ }
+
+ setSelectedMeasurement({
+ key: measurement.key,
+ label: measurement.label,
+ currentValue: measurement.value,
+ });
+ setModalVisible(true);
+ };
+
+ const handleUpdateMeasurement = (value: string | number) => {
+ if (!selectedMeasurement) return;
+
+ const updateData = {
+ [selectedMeasurement.key]: Number(value),
+ };
+
+ dispatch(updateUserBodyMeasurements(updateData));
+ setModalVisible(false);
+ setSelectedMeasurement(null);
+ };
+
+ return (
+
+ 围度 (cm)
+
+
+ {measurements.map((measurement, index) => (
+ handleMeasurementPress(measurement)}
+ activeOpacity={0.7}
+ >
+ {measurement.label}
+
+
+ {measurement.value ? measurement.value.toString() : '--'}
+
+
+
+ ))}
+
+
+ {
+ setModalVisible(false);
+ setSelectedMeasurement(null);
+ }}
+ title={selectedMeasurement ? `设置${selectedMeasurement.label}` : '设置围度'}
+ items={circumferenceOptions}
+ selectedValue={selectedMeasurement?.currentValue}
+ onValueChange={() => { }} // Real-time update not needed
+ onConfirm={handleUpdateMeasurement}
+ confirmButtonText="确认"
+ pickerHeight={180}
+ />
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ backgroundColor: '#FFFFFF',
+ borderRadius: 16,
+ padding: 20,
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 4,
+ },
+ shadowOpacity: 0.12,
+ shadowRadius: 12,
+ elevation: 6,
+ },
+ title: {
+ fontSize: 14,
+ fontWeight: '600',
+ color: '#192126',
+ marginBottom: 16,
+ },
+ measurementsContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ flexWrap: 'nowrap',
+ },
+ measurementItem: {
+ alignItems: 'center',
+ },
+ label: {
+ fontSize: 12,
+ color: '#888',
+ marginBottom: 8,
+ textAlign: 'center',
+ },
+ valueContainer: {
+ backgroundColor: '#F5F5F7',
+ borderRadius: 10,
+ paddingHorizontal: 8,
+ paddingVertical: 6,
+ minWidth: 20,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ value: {
+ fontSize: 14,
+ fontWeight: '600',
+ color: '#192126',
+ textAlign: 'center',
+ },
+});
+
+export default CircumferenceCard;
\ No newline at end of file
diff --git a/components/ui/FloatingSelectionCard.tsx b/components/ui/FloatingSelectionCard.tsx
new file mode 100644
index 0000000..ce389dd
--- /dev/null
+++ b/components/ui/FloatingSelectionCard.tsx
@@ -0,0 +1,122 @@
+import { Ionicons } from '@expo/vector-icons';
+import { BlurView } from 'expo-blur';
+import React from 'react';
+import {
+ Modal,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+} from 'react-native';
+
+interface FloatingSelectionCardProps {
+ visible: boolean;
+ onClose: () => void;
+ title: string;
+ children: React.ReactNode;
+}
+
+export function FloatingSelectionCard({
+ visible,
+ onClose,
+ title,
+ children
+}: FloatingSelectionCardProps) {
+ return (
+
+
+
+
+
+
+
+ {title}
+
+
+
+ {children}
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ overlay: {
+ flex: 1,
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ },
+ backdrop: {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ },
+ container: {
+ alignItems: 'center',
+ marginBottom: 40,
+ },
+ blurContainer: {
+ borderRadius: 20,
+ overflow: 'hidden',
+ backgroundColor: 'rgba(255, 255, 255, 0.95)',
+ minWidth: 340,
+ paddingVertical: 20,
+ paddingHorizontal: 16,
+ minHeight: 100,
+ },
+ header: {
+ paddingBottom: 20,
+ alignItems: 'center',
+ },
+ title: {
+ fontSize: 14,
+ fontWeight: '600',
+ color: '#636161ff',
+ },
+ content: {
+ alignItems: 'center',
+ },
+ closeButton: {
+ marginTop: 20,
+ },
+ closeButtonInner: {
+ width: 44,
+ height: 44,
+ borderRadius: 22,
+ backgroundColor: 'rgba(255, 255, 255, 0.9)',
+ alignItems: 'center',
+ justifyContent: 'center',
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 3,
+ },
+});
\ No newline at end of file
diff --git a/components/ui/FloatingSelectionModal.tsx b/components/ui/FloatingSelectionModal.tsx
new file mode 100644
index 0000000..59840c2
--- /dev/null
+++ b/components/ui/FloatingSelectionModal.tsx
@@ -0,0 +1,57 @@
+import React from 'react';
+import { FloatingSelectionCard } from './FloatingSelectionCard';
+import { SlidingSelection, SelectionItem } from './SlidingSelection';
+
+interface FloatingSelectionModalProps {
+ visible: boolean;
+ onClose: () => void;
+ title: string;
+ items: SelectionItem[];
+ selectedValue?: string | number;
+ onValueChange: (value: string | number, index: number) => void;
+ onConfirm?: (value: string | number, index: number) => void;
+ showConfirmButton?: boolean;
+ confirmButtonText?: string;
+ pickerHeight?: number;
+}
+
+export function FloatingSelectionModal({
+ visible,
+ onClose,
+ title,
+ items,
+ selectedValue,
+ onValueChange,
+ onConfirm,
+ showConfirmButton = true,
+ confirmButtonText = '确认',
+ pickerHeight = 150,
+}: FloatingSelectionModalProps) {
+ const handleConfirm = (value: string | number, index: number) => {
+ if (onConfirm) {
+ onConfirm(value, index);
+ }
+ onClose();
+ };
+
+ return (
+
+
+
+ );
+}
+
+// Export types for convenience
+export type { SelectionItem } from './SlidingSelection';
\ No newline at end of file
diff --git a/components/ui/SlidingSelection.tsx b/components/ui/SlidingSelection.tsx
new file mode 100644
index 0000000..9198c8a
--- /dev/null
+++ b/components/ui/SlidingSelection.tsx
@@ -0,0 +1,131 @@
+import React, { useState } from 'react';
+import {
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+} from 'react-native';
+import WheelPickerExpo from 'react-native-wheel-picker-expo';
+
+export interface SelectionItem {
+ label: string;
+ value: string | number;
+}
+
+interface SlidingSelectionProps {
+ items: SelectionItem[];
+ selectedValue?: string | number;
+ onValueChange: (value: string | number, index: number) => void;
+ onConfirm?: (value: string | number, index: number) => void;
+ showConfirmButton?: boolean;
+ confirmButtonText?: string;
+ height?: number;
+ itemTextStyle?: any;
+ selectedIndicatorStyle?: any;
+}
+
+export function SlidingSelection({
+ items,
+ selectedValue,
+ onValueChange,
+ onConfirm,
+ showConfirmButton = true,
+ confirmButtonText = '确认',
+ height = 150,
+ itemTextStyle,
+ selectedIndicatorStyle
+}: SlidingSelectionProps) {
+ const [currentIndex, setCurrentIndex] = useState(() => {
+ if (selectedValue !== undefined) {
+ const index = items.findIndex(item => item.value === selectedValue);
+ return index >= 0 ? index : 0;
+ }
+ return 0;
+ });
+
+ const handleValueChange = (index: number) => {
+ setCurrentIndex(index);
+ const selectedItem = items[index];
+ if (selectedItem) {
+ onValueChange(selectedItem.value, index);
+ }
+ };
+
+ const handleConfirm = () => {
+ const selectedItem = items[currentIndex];
+ if (selectedItem && onConfirm) {
+ onConfirm(selectedItem.value, currentIndex);
+ }
+ };
+
+ return (
+
+
+ ({ label: item.label, value: item.value }))}
+ onChange={({ item, index }) => handleValueChange(index)}
+ backgroundColor="transparent"
+ haptics
+ />
+
+
+ {showConfirmButton && (
+
+ {confirmButtonText}
+
+ )}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ width: '100%',
+ },
+ pickerContainer: {
+ width: '100%',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ picker: {
+ width: '100%',
+ height: '100%',
+ },
+ itemText: {
+ fontSize: 16,
+ color: '#333',
+ fontWeight: '500',
+ },
+ selectedIndicator: {
+ backgroundColor: 'rgba(74, 144, 226, 0.1)',
+ borderRadius: 8,
+ },
+ confirmButton: {
+ backgroundColor: '#4A90E2',
+ paddingHorizontal: 32,
+ paddingVertical: 12,
+ borderRadius: 20,
+ marginTop: 16,
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.15,
+ shadowRadius: 4,
+ elevation: 4,
+ },
+ confirmButtonText: {
+ color: 'white',
+ fontSize: 16,
+ fontWeight: '600',
+ },
+});
\ No newline at end of file
diff --git a/hooks/useAuthGuard.ts b/hooks/useAuthGuard.ts
index 5b5b8e7..ccd253f 100644
--- a/hooks/useAuthGuard.ts
+++ b/hooks/useAuthGuard.ts
@@ -19,9 +19,9 @@ export function useAuthGuard() {
const router = useRouter();
const dispatch = useAppDispatch();
const currentPath = usePathname();
- const token = useAppSelector((s) => (s as any)?.user?.token as string | null);
+ const user = useAppSelector(state => state.user);
- const isLoggedIn = !!token;
+ const isLoggedIn = !!user?.profile?.id;
const ensureLoggedIn = useCallback(async (options?: EnsureOptions): Promise => {
if (isLoggedIn) return true;
diff --git a/ios/OutLive/Info.plist b/ios/OutLive/Info.plist
index 0b6fef4..2f26a49 100644
--- a/ios/OutLive/Info.plist
+++ b/ios/OutLive/Info.plist
@@ -25,7 +25,7 @@
CFBundlePackageType
$(PRODUCT_BUNDLE_PACKAGE_TYPE)
CFBundleShortVersionString
- 1.0.12
+ 1.0.13
CFBundleSignature
????
CFBundleURLTypes
diff --git a/services/users.ts b/services/users.ts
index e88f703..6df0890 100644
--- a/services/users.ts
+++ b/services/users.ts
@@ -15,6 +15,12 @@ export type UpdateUserDto = {
activityLevel?: number; // 活动水平 1-4
initialWeight?: number; // 初始体重
targetWeight?: number; // 目标体重
+ chestCircumference?: number; // 胸围
+ waistCircumference?: number; // 腰围
+ upperHipCircumference?: number; // 上臀围
+ armCircumference?: number; // 臂围
+ thighCircumference?: number; // 大腿围
+ calfCircumference?: number; // 小腿围
};
export async function updateUser(dto: UpdateUserDto): Promise> {
@@ -28,4 +34,20 @@ export async function uploadImage(formData: FormData): Promise<{ fileKey: string
});
}
+export type BodyMeasurementsDto = {
+ chestCircumference?: number; // 胸围
+ waistCircumference?: number; // 腰围
+ upperHipCircumference?: number; // 上臀围
+ armCircumference?: number; // 臂围
+ thighCircumference?: number; // 大腿围
+ calfCircumference?: number; // 小腿围
+};
+
+export async function updateBodyMeasurements(dto: BodyMeasurementsDto): Promise<{
+ code: number;
+ message: string;
+}> {
+ return await api.put('/users/body-measurements', dto);
+}
+
diff --git a/store/userSlice.ts b/store/userSlice.ts
index cbd3cb9..38e8e54 100644
--- a/store/userSlice.ts
+++ b/store/userSlice.ts
@@ -1,5 +1,5 @@
import { api, setAuthToken, STORAGE_KEYS } from '@/services/api';
-import { updateUser, UpdateUserDto } from '@/services/users';
+import { BodyMeasurementsDto, updateBodyMeasurements, updateUser, UpdateUserDto } from '@/services/users';
import AsyncStorage from '@/utils/kvStore';
import { createAsyncThunk, createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';
import dayjs from 'dayjs';
@@ -53,6 +53,7 @@ function getPreloadedUserData() {
export type Gender = 'male' | 'female' | '';
export type UserProfile = {
+ id?: string;
name?: string;
email?: string;
gender?: Gender;
@@ -68,6 +69,12 @@ export type UserProfile = {
isVip?: boolean;
freeUsageCount?: number;
maxUsageCount?: number;
+ chestCircumference?: number; // 胸围
+ waistCircumference?: number; // 腰围
+ upperHipCircumference?: number; // 上臀围
+ armCircumference?: number; // 臂围
+ thighCircumference?: number; // 大腿围
+ calfCircumference?: number; // 小腿围
};
export type WeightHistoryItem = {
@@ -87,7 +94,6 @@ export type UserState = {
profile: UserProfile;
loading: boolean;
error: string | null;
- privacyAgreed: boolean;
weightHistory: WeightHistoryItem[];
activityHistory: ActivityHistoryItem[];
};
@@ -107,7 +113,6 @@ const getInitialState = (): UserState => {
},
loading: false,
error: null,
- privacyAgreed: preloaded.privacyAgreed,
weightHistory: [],
activityHistory: [],
};
@@ -184,38 +189,6 @@ export const login = createAsyncThunk(
}
);
-// 同步重新hydrate用户数据,避免异步状态更新
-export const rehydrateUserSync = createAsyncThunk('user/rehydrateSync', async () => {
- // 立即从预加载的数据获取,如果没有则异步获取
- if (preloadedUserData) {
- return preloadedUserData;
- }
-
- // 如果预加载的数据不存在,则执行正常的异步加载
- return await preloadUserData();
-});
-
-export const rehydrateUser = createAsyncThunk('user/rehydrate', async () => {
- const [profileStr, privacyAgreedStr, token] = await Promise.all([
- AsyncStorage.getItem(STORAGE_KEYS.userProfile),
- AsyncStorage.getItem(STORAGE_KEYS.privacyAgreed),
- AsyncStorage.getItem(STORAGE_KEYS.authToken),
- ]);
-
- let profile: UserProfile = {};
- if (profileStr) {
- try { profile = JSON.parse(profileStr) as UserProfile; } catch { profile = {}; }
- }
- const privacyAgreed = privacyAgreedStr === 'true';
-
- // 如果有 token,需要设置到 API 客户端
- if (token) {
- await setAuthToken(token);
- }
-
- return { profile, privacyAgreed, token } as { profile: UserProfile; privacyAgreed: boolean; token: string | null };
-});
-
export const setPrivacyAgreed = createAsyncThunk('user/setPrivacyAgreed', async () => {
await AsyncStorage.setItem(STORAGE_KEYS.privacyAgreed, 'true');
return true;
@@ -241,8 +214,12 @@ export const fetchMyProfile = createAsyncThunk('user/fetchMyProfile', async (_,
await AsyncStorage.setItem(STORAGE_KEYS.userProfile, JSON.stringify(profile ?? {}));
return profile;
} catch (err: any) {
- const message = err?.message ?? '获取用户信息失败';
- return rejectWithValue(message);
+ const profileStr = await AsyncStorage.getItem(STORAGE_KEYS.userProfile),
+ profile = JSON.parse(profileStr ?? '{}');
+
+ return profile
+ // const message = err?.message ?? '获取用户信息失败';
+ // return rejectWithValue(message);
}
});
@@ -306,6 +283,19 @@ export const updateWeightRecord = createAsyncThunk(
}
);
+// 更新用户围度信息
+export const updateUserBodyMeasurements = createAsyncThunk(
+ 'user/updateBodyMeasurements',
+ async (measurementsDto: BodyMeasurementsDto, { rejectWithValue }) => {
+ try {
+ await updateBodyMeasurements(measurementsDto);
+ return measurementsDto;
+ } catch (err: any) {
+ return rejectWithValue(err?.message ?? '更新围度信息失败');
+ }
+ }
+);
+
const userSlice = createSlice({
name: 'user',
initialState,
@@ -338,22 +328,6 @@ const userSlice = createSlice({
state.loading = false;
state.error = (action.payload as string) ?? '登录失败';
})
- .addCase(rehydrateUser.fulfilled, (state, action) => {
- state.profile = action.payload.profile;
- state.privacyAgreed = action.payload.privacyAgreed;
- state.token = action.payload.token;
- if (!state.profile?.name || !state.profile.name.trim()) {
- state.profile.name = DEFAULT_MEMBER_NAME;
- }
- })
- .addCase(rehydrateUserSync.fulfilled, (state, action) => {
- state.profile = action.payload.profile;
- state.privacyAgreed = action.payload.privacyAgreed;
- state.token = action.payload.token;
- if (!state.profile?.name || !state.profile.name.trim()) {
- state.profile.name = DEFAULT_MEMBER_NAME;
- }
- })
.addCase(fetchMyProfile.fulfilled, (state, action) => {
state.profile = action.payload || {};
if (!state.profile?.name || !state.profile.name.trim()) {
@@ -367,10 +341,8 @@ const userSlice = createSlice({
.addCase(logout.fulfilled, (state) => {
state.token = null;
state.profile = {};
- state.privacyAgreed = false;
})
.addCase(setPrivacyAgreed.fulfilled, (state) => {
- state.privacyAgreed = true;
})
.addCase(fetchWeightHistory.fulfilled, (state, action) => {
state.weightHistory = action.payload;
@@ -408,6 +380,14 @@ const userSlice = createSlice({
})
.addCase(updateWeightRecord.rejected, (state, action) => {
state.error = (action.payload as string) ?? '更新体重记录失败';
+ })
+ .addCase(updateUserBodyMeasurements.fulfilled, (state, action) => {
+ console.log('action.payload', action.payload);
+
+ state.profile = { ...state.profile, ...action.payload };
+ })
+ .addCase(updateUserBodyMeasurements.rejected, (state, action) => {
+ state.error = (action.payload as string) ?? '更新围度信息失败';
});
},
});