// // OrderSourcePickerViewController.swift // suixinkan // import SnapKit import UIKit /// 订单来源选择 Bottom Sheet,对齐 Android `OrderSourcePicker` 弹层。 final class OrderSourcePickerViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { private enum MenuLevel { case person case lead } private let orderNumber: String private let viewModel: OrderListViewModel private let onPersonSelected: (OrderSourcePerson) -> Void private let onLeadBound: (OrderSourceSelection) -> Void private let onCallPhone: (String) -> Void private let onPreviewImages: ([String], Int) -> Void private var level: MenuLevel = .person private var pendingPerson: OrderSourcePerson? private let headerStack = UIStackView() private let titleLabel = UILabel() private let subtitleLabel = UILabel() private let leadHeaderRow = UIStackView() private let backButton = UIButton(type: .system) private let leadTitleLabel = UILabel() private let leadPhoneButton = UIButton(type: .system) private let leadCallButton = UIButton(type: .system) private let tableView = UITableView(frame: .zero, style: .plain) private let loadingIndicator = UIActivityIndicatorView(style: .medium) private let emptyLabel = UILabel() private var referralOrder: BoundReferralOrderEntity? init( orderNumber: String, viewModel: OrderListViewModel, referralOrder: BoundReferralOrderEntity?, onPersonSelected: @escaping (OrderSourcePerson) -> Void, onLeadBound: @escaping (OrderSourceSelection) -> Void, onCallPhone: @escaping (String) -> Void, onPreviewImages: @escaping ([String], Int) -> Void ) { self.orderNumber = orderNumber self.viewModel = viewModel self.onPersonSelected = onPersonSelected self.onLeadBound = onLeadBound self.onCallPhone = onCallPhone self.onPreviewImages = onPreviewImages self.referralOrder = referralOrder self.pendingPerson = viewModel.orderSourceSelection( orderNumber: orderNumber, referralOrder: referralOrder )?.person super.init(nibName: nil, bundle: nil) } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white setupUI() applyViewModel() } func reloadFromViewModel() { applyViewModel() } private func setupUI() { titleLabel.font = .systemFont(ofSize: 18, weight: .bold) titleLabel.textColor = AppColor.text333 titleLabel.text = "选择获客员" subtitleLabel.font = .systemFont(ofSize: 13) subtitleLabel.textColor = AppColor.text666 subtitleLabel.text = "选择人员后,再选择该人员创建的带客单" subtitleLabel.numberOfLines = 0 backButton.setImage(UIImage(systemName: "chevron.left"), for: .normal) backButton.tintColor = AppColor.text333 backButton.addTarget(self, action: #selector(backTapped), for: .touchUpInside) leadTitleLabel.font = .systemFont(ofSize: 18, weight: .bold) leadTitleLabel.textColor = AppColor.text333 leadTitleLabel.numberOfLines = 2 leadPhoneButton.titleLabel?.font = .systemFont(ofSize: 13) leadPhoneButton.setTitleColor(AppColor.primary, for: .normal) leadPhoneButton.contentHorizontalAlignment = .leading leadPhoneButton.addTarget(self, action: #selector(leadPhoneTapped), for: .touchUpInside) leadCallButton.backgroundColor = OrderTokens.phoneIconBackground leadCallButton.layer.cornerRadius = 16 leadCallButton.tintColor = AppColor.primary leadCallButton.setImage(UIImage(systemName: "phone.fill"), for: .normal) leadCallButton.addTarget(self, action: #selector(leadPhoneTapped), for: .touchUpInside) leadHeaderRow.axis = .horizontal leadHeaderRow.alignment = .top leadHeaderRow.spacing = 10 leadHeaderRow.isHidden = true let leadTextStack = UIStackView(arrangedSubviews: [leadTitleLabel, leadPhoneButton]) leadTextStack.axis = .vertical leadTextStack.spacing = 3 leadHeaderRow.addArrangedSubview(backButton) leadHeaderRow.addArrangedSubview(leadTextStack) leadHeaderRow.addArrangedSubview(leadCallButton) headerStack.axis = .vertical headerStack.spacing = 5 headerStack.addArrangedSubview(titleLabel) headerStack.addArrangedSubview(subtitleLabel) headerStack.addArrangedSubview(leadHeaderRow) tableView.separatorStyle = .none tableView.backgroundColor = .white tableView.dataSource = self tableView.delegate = self tableView.register(OrderSourcePersonCell.self, forCellReuseIdentifier: OrderSourcePersonCell.reuseIdentifier) tableView.register(OrderSourceLeadCell.self, forCellReuseIdentifier: OrderSourceLeadCell.reuseIdentifier) loadingIndicator.hidesWhenStopped = true emptyLabel.font = .systemFont(ofSize: 14) emptyLabel.textColor = AppColor.text666 emptyLabel.textAlignment = .center emptyLabel.numberOfLines = 0 view.addSubview(headerStack) view.addSubview(tableView) view.addSubview(loadingIndicator) view.addSubview(emptyLabel) headerStack.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide).offset(AppSpacing.md) make.leading.trailing.equalToSuperview().inset(AppSpacing.md) } backButton.snp.makeConstraints { make in make.width.height.equalTo(28) } leadCallButton.snp.makeConstraints { make in make.width.height.equalTo(32) } tableView.snp.makeConstraints { make in make.top.equalTo(headerStack.snp.bottom).offset(14) make.leading.trailing.bottom.equalToSuperview() } loadingIndicator.snp.makeConstraints { make in make.center.equalTo(tableView) } emptyLabel.snp.makeConstraints { make in make.center.equalTo(tableView) make.leading.trailing.equalTo(tableView).inset(AppSpacing.lg) } } private func applyViewModel() { let currentSelection = viewModel.orderSourceSelection( orderNumber: orderNumber, referralOrder: referralOrder ) titleLabel.isHidden = level == .lead subtitleLabel.isHidden = level == .lead leadHeaderRow.isHidden = level == .person if level == .lead, let person = pendingPerson { leadTitleLabel.text = "\(person.name)创建的带客单" let phone = person.phone.trimmingCharacters(in: .whitespacesAndNewlines) leadPhoneButton.setTitle(phone, for: .normal) leadPhoneButton.isHidden = phone.isEmpty leadCallButton.isHidden = phone.isEmpty } let isLoadingPeople = viewModel.isLoadingOrderSourcePeople && level == .person let isLoadingLeads = viewModel.loadingOrderSourceLeadsFor != nil && level == .lead if isLoadingPeople || isLoadingLeads { loadingIndicator.startAnimating() tableView.isHidden = true emptyLabel.isHidden = true } else { loadingIndicator.stopAnimating() tableView.isHidden = false let isEmpty: Bool switch level { case .person: isEmpty = viewModel.orderSourcePeople.isEmpty case .lead: isEmpty = leadsForPendingPerson.isEmpty } emptyLabel.isHidden = !isEmpty emptyLabel.text = level == .person ? "暂无合作获客员" : "暂无可绑定带客单" tableView.reloadData() } _ = currentSelection } private var leadsForPendingPerson: [OrderSourceLead] { guard let person = pendingPerson else { return [] } return viewModel.orderSourceLeads[person.key] ?? [] } @objc private func backTapped() { level = .person applyViewModel() } @objc private func leadPhoneTapped() { guard let phone = pendingPerson?.phone.trimmingCharacters(in: .whitespacesAndNewlines), !phone.isEmpty else { return } onCallPhone(phone) } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch level { case .person: return viewModel.orderSourcePeople.count case .lead: return leadsForPendingPerson.count } } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let currentSelection = viewModel.orderSourceSelection( orderNumber: orderNumber, referralOrder: referralOrder ) switch level { case .person: let cell = tableView.dequeueReusableCell( withIdentifier: OrderSourcePersonCell.reuseIdentifier, for: indexPath ) as! OrderSourcePersonCell let person = viewModel.orderSourcePeople[indexPath.row] cell.configure( person: person, selected: currentSelection?.person.key == person.key ) cell.onCallPhone = onCallPhone return cell case .lead: let cell = tableView.dequeueReusableCell( withIdentifier: OrderSourceLeadCell.reuseIdentifier, for: indexPath ) as! OrderSourceLeadCell let lead = leadsForPendingPerson[indexPath.row] let selected = currentSelection?.person.key == pendingPerson?.key && currentSelection?.lead.key == lead.key cell.configure(lead: lead, selected: selected) cell.onCallPhone = onCallPhone cell.onImageTap = onPreviewImages cell.onSelect = { [weak self] in guard let self, let person = self.pendingPerson else { return } let selection = OrderSourceSelection(person: person, lead: lead) self.onLeadBound(selection) } return cell } } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard level == .person else { return } let person = viewModel.orderSourcePeople[indexPath.row] pendingPerson = person level = .lead onPersonSelected(person) applyViewModel() } }