import SwiftUI struct B2ScanView: View { var onShoot: () -> Void var onDone: () -> Void var onClose: () -> Void var page: Int = 2 var total: Int = 3 var body: some View { ZStack { Color(red: 0.04, green: 0.047, blue: 0.04).ignoresSafeArea() mockPaper DetectedEdge() .stroke(Color(red: 0.95, green: 0.78, blue: 0.45), style: StrokeStyle(lineWidth: 2, dash: [6, 4])) .opacity(0.95) .padding(.horizontal, 30) .padding(.top, 140) .padding(.bottom, 200) .allowsHitTesting(false) VStack(spacing: 0) { topBar Spacer() detectedBadge Spacer() thumbnails bottomControls } } .preferredColorScheme(.dark) } private var mockPaper: some View { VStack(spacing: 2) { Text("体 检 报 告 (第 \(page) 页)") .font(.system(size: 12, weight: .bold)) .padding(.bottom, 4) ForEach(reportRows, id: \.0) { row in HStack { Text(row.0).frame(maxWidth: .infinity, alignment: .leading) Text(row.1) Text(row.2).foregroundStyle(Tj.Palette.text3) } .font(.system(size: 9, design: .monospaced)) } } .padding(16) .foregroundStyle(Tj.Palette.text) .frame(maxWidth: .infinity) .background(Color(red: 0.97, green: 0.95, blue: 0.89).opacity(0.95)) .clipShape(RoundedRectangle(cornerRadius: 4, style: .continuous)) .rotation3DEffect(.degrees(8), axis: (x: 1, y: 0, z: 0)) .rotationEffect(.degrees(-1)) .shadow(color: .black.opacity(0.6), radius: 20, x: 0, y: 12) .padding(.horizontal, 40) .padding(.top, 160) .padding(.bottom, 220) } private var reportRows: [(String, String, String)] { [ ("总胆固醇", "5.42", "3.10–5.18"), ("甘油三酯", "1.78", "0.45–1.70"), ("低密度脂蛋白", "3.84↑", "<3.40"), ("高密度脂蛋白", "1.21", ">1.04"), ("载脂蛋白 A1", "1.42", "1.00–1.60"), ("载脂蛋白 B", "1.04", "0.55–1.05"), ("谷丙转氨酶", "28", "9–50"), ("谷草转氨酶", "24", "15–40"), ("空腹血糖", "5.4", "3.9–6.1"), ("糖化血红蛋白", "5.7", "4.0–6.0"), ] } private var topBar: some View { HStack { Button(action: onClose) { Image(systemName: "xmark") .font(.system(size: 18, weight: .semibold)) .foregroundStyle(Color.white) .frame(width: 36, height: 36) } Spacer() HStack(spacing: 4) { Text("\(page)").font(.system(size: 12, design: .monospaced)) Text(" / \(total) · 像扫描文档一样对准") .font(.system(size: 12)) } .foregroundStyle(Color.white) .padding(.horizontal, 14) .padding(.vertical, 6) .background(Capsule().fill(Color(red: 0.08, green: 0.11, blue: 0.094).opacity(0.7))) Spacer() Color.clear.frame(width: 36, height: 36) } .padding(.horizontal, 6) .padding(.top, 50) } private var detectedBadge: some View { Text("已识别边框 · 将自动透视校正") .font(.system(size: 10, weight: .semibold)) .tracking(0.4) .foregroundStyle(Color(red: 0.10, green: 0.115, blue: 0.094)) .padding(.horizontal, 8) .padding(.vertical, 3) .background(Capsule().fill(Color(red: 0.95, green: 0.78, blue: 0.45))) .padding(.top, 140) } private var thumbnails: some View { HStack { PageThumbStack(index: 1) Spacer() Text("已拍 1 页") .font(.system(size: 11, design: .monospaced)) .foregroundStyle(Color.white.opacity(0.7)) } .padding(.horizontal, 18) .padding(.bottom, 24) } private var bottomControls: some View { HStack { Color.clear.frame(width: 60, height: 60) Spacer() Button(action: onShoot) { ZStack { Circle().fill(Tj.Palette.paper) Circle().strokeBorder(Color.white.opacity(0.4), lineWidth: 4) } .frame(width: 72, height: 72) } .buttonStyle(.plain) Spacer() Button(action: onDone) { Text("完成") .font(.system(size: 14, weight: .semibold)) .foregroundStyle(Tj.Palette.paper) .padding(.horizontal, 16) .padding(.vertical, 10) .background(Capsule().fill(Color.white.opacity(0.1))) } .buttonStyle(.plain) } .padding(.horizontal, 32) .padding(.bottom, 40) } } private struct DetectedEdge: Shape { func path(in rect: CGRect) -> Path { var p = Path() let w = rect.width let h = rect.height p.move(to: CGPoint(x: w * 0.04, y: h * 0.05)) p.addLine(to: CGPoint(x: w * 0.92, y: h * 0.02)) p.addLine(to: CGPoint(x: w * 0.96, y: h * 0.96)) p.addLine(to: CGPoint(x: 0, y: h * 1.0)) p.closeSubpath() return p } } struct PageThumbStack: View { let index: Int var body: some View { ZStack { RoundedRectangle(cornerRadius: 4, style: .continuous) .fill(Color(red: 0.96, green: 0.93, blue: 0.87).opacity(0.7)) .frame(width: 56, height: 76) .rotationEffect(.degrees(2)) .offset(x: 4, y: 4) .shadow(color: .black.opacity(0.3), radius: 3, x: 0, y: 2) RoundedRectangle(cornerRadius: 4, style: .continuous) .fill(Color(red: 0.97, green: 0.95, blue: 0.89).opacity(0.85)) .frame(width: 56, height: 76) .rotationEffect(.degrees(-1)) .offset(x: 2, y: 2) .shadow(color: .black.opacity(0.3), radius: 3, x: 0, y: 2) RoundedRectangle(cornerRadius: 4, style: .continuous) .fill(Tj.Palette.paper) .frame(width: 56, height: 76) .overlay( Text("p.\(index)") .font(.system(size: 10, design: .monospaced)) .foregroundStyle(Tj.Palette.text3) ) .shadow(color: .black.opacity(0.4), radius: 4, x: 0, y: 2) } .frame(width: 64, height: 84, alignment: .topLeading) } }