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] @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) OngoingSymptomsCard() .padding(.bottom, 18) todaySummaryCard .padding(.bottom, 18) recentSection .padding(.bottom, 22) archiveSection } .padding(.horizontal, 20) .padding(.bottom, 20) } .background(Tj.Palette.sand.ignoresSafeArea()) } private var greeting: some View { HStack(alignment: .top) { VStack(alignment: .leading, spacing: 4) { Text("5 月 25 日 · 周一") .font(.system(size: 12)) .tracking(1) .foregroundStyle(Tj.Palette.text3) Text("早安,林意") .font(.tjTitle()) .foregroundStyle(Tj.Palette.text) } Spacer() TjLockChip() .padding(.top, 4) } } private var todaySummaryCard: some View { VStack(alignment: .leading, spacing: 0) { HStack(spacing: 10) { Text("今日 · 摘记") .font(.system(size: 12, weight: .semibold)) .tracking(0.3) .foregroundStyle(Tj.Palette.brick) .fixedSize() Rectangle() .fill(Tj.Palette.line) .frame(height: 1) Text("本机摘要") .font(.system(size: 11)) .foregroundStyle(Tj.Palette.text3) .fixedSize() } .padding(.bottom, 10) Text("上次体检后,\(Text("低密度脂蛋白").underline(color: Tj.Palette.brick).foregroundColor(Tj.Palette.text))持续偏高已 3 个月。建议本周记录一次空腹血脂。") .font(.tjSerifBody()) .foregroundStyle(Tj.Palette.text) .lineSpacing(6) .padding(.bottom, 14) HStack(spacing: 14) { Button("记录今日") {} .buttonStyle(TjPrimaryButton(height: 34, fontSize: 13, horizontalPadding: 14)) Button("查看趋势") {} .buttonStyle(TjGhostButton(height: 34, fontSize: 13, horizontalPadding: 14)) } } .padding(.leading, 20) .padding(.trailing, 18) .padding(.vertical, 18) .background( Tj.Palette.paper .overlay(alignment: .leading) { Tj.Palette.brick.frame(width: 3) } ) .clipShape(RoundedRectangle(cornerRadius: 2, style: .continuous)) .shadow(color: Color(red: 0.196, green: 0.157, blue: 0.098).opacity(0.06), radius: 0, x: 0, y: 1) } 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 TimelineRow(entry: entry) } } } } } } } } 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: "档案 · 12") .frame(width: 56, height: 56) VStack(alignment: .leading, spacing: 2) { Text("我的报告档案") .font(.system(size: 14, weight: .semibold)) .foregroundStyle(Tj.Palette.text) Text("12 份 · 218 项指标 · 端侧加密") .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() }