// // AccountSelectionViewController.swift // suixinkan // import SnapKit import UIKit private typealias AccountSelectionSection = Int private typealias AccountSelectionItem = String /// 多账号登录时的账号选择页,展示景区/门店账号列表并提交选择。 final class AccountSelectionViewController: UIViewController, UITableViewDelegate { private let payload: AccountSelectionPayload private var isLoading: Bool private let onCancel: () -> Void private let onConfirm: (AccountSwitchAccount) -> Void private var selectedAccountId: String? private let tableView = UITableView(frame: .zero, style: .plain) private var tableDataSource: UITableViewDiffableDataSource! private let confirmButton = UIButton(type: .system) private let bottomBar = UIView() init( payload: AccountSelectionPayload, isLoading: Bool, onCancel: @escaping () -> Void, onConfirm: @escaping (AccountSwitchAccount) -> Void ) { self.payload = payload self.isLoading = isLoading self.onCancel = onCancel self.onConfirm = onConfirm super.init(nibName: nil, bundle: nil) } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() title = "选择账号" view.backgroundColor = UIColor(hex: 0xF5F7FB) navigationItem.leftBarButtonItem = UIBarButtonItem( title: "取消", style: .plain, target: self, action: #selector(cancelTapped) ) isModalInPresentation = isLoading selectedAccountId = payload.accounts.first?.id configureTableView() configureTableDataSource() configureBottomBar() } /// 更新 Loading 状态。 func updateLoading(_ loading: Bool) { isLoading = loading isModalInPresentation = loading confirmButton.isEnabled = canConfirm confirmButton.backgroundColor = canConfirm ? AppColor.primary : AppColor.buttonDisabled } private var selectedAccount: AccountSwitchAccount? { payload.accounts.first { $0.id == selectedAccountId } } private var canConfirm: Bool { selectedAccount != nil && !isLoading } private func configureTableDataSource() { tableDataSource = UITableViewDiffableDataSource( tableView: tableView ) { [weak self] tableView, indexPath, item in guard let self, let cell = tableView.dequeueReusableCell( withIdentifier: AccountSelectionCell.reuseIdentifier, for: indexPath ) as? AccountSelectionCell, let account = self.payload.accounts.first(where: { $0.id == item }) else { return UITableViewCell() } cell.configure(account: account, selected: account.id == self.selectedAccountId) return cell } applyTableSnapshot(animated: false) } private func buildTableSnapshot() -> NSDiffableDataSourceSnapshot { var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([0]) snapshot.appendItems(payload.accounts.map(\.id), toSection: 0) return snapshot } private func applyTableSnapshot(animated: Bool = true, reconfigure: Bool = false) { var snapshot = buildTableSnapshot() if reconfigure, !snapshot.itemIdentifiers.isEmpty { snapshot.reconfigureItems(snapshot.itemIdentifiers) } tableDataSource.apply(snapshot, animatingDifferences: animated) } private func configureTableView() { tableView.backgroundColor = .clear tableView.separatorStyle = .none tableView.contentInset = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0) tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 92 tableView.delegate = self tableView.register(AccountSelectionCell.self, forCellReuseIdentifier: AccountSelectionCell.reuseIdentifier) view.addSubview(tableView) tableView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.bottom.equalTo(view.safeAreaLayoutGuide).inset(90) } } private func configureBottomBar() { bottomBar.backgroundColor = .white let divider = UIView() divider.backgroundColor = UIColor.separator confirmButton.setTitle("进入系统", for: .normal) confirmButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .semibold) confirmButton.setTitleColor(.white, for: .normal) confirmButton.layer.cornerRadius = 8 confirmButton.addTarget(self, action: #selector(confirmTapped), for: .touchUpInside) updateLoading(isLoading) view.addSubview(bottomBar) bottomBar.addSubview(divider) bottomBar.addSubview(confirmButton) bottomBar.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview() } divider.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.height.equalTo(0.5) } confirmButton.snp.makeConstraints { make in make.top.equalTo(divider.snp.bottom).offset(12) make.leading.trailing.equalToSuperview().inset(16) make.bottom.equalTo(view.safeAreaLayoutGuide).inset(14) make.height.equalTo(50) } } @objc private func cancelTapped() { onCancel() dismiss(animated: true) } @objc private func confirmTapped() { guard let selectedAccount else { return } onConfirm(selectedAccount) } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) guard let item = tableDataSource.itemIdentifier(for: indexPath) else { return } selectedAccountId = item applyTableSnapshot(animated: false, reconfigure: true) confirmButton.isEnabled = canConfirm confirmButton.backgroundColor = canConfirm ? AppColor.primary : AppColor.buttonDisabled } } /// 账号选择列表 Cell。 private final class AccountSelectionCell: UITableViewCell { static let reuseIdentifier = "AccountSelectionCell" private let card = UIView() private let avatarView = UIImageView() private let avatarPlaceholderLabel = UILabel() private let titleLabel = UILabel() private let subtitleLabel = UILabel() private let phoneLabel = UILabel() private let typeTag = UILabel() private let currentTag = UILabel() private let checkmark = UIImageView() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none backgroundColor = .clear contentView.backgroundColor = .clear card.backgroundColor = .white card.layer.cornerRadius = 10 card.layer.masksToBounds = true avatarView.clipsToBounds = true avatarView.layer.cornerRadius = 25 avatarView.contentMode = .scaleAspectFill avatarPlaceholderLabel.font = .systemFont(ofSize: 14, weight: .semibold) avatarPlaceholderLabel.textAlignment = .center titleLabel.font = .systemFont(ofSize: 16, weight: .semibold) titleLabel.textColor = AppColor.text333 subtitleLabel.font = .systemFont(ofSize: 13) subtitleLabel.textColor = AppColor.text666 phoneLabel.font = .systemFont(ofSize: 12) phoneLabel.textColor = UIColor(hex: 0x9AA1AA) typeTag.font = .systemFont(ofSize: 11, weight: .semibold) typeTag.textAlignment = .center typeTag.layer.cornerRadius = 4 typeTag.clipsToBounds = true typeTag.setContentCompressionResistancePriority(.required, for: .horizontal) currentTag.font = .systemFont(ofSize: 11, weight: .semibold) currentTag.textAlignment = .center currentTag.text = "当前" currentTag.textColor = AppColor.primary currentTag.backgroundColor = AppColor.secondaryButtonBackground currentTag.layer.cornerRadius = 4 currentTag.clipsToBounds = true currentTag.isHidden = true checkmark.contentMode = .scaleAspectFit contentView.addSubview(card) card.addSubview(avatarView) avatarView.addSubview(avatarPlaceholderLabel) card.addSubview(titleLabel) card.addSubview(subtitleLabel) card.addSubview(phoneLabel) card.addSubview(typeTag) card.addSubview(currentTag) card.addSubview(checkmark) card.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 6, left: 16, bottom: 6, right: 16)) } avatarView.snp.makeConstraints { make in make.leading.equalToSuperview().inset(16) make.centerY.equalToSuperview() make.width.height.equalTo(50) } avatarPlaceholderLabel.snp.makeConstraints { make in make.center.equalToSuperview() } titleLabel.snp.makeConstraints { make in make.top.equalToSuperview().inset(16) make.leading.equalTo(avatarView.snp.trailing).offset(12) make.trailing.lessThanOrEqualTo(typeTag.snp.leading).offset(-8) } currentTag.snp.makeConstraints { make in make.leading.equalTo(titleLabel.snp.trailing).offset(6) make.trailing.lessThanOrEqualTo(typeTag.snp.leading).offset(-8) make.centerY.equalTo(titleLabel) make.height.equalTo(18) } subtitleLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(4) make.leading.equalTo(titleLabel) make.trailing.equalTo(checkmark.snp.leading).offset(-8) } phoneLabel.snp.makeConstraints { make in make.top.equalTo(subtitleLabel.snp.bottom).offset(4) make.leading.equalTo(titleLabel) make.bottom.equalToSuperview().inset(16) } typeTag.snp.makeConstraints { make in make.trailing.equalTo(checkmark.snp.leading).offset(-8) make.top.equalTo(titleLabel) make.height.equalTo(18) } checkmark.snp.makeConstraints { make in make.trailing.equalToSuperview().inset(16) make.centerY.equalToSuperview() make.width.height.equalTo(22) } } required init?(coder: NSCoder) { nil } func configure(account: AccountSwitchAccount, selected: Bool) { titleLabel.text = account.title.isEmpty ? account.accountTypeLabel : account.title subtitleLabel.text = account.subtitle subtitleLabel.isHidden = account.subtitle.isEmpty phoneLabel.text = account.phone phoneLabel.isHidden = account.phone.isEmpty currentTag.isHidden = !account.isCurrent let avatarPlaceholderColor: UIColor let avatarTextColor: UIColor if account.isStoreUser { avatarPlaceholderColor = UIColor(hex: 0xE8F8F1) avatarTextColor = UIColor(hex: 0x0F9F6E) avatarPlaceholderLabel.text = "店" typeTag.text = " 门店 " typeTag.textColor = UIColor(hex: 0x0F9F6E) typeTag.backgroundColor = UIColor(hex: 0xE8F8F1) } else { avatarPlaceholderColor = UIColor(hex: 0xF3ECFF) avatarTextColor = UIColor(hex: 0x7C3AED) avatarPlaceholderLabel.text = "景" typeTag.text = " 景区 " typeTag.textColor = UIColor(hex: 0x7C3AED) typeTag.backgroundColor = UIColor(hex: 0xF3ECFF) } avatarPlaceholderLabel.textColor = avatarTextColor avatarPlaceholderLabel.isHidden = !account.avatar.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty avatarView.loadRemoteImage(urlString: account.avatar, placeholderColor: avatarPlaceholderColor) let symbolName = selected ? "checkmark.circle.fill" : "circle" checkmark.image = UIImage(systemName: symbolName) checkmark.tintColor = selected ? AppColor.primary : UIColor(hex: 0xB6BECA) card.layer.borderWidth = selected ? 1.4 : 1 card.layer.borderColor = (selected ? AppColor.primary : UIColor(hex: 0xE6ECF4)).cgColor } }