feat: 添加训练计划和打卡功能
- 新增训练计划页面,允许用户制定个性化的训练计划 - 集成打卡功能,用户可以记录每日的训练情况 - 更新 Redux 状态管理,添加训练计划相关的 reducer - 在首页中添加训练计划卡片,支持用户点击跳转 - 更新样式和布局,以适应新功能的展示和交互 - 添加日期选择器和相关依赖,支持用户选择训练日期
This commit is contained in:
78
store/checkinSlice.ts
Normal file
78
store/checkinSlice.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
export type CheckinExercise = {
|
||||
key: string;
|
||||
name: string;
|
||||
category: string;
|
||||
sets: number; // 组数
|
||||
reps?: number; // 每组重复(计次型)
|
||||
durationSec?: number; // 每组时长(计时型)
|
||||
completed?: boolean; // 是否已完成该动作
|
||||
};
|
||||
|
||||
export type CheckinRecord = {
|
||||
id: string;
|
||||
date: string; // YYYY-MM-DD
|
||||
items: CheckinExercise[];
|
||||
note?: string;
|
||||
};
|
||||
|
||||
export type CheckinState = {
|
||||
byDate: Record<string, CheckinRecord>;
|
||||
currentDate: string | null;
|
||||
};
|
||||
|
||||
const initialState: CheckinState = {
|
||||
byDate: {},
|
||||
currentDate: null,
|
||||
};
|
||||
|
||||
function ensureRecord(state: CheckinState, date: string): CheckinRecord {
|
||||
if (!state.byDate[date]) {
|
||||
state.byDate[date] = {
|
||||
id: `rec_${date}`,
|
||||
date,
|
||||
items: [],
|
||||
};
|
||||
}
|
||||
return state.byDate[date];
|
||||
}
|
||||
|
||||
const checkinSlice = createSlice({
|
||||
name: 'checkin',
|
||||
initialState,
|
||||
reducers: {
|
||||
setCurrentDate(state, action: PayloadAction<string>) {
|
||||
state.currentDate = action.payload; // 期望格式 YYYY-MM-DD
|
||||
ensureRecord(state, action.payload);
|
||||
},
|
||||
addExercise(state, action: PayloadAction<{ date: string; item: CheckinExercise }>) {
|
||||
const rec = ensureRecord(state, action.payload.date);
|
||||
// 若同 key 已存在则覆盖参数(更接近用户“重新选择/编辑”的心智)
|
||||
const idx = rec.items.findIndex((it) => it.key === action.payload.item.key);
|
||||
const normalized: CheckinExercise = { ...action.payload.item, completed: false };
|
||||
if (idx >= 0) rec.items[idx] = normalized; else rec.items.push(normalized);
|
||||
},
|
||||
removeExercise(state, action: PayloadAction<{ date: string; key: string }>) {
|
||||
const rec = ensureRecord(state, action.payload.date);
|
||||
rec.items = rec.items.filter((it) => it.key !== action.payload.key);
|
||||
},
|
||||
toggleExerciseCompleted(state, action: PayloadAction<{ date: string; key: string }>) {
|
||||
const rec = ensureRecord(state, action.payload.date);
|
||||
const idx = rec.items.findIndex((it) => it.key === action.payload.key);
|
||||
if (idx >= 0) rec.items[idx].completed = !rec.items[idx].completed;
|
||||
},
|
||||
setNote(state, action: PayloadAction<{ date: string; note: string }>) {
|
||||
const rec = ensureRecord(state, action.payload.date);
|
||||
rec.note = action.payload.note;
|
||||
},
|
||||
resetDate(state, action: PayloadAction<string>) {
|
||||
delete state.byDate[action.payload];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setCurrentDate, addExercise, removeExercise, toggleExerciseCompleted, setNote, resetDate } = checkinSlice.actions;
|
||||
export default checkinSlice.reducer;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user