// // WiredCameraTransferViewController.swift // suixinkan // import SnapKit import UIKit /// 有线相机传输页,仅同步 Android 断开空态 UI,不接入 OTG 或上传业务。 final class WiredCameraTransferViewController: BaseViewController { private let viewModel: WiredCameraTransferViewModel private let headerView = UIView() private let backButton = UIButton(type: .system) private let titleLabel = UILabel() private let phoneLabel = UILabel() private let statusCard = UIView() private let storagePanel = UIView() private let storageTitleLabel = UILabel() private let storageValueLabel = UILabel() private let statusLabel = UILabel() private let refreshButton = UIButton(type: .system) private let helpLabel = UILabel() private let chipsStack = UIStackView() private let statsCard = UIStackView() private let emptyLabel = UILabel() private let bottomBar = UIView() private let batchButton = UIButton(type: .system) private let specifyButton = UIButton(type: .system) init(viewModel: WiredCameraTransferViewModel) { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func setupNavigationBar() { navigationController?.setNavigationBarHidden(true, animated: false) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) navigationController?.setNavigationBarHidden(false, animated: animated) } override func setupUI() { view.backgroundColor = AppColor.pageBackground headerView.backgroundColor = AppColor.primary backButton.setImage(UIImage(systemName: "chevron.left"), for: .normal) backButton.tintColor = .white titleLabel.text = viewModel.albumTitle titleLabel.font = .systemFont(ofSize: 20, weight: .bold) titleLabel.textColor = .white phoneLabel.text = viewModel.headerPhone phoneLabel.font = .systemFont(ofSize: 13) phoneLabel.textColor = UIColor.white.withAlphaComponent(0.92) statusCard.backgroundColor = .white statusCard.layer.cornerRadius = 12 statusCard.layer.shadowColor = UIColor.black.withAlphaComponent(0.10).cgColor statusCard.layer.shadowOpacity = 1 statusCard.layer.shadowRadius = 8 statusCard.layer.shadowOffset = CGSize(width: 0, height: 3) storagePanel.layer.cornerRadius = 12 storagePanel.layer.borderColor = AppColor.border.cgColor storagePanel.layer.borderWidth = 3 storageTitleLabel.text = viewModel.deviceModelName storageTitleLabel.font = .systemFont(ofSize: 11, weight: .medium) storageTitleLabel.textColor = AppColor.textPrimary storageTitleLabel.textAlignment = .center storageValueLabel.text = viewModel.availableStorageText storageValueLabel.font = .systemFont(ofSize: 10) storageValueLabel.textColor = AppColor.primary storageValueLabel.textAlignment = .center statusLabel.text = viewModel.cameraStatusText statusLabel.font = .systemFont(ofSize: 11, weight: .medium) statusLabel.textColor = AppColor.danger statusLabel.backgroundColor = AppColor.danger.withAlphaComponent(0.10) statusLabel.layer.cornerRadius = 4 statusLabel.clipsToBounds = true statusLabel.textAlignment = .center refreshButton.setTitle(viewModel.actionButtonText, for: .normal) refreshButton.setTitleColor(.white, for: .normal) refreshButton.titleLabel?.font = .systemFont(ofSize: 12) refreshButton.backgroundColor = AppColor.danger refreshButton.layer.cornerRadius = 8 helpLabel.attributedText = helpText() helpLabel.font = .systemFont(ofSize: 10) helpLabel.numberOfLines = 2 chipsStack.axis = .horizontal chipsStack.spacing = 6 [viewModel.retouchOption, viewModel.photoFormatOption, viewModel.transferModeOption].forEach { chipsStack.addArrangedSubview(makeChip($0)) } statsCard.axis = .horizontal statsCard.distribution = .fillEqually ["全部", "已上传", "失败"].enumerated().forEach { index, title in statsCard.addArrangedSubview(makeStat(title: title, count: viewModel.tabCounts[index], selected: index == 0, danger: index == 2)) } emptyLabel.text = "暂无照片" emptyLabel.font = .systemFont(ofSize: 14) emptyLabel.textColor = AppColor.textTertiary emptyLabel.textAlignment = .center bottomBar.backgroundColor = .white configureBottomButton(batchButton, title: "批量上传", filled: true) configureBottomButton(specifyButton, title: "指定上传", filled: false) view.addSubview(headerView) headerView.addSubview(backButton) headerView.addSubview(titleLabel) headerView.addSubview(phoneLabel) view.addSubview(statusCard) statusCard.addSubview(storagePanel) storagePanel.addSubview(storageTitleLabel) storagePanel.addSubview(storageValueLabel) statusCard.addSubview(statusLabel) statusCard.addSubview(refreshButton) statusCard.addSubview(helpLabel) statusCard.addSubview(chipsStack) statusCard.addSubview(statsCard) view.addSubview(emptyLabel) view.addSubview(bottomBar) bottomBar.addSubview(batchButton) bottomBar.addSubview(specifyButton) } override func setupConstraints() { headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.height.equalTo(118) } backButton.snp.makeConstraints { make in make.leading.equalToSuperview().offset(8) make.bottom.equalToSuperview().offset(-28) make.size.equalTo(44) } titleLabel.snp.makeConstraints { make in make.leading.equalTo(backButton.snp.trailing).offset(4) make.trailing.equalToSuperview().offset(-16) make.top.equalTo(backButton).offset(4) } phoneLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(2) make.leading.trailing.equalTo(titleLabel) } statusCard.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom).offset(-10) make.leading.trailing.equalToSuperview().inset(16) } storagePanel.snp.makeConstraints { make in make.top.leading.equalToSuperview().offset(14) make.width.greaterThanOrEqualTo(76) make.height.equalTo(76) } storageTitleLabel.snp.makeConstraints { make in make.top.equalToSuperview().offset(10) make.leading.trailing.equalToSuperview().inset(8) } storageValueLabel.snp.makeConstraints { make in make.top.equalTo(storageTitleLabel.snp.bottom).offset(6) make.leading.trailing.equalTo(storageTitleLabel) } statusLabel.snp.makeConstraints { make in make.top.equalToSuperview().offset(16) make.leading.equalTo(storagePanel.snp.trailing).offset(10) make.height.equalTo(22) make.width.greaterThanOrEqualTo(84) } refreshButton.snp.makeConstraints { make in make.centerY.equalTo(statusLabel) make.trailing.equalToSuperview().offset(-14) make.height.equalTo(34) make.width.greaterThanOrEqualTo(72) } helpLabel.snp.makeConstraints { make in make.top.equalTo(statusLabel.snp.bottom).offset(4) make.leading.equalTo(statusLabel) make.trailing.equalTo(refreshButton) } chipsStack.snp.makeConstraints { make in make.top.equalTo(storagePanel.snp.bottom).offset(12) make.leading.trailing.equalToSuperview().inset(14) make.height.equalTo(34) } statsCard.snp.makeConstraints { make in make.top.equalTo(chipsStack.snp.bottom).offset(14) make.leading.trailing.bottom.equalToSuperview() make.height.equalTo(68) } bottomBar.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview() } batchButton.snp.makeConstraints { make in make.top.equalToSuperview().offset(12) make.leading.equalToSuperview().offset(16) make.height.equalTo(44) make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-12) } specifyButton.snp.makeConstraints { make in make.top.height.width.equalTo(batchButton) make.leading.equalTo(batchButton.snp.trailing).offset(12) make.trailing.equalToSuperview().offset(-16) } emptyLabel.snp.makeConstraints { make in make.top.equalTo(statusCard.snp.bottom) make.leading.trailing.equalToSuperview() make.bottom.equalTo(bottomBar.snp.top) } } override func bindActions() { viewModel.onShowMessage = { [weak self] message in Task { @MainActor in self?.showToast(message) } } backButton.addTarget(self, action: #selector(backTapped), for: .touchUpInside) refreshButton.addTarget(self, action: #selector(uiOnlyTapped), for: .touchUpInside) batchButton.addTarget(self, action: #selector(uiOnlyTapped), for: .touchUpInside) specifyButton.addTarget(self, action: #selector(uiOnlyTapped), for: .touchUpInside) } private func makeChip(_ text: String) -> UIView { let label = UILabel() label.text = text label.font = .systemFont(ofSize: 11) label.textColor = AppColor.textTertiary label.backgroundColor = AppColor.pageBackground label.layer.cornerRadius = 8 label.layer.borderWidth = 1 label.layer.borderColor = AppColor.border.cgColor label.clipsToBounds = true label.textAlignment = .center return label } private func makeStat(title: String, count: Int, selected: Bool, danger: Bool) -> UIView { let container = UIView() let countLabel = UILabel() let titleLabel = UILabel() let underline = UIView() countLabel.text = "\(count)" countLabel.font = .systemFont(ofSize: 18, weight: .semibold) countLabel.textColor = selected ? AppColor.primary : (danger ? AppColor.danger : AppColor.textPrimary) countLabel.textAlignment = .center titleLabel.text = title titleLabel.font = .systemFont(ofSize: 13) titleLabel.textColor = selected ? AppColor.primary : AppColor.textSecondary titleLabel.textAlignment = .center underline.backgroundColor = selected ? AppColor.primary : .clear underline.layer.cornerRadius = 1 container.addSubview(countLabel) container.addSubview(titleLabel) container.addSubview(underline) countLabel.snp.makeConstraints { make in make.top.equalToSuperview().offset(8) make.leading.trailing.equalToSuperview() } titleLabel.snp.makeConstraints { make in make.top.equalTo(countLabel.snp.bottom).offset(2) make.leading.trailing.equalToSuperview() } underline.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(4) make.centerX.equalToSuperview() make.width.equalTo(24) make.height.equalTo(2) } return container } private func configureBottomButton(_ button: UIButton, title: String, filled: Bool) { button.setTitle(title, for: .normal) button.titleLabel?.font = .systemFont(ofSize: 15) button.layer.cornerRadius = 10 if filled { button.backgroundColor = AppColor.primary button.setTitleColor(.white, for: .normal) } else { button.backgroundColor = .white button.setTitleColor(AppColor.primary, for: .normal) button.layer.borderWidth = 1 button.layer.borderColor = AppColor.primary.cgColor } } private func helpText() -> NSAttributedString { let text = NSMutableAttributedString( string: "未连接,查看", attributes: [.foregroundColor: AppColor.textSecondary] ) text.append(NSAttributedString(string: "《帮助文档》", attributes: [.foregroundColor: AppColor.primary])) return text } @objc private func backTapped() { navigationController?.popViewController(animated: true) } @objc private func uiOnlyTapped() { viewModel.showUIOnlyMessage() } }