```
feat(AI): 集成MNN推理引擎替换MLX作为主AI运行时 - 引入MNN(alibaba) + Arm SME2 + CPU作为主AI运行时,支持A19/iPhone17的 SME2和A17的NEON加速 - 添加MLX Swift作为兜底GPU推理方案,实现双后端切换机制 - 使用单一Qwen3.5-2B多模态模型(1.2GB),替代原有的LLM+VL分离架构 - 实现InferenceEngine.current引擎选择逻辑,真机默认MNN,模拟器回退MLX - 更新AIAgent架构,通过MNNLLMBridge(ObjC++) → MNNBackend进行推理 - 修改队列机制防止并发推理导致OOM,使用信号量闸门控制显存占用 - 更新文档中的技术栈说明、模块边界和周次交付计划 ```
This commit is contained in:
@@ -18,21 +18,19 @@ struct HomeView: View {
|
||||
|
||||
/// 点「最近记录」某行 → 打开只读详情 sheet(与档案库 C1 同款交互)。
|
||||
@State private var selectedEntry: TimelineEntry?
|
||||
/// 点指标行 → 打开同类聚合详情(历次翻页 + 趋势,与档案库 C1 同款)。
|
||||
@State private var selectedGroup: IndicatorGroup?
|
||||
|
||||
@MainActor
|
||||
private var recentEntries: [TimelineEntry] {
|
||||
let all =
|
||||
TimelineEntry.from(indicators: indicators) +
|
||||
TimelineEntry.aggregatedIndicators(indicators) +
|
||||
reports.map(TimelineEntry.from(report:)) +
|
||||
diaries.map(TimelineEntry.from(diary:)) +
|
||||
symptoms.map(TimelineEntry.from(symptom:))
|
||||
return all.sorted { $0.date > $1.date }.prefix(6).map { $0 }
|
||||
}
|
||||
|
||||
private var recentGrouped: [(section: DateSection, items: [TimelineEntry])] {
|
||||
TimelineGrouping.group(recentEntries)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollView(showsIndicators: false) {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
@@ -65,6 +63,9 @@ struct HomeView: View {
|
||||
TimelineEntryDetailView(detail: d)
|
||||
}
|
||||
}
|
||||
.sheet(item: $selectedGroup) { group in
|
||||
IndicatorSeriesDetailView(group: group)
|
||||
}
|
||||
}
|
||||
|
||||
private var greeting: some View {
|
||||
@@ -100,7 +101,10 @@ struct HomeView: View {
|
||||
}
|
||||
|
||||
private var recentSection: some View {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
// 聚合(含血压配对 O(m²))在一次 body 内只算一次,再派生分组,避免 .isEmpty 与分组各算一遍。
|
||||
let entries = recentEntries
|
||||
let groups = TimelineGrouping.group(entries)
|
||||
return VStack(alignment: .leading, spacing: 10) {
|
||||
HStack(alignment: .lastTextBaseline) {
|
||||
Text("最近记录").font(.tjH2()).foregroundStyle(Tj.Palette.text)
|
||||
Spacer()
|
||||
@@ -112,11 +116,11 @@ struct HomeView: View {
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
if recentEntries.isEmpty {
|
||||
if entries.isEmpty {
|
||||
emptyRecent
|
||||
} else {
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
ForEach(recentGrouped, id: \.section) { group in
|
||||
ForEach(groups, id: \.section) { group in
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text(group.section.label)
|
||||
.font(.tjScaled( 11, weight: .semibold))
|
||||
@@ -125,12 +129,16 @@ struct HomeView: View {
|
||||
VStack(spacing: 10) {
|
||||
ForEach(group.items) { entry in
|
||||
Button {
|
||||
if TimelineDetail.resolve(
|
||||
// 指标 → 同类聚合详情(历次 + 趋势);其余 → 只读详情。与档案库 C1 一致。
|
||||
guard let d = TimelineDetail.resolve(
|
||||
for: entry,
|
||||
indicators: indicators, reports: reports,
|
||||
diaries: diaries, symptoms: symptoms
|
||||
) != nil {
|
||||
selectedEntry = entry
|
||||
) else { return }
|
||||
switch d {
|
||||
case .indicator(let i): selectedGroup = IndicatorGroup.of(i)
|
||||
case .bloodPressure(let sys, _): selectedGroup = IndicatorGroup.of(sys)
|
||||
default: selectedEntry = entry
|
||||
}
|
||||
} label: {
|
||||
TimelineRow(entry: entry)
|
||||
|
||||
Reference in New Issue
Block a user