import SwiftUI import SwiftData struct HomeView: View { var onTapArchive: () -> Void = {} @Query(sort: \Indicator.capturedAt, order: .reverse) private var indicators: [Indicator] @Query(sort: \Report.reportDate, order: .reverse) private var reports: [Report] @Query(sort: \DiaryEntry.createdAt, order: .reverse) private var diaries: [DiaryEntry] @Query(sort: \Symptom.startedAt, order: .reverse) private var symptoms: [Symptom] /// 点「最近记录」某行 → 打开只读详情 sheet(与档案库 C1 同款交互)。 @State private var selectedEntry: TimelineEntry? @MainActor private var recentEntries: [TimelineEntry] { let all = TimelineEntry.from(indicators: indicators) + reports.map(TimelineEntry.from(report:)) + diaries.map(TimelineEntry.from(diary:)) + symptoms.map(TimelineEntry.from(symptom:)) return all.sorted { $0.date > $1.date }.prefix(6).map { $0 } } private var recentGrouped: [(section: DateSection, items: [TimelineEntry])] { TimelineGrouping.group(recentEntries) } var body: some View { ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 0) { greeting .padding(.top, 4) .padding(.bottom, 18) TodayRemindersCard() OngoingSymptomsCard() .padding(.bottom, 18) recentSection .padding(.bottom, 22) archiveSection } .padding(.horizontal, 20) .padding(.bottom, 20) } .background(Tj.Palette.sand.ignoresSafeArea()) .sheet(item: $selectedEntry) { entry in if let d = TimelineDetail.resolve( for: entry, indicators: indicators, reports: reports, diaries: diaries, symptoms: symptoms ) { TimelineEntryDetailView(detail: d) } } } private var greeting: some View { HStack(alignment: .top) { VStack(alignment: .leading, spacing: 4) { Text(todayLine) .font(.system(size: 12)) .tracking(1) .foregroundStyle(Tj.Palette.text3) Text(greetingWord) .font(.tjTitle()) .foregroundStyle(Tj.Palette.text) } Spacer() TjLockChip() .padding(.top, 4) } } private var todayLine: String { let now = Date() let day = now.formatted(.dateTime.month().day()) let weekday = now.formatted(.dateTime.weekday(.abbreviated)) return "\(day) · \(weekday)" } private var greetingWord: String { switch Calendar.current.component(.hour, from: Date()) { case 5..<12: return String(appLoc: "早安") case 12..<18: return String(appLoc: "下午好") default: return String(appLoc: "晚上好") } } private var recentSection: some View { VStack(alignment: .leading, spacing: 10) { HStack(alignment: .lastTextBaseline) { Text("最近记录").font(.tjH2()).foregroundStyle(Tj.Palette.text) Spacer() Button(action: onTapArchive) { Text("全部 ›") .font(.system(size: 12)) .foregroundStyle(Tj.Palette.text3) } .buttonStyle(.plain) } if recentEntries.isEmpty { emptyRecent } else { VStack(alignment: .leading, spacing: 14) { ForEach(recentGrouped, id: \.section) { group in VStack(alignment: .leading, spacing: 8) { Text(group.section.label) .font(.system(size: 11, weight: .semibold)) .tracking(0.5) .foregroundStyle(Tj.Palette.text3) VStack(spacing: 10) { ForEach(group.items) { entry in Button { if TimelineDetail.resolve( for: entry, indicators: indicators, reports: reports, diaries: diaries, symptoms: symptoms ) != nil { selectedEntry = entry } } label: { TimelineRow(entry: entry) } .buttonStyle(.plain) } } } } } } } } private var emptyRecent: some View { HStack { Text("还没有任何记录,点底部 + 号开始第一条") .font(.system(size: 13)) .foregroundStyle(Tj.Palette.text3) Spacer() } .padding(.vertical, 14) .padding(.horizontal, 16) .tjCard(bordered: true) } private var archiveSection: some View { VStack(alignment: .leading, spacing: 10) { Text("影像档案").font(.tjH2()).foregroundStyle(Tj.Palette.text) Button(action: onTapArchive) { HStack(spacing: 14) { TjPlaceholder(label: String(appLoc: "档案 · \(reports.count)")) .frame(width: 56, height: 56) VStack(alignment: .leading, spacing: 2) { Text("我的报告档案") .font(.system(size: 14, weight: .semibold)) .foregroundStyle(Tj.Palette.text) Text("\(reports.count) 份 · \(indicators.count) 项指标 · 端侧加密") .font(.system(size: 11)) .foregroundStyle(Tj.Palette.text3) } Spacer() Image(systemName: "chevron.right") .font(.system(size: 14, weight: .medium)) .foregroundStyle(Tj.Palette.text3) } .padding(14) .tjCard(bordered: true) } .buttonStyle(.plain) } } } #Preview { HomeView() }