Files
kangkang/康康/Features/Home/HomeView.swift
link2026 910ca99f21 feat(me,home): 模型推理自检 + 启动容错 + 首页假数据清理
- KangkangApp: ModelContainer 创建失败时重置本地 store 重建,
  避免 demo 阶段 schema 演进导致旧真机启动崩溃(注:生产需正式迁移)
- ModelSelfTestView: 正式的推理自检页(固定 prompt + 流式输出 + tok/s),
  仅当 LLM 模型就绪时从「模型管理」出现入口
- 删除 DEBUG-only 的 DebugAIRunner,自检转正为就绪后可见的正式入口
- HomeView: 删除写死的「今日摘记」假数据卡;问候改为按时段动态
  (早安/下午好/晚上好)+ 当天日期;影像档案数字接真实 @Query 计数
- MeView: 模型管理卡动态状态 + 关于页接真实版本号(用户改动一并纳入)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 08:02:35 +08:00

165 lines
5.4 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
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(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 f = DateFormatter()
f.locale = Locale(identifier: "zh_CN")
f.dateFormat = "M 月 d 日 · EEE"
return f.string(from: Date())
}
private var greetingWord: String {
switch Calendar.current.component(.hour, from: Date()) {
case 5..<12: return "早安"
case 12..<18: return "下午好"
default: return "晚上好"
}
}
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: "档案 · \(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()
}