ios应用,记录购买彩票,统计盈亏

HappyEightCompoents.swift 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. //
  2. // HappyEightCompoents.swift
  3. // LotteryTracker
  4. //
  5. // Created by aaa on 2026/1/26.
  6. //
  7. import SwiftUI
  8. struct HappyEightSection: View {
  9. @Binding var numbers: String
  10. @State private var selectedNumbers: [Int] = []
  11. @State private var selectionType: Happy8Type = .happy8One // 默认为选一
  12. private let columns = Array(repeating: GridItem(.flexible()), count: 10) // 10列网格
  13. var body: some View {
  14. VStack(alignment: .leading, spacing: 16) {
  15. // 头部标题
  16. HStack {
  17. SectionHeader(title: "选择号码", icon: "number.circle")
  18. Picker(selectionType.string, selection: $selectionType) {
  19. ForEach(Happy8Type.allCases) { type in // ✅ 使用 ForEach 简化
  20. Text(type.string).tag(type)
  21. }
  22. }
  23. .pickerStyle(.menu)
  24. }
  25. // 选号类型选择器
  26. SelectionTypePicker(selectedType: $selectionType)
  27. // 选择区号码网格(1-80)
  28. VStack(alignment: .leading, spacing: 12) {
  29. NumberSelectionGrid(
  30. lotteryType: .happy8,
  31. title: "号码区",
  32. color: .primary,
  33. range: 1...80,
  34. maxSelection: selectionType.rawValue,
  35. selectedNumbers: $selectedNumbers
  36. )
  37. }
  38. // 快速操作栏
  39. QuickActionsBar2(
  40. maxSelection: 1,
  41. count: selectedNumbers.count,
  42. isEmpty: selectedNumbers.isEmpty,
  43. onRandom: generateRandomNumbers,
  44. onClear: clearAllNumbers,
  45. onSmartRandom: generateSmartNumbers
  46. )
  47. // 已选号码展示
  48. if !selectedNumbers.isEmpty {
  49. SelectedNumbersDisplay()
  50. }
  51. }
  52. .onChange(of: selectedNumbers) {
  53. updateNumbers()
  54. }
  55. .onChange(of: selectionType) {
  56. adjustSelectedNumbers()
  57. }
  58. }
  59. // MARK: - 子组件
  60. private func SelectionBadgeView() -> some View {
  61. HStack(spacing: 6) {
  62. Image(systemName: "checkmark.circle.fill")
  63. .font(.caption)
  64. .foregroundColor(selectedNumbers.count >= 5 ? .green : .orange)
  65. Text("\(selectedNumbers.count)/\(selectionType.rawValue)")
  66. .font(.system(size: 14, weight: .bold))
  67. .foregroundColor(selectedNumbers.count == selectionType.rawValue ? .white : .primary)
  68. }
  69. .padding(.horizontal, 12)
  70. .padding(.vertical, 8)
  71. .background(
  72. Capsule()
  73. .fill(selectedNumbers.count == selectionType.rawValue ?
  74. Color.green.gradient :
  75. Color(.systemGray6).gradient)
  76. )
  77. .overlay(
  78. Capsule()
  79. .stroke(selectedNumbers.count == selectionType.rawValue ?
  80. Color.green.opacity(0.5) :
  81. Color.gray.opacity(0.3),
  82. lineWidth: 1)
  83. )
  84. .shadow(color: selectedNumbers.count == selectionType.rawValue ?
  85. Color.green.opacity(0.3) : Color.clear,
  86. radius: 3, x: 0, y: 2)
  87. }
  88. private func SelectedNumbersDisplay() -> some View {
  89. VStack(alignment: .leading, spacing: 12) {
  90. HStack {
  91. HStack {
  92. Text(" 快乐8")
  93. .font(.subheadline)
  94. .fontWeight(.semibold)
  95. .foregroundColor(.primary)
  96. Spacer()
  97. Capsule()
  98. .fill(Color(.gray).opacity(0.2))
  99. .frame(width: 48, height: 28)
  100. .overlay(
  101. Text(selectionType.string)
  102. .font(.subheadline)
  103. .fontWeight(.semibold)
  104. .foregroundColor(Color(.black))
  105. )
  106. }
  107. Spacer()
  108. if selectedNumbers.count == selectionType.rawValue {
  109. HStack(spacing: 4) {
  110. Image(systemName: "checkmark.seal.fill")
  111. .font(.caption)
  112. .foregroundColor(.green)
  113. Text("已选满")
  114. .font(.caption)
  115. .fontWeight(.medium)
  116. .foregroundColor(.green)
  117. }
  118. }
  119. }
  120. // 号码展示
  121. LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 5), spacing: 8) {
  122. ForEach(selectedNumbers.sorted(), id: \.self) { number in
  123. SelectedNumberPill(number: number)
  124. }
  125. }
  126. }
  127. .padding(16)
  128. .background(Color(.systemBackground))
  129. .cornerRadius(12)
  130. .overlay(
  131. RoundedRectangle(cornerRadius: 12)
  132. .stroke(Color.blue.opacity(0.1), lineWidth: 1)
  133. )
  134. }
  135. private func SelectedNumberPill(number: Int) -> some View {
  136. HStack(spacing: 6) {
  137. Text(String(format: "%02d", number))
  138. .font(.system(size: 16, weight: .bold, design: .rounded))
  139. // Button {
  140. // removeNumber(number)
  141. // } label: {
  142. // Image(systemName: "xmark.circle.fill")
  143. // .font(.caption)
  144. // .foregroundColor(.red.opacity(0.7))
  145. // }
  146. }
  147. .frame(width: 22, height: 22)
  148. .padding(.horizontal, 10)
  149. .padding(.vertical, 10)
  150. .background(
  151. LinearGradient(
  152. colors: [Color.blue, Color.purple],
  153. startPoint: .topLeading,
  154. endPoint: .bottomTrailing
  155. ).opacity(0.15)
  156. )
  157. .foregroundColor(.blue)
  158. .cornerRadius(20)
  159. .overlay(
  160. RoundedRectangle(cornerRadius: 20)
  161. .stroke(Color.blue.opacity(0.3), lineWidth: 1)
  162. )
  163. }
  164. // MARK: - 功能方法
  165. // private func removeNumber(_ number: Int) {
  166. // withAnimation(.spring(response: 0.3)) {
  167. // selectedNumbers.removeAll { $0 == number }
  168. // }
  169. // }
  170. private func generateRandomNumbers() {
  171. withAnimation(.spring(response: 0.4)) {
  172. var numbers = Set<Int>()
  173. while numbers.count < selectionType.rawValue {
  174. numbers.insert(Int.random(in: 1...80))
  175. }
  176. selectedNumbers = Array(numbers).sorted()
  177. }
  178. // 触觉反馈
  179. let generator = UIImpactFeedbackGenerator(style: .medium)
  180. generator.impactOccurred()
  181. }
  182. private func generateSmartNumbers() {
  183. // 智能选号:保证奇偶、大小均衡
  184. var selected = Set<Int>()
  185. // 确保有5个奇数,5个偶数
  186. let oddNumbers = (1...80).filter { $0 % 2 == 1 }.shuffled()
  187. let evenNumbers = (1...80).filter { $0 % 2 == 0 }.shuffled()
  188. let odds = selectionType.rawValue / 2
  189. selected.formUnion(oddNumbers.prefix(odds))
  190. selected.formUnion(evenNumbers.prefix(selectionType.rawValue - odds))
  191. withAnimation(.spring(response: 0.4)) {
  192. selectedNumbers = Array(selected).sorted()
  193. }
  194. }
  195. private func clearAllNumbers() {
  196. withAnimation(.spring(response: 0.3)) {
  197. selectedNumbers.removeAll()
  198. }
  199. }
  200. private func updateNumbers() {
  201. let numberStr = selectedNumbers.sorted().map { String(format: "%02d", $0) }.joined(separator: " ")
  202. numbers = numberStr
  203. }
  204. private func adjustSelectedNumbers() {
  205. clearAllNumbers()
  206. }
  207. }
  208. // MARK: - 子视图
  209. // 选号类型选择器
  210. struct SelectionTypePicker: View {
  211. @Binding var selectedType: Happy8Type
  212. private let types = Happy8Type.allCases // 使用枚举
  213. var body: some View {
  214. VStack {
  215. ScrollView(.horizontal, showsIndicators: false) {
  216. HStack {
  217. ForEach(types) { type in
  218. SelectionTypeButton(
  219. type: type,
  220. isSelected: selectedType == type,
  221. action: { selectedType = type }
  222. )
  223. }
  224. }
  225. .padding(.horizontal, 2)
  226. .padding(.vertical, 2)
  227. }
  228. }
  229. .padding(.horizontal, 4)
  230. }
  231. }
  232. // 选号类型按钮
  233. struct SelectionTypeButton: View {
  234. let type: Happy8Type
  235. let isSelected: Bool
  236. let action: () -> Void
  237. var body: some View {
  238. Button(action: {
  239. action()
  240. }) {
  241. Text(type.string)
  242. .font(.system(size: 14, weight: .medium))
  243. .foregroundColor(isSelected ? .blue : .primary)
  244. .frame(width: 52, height: 36)
  245. .background(
  246. Capsule()
  247. .fill(isSelected ? Color.blue.opacity(0.1) : Color.clear)
  248. )
  249. .overlay(
  250. Capsule()
  251. .stroke(isSelected ? Color.blue : Color.gray.opacity(0.2), lineWidth: 1)
  252. )
  253. }
  254. .buttonStyle(SelectionTypeButtonStyle())
  255. }
  256. }
  257. // 自定义按钮样式
  258. struct SelectionTypeButtonStyle: ButtonStyle {
  259. func makeBody(configuration: Configuration) -> some View {
  260. configuration.label
  261. .scaleEffect(configuration.isPressed ? 0.95 : 1.0)
  262. .opacity(configuration.isPressed ? 0.8 : 1.0)
  263. .animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
  264. }
  265. }