#if os(iOS) import SwiftUI @available(macOS 10.15, iOS 13.0, *) struct BubbleMetric: Identifiable { let id = UUID() let label: String let display: String let valueMinutes: Double // 用于相对半径 let color: Color } @available(macOS 10.15, iOS 13.0, *) struct BubbleMetricsView: View { let metrics: [BubbleMetric] private func radius(for v: Double, maxV: Double) -> CGFloat { guard maxV > 0 else { return 20 } let norm = sqrt(v / maxV) return 30 + norm * 70 } var body: some View { GeometryReader { geo in let maxV = metrics.map { $0.valueMinutes }.max() ?? 1 ZStack { ForEach(Array(metrics.enumerated()), id: \.[1].id) { idx, m in // 简单环形布局 let angle = Double(idx) / Double(metrics.count) * Double.pi * 2 let R = min(geo.size.width, geo.size.height) / 2 - 80 let x = geo.size.width/2 + CGFloat(cos(angle)) * R let y = geo.size.height/2 + CGFloat(sin(angle)) * R let r = radius(for: m.valueMinutes, maxV: maxV) Circle() .fill(m.color.opacity(0.75)) .frame(width: r, height: r) .overlay( VStack(spacing:4){ Text(m.display).font(.system(size: min(18, r*0.28))).bold().foregroundColor(.white) Text(m.label).font(.system(size: min(14, r*0.22))).foregroundColor(.white) }.padding(4) ) .position(x: x, y: y) .shadow(radius: 4, y: 2) } } .frame(maxWidth: .infinity, maxHeight: .infinity) } .frame(height: 340) } } #endif