@ -3,46 +3,651 @@
// s u i x i n k a n
//
import SnapKit
import UIKit
// / 普 通 订 单 退 款 弹 窗 入 口 , 对 齐 A n d r o i d ` O r d e r R e f u n d D i a l o g ` 。
enum OrderRefundAlertController {
static func present (
from presenter : UIViewController ,
order : OrderEntity ,
onConfirm : @ escaping ( OrderRefundRequest ) -> Void
) {
let alert = UIAlertControll er( title : " 订单退款 " , message : order . orderNumber , preferredStyle : . alert )
a ler t. addTextField { $0 . placeholder = " 退款金额 " ; $0 . text = o rder. actualPayAmount }
alert . addTextField { $0 . placeholder = " 退款原因 " }
alert . addAction ( UIAlertAction ( title : " 取消 " , style : . cancel ) )
alert . addAction ( UIAlertAction ( title : " 确认退款 " , style : . destructive ) { _ in
let amount = alert . textFields ? . first ? . text ? ? order . actualPayAmount
let reason = alert . textFields ? . last ? . text ? ? " "
onConfirm ( OrderRefundRequest ( orderNumber : order . orderNumber , refundType : 1 , refundAmount : amount , refundReason : reason ) )
} )
presenter . present ( alert , animated : true )
let fullAmount = ord er. orderAmount . isEmpty ? order . actualPayAmount : order . orderAmount
let dialog = O rderRefundDialogView (
configuration : . photographerOrder (
orderNumber : order . orderNumber ,
fullRefundAmount : fullAmount
)
)
dialog . onMessage = { [ weak p resenter ] message in
( presenter as ? BaseViewController ) ? . showToast ( message )
}
dialog . onConfirm = { payload in
onConfirm (
OrderRefundRequest (
orderNumber : order . orderNumber ,
refundType : payload . refundType ,
refundAmount : payload . refundAmount ,
refundReason : payload . refundReason
)
)
}
dialog . show ( in : presenter . view )
}
}
// / 店 铺 / 定 金 订 单 退 款 弹 窗 入 口 , 对 齐 A n d r o i d ` D e p o s i t O r d e r R e f u n d D i a l o g ` 。
enum DepositOrderRefundAlertController {
static func present (
from presenter : UIViewController ,
orderNumber : String ,
amount : String ,
refundType : Int ,
onConfirm : @ escaping ( String , String ) -> Void
onConfirm : @ escaping ( Int , String, String ) -> Void
) {
let title = refundType = = 3 ? " 部分退款 " : " 全额退款 "
let alert = UIAlertController ( title : title , message : orderNumber , preferredStyle : . al ert )
alert . addTextField { $0 . placeholder = " 退款金额 " ; $0 . text = amount }
alert . addTextField { $0 . placeholder = " 退款原因 " }
alert . addAction ( UIAlertAction ( title : " 取消 " , style : . cancel ) )
alert . addAction ( UIAlertAction ( title : " 确认 " , style : . destructive ) { _ in
let refundAmount = alert . textFields ? . first ? . text ? ? amount
let reason = alert . textFields ? . last ? . text ? ? " "
onConfirm ( refundAmount , reason )
let dialog = OrderRefundDialogView (
configuration : . storeOrd er (
orderNumber : orderNumber ,
actualPayAmount : amount ,
initialMode : OrderRefundMode ( rawValue : refundType ) ? ? . full
)
)
dialog . onMessage = { [ weak presenter ] message in
( presenter as ? BaseViewController ) ? . showToast ( message )
}
dialog . onConfirm = { payload in
onConfirm ( payload . refundType , payload . refundAmount , payload . refundReason )
}
dialog . show ( in : presenter . view )
}
}
// / 退 款 方 式 , 和 A n d r o i d 常 量 ` 1 = 全 额 退 款 、 3 = 部 分 退 款 ` 保 持 一 致 。
enum OrderRefundMode : Int {
case full = 1
case partial = 3
}
// / 退 款 弹 窗 校 验 场 景 。
enum OrderRefundDialogKind {
case photographerOrder
case storeOrder
}
// / 退 款 弹 窗 确 认 后 的 结 构 化 结 果 。
struct OrderRefundDialogPayload : Equatable {
let refundType : Int
let refundAmount : String
let refundReason : String
}
// / 退 款 弹 窗 校 验 失 败 信 息 。
struct OrderRefundDialogValidationError : Error , Equatable {
let message : String
}
// / 退 款 弹 窗 输 入 校 验 与 金 额 格 式 化 逻 辑 , 对 齐 A n d r o i d 两 个 退 款 弹 窗 。
enum OrderRefundDialogValidation {
static func filterMoneyInput ( _ text : String , maxLength : Int ) -> String {
var result = " "
var hasDot = false
var decimalCount = 0
for character in text {
guard result . count < maxLength else { break }
if character . isNumber {
if hasDot {
guard decimalCount < 2 else { continue }
decimalCount += 1
}
result . append ( character )
} else if character = = " . " , ! hasDot {
hasDot = true
result . append ( character )
}
}
return result
}
static func validate (
kind : OrderRefundDialogKind ,
mode : OrderRefundMode ,
amountText : String ,
reason : String ,
fullRefundAmount : String ,
maxRefundAmount : String
) -> Result < OrderRefundDialogPayload , OrderRefundDialogValidationError > {
let trimmedReason = reason . trimmingCharacters ( in : . whitespacesAndNewlines )
switch kind {
case . photographerOrder :
return validatePhotographerOrder (
mode : mode ,
amountText : amountText ,
reason : trimmedReason ,
fullRefundAmount : fullRefundAmount
)
case . storeOrder :
return validateStoreOrder (
mode : mode ,
amountText : amountText ,
reason : trimmedReason ,
maxRefundAmount : maxRefundAmount
)
}
}
private static func validatePhotographerOrder (
mode : OrderRefundMode ,
amountText : String ,
reason : String ,
fullRefundAmount : String
) -> Result < OrderRefundDialogPayload , OrderRefundDialogValidationError > {
guard ! reason . isEmpty else {
return . failure ( OrderRefundDialogValidationError ( message : " 请输入退款原因 " ) )
}
guard reason . count <= 255 else {
return . failure ( OrderRefundDialogValidationError ( message : " 退款原因最多255个字符 " ) )
}
if mode = = . partial , amountText . trimmingCharacters ( in : . whitespacesAndNewlines ) . isEmpty {
return . failure ( OrderRefundDialogValidationError ( message : " 请输入退款金额 " ) )
}
let refundAmount = mode = = . full ? fullRefundAmount : amountText
return . success (
OrderRefundDialogPayload (
refundType : mode . rawValue ,
refundAmount : refundAmount ,
refundReason : reason
)
)
}
private static func validateStoreOrder (
mode : OrderRefundMode ,
amountText : String ,
reason : String ,
maxRefundAmount : String
) -> Result < OrderRefundDialogPayload , OrderRefundDialogValidationError > {
guard reason . count >= 5 else {
return . failure ( OrderRefundDialogValidationError ( message : " 退款原因至少5个字符 " ) )
}
guard reason . count <= 255 else {
return . failure ( OrderRefundDialogValidationError ( message : " 退款原因最多255个字符 " ) )
}
guard mode = = . partial else {
return . success (
OrderRefundDialogPayload (
refundType : mode . rawValue ,
refundAmount : " 0 " ,
refundReason : reason
)
)
}
let trimmedAmount = amountText . trimmingCharacters ( in : . whitespacesAndNewlines )
guard ! trimmedAmount . isEmpty else {
return . failure ( OrderRefundDialogValidationError ( message : " 请输入退款金额 " ) )
}
guard let amount = Double ( trimmedAmount ) , amount > 0 else {
return . failure ( OrderRefundDialogValidationError ( message : " 请输入有效的退款金额 " ) )
}
let maxAmount = parseAmount ( maxRefundAmount )
guard maxAmount <= 0 || amount <= maxAmount else {
return . failure ( OrderRefundDialogValidationError ( message : " 退款金额不能大于实际支付金额 " ) )
}
return . success (
OrderRefundDialogPayload (
refundType : mode . rawValue ,
refundAmount : String ( format : " %.2f " , amount ) ,
refundReason : reason
)
)
}
private static func parseAmount ( _ text : String ) -> Double {
text
. replacingOccurrences ( of : " , " , with : " " )
. replacingOccurrences ( of : " ¥ " , with : " " )
. trimmingCharacters ( in : . whitespacesAndNewlines )
. toDoubleOrZero
}
}
private extension String {
var toDoubleOrZero : Double {
Double ( self ) ? ? 0
}
}
// / 自 定 义 订 单 退 款 弹 窗 视 图 , 对 齐 A n d r o i d ` O r d e r R e f u n d D i a l o g ` / ` D e p o s i t O r d e r R e f u n d D i a l o g ` 。
final class OrderRefundDialogView : UIView , UITextViewDelegate {
// / 退 款 弹 窗 展 示 与 校 验 配 置 , 用 于 区 分 普 通 订 单 和 店 铺 / 定 金 订 单 。
struct Configuration {
let kind : OrderRefundDialogKind
let orderNumber : String
let fullRefundAmount : String
let maxRefundAmount : String
let initialMode : OrderRefundMode
let remarkPlaceholder : String
let moneyMaxLength : Int
static func photographerOrder ( orderNumber : String , fullRefundAmount : String ) -> Configuration {
Configuration (
kind : . photographerOrder ,
orderNumber : orderNumber ,
fullRefundAmount : fullRefundAmount ,
maxRefundAmount : fullRefundAmount ,
initialMode : . full ,
remarkPlaceholder : " 请输入退款原因(必填) " ,
moneyMaxLength : 10
)
}
static func storeOrder (
orderNumber : String ,
actualPayAmount : String ,
initialMode : OrderRefundMode
) -> Configuration {
Configuration (
kind : . storeOrder ,
orderNumber : orderNumber ,
fullRefundAmount : " 0 " ,
maxRefundAmount : actualPayAmount ,
initialMode : initialMode ,
remarkPlaceholder : " 请输入退款原因(必填, 5-255字) " ,
moneyMaxLength : 15
)
}
}
var onConfirm : ( ( OrderRefundDialogPayload ) -> Void ) ?
var onMessage : ( ( String ) -> Void ) ?
private let configuration : Configuration
private var selectedMode : OrderRefundMode
private var keyboardObservers : [ NSObjectProtocol ] = [ ]
private var cardCenterYConstraint : Constraint ?
private let dimView = UIView ( )
private let cardView = UIView ( )
private let titleLabel = UILabel ( )
private let separatorView = UIView ( )
private let orderTitleLabel = UILabel ( )
private let orderNumberLabel = UILabel ( )
private let refundModeTitleLabel = UILabel ( )
private let modeButtonStack = UIStackView ( )
private let fullRefundButton = UIButton ( type : . system )
private let partialRefundButton = UIButton ( type : . system )
private let amountGroupView = UIView ( )
private let amountTitleLabel = UILabel ( )
private let amountContainerView = UIView ( )
private let amountField = UITextField ( )
private let remarkTitleLabel = UILabel ( )
private let remarkContainerView = UIView ( )
private let remarkTextView = UITextView ( )
private let remarkPlaceholderLabel = UILabel ( )
private let actionRow = UIStackView ( )
private let cancelButton = UIButton ( type : . system )
private let confirmButton = UIButton ( type : . system )
init ( configuration : Configuration ) {
self . configuration = configuration
self . selectedMode = configuration . initialMode
super . init ( frame : . zero )
setupUI ( )
applyMode ( animated : false )
}
@ available ( * , unavailable )
required init ? ( coder : NSCoder ) {
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
}
}
func dismiss ( ) {
endEditing ( true )
unregisterKeyboardObservers ( )
UIView . animate ( withDuration : 0.2 , animations : {
self . alpha = 0
} , completion : { _ in
self . removeFromSuperview ( )
} )
presenter . present ( alert , animated : true )
}
private func setupUI ( ) {
dimView . backgroundColor = UIColor . black . withAlphaComponent ( 0.2 )
dimView . addGestureRecognizer ( UITapGestureRecognizer ( target : self , action : #selector ( cancelTapped ) ) )
cardView . backgroundColor = . white
cardView . layer . cornerRadius = AppRadius . md
cardView . clipsToBounds = true
titleLabel . text = " 订单退款 "
titleLabel . font = . systemFont ( ofSize : 18 , weight : . bold )
titleLabel . textColor = . black
separatorView . backgroundColor = UIColor ( hex : 0xF4F4F4 )
configureSectionLabel ( orderTitleLabel , text : " 订单号 " )
orderNumberLabel . text = configuration . orderNumber
orderNumberLabel . font = . systemFont ( ofSize : 16 )
orderNumberLabel . textColor = . black
orderNumberLabel . numberOfLines = 0
configureSectionLabel ( refundModeTitleLabel , text : " 退款方式 " )
configureModeButton ( fullRefundButton , title : " 全额退款 " )
configureModeButton ( partialRefundButton , title : " 部分退款 " )
fullRefundButton . addTarget ( self , action : #selector ( fullRefundTapped ) , for : . touchUpInside )
partialRefundButton . addTarget ( self , action : #selector ( partialRefundTapped ) , for : . touchUpInside )
modeButtonStack . axis = . horizontal
modeButtonStack . spacing = AppSpacing . md
modeButtonStack . distribution = . fillEqually
configureSectionLabel ( amountTitleLabel , text : " 退款金额 " )
amountContainerView . layer . borderWidth = 1
amountContainerView . layer . borderColor = UIColor ( hex : 0xB6BECA ) . cgColor
amountContainerView . layer . cornerRadius = AppRadius . sm
amountField . placeholder = " 请输入退款金额 "
amountField . font = . systemFont ( ofSize : 14 )
amountField . textColor = AppColor . textPrimary
amountField . keyboardType = . decimalPad
amountField . borderStyle = . none
amountField . addTarget ( self , action : #selector ( amountChanged ) , for : . editingChanged )
configureSectionLabel ( remarkTitleLabel , text : " 退款备注 " )
remarkContainerView . layer . borderWidth = 1
remarkContainerView . layer . borderColor = UIColor ( hex : 0xB6BECA ) . cgColor
remarkContainerView . layer . cornerRadius = AppRadius . sm
remarkTextView . font = . systemFont ( ofSize : 14 )
remarkTextView . textColor = AppColor . textPrimary
remarkTextView . backgroundColor = . clear
remarkTextView . textContainerInset = UIEdgeInsets ( top : 10 , left : 8 , bottom : 10 , right : 8 )
remarkTextView . delegate = self
remarkPlaceholderLabel . text = configuration . remarkPlaceholder
remarkPlaceholderLabel . font = . systemFont ( ofSize : 14 )
remarkPlaceholderLabel . textColor = AppColor . textTertiary
actionRow . axis = . horizontal
actionRow . alignment = . center
actionRow . spacing = AppSpacing . xs
configureActionButton ( cancelButton , title : " 取消 " , foreground : AppColor . primary , background : AppColor . primaryLight )
configureActionButton ( confirmButton , title : " 确认 " , foreground : . white , background : AppColor . primary )
cancelButton . addTarget ( self , action : #selector ( cancelTapped ) , for : . touchUpInside )
confirmButton . addTarget ( self , action : #selector ( confirmTapped ) , for : . touchUpInside )
addSubview ( dimView )
addSubview ( cardView )
cardView . addSubview ( titleLabel )
cardView . addSubview ( separatorView )
cardView . addSubview ( orderTitleLabel )
cardView . addSubview ( orderNumberLabel )
cardView . addSubview ( refundModeTitleLabel )
cardView . addSubview ( modeButtonStack )
modeButtonStack . addArrangedSubview ( fullRefundButton )
modeButtonStack . addArrangedSubview ( partialRefundButton )
cardView . addSubview ( amountGroupView )
amountGroupView . addSubview ( amountTitleLabel )
amountGroupView . addSubview ( amountContainerView )
amountContainerView . addSubview ( amountField )
cardView . addSubview ( remarkTitleLabel )
cardView . addSubview ( remarkContainerView )
remarkContainerView . addSubview ( remarkTextView )
remarkContainerView . addSubview ( remarkPlaceholderLabel )
cardView . addSubview ( actionRow )
actionRow . addArrangedSubview ( cancelButton )
actionRow . 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 . equalToSuperview ( ) . offset ( AppSpacing . sm )
make . leading . trailing . equalToSuperview ( ) . inset ( AppSpacing . md )
}
separatorView . snp . makeConstraints { make in
make . top . equalTo ( titleLabel . snp . bottom ) . offset ( AppSpacing . sm )
make . leading . trailing . equalToSuperview ( ) . inset ( AppSpacing . md )
make . height . equalTo ( 1 )
}
orderTitleLabel . snp . makeConstraints { make in
make . top . equalTo ( separatorView . snp . bottom ) . offset ( AppSpacing . md )
make . leading . trailing . equalToSuperview ( ) . inset ( AppSpacing . md )
}
orderNumberLabel . snp . makeConstraints { make in
make . top . equalTo ( orderTitleLabel . snp . bottom ) . offset ( AppSpacing . xs )
make . leading . trailing . equalToSuperview ( ) . inset ( AppSpacing . md )
}
refundModeTitleLabel . snp . makeConstraints { make in
make . top . equalTo ( orderNumberLabel . snp . bottom ) . offset ( AppSpacing . xl )
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 )
}
amountGroupView . snp . makeConstraints { make in
make . top . equalTo ( modeButtonStack . snp . bottom ) . offset ( AppSpacing . xl )
make . leading . trailing . equalToSuperview ( )
make . height . equalTo ( 73 )
}
amountTitleLabel . snp . makeConstraints { make in
make . top . leading . trailing . equalToSuperview ( ) . inset ( UIEdgeInsets ( top : 0 , left : AppSpacing . md , bottom : 0 , right : AppSpacing . md ) )
}
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 )
}
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 . 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 )
}
remarkTextView . snp . makeConstraints { make in
make . edges . equalToSuperview ( )
}
remarkPlaceholderLabel . snp . makeConstraints { make in
make . top . equalToSuperview ( ) . offset ( 16 )
make . leading . trailing . equalToSuperview ( ) . inset ( AppSpacing . sm )
}
actionRow . snp . makeConstraints { make in
make . top . equalTo ( remarkContainerView . snp . bottom ) . offset ( 20 )
make . trailing . equalToSuperview ( ) . inset ( AppSpacing . md )
make . bottom . equalToSuperview ( ) . inset ( AppSpacing . lg )
}
[ cancelButton , confirmButton ] . forEach { button in
button . snp . makeConstraints { make in
make . width . equalTo ( 84 )
make . height . equalTo ( 38 )
}
}
}
private func configureSectionLabel ( _ label : UILabel , text : String ) {
label . text = text
label . font = . systemFont ( ofSize : 16 )
label . textColor = UIColor ( hex : 0x4B5563 )
}
private func configureModeButton ( _ button : UIButton , title : String ) {
button . setTitle ( title , for : . normal )
button . titleLabel ? . font = . systemFont ( ofSize : 14 )
button . layer . cornerRadius = AppRadius . sm
button . layer . borderWidth = 1
}
private func configureActionButton ( _ button : UIButton , title : String , foreground : UIColor , background : UIColor ) {
button . setTitle ( title , for : . normal )
button . setTitleColor ( foreground , for : . normal )
button . titleLabel ? . font = . systemFont ( ofSize : 16 , weight : . medium )
button . backgroundColor = background
button . layer . cornerRadius = AppRadius . sm
}
private func applyMode ( animated : Bool ) {
let amountHidden = selectedMode = = . full
amountGroupView . isHidden = amountHidden
amountGroupView . snp . updateConstraints { make in
make . height . equalTo ( amountHidden ? 0 : 73 )
}
remarkTitleLabel . snp . remakeConstraints { make in
make . top . equalTo ( ( amountHidden ? modeButtonStack : amountGroupView ) . snp . bottom ) . offset ( AppSpacing . xl )
make . leading . trailing . equalToSuperview ( ) . inset ( AppSpacing . md )
}
updateModeButton ( fullRefundButton , selected : selectedMode = = . full )
updateModeButton ( partialRefundButton , selected : selectedMode = = . partial )
let changes = { self . layoutIfNeeded ( ) }
if animated {
UIView . animate ( withDuration : 0.2 , animations : changes )
} else {
changes ( )
}
}
private func updateModeButton ( _ button : UIButton , selected : Bool ) {
button . backgroundColor = selected ? AppColor . primary : . white
button . layer . borderColor = selected ? UIColor . clear . cgColor : UIColor ( hex : 0xB6BECA ) . cgColor
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 ) )
}
remarkPlaceholderLabel . isHidden = ! textView . text . isEmpty
}
@objc private func amountChanged ( ) {
let filtered = OrderRefundDialogValidation . filterMoneyInput (
amountField . text ? ? " " ,
maxLength : configuration . moneyMaxLength
)
if amountField . text != filtered {
amountField . text = filtered
}
}
@objc private func fullRefundTapped ( ) {
selectedMode = . full
amountField . text = configuration . kind = = . storeOrder ? configuration . maxRefundAmount : configuration . fullRefundAmount
applyMode ( animated : true )
}
@objc private func partialRefundTapped ( ) {
selectedMode = . partial
amountField . text = " "
applyMode ( animated : true )
amountField . becomeFirstResponder ( )
}
@objc private func cancelTapped ( ) {
dismiss ( )
}
@objc private func confirmTapped ( ) {
let result = OrderRefundDialogValidation . validate (
kind : configuration . kind ,
mode : selectedMode ,
amountText : amountField . text ? ? " " ,
reason : remarkTextView . text ? ? " " ,
fullRefundAmount : configuration . fullRefundAmount ,
maxRefundAmount : configuration . maxRefundAmount
)
switch result {
case . success ( let payload ) :
onConfirm ? ( payload )
dismiss ( )
case . failure ( let error ) :
onMessage ? ( error . message )
}
}
}