feat: 添加拍药盒功能和语音直达入口

- 实现拍药盒扫描流程,支持本地OCR识别药品信息
- 在日记页面添加拍药盒和记症状的三选一入口
- 优化按钮点击区域,确保符合苹果HIG最小命中区标准
- 添加用药记录到时间线的独立分类显示
- 实现长按+号语音直达功能,支持语音意图分类跳转
- 更新项目配置文件,启用代码分析和死代码剥离选项
- 增加多项本地化字符串支持新功能
```
This commit is contained in:
link2026
2026-06-13 09:16:25 +08:00
parent f58d6064ba
commit 6c6a950140
30 changed files with 1856 additions and 64 deletions

View File

@@ -3,33 +3,36 @@ import SwiftData
import Foundation
enum TimelineKind: String, CaseIterable, Identifiable {
case indicator, report, symptom, diary
case diary, symptom, indicator, medication, report
var id: String { rawValue }
var label: String {
switch self {
case .indicator: return String(appLoc: "指标")
case .report: return String(appLoc: "报告")
case .symptom: return String(appLoc: "症状")
case .diary: return String(appLoc: "日记")
case .indicator: return String(appLoc: "指标")
case .report: return String(appLoc: "报告")
case .symptom: return String(appLoc: "症状")
case .diary: return String(appLoc: "日记")
case .medication: return String(appLoc: "用药")
}
}
var icon: String {
switch self {
case .indicator: return "drop.fill"
case .report: return "doc.fill"
case .symptom: return "waveform.path.ecg"
case .diary: return "pencil"
case .indicator: return "drop.fill"
case .report: return "doc.fill"
case .symptom: return "waveform.path.ecg"
case .diary: return "pencil"
case .medication: return "pills.fill"
}
}
var accent: Color {
switch self {
case .indicator: return Tj.Palette.brick
case .report: return Tj.Palette.ink2
case .symptom: return Tj.Palette.amber
case .diary: return Tj.Palette.leaf
case .indicator: return Tj.Palette.brick
case .report: return Tj.Palette.ink2
case .symptom: return Tj.Palette.amber
case .diary: return Tj.Palette.leaf
case .medication: return Tj.Palette.ink
}
}
}
@@ -132,13 +135,16 @@ struct TimelineEntry: Identifiable, Hashable {
}
}
/// tag () .medication ,
/// id "diary-" :TimelineDetail.resolve diaries
static func from(diary d: DiaryEntry) -> TimelineEntry {
TimelineEntry(
let isMed = d.isMedicationLog
return TimelineEntry(
id: "diary-\(d.persistentModelID)",
kind: .diary,
kind: isMed ? .medication : .diary,
date: d.createdAt,
title: d.content.firstLine(),
subtitle: String(appLoc: "文字日记"),
subtitle: isMed ? String(appLoc: "用药记录") : String(appLoc: "文字日记"),
trailing: nil,
trailingIsAlert: false,
isOngoing: false

View File

@@ -22,7 +22,8 @@ enum TimelineDetail {
case .report:
return reports.first { "report-\($0.persistentModelID)" == entry.id }
.map(TimelineDetail.report)
case .diary:
case .diary, .medication:
// tag DiaryEntry,
return diaries.first { "diary-\($0.persistentModelID)" == entry.id }
.map(TimelineDetail.diary)
case .symptom: