修复任务提交与弹窗交互问题
This commit is contained in:
@ -220,7 +220,18 @@ private extension String {
|
||||
}
|
||||
|
||||
/// 自定义订单退款弹窗视图,对齐 Android `OrderRefundDialog` / `DepositOrderRefundDialog`。
|
||||
final class OrderRefundDialogView: UIView, UITextViewDelegate {
|
||||
final class OrderRefundDialogView: KeyboardAvoidingDialogView, UITextViewDelegate {
|
||||
|
||||
/// 退款弹窗局部布局尺寸,保持内容紧凑但不牺牲基础点击区域。
|
||||
private enum Layout {
|
||||
static let sectionGap: CGFloat = AppSpacing.lg
|
||||
static let formGap: CGFloat = AppSpacing.md
|
||||
static let controlHeight: CGFloat = AppSpacing.minTouchTarget
|
||||
static let amountGroupHeight: CGFloat = 67
|
||||
static let remarkHeight: CGFloat = 88
|
||||
static let actionTop: CGFloat = 14
|
||||
static let bottomInset: CGFloat = AppSpacing.md
|
||||
}
|
||||
|
||||
/// 退款弹窗展示与校验配置,用于区分普通订单和店铺/定金订单。
|
||||
struct Configuration {
|
||||
@ -266,8 +277,6 @@ final class OrderRefundDialogView: UIView, UITextViewDelegate {
|
||||
|
||||
private let configuration: Configuration
|
||||
private var selectedMode: OrderRefundMode
|
||||
private var keyboardObservers: [NSObjectProtocol] = []
|
||||
private var cardCenterYConstraint: Constraint?
|
||||
|
||||
private let dimView = UIView()
|
||||
private let cardView = UIView()
|
||||
@ -304,16 +313,11 @@ final class OrderRefundDialogView: UIView, UITextViewDelegate {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
unregisterKeyboardObservers()
|
||||
}
|
||||
|
||||
func show(in parent: UIView) {
|
||||
frame = parent.bounds
|
||||
autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
alpha = 0
|
||||
parent.addSubview(self)
|
||||
registerKeyboardObservers()
|
||||
UIView.animate(withDuration: 0.2) {
|
||||
self.alpha = 1
|
||||
}
|
||||
@ -321,7 +325,6 @@ final class OrderRefundDialogView: UIView, UITextViewDelegate {
|
||||
|
||||
func dismiss() {
|
||||
endEditing(true)
|
||||
unregisterKeyboardObservers()
|
||||
UIView.animate(withDuration: 0.2, animations: {
|
||||
self.alpha = 0
|
||||
}, completion: { _ in
|
||||
@ -330,6 +333,9 @@ final class OrderRefundDialogView: UIView, UITextViewDelegate {
|
||||
}
|
||||
|
||||
private func setupUI() {
|
||||
keyboardAvoidingContentView = cardView
|
||||
keyboardAvoidingMinimumSpacing = AppSpacing.sm
|
||||
|
||||
dimView.backgroundColor = UIColor.black.withAlphaComponent(0.2)
|
||||
dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(cancelTapped)))
|
||||
|
||||
@ -422,7 +428,7 @@ final class OrderRefundDialogView: UIView, UITextViewDelegate {
|
||||
}
|
||||
cardView.snp.makeConstraints { make in
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||||
cardCenterYConstraint = make.centerY.equalToSuperview().constraint
|
||||
keyboardAvoidingCenterYConstraint = make.centerY.equalToSuperview().constraint
|
||||
}
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview().offset(AppSpacing.sm)
|
||||
@ -442,18 +448,18 @@ final class OrderRefundDialogView: UIView, UITextViewDelegate {
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||||
}
|
||||
refundModeTitleLabel.snp.makeConstraints { make in
|
||||
make.top.equalTo(orderNumberLabel.snp.bottom).offset(AppSpacing.xl)
|
||||
make.top.equalTo(orderNumberLabel.snp.bottom).offset(Layout.sectionGap)
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||||
}
|
||||
modeButtonStack.snp.makeConstraints { make in
|
||||
make.top.equalTo(refundModeTitleLabel.snp.bottom).offset(AppSpacing.xs)
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||||
make.height.equalTo(46)
|
||||
make.height.equalTo(Layout.controlHeight)
|
||||
}
|
||||
amountGroupView.snp.makeConstraints { make in
|
||||
make.top.equalTo(modeButtonStack.snp.bottom).offset(AppSpacing.xl)
|
||||
make.top.equalTo(modeButtonStack.snp.bottom).offset(Layout.formGap)
|
||||
make.leading.trailing.equalToSuperview()
|
||||
make.height.equalTo(73)
|
||||
make.height.equalTo(Layout.amountGroupHeight)
|
||||
}
|
||||
amountTitleLabel.snp.makeConstraints { make in
|
||||
make.top.leading.trailing.equalToSuperview().inset(UIEdgeInsets(top: 0, left: AppSpacing.md, bottom: 0, right: AppSpacing.md))
|
||||
@ -461,20 +467,20 @@ final class OrderRefundDialogView: UIView, UITextViewDelegate {
|
||||
amountContainerView.snp.makeConstraints { make in
|
||||
make.top.equalTo(amountTitleLabel.snp.bottom).offset(AppSpacing.xs)
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||||
make.height.equalTo(46)
|
||||
make.height.equalTo(Layout.controlHeight)
|
||||
}
|
||||
amountField.snp.makeConstraints { make in
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.sm)
|
||||
make.centerY.equalToSuperview()
|
||||
}
|
||||
remarkTitleLabel.snp.makeConstraints { make in
|
||||
make.top.equalTo(amountGroupView.snp.bottom).offset(AppSpacing.xl)
|
||||
make.top.equalTo(amountGroupView.snp.bottom).offset(Layout.formGap)
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||||
}
|
||||
remarkContainerView.snp.makeConstraints { make in
|
||||
make.top.equalTo(remarkTitleLabel.snp.bottom).offset(AppSpacing.xs)
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||||
make.height.equalTo(104)
|
||||
make.height.equalTo(Layout.remarkHeight)
|
||||
}
|
||||
remarkTextView.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
@ -484,9 +490,9 @@ final class OrderRefundDialogView: UIView, UITextViewDelegate {
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.sm)
|
||||
}
|
||||
actionRow.snp.makeConstraints { make in
|
||||
make.top.equalTo(remarkContainerView.snp.bottom).offset(20)
|
||||
make.top.equalTo(remarkContainerView.snp.bottom).offset(Layout.actionTop)
|
||||
make.trailing.equalToSuperview().inset(AppSpacing.md)
|
||||
make.bottom.equalToSuperview().inset(AppSpacing.lg)
|
||||
make.bottom.equalToSuperview().inset(Layout.bottomInset)
|
||||
}
|
||||
[cancelButton, confirmButton].forEach { button in
|
||||
button.snp.makeConstraints { make in
|
||||
@ -521,10 +527,10 @@ final class OrderRefundDialogView: UIView, UITextViewDelegate {
|
||||
let amountHidden = selectedMode == .full
|
||||
amountGroupView.isHidden = amountHidden
|
||||
amountGroupView.snp.updateConstraints { make in
|
||||
make.height.equalTo(amountHidden ? 0 : 73)
|
||||
make.height.equalTo(amountHidden ? 0 : Layout.amountGroupHeight)
|
||||
}
|
||||
remarkTitleLabel.snp.remakeConstraints { make in
|
||||
make.top.equalTo((amountHidden ? modeButtonStack : amountGroupView).snp.bottom).offset(AppSpacing.xl)
|
||||
make.top.equalTo((amountHidden ? modeButtonStack : amountGroupView).snp.bottom).offset(Layout.formGap)
|
||||
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||||
}
|
||||
|
||||
@ -544,60 +550,6 @@ final class OrderRefundDialogView: UIView, UITextViewDelegate {
|
||||
button.setTitleColor(selected ? .white : UIColor(hex: 0x4B5563), for: .normal)
|
||||
}
|
||||
|
||||
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 desiredBottom = keyboardFrame.minY - AppSpacing.sm
|
||||
let overlap = cardView.frame.maxY - desiredBottom
|
||||
return overlap > 0 ? -overlap : 0
|
||||
}
|
||||
|
||||
func textViewDidChange(_ textView: UITextView) {
|
||||
if textView.text.count > 255 {
|
||||
textView.text = String(textView.text.prefix(255))
|
||||
|
||||
@ -17,6 +17,7 @@ final class OrderSourceEntryView: UIControl {
|
||||
private let personRow = UIStackView()
|
||||
private let personNameLabel = UILabel()
|
||||
private let personPhoneLabel = UILabel()
|
||||
private let personSpacerView = UIView()
|
||||
private let divider = UIView()
|
||||
private let leadContentView = OrderSourceLeadContentView()
|
||||
private var currentImages: [String] = []
|
||||
@ -83,10 +84,19 @@ final class OrderSourceEntryView: UIControl {
|
||||
personRow.spacing = 10
|
||||
personNameLabel.font = .systemFont(ofSize: 13, weight: .medium)
|
||||
personNameLabel.textColor = AppColor.text333
|
||||
personNameLabel.lineBreakMode = .byTruncatingTail
|
||||
personNameLabel.setContentHuggingPriority(.required, for: .horizontal)
|
||||
personNameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
personPhoneLabel.font = .systemFont(ofSize: 13)
|
||||
personPhoneLabel.textColor = AppColor.text666
|
||||
personPhoneLabel.lineBreakMode = .byTruncatingTail
|
||||
personPhoneLabel.setContentHuggingPriority(.required, for: .horizontal)
|
||||
personPhoneLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
personSpacerView.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||
personSpacerView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
personRow.addArrangedSubview(personNameLabel)
|
||||
personRow.addArrangedSubview(personPhoneLabel)
|
||||
personRow.addArrangedSubview(personSpacerView)
|
||||
|
||||
divider.backgroundColor = OrderTokens.sourceDivider
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ final class OrderSourceLeadContentView: UIView {
|
||||
private let phoneRow = UIStackView()
|
||||
private let phoneTitleLabel = UILabel()
|
||||
private let phoneValueLabel = UILabel()
|
||||
private let phoneSpacerView = UIView()
|
||||
private let remarkLabel = UILabel()
|
||||
private let imageRow = UIStackView()
|
||||
private let emptyImageLabel = UILabel()
|
||||
@ -91,12 +92,20 @@ final class OrderSourceLeadContentView: UIView {
|
||||
phoneTitleLabel.text = "客户手机号"
|
||||
phoneTitleLabel.font = .systemFont(ofSize: 12)
|
||||
phoneTitleLabel.textColor = AppColor.text666
|
||||
phoneTitleLabel.setContentHuggingPriority(.required, for: .horizontal)
|
||||
phoneTitleLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
phoneValueLabel.font = .systemFont(ofSize: 13, weight: .medium)
|
||||
phoneValueLabel.lineBreakMode = .byTruncatingTail
|
||||
phoneValueLabel.setContentHuggingPriority(.required, for: .horizontal)
|
||||
phoneValueLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
phoneValueLabel.addGestureRecognizer(
|
||||
UITapGestureRecognizer(target: self, action: #selector(phoneTapped))
|
||||
)
|
||||
phoneSpacerView.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||
phoneSpacerView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
phoneRow.addArrangedSubview(phoneTitleLabel)
|
||||
phoneRow.addArrangedSubview(phoneValueLabel)
|
||||
phoneRow.addArrangedSubview(phoneSpacerView)
|
||||
|
||||
remarkLabel.textColor = AppColor.text333
|
||||
remarkLabel.lineBreakMode = .byTruncatingTail
|
||||
|
||||
Reference in New Issue
Block a user