| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- //
- // AmountInputView.swift
- // LotteryTracker
- //
- // Created by aaa on 2026/1/22.
- //
-
- import SwiftUI
-
- struct AmountInputView: View {
- @Binding var betCount: Int
- @Binding var amountPerBet: String
- @FocusState private var isAmountFocused: Bool
-
- let totalAmount: Double
-
- // 常用注数选项
- private let betCountOptions = [1, 3, 5, 10, 20]
-
- // 常用金额选项
- private let amountOptions = [2.0, 5.0, 10.0, 20.0, 50.0]
-
- var body: some View {
- VStack(alignment: .leading, spacing: 20) {
- // 注数选择
- VStack(alignment: .leading, spacing: 12) {
- Text("注数")
- .font(.headline)
-
- // 注数选择器
- HStack {
- Stepper(value: $betCount, in: 1...100) {
- HStack {
- Text("\(betCount) 注")
- .font(.body)
- .foregroundColor(.primary)
-
- Spacer()
-
- Text("¥\(totalAmount, specifier: "%.2f")")
- .font(.body.bold())
- .foregroundColor(.blue)
- }
- }
- }
-
- // 快速选择按钮
- ScrollView(.horizontal, showsIndicators: false) {
- HStack(spacing: 10) {
- ForEach(betCountOptions, id: \.self) { count in
- Button(action: {
- withAnimation {
- betCount = count
- }
- }) {
- Text("\(count)注")
- .font(.system(size: 14))
- .padding(.horizontal, 12)
- .padding(.vertical, 6)
- .background(betCount == count ? Color.blue : Color.gray.opacity(0.1))
- .foregroundColor(betCount == count ? .white : .primary)
- .cornerRadius(8)
- }
- }
- }
- .padding(.horizontal, 2)
- }
- }
-
- Divider()
-
- // 每注金额
- VStack(alignment: .leading, spacing: 12) {
- Text("每注金额")
- .font(.headline)
-
- // 金额输入
- HStack {
- Text("¥")
- .font(.title3)
- .foregroundColor(.secondary)
-
- TextField("0.00", text: $amountPerBet)
- .font(.title3)
- .keyboardType(.decimalPad)
- .focused($isAmountFocused)
- .padding(.vertical, 8)
- .padding(.horizontal, 12)
- .background(Color(.systemGray6))
- .cornerRadius(8)
- .overlay(
- RoundedRectangle(cornerRadius: 8)
- .stroke(isAmountFocused ? Color.blue : Color.clear, lineWidth: 1)
- )
- .onChange(of: amountPerBet) { oldValue, newValue in
- // iOS 17 新API:接收 oldValue 和 newValue 两个参数
-
- // 限制输入为数字和小数点
- let filtered = newValue.filter { "0123456789.".contains($0) }
- if filtered != newValue {
- amountPerBet = filtered
- }
-
- // 限制小数点后最多两位
- if let dotIndex = filtered.firstIndex(of: ".") {
- let decimalPart = filtered[filtered.index(after: dotIndex)...]
- if decimalPart.count > 2 {
- amountPerBet = String(filtered.prefix(upTo: filtered.index(dotIndex, offsetBy: 3)))
- }
- }
- }
- }
-
- // 快速金额按钮
- ScrollView(.horizontal, showsIndicators: false) {
- HStack(spacing: 10) {
- ForEach(amountOptions, id: \.self) { amount in
- Button(action: {
- withAnimation {
- amountPerBet = String(format: "%.2f", amount)
- }
- }) {
- Text("¥\(amount, specifier: "%.0f")")
- .font(.system(size: 14))
- .padding(.horizontal, 12)
- .padding(.vertical, 6)
- .background(amountPerBet == String(format: "%.2f", amount) ? Color.green : Color.gray.opacity(0.1))
- .foregroundColor(amountPerBet == String(format: "%.2f", amount) ? .white : .primary)
- .cornerRadius(8)
- }
- }
- }
- .padding(.horizontal, 2)
- }
- }
-
- Divider()
-
- // 总计显示
- VStack(alignment: .leading, spacing: 8) {
- Text("总计")
- .font(.headline)
-
- HStack {
- VStack(alignment: .leading, spacing: 4) {
- Text("\(betCount) 注 × ¥\(Double(amountPerBet) ?? 0, specifier: "%.2f")/注")
- .font(.subheadline)
- .foregroundColor(.secondary)
-
- Text("¥\(totalAmount, specifier: "%.2f")")
- .font(.system(size: 32, weight: .bold))
- .foregroundColor(.blue)
- }
-
- Spacer()
-
- Image(systemName: "creditcard.fill")
- .font(.system(size: 36))
- .foregroundColor(.green.opacity(0.7))
- }
- .padding()
- .background(
- RoundedRectangle(cornerRadius: 12)
- .fill(Color.blue.opacity(0.05))
- .overlay(
- RoundedRectangle(cornerRadius: 12)
- .stroke(Color.blue.opacity(0.2), lineWidth: 1)
- )
- )
- }
- }
- .padding(.vertical, 8)
- }
- }
-
- // 预览
- struct AmountInputView_Previews: PreviewProvider {
- static var previews: some View {
- // 方法1:使用简单的 State 包装
- Group {
- // 预览1:默认状态
- AmountInputViewPreviewWrapper()
-
- // 预览2:大额状态
- AmountInputViewPreviewWrapper(betCount: 10, amountPerBet: "50.00")
-
- // 预览3:小数金额
- AmountInputViewPreviewWrapper(betCount: 3, amountPerBet: "2.50")
- }
- }
- }
-
- // 预览包装器
- struct AmountInputViewPreviewWrapper: View {
- @State private var betCount: Int
- @State private var amountPerBet: String
-
- init(betCount: Int = 3, amountPerBet: String = "2.00") {
- self._betCount = State(initialValue: betCount)
- self._amountPerBet = State(initialValue: amountPerBet)
- }
-
- var body: some View {
- AmountInputView(
- betCount: $betCount,
- amountPerBet: $amountPerBet,
- totalAmount: (Double(amountPerBet) ?? 0) * Double(betCount)
- )
- .padding()
- .previewLayout(.sizeThatFits)
- }
- }
|