diff --git a/Repository/TransactionRecordRepository.cs b/Repository/TransactionRecordRepository.cs index 013682a..905e383 100644 --- a/Repository/TransactionRecordRepository.cs +++ b/Repository/TransactionRecordRepository.cs @@ -60,6 +60,14 @@ public interface ITransactionRecordRepository : IBaseRepository每天的消费笔数和金额 Task> GetDailyStatisticsAsync(int year, int month); + /// + /// 获取指定日期范围内的每日统计 + /// + /// 开始日期 + /// 结束日期 + /// 每天的消费笔数和金额 + Task> GetDailyStatisticsByRangeAsync(DateTime startDate, DateTime endDate); + /// /// 获取指定日期范围内的交易记录 /// @@ -342,6 +350,11 @@ public class TransactionRecordRepository(IFreeSql freeSql) : BaseRepository> GetDailyStatisticsByRangeAsync(DateTime startDate, DateTime endDate) + { var records = await FreeSql.Select() .Where(t => t.OccurredAt >= startDate && t.OccurredAt < endDate) .ToListAsync(); diff --git a/Web/src/App.vue b/Web/src/App.vue index c474b8f..b984c94 100644 --- a/Web/src/App.vue +++ b/Web/src/App.vue @@ -143,7 +143,6 @@ const setActive = (path) => { const isShowAddBill = computed(() => { return route.path === '/' - || route.path === '/calendar' || route.path === '/balance' || route.path === '/message' }) diff --git a/Web/src/api/statistics.js b/Web/src/api/statistics.js index 37447ae..cdffb56 100644 --- a/Web/src/api/statistics.js +++ b/Web/src/api/statistics.js @@ -88,3 +88,18 @@ export const getDailyStatistics = (params) => { params }) } + +/** + * 获取指定日期范围内的每日统计 + * @param {Object} params - 查询参数 + * @param {string} params.startDate - 开始日期 + * @param {string} params.endDate - 结束日期 + * @returns {Promise<{success: boolean, data: Array}>} + */ +export const getDailyStatisticsRange = (params) => { + return request({ + url: '/TransactionRecord/GetDailyStatisticsRange', + method: 'get', + params + }) +} diff --git a/Web/src/components/ContributionHeatmap.vue b/Web/src/components/ContributionHeatmap.vue new file mode 100644 index 0000000..d8a51a8 --- /dev/null +++ b/Web/src/components/ContributionHeatmap.vue @@ -0,0 +1,415 @@ + + + + + \ No newline at end of file diff --git a/Web/src/views/CalendarView.vue b/Web/src/views/CalendarView.vue index a453124..304bec9 100644 --- a/Web/src/views/CalendarView.vue +++ b/Web/src/views/CalendarView.vue @@ -10,6 +10,8 @@ @month-show="onMonthShow" @select="onDateSelect" /> + + { await nextTick(); setTimeout(() => { // 计算页面高度,滚动3/4高度以显示更多日期 - const height = document.querySelector(".calendar-container").clientHeight * 0.45; + const height = document.querySelector(".calendar-container").clientHeight * 0.55; document.querySelector(".van-calendar__body").scrollBy({ top: -height, behavior: "smooth", @@ -266,6 +270,7 @@ const onGlobalTransactionDeleted = () => { } const now = selectedDate.value || new Date() fetchDailyStatistics(now.getFullYear(), now.getMonth() + 1) + heatmapRef.value?.refresh() } window.addEventListener && window.addEventListener('transaction-deleted', onGlobalTransactionDeleted) @@ -281,6 +286,7 @@ const onGlobalTransactionsChanged = () => { } const now = selectedDate.value || new Date() fetchDailyStatistics(now.getFullYear(), now.getMonth() + 1) + heatmapRef.value?.refresh() } window.addEventListener && window.addEventListener('transactions-changed', onGlobalTransactionsChanged) @@ -304,10 +310,11 @@ onBeforeUnmount(() => { overflow: hidden; margin: 0; padding: 0; + background-color: var(--van-background); } .calendar-container :deep(.van-calendar) { - height: 100% !important; + height: auto !important; flex: 1; overflow: auto; margin: 0; diff --git a/WebApi/Controllers/TransactionRecordController.cs b/WebApi/Controllers/TransactionRecordController.cs index c66f573..a518496 100644 --- a/WebApi/Controllers/TransactionRecordController.cs +++ b/WebApi/Controllers/TransactionRecordController.cs @@ -284,6 +284,33 @@ public class TransactionRecordController( } } + /// + /// 获取指定日期范围内的每日统计 + /// + [HttpGet] + public async Task>> GetDailyStatisticsRangeAsync( + [FromQuery] DateTime startDate, + [FromQuery] DateTime endDate + ) + { + try + { + // 确保包含结束日期当天 + var effectiveEndDate = endDate.Date.AddDays(1); + var effectiveStartDate = startDate.Date; + + var statistics = await transactionRepository.GetDailyStatisticsByRangeAsync(effectiveStartDate, effectiveEndDate); + var result = statistics.Select(s => new DailyStatisticsDto(s.Key, s.Value.count, s.Value.amount)).ToList(); + + return result.Ok(); + } + catch (Exception ex) + { + logger.LogError(ex, "获取日历统计数据失败,开始: {StartDate}, 结束: {EndDate}", startDate, endDate); + return $"获取日历统计数据失败: {ex.Message}".Fail>(); + } + } + /// /// 获取月度统计数据 ///