根据提供的code differences信息,由于没有具体的代码变更内容,我将生成一个通用的commit message模板:
``` docs(readme): 更新文档说明 - 添加了项目使用指南 - 完善了API接口说明 - 修正了一些文字错误 ``` 注:由于未提供具体的代码差异信息,以上为示例格式。请提供具体的代码变更内容以便生成准确的commit message。
This commit is contained in:
@@ -4,11 +4,23 @@ import Charts
|
||||
struct SeriesChartCard: View {
|
||||
let bucket: SeriesBucket
|
||||
|
||||
private var allPoints: [(line: SeriesBucket.SeriesLine, point: SeriesBucket.Point)] {
|
||||
bucket.lines.flatMap { line in line.points.map { (line, $0) } }
|
||||
// bucket 不可变,这些派生量在 init 里一次性算好存为 let,避免 body 内被
|
||||
// header / chart / daysSpanLabel 多次访问时反复 flatMap / min / max 重算。
|
||||
private let allPoints: [(line: SeriesBucket.SeriesLine, point: SeriesBucket.Point)]
|
||||
private let dateDomain: ClosedRange<Date>?
|
||||
private let valueDomain: ClosedRange<Double>?
|
||||
|
||||
init(bucket: SeriesBucket) {
|
||||
self.bucket = bucket
|
||||
let pts = bucket.lines.flatMap { line in line.points.map { (line, $0) } }
|
||||
self.allPoints = pts
|
||||
self.dateDomain = Self.makeDateDomain(pts)
|
||||
self.valueDomain = Self.makeValueDomain(pts, lines: bucket.lines)
|
||||
}
|
||||
|
||||
private var dateDomain: ClosedRange<Date>? {
|
||||
private static func makeDateDomain(
|
||||
_ allPoints: [(line: SeriesBucket.SeriesLine, point: SeriesBucket.Point)]
|
||||
) -> ClosedRange<Date>? {
|
||||
let dates = allPoints.map(\.point.date)
|
||||
guard let lo = dates.min(), let hi = dates.max() else { return nil }
|
||||
if lo == hi {
|
||||
@@ -21,14 +33,17 @@ struct SeriesChartCard: View {
|
||||
return lo...hi
|
||||
}
|
||||
|
||||
private var valueDomain: ClosedRange<Double>? {
|
||||
private static func makeValueDomain(
|
||||
_ allPoints: [(line: SeriesBucket.SeriesLine, point: SeriesBucket.Point)],
|
||||
lines: [SeriesBucket.SeriesLine]
|
||||
) -> ClosedRange<Double>? {
|
||||
var lo = Double.greatestFiniteMagnitude
|
||||
var hi = -Double.greatestFiniteMagnitude
|
||||
for (_, p) in allPoints {
|
||||
lo = min(lo, p.value)
|
||||
hi = max(hi, p.value)
|
||||
}
|
||||
for line in bucket.lines {
|
||||
for line in lines {
|
||||
if let r = line.referenceRange {
|
||||
lo = min(lo, r.lowerBound)
|
||||
hi = max(hi, r.upperBound)
|
||||
|
||||
@@ -506,7 +506,11 @@ private struct TrendInsightCard: View {
|
||||
do {
|
||||
text = try await TrendInsightService.shared.generate(for: bucket)
|
||||
} catch {
|
||||
failedMessage = String(appLoc: "AI 解读暂不可用(模型未就绪或繁忙)")
|
||||
// 区分「模型没下载」与「下载了但这次推理没成功」,前者给下载引导(CLAUDE.md §4)。
|
||||
let downloaded = ModelStore.shared.isComplete(for: .mnnLLM) || ModelStore.shared.isComplete(for: .llm)
|
||||
failedMessage = downloaded
|
||||
? String(appLoc: "本地推理这次没成功,点右上「解读」重试")
|
||||
: String(appLoc: "AI 解读需先在「我的 · 模型管理」下载模型")
|
||||
}
|
||||
running = false
|
||||
}
|
||||
|
||||
@@ -22,36 +22,32 @@ struct TrendsView: View {
|
||||
customMetrics: customMetrics)
|
||||
}
|
||||
|
||||
private var monitorBuckets: [SeriesBucket] {
|
||||
seriesBuckets.filter { $0.kind == .monitor }
|
||||
}
|
||||
private var labBuckets: [SeriesBucket] {
|
||||
seriesBuckets.filter { $0.kind == .lab }
|
||||
}
|
||||
|
||||
private func filtered(_ buckets: [SeriesBucket]) -> [SeriesBucket] {
|
||||
let q = query.trimmingCharacters(in: .whitespaces)
|
||||
guard !q.isEmpty else { return buckets }
|
||||
return buckets.filter { $0.title.localizedCaseInsensitiveContains(q) }
|
||||
}
|
||||
private var filteredMonitor: [SeriesBucket] { filtered(monitorBuckets) }
|
||||
private var filteredLab: [SeriesBucket] { filtered(labBuckets) }
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
// SeriesBucket.build 一次性算,monitor / lab / 过滤全部本地派生 ——
|
||||
// 杜绝一次渲染(及每次搜索按键)对整张指标表重复 build ~7 次。
|
||||
let series = seriesBuckets
|
||||
let monitor = filtered(series.filter { $0.kind == .monitor })
|
||||
let lab = filtered(series.filter { $0.kind == .lab })
|
||||
return NavigationStack {
|
||||
ScrollView(showsIndicators: false) {
|
||||
VStack(alignment: .leading, spacing: 18) {
|
||||
header.padding(.top, 4)
|
||||
if seriesBuckets.isEmpty {
|
||||
if series.isEmpty {
|
||||
emptyState
|
||||
} else if filteredMonitor.isEmpty && filteredLab.isEmpty {
|
||||
} else if monitor.isEmpty && lab.isEmpty {
|
||||
noMatchState
|
||||
} else {
|
||||
if !filteredMonitor.isEmpty {
|
||||
section(title: String(appLoc: "长期监测"), buckets: filteredMonitor)
|
||||
if !monitor.isEmpty {
|
||||
section(title: String(appLoc: "长期监测"), buckets: monitor)
|
||||
}
|
||||
if !filteredLab.isEmpty {
|
||||
section(title: String(appLoc: "化验指标趋势"), buckets: filteredLab)
|
||||
if !lab.isEmpty {
|
||||
section(title: String(appLoc: "化验指标趋势"), buckets: lab)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user