添加即时收款流程并对齐 Android

This commit is contained in:
2026-07-07 09:29:03 +08:00
parent 0964ef8b80
commit 16e86c8899
17 changed files with 1548 additions and 1 deletions

View File

@ -0,0 +1,175 @@
//
// PaymentCollectionDetailsViewModel.swift
// suixinkan
//
import Foundation
import UIKit
/// ViewModel
final class PaymentCollectionDetailsViewModel {
private(set) var loading = false
private(set) var qrImage: UIImage?
private(set) var scenicName = ""
private(set) var staffId = ""
private(set) var isVoiceBroadcastOpen = false
private(set) var showAmountDialog = false
private(set) var amount = ""
private(set) var remark = ""
let merchantInfo = PaymentMerchantInfo.defaultInfo
var onStateChange: (() -> Void)?
var onShowMessage: ((String) -> Void)?
private var staticPayURL = ""
private var dynamicPayURL = ""
private let appStore: AppStore
init(appStore: AppStore = .shared) {
self.appStore = appStore
}
var hasPayCode: Bool {
qrImage != nil && !staticPayURL.isEmpty
}
var displayScenicName: String {
scenicName.isEmpty ? "暂无景区" : scenicName
}
func refreshLocalState() {
scenicName = appStore.currentScenicName
staffId = appStore.userId
isVoiceBroadcastOpen = appStore.isOpenReceiveVoice
notifyStateChange()
}
func loadPayCode(api: PaymentAPI) async {
guard !loading else { return }
refreshLocalState()
loading = true
notifyStateChange()
defer {
loading = false
notifyStateChange()
}
let scenicId = appStore.currentScenicId
guard scenicId > 0 else {
clearPayCode()
return
}
do {
let response = try await api.payCode(scenicId: scenicId)
staticPayURL = response.staticPayURL
dynamicPayURL = response.dynamicPayURL
qrImage = QRCodeGenerator.generateStyledQRCode(content: staticPayURL)
} catch {
clearPayCode()
}
}
func refresh(api: PaymentAPI) async {
await loadPayCode(api: api)
}
func showSetAmountDialog() {
guard !staticPayURL.isEmpty else {
onShowMessage?("请先获取收款码")
return
}
amount = ""
remark = ""
showAmountDialog = true
notifyStateChange()
}
func hideAmountDialog() {
showAmountDialog = false
amount = ""
remark = ""
notifyStateChange()
}
func updateAmount(_ value: String) {
guard value.isEmpty || value.matches(PaymentAmountValidator.inputPattern) else { return }
amount = value
notifyStateChange()
}
func updateRemark(_ value: String) {
guard value.count <= 200 else { return }
remark = value
notifyStateChange()
}
func confirmAmount() {
if let message = PaymentAmountValidator.validationMessage(for: amount) {
onShowMessage?(message)
return
}
guard !dynamicPayURL.isEmpty else {
onShowMessage?("请先获取收款码")
return
}
let dynamicURL = dynamicPayURL + "&amount=\(amount)&remark=\(remark)"
qrImage = QRCodeGenerator.generateStyledQRCode(content: dynamicURL)
showAmountDialog = false
onShowMessage?("金额设置成功,二维码已更新")
notifyStateChange()
}
func saveQRCode() async {
guard qrImage != nil else {
onShowMessage?("请先获取收款码")
return
}
let success = await PhotoLibrarySaver.saveImage(qrImage!)
onShowMessage?(success ? "保存成功" : "保存失败")
}
func toggleReceiveVoice() {
isVoiceBroadcastOpen.toggle()
appStore.isOpenReceiveVoice = isVoiceBroadcastOpen
notifyStateChange()
}
private func clearPayCode() {
staticPayURL = ""
dynamicPayURL = ""
qrImage = nil
}
private func notifyStateChange() {
onStateChange?()
}
}
/// Android `PaymentCollectionDetailsViewModel.confirmAmount`
enum PaymentAmountValidator {
static let inputPattern = #"^\d*(\.\d{0,2})?$"#
private static let confirmPattern = #"^\d+(\.\d{1,2})?$"#
static func validationMessage(for amount: String) -> String? {
if amount.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
return "请输入收款金额"
}
guard let value = Double(amount), value > 0 else {
return "请输入有效的收款金额"
}
if !amount.matches(confirmPattern) {
return "金额最多支持两位小数"
}
return nil
}
}
private extension String {
func matches(_ pattern: String) -> Bool {
range(of: pattern, options: .regularExpression) != nil
}
}

View File

@ -0,0 +1,61 @@
//
// PaymentCollectionRecordViewModel.swift
// suixinkan
//
import Foundation
/// ViewModel 7
final class PaymentCollectionRecordViewModel {
private(set) var loading = false
private(set) var groups: [RepaymentCollectionRecordGroup] = []
var onStateChange: (() -> Void)?
private let appStore: AppStore
init(appStore: AppStore = .shared) {
self.appStore = appStore
}
func loadRecord(api: PaymentAPI) async {
guard !loading else { return }
loading = true
notifyStateChange()
defer {
loading = false
notifyStateChange()
}
let scenicId = appStore.currentScenicId
guard scenicId > 0 else {
groups = []
return
}
do {
let response = try await api.collectionRecord(scenicId: scenicId)
groups = PaymentRecordGrouper.group(response: response)
} catch {
groups = []
}
}
private func notifyStateChange() {
onStateChange?()
}
}
/// Android `PaymentCollectionRecordViewModel.getRecord`
enum PaymentRecordGrouper {
static func group(response: RepaymentCollectionRecordResponse) -> [RepaymentCollectionRecordGroup] {
let mapByDate = Dictionary(grouping: response.list, by: \.createDate)
return response.analyse.map { analyseItem in
RepaymentCollectionRecordGroup(
analyse: analyseItem,
items: mapByDate[analyseItem.date] ?? []
)
}
}
}