完善实名认证、钱包与打卡点模块 UI 与业务逻辑。

对齐 Android 实名认证流程与审核页、钱包提现/积分兑换界面,优化打卡点列表与详情交互,并补充相关资源、主题 token 与单元测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-13 16:28:10 +08:00
parent 07b2b9d459
commit 5138c1c11a
63 changed files with 3101 additions and 745 deletions

View File

@ -89,6 +89,7 @@ final class WalletViewModel {
private let staffIdProvider: () -> Int?
private let calendar: Calendar
private let nowProvider: () -> Date
private var withdrawPage = 1
private var withdrawCanLoadMore = true
private var transactionPage = 1
@ -101,10 +102,12 @@ final class WalletViewModel {
staffIdProvider: @escaping () -> Int? = {
Int(AppStore.shared.session.userId.trimmingCharacters(in: .whitespacesAndNewlines))
},
calendar: Calendar = Calendar(identifier: .gregorian)
calendar: Calendar = Calendar(identifier: .gregorian),
nowProvider: @escaping () -> Date = Date.init
) {
self.staffIdProvider = staffIdProvider
self.calendar = calendar
self.nowProvider = nowProvider
}
///
@ -223,8 +226,11 @@ final class WalletViewModel {
private func loadWithdraw(api: any WalletPageServing, refresh: Bool) async {
if withdrawLoading && !refresh { return }
let targetPage = refresh ? 1 : withdrawPage
let previousPage = withdrawPage
let previousCanLoadMore = withdrawCanLoadMore
withdrawLoading = true
withdrawRefreshing = refresh
errorMessage = nil
if refresh {
withdrawCanLoadMore = true
withdrawPage = 1
@ -238,8 +244,8 @@ final class WalletViewModel {
withdrawPage = response.item.isEmpty ? targetPage : targetPage + 1
} catch {
if refresh {
withdrawRecords = []
withdrawCanLoadMore = false
withdrawPage = previousPage
withdrawCanLoadMore = previousCanLoadMore
}
errorMessage = error.localizedDescription
}
@ -252,9 +258,12 @@ final class WalletViewModel {
private func loadTransaction(api: any WalletPageServing, refresh: Bool) async {
if transactionLoading && !refresh { return }
let targetPage = refresh ? 1 : transactionPage
let previousPage = transactionPage
let previousCanLoadMore = transactionCanLoadMore
let range = dateRange(for: filter)
transactionLoading = true
transactionRefreshing = refresh
errorMessage = nil
if refresh {
transactionCanLoadMore = true
transactionPage = 1
@ -276,8 +285,8 @@ final class WalletViewModel {
transactionPage = response.list.isEmpty ? targetPage : targetPage + 1
} catch {
if refresh {
transactionGroups = []
transactionCanLoadMore = false
transactionPage = previousPage
transactionCanLoadMore = previousCanLoadMore
}
errorMessage = error.localizedDescription
}
@ -287,7 +296,8 @@ final class WalletViewModel {
notifyStateChange()
}
private func dateRange(for filter: WalletFilter, now: Date = Date()) -> (start: String, end: String) {
private func dateRange(for filter: WalletFilter) -> (start: String, end: String) {
let now = nowProvider()
let end = calendar.startOfDay(for: now)
let start = calendar.date(byAdding: .day, value: -filter.dayOffset, to: end) ?? end
return (Self.dayFormatter.string(from: start), Self.dayFormatter.string(from: end))
@ -367,7 +377,7 @@ final class WithdrawViewModel {
///
func updateVerificationCode(_ value: String) {
verificationCode = value.filter(\.isNumber)
verificationCode = value
notifyStateChange()
}
@ -416,14 +426,17 @@ final class WithdrawViewModel {
///
var canSubmit: Bool {
validationMessage == nil && !submitting
withdrawInfo != nil &&
!amount.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
!verificationCode.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
!submitting
}
///
var validationMessage: String? {
guard let withdrawInfo else { return "提现信息加载中" }
guard !amount.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return "请输入提现金额" }
guard let amountDecimal = Decimal(string: amount), amountDecimal > 0 else { return "请输入有效的金额" }
guard let amountDecimal = Decimal(string: amount) else { return "请输入有效的金额" }
let withdrawable = Decimal(string: withdrawInfo.amountWithdrawable) ?? 0
if amountDecimal > withdrawable { return "金额不能大于可提现金额" }
guard !verificationCode.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return "请输入验证码" }
@ -495,6 +508,7 @@ final class PointsRedemptionViewModel {
func refreshOverview(api: any WalletPageServing) async {
guard let staffId = staffIdProvider(), staffId > 0 else { return }
overviewLoading = true
errorMessage = nil
notifyStateChange()
do {
let response = try await api.pointOverview(staffId: staffId)
@ -527,7 +541,7 @@ final class PointsRedemptionViewModel {
return
}
let points = min(Int(digits) ?? 0, withdrawnPoints)
pointsInput = points > 0 ? String(points) : ""
pointsInput = String(points)
notifyStateChange()
}
@ -566,8 +580,11 @@ final class PointsRedemptionViewModel {
private func loadWithdrawList(api: any WalletPageServing, refresh: Bool) async {
if withdrawLoading && !refresh { return }
let targetPage = refresh ? 1 : withdrawPage
let previousPage = withdrawPage
let previousCanLoadMore = withdrawCanLoadMore
withdrawLoading = true
withdrawRefreshing = refresh
errorMessage = nil
if refresh {
withdrawCanLoadMore = true
withdrawPage = 1
@ -581,8 +598,8 @@ final class PointsRedemptionViewModel {
withdrawPage = response.list.isEmpty ? targetPage : targetPage + 1
} catch {
if refresh {
withdrawRecords = []
withdrawCanLoadMore = false
withdrawPage = previousPage
withdrawCanLoadMore = previousCanLoadMore
}
errorMessage = error.localizedDescription
}