feat: update app workflows and permissions
This commit is contained in:
@ -120,9 +120,6 @@ final class CooperationOrderSearchBar: UIView, UITextFieldDelegate {
|
||||
container.addSubview(icon)
|
||||
container.addSubview(textField)
|
||||
|
||||
snp.makeConstraints { make in
|
||||
make.height.equalTo(62)
|
||||
}
|
||||
container.snp.makeConstraints { make in
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||||
make.centerY.equalToSuperview()
|
||||
@ -451,12 +448,18 @@ final class CooperationAcquirerCell: UITableViewCell {
|
||||
var onCall: (() -> Void)?
|
||||
|
||||
private let cardView = UIView()
|
||||
private let nameLabel = UILabel()
|
||||
private let avatarContainer = UIView()
|
||||
private let avatarIconView = UIImageView(image: UIImage(systemName: "person.fill"))
|
||||
private let nameTitleLabel = UILabel()
|
||||
private let nameValueLabel = UILabel()
|
||||
private let editRemarkButton = UIButton(type: .system)
|
||||
private let phoneLabel = UILabel()
|
||||
private let phoneTitleLabel = UILabel()
|
||||
private let phoneValueLabel = UILabel()
|
||||
private let callButton = UIButton(type: .system)
|
||||
private let bindTimeRow = CooperationInfoRowView(label: "绑定时间")
|
||||
private let commissionRow = CooperationInfoRowView(label: "分成比例")
|
||||
private let bindTimeTitleLabel = UILabel()
|
||||
private let bindTimeValueLabel = UILabel()
|
||||
private let commissionTitleLabel = UILabel()
|
||||
private let commissionValueLabel = UILabel()
|
||||
private let editCommissionButton = UIButton(type: .system)
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
@ -466,70 +469,131 @@ final class CooperationAcquirerCell: UITableViewCell {
|
||||
contentView.backgroundColor = .clear
|
||||
|
||||
cardView.backgroundColor = .white
|
||||
cardView.layer.cornerRadius = AppRadius.lg
|
||||
cardView.layer.cornerRadius = AppRadius.md
|
||||
cardView.layer.shadowColor = UIColor.black.cgColor
|
||||
cardView.layer.shadowOpacity = 0.04
|
||||
cardView.layer.shadowOffset = CGSize(width: 0, height: 1)
|
||||
cardView.layer.shadowRadius = 2
|
||||
|
||||
nameLabel.font = .systemFont(ofSize: 16, weight: .bold)
|
||||
nameLabel.textColor = AppColor.textPrimary
|
||||
avatarContainer.backgroundColor = AppColor.primary.withAlphaComponent(0.08)
|
||||
avatarContainer.layer.cornerRadius = AppRadius.sm
|
||||
avatarContainer.clipsToBounds = true
|
||||
|
||||
avatarIconView.tintColor = AppColor.primary
|
||||
avatarIconView.contentMode = .scaleAspectFit
|
||||
|
||||
configureTitleLabel(nameTitleLabel, text: "获客员名称")
|
||||
configureTitleLabel(phoneTitleLabel, text: "手机号")
|
||||
configureTitleLabel(bindTimeTitleLabel, text: "绑定时间")
|
||||
configureTitleLabel(commissionTitleLabel, text: "分成比例")
|
||||
|
||||
nameValueLabel.font = .systemFont(ofSize: 15, weight: .bold)
|
||||
nameValueLabel.textColor = AppColor.textPrimary
|
||||
nameValueLabel.numberOfLines = 1
|
||||
nameValueLabel.lineBreakMode = .byTruncatingTail
|
||||
|
||||
[phoneValueLabel, bindTimeValueLabel, commissionValueLabel].forEach { label in
|
||||
label.font = .systemFont(ofSize: 13)
|
||||
label.textColor = AppColor.textSecondary
|
||||
label.numberOfLines = 1
|
||||
label.lineBreakMode = .byTruncatingTail
|
||||
}
|
||||
commissionValueLabel.font = .systemFont(ofSize: 13, weight: .medium)
|
||||
|
||||
editRemarkButton.setTitle("修改备注", for: .normal)
|
||||
editRemarkButton.titleLabel?.font = .systemFont(ofSize: 13)
|
||||
editRemarkButton.setTitleColor(AppColor.primary, for: .normal)
|
||||
editRemarkButton.contentEdgeInsets = UIEdgeInsets(top: 16, left: 14, bottom: 16, right: 14)
|
||||
editRemarkButton.addTarget(self, action: #selector(editRemarkTapped), for: .touchUpInside)
|
||||
|
||||
phoneLabel.font = .systemFont(ofSize: 13)
|
||||
phoneLabel.textColor = AppColor.textSecondary
|
||||
|
||||
callButton.setImage(UIImage(systemName: "phone.fill"), for: .normal)
|
||||
callButton.tintColor = AppColor.primary
|
||||
callButton.backgroundColor = AppColor.primaryLight
|
||||
callButton.layer.cornerRadius = 15
|
||||
callButton.imageView?.contentMode = .scaleAspectFit
|
||||
callButton.addTarget(self, action: #selector(callTapped), for: .touchUpInside)
|
||||
|
||||
editCommissionButton.setTitle("修改", for: .normal)
|
||||
editCommissionButton.setTitle("修改比例", for: .normal)
|
||||
editCommissionButton.titleLabel?.font = .systemFont(ofSize: 13)
|
||||
editCommissionButton.setTitleColor(AppColor.primary, for: .normal)
|
||||
editCommissionButton.contentEdgeInsets = UIEdgeInsets(top: 4, left: 8, bottom: 4, right: 8)
|
||||
editCommissionButton.addTarget(self, action: #selector(editCommissionTapped), for: .touchUpInside)
|
||||
|
||||
contentView.addSubview(cardView)
|
||||
cardView.addSubview(nameLabel)
|
||||
cardView.addSubview(avatarContainer)
|
||||
avatarContainer.addSubview(avatarIconView)
|
||||
cardView.addSubview(nameTitleLabel)
|
||||
cardView.addSubview(nameValueLabel)
|
||||
cardView.addSubview(editRemarkButton)
|
||||
cardView.addSubview(phoneLabel)
|
||||
cardView.addSubview(phoneTitleLabel)
|
||||
cardView.addSubview(phoneValueLabel)
|
||||
cardView.addSubview(callButton)
|
||||
cardView.addSubview(bindTimeRow)
|
||||
cardView.addSubview(commissionRow)
|
||||
cardView.addSubview(bindTimeTitleLabel)
|
||||
cardView.addSubview(bindTimeValueLabel)
|
||||
cardView.addSubview(commissionTitleLabel)
|
||||
cardView.addSubview(commissionValueLabel)
|
||||
cardView.addSubview(editCommissionButton)
|
||||
|
||||
cardView.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: AppSpacing.md, bottom: AppSpacing.sm, right: AppSpacing.md))
|
||||
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 14, bottom: AppSpacing.sm, right: 14))
|
||||
}
|
||||
nameLabel.snp.makeConstraints { make in
|
||||
make.top.leading.equalToSuperview().offset(14)
|
||||
avatarContainer.snp.makeConstraints { make in
|
||||
make.leading.equalToSuperview().offset(14)
|
||||
make.centerY.equalToSuperview()
|
||||
make.size.equalTo(44)
|
||||
}
|
||||
avatarIconView.snp.makeConstraints { make in
|
||||
make.center.equalToSuperview()
|
||||
make.size.equalTo(22)
|
||||
}
|
||||
nameTitleLabel.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview().offset(AppSpacing.md)
|
||||
make.leading.equalTo(avatarContainer.snp.trailing).offset(AppSpacing.sm)
|
||||
}
|
||||
nameValueLabel.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(nameTitleLabel)
|
||||
make.leading.equalTo(nameTitleLabel.snp.trailing).offset(AppSpacing.sm)
|
||||
make.trailing.lessThanOrEqualTo(editRemarkButton.snp.leading).offset(-AppSpacing.xs)
|
||||
}
|
||||
editRemarkButton.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(nameLabel)
|
||||
make.leading.equalTo(nameLabel.snp.trailing).offset(AppSpacing.xs)
|
||||
make.top.trailing.equalToSuperview()
|
||||
}
|
||||
phoneLabel.snp.makeConstraints { make in
|
||||
make.top.equalTo(nameLabel.snp.bottom).offset(10)
|
||||
make.leading.equalToSuperview().offset(14)
|
||||
phoneTitleLabel.snp.makeConstraints { make in
|
||||
make.top.equalTo(nameTitleLabel.snp.bottom).offset(AppSpacing.xs)
|
||||
make.leading.equalTo(nameTitleLabel)
|
||||
}
|
||||
phoneValueLabel.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(phoneTitleLabel)
|
||||
make.leading.equalTo(phoneTitleLabel.snp.trailing).offset(AppSpacing.sm)
|
||||
make.trailing.lessThanOrEqualTo(callButton.snp.leading).offset(-AppSpacing.xs)
|
||||
}
|
||||
callButton.snp.makeConstraints { make in
|
||||
make.trailing.equalToSuperview().inset(14)
|
||||
make.centerY.equalTo(phoneLabel)
|
||||
make.centerY.equalTo(phoneTitleLabel)
|
||||
make.size.equalTo(30)
|
||||
}
|
||||
bindTimeRow.snp.makeConstraints { make in
|
||||
make.top.equalTo(phoneLabel.snp.bottom).offset(10)
|
||||
make.leading.trailing.equalToSuperview().inset(14)
|
||||
bindTimeTitleLabel.snp.makeConstraints { make in
|
||||
make.top.equalTo(phoneTitleLabel.snp.bottom).offset(AppSpacing.xs)
|
||||
make.leading.equalTo(nameTitleLabel)
|
||||
}
|
||||
commissionRow.snp.makeConstraints { make in
|
||||
make.top.equalTo(bindTimeRow.snp.bottom).offset(10)
|
||||
make.leading.equalToSuperview().offset(14)
|
||||
make.bottom.equalToSuperview().inset(14)
|
||||
bindTimeValueLabel.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(bindTimeTitleLabel)
|
||||
make.leading.equalTo(bindTimeTitleLabel.snp.trailing).offset(AppSpacing.sm)
|
||||
make.trailing.lessThanOrEqualToSuperview().inset(14)
|
||||
}
|
||||
commissionTitleLabel.snp.makeConstraints { make in
|
||||
make.top.equalTo(bindTimeTitleLabel.snp.bottom).offset(AppSpacing.xs)
|
||||
make.leading.equalTo(nameTitleLabel)
|
||||
make.bottom.equalToSuperview().inset(AppSpacing.md)
|
||||
}
|
||||
commissionValueLabel.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(commissionTitleLabel)
|
||||
make.leading.equalTo(commissionTitleLabel.snp.trailing).offset(AppSpacing.sm)
|
||||
make.trailing.lessThanOrEqualTo(editCommissionButton.snp.leading).offset(-AppSpacing.xs)
|
||||
}
|
||||
editCommissionButton.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(commissionRow)
|
||||
make.leading.equalTo(commissionRow.snp.trailing).offset(AppSpacing.xs)
|
||||
make.centerY.equalTo(commissionTitleLabel)
|
||||
make.trailing.equalToSuperview().inset(AppSpacing.xs)
|
||||
}
|
||||
}
|
||||
|
||||
@ -538,11 +602,26 @@ final class CooperationAcquirerCell: UITableViewCell {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
onEditRemark = nil
|
||||
onEditCommission = nil
|
||||
onCall = nil
|
||||
}
|
||||
|
||||
func configure(with acquirer: CooperativeSalerEntity) {
|
||||
nameLabel.text = acquirer.displayName.isEmpty ? "—" : acquirer.displayName
|
||||
phoneLabel.text = "手机号 \(CooperationOrderPhoneMask.mask(acquirer.displayPhone))"
|
||||
bindTimeRow.setValue(acquirer.displayBindTime.isEmpty ? "—" : acquirer.displayBindTime)
|
||||
commissionRow.setValue(acquirer.displayCommissionRate)
|
||||
nameValueLabel.text = acquirer.displayName.isEmpty ? "—" : acquirer.displayName
|
||||
phoneValueLabel.text = acquirer.displayPhone.isEmpty ? "—" : CooperationOrderPhoneMask.mask(acquirer.displayPhone)
|
||||
bindTimeValueLabel.text = acquirer.displayBindTime.isEmpty ? "—" : acquirer.displayBindTime
|
||||
commissionValueLabel.text = acquirer.displayCommissionRate.isEmpty ? "—" : acquirer.displayCommissionRate
|
||||
}
|
||||
|
||||
private func configureTitleLabel(_ label: UILabel, text: String) {
|
||||
label.text = text
|
||||
label.font = .systemFont(ofSize: 13)
|
||||
label.textColor = AppColor.textTertiary
|
||||
label.setContentHuggingPriority(.required, for: .horizontal)
|
||||
label.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
}
|
||||
|
||||
@objc private func editRemarkTapped() { onEditRemark?() }
|
||||
@ -550,6 +629,364 @@ final class CooperationAcquirerCell: UITableViewCell {
|
||||
@objc private func callTapped() { onCall?() }
|
||||
}
|
||||
|
||||
/// 修改获客员分成比例弹窗,对齐 Android `CommissionRateEditDialog`。
|
||||
final class CooperationCommissionRateDialogView: UIView {
|
||||
|
||||
var onCommissionRateChange: ((String) -> Void)?
|
||||
var onSmsCodeChange: ((String) -> Void)?
|
||||
var onSendSmsCode: (() -> Void)?
|
||||
var onCancel: (() -> Void)?
|
||||
var onConfirm: (() -> Void)?
|
||||
|
||||
var currentCommissionRate: String { rateField.text }
|
||||
var currentSmsCode: String { smsCodeField.text }
|
||||
|
||||
private let dimView = UIView()
|
||||
private let cardView = UIView()
|
||||
private let titleLabel = UILabel()
|
||||
private let infoBox = UIView()
|
||||
private let infoLabel = UILabel()
|
||||
private let contentStack = UIStackView()
|
||||
private let rateField = CooperationOutlinedTextFieldView(title: "分成比例", placeholder: "请输入0到100", suffix: "%")
|
||||
private let phoneField = CooperationOutlinedTextFieldView(title: "验证码发送手机号", placeholder: "—")
|
||||
private let smsCodeField = CooperationOutlinedTextFieldView(title: "验证码", placeholder: "请输入验证码")
|
||||
private let sendCodeButton = UIButton(type: .system)
|
||||
private let buttonStack = UIStackView()
|
||||
private let cancelButton = UIButton(type: .system)
|
||||
private let confirmButton = UIButton(type: .system)
|
||||
private var cardCenterYConstraint: Constraint?
|
||||
private var keyboardObservers: [NSObjectProtocol] = []
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setupUI()
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
unregisterKeyboardObservers()
|
||||
}
|
||||
|
||||
func apply(commissionRate: String, smsCode: String, phone: String, countdown: Int) {
|
||||
rateField.text = commissionRate
|
||||
smsCodeField.text = smsCode
|
||||
phoneField.text = phone.isEmpty ? "—" : phone
|
||||
|
||||
let isCountingDown = countdown > 0
|
||||
sendCodeButton.setTitle(isCountingDown ? "\(countdown)秒" : "获取验证码", for: .normal)
|
||||
sendCodeButton.isEnabled = !isCountingDown
|
||||
sendCodeButton.backgroundColor = AppColor.primary.withAlphaComponent(isCountingDown ? 0.7 : 1)
|
||||
sendCodeButton.setTitleColor(.white.withAlphaComponent(isCountingDown ? 0.7 : 1), for: .normal)
|
||||
sendCodeButton.setTitleColor(.white.withAlphaComponent(0.7), for: .disabled)
|
||||
}
|
||||
|
||||
func show(in parent: UIView) {
|
||||
frame = parent.bounds
|
||||
autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
alpha = 0
|
||||
parent.addSubview(self)
|
||||
layoutIfNeeded()
|
||||
registerKeyboardObservers()
|
||||
rateField.focus()
|
||||
UIView.animate(withDuration: 0.2) {
|
||||
self.alpha = 1
|
||||
}
|
||||
}
|
||||
|
||||
func dismiss() {
|
||||
endEditing(true)
|
||||
unregisterKeyboardObservers()
|
||||
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
|
||||
|
||||
infoBox.backgroundColor = AppColor.primary.withAlphaComponent(0.08)
|
||||
infoBox.layer.cornerRadius = AppRadius.sm
|
||||
|
||||
infoLabel.text = "修改分成比例需验证手机号"
|
||||
infoLabel.font = .systemFont(ofSize: 13)
|
||||
infoLabel.textColor = AppColor.primary
|
||||
|
||||
contentStack.axis = .vertical
|
||||
contentStack.spacing = AppSpacing.sm
|
||||
|
||||
rateField.keyboardType = .numberPad
|
||||
smsCodeField.keyboardType = .numberPad
|
||||
phoneField.isReadOnly = true
|
||||
|
||||
rateField.onTextChange = { [weak self] text in
|
||||
self?.onCommissionRateChange?(text)
|
||||
}
|
||||
smsCodeField.onTextChange = { [weak self] text in
|
||||
self?.onSmsCodeChange?(text)
|
||||
}
|
||||
|
||||
sendCodeButton.setTitle("获取验证码", for: .normal)
|
||||
sendCodeButton.titleLabel?.font = .systemFont(ofSize: 13, weight: .medium)
|
||||
sendCodeButton.setTitleColor(.white, for: .normal)
|
||||
sendCodeButton.backgroundColor = AppColor.primary
|
||||
sendCodeButton.layer.cornerRadius = AppRadius.sm
|
||||
sendCodeButton.addTarget(self, action: #selector(sendCodeTapped), for: .touchUpInside)
|
||||
|
||||
buttonStack.axis = .horizontal
|
||||
buttonStack.alignment = .center
|
||||
buttonStack.distribution = .fillEqually
|
||||
buttonStack.spacing = AppSpacing.sm
|
||||
|
||||
cancelButton.setTitle("取消", for: .normal)
|
||||
cancelButton.titleLabel?.font = .systemFont(ofSize: 15, weight: .medium)
|
||||
cancelButton.setTitleColor(AppColor.textSecondary, for: .normal)
|
||||
cancelButton.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside)
|
||||
|
||||
confirmButton.setTitle("确认修改", for: .normal)
|
||||
confirmButton.titleLabel?.font = .systemFont(ofSize: 15, weight: .medium)
|
||||
confirmButton.setTitleColor(AppColor.primary, for: .normal)
|
||||
confirmButton.addTarget(self, action: #selector(confirmTapped), for: .touchUpInside)
|
||||
|
||||
addSubview(dimView)
|
||||
addSubview(cardView)
|
||||
cardView.addSubview(titleLabel)
|
||||
cardView.addSubview(infoBox)
|
||||
infoBox.addSubview(infoLabel)
|
||||
cardView.addSubview(contentStack)
|
||||
contentStack.addArrangedSubview(rateField)
|
||||
contentStack.addArrangedSubview(phoneField)
|
||||
contentStack.addArrangedSubview(makeSmsRow())
|
||||
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)
|
||||
cardCenterYConstraint = make.centerY.equalToSuperview().constraint
|
||||
}
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.top.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||||
}
|
||||
infoBox.snp.makeConstraints { make in
|
||||
make.top.equalTo(titleLabel.snp.bottom).offset(AppSpacing.md)
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||||
}
|
||||
infoLabel.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 10, left: 12, bottom: 10, right: 12))
|
||||
}
|
||||
contentStack.snp.makeConstraints { make in
|
||||
make.top.equalTo(infoBox.snp.bottom).offset(AppSpacing.sm)
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||||
}
|
||||
buttonStack.snp.makeConstraints { make in
|
||||
make.top.equalTo(contentStack.snp.bottom).offset(AppSpacing.md)
|
||||
make.leading.trailing.bottom.equalToSuperview().inset(AppSpacing.md)
|
||||
make.height.equalTo(44)
|
||||
}
|
||||
}
|
||||
|
||||
private func makeSmsRow() -> UIView {
|
||||
let row = UIStackView(arrangedSubviews: [smsCodeField, sendCodeButton])
|
||||
row.axis = .horizontal
|
||||
row.alignment = .fill
|
||||
row.spacing = 10
|
||||
sendCodeButton.snp.makeConstraints { make in
|
||||
make.width.equalTo(104)
|
||||
make.height.equalTo(46)
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
private func registerKeyboardObservers() {
|
||||
unregisterKeyboardObservers()
|
||||
let center = NotificationCenter.default
|
||||
keyboardObservers = [
|
||||
center.addObserver(
|
||||
forName: UIResponder.keyboardWillChangeFrameNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] notification in
|
||||
self?.handleKeyboardNotification(notification)
|
||||
},
|
||||
center.addObserver(
|
||||
forName: UIResponder.keyboardWillHideNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] notification in
|
||||
self?.handleKeyboardNotification(notification)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
private func unregisterKeyboardObservers() {
|
||||
keyboardObservers.forEach(NotificationCenter.default.removeObserver)
|
||||
keyboardObservers.removeAll()
|
||||
}
|
||||
|
||||
private func handleKeyboardNotification(_ notification: Notification) {
|
||||
layoutIfNeeded()
|
||||
guard let userInfo = notification.userInfo else { return }
|
||||
|
||||
let duration = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0.25
|
||||
let curveRaw = (userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber)?.uintValue
|
||||
?? UInt(UIView.AnimationCurve.easeInOut.rawValue)
|
||||
let options = UIView.AnimationOptions(rawValue: curveRaw << 16)
|
||||
|
||||
let offset = keyboardAvoidanceOffset(from: userInfo)
|
||||
cardCenterYConstraint?.update(offset: offset)
|
||||
UIView.animate(withDuration: duration, delay: 0, options: options) {
|
||||
self.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
private func keyboardAvoidanceOffset(from userInfo: [AnyHashable: Any]) -> CGFloat {
|
||||
guard
|
||||
let keyboardFrameValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue
|
||||
else {
|
||||
return 0
|
||||
}
|
||||
|
||||
let keyboardFrame = convert(keyboardFrameValue.cgRectValue, from: nil)
|
||||
guard keyboardFrame.minY < bounds.height else { return 0 }
|
||||
|
||||
let cardHeight = cardView.bounds.height
|
||||
let baseMinY = bounds.midY - cardHeight / 2
|
||||
let baseMaxY = bounds.midY + cardHeight / 2
|
||||
let desiredBottom = keyboardFrame.minY - AppSpacing.md
|
||||
let neededShift = max(0, baseMaxY - desiredBottom)
|
||||
let maxShift = max(0, baseMinY - AppSpacing.md)
|
||||
return -min(neededShift, maxShift)
|
||||
}
|
||||
|
||||
@objc private func sendCodeTapped() {
|
||||
onSendSmsCode?()
|
||||
}
|
||||
|
||||
@objc private func cancelTapped() {
|
||||
onCancel?()
|
||||
}
|
||||
|
||||
@objc private func confirmTapped() {
|
||||
onConfirm?()
|
||||
}
|
||||
}
|
||||
|
||||
/// 合作订单弹窗内的描边输入框。
|
||||
private final class CooperationOutlinedTextFieldView: UIView {
|
||||
|
||||
var onTextChange: ((String) -> Void)?
|
||||
var text: String {
|
||||
get { textField.text ?? "" }
|
||||
set {
|
||||
if textField.text != newValue {
|
||||
textField.text = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
var keyboardType: UIKeyboardType {
|
||||
get { textField.keyboardType }
|
||||
set { textField.keyboardType = newValue }
|
||||
}
|
||||
var isReadOnly = false {
|
||||
didSet {
|
||||
textField.isUserInteractionEnabled = !isReadOnly
|
||||
textField.textColor = isReadOnly ? AppColor.textSecondary : AppColor.textPrimary
|
||||
}
|
||||
}
|
||||
|
||||
private let titleLabel = UILabel()
|
||||
private let containerView = UIView()
|
||||
private let textField = UITextField()
|
||||
private let suffixLabel = UILabel()
|
||||
private let suffix: String?
|
||||
|
||||
init(title: String, placeholder: String, suffix: String? = nil) {
|
||||
self.suffix = suffix
|
||||
super.init(frame: .zero)
|
||||
setupUI(title: title, placeholder: placeholder)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func focus() {
|
||||
textField.becomeFirstResponder()
|
||||
}
|
||||
|
||||
private func setupUI(title: String, placeholder: String) {
|
||||
titleLabel.text = title
|
||||
titleLabel.font = .systemFont(ofSize: 12)
|
||||
titleLabel.textColor = AppColor.textSecondary
|
||||
titleLabel.backgroundColor = .white
|
||||
|
||||
containerView.layer.borderWidth = 1
|
||||
containerView.layer.borderColor = UIColor(hex: 0xD9DDE4).cgColor
|
||||
containerView.layer.cornerRadius = AppRadius.sm
|
||||
|
||||
textField.placeholder = placeholder
|
||||
textField.font = .systemFont(ofSize: 15)
|
||||
textField.textColor = AppColor.textPrimary
|
||||
textField.borderStyle = .none
|
||||
textField.backgroundColor = .clear
|
||||
textField.addTarget(self, action: #selector(textChanged), for: .editingChanged)
|
||||
|
||||
suffixLabel.text = suffix
|
||||
suffixLabel.font = .systemFont(ofSize: 15)
|
||||
suffixLabel.textColor = AppColor.textSecondary
|
||||
suffixLabel.isHidden = suffix == nil
|
||||
|
||||
addSubview(containerView)
|
||||
addSubview(titleLabel)
|
||||
containerView.addSubview(textField)
|
||||
containerView.addSubview(suffixLabel)
|
||||
|
||||
snp.makeConstraints { make in
|
||||
make.height.equalTo(56)
|
||||
}
|
||||
containerView.snp.makeConstraints { make in
|
||||
make.leading.trailing.bottom.equalToSuperview()
|
||||
make.top.equalToSuperview().offset(6)
|
||||
}
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.leading.equalToSuperview().offset(10)
|
||||
make.top.equalToSuperview()
|
||||
}
|
||||
textField.snp.makeConstraints { make in
|
||||
make.leading.equalToSuperview().offset(AppSpacing.sm)
|
||||
make.trailing.equalTo(suffixLabel.snp.leading).offset(-AppSpacing.xs)
|
||||
make.centerY.equalTo(containerView)
|
||||
}
|
||||
suffixLabel.snp.makeConstraints { make in
|
||||
make.trailing.equalToSuperview().inset(AppSpacing.sm)
|
||||
make.centerY.equalTo(containerView)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func textChanged() {
|
||||
onTextChange?(textField.text ?? "")
|
||||
}
|
||||
}
|
||||
|
||||
/// 标签-值行。
|
||||
final class CooperationInfoRowView: UIView {
|
||||
private let titleLabel = UILabel()
|
||||
|
||||
Reference in New Issue
Block a user