import SwiftUI // MARK: - 通用计量器:根据 GaugeDisplayStyle 动态渲染 struct StyledGauge: View { let title: String let icon: String let value: Double // 0-100 let unit: String // "%" / "GB" let detail: String // 副标题(如 "3.2 / 16 GB") let style: GaugeDisplayStyle let fontSize: Double // 主题色(根据负载渐变) private var gaugeColor: Color { if value < 40 { return .green } if value < 70 { return .yellow } if value < 85 { return .orange } return .red } var body: some View { VStack(alignment: .leading, spacing: 6) { // 标题行 HStack(spacing: 6) { Image(systemName: icon) .font(.system(size: fontSize)) .foregroundColor(.secondary) Text(title) .font(.system(size: fontSize, weight: .medium)) .foregroundColor(.secondary) Spacer() } // 根据样式渲染 switch style { case .bar: barView case .ring: ringView case .text: textView case .pie: pieView case .compact: compactView } } } // MARK: - 条状百分比 private var barView: some View { VStack(spacing: 2) { GeometryReader { geo in ZStack(alignment: .leading) { Capsule() .fill(Color.white.opacity(0.12)) .frame(height: 8) Capsule() .fill( LinearGradient( colors: [gaugeColor.opacity(0.8), gaugeColor], startPoint: .leading, endPoint: .trailing ) ) .frame(width: max(geo.size.width * CGFloat(value / 100), 4), height: 8) .animation(.easeOut(duration: 0.2), value: value) } } .frame(height: 8) HStack { Text(String(format: "%.1f%@", value, unit)) .font(.system(size: fontSize + 2, weight: .bold, design: .monospaced)) .foregroundColor(.primary) Spacer() Text(detail) .font(.system(size: fontSize - 2)) .foregroundColor(.secondary) .lineLimit(1) } } } // MARK: - 圆环进度 private var ringView: some View { HStack(spacing: 10) { ZStack { Circle() .stroke(Color.white.opacity(0.12), lineWidth: 5) .frame(width: 48, height: 48) .animation(.easeOut(duration: 0.2), value: value) Circle() .trim(from: 0, to: CGFloat(value / 100)) .stroke( AngularGradient( colors: [gaugeColor.opacity(0.7), gaugeColor, gaugeColor.opacity(0.9)], center: .center ), style: StrokeStyle(lineWidth: 5, lineCap: .round) ) .rotationEffect(.degrees(-90)) .frame(width: 48, height: 48) Text(String(format: "%.0f", value)) .font(.system(size: fontSize - 1, weight: .bold, design: .monospaced)) .foregroundColor(.primary) } VStack(alignment: .leading, spacing: 2) { Text(String(format: "%.1f%@", value, unit)) .font(.system(size: fontSize + 2, weight: .bold, design: .monospaced)) .foregroundColor(.primary) Text(detail) .font(.system(size: fontSize - 2)) .foregroundColor(.secondary) .lineLimit(1) } } } // MARK: - 纯文本 private var textView: some View { HStack { Text(String(format: "%.1f%@", value, unit)) .font(.system(size: fontSize + 6, weight: .bold, design: .monospaced)) .foregroundColor(gaugeColor) .animation(.easeOut(duration: 0.2), value: value) Spacer() Text(detail) .font(.system(size: fontSize - 2)) .foregroundColor(.secondary) .lineLimit(1) } } // MARK: - 扇形圆盘 private var pieView: some View { HStack(spacing: 10) { ZStack { Circle() .fill(Color.white.opacity(0.05)) .frame(width: 44, height: 44) PieSlice(endAngle: .degrees(360 * value / 100)) .fill(gaugeColor.opacity(0.85)) .frame(width: 42, height: 42) .clipShape(Circle()) .animation(.easeOut(duration: 0.2), value: value) Text(String(format: "%.0f", value)) .font(.system(size: fontSize - 2, weight: .bold, design: .monospaced)) .foregroundColor(.white) } VStack(alignment: .leading, spacing: 2) { Text(String(format: "%.1f%@", value, unit)) .font(.system(size: fontSize + 2, weight: .bold, design: .monospaced)) .foregroundColor(.primary) Text(detail) .font(.system(size: fontSize - 2)) .foregroundColor(.secondary) .lineLimit(1) } } } // MARK: - 紧凑横条 private var compactView: some View { VStack(spacing: 4) { GeometryReader { geo in ZStack(alignment: .leading) { RoundedRectangle(cornerRadius: 4) .fill(Color.white.opacity(0.1)) .frame(height: 14) RoundedRectangle(cornerRadius: 4) .fill(gaugeColor) .frame(width: max(geo.size.width * CGFloat(value / 100), 3), height: 14) .animation(.easeOut(duration: 0.2), value: value) } } .frame(height: 14) HStack { Text(String(format: "%.1f%@", value, unit)) .font(.system(size: fontSize, weight: .bold, design: .monospaced)) .foregroundColor(.primary) Spacer() Text(detail) .font(.system(size: fontSize - 2)) .foregroundColor(.secondary) .lineLimit(1) } } } } // MARK: - 扇形 Shape struct PieSlice: Shape { var endAngle: Angle func path(in rect: CGRect) -> Path { var path = Path() let center = CGPoint(x: rect.midX, y: rect.midY) let radius = min(rect.width, rect.height) / 2 path.move(to: center) path.addArc(center: center, radius: radius, startAngle: .degrees(-90), endAngle: endAngle - .degrees(90), clockwise: false) path.closeSubpath() return path } }