// // 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.. Void)? var onEditCommission: (() -> Void)? var onCall: (() -> Void)? private let cardView = UIView() 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 phoneTitleLabel = UILabel() private let phoneValueLabel = UILabel() private let callButton = UIButton(type: .system) 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?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none backgroundColor = .clear contentView.backgroundColor = .clear cardView.backgroundColor = .white 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 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.setConfigurationContentInsets( NSDirectionalEdgeInsets(top: 16, leading: 14, bottom: 16, trailing: 14) ) editRemarkButton.addTarget(self, action: #selector(editRemarkTapped), for: .touchUpInside) 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.titleLabel?.font = .systemFont(ofSize: 13) editCommissionButton.setTitleColor(AppColor.primary, for: .normal) editCommissionButton.setConfigurationContentInsets( NSDirectionalEdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8) ) editCommissionButton.addTarget(self, action: #selector(editCommissionTapped), for: .touchUpInside) contentView.addSubview(cardView) cardView.addSubview(avatarContainer) avatarContainer.addSubview(avatarIconView) cardView.addSubview(nameTitleLabel) cardView.addSubview(nameValueLabel) cardView.addSubview(editRemarkButton) cardView.addSubview(phoneTitleLabel) cardView.addSubview(phoneValueLabel) cardView.addSubview(callButton) 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: 14, bottom: AppSpacing.sm, right: 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.top.trailing.equalToSuperview() } 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(phoneTitleLabel) make.size.equalTo(30) } bindTimeTitleLabel.snp.makeConstraints { make in make.top.equalTo(phoneTitleLabel.snp.bottom).offset(AppSpacing.xs) make.leading.equalTo(nameTitleLabel) } 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(commissionTitleLabel) make.trailing.equalToSuperview().inset(AppSpacing.xs) } } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func prepareForReuse() { super.prepareForReuse() onEditRemark = nil onEditCommission = nil onCall = nil } func configure(with acquirer: CooperativeSalerEntity) { 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?() } @objc private func editCommissionTapped() { onEditCommission?() } @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))" } }