You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI Charts日期筛选问题:特定月份周度支出图表实现

解决方案

一、实现特定月份支出按周拆分展示

要实现按周拆分展示特定月份的支出,需先筛选目标月份数据,再按周分组统计总支出,最后用分组后的数据渲染图表。

1. 筛选目标月份的支付数据

先定义工具方法获取目标月份的起止日期:

extension Date {
    func startOfMonth() -> Date {
        Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: self))!
    }
    
    func endOfMonth() -> Date {
        Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: self.startOfMonth())!
    }
    
    // 获取上月起止日期
    func startOfPreviousMonth() -> Date {
        Calendar.current.date(byAdding: .month, value: -1, to: self.startOfMonth())!
    }
    
    func endOfPreviousMonth() -> Date {
        Calendar.current.date(byAdding: DateComponents(day: -1), to: self.startOfMonth())!
    }
}

在ViewModel中添加筛选计算属性,比如获取当月数据:

var currentMonthPayments: [Payment] {
    let today = Date()
    let start = today.startOfMonth()
    let end = today.endOfMonth()
    return payments.filter {
        $0.paymentDate >= start && $0.paymentDate <= end
    }
}

2. 按周分组统计总支出

将筛选后的数据按周分组,计算每周总金额:

struct WeeklyExpense: Identifiable {
    let id = UUID()
    let weekStart: Date
    let totalAmount: Double
}

var currentMonthWeeklyExpenses: [WeeklyExpense] {
    let calendar = Calendar.current
    // 按周分组
    let grouped = Dictionary(grouping: currentMonthPayments) { payment in
        calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: payment.paymentDate)
    }
    
    // 转换为有序的WeeklyExpense数组
    return grouped.map { (components, payments) in
        let weekStart = calendar.date(from: components)!
        let total = payments.reduce(0) { $0 + $1.paymentAmount }
        return WeeklyExpense(weekStart: weekStart, totalAmount: total)
    }.sorted { $0.weekStart < $1.weekStart }
}

3. 修改Chart代码

用分组后的数据渲染图表,确保每个Bar对应一周的总支出:

Chart {
    ForEach(paymentModel.currentMonthWeeklyExpenses) { expense in
        BarMark(
            x: .value("Week", expense.weekStart, unit: .weekOfMonth),
            y: .value("Total Amount", expense.totalAmount)
        )
    }
}
.frame(height: 180)
.padding()
.chartXAxis {
    AxisMarks() { date in
        AxisValueLabel(format: .dateTime.week(.defaultDigits), centered: true)
    }
}
.onAppear {
    paymentModel.getCurrentMonthPayments() // 替换为下文的筛选查询方法
}

二、Firestore按日期/类别筛选数据

避免全量获取数据,直接在Firestore查询阶段添加过滤条件,减少数据传输。

1. 按日期范围筛选

查询当月支付数据:

func getCurrentMonthPayments() {
    let today = Date()
    let startOfMonth = today.startOfMonth()
    let endOfMonth = today.endOfMonth()
    
    // Firestore日期范围查询
    db.collection("payments")
        .whereField("paymentDate", isGreaterThanOrEqualTo: startOfMonth)
        .whereField("paymentDate", isLessThanOrEqualTo: endOfMonth)
        .getDocuments { snapshot, error in
            if let error = error {
                print("获取数据失败:\(error)")
                return
            }
            // 解析数据到payments数组
            self.payments = snapshot?.documents.compactMap { doc in
                try? doc.data(as: Payment.self)
            } ?? []
        }
}

2. 按类别筛选

查询特定类别的支付数据:

func getPaymentsByCategory(_ category: String) {
    db.collection("payments")
        .whereField("category", isEqualTo: category)
        .getDocuments { snapshot, error in
            // 解析逻辑同上
        }
}

3. 组合筛选(日期+类别)

同时按日期范围和类别筛选:

func getCurrentMonthPaymentsByCategory(_ category: String) {
    let today = Date()
    let startOfMonth = today.startOfMonth()
    let endOfMonth = today.endOfMonth()
    
    db.collection("payments")
        .whereField("paymentDate", isGreaterThanOrEqualTo: startOfMonth)
        .whereField("paymentDate", isLessThanOrEqualTo: endOfMonth)
        .whereField("category", isEqualTo: category)
        .getDocuments { snapshot, error in
            // 解析逻辑同上
        }
}

注意:确保Firestore中paymentDate字段存储为Timestamp类型,而非字符串,否则无法正确执行日期范围查询。

内容的提问来源于stack exchange,提问作者H121

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 11:00:31