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
相关产品推荐
相关产品推荐

