# Learnings - Statistics Year Selection Enhancement ## [2026-01-28] Initial Analysis ### Current Implementation - **File**: `Web/src/views/StatisticsView.vue` - **Current picker**: `columns-type="['year', 'month']` (year-month only) - **State variables**: - `currentYear` - integer year - `currentMonth` - integer month (1-12) - `selectedDate` - array `['YYYY', 'MM']` for picker - **API calls**: All endpoints use `{ year, month }` parameters ### Vant UI Year-Only Pattern - **Key prop**: `columns-type="['year']"` - **Picker value**: Single-element array `['YYYY']` - **Confirmation**: `selectedValues[0]` contains year string ### Implementation Strategy 1. Add UI toggle to switch between year-month and year-only modes 2. When year-only selected, set `currentMonth = 0` or null to indicate full year 3. Backend API already supports year-only queries (when month=0 or null) 4. Update display logic to show "YYYY年" vs "YYYY年MM月" ### API Compatibility - CRITICAL FINDING - **Backend limitation**: `TransactionRecordRepository.BuildQuery()` (lines 81-86) requires BOTH year AND month - Current logic: `if (year.HasValue && month.HasValue)` - year-only queries are NOT supported - **Must modify repository** to support year-only queries: - When year provided but month is null/0: query entire year (Jan 1 to Dec 31) - When both year and month provided: query specific month (current behavior) - All statistics endpoints use `QueryAsync(year, month, ...)` pattern ### Required Backend Changes **File**: `Repository/TransactionRecordRepository.cs` **Method**: `BuildQuery()` lines 81-86 **Change**: Modify year/month filtering logic to support year-only queries ```csharp // Current (line 81-86): if (year.HasValue && month.HasValue) { var dateStart = new DateTime(year.Value, month.Value, 1); var dateEnd = dateStart.AddMonths(1); query = query.Where(t => t.OccurredAt >= dateStart && t.OccurredAt < dateEnd); } // Needed: if (year.HasValue) { if (month.HasValue && month.Value > 0) { // Specific month var dateStart = new DateTime(year.Value, month.Value, 1); var dateEnd = dateStart.AddMonths(1); query = query.Where(t => t.OccurredAt >= dateStart && t.OccurredAt < dateEnd); } else { // Entire year var dateStart = new DateTime(year.Value, 1, 1); var dateEnd = new DateTime(year.Value + 1, 1, 1); query = query.Where(t => t.OccurredAt >= dateStart && t.OccurredAt < dateEnd); } } ``` ### Existing Patterns - BudgetView.vue uses same year-month picker pattern - Dayjs used for all date formatting: `dayjs().format('YYYY-MM-DD')` - Date picker values always arrays for Vant UI ## [2026-01-28] Repository BuildQuery() Enhancement ### Implementation Completed - **File Modified**: `Repository/TransactionRecordRepository.cs` lines 81-94 - **Change**: Updated year/month filtering logic to support year-only queries ### Logic Changes ```csharp // Old: Required both year AND month if (year.HasValue && month.HasValue) { ... } // New: Support year-only queries if (year.HasValue) { if (month.HasValue && month.Value > 0) { // 查询指定年月 var dateStart = new DateTime(year.Value, month.Value, 1); var dateEnd = dateStart.AddMonths(1); query = query.Where(t => t.OccurredAt >= dateStart && t.OccurredAt < dateEnd); } else { // 查询整年数据(1月1日到下年1月1日) var dateStart = new DateTime(year.Value, 1, 1); var dateEnd = new DateTime(year.Value + 1, 1, 1); query = query.Where(t => t.OccurredAt >= dateStart && t.OccurredAt < dateEnd); } } ``` ### Behavior - **Month-specific** (month.HasValue && month.Value > 0): Query from 1st of month to 1st of next month - **Year-only** (month is null or 0): Query from Jan 1 to Jan 1 of next year - **No year provided**: No date filtering applied ### Verification - All 14 tests pass: `dotnet test WebApi.Test/WebApi.Test.csproj` - No breaking changes to existing functionality - Chinese comments added for business logic clarity ### Key Pattern - Use `month.Value > 0` check to distinguish year-only (0/null) from month-specific (1-12) - Date range is exclusive on upper bound (`< dateEnd`) to avoid including boundary dates ## [2026-01-28] Frontend Year-Only Selection Implementation ### Changes Made **File**: `Web/src/views/StatisticsView.vue` #### 1. Nav Bar Title Display (Line 12) - Updated to show "YYYY年" when `currentMonth === 0` - Shows "YYYY年MM月" when month is selected - Template: `{{ currentMonth === 0 ? \`${currentYear}年\` : \`${currentYear}年${currentMonth}月\` }}` #### 2. Date Picker Popup (Lines 268-289) - Added toggle switch using `van-tabs` component - Two modes: "按月" (month) and "按年" (year) - Tabs positioned above the date picker - Dynamic `columns-type` based on selection mode: - Year mode: `['year']` - Month mode: `['year', 'month']` #### 3. State Management (Line 347) - Added `dateSelectionMode` ref: `'month'` | `'year'` - Default: `'month'` for backward compatibility - `currentMonth` set to `0` when year-only selected #### 4. Confirmation Handler (Lines 532-544) - Updated to handle both year-only and year-month modes - When year mode: `newMonth = 0` - When month mode: `newMonth = parseInt(selectedValues[1])` #### 5. API Calls (All Statistics Endpoints) - Updated all API calls to use `month: currentMonth.value || 0` - Ensures backend receives `0` for year-only queries - Modified functions: - `fetchMonthlyData()` (line 574) - `fetchCategoryData()` (lines 592, 610, 626) - `fetchDailyData()` (line 649) - `fetchBalanceData()` (line 672) - `loadCategoryBills()` (line 1146) #### 6. Mode Switching Watcher (Lines 1355-1366) - Added `watch(dateSelectionMode)` to update `selectedDate` array - When switching to year mode: `selectedDate = [year.toString()]` - When switching to month mode: `selectedDate = [year, month]` #### 7. Styling (Lines 1690-1705) - Added `.date-picker-header` styles for tabs - Clean, minimal design matching Vant UI conventions - Proper spacing and background colors ### Vant UI Patterns Used - **van-tabs**: For mode switching toggle - **van-date-picker**: Dynamic `columns-type` prop - **van-popup**: Container for picker and tabs - Composition API with `watch` for reactive updates ### User Experience 1. Click nav bar date → popup opens with "按月" default 2. Switch to "按年" → picker shows only year column 3. Select year and confirm → `currentMonth = 0` 4. Nav bar shows "2025年" instead of "2025年1月" 5. All statistics refresh with year-only data ### Verification - Build succeeds: `cd Web && pnpm build` - No TypeScript errors - No breaking changes to existing functionality - Backward compatible with month-only selection