import SwiftUI import SwiftData /// 趋势 Tab。日历已迁至主页;此页专注「时间序列」: /// 任何出现 ≥2 次的指标都能成趋势,分「长期监测」(seriesKey)与「化验指标」(按名归并)两段。 struct TrendsView: View { @Query(sort: \Indicator.capturedAt, order: .reverse) private var indicators: [Indicator] @Query private var profiles: [UserProfile] @Query private var customMetrics: [CustomMonitorMetric] private var profile: UserProfile? { profiles.first } private var seriesBuckets: [SeriesBucket] { SeriesBucket.build(from: indicators, profile: profile, customMetrics: customMetrics) } private var monitorBuckets: [SeriesBucket] { seriesBuckets.filter { $0.kind == .monitor } } private var labBuckets: [SeriesBucket] { seriesBuckets.filter { $0.kind == .lab } } var body: some View { NavigationStack { ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 18) { header.padding(.top, 4) if seriesBuckets.isEmpty { emptyState } else { if !monitorBuckets.isEmpty { section(title: String(appLoc: "长期监测"), buckets: monitorBuckets) } if !labBuckets.isEmpty { section(title: String(appLoc: "化验指标趋势"), buckets: labBuckets) } } } .padding(.horizontal, 20) .padding(.bottom, 24) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) .background(Tj.Palette.sand.ignoresSafeArea()) .navigationBarHidden(true) } } private var header: some View { Text("趋势") .font(.tjTitle(26)) .foregroundStyle(Tj.Palette.text) } private func section(title: String, buckets: [SeriesBucket]) -> some View { VStack(alignment: .leading, spacing: 12) { HStack(alignment: .lastTextBaseline) { Text(title) .font(.tjH2()) .foregroundStyle(Tj.Palette.text) Text("\(buckets.count) 项") .font(.tjScaled( 12)) .foregroundStyle(Tj.Palette.text3) Spacer() } VStack(spacing: 12) { ForEach(buckets) { bucket in NavigationLink { TrendDetailView(bucket: bucket) } label: { TrendRow(bucket: bucket) } .buttonStyle(.plain) } } } } private var emptyState: some View { VStack(spacing: 12) { TjPlaceholder(label: String(appLoc: "还没有可成趋势的指标")) .frame(height: 120) .frame(maxWidth: 260) Text("同一指标记录满 2 次后,会在这里出现时间序列") .font(.tjScaled( 12)) .foregroundStyle(Tj.Palette.text3) .multilineTextAlignment(.center) } .frame(maxWidth: .infinity) .padding(.top, 60) } } #Preview { TrendsView() .modelContainer(for: [ Indicator.self, Report.self, DiaryEntry.self, Symptom.self, Asset.self ], inMemory: true) }