Files
suixinkan_uikit/suixinkan/UI/WildPhotographerReport/WildPhotographerReportSubmitViewController.swift
2026-07-08 14:02:44 +08:00

792 lines
31 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// WildPhotographerReportSubmitViewController.swift
// suixinkan
//
import SnapKit
import UIKit
///
final class WildPhotographerReportSubmitViewController: BaseViewController {
private let viewModel: WildPhotographerReportSubmitViewModel
private let homeViewModel: WildPhotographerReportHomeViewModel
private let scrollView = UIScrollView()
private let contentStack = UIStackView()
private let descriptionView = UITextView()
private let contactField = UITextField()
private let submitButton = UIButton(type: .system)
init(
homeViewModel: WildPhotographerReportHomeViewModel,
locationProvider: any LocationProviding = LocationProvider.shared
) {
self.homeViewModel = homeViewModel
self.viewModel = WildPhotographerReportSubmitViewModel(
homeViewModel: homeViewModel,
locationProvider: locationProvider
)
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 = AppColor.pageBackgroundSoft
scrollView.keyboardDismissMode = .interactive
scrollView.showsVerticalScrollIndicator = false
contentStack.axis = .vertical
contentStack.spacing = AppSpacing.sm
descriptionView.font = .systemFont(ofSize: 15)
descriptionView.textColor = AppColor.textPrimary
descriptionView.backgroundColor = .clear
descriptionView.textContainerInset = UIEdgeInsets(top: 10, left: 8, bottom: 34, right: 8)
descriptionView.delegate = self
contactField.placeholder = "请输入摄影师微信号或手机号(选填)"
contactField.font = .app(.body)
contactField.textColor = AppColor.textPrimary
contactField.backgroundColor = UIColor(hex: 0xFBFCFE)
contactField.layer.cornerRadius = 10
contactField.layer.borderWidth = 1
contactField.layer.borderColor = UIColor(hex: 0xCDD5E1).cgColor
contactField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: 1))
contactField.leftViewMode = .always
contactField.addTarget(self, action: #selector(contactChanged), for: .editingChanged)
submitButton.setTitle("提交举报", for: .normal)
submitButton.titleLabel?.font = .systemFont(ofSize: 22, weight: .bold)
submitButton.setTitleColor(.white, for: .normal)
submitButton.backgroundColor = AppColor.primary
submitButton.layer.cornerRadius = 14
submitButton.layer.shadowColor = AppColor.primary.cgColor
submitButton.layer.shadowOpacity = 0.22
submitButton.layer.shadowRadius = 12
submitButton.layer.shadowOffset = CGSize(width: 0, height: 6)
submitButton.addTarget(self, action: #selector(submitTapped), for: .touchUpInside)
view.addSubview(scrollView)
scrollView.addSubview(contentStack)
rebuildContent()
}
override func setupConstraints() {
scrollView.snp.makeConstraints { make in
make.edges.equalTo(view.safeAreaLayoutGuide)
}
contentStack.snp.makeConstraints { make in
make.edges.equalTo(scrollView.contentLayoutGuide).inset(UIEdgeInsets(top: 14, left: 18, bottom: 26, right: 18))
make.width.equalTo(scrollView.frameLayoutGuide).offset(-36)
}
}
override func bindActions() {
viewModel.onStateChange = { [weak self] in
Task { @MainActor in self?.rebuildContent() }
}
viewModel.onShowMessage = { [weak self] message in
Task { @MainActor in self?.showToast(message) }
}
viewModel.onSubmitSuccess = { [weak self] result in
Task { @MainActor in
guard let self else { return }
self.navigationController?.pushViewController(
WildPhotographerReportSuccessViewController(
result: result,
homeViewModel: self.homeViewModel
),
animated: true
)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
viewModel.loadInitialLocationIfNeeded()
}
@MainActor
private func rebuildContent() {
contentStack.arrangedSubviews.forEach { view in
contentStack.removeArrangedSubview(view)
view.removeFromSuperview()
}
contentStack.addArrangedSubview(makeRealNameBanner())
contentStack.addArrangedSubview(makeTypeCard())
contentStack.addArrangedSubview(makeEvidenceCard())
contentStack.addArrangedSubview(makeDescriptionCard())
contentStack.addArrangedSubview(makeLocationCard())
contentStack.addArrangedSubview(makeContactCard())
let paymentCard = makePaymentCard()
contentStack.addArrangedSubview(paymentCard)
contentStack.setCustomSpacing(16, after: paymentCard)
contentStack.addArrangedSubview(submitButton)
submitButton.snp.remakeConstraints { make in
make.height.equalTo(56)
}
}
private func makeRealNameBanner() -> UIView {
let banner = UIView()
banner.backgroundColor = UIColor(hex: 0xF6FAFF)
banner.layer.cornerRadius = 16
banner.layer.borderWidth = 1
banner.layer.borderColor = UIColor(hex: 0xBFD7FF).cgColor
let iconWrap = UIView()
iconWrap.backgroundColor = AppColor.primary.withAlphaComponent(0.10)
iconWrap.layer.cornerRadius = 17
let icon = UIImageView(image: UIImage(systemName: "checkmark.shield.fill"))
icon.tintColor = AppColor.primary
iconWrap.addSubview(icon)
icon.snp.makeConstraints { make in
make.center.equalToSuperview()
make.size.equalTo(22)
}
let message = UILabel()
message.numberOfLines = 0
message.attributedText = realNameBannerText()
banner.addSubview(iconWrap)
banner.addSubview(message)
iconWrap.snp.makeConstraints { make in
make.top.leading.equalToSuperview().offset(16)
make.size.equalTo(34)
make.bottom.lessThanOrEqualToSuperview().inset(16)
}
message.snp.makeConstraints { make in
make.top.trailing.bottom.equalToSuperview().inset(16)
make.leading.equalTo(iconWrap.snp.trailing).offset(12)
}
return banner
}
private func makeTypeCard() -> UIView {
let card = sectionCard(index: 1, title: "举报类型")
let row = UIStackView()
row.axis = .horizontal
row.spacing = 8
row.distribution = .fillEqually
WildReportType.allCases.forEach { type in
let button = WildReportTypeOptionButton()
button.apply(type: type, isSelected: type == viewModel.selectedReportType)
button.addAction(UIAction { [weak self] _ in self?.viewModel.selectReportType(type) }, for: .touchUpInside)
button.snp.makeConstraints { make in make.height.equalTo(72) }
row.addArrangedSubview(button)
}
card.stack.addArrangedSubview(row)
return card
}
private func makeEvidenceCard() -> UIView {
let card = sectionCard(index: 2, title: "上传现场证据")
card.stack.addArrangedSubview(makeEvidenceUploadBlock(
icon: "photo.fill",
title: "上传图片",
subtitle: "支持 JPG、PNG 格式,最多 9 张",
attachments: viewModel.images,
maxCount: 9,
kind: .image
))
card.stack.addArrangedSubview(makeDivider())
card.stack.addArrangedSubview(makeEvidenceUploadBlock(
icon: "video.fill",
title: "上传视频",
subtitle: "支持 MP4 格式,时长不超过 60 秒",
attachments: viewModel.videos,
maxCount: 3,
kind: .video
))
return card
}
private func makeDescriptionCard() -> UIView {
let card = sectionCard(index: 3, title: "举报说明")
descriptionView.text = viewModel.description
let inputContainer = UIView()
inputContainer.backgroundColor = UIColor(hex: 0xFBFCFE)
inputContainer.layer.cornerRadius = 12
inputContainer.layer.borderWidth = 1
inputContainer.layer.borderColor = UIColor(hex: 0xCDD5E1).cgColor
let placeholder = UILabel()
placeholder.text = "请描述摄影师的位置、特征、行为..."
placeholder.font = .systemFont(ofSize: 15)
placeholder.textColor = UIColor(hex: 0x9CA3AF)
placeholder.isHidden = !viewModel.description.isEmpty
let count = WildReportUI.label("\(viewModel.description.count)/500", font: .systemFont(ofSize: 14), color: UIColor(hex: 0x6B7280))
count.textAlignment = .right
inputContainer.addSubview(descriptionView)
inputContainer.addSubview(placeholder)
inputContainer.addSubview(count)
descriptionView.snp.makeConstraints { make in
make.edges.equalToSuperview()
make.height.equalTo(132)
}
placeholder.snp.makeConstraints { make in
make.top.equalToSuperview().offset(18)
make.leading.equalToSuperview().offset(14)
make.trailing.lessThanOrEqualToSuperview().inset(14)
}
count.snp.makeConstraints { make in
make.trailing.bottom.equalToSuperview().inset(12)
}
card.stack.addArrangedSubview(inputContainer)
return card
}
private func makeLocationCard() -> UIView {
let card = sectionCard(index: 4, title: "举报位置")
let container = UIView()
container.backgroundColor = UIColor(hex: 0xF6FAFF)
container.layer.cornerRadius = 12
container.layer.borderWidth = 1
container.layer.borderColor = UIColor(hex: 0xC8DDFF).cgColor
let iconWrap = UIView()
iconWrap.backgroundColor = AppColor.primary.withAlphaComponent(0.10)
iconWrap.layer.cornerRadius = 21
let icon = UIImageView(image: UIImage(systemName: "mappin.circle.fill"))
icon.tintColor = AppColor.primary
iconWrap.addSubview(icon)
icon.snp.makeConstraints { make in
make.center.equalToSuperview()
make.size.equalTo(30)
}
let textStack = UIStackView()
textStack.axis = .vertical
textStack.spacing = 5
let title = WildReportUI.label(viewModel.locationTitleText, font: .systemFont(ofSize: 15, weight: .semibold), color: AppColor.textPrimary, lines: 2)
title.adjustsFontSizeToFitWidth = true
title.minimumScaleFactor = 0.78
let subtitle = WildReportUI.label(viewModel.locationSubtitleText, font: .systemFont(ofSize: 12), color: AppColor.textSecondary, lines: 0)
textStack.addArrangedSubview(title)
textStack.addArrangedSubview(subtitle)
let button = UIButton(type: .system)
button.setTitle(viewModel.isUpdatingLocation ? "定位中" : "更新定位", for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 13, weight: .semibold)
button.setTitleColor(AppColor.primary, for: .normal)
button.backgroundColor = .white
button.layer.cornerRadius = 17
button.layer.borderWidth = 1
button.layer.borderColor = AppColor.primary.cgColor
button.isEnabled = !viewModel.isUpdatingLocation
button.alpha = viewModel.isUpdatingLocation ? 0.6 : 1
button.addTarget(self, action: #selector(refreshLocationTapped), for: .touchUpInside)
container.addSubview(iconWrap)
container.addSubview(textStack)
container.addSubview(button)
iconWrap.snp.makeConstraints { make in
make.leading.top.bottom.equalToSuperview().inset(12)
make.size.equalTo(42)
}
button.snp.makeConstraints { make in
make.trailing.equalToSuperview().inset(12)
make.centerY.equalToSuperview()
make.width.equalTo(82)
make.height.equalTo(34)
}
textStack.snp.makeConstraints { make in
make.leading.equalTo(iconWrap.snp.trailing).offset(12)
make.trailing.equalTo(button.snp.leading).offset(-8)
make.centerY.equalToSuperview()
make.top.greaterThanOrEqualToSuperview().offset(12)
make.bottom.lessThanOrEqualToSuperview().inset(12)
}
card.stack.addArrangedSubview(container)
return card
}
private func makeContactCard() -> UIView {
let card = sectionCard(index: 5, title: "摄影师微信号/手机号", optional: true)
contactField.text = viewModel.contact
card.stack.addArrangedSubview(contactField)
contactField.snp.makeConstraints { make in make.height.equalTo(48) }
return card
}
private func makePaymentCard() -> UIView {
let card = sectionCard(index: 6, title: "线下微信/支付宝支付截图", optional: true)
let row = UIView()
row.backgroundColor = UIColor(hex: 0xFBFCFE)
row.layer.cornerRadius = 12
row.layer.borderWidth = 1
row.layer.borderColor = UIColor(hex: 0xCDD5E1).cgColor
let iconWrap = UIView()
iconWrap.backgroundColor = AppColor.primaryLight
iconWrap.layer.cornerRadius = 12
let icon = UIImageView(image: UIImage(systemName: "creditcard.fill"))
icon.tintColor = AppColor.primary
iconWrap.addSubview(icon)
icon.snp.makeConstraints { make in
make.center.equalToSuperview()
make.size.equalTo(24)
}
let textStack = UIStackView()
textStack.axis = .vertical
textStack.spacing = 4
textStack.addArrangedSubview(WildReportUI.label("上传线下微信/支付宝支付截图(选填)", font: .systemFont(ofSize: 15, weight: .semibold), color: AppColor.textPrimary, lines: 0))
textStack.addArrangedSubview(WildReportUI.label("支持 JPG、PNG 格式,最多 3 张", font: .systemFont(ofSize: 12), color: AppColor.textSecondary))
let trailingView: UIView
if let first = viewModel.paymentImages.first {
trailingView = WildReportAttachmentTileView(attachment: first) { [weak self] in
self?.viewModel.removeAttachment(first)
}
} else {
trailingView = WildReportAddAttachmentTileView { [weak self] in
self?.viewModel.addPaymentImage()
}
}
row.addSubview(iconWrap)
row.addSubview(textStack)
row.addSubview(trailingView)
iconWrap.snp.makeConstraints { make in
make.leading.top.bottom.equalToSuperview().inset(12)
make.size.equalTo(44)
}
trailingView.snp.makeConstraints { make in
make.trailing.equalToSuperview().inset(12)
make.centerY.equalToSuperview()
make.size.equalTo(58)
}
textStack.snp.makeConstraints { make in
make.leading.equalTo(iconWrap.snp.trailing).offset(12)
make.trailing.lessThanOrEqualTo(trailingView.snp.leading).offset(-10)
make.centerY.equalToSuperview()
make.top.greaterThanOrEqualToSuperview().offset(12)
make.bottom.lessThanOrEqualToSuperview().inset(12)
}
card.stack.addArrangedSubview(row)
return card
}
private func sectionCard(index: Int, title: String, optional: Bool = false) -> WildReportCardView {
let card = WildReportCardView(spacing: 18, inset: 16)
card.layer.cornerRadius = 16
card.stack.addArrangedSubview(WildReportSectionTitleView(index: index, title: title, optional: optional))
return card
}
private func makeEvidenceUploadBlock(
icon: String,
title: String,
subtitle: String,
attachments: [WildReportAttachment],
maxCount: Int,
kind: WildReportAttachmentKind
) -> UIView {
let block = UIStackView()
block.axis = .vertical
block.spacing = 12
let header = UIStackView()
header.axis = .horizontal
header.alignment = .center
header.spacing = 12
let iconWrap = UIView()
iconWrap.backgroundColor = AppColor.primaryLight
iconWrap.layer.cornerRadius = 12
let image = UIImageView(image: UIImage(systemName: icon))
image.tintColor = AppColor.primary
iconWrap.addSubview(image)
image.snp.makeConstraints { make in
make.center.equalToSuperview()
make.size.equalTo(22)
}
iconWrap.snp.makeConstraints { make in
make.size.equalTo(42)
}
let textStack = UIStackView()
textStack.axis = .vertical
textStack.spacing = 3
textStack.addArrangedSubview(WildReportUI.label(title, font: .systemFont(ofSize: 16, weight: .bold), color: AppColor.textPrimary))
textStack.addArrangedSubview(WildReportUI.label(subtitle, font: .systemFont(ofSize: 13), color: AppColor.textSecondary, lines: 0))
header.addArrangedSubview(iconWrap)
header.addArrangedSubview(textStack)
header.addArrangedSubview(UIView())
block.addArrangedSubview(header)
let scroll = UIScrollView()
scroll.showsHorizontalScrollIndicator = false
let tiles = UIStackView()
tiles.axis = .horizontal
tiles.spacing = 10
scroll.addSubview(tiles)
tiles.snp.makeConstraints { make in
make.edges.equalTo(scroll.contentLayoutGuide).inset(UIEdgeInsets(top: 2, left: 0, bottom: 2, right: 0))
make.height.equalTo(scroll.frameLayoutGuide).offset(-4)
}
attachments.forEach { attachment in
tiles.addArrangedSubview(WildReportAttachmentTileView(attachment: attachment) { [weak self] in
self?.viewModel.removeAttachment(attachment)
})
}
let addCount = max(0, min(3, maxCount - attachments.count))
for _ in 0..<addCount {
let addTile = WildReportAddAttachmentTileView { [weak self] in
switch kind {
case .image:
self?.viewModel.addImage()
case .video:
self?.viewModel.addVideo()
case .payment:
self?.viewModel.addPaymentImage()
}
}
tiles.addArrangedSubview(addTile)
}
scroll.snp.makeConstraints { make in
make.height.equalTo(62)
}
block.addArrangedSubview(scroll)
return block
}
private func makeDivider() -> UIView {
let divider = UIView()
divider.backgroundColor = AppColor.border
divider.snp.makeConstraints { make in
make.height.equalTo(1)
}
return divider
}
private func realNameBannerText() -> NSAttributedString {
let text = NSMutableAttributedString()
[
("实名登录", AppColor.primary, UIFont.systemFont(ofSize: 16, weight: .bold)),
("后提交,", AppColor.textPrimary, UIFont.systemFont(ofSize: 16, weight: .medium)),
("景区公安", AppColor.primary, UIFont.systemFont(ofSize: 16, weight: .bold)),
("将接收并处理", AppColor.textPrimary, UIFont.systemFont(ofSize: 16, weight: .medium))
].forEach { value, color, font in
text.append(NSAttributedString(string: value, attributes: [
.foregroundColor: color,
.font: font
]))
}
let paragraph = NSMutableParagraphStyle()
paragraph.lineSpacing = 4
text.addAttribute(.paragraphStyle, value: paragraph, range: NSRange(location: 0, length: text.length))
return text
}
@objc private func addImageTapped() { viewModel.addImage() }
@objc private func addVideoTapped() { viewModel.addVideo() }
@objc private func addPaymentTapped() { viewModel.addPaymentImage() }
@objc private func refreshLocationTapped() { viewModel.refreshCurrentLocation() }
@objc private func submitTapped() { viewModel.submitReport() }
@objc private func contactChanged() { viewModel.updateContact(contactField.text ?? "") }
}
extension WildPhotographerReportSubmitViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
viewModel.updateDescription(textView.text)
}
}
///
private final class WildReportSectionTitleView: UIView {
init(index: Int, title: String, optional: Bool) {
super.init(frame: .zero)
let bar = UIView()
bar.backgroundColor = AppColor.primary
bar.layer.cornerRadius = 2
let label = UILabel()
label.font = .systemFont(ofSize: 17, weight: .bold)
label.textColor = AppColor.textPrimary
label.text = "\(index). \(title)"
let optionalLabel = UILabel()
optionalLabel.font = .systemFont(ofSize: 14)
optionalLabel.textColor = AppColor.textSecondary
optionalLabel.text = "(选填)"
optionalLabel.isHidden = !optional
addSubview(bar)
addSubview(label)
addSubview(optionalLabel)
bar.snp.makeConstraints { make in
make.leading.equalToSuperview()
make.centerY.equalToSuperview()
make.width.equalTo(4)
make.height.equalTo(20)
}
label.snp.makeConstraints { make in
make.leading.equalTo(bar.snp.trailing).offset(8)
make.top.bottom.equalToSuperview()
}
optionalLabel.snp.makeConstraints { make in
make.leading.equalTo(label.snp.trailing)
make.centerY.equalTo(label)
make.trailing.lessThanOrEqualToSuperview()
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
///
private final class WildReportTypeOptionButton: UIControl {
private let iconView = UIImageView()
private let titleLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
layer.cornerRadius = 12
layer.borderWidth = 1
let stack = UIStackView()
stack.axis = .vertical
stack.alignment = .center
stack.spacing = 6
stack.isUserInteractionEnabled = false
iconView.contentMode = .scaleAspectFit
titleLabel.font = .systemFont(ofSize: 13, weight: .bold)
titleLabel.textAlignment = .center
titleLabel.adjustsFontSizeToFitWidth = true
titleLabel.minimumScaleFactor = 0.8
titleLabel.lineBreakMode = .byClipping
addSubview(stack)
stack.addArrangedSubview(iconView)
stack.addArrangedSubview(titleLabel)
iconView.snp.makeConstraints { make in
make.size.equalTo(22)
}
stack.snp.makeConstraints { make in
make.center.equalToSuperview()
make.leading.trailing.equalToSuperview().inset(6)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func apply(type: WildReportType, isSelected: Bool) {
iconView.image = UIImage(systemName: type.iconName)
titleLabel.text = type.rawValue
backgroundColor = isSelected ? AppColor.primary : UIColor(hex: 0xF6FAFF)
layer.borderColor = (isSelected ? AppColor.primary : UIColor(hex: 0xC8DDFF)).cgColor
iconView.tintColor = isSelected ? .white : AppColor.textPrimary
titleLabel.textColor = isSelected ? .white : AppColor.textPrimary
}
}
/// tile使 Mock
private final class WildReportAttachmentTileView: UIControl {
private let gradientLayer = CAGradientLayer()
private let removeAction: () -> Void
init(attachment: WildReportAttachment, onRemove: @escaping () -> Void) {
self.removeAction = onRemove
super.init(frame: .zero)
layer.cornerRadius = 10
clipsToBounds = false
gradientLayer.cornerRadius = 10
let colors: [UIColor] = attachment.kind == .video
? [UIColor(hex: 0xB7D8FF), UIColor(hex: 0x4C8FEF)]
: [UIColor(hex: 0xCFE6FF), UIColor(hex: 0x7EB4FF)]
gradientLayer.colors = colors.map(\.cgColor)
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1)
layer.insertSublayer(gradientLayer, at: 0)
let icon = UIImageView(image: UIImage(systemName: attachment.kind == .video ? "play.circle.fill" : "mountain.2.fill"))
icon.tintColor = UIColor.white.withAlphaComponent(0.95)
icon.contentMode = .scaleAspectFit
addSubview(icon)
icon.snp.makeConstraints { make in
make.center.equalToSuperview()
make.size.equalTo(attachment.kind == .video ? 30 : 26)
}
let removeButton = UIButton(type: .system)
removeButton.setImage(UIImage(systemName: "xmark.circle.fill"), for: .normal)
removeButton.tintColor = UIColor(hex: 0x8D93A1)
removeButton.backgroundColor = .white
removeButton.layer.cornerRadius = 9
removeButton.addTarget(self, action: #selector(removeTapped), for: .touchUpInside)
addSubview(removeButton)
removeButton.snp.makeConstraints { make in
make.top.trailing.equalToSuperview().inset(-6)
make.size.equalTo(18)
}
snp.makeConstraints { make in
make.size.equalTo(58)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
}
@objc private func removeTapped() {
removeAction()
}
}
/// tile线
private final class WildReportAddAttachmentTileView: UIControl {
private let tapAction: () -> Void
private let borderLayer = CAShapeLayer()
init(action: @escaping () -> Void) {
self.tapAction = action
super.init(frame: .zero)
backgroundColor = .white
layer.cornerRadius = 10
borderLayer.strokeColor = UIColor(hex: 0xCDD5E1).cgColor
borderLayer.fillColor = UIColor.clear.cgColor
borderLayer.lineWidth = 1
borderLayer.lineDashPattern = [5, 4]
layer.addSublayer(borderLayer)
let icon = UIImageView(image: UIImage(systemName: "plus"))
icon.tintColor = UIColor(hex: 0x8D93A1)
icon.contentMode = .scaleAspectFit
addSubview(icon)
icon.snp.makeConstraints { make in
make.center.equalToSuperview()
make.size.equalTo(30)
}
addTarget(self, action: #selector(tapped), for: .touchUpInside)
snp.makeConstraints { make in
make.size.equalTo(58)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
borderLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 10).cgPath
}
@objc private func tapped() {
tapAction()
}
}
///
final class WildPhotographerReportSuccessViewController: BaseViewController {
private let viewModel: WildPhotographerReportSuccessViewModel
private let homeViewModel: WildPhotographerReportHomeViewModel
private let stack = UIStackView()
init(result: WildReportSubmitResult, homeViewModel: WildPhotographerReportHomeViewModel) {
self.viewModel = WildPhotographerReportSuccessViewModel(result: result)
self.homeViewModel = homeViewModel
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 = AppColor.pageBackgroundSoft
stack.axis = .vertical
stack.spacing = AppSpacing.sm
view.addSubview(stack)
let hero = WildReportCardView(spacing: AppSpacing.md)
hero.stack.alignment = .center
let icon = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
icon.tintColor = AppColor.success
icon.snp.makeConstraints { make in make.size.equalTo(58) }
hero.stack.addArrangedSubview(icon)
hero.stack.addArrangedSubview(WildReportUI.label("举报已提交", font: .app(.metric), color: AppColor.textPrimary))
let desc = WildReportUI.label("景区公安已收到线索,将尽快前往现场核实处理", font: .app(.body), color: AppColor.textSecondary, lines: 0)
desc.textAlignment = .center
hero.stack.addArrangedSubview(desc)
stack.addArrangedSubview(hero)
let info = WildReportCardView(spacing: AppSpacing.sm)
info.stack.addArrangedSubview(WildReportInfoRowView(title: "举报编号", value: viewModel.result.record.id))
info.stack.addArrangedSubview(WildReportInfoRowView(title: "上传材料", value: viewModel.uploadSummary))
info.stack.addArrangedSubview(WildReportInfoRowView(title: "提交时间", value: viewModel.result.record.submitTime))
stack.addArrangedSubview(info)
let progress = WildReportCardView(spacing: AppSpacing.sm)
progress.stack.addArrangedSubview(WildReportUI.label("处理进度", font: .app(.title), color: AppColor.textPrimary))
viewModel.progressSteps.enumerated().forEach { index, title in
progress.stack.addArrangedSubview(WildReportUI.label("\(index + 1). \(title)", font: .app(.body), color: AppColor.textSecondary))
}
stack.addArrangedSubview(progress)
let detailButton = AppButton(title: "查看处理进度")
detailButton.addTarget(self, action: #selector(detailTapped), for: .touchUpInside)
let homeButton = AppButton(title: "返回首页", style: .secondary)
homeButton.addTarget(self, action: #selector(homeTapped), for: .touchUpInside)
stack.addArrangedSubview(detailButton)
stack.addArrangedSubview(homeButton)
}
override func setupConstraints() {
stack.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide).offset(AppSpacing.md)
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
}
}
@objc private func detailTapped() {
navigationController?.pushViewController(
WildPhotographerReportDetailViewController(record: viewModel.result.record, homeViewModel: homeViewModel),
animated: true
)
}
@objc private func homeTapped() {
navigationController?.popToViewController(
navigationController?.viewControllers.first(where: { $0 is WildPhotographerReportHomeViewController }) ?? self,
animated: true
)
}
}