feat: 更新心情记录功能和界面
- 调整启动画面中的图片宽度,提升视觉效果 - 移除引导页面相关组件,简化应用结构 - 新增心情统计页面,支持用户查看和分析心情数据 - 优化心情卡片组件,增强用户交互体验 - 更新登录页面标题,提升品牌一致性 - 新增心情日历和编辑功能,支持用户记录和管理心情
This commit is contained in:
324
store/moodSlice.ts
Normal file
324
store/moodSlice.ts
Normal file
@@ -0,0 +1,324 @@
|
||||
import {
|
||||
createMoodCheckin,
|
||||
CreateMoodCheckinDto,
|
||||
deleteMoodCheckin,
|
||||
DeleteMoodCheckinDto,
|
||||
getDailyMoodCheckins,
|
||||
getMoodCheckinsHistory,
|
||||
getMoodStatistics,
|
||||
MoodCheckin,
|
||||
MoodType,
|
||||
updateMoodCheckin,
|
||||
UpdateMoodCheckinDto
|
||||
} from '@/services/moodCheckins';
|
||||
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
// 状态接口
|
||||
interface MoodState {
|
||||
// 按日期存储的心情记录
|
||||
moodRecords: Record<string, MoodCheckin[]>;
|
||||
// 当前选中的日期
|
||||
selectedDate: string;
|
||||
// 当前选中的心情记录
|
||||
selectedMoodRecord: MoodCheckin | null;
|
||||
// 加载状态
|
||||
loading: {
|
||||
daily: boolean;
|
||||
history: boolean;
|
||||
statistics: boolean;
|
||||
create: boolean;
|
||||
update: boolean;
|
||||
delete: boolean;
|
||||
};
|
||||
// 错误信息
|
||||
error: string | null;
|
||||
// 统计数据
|
||||
statistics: {
|
||||
totalCheckins: number;
|
||||
averageIntensity: number;
|
||||
moodDistribution: Record<MoodType, number>;
|
||||
mostFrequentMood: MoodType | null;
|
||||
} | null;
|
||||
}
|
||||
|
||||
// 初始状态
|
||||
const initialState: MoodState = {
|
||||
moodRecords: {},
|
||||
selectedDate: dayjs().format('YYYY-MM-DD'),
|
||||
selectedMoodRecord: null,
|
||||
loading: {
|
||||
daily: false,
|
||||
history: false,
|
||||
statistics: false,
|
||||
create: false,
|
||||
update: false,
|
||||
delete: false,
|
||||
},
|
||||
error: null,
|
||||
statistics: null,
|
||||
};
|
||||
|
||||
// 异步 actions
|
||||
export const fetchDailyMoodCheckins = createAsyncThunk(
|
||||
'mood/fetchDailyMoodCheckins',
|
||||
async (date: string) => {
|
||||
const checkins = await getDailyMoodCheckins(date);
|
||||
return { date, checkins };
|
||||
}
|
||||
);
|
||||
|
||||
export const fetchMoodHistory = createAsyncThunk(
|
||||
'mood/fetchMoodHistory',
|
||||
async (params: { startDate: string; endDate: string; moodType?: MoodType }) => {
|
||||
const checkins = await getMoodCheckinsHistory(params);
|
||||
return { params, checkins };
|
||||
}
|
||||
);
|
||||
|
||||
export const fetchMoodStatistics = createAsyncThunk(
|
||||
'mood/fetchMoodStatistics',
|
||||
async (params: { startDate: string; endDate: string }) => {
|
||||
const statistics = await getMoodStatistics(params);
|
||||
return statistics;
|
||||
}
|
||||
);
|
||||
|
||||
export const createMoodRecord = createAsyncThunk(
|
||||
'mood/createMoodRecord',
|
||||
async (dto: CreateMoodCheckinDto) => {
|
||||
const newRecord = await createMoodCheckin(dto);
|
||||
return newRecord;
|
||||
}
|
||||
);
|
||||
|
||||
export const updateMoodRecord = createAsyncThunk(
|
||||
'mood/updateMoodRecord',
|
||||
async (dto: UpdateMoodCheckinDto) => {
|
||||
const updatedRecord = await updateMoodCheckin(dto);
|
||||
return updatedRecord;
|
||||
}
|
||||
);
|
||||
|
||||
export const deleteMoodRecord = createAsyncThunk(
|
||||
'mood/deleteMoodRecord',
|
||||
async (dto: DeleteMoodCheckinDto) => {
|
||||
await deleteMoodCheckin(dto);
|
||||
return dto.id;
|
||||
}
|
||||
);
|
||||
|
||||
// 创建 slice
|
||||
const moodSlice = createSlice({
|
||||
name: 'mood',
|
||||
initialState,
|
||||
reducers: {
|
||||
// 设置选中的日期
|
||||
setSelectedDate: (state, action: PayloadAction<string>) => {
|
||||
state.selectedDate = action.payload;
|
||||
// 如果该日期没有记录,设置为 null
|
||||
const records = state.moodRecords[action.payload];
|
||||
state.selectedMoodRecord = records && records.length > 0 ? records[0] : null;
|
||||
},
|
||||
// 设置选中的心情记录
|
||||
setSelectedMoodRecord: (state, action: PayloadAction<MoodCheckin | null>) => {
|
||||
state.selectedMoodRecord = action.payload;
|
||||
},
|
||||
// 清除错误
|
||||
clearError: (state) => {
|
||||
state.error = null;
|
||||
},
|
||||
// 清除统计数据
|
||||
clearStatistics: (state) => {
|
||||
state.statistics = null;
|
||||
},
|
||||
// 清除所有数据
|
||||
clearMoodData: (state) => {
|
||||
state.moodRecords = {};
|
||||
state.selectedMoodRecord = null;
|
||||
state.statistics = null;
|
||||
state.error = null;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
// fetchDailyMoodCheckins
|
||||
builder
|
||||
.addCase(fetchDailyMoodCheckins.pending, (state) => {
|
||||
state.loading.daily = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(fetchDailyMoodCheckins.fulfilled, (state, action) => {
|
||||
state.loading.daily = false;
|
||||
const { date, checkins } = action.payload;
|
||||
state.moodRecords[date] = checkins;
|
||||
|
||||
// 如果是当前选中的日期,更新选中的记录
|
||||
if (date === state.selectedDate) {
|
||||
state.selectedMoodRecord = checkins.length > 0 ? checkins[0] : null;
|
||||
}
|
||||
})
|
||||
.addCase(fetchDailyMoodCheckins.rejected, (state, action) => {
|
||||
state.loading.daily = false;
|
||||
state.error = action.error.message || '获取心情记录失败';
|
||||
});
|
||||
|
||||
// fetchMoodHistory
|
||||
builder
|
||||
.addCase(fetchMoodHistory.pending, (state) => {
|
||||
state.loading.history = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(fetchMoodHistory.fulfilled, (state, action) => {
|
||||
state.loading.history = false;
|
||||
const { checkins } = action.payload;
|
||||
|
||||
// 将历史记录按日期分组存储
|
||||
checkins.forEach(checkin => {
|
||||
const date = checkin.checkinDate;
|
||||
if (!state.moodRecords[date]) {
|
||||
state.moodRecords[date] = [];
|
||||
}
|
||||
|
||||
// 检查是否已存在相同 ID 的记录
|
||||
const existingIndex = state.moodRecords[date].findIndex(r => r.id === checkin.id);
|
||||
if (existingIndex >= 0) {
|
||||
state.moodRecords[date][existingIndex] = checkin;
|
||||
} else {
|
||||
state.moodRecords[date].push(checkin);
|
||||
}
|
||||
});
|
||||
})
|
||||
.addCase(fetchMoodHistory.rejected, (state, action) => {
|
||||
state.loading.history = false;
|
||||
state.error = action.error.message || '获取心情历史失败';
|
||||
});
|
||||
|
||||
// fetchMoodStatistics
|
||||
builder
|
||||
.addCase(fetchMoodStatistics.pending, (state) => {
|
||||
state.loading.statistics = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(fetchMoodStatistics.fulfilled, (state, action) => {
|
||||
state.loading.statistics = false;
|
||||
state.statistics = action.payload;
|
||||
})
|
||||
.addCase(fetchMoodStatistics.rejected, (state, action) => {
|
||||
state.loading.statistics = false;
|
||||
state.error = action.error.message || '获取心情统计失败';
|
||||
});
|
||||
|
||||
// createMoodRecord
|
||||
builder
|
||||
.addCase(createMoodRecord.pending, (state) => {
|
||||
state.loading.create = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(createMoodRecord.fulfilled, (state, action) => {
|
||||
state.loading.create = false;
|
||||
const newRecord = action.payload;
|
||||
const date = newRecord.checkinDate;
|
||||
|
||||
// 添加到对应日期的记录中
|
||||
if (!state.moodRecords[date]) {
|
||||
state.moodRecords[date] = [];
|
||||
}
|
||||
state.moodRecords[date].unshift(newRecord); // 添加到开头
|
||||
|
||||
// 如果是当前选中的日期,更新选中的记录
|
||||
if (date === state.selectedDate) {
|
||||
state.selectedMoodRecord = newRecord;
|
||||
}
|
||||
})
|
||||
.addCase(createMoodRecord.rejected, (state, action) => {
|
||||
state.loading.create = false;
|
||||
state.error = action.error.message || '创建心情记录失败';
|
||||
});
|
||||
|
||||
// updateMoodRecord
|
||||
builder
|
||||
.addCase(updateMoodRecord.pending, (state) => {
|
||||
state.loading.update = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(updateMoodRecord.fulfilled, (state, action) => {
|
||||
state.loading.update = false;
|
||||
const updatedRecord = action.payload;
|
||||
const date = updatedRecord.checkinDate;
|
||||
|
||||
// 更新对应日期的记录
|
||||
if (state.moodRecords[date]) {
|
||||
const index = state.moodRecords[date].findIndex(r => r.id === updatedRecord.id);
|
||||
if (index >= 0) {
|
||||
state.moodRecords[date][index] = updatedRecord;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是当前选中的记录,更新选中的记录
|
||||
if (state.selectedMoodRecord?.id === updatedRecord.id) {
|
||||
state.selectedMoodRecord = updatedRecord;
|
||||
}
|
||||
})
|
||||
.addCase(updateMoodRecord.rejected, (state, action) => {
|
||||
state.loading.update = false;
|
||||
state.error = action.error.message || '更新心情记录失败';
|
||||
});
|
||||
|
||||
// deleteMoodRecord
|
||||
builder
|
||||
.addCase(deleteMoodRecord.pending, (state) => {
|
||||
state.loading.delete = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(deleteMoodRecord.fulfilled, (state, action) => {
|
||||
state.loading.delete = false;
|
||||
const deletedId = action.payload;
|
||||
|
||||
// 从所有日期的记录中删除
|
||||
Object.keys(state.moodRecords).forEach(date => {
|
||||
state.moodRecords[date] = state.moodRecords[date].filter(r => r.id !== deletedId);
|
||||
});
|
||||
|
||||
// 如果是当前选中的记录被删除,清空选中的记录
|
||||
if (state.selectedMoodRecord?.id === deletedId) {
|
||||
state.selectedMoodRecord = null;
|
||||
}
|
||||
})
|
||||
.addCase(deleteMoodRecord.rejected, (state, action) => {
|
||||
state.loading.delete = false;
|
||||
state.error = action.error.message || '删除心情记录失败';
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// 导出 actions
|
||||
export const {
|
||||
setSelectedDate,
|
||||
setSelectedMoodRecord,
|
||||
clearError,
|
||||
clearStatistics,
|
||||
clearMoodData,
|
||||
} = moodSlice.actions;
|
||||
|
||||
// 导出 reducer
|
||||
export default moodSlice.reducer;
|
||||
|
||||
// 导出选择器
|
||||
export const selectMoodState = (state: { mood: MoodState }) => state.mood;
|
||||
export const selectMoodRecords = (state: { mood: MoodState }) => state.mood.moodRecords;
|
||||
export const selectSelectedDate = (state: { mood: MoodState }) => state.mood.selectedDate;
|
||||
export const selectSelectedMoodRecord = (state: { mood: MoodState }) => state.mood.selectedMoodRecord;
|
||||
export const selectMoodLoading = (state: { mood: MoodState }) => state.mood.loading;
|
||||
export const selectMoodError = (state: { mood: MoodState }) => state.mood.error;
|
||||
export const selectMoodStatistics = (state: { mood: MoodState }) => state.mood.statistics;
|
||||
|
||||
// 获取指定日期的心情记录
|
||||
export const selectMoodRecordsByDate = (date: string) => (state: { mood: MoodState }) => {
|
||||
return state.mood.moodRecords[date] || [];
|
||||
};
|
||||
|
||||
// 获取指定日期的最新心情记录
|
||||
export const selectLatestMoodRecordByDate = (date: string) => (state: { mood: MoodState }) => {
|
||||
const records = state.mood.moodRecords[date] || [];
|
||||
return records.length > 0 ? records[0] : null;
|
||||
};
|
||||
Reference in New Issue
Block a user