缺少代码差异信息,无法生成具体的commit message。请提供code differences内容以便分析并生成符合Angular规范的提交信息。

当您提供代码差异后,我将按照以下格式生成:

```
<type>(<scope>): <subject>

<body>
```

其中type会根据更改类型选择(feat、fix、docs、style、refactor等),scope表示影响范围,subject简要描述变更内容,body详细说明修改内容。
This commit is contained in:
link2026
2026-06-07 14:17:18 +08:00
parent 074d99715d
commit 77a4ee1c37
66 changed files with 2676 additions and 548 deletions

View File

@@ -21,7 +21,8 @@ struct QuickRegionCaptureFlow: View {
@State private var analyzeTask: Task<Void, Never>? = nil
/// VL (); cancel ,UI
private let analyzeTimeoutSeconds: Int = 30
/// token ,30s , 60s
private let analyzeTimeoutSeconds: Int = 60
enum Phase {
case idle
@@ -86,23 +87,42 @@ struct QuickRegionCaptureFlow: View {
}
}
// MARK: - :()/ ()
// MARK: - :()/ ()
// RegionCameraView ( 1-2 ); ·
// , ,VL VisionKit :
// + ,VL / 退
@ViewBuilder
private var captureEntry: some View {
#if targetEnvironment(simulator)
PhotoPickerSheet(
onFinish: { imgs in if let first = imgs.first { startAnalyze(image: first) } },
onFinish: { imgs in handleScanned(imgs) },
onCancel: onClose
)
#else
RegionCameraView(
onCapture: { startAnalyze(image: $0) },
onCancel: onClose
)
if DocumentScannerView.isSupported {
DocumentScannerView(
onFinish: { imgs in handleScanned(imgs) },
onCancel: onClose
)
} else {
PhotoPickerSheet(
onFinish: { imgs in handleScanned(imgs) },
onCancel: onClose
)
}
#endif
}
/// /:();
private func handleScanned(_ images: [UIImage]) {
if let first = images.first {
startAnalyze(image: first)
} else {
onClose()
}
}
// MARK: -
private func startAnalyze(image: UIImage) {
@@ -110,12 +130,9 @@ struct QuickRegionCaptureFlow: View {
phase = .analyzing(image: image)
let timeout = analyzeTimeoutSeconds
// MainActor ,Task{} , phase 线,
// :Vision OCR Qwen3-1.7B LLM ( 3B VL )
analyzeTask = Task {
guard let data = image.jpegData(compressionQuality: 0.9) else {
phase = .confirm(image: image, items: [],
warning: String(appLoc: "图片编码失败,手动补充或重拍"))
return
}
let timeoutWarn = String(appLoc: "识别超时(>\(timeout)s),手动补充或重拍")
let watchdog = Task {
try? await Task.sleep(for: .seconds(timeout))
@@ -124,12 +141,25 @@ struct QuickRegionCaptureFlow: View {
defer { watchdog.cancel() }
do {
let parsed = try await CaptureService.shared.recognizeRegion(imageData: data)
// 1. OCR
let text = try await OCRService.recognizeText(in: image)
if Task.isCancelled {
phase = .confirm(image: image, items: [], warning: timeoutWarn); return
}
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
#if DEBUG
print("🔤 [OCR] recognized text:\n\(trimmed)\n--- end OCR ---")
#endif
if trimmed.isEmpty {
phase = .confirm(image: image, items: [],
warning: String(appLoc: "识别超时(>\(timeout)s),手动补充或重拍"))
warning: String(appLoc: "识别到文字,手动补充或重拍"))
return
}
// 2. LLM
let parsed = try await CaptureService.shared.recognizeIndicators(fromOCRText: trimmed)
if Task.isCancelled {
phase = .confirm(image: image, items: [], warning: timeoutWarn); return
}
let items = Self.buildItems(from: parsed)
phase = .confirm(
image: image,
@@ -138,23 +168,23 @@ struct QuickRegionCaptureFlow: View {
)
} catch CaptureError.modelNotReady {
phase = .confirm(image: image, items: [],
warning: String(appLoc: "VL 模型未就绪,手动补充"))
warning: String(appLoc: "AI 模型未就绪,手动补充"))
} catch let CaptureError.parseFailed(msg) {
phase = .confirm(image: image, items: [],
warning: String(appLoc: "VL 输出无法解析:\(msg)"))
warning: String(appLoc: "解析失败:\(msg)"))
} catch let CaptureError.inferenceFailed(msg) {
phase = .confirm(image: image, items: [],
warning: Task.isCancelled
? String(appLoc: "识别超时(>\(timeout)s),手动补充或重拍")
: String(appLoc: "推理失败:\(msg)"))
warning: Task.isCancelled ? timeoutWarn
: String(appLoc: "识别失败:\(msg)"))
} catch {
phase = .confirm(image: image, items: [],
warning: String(appLoc: "未知错误:\(error.localizedDescription)"))
warning: Task.isCancelled ? timeoutWarn
: String(appLoc: "未知错误:\(error.localizedDescription)"))
}
}
}
/// VL ,(high/low)
/// LLM ,(high/low)
private static func buildItems(from parsed: [ParsedReport.ParsedIndicator]) -> [QuickRegionItem] {
let mapped = parsed.map {
QuickRegionItem(name: $0.name, value: $0.value, unit: $0.unit,
@@ -233,16 +263,16 @@ private struct AnalyzingRegionView: View {
.font(.tjH2())
.foregroundStyle(Tj.Palette.text)
Text("100% 本地推理 · 已用 \(elapsed)s")
.font(.system(size: 12))
.font(.tjScaled( 12))
.foregroundStyle(Tj.Palette.text3)
if elapsed >= timeoutSeconds - 5 {
Text("快超时了,>\(timeoutSeconds)s 会自动转手动录入")
.font(.system(size: 11))
.font(.tjScaled( 11))
.foregroundStyle(Tj.Palette.amber)
}
}
Button("取消识别 · 改为手动录入", action: onCancel)
.font(.system(size: 13, weight: .medium))
.font(.tjScaled( 13, weight: .medium))
.foregroundStyle(Tj.Palette.text3)
.padding(.top, 4)
Spacer()