Created
June 11, 2026 09:51
-
-
Save jyxjjj/75c9a22725b5b88820fa2104a706f813 to your computer and use it in GitHub Desktop.
Salary Per Second
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Combine | |
| import SwiftUI | |
| struct ContentView: View { | |
| @State private var salary = "20000" | |
| @State private var days = "21" | |
| @State private var hours = "8" | |
| @State private var startedAt: Date? | |
| @State private var now = Date() | |
| @State private var showSummary = false | |
| @State private var finalEarned = 0.0 | |
| @State private var finalAverage = 0.0 | |
| @FocusState private var focusedField: Field? | |
| private let timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect() | |
| private var monthSalary: Double { Double(salary) ?? 0 } | |
| private var workDays: Double { Double(days) ?? 0 } | |
| private var workHours: Double { Double(hours) ?? 0 } | |
| private var daySalary: Double { workDays > 0 ? monthSalary / workDays : 0 } | |
| private var planSeconds: Double { workHours * 3600 } | |
| private var elapsed: Double { | |
| guard let startedAt else { return 0 } | |
| return max(0, now.timeIntervalSince(startedAt)) | |
| } | |
| private var earned: Double { | |
| guard planSeconds > 0 else { return 0 } | |
| return daySalary * min(elapsed, planSeconds) / planSeconds | |
| } | |
| private var average: Double { | |
| guard elapsed > 0 else { return planSeconds > 0 ? daySalary / planSeconds : 0 } | |
| return earned / elapsed | |
| } | |
| private var canStart: Bool { monthSalary > 0 && workDays > 0 && workHours > 0 } | |
| private enum Field { case salary, days, hours } | |
| var body: some View { | |
| ZStack { | |
| VStack(spacing: 8) { | |
| TextField("请输入税前月薪", text: $salary) | |
| .focused($focusedField, equals: .salary) | |
| TextField("本月上班天数", text: $days) | |
| .focused($focusedField, equals: .days) | |
| TextField("每日工作小时数", text: $hours) | |
| .focused($focusedField, equals: .hours) | |
| Button(startedAt == nil ? "上班打卡" : "下班打卡") { | |
| startedAt == nil ? start() : stop() | |
| } | |
| .controlSize(.large) | |
| .disabled(startedAt == nil && !canStart) | |
| Text("今日已赚金额:\(money(earned))") | |
| Text("秒均金额:\(money(average))") | |
| } | |
| .disabled(showSummary) | |
| if showSummary { | |
| VStack(spacing: 10) { | |
| Text("恭喜你") | |
| Text("今天赚了 \(money(finalEarned))") | |
| Text("平均每秒 \(money(finalAverage))") | |
| Button("返回") { | |
| showSummary = false | |
| } | |
| .controlSize(.large) | |
| } | |
| .frame(maxWidth: .infinity, maxHeight: .infinity) | |
| .background(.background) | |
| .zIndex(1) | |
| } | |
| } | |
| .textFieldStyle(.roundedBorder) | |
| .controlSize(.small) | |
| .font(.system(size: 16)) | |
| .frame(width: 220) | |
| .padding(10) | |
| .onReceive(timer) { date in | |
| if startedAt != nil { now = date } | |
| } | |
| } | |
| private func start() { | |
| now = Date() | |
| startedAt = now | |
| focusedField = nil | |
| showSummary = false | |
| } | |
| private func stop() { | |
| focusedField = nil | |
| now = Date() | |
| finalEarned = earned | |
| finalAverage = average | |
| startedAt = nil | |
| showSummary = true | |
| } | |
| private func money(_ value: Double) -> String { | |
| "¥" + value.formatted(.number.precision(.fractionLength(4))) | |
| } | |
| } | |
| #Preview { | |
| ContentView() | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment