// // 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) snp.makeConstraints { make in make.height.equalTo(62) } 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 nameLabel = UILabel() private let editRemarkButton = UIButton(type: .system) private let phoneLabel = UILabel() private let callButton = UIButton(type: .system) private let bindTimeRow = CooperationInfoRowView(label: "绑定时间") private let commissionRow = CooperationInfoRowView(label: "分成比例") 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.lg nameLabel.font = .systemFont(ofSize: 16, weight: .bold) nameLabel.textColor = AppColor.textPrimary editRemarkButton.setTitle("修改备注", for: .normal) editRemarkButton.titleLabel?.font = .systemFont(ofSize: 13) editRemarkButton.setTitleColor(AppColor.primary, for: .normal) editRemarkButton.addTarget(self, action: #selector(editRemarkTapped), for: .touchUpInside) phoneLabel.font = .systemFont(ofSize: 13) phoneLabel.textColor = AppColor.textSecondary callButton.setImage(UIImage(systemName: "phone.fill"), for: .normal) callButton.tintColor = AppColor.primary callButton.backgroundColor = AppColor.primaryLight callButton.layer.cornerRadius = 15 callButton.addTarget(self, action: #selector(callTapped), for: .touchUpInside) editCommissionButton.setTitle("修改", for: .normal) editCommissionButton.titleLabel?.font = .systemFont(ofSize: 13) editCommissionButton.setTitleColor(AppColor.primary, for: .normal) editCommissionButton.addTarget(self, action: #selector(editCommissionTapped), for: .touchUpInside) contentView.addSubview(cardView) cardView.addSubview(nameLabel) cardView.addSubview(editRemarkButton) cardView.addSubview(phoneLabel) cardView.addSubview(callButton) cardView.addSubview(bindTimeRow) cardView.addSubview(commissionRow) cardView.addSubview(editCommissionButton) cardView.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: AppSpacing.md, bottom: AppSpacing.sm, right: AppSpacing.md)) } nameLabel.snp.makeConstraints { make in make.top.leading.equalToSuperview().offset(14) } editRemarkButton.snp.makeConstraints { make in make.centerY.equalTo(nameLabel) make.leading.equalTo(nameLabel.snp.trailing).offset(AppSpacing.xs) } phoneLabel.snp.makeConstraints { make in make.top.equalTo(nameLabel.snp.bottom).offset(10) make.leading.equalToSuperview().offset(14) } callButton.snp.makeConstraints { make in make.trailing.equalToSuperview().inset(14) make.centerY.equalTo(phoneLabel) make.size.equalTo(30) } bindTimeRow.snp.makeConstraints { make in make.top.equalTo(phoneLabel.snp.bottom).offset(10) make.leading.trailing.equalToSuperview().inset(14) } commissionRow.snp.makeConstraints { make in make.top.equalTo(bindTimeRow.snp.bottom).offset(10) make.leading.equalToSuperview().offset(14) make.bottom.equalToSuperview().inset(14) } editCommissionButton.snp.makeConstraints { make in make.centerY.equalTo(commissionRow) make.leading.equalTo(commissionRow.snp.trailing).offset(AppSpacing.xs) } } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configure(with acquirer: CooperativeSalerEntity) { nameLabel.text = acquirer.displayName.isEmpty ? "—" : acquirer.displayName phoneLabel.text = "手机号 \(CooperationOrderPhoneMask.mask(acquirer.displayPhone))" bindTimeRow.setValue(acquirer.displayBindTime.isEmpty ? "—" : acquirer.displayBindTime) commissionRow.setValue(acquirer.displayCommissionRate) } @objc private func editRemarkTapped() { onEditRemark?() } @objc private func editCommissionTapped() { onEditCommission?() } @objc private func callTapped() { onCall?() } } /// 标签-值行。 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))" } }