// // CreditsDetailView.swift // wake // // Created by fairclip on 2025/8/19. // import SwiftUI // MARK: - 积分交易类型 enum CreditTransactionType: String, CaseIterable { case photoUnderstanding = "Photo Understanding" case videoUnderstanding = "Video Understanding" case mysteryBoxPurchase = "Mystery Box Purchase" case dailyBonus = "Daily Bonus" case subscriptionBonus = "Subscription Bonus" var creditChange: Int { switch self { case .photoUnderstanding: return -1 case .videoUnderstanding: return -32 case .mysteryBoxPurchase: return -100 case .dailyBonus: return 200 case .subscriptionBonus: return 500 } } var icon: String { switch self { case .photoUnderstanding: return "photo" case .videoUnderstanding: return "video" case .mysteryBoxPurchase: return "gift" case .dailyBonus: return "calendar" case .subscriptionBonus: return "star.fill" } } } // MARK: - 积分交易记录 struct CreditTransaction { let id = UUID() let type: CreditTransactionType let date: Date let creditChange: Int init(type: CreditTransactionType, date: Date, creditChange: Int? = nil) { self.type = type self.date = date self.creditChange = creditChange ?? type.creditChange } } // MARK: - 积分详情页面 struct CreditsDetailView: View { // Removed presentationMode; use Router.shared.pop() for back navigation @State private var showRules = false // 示例数据 private let totalCredits = 3290 private let expiringToday = 200 private let transactions: [CreditTransaction] = [ CreditTransaction(type: .photoUnderstanding, date: Calendar.current.date(byAdding: .hour, value: -2, to: Date()) ?? Date()), CreditTransaction(type: .videoUnderstanding, date: Calendar.current.date(byAdding: .hour, value: -4, to: Date()) ?? Date()), CreditTransaction(type: .mysteryBoxPurchase, date: Calendar.current.date(byAdding: .day, value: -1, to: Date()) ?? Date()), CreditTransaction(type: .dailyBonus, date: Calendar.current.date(byAdding: .day, value: -1, to: Date()) ?? Date()), CreditTransaction(type: .subscriptionBonus, date: Calendar.current.date(byAdding: .day, value: -2, to: Date()) ?? Date()) ] var body: some View { ScrollView { VStack(spacing: 0) { // 导航栏 navigationHeader // 主积分卡片 mainCreditsCard // 积分历史 creditsHistorySection Spacer(minLength: 100) } } .background(Theme.Colors.background) .navigationBarHidden(true) } // MARK: - 导航栏 private var navigationHeader: some View { NaviHeader(title: "Credits") { Router.shared.pop() } } // MARK: - 主积分卡片 private var mainCreditsCard: some View { VStack(spacing: 0) { // 主要积分显示区域 HStack { // 左侧三角形图标 Circle() .fill(Color.black) .frame(width: 80, height: 80) .overlay( Image(systemName: "triangle.fill") .foregroundColor(.white) .font(.system(size: 24, weight: .bold)) ) Spacer() // 右侧积分信息 VStack(alignment: .trailing, spacing: 8) { HStack(spacing: 8) { Circle() .fill(Color.black) .frame(width: 24, height: 24) .overlay( Image(systemName: "triangle.fill") .foregroundColor(.white) .font(.system(size: 8)) ) Text("\(totalCredits)") .font(Typography.font(for: .headline, family: .quicksandBold, size: 36)) .foregroundColor(.black) } Text("Expiring Today : \(expiringToday)") .font(Typography.font(for: .body, family: .quicksand)) .foregroundColor(.black.opacity(0.8)) } } .padding(Theme.Spacing.xl) // 虚线分隔 DashedLine() .stroke(Color.black.opacity(0.3), style: StrokeStyle(lineWidth: 1, dash: [5, 5])) .frame(height: 1) .padding(.horizontal, Theme.Spacing.xl) // 积分规则展开区域 creditsRulesSection } .background( LinearGradient( colors: [ Color(hex: "FFB645"), Color(hex: "FFA726") ], startPoint: .topLeading, endPoint: .bottomTrailing ) ) .cornerRadius(Theme.CornerRadius.large) .padding(.horizontal, Theme.Spacing.xl) .padding(.top, Theme.Spacing.xl) } // MARK: - 积分规则区域 private var creditsRulesSection: some View { VStack(spacing: 0) { // 规则标题按钮 Button(action: { withAnimation(.easeInOut(duration: 0.3)) { showRules.toggle() } }) { HStack { Text("Credits Rules") .font(Typography.font(for: .body, family: .quicksandBold)) .foregroundColor(.black) Spacer() Image(systemName: "chevron.right") .foregroundColor(.black) .font(.system(size: 14, weight: .medium)) .rotationEffect(.degrees(showRules ? 90 : 0)) .animation(.easeInOut(duration: 0.3), value: showRules) } .padding(.horizontal, Theme.Spacing.xl) .padding(.vertical, Theme.Spacing.lg) } // 规则内容 if showRules { VStack(alignment: .leading, spacing: Theme.Spacing.sm) { Text("Credits can be used for material indexing (1 credit per photo or per second of video) and for buying blind boxes (100 credits each).") .font(Typography.font(for: .subtitle, family: .quicksand)) .foregroundColor(.black.opacity(0.8)) .multilineTextAlignment(.leading) } .padding(.horizontal, Theme.Spacing.xl) .padding(.bottom, Theme.Spacing.lg) } } } // MARK: - 积分历史区域 private var creditsHistorySection: some View { VStack(alignment: .leading, spacing: Theme.Spacing.lg) { Text("Points History") .font(Typography.font(for: .title, family: .quicksandBold)) .foregroundColor(Theme.Colors.textPrimary) .padding(.horizontal, Theme.Spacing.xxl) LazyVStack(spacing: 0) { ForEach(Array(transactions.enumerated()), id: \.element.id) { index, transaction in CreditTransactionRow( transaction: transaction, isLast: index == transactions.count - 1 ) } } .background(Color(.systemBackground)) .cornerRadius(Theme.CornerRadius.medium) .padding(.horizontal, Theme.Spacing.xl) } .padding(.top, Theme.Spacing.xl) } } // MARK: - 积分交易行组件 struct CreditTransactionRow: View { let transaction: CreditTransaction let isLast: Bool var body: some View { VStack(spacing: 0) { HStack(spacing: Theme.Spacing.lg) { VStack(alignment: .leading, spacing: 4) { Text(transaction.type.rawValue) .font(Typography.font(for: .body, family: .quicksandBold)) .foregroundColor(Theme.Colors.textPrimary) Text(formatDate(transaction.date)) .font(Typography.font(for: .caption, family: .quicksand)) .foregroundColor(Theme.Colors.textSecondary) } Spacer() Text("\(transaction.creditChange > 0 ? "+" : "")\(transaction.creditChange)") .font(Typography.font(for: .body, family: .quicksandBold)) .foregroundColor(transaction.creditChange > 0 ? Theme.Colors.success : Theme.Colors.textPrimary) } .padding(.horizontal, Theme.Spacing.lg) .padding(.vertical, Theme.Spacing.lg) if !isLast { Divider() .background(Theme.Colors.borderLight) } } } private func formatDate(_ date: Date) -> String { let formatter = DateFormatter() formatter.dateFormat = "MM-dd-yyyy" return formatter.string(from: date) } } // MARK: - 虚线组件 struct DashedLine: Shape { func path(in rect: CGRect) -> Path { var path = Path() path.move(to: CGPoint(x: 0, y: 0)) path.addLine(to: CGPoint(x: rect.width, y: 0)) return path } } // MARK: - 预览 #Preview { CreditsDetailView() }