// // TaskOrderSelectViewController.swift // suixinkan // import SnapKit import UIKit /// 任务关联订单选择页,对齐 Android `OrderSelectScreen`。 final class TaskOrderSelectViewController: BaseViewController { var onConfirmed: ((String?) -> Void)? private let viewModel: TaskOrderSelectViewModel private let taskAPI = NetworkServices.shared.taskAPI private let tableView = UITableView(frame: .zero, style: .plain) private let confirmButton = AppButton(title: "确定") private let emptyLabel = UILabel() private let bottomBar = UIView() init(initialOrderNumber: String? = nil) { viewModel = TaskOrderSelectViewModel(initialOrderNumber: initialOrderNumber) super.init(nibName: nil, bundle: nil) } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func setupNavigationBar() { title = "订单列表" } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) ProfileNavigationStyle.apply(to: navigationController, showsDivider: false) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) ProfileNavigationStyle.restore(navigationController) } override func setupUI() { tableView.backgroundColor = AppColor.pageBackground tableView.separatorStyle = .none tableView.register(TaskOrderCell.self, forCellReuseIdentifier: TaskOrderCell.reuseIdentifier) tableView.dataSource = self tableView.delegate = self tableView.contentInset = UIEdgeInsets(top: 12, left: 0, bottom: 0, right: 0) emptyLabel.text = "无有效订单" emptyLabel.font = .app(.body) emptyLabel.textColor = AppColor.textPrimary emptyLabel.textAlignment = .center emptyLabel.isHidden = true bottomBar.backgroundColor = .white view.addSubview(tableView) view.addSubview(emptyLabel) view.addSubview(bottomBar) bottomBar.addSubview(confirmButton) confirmButton.snp.updateConstraints { make in make.height.equalTo(48) } } override func setupConstraints() { bottomBar.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview() } confirmButton.snp.makeConstraints { make in make.top.equalToSuperview().offset(16) make.leading.trailing.equalToSuperview().inset(16) make.bottom.equalTo(view.safeAreaLayoutGuide).inset(16) } tableView.snp.makeConstraints { make in make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide) make.bottom.equalTo(bottomBar.snp.top) } 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) } } confirmButton.addTarget(self, action: #selector(confirmTapped), for: .touchUpInside) } override func viewDidLoad() { super.viewDidLoad() Task { await viewModel.loadOrders(api: taskAPI) } } @MainActor private func applyViewModel() { emptyLabel.isHidden = viewModel.isLoading || !viewModel.orders.isEmpty tableView.reloadData() if viewModel.isLoading { showLoading() } else { hideLoading() } } @objc private func confirmTapped() { onConfirmed?(viewModel.confirmedOrderNumber()) navigationController?.popViewController(animated: true) } } extension TaskOrderSelectViewController: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { viewModel.orders.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell( withIdentifier: TaskOrderCell.reuseIdentifier, for: indexPath ) as! TaskOrderCell let order = viewModel.orders[indexPath.row] cell.apply( order: order, isSelected: viewModel.selectedOrderNumber == order.orderNumber ) return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) viewModel.toggleSelection(orderNumber: viewModel.orders[indexPath.row].orderNumber) } } /// 订单选择卡片 cell。 private final class TaskOrderCell: UITableViewCell { static let reuseIdentifier = "TaskOrderCell" private let cardView = UIView() private let projectRow = TaskInfoRowView(label: "项目名称:") private let statusRow = TaskInfoRowView(label: "订单状态:") private let numberRow = TaskInfoRowView(label: "订单编号:") private let payTimeRow = TaskInfoRowView(label: "付款时间:") private let userIDRow = TaskInfoRowView(label: "用户 UID:") private let phoneRow = TaskInfoRowView(label: "用户手机号:") 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.sm cardView.layer.borderWidth = 1 cardView.layer.borderColor = AppColor.border.cgColor contentView.addSubview(cardView) cardView.addSubview(projectRow) cardView.addSubview(statusRow) cardView.addSubview(numberRow) cardView.addSubview(payTimeRow) cardView.addSubview(userIDRow) cardView.addSubview(phoneRow) cardView.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: AppSpacing.md, bottom: AppSpacing.sm, right: AppSpacing.md)) } projectRow.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview().inset(AppSpacing.md) } statusRow.snp.makeConstraints { make in make.top.equalTo(projectRow.snp.bottom).offset(AppSpacing.xs) make.leading.trailing.equalTo(projectRow) } numberRow.snp.makeConstraints { make in make.top.equalTo(statusRow.snp.bottom).offset(AppSpacing.xs) make.leading.trailing.equalTo(projectRow) } payTimeRow.snp.makeConstraints { make in make.top.equalTo(numberRow.snp.bottom).offset(AppSpacing.xs) make.leading.trailing.equalTo(projectRow) } userIDRow.snp.makeConstraints { make in make.top.equalTo(payTimeRow.snp.bottom).offset(AppSpacing.xs) make.leading.trailing.equalTo(projectRow) } phoneRow.snp.makeConstraints { make in make.top.equalTo(userIDRow.snp.bottom).offset(AppSpacing.xs) make.leading.trailing.bottom.equalToSuperview().inset(AppSpacing.md) } } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func apply(order: AvailableOrder, isSelected: Bool) { projectRow.apply(value: order.projectName.isEmpty ? "--" : order.projectName) statusRow.apply(value: order.orderStatusLabel.isEmpty ? "--" : order.orderStatusLabel) numberRow.apply(value: order.orderNumber) payTimeRow.apply(value: order.payTime.isEmpty ? "--" : order.payTime) userIDRow.apply(value: "U\(order.userId)") phoneRow.apply(value: order.userPhone) cardView.layer.borderColor = isSelected ? UIColor(hex: 0xFF0000).cgColor : AppColor.border.cgColor cardView.layer.borderWidth = isSelected ? 2 : 1 accessibilityLabel = "订单\(order.orderNumber)" accessibilityTraits = isSelected ? [.button, .selected] : .button } } /// 订单信息行。 private final class TaskInfoRowView: UIView { private let labelView = UILabel() private let valueView = UILabel() init(label: String) { super.init(frame: .zero) labelView.text = label labelView.font = .app(.body) labelView.textColor = AppColor.textPrimary valueView.font = .app(.body) valueView.textColor = AppColor.textPrimary valueView.numberOfLines = 0 addSubview(labelView) addSubview(valueView) labelView.setContentCompressionResistancePriority(.required, for: .horizontal) labelView.snp.makeConstraints { make in make.leading.top.bottom.equalToSuperview() } valueView.snp.makeConstraints { make in make.leading.equalTo(labelView.snp.trailing).offset(AppSpacing.xs) make.trailing.top.bottom.equalToSuperview() } } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func apply(value: String) { valueView.text = value } }