feat: 更新心情记录功能和界面

- 调整启动画面中的图片宽度,提升视觉效果
- 移除引导页面相关组件,简化应用结构
- 新增心情统计页面,支持用户查看和分析心情数据
- 优化心情卡片组件,增强用户交互体验
- 更新登录页面标题,提升品牌一致性
- 新增心情日历和编辑功能,支持用户记录和管理心情
This commit is contained in:
richarjiang
2025-08-21 17:59:22 +08:00
parent a7607e0f74
commit 72e75b602e
24 changed files with 2964 additions and 1238 deletions

324
store/moodSlice.ts Normal file
View 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;
};