import SwiftUI import SwiftData struct SymptomEndSheet: View { @Environment(\.modelContext) private var ctx @Environment(\.dismiss) private var dismiss let symptom: Symptom @State private var endedAt: Date = .now private var lowerBound: Date { symptom.startedAt } private var durationLabel: String { let interval = max(0, endedAt.timeIntervalSince(lowerBound)) return formatDuration(interval) } var body: some View { VStack(spacing: 0) { Capsule() .fill(Tj.Palette.line) .frame(width: 40, height: 4) .padding(.top, 10) .padding(.bottom, 14) VStack(alignment: .leading, spacing: 18) { HStack { VStack(alignment: .leading, spacing: 4) { Text("结束症状") .font(.system(size: 12, weight: .semibold)) .tracking(0.3) .foregroundStyle(Tj.Palette.text3) Text(symptom.name) .font(.tjTitle(24)) .foregroundStyle(Tj.Palette.text) } Spacer() } VStack(alignment: .leading, spacing: 6) { Text("开始于") .font(.system(size: 12)) .foregroundStyle(Tj.Palette.text3) Text(symptom.startedAt.formatted(date: .abbreviated, time: .shortened)) .font(.system(size: 14, weight: .medium)) .foregroundStyle(Tj.Palette.text) } VStack(alignment: .leading, spacing: 8) { Text("结束时间") .font(.system(size: 12, weight: .semibold)) .tracking(0.3) .foregroundStyle(Tj.Palette.text2) DatePicker("", selection: $endedAt, in: lowerBound...Date.now) .datePickerStyle(.compact) .labelsHidden() } HStack { Text("本次持续") .font(.system(size: 13)) .foregroundStyle(Tj.Palette.text3) Spacer() Text(durationLabel) .font(.system(size: 15, weight: .semibold, design: .monospaced)) .foregroundStyle(Tj.Palette.brick) } .padding(.horizontal, 14) .padding(.vertical, 12) .background( RoundedRectangle(cornerRadius: Tj.Radius.sm, style: .continuous) .fill(Tj.Palette.paper) ) Spacer(minLength: 8) } .padding(.horizontal, 20) HStack(spacing: 12) { Button("取消") { dismiss() } .buttonStyle(TjGhostButton(height: 44, fontSize: 15, horizontalPadding: 18)) Button("结束并保存") { submit() } .buttonStyle(TjPrimaryButton(height: 44, fontSize: 15, horizontalPadding: 18)) } .padding(.horizontal, 20) .padding(.vertical, 14) } .background( Tj.Palette.sand .clipShape(RoundedRectangle(cornerRadius: Tj.Radius.xl, style: .continuous)) .ignoresSafeArea(edges: .bottom) ) .presentationDetents([.medium]) .presentationDragIndicator(.hidden) .presentationBackground(Tj.Palette.sand) .presentationCornerRadius(Tj.Radius.xl) } private func submit() { symptom.endedAt = max(endedAt, symptom.startedAt) try? ctx.save() dismiss() } } func formatDuration(_ interval: TimeInterval) -> String { let totalMinutes = Int(interval / 60) let days = totalMinutes / (60 * 24) let hours = (totalMinutes % (60 * 24)) / 60 let minutes = totalMinutes % 60 if days > 0 && hours > 0 { return "\(days) 天 \(hours) 小时" } if days > 0 { return "\(days) 天" } if hours > 0 && minutes > 0 { return "\(hours) 小时 \(minutes) 分" } if hours > 0 { return "\(hours) 小时" } if minutes > 0 { return "\(minutes) 分钟" } return "刚刚" }