// // 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 = .free @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 // 订阅计划选择 subscriptionPlansSection // 特别优惠提示 specialOfferBanner // 功能对比表 featureComparisonTable // 订阅按钮 subscribeButton // 法律链接 legalLinks Spacer(minLength: 100) } } .background(Color(.systemGroupedBackground)) .navigationBarHidden(true) } } // MARK: - 导航栏 private var navigationHeader: some View { NaviHeader(title: "Subscription") { presentationMode.wrappedValue.dismiss() } } // MARK: - 当前订阅状态卡片 private var currentSubscriptionCard: some View { VStack(spacing: 0) { HStack { VStack(alignment: .leading, spacing: 8) { Text("Free") .font(Typography.font(for: .headline, family: .quicksand)) .fontWeight(.bold) Button(action: { // 订阅操作 }) { Text("Subscribe") .font(Typography.font(for: .subtitle, family: .quicksand)) .fontWeight(.medium) .foregroundColor(.black) .padding(.horizontal, 16) .padding(.vertical, 8) .background(Color.orange) .cornerRadius(20) } } Spacer() // 播放按钮图标 Circle() .fill(Color.black) .frame(width: 60, height: 60) .overlay( Image(systemName: "play.fill") .foregroundColor(.white) .font(.title2) ) } .padding(20) .background(Color.orange.opacity(0.2)) .cornerRadius(16) .padding(.horizontal, 20) } } // MARK: - 积分信息 private var creditsSection: some View { HStack { Text("Credits: 3290") .font(Typography.font(for: .body, family: .quicksand)) .fontWeight(.medium) Button(action: { // 积分信息操作 }) { Image(systemName: "info.circle") .foregroundColor(.gray) } Spacer() Image(systemName: "chevron.right") .foregroundColor(.gray) .font(.caption) } .padding(.horizontal, 20) .padding(.vertical, 16) .background(Color(.systemBackground)) .cornerRadius(12) .padding(.horizontal, 20) .padding(.top, 20) } // MARK: - 订阅计划选择 private var subscriptionPlansSection: some View { HStack(spacing: 16) { // Free 计划 SubscriptionPlanCard( plan: .free, isSelected: selectedPlan == .free, onTap: { selectedPlan = .free } ) // Pioneer 计划 SubscriptionPlanCard( plan: .pioneer, isSelected: selectedPlan == .pioneer, onTap: { selectedPlan = .pioneer } ) } .padding(.horizontal, 20) .padding(.top, 20) } // MARK: - 特别优惠横幅 private var specialOfferBanner: some View { Text("First 100 users get a special deal: just $1 for your first month!") .font(Typography.font(for: .caption, family: .quicksand)) .multilineTextAlignment(.center) .padding(.horizontal, 20) .padding(.top, 12) .foregroundColor(.secondary) } // 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() }