Files
suixinkan_uikit/suixinkan/UI/CooperationOrder/Views/CooperationOrderViews.swift

1044 lines
41 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// CooperationOrderViews.swift
// suixinkan
//
import Kingfisher
import SnapKit
import UIKit
/// Tab
final class CooperationOrderTabBar: UIView {
var onTabSelected: ((Int) -> Void)?
private let stack = UIStackView()
private var buttons: [UIButton] = []
private var selectedIndex = 0
private let indicator = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
stack.axis = .horizontal
stack.distribution = .fillEqually
addSubview(stack)
addSubview(indicator)
indicator.backgroundColor = AppColor.primary
indicator.layer.cornerRadius = 1.5
["获客员线索", "获客订单"].enumerated().forEach { index, title in
let button = UIButton(type: .system)
button.setTitle(title, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 15, weight: index == 0 ? .bold : .regular)
button.setTitleColor(index == 0 ? AppColor.primary : AppColor.textSecondary, for: .normal)
button.tag = index
button.addTarget(self, action: #selector(tabTapped(_:)), for: .touchUpInside)
buttons.append(button)
stack.addArrangedSubview(button)
}
stack.snp.makeConstraints { make in
make.edges.equalToSuperview()
make.height.equalTo(48)
}
layoutIndicator(animated: false)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setSelectedTab(_ index: Int) {
guard selectedIndex != index else { return }
selectedIndex = index
updateButtonStyles()
layoutIndicator(animated: true)
}
@objc private func tabTapped(_ sender: UIButton) {
setSelectedTab(sender.tag)
onTabSelected?(sender.tag)
}
private func updateButtonStyles() {
buttons.enumerated().forEach { index, button in
let selected = index == selectedIndex
button.setTitleColor(selected ? AppColor.primary : AppColor.textSecondary, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 15, weight: selected ? .bold : .regular)
}
}
private func layoutIndicator(animated: Bool) {
guard !buttons.isEmpty else { return }
let button = buttons[selectedIndex]
let apply = {
self.indicator.snp.remakeConstraints { make in
make.bottom.equalToSuperview()
make.height.equalTo(3)
make.width.equalTo(button.snp.width).multipliedBy(0.5)
make.centerX.equalTo(button)
}
self.layoutIfNeeded()
}
if animated {
UIView.animate(withDuration: 0.2, animations: apply)
} else {
apply()
}
}
}
///
final class CooperationOrderSearchBar: UIView, UITextFieldDelegate {
var onSearchTextChange: ((String) -> Void)?
var onSearch: (() -> Void)?
let textField = UITextField()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
let container = UIView()
container.backgroundColor = AppColor.inputBackground
container.layer.cornerRadius = AppRadius.sm
let icon = UIImageView(image: UIImage(systemName: "magnifyingglass"))
icon.tintColor = AppColor.textTertiary
textField.placeholder = "名称搜索"
textField.font = .systemFont(ofSize: 14)
textField.textColor = AppColor.textPrimary
textField.returnKeyType = .search
textField.delegate = self
textField.addTarget(self, action: #selector(textChanged), for: .editingChanged)
addSubview(container)
container.addSubview(icon)
container.addSubview(textField)
container.snp.makeConstraints { make in
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
make.centerY.equalToSuperview()
make.height.equalTo(38)
}
icon.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(AppSpacing.sm)
make.centerY.equalToSuperview()
make.size.equalTo(18)
}
textField.snp.makeConstraints { make in
make.leading.equalTo(icon.snp.trailing).offset(AppSpacing.xs)
make.trailing.equalToSuperview().inset(AppSpacing.sm)
make.centerY.equalToSuperview()
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func textChanged() {
onSearchTextChange?(textField.text ?? "")
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
onSearch?()
return true
}
}
/// 线 Cell
final class ReferralLeadCell: UITableViewCell {
static let reuseIdentifier = "ReferralLeadCell"
var onImageTap: ((Int, [String]) -> Void)?
private let cardView = UIView()
private let phoneLabel = UILabel()
private let phoneValueLabel = UILabel()
private let timeLabel = UILabel()
private let salerRow = CooperationInfoRowView(label: "获客员")
private let remarkTitleLabel = UILabel()
private let remarkLabel = UILabel()
private let imageTitleLabel = UILabel()
private let imageStack = UIStackView()
private let emptyImageLabel = UILabel()
private var imageURLs: [String] = []
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none
backgroundColor = .clear
contentView.backgroundColor = .clear
cardView.backgroundColor = .white
cardView.layer.cornerRadius = AppRadius.lg
cardView.layer.shadowColor = UIColor.black.cgColor
cardView.layer.shadowOpacity = 0.04
cardView.layer.shadowOffset = CGSize(width: 0, height: 1)
cardView.layer.shadowRadius = 2
phoneLabel.text = "用户手机号"
phoneLabel.font = .systemFont(ofSize: 13)
phoneLabel.textColor = AppColor.textTertiary
phoneValueLabel.font = .systemFont(ofSize: 15, weight: .medium)
phoneValueLabel.textColor = AppColor.textPrimary
timeLabel.font = .systemFont(ofSize: 11)
timeLabel.textColor = AppColor.textTertiary
timeLabel.textAlignment = .right
remarkTitleLabel.text = "备注"
remarkTitleLabel.font = .systemFont(ofSize: 13)
remarkTitleLabel.textColor = AppColor.textTertiary
remarkLabel.font = .systemFont(ofSize: 14)
remarkLabel.textColor = AppColor.textPrimary
remarkLabel.numberOfLines = 0
imageTitleLabel.text = "图片"
imageTitleLabel.font = .systemFont(ofSize: 13)
imageTitleLabel.textColor = AppColor.textTertiary
imageStack.axis = .horizontal
imageStack.spacing = AppSpacing.xs
imageStack.distribution = .fillEqually
emptyImageLabel.text = "暂无图片"
emptyImageLabel.font = .systemFont(ofSize: 13)
emptyImageLabel.textColor = AppColor.textTertiary
emptyImageLabel.textAlignment = .center
emptyImageLabel.backgroundColor = UIColor(hex: 0xF7F8FA)
emptyImageLabel.layer.cornerRadius = AppRadius.sm
emptyImageLabel.clipsToBounds = true
emptyImageLabel.isHidden = true
contentView.addSubview(cardView)
cardView.addSubview(phoneLabel)
cardView.addSubview(phoneValueLabel)
cardView.addSubview(timeLabel)
cardView.addSubview(salerRow)
cardView.addSubview(remarkTitleLabel)
cardView.addSubview(remarkLabel)
cardView.addSubview(imageTitleLabel)
cardView.addSubview(imageStack)
cardView.addSubview(emptyImageLabel)
cardView.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: AppSpacing.md, bottom: AppSpacing.sm, right: AppSpacing.md))
}
phoneLabel.snp.makeConstraints { make in
make.top.leading.equalToSuperview().offset(14)
}
phoneValueLabel.snp.makeConstraints { make in
make.leading.equalTo(phoneLabel.snp.trailing).offset(14)
make.centerY.equalTo(phoneLabel)
}
timeLabel.snp.makeConstraints { make in
make.trailing.equalToSuperview().inset(14)
make.centerY.equalTo(phoneLabel)
make.leading.greaterThanOrEqualTo(phoneValueLabel.snp.trailing).offset(AppSpacing.xs)
}
salerRow.snp.makeConstraints { make in
make.top.equalTo(phoneLabel.snp.bottom).offset(10)
make.leading.trailing.equalToSuperview().inset(14)
}
remarkTitleLabel.snp.makeConstraints { make in
make.top.equalTo(salerRow.snp.bottom).offset(10)
make.leading.equalToSuperview().offset(14)
}
remarkLabel.snp.makeConstraints { make in
make.top.equalTo(remarkTitleLabel.snp.bottom).offset(4)
make.leading.trailing.equalToSuperview().inset(14)
}
imageTitleLabel.snp.makeConstraints { make in
make.top.equalTo(remarkLabel.snp.bottom).offset(10)
make.leading.equalToSuperview().offset(14)
}
imageStack.snp.makeConstraints { make in
make.top.equalTo(imageTitleLabel.snp.bottom).offset(4)
make.leading.trailing.equalToSuperview().inset(14)
make.height.equalTo(imageStack.snp.width).multipliedBy(1.0 / 3.0).offset(-AppSpacing.xs * 2 / 3)
make.bottom.equalToSuperview().inset(14)
}
emptyImageLabel.snp.makeConstraints { make in
make.edges.equalTo(imageStack)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(with lead: ReferralLeadEntity) {
phoneValueLabel.text = lead.displayPhone.isEmpty ? "" : lead.displayPhone
timeLabel.text = lead.createdAt
salerRow.setValue(lead.displaySalerName.isEmpty ? "" : lead.displaySalerName)
remarkLabel.text = lead.remark.isEmpty ? "暂无备注" : lead.remark
imageURLs = lead.displayImages
imageStack.arrangedSubviews.forEach { $0.removeFromSuperview() }
if imageURLs.isEmpty {
emptyImageLabel.isHidden = false
imageStack.isHidden = true
} else {
emptyImageLabel.isHidden = true
imageStack.isHidden = false
imageURLs.prefix(3).enumerated().forEach { index, url in
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.cornerRadius = AppRadius.sm
imageView.backgroundColor = UIColor(hex: 0xF4F4F4)
imageView.isUserInteractionEnabled = true
imageView.loadRemoteImage(urlString: url)
imageView.tag = index
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(imageTapped(_:))))
imageStack.addArrangedSubview(imageView)
}
let missing = 3 - imageStack.arrangedSubviews.count
if missing > 0 {
(0..<missing).forEach { _ in
let spacer = UIView()
imageStack.addArrangedSubview(spacer)
}
}
}
}
@objc private func imageTapped(_ gesture: UITapGestureRecognizer) {
guard let index = gesture.view?.tag else { return }
onImageTap?(index, imageURLs)
}
}
/// Cell
final class AcquisitionOrderCell: UITableViewCell {
static let reuseIdentifier = "AcquisitionOrderCell"
private let cardView = UIView()
private let titleLabel = UILabel()
private let typeTag = UILabel()
private let orderNumberLabel = UILabel()
private let partnerRow = CooperationInfoRowView(label: "合作人员")
private let timeRow = CooperationInfoRowView(label: "下单时间")
private let customerLabel = UILabel()
private let amountLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none
backgroundColor = .clear
contentView.backgroundColor = .clear
cardView.backgroundColor = .white
cardView.layer.cornerRadius = AppRadius.lg
cardView.layer.shadowColor = UIColor.black.cgColor
cardView.layer.shadowOpacity = 0.04
cardView.layer.shadowOffset = CGSize(width: 0, height: 1)
cardView.layer.shadowRadius = 2
titleLabel.font = .systemFont(ofSize: 16, weight: .bold)
titleLabel.textColor = AppColor.textPrimary
typeTag.font = .systemFont(ofSize: 11)
typeTag.textColor = AppColor.primary
typeTag.backgroundColor = AppColor.primaryLight
typeTag.layer.cornerRadius = AppRadius.xs
typeTag.clipsToBounds = true
typeTag.textAlignment = .center
orderNumberLabel.font = .systemFont(ofSize: 13)
orderNumberLabel.textColor = AppColor.textSecondary
customerLabel.font = .systemFont(ofSize: 12)
customerLabel.textColor = AppColor.textTertiary
amountLabel.font = .systemFont(ofSize: 17, weight: .bold)
amountLabel.textColor = AppColor.primary
amountLabel.textAlignment = .right
contentView.addSubview(cardView)
cardView.addSubview(titleLabel)
cardView.addSubview(typeTag)
cardView.addSubview(orderNumberLabel)
cardView.addSubview(partnerRow)
cardView.addSubview(timeRow)
cardView.addSubview(customerLabel)
cardView.addSubview(amountLabel)
cardView.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: AppSpacing.md, bottom: AppSpacing.sm, right: AppSpacing.md))
}
titleLabel.snp.makeConstraints { make in
make.top.leading.equalToSuperview().offset(14)
make.trailing.lessThanOrEqualToSuperview().inset(14)
}
typeTag.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp.bottom).offset(10)
make.leading.equalToSuperview().offset(14)
make.height.equalTo(22)
}
orderNumberLabel.snp.makeConstraints { make in
make.centerY.equalTo(typeTag)
make.leading.equalTo(typeTag.snp.trailing).offset(AppSpacing.xs)
make.trailing.lessThanOrEqualToSuperview().inset(14)
}
partnerRow.snp.makeConstraints { make in
make.top.equalTo(typeTag.snp.bottom).offset(10)
make.leading.trailing.equalToSuperview().inset(14)
}
timeRow.snp.makeConstraints { make in
make.top.equalTo(partnerRow.snp.bottom).offset(10)
make.leading.trailing.equalToSuperview().inset(14)
}
customerLabel.snp.makeConstraints { make in
make.top.equalTo(timeRow.snp.bottom).offset(10)
make.leading.equalToSuperview().offset(14)
make.bottom.equalToSuperview().inset(14)
}
amountLabel.snp.makeConstraints { make in
make.centerY.equalTo(customerLabel)
make.trailing.equalToSuperview().inset(14)
make.leading.greaterThanOrEqualTo(customerLabel.snp.trailing).offset(AppSpacing.xs)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(with order: AcquisitionOrderEntity) {
titleLabel.text = order.projectName.isEmpty ? "" : order.projectName
if order.orderTypeLabel.isEmpty {
typeTag.isHidden = true
orderNumberLabel.snp.remakeConstraints { make in
make.top.equalTo(titleLabel.snp.bottom).offset(10)
make.leading.equalToSuperview().offset(14)
make.trailing.lessThanOrEqualToSuperview().inset(14)
}
} else {
typeTag.isHidden = false
typeTag.text = " \(order.orderTypeLabel) "
orderNumberLabel.snp.remakeConstraints { make in
make.centerY.equalTo(typeTag)
make.leading.equalTo(typeTag.snp.trailing).offset(AppSpacing.xs)
make.trailing.lessThanOrEqualToSuperview().inset(14)
}
}
orderNumberLabel.text = "订单号 \(order.orderNumber)"
partnerRow.setValue(order.displayPartner.isEmpty ? "暂未绑定" : order.displayPartner)
timeRow.setValue(order.createdAt.isEmpty ? "" : order.createdAt)
customerLabel.text = "客户 \(order.displayCustomerPhone.isEmpty ? "" : order.displayCustomerPhone)"
amountLabel.text = "¥\(order.displayAmount)"
}
}
/// Cell
final class CooperationAcquirerCell: UITableViewCell {
static let reuseIdentifier = "CooperationAcquirerCell"
var onEditRemark: (() -> Void)?
var onEditCommission: (() -> Void)?
var onViewCommissionLogs: (() -> Void)?
var onCall: (() -> Void)?
private let cardView = UIView()
private let avatarContainer = UIView()
private let avatarIconView = UIImageView(image: UIImage(systemName: "person.fill"))
private let nameValueLabel = UILabel()
private let phoneValueLabel = UILabel()
private let callButton = UIButton(type: .system)
private let commissionContainer = UIView()
private let commissionTitleLabel = UILabel()
private let commissionValueLabel = UILabel()
private let bindTimeIconView = UIImageView(image: UIImage(systemName: "calendar"))
private let bindTimeValueLabel = UILabel()
private let dividerView = UIView()
private let editRemarkButton = UIButton(type: .system)
private let editCommissionButton = UIButton(type: .system)
private let commissionLogButton = UIButton(type: .system)
private let actionStack = UIStackView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none
backgroundColor = .clear
contentView.backgroundColor = .clear
cardView.backgroundColor = .white
cardView.layer.cornerRadius = AppRadius.xl
cardView.layer.borderWidth = 0.5
cardView.layer.borderColor = AppColor.cardOutline.cgColor
cardView.layer.shadowColor = UIColor.black.cgColor
cardView.layer.shadowOpacity = 0.04
cardView.layer.shadowOffset = CGSize(width: 0, height: 3)
cardView.layer.shadowRadius = 8
cardView.accessibilityIdentifier = "cooperation_acquirer_card"
avatarContainer.backgroundColor = AppColor.primary.withAlphaComponent(0.08)
avatarContainer.layer.cornerRadius = 24
avatarContainer.clipsToBounds = true
avatarIconView.tintColor = AppColor.primary
avatarIconView.contentMode = .scaleAspectFit
nameValueLabel.font = .systemFont(ofSize: 17, weight: .semibold)
nameValueLabel.textColor = AppColor.textPrimary
nameValueLabel.numberOfLines = 1
nameValueLabel.lineBreakMode = .byTruncatingTail
nameValueLabel.accessibilityIdentifier = "cooperation_acquirer_name"
phoneValueLabel.font = .systemFont(ofSize: 14)
phoneValueLabel.textColor = AppColor.textTertiary
phoneValueLabel.numberOfLines = 1
phoneValueLabel.lineBreakMode = .byTruncatingTail
phoneValueLabel.accessibilityIdentifier = "cooperation_acquirer_phone"
callButton.setImage(UIImage(systemName: "phone.fill"), for: .normal)
callButton.tintColor = AppColor.primary
callButton.backgroundColor = AppColor.primaryLight
callButton.layer.cornerRadius = AppSpacing.minTouchTarget / 2
callButton.layer.borderWidth = 0.5
callButton.layer.borderColor = AppColor.primary.withAlphaComponent(0.12).cgColor
callButton.imageView?.contentMode = .scaleAspectFit
callButton.accessibilityLabel = "拨打获客员电话"
callButton.accessibilityIdentifier = "cooperation_acquirer_call"
callButton.addTarget(self, action: #selector(callTapped), for: .touchUpInside)
commissionContainer.backgroundColor = AppColor.primary.withAlphaComponent(0.06)
commissionContainer.layer.cornerRadius = AppRadius.lg
commissionTitleLabel.text = "分成比例"
commissionTitleLabel.font = .systemFont(ofSize: 12)
commissionTitleLabel.textColor = AppColor.textTertiary
commissionValueLabel.font = .systemFont(ofSize: 24, weight: .bold)
commissionValueLabel.textColor = AppColor.primary
commissionValueLabel.numberOfLines = 1
commissionValueLabel.adjustsFontSizeToFitWidth = true
commissionValueLabel.minimumScaleFactor = 0.75
commissionValueLabel.accessibilityIdentifier = "cooperation_acquirer_commission_rate"
bindTimeIconView.tintColor = AppColor.textTertiary
bindTimeIconView.contentMode = .scaleAspectFit
bindTimeValueLabel.font = .systemFont(ofSize: 13)
bindTimeValueLabel.textColor = AppColor.textTertiary
bindTimeValueLabel.numberOfLines = 2
bindTimeValueLabel.lineBreakMode = .byTruncatingTail
bindTimeValueLabel.accessibilityIdentifier = "cooperation_acquirer_bind_time"
dividerView.backgroundColor = AppColor.cardOutline
configureActionButton(
editRemarkButton,
title: "修改备注",
isPrimary: false,
accessibilityIdentifier: "cooperation_acquirer_edit_remark"
)
editRemarkButton.addTarget(self, action: #selector(editRemarkTapped), for: .touchUpInside)
configureActionButton(
commissionLogButton,
title: "修改记录",
isPrimary: false,
accessibilityIdentifier: "cooperation_acquirer_commission_logs"
)
commissionLogButton.accessibilityLabel = "查看分成比例修改记录"
commissionLogButton.addTarget(self, action: #selector(commissionLogTapped), for: .touchUpInside)
configureActionButton(
editCommissionButton,
title: "修改比例",
isPrimary: true,
accessibilityIdentifier: "cooperation_acquirer_edit_commission"
)
editCommissionButton.addTarget(self, action: #selector(editCommissionTapped), for: .touchUpInside)
actionStack.axis = .horizontal
actionStack.alignment = .fill
actionStack.distribution = .fillEqually
actionStack.spacing = AppSpacing.xs
actionStack.addArrangedSubview(editRemarkButton)
actionStack.addArrangedSubview(commissionLogButton)
actionStack.addArrangedSubview(editCommissionButton)
contentView.addSubview(cardView)
cardView.addSubview(avatarContainer)
avatarContainer.addSubview(avatarIconView)
cardView.addSubview(nameValueLabel)
cardView.addSubview(phoneValueLabel)
cardView.addSubview(callButton)
cardView.addSubview(commissionContainer)
commissionContainer.addSubview(commissionTitleLabel)
commissionContainer.addSubview(commissionValueLabel)
cardView.addSubview(bindTimeIconView)
cardView.addSubview(bindTimeValueLabel)
cardView.addSubview(dividerView)
cardView.addSubview(actionStack)
cardView.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(
UIEdgeInsets(top: 0, left: AppSpacing.md, bottom: AppSpacing.sm, right: AppSpacing.md)
)
}
avatarContainer.snp.makeConstraints { make in
make.top.leading.equalToSuperview().inset(AppSpacing.md)
make.size.equalTo(48)
}
avatarIconView.snp.makeConstraints { make in
make.center.equalToSuperview()
make.size.equalTo(24)
}
nameValueLabel.snp.makeConstraints { make in
make.top.equalTo(avatarContainer).offset(2)
make.leading.equalTo(avatarContainer.snp.trailing).offset(AppSpacing.sm)
make.trailing.lessThanOrEqualTo(callButton.snp.leading).offset(-AppSpacing.sm)
}
phoneValueLabel.snp.makeConstraints { make in
make.top.equalTo(nameValueLabel.snp.bottom).offset(AppSpacing.xxs)
make.leading.equalTo(nameValueLabel)
make.trailing.lessThanOrEqualTo(callButton.snp.leading).offset(-AppSpacing.sm)
}
callButton.snp.makeConstraints { make in
make.top.trailing.equalToSuperview().inset(AppSpacing.md)
make.size.equalTo(AppSpacing.minTouchTarget)
}
commissionContainer.snp.makeConstraints { make in
make.top.equalTo(avatarContainer.snp.bottom).offset(14)
make.leading.equalToSuperview().inset(AppSpacing.md)
make.width.equalTo(120)
make.height.equalTo(64)
}
commissionTitleLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(AppSpacing.xs)
make.centerX.equalToSuperview()
}
commissionValueLabel.snp.makeConstraints { make in
make.top.equalTo(commissionTitleLabel.snp.bottom).offset(2)
make.centerX.equalToSuperview()
make.leading.greaterThanOrEqualToSuperview().offset(AppSpacing.xs)
make.trailing.lessThanOrEqualToSuperview().inset(AppSpacing.xs)
}
bindTimeIconView.snp.makeConstraints { make in
make.leading.equalTo(commissionContainer.snp.trailing).offset(AppSpacing.md)
make.centerY.equalTo(commissionContainer)
make.size.equalTo(18)
}
bindTimeValueLabel.snp.makeConstraints { make in
make.leading.equalTo(bindTimeIconView.snp.trailing).offset(AppSpacing.xs)
make.trailing.equalToSuperview().inset(AppSpacing.md)
make.centerY.equalTo(bindTimeIconView)
}
dividerView.snp.makeConstraints { make in
make.top.equalTo(commissionContainer.snp.bottom).offset(14)
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
make.height.equalTo(0.5)
}
actionStack.snp.makeConstraints { make in
make.top.equalTo(dividerView.snp.bottom).offset(AppSpacing.sm)
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
make.height.equalTo(AppSpacing.minTouchTarget)
make.bottom.equalToSuperview().inset(14)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareForReuse() {
super.prepareForReuse()
onEditRemark = nil
onEditCommission = nil
onViewCommissionLogs = nil
onCall = nil
}
func configure(with acquirer: CooperativeSalerEntity) {
nameValueLabel.text = acquirer.displayName.isEmpty ? "" : acquirer.displayName
phoneValueLabel.text = acquirer.displayPhone.isEmpty ? "" : CooperationOrderPhoneMask.mask(acquirer.displayPhone)
let bindTime = acquirer.displayBindTime.isEmpty ? "" : Self.minutePrecisionTime(acquirer.displayBindTime)
bindTimeValueLabel.text = "绑定于 \(bindTime)"
commissionValueLabel.text = acquirer.displayCommissionRate.isEmpty ? "" : acquirer.displayCommissionRate
}
private func configureActionButton(
_ button: UIButton,
title: String,
isPrimary: Bool,
accessibilityIdentifier: String
) {
var configuration = UIButton.Configuration.plain()
configuration.title = title
configuration.baseForegroundColor = isPrimary ? AppColor.primary : UIColor(hex: 0x5F6673)
configuration.background.backgroundColor = isPrimary ? AppColor.primaryLight : .white
configuration.background.strokeColor = isPrimary
? AppColor.primary.withAlphaComponent(0.35)
: UIColor(hex: 0xD9DEE7)
configuration.background.strokeWidth = 1
configuration.background.cornerRadius = AppRadius.md
configuration.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 6, bottom: 0, trailing: 6)
configuration.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { attributes in
var updated = attributes
updated.font = .systemFont(ofSize: 12, weight: .medium)
return updated
}
button.configuration = configuration
button.accessibilityIdentifier = accessibilityIdentifier
}
private static func minutePrecisionTime(_ value: String) -> String {
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
guard trimmed.count == 19, trimmed.dropFirst(16).first == ":" else { return trimmed }
return String(trimmed.prefix(16))
}
@objc private func editRemarkTapped() { onEditRemark?() }
@objc private func editCommissionTapped() { onEditCommission?() }
@objc private func commissionLogTapped() { onViewCommissionLogs?() }
@objc private func callTapped() { onCall?() }
}
/// Android `CommissionRateEditDialog`
final class CooperationCommissionRateDialogView: KeyboardAvoidingDialogView {
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)
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
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()
rateField.focus()
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() {
keyboardAvoidingContentView = cardView
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)
keyboardAvoidingCenterYConstraint = 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
}
@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()
private let valueLabel = UILabel()
init(label: String) {
super.init(frame: .zero)
titleLabel.text = label
titleLabel.font = .systemFont(ofSize: 13)
titleLabel.textColor = AppColor.textTertiary
valueLabel.font = .systemFont(ofSize: 13)
valueLabel.textColor = AppColor.textSecondary
valueLabel.numberOfLines = 0
addSubview(titleLabel)
addSubview(valueLabel)
titleLabel.snp.makeConstraints { make in
make.leading.top.bottom.equalToSuperview()
make.width.equalTo(72)
}
valueLabel.snp.makeConstraints { make in
make.leading.equalTo(titleLabel.snp.trailing).offset(12)
make.trailing.top.bottom.equalToSuperview()
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setValue(_ value: String) {
valueLabel.text = value
}
}
enum CooperationOrderPhoneMask {
static func mask(_ phone: String) -> String {
let digits = phone.filter(\.isNumber)
guard digits.count >= 7 else { return phone }
return "\(digits.prefix(3))****\(digits.suffix(4))"
}
}