feat: 更新应用版本和主题设置

- 将应用版本更新至 1.0.3,修改相关配置文件
- 强制全局使用浅色主题,确保一致的用户体验
- 在训练计划功能中新增激活计划的 API 接口,支持用户激活训练计划
- 优化打卡功能,支持自动同步打卡记录至服务器
- 更新样式以适应新功能的展示和交互
This commit is contained in:
2025-08-14 22:23:45 +08:00
parent 56d4c7fd7f
commit 807e185761
21 changed files with 677 additions and 141 deletions

View File

@@ -1,5 +1,5 @@
import DateTimePicker from '@react-native-community/datetimepicker';
import { useRouter } from 'expo-router';
import { useLocalSearchParams, useRouter } from 'expo-router';
import React, { useEffect, useMemo, useState } from 'react';
import { Modal, Platform, Pressable, SafeAreaView, ScrollView, StyleSheet, TextInput, View } from 'react-native';
@@ -38,7 +38,8 @@ const GOALS: { key: PlanGoal; title: string; desc: string }[] = [
export default function TrainingPlanCreateScreen() {
const router = useRouter();
const dispatch = useAppDispatch();
const { draft, loading, error } = useAppSelector((s) => s.trainingPlan);
const { draft, loading, error, editingId } = useAppSelector((s) => s.trainingPlan);
const { id } = useLocalSearchParams<{ id?: string }>();
const [weightInput, setWeightInput] = useState<string>('');
const [datePickerVisible, setDatePickerVisible] = useState(false);
const [pickerDate, setPickerDate] = useState<Date>(new Date());
@@ -47,6 +48,17 @@ export default function TrainingPlanCreateScreen() {
dispatch(loadPlans());
}, [dispatch]);
// 如果带有 id加载详情并进入编辑模式
useEffect(() => {
if (id) {
dispatch({ type: 'trainingPlan/clearError' } as any);
dispatch((require('@/store/trainingPlanSlice') as any).loadPlanForEdit(id as string));
} else {
// 离开编辑模式
dispatch((require('@/store/trainingPlanSlice') as any).setEditingId(null));
}
}, [id, dispatch]);
useEffect(() => {
if (draft.startWeightKg && !weightInput) setWeightInput(String(draft.startWeightKg));
}, [draft.startWeightKg]);
@@ -76,7 +88,11 @@ export default function TrainingPlanCreateScreen() {
const handleSave = async () => {
try {
await dispatch(saveDraftAsPlan()).unwrap();
if (editingId) {
await dispatch((require('@/store/trainingPlanSlice') as any).updatePlanFromDraft()).unwrap();
} else {
await dispatch(saveDraftAsPlan()).unwrap();
}
router.back();
} catch (error) {
// 错误已经在Redux中处理这里可以显示额外的用户反馈
@@ -114,7 +130,7 @@ export default function TrainingPlanCreateScreen() {
return (
<SafeAreaView style={styles.safeArea}>
<ThemedView style={styles.container}>
<HeaderBar title="新建训练计划" onBack={() => router.back()} withSafeTop={false} transparent />
<HeaderBar title={editingId ? '编辑训练计划' : '新建训练计划'} onBack={() => router.back()} withSafeTop={false} transparent />
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={styles.content}>
<ThemedText style={styles.title}></ThemedText>
<ThemedText style={styles.subtitle}></ThemedText>
@@ -242,7 +258,7 @@ export default function TrainingPlanCreateScreen() {
<Pressable disabled={!canSave || loading} onPress={handleSave} style={[styles.primaryBtn, (!canSave || loading) && styles.primaryBtnDisabled]}>
<ThemedText style={styles.primaryBtnText}>
{loading ? '创建中...' : canSave ? '生成计划' : '请先选择目标/频率'}
{loading ? (editingId ? '更新中...' : '创建中...') : canSave ? (editingId ? '更新计划' : '生成计划') : '请先选择目标/频率'}
</ThemedText>
</Pressable>