// // MessageDetailViewController.swift // suixinkan // import SnapKit import UIKit /// 消息详情页,对齐 Android `MsgDetailScreen`。 final class MessageDetailViewController: BaseViewController { var onDeleted: ((Int) -> Void)? private let viewModel: MessageDetailViewModel private let api: any MessageCenterServing private let scrollView = UIScrollView() private let contentView = UIView() private let typeImageView = UIImageView() private let titleLabel = UILabel() private let timeContainer = UIView() private let timeLabel = UILabel() private let bodyLabel = UILabel() private let bottomBar = UIView() private let deleteButton = UIButton(type: .system) /// 初始化消息详情页。 init(message: MessageItem, api: (any MessageCenterServing)? = nil) { viewModel = MessageDetailViewModel(message: message) self.api = api ?? NetworkServices.shared.messageCenterAPI 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() { view.backgroundColor = .white typeImageView.contentMode = .scaleAspectFit typeImageView.image = UIImage(named: MessageTypeIcon.assetName(for: viewModel.message.type)) titleLabel.text = viewModel.message.displayTitle titleLabel.font = .systemFont(ofSize: 20, weight: .bold) titleLabel.textColor = .black titleLabel.textAlignment = .center titleLabel.numberOfLines = 0 timeContainer.backgroundColor = UIColor(hex: 0xF4F4F4) timeContainer.layer.cornerRadius = 4 timeContainer.clipsToBounds = true timeLabel.text = viewModel.message.createdAt timeLabel.font = .systemFont(ofSize: 12) timeLabel.textColor = UIColor(hex: 0x4B5563) timeLabel.textAlignment = .center timeLabel.numberOfLines = 1 bodyLabel.text = viewModel.message.content bodyLabel.font = .systemFont(ofSize: 16) bodyLabel.textColor = .black bodyLabel.numberOfLines = 0 bodyLabel.textAlignment = .left bottomBar.backgroundColor = .white deleteButton.setTitle("删除并返回", for: .normal) deleteButton.setTitleColor(.white, for: .normal) deleteButton.titleLabel?.font = .systemFont(ofSize: 16) deleteButton.backgroundColor = UIColor(hex: 0xEF4444) deleteButton.layer.cornerRadius = 8 deleteButton.clipsToBounds = true view.addSubview(scrollView) scrollView.addSubview(contentView) [typeImageView, titleLabel, timeContainer, bodyLabel].forEach(contentView.addSubview) timeContainer.addSubview(timeLabel) view.addSubview(bottomBar) bottomBar.addSubview(deleteButton) } override func setupConstraints() { bottomBar.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview() } deleteButton.snp.makeConstraints { make in make.top.equalToSuperview().offset(16) make.leading.trailing.equalToSuperview().inset(32) make.height.equalTo(52) make.bottom.equalTo(view.safeAreaLayoutGuide).inset(16) } scrollView.snp.makeConstraints { make in make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide) make.bottom.equalTo(bottomBar.snp.top) } contentView.snp.makeConstraints { make in make.edges.equalTo(scrollView.contentLayoutGuide) make.width.equalTo(scrollView.frameLayoutGuide) make.height.greaterThanOrEqualTo(scrollView.frameLayoutGuide) } typeImageView.snp.makeConstraints { make in make.top.equalToSuperview().offset(32) make.centerX.equalToSuperview() make.size.equalTo(64) } titleLabel.snp.makeConstraints { make in make.top.equalTo(typeImageView.snp.bottom).offset(8) make.leading.trailing.equalToSuperview().inset(32) } timeContainer.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(8) make.centerX.equalToSuperview() } timeLabel.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 2, left: 4, bottom: 2, right: 4)) } bodyLabel.snp.makeConstraints { make in make.top.equalTo(timeContainer.snp.bottom).offset(24) make.leading.trailing.equalToSuperview().inset(32) make.bottom.lessThanOrEqualToSuperview().inset(24) } } override func bindActions() { deleteButton.addTarget(self, action: #selector(deleteTapped), for: .touchUpInside) viewModel.onStateChange = { [weak self] in Task { @MainActor in self?.applyState() } } viewModel.onShowMessage = { [weak self] message in Task { @MainActor in self?.showToast(message) } } viewModel.onDeleted = { [weak self] id in Task { @MainActor in self?.onDeleted?(id) self?.navigationController?.popViewController(animated: true) } } } @objc private func deleteTapped() { let alert = UIAlertController(title: "删除消息", message: "确定要删除这条消息吗?", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "取消", style: .cancel)) alert.addAction(UIAlertAction(title: "确认", style: .destructive) { [weak self] _ in guard let self else { return } Task { @MainActor in await self.viewModel.delete(api: self.api) } }) present(alert, animated: true) } private func applyState() { deleteButton.isEnabled = !viewModel.isDeleting deleteButton.alpha = viewModel.isDeleting ? 0.65 : 1 if viewModel.isDeleting { showLoading() } else { hideLoading() } } }