Add instant payment collection flow aligned with Android.

Implement收款详情与收款记录 pages with pay-code API, QR generation, amount setting, and record grouping so merchants can collect payments from the home quick action.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-07 09:29:03 +08:00
parent 731de7d7f0
commit d8329d19fd
17 changed files with 1548 additions and 1 deletions

View File

@ -0,0 +1,382 @@
//
// PaymentCollectionDetailsViewController.swift
// suixinkan
//
import SnapKit
import UIKit
///
final class PaymentCollectionDetailsViewController: BaseViewController {
private let viewModel = PaymentCollectionDetailsViewModel()
private let paymentAPI = NetworkServices.shared.paymentAPI
private let scrollView = UIScrollView()
private let contentStack = UIStackView()
private let qrCardView = UIView()
private let scenicNameLabel = UILabel()
private let qrImageView = UIImageView()
private let qrPlaceholderView = UIView()
private let qrErrorLabel = UILabel()
private let refreshButton = UIButton(type: .system)
private let setAmountButton = UIButton(type: .system)
private let saveQRButton = UIButton(type: .system)
private let actionRow = UIStackView()
private let infoCardView = UIView()
private let scenicRow = PaymentInfoRowView(title: "景区名称")
private let merchantRow = PaymentInfoRowView(title: "收款方")
private let staffRow = PaymentInfoRowView(title: "员工 ID")
private let recordRow = PaymentNavigationRowView(title: "收款记录")
private let voiceCardView = UIView()
private let voiceTitleLabel = UILabel()
private let voiceSwitch = UISwitch()
private var amountDialog: PaymentSetAmountDialogView?
override func setupNavigationBar() {
title = "收款详情"
}
override func setupUI() {
view.backgroundColor = AppColor.pageBackground
contentStack.axis = .vertical
contentStack.spacing = AppSpacing.md
qrCardView.backgroundColor = .white
qrCardView.layer.cornerRadius = AppRadius.lg
scenicNameLabel.font = .systemFont(ofSize: 16, weight: .medium)
scenicNameLabel.textColor = AppColor.textPrimary
scenicNameLabel.textAlignment = .center
qrImageView.contentMode = .scaleAspectFit
qrImageView.isHidden = true
qrPlaceholderView.backgroundColor = AppColor.inputBackground
qrPlaceholderView.layer.cornerRadius = 24
qrPlaceholderView.isHidden = true
qrErrorLabel.text = "未选景区或网络问题"
qrErrorLabel.font = .systemFont(ofSize: 14)
qrErrorLabel.textColor = AppColor.danger
qrErrorLabel.textAlignment = .center
refreshButton.setTitle("点击刷新", for: .normal)
refreshButton.setTitleColor(AppColor.primary, for: .normal)
refreshButton.titleLabel?.font = .systemFont(ofSize: 14)
actionRow.axis = .horizontal
actionRow.alignment = .center
actionRow.spacing = AppSpacing.xl
configureLinkButton(setAmountButton, title: "设置金额")
configureLinkButton(saveQRButton, title: "保存二维码")
infoCardView.backgroundColor = .white
infoCardView.layer.cornerRadius = AppRadius.lg
recordRow.backgroundColor = .white
recordRow.layer.cornerRadius = AppRadius.lg
voiceCardView.backgroundColor = .white
voiceCardView.layer.cornerRadius = AppRadius.lg
voiceTitleLabel.text = "收款到账语音提醒"
voiceTitleLabel.font = .systemFont(ofSize: 14)
voiceTitleLabel.textColor = UIColor(hex: 0x4B5563)
voiceSwitch.onTintColor = AppColor.primary
view.addSubview(scrollView)
scrollView.addSubview(contentStack)
contentStack.addArrangedSubview(qrCardView)
contentStack.addArrangedSubview(infoCardView)
contentStack.addArrangedSubview(recordRow)
contentStack.addArrangedSubview(voiceCardView)
let qrContentStack = UIStackView(arrangedSubviews: [
scenicNameLabel,
qrImageView,
qrPlaceholderView,
actionRow,
])
qrContentStack.axis = .vertical
qrContentStack.alignment = .center
qrContentStack.spacing = AppSpacing.lg
qrCardView.addSubview(qrContentStack)
qrPlaceholderView.addSubview(qrErrorLabel)
qrPlaceholderView.addSubview(refreshButton)
actionRow.addArrangedSubview(setAmountButton)
actionRow.addArrangedSubview(saveQRButton)
let infoStack = UIStackView(arrangedSubviews: [scenicRow, merchantRow, staffRow])
infoStack.axis = .vertical
infoCardView.addSubview(infoStack)
voiceCardView.addSubview(voiceTitleLabel)
voiceCardView.addSubview(voiceSwitch)
qrContentStack.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 32, left: 16, bottom: 32, right: 16))
}
scenicNameLabel.snp.makeConstraints { make in
make.leading.trailing.equalToSuperview()
}
qrImageView.snp.makeConstraints { make in
make.width.height.equalTo(182)
}
qrPlaceholderView.snp.makeConstraints { make in
make.width.height.equalTo(182)
}
qrErrorLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(56)
make.leading.trailing.equalToSuperview().inset(AppSpacing.sm)
}
refreshButton.snp.makeConstraints { make in
make.top.equalTo(qrErrorLabel.snp.bottom).offset(AppSpacing.xs)
make.centerX.equalToSuperview()
}
infoStack.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
voiceTitleLabel.snp.makeConstraints { make in
make.leading.equalToSuperview().inset(AppSpacing.md)
make.centerY.equalToSuperview()
}
voiceSwitch.snp.makeConstraints { make in
make.trailing.equalToSuperview().inset(AppSpacing.md)
make.centerY.equalToSuperview()
}
voiceCardView.snp.makeConstraints { make in
make.height.equalTo(AppSpacing.formRowHeight)
}
}
override func setupConstraints() {
scrollView.snp.makeConstraints { make in
make.edges.equalTo(view.safeAreaLayoutGuide)
}
contentStack.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(AppSpacing.screenHorizontalInset)
make.width.equalTo(scrollView.snp.width).offset(-AppSpacing.screenHorizontalInset * 2)
}
}
override func bindActions() {
viewModel.onStateChange = { [weak self] in
Task { @MainActor in self?.applyViewModel() }
}
viewModel.onShowMessage = { [weak self] message in
Task { @MainActor in self?.showToast(message) }
}
setAmountButton.addTarget(self, action: #selector(setAmountTapped), for: .touchUpInside)
saveQRButton.addTarget(self, action: #selector(saveQRTapped), for: .touchUpInside)
refreshButton.addTarget(self, action: #selector(refreshTapped), for: .touchUpInside)
recordRow.addTarget(self, action: #selector(recordTapped), for: .touchUpInside)
voiceSwitch.addTarget(self, action: #selector(voiceSwitchChanged), for: .valueChanged)
}
override func viewDidLoad() {
super.viewDidLoad()
Task { await loadData() }
}
private func loadData() async {
showLoading()
await viewModel.loadPayCode(api: paymentAPI)
hideLoading()
}
@MainActor
private func applyViewModel() {
scenicNameLabel.text = viewModel.displayScenicName
scenicRow.setValue(viewModel.displayScenicName)
merchantRow.setValue(viewModel.merchantInfo.company)
staffRow.setValue(viewModel.staffId.isEmpty ? "-" : viewModel.staffId)
voiceSwitch.isOn = viewModel.isVoiceBroadcastOpen
if let image = viewModel.qrImage {
qrImageView.image = image
qrImageView.isHidden = false
qrPlaceholderView.isHidden = true
actionRow.isHidden = false
} else {
qrImageView.isHidden = true
qrPlaceholderView.isHidden = false
actionRow.isHidden = true
}
if viewModel.showAmountDialog {
presentAmountDialogIfNeeded()
} else {
amountDialog?.dismiss()
amountDialog = nil
}
}
private func presentAmountDialogIfNeeded() {
guard amountDialog == nil else {
amountDialog?.apply(amount: viewModel.amount, remark: viewModel.remark)
return
}
let dialog = PaymentSetAmountDialogView()
dialog.onAmountChange = { [weak self] value in
self?.viewModel.updateAmount(value)
}
dialog.onRemarkChange = { [weak self] value in
self?.viewModel.updateRemark(value)
}
dialog.onConfirm = { [weak self] in
self?.viewModel.confirmAmount()
}
dialog.onCancel = { [weak self] in
self?.viewModel.hideAmountDialog()
}
dialog.apply(amount: viewModel.amount, remark: viewModel.remark)
dialog.show(in: view)
amountDialog = dialog
}
private func configureLinkButton(_ button: UIButton, title: String) {
button.setTitle(title, for: .normal)
button.setTitleColor(AppColor.primary, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 14)
}
@objc private func setAmountTapped() {
viewModel.showSetAmountDialog()
}
@objc private func saveQRTapped() {
Task {
await viewModel.saveQRCode()
}
}
@objc private func refreshTapped() {
Task {
showLoading()
await viewModel.refresh(api: paymentAPI)
hideLoading()
}
}
@objc private func recordTapped() {
navigationController?.pushViewController(PaymentCollectionRecordViewController(), animated: true)
}
@objc private func voiceSwitchChanged() {
viewModel.toggleReceiveVoice()
}
}
///
private final class PaymentInfoRowView: UIView {
private let titleLabel = UILabel()
private let valueLabel = UILabel()
private let divider = UIView()
init(title: String) {
super.init(frame: .zero)
titleLabel.text = title
setupUI()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setValue(_ value: String) {
valueLabel.text = value
}
private func setupUI() {
titleLabel.font = .systemFont(ofSize: 14)
titleLabel.textColor = UIColor(hex: 0x4B5563)
valueLabel.font = .systemFont(ofSize: 14)
valueLabel.textColor = AppColor.textPrimary
valueLabel.textAlignment = .right
valueLabel.numberOfLines = 2
divider.backgroundColor = UIColor(hex: 0xE5E7EB)
addSubview(titleLabel)
addSubview(valueLabel)
addSubview(divider)
snp.makeConstraints { make in
make.height.greaterThanOrEqualTo(AppSpacing.formRowHeight)
}
titleLabel.snp.makeConstraints { make in
make.leading.equalToSuperview().inset(AppSpacing.md)
make.centerY.equalToSuperview()
make.width.equalTo(102)
}
valueLabel.snp.makeConstraints { make in
make.leading.equalTo(titleLabel.snp.trailing).offset(AppSpacing.xs)
make.trailing.equalToSuperview().inset(AppSpacing.md)
make.top.bottom.equalToSuperview().inset(AppSpacing.sm)
}
divider.snp.makeConstraints { make in
make.leading.trailing.bottom.equalToSuperview()
make.height.equalTo(0.5)
}
}
}
///
private final class PaymentNavigationRowView: UIControl {
private let titleLabel = UILabel()
private let chevronView = UIImageView()
init(title: String) {
super.init(frame: .zero)
titleLabel.text = title
setupUI()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
titleLabel.font = .systemFont(ofSize: 14)
titleLabel.textColor = UIColor(hex: 0x4B5563)
chevronView.image = UIImage(systemName: "chevron.right")
chevronView.tintColor = AppColor.textSecondary
chevronView.contentMode = .scaleAspectFit
addSubview(titleLabel)
addSubview(chevronView)
snp.makeConstraints { make in
make.height.equalTo(AppSpacing.formRowHeight)
}
titleLabel.snp.makeConstraints { make in
make.leading.equalToSuperview().inset(AppSpacing.md)
make.centerY.equalToSuperview()
}
chevronView.snp.makeConstraints { make in
make.trailing.equalToSuperview().inset(AppSpacing.md)
make.centerY.equalToSuperview()
make.width.height.equalTo(16)
}
}
}

View File

@ -0,0 +1,110 @@
//
// PaymentCollectionRecordViewController.swift
// suixinkan
//
import SnapKit
import UIKit
/// 7
final class PaymentCollectionRecordViewController: BaseViewController {
private let viewModel = PaymentCollectionRecordViewModel()
private let paymentAPI = NetworkServices.shared.paymentAPI
private let scrollView = UIScrollView()
private let contentStack = UIStackView()
private let emptyLabel = UILabel()
private let recordCardView = UIView()
private let groupsStack = UIStackView()
private let footerLabel = UILabel()
override func setupNavigationBar() {
title = "收款记录"
}
override func setupUI() {
view.backgroundColor = AppColor.pageBackground
contentStack.axis = .vertical
contentStack.spacing = AppSpacing.md
emptyLabel.text = "暂无收款记录"
emptyLabel.font = .systemFont(ofSize: 14)
emptyLabel.textColor = AppColor.textTertiary
emptyLabel.textAlignment = .center
emptyLabel.isHidden = true
recordCardView.backgroundColor = .white
recordCardView.layer.cornerRadius = AppRadius.lg
recordCardView.isHidden = true
groupsStack.axis = .vertical
groupsStack.spacing = AppSpacing.md
footerLabel.text = "仅支持查看7天的数据"
footerLabel.font = .systemFont(ofSize: 12)
footerLabel.textColor = UIColor(hex: 0xB6BECA)
footerLabel.textAlignment = .center
footerLabel.isHidden = true
view.addSubview(scrollView)
scrollView.addSubview(contentStack)
contentStack.addArrangedSubview(emptyLabel)
contentStack.addArrangedSubview(recordCardView)
contentStack.addArrangedSubview(footerLabel)
recordCardView.addSubview(groupsStack)
}
override func setupConstraints() {
scrollView.snp.makeConstraints { make in
make.edges.equalTo(view.safeAreaLayoutGuide)
}
contentStack.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(AppSpacing.screenHorizontalInset)
make.width.equalTo(scrollView.snp.width).offset(-AppSpacing.screenHorizontalInset * 2)
}
emptyLabel.snp.makeConstraints { make in
make.height.greaterThanOrEqualTo(240)
}
groupsStack.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(AppSpacing.md)
}
}
override func bindActions() {
viewModel.onStateChange = { [weak self] in
Task { @MainActor in self?.applyViewModel() }
}
}
override func viewDidLoad() {
super.viewDidLoad()
Task { await loadData() }
}
private func loadData() async {
showLoading()
await viewModel.loadRecord(api: paymentAPI)
hideLoading()
}
@MainActor
private func applyViewModel() {
let hasData = !viewModel.groups.isEmpty
emptyLabel.isHidden = hasData
recordCardView.isHidden = !hasData
footerLabel.isHidden = !hasData
groupsStack.arrangedSubviews.forEach {
groupsStack.removeArrangedSubview($0)
$0.removeFromSuperview()
}
viewModel.groups.forEach { group in
let groupView = PaymentRecordGroupView()
groupView.apply(group: group)
groupsStack.addArrangedSubview(groupView)
}
}
}

View File

@ -0,0 +1,136 @@
//
// PaymentRecordGroupView.swift
// suixinkan
//
import SnapKit
import UIKit
///
final class PaymentRecordGroupView: UIView {
private let headerStack = UIStackView()
private let dateLabel = UILabel()
private let summaryLabel = UILabel()
private let itemsStack = UIStackView()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func apply(group: RepaymentCollectionRecordGroup) {
dateLabel.text = group.analyse.date
summaryLabel.text = "共收款\(group.analyse.orderCount)笔, 当日收益:¥\(group.analyse.orderAmountSum)"
itemsStack.arrangedSubviews.forEach {
itemsStack.removeArrangedSubview($0)
$0.removeFromSuperview()
}
group.items.forEach { item in
let card = PaymentRecordItemView()
card.apply(item: item)
itemsStack.addArrangedSubview(card)
}
}
private func setupUI() {
headerStack.axis = .horizontal
headerStack.alignment = .center
headerStack.distribution = .equalSpacing
dateLabel.font = .systemFont(ofSize: 14)
dateLabel.textColor = AppColor.textTertiary
summaryLabel.font = .systemFont(ofSize: 14)
summaryLabel.textColor = UIColor(hex: 0x4B5563)
summaryLabel.textAlignment = .right
summaryLabel.numberOfLines = 2
itemsStack.axis = .vertical
itemsStack.spacing = AppSpacing.sm
addSubview(headerStack)
addSubview(itemsStack)
headerStack.addArrangedSubview(dateLabel)
headerStack.addArrangedSubview(summaryLabel)
headerStack.snp.makeConstraints { make in
make.top.leading.trailing.equalToSuperview()
}
itemsStack.snp.makeConstraints { make in
make.top.equalTo(headerStack.snp.bottom).offset(6)
make.leading.trailing.bottom.equalToSuperview()
}
}
}
///
final class PaymentRecordItemView: UIView {
private let amountLabel = UILabel()
private let orderNumberLabel = UILabel()
private let phoneLabel = UILabel()
private let timeLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func apply(item: RepaymentCollectionRecordItem) {
amountLabel.text = "¥ \(item.orderAmount)"
orderNumberLabel.text = item.orderNumber
phoneLabel.text = item.userPhone
timeLabel.text = item.createTime
}
private func setupUI() {
backgroundColor = .white
layer.borderColor = UIColor(hex: 0xF3F4F6).cgColor
layer.borderWidth = 1
layer.cornerRadius = AppRadius.lg
amountLabel.font = .systemFont(ofSize: 18, weight: .bold)
amountLabel.textColor = AppColor.primary
orderNumberLabel.font = .systemFont(ofSize: 14)
orderNumberLabel.textColor = AppColor.textSecondary
orderNumberLabel.textAlignment = .right
phoneLabel.font = .systemFont(ofSize: 14)
phoneLabel.textColor = AppColor.textSecondary
timeLabel.font = .systemFont(ofSize: 14)
timeLabel.textColor = AppColor.textSecondary
timeLabel.textAlignment = .right
let topRow = UIStackView(arrangedSubviews: [amountLabel, orderNumberLabel])
topRow.axis = .horizontal
topRow.distribution = .fillEqually
let bottomRow = UIStackView(arrangedSubviews: [phoneLabel, timeLabel])
bottomRow.axis = .horizontal
bottomRow.distribution = .fillEqually
let stack = UIStackView(arrangedSubviews: [topRow, bottomRow])
stack.axis = .vertical
stack.spacing = AppSpacing.xs
addSubview(stack)
stack.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(AppSpacing.md)
}
}
}

View File

@ -0,0 +1,205 @@
//
// PaymentSetAmountDialogView.swift
// suixinkan
//
import SnapKit
import UIKit
/// Android `SetAmountDialog`
final class PaymentSetAmountDialogView: UIView {
var onConfirm: (() -> Void)?
var onCancel: (() -> Void)?
var onAmountChange: ((String) -> Void)?
var onRemarkChange: ((String) -> Void)?
private let dimView = UIView()
private let cardView = UIView()
private let titleLabel = UILabel()
private let amountContainer = UIView()
private let currencyLabel = UILabel()
private let amountField = UITextField()
private let remarkTitleLabel = UILabel()
private let remarkField = UITextView()
private let remarkCountLabel = UILabel()
private let buttonStack = UIStackView()
private let cancelButton = UIButton(type: .system)
private let confirmButton = UIButton(type: .system)
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func apply(amount: String, remark: String) {
if amountField.text != amount {
amountField.text = amount
}
if remarkField.text != remark {
remarkField.text = remark
}
remarkCountLabel.text = "\(remark.count)/200"
}
func show(in parent: UIView) {
frame = parent.bounds
autoresizingMask = [.flexibleWidth, .flexibleHeight]
alpha = 0
parent.addSubview(self)
UIView.animate(withDuration: 0.2) {
self.alpha = 1
}
}
func dismiss() {
endEditing(true)
UIView.animate(withDuration: 0.2, animations: {
self.alpha = 0
}, completion: { _ in
self.removeFromSuperview()
})
}
private func setupUI() {
dimView.backgroundColor = AppColor.overlayScrim
dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(cancelTapped)))
cardView.backgroundColor = .white
cardView.layer.cornerRadius = AppRadius.xl
cardView.clipsToBounds = true
titleLabel.text = "收款金额"
titleLabel.font = .systemFont(ofSize: 18, weight: .bold)
titleLabel.textColor = AppColor.textPrimary
amountContainer.backgroundColor = AppColor.inputBackground
amountContainer.layer.cornerRadius = AppRadius.sm
currencyLabel.text = "¥"
currencyLabel.font = .systemFont(ofSize: 20, weight: .bold)
currencyLabel.textColor = AppColor.textPrimary
amountField.placeholder = "请输入收款金额"
amountField.font = .systemFont(ofSize: 18, weight: .medium)
amountField.keyboardType = .decimalPad
amountField.borderStyle = .none
amountField.backgroundColor = .clear
amountField.addTarget(self, action: #selector(amountChanged), for: .editingChanged)
remarkTitleLabel.text = "备注 (选填)"
remarkTitleLabel.font = .systemFont(ofSize: 14)
remarkTitleLabel.textColor = UIColor(hex: 0x4B5563)
remarkField.font = .systemFont(ofSize: 14)
remarkField.textColor = AppColor.textPrimary
remarkField.backgroundColor = .white
remarkField.layer.borderColor = UIColor(hex: 0xE5E7EB).cgColor
remarkField.layer.borderWidth = 1
remarkField.layer.cornerRadius = AppRadius.sm
remarkField.textContainerInset = UIEdgeInsets(top: 10, left: 8, bottom: 10, right: 8)
remarkField.delegate = self
remarkCountLabel.font = .systemFont(ofSize: 12)
remarkCountLabel.textColor = AppColor.textTertiary
remarkCountLabel.textAlignment = .right
remarkCountLabel.text = "0/200"
buttonStack.axis = .horizontal
buttonStack.spacing = AppSpacing.sm
buttonStack.distribution = .fillEqually
cancelButton.setTitle("取消", for: .normal)
cancelButton.setTitleColor(AppColor.textSecondary, for: .normal)
cancelButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
cancelButton.backgroundColor = AppColor.inputBackground
cancelButton.layer.cornerRadius = AppRadius.sm
cancelButton.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside)
confirmButton.setTitle("确定", for: .normal)
confirmButton.setTitleColor(.white, for: .normal)
confirmButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
confirmButton.backgroundColor = AppColor.primary
confirmButton.layer.cornerRadius = AppRadius.sm
confirmButton.addTarget(self, action: #selector(confirmTapped), for: .touchUpInside)
addSubview(dimView)
addSubview(cardView)
cardView.addSubview(titleLabel)
cardView.addSubview(amountContainer)
amountContainer.addSubview(currencyLabel)
amountContainer.addSubview(amountField)
cardView.addSubview(remarkTitleLabel)
cardView.addSubview(remarkField)
cardView.addSubview(remarkCountLabel)
cardView.addSubview(buttonStack)
buttonStack.addArrangedSubview(cancelButton)
buttonStack.addArrangedSubview(confirmButton)
dimView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
cardView.snp.makeConstraints { make in
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
make.centerY.equalToSuperview()
}
titleLabel.snp.makeConstraints { make in
make.top.leading.trailing.equalToSuperview().inset(AppSpacing.md)
}
amountContainer.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp.bottom).offset(20)
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
make.height.equalTo(48)
}
currencyLabel.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(AppSpacing.sm)
make.centerY.equalToSuperview()
}
amountField.snp.makeConstraints { make in
make.leading.equalTo(currencyLabel.snp.trailing).offset(AppSpacing.xs)
make.trailing.equalToSuperview().inset(AppSpacing.sm)
make.centerY.equalToSuperview()
}
remarkTitleLabel.snp.makeConstraints { make in
make.top.equalTo(amountContainer.snp.bottom).offset(AppSpacing.md)
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
}
remarkField.snp.makeConstraints { make in
make.top.equalTo(remarkTitleLabel.snp.bottom).offset(AppSpacing.xs)
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
make.height.equalTo(100)
}
remarkCountLabel.snp.makeConstraints { make in
make.top.equalTo(remarkField.snp.bottom).offset(AppSpacing.xxs)
make.trailing.equalToSuperview().inset(AppSpacing.md)
}
buttonStack.snp.makeConstraints { make in
make.top.equalTo(remarkCountLabel.snp.bottom).offset(AppSpacing.lg)
make.leading.trailing.bottom.equalToSuperview().inset(AppSpacing.md)
make.height.equalTo(44)
}
}
@objc private func amountChanged() {
onAmountChange?(amountField.text ?? "")
}
@objc private func cancelTapped() {
onCancel?()
}
@objc private func confirmTapped() {
onConfirm?()
}
}
extension PaymentSetAmountDialogView: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
onRemarkChange?(textView.text ?? "")
}
}