```
feat: 添加拍药盒功能和语音直达入口 - 实现拍药盒扫描流程,支持本地OCR识别药品信息 - 在日记页面添加拍药盒和记症状的三选一入口 - 优化按钮点击区域,确保符合苹果HIG最小命中区标准 - 添加用药记录到时间线的独立分类显示 - 实现长按+号语音直达功能,支持语音意图分类跳转 - 更新项目配置文件,启用代码分析和死代码剥离选项 - 增加多项本地化字符串支持新功能 ```
This commit is contained in:
@@ -11,6 +11,10 @@ struct DiaryQuickSheet: View {
|
||||
|
||||
@State private var content: String = ""
|
||||
@State private var createdAt: Date = .now
|
||||
/// 「拍药盒」分支:全屏扫描流程,确认后存为带「用药」tag 的日记。
|
||||
@State private var showMedicationScan = false
|
||||
/// 「记症状」分支:嵌套弹出 SymptomStartSheet(自带保存/取消,关闭后回到本页)。
|
||||
@State private var showSymptomStart = false
|
||||
|
||||
/// AI 辅助状态
|
||||
enum AssistPhase {
|
||||
@@ -92,6 +96,24 @@ struct DiaryQuickSheet: View {
|
||||
.foregroundStyle(Tj.Palette.text3)
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.bottom, 10)
|
||||
|
||||
// 入口三选一:写日记(本页)/ 拍药盒(存「用药」日记)/ 记症状(SymptomStartSheet)
|
||||
HStack(spacing: 10) {
|
||||
modeCard(icon: "pencil", title: String(appLoc: "写日记"),
|
||||
subtitle: String(appLoc: "文字或语音"), active: true) {
|
||||
contentFocused = true
|
||||
}
|
||||
modeCard(icon: "pills.fill", title: String(appLoc: "拍药盒"),
|
||||
subtitle: String(appLoc: "识别用药"), active: false) {
|
||||
showMedicationScan = true
|
||||
}
|
||||
modeCard(icon: "waveform.path.ecg", title: String(appLoc: "记症状"),
|
||||
subtitle: String(appLoc: "持续追踪"), active: false) {
|
||||
showSymptomStart = true
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.bottom, 14)
|
||||
|
||||
ScrollViewReader { proxy in
|
||||
@@ -228,6 +250,20 @@ struct DiaryQuickSheet: View {
|
||||
.presentationDragIndicator(.hidden)
|
||||
.presentationBackground(Tj.Palette.sand)
|
||||
.presentationCornerRadius(Tj.Radius.xl)
|
||||
.fullScreenCover(isPresented: $showMedicationScan) {
|
||||
MedicationScanFlow(
|
||||
onSave: { entries in
|
||||
// 落库:「用药」日记(进记录时间线)+ 同步个人资料·当前用药。
|
||||
MedicationArchiver.archive(entries: entries, in: ctx)
|
||||
dismiss()
|
||||
},
|
||||
onClose: { showMedicationScan = false }
|
||||
)
|
||||
}
|
||||
.sheet(isPresented: $showSymptomStart) {
|
||||
// 嵌套 sheet:症状表单自带保存/取消;取消回到日记,不强行关闭。
|
||||
SymptomStartSheet()
|
||||
}
|
||||
.onDisappear {
|
||||
suggestTask?.cancel()
|
||||
voiceFlowTask?.cancel()
|
||||
@@ -555,6 +591,41 @@ struct DiaryQuickSheet: View {
|
||||
.foregroundStyle(Tj.Palette.text2)
|
||||
}
|
||||
|
||||
/// 顶部入口三选一卡片(写日记 / 拍药盒 / 记症状)。active 表示当前所在模式。
|
||||
/// 竖排紧凑布局:三卡并排在 iPhone 宽度下横排放不下完整文案。
|
||||
private func modeCard(icon: String, title: String, subtitle: String,
|
||||
active: Bool, action: @escaping () -> Void) -> some View {
|
||||
Button(action: action) {
|
||||
VStack(spacing: 5) {
|
||||
Image(systemName: icon)
|
||||
.font(.tjScaled( 15, weight: .medium))
|
||||
.foregroundStyle(active ? Tj.Palette.paper : Tj.Palette.ink)
|
||||
.frame(width: 28, height: 28)
|
||||
.background(Circle().fill(active ? Tj.Palette.ink : Tj.Palette.sand2))
|
||||
Text(title)
|
||||
.font(.tjScaled( 13, weight: .semibold))
|
||||
.foregroundStyle(Tj.Palette.text)
|
||||
Text(subtitle)
|
||||
.font(.tjScaled( 10))
|
||||
.foregroundStyle(Tj.Palette.text3)
|
||||
.lineLimit(1)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 10)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: Tj.Radius.sm, style: .continuous)
|
||||
.fill(Tj.Palette.paper)
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: Tj.Radius.sm, style: .continuous)
|
||||
.strokeBorder(active ? Tj.Palette.ink : Tj.Palette.line,
|
||||
lineWidth: active ? 1.5 : 1)
|
||||
)
|
||||
.contentShape(RoundedRectangle(cornerRadius: Tj.Radius.sm, style: .continuous))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
// MARK: 语音输入流程
|
||||
|
||||
private func startVoice() {
|
||||
|
||||
Reference in New Issue
Block a user