import { scheduleExerciseApi, type CreateScheduleExerciseDto, type ReorderExercisesDto, type ScheduleExercise, type UpdateScheduleExerciseDto } from '@/services/scheduleExerciseApi'; import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; export type ScheduleExerciseState = { exercises: ScheduleExercise[]; loading: boolean; error: string | null; currentPlanId: string | null; }; const initialState: ScheduleExerciseState = { exercises: [], loading: false, error: null, currentPlanId: null, }; // 加载训练计划的所有项目 export const loadExercises = createAsyncThunk( 'scheduleExercise/loadExercises', async (planId: string, { rejectWithValue }) => { try { const exercises = await scheduleExerciseApi.list(planId); return { exercises, planId }; } catch (error: any) { return rejectWithValue(error.message || '加载训练项目失败'); } } ); // 添加训练项目 export const addExercise = createAsyncThunk( 'scheduleExercise/addExercise', async ({ planId, dto }: { planId: string; dto: CreateScheduleExerciseDto }, { rejectWithValue }) => { try { const exercise = await scheduleExerciseApi.create(planId, dto); return { exercise }; } catch (error: any) { return rejectWithValue(error.message || '添加训练项目失败'); } } ); // 更新训练项目 export const updateExercise = createAsyncThunk( 'scheduleExercise/updateExercise', async ({ planId, exerciseId, dto }: { planId: string; exerciseId: string; dto: UpdateScheduleExerciseDto; }, { rejectWithValue }) => { try { const exercise = await scheduleExerciseApi.update(planId, exerciseId, dto); return { exercise }; } catch (error: any) { return rejectWithValue(error.message || '更新训练项目失败'); } } ); // 删除训练项目 export const deleteExercise = createAsyncThunk( 'scheduleExercise/deleteExercise', async ({ planId, exerciseId }: { planId: string; exerciseId: string }, { rejectWithValue }) => { try { await scheduleExerciseApi.delete(planId, exerciseId); return { exerciseId }; } catch (error: any) { return rejectWithValue(error.message || '删除训练项目失败'); } } ); // 重新排序训练项目 export const reorderExercises = createAsyncThunk( 'scheduleExercise/reorderExercises', async ({ planId, dto }: { planId: string; dto: ReorderExercisesDto }, { rejectWithValue }) => { try { await scheduleExerciseApi.reorder(planId, dto); // 重新加载排序后的列表 const exercises = await scheduleExerciseApi.list(planId); return { exercises }; } catch (error: any) { return rejectWithValue(error.message || '重新排序失败'); } } ); // 更新完成状态 export const toggleCompletion = createAsyncThunk( 'scheduleExercise/toggleCompletion', async ({ planId, exerciseId, completed }: { planId: string; exerciseId: string; completed: boolean; }, { rejectWithValue }) => { try { const exercise = await scheduleExerciseApi.updateCompletion(planId, exerciseId, completed); return { exercise }; } catch (error: any) { return rejectWithValue(error.message || '更新完成状态失败'); } } ); const scheduleExerciseSlice = createSlice({ name: 'scheduleExercise', initialState, reducers: { clearError(state) { state.error = null; }, clearExercises(state) { state.exercises = []; state.currentPlanId = null; }, // 本地更新排序(用于拖拽等即时反馈) updateLocalOrder(state, action: PayloadAction) { const newOrder = action.payload; const orderedExercises = newOrder.map(id => state.exercises.find(ex => ex.id === id) ).filter(Boolean) as ScheduleExercise[]; state.exercises = orderedExercises; }, }, extraReducers: (builder) => { builder // loadExercises .addCase(loadExercises.pending, (state) => { state.loading = true; state.error = null; }) .addCase(loadExercises.fulfilled, (state, action) => { state.loading = false; state.exercises = action.payload.exercises; state.currentPlanId = action.payload.planId; }) .addCase(loadExercises.rejected, (state, action) => { state.loading = false; state.error = action.payload as string; }) // addExercise .addCase(addExercise.pending, (state) => { state.loading = true; state.error = null; }) .addCase(addExercise.fulfilled, (state, action) => { state.loading = false; state.exercises.push(action.payload.exercise); }) .addCase(addExercise.rejected, (state, action) => { state.loading = false; state.error = action.payload as string; }) // updateExercise .addCase(updateExercise.pending, (state) => { state.loading = true; state.error = null; }) .addCase(updateExercise.fulfilled, (state, action) => { state.loading = false; const index = state.exercises.findIndex(ex => ex.id === action.payload.exercise.id); if (index !== -1) { state.exercises[index] = action.payload.exercise; } }) .addCase(updateExercise.rejected, (state, action) => { state.loading = false; state.error = action.payload as string; }) // deleteExercise .addCase(deleteExercise.pending, (state) => { state.loading = true; state.error = null; }) .addCase(deleteExercise.fulfilled, (state, action) => { state.loading = false; state.exercises = state.exercises.filter(ex => ex.id !== action.payload.exerciseId); }) .addCase(deleteExercise.rejected, (state, action) => { state.loading = false; state.error = action.payload as string; }) // reorderExercises .addCase(reorderExercises.pending, (state) => { state.loading = true; state.error = null; }) .addCase(reorderExercises.fulfilled, (state, action) => { state.loading = false; state.exercises = action.payload.exercises; }) .addCase(reorderExercises.rejected, (state, action) => { state.loading = false; state.error = action.payload as string; }) // toggleCompletion .addCase(toggleCompletion.pending, (state) => { state.error = null; }) .addCase(toggleCompletion.fulfilled, (state, action) => { const index = state.exercises.findIndex(ex => ex.id === action.payload.exercise.id); if (index !== -1) { state.exercises[index] = action.payload.exercise; } }) .addCase(toggleCompletion.rejected, (state, action) => { state.error = action.payload as string; }); }, }); export const { clearError, clearExercises, updateLocalOrder } = scheduleExerciseSlice.actions; export default scheduleExerciseSlice.reducer;