Add scenic application flow aligned with Android.
Implement scenic apply models, API, upload, UI, and tests; wire entry from permission apply pages and include related permission/scenic selection UI fixes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
189
suixinkan/UI/Orders/OrderSourceLeadListViewController.swift
Normal file
189
suixinkan/UI/Orders/OrderSourceLeadListViewController.swift
Normal file
@ -0,0 +1,189 @@
|
||||
//
|
||||
// OrderSourceLeadListViewController.swift
|
||||
// suixinkan
|
||||
//
|
||||
|
||||
import SnapKit
|
||||
import UIKit
|
||||
|
||||
/// 某获客员的带客单选择页,从「选择获客员」页 push 进入。
|
||||
final class OrderSourceLeadListViewController: BaseViewController, UITableViewDataSource, UITableViewDelegate {
|
||||
|
||||
private let person: OrderSourcePerson
|
||||
private let orderNumber: String
|
||||
private let viewModel: OrderListViewModel
|
||||
private let referralOrder: BoundReferralOrderEntity?
|
||||
private let loadLeads: (OrderSourcePerson) async -> Void
|
||||
private let onLeadBound: (OrderSourceSelection) -> Void
|
||||
private let onCallPhone: (String) -> Void
|
||||
private let onPreviewImages: ([String], Int) -> Void
|
||||
|
||||
private let phoneRow = UIStackView()
|
||||
private let phoneLabel = UILabel()
|
||||
private let callButton = UIButton(type: .system)
|
||||
private let tableView = UITableView(frame: .zero, style: .plain)
|
||||
private let loadingIndicator = UIActivityIndicatorView(style: .medium)
|
||||
private let emptyLabel = UILabel()
|
||||
|
||||
init(
|
||||
person: OrderSourcePerson,
|
||||
orderNumber: String,
|
||||
viewModel: OrderListViewModel,
|
||||
referralOrder: BoundReferralOrderEntity?,
|
||||
loadLeads: @escaping (OrderSourcePerson) async -> Void,
|
||||
onLeadBound: @escaping (OrderSourceSelection) -> Void,
|
||||
onCallPhone: @escaping (String) -> Void,
|
||||
onPreviewImages: @escaping ([String], Int) -> Void
|
||||
) {
|
||||
self.person = person
|
||||
self.orderNumber = orderNumber
|
||||
self.viewModel = viewModel
|
||||
self.referralOrder = referralOrder
|
||||
self.loadLeads = loadLeads
|
||||
self.onLeadBound = onLeadBound
|
||||
self.onCallPhone = onCallPhone
|
||||
self.onPreviewImages = onPreviewImages
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func setupNavigationBar() {
|
||||
title = "\(person.name)的带客单"
|
||||
}
|
||||
|
||||
override func setupUI() {
|
||||
view.backgroundColor = .white
|
||||
|
||||
phoneLabel.font = .systemFont(ofSize: 14)
|
||||
phoneLabel.textColor = AppColor.primary
|
||||
phoneLabel.text = person.phone
|
||||
|
||||
callButton.backgroundColor = OrderTokens.phoneIconBackground
|
||||
callButton.layer.cornerRadius = 16
|
||||
callButton.tintColor = AppColor.primary
|
||||
callButton.setImage(UIImage(systemName: "phone.fill"), for: .normal)
|
||||
callButton.addTarget(self, action: #selector(callTapped), for: .touchUpInside)
|
||||
|
||||
phoneRow.axis = .horizontal
|
||||
phoneRow.alignment = .center
|
||||
phoneRow.spacing = 10
|
||||
phoneRow.addArrangedSubview(phoneLabel)
|
||||
phoneRow.addArrangedSubview(callButton)
|
||||
|
||||
let phone = person.phone.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
phoneRow.isHidden = phone.isEmpty
|
||||
|
||||
tableView.separatorStyle = .none
|
||||
tableView.backgroundColor = .white
|
||||
tableView.dataSource = self
|
||||
tableView.delegate = self
|
||||
tableView.register(OrderSourceLeadCell.self, forCellReuseIdentifier: OrderSourceLeadCell.reuseIdentifier)
|
||||
|
||||
loadingIndicator.hidesWhenStopped = true
|
||||
loadingIndicator.color = AppColor.primary
|
||||
|
||||
emptyLabel.font = .systemFont(ofSize: 14)
|
||||
emptyLabel.textColor = AppColor.text666
|
||||
emptyLabel.textAlignment = .center
|
||||
emptyLabel.numberOfLines = 0
|
||||
emptyLabel.text = "暂无可绑定带客单"
|
||||
|
||||
view.addSubview(phoneRow)
|
||||
view.addSubview(tableView)
|
||||
view.addSubview(loadingIndicator)
|
||||
view.addSubview(emptyLabel)
|
||||
}
|
||||
|
||||
override func setupConstraints() {
|
||||
phoneRow.snp.makeConstraints { make in
|
||||
make.top.equalTo(view.safeAreaLayoutGuide).offset(AppSpacing.sm)
|
||||
make.leading.equalToSuperview().inset(AppSpacing.md)
|
||||
}
|
||||
callButton.snp.makeConstraints { make in
|
||||
make.width.height.equalTo(32)
|
||||
}
|
||||
tableView.snp.makeConstraints { make in
|
||||
make.top.equalTo(phoneRow.snp.bottom).offset(AppSpacing.sm)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
applyViewModel()
|
||||
Task {
|
||||
await loadLeads(person)
|
||||
reloadFromViewModel()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
if isMovingFromParent {
|
||||
navigationController?.setNavigationBarHidden(true, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
func reloadFromViewModel() {
|
||||
applyViewModel()
|
||||
}
|
||||
|
||||
private func applyViewModel() {
|
||||
let isLoading = viewModel.loadingOrderSourceLeadsFor == person.key
|
||||
let leads = viewModel.orderSourceLeads[person.key] ?? []
|
||||
|
||||
if isLoading {
|
||||
loadingIndicator.startAnimating()
|
||||
tableView.isHidden = true
|
||||
emptyLabel.isHidden = true
|
||||
} else {
|
||||
loadingIndicator.stopAnimating()
|
||||
tableView.isHidden = false
|
||||
emptyLabel.isHidden = !leads.isEmpty
|
||||
tableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func callTapped() {
|
||||
onCallPhone(person.phone)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
viewModel.orderSourceLeads[person.key]?.count ?? 0
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(
|
||||
withIdentifier: OrderSourceLeadCell.reuseIdentifier,
|
||||
for: indexPath
|
||||
) as! OrderSourceLeadCell
|
||||
let leads = viewModel.orderSourceLeads[person.key] ?? []
|
||||
let lead = leads[indexPath.row]
|
||||
let currentSelection = viewModel.orderSourceSelection(
|
||||
orderNumber: orderNumber,
|
||||
referralOrder: referralOrder
|
||||
)
|
||||
let selected = currentSelection?.person.key == person.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 else { return }
|
||||
let selection = OrderSourceSelection(person: self.person, lead: lead)
|
||||
self.onLeadBound(selection)
|
||||
}
|
||||
return cell
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user