缺少代码差异信息,无法生成具体的commit message。
请提供 "code differences" 的具体内容,以便我能够根据代码变更情况生成符合 Angular 规范的中文 commit message。
This commit is contained in:
176
康康/Features/Home/HomeCalendarCard.swift
Normal file
176
康康/Features/Home/HomeCalendarCard.swift
Normal file
@@ -0,0 +1,176 @@
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
|
||||
/// 主页「健康日历」卡:当前一周横条 + 本月记录摘要。
|
||||
/// 点整卡或某一天 → 打开 CalendarOverviewView 看月/年总览。自包含 @Query(对齐 TodayRemindersCard)。
|
||||
struct HomeCalendarCard: View {
|
||||
@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]
|
||||
|
||||
/// 打开总览时定位的日期(nil = 不展示)。
|
||||
@State private var openDay: SelectedDay?
|
||||
|
||||
private let calendar: Calendar = {
|
||||
var c = Calendar(identifier: .gregorian)
|
||||
c.firstWeekday = 2
|
||||
c.locale = Locale.current
|
||||
return c
|
||||
}()
|
||||
|
||||
@MainActor
|
||||
private var data: CalendarData {
|
||||
CalendarData.build(
|
||||
indicators: indicators,
|
||||
reports: reports,
|
||||
diaries: diaries,
|
||||
symptoms: symptoms
|
||||
)
|
||||
}
|
||||
|
||||
/// 本周一 → 本周日。
|
||||
private var weekDays: [Date] {
|
||||
let today = calendar.startOfDay(for: .now)
|
||||
let weekdayIndex = (calendar.component(.weekday, from: today) - calendar.firstWeekday + 7) % 7
|
||||
guard let monday = calendar.date(byAdding: .day, value: -weekdayIndex, to: today) else {
|
||||
return []
|
||||
}
|
||||
return (0..<7).compactMap { calendar.date(byAdding: .day, value: $0, to: monday) }
|
||||
}
|
||||
|
||||
/// 本月有记录的天数(指标/报告/日记/症状任一)。
|
||||
private var daysWithRecordsThisMonth: Int {
|
||||
guard let interval = calendar.dateInterval(of: .month, for: .now) else { return 0 }
|
||||
let count = calendar.range(of: .day, in: .month, for: .now)?.count ?? 30
|
||||
var n = 0
|
||||
for i in 0..<count {
|
||||
guard let d = calendar.date(byAdding: .day, value: i, to: interval.start) else { continue }
|
||||
if data.marks(for: d, calendar: calendar).hasAnyEvent ||
|
||||
!data.ranges(touching: d, calendar: calendar).isEmpty {
|
||||
n += 1
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
header
|
||||
weekStrip
|
||||
}
|
||||
.padding(14)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.tjCard(bordered: true)
|
||||
.padding(.bottom, 18)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture { openDay = SelectedDay(date: .now) }
|
||||
.fullScreenCover(item: $openDay) { day in
|
||||
CalendarOverviewView(initialDate: day.date, onClose: { openDay = nil })
|
||||
}
|
||||
}
|
||||
|
||||
private var header: some View {
|
||||
HStack(alignment: .firstTextBaseline) {
|
||||
Text("健康日历")
|
||||
.font(.tjH2())
|
||||
.foregroundStyle(Tj.Palette.text)
|
||||
Spacer()
|
||||
HStack(spacing: 3) {
|
||||
Text(summaryLine)
|
||||
.font(.system(size: 12))
|
||||
.foregroundStyle(Tj.Palette.text3)
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.system(size: 11, weight: .semibold))
|
||||
.foregroundStyle(Tj.Palette.text3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var summaryLine: String {
|
||||
let n = daysWithRecordsThisMonth
|
||||
return n > 0 ? String(appLoc: "本月 \(n) 天有记录") : String(appLoc: "本月暂无记录")
|
||||
}
|
||||
|
||||
private var weekStrip: some View {
|
||||
HStack(spacing: 6) {
|
||||
ForEach(weekDays, id: \.self) { day in
|
||||
dayCell(day)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func dayCell(_ day: Date) -> some View {
|
||||
let marks = data.marks(for: day, calendar: calendar)
|
||||
let ranges = data.ranges(touching: day, calendar: calendar)
|
||||
let isToday = calendar.isDateInToday(day)
|
||||
let hasSymptom = !ranges.isEmpty
|
||||
|
||||
return Button {
|
||||
openDay = SelectedDay(date: day)
|
||||
} label: {
|
||||
VStack(spacing: 5) {
|
||||
Text(weekdayLabel(day))
|
||||
.font(.system(size: 10, weight: .medium))
|
||||
.foregroundStyle(Tj.Palette.text3)
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: 9, style: .continuous)
|
||||
.fill(cellFill(isToday: isToday, hasSymptom: hasSymptom))
|
||||
if isToday {
|
||||
RoundedRectangle(cornerRadius: 9, style: .continuous)
|
||||
.strokeBorder(Tj.Palette.ink, lineWidth: 1.2)
|
||||
}
|
||||
Text("\(calendar.component(.day, from: day))")
|
||||
.font(.system(size: 14, weight: isToday ? .bold : .regular))
|
||||
.foregroundStyle(isToday ? Tj.Palette.ink : Tj.Palette.text)
|
||||
}
|
||||
.frame(height: 38)
|
||||
marksDots(marks)
|
||||
.frame(height: 5)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func marksDots(_ marks: DayMarks) -> some View {
|
||||
HStack(spacing: 2) {
|
||||
if marks.abnormalCount > 0 {
|
||||
dot(Tj.Palette.brick)
|
||||
} else if marks.normalCount > 0 {
|
||||
dot(Tj.Palette.leaf)
|
||||
}
|
||||
if marks.reportCount > 0 { dot(Tj.Palette.ink2) }
|
||||
if marks.diaryCount > 0 { dot(Tj.Palette.text3.opacity(0.7)) }
|
||||
}
|
||||
}
|
||||
|
||||
private func dot(_ color: Color) -> some View {
|
||||
Circle().fill(color).frame(width: 4, height: 4)
|
||||
}
|
||||
|
||||
private func cellFill(isToday: Bool, hasSymptom: Bool) -> Color {
|
||||
if hasSymptom { return Tj.Palette.amber.opacity(0.18) }
|
||||
if isToday { return Tj.Palette.sand2 }
|
||||
return Tj.Palette.sand2.opacity(0.5)
|
||||
}
|
||||
|
||||
private func weekdayLabel(_ day: Date) -> String {
|
||||
let labels = [
|
||||
String(appLoc: "一"), String(appLoc: "二"), String(appLoc: "三"),
|
||||
String(appLoc: "四"), String(appLoc: "五"), String(appLoc: "六"),
|
||||
String(appLoc: "日")
|
||||
]
|
||||
let idx = (calendar.component(.weekday, from: day) - calendar.firstWeekday + 7) % 7
|
||||
return labels[idx]
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,8 @@ struct HomeView: View {
|
||||
.padding(.top, 4)
|
||||
.padding(.bottom, 18)
|
||||
|
||||
HomeCalendarCard()
|
||||
|
||||
TodayRemindersCard()
|
||||
|
||||
OngoingSymptomsCard()
|
||||
|
||||
Reference in New Issue
Block a user