// // SubscribeView.swift // wake // // Created by fairclip on 2025/8/19. // import SwiftUI // MARK: - 订阅计划枚举 enum SubscriptionPlan: String, CaseIterable { case free = "Free" case pioneer = "Pioneer" var displayName: String { return self.rawValue } var price: String { switch self { case .free: return "Free" case .pioneer: return "1$/Mon" } } var isPopular: Bool { return self == .pioneer } } // MARK: - 功能特性结构体 struct SubscriptionFeature { let name: String let freeValue: String let proValue: String } struct SubscribeView: View { @State private var selectedPlan: SubscriptionPlan? = .pioneer @State private var isLoading = false @Environment(\.presentationMode) var presentationMode // 功能对比数据 private let features = [ SubscriptionFeature(name: "Mystery Box Purchase:", freeValue: "3 /week", proValue: "Free"), SubscriptionFeature(name: "Material Upload:", freeValue: "50 images and\n5 videos/day", proValue: "Unlimited"), SubscriptionFeature(name: "Free Credits:", freeValue: "200 /day", proValue: "500 /day") ] var body: some View { NavigationView { ScrollView { VStack(spacing: 0) { // 导航栏 navigationHeader // 当前订阅状态卡片 currentSubscriptionCard // 积分信息 creditsSection VStack { // 订阅计划选择 subscriptionPlansSection // 特别优惠提示 specialOfferBanner } .background(Theme.Colors.cardBackground) .cornerRadius(Theme.CornerRadius.medium) .padding(.horizontal, Theme.Spacing.xl) .padding(.vertical, Theme.Spacing.xl) // 功能对比表 featureComparisonTable // 订阅按钮 subscribeButton // 法律链接 legalLinks Spacer(minLength: 100) } } .background(Theme.Colors.background) .navigationBarHidden(true) } } // MARK: - 导航栏 private var navigationHeader: some View { NaviHeader(title: "Subscription") { presentationMode.wrappedValue.dismiss() } } // MARK: - 当前订阅状态卡片 private var currentSubscriptionCard: some View { SubscriptionStatusBar( status: .pioneer(expiryDate: Date()) , onSubscribeTap: { // 订阅操作 handleSubscribe() } ) .padding(.horizontal, Theme.Spacing.xl) } // MARK: - 积分信息 private var creditsSection: some View { VStack(spacing: 16) { CreditsInfoCard( totalCredits: 3290, onInfoTap: { // 显示积分信息说明 }, onDetailTap: { // 跳转到积分详情页面 } ) } .padding(.horizontal, Theme.Spacing.xl) .padding(.top, Theme.Spacing.xl) } // MARK: - 订阅计划选择 private var subscriptionPlansSection: some View { PlanSelector( selectedPlan: $selectedPlan, onPlanSelected: { plan in print("Selected plan: \(plan.displayName)") } ) .padding(.horizontal, Theme.Spacing.xl) .padding(.top, Theme.Spacing.xl) } // MARK: - 特别优惠横幅 private var specialOfferBanner: some View { HStack(spacing: 0) { Text("First") .font(Typography.font(for: .footnote, family: .quicksandRegular)) .foregroundColor(Theme.Colors.textPrimary) Text(" 100") .font(Typography.font(for: .footnote, family: .quicksandBold)) .foregroundColor(Theme.Colors.textPrimary) Text(" users get a special deal: justs") .font(Typography.font(for: .footnote, family: .quicksandRegular)) .foregroundColor(Theme.Colors.textPrimary) Text(" $1") .font(Typography.font(for: .footnote, family: .quicksandBold)) .foregroundColor(Theme.Colors.textPrimary) Text(" for your first month!") .font(Typography.font(for: .footnote, family: .quicksandRegular)) .foregroundColor(Theme.Colors.textPrimary) } .multilineTextAlignment(.center) .padding(.horizontal, Theme.Spacing.lg) .padding(.top, Theme.Spacing.sm) .padding(.bottom, Theme.Spacing.lg) } // MARK: - 功能对比表 private var featureComparisonTable: some View { VStack(spacing: 0) { // 表头 HStack { Spacer() Text("Free") .font(Typography.font(for: .subtitle, family: .quicksand)) .fontWeight(.medium) .frame(maxWidth: .infinity) .foregroundColor(.gray) Text("Pro") .font(Typography.font(for: .subtitle, family: .quicksand)) .fontWeight(.medium) .frame(maxWidth: .infinity) .foregroundColor(.gray) } .padding(.vertical, 16) .background(Color(.systemGray6)) // 功能行 ForEach(Array(features.enumerated()), id: \.offset) { index, feature in FeatureRow(feature: feature, isLast: index == features.count - 1) } } .background(Color(.systemBackground)) .cornerRadius(12) .padding(.horizontal, 20) .padding(.top, 20) .shadow(color: Color.black.opacity(0.05), radius: 2, x: 0, y: 1) } // MARK: - 订阅按钮 private var subscribeButton: some View { VStack(spacing: 12) { Button(action: { handleSubscribe() }) { if isLoading { HStack { ProgressView() .progressViewStyle(CircularProgressViewStyle(tint: .white)) .scaleEffect(0.8) Text("Subscribe") .font(Typography.font(for: .body, family: .quicksand)) .fontWeight(.semibold) } } else { Text("Subscribe") .font(Typography.font(for: .body, family: .quicksand)) .fontWeight(.semibold) } } .foregroundColor(.white) .frame(maxWidth: .infinity) .padding(.vertical, 16) .background(Color.blue) .cornerRadius(25) .disabled(isLoading) Text("Get 5,000 Permanent Credits") .font(Typography.font(for: .caption, family: .quicksand)) .foregroundColor(.secondary) } .padding(.horizontal, 20) .padding(.top, 30) } // MARK: - 法律链接 private var legalLinks: some View { HStack(spacing: 8) { Button("Terms of Service") { // 打开服务条款 } Text("|") .foregroundColor(.secondary) Button("Privacy Policy") { // 打开隐私政策 } Text("|") .foregroundColor(.secondary) Button("Restore Purchase") { // 恢复购买 } } .font(Typography.font(for: .caption, family: .quicksand)) .foregroundColor(.secondary) .padding(.top, 16) } // MARK: - 订阅处理 private func handleSubscribe() { isLoading = true // 模拟订阅处理 DispatchQueue.main.asyncAfter(deadline: .now() + 2) { isLoading = false // 处理订阅逻辑 } } } // MARK: - 订阅计划卡片 struct SubscriptionPlanCard: View { let plan: SubscriptionPlan let isSelected: Bool let onTap: () -> Void var body: some View { Button(action: onTap) { VStack(spacing: 12) { if plan.isPopular { HStack { Spacer() Text("Popular") .font(Typography.font(for: .caption, family: .quicksand)) .fontWeight(.medium) .foregroundColor(.white) .padding(.horizontal, 12) .padding(.vertical, 4) .background(Color.black) .cornerRadius(12) } .padding(.top, -8) } Text(plan.displayName) .font(Typography.font(for: .title, family: .quicksand)) .fontWeight(.bold) .foregroundColor(plan == .pioneer ? .white : .gray) Text(plan.price) .font(Typography.font(for: .body, family: .quicksand)) .fontWeight(.medium) .foregroundColor(plan == .pioneer ? .white : .gray) Spacer() } .frame(maxWidth: .infinity, minHeight: 120) .padding(16) .background(plan == .pioneer ? Color.orange : Color.white) .cornerRadius(16) .overlay( RoundedRectangle(cornerRadius: 16) .stroke(isSelected ? Color.blue : (plan == .free ? Color.blue : Color.clear), lineWidth: 2) ) .shadow(color: Color.black.opacity(0.1), radius: 4, x: 0, y: 2) } } } // MARK: - 功能对比行 struct FeatureRow: View { let feature: SubscriptionFeature let isLast: Bool var body: some View { HStack { Text(feature.name) .font(Typography.font(for: .subtitle, family: .quicksand)) .fontWeight(.medium) .frame(maxWidth: .infinity, alignment: .leading) .foregroundColor(.primary) Text(feature.freeValue) .font(Typography.font(for: .caption, family: .quicksand)) .multilineTextAlignment(.center) .frame(maxWidth: .infinity) .foregroundColor(.gray) Text(feature.proValue) .font(Typography.font(for: .caption, family: .quicksand)) .multilineTextAlignment(.center) .frame(maxWidth: .infinity) .foregroundColor(.gray) } .padding(.vertical, 12) .padding(.horizontal, 16) .background(Color(.systemBackground)) if !isLast { Divider() .padding(.leading, 16) } } } #Preview { SubscribeView() }