Files
kangkang/康康/Features/Trends/CalendarMarkers.swift
link2026 37b47b2076 docs(claude): sync §5/§7/§10 with Monitor+Profile; fix SeriesBucket SwiftData import
- §5 schema 重写为 7 @Model 完整列表(含 UserProfile + Indicator.seriesKey)
- §7 IA 改成 5 槽 TabBar(2 内容 + 中间 + + 2 设置),记录入口 5 个 kind
- §10.6 红线例外清单加 Monitor + Profile(Symptom 也补上)
- SeriesBucket.swift 缺 import SwiftData(persistentModelID 报错)

全套测试 50 case pass / 0 fail / 0 warning。
2026-05-26 07:53:16 +08:00

109 lines
3.1 KiB
Swift

import SwiftUI
import SwiftData
import Foundation
struct SymptomRange: Identifiable, Hashable {
let id: String
let name: String
let startDay: Date
let endDay: Date
let severity: Int
let isOngoing: Bool
var color: Color {
switch severity {
case 1, 2: return Tj.Palette.leaf
case 3: return Tj.Palette.amber
default: return Tj.Palette.brick
}
}
func contains(_ day: Date, calendar: Calendar = .current) -> Bool {
let d = calendar.startOfDay(for: day)
return d >= startDay && d <= endDay
}
func position(_ day: Date, calendar: Calendar = .current) -> Position {
let d = calendar.startOfDay(for: day)
let isStart = d == startDay
let isEnd = d == endDay
if isStart && isEnd { return .single }
if isStart { return .start }
if isEnd { return .end }
return .middle
}
enum Position { case single, start, middle, end }
}
struct DayMarks: Hashable {
var abnormalCount: Int = 0
var normalCount: Int = 0
var reportCount: Int = 0
var diaryCount: Int = 0
var hasAnyEvent: Bool {
abnormalCount + normalCount + reportCount + diaryCount > 0
}
}
struct CalendarData {
let dayMarks: [Date: DayMarks]
let symptomRanges: [SymptomRange]
func marks(for day: Date, calendar: Calendar = .current) -> DayMarks {
dayMarks[calendar.startOfDay(for: day)] ?? DayMarks()
}
func ranges(touching day: Date, calendar: Calendar = .current) -> [SymptomRange] {
symptomRanges.filter { $0.contains(day, calendar: calendar) }
}
static func build(indicators: [Indicator],
reports: [Report],
diaries: [DiaryEntry],
symptoms: [Symptom],
now: Date = .now,
calendar: Calendar = .current) -> CalendarData {
var buckets: [Date: DayMarks] = [:]
for i in indicators {
let day = calendar.startOfDay(for: i.capturedAt)
var m = buckets[day] ?? DayMarks()
if i.status == .normal {
m.normalCount += 1
} else {
m.abnormalCount += 1
}
buckets[day] = m
}
for r in reports {
let day = calendar.startOfDay(for: r.reportDate)
var m = buckets[day] ?? DayMarks()
m.reportCount += 1
buckets[day] = m
}
for d in diaries {
let day = calendar.startOfDay(for: d.createdAt)
var m = buckets[day] ?? DayMarks()
m.diaryCount += 1
buckets[day] = m
}
let ranges: [SymptomRange] = symptoms.map { s in
SymptomRange(
id: "\(s.persistentModelID)",
name: s.name,
startDay: calendar.startOfDay(for: s.startedAt),
endDay: calendar.startOfDay(for: s.endedAt ?? now),
severity: s.severity,
isOngoing: s.isOngoing
)
}
return CalendarData(dayMarks: buckets, symptomRanges: ranges)
}
}