Files
EmailBill/.sisyphus/notepads/statistics-year-selection/learnings.md
SunCheng e93c3d6bae
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 27s
Docker Build & Deploy / Deploy to Production (push) Successful in 9s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 2s
Docker Build & Deploy / WeChat Notification (push) Successful in 2s
测试覆盖率
2026-01-28 17:00:58 +08:00

6.7 KiB
Raw Blame History

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

// 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

// 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