完善实名认证、钱包与打卡点模块 UI 与业务逻辑。
对齐 Android 实名认证流程与审核页、钱包提现/积分兑换界面,优化打卡点列表与详情交互,并补充相关资源、主题 token 与单元测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -51,14 +51,22 @@ final class WithdrawViewController: BaseViewController {
|
||||
}
|
||||
|
||||
override func setupNavigationBar() {
|
||||
title = "提现设置"
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.text = "提现设置"
|
||||
titleLabel.textColor = WalletTokens.text333
|
||||
titleLabel.font = UIFont.systemFont(ofSize: 18)
|
||||
navigationItem.titleView = titleLabel
|
||||
}
|
||||
|
||||
override func setupUI() {
|
||||
view.backgroundColor = UIColor(hex: 0xF5F6F8)
|
||||
view.backgroundColor = WalletTokens.withdrawPageBackground
|
||||
contentStack.axis = .vertical
|
||||
contentStack.spacing = 16
|
||||
contentStack.spacing = WalletTokens.cardGap
|
||||
contentStack.backgroundColor = .white
|
||||
contentStack.layer.cornerRadius = WalletTokens.cardRadius
|
||||
contentStack.clipsToBounds = true
|
||||
bottomBar.backgroundColor = .white
|
||||
bottomBar.isHidden = true
|
||||
|
||||
view.addSubview(scrollView)
|
||||
scrollView.addSubview(contentStack)
|
||||
@ -76,16 +84,16 @@ final class WithdrawViewController: BaseViewController {
|
||||
make.leading.trailing.bottom.equalToSuperview()
|
||||
}
|
||||
submitButton.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview().offset(16)
|
||||
make.leading.trailing.equalToSuperview().inset(16)
|
||||
make.bottom.equalTo(view.safeAreaLayoutGuide).inset(16)
|
||||
make.top.equalToSuperview().offset(WalletTokens.screenInset)
|
||||
make.leading.trailing.equalToSuperview().inset(WalletTokens.screenInset)
|
||||
make.bottom.equalTo(view.safeAreaLayoutGuide).inset(WalletTokens.screenInset)
|
||||
}
|
||||
scrollView.snp.makeConstraints { make in
|
||||
make.top.leading.trailing.equalToSuperview()
|
||||
make.bottom.equalTo(bottomBar.snp.top)
|
||||
}
|
||||
contentStack.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(16)
|
||||
make.edges.equalToSuperview().inset(WalletTokens.screenInset)
|
||||
make.width.equalTo(scrollView).offset(-32)
|
||||
}
|
||||
}
|
||||
@ -98,7 +106,15 @@ final class WithdrawViewController: BaseViewController {
|
||||
codeField.addTarget(self, action: #selector(codeChanged), for: .editingChanged)
|
||||
codeButton.addTarget(self, action: #selector(sendCodeTapped), for: .touchUpInside)
|
||||
submitButton.addTarget(self, action: #selector(submitTapped), for: .touchUpInside)
|
||||
Task { await viewModel.load(api: walletAPI) }
|
||||
Task {
|
||||
showLoading()
|
||||
await viewModel.load(api: walletAPI)
|
||||
hideLoading()
|
||||
if viewModel.withdrawInfo == nil, let message = viewModel.errorMessage {
|
||||
navigationController?.popViewController(animated: true)
|
||||
(navigationController?.topViewController as? BaseViewController)?.showToast(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func applyState() {
|
||||
@ -116,9 +132,24 @@ final class WithdrawViewController: BaseViewController {
|
||||
codeButton.setTitle(viewModel.countdown > 0 ? "\(viewModel.countdown)s" : "获取验证码", for: .normal)
|
||||
codeButton.isEnabled = viewModel.countdown == 0
|
||||
submitButton.isEnabled = viewModel.canSubmit
|
||||
updateBottomBarVisibility(hasContent: viewModel.withdrawInfo != nil && !viewModel.loading)
|
||||
if viewModel.countdown > 0 { startCountdownTimerIfNeeded() }
|
||||
showMessageIfNeeded(viewModel.statusMessage)
|
||||
showMessageIfNeeded(viewModel.errorMessage)
|
||||
if viewModel.withdrawInfo != nil {
|
||||
showMessageIfNeeded(viewModel.errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateBottomBarVisibility(hasContent: Bool) {
|
||||
bottomBar.isHidden = !hasContent
|
||||
scrollView.snp.remakeConstraints { make in
|
||||
make.top.leading.trailing.equalToSuperview()
|
||||
if hasContent {
|
||||
make.bottom.equalTo(bottomBar.snp.top)
|
||||
} else {
|
||||
make.bottom.equalToSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func makeAmountCard() -> UIView {
|
||||
@ -129,19 +160,19 @@ final class WithdrawViewController: BaseViewController {
|
||||
row.alignment = .center
|
||||
row.spacing = 16
|
||||
let withdrawableTitle = makeBodyLabel("可提现金额", color: .black, weight: .medium)
|
||||
withdrawableLabel.font = .systemFont(ofSize: 14, weight: .medium)
|
||||
withdrawableLabel.textColor = UIColor(hex: 0x0073FF)
|
||||
withdrawableLabel.font = WalletTokens.bodyMediumFont
|
||||
withdrawableLabel.textColor = WalletTokens.primary
|
||||
row.addArrangedSubview(withdrawableTitle)
|
||||
row.addArrangedSubview(withdrawableLabel)
|
||||
|
||||
let amountContainer = borderedInputContainer()
|
||||
amountField.placeholder = "请输入金额"
|
||||
amountField.keyboardType = .decimalPad
|
||||
amountField.font = .systemFont(ofSize: 14)
|
||||
amountField.font = WalletTokens.bodyFont
|
||||
let allButton = UIButton(type: .system)
|
||||
allButton.setTitle("全部提现", for: .normal)
|
||||
allButton.setTitleColor(UIColor(hex: 0x0073FF), for: .normal)
|
||||
allButton.titleLabel?.font = .systemFont(ofSize: 14)
|
||||
allButton.setTitleColor(WalletTokens.primary, for: .normal)
|
||||
allButton.titleLabel?.font = WalletTokens.bodyFont
|
||||
allButton.addTarget(self, action: #selector(withdrawAllTapped), for: .touchUpInside)
|
||||
amountContainer.addSubview(amountField)
|
||||
amountContainer.addSubview(allButton)
|
||||
@ -155,10 +186,10 @@ final class WithdrawViewController: BaseViewController {
|
||||
make.centerY.equalToSuperview()
|
||||
}
|
||||
|
||||
limitLabel.font = .systemFont(ofSize: 12)
|
||||
limitLabel.textColor = UIColor(hex: 0x9CA3AF)
|
||||
phoneLabel.font = .systemFont(ofSize: 12)
|
||||
phoneLabel.textColor = UIColor(hex: 0x4B5563)
|
||||
limitLabel.font = WalletTokens.captionFont
|
||||
limitLabel.textColor = WalletTokens.textTertiary
|
||||
phoneLabel.font = WalletTokens.captionFont
|
||||
phoneLabel.textColor = WalletTokens.textSecondary
|
||||
|
||||
let codeRow = UIStackView()
|
||||
codeRow.axis = .horizontal
|
||||
@ -166,18 +197,19 @@ final class WithdrawViewController: BaseViewController {
|
||||
let codeContainer = borderedInputContainer()
|
||||
codeField.placeholder = "请输入验证码"
|
||||
codeField.keyboardType = .numberPad
|
||||
codeField.font = .systemFont(ofSize: 14)
|
||||
codeField.font = WalletTokens.bodyFont
|
||||
codeContainer.addSubview(codeField)
|
||||
codeField.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12)) }
|
||||
codeButton.setTitle("获取验证码", for: .normal)
|
||||
codeButton.setTitleColor(UIColor(hex: 0x0073FF), for: .normal)
|
||||
codeButton.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
|
||||
codeButton.setTitleColor(WalletTokens.primary, for: .normal)
|
||||
codeButton.setTitleColor(WalletTokens.textDisabled, for: .disabled)
|
||||
codeButton.titleLabel?.font = WalletTokens.bodyMediumFont
|
||||
codeButton.layer.borderWidth = 1
|
||||
codeButton.layer.borderColor = UIColor(hex: 0xEEEEEE).cgColor
|
||||
codeButton.layer.cornerRadius = 4
|
||||
codeButton.layer.borderColor = WalletTokens.border.cgColor
|
||||
codeButton.layer.cornerRadius = WalletTokens.chipRadius
|
||||
codeRow.addArrangedSubview(codeContainer)
|
||||
codeRow.addArrangedSubview(codeButton)
|
||||
codeContainer.snp.makeConstraints { make in make.height.equalTo(46) }
|
||||
codeContainer.snp.makeConstraints { make in make.height.equalTo(WalletTokens.inputHeight) }
|
||||
codeButton.snp.makeConstraints { make in make.width.equalTo(100) }
|
||||
|
||||
let stack = UIStackView(arrangedSubviews: [title, row, amountContainer, limitLabel, phoneLabel, codeRow])
|
||||
@ -185,23 +217,26 @@ final class WithdrawViewController: BaseViewController {
|
||||
stack.spacing = 12
|
||||
card.addSubview(stack)
|
||||
stack.snp.makeConstraints { make in make.edges.equalToSuperview().inset(16) }
|
||||
amountContainer.snp.makeConstraints { make in make.height.equalTo(46) }
|
||||
amountContainer.snp.makeConstraints { make in make.height.equalTo(WalletTokens.inputHeight) }
|
||||
return card
|
||||
}
|
||||
|
||||
private func makeBankCard() -> UIView {
|
||||
let card = makeCard()
|
||||
let title = makeTitle("银行卡信息")
|
||||
let title = makeTitle("银行卡信息", size: 16)
|
||||
let inner = makeInnerPanel()
|
||||
bankCardLabel.font = .systemFont(ofSize: 14, weight: .medium)
|
||||
bankCardLabel.textColor = UIColor(hex: 0x333333)
|
||||
bankHolderLabel.font = .systemFont(ofSize: 14)
|
||||
bankHolderLabel.textColor = UIColor(hex: 0x4B5563)
|
||||
bankCardLabel.font = WalletTokens.bodyMediumFont
|
||||
bankCardLabel.textColor = WalletTokens.text333
|
||||
bankHolderLabel.font = WalletTokens.bodyFont
|
||||
bankHolderLabel.textColor = WalletTokens.textSecondary
|
||||
let innerStack = UIStackView(arrangedSubviews: [bankCardLabel, bankHolderLabel])
|
||||
innerStack.axis = .vertical
|
||||
innerStack.spacing = 12
|
||||
inner.addSubview(innerStack)
|
||||
innerStack.snp.makeConstraints { make in make.edges.equalToSuperview().inset(16) }
|
||||
innerStack.snp.makeConstraints { make in
|
||||
make.top.bottom.equalToSuperview().inset(WalletTokens.compactGap)
|
||||
make.leading.trailing.equalToSuperview().inset(WalletTokens.cardPadding)
|
||||
}
|
||||
let stack = UIStackView(arrangedSubviews: [title, inner])
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 8
|
||||
@ -212,16 +247,19 @@ final class WithdrawViewController: BaseViewController {
|
||||
|
||||
private func makeSettlementCard() -> UIView {
|
||||
let card = makeCard()
|
||||
let title = makeTitle("预计到账时间")
|
||||
let title = makeTitle("预计到账时间", size: 16)
|
||||
let inner = makeInnerPanel()
|
||||
settlementLabel.font = .systemFont(ofSize: 14)
|
||||
settlementLabel.textColor = UIColor(hex: 0x4B5563)
|
||||
settlementLabel.font = WalletTokens.bodyFont
|
||||
settlementLabel.textColor = WalletTokens.textSecondary
|
||||
settlementLabel.numberOfLines = 0
|
||||
inner.addSubview(settlementLabel)
|
||||
settlementLabel.snp.makeConstraints { make in make.edges.equalToSuperview().inset(16) }
|
||||
settlementLabel.snp.makeConstraints { make in
|
||||
make.top.bottom.equalToSuperview().inset(WalletTokens.compactGap)
|
||||
make.leading.trailing.equalToSuperview().inset(WalletTokens.cardPadding)
|
||||
}
|
||||
let stack = UIStackView(arrangedSubviews: [title, inner])
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 8
|
||||
stack.spacing = WalletTokens.tinyGap
|
||||
card.addSubview(stack)
|
||||
stack.snp.makeConstraints { make in make.edges.equalToSuperview().inset(16) }
|
||||
return card
|
||||
@ -229,15 +267,18 @@ final class WithdrawViewController: BaseViewController {
|
||||
|
||||
private func makeInfoCard() -> UIView {
|
||||
let card = makeCard()
|
||||
let title = makeTitle("提现说明")
|
||||
let title = makeTitle("提现说明", size: 16)
|
||||
let inner = makeInnerPanel()
|
||||
infoStack.axis = .vertical
|
||||
infoStack.spacing = 4
|
||||
inner.addSubview(infoStack)
|
||||
infoStack.snp.makeConstraints { make in make.edges.equalToSuperview().inset(16) }
|
||||
infoStack.snp.makeConstraints { make in
|
||||
make.top.bottom.equalToSuperview().inset(WalletTokens.compactGap)
|
||||
make.leading.trailing.equalToSuperview().inset(WalletTokens.cardPadding)
|
||||
}
|
||||
let stack = UIStackView(arrangedSubviews: [title, inner])
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 8
|
||||
stack.spacing = WalletTokens.rowGap
|
||||
card.addSubview(stack)
|
||||
stack.snp.makeConstraints { make in make.edges.equalToSuperview().inset(16) }
|
||||
return card
|
||||
@ -246,32 +287,32 @@ final class WithdrawViewController: BaseViewController {
|
||||
private func makeCard() -> UIView {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .white
|
||||
view.layer.cornerRadius = 12
|
||||
view.layer.cornerRadius = WalletTokens.cardRadius
|
||||
view.clipsToBounds = true
|
||||
return view
|
||||
}
|
||||
|
||||
private func makeInnerPanel() -> UIView {
|
||||
let view = UIView()
|
||||
view.backgroundColor = UIColor(hex: 0xF4F4F4)
|
||||
view.layer.cornerRadius = 8
|
||||
view.backgroundColor = WalletTokens.inputBackground
|
||||
view.layer.cornerRadius = WalletTokens.innerRadius
|
||||
view.layer.borderWidth = 1
|
||||
view.layer.borderColor = UIColor(hex: 0xEEEEEE).cgColor
|
||||
view.layer.borderColor = WalletTokens.border.cgColor
|
||||
return view
|
||||
}
|
||||
|
||||
private func borderedInputContainer() -> UIView {
|
||||
let view = UIView()
|
||||
view.layer.cornerRadius = 4
|
||||
view.layer.cornerRadius = WalletTokens.chipRadius
|
||||
view.layer.borderWidth = 1
|
||||
view.layer.borderColor = UIColor(hex: 0xEEEEEE).cgColor
|
||||
view.layer.borderColor = WalletTokens.border.cgColor
|
||||
return view
|
||||
}
|
||||
|
||||
private func makeTitle(_ text: String) -> UILabel {
|
||||
private func makeTitle(_ text: String, size: CGFloat = 18) -> UILabel {
|
||||
let label = UILabel()
|
||||
label.text = text
|
||||
label.font = .systemFont(ofSize: 18, weight: .medium)
|
||||
label.font = .systemFont(ofSize: size, weight: .medium)
|
||||
label.textColor = .black
|
||||
return label
|
||||
}
|
||||
@ -287,7 +328,7 @@ final class WithdrawViewController: BaseViewController {
|
||||
private func applyInfoLines(_ lines: [String]) {
|
||||
infoStack.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
||||
lines.forEach { text in
|
||||
let label = makeBodyLabel(text, color: UIColor(hex: 0x4B5563))
|
||||
let label = makeBodyLabel(text, color: WalletTokens.textSecondary)
|
||||
label.numberOfLines = 0
|
||||
infoStack.addArrangedSubview(label)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user