143 lines
4.4 KiB
Swift
143 lines
4.4 KiB
Swift
//
|
|
// FeedbackVC.swift
|
|
// QuickLocation
|
|
//
|
|
|
|
import UIKit
|
|
import RxSwift
|
|
import RxCocoa
|
|
|
|
|
|
final class FeedbackVC: BaseViewController {
|
|
|
|
private enum FeedbackCategory: String, CaseIterable {
|
|
case feature = "功能问题"
|
|
case suggestion = "优化建议"
|
|
case other = "其他"
|
|
}
|
|
|
|
private struct FeedbackDraft {
|
|
let category: FeedbackCategory
|
|
let detail: String
|
|
let images: [UIImage]
|
|
let contact: String
|
|
}
|
|
|
|
private var rootView: FeedbackView!
|
|
private var selectedCategory: FeedbackCategory = .feature
|
|
private var selectedImages: [UIImage] = []
|
|
private var pendingDraft: FeedbackDraft?
|
|
private var isSubmitting = false
|
|
|
|
override func loadView() {
|
|
rootView = FeedbackView(frame: UIScreen.main.bounds)
|
|
view = rootView
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
bind()
|
|
}
|
|
|
|
private func bind() {
|
|
rootView.navView.onBackTapped = { [weak self] in
|
|
guard self?.isSubmitting == false else { return }
|
|
self?.navigationController?.popViewController(animated: true)
|
|
}
|
|
|
|
rootView.categoryButtons.forEach { button in
|
|
button.addTarget(self, action: #selector(categoryTapped(_:)), for: .touchUpInside)
|
|
}
|
|
|
|
rootView.detailTextView.rx.text.orEmpty
|
|
.subscribe(onNext: { [weak self] text in
|
|
self?.handleDetailChange(text)
|
|
})
|
|
.disposed(by: disposeBag)
|
|
|
|
rootView.contactTextField.rx.controlEvent(.editingDidEndOnExit)
|
|
.subscribe(onNext: { [weak self] in
|
|
self?.rootView.contactTextField.resignFirstResponder()
|
|
})
|
|
.disposed(by: disposeBag)
|
|
|
|
rootView.onAddImage = { [weak self] in
|
|
self?.showAlbum()
|
|
}
|
|
|
|
rootView.onDeleteImage = { [weak self] index in
|
|
guard let self, self.selectedImages.indices.contains(index) else { return }
|
|
self.selectedImages.remove(at: index)
|
|
self.rootView.updateImages(self.selectedImages)
|
|
}
|
|
|
|
rootView.saveBtn.rx.tap
|
|
.subscribe(onNext: { [weak self] in
|
|
self?.submit()
|
|
})
|
|
.disposed(by: disposeBag)
|
|
}
|
|
|
|
@objc private func categoryTapped(_ sender: UIButton) {
|
|
let categories = FeedbackCategory.allCases
|
|
guard categories.indices.contains(sender.tag) else { return }
|
|
let category = categories[sender.tag]
|
|
selectedCategory = category
|
|
rootView.updateCategory(selectedIndex: sender.tag)
|
|
}
|
|
|
|
private func handleDetailChange(_ text: String) {
|
|
let limitedText = text.count > 200 ? String(text.prefix(200)) : text
|
|
if limitedText != text {
|
|
rootView.detailTextView.text = limitedText
|
|
rootView.detailTextView.selectedRange = NSRange(location: limitedText.count, length: 0)
|
|
}
|
|
rootView.updateDetail(text: limitedText)
|
|
}
|
|
|
|
private func showAlbum() {
|
|
let remainingCount = 3 - selectedImages.count
|
|
guard remainingCount > 0, !isSubmitting else { return }
|
|
let picker = PhotoPickerVC(maxCount: remainingCount)
|
|
picker.onConfirm = { [weak self] images in
|
|
guard let self else { return }
|
|
self.selectedImages.append(contentsOf: images.prefix(remainingCount))
|
|
self.selectedImages = Array(self.selectedImages.prefix(3))
|
|
self.rootView.updateImages(self.selectedImages)
|
|
}
|
|
present(picker, animated: true)
|
|
}
|
|
|
|
private func submit() {
|
|
guard !isSubmitting else { return }
|
|
view.endEditing(true)
|
|
|
|
let detail = (rootView.detailTextView.text ?? "")
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
guard !detail.isEmpty else {
|
|
DLToast.show(text: "请补充详细问题或意见")
|
|
return
|
|
}
|
|
|
|
pendingDraft = FeedbackDraft(
|
|
category: selectedCategory,
|
|
detail: detail,
|
|
images: selectedImages,
|
|
contact: rootView.contactTextField.text ?? ""
|
|
)
|
|
isSubmitting = true
|
|
rootView.setSubmitting(true)
|
|
|
|
let successView = FeedbackSuccessView()
|
|
successView.show(in: view)
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self, weak successView] in
|
|
guard let self, let successView else { return }
|
|
successView.dismiss {
|
|
AppRouter.shared.popOrDismiss()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|