feat(capture): 统一报告捕获流程并集成视觉语言模型识别
- 替换 QuickCaptureFlow 和 ArchiveFlow 为 UnifiedCaptureFlow 统一流程 - 新增 VLSession 封装 Qwen2.5-VL 模型进行图像文本推理 - 实现 AIRuntime 中 VL 模型的准备和分析功能 - 添加 VLPrompts 定义体检化验单识别的 JSON 输出模板 - 创建 CaptureReviewForm 提供 VL 解析结果的可编辑表单界面 - 集成 VisionKit 文档扫描器支持真机多页文档扫描 - 为模拟器实现 PhotosPicker 回退方案选择已有照片 - 在 RootView 中统一使用 UnifiedCaptureFlow 处理快速和归档流程 - 添加 CustomMetricEditor 支持自定义监测指标的创建编辑删除 - 扩展 KangkangApp 模型配置以支持新数据类型 - 实现档案列表中症状结束功能通过时间线行点击触发
This commit is contained in:
@@ -36,6 +36,7 @@ extension SeriesBucket {
|
||||
/// `minPoints` 以下的系列不返回,默认 2(单点不画线)。
|
||||
static func build(from indicators: [Indicator],
|
||||
profile: UserProfile? = nil,
|
||||
customMetrics: [CustomMonitorMetric] = [],
|
||||
minPoints: Int = 2) -> [SeriesBucket] {
|
||||
var buckets: [String: [Indicator]] = [:]
|
||||
for i in indicators {
|
||||
@@ -55,9 +56,15 @@ extension SeriesBucket {
|
||||
}
|
||||
for k in bpKeys { buckets.removeValue(forKey: k) }
|
||||
|
||||
let customByKey: [String: CustomMonitorMetric] = Dictionary(
|
||||
uniqueKeysWithValues: customMetrics.map { ($0.seriesKey, $0) }
|
||||
)
|
||||
|
||||
for (key, items) in buckets {
|
||||
guard items.count >= minPoints else { continue }
|
||||
if let bucket = buildSingle(key: key, items: items, profile: profile) {
|
||||
if let bucket = buildSingle(key: key, items: items,
|
||||
profile: profile,
|
||||
custom: customByKey[key]) {
|
||||
results.append(bucket)
|
||||
}
|
||||
}
|
||||
@@ -67,15 +74,24 @@ extension SeriesBucket {
|
||||
|
||||
private static func buildSingle(key: String,
|
||||
items: [Indicator],
|
||||
profile: UserProfile?) -> SeriesBucket? {
|
||||
profile: UserProfile?,
|
||||
custom: CustomMonitorMetric? = nil) -> SeriesBucket? {
|
||||
let sorted = items.sorted { $0.capturedAt < $1.capturedAt }
|
||||
guard let latest = sorted.last else { return nil }
|
||||
|
||||
// 优先 custom,其次 builtin metric,最后 fallback 到 Indicator 自身
|
||||
let metric = monitorMetric(for: key)
|
||||
let field = metric?.fields.first { $0.seriesKey == key }
|
||||
let title = metric?.displayName ?? sorted.first?.name ?? key
|
||||
let unit = field?.unit ?? sorted.first?.unit ?? ""
|
||||
let range = field.flatMap { metric?.effectiveRange(for: $0, profile: profile) }
|
||||
let title = custom?.name
|
||||
?? metric?.displayName
|
||||
?? sorted.first?.name
|
||||
?? key
|
||||
let unit = custom?.unit.nonEmptyOr(nil)
|
||||
?? field?.unit
|
||||
?? sorted.first?.unit
|
||||
?? ""
|
||||
let range = custom?.referenceRange
|
||||
?? field.flatMap { metric?.effectiveRange(for: $0, profile: profile) }
|
||||
|
||||
let line = SeriesLine(
|
||||
id: key,
|
||||
@@ -151,3 +167,10 @@ extension SeriesBucket {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension String {
|
||||
/// 空串 → fallback;非空 → 自身。
|
||||
func nonEmptyOr(_ fallback: String?) -> String? {
|
||||
trimmingCharacters(in: .whitespaces).isEmpty ? fallback : self
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user