// // 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 setupUI() { tableView.backgroundColor = AppColor.pageBackground tableView.separatorStyle = .none tableView.register(TaskOrderCell.self, forCellReuseIdentifier: TaskOrderCell.reuseIdentifier) tableView.dataSource = self tableView.delegate = self 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) } override func setupConstraints() { bottomBar.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview() } confirmButton.snp.makeConstraints { make in make.edges.equalToSuperview().inset(AppSpacing.md) } 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: "付款时间:") 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.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.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) cardView.layer.borderColor = isSelected ? AppColor.danger.cgColor : AppColor.border.cgColor cardView.layer.borderWidth = isSelected ? 2 : 1 } } /// 订单信息行。 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.textSecondary valueView.font = .app(.body) valueView.textColor = AppColor.textPrimary valueView.numberOfLines = 0 addSubview(labelView) addSubview(valueView) labelView.snp.makeConstraints { make in make.leading.top.bottom.equalToSuperview() make.width.equalTo(80) } 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 } }