Files
suixinkan_uikit/suixinkan/UI/CooperationOrder/CooperationAcquirerViewController.swift
T

268 lines
10 KiB
Swift

//
// CooperationAcquirerViewController.swift
// suixinkan
//
import AVFoundation
import SnapKit
import UIKit
/// 合作获客员页,对齐 Android `CooperationAcquirerScreen`。
final class CooperationAcquirerViewController: BaseViewController, UITableViewDataSource, UITableViewDelegate {
private let viewModel = CooperationAcquirerViewModel()
private let orderAPI = NetworkServices.shared.orderAPI
private let tableView = UITableView(frame: .zero, style: .plain)
private let refreshControl = UIRefreshControl()
private let emptyLabel = UILabel()
private var shouldRefreshOnAppear = false
private var commissionDialogView: CooperationCommissionRateDialogView?
override func setupNavigationBar() {
title = "合作获客员"
navigationItem.rightBarButtonItem = UIBarButtonItem(
title: "扫码绑定",
style: .plain,
target: self,
action: #selector(startScanBind)
)
navigationItem.rightBarButtonItem?.tintColor = AppColor.primary
}
override func setupUI() {
view.backgroundColor = UIColor(hex: 0xF6F7FA)
tableView.backgroundColor = .clear
tableView.separatorStyle = .none
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 224
tableView.contentInset = UIEdgeInsets(top: AppSpacing.sm, left: 0, bottom: AppSpacing.md, right: 0)
tableView.verticalScrollIndicatorInsets = UIEdgeInsets(top: AppSpacing.sm, left: 0, bottom: AppSpacing.md, right: 0)
tableView.dataSource = self
tableView.delegate = self
tableView.register(CooperationAcquirerCell.self, forCellReuseIdentifier: CooperationAcquirerCell.reuseIdentifier)
tableView.refreshControl = refreshControl
emptyLabel.text = "暂无合作获客员"
emptyLabel.font = .systemFont(ofSize: 14)
emptyLabel.textColor = AppColor.textTertiary
emptyLabel.textAlignment = .center
emptyLabel.isHidden = true
view.addSubview(tableView)
view.addSubview(emptyLabel)
}
override func setupConstraints() {
tableView.snp.makeConstraints { make in
make.edges.equalTo(view.safeAreaLayoutGuide)
}
emptyLabel.snp.makeConstraints { make in
make.center.equalTo(tableView)
}
}
override func bindActions() {
viewModel.onStateChange = { [weak self] in
Task { @MainActor in self?.applyViewModel() }
}
viewModel.onShowMessage = { [weak self] message in
Task { @MainActor in self?.showToast(message) }
}
viewModel.onNavigateBindAcquirer = { [weak self] saleUserId in
Task { @MainActor in self?.pushBindAcquirer(saleUserId: saleUserId) }
}
refreshControl.addTarget(self, action: #selector(refreshTriggered), for: .valueChanged)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard CooperationOrderFeature.hasPermission(in: AppStore.shared.permissions.permissionItems()) else {
showToast("暂无合作订单权限")
navigationController?.popViewController(animated: false)
return
}
if shouldRefreshOnAppear {
shouldRefreshOnAppear = false
Task { await viewModel.refresh(api: orderAPI) }
}
}
override func viewDidLoad() {
super.viewDidLoad()
Task {
showLoading()
defer { hideLoading() }
await viewModel.loadAcquirers(api: orderAPI, initial: true)
}
}
@objc private func startScanBind() {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
presentScanner()
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { granted in
Task { @MainActor [weak self] in
if granted {
self?.presentScanner()
}
}
}
default:
showToast("需要相机权限才能扫码")
}
}
@objc private func refreshTriggered() {
Task { await viewModel.refresh(api: orderAPI) }
}
private func presentScanner() {
let scanner = QRCodeScannerViewController()
scanner.onScanResult = { [weak self] result in
self?.viewModel.processScanResult(result)
}
let nav = UINavigationController(rootViewController: scanner)
nav.modalPresentationStyle = .fullScreen
present(nav, animated: true)
}
private func pushBindAcquirer(saleUserId: Int) {
let controller = BindAcquirerViewController(saleUserId: saleUserId)
controller.onBindSuccess = { [weak self] in
self?.shouldRefreshOnAppear = true
self?.navigationController?.popViewController(animated: true)
}
if presentedViewController != nil {
dismiss(animated: true) { [weak self] in
self?.navigationController?.pushViewController(controller, animated: true)
}
return
}
navigationController?.pushViewController(controller, animated: true)
}
private func applyViewModel() {
if viewModel.isRefreshing {
if !refreshControl.isRefreshing { refreshControl.beginRefreshing() }
} else {
refreshControl.endRefreshing()
}
emptyLabel.isHidden = !viewModel.acquirers.isEmpty || viewModel.isRefreshing
tableView.reloadData()
applyCommissionDialogState()
}
private func presentRemarkDialog(for acquirer: CooperativeSalerEntity) {
viewModel.startEditRemark(acquirer)
let alert = UIAlertController(title: "修改备注", message: "最多\(CooperationOrderFeature.remarkNameMaxLength)个字符", preferredStyle: .alert)
alert.addTextField { [weak self] field in
field.text = self?.viewModel.remarkDraft
}
alert.addAction(UIAlertAction(title: "取消", style: .cancel) { [weak self] _ in
self?.viewModel.dismissRemarkDialog()
})
alert.addAction(UIAlertAction(title: "保存", style: .default) { [weak self] _ in
guard let self else { return }
if let text = alert.textFields?.first?.text {
self.viewModel.updateRemarkDraft(text)
}
Task { await self.viewModel.saveRemark(api: self.orderAPI) }
})
present(alert, animated: true)
}
private func presentCommissionDialog(for acquirer: CooperativeSalerEntity) {
viewModel.startEditCommissionRate(acquirer)
commissionDialogView?.dismiss()
let dialog = CooperationCommissionRateDialogView()
dialog.onCommissionRateChange = { [weak self] text in
self?.viewModel.updateCommissionRateDraft(text)
}
dialog.onSmsCodeChange = { [weak self] text in
self?.viewModel.updateCommissionSmsCodeDraft(text)
}
dialog.onSendSmsCode = { [weak self, weak dialog] in
guard let self else { return }
if let dialog {
self.syncCommissionDialogDrafts(dialog)
}
Task { await self.viewModel.requestCommissionSmsCode(api: self.orderAPI) }
}
dialog.onCancel = { [weak self] in
self?.viewModel.dismissCommissionRateDialog()
}
dialog.onConfirm = { [weak self, weak dialog] in
guard let self else { return }
if let dialog {
self.syncCommissionDialogDrafts(dialog)
}
Task { await self.viewModel.saveCommissionRate(api: self.orderAPI) }
}
commissionDialogView = dialog
applyCommissionDialogState()
dialog.show(in: view)
}
private func syncCommissionDialogDrafts(_ dialog: CooperationCommissionRateDialogView) {
viewModel.updateCommissionRateDraft(dialog.currentCommissionRate)
viewModel.updateCommissionSmsCodeDraft(dialog.currentSmsCode)
}
private func applyCommissionDialogState() {
guard let dialog = commissionDialogView else { return }
guard let acquirer = viewModel.commissionEditingAcquirer else {
dialog.dismiss()
commissionDialogView = nil
return
}
let phone = acquirer.salerPhone.isEmpty ? acquirer.displayPhone : acquirer.salerPhone
dialog.apply(
commissionRate: viewModel.commissionRateDraft,
smsCode: viewModel.commissionSmsCodeDraft,
phone: CooperationOrderPhoneMask.mask(phone),
countdown: viewModel.commissionSmsCountdown
)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
viewModel.acquirers.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(
withIdentifier: CooperationAcquirerCell.reuseIdentifier,
for: indexPath
) as? CooperationAcquirerCell else {
return UITableViewCell()
}
let acquirer = viewModel.acquirers[indexPath.row]
cell.configure(with: acquirer)
cell.onEditRemark = { [weak self] in
self?.presentRemarkDialog(for: acquirer)
}
cell.onEditCommission = { [weak self] in
self?.presentCommissionDialog(for: acquirer)
}
cell.onViewCommissionLogs = { [weak self] in
guard acquirer.saleUserId > 0 else {
self?.showToast("获客员信息无效")
return
}
let controller = CommissionRateLogViewController(
saleUserId: acquirer.saleUserId,
acquirerName: acquirer.displayName
)
self?.navigationController?.pushViewController(controller, animated: true)
}
cell.onCall = {
let phone = acquirer.displayPhone.trimmingCharacters(in: .whitespacesAndNewlines)
guard !phone.isEmpty, let url = URL(string: "tel://\(phone)") else { return }
UIApplication.shared.open(url)
}
return cell
}
}