fix: 优化收款码加载态,避免首屏错误占位闪现
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -28,6 +28,7 @@ final class PaymentCollectionDetailsViewModel {
|
||||
|
||||
private var staticPayURL = ""
|
||||
private var dynamicPayURL = ""
|
||||
private var hasCompletedPayCodeRequest = false
|
||||
private let appStore: AppStore
|
||||
private let payPageConfigCache: PayPageConfigCaching
|
||||
|
||||
@ -50,6 +51,11 @@ final class PaymentCollectionDetailsViewModel {
|
||||
qrImage != nil && !staticPayURL.isEmpty
|
||||
}
|
||||
|
||||
/// 是否应在二维码区域展示加载状态,首次请求开始前也视为加载中,避免短暂闪现错误占位。
|
||||
var isPayCodeLoading: Bool {
|
||||
loading || !hasCompletedPayCodeRequest
|
||||
}
|
||||
|
||||
var displayScenicName: String {
|
||||
scenicName.isEmpty ? "暂无景区" : scenicName
|
||||
}
|
||||
@ -85,11 +91,11 @@ final class PaymentCollectionDetailsViewModel {
|
||||
|
||||
func loadPayCode(api: PaymentAPI) async {
|
||||
guard !loading else { return }
|
||||
refreshLocalState()
|
||||
loading = true
|
||||
notifyStateChange()
|
||||
refreshLocalState()
|
||||
defer {
|
||||
loading = false
|
||||
hasCompletedPayCodeRequest = true
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@ final class PaymentCollectionDetailsViewController: BaseViewController {
|
||||
private let qrContainerView = UIView()
|
||||
private let qrImageView = UIImageView()
|
||||
private let qrPlaceholderView = UIView()
|
||||
private let qrLoadingIndicator = UIActivityIndicatorView(style: .medium)
|
||||
private let qrErrorLabel = UILabel()
|
||||
private let refreshButton = UIButton(type: .system)
|
||||
private let setAmountButton = UIButton(type: .system)
|
||||
@ -127,6 +128,10 @@ final class PaymentCollectionDetailsViewController: BaseViewController {
|
||||
qrPlaceholderView.layer.cornerRadius = usesBranding ? 10 : 24
|
||||
qrPlaceholderView.isHidden = true
|
||||
|
||||
qrLoadingIndicator.color = AppColor.primary
|
||||
qrLoadingIndicator.hidesWhenStopped = true
|
||||
qrLoadingIndicator.accessibilityLabel = "收款码加载中"
|
||||
|
||||
qrErrorLabel.text = "未选景区或网络问题"
|
||||
qrErrorLabel.font = .systemFont(ofSize: 14)
|
||||
qrErrorLabel.textColor = AppColor.danger
|
||||
@ -150,6 +155,9 @@ final class PaymentCollectionDetailsViewController: BaseViewController {
|
||||
trailing: 24
|
||||
)
|
||||
}
|
||||
actionRow.alpha = 0
|
||||
actionRow.isUserInteractionEnabled = false
|
||||
actionRow.accessibilityElementsHidden = true
|
||||
|
||||
configureLinkButton(setAmountButton, title: "设置金额")
|
||||
configureLinkButton(saveQRButton, title: "保存二维码")
|
||||
@ -206,6 +214,7 @@ final class PaymentCollectionDetailsViewController: BaseViewController {
|
||||
qrContentStack.spacing = usesBranding ? brandSectionSpacing : AppSpacing.lg
|
||||
qrCardView.addSubview(qrContentStack)
|
||||
|
||||
qrPlaceholderView.addSubview(qrLoadingIndicator)
|
||||
qrPlaceholderView.addSubview(qrErrorLabel)
|
||||
qrPlaceholderView.addSubview(refreshButton)
|
||||
|
||||
@ -275,6 +284,9 @@ final class PaymentCollectionDetailsViewController: BaseViewController {
|
||||
qrPlaceholderView.snp.makeConstraints { make in
|
||||
make.width.height.equalTo(usesBranding ? brandQRContainerSize : 182)
|
||||
}
|
||||
qrLoadingIndicator.snp.makeConstraints { make in
|
||||
make.center.equalToSuperview()
|
||||
}
|
||||
qrErrorLabel.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview().offset(56)
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.sm)
|
||||
@ -370,9 +382,7 @@ final class PaymentCollectionDetailsViewController: BaseViewController {
|
||||
|
||||
private func loadData() async {
|
||||
async let pageConfig: Void = viewModel.loadPayPageConfig(api: paymentAPI)
|
||||
showLoading()
|
||||
await viewModel.loadPayCode(api: paymentAPI)
|
||||
hideLoading()
|
||||
await pageConfig
|
||||
}
|
||||
|
||||
@ -387,17 +397,33 @@ final class PaymentCollectionDetailsViewController: BaseViewController {
|
||||
voiceSwitch.isOn = viewModel.isVoiceBroadcastOpen
|
||||
applyBrandConfigIfNeeded()
|
||||
|
||||
if let image = viewModel.qrImage {
|
||||
if viewModel.isPayCodeLoading {
|
||||
qrImageView.isHidden = true
|
||||
qrContainerView.isHidden = true
|
||||
qrPlaceholderView.backgroundColor = viewModel.usesNalatiBranding
|
||||
? .white
|
||||
: AppColor.inputBackground
|
||||
qrPlaceholderView.isHidden = false
|
||||
qrErrorLabel.isHidden = true
|
||||
refreshButton.isHidden = true
|
||||
qrLoadingIndicator.startAnimating()
|
||||
setActionRowAvailable(false)
|
||||
} else if let image = viewModel.qrImage {
|
||||
qrImageView.image = image
|
||||
qrImageView.isHidden = false
|
||||
qrContainerView.isHidden = !viewModel.usesNalatiBranding
|
||||
qrPlaceholderView.isHidden = true
|
||||
actionRow.isHidden = false
|
||||
qrLoadingIndicator.stopAnimating()
|
||||
setActionRowAvailable(true)
|
||||
} else {
|
||||
qrImageView.isHidden = true
|
||||
qrContainerView.isHidden = true
|
||||
qrPlaceholderView.backgroundColor = AppColor.inputBackground
|
||||
qrPlaceholderView.isHidden = false
|
||||
actionRow.isHidden = true
|
||||
qrErrorLabel.isHidden = false
|
||||
refreshButton.isHidden = false
|
||||
qrLoadingIndicator.stopAnimating()
|
||||
setActionRowAvailable(false)
|
||||
}
|
||||
|
||||
if viewModel.showAmountDialog {
|
||||
@ -408,6 +434,12 @@ final class PaymentCollectionDetailsViewController: BaseViewController {
|
||||
}
|
||||
}
|
||||
|
||||
private func setActionRowAvailable(_ isAvailable: Bool) {
|
||||
actionRow.alpha = isAvailable ? 1 : 0
|
||||
actionRow.isUserInteractionEnabled = isAvailable
|
||||
actionRow.accessibilityElementsHidden = !isAvailable
|
||||
}
|
||||
|
||||
private func presentAmountDialogIfNeeded() {
|
||||
guard amountDialog == nil else {
|
||||
amountDialog?.apply(amount: viewModel.amount, remark: viewModel.remark)
|
||||
@ -625,9 +657,7 @@ final class PaymentCollectionDetailsViewController: BaseViewController {
|
||||
|
||||
@objc private func refreshTapped() {
|
||||
Task {
|
||||
showLoading()
|
||||
await viewModel.refresh(api: paymentAPI)
|
||||
hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user