feat: 添加云盘与消息中心功能

This commit is contained in:
2026-07-09 22:37:43 +08:00
parent 8e356973bd
commit f20ec7f06c
91 changed files with 5437 additions and 89 deletions

View File

@ -0,0 +1,169 @@
//
// 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 = NetworkServices.shared.messageCenterAPI) {
viewModel = MessageDetailViewModel(message: message)
self.api = api
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()
}
}
}