import SwiftUI import SwiftData /// 「写日记 · 用药」:记一次服用流水 —— 选药(药品库或手输)+ 剂量 + 时间 /// → 带 `DiaryEntry.medicationTag` 的日记,进「记录」时间线的「用药」分类。 /// /// 与药品库(`Medication`,master 清单)分层:这里是「某次吃了多少、什么时候吃的」。 /// 嵌套 sheet,自带保存 / 取消(同 `SymptomStartSheet`),关闭后回到写日记页。 struct MedicationLogSheet: View { @Environment(\.modelContext) private var ctx @Environment(\.dismiss) private var dismiss @Query(sort: \Medication.updatedAt, order: .reverse) private var library: [Medication] /// 选中的药品库药;手输模式为 nil。 @State private var selectedMed: Medication? /// 手输药名(药品库为空,或想记不在库里的药)。与 selectedMed 互斥。 @State private var manualName = "" @State private var dosage = "" @State private var takenAt: Date = .now /// 从药品库某个药直接拉起时预选它(药品库详情页「记录一次服用」)。默认 nil = 自由选择。 init(preselected: Medication? = nil) { _selectedMed = State(initialValue: preselected) } private var resolvedName: String { (selectedMed?.name ?? manualName).trimmingCharacters(in: .whitespacesAndNewlines) } private var canSave: Bool { !resolvedName.isEmpty } var body: some View { NavigationStack { Form { Section { if library.isEmpty { TextField(String(appLoc: "药名,如:缬沙坦胶囊"), text: $manualName) .foregroundStyle(Tj.Palette.text) } else { ForEach(library) { m in Button { select(m) } label: { medRow(m) } .buttonStyle(.plain) } HStack(spacing: 8) { Image(systemName: "pencil") .foregroundStyle(Tj.Palette.text3) TextField(String(appLoc: "或手动输入药名"), text: $manualName) .foregroundStyle(Tj.Palette.text) .onChange(of: manualName) { _, v in if !v.trimmingCharacters(in: .whitespaces).isEmpty { selectedMed = nil } } } } } header: { Text("吃了哪个药") } footer: { if library.isEmpty { Text("药品库还没有药,可在「记录 · 药品库」拍药盒或手动添加。这里直接手输也行。") } } Section { TextField(String(appLoc: "剂量,如:1 片 / 80mg"), text: $dosage) .foregroundStyle(Tj.Palette.text) } header: { Text("剂量") } Section { DatePicker(String(appLoc: "时间"), selection: $takenAt, in: ...Date.now) } header: { Text("时间") } } .scrollContentBackground(.hidden) .background(Tj.Palette.sand.ignoresSafeArea()) .navigationTitle("记录用药") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { Button(String(appLoc: "取消")) { dismiss() } } ToolbarItem(placement: .topBarTrailing) { Button(String(appLoc: "保存")) { save() } .fontWeight(.semibold) .disabled(!canSave) } } } } private func medRow(_ m: Medication) -> some View { let on = selectedMed === m return HStack(spacing: 10) { Image(systemName: on ? "checkmark.circle.fill" : "circle") .foregroundStyle(on ? Tj.Palette.ink : Tj.Palette.text3) VStack(alignment: .leading, spacing: 2) { Text(m.name) .foregroundStyle(Tj.Palette.text) if !m.detailLine.isEmpty { Text(m.detailLine) .font(.tjScaled( 11)) .foregroundStyle(Tj.Palette.text3) } } Spacer(minLength: 0) } .contentShape(Rectangle()) } private func select(_ m: Medication) { selectedMed = m manualName = "" } private func save() { guard canSave else { return } // content 单行:「药名 [规格] · 剂量」。剂量进正文,时间用 createdAt 承载。 // 与 TimelineEntry.firstLine / TimelineEntryDetailView.medicationLines 单行解析兼容。 var line = resolvedName if let s = selectedMed?.strength, !s.isEmpty { line += " \(s)" } let dose = dosage.trimmingCharacters(in: .whitespacesAndNewlines) if !dose.isEmpty { line += " · \(dose)" } let entry = DiaryEntry(content: line, createdAt: takenAt, tags: [DiaryEntry.medicationTag]) ctx.insert(entry) try? ctx.save() dismiss() } } #Preview { MedicationLogSheet() .modelContainer(for: [Medication.self, DiaryEntry.self, Asset.self], inMemory: true) }