315 lines
9.9 KiB
Swift
315 lines
9.9 KiB
Swift
//
|
|
// CreditsInfoCard.swift
|
|
// wake
|
|
//
|
|
// Created by fairclip on 2025/8/19.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
// MARK: - 积分类型枚举
|
|
enum CreditType: String, CaseIterable {
|
|
case daily = "Daily"
|
|
case purchased = "Purchased"
|
|
case bonus = "Bonus"
|
|
case permanent = "Permanent"
|
|
|
|
var displayName: String {
|
|
switch self {
|
|
case .daily:
|
|
return "Daily Credits"
|
|
case .purchased:
|
|
return "Purchased Credits"
|
|
case .bonus:
|
|
return "Bonus Credits"
|
|
case .permanent:
|
|
return "Permanent Credits"
|
|
}
|
|
}
|
|
|
|
var icon: String {
|
|
switch self {
|
|
case .daily:
|
|
return "calendar"
|
|
case .purchased:
|
|
return "creditcard"
|
|
case .bonus:
|
|
return "gift"
|
|
case .permanent:
|
|
return "infinity"
|
|
}
|
|
}
|
|
|
|
var color: Color {
|
|
switch self {
|
|
case .daily:
|
|
return Theme.Colors.info
|
|
case .purchased:
|
|
return Theme.Colors.success
|
|
case .bonus:
|
|
return Theme.Colors.warning
|
|
case .permanent:
|
|
return Theme.Colors.primary
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - 积分信息数据模型
|
|
struct CreditInfo {
|
|
let type: CreditType
|
|
let amount: Int
|
|
let description: String
|
|
}
|
|
|
|
// MARK: - 积分信息卡片组件
|
|
struct CreditsInfoCard: View {
|
|
let totalCredits: Int
|
|
let creditBreakdown: [CreditInfo]
|
|
let onInfoTap: (() -> Void)?
|
|
let onDetailTap: (() -> Void)?
|
|
|
|
@State private var showBreakdown = false
|
|
|
|
init(
|
|
totalCredits: Int,
|
|
creditBreakdown: [CreditInfo] = [],
|
|
onInfoTap: (() -> Void)? = nil,
|
|
onDetailTap: (() -> Void)? = nil
|
|
) {
|
|
self.totalCredits = totalCredits
|
|
self.creditBreakdown = creditBreakdown
|
|
self.onInfoTap = onInfoTap
|
|
self.onDetailTap = onDetailTap
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(spacing: 0) {
|
|
// 主要积分显示区域
|
|
mainCreditsSection
|
|
|
|
// 积分明细展开区域
|
|
if showBreakdown && !creditBreakdown.isEmpty {
|
|
creditsBreakdownSection
|
|
}
|
|
}
|
|
.background(Color(.systemBackground))
|
|
.cornerRadius(Theme.CornerRadius.medium)
|
|
.shadow(color: Theme.Shadows.small, radius: Theme.Shadows.cardShadow.radius, x: Theme.Shadows.cardShadow.x, y: Theme.Shadows.cardShadow.y)
|
|
}
|
|
|
|
// MARK: - 主要积分显示区域
|
|
private var mainCreditsSection: some View {
|
|
HStack(spacing: Theme.Spacing.lg) {
|
|
// 积分图标和数量
|
|
HStack(spacing: Theme.Spacing.sm) {
|
|
Circle()
|
|
.fill(Theme.Gradients.primaryGradient)
|
|
.frame(width: 40, height: 40)
|
|
.overlay(
|
|
Image(systemName: "star.fill")
|
|
.foregroundColor(.white)
|
|
.font(.system(size: 18, weight: .semibold))
|
|
)
|
|
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text("Credits")
|
|
.font(Typography.font(for: .caption, family: .quicksand))
|
|
.foregroundColor(Theme.Colors.textSecondary)
|
|
|
|
Text("\(totalCredits)")
|
|
.font(Typography.font(for: .title, family: .quicksandBold))
|
|
.foregroundColor(Theme.Colors.textPrimary)
|
|
}
|
|
}
|
|
|
|
Spacer()
|
|
|
|
// 操作按钮区域
|
|
HStack(spacing: Theme.Spacing.sm) {
|
|
// 信息按钮
|
|
Button(action: {
|
|
onInfoTap?()
|
|
}) {
|
|
Image(systemName: "info.circle")
|
|
.foregroundColor(Theme.Colors.textSecondary)
|
|
.font(.system(size: 16))
|
|
}
|
|
|
|
// 展开/收起按钮
|
|
if !creditBreakdown.isEmpty {
|
|
Button(action: {
|
|
withAnimation(.easeInOut(duration: 0.3)) {
|
|
showBreakdown.toggle()
|
|
}
|
|
}) {
|
|
Image(systemName: showBreakdown ? "chevron.up" : "chevron.down")
|
|
.foregroundColor(Theme.Colors.textSecondary)
|
|
.font(.system(size: 14, weight: .medium))
|
|
}
|
|
}
|
|
|
|
// 详情按钮
|
|
Button(action: {
|
|
onDetailTap?()
|
|
}) {
|
|
Image(systemName: "chevron.right")
|
|
.foregroundColor(Theme.Colors.textSecondary)
|
|
.font(.system(size: 14, weight: .medium))
|
|
}
|
|
}
|
|
}
|
|
.padding(Theme.Spacing.lg)
|
|
}
|
|
|
|
// MARK: - 积分明细展开区域
|
|
private var creditsBreakdownSection: some View {
|
|
VStack(spacing: 0) {
|
|
Divider()
|
|
.background(Theme.Colors.border)
|
|
|
|
VStack(spacing: Theme.Spacing.sm) {
|
|
ForEach(Array(creditBreakdown.enumerated()), id: \.offset) { index, credit in
|
|
CreditBreakdownRow(credit: credit, isLast: index == creditBreakdown.count - 1)
|
|
}
|
|
}
|
|
.padding(Theme.Spacing.lg)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - 积分明细行组件
|
|
struct CreditBreakdownRow: View {
|
|
let credit: CreditInfo
|
|
let isLast: Bool
|
|
|
|
var body: some View {
|
|
HStack(spacing: Theme.Spacing.md) {
|
|
// 积分类型图标
|
|
Circle()
|
|
.fill(credit.type.color.opacity(0.1))
|
|
.frame(width: 32, height: 32)
|
|
.overlay(
|
|
Image(systemName: credit.type.icon)
|
|
.foregroundColor(credit.type.color)
|
|
.font(.system(size: 14, weight: .medium))
|
|
)
|
|
|
|
// 积分信息
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(credit.type.displayName)
|
|
.font(Typography.font(for: .subtitle, family: .quicksand))
|
|
.foregroundColor(Theme.Colors.textPrimary)
|
|
|
|
Text(credit.description)
|
|
.font(Typography.font(for: .caption, family: .quicksand))
|
|
.foregroundColor(Theme.Colors.textSecondary)
|
|
.lineLimit(2)
|
|
}
|
|
|
|
Spacer()
|
|
|
|
// 积分数量
|
|
Text("+\(credit.amount)")
|
|
.font(Typography.font(for: .body, family: .quicksandBold))
|
|
.foregroundColor(credit.type.color)
|
|
}
|
|
.padding(.vertical, Theme.Spacing.xs)
|
|
|
|
if !isLast {
|
|
Divider()
|
|
.background(Theme.Colors.borderLight)
|
|
.padding(.leading, 44)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - 积分使用统计组件
|
|
struct CreditsUsageCard: View {
|
|
let todayUsed: Int
|
|
let weeklyUsed: Int
|
|
let monthlyUsed: Int
|
|
|
|
var body: some View {
|
|
VStack(spacing: Theme.Spacing.md) {
|
|
HStack {
|
|
Text("Credits Usage")
|
|
.font(Typography.font(for: .subtitle, family: .quicksandBold))
|
|
.foregroundColor(Theme.Colors.textPrimary)
|
|
|
|
Spacer()
|
|
|
|
Text("This Period")
|
|
.font(Typography.font(for: .caption, family: .quicksand))
|
|
.foregroundColor(Theme.Colors.textSecondary)
|
|
}
|
|
|
|
HStack(spacing: Theme.Spacing.lg) {
|
|
UsageStatItem(title: "Today", value: todayUsed, color: Theme.Colors.info)
|
|
|
|
Divider()
|
|
.frame(height: 40)
|
|
|
|
UsageStatItem(title: "Week", value: weeklyUsed, color: Theme.Colors.warning)
|
|
|
|
Divider()
|
|
.frame(height: 40)
|
|
|
|
UsageStatItem(title: "Month", value: monthlyUsed, color: Theme.Colors.success)
|
|
}
|
|
}
|
|
.padding(Theme.Spacing.lg)
|
|
.background(Color(.systemBackground))
|
|
.cornerRadius(Theme.CornerRadius.medium)
|
|
.shadow(color: Theme.Shadows.small, radius: Theme.Shadows.cardShadow.radius, x: Theme.Shadows.cardShadow.x, y: Theme.Shadows.cardShadow.y)
|
|
}
|
|
}
|
|
|
|
// MARK: - 使用统计项组件
|
|
struct UsageStatItem: View {
|
|
let title: String
|
|
let value: Int
|
|
let color: Color
|
|
|
|
var body: some View {
|
|
VStack(spacing: Theme.Spacing.xs) {
|
|
Text("\(value)")
|
|
.font(Typography.font(for: .title, family: .quicksandBold))
|
|
.foregroundColor(color)
|
|
|
|
Text(title)
|
|
.font(Typography.font(for: .caption, family: .quicksand))
|
|
.foregroundColor(Theme.Colors.textSecondary)
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
}
|
|
|
|
// MARK: - 预览
|
|
#Preview("Credits Info Card") {
|
|
VStack(spacing: 20) {
|
|
CreditsInfoCard(
|
|
totalCredits: 3290,
|
|
creditBreakdown: [
|
|
CreditInfo(type: .daily, amount: 200, description: "Daily free credits"),
|
|
CreditInfo(type: .purchased, amount: 1000, description: "Purchased package"),
|
|
CreditInfo(type: .bonus, amount: 500, description: "Welcome bonus"),
|
|
CreditInfo(type: .permanent, amount: 1590, description: "Subscription credits")
|
|
],
|
|
onInfoTap: {
|
|
print("Info tapped")
|
|
},
|
|
onDetailTap: {
|
|
print("Detail tapped")
|
|
}
|
|
)
|
|
|
|
CreditsUsageCard(
|
|
todayUsed: 45,
|
|
weeklyUsed: 280,
|
|
monthlyUsed: 1150
|
|
)
|
|
}
|
|
.padding()
|
|
.background(Color(.systemGroupedBackground))
|
|
}
|