// // PlanSelector.swift // wake // // Created by fairclip on 2025/8/19. // import SwiftUI // MARK: - 计划选择器组件 struct PlanSelector: View { @Binding var selectedPlan: SubscriptionPlan? let onPlanSelected: (SubscriptionPlan) -> Void private let plans: [SubscriptionPlan] = [.free, .pioneer] init( selectedPlan: Binding, onPlanSelected: @escaping (SubscriptionPlan) -> Void = { _ in } ) { self._selectedPlan = selectedPlan self.onPlanSelected = onPlanSelected } var body: some View { HStack(spacing: Theme.Spacing.md) { ForEach(plans, id: \.self) { plan in PlanCard( plan: plan, isSelected: selectedPlan == plan, onTap: { selectedPlan = plan onPlanSelected(plan) } ) } } } } // MARK: - 单个计划卡片 struct PlanCard: View { let plan: SubscriptionPlan let isSelected: Bool let onTap: () -> Void var body: some View { Button(action: onTap) { ZStack { // 主卡片内容 VStack(spacing: Theme.Spacing.sm) { // Popular 标签 if plan.isPopular { VStack { HStack { Spacer() Text("Popular") .font(Typography.font(for: .caption, family: .quicksandRegular)) .foregroundColor(Color.white) .padding(.horizontal, Theme.Spacing.sm) .padding(.vertical, Theme.Spacing.xs) .background(Color.black) .cornerRadius(Theme.CornerRadius.round, corners: [.bottomLeft]) } Spacer() VStack { // 计划名称 Text(plan.displayName) .font(Typography.font(for: .title, family: .quicksandBold, size: 18)) .foregroundColor(plan == .pioneer ? Theme.Colors.textPrimary: Theme.Colors.textTertiary ) // 价格 if plan == .pioneer { Text(plan.price) .font(Typography.font(for: .body, family: .quicksandBold, size: 20)) .foregroundColor(Theme.Colors.textPrimary) } } Spacer() Spacer() } } else { // 计划名称 Text(plan.displayName) .font(Typography.font(for: .title, family: .quicksandBold, size: 18)) .foregroundColor(plan == .pioneer ? Theme.Colors.textPrimary: Theme.Colors.textTertiary ) // 价格 if plan == .pioneer { Text(plan.price) .font(Typography.font(for: .body, family: .quicksandBold, size: 20)) .foregroundColor(Theme.Colors.textPrimary) } } } .frame(maxWidth: .infinity) .frame(height: 120) .background( plan == .pioneer ? Theme.Colors.primary : Theme.Colors.surfaceTertiary ) .overlay( RoundedRectangle(cornerRadius: Theme.CornerRadius.medium) .stroke( isSelected ? Theme.Colors.borderDark : Theme.Colors.border, lineWidth: 2 ) ) .cornerRadius(Theme.CornerRadius.medium) } } .buttonStyle(PlainButtonStyle()) } } // MARK: - 预览 #Preview("Plan Selector") { @State var selectedPlan: SubscriptionPlan? = .pioneer VStack(spacing: 20) { PlanSelector( selectedPlan: $selectedPlan, onPlanSelected: { plan in print("Selected plan: \(plan.displayName)") } ) } .padding() .background(Color(.systemGroupedBackground)) }