#if os(iOS) import SwiftUI import Charts struct StatsView: View { let index: BundleIndex struct DayVal: Identifiable { let id = UUID(); let label: String; let value: Int } struct MonthVal: Identifiable { let id = UUID(); let label: String; let value: Int } struct YearVal: Identifiable { let id = UUID(); let label: String; let hours: Double } var weekSeries: [DayVal] { let arr = (index.stats.global.week_total_minutes, index.stats.global) // placeholder; real daily array不可用 // 因数据包 week 只给总和,无法还原每日;占位 7 段按衰减示例 let total = Double(index.stats.global.week_total_minutes) return (0..<7).map { i in let frac = (7.0-Double(i))*1.0/28.0 + 0.05 return DayVal(label: ["今","昨","2","3","4","5","6"][i], value: Int(total * frac / 2.0)) } } var body: some View { ScrollView { VStack(alignment:.leading, spacing: 24) { Text("阅读统计").font(.title2).bold().padding(.top) // 气泡指标 let g = index.stats.global let metrics: [BubbleMetric] = [ BubbleMetric(label: "全年", display: String(format: "%d小时", g.year_total_minutes/60), valueMinutes: Double(g.year_total_minutes), color: .blue), BubbleMetric(label: "月均", display: String(format: "%d分钟", g.month_avg_minutes), valueMinutes: Double(g.month_avg_minutes), color: .purple), BubbleMetric(label: "近7天", display: String(format: "%d分钟", g.week_total_minutes), valueMinutes: Double(g.week_total_minutes), color: .pink), BubbleMetric(label: "日均", display: String(format: "%d分钟", g.day_avg_minutes), valueMinutes: Double(g.day_avg_minutes), color: .yellow), BubbleMetric(label: "已读", display: String(format: "%d本", g.finished_books_count), valueMinutes: Double(g.finished_books_count*60), color: .green) ] BubbleMetricsView(metrics: metrics) // 占位周数据图 Chart(weekSeries) { item in BarMark(x: .value("Day", item.label), y: .value("Min", item.value)) }.frame(height: 180) Text("说明: 因数据包未携带逐日数组,这里周图为占位模拟。可扩展导出添加 daily 数组。") .font(.footnote).foregroundColor(.secondary) // 年度平均小时散点(使用 readtime_year) let yearHours = Double(g.year_total_minutes)/60.0 HStack { Text("全年总时长约 \(String(format: "%.1f", yearHours)) 小时").font(.headline); Spacer() } } .padding(.horizontal) } } } #endif